nghttpd: Issue RST_STREAM if content-length does not match uploaded bytes

This commit is contained in:
Tatsuhiro Tsujikawa 2015-01-15 23:07:25 +09:00
parent 22e41bab3f
commit d142830109
7 changed files with 77 additions and 1 deletions

View File

@ -246,7 +246,8 @@ private:
};
Stream::Stream(Http2Handler *handler, int32_t stream_id)
: handler(handler), body_left(0), stream_id(stream_id), file(-1) {
: handler(handler), body_left(0), upload_left(-1), stream_id(stream_id),
file(-1) {
auto config = handler->get_config();
ev_timer_init(&rtimer, stream_timeout_cb, 0., config->stream_read_timeout);
ev_timer_init(&wtimer, stream_timeout_cb, 0., config->stream_write_timeout);
@ -1048,6 +1049,13 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
}
if (token == http2::HD_CONTENT_LENGTH) {
auto upload_left = util::parse_uint(value, valuelen);
if (upload_left != -1) {
stream->upload_left = upload_left;
}
}
http2::index_header(stream->hdidx, token, stream->headers.size());
http2::add_header(stream->headers, name, namelen, value, valuelen,
flags & NGHTTP2_NV_FLAG_NO_INDEX);
@ -1093,6 +1101,10 @@ int hd_on_frame_recv_callback(nghttp2_session *session,
if (frame->hd.flags & NGHTTP2_FLAG_END_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) {
prepare_response(stream, hd);
}
@ -1129,6 +1141,10 @@ int hd_on_frame_recv_callback(nghttp2_session *session,
if (frame->hd.flags & NGHTTP2_FLAG_END_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) {
prepare_response(stream, hd);
}
@ -1230,6 +1246,15 @@ int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
// TODO Handle POST
if (stream->upload_left != -1) {
if (stream->upload_left < len) {
stream->upload_left = -1;
hd->submit_rst_stream(stream, NGHTTP2_PROTOCOL_ERROR);
return 0;
}
stream->upload_left -= len;
}
add_stream_read_timeout(stream);
return 0;

View File

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

View File

@ -133,6 +133,7 @@ int main(int argc, char *argv[]) {
shrpx::test_util_utos_with_unit) ||
!CU_add_test(pSuite, "util_parse_uint_with_unit",
shrpx::test_util_parse_uint_with_unit) ||
!CU_add_test(pSuite, "util_parse_uint", shrpx::test_util_parse_uint) ||
!CU_add_test(pSuite, "gzip_inflate", test_nghttp2_gzip_inflate) ||
!CU_add_test(pSuite, "ringbuf_write", nghttp2::test_ringbuf_write) ||
!CU_add_test(pSuite, "ringbuf_iovec", nghttp2::test_ringbuf_iovec) ||

View File

@ -950,6 +950,33 @@ int64_t parse_uint_with_unit(const char *s) {
return n * mul;
}
int64_t parse_uint(const char *s) {
return parse_uint(reinterpret_cast<const uint8_t *>(s), strlen(s));
}
int64_t parse_uint(const uint8_t *s, size_t len) {
if (len == 0) {
return -1;
}
constexpr int64_t max = std::numeric_limits<int64_t>::max();
int64_t n = 0;
for (size_t i = 0; i < len; ++i) {
if ('0' <= s[i] && s[i] <= '9') {
if (n > max / 10) {
return -1;
}
n *= 10;
if (n > max - (s[i] - '0')) {
return -1;
}
n += s[i] - '0';
continue;
}
return -1;
}
return n;
}
} // namespace util
} // namespace nghttp2

View File

@ -502,6 +502,11 @@ bool ipv6_numeric_addr(const char *host);
// -1.
int64_t parse_uint_with_unit(const char *s);
// Parses NULL terminated string |s| as unsigned integer and returns
// the parsed integer. If there is an error, returns -1.
int64_t parse_uint(const char *s);
int64_t parse_uint(const uint8_t *s, size_t len);
} // namespace util
} // namespace nghttp2

View File

@ -207,4 +207,20 @@ void test_util_parse_uint_with_unit(void) {
CU_ASSERT(-1 == util::parse_uint_with_unit(""));
}
void test_util_parse_uint(void) {
CU_ASSERT(0 == util::parse_uint("0"));
CU_ASSERT(1023 == util::parse_uint("1023"));
CU_ASSERT(-1 == util::parse_uint("1k"));
CU_ASSERT(9223372036854775807LL == util::parse_uint("9223372036854775807"));
// check overflow case
CU_ASSERT(-1 == util::parse_uint("9223372036854775808"));
CU_ASSERT(-1 == util::parse_uint("10000000000000000000"));
// bad characters
CU_ASSERT(-1 == util::parse_uint("1.1"));
CU_ASSERT(-1 == util::parse_uint("1a"));
CU_ASSERT(-1 == util::parse_uint("a1"));
CU_ASSERT(-1 == util::parse_uint("1T"));
CU_ASSERT(-1 == util::parse_uint(""));
}
} // namespace shrpx

View File

@ -39,6 +39,7 @@ void test_util_select_h2(void);
void test_util_ipv6_numeric_addr(void);
void test_util_utos_with_unit(void);
void test_util_parse_uint_with_unit(void);
void test_util_parse_uint(void);
} // namespace shrpx