http2 can keep upgraded connection up
authorAndy Green <andy.green@linaro.org>
Wed, 8 Oct 2014 04:00:53 +0000 (12:00 +0800)
committerAndy Green <andy.green@linaro.org>
Wed, 8 Oct 2014 04:00:53 +0000 (12:00 +0800)
Signed-off-by: Andy Green <andy.green@linaro.org>
12 files changed:
CMakeLists.txt
lib/client.c
lib/handshake.c
lib/http2.c [new file with mode: 0644]
lib/libwebsockets.c
lib/libwebsockets.h
lib/output.c
lib/private-libwebsockets.h
lib/server-handshake.c
lib/server.c
lib/service.c
test-server/test-server.c

index 473b057..59d6c3a 100644 (file)
@@ -295,6 +295,7 @@ endif()
 
 if (LWS_WITH_HTTP2)
        list(APPEND SOURCES
+               lib/http2.c
                lib/ssl-http2.c
                )
 endif()
index 01f2aa1..3cff9a0 100755 (executable)
@@ -733,7 +733,7 @@ check_accept:
 
        memset(&wsi->u, 0, sizeof(wsi->u));
 
-       wsi->u.ws.rxflow_change_to = LWS_RXFLOW_ALLOW;
+       wsi->rxflow_change_to = LWS_RXFLOW_ALLOW;
 
        /*
         * create the frame buffer for this connection according to the
index d81c1d2..5903557 100644 (file)
@@ -49,7 +49,6 @@
 #ifndef min
 #define min(a, b) ((a) < (b) ? (a) : (b))
 #endif
-
 /*
  * We have to take care about parsing because the headers may be split
  * into multiple fragments.  They may contain unknown headers with arbitrary
@@ -66,6 +65,27 @@ libwebsocket_read(struct libwebsocket_context *context,
        unsigned char *last_char;
 
        switch (wsi->state) {
+       case WSI_STATE_HTTP2_AWAIT_CLIENT_PREFACE:
+       case WSI_STATE_HTTP2_ESTABLISHED_PRE_SETTINGS:
+       case WSI_STATE_HTTP2_ESTABLISHED:
+               n = 0;
+               while (n < len) {
+                       /*
+                        * we were accepting input but now we stopped doing so
+                        */
+                       if (!(wsi->rxflow_change_to & LWS_RXFLOW_ALLOW)) {
+                               lws_rxflow_cache(wsi, buf, n, len);
+
+                               return 1;
+                       }
+
+                       /* account for what we're using in rxflow buffer */
+                       if (wsi->rxflow_buffer)
+                               wsi->rxflow_pos++;
+                       if (lws_http2_parser(context, wsi, buf[n++]))
+                               goto bail;
+               }
+               break;
 http_new:
        case WSI_STATE_HTTP:
                wsi->hdr_parsing_completed = 0;
diff --git a/lib/http2.c b/lib/http2.c
new file mode 100644 (file)
index 0000000..74f196a
--- /dev/null
@@ -0,0 +1,324 @@
+/*
+ * 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
+                       switch(wsi->u.http2.type) {
+                       case LWS_HTTP2_FRAME_TYPE_SETTINGS:
+                               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.peer_settings,
+                                            wsi->u.http2.one_setting,
+                                            LWS_HTTP2_SETTINGS_LENGTH))
+                                               return 1;
+                               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;
+                       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:
+                               wsi_new = lws_http2_wsi_from_id(wsi, wsi->u.http2.stream_id);
+                               if (!wsi_new) {
+                                       wsi_new = lws_create_server_child_wsi(context, wsi, wsi->u.http2.stream_id);
+                               }
+                       }
+                       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;
+}
\ No newline at end of file
index 12588f7..a332fc6 100644 (file)
@@ -88,6 +88,13 @@ libwebsocket_close_and_free_session(struct libwebsocket_context *context,
                free(wsi->u.hdr.ah);
                goto just_kill_connection;
        }
+       
+       if (wsi->mode == LWS_CONNMODE_HTTP2_SERVING) {
+               if (wsi->u.hdr.ah) {
+                       free(wsi->u.hdr.ah);
+                       wsi->u.hdr.ah = NULL;
+               }
+       }
 
        if (wsi->mode == LWS_CONNMODE_HTTP_SERVING_ACCEPTED) {
                if (wsi->u.http.fd != LWS_INVALID_FILE) {
@@ -205,6 +212,11 @@ just_kill_connection:
        remove_wsi_socket_from_fds(context, wsi);
 
        wsi->state = WSI_STATE_DEAD_SOCKET;
+       
+       if (wsi->rxflow_buffer) {
+               free(wsi->rxflow_buffer);
+               wsi->rxflow_buffer = NULL;
+       }
 
        if ((old_state == WSI_STATE_ESTABLISHED ||
             wsi->mode == LWS_CONNMODE_WS_SERVING ||
@@ -214,10 +226,7 @@ just_kill_connection:
                        free(wsi->u.ws.rx_user_buffer);
                        wsi->u.ws.rx_user_buffer = NULL;
                }
-               if (wsi->u.ws.rxflow_buffer) {
-                       free(wsi->u.ws.rxflow_buffer);
-                       wsi->u.ws.rxflow_buffer = NULL;
-               }
+
                if (wsi->truncated_send_malloc) {
                        /* not going to be completed... nuke it */
                        free(wsi->truncated_send_malloc);
