char s[200];
int len = lws_hdr_copy(wsi, s, sizeof(s) - 1, hdr);
s[len] = '\0';
- lwsl_info(" hdr tok %d '%s'\n", hdr, s);
+ lwsl_info(" hdr tok %d (%s) = '%s'\n", hdr, lws_token_to_string(hdr), s);
}
-static int lws_token_from_index(struct libwebsocket *wsi, int index)
+static int lws_token_from_index(struct libwebsocket *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 libwebsocket *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 = malloc(sizeof(*dyn));
+ if (!dyn)
+ return 1;
+ memset(dyn, 0, sizeof(*dyn));
+ wsi->u.http2.hpack_dyn_table = dyn;
+
+ dyn->args = malloc(1024);
+ if (!dyn->args)
+ goto bail1;
+ dyn->args_length = 1024;
+ dyn->entries = malloc(sizeof(dyn->entries[0]) * 20);
+ if (!dyn->entries)
+ goto bail2;
+ dyn->num_entries = 20;
+ }
- // dynamic indexes
+ 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:
+ free(dyn->args);
+bail1:
+ free(dyn);
+ wsi->u.http2.hpack_dyn_table = NULL;
+
+ return ret;
}
-static int lws_add_indexed_hdr(struct libwebsocket *wsi, int idx)
+static int lws_write_indexed_hdr(struct libwebsocket *wsi, int idx)
{
const char *p;
- int tok = lws_token_from_index(wsi, idx);
+ int tok = lws_token_from_index(wsi, idx, NULL, 0);
- lwsl_info("adding indexed hdr %d (tok %d)\n", idx, tok);
+ 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;
int n;
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 (c & 0x80) { /* indexed header field only */
/* just a possibly-extended integer */
wsi->u.http2.hpack = HPKS_IDX_EXT;
break;
}
- if (lws_add_indexed_hdr(wsi, c & 0x7f))
+ if (lws_write_indexed_hdr(wsi, c & 0x7f))
return 1;
/* stay at same state */
break;
if (!(c & 0x80)) {
switch (wsi->u.http2.hpack_type) {
case HPKT_INDEXED_HDR_7:
- if (lws_add_indexed_hdr(wsi, 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;
if (wsi->u.http2.value) {
if (lws_frag_start(wsi,
lws_token_from_index(wsi,
- wsi->u.http2.header_index)))
+ wsi->u.http2.header_index, NULL, NULL)))
return 1;
} else
wsi->u.hdr.parser_state = WSI_TOKEN_NAME_PART;
}
}
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;
- lws_dump_header(wsi, lws_token_from_index(wsi, wsi->u.http2.header_index));
-
- wsi->u.http2.hpack = HPKS_TYPE;
+ 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)
}
}
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;
return 0;
}
+struct libwebsocket *lws_http2_get_network_wsi(struct libwebsocket *wsi)
+{
+ while (wsi->u.http2.parent_wsi)
+ wsi = wsi->u.http2.parent_wsi;
+
+ return wsi;
+}
+
int lws_http2_frame_write(struct libwebsocket *wsi, int type, int flags, unsigned int sid, unsigned int len, unsigned char *buf)
{
- struct libwebsocket *wsi_eff = wsi;
+ struct libwebsocket *wsi_eff = lws_http2_get_network_wsi(wsi);
unsigned char *p = &buf[-LWS_HTTP2_FRAME_HEADER_LENGTH];
int n;
-
- while (wsi_eff->u.http2.parent_wsi)
- wsi_eff = wsi_eff->u.http2.parent_wsi;
*p++ = len >> 16;
*p++ = len >> 8;
lws_http2_parser(struct libwebsocket_context *context,
struct libwebsocket *wsi, unsigned char c)
{
+ struct libwebsocket *swsi;
int n;
//dstruct libwebsocket *wsi_new;
case WSI_STATE_HTTP2_ESTABLISHED_PRE_SETTINGS:
case WSI_STATE_HTTP2_ESTABLISHED:
if (wsi->u.http2.frame_state == LWS_HTTP2_FRAME_HEADER_LENGTH) { // payload
+ wsi->u.http2.count++;
+ wsi->u.http2.stream_wsi->u.http2.count = wsi->u.http2.count;
/* applies to wsi->u.http2.stream_wsi which may be wsi*/
switch(wsi->u.http2.type) {
case LWS_HTTP2_FRAME_TYPE_SETTINGS:
return 1;
break;
}
- wsi->u.http2.count++;
if (wsi->u.http2.count != wsi->u.http2.length)
break;
wsi->u.http2.stream_wsi = lws_http2_wsi_from_id(wsi, wsi->u.http2.stream_id);
if (!wsi->u.http2.stream_wsi)
wsi->u.http2.stream_wsi = lws_create_server_child_wsi(context, wsi, wsi->u.http2.stream_id);
-
- if (!wsi->u.http2.stream_wsi)
- return 1;
-
+
/* END_STREAM means after servicing this, close the stream */
wsi->u.http2.END_STREAM = !!(wsi->u.http2.flags & LWS_HTTP2_FLAG_END_STREAM);
+ lwsl_info("%s: headers END_STREAM = %d\n",__func__, wsi->u.http2.END_STREAM);
update_end_headers:
/* no END_HEADERS means CONTINUATION must come */
wsi->u.http2.END_HEADERS = !!(wsi->u.http2.flags & LWS_HTTP2_FLAG_END_HEADERS);
+
+ swsi = wsi->u.http2.stream_wsi;
+ if (!swsi)
+ return 1;
+
+
+ /* prepare the hpack parser at the right start */
+
+ swsi->u.http2.flags = wsi->u.http2.flags;
+ swsi->u.http2.length = wsi->u.http2.length;
+ swsi->u.http2.END_STREAM = wsi->u.http2.END_STREAM;
+
+ if (swsi->u.http2.flags & LWS_HTTP2_FLAG_PADDED)
+ swsi->u.http2.hpack = HPKS_OPT_PADDING;
+ else
+ if (swsi->u.http2.flags & LWS_HTTP2_FLAG_PRIORITY) {
+ swsi->u.http2.hpack = HKPS_OPT_E_DEPENDENCY;
+ swsi->u.http2.hpack_m = 4;
+ } else
+ swsi->u.http2.hpack = HPKS_TYPE;
+ lwsl_info("initial hpack state %d\n", swsi->u.http2.hpack);
break;
}
if (wsi->u.http2.length == 0)
LWS_WRITE_PING,
LWS_WRITE_PONG,
+ /* Same as write_http but we know this write ends the transaction */
+ LWS_WRITE_HTTP_FINAL,
+
/* HTTP2 */
LWS_WRITE_HTTP_HEADERS,
int length,
unsigned char **p,
unsigned char *end);
+LWS_VISIBLE LWS_EXTERN int lws_add_http_header_content_length(struct libwebsocket_context *context,
+ struct libwebsocket *wsi,
+ unsigned long content_length,
+ unsigned char **p,
+ unsigned char *end);
LWS_VISIBLE LWS_EXTERN int
lws_add_http_header_status(struct libwebsocket_context *context,
struct libwebsocket *wsi,
return 0;
}
- if (protocol == LWS_WRITE_HTTP || protocol == LWS_WRITE_HTTP_HEADERS)
+ if (protocol == LWS_WRITE_HTTP ||
+ protocol == LWS_WRITE_HTTP_FINAL ||
+ protocol == LWS_WRITE_HTTP_HEADERS)
goto send_raw;
/* websocket protocol, either binary or text */
case LWS_WRITE_CLOSE:
/* lwsl_hexdump(&buf[-pre], len + post); */
case LWS_WRITE_HTTP:
+ case LWS_WRITE_HTTP_FINAL:
case LWS_WRITE_HTTP_HEADERS:
case LWS_WRITE_PONG:
case LWS_WRITE_PING:
n = LWS_HTTP2_FRAME_TYPE_HEADERS;
flags = LWS_HTTP2_FLAG_END_HEADERS;
}
+
+ if ((protocol == LWS_WRITE_HTTP || protocol == LWS_WRITE_HTTP_FINAL) && wsi->u.http.content_length) {
+ wsi->u.http.content_remain -= len;
+ lwsl_info("%s: content_remain = %lu\n", __func__, wsi->u.http.content_remain);
+ if (!wsi->u.http.content_remain) {
+ lwsl_info("%s: selecting final write mode\n", __func__);
+ protocol = LWS_WRITE_HTTP_FINAL;
+ }
+ }
+
+ if (protocol == LWS_WRITE_HTTP_FINAL && wsi->u.http2.END_STREAM) {
+ lwsl_info("%s: setting END_STREAM\n", __func__);
+ flags |= LWS_HTTP2_FLAG_END_STREAM;
+ }
+
return lws_http2_frame_write(wsi, n, flags, wsi->u.http2.my_stream_id, len, buf);
}
#endif
if (n < 0)
return -1; /* caller will close */
if (n) {
+ wsi->u.http.filepos += n;
m = libwebsocket_write(wsi, context->service_buffer, n,
- LWS_WRITE_HTTP);
+ wsi->u.http.filepos == wsi->u.http.filelen ? LWS_WRITE_HTTP_FINAL : LWS_WRITE_HTTP);
if (m < 0)
return -1;
- wsi->u.http.filepos += m;
if (m != n)
/* adjust for what was not sent */
compatible_file_seek_cur(wsi->u.http.fd, m - n);
};
enum http2_hpack_state {
+
+ /* optional before first header block */
+ HPKS_OPT_PADDING,
+ HKPS_OPT_E_DEPENDENCY,
+ HKPS_OPT_WEIGHT,
+
+ /* header block */
HPKS_TYPE,
HPKS_IDX_EXT,
HPKS_HLEN_EXT,
HPKS_DATA,
+
+ /* optional after last header block */
+ HKPS_OPT_DISCARD_PADDING,
};
enum http2_hpack_type {
HPKT_SIZE_5
};
+struct hpack_dt_entry {
+ int token; /* additions that don't map to a token are ignored */
+ int arg_offset;
+ int arg_len;
+};
+
+struct hpack_dynamic_table {
+ struct hpack_dt_entry *entries;
+ char *args;
+ int pos;
+ int next;
+ int num_entries;
+ int args_length;
+};
+
struct _lws_http2_related {
/*
* having this first lets us also re-use all HTTP union code
struct libwebsocket *parent_wsi;
struct libwebsocket *next_child_wsi;
+ struct hpack_dynamic_table *hpack_dyn_table;
+
unsigned int count;
/* frame */
unsigned char type;
unsigned char flags;
unsigned char frame_state;
+ unsigned char padding;
unsigned int END_STREAM:1;
unsigned int END_HEADERS:1;
unsigned int hpack_len;
unsigned short hpack_pos;
unsigned char hpack_m;
+ unsigned int hpack_e_dep;
unsigned int huff:1;
unsigned int value:1;
enum libwebsocket_callback_reasons reason, void *user,
void *in, size_t len);
#ifdef LWS_USE_HTTP2
+LWS_EXTERN struct libwebsocket *lws_http2_get_network_wsi(struct libwebsocket *wsi);
LWS_EXTERN int
lws_http2_interpret_settings_payload(struct http2_settings *settings, unsigned char *buf, int len);
LWS_EXTERN void lws_http2_init(struct http2_settings *settings);
return lws_add_http_header_by_name(context, wsi, name, value, length, p, end);
}
+int lws_add_http_header_content_length(struct libwebsocket_context *context,
+ struct libwebsocket *wsi,
+ unsigned long content_length,
+ unsigned char **p,
+ unsigned char *end)
+{
+ char b[24];
+ int n;
+
+ n = sprintf(b, "%lu", content_length);
+ if (lws_add_http_header_by_token(context, wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH, (unsigned char *)b, n, p, end))
+ return 1;
+ wsi->u.http.content_length = content_length;
+ wsi->u.http.content_remain = content_length;
+
+ return 0;
+}
+
static const char *err400[] = {
"Bad Request",
"Unauthorized",
unsigned char *p = response;
unsigned char *end = p + sizeof(context->service_buffer) -
LWS_SEND_BUFFER_PRE_PADDING;
- unsigned char clen[10];
int ret = 0;
- int n;
wsi->u.http.fd = lws_plat_open_file(file, &wsi->u.http.filelen);
return -1;
if (lws_add_http_header_by_token(context, wsi, WSI_TOKEN_HTTP_CONTENT_TYPE, (unsigned char *)content_type, strlen(content_type), &p, end))
return -1;
- n = sprintf((char *)clen, "%lu", (unsigned long)wsi->u.http.filelen);
- if (lws_add_http_header_by_token(context, wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH, clen, n, &p, end))
+ if (lws_add_http_header_content_length(context, wsi, wsi->u.http.filelen, &p, end))
return -1;
if (other_headers) {
return 1;
if (lws_add_http_header_by_token(context, wsi, WSI_TOKEN_HTTP_CONTENT_TYPE, (unsigned char *)"image/jpeg", 10, &p, end))
return 1;
- n = sprintf(b64, "%u", (unsigned int)stat_buf.st_size);
- if (lws_add_http_header_by_token(context, wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH, (unsigned char *)b64, n, &p, end))
+ if (lws_add_http_header_content_length(context, wsi,stat_buf.st_size, &p, end))
return 1;
if (lws_finalize_http_header(context, wsi, &p, end))
return 1;
/* sent it all, close conn */
if (n == 0)
goto flush_bail;
+
/*
* To support HTTP2, must take care about preamble space
+ * and identify when we send the last frame
*/
m = libwebsocket_write(wsi,
buffer + LWS_SEND_BUFFER_PRE_PADDING,
if (m < 0)
/* write failed, close conn */
goto bail;
+ /*
+ * http2 won't do this
+ */
if (m != n)
/* partial write, adjust */
lseek(pss->fd, m - n, SEEK_CUR);
break;
} while (!lws_send_pipe_choked(wsi));
+
libwebsocket_callback_on_writable(context, wsi);
break;
flush_bail: