nghttp: implement Expect/Continue handshake

Requests that expect a 100 Continue will not submit their DATA frames
until the server sends the interim response.
This commit is contained in:
Jacob Champion 2016-03-21 14:43:48 -07:00
parent feb3d1b478
commit f4c7ebcbca
2 changed files with 40 additions and 7 deletions

View File

@ -160,7 +160,8 @@ Request::Request(const std::string &uri, const http_parser_url &u,
stream_id(-1), stream_id(-1),
status(0), status(0),
level(level), level(level),
expect_final_response(false) { expect_final_response(false),
expect_continue(false) {
http2::init_hdidx(res_hdidx); http2::init_hdidx(res_hdidx);
http2::init_hdidx(req_hdidx); http2::init_hdidx(req_hdidx);
} }
@ -360,10 +361,19 @@ int submit_request(HttpClient *client, const Headers &headers, Request *req) {
std::string(4_k, '-')); std::string(4_k, '-'));
} }
} }
auto num_initial_headers = build_headers.size(); auto num_initial_headers = build_headers.size();
if (!config.no_content_length && req->data_prd) {
build_headers.emplace_back("content-length", util::utos(req->data_length)); if (req->data_prd) {
if (!config.no_content_length) {
build_headers.emplace_back("content-length", util::utos(req->data_length));
}
if (config.expect_continue) {
req->expect_continue = true;
build_headers.emplace_back("expect", "100-continue");
}
} }
for (auto &kv : headers) { for (auto &kv : headers) {
size_t i; size_t i;
for (i = 0; i < num_initial_headers; ++i) { for (i = 0; i < num_initial_headers; ++i) {
@ -401,11 +411,21 @@ int submit_request(HttpClient *client, const Headers &headers, Request *req) {
nva.push_back(http2::make_nv_ls("trailer", trailer_names)); nva.push_back(http2::make_nv_ls("trailer", trailer_names));
} }
auto stream_id = int32_t stream_id;
nghttp2_submit_request(client->session, &req->pri_spec, nva.data(),
nva.size(), req->data_prd, req); if (req->expect_continue) {
stream_id = nghttp2_submit_headers(client->session, 0, -1, &req->pri_spec,
nva.data(), nva.size(), req);
} else {
stream_id =
nghttp2_submit_request(client->session, &req->pri_spec, nva.data(),
nva.size(), req->data_prd, req);
}
if (stream_id < 0) { if (stream_id < 0) {
std::cerr << "[ERROR] nghttp2_submit_request() returned error: " std::cerr << "[ERROR] nghttp2_submit_"
<< (req->expect_continue ? "headers" : "request")
<< "() returned error: "
<< nghttp2_strerror(stream_id) << std::endl; << nghttp2_strerror(stream_id) << std::endl;
return -1; return -1;
} }
@ -1619,6 +1639,18 @@ void check_response_header(nghttp2_session *session, Request *req) {
} }
if (req->status / 100 == 1) { if (req->status / 100 == 1) {
if (req->expect_continue && (req->status == 100)) {
int error = nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM,
req->stream_id, req->data_prd);
if (error) {
std::cerr << "[ERROR] nghttp2_submit_data() returned error: "
<< nghttp2_strerror(error) << std::endl;
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, req->stream_id,
NGHTTP2_INTERNAL_ERROR);
return;
}
}
req->expect_final_response = true; req->expect_final_response = true;
req->status = 0; req->status = 0;
req->res_nva.clear(); req->res_nva.clear();

View File

@ -157,6 +157,7 @@ struct Request {
// used for incoming PUSH_PROMISE // used for incoming PUSH_PROMISE
http2::HeaderIndex req_hdidx; http2::HeaderIndex req_hdidx;
bool expect_final_response; bool expect_final_response;
bool expect_continue;
}; };
struct SessionTiming { struct SessionTiming {