@@ -546,11 +555,11 @@ lws_latency(struct libwebsocket_context *context, struct libwebsocket *wsi,
 LWS_VISIBLE int
 libwebsocket_rx_flow_control(struct libwebsocket *wsi, int enable)
 {
-       if (enable == (wsi->u.ws.rxflow_change_to & LWS_RXFLOW_ALLOW))
+       if (enable == (wsi->rxflow_change_to & LWS_RXFLOW_ALLOW))
                return 0;
 
        lwsl_info("libwebsocket_rx_flow_control(0x%p, %d)\n", wsi, enable);
-       wsi->u.ws.rxflow_change_to = LWS_RXFLOW_PENDING_CHANGE | !!enable;
+       wsi->rxflow_change_to = LWS_RXFLOW_PENDING_CHANGE | !!enable;
 
        return 0;
 }
@@ -803,3 +812,16 @@ lws_partial_buffered(struct libwebsocket *wsi)
 {
        return !!wsi->truncated_send_len;       
 }
+
+void lws_set_protocol_write_pending(struct libwebsocket_context *context,
+                                   struct libwebsocket *wsi,
+                                   enum lws_pending_protocol_send pend)
+{
+               lwsl_err("setting pps %d\n", pend);
+       
+       if (wsi->pps)
+               lwsl_err("pps overwrite\n");
+       wsi->pps = pend;
+       libwebsocket_rx_flow_control(wsi, 0);
+       libwebsocket_callback_on_writable(context, wsi);
+}
\ No newline at end of file
index cbd1ce5..777b315 100644 (file)
@@ -257,6 +257,10 @@ enum libwebsocket_write_protocol {
        LWS_WRITE_PING,
        LWS_WRITE_PONG,
 
+       /* HTTP2 */
+
+       LWS_WRITE_HTTP_HEADERS,
+       
        /* flags */
 
        LWS_WRITE_NO_FIN = 0x40,
index cd8da35..ccd02d7 100644 (file)
@@ -262,7 +262,7 @@ LWS_VISIBLE int libwebsocket_write(struct libwebsocket *wsi, unsigned char *buf,
                return 0;
        }
 
-       if (protocol == LWS_WRITE_HTTP)
+       if (protocol == LWS_WRITE_HTTP || protocol == LWS_WRITE_HTTP_HEADERS)
                goto send_raw;
 
        /* websocket protocol, either binary or text */
@@ -429,8 +429,15 @@ send_raw:
        case LWS_WRITE_CLOSE:
 /*             lwsl_hexdump(&buf[-pre], len + post); */
        case LWS_WRITE_HTTP:
+       case LWS_WRITE_HTTP_HEADERS:
        case LWS_WRITE_PONG:
        case LWS_WRITE_PING:
+               if (wsi->mode == LWS_CONNMODE_HTTP2_SERVING) {
+                       n = LWS_HTTP2_FRAME_TYPE_DATA;
+                       if (protocol == LWS_WRITE_HTTP_HEADERS)
+                               n = LWS_HTTP2_FRAME_TYPE_HEADERS;
+                       return lws_http2_frame_write(wsi, n, 0, wsi->u.http2.my_stream_id, len, buf);
+               }
                return lws_issue_raw(wsi, (unsigned char *)buf - pre,
                                                              len + pre + post);
        default:
index 3748acb..3ec5d02 100755 (executable)
@@ -305,6 +305,10 @@ enum lws_connection_states {
        WSI_STATE_RETURNED_CLOSE_ALREADY,
        WSI_STATE_AWAITING_CLOSE_ACK,
        WSI_STATE_FLUSHING_STORED_SEND_BEFORE_CLOSE,
+       
+       WSI_STATE_HTTP2_AWAIT_CLIENT_PREFACE,
+       WSI_STATE_HTTP2_ESTABLISHED_PRE_SETTINGS,
+       WSI_STATE_HTTP2_ESTABLISHED,
 };
 
 enum http_version {
@@ -317,6 +321,12 @@ enum http_connection_type {
        HTTP_CONNECTION_KEEP_ALIVE
 };
 
+enum lws_pending_protocol_send {
+       LWS_PPS_NONE,
+       LWS_PPS_HTTP2_MY_SETTINGS,
+       LWS_PPS_HTTP2_ACK_SETTINGS,
+};
+
 enum lws_rx_parse_state {
        LWS_RXPS_NEW,
 
@@ -526,6 +536,18 @@ struct lws_fragments {
        unsigned char next_frag_index;
 };
 
+/* notice that these union members:
+ * 
+ *  hdr
+ *  http
+ *  http2
+ * 
+ * all have a pointer to allocated_headers struct as their first member.
+ * 
+ * It means for allocated_headers access, the three union paths can all be
+ * used interchangably to access the same data
+ */
+
 struct allocated_headers {
        unsigned short next_frag_index;
        unsigned short pos;
@@ -539,6 +561,7 @@ struct allocated_headers {
 };
 
 struct _lws_http_mode_related {
+       /* MUST be first in struct */
        struct allocated_headers *ah; /* mirroring  _lws_header_related */
 #if defined(WIN32) || defined(_WIN32)
        HANDLE fd;
@@ -554,10 +577,80 @@ struct _lws_http_mode_related {
        int content_remain;
 };
 
+#ifdef LWS_USE_HTTP2
+
+enum lws_http2_settings {
+       LWS_HTTP2_SETTINGS__HEADER_TABLE_SIZE = 1,
+       LWS_HTTP2_SETTINGS__ENABLE_PUSH,
+       LWS_HTTP2_SETTINGS__MAX_CONCURRENT_STREAMS,
+       LWS_HTTP2_SETTINGS__INITIAL_WINDOW_SIZE,
+       LWS_HTTP2_SETTINGS__MAX_FRAME_SIZE,
+       LWS_HTTP2_SETTINGS__MAX_HEADER_LIST_SIZE,
+       
+       LWS_HTTP2_SETTINGS__COUNT /* always last */
+};
+
+enum lws_http2_wellknown_frame_types {
+       LWS_HTTP2_FRAME_TYPE_DATA,
+       LWS_HTTP2_FRAME_TYPE_HEADERS,
+       LWS_HTTP2_FRAME_TYPE_PRIORITY,
+       LWS_HTTP2_FRAME_TYPE_RST_STREAM,
+       LWS_HTTP2_FRAME_TYPE_SETTINGS,
+       LWS_HTTP2_FRAME_TYPE_PUSH_PROMISE,
+       LWS_HTTP2_FRAME_TYPE_PING,
+       LWS_HTTP2_FRAME_TYPE_GOAWAY,
+       LWS_HTTP2_FRAME_TYPE_WINDOW_UPDATE,
+       LWS_HTTP2_FRAME_TYPE_CONTINUATION,
+       
+       LWS_HTTP2_FRAME_TYPE_COUNT /* always last */
+};
+
+#define LWS_HTTP2_STREAM_ID_MASTER 0
+#define LWS_HTTP2_FRAME_HEADER_LENGTH 9
+#define LWS_HTTP2_SETTINGS_LENGTH 6
+
+struct http2_settings {
+       unsigned int setting[LWS_HTTP2_SETTINGS__COUNT];
+};
+
 struct _lws_http2_related {
+       /* 
+        * having this first lets us also re-use all HTTP union code
+        * and in turn, http_mode_related has allocated headers in right
+        * place so we can use the header apis on the wsi directly still
+        */
+       struct _lws_http_mode_related http; /* MUST BE FIRST IN STRUCT */
+
+       struct http2_settings my_settings;
+       struct http2_settings peer_settings;
+       
+       struct libwebsocket *parent_wsi;
+       struct libwebsocket *next_child_wsi;
+
+       unsigned int count;
+       
+       /* frame */
+       unsigned int length;
+       unsigned int stream_id;
+       struct libwebsocket *stream_wsi;
+       unsigned char type;
+       unsigned char flags;
+       unsigned char frame_state;
+
+       unsigned int tx_credit;
+       unsigned int my_stream_id;
+       unsigned int child_count;
+       int my_priority;
+       unsigned char initialized;
+       unsigned char one_setting[LWS_HTTP2_SETTINGS_LENGTH];
 };
 
+#define HTTP2_IS_TOPLEVEL_WSI(wsi) (!wsi->parent_wsi)
+
+#endif
+
 struct _lws_header_related {
+       /* MUST be first in struct */
        struct allocated_headers *ah;
        short lextable_pos;
        unsigned short current_token_limit;
@@ -579,10 +672,7 @@ struct _lws_websocket_related {
        unsigned int frame_is_binary:1;
        unsigned int all_zero_nonce:1;
        short close_reason; /* enum lws_close_status */
-       unsigned char *rxflow_buffer;
-       int rxflow_len;
-       int rxflow_pos;
-       unsigned int rxflow_change_to:2;
+
        unsigned int this_frame_masked:1;
        unsigned int inside_frame:1; /* next write will be more of frame */
        unsigned int clean_buffer:1; /* buffer not rewritten by extension */
@@ -609,6 +699,7 @@ struct libwebsocket {
        unsigned int extension_data_pending:1;
 #endif
        unsigned char ietf_spec_revision;
+       enum lws_pending_protocol_send pps;
 
        char mode; /* enum connection_mode */
        char state; /* enum lws_connection_states */
@@ -627,6 +718,11 @@ struct libwebsocket {
        unsigned long action_start;
        unsigned long latency_start;
 #endif
+       /* rxflow handling */
+       unsigned char *rxflow_buffer;
+       int rxflow_len;
+       int rxflow_pos;
+       unsigned int rxflow_change_to:2;
 
        /* truncated send handling */
        unsigned char *truncated_send_malloc; /* non-NULL means buffering in progress */
@@ -640,7 +736,9 @@ struct libwebsocket {
 
        union u {
                struct _lws_http_mode_related http;
+#ifdef LWS_USE_HTTP2
                struct _lws_http2_related http2;
+#endif
                struct _lws_header_related hdr;
                struct _lws_websocket_related ws;
        } u;
@@ -665,6 +763,8 @@ libwebsocket_close_and_free_session(struct libwebsocket_context *context,
 LWS_EXTERN int
 remove_wsi_socket_from_fds(struct libwebsocket_context *context,
                                                      struct libwebsocket *wsi);
+LWS_EXTERN int
+lws_rxflow_cache(struct libwebsocket *wsi, unsigned char *buf, int n, int len);
 
 #ifndef LWS_LATENCY
 static inline void lws_latency(struct libwebsocket_context *context,
@@ -680,6 +780,9 @@ lws_latency(struct libwebsocket_context *context,
                                                       int ret, int completion);
 #endif
 
+LWS_EXTERN void lws_set_protocol_write_pending(struct libwebsocket_context *context,
+                                   struct libwebsocket *wsi,
+                                   enum lws_pending_protocol_send pend);
 LWS_EXTERN int
 libwebsocket_client_rx_sm(struct libwebsocket *wsi, unsigned char c);
 
@@ -688,6 +791,9 @@ libwebsocket_parse(struct libwebsocket_context *context,
                struct libwebsocket *wsi, unsigned char c);
 
 LWS_EXTERN int
+lws_http_action(struct libwebsocket_context *context, struct libwebsocket *wsi);
+
+LWS_EXTERN int
 lws_b64_selftest(void);
 
 LWS_EXTERN struct libwebsocket *
@@ -768,6 +874,18 @@ user_callback_handle_rxflow(callback_function,
                        struct libwebsocket *wsi,
                         enum libwebsocket_callback_reasons reason, void *user,
                                                          void *in, size_t len);
+#ifdef LWS_USE_HTTP2
+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);
+LWS_EXTERN int
+lws_http2_parser(struct libwebsocket_context *context,
+                    struct libwebsocket *wsi, unsigned char c);
+LWS_EXTERN int lws_http2_do_pps_send(struct libwebsocket_context *context, struct libwebsocket *wsi);
+LWS_EXTERN int lws_http2_frame_write(struct libwebsocket *wsi, int type, int flags, unsigned int sid, unsigned int len, unsigned char *buf);
+LWS_EXTERN struct libwebsocket *
+lws_http2_wsi_from_id(struct libwebsocket *wsi, unsigned int sid);
+#endif
 
 LWS_EXTERN int
 lws_plat_set_socket_options(struct libwebsocket_context *context, int fd);
index 21beaa0..4c0fa73 100644 (file)
@@ -206,7 +206,7 @@ handshake_0405(struct libwebsocket_context *context, struct libwebsocket *wsi)
 
        /* make a buffer big enough for everything */
 
-       response = (char *)context->service_buffer + MAX_WEBSOCKET_04_KEY_LEN;
+       response = (char *)context->service_buffer + MAX_WEBSOCKET_04_KEY_LEN + LWS_SEND_BUFFER_PRE_PADDING;
        p = response;
        LWS_CPYAPP(p, "HTTP/1.1 101 Switching Protocols\x0d\x0a"
                      "Upgrade: WebSocket\x0d\x0a"
@@ -246,7 +246,7 @@ handshake_0405(struct libwebsocket_context *context, struct libwebsocket *wsi)
                fwrite(response, 1,  p - response, stderr);
 #endif
                n = libwebsocket_write(wsi, (unsigned char *)response,
-                                                 p - response, LWS_WRITE_HTTP);
+                                                 p - response, LWS_WRITE_HTTP_HEADERS);
                if (n != (p - response)) {
                        lwsl_debug("handshake_0405: ERROR writing to socket\n");
                        goto bail;
index 5f3ac2d..68ee481 100644 (file)
@@ -135,11 +135,11 @@ _libwebsocket_rx_flow_control(struct libwebsocket *wsi)
        struct libwebsocket_context *context = wsi->protocol->owning_server;
 
        /* there is no pending change */
-       if (!(wsi->u.ws.rxflow_change_to & LWS_RXFLOW_PENDING_CHANGE))
+       if (!(wsi->rxflow_change_to & LWS_RXFLOW_PENDING_CHANGE))
                return 0;
 
        /* stuff is still buffered, not ready to really accept new input */
-       if (wsi->u.ws.rxflow_buffer) {
+       if (wsi->rxflow_buffer) {
                /* get ourselves called back to deal with stashed buffer */
                libwebsocket_callback_on_writable(context, wsi);
                return 0;
@@ -147,14 +147,14 @@ _libwebsocket_rx_flow_control(struct libwebsocket *wsi)
 
        /* pending is cleared, we can change rxflow state */
 
-       wsi->u.ws.rxflow_change_to &= ~LWS_RXFLOW_PENDING_CHANGE;
+       wsi->rxflow_change_to &= ~LWS_RXFLOW_PENDING_CHANGE;
 
        lwsl_info("rxflow: wsi %p change_to %d\n", wsi,
-                             wsi->u.ws.rxflow_change_to & LWS_RXFLOW_ALLOW);
+                             wsi->rxflow_change_to & LWS_RXFLOW_ALLOW);
 
        /* adjust the pollfd for this wsi */
 
-       if (wsi->u.ws.rxflow_change_to & LWS_RXFLOW_ALLOW) {
+       if (wsi->rxflow_change_to & LWS_RXFLOW_ALLOW) {
                if (lws_change_pollfd(wsi, 0, LWS_POLLIN)) {
                        lwsl_info("%s: fail\n", __func__);
                        return -1;
@@ -166,20 +166,164 @@ _libwebsocket_rx_flow_control(struct libwebsocket *wsi)
        return 0;
 }
 
-
-int lws_handshake_server(struct libwebsocket_context *context,
-               struct libwebsocket *wsi, unsigned char **buf, size_t len)
+int lws_http_action(struct libwebsocket_context *context,
+                   struct libwebsocket *wsi)
 {
-       struct allocated_headers *ah;
        char *uri_ptr = NULL;
        int uri_len = 0;
        enum http_version request_version;
        enum http_connection_type connection_type;
-       int http_version_len, protocol_len;
+       int http_version_len;
        char content_length_str[32];
+       char http_version_str[10];
+       char http_conn_str[20];
+       int n;
+
+       /* it's not websocket.... shall we accept it as http? */
+
+       if (!lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI) &&
+               !lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI) &&
+               !lws_hdr_total_length(wsi, WSI_TOKEN_OPTIONS_URI)) {
+               lwsl_warn("Missing URI in HTTP request\n");
+               goto bail_nuke_ah;
+       }
+
+       if (lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI) &&
+               lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)) {
+               lwsl_warn("GET and POST methods?\n");
+               goto bail_nuke_ah;
+       }
+
+       if (libwebsocket_ensure_user_space(wsi))
+               goto bail_nuke_ah;
+
+       if (lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI)) {
+               uri_ptr = lws_hdr_simple_ptr(wsi, WSI_TOKEN_GET_URI);
+               uri_len = lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI);
+               lwsl_info("HTTP GET request for '%s'\n",
+                               lws_hdr_simple_ptr(wsi, WSI_TOKEN_GET_URI));
+       }
+       if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)) {
+               lwsl_info("HTTP POST request for '%s'\n",
+                               lws_hdr_simple_ptr(wsi, WSI_TOKEN_POST_URI));
+               uri_ptr = lws_hdr_simple_ptr(wsi, WSI_TOKEN_POST_URI);
+               uri_len = lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI);
+       }
+       if (lws_hdr_total_length(wsi, WSI_TOKEN_OPTIONS_URI)) {
+               lwsl_info("HTTP OPTIONS request for '%s'\n",
+                               lws_hdr_simple_ptr(wsi, WSI_TOKEN_OPTIONS_URI));
+               uri_ptr = lws_hdr_simple_ptr(wsi, WSI_TOKEN_OPTIONS_URI);
+               uri_len = lws_hdr_total_length(wsi, WSI_TOKEN_OPTIONS_URI);
+       }
+
+       /* HTTP header had a content length? */
+
+       wsi->u.http.content_length = 0;
+       if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI))
+               wsi->u.http.content_length = 100 * 1024 * 1024;
+
+       if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) {
+               lws_hdr_copy(wsi, content_length_str,
+                               sizeof(content_length_str) - 1,
+                                               WSI_TOKEN_HTTP_CONTENT_LENGTH);
+               wsi->u.http.content_length = atoi(content_length_str);
+       }
+
+       /* http_version? Default to 1.0, override with token: */
+       request_version = HTTP_VERSION_1_0;
+
+       /* Works for single digit HTTP versions. : */
+       http_version_len = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP);
+       if (http_version_len > 7) {
+               lws_hdr_copy(wsi, http_version_str,
+                               sizeof(http_version_str) - 1, WSI_TOKEN_HTTP);
+               if (http_version_str[5] == '1' && http_version_str[7] == '1')
+                       request_version = HTTP_VERSION_1_1;
+       }
+       wsi->u.http.request_version = request_version;
+
+       /* HTTP/1.1 defaults to "keep-alive", 1.0 to "close" */
+       if (request_version == HTTP_VERSION_1_1)
+               connection_type = HTTP_CONNECTION_KEEP_ALIVE;
+       else
+               connection_type = HTTP_CONNECTION_CLOSE;
+
+       /* Override default if http "Connection:" header: */
+       if (lws_hdr_total_length(wsi, WSI_TOKEN_CONNECTION)) {
+               lws_hdr_copy(wsi, http_conn_str, sizeof(http_conn_str) - 1,
+                            WSI_TOKEN_CONNECTION);
+               http_conn_str[sizeof(http_conn_str) - 1] = '\0';
+               if (!strcasecmp(http_conn_str, "keep-alive"))
+                       connection_type = HTTP_CONNECTION_KEEP_ALIVE;
+               else
+                       if (strcasecmp(http_conn_str, "close"))
+                               connection_type = HTTP_CONNECTION_CLOSE;
+       }
+       wsi->u.http.connection_type = connection_type;
+
+       n = 0;
+       if (wsi->protocol->callback)
+               n = wsi->protocol->callback(context, wsi,
+                                       LWS_CALLBACK_FILTER_HTTP_CONNECTION,
+                                            wsi->user_space, uri_ptr, uri_len);
+
+       if (!n) {
+               /*
+                * if there is content supposed to be coming,
+                * put a timeout on it having arrived
+                */
+               libwebsocket_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT,
+                                                             AWAITING_TIMEOUT);
+
+               if (wsi->protocol->callback)
+                       n = wsi->protocol->callback(context, wsi,
+                           LWS_CALLBACK_HTTP,
+                           wsi->user_space, uri_ptr, uri_len);
+       }
+
+       /* now drop the header info we kept a pointer to */
+       if (wsi->u.http.ah)
+               free(wsi->u.http.ah);
+       /* not possible to continue to use past here */
+       wsi->u.http.ah = NULL;
+
+       if (n) {
+               lwsl_info("LWS_CALLBACK_HTTP closing\n");
+               return 1; /* struct ah ptr already nuked */             }
+
+       /* 
+        * If we're not issuing a file, check for content_length or
+        * HTTP keep-alive. No keep-alive header allocation for
+        * ISSUING_FILE, as this uses HTTP/1.0. 
+        * 
+        * In any case, return 0 and let libwebsocket_read decide how to
+        * proceed based on state
+        */
+       if (wsi->state != WSI_STATE_HTTP_ISSUING_FILE)
+               /* Prepare to read body if we have a content length: */
+               if (wsi->u.http.content_length > 0)
+                       wsi->state = WSI_STATE_HTTP_BODY;
+
+       return 0;
+
+bail_nuke_ah:
+       /* drop the header info */
+       if (wsi->u.hdr.ah) {
+               free(wsi->u.hdr.ah);
+               wsi->u.hdr.ah = NULL;
+       }
+       
+       return 1;
+}
+
+
+int lws_handshake_server(struct libwebsocket_context *context,
+               struct libwebsocket *wsi, unsigned char **buf, size_t len)
+{
+       struct allocated_headers *ah;
+       int protocol_len;
        char protocol_list[128];
        char protocol_name[32];
-       char http_version_str[10];
        char *p;
        int n, hit;
 
@@ -203,53 +347,9 @@ int lws_handshake_server(struct libwebsocket_context *context,
 
                if (!lws_hdr_total_length(wsi, WSI_TOKEN_UPGRADE) ||
                             !lws_hdr_total_length(wsi, WSI_TOKEN_CONNECTION)) {
-
-                       /* it's not websocket.... shall we accept it as http? */
-
-                       if (!lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI) &&
-                               !lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI) &&
-                               !lws_hdr_total_length(wsi, WSI_TOKEN_OPTIONS_URI)) {
-                               lwsl_warn("Missing URI in HTTP request\n");
-                               goto bail_nuke_ah;
-                       }
-
-                       if (lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI) &&
-                               lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)) {
-                               lwsl_warn("GET and POST methods?\n");
-                               goto bail_nuke_ah;
-                       }
-
-                       if (libwebsocket_ensure_user_space(wsi))
-                               goto bail_nuke_ah;
-
-                       if (lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI)) {
-                               uri_ptr = lws_hdr_simple_ptr(wsi, WSI_TOKEN_GET_URI);
-                               uri_len = lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI);
-                               lwsl_info("HTTP GET request for '%s'\n",
-                                   lws_hdr_simple_ptr(wsi, WSI_TOKEN_GET_URI));
-
-                       }
-                       if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)) {
-                               lwsl_info("HTTP POST request for '%s'\n",
-                                  lws_hdr_simple_ptr(wsi, WSI_TOKEN_POST_URI));
-                               uri_ptr = lws_hdr_simple_ptr(wsi, WSI_TOKEN_POST_URI);
-                               uri_len = lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI);
-                       }
-                       if (lws_hdr_total_length(wsi, WSI_TOKEN_OPTIONS_URI)) {
-                               lwsl_info("HTTP OPTIONS request for '%s'\n",
-                                  lws_hdr_simple_ptr(wsi, WSI_TOKEN_OPTIONS_URI));
-                               uri_ptr = lws_hdr_simple_ptr(wsi, WSI_TOKEN_OPTIONS_URI);
-                               uri_len = lws_hdr_total_length(wsi, WSI_TOKEN_OPTIONS_URI);
-                       }
-
-                       /*
-                        * Hm we still need the headers so the
-                        * callback can look at leaders like the URI, but we
-                        * need to transition to http union state.... hold a
-                        * copy of u.hdr.ah and deallocate afterwards
-                        */
+                       
                        ah = wsi->u.hdr.ah;
