nghttpx: Add --header-field-buffer and --max-header-fields options

This commit is contained in:
Tatsuhiro Tsujikawa 2015-04-29 21:10:59 +09:00
parent f9a50333d2
commit 552f675466
8 changed files with 121 additions and 37 deletions

View File

@ -912,6 +912,8 @@ void fill_default_config() {
mod_config()->fetch_ocsp_response_file = mod_config()->fetch_ocsp_response_file =
strcopy(PKGDATADIR "/fetch-ocsp-response"); strcopy(PKGDATADIR "/fetch-ocsp-response");
mod_config()->no_ocsp = false; mod_config()->no_ocsp = false;
mod_config()->header_field_buffer = 64 * 1024;
mod_config()->max_header_fields = 100;
} }
} // namespace } // namespace
@ -1336,6 +1338,16 @@ HTTP:
won't replace anything already set. This option can be won't replace anything already set. This option can be
used several times to specify multiple header fields. used several times to specify multiple header fields.
Example: --add-response-header="foo: bar" Example: --add-response-header="foo: bar"
--header-field-buffer=<SIZE>
Set maximum buffer size for incoming HTTP header field
list. This is the sum of header name and value in
bytes.
Default: )"
<< util::utos_with_unit(get_config()->header_field_buffer) << R"(
--max-header-fields=<N>
Set maximum number of incoming HTTP header fields, which
appear in one request or response header field list.
Default: )" << get_config()->max_header_fields << R"(
Debug: Debug:
--frontend-http2-dump-request-header=<PATH> --frontend-http2-dump-request-header=<PATH>
@ -1496,6 +1508,8 @@ int main(int argc, char **argv) {
{"fetch-ocsp-response-file", required_argument, &flag, 77}, {"fetch-ocsp-response-file", required_argument, &flag, 77},
{"ocsp-update-interval", required_argument, &flag, 78}, {"ocsp-update-interval", required_argument, &flag, 78},
{"no-ocsp", no_argument, &flag, 79}, {"no-ocsp", no_argument, &flag, 79},
{"header-field-buffer", required_argument, &flag, 80},
{"max-header-fields", required_argument, &flag, 81},
{nullptr, 0, nullptr, 0}}; {nullptr, 0, nullptr, 0}};
int option_index = 0; int option_index = 0;
@ -1846,6 +1860,14 @@ int main(int argc, char **argv) {
// --no-ocsp // --no-ocsp
cmdcfgs.emplace_back(SHRPX_OPT_NO_OCSP, "yes"); cmdcfgs.emplace_back(SHRPX_OPT_NO_OCSP, "yes");
break; break;
case 80:
// --header-field-buffer
cmdcfgs.emplace_back(SHRPX_OPT_HEADER_FIELD_BUFFER, optarg);
break;
case 81:
// --max-header-fields
cmdcfgs.emplace_back(SHRPX_OPT_MAX_HEADER_FIELDS, optarg);
break;
default: default:
break; break;
} }

View File

