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:
parent
cade614d16
commit
024eb6c80c
12 changed files with 798 additions and 234 deletions
|
@ -295,6 +295,7 @@ endif()
|
|||
|
||||
if (LWS_WITH_HTTP2)
|
||||
list(APPEND SOURCES
|
||||
lib/http2.c
|
||||
lib/ssl-http2.c
|
||||
)
|
||||
endif()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
324
lib/http2.c
Normal 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;
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
415
lib/server.c
415
lib/server.c
|
@ -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++]);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Reference in a new issue