Modified: serf/trunk/buckets/hpack_buckets.c URL: http://svn.apache.org/viewvc/serf/trunk/buckets/hpack_buckets.c?rev=1714622&r1=1714621&r2=1714622&view=diff ============================================================================== --- serf/trunk/buckets/hpack_buckets.c (original) +++ serf/trunk/buckets/hpack_buckets.c Mon Nov 16 16:27:04 2015 @@ -35,18 +35,18 @@ static int hpack_hm_compare(const void *k, const void *i) { - const apr_uint32_t *key = k; - const struct serf_hpack_huffman_item_t *it = i; + const apr_uint32_t *key = k; + const struct serf_hpack_huffman_item_t *it = i; - apr_uint32_t k1 = (*key & it->hmask); - apr_uint32_t k2 = it->hval; + apr_uint32_t k1 = (*key & it->hmask); + apr_uint32_t k2 = it->hval; - if (k1 < k2) - return -1; - else if (k1 > k2) - return 1; - else - return 0; + if (k1 < k2) + return -1; + else if (k1 > k2) + return 1; + else + return 0; } /* Convert encoded data in ENCODED of size ENCODED_LEN. If TEXT is not @@ -65,52 +65,52 @@ serf__hpack_huffman_decode(const unsigne char *text, apr_size_t *text_len) { - apr_uint64_t stash = 0; - apr_int16_t bits_left = 0; - apr_size_t result_len = 0; + apr_uint64_t stash = 0; + apr_int16_t bits_left = 0; + apr_size_t result_len = 0; - while (encoded_len || bits_left) + while (encoded_len || bits_left) { - apr_uint32_t match; - struct serf_hpack_huffman_item_t *r; + apr_uint32_t match; + struct serf_hpack_huffman_item_t *r; - while (bits_left < 30 && encoded_len) + while (bits_left < 30 && encoded_len) { - stash |= (apr_uint64_t)*encoded << (64 - 8 - bits_left); - bits_left += 8; - encoded_len--; - encoded++; + stash |= (apr_uint64_t)*encoded << (64 - 8 - bits_left); + bits_left += 8; + encoded_len--; + encoded++; } - match = stash >> 32; - r = bsearch(&match, &serf_hpack_hm_map, - sizeof(serf_hpack_hm_map) / sizeof(serf_hpack_hm_map[0]), - sizeof(serf_hpack_hm_map[0]), hpack_hm_compare); + match = stash >> 32; + r = bsearch(&match, &serf_hpack_hm_map, + sizeof(serf_hpack_hm_map) / sizeof(serf_hpack_hm_map[0]), + sizeof(serf_hpack_hm_map[0]), hpack_hm_compare); - if (!r || (r->bits > bits_left)) + if (!r || (r->bits > bits_left)) { /* With a canonical huffman code we can only reach this state at the end of the string */ - break; + break; } - if (r->cval == EOS_CHAR) - return APR_EINVAL; + if (r->cval == EOS_CHAR) + return APR_EINVAL; - if (text) + if (text) { - if (result_len < text_avail) - text[result_len] = (char)r->cval; - else - return APR_ENOMEM; + if (result_len < text_avail) + text[result_len] = (char)r->cval; + else + return APR_ENOMEM; } - result_len++; - stash <<= r->bits; - bits_left -= r->bits; + result_len++; + stash <<= r->bits; + bits_left -= r->bits; } - if (bits_left) + if (bits_left) { /* https://tools.ietf.org/html/rfc7541#section-5.2 @@ -121,25 +121,25 @@ serf__hpack_huffman_decode(const unsigne symbol MUST be treated as a decoding error. A Huffman-encoded string literal containing the EOS symbol MUST be treated as a decoding error. */ - const struct serf_hpack_huffman_item_t *eos; - apr_uint64_t exp_stash; + const struct serf_hpack_huffman_item_t *eos; + apr_uint64_t exp_stash; - eos = &serf_hpack_hm_map[serf_hpack_hm_rmap[EOS_CHAR]]; + eos = &serf_hpack_hm_map[serf_hpack_hm_rmap[EOS_CHAR]]; - /* Trim EOS value to the bits we need */ - exp_stash = ((apr_uint32_t)eos->hval >> (32 - bits_left)); - /* And move it to the right position */ - exp_stash <<= 64 - bits_left; + /* Trim EOS value to the bits we need */ + exp_stash = ((apr_uint32_t)eos->hval >> (32 - bits_left)); + /* And move it to the right position */ + exp_stash <<= 64 - bits_left; - if (exp_stash != stash) - return APR_EINVAL; + if (exp_stash != stash) + return APR_EINVAL; } - *text_len = result_len; - if (text && result_len < text_avail) - text[result_len] = 0; + *text_len = result_len; + if (text && result_len < text_avail) + text[result_len] = 0; - return APR_SUCCESS; + return APR_SUCCESS; } /* Encodes data in TEXT of size TEXT_LEN. @@ -158,59 +158,59 @@ serf__hpack_huffman_encode(const char *t unsigned char *encoded, apr_size_t *encoded_len) { - apr_uint64_t stash = 0; - apr_int16_t bits_left = 0; - apr_size_t result_len = 0; + apr_uint64_t stash = 0; + apr_int16_t bits_left = 0; + apr_size_t result_len = 0; - if (! encoded) + if (!encoded) { /* Just calculating size needed. Avoid bit handling */ - apr_int64_t result_bits = 0; - while (text_len) + apr_int64_t result_bits = 0; + while (text_len) { - const struct serf_hpack_huffman_item_t *r; + const struct serf_hpack_huffman_item_t *r; - r = &serf_hpack_hm_map[serf_hpack_hm_rmap[*(unsigned char*)text]]; + r = &serf_hpack_hm_map[serf_hpack_hm_rmap[*(unsigned char*)text]]; - result_bits += r->bits; - text_len--; - text++; + result_bits += r->bits; + text_len--; + text++; } - *encoded_len = (apr_size_t)((result_bits+7) / 8); - return APR_SUCCESS; + *encoded_len = (apr_size_t)((result_bits + 7) / 8); + return APR_SUCCESS; } - while (text_len) + while (text_len) { - if (text_len) + if (text_len) { - const struct serf_hpack_huffman_item_t *r; + const struct serf_hpack_huffman_item_t *r; - r = &serf_hpack_hm_map[serf_hpack_hm_rmap[*(unsigned char*)text]]; + r = &serf_hpack_hm_map[serf_hpack_hm_rmap[*(unsigned char*)text]]; - stash |= (apr_uint64_t)r->hval << (64 - 32 - bits_left); - bits_left += r->bits; - text_len--; - text++; + stash |= (apr_uint64_t)r->hval << (64 - 32 - bits_left); + bits_left += r->bits; + text_len--; + text++; } - while (bits_left > 8) + while (bits_left > 8) { - if (! encoded_avail) - return APR_ENOMEM; + if (!encoded_avail) + return APR_ENOMEM; - *encoded = (unsigned char)(stash >> (64 - 8)); - encoded++; - stash <<= 8; - bits_left -= 8; + *encoded = (unsigned char)(stash >> (64 - 8)); + encoded++; + stash <<= 8; + bits_left -= 8; - encoded_avail--; - result_len++; + encoded_avail--; + result_len++; } } - if (bits_left) + if (bits_left) { /* https://tools.ietf.org/html/rfc7541#section-5.2 @@ -219,53 +219,53 @@ serf__hpack_huffman_encode(const char *t prevent this padding from being misinterpreted as part of the string literal, the most significant bits of the code corresponding to the EOS (end-of-string) symbol are used. */ - const struct serf_hpack_huffman_item_t *r; + const struct serf_hpack_huffman_item_t *r; - if (! encoded_avail) - return APR_ENOMEM; + if (!encoded_avail) + return APR_ENOMEM; - r = &serf_hpack_hm_map[serf_hpack_hm_rmap[EOS_CHAR]]; - stash |= (apr_uint64_t)r->hval << (64 - 32 - bits_left); - /* bits_left += r->bits; */ + r = &serf_hpack_hm_map[serf_hpack_hm_rmap[EOS_CHAR]]; + stash |= (apr_uint64_t)r->hval << (64 - 32 - bits_left); + /* bits_left += r->bits; */ - *encoded = (unsigned char)(stash >> (64 - 8)); - /* encoded++ */ - /* stash <<= 8; */ - /* bits_left -= 8; */ - /* encoded_avail--; */ - result_len++; + *encoded = (unsigned char)(stash >> (64 - 8)); + /* encoded++ */ + /* stash <<= 8; */ + /* bits_left -= 8; */ + /* encoded_avail--; */ + result_len++; } - *encoded_len = result_len; + *encoded_len = result_len; - return APR_SUCCESS; + return APR_SUCCESS; } /* ==================================================================== */ typedef struct serf_hpack_entry_t { - const char *key; - apr_size_t key_len; - const char *value; - apr_size_t value_len; - - struct serf_hpack_entry_t *next; - struct serf_hpack_entry_t *prev; - - bool free_key; /* Key must be freed */ - bool free_val; /* Value must be freed */ - char dont_index; /* 0=index, 1=no-index, 2=never-index */ + const char *key; + apr_size_t key_len; + const char *value; + apr_size_t value_len; + + struct serf_hpack_entry_t *next; + struct serf_hpack_entry_t *prev; + + bool free_key; /* Key must be freed */ + bool free_val; /* Value must be freed */ + char dont_index; /* 0=index, 1=no-index, 2=never-index */ } serf_hpack_entry_t; static void hpack_free_entry(serf_hpack_entry_t *entry, serf_bucket_alloc_t *alloc) { - if (entry->free_key) - serf_bucket_mem_free(alloc, (char*)entry->key); - if (entry->free_val) - serf_bucket_mem_free(alloc, (char*)entry->value); - serf_bucket_mem_free(alloc, entry); + if (entry->free_key) + serf_bucket_mem_free(alloc, (char*)entry->key); + if (entry->free_val) + serf_bucket_mem_free(alloc, (char*)entry->value); + serf_bucket_mem_free(alloc, entry); } /* https://tools.ietf.org/html/rfc7541#section-4.1 @@ -282,26 +282,26 @@ static void hpack_free_entry(serf_hpack_ struct serf_hpack_table_t { - apr_pool_t *pool; - serf_bucket_alloc_t *alloc; + apr_pool_t *pool; + serf_bucket_alloc_t *alloc; - char lowercase_keys; - char send_tablesize_update; + char lowercase_keys; + char send_tablesize_update; - /* The local -> remote 'encoder' list */ - serf_hpack_entry_t *lr_first, *lr_last, *lr_start; - unsigned int lr_count; /* Number of items (first..last) */ - unsigned int lr_indexable; /* Number of items (start..last) */ - apr_size_t lr_size; /* 'Bytes' in list, calculated by HPACK_ENTRY_SIZE() */ - apr_size_t lr_max_table_size; - apr_size_t lr_sys_table_size; - - serf_hpack_entry_t *rl_first, *rl_last, *rl_start; - unsigned int rl_count; /* Number of items (first..last) */ - unsigned int rl_indexable; /* Number of items (start..last) */ - apr_size_t rl_size; /* 'Bytes' in list, calculated by HPACK_ENTRY_SIZE() */ - apr_size_t rl_max_table_size; - apr_size_t rl_sys_table_size; + /* The local -> remote 'encoder' list */ + serf_hpack_entry_t *lr_first, *lr_last, *lr_start; + unsigned int lr_count; /* Number of items (first..last) */ + unsigned int lr_indexable; /* Number of items (start..last) */ + apr_size_t lr_size; /* 'Bytes' in list, calculated by HPACK_ENTRY_SIZE() */ + apr_size_t lr_max_table_size; + apr_size_t lr_sys_table_size; + + serf_hpack_entry_t *rl_first, *rl_last, *rl_start; + unsigned int rl_count; /* Number of items (first..last) */ + unsigned int rl_indexable; /* Number of items (start..last) */ + apr_size_t rl_size; /* 'Bytes' in list, calculated by HPACK_ENTRY_SIZE() */ + apr_size_t rl_max_table_size; + apr_size_t rl_sys_table_size; }; /* The staticly defined list of pre-encoded entries. All numbers above @@ -374,37 +374,37 @@ static const serf_hpack_entry_t hpack_st #undef HPACK_STR }; static const apr_uint64_t hpack_static_table_count = - (sizeof(hpack_static_table) / sizeof(hpack_static_table[0])); +(sizeof(hpack_static_table) / sizeof(hpack_static_table[0])); static apr_status_t cleanup_hpack_table(void *data) { #ifdef _DEBUG - serf_hpack_table_t *tbl = data; - serf_hpack_entry_t *hi, *next; + serf_hpack_table_t *tbl = data; + serf_hpack_entry_t *hi, *next; - /* This is not really necessary, as we create our own allocator, - which lives in the same pool. But it helps tracking down - memory leaks in different locations */ - for (hi = tbl->lr_first; hi; hi = next) + /* This is not really necessary, as we create our own allocator, + which lives in the same pool. But it helps tracking down + memory leaks in different locations */ + for (hi = tbl->lr_first; hi; hi = next) { - next = hi->next; + next = hi->next; - hpack_free_entry(hi, tbl->alloc); + hpack_free_entry(hi, tbl->alloc); } - tbl->lr_first = tbl->lr_start = tbl->lr_last = NULL; - tbl->lr_size = 0; + tbl->lr_first = tbl->lr_start = tbl->lr_last = NULL; + tbl->lr_size = 0; - for (hi = tbl->rl_first; hi; hi = next) + for (hi = tbl->rl_first; hi; hi = next) { - next = hi->next; + next = hi->next; - hpack_free_entry(hi, tbl->alloc); + hpack_free_entry(hi, tbl->alloc); } - tbl->rl_first = tbl->rl_start = tbl->rl_last = NULL; - tbl->rl_size = 0; + tbl->rl_first = tbl->rl_start = tbl->rl_last = NULL; + tbl->rl_size = 0; #endif - return APR_SUCCESS; + return APR_SUCCESS; } @@ -413,33 +413,33 @@ serf__hpack_table_create(int for_http2, apr_size_t default_max_table_size, apr_pool_t *result_pool) { - serf_hpack_table_t *tbl = apr_pcalloc(result_pool, sizeof(*tbl)); + serf_hpack_table_t *tbl = apr_pcalloc(result_pool, sizeof(*tbl)); - tbl->pool = result_pool; - tbl->alloc = serf_bucket_allocator_create(result_pool, NULL, NULL); + tbl->pool = result_pool; + tbl->alloc = serf_bucket_allocator_create(result_pool, NULL, NULL); - /* We register this this after creating the allocator, or we would touch - memory that is already freed.*/ - apr_pool_cleanup_register(result_pool, tbl, cleanup_hpack_table, - apr_pool_cleanup_null); + /* We register this this after creating the allocator, or we would touch + memory that is already freed.*/ + apr_pool_cleanup_register(result_pool, tbl, cleanup_hpack_table, + apr_pool_cleanup_null); - tbl->lr_sys_table_size = tbl->lr_max_table_size = default_max_table_size; - tbl->rl_sys_table_size = tbl->rl_max_table_size = default_max_table_size; + tbl->lr_sys_table_size = tbl->lr_max_table_size = default_max_table_size; + tbl->rl_sys_table_size = tbl->rl_max_table_size = default_max_table_size; - tbl->lowercase_keys = FALSE; - tbl->send_tablesize_update = FALSE; + tbl->lowercase_keys = FALSE; + tbl->send_tablesize_update = FALSE; - if (for_http2) + if (for_http2) { /* HTTP2 (aka RFC7540) has some additional rules on how it uses HPACK (aka RFC7541), most notably that all header keys *MUST* be lowercase. Let's keep this thing generic and keep this as a configuration knob. */ - tbl->lowercase_keys = TRUE; + tbl->lowercase_keys = TRUE; } - return tbl; + return tbl; } static void @@ -450,22 +450,22 @@ hpack_shrink_table(serf_hpack_entry_t ** apr_size_t max_size, serf_bucket_alloc_t *allocator) { - while (*last && (*size > max_size)) + while (*last && (*size > max_size)) { - serf_hpack_entry_t *entry = *last; + serf_hpack_entry_t *entry = *last; - *last = entry->prev; + *last = entry->prev; - if (start && (*start == entry)) - *start = NULL; - if (first && (*first == entry)) - *first = NULL; + if (start && (*start == entry)) + *start = NULL; + if (first && (*first == entry)) + *first = NULL; - if (entry->prev) - entry->prev->next = NULL; + if (entry->prev) + entry->prev->next = NULL; - *size -= HPACK_ENTRY_SIZE(entry); - hpack_free_entry(entry, allocator); + *size -= HPACK_ENTRY_SIZE(entry); + hpack_free_entry(entry, allocator); } } @@ -474,24 +474,24 @@ serf__hpack_table_set_max_table_size(ser apr_size_t max_decoder_size, apr_size_t max_encoder_size) { - if (max_decoder_size != hpack_tbl->rl_sys_table_size) + if (max_decoder_size != hpack_tbl->rl_sys_table_size) { - hpack_tbl->rl_sys_table_size = max_decoder_size; + hpack_tbl->rl_sys_table_size = max_decoder_size; } - if (max_encoder_size != hpack_tbl->lr_max_table_size) + if (max_encoder_size != hpack_tbl->lr_max_table_size) { - hpack_tbl->lr_sys_table_size = max_encoder_size; + hpack_tbl->lr_sys_table_size = max_encoder_size; - if (max_encoder_size > (128 * 1024)) - max_encoder_size = (128 * 1024); + if (max_encoder_size > (128 * 1024)) + max_encoder_size = (128 * 1024); - if (max_encoder_size < hpack_tbl->lr_max_table_size) - hpack_tbl->send_tablesize_update = TRUE; + if (max_encoder_size < hpack_tbl->lr_max_table_size) + hpack_tbl->send_tablesize_update = TRUE; - hpack_shrink_table(&hpack_tbl->lr_first, &hpack_tbl->lr_start, - &hpack_tbl->lr_last, &hpack_tbl->lr_size, - hpack_tbl->lr_max_table_size, hpack_tbl->alloc); + hpack_shrink_table(&hpack_tbl->lr_first, &hpack_tbl->lr_start, + &hpack_tbl->lr_last, &hpack_tbl->lr_size, + hpack_tbl->lr_max_table_size, hpack_tbl->alloc); } } @@ -499,18 +499,18 @@ static apr_status_t hpack_table_size_update(serf_hpack_table_t *hpack_tbl, apr_size_t size) { - if (size <= hpack_tbl->rl_sys_table_size) + if (size <= hpack_tbl->rl_sys_table_size) { - hpack_tbl->rl_max_table_size = size; + hpack_tbl->rl_max_table_size = size; - hpack_shrink_table(&hpack_tbl->rl_first, &hpack_tbl->rl_start, - &hpack_tbl->rl_last, &hpack_tbl->rl_size, - hpack_tbl->rl_max_table_size, hpack_tbl->alloc); + hpack_shrink_table(&hpack_tbl->rl_first, &hpack_tbl->rl_start, + &hpack_tbl->rl_last, &hpack_tbl->rl_size, + hpack_tbl->rl_max_table_size, hpack_tbl->alloc); } - else - return SERF_ERROR_HTTP2_COMPRESSION_ERROR; + else + return SERF_ERROR_HTTP2_COMPRESSION_ERROR; - return APR_SUCCESS; + return APR_SUCCESS; } static apr_status_t @@ -521,50 +521,50 @@ hpack_table_get(apr_uint32_t v, const char **value, apr_size_t *value_size) { - const serf_hpack_entry_t *entry = NULL; - if (v == 0) - return SERF_ERROR_HTTP2_COMPRESSION_ERROR; + const serf_hpack_entry_t *entry = NULL; + if (v == 0) + return SERF_ERROR_HTTP2_COMPRESSION_ERROR; - v--; - if (v < hpack_static_table_count) - entry = &hpack_static_table[v]; - else + v--; + if (v < hpack_static_table_count) + entry = &hpack_static_table[v]; + else { - serf_hpack_entry_t *i; - v -= sizeof(hpack_static_table) / sizeof(hpack_static_table[0]); + serf_hpack_entry_t *i; + v -= sizeof(hpack_static_table) / sizeof(hpack_static_table[0]); - for (i = tbl->rl_start; i; i = i->next) + for (i = tbl->rl_start; i; i = i->next) { - if (!v) + if (!v) { - entry = i; - break; + entry = i; + break; } - v--; + v--; } } - if (!entry) - return SERF_ERROR_HTTP2_COMPRESSION_ERROR; + if (!entry) + return SERF_ERROR_HTTP2_COMPRESSION_ERROR; - if (key) - *key = entry->key; - if (key_size) - *key_size = entry->key_len; - if (value) - *value = entry->value; - if (value_size) - *value_size = entry->value_len; + if (key) + *key = entry->key; + if (key_size) + *key_size = entry->key_len; + if (value) + *value = entry->value; + if (value_size) + *value_size = entry->value_len; - return APR_SUCCESS; + return APR_SUCCESS; } typedef struct serf_hpack_context_t { - serf_hpack_table_t *tbl; + serf_hpack_table_t *tbl; - serf_hpack_entry_t *first; - serf_hpack_entry_t *last; + serf_hpack_entry_t *first; + serf_hpack_entry_t *last; } serf_hpack_context_t; static apr_status_t @@ -572,24 +572,24 @@ hpack_copy_from_headers(void *baton, const char *key, const char *value) { - serf_bucket_t *hpack = baton; - apr_size_t key_sz = strlen(key); + serf_bucket_t *hpack = baton; + apr_size_t key_sz = strlen(key); - /* TODO: Others? */ - if ((key_sz == 4 && !strcasecmp(key, "Host")) - || (key_sz == 7 && !strcasecmp(key, "Upgrade")) - || (key_sz == 10 && !strcasecmp(key, "Keep-Alive")) - || (key_sz == 10 && !strcasecmp(key, "Connection")) - || (key_sz > 11 && !strncasecmp(key, "Connection-", 11)) - || (key_sz == 16 && !strcasecmp(key, "Proxy-Connection")) - || (key_sz == 17 && !strcasecmp(key, "Transfer-Encoding"))) + /* TODO: Others? */ + if ((key_sz == 4 && !strcasecmp(key, "Host")) + || (key_sz == 7 && !strcasecmp(key, "Upgrade")) + || (key_sz == 10 && !strcasecmp(key, "Keep-Alive")) + || (key_sz == 10 && !strcasecmp(key, "Connection")) + || (key_sz > 11 && !strncasecmp(key, "Connection-", 11)) + || (key_sz == 16 && !strcasecmp(key, "Proxy-Connection")) + || (key_sz == 17 && !strcasecmp(key, "Transfer-Encoding"))) { - return APR_SUCCESS; + return APR_SUCCESS; } - serf__bucket_hpack_setc(hpack, key, value); + serf__bucket_hpack_setc(hpack, key, value); - return APR_SUCCESS; + return APR_SUCCESS; } @@ -600,26 +600,26 @@ serf__bucket_hpack_create_from_request(s const char *scheme, serf_bucket_alloc_t *allocator) { - const char *uri, *method, *host; + const char *uri, *method, *host; - serf_bucket_t *hpack = serf__bucket_hpack_create(hpack_table, allocator); + serf_bucket_t *hpack = serf__bucket_hpack_create(hpack_table, allocator); - serf_bucket_t *headers = serf_bucket_request_get_headers(request); + serf_bucket_t *headers = serf_bucket_request_get_headers(request); - host = serf_bucket_headers_get(headers, "Host"); + host = serf_bucket_headers_get(headers, "Host"); - serf__bucket_request_read(request, NULL, &uri, &method); + serf__bucket_request_read(request, NULL, &uri, &method); - serf__bucket_hpack_setc(hpack, ":method", method); - serf__bucket_hpack_setc(hpack, ":scheme", scheme); - serf__bucket_hpack_setc(hpack, ":authority", host); - serf__bucket_hpack_setc(hpack, ":path", uri); + serf__bucket_hpack_setc(hpack, ":method", method); + serf__bucket_hpack_setc(hpack, ":scheme", scheme); + serf__bucket_hpack_setc(hpack, ":authority", host); + serf__bucket_hpack_setc(hpack, ":path", uri); - serf_bucket_headers_do(headers, hpack_copy_from_headers, hpack); + serf_bucket_headers_do(headers, hpack_copy_from_headers, hpack); - *new_hpack_bucket = hpack; + *new_hpack_bucket = hpack; - return APR_SUCCESS; + return APR_SUCCESS; } @@ -627,12 +627,12 @@ serf_bucket_t * serf__bucket_hpack_create(serf_hpack_table_t *hpack_table, serf_bucket_alloc_t *allocator) { - serf_hpack_context_t *ctx = serf_bucket_mem_alloc(allocator, sizeof(*ctx)); + serf_hpack_context_t *ctx = serf_bucket_mem_alloc(allocator, sizeof(*ctx)); - ctx->tbl = hpack_table; - ctx->first = ctx->last = NULL; + ctx->tbl = hpack_table; + ctx->first = ctx->last = NULL; - return serf_bucket_create(&serf_bucket_type__hpack, allocator, ctx); + return serf_bucket_create(&serf_bucket_type__hpack, allocator, ctx); } void @@ -640,10 +640,10 @@ serf__bucket_hpack_setc(serf_bucket_t *h const char *key, const char *value) { - serf__bucket_hpack_setx(hpack_bucket, - key, strlen(key), TRUE, - value, strlen(value), TRUE, - FALSE, FALSE); + serf__bucket_hpack_setx(hpack_bucket, + key, strlen(key), TRUE, + value, strlen(value), TRUE, + FALSE, FALSE); } void @@ -657,41 +657,41 @@ serf__bucket_hpack_setx(serf_bucket_t *b int dont_index, int never_index) { - serf_hpack_context_t *ctx = bucket->data; - serf_hpack_entry_t *entry; - apr_size_t i; + serf_hpack_context_t *ctx = bucket->data; + serf_hpack_entry_t *entry; + apr_size_t i; - for (entry = ctx->first; entry; entry = entry->next) + for (entry = ctx->first; entry; entry = entry->next) { - if (key_size == entry->key_len - && !strncasecmp(key, entry->key, key_size)) + if (key_size == entry->key_len + && !strncasecmp(key, entry->key, key_size)) { - break; + break; } } /* TODO: Handle *_copy by keeping some flags */ - if (entry && value[0] == ':') + if (entry && value[0] == ':') { - if (entry->free_val) - serf_bucket_mem_free(bucket->allocator, (void*)entry->value); + if (entry->free_val) + serf_bucket_mem_free(bucket->allocator, (void*)entry->value); - entry->value = serf_bstrmemdup(bucket->allocator, value, value_size); - entry->value_len = value_size; - entry->free_val = true; - entry->dont_index = never_index ? 2 : (dont_index ? 1 : 0); + entry->value = serf_bstrmemdup(bucket->allocator, value, value_size); + entry->value_len = value_size; + entry->free_val = true; + entry->dont_index = never_index ? 2 : (dont_index ? 1 : 0); - return; + return; } - else if (entry) + else if (entry) { /* We probably want to allow duplicate *and* join behavior? */ } - entry = serf_bucket_mem_calloc(bucket->allocator, sizeof(*entry)); + entry = serf_bucket_mem_calloc(bucket->allocator, sizeof(*entry)); - if (ctx->tbl && ctx->tbl->lowercase_keys) + if (ctx->tbl && ctx->tbl->lowercase_keys) { /* https://tools.ietf.org/html/rfc7540#section-8.1.2 Just as in HTTP/1.x, header field names are strings of ASCII @@ -700,112 +700,119 @@ serf__bucket_hpack_setx(serf_bucket_t *b encoding in HTTP/2. A request or response containing uppercase header field names MUST be treated as malformed (Section 8.1.2.6). */ - char *ckey = serf_bstrmemdup(bucket->allocator, key, key_size); - for (i = 0; i < key_size; i++) + char *ckey = serf_bstrmemdup(bucket->allocator, key, key_size); + for (i = 0; i < key_size; i++) { - if (ckey[i] >= 'A' && key[i] <= 'Z') - ckey[i] += ('a' - 'A'); + if (ckey[i] >= 'A' && key[i] <= 'Z') + ckey[i] += ('a' - 'A'); } - entry->key = ckey; - entry->free_key = true; + entry->key = ckey; + entry->free_key = true; } - else if (!key_copy) + else if (!key_copy) { - entry->key = key; - entry->free_key = false; + entry->key = key; + entry->free_key = false; } - else + else { - entry->key = serf_bstrmemdup(bucket->allocator, key, key_size); - entry->free_key = true; + entry->key = serf_bstrmemdup(bucket->allocator, key, key_size); + entry->free_key = true; } - entry->key_len = key_size; - if (value_copy) + entry->key_len = key_size; + if (value_copy) { - entry->value = serf_bstrmemdup(bucket->allocator, value, value_size); - entry->free_val = true; + entry->value = serf_bstrmemdup(bucket->allocator, value, value_size); + entry->free_val = true; } - else + else { - entry->value = value; - entry->free_val = false; + entry->value = value; + entry->free_val = false; } - entry->value_len = value_size; - entry->dont_index = never_index ? 2 : (dont_index ? 1 : 0); + entry->value_len = value_size; + entry->dont_index = never_index ? 2 : (dont_index ? 1 : 0); - entry->prev = ctx->last; - entry->next = NULL; - if (ctx->last) + entry->prev = ctx->last; + entry->next = NULL; + if (ctx->last) { - ctx->last->next = entry; - ctx->last = entry; + ctx->last->next = entry; + ctx->last = entry; } - else - ctx->first = ctx->last = entry; + else + ctx->first = ctx->last = entry; } const char *serf__bucket_hpack_getc(serf_bucket_t *hpack_bucket, const char *key) { - serf_hpack_context_t *ctx = hpack_bucket->data; - serf_hpack_entry_t *entry; - apr_size_t key_len = strlen(key); + serf_hpack_context_t *ctx = hpack_bucket->data; + serf_hpack_entry_t *entry; + apr_size_t key_len = strlen(key); - for (entry = ctx->first; entry; entry = entry->next) + for (entry = ctx->first; entry; entry = entry->next) { - if (key_len == entry->key_len - && !strncasecmp(key, entry->key, key_len)) - { - return entry->value; - } + if (key_len == entry->key_len + && !strncasecmp(key, entry->key, key_len)) + { + return entry->value; + } } - return NULL; + return NULL; } void serf__bucket_hpack_do(serf_bucket_t *hpack_bucket, serf_bucket_hpack_do_callback_fn_t func, void *baton) { - serf_hpack_context_t *ctx = hpack_bucket->data; - serf_hpack_entry_t *entry; + serf_hpack_context_t *ctx = hpack_bucket->data; + serf_hpack_entry_t *entry; - for (entry = ctx->first; entry; entry = entry->next) + for (entry = ctx->first; entry; entry = entry->next) { - if (func(baton, entry->key, entry->key_len, entry->value, entry->value_len)) - break; + if (func(baton, entry->key, entry->key_len, entry->value, + entry->value_len)) + { + break; + } } } -static void hpack_int(unsigned char flags, int bits, apr_uint32_t value, char to[6], apr_size_t *used) +static void hpack_int(unsigned char flags, + int bits, + apr_uint32_t value, + char to[6], + apr_size_t *used) { - unsigned char max_direct; - apr_size_t u; + unsigned char max_direct; + apr_size_t u; - flags = flags & ~((1 << bits) - 1); + flags = flags & ~((1 << bits) - 1); - max_direct = (unsigned char)(((apr_uint16_t)1 << bits) - 1); + max_direct = (unsigned char)(((apr_uint16_t)1 << bits) - 1); - if (value < max_direct) + if (value < max_direct) { - to[0] = flags | (unsigned char)value; - *used = 1; - return; + to[0] = flags | (unsigned char)value; + *used = 1; + return; } - to[0] = flags | max_direct; - value -= max_direct; - u = 1; + to[0] = flags | max_direct; + value -= max_direct; + u = 1; - while (value >= 0x80) + while (value >= 0x80) { - to[u++] = (value & 0x7F) | 0x80; - value >>= 7; + to[u++] = (value & 0x7F) | 0x80; + value >>= 7; } - to[u++] = (unsigned char)value; - *used = u; + to[u++] = (unsigned char)value; + *used = u; } static apr_status_t @@ -818,140 +825,144 @@ serialize(serf_bucket_t *bucket) Currently implements a complete in memory copy to the least efficient HTTP2 / HPACK header format */ - serf_hpack_context_t *ctx = bucket->data; - serf_bucket_alloc_t *alloc = bucket->allocator; - serf_hpack_table_t *tbl = ctx->tbl; + serf_hpack_context_t *ctx = bucket->data; + serf_bucket_alloc_t *alloc = bucket->allocator; + serf_hpack_table_t *tbl = ctx->tbl; - serf_hpack_entry_t *entry; - serf_hpack_entry_t *next; + serf_hpack_entry_t *entry; + serf_hpack_entry_t *next; - char *buffer = NULL; - apr_size_t offset = 0; - const apr_size_t chunksize = 2048; /* Wild guess */ + char *buffer = NULL; + apr_size_t offset = 0; + const apr_size_t chunksize = 2048; /* Wild guess */ - /* Put on our aggregate bucket cloak */ - serf_bucket_aggregate_become(bucket); + /* Put on our aggregate bucket cloak */ + serf_bucket_aggregate_become(bucket); - /* Is there a tablesize update queued? */ - if (tbl && tbl->send_tablesize_update) + /* Is there a tablesize update queued? */ + if (tbl && tbl->send_tablesize_update) { - apr_size_t len; + apr_size_t len; - buffer = serf_bucket_mem_alloc(alloc, chunksize); + buffer = serf_bucket_mem_alloc(alloc, chunksize); - hpack_int(0x20, 5, tbl->lr_max_table_size, buffer+offset, &len); - offset += len; - tbl->send_tablesize_update = FALSE; + hpack_int(0x20, 5, tbl->lr_max_table_size, buffer + offset, &len); + offset += len; + tbl->send_tablesize_update = FALSE; } - for (entry = ctx->first; entry; entry = next) + for (entry = ctx->first; entry; entry = next) { - apr_status_t status; - apr_size_t len; + apr_status_t status; + apr_size_t len; - next = entry->next; + next = entry->next; - /* Make 100% sure the next entry will fit. - ### Temporarily wastes ram - */ - if (!buffer || (HPACK_ENTRY_SIZE(entry) > chunksize - offset)) + /* Make 100% sure the next entry will fit. + ### Temporarily wastes ram + */ + if (!buffer || (HPACK_ENTRY_SIZE(entry) > chunksize - offset)) { - if (offset) + if (offset) { - serf_bucket_aggregate_append( + serf_bucket_aggregate_append( bucket, serf_bucket_simple_own_create(buffer, offset, alloc)); } - buffer = serf_bucket_mem_alloc(alloc, MAX(chunksize, - HPACK_ENTRY_SIZE(entry))); - offset = 0; + buffer = serf_bucket_mem_alloc(alloc, + MAX(chunksize, + HPACK_ENTRY_SIZE(entry))); + offset = 0; } /* Literal header, no indexing (=has a name) */ - hpack_int(0x40, 6, 0, buffer+offset, &len); - offset += len; + hpack_int(0x40, 6, 0, buffer + offset, &len); + offset += len; - /* ### TODO: Check if we can refer key or key+value by index */ + /* ### TODO: Check if we can refer key or key+value by index */ - /* To huff or not... */ - status = serf__hpack_huffman_encode(entry->key, entry->key_len, - 0, NULL, &len); - if (!status && len < entry->key_len) - { - apr_size_t int_len; - - /* It is more efficient to huffman encode */ - hpack_int(0x80, 7, len, buffer + offset, &int_len); - offset += int_len; - - status = serf__hpack_huffman_encode(entry->key, entry->key_len, - len, - (void*)(buffer + offset), &len); - offset += len; + /* To huff or not... */ + status = serf__hpack_huffman_encode(entry->key, entry->key_len, + 0, NULL, &len); + if (!status && len < entry->key_len) + { + apr_size_t int_len; - if (status) - return status; + /* It is more efficient to huffman encode */ + hpack_int(0x80, 7, len, buffer + offset, &int_len); + offset += int_len; + + status = serf__hpack_huffman_encode(entry->key, entry->key_len, + len, + (void*)(buffer + offset), + &len); + offset += len; + + if (status) + return status; } - else + else { /* It is more efficient not to encode */ - hpack_int(0, 7, entry->key_len, buffer + offset, &len); - offset += len; + hpack_int(0, 7, entry->key_len, buffer + offset, &len); + offset += len; - memcpy(buffer + offset, entry->key, entry->key_len); - offset += entry->key_len; + memcpy(buffer + offset, entry->key, entry->key_len); + offset += entry->key_len; } /* To huff or not... */ - status = serf__hpack_huffman_encode(entry->value, entry->value_len, - 0, NULL, &len); - if (!status && len < entry->key_len) - { - apr_size_t int_len; - - /* It is more efficient to huffman encode */ - hpack_int(0x80, 7, len, buffer + offset, &int_len); - offset += int_len; - - status = serf__hpack_huffman_encode(entry->value, entry->value_len, - len, - (void*)(buffer + offset), &len); - offset += len; + status = serf__hpack_huffman_encode(entry->value, entry->value_len, + 0, NULL, &len); + if (!status && len < entry->key_len) + { + apr_size_t int_len; + + /* It is more efficient to huffman encode */ + hpack_int(0x80, 7, len, buffer + offset, &int_len); + offset += int_len; + + status = serf__hpack_huffman_encode(entry->value, + entry->value_len, + len, + (void*)(buffer + offset), + &len); + offset += len; - if (status) - return status; + if (status) + return status; } - else + else { /* It is more efficient not to encode */ - hpack_int(0, 7, entry->value_len, buffer + offset, &len); - offset += len; + hpack_int(0, 7, entry->value_len, buffer + offset, &len); + offset += len; - memcpy(buffer + offset, entry->value, entry->value_len); - offset += entry->value_len; + memcpy(buffer + offset, entry->value, entry->value_len); + offset += entry->value_len; } /* And now free the item */ - hpack_free_entry(entry, alloc); + hpack_free_entry(entry, alloc); } - ctx->first = ctx->last = NULL; + ctx->first = ctx->last = NULL; - if (buffer) + if (buffer) { - if (offset) + if (offset) { - serf_bucket_aggregate_append( - bucket, - serf_bucket_simple_own_create(buffer, offset, alloc)); + serf_bucket_aggregate_append( + bucket, + serf_bucket_simple_own_create(buffer, offset, alloc)); } - else - serf_bucket_mem_free(alloc, buffer); + else + serf_bucket_mem_free(alloc, buffer); } - serf_bucket_mem_free(alloc, ctx); + serf_bucket_mem_free(alloc, ctx); - return APR_SUCCESS; + return APR_SUCCESS; } static apr_status_t @@ -960,28 +971,28 @@ serf_hpack_read(serf_bucket_t *bucket, const char **data, apr_size_t *len) { - apr_status_t status = serialize(bucket); + apr_status_t status = serialize(bucket); - if (status) - return status; + if (status) + return status; - return bucket->type->read(bucket, requested, data, len); + return bucket->type->read(bucket, requested, data, len); } static apr_status_t serf_hpack_read_iovec(serf_bucket_t *bucket, apr_size_t requested, int vecs_size, - struct iovec *vecs, - int *vecs_used) +struct iovec *vecs, + int *vecs_used) { - apr_status_t status = serialize(bucket); + apr_status_t status = serialize(bucket); - if (status) - return status; + if (status) + return status; - return bucket->type->read_iovec(bucket, requested, vecs_size, vecs, - vecs_used); + return bucket->type->read_iovec(bucket, requested, vecs_size, vecs, + vecs_used); } static apr_status_t @@ -989,43 +1000,43 @@ serf_hpack_peek(serf_bucket_t *bucket, const char **data, apr_size_t *len) { - apr_status_t status = serialize(bucket); + apr_status_t status = serialize(bucket); - if (status) - return status; + if (status) + return status; - return bucket->type->peek(bucket, data, len); + return bucket->type->peek(bucket, data, len); } static apr_uint64_t serf_hpack_get_remaining(serf_bucket_t *bucket) { - apr_status_t status = serialize(bucket); + apr_status_t status = serialize(bucket); - if (status) - return SERF_LENGTH_UNKNOWN; + if (status) + return SERF_LENGTH_UNKNOWN; - /* This assumes that the aggregate is a v2 bucket */ - return bucket->type->get_remaining(bucket); + /* This assumes that the aggregate is a v2 bucket */ + return bucket->type->get_remaining(bucket); } static void serf_hpack_destroy_and_data(serf_bucket_t *bucket) { - serf_hpack_context_t *ctx = bucket->data; - serf_hpack_entry_t *hi; - serf_hpack_entry_t *next; + serf_hpack_context_t *ctx = bucket->data; + serf_hpack_entry_t *hi; + serf_hpack_entry_t *next; - for (hi = ctx->first; hi; hi = next) + for (hi = ctx->first; hi; hi = next) { - next = hi->next; + next = hi->next; - hpack_free_entry(hi, bucket->allocator); + hpack_free_entry(hi, bucket->allocator); } - serf_default_destroy_and_data(bucket); + serf_default_destroy_and_data(bucket); } @@ -1047,129 +1058,129 @@ const serf_bucket_type_t serf_bucket_typ typedef struct serf_hpack_decode_ctx_t { - serf_hpack_table_t *tbl; + serf_hpack_table_t *tbl; - serf_bucket_t *stream; - apr_size_t header_allowed; + serf_bucket_t *stream; + apr_size_t header_allowed; - apr_status_t(*item_callback)(void *baton, - const char *key, - apr_size_t key_size, - const char *value, - apr_size_t value_size); - void *item_baton; - - char *buffer; - apr_size_t buffer_size; - apr_size_t buffer_used; - - const char *key; /* Allocated in tbl->alloc */ - apr_size_t key_size; - const char *val; /* Allocated in tbl->alloc */ - apr_size_t val_size; - char index_item; - char key_hm; - char val_hm; - apr_uint32_t reuse_item; - - enum - { - HPACK_DECODE_STATE_INITIAL = 0, - HPACK_DECODE_STATE_INDEX, - HPACK_DECODE_STATE_KEYINDEX, - HPACK_DECODE_STATE_KEY_LEN, - HPACK_DECODE_STATE_KEY, - HPACK_DECODE_STATE_VALUE_LEN, - HPACK_DECODE_STATE_VALUE, - HPACK_DECODE_TABLESIZE_UPDATE - } state; - - /* When producing HTTP/1.1 style output */ - serf_bucket_t *agg; - serf_config_t *config; - - bool wrote_header; - bool is_request; - bool hit_eof; - - const char *method; - const char *path; - const char *authority; + apr_status_t(*item_callback)(void *baton, + const char *key, + apr_size_t key_size, + const char *value, + apr_size_t value_size); + void *item_baton; + + char *buffer; + apr_size_t buffer_size; + apr_size_t buffer_used; + + const char *key; /* Allocated in tbl->alloc */ + apr_size_t key_size; + const char *val; /* Allocated in tbl->alloc */ + apr_size_t val_size; + char index_item; + char key_hm; + char val_hm; + apr_uint32_t reuse_item; + + enum + { + HPACK_DECODE_STATE_INITIAL = 0, + HPACK_DECODE_STATE_INDEX, + HPACK_DECODE_STATE_KEYINDEX, + HPACK_DECODE_STATE_KEY_LEN, + HPACK_DECODE_STATE_KEY, + HPACK_DECODE_STATE_VALUE_LEN, + HPACK_DECODE_STATE_VALUE, + HPACK_DECODE_TABLESIZE_UPDATE + } state; + + /* When producing HTTP/1.1 style output */ + serf_bucket_t *agg; + serf_config_t *config; + + bool wrote_header; + bool is_request; + bool hit_eof; + + const char *method; + const char *path; + const char *authority; } serf_hpack_decode_ctx_t; serf_bucket_t * serf__bucket_hpack_decode_create(serf_bucket_t *stream, - apr_status_t (*item_callback)( - void *baton, - const char *key, - apr_size_t key_size, - const char *value, - apr_size_t value_size), + apr_status_t(*item_callback)( + void *baton, + const char *key, + apr_size_t key_size, + const char *value, + apr_size_t value_size), void *item_baton, apr_size_t max_header_size, serf_hpack_table_t *hpack_table, serf_bucket_alloc_t *alloc) { - serf_hpack_decode_ctx_t *ctx = serf_bucket_mem_calloc(alloc, sizeof(*ctx)); + serf_hpack_decode_ctx_t *ctx = serf_bucket_mem_calloc(alloc, sizeof(*ctx)); - ctx->tbl = hpack_table; - ctx->stream = stream; - ctx->header_allowed = max_header_size; + ctx->tbl = hpack_table; + ctx->stream = stream; + ctx->header_allowed = max_header_size; - /* The buffer should be large enough to keep a *compressed* key - or value and will be resized if necessary. + /* The buffer should be large enough to keep a *compressed* key + or value and will be resized if necessary. - Longer keys are more likely to use compression, so the default - should be enough for simple requests. + Longer keys are more likely to use compression, so the default + should be enough for simple requests. - (It is also used for compressed integer values, but there 10 bytes - should be enough to store a uint64 with 7 bits/byte) */ - ctx->buffer_size = 128; - ctx->buffer_used = 0; - ctx->buffer = serf_bucket_mem_alloc(alloc, ctx->buffer_size); + (It is also used for compressed integer values, but there 10 bytes + should be enough to store a uint64 with 7 bits/byte) */ + ctx->buffer_size = 128; + ctx->buffer_used = 0; + ctx->buffer = serf_bucket_mem_alloc(alloc, ctx->buffer_size); - if (item_callback) + if (item_callback) { - ctx->item_callback = item_callback; - ctx->item_baton = item_baton; - ctx->agg = NULL; + ctx->item_callback = item_callback; + ctx->item_baton = item_baton; + ctx->agg = NULL; } - else + else { - ctx->item_callback = NULL; - ctx->item_baton = NULL; - ctx->agg = serf_bucket_aggregate_create(alloc); + ctx->item_callback = NULL; + ctx->item_baton = NULL; + ctx->agg = serf_bucket_aggregate_create(alloc); } /* Prepare TBL for decoding */ - ctx->tbl->rl_start = ctx->tbl->rl_first; - ctx->tbl->rl_indexable = ctx->tbl->rl_count; + ctx->tbl->rl_start = ctx->tbl->rl_first; + ctx->tbl->rl_indexable = ctx->tbl->rl_count; - return serf_bucket_create(&serf_bucket_type__hpack_decode, alloc, ctx); + return serf_bucket_create(&serf_bucket_type__hpack_decode, alloc, ctx); } static void hpack_decode_buffer_ensure(serf_bucket_t *bucket, apr_size_t minsize) { - serf_hpack_decode_ctx_t *ctx = bucket->data; - char *new_buffer; + serf_hpack_decode_ctx_t *ctx = bucket->data; + char *new_buffer; - if (minsize < ctx->buffer_size) - return; + if (minsize < ctx->buffer_size) + return; - while (minsize < ctx->buffer_size) + while (minsize < ctx->buffer_size) { - ctx->buffer_size *= 2; + ctx->buffer_size *= 2; } - new_buffer = serf_bucket_mem_alloc(bucket->allocator, - ctx->buffer_size); + new_buffer = serf_bucket_mem_alloc(bucket->allocator, + ctx->buffer_size); - /* In general only a small part of the old buffer is used at this point */ - memcpy(new_buffer, ctx->buffer, ctx->buffer_used); - serf_bucket_mem_free(bucket->allocator, ctx->buffer); - ctx->buffer = new_buffer; + /* In general only a small part of the old buffer is used at this point */ + memcpy(new_buffer, ctx->buffer, ctx->buffer_used); + serf_bucket_mem_free(bucket->allocator, ctx->buffer); + ctx->buffer = new_buffer; } static apr_status_t @@ -1178,289 +1189,292 @@ read_hpack_int(apr_uint32_t *v, serf_bucket_t *bucket, int bits) { - serf_hpack_decode_ctx_t *ctx = bucket->data; - apr_status_t status; - apr_uint16_t value_mask; - apr_uint64_t vv; + serf_hpack_decode_ctx_t *ctx = bucket->data; + apr_status_t status; + apr_uint16_t value_mask; + apr_uint64_t vv; - if (!ctx->buffer_used) + if (!ctx->buffer_used) { - const char *data; - apr_size_t len; + const char *data; + apr_size_t len; - status = serf_bucket_read(ctx->stream, 1, &data, &len); - if (status || len == 0) - return status ? status : APR_EAGAIN; + status = serf_bucket_read(ctx->stream, 1, &data, &len); + if (status || len == 0) + return status ? status : APR_EAGAIN; - ctx->buffer[0] = *data; - ctx->buffer_used++; + ctx->buffer[0] = *data; + ctx->buffer_used++; } - value_mask = (1 << bits) - 1; + value_mask = (1 << bits) - 1; - if (((unsigned char)ctx->buffer[0] & value_mask) != value_mask) + if (((unsigned char)ctx->buffer[0] & value_mask) != value_mask) { /* Everything fits in the initial byte :-) */ - vv = ((unsigned char)ctx->buffer[0] & value_mask); + vv = ((unsigned char)ctx->buffer[0] & value_mask); } - else + else { - apr_size_t i; + apr_size_t i; - /* Here we read the necessary bytes for the integer upto the - first byte that doesn't have the 0x80 bit set. + /* Here we read the necessary bytes for the integer upto the + first byte that doesn't have the 0x80 bit set. - We could try to be smart by peeking, getting the size if - possible, etc.... but that would optimize for large ints - while the value typically fits in 1 or 2 bytes max. + We could try to be smart by peeking, getting the size if + possible, etc.... but that would optimize for large ints + while the value typically fits in 1 or 2 bytes max. - My guess is that trying to be smart will be more expensive - here. */ - do + My guess is that trying to be smart will be more expensive + here. */ + do { - const char *data; - apr_size_t len; + const char *data; + apr_size_t len; - /* We already have all the bits we can store */ - if ((7 * (ctx->buffer_used - 1) + bits) >= 32) - return SERF_ERROR_HTTP2_COMPRESSION_ERROR; - - status = serf_bucket_read(ctx->stream, 1, &data, &len); - if (status || len == 0) - return status ? status : APR_EAGAIN; + /* We already have all the bits we can store */ + if ((7 * (ctx->buffer_used - 1) + bits) >= 32) + return SERF_ERROR_HTTP2_COMPRESSION_ERROR; - ctx->buffer[ctx->buffer_used] = *data; - ctx->buffer_used++; - } - while (ctx->buffer[ctx->buffer_used - 1] & 0x80); + status = serf_bucket_read(ctx->stream, 1, &data, &len); + if (status || len == 0) + return status ? status : APR_EAGAIN; + + ctx->buffer[ctx->buffer_used] = *data; + ctx->buffer_used++; + } while (ctx->buffer[ctx->buffer_used - 1] & 0x80); /* Check if the value could have been stored more efficiently. If it can then this is a compression error. The value where all the bits in the first byte are 1 really needs the next byte as 0, to encode that. */ - if (ctx->buffer_used > 2 && ctx->buffer[ctx->buffer_used - 1] == 0) - return SERF_ERROR_HTTP2_COMPRESSION_ERROR; + if (ctx->buffer_used > 2 && ctx->buffer[ctx->buffer_used - 1] == 0) + return SERF_ERROR_HTTP2_COMPRESSION_ERROR; - vv = value_mask; + vv = value_mask; - for (i = 1; i < ctx->buffer_used; i++) - vv += (apr_uint64_t)((unsigned char)ctx->buffer[i] & 0x7F) << (7 * (i - 1)); + for (i = 1; i < ctx->buffer_used; i++) + vv += (apr_uint64_t)((unsigned char)ctx->buffer[i] & 0x7F) + << (7 * (i - 1)); - if ((vv & APR_UINT32_MAX) != vv) - return SERF_ERROR_HTTP2_COMPRESSION_ERROR; + if ((vv & APR_UINT32_MAX) != vv) + return SERF_ERROR_HTTP2_COMPRESSION_ERROR; } - *v = (apr_uint32_t)vv; + *v = (apr_uint32_t)vv; - if (flags) - *flags = (((unsigned char)ctx->buffer[0]) & ~value_mask); + if (flags) + *flags = (((unsigned char)ctx->buffer[0]) & ~value_mask); - ctx->buffer_used = 0; /* Done with buffer */ + ctx->buffer_used = 0; /* Done with buffer */ - return APR_SUCCESS; + return APR_SUCCESS; } static void write_request_header(serf_hpack_decode_ctx_t *ctx) { - serf_bucket_t *b; - serf_bucket_alloc_t *alloc = ctx->agg->allocator; - - ctx->wrote_header = TRUE; + serf_bucket_t *b; + serf_bucket_alloc_t *alloc = ctx->agg->allocator; - if (ctx->method) - b = serf_bucket_simple_own_create(ctx->method, strlen(ctx->method), alloc); - else - b = SERF_BUCKET_SIMPLE_STRING("GET", alloc); + ctx->wrote_header = TRUE; - serf_bucket_aggregate_append(ctx->agg, b); - - b = SERF_BUCKET_SIMPLE_STRING(" ", alloc); - serf_bucket_aggregate_append(ctx->agg, b); - - if (ctx->path) - b = serf_bucket_simple_own_create(ctx->path, strlen(ctx->path), alloc); - else - b = SERF_BUCKET_SIMPLE_STRING("/", alloc); - serf_bucket_aggregate_append(ctx->agg, b); - - b = SERF_BUCKET_SIMPLE_STRING(" HTTP/2.0\r\n", alloc); - serf_bucket_aggregate_append(ctx->agg, b); - - if (ctx->authority) - { - b = SERF_BUCKET_SIMPLE_STRING("Host: ", alloc); - serf_bucket_aggregate_append(ctx->agg, b); - - b = serf_bucket_simple_own_create(ctx->authority, strlen(ctx->authority), - alloc); - serf_bucket_aggregate_append(ctx->agg, b); - b = SERF_BUCKET_SIMPLE_STRING("\r\n", alloc); - serf_bucket_aggregate_append(ctx->agg, b); + if (ctx->method) + b = serf_bucket_simple_own_create(ctx->method, strlen(ctx->method), + alloc); + else + b = SERF_BUCKET_SIMPLE_STRING("GET", alloc); + + serf_bucket_aggregate_append(ctx->agg, b); + + b = SERF_BUCKET_SIMPLE_STRING(" ", alloc); + serf_bucket_aggregate_append(ctx->agg, b); + + if (ctx->path) + b = serf_bucket_simple_own_create(ctx->path, strlen(ctx->path), + alloc); + else + b = SERF_BUCKET_SIMPLE_STRING("/", alloc); + serf_bucket_aggregate_append(ctx->agg, b); + + b = SERF_BUCKET_SIMPLE_STRING(" HTTP/2.0\r\n", alloc); + serf_bucket_aggregate_append(ctx->agg, b); + + if (ctx->authority) + { + b = SERF_BUCKET_SIMPLE_STRING("Host: ", alloc); + serf_bucket_aggregate_append(ctx->agg, b); + + b = serf_bucket_simple_own_create(ctx->authority, + strlen(ctx->authority), + alloc); + serf_bucket_aggregate_append(ctx->agg, b); + b = SERF_BUCKET_SIMPLE_STRING("\r\n", alloc); + serf_bucket_aggregate_append(ctx->agg, b); } /* Now owned by bucket */ - ctx->method = ctx->path = ctx->authority = NULL; + ctx->method = ctx->path = ctx->authority = NULL; } static apr_status_t handle_read_entry_and_clear(serf_hpack_decode_ctx_t *ctx, serf_bucket_alloc_t *alloc) { - serf_hpack_table_t *tbl = ctx->tbl; - const char *keep_key = NULL; - const char *keep_val = NULL; - apr_status_t status; - char own_key; - char own_val; - - serf__log(LOGLVL_INFO, SERF_LOGCOMP_PROTOCOL, __FILE__, ctx->config, - "Parsed from HPACK: %.*s: %.*s\n", - ctx->key_size, ctx->key, ctx->val_size, ctx->val); - - if (ctx->item_callback) - { - status = ctx->item_callback(ctx->item_baton, - ctx->key, ctx->key_size, - ctx->val, ctx->val_size); + serf_hpack_table_t *tbl = ctx->tbl; + const char *keep_key = NULL; + const char *keep_val = NULL; + apr_status_t status; + char own_key; + char own_val; + + serf__log(LOGLVL_INFO, SERF_LOGCOMP_PROTOCOL, __FILE__, ctx->config, + "Parsed from HPACK: %.*s: %.*s\n", + ctx->key_size, ctx->key, ctx->val_size, ctx->val); + + if (ctx->item_callback) + { + status = ctx->item_callback(ctx->item_baton, + ctx->key, ctx->key_size, + ctx->val, ctx->val_size); - if (status) - return status; + if (status) + return status; } - else if (!ctx->wrote_header) + else if (!ctx->wrote_header) { - serf_bucket_t *b; + serf_bucket_t *b; - if (ctx->key_size == 7 && !strcmp(ctx->key, ":status")) + if (ctx->key_size == 7 && !strcmp(ctx->key, ":status")) { - ctx->wrote_header = TRUE; + ctx->wrote_header = TRUE; - b = SERF_BUCKET_SIMPLE_STRING("HTTP/2.0 ", alloc); - serf_bucket_aggregate_append(ctx->agg, b); + b = SERF_BUCKET_SIMPLE_STRING("HTTP/2.0 ", alloc); + serf_bucket_aggregate_append(ctx->agg, b); - b = serf_bucket_simple_copy_create(ctx->val, ctx->val_size, alloc); - serf_bucket_aggregate_append(ctx->agg, b); + b = serf_bucket_simple_copy_create(ctx->val, ctx->val_size, alloc); + serf_bucket_aggregate_append(ctx->agg, b); - b = SERF_BUCKET_SIMPLE_STRING(" <http2>\r\n", alloc); - serf_bucket_aggregate_append(ctx->agg, b); + b = SERF_BUCKET_SIMPLE_STRING(" <http2>\r\n", alloc); + serf_bucket_aggregate_append(ctx->agg, b); } - else if (ctx->key_size == 7 && !strcmp(ctx->key, ":method")) + else if (ctx->key_size == 7 && !strcmp(ctx->key, ":method")) { - ctx->is_request = true; - ctx->method = serf_bstrmemdup(ctx->agg->allocator, - ctx->val, ctx->val_size); - if (ctx->authority && ctx->method && ctx->path) - write_request_header(ctx); + ctx->is_request = true; + ctx->method = serf_bstrmemdup(ctx->agg->allocator, + ctx->val, ctx->val_size); + if (ctx->authority && ctx->method && ctx->path) + write_request_header(ctx); } - else if (ctx->key_size == 10 && !strcmp(ctx->key, ":authority")) + else if (ctx->key_size == 10 && !strcmp(ctx->key, ":authority")) { - ctx->is_request = true; - ctx->authority = serf_bstrmemdup(ctx->agg->allocator, - ctx->val, ctx->val_size); - if (ctx->authority && ctx->method && ctx->path) - write_request_header(ctx); - } - else if (ctx->key_size == 5 && !strcmp(ctx->key, ":path")) - { - ctx->is_request = true; - ctx->path = serf_bstrmemdup(ctx->agg->allocator, - ctx->val, ctx->val_size); - if (ctx->authority && ctx->method && ctx->path) - write_request_header(ctx); + ctx->is_request = true; + ctx->authority = serf_bstrmemdup(ctx->agg->allocator, + ctx->val, ctx->val_size); + if (ctx->authority && ctx->method && ctx->path) + write_request_header(ctx); } - else if (ctx->key_size && ctx->key[0] == ':') + else if (ctx->key_size == 5 && !strcmp(ctx->key, ":path")) + { + ctx->is_request = true; + ctx->path = serf_bstrmemdup(ctx->agg->allocator, + ctx->val, ctx->val_size); + if (ctx->authority && ctx->method && ctx->path) + write_request_header(ctx); + } + else if (ctx->key_size && ctx->key[0] == ':') { /* Ignore all magic headers */ } - else + else { /* Write some header with some status code first */ - ctx->wrote_header = TRUE; + ctx->wrote_header = TRUE; - if (ctx->is_request) - write_request_header(ctx); - else + if (ctx->is_request) + write_request_header(ctx); + else { - b = SERF_BUCKET_SIMPLE_STRING( - "HTTP/2.0 505 Missing ':status' header\r\n", - alloc); - serf_bucket_aggregate_append(ctx->agg, b); + b = SERF_BUCKET_SIMPLE_STRING( + "HTTP/2.0 505 Missing ':status' header\r\n", + alloc); + serf_bucket_aggregate_append(ctx->agg, b); } /* And now the actual header */ - b = serf_bucket_simple_copy_create(ctx->key, ctx->key_size, alloc); - serf_bucket_aggregate_append(ctx->agg, b); + b = serf_bucket_simple_copy_create(ctx->key, ctx->key_size, alloc); + serf_bucket_aggregate_append(ctx->agg, b); - b = SERF_BUCKET_SIMPLE_STRING(": ", alloc); - serf_bucket_aggregate_append(ctx->agg, b); + b = SERF_BUCKET_SIMPLE_STRING(": ", alloc); + serf_bucket_aggregate_append(ctx->agg, b); - b = serf_bucket_simple_copy_create(ctx->val, ctx->val_size, alloc); - serf_bucket_aggregate_append(ctx->agg, b); + b = serf_bucket_simple_copy_create(ctx->val, ctx->val_size, alloc); + serf_bucket_aggregate_append(ctx->agg, b); - b = SERF_BUCKET_SIMPLE_STRING("\r\n", alloc); - serf_bucket_aggregate_append(ctx->agg, b); + b = SERF_BUCKET_SIMPLE_STRING("\r\n", alloc); + serf_bucket_aggregate_append(ctx->agg, b); } } - else if (ctx->key_size && ctx->key[0] != ':') + else if (ctx->key_size && ctx->key[0] != ':') { - serf_bucket_t *b; + serf_bucket_t *b; - /* Write header */ - b = serf_bucket_simple_copy_create(ctx->key, ctx->key_size, alloc); - serf_bucket_aggregate_append(ctx->agg, b); + /* Write header */ + b = serf_bucket_simple_copy_create(ctx->key, ctx->key_size, alloc); + serf_bucket_aggregate_append(ctx->agg, b); - b = SERF_BUCKET_SIMPLE_STRING(": ", alloc); - serf_bucket_aggregate_append(ctx->agg, b); + b = SERF_BUCKET_SIMPLE_STRING(": ", alloc); + serf_bucket_aggregate_append(ctx->agg, b); - b = serf_bucket_simple_copy_create(ctx->val, ctx->val_size, alloc); - serf_bucket_aggregate_append(ctx->agg, b); + b = serf_bucket_simple_copy_create(ctx->val, ctx->val_size, alloc); + serf_bucket_aggregate_append(ctx->agg, b); - b = SERF_BUCKET_SIMPLE_STRING("\r\n", alloc); - serf_bucket_aggregate_append(ctx->agg, b); + b = SERF_BUCKET_SIMPLE_STRING("\r\n", alloc); + serf_bucket_aggregate_append(ctx->agg, b); } - if (ctx->reuse_item) + if (ctx->reuse_item) { - status = hpack_table_get(ctx->reuse_item, tbl, - &keep_key, NULL, - &keep_val, NULL); + status = hpack_table_get(ctx->reuse_item, tbl, + &keep_key, NULL, + &keep_val, NULL); } - own_key = (ctx->key && ctx->key != keep_key); - own_val = (ctx->val && ctx->val != keep_val); + own_key = (ctx->key && ctx->key != keep_key); + own_val = (ctx->val && ctx->val != keep_val); - if (ctx->index_item) + if (ctx->index_item) { - serf_hpack_entry_t *entry = serf_bucket_mem_calloc(tbl->alloc, - sizeof(*entry)); + serf_hpack_entry_t *entry = serf_bucket_mem_calloc(tbl->alloc, + sizeof(*entry)); - entry->key = own_key ? ctx->key : serf_bstrmemdup(tbl->alloc, ctx->key, - ctx->key_size); - entry->key_len = ctx->key_size; - entry->value = own_val ? ctx->val : serf_bstrmemdup(tbl->alloc, - ctx->val, - ctx->val_size); - entry->value_len = ctx->val_size; - entry->free_key = entry->free_val = true; - entry->next = tbl->rl_first; - tbl->rl_first = entry; - tbl->rl_count++; - tbl->rl_size += HPACK_ENTRY_SIZE(entry); - if (entry->next) - entry->next->prev = entry; - else - tbl->rl_last = entry; + entry->key = own_key ? ctx->key : serf_bstrmemdup(tbl->alloc, ctx->key, + ctx->key_size); + entry->key_len = ctx->key_size; + entry->value = own_val ? ctx->val : serf_bstrmemdup(tbl->alloc, + ctx->val, + ctx->val_size); + entry->value_len = ctx->val_size; + entry->free_key = entry->free_val = true; + entry->next = tbl->rl_first; + tbl->rl_first = entry; + tbl->rl_count++; + tbl->rl_size += HPACK_ENTRY_SIZE(entry); + if (entry->next) + entry->next->prev = entry; + else + tbl->rl_last = entry; - /* We don't update lr_start... that is the idea */ + /* We don't update lr_start... that is the idea */ } - else + else { - if (own_key) - serf_bucket_mem_free(tbl->alloc, (void*)ctx->key); - if (own_val) - serf_bucket_mem_free(tbl->alloc, (void*)ctx->val); + if (own_key) + serf_bucket_mem_free(tbl->alloc, (void*)ctx->key); + if (own_val) + serf_bucket_mem_free(tbl->alloc, (void*)ctx->val); } - return APR_SUCCESS; + return APR_SUCCESS; } /* Reads the exact amount of bytes, buffered if necessary. @@ -1472,374 +1486,383 @@ static apr_status_t hpack_read_bytes(ser apr_size_t required, const void **data) { - serf_hpack_decode_ctx_t *ctx = bucket->data; - apr_status_t status = APR_SUCCESS; - apr_size_t len; - const char *some_data; + serf_hpack_decode_ctx_t *ctx = bucket->data; + apr_status_t status = APR_SUCCESS; + apr_size_t len; + const char *some_data; - /* assert(required < ctx->buffer_used); */ + /* assert(required < ctx->buffer_used); */ - if (required == 0) + if (required == 0) { - *data = ctx->buffer; - return APR_SUCCESS; + *data = ctx->buffer; + return APR_SUCCESS; } - if (!ctx->buffer_used) + if (!ctx->buffer_used) { - status = serf_bucket_read(ctx->stream, required, &some_data, &len); + status = serf_bucket_read(ctx->stream, required, &some_data, &len); - if (SERF_BUCKET_READ_ERROR(status) || (len == required)) + if (SERF_BUCKET_READ_ERROR(status) || (len == required)) { - if (APR_STATUS_IS_EOF(status) && len == required) - status = APR_SUCCESS; - *data = some_data; - return status; + if (APR_STATUS_IS_EOF(status) && len == required) + status = APR_SUCCESS; + *data = some_data; + return status; } - hpack_decode_buffer_ensure(bucket, required); + hpack_decode_buffer_ensure(bucket, required); - memcpy(ctx->buffer, data, len); - ctx->buffer_used = len; + memcpy(ctx->buffer, data, len); + ctx->buffer_used = len; - if (status) - return status; + if (status) + return status; - /* Fall through: Try to continue reading*/ + /* Fall through: Try to continue reading*/ } - else + else { /* Ensure that the buffer is large enough to hold everything */ - hpack_decode_buffer_ensure(bucket, required); + hpack_decode_buffer_ensure(bucket, required); } - while (ctx->buffer_used < required) + while (ctx->buffer_used < required) { - status = serf_bucket_read(ctx->stream, required - ctx->buffer_used, - &some_data, &len); + status = serf_bucket_read(ctx->stream, required - ctx->buffer_used, + &some_data, &len); - if (SERF_BUCKET_READ_ERROR(status)) - return status; + if (SERF_BUCKET_READ_ERROR(status)) + return status; - memcpy(ctx->buffer + ctx->buffer_used, some_data, len); - ctx->buffer_used += len; + memcpy(ctx->buffer + ctx->buffer_used, some_data, len); + ctx->buffer_used += len; - if (status) - break; + if (status) + break; } - if (ctx->buffer_used == required) + if (ctx->buffer_used == required) { - *data = ctx->buffer; - ctx->buffer_used = 0; /* Done with buffer */ - status = APR_SUCCESS; + *data = ctx->buffer; + ctx->buffer_used = 0; /* Done with buffer */ + status = APR_SUCCESS; } - return status; + return status; } static apr_status_t hpack_process(serf_bucket_t *bucket) { - serf_hpack_decode_ctx_t *ctx = bucket->data; - apr_status_t status = APR_SUCCESS; + serf_hpack_decode_ctx_t *ctx = bucket->data; + apr_status_t status = APR_SUCCESS; - if (ctx->hit_eof) - return APR_EOF; + if (ctx->hit_eof) + return APR_EOF; - while (status == APR_SUCCESS) + while (status == APR_SUCCESS) { - switch (ctx->state) + switch (ctx->state) { - case HPACK_DECODE_STATE_INITIAL: - { - unsigned char uc; - const char *data; - apr_size_t len; - - status = serf_bucket_read(ctx->stream, 1, &data, &len); - if (status || !len) - continue; - - ctx->key_hm = ctx->val_hm = FALSE; - ctx->reuse_item = 0; - - uc = *data; - if (uc & 0x80) + case HPACK_DECODE_STATE_INITIAL: { - /* 6.1. Indexed Header Field Representation - https://tools.ietf.org/html/rfc7541#section-6.1 */ - - ctx->state = HPACK_DECODE_STATE_INDEX; - ctx->buffer[0] = *data; - ctx->buffer_used = 1; /* Initial state for read_hpack_int */ - ctx->index_item = FALSE; + unsigned char uc; + const char *data; + apr_size_t len; + + status = serf_bucket_read(ctx->stream, 1, &data, &len); + if (status || !len) + continue; + + ctx->key_hm = ctx->val_hm = FALSE; + ctx->reuse_item = 0; + + uc = *data; + if (uc & 0x80) + { + /* 6.1. Indexed Header Field Representation + https://tools.ietf.org/html/rfc7541#section-6.1 */ + + ctx->state = HPACK_DECODE_STATE_INDEX; + ctx->buffer[0] = *data; + ctx->buffer_used = 1; /* Initial state for + read_hpack_int() */ + ctx->index_item = FALSE; + } + else if (uc == 0x40 || uc == 0x00 || uc == 0x10) + { + /* 0x40: Literal Header Field with Incremental Indexing + -- New Name + https://tools.ietf.org/html/rfc7541#section-6.2.1 + 0x00: Literal Header Field without Indexing + -- New Name + https://tools.ietf.org/html/rfc7541#section-6.2.2 + 0x10: Literal Header Field Never Indexed + -- New Name + https://tools.ietf.org/html/rfc7541#section-6.2.3 */ + + ctx->state = HPACK_DECODE_STATE_KEY_LEN; + ctx->index_item = (uc == 0x40); + } + else if ((uc & 0x60) == 0x20) + { + /* 6.3. Dynamic Table Size Update + https://tools.ietf.org/html/rfc7541#section-6.3 */ + ctx->state = HPACK_DECODE_TABLESIZE_UPDATE; + ctx->buffer[0] = *data; + ctx->buffer_used = 1; /* Initial state for + read_hpack_int() */ + } + else + { + /* 6.2.1 Literal Header Field with Incremental Indexing + -- Indexed Name + https://tools.ietf.org/html/rfc7541#section-6.2.1 + 6.2.2: Literal Header Field without Indexing + -- Indexed Name + https://tools.ietf.org/html/rfc7541#section-6.2.2 + 6.2.3. Literal Header Field Never Indexed + -- Indexed Name + https://tools.ietf.org/html/rfc7541#section-6.2.3 */ + + ctx->state = HPACK_DECODE_STATE_KEYINDEX; + ctx->buffer[0] = *data; + ctx->buffer_used = 1; /* Initial state for + read_hpack_int() */ + ctx->index_item = (uc & 0x40) != 0; + } + continue; } - else if (uc == 0x40 || uc == 0x00 || uc == 0x10) + case HPACK_DECODE_STATE_INDEX: { - /* 0x40: Literal Header Field with Incremental Indexing - -- New Name - https://tools.ietf.org/html/rfc7541#section-6.2.1 - 0x00: Literal Header Field without Indexing - -- New Name - https://tools.ietf.org/html/rfc7541#section-6.2.2 - 0x10: Literal Header Field Never Indexed - -- New Name - https://tools.ietf.org/html/rfc7541#section-6.2.3 */ - - ctx->state = HPACK_DECODE_STATE_KEY_LEN; - ctx->index_item = (uc == 0x40); + apr_uint32_t v; + status = read_hpack_int(&v, NULL, bucket, 7); + if (status) + continue; + if (v == 0) + return SERF_ERROR_HTTP2_COMPRESSION_ERROR; + + ctx->reuse_item = v; + status = hpack_table_get(v, ctx->tbl, + &ctx->key, &ctx->key_size, + &ctx->val, &ctx->val_size); + if (status) + return status; + + if (ctx->header_allowed <= HPACK_KEY_SIZE(ctx->key_size) + + HPACK_KEY_SIZE(ctx->val_size)) + { + return SERF_ERROR_HTTP2_COMPRESSION_ERROR; + } + ctx->header_allowed -= HPACK_KEY_SIZE(ctx->key_size) + + HPACK_KEY_SIZE(ctx->val_size); + + status = handle_read_entry_and_clear(ctx, + bucket->allocator); + if (status) + return status; + + /* Get key and value from table and handle result */ + ctx->state = HPACK_DECODE_STATE_INITIAL; + continue; } - else if ((uc & 0x60) == 0x20) + case HPACK_DECODE_STATE_KEYINDEX: { - /* 6.3. Dynamic Table Size Update - https://tools.ietf.org/html/rfc7541#section-6.3 */ - ctx->state = HPACK_DECODE_TABLESIZE_UPDATE; - ctx->buffer[0] = *data; - ctx->buffer_used = 1; /* Initial state for read_hpack_int */ + apr_uint32_t v; + status = read_hpack_int(&v, NULL, bucket, + ctx->index_item ? 6 : 4); + if (status) + continue; + + ctx->reuse_item = v; + status = hpack_table_get(v, ctx->tbl, + &ctx->key, &ctx->key_size, + NULL, NULL); + if (status) + return status; + + /* Get key from table */ + ctx->state = HPACK_DECODE_STATE_VALUE_LEN; + if (HPACK_KEY_SIZE(ctx->key_size) >= ctx->header_allowed) + return SERF_ERROR_HTTP2_COMPRESSION_ERROR; + + ctx->header_allowed -= HPACK_KEY_SIZE(ctx->key_size); + continue; } - else + case HPACK_DECODE_STATE_KEY_LEN: { - /* 6.2.1 Literal Header Field with Incremental Indexing - -- Indexed Name - https://tools.ietf.org/html/rfc7541#section-6.2.1 - 6.2.2: Literal Header Field without Indexing - -- Indexed Name - https://tools.ietf.org/html/rfc7541#section-6.2.2 - 6.2.3. Literal Header Field Never Indexed - -- Indexed Name - https://tools.ietf.org/html/rfc7541#section-6.2.3 */ - - ctx->state = HPACK_DECODE_STATE_KEYINDEX; - ctx->buffer[0] = *data; - ctx->buffer_used = 1; /* Initial state for read_hpack_int */ - ctx->index_item = (uc & 0x40) != 0; + apr_uint32_t v; + unsigned char flags; + status = read_hpack_int(&v, &flags, bucket, 7); + if (status) + continue; + + ctx->key_hm = (flags & 0x80) != 0; + + /* Just check compressed size first. If the result is + smaller the encoder shouldn't have used compression */ + if (HPACK_KEY_SIZE(v) >= ctx->header_allowed) + return SERF_ERROR_HTTP2_COMPRESSION_ERROR; + + ctx->key_size = (apr_size_t)v; + ctx->state = HPACK_DECODE_STATE_KEY; + /* Fall through */ } - continue; - } - case HPACK_DECODE_STATE_INDEX: - { - apr_uint32_t v; - status = read_hpack_int(&v, NULL, bucket, 7); - if (status) - continue; - if (v == 0) - return SERF_ERROR_HTTP2_COMPRESSION_ERROR; - - ctx->reuse_item = v; - status = hpack_table_get(v, ctx->tbl, - &ctx->key, &ctx->key_size, - &ctx->val, &ctx->val_size); - if (status) - return status; + case HPACK_DECODE_STATE_KEY: + { + const void *data; - if (ctx->header_allowed <= HPACK_KEY_SIZE(ctx->key_size) - + HPACK_KEY_SIZE(ctx->val_size)) + status = hpack_read_bytes(bucket, ctx->key_size, &data); + if (status) + continue; + + if (ctx->key_hm) + { + apr_size_t ks; + char *key; + + status = serf__hpack_huffman_decode(data, + ctx->key_size, + 0, NULL, &ks); + + if (status) + return status; + + if (HPACK_KEY_SIZE(ks) >= ctx->header_allowed) + return SERF_ERROR_HTTP2_COMPRESSION_ERROR; + + key = serf_bucket_mem_alloc(ctx->tbl->alloc, ks + 1); + + status = serf__hpack_huffman_decode(data, + ctx->key_size, + ks + 1, key, + &ctx->key_size); + if (status) + return status; + + ctx->key = key; + } + else + ctx->key = serf_bstrmemdup(ctx->tbl->alloc, data, + ctx->key_size); + + ctx->state = HPACK_DECODE_STATE_VALUE_LEN; + ctx->header_allowed -= HPACK_KEY_SIZE(ctx->key_size); + /* Fall through */ + } + case HPACK_DECODE_STATE_VALUE_LEN: { - return SERF_ERROR_HTTP2_COMPRESSION_ERROR; + apr_uint32_t v; + unsigned char flags; + status = read_hpack_int(&v, &flags, bucket, 7); + if (status) + continue; + + ctx->val_hm = (flags & 0x80) != 0; + + /* Just check compressed size first. If the result is + smaller the encoder shouldn't have used compression */ + if (HPACK_KEY_SIZE(v) >= ctx->header_allowed) + return SERF_ERROR_HTTP2_COMPRESSION_ERROR; + + ctx->val_size = v; + ctx->state = HPACK_DECODE_STATE_VALUE; + /* Fall through */ } - ctx->header_allowed -= HPACK_KEY_SIZE(ctx->key_size) - + HPACK_KEY_SIZE(ctx->val_size); - - status = handle_read_entry_and_clear(ctx, bucket->allocator); - if (status) - return status; - - /* Get key and value from table and handle result */ - ctx->state = HPACK_DECODE_STATE_INITIAL; - continue; - } - case HPACK_DECODE_STATE_KEYINDEX: - { - apr_uint32_t v; - status = read_hpack_int(&v, NULL, bucket, - ctx->index_item ? 6 : 4); - if (status) - continue; - - ctx->reuse_item = v; - status = hpack_table_get(v, ctx->tbl, - &ctx->key, &ctx->key_size, - NULL, NULL); - if (status) - return status; - - /* Get key from table */ - ctx->state = HPACK_DECODE_STATE_VALUE_LEN; - if (HPACK_KEY_SIZE(ctx->key_size) >= ctx->header_allowed) - return SERF_ERROR_HTTP2_COMPRESSION_ERROR; - - ctx->header_allowed -= HPACK_KEY_SIZE(ctx->key_size); - continue; - } - case HPACK_DECODE_STATE_KEY_LEN: - { - apr_uint32_t v; - unsigned char flags; - status = read_hpack_int(&v, &flags, bucket, 7); - if (status) - continue; - - ctx->key_hm = (flags & 0x80) != 0; - - /* Just check compressed size first. If the result is - smaller the encoder shouldn't have used compression */ - if (HPACK_KEY_SIZE(v) >= ctx->header_allowed) - return SERF_ERROR_HTTP2_COMPRESSION_ERROR; - - ctx->key_size = (apr_size_t)v; - ctx->state = HPACK_DECODE_STATE_KEY; - /* Fall through */ - } - case HPACK_DECODE_STATE_KEY: - { - const void *data; - - status = hpack_read_bytes(bucket, ctx->key_size, &data); - if (status) - continue; - - if (ctx->key_hm) + case HPACK_DECODE_STATE_VALUE: { - apr_size_t ks; - char *key; - - status = serf__hpack_huffman_decode(data, ctx->key_size, - 0, NULL, &ks); - - if (status) - return status; + const void *data; - if (HPACK_KEY_SIZE(ks) >= ctx->header_allowed) - return SERF_ERROR_HTTP2_COMPRESSION_ERROR; + status = hpack_read_bytes(bucket, ctx->val_size, &data); + if (status) + continue; + + if (ctx->val_hm) + { + apr_size_t ks; + char *val; + + status = serf__hpack_huffman_decode(data, + ctx->val_size, + 0, NULL, &ks); + if (status) + return status; + + if (HPACK_KEY_SIZE(ks) >= ctx->header_allowed) + return SERF_ERROR_HTTP2_COMPRESSION_ERROR; + + val = serf_bucket_mem_alloc(ctx->tbl->alloc, ks + 1); + + status = serf__hpack_huffman_decode(data, + ctx->val_size, + ks + 1, val, + &ctx->val_size); + if (status) + return status; + + ctx->val = val; + } + else + ctx->val = serf_bstrmemdup(ctx->tbl->alloc, data, + ctx->val_size); + + ctx->header_allowed -= HPACK_KEY_SIZE(ctx->val_size); + + status = handle_read_entry_and_clear(ctx, + bucket->allocator); + if (status) + continue; - key = serf_bucket_mem_alloc(ctx->tbl->alloc, ks + 1); - - status = serf__hpack_huffman_decode(data, ctx->key_size, - ks + 1, key, - &ctx->key_size); - if (status) - return status; - - ctx->key = key; + ctx->state = HPACK_DECODE_STATE_INITIAL; + continue; } - else - ctx->key = serf_bstrmemdup(ctx->tbl->alloc, data, - ctx->key_size); - - ctx->state = HPACK_DECODE_STATE_VALUE_LEN;
[... 270 lines stripped ...]