@ -150,6 +150,8 @@ const char SHRPX_OPT_BACKEND_HTTP2_CONNECTIONS_PER_WORKER[] =
const char SHRPX_OPT_FETCH_OCSP_RESPONSE_FILE[] = "fetch-ocsp-response-file"; const char SHRPX_OPT_FETCH_OCSP_RESPONSE_FILE[] = "fetch-ocsp-response-file";
const char SHRPX_OPT_OCSP_UPDATE_INTERVAL[] = "ocsp-update-interval"; const char SHRPX_OPT_OCSP_UPDATE_INTERVAL[] = "ocsp-update-interval";
const char SHRPX_OPT_NO_OCSP[] = "no-ocsp"; const char SHRPX_OPT_NO_OCSP[] = "no-ocsp";
const char SHRPX_OPT_HEADER_FIELD_BUFFER[] = "header-field-buffer";
const char SHRPX_OPT_MAX_HEADER_FIELDS[] = "max-header-fields";
namespace { namespace {
Config *config = nullptr; Config *config = nullptr;
@ -1212,6 +1214,15 @@ int parse_config(const char *opt, const char *optarg) {
return 0; return 0;
} }
if (util::strieq(opt, SHRPX_OPT_HEADER_FIELD_BUFFER)) {
return parse_uint_with_unit(&mod_config()->header_field_buffer, opt,
optarg);
}
if (util::strieq(opt, SHRPX_OPT_MAX_HEADER_FIELDS)) {
return parse_uint(&mod_config()->max_header_fields, opt, optarg);
}
if (util::strieq(opt, "conf")) { if (util::strieq(opt, "conf")) {
LOG(WARN) << "conf: ignored"; LOG(WARN) << "conf: ignored";

View File

@ -139,6 +139,8 @@ extern const char SHRPX_OPT_BACKEND_HTTP2_CONNECTIONS_PER_WORKER[];
extern const char SHRPX_OPT_FETCH_OCSP_RESPONSE_FILE[]; extern const char SHRPX_OPT_FETCH_OCSP_RESPONSE_FILE[];
extern const char SHRPX_OPT_OCSP_UPDATE_INTERVAL[]; extern const char SHRPX_OPT_OCSP_UPDATE_INTERVAL[];
extern const char SHRPX_OPT_NO_OCSP[]; extern const char SHRPX_OPT_NO_OCSP[];
extern const char SHRPX_OPT_HEADER_FIELD_BUFFER[];
extern const char SHRPX_OPT_MAX_HEADER_FIELDS[];
union sockaddr_union { union sockaddr_union {
sockaddr_storage storage; sockaddr_storage storage;
@ -282,6 +284,8 @@ struct Config {
size_t rlimit_nofile; size_t rlimit_nofile;
size_t downstream_request_buffer_size; size_t downstream_request_buffer_size;
size_t downstream_response_buffer_size; size_t downstream_response_buffer_size;
size_t header_field_buffer;
size_t max_header_fields;
// Bit mask to disable SSL/TLS protocol versions. This will be // Bit mask to disable SSL/TLS protocol versions. This will be
// passed to SSL_CTX_set_options(). // passed to SSL_CTX_set_options().
long int tls_proto_mask; long int tls_proto_mask;

View File

@ -286,9 +286,6 @@ public:
// Change the priority of downstream // Change the priority of downstream
int change_priority(int32_t pri); int change_priority(int32_t pri);
// Maximum buffer size for header name/value pairs.
static constexpr size_t MAX_HEADERS_SUM = 128 * 1024;
bool get_rst_stream_after_end_stream() const; bool get_rst_stream_after_end_stream() const;
void set_rst_stream_after_end_stream(bool f); void set_rst_stream_after_end_stream(bool f);

View File

@ -733,10 +733,15 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
auto trailer = frame->headers.cat != NGHTTP2_HCAT_RESPONSE && auto trailer = frame->headers.cat != NGHTTP2_HCAT_RESPONSE &&
!downstream->get_expect_final_response(); !downstream->get_expect_final_response();
if (downstream->get_response_headers_sum() > Downstream::MAX_HEADERS_SUM) { if (downstream->get_response_headers_sum() + namelen + valuelen >
get_config()->header_field_buffer ||
downstream->get_response_headers().size() >=
get_config()->max_header_fields) {
if (LOG_ENABLED(INFO)) { if (LOG_ENABLED(INFO)) {
DLOG(INFO, downstream) << "Too large header block size=" DLOG(INFO, downstream)
<< downstream->get_response_headers_sum(); << "Too large or many header field size="
<< downstream->get_response_headers_sum() + namelen + valuelen
<< ", num=" << downstream->get_response_headers().size() + 1;
} }
if (trailer) { if (trailer) {

View File

@ -202,14 +202,19 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
return 0; return 0;
} }
if (downstream->get_request_headers_sum() > Downstream::MAX_HEADERS_SUM) { if (downstream->get_request_headers_sum() + namelen + valuelen >
get_config()->header_field_buffer ||
downstream->get_request_headers().size() >=
get_config()->max_header_fields) {
if (downstream->get_response_state() == Downstream::MSG_COMPLETE) { if (downstream->get_response_state() == Downstream::MSG_COMPLETE) {
return 0; return 0;
} }
if (LOG_ENABLED(INFO)) { if (LOG_ENABLED(INFO)) {
ULOG(INFO, upstream) << "Too large header block size=" ULOG(INFO, upstream) << "Too large or many header field size="
<< downstream->get_request_headers_sum(); << downstream->get_request_headers_sum() + namelen +
valuelen << ", num="
<< downstream->get_request_headers().size() + 1;
} }
// just ignore header fields if this is trailer part. // just ignore header fields if this is trailer part.

View File

@ -582,10 +582,29 @@ int htp_hdrs_completecb(http_parser *htp) {
namespace { namespace {
int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) { int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) {
auto downstream = static_cast<Downstream *>(htp->data); auto downstream = static_cast<Downstream *>(htp->data);
if (downstream->get_response_headers_sum() + len >
get_config()->header_field_buffer) {
if (LOG_ENABLED(INFO)) {
DLOG(INFO, downstream) << "Too large header block size="
<< downstream->get_response_headers_sum() + len;
}
return -1;
}
if (downstream->get_response_state() == Downstream::INITIAL) { if (downstream->get_response_state() == Downstream::INITIAL) {
if (downstream->get_response_header_key_prev()) { if (downstream->get_response_header_key_prev()) {
downstream->append_last_response_header_key(data, len); downstream->append_last_response_header_key(data, len);
} else { } else {
if (downstream->get_response_headers().size() >=
get_config()->max_header_fields) {
if (LOG_ENABLED(INFO)) {
DLOG(INFO, downstream)
<< "Too many header field num="
<< downstream->get_response_headers().size() + 1;
}
return -1;
}
downstream->add_response_header(std::string(data, len), ""); downstream->add_response_header(std::string(data, len), "");
} }
} else { } else {
@ -593,16 +612,18 @@ int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) {
if (downstream->get_response_trailer_key_prev()) { if (downstream->get_response_trailer_key_prev()) {
downstream->append_last_response_trailer_key(data, len); downstream->append_last_response_trailer_key(data, len);
} else { } else {
downstream->add_response_trailer(std::string(data, len), ""); if (downstream->get_response_headers().size() >=
} get_config()->max_header_fields) {
}
if (downstream->get_response_headers_sum() > Downstream::MAX_HEADERS_SUM) {
if (LOG_ENABLED(INFO)) { if (LOG_ENABLED(INFO)) {
DLOG(INFO, downstream) << "Too large header block size=" DLOG(INFO, downstream)
<< downstream->get_response_headers_sum(); << "Too many header field num="
<< downstream->get_response_headers().size() + 1;
} }
return -1; return -1;
} }
downstream->add_response_trailer(std::string(data, len), "");
}
}
return 0; return 0;
} }
} // namespace } // namespace
@ -610,6 +631,14 @@ int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) {
namespace { namespace {
int htp_hdr_valcb(http_parser *htp, const char *data, size_t len) { int htp_hdr_valcb(http_parser *htp, const char *data, size_t len) {
auto downstream = static_cast<Downstream *>(htp->data); auto downstream = static_cast<Downstream *>(htp->data);
if (downstream->get_response_headers_sum() + len >
get_config()->header_field_buffer) {
if (LOG_ENABLED(INFO)) {
DLOG(INFO, downstream) << "Too large header block size="
<< downstream->get_response_headers_sum() + len;
}
return -1;
}
if (downstream->get_response_state() == Downstream::INITIAL) { if (downstream->get_response_state() == Downstream::INITIAL) {
if (downstream->get_response_header_key_prev()) { if (downstream->get_response_header_key_prev()) {
downstream->set_last_response_header_value(data, len); downstream->set_last_response_header_value(data, len);
@ -623,13 +652,6 @@ int htp_hdr_valcb(http_parser *htp, const char *data, size_t len) {
downstream->append_last_response_trailer_value(data, len); downstream->append_last_response_trailer_value(data, len);
} }
} }
if (downstream->get_response_headers_sum() > Downstream::MAX_HEADERS_SUM) {
if (LOG_ENABLED(INFO)) {
DLOG(INFO, downstream) << "Too large header block size="
<< downstream->get_response_headers_sum();
}
return -1;
}
return 0; return 0;
} }
} // namespace } // namespace

View File

@ -87,10 +87,26 @@ namespace {
int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) { int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) {
auto upstream = static_cast<HttpsUpstream *>(htp->data); auto upstream = static_cast<HttpsUpstream *>(htp->data);
auto downstream = upstream->get_downstream(); auto downstream = upstream->get_downstream();
if (downstream->get_request_headers_sum() + len >
get_config()->header_field_buffer) {
if (LOG_ENABLED(INFO)) {
ULOG(INFO, upstream) << "Too large header block size="
<< downstream->get_request_headers_sum() + len;
}
return -1;
}
if (downstream->get_request_state() == Downstream::INITIAL) { if (downstream->get_request_state() == Downstream::INITIAL) {
if (downstream->get_request_header_key_prev()) { if (downstream->get_request_header_key_prev()) {
downstream->append_last_request_header_key(data, len); downstream->append_last_request_header_key(data, len);
} else { } else {
if (downstream->get_request_headers().size() >=
get_config()->max_header_fields) {
if (LOG_ENABLED(INFO)) {
ULOG(INFO, upstream) << "Too many header field num="
<< downstream->get_request_headers().size() + 1;
}
return -1;
}
downstream->add_request_header(std::string(data, len), ""); downstream->add_request_header(std::string(data, len), "");
} }
} else { } else {
@ -98,16 +114,17 @@ int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) {
if (downstream->get_request_trailer_key_prev()) { if (downstream->get_request_trailer_key_prev()) {
downstream->append_last_request_trailer_key(data, len); downstream->append_last_request_trailer_key(data, len);
} else { } else {
downstream->add_request_trailer(std::string(data, len), ""); if (downstream->get_request_headers().size() >=
} get_config()->max_header_fields) {
}
if (downstream->get_request_headers_sum() > Downstream::MAX_HEADERS_SUM) {
if (LOG_ENABLED(INFO)) { if (LOG_ENABLED(INFO)) {
ULOG(INFO, upstream) << "Too large header block size=" ULOG(INFO, upstream) << "Too many header field num="
<< downstream->get_request_headers_sum(); << downstream->get_request_headers().size() + 1;
} }
return -1; return -1;
} }
downstream->add_request_trailer(std::string(data, len), "");
}
}
return 0; return 0;
} }
} // namespace } // namespace
@ -116,6 +133,14 @@ namespace {
int htp_hdr_valcb(http_parser *htp, const char *data, size_t len) { int htp_hdr_valcb(http_parser *htp, const char *data, size_t len) {
auto upstream = static_cast<HttpsUpstream *>(htp->data); auto upstream = static_cast<HttpsUpstream *>(htp->data);
auto downstream = upstream->get_downstream(); auto downstream = upstream->get_downstream();
if (downstream->get_request_headers_sum() + len >
get_config()->header_field_buffer) {
if (LOG_ENABLED(INFO)) {
ULOG(INFO, upstream) << "Too large header block size="
<< downstream->get_request_headers_sum() + len;
}
return -1;
}
if (downstream->get_request_state() == Downstream::INITIAL) { if (downstream->get_request_state() == Downstream::INITIAL) {
if (downstream->get_request_header_key_prev()) { if (downstream->get_request_header_key_prev()) {
downstream->set_last_request_header_value(data, len); downstream->set_last_request_header_value(data, len);
@ -129,13 +154,6 @@ int htp_hdr_valcb(http_parser *htp, const char *data, size_t len) {
downstream->append_last_request_trailer_value(data, len); downstream->append_last_request_trailer_value(data, len);
} }
} }
if (downstream->get_request_headers_sum() > Downstream::MAX_HEADERS_SUM) {
if (LOG_ENABLED(INFO)) {
ULOG(INFO, upstream) << "Too large header block size="
<< downstream->get_request_headers_sum();
}
return -1;
}
return 0; return 0;
} }
} // namespace } // namespace