123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704 |
- /*
- * lib/hpack.c
- *
- * Copyright (C) 2014 Andy Green <andy@warmcat.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation:
- * version 2.1 of the License.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301 USA
- */
- #include "private-libwebsockets.h"
- /*
- * Official static header table for HPACK
- * +-------+-----------------------------+---------------+
- | 1 | :authority | |
- | 2 | :method | GET |
- | 3 | :method | POST |
- | 4 | :path | / |
- | 5 | :path | /index.html |
- | 6 | :scheme | http |
- | 7 | :scheme | https |
- | 8 | :status | 200 |
- | 9 | :status | 204 |
- | 10 | :status | 206 |
- | 11 | :status | 304 |
- | 12 | :status | 400 |
- | 13 | :status | 404 |
- | 14 | :status | 500 |
- | 15 | accept-charset | |
- | 16 | accept-encoding | gzip, deflate |
- | 17 | accept-language | |
- | 18 | accept-ranges | |
- | 19 | accept | |
- | 20 | access-control-allow-origin | |
- | 21 | age | |
- | 22 | allow | |
- | 23 | authorization | |
- | 24 | cache-control | |
- | 25 | content-disposition | |
- | 26 | content-encoding | |
- | 27 | content-language | |
- | 28 | content-length | |
- | 29 | content-location | |
- | 30 | content-range | |
- | 31 | content-type | |
- | 32 | cookie | |
- | 33 | date | |
- | 34 | etag | |
- | 35 | expect | |
- | 36 | expires | |
- | 37 | from | |
- | 38 | host | |
- | 39 | if-match | |
- | 40 | if-modified-since | |
- | 41 | if-none-match | |
- | 42 | if-range | |
- | 43 | if-unmodified-since | |
- | 44 | last-modified | |
- | 45 | link | |
- | 46 | location | |
- | 47 | max-forwards | |
- | 48 | proxy-authenticate | |
- | 49 | proxy-authorization | |
- | 50 | range | |
- | 51 | referer | |
- | 52 | refresh | |
- | 53 | retry-after | |
- | 54 | server | |
- | 55 | set-cookie | |
- | 56 | strict-transport-security | |
- | 57 | transfer-encoding | |
- | 58 | user-agent | |
- | 59 | vary | |
- | 60 | via | |
- | 61 | www-authenticate | |
- +-------+-----------------------------+---------------+
- */
- static const unsigned char static_token[] = {
- 0,
- WSI_TOKEN_HTTP_COLON_AUTHORITY,
- WSI_TOKEN_HTTP_COLON_METHOD,
- WSI_TOKEN_HTTP_COLON_METHOD,
- WSI_TOKEN_HTTP_COLON_PATH,
- WSI_TOKEN_HTTP_COLON_PATH,
- WSI_TOKEN_HTTP_COLON_SCHEME,
- WSI_TOKEN_HTTP_COLON_SCHEME,
- WSI_TOKEN_HTTP_COLON_STATUS,
- WSI_TOKEN_HTTP_COLON_STATUS,
- WSI_TOKEN_HTTP_COLON_STATUS,
- WSI_TOKEN_HTTP_COLON_STATUS,
- WSI_TOKEN_HTTP_COLON_STATUS,
- WSI_TOKEN_HTTP_COLON_STATUS,
- WSI_TOKEN_HTTP_COLON_STATUS,
- WSI_TOKEN_HTTP_ACCEPT_CHARSET,
- WSI_TOKEN_HTTP_ACCEPT_ENCODING,
- WSI_TOKEN_HTTP_ACCEPT_LANGUAGE,
- WSI_TOKEN_HTTP_ACCEPT_RANGES,
- WSI_TOKEN_HTTP_ACCEPT,
- WSI_TOKEN_HTTP_ACCESS_CONTROL_ALLOW_ORIGIN,
- WSI_TOKEN_HTTP_AGE,
- WSI_TOKEN_HTTP_ALLOW,
- WSI_TOKEN_HTTP_AUTHORIZATION,
- WSI_TOKEN_HTTP_CACHE_CONTROL,
- WSI_TOKEN_HTTP_CONTENT_DISPOSITION,
- WSI_TOKEN_HTTP_CONTENT_ENCODING,
- WSI_TOKEN_HTTP_CONTENT_LANGUAGE,
- WSI_TOKEN_HTTP_CONTENT_LENGTH,
- WSI_TOKEN_HTTP_CONTENT_LOCATION,
- WSI_TOKEN_HTTP_CONTENT_RANGE,
- WSI_TOKEN_HTTP_CONTENT_TYPE,
- WSI_TOKEN_HTTP_COOKIE,
- WSI_TOKEN_HTTP_DATE,
- WSI_TOKEN_HTTP_ETAG,
- WSI_TOKEN_HTTP_EXPECT,
- WSI_TOKEN_HTTP_EXPIRES,
- WSI_TOKEN_HTTP_FROM,
- WSI_TOKEN_HOST,
- WSI_TOKEN_HTTP_IF_MATCH,
- WSI_TOKEN_HTTP_IF_MODIFIED_SINCE,
- WSI_TOKEN_HTTP_IF_NONE_MATCH,
- WSI_TOKEN_HTTP_IF_RANGE,
- WSI_TOKEN_HTTP_IF_UNMODIFIED_SINCE,
- WSI_TOKEN_HTTP_LAST_MODIFIED,
- WSI_TOKEN_HTTP_LINK,
- WSI_TOKEN_HTTP_LOCATION,
- WSI_TOKEN_HTTP_MAX_FORWARDS,
- WSI_TOKEN_HTTP_PROXY_AUTHENTICATE,
- WSI_TOKEN_HTTP_PROXY_AUTHORIZATION,
- WSI_TOKEN_HTTP_RANGE,
- WSI_TOKEN_HTTP_REFERER,
- WSI_TOKEN_HTTP_REFRESH,
- WSI_TOKEN_HTTP_RETRY_AFTER,
- WSI_TOKEN_HTTP_SERVER,
- WSI_TOKEN_HTTP_SET_COOKIE,
- WSI_TOKEN_HTTP_STRICT_TRANSPORT_SECURITY,
- WSI_TOKEN_HTTP_TRANSFER_ENCODING,
- WSI_TOKEN_HTTP_USER_AGENT,
- WSI_TOKEN_HTTP_VARY,
- WSI_TOKEN_HTTP_VIA,
- WSI_TOKEN_HTTP_WWW_AUTHENTICATE,
- };
- /* some of the entries imply values as well as header names */
- static const char * const http2_canned[] = {
- "",
- "",
- "GET",
- "POST",
- "/",
- "/index.html",
- "http",
- "https",
- "200",
- "204",
- "206",
- "304",
- "400",
- "404",
- "500",
- "",
- "gzip, deflate"
- };
- /* see minihuf.c */
- #include "huftable.h"
- static int huftable_decode(int pos, char c)
- {
- int q = pos + !!c;
- if (lextable_terms[q >> 3] & (1 << (q & 7))) /* terminal */
- return lextable[q] | 0x8000;
- return pos + (lextable[q] << 1);
- }
- static int lws_hpack_update_table_size(struct lws *wsi, int idx)
- {
- lwsl_info("hpack set table size %d\n", idx);
- return 0;
- }
- static int lws_frag_start(struct lws *wsi, int hdr_token_idx)
- {
- struct allocated_headers * ah = wsi->u.http2.http.ah;
- if (!hdr_token_idx) {
- lwsl_err("%s: zero hdr_token_idx\n", __func__);
- return 1;
- }
- if (ah->nfrag >= ARRAY_SIZE(ah->frag_index)) {
- lwsl_err("%s: frag index %d too big\n", __func__, ah->nfrag);
- return 1;
- }
- ah->frags[ah->nfrag].offset = ah->pos;
- ah->frags[ah->nfrag].len = 0;
- ah->frags[ah->nfrag].nfrag = 0;
- ah->frag_index[hdr_token_idx] = ah->nfrag;
- return 0;
- }
- static int lws_frag_append(struct lws *wsi, unsigned char c)
- {
- struct allocated_headers * ah = wsi->u.http2.http.ah;
- ah->data[ah->pos++] = c;
- ah->frags[ah->nfrag].len++;
- return ah->pos >= wsi->context->max_http_header_data;
- }
- static int lws_frag_end(struct lws *wsi)
- {
- if (lws_frag_append(wsi, 0))
- return 1;
- wsi->u.http2.http.ah->nfrag++;
- return 0;
- }
- static void lws_dump_header(struct lws *wsi, int hdr)
- {
- char s[200];
- int len = lws_hdr_copy(wsi, s, sizeof(s) - 1, hdr);
- s[len] = '\0';
- lwsl_info(" hdr tok %d (%s) = '%s'\n", hdr, lws_token_to_string(hdr), s);
- }
- static int
- lws_token_from_index(struct lws *wsi, int index, char **arg, int *len)
- {
- struct hpack_dynamic_table *dyn;
- /* dynamic table only belongs to network wsi */
- wsi = lws_http2_get_network_wsi(wsi);
- dyn = wsi->u.http2.hpack_dyn_table;
- if (index < ARRAY_SIZE(static_token))
- return static_token[index];
- if (!dyn)
- return 0;
- index -= ARRAY_SIZE(static_token);
- if (index >= dyn->num_entries)
- return 0;
- if (arg && len) {
- *arg = dyn->args + dyn->entries[index].arg_offset;
- *len = dyn->entries[index].arg_len;
- }
- return dyn->entries[index].token;
- }
- static int
- lws_hpack_add_dynamic_header(struct lws *wsi, int token, char *arg, int len)
- {
- struct hpack_dynamic_table *dyn;
- int ret = 1;
- wsi = lws_http2_get_network_wsi(wsi);
- dyn = wsi->u.http2.hpack_dyn_table;
- if (!dyn) {
- dyn = lws_zalloc(sizeof(*dyn));
- if (!dyn)
- return 1;
- wsi->u.http2.hpack_dyn_table = dyn;
- dyn->args = lws_malloc(1024);
- if (!dyn->args)
- goto bail1;
- dyn->args_length = 1024;
- dyn->entries = lws_malloc(sizeof(dyn->entries[0]) * 20);
- if (!dyn->entries)
- goto bail2;
- dyn->num_entries = 20;
- }
- if (dyn->next == dyn->num_entries)
- return 1;
- if (dyn->args_length - dyn->pos < len)
- return 1;
- dyn->entries[dyn->next].token = token;
- dyn->entries[dyn->next].arg_offset = dyn->pos;
- if (len)
- memcpy(dyn->args + dyn->pos, arg, len);
- dyn->entries[dyn->next].arg_len = len;
- lwsl_info("%s: added dynamic hdr %d, token %d (%s), len %d\n",
- __func__, dyn->next, token, lws_token_to_string(token), len);
- dyn->pos += len;
- dyn->next++;
- return 0;
- bail2:
- lws_free(dyn->args);
- bail1:
- lws_free(dyn);
- wsi->u.http2.hpack_dyn_table = NULL;
- return ret;
- }
- static int lws_write_indexed_hdr(struct lws *wsi, int idx)
- {
- const char *p;
- int tok = lws_token_from_index(wsi, idx, NULL, 0);
- lwsl_info("writing indexed hdr %d (tok %d '%s')\n", idx, tok,
- lws_token_to_string(tok));
- if (lws_frag_start(wsi, tok))
- return 1;
- if (idx < ARRAY_SIZE(http2_canned)) {
- p = http2_canned[idx];
- while (*p)
- if (lws_frag_append(wsi, *p++))
- return 1;
- }
- if (lws_frag_end(wsi))
- return 1;
- lws_dump_header(wsi, tok);
- return 0;
- }
- int lws_hpack_interpret(struct lws *wsi, unsigned char c)
- {
- unsigned int prev;
- unsigned char c1;
- int n;
- lwsl_debug(" state %d\n", wsi->u.http2.hpack);
- switch (wsi->u.http2.hpack) {
- case HPKS_OPT_PADDING:
- wsi->u.http2.padding = c;
- lwsl_info("padding %d\n", c);
- if (wsi->u.http2.flags & LWS_HTTP2_FLAG_PRIORITY) {
- wsi->u.http2.hpack = HKPS_OPT_E_DEPENDENCY;
- wsi->u.http2.hpack_m = 4;
- } else
- wsi->u.http2.hpack = HPKS_TYPE;
- break;
- case HKPS_OPT_E_DEPENDENCY:
- wsi->u.http2.hpack_e_dep <<= 8;
- wsi->u.http2.hpack_e_dep |= c;
- if (! --wsi->u.http2.hpack_m) {
- lwsl_info("hpack_e_dep = 0x%x\n", wsi->u.http2.hpack_e_dep);
- wsi->u.http2.hpack = HKPS_OPT_WEIGHT;
- }
- break;
- case HKPS_OPT_WEIGHT:
- /* weight */
- wsi->u.http2.hpack = HPKS_TYPE;
- break;
- case HPKS_TYPE:
- if (wsi->u.http2.count > (wsi->u.http2.length - wsi->u.http2.padding)) {
- lwsl_info("padding eat\n");
- break;
- }
- if (c & 0x80) { /* indexed header field only */
- /* just a possibly-extended integer */
- wsi->u.http2.hpack_type = HPKT_INDEXED_HDR_7;
- lwsl_debug("HKPS_TYPE setting header_index %d\n", c & 0x7f);
- wsi->u.http2.header_index = c & 0x7f;
- if ((c & 0x7f) == 0x7f) {
- wsi->u.http2.hpack_len = c & 0x7f;
- wsi->u.http2.hpack_m = 0;
- wsi->u.http2.hpack = HPKS_IDX_EXT;
- break;
- }
- lwsl_debug("HKPS_TYPE: %d\n", c & 0x7f);
- if (lws_write_indexed_hdr(wsi, c & 0x7f))
- return 1;
- /* stay at same state */
- break;
- }
- if (c & 0x40) { /* literal header incr idx */
- /*
- * [possibly-extended hdr idx (6) | new literal hdr name]
- * H + possibly-extended value length
- * literal value
- */
- lwsl_debug("HKPS_TYPE 2 setting header_index %d\n", 0);
- wsi->u.http2.header_index = 0;
- if (c == 0x40) { /* literal name */
- wsi->u.http2.hpack_type = HPKT_LITERAL_HDR_VALUE_INCR;
- wsi->u.http2.value = 0;
- wsi->u.http2.hpack = HPKS_HLEN;
- break;
- }
- /* indexed name */
- wsi->u.http2.hpack_type = HPKT_INDEXED_HDR_6_VALUE_INCR;
- if ((c & 0x3f) == 0x3f) {
- wsi->u.http2.hpack_len = c & 0x3f;
- wsi->u.http2.hpack_m = 0;
- wsi->u.http2.hpack = HPKS_IDX_EXT;
- break;
- }
- lwsl_debug("HKPS_TYPE 3 setting header_index %d\n", c & 0x3f);
- wsi->u.http2.header_index = c & 0x3f;
- wsi->u.http2.value = 1;
- wsi->u.http2.hpack = HPKS_HLEN;
- break;
- }
- switch(c & 0xf0) {
- case 0x10: /* literal header never index */
- case 0: /* literal header without indexing */
- /*
- * follows 0x40 except 4-bit hdr idx
- * and don't add to index
- */
- if (c == 0) { /* literal name */
- wsi->u.http2.hpack_type = HPKT_LITERAL_HDR_VALUE;
- wsi->u.http2.hpack = HPKS_HLEN;
- wsi->u.http2.value = 0;
- break;
- }
- //lwsl_debug("indexed\n");
- /* indexed name */
- wsi->u.http2.hpack_type = HPKT_INDEXED_HDR_4_VALUE;
- wsi->u.http2.header_index = 0;
- if ((c & 0xf) == 0xf) {
- wsi->u.http2.hpack_len = c & 0xf;
- wsi->u.http2.hpack_m = 0;
- wsi->u.http2.hpack = HPKS_IDX_EXT;
- break;
- }
- //lwsl_err("HKPS_TYPE 5 setting header_index %d\n", c & 0xf);
- wsi->u.http2.header_index = c & 0xf;
- wsi->u.http2.value = 1;
- wsi->u.http2.hpack = HPKS_HLEN;
- break;
- case 0x20:
- case 0x30: /* header table size update */
- /* possibly-extended size value (5) */
- wsi->u.http2.hpack_type = HPKT_SIZE_5;
- if ((c & 0x1f) == 0x1f) {
- wsi->u.http2.hpack_len = c & 0x1f;
- wsi->u.http2.hpack_m = 0;
- wsi->u.http2.hpack = HPKS_IDX_EXT;
- break;
- }
- lws_hpack_update_table_size(wsi, c & 0x1f);
- /* stay at HPKS_TYPE state */
- break;
- }
- break;
- case HPKS_IDX_EXT:
- wsi->u.http2.hpack_len += (c & 0x7f) << wsi->u.http2.hpack_m;
- wsi->u.http2.hpack_m += 7;
- if (!(c & 0x80)) {
- switch (wsi->u.http2.hpack_type) {
- case HPKT_INDEXED_HDR_7:
- //lwsl_err("HKPS_IDX_EXT hdr idx %d\n", wsi->u.http2.hpack_len);
- if (lws_write_indexed_hdr(wsi, wsi->u.http2.hpack_len))
- return 1;
- wsi->u.http2.hpack = HPKS_TYPE;
- break;
- default:
- // lwsl_err("HKPS_IDX_EXT setting header_index %d\n",
- // wsi->u.http2.hpack_len);
- wsi->u.http2.header_index = wsi->u.http2.hpack_len;
- wsi->u.http2.value = 1;
- wsi->u.http2.hpack = HPKS_HLEN;
- break;
- }
- }
- break;
- case HPKS_HLEN: /* [ H | 7+ ] */
- wsi->u.http2.huff = !!(c & 0x80);
- wsi->u.http2.hpack_pos = 0;
- wsi->u.http2.hpack_len = c & 0x7f;
- if (wsi->u.http2.hpack_len < 0x7f) {
- pre_data:
- if (wsi->u.http2.value) {
- if (wsi->u.http2.header_index)
- if (lws_frag_start(wsi, lws_token_from_index(wsi,
- wsi->u.http2.header_index,
- NULL, NULL))) {
- // lwsl_notice("%s: hlen failed\n", __func__);
- return 1;
- }
- } else
- wsi->u.hdr.parser_state = WSI_TOKEN_NAME_PART;
- wsi->u.http2.hpack = HPKS_DATA;
- break;
- }
- wsi->u.http2.hpack_m = 0;
- wsi->u.http2.hpack = HPKS_HLEN_EXT;
- break;
- case HPKS_HLEN_EXT:
- wsi->u.http2.hpack_len += (c & 0x7f) <<
- wsi->u.http2.hpack_m;
- wsi->u.http2.hpack_m += 7;
- if (!(c & 0x80))
- goto pre_data;
- break;
- case HPKS_DATA:
- for (n = 0; n < 8; n++) {
- if (wsi->u.http2.huff) {
- prev = wsi->u.http2.hpack_pos;
- wsi->u.http2.hpack_pos = huftable_decode(
- wsi->u.http2.hpack_pos,
- (c >> 7) & 1);
- c <<= 1;
- if (wsi->u.http2.hpack_pos == 0xffff)
- return 1;
- if (!(wsi->u.http2.hpack_pos & 0x8000))
- continue;
- c1 = wsi->u.http2.hpack_pos & 0x7fff;
- wsi->u.http2.hpack_pos = 0;
- if (!c1 && prev == HUFTABLE_0x100_PREV)
- ; /* EOT */
- } else {
- n = 8;
- c1 = c;
- }
- if (wsi->u.http2.value) { /* value */
- if (wsi->u.http2.header_index)
- if (lws_frag_append(wsi, c1))
- return 1;
- } else { /* name */
- if (lws_parse(wsi, c1))
- return 1;
- }
- }
- if (--wsi->u.http2.hpack_len == 0) {
- switch (wsi->u.http2.hpack_type) {
- case HPKT_LITERAL_HDR_VALUE_INCR:
- case HPKT_INDEXED_HDR_6_VALUE_INCR: // !!!
- if (lws_hpack_add_dynamic_header(wsi,
- lws_token_from_index(wsi,
- wsi->u.http2.header_index,
- NULL, NULL), NULL, 0))
- return 1;
- break;
- default:
- break;
- }
- n = 8;
- if (wsi->u.http2.value) {
- if (lws_frag_end(wsi))
- return 1;
- // lwsl_err("data\n");
- lws_dump_header(wsi, lws_token_from_index(
- wsi, wsi->u.http2.header_index,
- NULL, NULL));
- if (wsi->u.http2.count + wsi->u.http2.padding ==
- wsi->u.http2.length)
- wsi->u.http2.hpack = HKPS_OPT_DISCARD_PADDING;
- else
- wsi->u.http2.hpack = HPKS_TYPE;
- } else { /* name */
- //if (wsi->u.hdr.parser_state < WSI_TOKEN_COUNT)
- wsi->u.http2.value = 1;
- wsi->u.http2.hpack = HPKS_HLEN;
- }
- }
- break;
- case HKPS_OPT_DISCARD_PADDING:
- lwsl_info("eating padding %x\n", c);
- if (! --wsi->u.http2.padding)
- wsi->u.http2.hpack = HPKS_TYPE;
- break;
- }
- return 0;
- }
- static int lws_http2_num(int starting_bits, unsigned long num,
- unsigned char **p, unsigned char *end)
- {
- int mask = (1 << starting_bits) - 1;
- if (num < mask) {
- *((*p)++) |= num;
- return *p >= end;
- }
- *((*p)++) |= mask;
- if (*p >= end)
- return 1;
- num -= mask;
- while (num >= 128) {
- *((*p)++) = 0x80 | (num & 0x7f);
- if (*p >= end)
- return 1;
- num >>= 7;
- }
- return 0;
- }
- int lws_add_http2_header_by_name(struct lws *wsi,
- const unsigned char *name,
- const unsigned char *value, int length,
- unsigned char **p, unsigned char *end)
- {
- int len;
- lwsl_info("%s: %p %s:%s\n", __func__, *p, name, value);
- len = strlen((char *)name);
- if (len)
- if (name[len - 1] == ':')
- len--;
- if (end - *p < len + length + 8)
- return 1;
- *((*p)++) = 0; /* not indexed, literal name */
- **p = 0; /* non-HUF */
- if (lws_http2_num(7, len, p, end))
- return 1;
- memcpy(*p, name, len);
- *p += len;
- *(*p) = 0; /* non-HUF */
- if (lws_http2_num(7, length, p, end))
- return 1;
- memcpy(*p, value, length);
- *p += length;
- return 0;
- }
- int lws_add_http2_header_by_token(struct lws *wsi, enum lws_token_indexes token,
- const unsigned char *value, int length,
- unsigned char **p, unsigned char *end)
- {
- const unsigned char *name;
- name = lws_token_to_string(token);
- if (!name)
- return 1;
- return lws_add_http2_header_by_name(wsi, name, value, length, p, end);
- }
- int lws_add_http2_header_status(struct lws *wsi,
- unsigned int code, unsigned char **p,
- unsigned char *end)
- {
- unsigned char status[10];
- int n;
- wsi->u.http2.send_END_STREAM = !!(code >= 400);
- n = sprintf((char *)status, "%u", code);
- if (lws_add_http2_header_by_token(wsi, WSI_TOKEN_HTTP_COLON_STATUS,
- status, n, p, end))
- return 1;
- return 0;
- }
|