diff --git a/src/shrpx_downstream.h b/src/shrpx_downstream.h index ff1af90c..c81fcf68 100644 --- a/src/shrpx_downstream.h +++ b/src/shrpx_downstream.h @@ -32,6 +32,7 @@ #include #include #include +#include #include @@ -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>>(); + } + 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>> + 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 diff --git a/src/shrpx_http2_upstream.cc b/src/shrpx_http2_upstream.cc index a61264fe..e67ea6ca 100644 --- a/src/shrpx_http2_upstream.cc +++ b/src/shrpx_http2_upstream.cc @@ -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; }