nghttp, nghttpd, nghttpx: Remove validations libnghttp2 offers

This commit is contained in:
Tatsuhiro Tsujikawa 2015-02-20 00:58:20 +09:00
parent b157d4ebb2
commit 489b4f307c
5 changed files with 15 additions and 236 deletions

View File

@ -247,8 +247,7 @@ private:
}; };
Stream::Stream(Http2Handler *handler, int32_t stream_id) Stream::Stream(Http2Handler *handler, int32_t stream_id)
: handler(handler), body_left(0), upload_left(-1), stream_id(stream_id), : handler(handler), body_left(0), stream_id(stream_id), file(-1) {
file(-1) {
auto config = handler->get_config(); auto config = handler->get_config();
ev_timer_init(&rtimer, stream_timeout_cb, 0., config->stream_read_timeout); ev_timer_init(&rtimer, stream_timeout_cb, 0., config->stream_read_timeout);
ev_timer_init(&wtimer, stream_timeout_cb, 0., config->stream_write_timeout); ev_timer_init(&wtimer, stream_timeout_cb, 0., config->stream_write_timeout);
@ -1021,44 +1020,8 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
return 0; return 0;
} }
if (!http2::check_nv(name, namelen, value, valuelen)) {
hd->submit_rst_stream(stream, NGHTTP2_PROTOCOL_ERROR);
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
}
auto token = http2::lookup_token(name, namelen); auto token = http2::lookup_token(name, namelen);
if (name[0] == ':') {
if ((!stream->headers.empty() &&
stream->headers.back().name.c_str()[0] != ':') ||
!http2::check_http2_request_pseudo_header(stream->hdidx, token)) {
hd->submit_rst_stream(stream, NGHTTP2_PROTOCOL_ERROR);
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
}
}
if (!http2::http2_header_allowed(token)) {
hd->submit_rst_stream(stream, NGHTTP2_PROTOCOL_ERROR);
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
}
switch (token) {
case http2::HD_CONTENT_LENGTH: {
auto upload_left = util::parse_uint(value, valuelen);
if (upload_left != -1) {
stream->upload_left = upload_left;
}
break;
}
case http2::HD_TE:
if (!util::strieq("trailers", value, valuelen)) {
hd->submit_rst_stream(stream, NGHTTP2_PROTOCOL_ERROR);
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
}
break;
}
http2::index_header(stream->hdidx, token, stream->headers.size()); http2::index_header(stream->hdidx, token, stream->headers.size());
http2::add_header(stream->headers, name, namelen, value, valuelen, http2::add_header(stream->headers, name, namelen, value, valuelen,
flags & NGHTTP2_NV_FLAG_NO_INDEX, token); flags & NGHTTP2_NV_FLAG_NO_INDEX, token);
@ -1104,10 +1067,6 @@ int hd_on_frame_recv_callback(nghttp2_session *session,
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
remove_stream_read_timeout(stream); remove_stream_read_timeout(stream);
if (stream->upload_left > 0) {
hd->submit_rst_stream(stream, NGHTTP2_PROTOCOL_ERROR);
return 0;
}
if (!hd->get_config()->early_response) { if (!hd->get_config()->early_response) {
prepare_response(stream, hd); prepare_response(stream, hd);
} }
@ -1125,11 +1084,6 @@ int hd_on_frame_recv_callback(nghttp2_session *session,
if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) { if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) {
if (!http2::http2_mandatory_request_headers_presence(stream->hdidx)) {
hd->submit_rst_stream(stream, NGHTTP2_PROTOCOL_ERROR);
return 0;
}
auto expect100 = auto expect100 =
http2::get_header(stream->hdidx, http2::HD_EXPECT, stream->headers); http2::get_header(stream->hdidx, http2::HD_EXPECT, stream->headers);
@ -1144,10 +1098,6 @@ int hd_on_frame_recv_callback(nghttp2_session *session,
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
remove_stream_read_timeout(stream); remove_stream_read_timeout(stream);
if (stream->upload_left > 0) {
hd->submit_rst_stream(stream, NGHTTP2_PROTOCOL_ERROR);
return 0;
}
if (!hd->get_config()->early_response) { if (!hd->get_config()->early_response) {
prepare_response(stream, hd); prepare_response(stream, hd);
} }
@ -1244,15 +1194,6 @@ int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
// TODO Handle POST // TODO Handle POST
if (stream->upload_left != -1) {
if (stream->upload_left < static_cast<int64_t>(len)) {
stream->upload_left = -1;
hd->submit_rst_stream(stream, NGHTTP2_PROTOCOL_ERROR);
return 0;
}
stream->upload_left -= len;
}
add_stream_read_timeout(stream); add_stream_read_timeout(stream);
return 0; return 0;

View File

@ -81,7 +81,6 @@ struct Stream {
ev_timer rtimer; ev_timer rtimer;
ev_timer wtimer; ev_timer wtimer;
int64_t body_left; int64_t body_left;
int64_t upload_left;
int32_t stream_id; int32_t stream_id;
int file; int file;
http2::HeaderIndex hdidx; http2::HeaderIndex hdidx;

View File

@ -1505,12 +1505,6 @@ int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
user_data); user_data);
} }
if (req->expect_final_response) {
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, stream_id,
NGHTTP2_PROTOCOL_ERROR);
return 0;
}
req->response_len += len; req->response_len += len;
if (req->inflater) { if (req->inflater) {
@ -1667,12 +1661,6 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
flags, user_data); flags, user_data);
} }
if (!http2::check_nv(name, namelen, value, valuelen)) {
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, frame->hd.stream_id,
NGHTTP2_PROTOCOL_ERROR);
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
}
switch (frame->hd.type) { switch (frame->hd.type) {
case NGHTTP2_HEADERS: { case NGHTTP2_HEADERS: {
auto req = static_cast<Request *>( auto req = static_cast<Request *>(
@ -1682,23 +1670,14 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
break; break;
} }
if (frame->headers.cat != NGHTTP2_HCAT_RESPONSE && /* ignore trailer header */
frame->headers.cat != NGHTTP2_HCAT_PUSH_RESPONSE && if (frame->headers.cat == NGHTTP2_HCAT_HEADERS &&
(frame->headers.cat != NGHTTP2_HCAT_HEADERS || !req->expect_final_response) {
!req->expect_final_response)) {
break; break;
} }
auto token = http2::lookup_token(name, namelen); auto token = http2::lookup_token(name, namelen);
if (name[0] == ':') {
if (!req->response_pseudo_header_allowed(token)) {
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
frame->hd.stream_id, NGHTTP2_PROTOCOL_ERROR);
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
}
}
http2::index_header(req->res_hdidx, token, req->res_nva.size()); http2::index_header(req->res_hdidx, token, req->res_nva.size());
http2::add_header(req->res_nva, name, namelen, value, valuelen, http2::add_header(req->res_nva, name, namelen, value, valuelen,
flags & NGHTTP2_NV_FLAG_NO_INDEX, token); flags & NGHTTP2_NV_FLAG_NO_INDEX, token);
@ -1714,15 +1693,6 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
auto token = http2::lookup_token(name, namelen); auto token = http2::lookup_token(name, namelen);
if (name[0] == ':') {
if (!req->push_request_pseudo_header_allowed(token)) {
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
frame->push_promise.promised_stream_id,
NGHTTP2_PROTOCOL_ERROR);
break;
}
}
http2::index_header(req->req_hdidx, token, req->req_nva.size()); http2::index_header(req->req_hdidx, token, req->req_nva.size());
http2::add_header(req->req_nva, name, namelen, value, valuelen, http2::add_header(req->req_nva, name, namelen, value, valuelen,
flags & NGHTTP2_NV_FLAG_NO_INDEX, token); flags & NGHTTP2_NV_FLAG_NO_INDEX, token);
@ -1752,12 +1722,6 @@ int on_frame_recv_callback2(nghttp2_session *session,
; ;
} }
if (req->expect_final_response) {
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, frame->hd.stream_id,
NGHTTP2_PROTOCOL_ERROR);
return 0;
}
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
req->record_response_end_time(); req->record_response_end_time();
} }
@ -1795,11 +1759,6 @@ int on_frame_recv_callback2(nghttp2_session *session,
} }
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
if (req->expect_final_response) {
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
frame->hd.stream_id, NGHTTP2_PROTOCOL_ERROR);
return 0;
}
req->record_response_end_time(); req->record_response_end_time();
} }
@ -1826,9 +1785,7 @@ int on_frame_recv_callback2(nghttp2_session *session,
authority = req->get_req_header(http2::HD_HOST); authority = req->get_req_header(http2::HD_HOST);
} }
if (!scheme || !authority || !method || !path || scheme->value.empty() || if (!scheme || !authority || !method || !path || path->value[0] != '/') {
authority->value.empty() || method->value.empty() ||
path->value.empty() || path->value[0] != '/') {
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
frame->push_promise.promised_stream_id, frame->push_promise.promised_stream_id,
NGHTTP2_PROTOCOL_ERROR); NGHTTP2_PROTOCOL_ERROR);

