nghttpx: Remember which resource is pushed

Remember which resource is pushed in order to conform to the semantics
described in RFC 8297.
This commit is contained in:
Tatsuhiro Tsujikawa 2018-01-04 22:05:25 +09:00
parent a776b0dbcc
commit a31a2e3b2c
2 changed files with 49 additions and 1 deletions

View File

@ -32,6 +32,7 @@
#include <string>
#include <memory>
#include <chrono>
#include <algorithm>
#include <ev.h>
@ -207,7 +208,40 @@ struct Response {
unconsumed_body_length -= len;
}
// returns true if a resource denoted by scheme, authority, and path
// has already been pushed.
bool is_resource_pushed(const StringRef &scheme, const StringRef &authority,
const StringRef &path) const {
if (!pushed_resources) {
return false;
}
return std::find(std::begin(*pushed_resources), std::end(*pushed_resources),
std::make_tuple(scheme, authority, path)) !=
std::end(*pushed_resources);
}
// remember that a resource denoted by scheme, authority, and path
// is pushed.
void resource_pushed(const StringRef &scheme, const StringRef &authority,
const StringRef &path) {
if (!pushed_resources) {
pushed_resources = make_unique<
std::vector<std::tuple<StringRef, StringRef, StringRef>>>();
}
pushed_resources->emplace_back(scheme, authority, path);
}
FieldStore fs;
// array of the tuple of scheme, authority, and path of pushed
// resource. This is required because RFC 8297 says that server
// typically includes header fields appeared in non-final response
// header fields in final response header fields. Without checking
// that a particular resource has already been pushed, or not, we
// end up pushing the same resource at least twice. It is unknown
// that we should use more complex data structure (e.g., std::set)
// to find the resources faster.
std::unique_ptr<std::vector<std::tuple<StringRef, StringRef, StringRef>>>
pushed_resources;
// the length of response body received so far
int64_t recv_body_length;
// The number of bytes not consumed by the application yet. This is

View File

@ -2039,7 +2039,7 @@ int Http2Upstream::prepare_push_promise(Downstream *downstream) {
int rv;
const auto &req = downstream->request();
const auto &resp = downstream->response();
auto &resp = downstream->response();
auto base = http2::get_pure_path_component(req.path);
if (base.empty()) {
@ -2069,10 +2069,16 @@ int Http2Upstream::prepare_push_promise(Downstream *downstream) {
authority = req.authority;
}
if (resp.is_resource_pushed(scheme, authority, path)) {
continue;
}
rv = submit_push_promise(scheme, authority, path, downstream);
if (rv != 0) {
return -1;
}
resp.resource_pushed(scheme, authority, path);
}
}
return 0;
@ -2182,12 +2188,20 @@ int Http2Upstream::initiate_push(Downstream *downstream, const StringRef &uri) {
authority = req.authority;
}
auto &resp = downstream->response();
if (resp.is_resource_pushed(scheme, authority, path)) {
return 0;
}
rv = submit_push_promise(scheme, authority, path, downstream);
if (rv != 0) {
return -1;
}
resp.resource_pushed(scheme, authority, path);
return 0;
}