Merge branch 'limit-incoming-headers'
This commit is contained in:
commit
5ad753b90c
|
@ -1608,6 +1608,14 @@ typedef int (*nghttp2_on_begin_headers_callback)(nghttp2_session *session,
|
||||||
*
|
*
|
||||||
* To set this callback to :type:`nghttp2_session_callbacks`, use
|
* To set this callback to :type:`nghttp2_session_callbacks`, use
|
||||||
* `nghttp2_session_callbacks_set_on_header_callback()`.
|
* `nghttp2_session_callbacks_set_on_header_callback()`.
|
||||||
|
*
|
||||||
|
* .. warning::
|
||||||
|
*
|
||||||
|
* Application should properly limit the total buffer size to store
|
||||||
|
* incoming header fields. Without it, peer may send large number
|
||||||
|
* of header fields or large header fields to cause out of memory in
|
||||||
|
* local endpoint. Due to how HPACK works, peer can do this
|
||||||
|
* effectively without using much memory on their own.
|
||||||
*/
|
*/
|
||||||
typedef int (*nghttp2_on_header_callback)(nghttp2_session *session,
|
typedef int (*nghttp2_on_header_callback)(nghttp2_session *session,
|
||||||
const nghttp2_frame *frame,
|
const nghttp2_frame *frame,
|
||||||
|
|
|
@ -447,6 +447,7 @@ Stream::Stream(Http2Handler *handler, int32_t stream_id)
|
||||||
file_ent(nullptr),
|
file_ent(nullptr),
|
||||||
body_length(0),
|
body_length(0),
|
||||||
body_offset(0),
|
body_offset(0),
|
||||||
|
header_buffer_size(0),
|
||||||
stream_id(stream_id),
|
stream_id(stream_id),
|
||||||
echo_upload(false) {
|
echo_upload(false) {
|
||||||
auto config = handler->get_config();
|
auto config = handler->get_config();
|
||||||
|
@ -1389,6 +1390,13 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (stream->header_buffer_size + namelen + valuelen > 64_k) {
|
||||||
|
hd->submit_rst_stream(stream, NGHTTP2_INTERNAL_ERROR);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream->header_buffer_size += namelen + valuelen;
|
||||||
|
|
||||||
auto token = http2::lookup_token(name, namelen);
|
auto token = http2::lookup_token(name, namelen);
|
||||||
|
|
||||||
http2::index_header(stream->hdidx, token, stream->headers.size());
|
http2::index_header(stream->hdidx, token, stream->headers.size());
|
||||||
|
|
|
@ -119,6 +119,9 @@ struct Stream {
|
||||||
ev_timer wtimer;
|
ev_timer wtimer;
|
||||||
int64_t body_length;
|
int64_t body_length;
|
||||||
int64_t body_offset;
|
int64_t body_offset;
|
||||||
|
// Total amount of bytes (sum of name and value length) used in
|
||||||
|
// headers.
|
||||||
|
size_t header_buffer_size;
|
||||||
int32_t stream_id;
|
int32_t stream_id;
|
||||||
http2::HeaderIndex hdidx;
|
http2::HeaderIndex hdidx;
|
||||||
bool echo_upload;
|
bool echo_upload;
|
||||||
|
|
|
@ -32,7 +32,7 @@ namespace nghttp2 {
|
||||||
namespace asio_http2 {
|
namespace asio_http2 {
|
||||||
namespace client {
|
namespace client {
|
||||||
|
|
||||||
request_impl::request_impl() : strm_(nullptr) {}
|
request_impl::request_impl() : strm_(nullptr), header_buffer_size_(0) {}
|
||||||
|
|
||||||
void request_impl::write_trailer(header_map h) {
|
void request_impl::write_trailer(header_map h) {
|
||||||
auto sess = strm_->session();
|
auto sess = strm_->session();
|
||||||
|
@ -105,6 +105,12 @@ void request_impl::method(std::string s) { method_ = std::move(s); }
|
||||||
|
|
||||||
const std::string &request_impl::method() const { return method_; }
|
const std::string &request_impl::method() const { return method_; }
|
||||||
|
|
||||||
|
size_t request_impl::header_buffer_size() const { return header_buffer_size_; }
|
||||||
|
|
||||||
|
void request_impl::update_header_buffer_size(size_t len) {
|
||||||
|
header_buffer_size_ += len;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace client
|
} // namespace client
|
||||||
} // namespace asio_http2
|
} // namespace asio_http2
|
||||||
} // namespace nghttp2
|
} // namespace nghttp2
|
||||||
|
|
|
@ -75,6 +75,9 @@ public:
|
||||||
void method(std::string s);
|
void method(std::string s);
|
||||||
const std::string &method() const;
|
const std::string &method() const;
|
||||||
|
|
||||||
|
size_t header_buffer_size() const;
|
||||||
|
void update_header_buffer_size(size_t len);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
header_map header_;
|
header_map header_;
|
||||||
response_cb response_cb_;
|
response_cb response_cb_;
|
||||||
|
@ -84,6 +87,7 @@ private:
|
||||||
class stream *strm_;
|
class stream *strm_;
|
||||||
uri_ref uri_;
|
uri_ref uri_;
|
||||||
std::string method_;
|
std::string method_;
|
||||||
|
size_t header_buffer_size_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace client
|
} // namespace client
|
||||||
|
|
|
@ -30,7 +30,8 @@ namespace nghttp2 {
|
||||||
namespace asio_http2 {
|
namespace asio_http2 {
|
||||||
namespace client {
|
namespace client {
|
||||||
|
|
||||||
response_impl::response_impl() : content_length_(-1), status_code_(0) {}
|
response_impl::response_impl()
|
||||||
|
: content_length_(-1), header_buffer_size_(0), status_code_(0) {}
|
||||||
|
|
||||||
void response_impl::on_data(data_cb cb) { data_cb_ = std::move(cb); }
|
void response_impl::on_data(data_cb cb) { data_cb_ = std::move(cb); }
|
||||||
|
|
||||||
|
@ -52,6 +53,12 @@ header_map &response_impl::header() { return header_; }
|
||||||
|
|
||||||
const header_map &response_impl::header() const { return header_; }
|
const header_map &response_impl::header() const { return header_; }
|
||||||
|
|
||||||
|
size_t response_impl::header_buffer_size() const { return header_buffer_size_; }
|
||||||
|
|
||||||
|
void response_impl::update_header_buffer_size(size_t len) {
|
||||||
|
header_buffer_size_ += len;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace client
|
} // namespace client
|
||||||
} // namespace asio_http2
|
} // namespace asio_http2
|
||||||
} // namespace nghttp2
|
} // namespace nghttp2
|
||||||
|
|
|
@ -53,12 +53,16 @@ public:
|
||||||
header_map &header();
|
header_map &header();
|
||||||
const header_map &header() const;
|
const header_map &header() const;
|
||||||
|
|
||||||
|
size_t header_buffer_size() const;
|
||||||
|
void update_header_buffer_size(size_t len);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
data_cb data_cb_;
|
data_cb data_cb_;
|
||||||
|
|
||||||
header_map header_;
|
header_map header_;
|
||||||
|
|
||||||
int64_t content_length_;
|
int64_t content_length_;
|
||||||
|
size_t header_buffer_size_;
|
||||||
int status_code_;
|
int status_code_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -183,6 +183,12 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
||||||
if (token == http2::HD__STATUS) {
|
if (token == http2::HD__STATUS) {
|
||||||
res.status_code(util::parse_uint(value, valuelen));
|
res.status_code(util::parse_uint(value, valuelen));
|
||||||
} else {
|
} else {
|
||||||
|
if (res.header_buffer_size() + namelen + valuelen > 64_k) {
|
||||||
|
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
|
||||||
|
frame->hd.stream_id, NGHTTP2_INTERNAL_ERROR);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
res.update_header_buffer_size(namelen + valuelen);
|
||||||
|
|
||||||
if (token == http2::HD_CONTENT_LENGTH) {
|
if (token == http2::HD_CONTENT_LENGTH) {
|
||||||
res.content_length(util::parse_uint(value, valuelen));
|
res.content_length(util::parse_uint(value, valuelen));
|
||||||
|
@ -223,6 +229,13 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
||||||
}
|
}
|
||||||
// fall through
|
// fall through
|
||||||
default:
|
default:
|
||||||
|
if (req.header_buffer_size() + namelen + valuelen > 64_k) {
|
||||||
|
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
|
||||||
|
frame->hd.stream_id, NGHTTP2_INTERNAL_ERROR);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
req.update_header_buffer_size(namelen + valuelen);
|
||||||
|
|
||||||
req.header().emplace(
|
req.header().emplace(
|
||||||
std::string(name, name + namelen),
|
std::string(name, name + namelen),
|
||||||
header_value{std::string(value, value + valuelen),
|
header_value{std::string(value, value + valuelen),
|
||||||
|
|
|
@ -105,6 +105,13 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
||||||
}
|
}
|
||||||
// fall through
|
// fall through
|
||||||
default:
|
default:
|
||||||
|
if (req.header_buffer_size() + namelen + valuelen > 64_k) {
|
||||||
|
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, frame->hd.stream_id,
|
||||||
|
NGHTTP2_INTERNAL_ERROR);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
req.update_header_buffer_size(namelen + valuelen);
|
||||||
|
|
||||||
req.header().emplace(std::string(name, name + namelen),
|
req.header().emplace(std::string(name, name + namelen),
|
||||||
header_value{std::string(value, value + valuelen),
|
header_value{std::string(value, value + valuelen),
|
||||||
(flags & NGHTTP2_NV_FLAG_NO_INDEX) != 0});
|
(flags & NGHTTP2_NV_FLAG_NO_INDEX) != 0});
|
||||||
|
|
|
@ -28,7 +28,7 @@ namespace nghttp2 {
|
||||||
namespace asio_http2 {
|
namespace asio_http2 {
|
||||||
namespace server {
|
namespace server {
|
||||||
|
|
||||||
request_impl::request_impl() : strm_(nullptr) {}
|
request_impl::request_impl() : strm_(nullptr), header_buffer_size_(0) {}
|
||||||
|
|
||||||
const header_map &request_impl::header() const { return header_; }
|
const header_map &request_impl::header() const { return header_; }
|
||||||
|
|
||||||
|
@ -62,6 +62,12 @@ void request_impl::remote_endpoint(boost::asio::ip::tcp::endpoint ep) {
|
||||||
remote_ep_ = std::move(ep);
|
remote_ep_ = std::move(ep);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t request_impl::header_buffer_size() const { return header_buffer_size_; }
|
||||||
|
|
||||||
|
void request_impl::update_header_buffer_size(size_t len) {
|
||||||
|
header_buffer_size_ += len;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace server
|
} // namespace server
|
||||||
} // namespace asio_http2
|
} // namespace asio_http2
|
||||||
} // namespace nghttp2
|
} // namespace nghttp2
|
||||||
|
|
|
@ -58,6 +58,9 @@ public:
|
||||||
const boost::asio::ip::tcp::endpoint &remote_endpoint() const;
|
const boost::asio::ip::tcp::endpoint &remote_endpoint() const;
|
||||||
void remote_endpoint(boost::asio::ip::tcp::endpoint ep);
|
void remote_endpoint(boost::asio::ip::tcp::endpoint ep);
|
||||||
|
|
||||||
|
size_t header_buffer_size() const;
|
||||||
|
void update_header_buffer_size(size_t len);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class stream *strm_;
|
class stream *strm_;
|
||||||
header_map header_;
|
header_map header_;
|
||||||
|
@ -65,6 +68,7 @@ private:
|
||||||
uri_ref uri_;
|
uri_ref uri_;
|
||||||
data_cb on_data_cb_;
|
data_cb on_data_cb_;
|
||||||
boost::asio::ip::tcp::endpoint remote_ep_;
|
boost::asio::ip::tcp::endpoint remote_ep_;
|
||||||
|
size_t header_buffer_size_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace server
|
} // namespace server
|
||||||
|
|
|
@ -155,6 +155,7 @@ Request::Request(const std::string &uri, const http_parser_url &u,
|
||||||
inflater(nullptr),
|
inflater(nullptr),
|
||||||
html_parser(nullptr),
|
html_parser(nullptr),
|
||||||
data_prd(data_prd),
|
data_prd(data_prd),
|
||||||
|
header_buffer_size(0),
|
||||||
stream_id(-1),
|
stream_id(-1),
|
||||||
status(0),
|
status(0),
|
||||||
level(level),
|
level(level),
|
||||||
|
@ -1736,6 +1737,14 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (req->header_buffer_size + namelen + valuelen > 64_k) {
|
||||||
|
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, frame->hd.stream_id,
|
||||||
|
NGHTTP2_INTERNAL_ERROR);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
req->header_buffer_size += namelen + valuelen;
|
||||||
|
|
||||||
auto token = http2::lookup_token(name, namelen);
|
auto token = http2::lookup_token(name, namelen);
|
||||||
|
|
||||||
http2::index_header(req->res_hdidx, token, req->res_nva.size());
|
http2::index_header(req->res_hdidx, token, req->res_nva.size());
|
||||||
|
@ -1751,6 +1760,15 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (req->header_buffer_size + namelen + valuelen > 64_k) {
|
||||||
|
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
|
||||||
|
frame->push_promise.promised_stream_id,
|
||||||
|
NGHTTP2_INTERNAL_ERROR);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
req->header_buffer_size += namelen + valuelen;
|
||||||
|
|
||||||
auto token = http2::lookup_token(name, namelen);
|
auto token = http2::lookup_token(name, namelen);
|
||||||
|
|
||||||
http2::index_header(req->req_hdidx, token, req->req_nva.size());
|
http2::index_header(req->req_hdidx, token, req->req_nva.size());
|
||||||
|
@ -1838,6 +1856,10 @@ int on_frame_recv_callback2(nghttp2_session *session,
|
||||||
if (!req) {
|
if (!req) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reset for response header field reception
|
||||||
|
req->header_buffer_size = 0;
|
||||||
|
|
||||||
auto scheme = req->get_req_header(http2::HD__SCHEME);
|
auto scheme = req->get_req_header(http2::HD__SCHEME);
|
||||||
auto authority = req->get_req_header(http2::HD__AUTHORITY);
|
auto authority = req->get_req_header(http2::HD__AUTHORITY);
|
||||||
auto path = req->get_req_header(http2::HD__PATH);
|
auto path = req->get_req_header(http2::HD__PATH);
|
||||||
|
|
|
@ -150,6 +150,7 @@ struct Request {
|
||||||
nghttp2_gzip *inflater;
|
nghttp2_gzip *inflater;
|
||||||
HtmlParser *html_parser;
|
HtmlParser *html_parser;
|
||||||
const nghttp2_data_provider *data_prd;
|
const nghttp2_data_provider *data_prd;
|
||||||
|
size_t header_buffer_size;
|
||||||
int32_t stream_id;
|
int32_t stream_id;
|
||||||
int status;
|
int status;
|
||||||
// Recursion level: 0: first entity, 1: entity linked from first entity
|
// Recursion level: 0: first entity, 1: entity linked from first entity
|
||||||
|
|
Loading…
Reference in New Issue