View File

@ -692,7 +692,6 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
const uint8_t *name, size_t namelen, const uint8_t *name, size_t namelen,
const uint8_t *value, size_t valuelen, uint8_t flags, const uint8_t *value, size_t valuelen, uint8_t flags,
void *user_data) { void *user_data) {
auto http2session = static_cast<Http2Session *>(user_data);
auto sd = static_cast<StreamData *>( auto sd = static_cast<StreamData *>(
nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)); nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
if (!sd || !sd->dconn) { if (!sd || !sd->dconn) {
@ -716,40 +715,12 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
} }
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
} }
if (!http2::check_nv(name, namelen, value, valuelen)) {
return 0;
}
auto token = http2::lookup_token(name, namelen); auto token = http2::lookup_token(name, namelen);
if (name[0] == ':') {
if (!downstream->response_pseudo_header_allowed(token)) {
http2session->submit_rst_stream(frame->hd.stream_id,
NGHTTP2_PROTOCOL_ERROR);
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
}
}
if (!http2::http2_header_allowed(token)) {
http2session->submit_rst_stream(frame->hd.stream_id,
NGHTTP2_PROTOCOL_ERROR);
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
}
if (token == http2::HD_CONTENT_LENGTH) { if (token == http2::HD_CONTENT_LENGTH) {
// libnghttp2 guarantees this can be parsed
auto len = util::parse_uint(value, valuelen); auto len = util::parse_uint(value, valuelen);
if (len == -1) {
http2session->submit_rst_stream(frame->hd.stream_id,
NGHTTP2_PROTOCOL_ERROR);
downstream->set_response_state(Downstream::MSG_BAD_HEADER);
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
}
if (downstream->get_response_content_length() != -1) {
http2session->submit_rst_stream(frame->hd.stream_id,
NGHTTP2_PROTOCOL_ERROR);
downstream->set_response_state(Downstream::MSG_BAD_HEADER);
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
}
downstream->set_response_content_length(len); downstream->set_response_content_length(len);
} }
@ -796,18 +767,8 @@ int on_response_headers(Http2Session *http2session, Downstream *downstream,
downstream->set_expect_final_response(false); downstream->set_expect_final_response(false);
auto status = downstream->get_response_header(http2::HD__STATUS); auto status = downstream->get_response_header(http2::HD__STATUS);
int status_code; // libnghttp2 guarantees this exists and can be parsed
auto status_code = http2::parse_http_status_code(status->value);
if (!http2::non_empty_value(status) ||
(status_code = http2::parse_http_status_code(status->value)) == -1) {
http2session->submit_rst_stream(frame->hd.stream_id,
NGHTTP2_PROTOCOL_ERROR);
downstream->set_response_state(Downstream::MSG_RESET);
call_downstream_readcb(http2session, downstream);
return 0;
}
downstream->set_response_http_status(status_code); downstream->set_response_http_status(status_code);
downstream->set_response_major(2); downstream->set_response_major(2);
@ -912,12 +873,6 @@ int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame,
break; break;
} }
if (downstream->get_expect_final_response()) {
http2session->submit_rst_stream(frame->hd.stream_id,
NGHTTP2_PROTOCOL_ERROR);
break;
}
auto upstream = downstream->get_upstream(); auto upstream = downstream->get_upstream();
rv = upstream->on_downstream_body(downstream, nullptr, 0, true); rv = upstream->on_downstream_body(downstream, nullptr, 0, true);
if (rv != 0) { if (rv != 0) {
@ -977,12 +932,6 @@ int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame,
} }
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
if (downstream->get_expect_final_response()) {
http2session->submit_rst_stream(frame->hd.stream_id,
NGHTTP2_PROTOCOL_ERROR);
return 0;
}
downstream->disable_downstream_rtimer(); downstream->disable_downstream_rtimer();
if (downstream->get_response_state() == Downstream::HEADER_COMPLETE) { if (downstream->get_response_state() == Downstream::HEADER_COMPLETE) {

View File

@ -180,49 +180,12 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
return 0; return 0;
} }
if (!http2::check_nv(name, namelen, value, valuelen)) {
upstream->rst_stream(downstream, NGHTTP2_PROTOCOL_ERROR);
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
}
auto token = http2::lookup_token(name, namelen); auto token = http2::lookup_token(name, namelen);
if (name[0] == ':') { if (token == http2::HD_CONTENT_LENGTH) {
if (!downstream->request_pseudo_header_allowed(token)) { // libnghttp2 guarantees this can be parsed
upstream->rst_stream(downstream, NGHTTP2_PROTOCOL_ERROR);
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
}
}
if (!http2::http2_header_allowed(token)) {
upstream->rst_stream(downstream, NGHTTP2_PROTOCOL_ERROR);
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
}
switch (token) {
case http2::HD_CONTENT_LENGTH: {
auto len = util::parse_uint(value, valuelen); auto len = util::parse_uint(value, valuelen);
if (len == -1) {
if (upstream->error_reply(downstream, 400) != 0) {
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
}
return 0;
}
if (downstream->get_request_content_length() != -1) {
if (upstream->error_reply(downstream, 400) != 0) {
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
}
return 0;
}
downstream->set_request_content_length(len); downstream->set_request_content_length(len);
break;
}
case http2::HD_TE:
if (!util::strieq("trailers", value, valuelen)) {
upstream->rst_stream(downstream, NGHTTP2_PROTOCOL_ERROR);
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
}
break;
} }
downstream->add_request_header(name, namelen, value, valuelen, downstream->add_request_header(name, namelen, value, valuelen,
@ -282,34 +245,19 @@ int on_request_headers(Http2Upstream *upstream, Downstream *downstream,
http2::dump_nv(get_config()->http2_upstream_dump_request_header, nva); http2::dump_nv(get_config()->http2_upstream_dump_request_header, nva);
} }
auto host = downstream->get_request_header(http2::HD_HOST);
auto authority = downstream->get_request_header(http2::HD__AUTHORITY); auto authority = downstream->get_request_header(http2::HD__AUTHORITY);
auto path = downstream->get_request_header(http2::HD__PATH); auto path = downstream->get_request_header(http2::HD__PATH);
auto method = downstream->get_request_header(http2::HD__METHOD); auto method = downstream->get_request_header(http2::HD__METHOD);
auto scheme = downstream->get_request_header(http2::HD__SCHEME); auto scheme = downstream->get_request_header(http2::HD__SCHEME);
bool is_connect = method && "CONNECT" == method->value; bool is_connect = "CONNECT" == method->value;
bool having_host = http2::non_empty_value(host);
bool having_authority = http2::non_empty_value(authority); bool having_authority = http2::non_empty_value(authority);
if (is_connect) { // presence of mandatory header fields are guaranteed by libnghttp2.
// Here we strictly require :authority header field. if (!is_connect) {
if (scheme || path || !having_authority) { // For HTTP/2 proxy, :authority is required.
if (get_config()->http2_proxy && !having_authority) {
upstream->rst_stream(downstream, NGHTTP2_PROTOCOL_ERROR); upstream->rst_stream(downstream, NGHTTP2_PROTOCOL_ERROR);
return 0;
}
} else {
// For proxy, :authority is required. Otherwise, we can accept
// :authority or host for methods.
if (!http2::non_empty_value(method) || !http2::non_empty_value(scheme) ||
(get_config()->http2_proxy && !having_authority) ||
(!get_config()->http2_proxy && !having_authority && !having_host) ||
!http2::non_empty_value(path)) {
upstream->rst_stream(downstream, NGHTTP2_PROTOCOL_ERROR);
return 0; return 0;
} }
} }
@ -327,11 +275,6 @@ int on_request_headers(Http2Upstream *upstream, Downstream *downstream,
downstream->set_request_state(Downstream::HEADER_COMPLETE); downstream->set_request_state(Downstream::HEADER_COMPLETE);
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
if (!downstream->validate_request_bodylen()) {
upstream->rst_stream(downstream, NGHTTP2_PROTOCOL_ERROR);
return 0;
}
downstream->disable_upstream_rtimer(); downstream->disable_upstream_rtimer();
downstream->set_request_state(Downstream::MSG_COMPLETE); downstream->set_request_state(Downstream::MSG_COMPLETE);
@ -411,11 +354,6 @@ int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame,
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
downstream->disable_upstream_rtimer(); downstream->disable_upstream_rtimer();
if (!downstream->validate_request_bodylen()) {
upstream->rst_stream(downstream, NGHTTP2_PROTOCOL_ERROR);
return 0;
}
downstream->end_upload_data(); downstream->end_upload_data();
downstream->set_request_state(Downstream::MSG_COMPLETE); downstream->set_request_state(Downstream::MSG_COMPLETE);
} }
@ -435,11 +373,6 @@ int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame,
} }
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
if (!downstream->validate_request_bodylen()) {
upstream->rst_stream(downstream, NGHTTP2_PROTOCOL_ERROR);
return 0;
}
downstream->disable_upstream_rtimer(); downstream->disable_upstream_rtimer();
downstream->end_upload_data(); downstream->end_upload_data();