-
+                       
                        /* union transition */
                        memset(&wsi->u, 0, sizeof(wsi->u));
                        wsi->mode = LWS_CONNMODE_HTTP_SERVING_ACCEPTED;
@@ -258,100 +358,10 @@ int lws_handshake_server(struct libwebsocket_context *context,
 
                        /* expose it at the same offset as u.hdr */
                        wsi->u.http.ah = ah;
+                       
+                       n = lws_http_action(context, wsi);
 
-                       /* HTTP header had a content length? */
-
-                       wsi->u.http.content_length = 0;
-                       if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI))
-                               wsi->u.http.content_length = 100 * 1024 * 1024;
-
-                       if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) {
-                               lws_hdr_copy(wsi, content_length_str,
-                                               sizeof(content_length_str) - 1,
-                                                               WSI_TOKEN_HTTP_CONTENT_LENGTH);
-                               wsi->u.http.content_length = atoi(content_length_str);
-                       }
-
-                       /* http_version? Default to 1.0, override with token: */
-                       request_version = HTTP_VERSION_1_0;
-
-                       /* Works for single digit HTTP versions. : */
-                       http_version_len = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP);
-                       if (http_version_len > 7) {
-                               lws_hdr_copy(wsi, http_version_str,
-                                               sizeof(http_version_str) - 1, WSI_TOKEN_HTTP);
-                               if (http_version_str[5] == '1' &&
-                                       http_version_str[7] == '1')
-                                       request_version = HTTP_VERSION_1_1;
-                       }
-                       wsi->u.http.request_version = request_version;
-
-                       /* HTTP/1.1 defaults to "keep-alive", 1.0 to "close" */
-                       if (request_version == HTTP_VERSION_1_1)
-                               connection_type = HTTP_CONNECTION_KEEP_ALIVE;
-                       else
-                               connection_type = HTTP_CONNECTION_CLOSE;
-
-                       /* Override default if http "Connection:" header: */
-                       if (lws_hdr_total_length(wsi, WSI_TOKEN_CONNECTION)) {
-                               char http_conn_str[20];
-                               lws_hdr_copy(wsi, http_conn_str,
-                                               sizeof(http_conn_str)-1, WSI_TOKEN_CONNECTION);
-                               http_conn_str[sizeof(http_conn_str)-1] = '\0';
-                               if (strcasecmp(http_conn_str,"keep-alive") == 0)
-                                       connection_type = HTTP_CONNECTION_KEEP_ALIVE;
-
-                               else if (strcasecmp(http_conn_str,"close") == 0)
-                                       connection_type = HTTP_CONNECTION_CLOSE;
-
-                       }
-                       wsi->u.http.connection_type = connection_type;
-
-                       n = 0;
-                       if (wsi->protocol->callback)
-                               n = wsi->protocol->callback(context, wsi,
-                                       LWS_CALLBACK_FILTER_HTTP_CONNECTION,
-                                            wsi->user_space, uri_ptr, uri_len);
-
-                       if (!n) {
-                               /*
-                                * if there is content supposed to be coming,
-                                * put a timeout on it having arrived
-                                */
-                               libwebsocket_set_timeout(wsi,
-                                       PENDING_TIMEOUT_HTTP_CONTENT,
-                                                             AWAITING_TIMEOUT);
-
-                               if (wsi->protocol->callback)
-                                       n = wsi->protocol->callback(context, wsi,
-                                           LWS_CALLBACK_HTTP,
-                                           wsi->user_space, uri_ptr, uri_len);
-                       }
-
-                       /* now drop the header info we kept a pointer to */
-                       if (ah)
-                               free(ah);
-                       /* not possible to continue to use past here */
-                       wsi->u.http.ah = NULL;
-
-                       if (n) {
-                               lwsl_info("LWS_CALLBACK_HTTP closing\n");
-                               return 1; /* struct ah ptr already nuked */
-                       }
-
-                       /* If we're not issuing a file, check for content_length or
-                        * HTTP keep-alive. No keep-alive header allocation for
-                        * ISSUING_FILE, as this uses HTTP/1.0. 
-                        * In any case, return 0 and let libwebsocket_read decide how to
-                        * proceed based on state. */
-                       if (wsi->state != WSI_STATE_HTTP_ISSUING_FILE)
-
-                               /* Prepare to read body if we have a content length: */
-                               if (wsi->u.http.content_length > 0)
-                                       wsi->state = WSI_STATE_HTTP_BODY;
-
-
-                       return 0; /* don't bail out of libwebsocket_read, just yet */
+                       return n;
                }
                
                lwsl_err(lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE));
