http2 can keep upgraded connection up

Signed-off-by: Andy Green <andy.green@linaro.org>
diff --git a/lib/http2.c b/lib/http2.c
new file mode 100644
index 0000000..74f196a
--- /dev/null
+++ b/lib/http2.c
@@ -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