[core] extend (data_string *) to store header id

(optional addition to (data_string *), used by http_header.[ch])

extend (data_string *) instead of creating another data_* TYPE_*
  (new data type would probably have (data_string *) as base class)
  (might revisit choice in the future)

HTTP_HEADER_UNSPECIFIED has been removed.  It was used in select
locations as an optimization to avoid looking up enum header_header_e
before checking the array, but the ordering in the array now relies
on having the id.  Having the id allows for a quick check if a common
header is present or not in the htags bitmask, before checking the
array, and allows for integer comparison in the log(n) search of the
array, instead of strncasecmp().

With HTTP_HEADER_UNSPECIFIED removed, add optimization to set bit
in htags for HTTP_HEADER_OTHER when an "other" header is added,
but do not clear the bit, as there might be addtl "other" headers
master
Glenn Strauss 3 years ago
parent 68ec5ad642
commit 2e0676fd6d

@ -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;

@ -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);

@ -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:

@ -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;

@ -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 <ctype.h>
@ -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) {

@ -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) {

@ -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

@ -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("-"));

@ -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;

@ -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;
}

@ -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));
}
}

Loading…
Cancel
Save