@@ -359,37 +369,65 @@ int lws_handshake_server(struct libwebsocket_context *context,
                if (!strcasecmp(lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE),
                                                                "websocket"))
                        goto upgrade_ws;
-
+#ifdef LWS_USE_HTTP2
                if (!strcasecmp(lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE),
                                                                "h2c-14"))
                        goto upgrade_h2c;
-               
+#endif
                /* dunno what he wanted to upgrade to */
                goto bail_nuke_ah;
-               
-upgrade_h2c:
 
-               strcpy(protocol_list, "HTTP/1.1 101 Switching Protocols\x0d\x0a"
-                     "Connection: Upgrade\x0d\x0a"
-                     "Upgrade: h2c\x0d\x0a\x0d\x0a");
-               n = libwebsocket_write(wsi, (unsigned char *)protocol_list,
-                                       strlen(protocol_list), LWS_WRITE_HTTP);
-               if (n != strlen(protocol_list)) {
-                       lwsl_debug("http2 switch: ERROR writing to socket\n");
+#ifdef LWS_USE_HTTP2
+upgrade_h2c:
+               if (!lws_hdr_total_length(wsi, WSI_TOKEN_HTTP2_SETTINGS)) {
+                       lwsl_err("missing http2_settings\n");
                        goto bail_nuke_ah;
                }
 
-               /* drop the header info -- no bail_nuke_ah after this */
+               lwsl_err("h2c upgrade...\n");
 
-               if (wsi->u.hdr.ah)
-                       free(wsi->u.hdr.ah);
+               p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP2_SETTINGS);
+               /* convert the peer's HTTP-Settings */
+               n = lws_b64_decode_string(p, protocol_list, sizeof(protocol_list));
+               if (n < 0) {
+                       lwsl_parser("HTTP2_SETTINGS too long\n");
+                       return 1;
+               }
+
+               /* adopt the header info */
+
+               ah = wsi->u.hdr.ah;
 
                wsi->mode = LWS_CONNMODE_HTTP2_SERVING;
 
                /* union transition */
                memset(&wsi->u, 0, sizeof(wsi->u));
                
