Commit 9cc7f9fb authored by Tatsuhiro Tsujikawa's avatar Tatsuhiro Tsujikawa

nghttp: Wait for pushed resources to complete

The statistics of pushed resources are also calculated.
parent d1c1deaf
...@@ -248,6 +248,7 @@ std::string strip_fragment(const char *raw_uri) ...@@ -248,6 +248,7 @@ std::string strip_fragment(const char *raw_uri)
namespace { namespace {
struct Request { struct Request {
Headers res_nva; Headers res_nva;
Headers push_req_nva;
// URI without fragment // URI without fragment
std::string uri; std::string uri;
std::string status; std::string status;
...@@ -261,6 +262,7 @@ struct Request { ...@@ -261,6 +262,7 @@ struct Request {
int32_t pri; int32_t pri;
// Recursion level: 0: first entity, 1: entity linked from first entity // Recursion level: 0: first entity, 1: entity linked from first entity
int level; int level;
// For pushed request, |uri| is empty and |u| is zero-cleared.
Request(const std::string& uri, const http_parser_url &u, Request(const std::string& uri, const http_parser_url &u,
const nghttp2_data_provider *data_prd, int64_t data_length, const nghttp2_data_provider *data_prd, int64_t data_length,
int32_t pri, int level = 0) int32_t pri, int level = 0)
...@@ -1149,6 +1151,29 @@ void check_response_header(nghttp2_session *session, Request* req) ...@@ -1149,6 +1151,29 @@ void check_response_header(nghttp2_session *session, Request* req)
} }
} // namespace } // namespace
namespace {
int on_begin_headers_callback(nghttp2_session *session,
const nghttp2_frame *frame,
void *user_data)
{
auto client = get_session(user_data);
switch(frame->hd.type) {
case NGHTTP2_PUSH_PROMISE: {
auto stream_id = frame->push_promise.promised_stream_id;
http_parser_url u;
memset(&u, 0, sizeof(u));
// TODO Set pri and level
auto req = util::make_unique<Request>("", u, nullptr, 0, 0, 0);
nghttp2_session_set_stream_user_data(session, stream_id, req.get());
client->reqvec.push_back(std::move(req));
check_stream_id(session, stream_id, user_data);
break;
}
}
return 0;
}
} //namespace
namespace { namespace {
int on_header_callback(nghttp2_session *session, int on_header_callback(nghttp2_session *session,
const nghttp2_frame *frame, const nghttp2_frame *frame,
...@@ -1160,17 +1185,30 @@ int on_header_callback(nghttp2_session *session, ...@@ -1160,17 +1185,30 @@ int on_header_callback(nghttp2_session *session,
verbose_on_header_callback(session, frame, name, namelen, value, valuelen, verbose_on_header_callback(session, frame, name, namelen, value, valuelen,
user_data); user_data);
} }
if(frame->hd.type != NGHTTP2_HEADERS || switch(frame->hd.type) {
frame->headers.cat != NGHTTP2_HCAT_RESPONSE) { case NGHTTP2_HEADERS: {
return 0; if(frame->headers.cat != NGHTTP2_HCAT_RESPONSE &&
frame->headers.cat != NGHTTP2_HCAT_PUSH_RESPONSE) {
break;
} }
auto req = (Request*)nghttp2_session_get_stream_user_data auto req = (Request*)nghttp2_session_get_stream_user_data
(session, frame->hd.stream_id); (session, frame->hd.stream_id);
if(!req) { if(!req) {
// Server-pushed stream does not have stream user data break;
return 0;
} }
http2::split_add_header(req->res_nva, name, namelen, value, valuelen); http2::split_add_header(req->res_nva, name, namelen, value, valuelen);
break;
}
case NGHTTP2_PUSH_PROMISE: {
auto req = (Request*)nghttp2_session_get_stream_user_data
(session, frame->push_promise.promised_stream_id);
if(!req) {
break;
}
http2::split_add_header(req->push_req_nva, name, namelen, value, valuelen);
break;
}
}
return 0; return 0;
} }
} // namespace } // namespace
...@@ -1179,8 +1217,13 @@ namespace { ...@@ -1179,8 +1217,13 @@ namespace {
int on_frame_recv_callback2 int on_frame_recv_callback2
(nghttp2_session *session, const nghttp2_frame *frame, void *user_data) (nghttp2_session *session, const nghttp2_frame *frame, void *user_data)
{ {
if(frame->hd.type == NGHTTP2_HEADERS && auto client = get_session(user_data);
frame->headers.cat == NGHTTP2_HCAT_RESPONSE) { switch(frame->hd.type) {
case NGHTTP2_HEADERS: {
if(frame->headers.cat != NGHTTP2_HCAT_RESPONSE &&
frame->headers.cat != NGHTTP2_HCAT_PUSH_RESPONSE) {
break;
}
auto req = (Request*)nghttp2_session_get_stream_user_data auto req = (Request*)nghttp2_session_get_stream_user_data
(session, frame->hd.stream_id); (session, frame->hd.stream_id);
// If this is the HTTP Upgrade with OPTIONS method to avoid POST, // If this is the HTTP Upgrade with OPTIONS method to avoid POST,
...@@ -1189,14 +1232,65 @@ int on_frame_recv_callback2 ...@@ -1189,14 +1232,65 @@ int on_frame_recv_callback2
req->record_response_time(); req->record_response_time();
check_response_header(session, req); check_response_header(session, req);
} }
} else if(frame->hd.type == NGHTTP2_SETTINGS && break;
(frame->hd.flags & NGHTTP2_FLAG_ACK)) { }
auto client = get_session(user_data); case NGHTTP2_SETTINGS:
if((frame->hd.flags & NGHTTP2_FLAG_ACK) == 0) {
break;
}
if(client->settings_timerev) { if(client->settings_timerev) {
evtimer_del(client->settings_timerev); evtimer_del(client->settings_timerev);
event_free(client->settings_timerev); event_free(client->settings_timerev);
client->settings_timerev = nullptr; client->settings_timerev = nullptr;
} }
break;
case NGHTTP2_PUSH_PROMISE: {
auto req = (Request*)nghttp2_session_get_stream_user_data
(session, frame->push_promise.promised_stream_id);
if(!req) {
break;
}
std::string scheme, authority, method, path;
for(auto& nv : req->push_req_nva) {
if(nv.first == ":scheme") {
scheme = nv.second;
continue;
}
if(nv.first == ":authority" || nv.first == "host") {
authority = nv.second;
continue;
}
if(nv.first == ":method") {
method = nv.second;
continue;
}
if(nv.first == ":path") {
path = nv.second;
continue;
}
}
if(scheme.empty() || authority.empty() || method.empty() || path.empty() ||
path[0] != '/') {
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
frame->push_promise.promised_stream_id,
NGHTTP2_PROTOCOL_ERROR);
break;
}
std::string uri = scheme;
uri += "://";
uri += authority;
uri += path;
http_parser_url u;
if(http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) != 0) {
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
frame->push_promise.promised_stream_id,
NGHTTP2_PROTOCOL_ERROR);
break;
}
req->uri = uri;
req->u = u;
break;
}
} }
if(config.verbose) { if(config.verbose) {
verbose_on_frame_recv_callback(session, frame, user_data); verbose_on_frame_recv_callback(session, frame, user_data);
...@@ -1216,10 +1310,10 @@ int on_stream_close_callback ...@@ -1216,10 +1310,10 @@ int on_stream_close_callback
update_html_parser(client, (*itr).second, nullptr, 0, 1); update_html_parser(client, (*itr).second, nullptr, 0, 1);
(*itr).second->record_complete_time(); (*itr).second->record_complete_time();
++client->complete; ++client->complete;
}
if(client->all_requests_processed()) { if(client->all_requests_processed()) {
nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR); nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR);
} }
}
return 0; return 0;
} }
} // namespace } // namespace
...@@ -1554,6 +1648,7 @@ int run(char **uris, int n) ...@@ -1554,6 +1648,7 @@ int run(char **uris, int n)
verbose_on_unknown_frame_recv_callback; verbose_on_unknown_frame_recv_callback;
} }
callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback; callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback;
callbacks.on_begin_headers_callback = on_begin_headers_callback;
callbacks.on_header_callback = on_header_callback; callbacks.on_header_callback = on_header_callback;
if(config.padding) { if(config.padding) {
callbacks.select_padding_callback = select_padding_callback; callbacks.select_padding_callback = select_padding_callback;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment