static int set_h1_header(void *ctx, const char *key, const char *value)
{
h1_ctx *x = ctx;
- x->status = h2_req_add_header(x->headers, x->pool, key, strlen(key),
- value, strlen(value));
- return (x->status == APR_SUCCESS)? 1 : 0;
+ int was_added;
+ h2_req_add_header(x->headers, x->pool, key, strlen(key), value, strlen(value), 0, &was_added);
+ return 1;
}
apr_status_t h2_request_rcreate(h2_request **preq, apr_pool_t *pool,
apr_status_t h2_request_add_header(h2_request *req, apr_pool_t *pool,
const char *name, size_t nlen,
- const char *value, size_t vlen)
+ const char *value, size_t vlen,
+ size_t max_field_len, int *pwas_added)
{
apr_status_t status = APR_SUCCESS;
+ *pwas_added = 0;
if (nlen <= 0) {
return status;
}
}
}
else {
- /* non-pseudo header, append to work bucket of stream */
- status = h2_req_add_header(req->headers, pool, name, nlen, value, vlen);
+ /* non-pseudo header, add to table */
+ status = h2_req_add_header(req->headers, pool, name, nlen, value, vlen,
+ max_field_len, pwas_added);
}
return status;
static apr_status_t add_trailer(h2_stream *stream,
const char *name, size_t nlen,
- const char *value, size_t vlen)
+ const char *value, size_t vlen,
+ size_t max_field_len, int *pwas_added)
{
conn_rec *c = stream->session->c;
char *hname, *hvalue;
+ const char *existing;
+ *pwas_added = 0;
if (nlen == 0 || name[0] == ':') {
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, APR_EINVAL, c,
H2_STRM_LOG(APLOGNO(03060), stream,
stream->trailers = apr_table_make(stream->pool, 5);
}
hname = apr_pstrndup(stream->pool, name, nlen);
- hvalue = apr_pstrndup(stream->pool, value, vlen);
h2_util_camel_case_header(hname, nlen);
+ existing = apr_table_get(stream->trailers, hname);
+ if (max_field_len
+ && ((existing? strlen(existing)+2 : 0) + vlen + nlen + 2 > max_field_len)) {
+ /* "key: (oldval, )?nval" is too long */
+ return APR_EINVAL;
+ }
+ if (!existing) *pwas_added = 1;
+ hvalue = apr_pstrndup(stream->pool, value, vlen);
apr_table_mergen(stream->trailers, hname, hvalue);
ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c,
H2_STRM_MSG(stream, "added trailer '%s: %s'"), hname, hvalue);
const char *value, size_t vlen)
{
h2_session *session = stream->session;
- int error = 0;
- apr_status_t status;
+ int error = 0, was_added = 0;
+ apr_status_t status = APR_SUCCESS;
if (stream->has_response) {
return APR_EINVAL;
}
- ++stream->request_headers_added;
+
if (name[0] == ':') {
if ((vlen) > session->s->limit_req_line) {
/* pseudo header: approximation of request line size check */
"LimitRequestFieldSize: %s"), name);
}
error = HTTP_REQUEST_URI_TOO_LARGE;
+ goto cleanup;
+ }
+ }
+
+ if (session->s->limit_req_fields > 0
+ && stream->request_headers_added > session->s->limit_req_fields) {
+ /* already over limit, count this attempt, but do not take it in */
+ ++stream->request_headers_added;
+ }
+ else if (H2_SS_IDLE == stream->state) {
+ if (!stream->rtmp) {
+ stream->rtmp = h2_req_create(stream->id, stream->pool,
+ NULL, NULL, NULL, NULL, NULL, 0);
}
+ status = h2_request_add_header(stream->rtmp, stream->pool,
+ name, nlen, value, vlen,
+ session->s->limit_req_fieldsize, &was_added);
+ if (was_added) ++stream->request_headers_added;
+ }
+ else if (H2_SS_OPEN == stream->state) {
+ status = add_trailer(stream, name, nlen, value, vlen,
+ session->s->limit_req_fieldsize, &was_added);
+ if (was_added) ++stream->request_headers_added;
}
- else if ((nlen + 2 + vlen) > session->s->limit_req_fieldsize) {
+ else {
+ status = APR_EINVAL;
+ goto cleanup;
+ }
+
+ if (APR_EINVAL == status) {
/* header too long */
if (!h2_stream_is_ready(stream)) {
ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, session->c,
(int)H2MIN(nlen, 80), name);
}
error = HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE;
+ goto cleanup;
}
- if (stream->request_headers_added > session->s->limit_req_fields + 4) {
- /* too many header lines, include 4 pseudo headers */
- if (stream->request_headers_added
- > session->s->limit_req_fields + 4 + 100) {
- /* yeah, right */
+ if (session->s->limit_req_fields > 0
+ && stream->request_headers_added > session->s->limit_req_fields) {
+ /* too many header lines */
+ if (stream->request_headers_added > session->s->limit_req_fields + 100) {
+ /* yeah, right, this request is way over the limit, say goodbye */
h2_stream_rst(stream, H2_ERR_ENHANCE_YOUR_CALM);
return APR_ECONNRESET;
}
"exceeds LimitRequestFields"));
}
error = HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE;
+ goto cleanup;
}
+cleanup:
if (error) {
set_error_response(stream, error);
return APR_EINVAL;
}
- else if (H2_SS_IDLE == stream->state) {
- if (!stream->rtmp) {
- stream->rtmp = h2_req_create(stream->id, stream->pool,
- NULL, NULL, NULL, NULL, NULL, 0);
- }
- status = h2_request_add_header(stream->rtmp, stream->pool,
- name, nlen, value, vlen);
- }
- else if (H2_SS_OPEN == stream->state) {
- status = add_trailer(stream, name, nlen, value, vlen);
- }
- else {
- status = APR_EINVAL;
- }
-
- if (status != APR_SUCCESS) {
+ else if (status != APR_SUCCESS) {
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
H2_STRM_MSG(stream, "header %s not accepted"), name);
h2_stream_dispatch(stream, H2_SEV_CANCELLED);
}
apr_status_t h2_req_add_header(apr_table_t *headers, apr_pool_t *pool,
- const char *name, size_t nlen,
- const char *value, size_t vlen)
+ const char *name, size_t nlen,
+ const char *value, size_t vlen,
+ size_t max_field_len, int *pwas_added)
{
char *hname, *hvalue;
+ const char *existing;
+ *pwas_added = 0;
if (h2_req_ignore_header(name, nlen)) {
return APR_SUCCESS;
}
else if (H2_HD_MATCH_LIT("cookie", name, nlen)) {
- const char *existing = apr_table_get(headers, "cookie");
+ existing = apr_table_get(headers, "cookie");
if (existing) {
char *nval;
/* Cookie header come separately in HTTP/2, but need
* to be merged by "; " (instead of default ", ")
*/
+ if (max_field_len && strlen(existing) + vlen + nlen + 4 > max_field_len) {
+ /* "key: oldval, nval" is too long */
+ return APR_EINVAL;
+ }
hvalue = apr_pstrndup(pool, value, vlen);
nval = apr_psprintf(pool, "%s; %s", existing, hvalue);
apr_table_setn(headers, "Cookie", nval);
}
hname = apr_pstrndup(pool, name, nlen);
- hvalue = apr_pstrndup(pool, value, vlen);
h2_util_camel_case_header(hname, nlen);
+ existing = apr_table_get(headers, hname);
+ if (max_field_len) {
+ if ((existing? strlen(existing)+2 : 0) + vlen + nlen + 2 > max_field_len) {
+ /* "key: (oldval, )?nval" is too long */
+ return APR_EINVAL;
+ }
+ }
+ if (!existing) *pwas_added = 1;
+ hvalue = apr_pstrndup(pool, value, vlen);
apr_table_mergen(headers, hname, hvalue);
return APR_SUCCESS;