+               /* http2 union member has http union struct at start */
+               wsi->u.http.ah = ah;
+               
+               lws_http2_init(&wsi->u.http2.peer_settings);
+               lws_http2_init(&wsi->u.http2.my_settings);
+               
+               /* HTTP2 union */
+               
+               lws_http2_interpret_settings_payload(&wsi->u.http2.peer_settings, (unsigned char *)protocol_list, n);
+
+               strcpy(protocol_list,
+                      "HTTP/1.1 101 Switching Protocols\x0d\x0a"
+                     "Connection: Upgrade\x0d\x0a"
+                     "Upgrade: h2c\x0d\x0a\x0d\x0a");
+               n = lws_issue_raw(wsi, (unsigned char *)protocol_list,
+                                       strlen(protocol_list));
+               if (n != strlen(protocol_list)) {
+                       lwsl_debug("http2 switch: ERROR writing to socket\n");
+                       return 1;
+               }
+               
+               wsi->state = WSI_STATE_HTTP2_AWAIT_CLIENT_PREFACE;
+               
                return 0;
+#endif
 
 upgrade_ws:
                if (!wsi->protocol)
@@ -509,7 +547,6 @@ upgrade_ws:
 
                /* union transition */
                memset(&wsi->u, 0, sizeof(wsi->u));
