if (LWS_WITH_HTTP2)
list(APPEND SOURCES
+ lib/http2.c
lib/ssl-http2.c
)
endif()
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
#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
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;
--- /dev/null
+/*
+ * 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
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) {
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 ||
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);
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;
}
{
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
LWS_WRITE_PING,
LWS_WRITE_PONG,
+ /* HTTP2 */
+
+ LWS_WRITE_HTTP_HEADERS,
+
/* flags */
LWS_WRITE_NO_FIN = 0x40,
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 */
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:
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 {
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,
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;
};
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;
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;
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 */
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 */
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 */
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;
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,
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);
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 *
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);
/* 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"
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;
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;
/* 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;
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;
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;
/* 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));
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)
/* 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
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 */
case LWS_CONNMODE_HTTP_SERVING:
case LWS_CONNMODE_HTTP_SERVING_ACCEPTED:
+ case LWS_CONNMODE_HTTP2_SERVING:
/* handle http headers coming in */
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"
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;
}
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;
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);
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;
}
/*
* 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++]);
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],
/* 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);
}
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
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;
}
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 */
}
goto read_pending;
break;
- case LWS_CONNMODE_HTTP2_SERVING:
- break;
-
default:
#ifdef LWS_NO_CLIENT
break;
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);
/* 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);
"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);
/*
* (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);
*/
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;
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;