| /* |
| * libwebsockets - small server side websockets and web server implementation |
| * |
| * Copyright (C) 2010-2013 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" |
| |
| const struct http2_settings lws_http2_default_settings = { { |
| 0, |
| /* LWS_HTTP2_SETTINGS__HEADER_TABLE_SIZE */ 4096, |
| /* LWS_HTTP2_SETTINGS__ENABLE_PUSH */ 1, |
| /* LWS_HTTP2_SETTINGS__MAX_CONCURRENT_STREAMS */ 100, |
| /* LWS_HTTP2_SETTINGS__INITIAL_WINDOW_SIZE */ 65535, |
| /* LWS_HTTP2_SETTINGS__MAX_FRAME_SIZE */ 16384, |
| /* LWS_HTTP2_SETTINGS__MAX_HEADER_LIST_SIZE */ ~0, |
| }}; |
| |
| |
| void lws_http2_init(struct http2_settings *settings) |
| { |
| memcpy(settings, lws_http2_default_settings.setting, sizeof(*settings)); |
| } |
| |
| struct libwebsocket * |
| lws_http2_wsi_from_id(struct libwebsocket *wsi, unsigned int sid) |
| { |
| do { |
| if (wsi->u.http2.my_stream_id == sid) |
| return wsi; |
| |
| wsi = wsi->u.http2.next_child_wsi; |
| } while (wsi); |
| |
| return NULL; |
| } |
| |
| struct libwebsocket * |
| lws_create_server_child_wsi(struct libwebsocket_context *context, struct libwebsocket *parent_wsi, unsigned int sid) |
| { |
| struct libwebsocket *wsi = libwebsocket_create_new_server_wsi(context); |
| |
| if (!wsi) |
| return NULL; |
| |
| /* no more children allowed by parent */ |
| if (parent_wsi->u.http2.child_count + 1 == parent_wsi->u.http2.peer_settings.setting[LWS_HTTP2_SETTINGS__MAX_CONCURRENT_STREAMS]) |
| return NULL; |
| |
| lws_http2_init(&wsi->u.http2.peer_settings); |
| lws_http2_init(&wsi->u.http2.my_settings); |
| wsi->u.http2.stream_id = sid; |
| |
| wsi->u.http2.parent_wsi = parent_wsi; |
| wsi->u.http2.next_child_wsi = parent_wsi->u.http2.next_child_wsi; |
| parent_wsi->u.http2.next_child_wsi = wsi; |
| parent_wsi->u.http2.child_count++; |
| |
| wsi->u.http2.my_priority = 16; |
| |
| wsi->state = WSI_STATE_HTTP2_ESTABLISHED; |
| wsi->mode = parent_wsi->mode; |
| |
| lwsl_info("%s: %p new child %p, sid %d\n", __func__, parent_wsi, wsi, sid); |
| |
| return wsi; |
| } |
| |
| int lws_remove_server_child_wsi(struct libwebsocket_context *context, struct libwebsocket *wsi) |
| { |
| struct libwebsocket **w = &wsi->u.http2.parent_wsi; |
| do { |
| if (*w == wsi) { |
| *w = wsi->u.http2.next_child_wsi; |
| (wsi->u.http2.parent_wsi)->u.http2.child_count--; |
| return 0; |
| } |
| |
| w = &((*w)->u.http2.next_child_wsi); |
| } while (*w); |
| |
| lwsl_err("%s: can't find %p\n", __func__, wsi); |
| return 1; |
| } |
| |
| int |
| lws_http2_interpret_settings_payload(struct http2_settings *settings, unsigned char *buf, int len) |
| { |
| unsigned int a, b; |
| |
| if (!len) |
| return 0; |
| |
| if (len < LWS_HTTP2_SETTINGS_LENGTH) |
| return 1; |
| |
| while (len >= LWS_HTTP2_SETTINGS_LENGTH) { |
| a = (buf[0] << 8) | buf[1]; |
| if (a < LWS_HTTP2_SETTINGS__COUNT) { |
| b = buf[2] << 24 | buf[3] << 16 | buf[4] << 8 | buf[5]; |
| settings->setting[a] = b; |
| lwsl_info("http2 settings %d <- 0x%x\n", a, b); |
| } |
| len -= LWS_HTTP2_SETTINGS_LENGTH; |
| buf += LWS_HTTP2_SETTINGS_LENGTH; |
| } |
| |
| if (len) |
| return 1; |
| |
| return 0; |
| } |
| |
| int lws_http2_frame_write(struct libwebsocket *wsi, int type, int flags, unsigned int sid, unsigned int len, unsigned char *buf) |
| { |
| unsigned char *p = &buf[-LWS_HTTP2_FRAME_HEADER_LENGTH]; |
| int n; |
| |
| *p++ = len >> 16; |
| *p++ = len >> 8; |
| *p++ = len; |
| *p++ = type; |
| *p++ = flags; |
| *p++ = sid >> 24; |
| *p++ = sid >> 16; |
| *p++ = sid >> 8; |
| *p++ = sid; |
| |
| lwsl_info("%s: %p. type %d, flags 0x%x, sid=%d, len=%d\n", |
| __func__, wsi, type, flags, sid, len); |
| |
| n = lws_issue_raw(wsi, &buf[-LWS_HTTP2_FRAME_HEADER_LENGTH], len + LWS_HTTP2_FRAME_HEADER_LENGTH); |
| if (n >= LWS_HTTP2_FRAME_HEADER_LENGTH) |
| return n - LWS_HTTP2_FRAME_HEADER_LENGTH; |
| |
| return n; |
| } |
| |
| static void lws_http2_settings_write(struct libwebsocket *wsi, int n, unsigned char *buf) |
| { |
| *buf++ = n >> 8; |
| *buf++ = n; |
| *buf++ = wsi->u.http2.my_settings.setting[n] >> 24; |
| *buf++ = wsi->u.http2.my_settings.setting[n] >> 16; |
| *buf++ = wsi->u.http2.my_settings.setting[n] >> 8; |
| *buf = wsi->u.http2.my_settings.setting[n]; |
| } |
| |
| static const char const * https_client_preface = |
| "PRI * HTTP/2.0\x0d\x0a\x0d\x0aSM\x0d\x0a\x0d\x0a"; |
| |
| int |
| lws_http2_parser(struct libwebsocket_context *context, |
| struct libwebsocket *wsi, unsigned char c) |
| { |
| struct libwebsocket *wsi_new; |
| |
| switch (wsi->state) { |
| case WSI_STATE_HTTP2_AWAIT_CLIENT_PREFACE: |
| if (https_client_preface[wsi->u.http2.count++] != c) |
| return 1; |
| |
| if (!https_client_preface[wsi->u.http2.count]) { |
| lwsl_err("http2: %p: established\n", wsi); |
| wsi->state = WSI_STATE_HTTP2_ESTABLISHED_PRE_SETTINGS; |
| wsi->u.http2.count = 0; |
| |
| /* |
| * we must send a settings frame -- empty one is OK... |
| * that must be the first thing sent by server |
| * and the peer must send a SETTINGS with ACK flag... |
| */ |
| |
| lws_set_protocol_write_pending(context, wsi, LWS_PPS_HTTP2_MY_SETTINGS); |
| } |
| break; |
| |
| case WSI_STATE_HTTP2_ESTABLISHED_PRE_SETTINGS: |
| case WSI_STATE_HTTP2_ESTABLISHED: |
| if (wsi->u.http2.frame_state == LWS_HTTP2_FRAME_HEADER_LENGTH) { // payload |
| /* applies to wsi->u.http2.stream_wsi which may be wsi*/ |
| switch(wsi->u.http2.type) { |
| case LWS_HTTP2_FRAME_TYPE_SETTINGS: |
| wsi->u.http2.stream_wsi->u.http2.one_setting[wsi->u.http2.count % LWS_HTTP2_SETTINGS_LENGTH] = c; |
| if (wsi->u.http2.count % LWS_HTTP2_SETTINGS_LENGTH == LWS_HTTP2_SETTINGS_LENGTH - 1) |
| if (lws_http2_interpret_settings_payload( |
| &wsi->u.http2.stream_wsi->u.http2.peer_settings, |
| wsi->u.http2.one_setting, |
| LWS_HTTP2_SETTINGS_LENGTH)) |
| return 1; |
| break; |
| case LWS_HTTP2_FRAME_TYPE_HEADERS: |
| |
| break; |
| } |
| wsi->u.http2.count++; |
| if (wsi->u.http2.count == wsi->u.http2.length) { |
| wsi->u.http2.frame_state = 0; |
| wsi->u.http2.count = 0; |
| /* set our initial window size */ |
| if (!wsi->u.http2.initialized) { |
| wsi->u.http2.tx_credit = wsi->u.http2.peer_settings.setting[LWS_HTTP2_SETTINGS__INITIAL_WINDOW_SIZE]; |
| lwsl_info("initial tx credit on master conn %p: %d\n", wsi, wsi->u.http2.tx_credit); |
| wsi->u.http2.initialized = 1; |
| } |
| } |
| break; |
| } |
| switch (wsi->u.http2.frame_state++) { |
| case 0: |
| wsi->u.http2.length = c; |
| break; |
| case 1: |
| case 2: |
| wsi->u.http2.length <<= 8; |
| wsi->u.http2.length |= c; |
| break; |
| case 3: |
| wsi->u.http2.type = c; |
| break; |
| case 4: |
| wsi->u.http2.flags = c; |
| break; |
| case 5: |
| case 6: |
| case 7: |
| case 8: |
| wsi->u.http2.stream_id <<= 8; |
| wsi->u.http2.stream_id |= c; |
| wsi->u.http2.stream_wsi = wsi; |
| break; |
| } |
| if (wsi->u.http2.frame_state == LWS_HTTP2_FRAME_HEADER_LENGTH) { /* frame header complete */ |
| lwsl_info("frame: type 0x%x, flags 0x%x, sid 0x%x, len 0x%x\n", |
| wsi->u.http2.type, wsi->u.http2.flags, wsi->u.http2.stream_id, wsi->u.http2.length); |
| wsi->u.http2.count = 0; |
| |
| |
| switch (wsi->u.http2.type) { |
| case LWS_HTTP2_FRAME_TYPE_SETTINGS: |
| /* nonzero sid on settings is illegal */ |
| if (wsi->u.http2.stream_id) |
| return 1; |
| |
| if (wsi->u.http2.flags & 1) { // ack |
| } else { |
| lws_set_protocol_write_pending(context, wsi, LWS_PPS_HTTP2_ACK_SETTINGS); |
| } |
| break; |
| case LWS_HTTP2_FRAME_TYPE_HEADERS: |
| if (!wsi->u.http2.stream_id) |
| return 1; |
| 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; |
| } |
| if (wsi->u.http2.length == 0) |
| wsi->u.http2.frame_state = 0; |
| |
| } |
| break; |
| } |
| |
| return 0; |
| } |
| |
| int lws_http2_do_pps_send(struct libwebsocket_context *context, struct libwebsocket *wsi) |
| { |
| unsigned char settings[LWS_SEND_BUFFER_PRE_PADDING + 6 * LWS_HTTP2_SETTINGS__COUNT]; |
| int n, m = 0; |
| |
| switch (wsi->pps) { |
| case LWS_PPS_HTTP2_MY_SETTINGS: |
| for (n = 1; n < LWS_HTTP2_SETTINGS__COUNT; n++) |
| if (wsi->u.http2.my_settings.setting[n] != lws_http2_default_settings.setting[n]) { |
| lws_http2_settings_write(wsi, n, |
| &settings[LWS_SEND_BUFFER_PRE_PADDING + m]); |
| m += sizeof(wsi->u.http2.one_setting); |
| } |
| n = lws_http2_frame_write(wsi, LWS_HTTP2_FRAME_TYPE_SETTINGS, |
| 0, LWS_HTTP2_STREAM_ID_MASTER, m, |
| &settings[LWS_SEND_BUFFER_PRE_PADDING]); |
| if (n != m) { |
| lwsl_info("send %d %d\n", n, m); |
| return 1; |
| } |
| break; |
| case LWS_PPS_HTTP2_ACK_SETTINGS: |
| /* send ack ... always empty */ |
| n = lws_http2_frame_write(wsi, LWS_HTTP2_FRAME_TYPE_SETTINGS, |
| 1, LWS_HTTP2_STREAM_ID_MASTER, 0, |
| &settings[LWS_SEND_BUFFER_PRE_PADDING]); |
| if (n) { |
| lwsl_err("ack tells %d\n", n); |
| return 1; |
| } |
| /* this is the end of the preface dance then? */ |
| if (wsi->state == WSI_STATE_HTTP2_ESTABLISHED_PRE_SETTINGS) { |
| wsi->state = WSI_STATE_HTTP2_ESTABLISHED; |
| |
| wsi->u.http.fd = LWS_INVALID_FILE; |
| |
| /* service the http request itself */ |
| //lwsl_info("servicing initial http request\n"); |
| //n = lws_http_action(context, wsi); |
| |
| return 0; |
| } |
| break; |
| default: |
| break; |
| } |
| |
| return 0; |
| } |