diff --git a/src/array.c b/src/array.c index 38e110d4..f45fb013 100644 --- a/src/array.c +++ b/src/array.c @@ -115,6 +115,50 @@ static int array_keycmp(const char * const a, const uint32_t alen, const char * return alen < blen ? -1 : alen > blen ? 1 : array_caseless_compare(a, b, blen); } +__attribute_cold__ +__attribute_pure__ +static int array_keycmpb(const char * const k, const uint32_t klen, const buffer * const b) { + /* key is non-empty (0==b->used), though possibly blank (1==b->used) + * if inserted into key-value array */ + /*force_assert(b && b->used);*/ + return array_keycmp(k, klen, b->ptr, b->used-1); + /*return array_keycmp(k, klen, CONST_BUF_LEN(b));*/ +} + +/* returns pos into a->sorted[] which contains copy of data (ptr) in a->data[] + * if pos >= 0, or returns -pos-1 if that is the position-1 in a->sorted[] + * where the key needs to be inserted (-1 to avoid -0) + */ +__attribute_hot__ +__attribute_pure__ +static int32_t array_get_index_ext(const array * const a, const int ext, const char * const k, const uint32_t klen) { + /* invariant: [lower-1] < probe < [upper] + * invariant: 0 <= lower <= upper <= a->used + */ + uint32_t lower = 0, upper = a->used; + while (lower != upper) { + const uint32_t probe = (lower + upper) / 2; + const int x = ((data_string *)a->sorted[probe])->ext; + /* (compare strings only if ext is 0 for both)*/ + const int e = (ext|x) + ? ext + : array_keycmpb(k, klen, &a->sorted[probe]->key); + if (e < x) /* e < [probe] */ + upper = probe; /* still: lower <= upper */ + else if (e > x) /* e > [probe] */ + lower = probe + 1; /* still: lower <= upper */ + else /*(e == x)*/ /* found */ + return (int32_t)probe; + } + /* not found: [lower-1] < key < [upper] = [lower] ==> insert at [lower] */ + return -(int)lower - 1; +} + +data_unset *array_get_element_klen_ext(const array * const a, const int ext, const char *key, const uint32_t klen) { + const int32_t ipos = array_get_index_ext(a, ext, key, klen); + return ipos >= 0 ? a->sorted[ipos] : NULL; +} + /* returns pos into a->sorted[] which contains copy of data (ptr) in a->data[] * if pos >= 0, or returns -pos-1 if that is the position-1 in a->sorted[] * where the key needs to be inserted (-1 to avoid -0) @@ -209,6 +253,7 @@ static data_unset *array_get_unused_element(array * const a, const data_type_t t #endif } +__attribute_hot__ static void array_insert_data_at_pos(array * const a, data_unset * const entry, const uint32_t pos) { /* This data structure should not be used for nearly so many entries */ force_assert(a->used + 1 <= INT32_MAX); @@ -242,6 +287,7 @@ static data_integer * array_insert_integer_at_pos(array * const a, const uint32_ return di; } +__attribute_hot__ static data_string * array_insert_string_at_pos(array * const a, const uint32_t pos) { data_string *ds = (data_string *)array_get_unused_element(a, TYPE_STRING); if (NULL == ds) ds = data_string_init(); @@ -249,6 +295,18 @@ static data_string * array_insert_string_at_pos(array * const a, const uint32_t return ds; } +__attribute_hot__ +buffer * array_get_buf_ptr_ext(array * const a, const int ext, const char * const k, const uint32_t klen) { + int32_t ipos = array_get_index_ext(a, ext, k, klen); + if (ipos >= 0) return &((data_string *)a->sorted[ipos])->value; + + data_string * const ds = array_insert_string_at_pos(a, (uint32_t)(-ipos-1)); + ds->ext = ext; + buffer_copy_string_len(&ds->key, k, klen); + buffer_clear(&ds->value); + return &ds->value; +} + int * array_get_int_ptr(array * const a, const char * const k, const uint32_t klen) { int32_t ipos = array_get_index(a, k, klen); if (ipos >= 0) return &((data_integer *)a->sorted[ipos])->value; diff --git a/src/array.h b/src/array.h index 1a06b613..706ee4df 100644 --- a/src/array.h +++ b/src/array.h @@ -33,7 +33,7 @@ typedef struct { typedef struct { DATA_UNSET; - + int ext; /*(fits in space due to alignment in 64-bit; extends 32-bit)*/ buffer value; } data_string; @@ -91,6 +91,9 @@ __attribute_cold__ __attribute_pure__ int array_is_kvstring(const array *a); +__attribute_pure__ +data_unset *array_get_element_klen_ext(const array *a, int ext, const char *key, uint32_t klen); + __attribute_pure__ data_unset *array_get_element_klen(const array *a, const char *key, uint32_t klen); @@ -104,6 +107,9 @@ data_unset *array_extract_element_klen(array *a, const char *key, uint32_t klen) __attribute_returns_nonnull__ int * array_get_int_ptr(array *a, const char *k, uint32_t klen); +__attribute_returns_nonnull__ +buffer * array_get_buf_ptr_ext(array *a, int ext, const char *k, uint32_t klen); + __attribute_returns_nonnull__ buffer * array_get_buf_ptr(array *a, const char *k, uint32_t klen); diff --git a/src/configfile-glue.c b/src/configfile-glue.c index b53e8995..c8aca54b 100644 --- a/src/configfile-glue.c +++ b/src/configfile-glue.c @@ -572,7 +572,7 @@ static cond_result_t config_check_cond_nocache(request_st * const r, const data_ break; case COMP_HTTP_REQUEST_HEADER: - *((const buffer **)&l) = http_header_request_get(r, HTTP_HEADER_UNSPECIFIED, CONST_BUF_LEN(dc->comp_tag)); + *((const buffer **)&l) = http_header_request_get(r, dc->ext, CONST_BUF_LEN(dc->comp_tag)); if (NULL == l) l = (buffer *)&empty_string; break; case COMP_HTTP_REQUEST_METHOD: diff --git a/src/configfile.h b/src/configfile.h index 45fe3c7b..911ce952 100644 --- a/src/configfile.h +++ b/src/configfile.h @@ -37,6 +37,7 @@ struct data_config { void *regex; struct pcre_extra *regex_study; #endif + int ext; buffer *comp_tag; buffer *comp_key; const char *op; diff --git a/src/configparser.y b/src/configparser.y index eae39f88..a00e5554 100644 --- a/src/configparser.y +++ b/src/configparser.y @@ -8,6 +8,7 @@ #include "configfile.h" #include "buffer.h" #include "array.h" +#include "http_header.h" /* http_header_hkey_get() */ #include "request.h" /* http_request_host_normalize() */ #include @@ -693,6 +694,10 @@ context ::= DOLLAR SRVVARNAME(B) LBRACKET stringop(C) RBRACKET cond(E) expressio } } + if (COMP_HTTP_REQUEST_HEADER == dc->comp) { + dc->ext = http_header_hkey_get(CONST_BUF_LEN(dc->comp_tag)); + } + buffer_copy_buffer(&dc->string, rvalue); if (ctx->ok) switch(E) { diff --git a/src/http_header.c b/src/http_header.c index 5880eb09..8c321efe 100644 --- a/src/http_header.c +++ b/src/http_header.c @@ -159,23 +159,29 @@ static inline void http_header_token_append_cookie(buffer * const vb, const char } __attribute_pure__ -static inline buffer * http_header_generic_get_ifnotempty(const array * const a, const char * const k, const uint32_t klen) { +static inline buffer * http_header_generic_get_ifnotempty(const array * const a, const enum http_header_e id, const char * const k, const uint32_t klen) { data_string * const ds = - (data_string *)array_get_element_klen(a, k, klen); + (data_string *)array_get_element_klen_ext(a, id, k, klen); return ds && !buffer_string_is_empty(&ds->value) ? &ds->value : NULL; } +static inline void http_header_set_key_value(array * const a, enum http_header_e id, const char * const k, const size_t klen, const char * const v, const size_t vlen) { + buffer_copy_string_len(array_get_buf_ptr_ext(a, id, k, klen), v, vlen); +} + buffer * http_header_response_get(const request_st * const r, enum http_header_e id, const char *k, uint32_t klen) { - return (id <= HTTP_HEADER_OTHER || light_btst(r->resp_htags, id)) - ? http_header_generic_get_ifnotempty(&r->resp_headers, k, klen) + return light_btst(r->resp_htags, id) + ? http_header_generic_get_ifnotempty(&r->resp_headers, id, k, klen) : NULL; } void http_header_response_unset(request_st * const r, enum http_header_e id, const char *k, uint32_t klen) { - if (id <= HTTP_HEADER_OTHER || light_btst(r->resp_htags, id)) { + if (light_btst(r->resp_htags, id)) { + /* (do not clear bit for HTTP_HEADER_OTHER, + * as there might be addtl "other" headers) */ if (id > HTTP_HEADER_OTHER) light_bclr(r->resp_htags, id); - array_set_key_value(&r->resp_headers, k, klen, CONST_STR_LEN("")); + http_header_set_key_value(&r->resp_headers,id,k,klen,CONST_STR_LEN("")); } } @@ -183,50 +189,59 @@ void http_header_response_set(request_st * const r, enum http_header_e id, const /* set value, including setting blank value if 0 == vlen * (note: if 0 == vlen, header is still inserted with blank value, * which is used to indicate a "removed" header) - */ - if (id > HTTP_HEADER_OTHER) - (vlen) ? light_bset(r->resp_htags, id) : light_bclr(r->resp_htags, id); - array_set_key_value(&r->resp_headers, k, klen, v, vlen); + * (do not clear bit for HTTP_HEADER_OTHER if 0 == vlen, + * as there might be addtl "other" headers) */ + (vlen) + ? light_bset(r->resp_htags, id) + : (id > HTTP_HEADER_OTHER ? light_bclr(r->resp_htags, id) : 0); + http_header_set_key_value(&r->resp_headers, id, k, klen, v, vlen); } void http_header_response_append(request_st * const r, enum http_header_e id, const char *k, uint32_t klen, const char *v, uint32_t vlen) { if (0 == vlen) return; - if (id > HTTP_HEADER_OTHER) light_bset(r->resp_htags, id); - buffer * const vb = array_get_buf_ptr(&r->resp_headers, k, klen); + light_bset(r->resp_htags, id); + buffer * const vb = array_get_buf_ptr_ext(&r->resp_headers, id, k, klen); http_header_token_append(vb, v, vlen); } +__attribute_cold__ +static void http_header_response_insert_addtl(request_st * const r, enum http_header_e id, const char *k, uint32_t klen, buffer * const vb, uint32_t vlen) { + UNUSED(id); + buffer_append_string_len(vb, CONST_STR_LEN("\r\n")); + if (r->http_version >= HTTP_VERSION_2) { + r->resp_header_repeated = 1; + char * const h = buffer_string_prepare_append(vb, klen + vlen + 2); + for (uint32_t i = 0; i < klen; ++i) + h[i] = !light_isupper(k[i]) ? k[i] : (k[i] | 0x20); + buffer_commit(vb, klen); + } + else + buffer_append_string_len(vb, k, klen); + buffer_append_string_len(vb, CONST_STR_LEN(": ")); +} + void http_header_response_insert(request_st * const r, enum http_header_e id, const char *k, uint32_t klen, const char *v, uint32_t vlen) { if (0 == vlen) return; - if (id > HTTP_HEADER_OTHER) light_bset(r->resp_htags, id); - buffer * const vb = array_get_buf_ptr(&r->resp_headers, k, klen); - if (!buffer_string_is_empty(vb)) { /* append value */ - buffer_append_string_len(vb, CONST_STR_LEN("\r\n")); - if (r->http_version >= HTTP_VERSION_2) { - r->resp_header_repeated = 1; - char * const h = buffer_string_prepare_append(vb, klen + vlen + 2); - for (uint32_t i = 0; i < klen; ++i) - h[i] = !light_isupper(k[i]) ? k[i] : (k[i] | 0x20); - buffer_commit(vb, klen); - } - else - buffer_append_string_len(vb, k, klen); - buffer_append_string_len(vb, CONST_STR_LEN(": ")); - } + light_bset(r->resp_htags, id); + buffer * const vb = array_get_buf_ptr_ext(&r->resp_headers, id, k, klen); + if (!buffer_string_is_empty(vb)) /*append repeated field-name on new line*/ + http_header_response_insert_addtl(r, id, k, klen, vb, vlen); buffer_append_string_len(vb, v, vlen); } buffer * http_header_request_get(const request_st * const r, enum http_header_e id, const char *k, uint32_t klen) { - return (id <= HTTP_HEADER_OTHER || light_btst(r->rqst_htags, id)) - ? http_header_generic_get_ifnotempty(&r->rqst_headers, k, klen) + return light_btst(r->rqst_htags, id) + ? http_header_generic_get_ifnotempty(&r->rqst_headers, id, k, klen) : NULL; } void http_header_request_unset(request_st * const r, enum http_header_e id, const char *k, uint32_t klen) { - if (id <= HTTP_HEADER_OTHER || light_btst(r->rqst_htags, id)) { + if (light_btst(r->rqst_htags, id)) { + /* (do not clear bit for HTTP_HEADER_OTHER, + * as there might be addtl "other" headers) */ if (id > HTTP_HEADER_OTHER) light_bclr(r->rqst_htags, id); - array_set_key_value(&r->rqst_headers, k, klen, CONST_STR_LEN("")); + http_header_set_key_value(&r->rqst_headers,id,k,klen,CONST_STR_LEN("")); } } @@ -234,16 +249,18 @@ void http_header_request_set(request_st * const r, enum http_header_e id, const /* set value, including setting blank value if 0 == vlen * (note: if 0 == vlen, header is still inserted with blank value, * which is used to indicate a "removed" header) - */ - if (id > HTTP_HEADER_OTHER) - (vlen) ? light_bset(r->rqst_htags, id) : light_bclr(r->rqst_htags, id); - array_set_key_value(&r->rqst_headers, k, klen, v, vlen); + * (do not clear bit for HTTP_HEADER_OTHER if 0 == vlen, + * as there might be addtl "other" headers) */ + (vlen) + ? light_bset(r->rqst_htags, id) + : (id > HTTP_HEADER_OTHER ? light_bclr(r->rqst_htags, id) : 0); + http_header_set_key_value(&r->rqst_headers, id, k, klen, v, vlen); } void http_header_request_append(request_st * const r, enum http_header_e id, const char *k, uint32_t klen, const char *v, uint32_t vlen) { if (0 == vlen) return; - if (id > HTTP_HEADER_OTHER) light_bset(r->rqst_htags, id); - buffer * const vb = array_get_buf_ptr(&r->rqst_headers, k, klen); + light_bset(r->rqst_htags, id); + buffer * const vb = array_get_buf_ptr_ext(&r->rqst_headers, id, k, klen); if (id != HTTP_HEADER_COOKIE) http_header_token_append(vb, v, vlen); else @@ -252,7 +269,10 @@ void http_header_request_append(request_st * const r, enum http_header_e id, con buffer * http_header_env_get(const request_st * const r, const char *k, uint32_t klen) { - return http_header_generic_get_ifnotempty(&r->env, k, klen); + /* similar to http_header_generic_get_ifnotempty() but without id */ + data_string * const ds = + (data_string *)array_get_element_klen(&r->env, k, klen); + return ds && !buffer_string_is_empty(&ds->value) ? &ds->value : NULL; } void http_header_env_set(request_st * const r, const char *k, uint32_t klen, const char *v, uint32_t vlen) { diff --git a/src/http_header.h b/src/http_header.h index 17f6be89..a9bff769 100644 --- a/src/http_header.h +++ b/src/http_header.h @@ -8,8 +8,7 @@ /* Note: must be kept in sync with http_header.c http_headers[] */ /* Note: when adding new items, must replace OTHER in existing code for item */ enum http_header_e { - HTTP_HEADER_UNSPECIFIED = -1 - ,HTTP_HEADER_OTHER = 0 + HTTP_HEADER_OTHER = 0 ,HTTP_HEADER_ACCEPT_ENCODING ,HTTP_HEADER_AUTHORIZATION ,HTTP_HEADER_CACHE_CONTROL diff --git a/src/mod_accesslog.c b/src/mod_accesslog.c index b4fc21b7..6f744cdd 100644 --- a/src/mod_accesslog.c +++ b/src/mod_accesslog.c @@ -722,6 +722,9 @@ static format_fields * mod_accesslog_process_format(const char * const format, c mod_accesslog_free_format_fields(parsed_format); return NULL; } + } else if (FORMAT_HEADER == f->field + || FORMAT_RESPONSE_HEADER == f->field) { + f->opt = http_header_hkey_get(CONST_BUF_LEN(fstr)); } } @@ -994,14 +997,14 @@ static int log_access_record (const request_st * const r, buffer * const b, form break; } case FORMAT_HEADER: - if (NULL != (vb = http_header_request_get(r, HTTP_HEADER_UNSPECIFIED, CONST_BUF_LEN(&f->string)))) { + if (NULL != (vb = http_header_request_get(r, f->opt, CONST_BUF_LEN(&f->string)))) { accesslog_append_escaped(b, vb); } else { buffer_append_string_len(b, CONST_STR_LEN("-")); } break; case FORMAT_RESPONSE_HEADER: - if (NULL != (vb = http_header_response_get(r, HTTP_HEADER_UNSPECIFIED, CONST_BUF_LEN(&f->string)))) { + if (NULL != (vb = http_header_response_get(r, f->opt, CONST_BUF_LEN(&f->string)))) { accesslog_append_escaped(b, vb); } else { buffer_append_string_len(b, CONST_STR_LEN("-")); diff --git a/src/mod_extforward.c b/src/mod_extforward.c index 00d19c4d..1a6829c2 100644 --- a/src/mod_extforward.c +++ b/src/mod_extforward.c @@ -375,6 +375,15 @@ SETDEFAULTS_FUNC(mod_extforward_set_defaults) { cpv->vtype = T_CONFIG_LOCAL; break; case 1: /* extforward.headers */ + if (cpv->v.a->used) { + array *a; + *(const array **)&a = cpv->v.a; + for (uint32_t j = 0; j < a->used; ++j) { + data_string * const ds = (data_string *)a->data[j]; + ds->ext = + http_header_hkey_get(CONST_BUF_LEN(&ds->value)); + } + } break; case 2: /* extforward.params */ cpv->v.u = mod_extforward_parse_opts(srv, cpv->v.a); @@ -1105,8 +1114,9 @@ URIHANDLER_FUNC(mod_extforward_uri_handler) { return HANDLER_GO_ON; for (uint32_t k = 0; k < p->conf.headers->used; ++k) { - buffer *hdr = &((data_string *)p->conf.headers->data[k])->value; - forwarded = http_header_request_get(r, HTTP_HEADER_UNSPECIFIED, CONST_BUF_LEN(hdr)); + const data_string * const ds = (data_string *)p->conf.headers->data[k]; + const buffer * const hdr = &ds->value; + forwarded = http_header_request_get(r, ds->ext, CONST_BUF_LEN(hdr)); if (forwarded) { is_forwarded_header = buffer_is_equal_caseless_string(hdr, CONST_STR_LEN("Forwarded")); break; diff --git a/src/mod_magnet.c b/src/mod_magnet.c index 05a6a0c0..f5aa758b 100644 --- a/src/mod_magnet.c +++ b/src/mod_magnet.c @@ -368,8 +368,8 @@ static int magnet_reqhdr_get(lua_State *L) { size_t klen; const char * const k = luaL_checklstring(L, 2, &klen); request_st * const r = magnet_get_request(L); - const buffer * const vb = - http_header_request_get(r, HTTP_HEADER_UNSPECIFIED, k, klen); + const int id = http_header_hkey_get(k, (uint32_t)klen); + const buffer * const vb = http_header_request_get(r, id, k, klen); magnet_push_buffer(L, NULL != vb ? vb : NULL); return 1; } diff --git a/src/mod_setenv.c b/src/mod_setenv.c index cd47821e..bb8b2c4e 100644 --- a/src/mod_setenv.c +++ b/src/mod_setenv.c @@ -84,6 +84,15 @@ static void mod_setenv_patch_config(request_st * const r, plugin_data * const p, } } +static void mod_setenv_prep_ext (const array * const ac) { + array *a; + *(const array **)&a = ac; + for (uint32_t i = 0; i < a->used; ++i) { + data_string * const ds = (data_string *)a->data[i]; + ds->ext = http_header_hkey_get(CONST_BUF_LEN(&ds->key)); + } +} + SETDEFAULTS_FUNC(mod_setenv_set_defaults) { static const config_plugin_keys_t cpk[] = { { CONST_STR_LEN("setenv.add-request-header"), @@ -113,11 +122,6 @@ SETDEFAULTS_FUNC(mod_setenv_set_defaults) { if (!config_plugin_values_init(srv, p, cpk, "mod_setenv")) return HANDLER_ERROR; - /* future: might create custom data structures here - * then look up and store http_header_e at config time - * enum http_header_e id = http_header_hkey_get(CONST_BUF_LEN(&ds->key)); - */ - /* process and validate config directives * (init i to 0 if global context; to 1 to skip empty global context) */ for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) { @@ -126,9 +130,14 @@ SETDEFAULTS_FUNC(mod_setenv_set_defaults) { switch (cpv->k_id) { case 0: /* setenv.add-request-header */ case 1: /* setenv.add-response-header */ + mod_setenv_prep_ext(cpv->v.a); + break; case 2: /* setenv.add-environment */ + break; case 3: /* setenv.set-request-header */ case 4: /* setenv.set-response-header */ + mod_setenv_prep_ext(cpv->v.a); + break; case 5: /* setenv.set-environment */ break; default:/* should not happen */ @@ -164,22 +173,18 @@ URIHANDLER_FUNC(mod_setenv_uri_handler) { if (aa) { for (uint32_t k = 0; k < aa->used; ++k) { const data_string * const ds = (const data_string *)aa->data[k]; - const enum http_header_e id = - http_header_hkey_get(CONST_BUF_LEN(&ds->key)); - http_header_request_append(r, id, CONST_BUF_LEN(&ds->key), - CONST_BUF_LEN(&ds->value)); + http_header_request_append(r, ds->ext, CONST_BUF_LEN(&ds->key), + CONST_BUF_LEN(&ds->value)); } } if (as) { for (uint32_t k = 0; k < as->used; ++k) { const data_string * const ds = (const data_string *)as->data[k]; - const enum http_header_e id = - http_header_hkey_get(CONST_BUF_LEN(&ds->key)); !buffer_string_is_empty(&ds->value) - ? http_header_request_set(r, id, CONST_BUF_LEN(&ds->key), - CONST_BUF_LEN(&ds->value)) - : http_header_request_unset(r, id, CONST_BUF_LEN(&ds->key)); + ? http_header_request_set(r, ds->ext, CONST_BUF_LEN(&ds->key), + CONST_BUF_LEN(&ds->value)) + : http_header_request_unset(r, ds->ext, CONST_BUF_LEN(&ds->key)); } } @@ -226,22 +231,18 @@ REQUEST_FUNC(mod_setenv_handle_response_start) { if (aa) { for (uint32_t k = 0; k < aa->used; ++k) { const data_string * const ds = (const data_string *)aa->data[k]; - const enum http_header_e id = - http_header_hkey_get(CONST_BUF_LEN(&ds->key)); - http_header_response_insert(r, id, CONST_BUF_LEN(&ds->key), - CONST_BUF_LEN(&ds->value)); + http_header_response_insert(r, ds->ext, CONST_BUF_LEN(&ds->key), + CONST_BUF_LEN(&ds->value)); } } if (as) { for (uint32_t k = 0; k < as->used; ++k) { const data_string * const ds = (const data_string *)as->data[k]; - const enum http_header_e id = - http_header_hkey_get(CONST_BUF_LEN(&ds->key)); !buffer_string_is_empty(&ds->value) - ? http_header_response_set(r, id, CONST_BUF_LEN(&ds->key), - CONST_BUF_LEN(&ds->value)) - : http_header_response_unset(r, id, CONST_BUF_LEN(&ds->key)); + ? http_header_response_set(r, ds->ext, CONST_BUF_LEN(&ds->key), + CONST_BUF_LEN(&ds->value)) + : http_header_response_unset(r, ds->ext, CONST_BUF_LEN(&ds->key)); } }