-               wsi->u.ws.rxflow_change_to = LWS_RXFLOW_ALLOW;
 
                /*
                 * create the frame buffer for this connection according to the
@@ -559,6 +596,7 @@ libwebsocket_create_new_server_wsi(struct libwebsocket_context *context)
 
        memset(new_wsi, 0, sizeof(struct libwebsocket));
        new_wsi->pending_timeout = NO_PENDING_TIMEOUT;
+       new_wsi->rxflow_change_to = LWS_RXFLOW_ALLOW;
 
        /* intialize the instance struct */
 
@@ -605,6 +643,7 @@ int lws_server_socket_service(struct libwebsocket_context *context,
 
        case LWS_CONNMODE_HTTP_SERVING:
        case LWS_CONNMODE_HTTP_SERVING_ACCEPTED:
+       case LWS_CONNMODE_HTTP2_SERVING:
 
                /* handle http headers coming in */
 
@@ -839,7 +878,8 @@ LWS_VISIBLE int libwebsockets_return_http_status(
        if (code >= 500 && code < (500 + ARRAY_SIZE(err500)))
                description = err500[code - 500];
 
-       n = sprintf((char *)context->service_buffer,
+       n = sprintf((char *)context->service_buffer +
+                   LWS_SEND_BUFFER_PRE_PADDING,
                "HTTP/1.0 %u %s\x0d\x0a"
                "Server: libwebsockets\x0d\x0a"
                "Content-Type: text/html\x0d\x0a\x0d\x0a"
@@ -848,7 +888,8 @@ LWS_VISIBLE int libwebsockets_return_http_status(
 
        lwsl_info((const char *)context->service_buffer);
 
-       m = libwebsocket_write(wsi, context->service_buffer, n, LWS_WRITE_HTTP);
+       m = libwebsocket_write(wsi, context->service_buffer, n,
+                              LWS_WRITE_HTTP_HEADERS);
 
        return m;
 }
@@ -876,7 +917,8 @@ LWS_VISIBLE int libwebsockets_serve_http_file(
                        struct libwebsocket *wsi, const char *file,
                           const char *content_type, const char *other_headers)
 {
-       unsigned char *p = context->service_buffer;
+       unsigned char *response = context->service_buffer + LWS_SEND_BUFFER_PRE_PADDING;
+       unsigned char *p = response;
        int ret = 0;
        int n;
 
@@ -889,9 +931,9 @@ LWS_VISIBLE int libwebsockets_serve_http_file(
                return -1;
        }
 
-       p += sprintf((char *)p,
-"HTTP/1.0 200 OK\x0d\x0aServer: libwebsockets\x0d\x0a""Content-Type: %s\x0d\x0a",
-                                                                 content_type);
+       p += sprintf((char *)p, "HTTP/1.0 200 OK\x0d\x0a"
+                               "Server: libwebsockets\x0d\x0a"
+                               "Content-Type: %s\x0d\x0a", content_type);
        if (other_headers) {
                n = strlen(other_headers);
                memcpy(p, other_headers, n);
@@ -900,10 +942,10 @@ LWS_VISIBLE int libwebsockets_serve_http_file(
        p += sprintf((char *)p,
                "Content-Length: %lu\x0d\x0a\x0d\x0a", wsi->u.http.filelen);
 
-       ret = libwebsocket_write(wsi, context->service_buffer,
-                                  p - context->service_buffer, LWS_WRITE_HTTP);
-       if (ret != (p - context->service_buffer)) {
-               lwsl_err("_write returned %d from %d\n", ret, (p - context->service_buffer));
+       ret = libwebsocket_write(wsi, response,
+                                  p - response, LWS_WRITE_HTTP_HEADERS);
+       if (ret != (p - response)) {
+               lwsl_err("_write returned %d from %d\n", ret, (p - response));
                return -1;
        }
 
@@ -931,28 +973,15 @@ int libwebsocket_interpret_incoming_packet(struct libwebsocket *wsi,
                /*
                 * we were accepting input but now we stopped doing so
                 */
-               if (!(wsi->u.ws.rxflow_change_to & LWS_RXFLOW_ALLOW)) {
-                       /* his RX is flowcontrolled, don't send remaining now */
-                       if (!wsi->u.ws.rxflow_buffer) {
-                               /* a new rxflow, buffer it and warn caller */
-                               lwsl_info("new rxflow input buffer len %d\n",
-                                                                      len - n);
-                               wsi->u.ws.rxflow_buffer =
-                                              (unsigned char *)malloc(len - n);
-                               wsi->u.ws.rxflow_len = len - n;
-                               wsi->u.ws.rxflow_pos = 0;
-                               memcpy(wsi->u.ws.rxflow_buffer,
-                                                       buf + n, len - n);
-                       } else
-                               /* rxflow while we were spilling prev rxflow */
-                               lwsl_info("stalling in existing rxflow buf\n");
+               if (!(wsi->rxflow_change_to & LWS_RXFLOW_ALLOW)) {
+                       lws_rxflow_cache(wsi, buf, n, len);
 
                        return 1;
                }
 
                /* account for what we're using in rxflow buffer */
-               if (wsi->u.ws.rxflow_buffer)
-                       wsi->u.ws.rxflow_pos++;
+               if (wsi->rxflow_buffer)
+                       wsi->rxflow_pos++;
 
                /* process the byte */
                m = libwebsocket_rx_sm(wsi, buf[n++]);
index 9238967..160bda4 100644 (file)
@@ -48,9 +48,26 @@ lws_handle_POLLOUT_event(struct libwebsocket_context *context,
                        return -1; /* retry closing now */
                }
 
+       /* protocol packets are next */
+       if (wsi->pps) {
+               lwsl_err("servicing pps %d\n", wsi->pps);
+               switch (wsi->pps) {
+               case LWS_PPS_HTTP2_MY_SETTINGS:
+               case LWS_PPS_HTTP2_ACK_SETTINGS:
+                       lws_http2_do_pps_send(context, wsi);
+                       break;
+               default:
+                       break;
+               }
+               wsi->pps = LWS_PPS_NONE;
+               libwebsocket_rx_flow_control(wsi, 1);
+               
+               return 0; /* leave POLLOUT active */
+       }
+               
        /* pending control packets have next priority */
        
-       if (wsi->u.ws.ping_payload_len) {
+       if (wsi->state == WSI_STATE_ESTABLISHED && wsi->u.ws.ping_payload_len) {
                n = libwebsocket_write(wsi, 
                                &wsi->u.ws.ping_payload_buf[
                                        LWS_SEND_BUFFER_PRE_PADDING],
@@ -160,8 +177,10 @@ user_service:
        /* one shot */
 
        if (pollfd) {
-               if (lws_change_pollfd(wsi, LWS_POLLOUT, 0))
+               if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
+                       lwsl_info("failled at set pollfd\n");
                        return 1;
+               }
 
                lws_libev_io(context, wsi, LWS_EV_STOP | LWS_EV_WRITE);
        }
@@ -207,6 +226,25 @@ libwebsocket_service_timeout_check(struct libwebsocket_context *context,
        return 0;
 }
 
+int lws_rxflow_cache(struct libwebsocket *wsi, unsigned char *buf, int n, int len)
+{
+       /* his RX is flowcontrolled, don't send remaining now */
+       if (wsi->rxflow_buffer) {
+               /* rxflow while we were spilling prev rxflow */
+               lwsl_info("stalling in existing rxflow buf\n");
+               return 1;
+       }
+
+       /* a new rxflow, buffer it and warn caller */
+       lwsl_info("new rxflow input buffer len %d\n", len - n);
+       wsi->rxflow_buffer = (unsigned char *)malloc(len - n);
+       wsi->rxflow_len = len - n;
+       wsi->rxflow_pos = 0;
+       memcpy(wsi->rxflow_buffer, buf + n, len - n);
+
+       return 0;
+}
+
 /**
  * libwebsocket_service_fd() - Service polled socket with something waiting
  * @context:   Websocket context
@@ -370,25 +408,25 @@ libwebsocket_service_fd(struct libwebsocket_context *context,
 
        case LWS_CONNMODE_WS_SERVING:
        case LWS_CONNMODE_WS_CLIENT:
+       case LWS_CONNMODE_HTTP2_SERVING:
 
                /* the guy requested a callback when it was OK to write */
 
                if ((pollfd->revents & LWS_POLLOUT) &&
-                       (wsi->state == WSI_STATE_ESTABLISHED ||
+                       (wsi->state == WSI_STATE_ESTABLISHED || wsi->state == WSI_STATE_HTTP2_ESTABLISHED || wsi->state == WSI_STATE_HTTP2_ESTABLISHED_PRE_SETTINGS ||
                                wsi->state == WSI_STATE_FLUSHING_STORED_SEND_BEFORE_CLOSE) &&
                           lws_handle_POLLOUT_event(context, wsi, pollfd)) {
                        lwsl_info("libwebsocket_service_fd: closing\n");
                        goto close_and_handled;
                }
 
-               if (wsi->u.ws.rxflow_buffer &&
-                             (wsi->u.ws.rxflow_change_to & LWS_RXFLOW_ALLOW)) {
+               if (wsi->rxflow_buffer &&
+                             (wsi->rxflow_change_to & LWS_RXFLOW_ALLOW)) {
                        lwsl_info("draining rxflow\n");
                        /* well, drain it */
-                       eff_buf.token = (char *)wsi->u.ws.rxflow_buffer +
-                                               wsi->u.ws.rxflow_pos;
-                       eff_buf.token_len = wsi->u.ws.rxflow_len -
-                                               wsi->u.ws.rxflow_pos;
+                       eff_buf.token = (char *)wsi->rxflow_buffer +
+                                               wsi->rxflow_pos;
+                       eff_buf.token_len = wsi->rxflow_len - wsi->rxflow_pos;
                        draining_flow = 1;
                        goto drain;
                }
@@ -458,11 +496,11 @@ drain:
                        eff_buf.token_len = 0;
                } while (more);
 
-               if (draining_flow && wsi->u.ws.rxflow_buffer &&
-                                wsi->u.ws.rxflow_pos == wsi->u.ws.rxflow_len) {
+               if (draining_flow && wsi->rxflow_buffer &&
+                                wsi->rxflow_pos == wsi->rxflow_len) {
                        lwsl_info("flow buffer: drained\n");
-                       free(wsi->u.ws.rxflow_buffer);
-                       wsi->u.ws.rxflow_buffer = NULL;
+                       free(wsi->rxflow_buffer);
+                       wsi->rxflow_buffer = NULL;
                        /* having drained the rxflow buffer, can rearm POLLIN */
                        n = _libwebsocket_rx_flow_control(wsi); /* n ignored, needed for NO_SERVER case */
                }
@@ -471,9 +509,6 @@ drain:
                        goto read_pending;
                break;
 
-       case LWS_CONNMODE_HTTP2_SERVING:
-               break;
-
        default:
 #ifdef LWS_NO_CLIENT
                break;
index 984c43a..b89d014 100644 (file)
@@ -221,7 +221,7 @@ static int callback_http(struct libwebsocket_context *context,
                        return -1;
                }
 
-               /* this server has no concept of directories */
+               /* this example server has no concept of directories */
                if (strchr((const char *)in + 1, '/')) {
                        libwebsockets_return_http_status(context, wsi,
                                                HTTP_STATUS_FORBIDDEN, NULL);
@@ -241,7 +241,7 @@ static int callback_http(struct libwebsocket_context *context,
 
                        /* well, let's demonstrate how to send the hard way */
 
-                       p = buffer;
+                       p = buffer + LWS_SEND_BUFFER_PRE_PADDING;
 
 #ifdef WIN32
                        pss->fd = open(leaf_path, O_RDONLY | _O_BINARY);
@@ -264,7 +264,7 @@ static int callback_http(struct libwebsocket_context *context,
                                "HTTP/1.0 200 OK\x0d\x0a"
                                "Server: libwebsockets\x0d\x0a"
                                "Content-Type: image/jpeg\x0d\x0a"
-                                       "Content-Length: %u\x0d\x0a\x0d\x0a",
+                               "Content-Length: %u\x0d\x0a\x0d\x0a",
                                        (unsigned int)stat_buf.st_size);
 
                        /*
@@ -274,8 +274,10 @@ static int callback_http(struct libwebsocket_context *context,
                         * (too small for partial)
                         */
 
-                       n = libwebsocket_write(wsi, buffer,
-                                  p - buffer, LWS_WRITE_HTTP);
+                       n = libwebsocket_write(wsi,
+                                              buffer + LWS_SEND_BUFFER_PRE_PADDING,
+                                              p - (buffer + LWS_SEND_BUFFER_PRE_PADDING),
+                                              LWS_WRITE_HTTP_HEADERS);
 
                        if (n < 0) {
                                close(pss->fd);
@@ -367,7 +369,8 @@ static int callback_http(struct libwebsocket_context *context,
                 */
 
                do {
-                       n = read(pss->fd, buffer, sizeof buffer);
+                       n = read(pss->fd, buffer + LWS_SEND_BUFFER_PRE_PADDING,
+                                sizeof (buffer) - LWS_SEND_BUFFER_PRE_PADDING);
                        /* problem reading, close conn */
                        if (n < 0)
                                goto bail;
@@ -375,10 +378,11 @@ static int callback_http(struct libwebsocket_context *context,
                        if (n == 0)
                                goto flush_bail;
                        /*
-                        * because it's HTTP and not websocket, don't need to take
-                        * care about pre and postamble
+                        * To support HTTP2, must take care about preamble space
                         */
-                       m = libwebsocket_write(wsi, buffer, n, LWS_WRITE_HTTP);
+                       m = libwebsocket_write(wsi,
+                                              buffer + LWS_SEND_BUFFER_PRE_PADDING,
+                                              n, LWS_WRITE_HTTP);
                        if (m < 0)
                                /* write failed, close conn */
                                goto bail;