1
0
Fork 0
mirror of https://github.com/warmcat/libwebsockets.git synced 2025-03-09 00:00:04 +01:00

http2 can keep upgraded connection up

Signed-off-by: Andy Green <andy.green@linaro.org>
This commit is contained in:
Andy Green 2014-10-08 12:00:53 +08:00
parent cade614d16
commit 024eb6c80c
12 changed files with 798 additions and 234 deletions

View file

@ -295,6 +295,7 @@ endif()
if (LWS_WITH_HTTP2)
list(APPEND SOURCES
lib/http2.c
lib/ssl-http2.c
)
endif()

View file

@ -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

View 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;

324
lib/http2.c Normal file
View file

@ -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;
}

View 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);
}

View 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,

View 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:

View file

@ -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;
};
struct _lws_http2_related {
#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);
@ -687,6 +790,9 @@ LWS_EXTERN int
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);
@ -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);

View 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;

View 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_http_action(struct libwebsocket_context *context,
struct libwebsocket *wsi)
{
char *uri_ptr = NULL;
int uri_len = 0;
enum http_version request_version;
enum http_connection_type connection_type;
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;
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;
char content_length_str[32];
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++]);

View 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;

View 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;