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

h2 ws: fixes against chrome

This commit is contained in:
Andy Green 2018-03-11 11:26:06 +08:00
parent 0e39e7f5c6
commit 302f8fad82
15 changed files with 598 additions and 397 deletions

View file

@ -8,6 +8,23 @@ libwebsockets
News
----
## Lws has the first official ws-over-h2 server support
There's a new standard on the RFC track that enabled multiplexing ws connections
over an http/2 link. Compared to making individual tcp and tls connections for
each ws link back to the same server, this makes your site start up radically
faster, and since all the connections are in one tls tunnel, with much memory
reduction serverside.
To enable it on master you just need -DLWS_WITH_HTTP2=1 at cmake. No changes to
existing code are necessary for either http/2 (if you use the official header creation
apis if you return your own headers, as shown in the test apps for several versions)
or to take advantage of ws-over-h2. When built with http/2 support, it automatically
falls back to http/1 and traditional ws upgrade if that's all the client can handle.
Currently only Chrome Canary v67 supports this ws-over-h2 encapsulation but the other
browsers will catch up soon.
## New "minimal examples"
https://github.com/warmcat/libwebsockets/tree/master/minimal-examples

View file

@ -65,9 +65,12 @@ lws_read(struct lws *wsi, unsigned char *buf, lws_filepos_t len)
unsigned char *last_char, *oldbuf = buf;
lws_filepos_t body_chunk_len;
size_t n;
#if defined(LWS_WITH_HTTP2)
int m;
#endif
switch (wsi->state) {
#ifdef LWS_WITH_HTTP2
#if defined(LWS_WITH_HTTP2)
case LWSS_HTTP2_AWAIT_CLIENT_PREFACE:
case LWSS_HTTP2_ESTABLISHED_PRE_SETTINGS:
case LWSS_HTTP2_ESTABLISHED:
@ -113,10 +116,17 @@ lws_read(struct lws *wsi, unsigned char *buf, lws_filepos_t len)
* file transfers operate.
*/
if (lws_h2_parser(wsi, buf, len, &body_chunk_len)) {
m = lws_h2_parser(wsi, buf, len, &body_chunk_len);
if (m && m != 2) {
lwsl_debug("%s: http2_parser bailed\n", __func__);
goto bail;
}
if (m && m == 2) {
/* swsi has been closed */
buf += body_chunk_len;
len -= body_chunk_len;
goto read_ok;
}
/* account for what we're using in rxflow buffer */
if (wsi->rxflow_buffer) {
@ -277,12 +287,16 @@ postbody_completion:
case LWSS_AWAITING_CLOSE_ACK:
case LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION:
case LWSS_SHUTDOWN:
case LWSS_SHUTDOWN | _LSF_POLLOUT | _LSF_CCB:
if (lws_handshake_client(wsi, &buf, (size_t)len))
goto bail;
switch (wsi->mode) {
case LWSCM_WS_SERVING:
case LWSCM_HTTP2_WS_SERVING:
/*
* for h2 we are on the swsi
*/
if (lws_interpret_incoming_packet(wsi, &buf,
(size_t)len) < 0) {
lwsl_info("interpret_incoming_packet bailed\n");
@ -296,6 +310,11 @@ postbody_completion:
lwsl_debug("%s: LWSS_HTTP_DEFERRING_ACTION\n", __func__);
break;
case LWSS_DEAD_SOCKET:
lwsl_err("%s: Unhandled state LWSS_DEAD_SOCKET\n", __func__);
assert(0);
/* fallthru */
default:
lwsl_err("%s: Unhandled state %d\n", __func__, wsi->state);
goto bail;
@ -311,8 +330,10 @@ read_ok:
bail:
/*
* h2 / h2-ws calls us recursively in lws_read()->lws_h2_parser()->
* lws_read() pattern. Make sure that only the outer lws_read() does
* the wsi close.
* lws_read() pattern, having stripped the h2 framing in the middle.
*
* When taking down the whole connection, make sure that only the
* outer lws_read() does the wsi close.
*/
if (!wsi->outer_will_close)
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "lws_read bail");

View file

@ -99,6 +99,8 @@ static const char * const h2_setting_names[] = {
"H2SET_INITIAL_WINDOW_SIZE",
"H2SET_MAX_FRAME_SIZE",
"H2SET_MAX_HEADER_LIST_SIZE",
"reserved",
"H2SET_ENABLE_CONNECT_PROTOCOL"
};
void
@ -465,6 +467,9 @@ int lws_h2_frame_write(struct lws *wsi, int type, int flags,
unsigned char *p = &buf[-LWS_H2_FRAME_HEADER_LENGTH];
int n;
//if (wsi->h2_stream_carries_ws)
// lwsl_hexdump_level(LLL_NOTICE, buf, len);
*p++ = len >> 16;
*p++ = len >> 8;
*p++ = len;
@ -658,7 +663,7 @@ int lws_h2_do_pps_send(struct lws *wsi)
break;
case LWS_H2_PPS_UPDATE_WINDOW:
lwsl_notice("Issuing LWS_H2_PPS_UPDATE_WINDOW: sid %d: add %d\n",
lwsl_debug("Issuing LWS_H2_PPS_UPDATE_WINDOW: sid %d: add %d\n",
pps->u.update_window.sid,
pps->u.update_window.credit);
*p++ = pps->u.update_window.credit >> 24;
@ -714,10 +719,8 @@ lws_h2_parse_frame_header(struct lws *wsi)
}
/* let the network wsi live a bit longer if subs are active */
lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE, 31);
/* let the network wsi live a bit longer if subs are active */
lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE, 31);
if (!wsi->ws_over_h2_count)
lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE, 31);
if (h2n->sid)
h2n->swsi = lws_h2_wsi_from_id(wsi, h2n->sid);
@ -737,6 +740,7 @@ lws_h2_parse_frame_header(struct lws *wsi)
* peer sent us something bigger than we told
* it we would allow
*/
lwsl_notice("received oversize frame %d\n", h2n->length);
lws_h2_goaway(wsi, H2_ERR_FRAME_SIZE_ERROR,
"Peer ignored our frame size setting");
return 0;
@ -749,9 +753,14 @@ lws_h2_parse_frame_header(struct lws *wsi)
else {
/* if it's data, either way no swsi means CLOSED state */
if (h2n->type == LWS_H2_FRAME_TYPE_DATA) {
lws_h2_goaway(wsi, H2_ERR_STREAM_CLOSED,
if (h2n->sid <= h2n->highest_sid_opened) {
lwsl_notice("ignoring straggling data\n");
h2n->type = LWS_H2_FRAME_TYPE_COUNT; /* ie, IGNORE */
} else {
lws_h2_goaway(wsi, H2_ERR_STREAM_CLOSED,
"Data for nonexistent sid");
return 0;
return 0;
}
}
/* if the sid is credible, treat as wsi for it closed */
if (h2n->sid > h2n->highest_sid_opened &&
@ -838,6 +847,7 @@ lws_h2_parse_frame_header(struct lws *wsi)
break;
case LWS_H2_FRAME_TYPE_GOAWAY:
lwsl_debug("LWS_H2_FRAME_TYPE_GOAWAY received\n");
break;
case LWS_H2_FRAME_TYPE_RST_STREAM:
@ -1023,6 +1033,8 @@ update_end_headers:
}
lwsl_info("LWS_H2_FRAME_TYPE_WINDOW_UPDATE\n");
break;
case LWS_H2_FRAME_TYPE_COUNT:
break;
default:
lwsl_info("%s: ILLEGAL FRAME TYPE %d\n", __func__, h2n->type);
h2n->type = LWS_H2_FRAME_TYPE_COUNT; /* ie, IGNORE */
@ -1303,6 +1315,11 @@ lws_h2_parse_end_of_frame(struct lws *wsi)
return 1;
case LWS_H2_FRAME_TYPE_RST_STREAM:
lwsl_info("LWS_H2_FRAME_TYPE_RST_STREAM: sid %d: reason 0x%x\n",
h2n->sid, h2n->hpack_e_dep);
break;
case LWS_H2_FRAME_TYPE_COUNT: /* IGNORING FRAME */
break;
}
@ -1322,6 +1339,9 @@ lws_h2_parse_end_of_frame(struct lws *wsi)
* Therefore if we will send non-PPS, ie, lws_http_action() for a stream
* wsi, we must change its state and handle it as a priority in the
* POLLOUT handler instead of writing it here.
*
* About closing... for the main network wsi, it should return nonzero to
* close it all. If it needs to close an swsi, it can do it here.
*/
int
lws_h2_parser(struct lws *wsi, unsigned char *in, lws_filepos_t inlen,
@ -1468,7 +1488,8 @@ lws_h2_parser(struct lws *wsi, unsigned char *in, lws_filepos_t inlen,
/* let the network wsi live a bit longer if subs are active...
* our frame may take a long time to chew through */
lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE, 31);
if (!wsi->ws_over_h2_count)
lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE, 31);
if (!h2n->swsi)
break;
@ -1505,8 +1526,13 @@ lws_h2_parser(struct lws *wsi, unsigned char *in, lws_filepos_t inlen,
* can return 0 in POST body with content len
* exhausted somehow.
*/
if (n <= 0)
goto fail;
if (n <= 0) {
in += h2n->length - h2n->count;
h2n->inside = h2n->length;
h2n->count = h2n->length - 1;
lwsl_debug("%s: lws_read told %d\n", __func__, n);
goto close_swsi_and_return;
}
inlen -= n - 1;
in += n - 1;
@ -1562,6 +1588,8 @@ lws_h2_parser(struct lws *wsi, unsigned char *in, lws_filepos_t inlen,
break;
case LWS_H2_FRAME_TYPE_RST_STREAM:
h2n->hpack_e_dep <<= 8;
h2n->hpack_e_dep |= c;
break;
case LWS_H2_FRAME_TYPE_PUSH_PROMISE:
@ -1593,7 +1621,8 @@ lws_h2_parser(struct lws *wsi, unsigned char *in, lws_filepos_t inlen,
frame_end:
if (h2n->count > h2n->length)
lwsl_notice("%d %d\n", h2n->count, h2n->length);
lwsl_notice("%s: count > length %d %d\n",
__func__, h2n->count, h2n->length);
if (h2n->count != h2n->length)
break;
@ -1651,6 +1680,17 @@ try_frame_start:
return 0;
close_swsi_and_return:
lws_close_free_wsi(h2n->swsi, 0, "close_swsi_and_return");
h2n->swsi = NULL;
h2n->frame_state = 0;
h2n->count = 0;
*inused = in - oldin;
return 2;
fail:
*inused = in - oldin;
@ -1660,10 +1700,11 @@ fail:
int
lws_h2_ws_handshake(struct lws *wsi)
{
uint8_t buf[256], *p = buf, *start = p, *end = &buf[sizeof(buf) - 1];
uint8_t buf[LWS_PRE + 384], *p = buf + LWS_PRE, *start = p,
*end = &buf[sizeof(buf) - 1];
const struct lws_http_mount *hit;
const char * uri_ptr;
int n;
int n, m;
if (lws_add_http_header_status(wsi, HTTP_STATUS_OK, &p, end))
return -1;
@ -1678,18 +1719,18 @@ lws_h2_ws_handshake(struct lws *wsi)
wsi->protocol->name && wsi->protocol->name[0]) {
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_PROTOCOL,
(unsigned char *)wsi->protocol->name,
(int)strlen(wsi->protocol->name), &p, end))
(int)strlen(wsi->protocol->name),
&p, end))
return -1;
}
if (lws_finalize_http_header(wsi, &p, end))
return -1;
n = lws_write(wsi, start, lws_ptr_diff(p, start),
LWS_WRITE_HTTP_HEADERS);
if (n != lws_ptr_diff(p, start)) {
lwsl_err("_write returned %d from %d\n", n,
lws_ptr_diff(p, start));
m = lws_ptr_diff(p, start);
n = lws_write(wsi, start, m, LWS_WRITE_HTTP_HEADERS);
if (n != m) {
lwsl_err("_write returned %d from %d\n", n, m);
return -1;
}

View file

@ -389,7 +389,7 @@ __lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason, const char *
struct lws_tokens eff_buf;
int n, m, ret;
lwsl_debug("%s: %p: caller: %s\n", __func__, wsi, caller);
lwsl_info("%s: %p: caller: %s\n", __func__, wsi, caller);
if (!wsi)
return;
@ -411,88 +411,16 @@ __lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason, const char *
wsi2->parent = NULL;
/* stop it doing shutdown processing */
wsi2->socket_is_permanently_unusable = 1;
lws_close_free_wsi(wsi2, reason, "general child recurse");
__lws_close_free_wsi(wsi2, reason, "general child recurse");
wsi2 = wsi1;
}
wsi->child_list = NULL;
}
#if defined(LWS_WITH_HTTP2)
if (wsi->h2.parent_wsi) {
lwsl_info(" wsi: %p, his parent %p: siblings:\n", wsi,
wsi->h2.parent_wsi);
lws_start_foreach_llp(struct lws **, w,
wsi->h2.parent_wsi->h2.child_list) {
lwsl_info(" \\---- child %p\n", *w);
} lws_end_foreach_llp(w, h2.sibling_list);
}
if (wsi->upgraded_to_http2 || wsi->http2_substream) {
lwsl_info("closing %p: parent %p\n", wsi, wsi->h2.parent_wsi);
if (wsi->h2.child_list) {
lwsl_info(" parent %p: closing children: list:\n", wsi);
lws_start_foreach_llp(struct lws **, w,
wsi->h2.child_list) {
lwsl_info(" \\---- child %p\n", *w);
} lws_end_foreach_llp(w, h2.sibling_list);
/* trigger closing of all of our http2 children first */
lws_start_foreach_llp(struct lws **, w,
wsi->h2.child_list) {
lwsl_info(" closing child %p\n", *w);
/* disconnect from siblings */
wsi2 = (*w)->h2.sibling_list;
(*w)->h2.sibling_list = NULL;
(*w)->socket_is_permanently_unusable = 1;
lws_close_free_wsi(*w, reason, "h2 child recurse");
*w = wsi2;
continue;
} lws_end_foreach_llp(w, h2.sibling_list);
}
}
if (wsi->upgraded_to_http2) {
/* remove pps */
struct lws_h2_protocol_send *w = wsi->h2.h2n->pps, *w1;
while (w) {
w1 = w->next;
free(w);
w = w1;
}
wsi->h2.h2n->pps = NULL;
}
if (wsi->http2_substream && wsi->h2.parent_wsi) {
lwsl_info(" %p: disentangling from siblings\n", wsi);
lws_start_foreach_llp(struct lws **, w,
wsi->h2.parent_wsi->h2.child_list) {
/* disconnect from siblings */
if (*w == wsi) {
wsi2 = (*w)->h2.sibling_list;
(*w)->h2.sibling_list = NULL;
*w = wsi2;
lwsl_info(" %p disentangled from sibling %p\n",
wsi, wsi2);
break;
}
} lws_end_foreach_llp(w, h2.sibling_list);
wsi->h2.parent_wsi->h2.child_count--;
wsi->h2.parent_wsi = NULL;
if (wsi->h2.pending_status_body)
lws_free_set_NULL(wsi->h2.pending_status_body);
}
if (wsi->upgraded_to_http2 && wsi->h2.h2n &&
wsi->h2.h2n->rx_scratch)
lws_free_set_NULL(wsi->h2.h2n->rx_scratch);
#endif
if (wsi->mode == LWSCM_RAW_FILEDESC) {
lws_remove_child_from_any_parent(wsi);
__remove_wsi_socket_from_fds(wsi);
wsi->protocol->callback(wsi,
LWS_CALLBACK_RAW_CLOSE_FILE,
wsi->protocol->callback(wsi, LWS_CALLBACK_RAW_CLOSE_FILE,
wsi->user_space, NULL, 0);
goto async_close;
}
@ -563,7 +491,7 @@ __lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason, const char *
if (wsi->trunc_len) {
lwsl_info("%p: FLUSHING_STORED_SEND_BEFORE_CLOSE\n", wsi);
wsi->state = LWSS_FLUSHING_SEND_BEFORE_CLOSE;
lws_set_timeout(wsi,
__lws_set_timeout(wsi,
PENDING_FLUSH_STORED_SEND_BEFORE_CLOSE, 5);
return;
}
@ -662,7 +590,7 @@ __lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason, const char *
lwsl_debug("waiting for chance to send close\n");
wsi->waiting_to_send_close_frame = 1;
wsi->state = LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION;
lws_set_timeout(wsi, PENDING_TIMEOUT_CLOSE_SEND, 2);
__lws_set_timeout(wsi, PENDING_TIMEOUT_CLOSE_SEND, 5);
lws_callback_on_writable(wsi);
return;
@ -670,6 +598,90 @@ __lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason, const char *
just_kill_connection:
#if defined(LWS_WITH_HTTP2)
if (wsi->http2_substream && wsi->h2_stream_carries_ws)
lws_h2_rst_stream(wsi, 0, "none");
if (wsi->h2.parent_wsi) {
lwsl_info(" wsi: %p, his parent %p: siblings:\n", wsi,
wsi->h2.parent_wsi);
lws_start_foreach_llp(struct lws **, w,
wsi->h2.parent_wsi->h2.child_list) {
lwsl_info(" \\---- child %p\n", *w);
} lws_end_foreach_llp(w, h2.sibling_list);
}
if (wsi->upgraded_to_http2 || wsi->http2_substream) {
lwsl_info("closing %p: parent %p\n", wsi, wsi->h2.parent_wsi);
if (wsi->h2.child_list) {
lwsl_info(" parent %p: closing children: list:\n", wsi);
lws_start_foreach_llp(struct lws **, w,
wsi->h2.child_list) {
lwsl_info(" \\---- child %p\n", *w);
} lws_end_foreach_llp(w, h2.sibling_list);
/* trigger closing of all of our http2 children first */
lws_start_foreach_llp(struct lws **, w,
wsi->h2.child_list) {
lwsl_info(" closing child %p\n", *w);
/* disconnect from siblings */
wsi2 = (*w)->h2.sibling_list;
(*w)->h2.sibling_list = NULL;
(*w)->socket_is_permanently_unusable = 1;
__lws_close_free_wsi(*w, reason, "h2 child recurse");
*w = wsi2;
continue;
} lws_end_foreach_llp(w, h2.sibling_list);
}
}
if (wsi->upgraded_to_http2) {
/* remove pps */
struct lws_h2_protocol_send *w = wsi->h2.h2n->pps, *w1;
while (w) {
w1 = w->next;
free(w);
w = w1;
}
wsi->h2.h2n->pps = NULL;
}
if (wsi->http2_substream && wsi->h2.parent_wsi) {
lwsl_info(" %p: disentangling from siblings\n", wsi);
lws_start_foreach_llp(struct lws **, w,
wsi->h2.parent_wsi->h2.child_list) {
/* disconnect from siblings */
if (*w == wsi) {
wsi2 = (*w)->h2.sibling_list;
(*w)->h2.sibling_list = NULL;
*w = wsi2;
lwsl_info(" %p disentangled from sibling %p\n",
wsi, wsi2);
break;
}
} lws_end_foreach_llp(w, h2.sibling_list);
wsi->h2.parent_wsi->h2.child_count--;
wsi->h2.parent_wsi = NULL;
if (wsi->h2.pending_status_body)
lws_free_set_NULL(wsi->h2.pending_status_body);
}
if (wsi->h2_stream_carries_ws) {
struct lws *nwsi = lws_get_network_wsi(wsi);
nwsi->ws_over_h2_count++;
/* if no ws, then put a timeout on the parent wsi */
if (!nwsi->ws_over_h2_count)
__lws_set_timeout(nwsi,
PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE, 31);
}
if (wsi->upgraded_to_http2 && wsi->h2.h2n &&
wsi->h2.h2n->rx_scratch)
lws_free_set_NULL(wsi->h2.h2n->rx_scratch);
#endif
lws_remove_child_from_any_parent(wsi);
n = 0;
@ -719,7 +731,7 @@ just_kill_connection:
#ifdef LWS_OPENSSL_SUPPORT
if (lws_is_ssl(wsi) && wsi->ssl) {
n = 0;
switch (lws_tls_shutdown(wsi)) {
switch (__lws_tls_shutdown(wsi)) {
case LWS_SSL_CAPABLE_DONE:
case LWS_SSL_CAPABLE_ERROR:
case LWS_SSL_CAPABLE_MORE_SERVICE_READ:
@ -753,9 +765,9 @@ just_kill_connection:
lws_sockfd_valid(wsi->desc.sockfd) &&
wsi->state != ((wsi->state & ~0x1f) | LWSS_SHUTDOWN) &&
!LWS_LIBUV_ENABLED(context)) {
lws_change_pollfd(wsi, LWS_POLLOUT, LWS_POLLIN);
__lws_change_pollfd(wsi, LWS_POLLOUT, LWS_POLLIN);
wsi->state = (wsi->state & ~0x1f) | LWSS_SHUTDOWN;
lws_set_timeout(wsi, PENDING_TIMEOUT_SHUTDOWN_FLUSH,
__lws_set_timeout(wsi, PENDING_TIMEOUT_SHUTDOWN_FLUSH,
context->timeout_secs);
return;
@ -790,7 +802,9 @@ just_kill_connection:
lws_free_set_NULL(wsi->rxflow_buffer);
if (lws_state_is_ws(wsi->state_pre_close) ||
wsi->mode == LWSCM_WS_SERVING || wsi->mode == LWSCM_WS_CLIENT) {
wsi->mode == LWSCM_WS_SERVING ||
wsi->mode == LWSCM_HTTP2_WS_SERVING ||
wsi->mode == LWSCM_WS_CLIENT) {
if (wsi->ws->rx_draining_ext) {
struct lws **w = &pt->rx_draining_ext_list;
@ -833,8 +847,11 @@ just_kill_connection:
/* tell the user it's all over for this guy */
if (wsi->protocol && !wsi->told_user_closed && wsi->protocol->callback &&
wsi->mode != LWSCM_RAW && (wsi->state_pre_close & _LSF_CCB)) {
if (wsi->protocol &&
!wsi->told_user_closed &&
wsi->protocol->callback &&
wsi->mode != LWSCM_RAW &&
(wsi->state_pre_close & _LSF_CCB)) {
wsi->protocol->callback(wsi, LWS_CALLBACK_CLOSED,
wsi->user_space, NULL, 0);
} else if (wsi->mode == LWSCM_HTTP_SERVING_ACCEPTED) {
@ -2136,7 +2153,9 @@ lws_close_reason(struct lws *wsi, enum lws_close_status status,
unsigned char *p, *start;
int budget = sizeof(wsi->ws->ping_payload_buf) - LWS_PRE;
assert(wsi->mode == LWSCM_WS_SERVING || wsi->mode == LWSCM_WS_CLIENT);
assert(wsi->mode == LWSCM_WS_SERVING ||
wsi->mode == LWSCM_HTTP2_WS_SERVING ||
wsi->mode == LWSCM_WS_CLIENT);
start = p = &wsi->ws->ping_payload_buf[LWS_PRE];

View file

@ -347,10 +347,10 @@ lwsl_timestamp(int level, char *p, int len);
* lwsl_hexdump() - helper to hexdump a buffer
*
* \param level: one of LLL_ constants
* \param buf: buffer start to dump
* \param vbuf: buffer start to dump
* \param len: length of buffer to dump
*
* If \p level is visible, does a nice hexdump -C style dump of \p buf for
* If \p level is visible, does a nice hexdump -C style dump of \p vbuf for
* \p len bytes. This can be extremely convenient while debugging.
*/
LWS_VISIBLE LWS_EXTERN void
@ -925,7 +925,11 @@ enum lws_callback_reasons {
LWS_CALLBACK_ESTABLISHED = 0,
/**< (VH) after the server completes a handshake with an incoming
* client. If you built the library with ssl support, in is a
* pointer to the ssl struct associated with the connection or NULL.*/
* pointer to the ssl struct associated with the connection or NULL.
*
* b0 of len is set if the connection was made using ws-over-h2
*
* */
LWS_CALLBACK_CLIENT_CONNECTION_ERROR = 1,
/**< the request client connection has been unable to complete a
* handshake with the remote server. If in is non-NULL, you can

View file

@ -274,14 +274,15 @@ LWS_VISIBLE int lws_write(struct lws *wsi, unsigned char *buf, size_t len,
wp1f == LWS_WRITE_HTTP_HEADERS)
goto send_raw;
/* if not in a state to send stuff, then just send nothing */
/* if not in a state to send ws stuff, then just send nothing */
if (!lws_state_is_ws(wsi->state) &&
((wsi->state != LWSS_RETURNED_CLOSE_ALREADY &&
wsi->state != LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION &&
wsi->state != LWSS_AWAITING_CLOSE_ACK) ||
wp != LWS_WRITE_CLOSE)) {
lwsl_debug("binning\n");
wp1f != LWS_WRITE_CLOSE)) {
//assert(0);
lwsl_debug("binning %d %d\n", wsi->state, wp1f);
return 0;
}
@ -483,6 +484,12 @@ do_more_inside_frame:
send_raw:
switch (wp1f) {
case LWS_WRITE_TEXT:
case LWS_WRITE_BINARY:
case LWS_WRITE_CONTINUATION:
if (!wsi->h2_stream_carries_ws)
break;
/* fallthru */
case LWS_WRITE_CLOSE:
/* lwsl_hexdump(&buf[-pre], len); */
case LWS_WRITE_HTTP:
@ -895,6 +902,7 @@ lws_ssl_capable_write_no_ssl(struct lws *wsi, unsigned char *buf, int len)
lwsl_debug("ERROR writing len %d to skt fd %d err %d / errno %d\n",
len, wsi->desc.sockfd, n, LWS_ERRNO);
return LWS_SSL_CAPABLE_ERROR;
}
#endif

View file

@ -462,7 +462,7 @@ lws_callback_on_writable(struct lws *wsi)
#endif
#ifdef LWS_WITH_HTTP2
lwsl_info("%s: %p\n", __func__, wsi);
lwsl_info("%s: %p (mode %d)\n", __func__, wsi, wsi->mode);
if (wsi->mode != LWSCM_HTTP2_SERVING &&
wsi->mode != LWSCM_HTTP2_WS_SERVING)

View file

@ -509,7 +509,8 @@ enum lws_connection_states {
LWSS_HTTP2_ESTABLISHED = _LSF_CCB | 15 |
_LSF_POLLOUT,
LWSS_HTTP2_ESTABLISHED_WS = _LSF_CCB | 16 |
_LSF_WEBSOCKET,
_LSF_WEBSOCKET |
_LSF_POLLOUT,
LWSS_CGI = 17,
@ -520,7 +521,7 @@ enum lws_connection_states {
_LSF_POLLOUT,
};
#define lws_state_is_ws(s) (!!(s & _LSF_WEBSOCKET))
#define lws_state_is_ws(s) (!!((s) & _LSF_WEBSOCKET))
enum http_version {
HTTP_VERSION_1_0,
@ -872,6 +873,7 @@ struct lws_context_per_thread {
short ah_count_in_use;
unsigned char tid;
unsigned char lock_depth;
#if LWS_MAX_SMP > 1
pthread_t lock_owner;
#endif
@ -2018,6 +2020,7 @@ struct lws {
#if defined(LWS_WITH_STATS) && defined(LWS_OPENSSL_SUPPORT)
char seen_rx;
#endif
uint8_t ws_over_h2_count;
/* volatile to make sure code is aware other thread can change */
volatile char handling_pollout;
volatile char leave_pollout_active;
@ -2188,6 +2191,8 @@ user_callback_handle_rxflow(lws_callback_function, struct lws *wsi,
enum lws_callback_reasons reason, void *user,
void *in, size_t len);
#ifdef LWS_WITH_HTTP2
int
lws_h2_rst_stream(struct lws *wsi, uint32_t err, const char *reason);
struct lws * lws_h2_get_nth_child(struct lws *wsi, int n);
LWS_EXTERN void lws_h2_init(struct lws *wsi);
LWS_EXTERN int
@ -2428,7 +2433,7 @@ LWS_EXTERN enum lws_ssl_capable_status
lws_tls_server_abort_connection(struct lws *wsi);
LWS_EXTERN enum lws_ssl_capable_status
lws_tls_shutdown(struct lws *wsi);
__lws_tls_shutdown(struct lws *wsi);
LWS_EXTERN enum lws_ssl_capable_status
lws_tls_client_connect(struct lws *wsi);
@ -2477,8 +2482,8 @@ static LWS_INLINE void
lws_pt_lock(struct lws_context_per_thread *pt, const char *reason)
{
if (pt->lock_owner == pthread_self()) {
lwsl_err("tid %d: lock collision: already held for %s, reacquiring for %s\n", pt->tid, pt->last_lock_reason, reason);
assert(0);
pt->lock_depth++;
return;
}
pthread_mutex_lock(&pt->lock);
pt->last_lock_reason = reason;
@ -2489,6 +2494,10 @@ lws_pt_lock(struct lws_context_per_thread *pt, const char *reason)
static LWS_INLINE void
lws_pt_unlock(struct lws_context_per_thread *pt)
{
if (pt->lock_depth) {
pt->lock_depth--;
return;
}
pt->last_lock_reason ="free";
pt->lock_owner = 0;
//lwsl_notice("tid %d: unlock %s\n", pt->tid, pt->last_lock_reason);

View file

@ -1567,7 +1567,7 @@ spill:
switch (wsi->ws->opcode) {
case LWSWSOPC_CLOSE:
/* is this an acknowledgement of our close? */
/* is this an acknowledgment of our close? */
if (wsi->state == LWSS_AWAITING_CLOSE_ACK) {
/*
* fine he has told us he is closing too, let's
@ -1729,7 +1729,6 @@ drain_extension:
eff_buf.token[eff_buf.token_len] = '\0';
if (wsi->protocol->callback) {
if (callback_action == LWS_CALLBACK_RECEIVE_PONG)
lwsl_info("Doing pong callback\n");

View file

@ -716,6 +716,244 @@ int lws_clean_url(char *p)
return 0;
}
static int
lws_server_init_wsi_for_ws(struct lws *wsi)
{
int n;
wsi->state = LWSS_ESTABLISHED;
lws_restart_ws_ping_pong_timer(wsi);
/*
* create the frame buffer for this connection according to the
* size mentioned in the protocol definition. If 0 there, use
* a big default for compatibility
*/
n = (int)wsi->protocol->rx_buffer_size;
if (!n)
n = wsi->context->pt_serv_buf_size;
n += LWS_PRE;
wsi->ws->rx_ubuf = lws_malloc(n + 4 /* 0x0000ffff zlib */, "rx_ubuf");
if (!wsi->ws->rx_ubuf) {
lwsl_err("Out of Mem allocating rx buffer %d\n", n);
return 1;
}
wsi->ws->rx_ubuf_alloc = n;
lwsl_debug("Allocating RX buffer %d\n", n);
#if LWS_POSIX && !defined(LWS_WITH_ESP32)
if (!wsi->parent_carries_io &&
!wsi->h2_stream_carries_ws)
if (setsockopt(wsi->desc.sockfd, SOL_SOCKET, SO_SNDBUF,
(const char *)&n, sizeof n)) {
lwsl_warn("Failed to set SNDBUF to %d", n);
return 1;
}
#endif
/* notify user code that we're ready to roll */
if (wsi->protocol->callback)
if (wsi->protocol->callback(wsi, LWS_CALLBACK_ESTABLISHED,
wsi->user_space,
#ifdef LWS_OPENSSL_SUPPORT
wsi->ssl,
#else
NULL,
#endif
wsi->h2_stream_carries_ws))
return 1;
lwsl_debug("ws established\n");
return 0;
}
static int
lws_process_ws_upgrade(struct lws *wsi)
{
struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
char protocol_list[128], protocol_name[64], *p;
int protocol_len, hit, n = 0, non_space_char_found = 0;
if (!wsi->protocol)
lwsl_err("NULL protocol at lws_read\n");
/*
* It's either websocket or h2->websocket
*
* Select the first protocol we support from the list
* the client sent us.
*
* Copy it to remove header fragmentation
*/
if (lws_hdr_copy(wsi, protocol_list, sizeof(protocol_list) - 1,
WSI_TOKEN_PROTOCOL) < 0) {
lwsl_err("protocol list too long");
return 1;
}
protocol_len = lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL);
protocol_list[protocol_len] = '\0';
p = protocol_list;
hit = 0;
while (*p && !hit) {
n = 0;
non_space_char_found = 0;
while (n < (int)sizeof(protocol_name) - 1 &&
*p && *p != ',') {
/* ignore leading spaces */
if (!non_space_char_found && *p == ' ') {
n++;
continue;
}
non_space_char_found = 1;
protocol_name[n++] = *p++;
}
protocol_name[n] = '\0';
if (*p)
p++;
lwsl_debug("checking %s\n", protocol_name);
n = 0;
while (wsi->vhost->protocols[n].callback) {
lwsl_debug("try %s\n",
wsi->vhost->protocols[n].name);
if (wsi->vhost->protocols[n].name &&
!strcmp(wsi->vhost->protocols[n].name,
protocol_name)) {
wsi->protocol = &wsi->vhost->protocols[n];
hit = 1;
break;
}
n++;
}
}
/* we didn't find a protocol he wanted? */
if (!hit) {
if (lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL)) {
lwsl_notice("No protocol from \"%s\" supported\n",
protocol_list);
return 1;
}
/*
* some clients only have one protocol and
* do not send the protocol list header...
* allow it and match to the vhost's default
* protocol (which itself defaults to zero)
*/
lwsl_info("defaulting to prot handler %d\n",
wsi->vhost->default_protocol_index);
n = wsi->vhost->default_protocol_index;
wsi->protocol = &wsi->vhost->protocols[
(int)wsi->vhost->default_protocol_index];
}
/* allocate the ws struct for the wsi */
wsi->ws = lws_zalloc(sizeof(*wsi->ws), "ws struct");
if (!wsi->ws) {
lwsl_notice("OOM\n");
return 1;
}
if (lws_hdr_total_length(wsi, WSI_TOKEN_VERSION))
wsi->ws->ietf_spec_revision =
atoi(lws_hdr_simple_ptr(wsi, WSI_TOKEN_VERSION));
/* allocate wsi->user storage */
if (lws_ensure_user_space(wsi)) {
lwsl_notice("problem with user space\n");
return 1;
}
/*
* Give the user code a chance to study the request and
* have the opportunity to deny it
*/
if ((wsi->protocol->callback)(wsi,
LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION,
wsi->user_space,
lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL), 0)) {
lwsl_warn("User code denied connection\n");
return 1;
}
/*
* Perform the handshake according to the protocol version the
* client announced
*/
switch (wsi->ws->ietf_spec_revision) {
default:
lwsl_notice("Unknown client spec version %d\n",
wsi->ws->ietf_spec_revision);
wsi->ws->ietf_spec_revision = 13;
//return 1;
/* fallthru */
case 13:
#if defined(LWS_WITH_HTTP2)
if (wsi->h2_stream_carries_ws) {
if (lws_h2_ws_handshake(wsi)) {
lwsl_notice("h2 ws handshake failed\n");
return 1;
}
} else
#endif
{
lwsl_parser("lws_parse calling handshake_04\n");
if (handshake_0405(wsi->context, wsi)) {
lwsl_notice("hs0405 has failed the connection\n");
return 1;
}
}
break;
}
lws_same_vh_protocol_insert(wsi, n);
/* we are upgrading to ws, so http/1.1 + h2 and keepalive +
* pipelined header considerations about keeping the ah around
* no longer apply. However it's common for the first ws
* protocol data to have been coalesced with the browser
* upgrade request and to already be in the ah rx buffer.
*/
lwsl_debug("%s: %p: inheriting ws ah (rxpos:%d, rxlen:%d)\n",
__func__, wsi, wsi->ah->rxpos, wsi->ah->rxlen);
lws_pt_lock(pt, __func__);
if (wsi->h2_stream_carries_ws)
lws_union_transition(wsi, LWSCM_HTTP2_WS_SERVING);
else
lws_union_transition(wsi, LWSCM_WS_SERVING);
/*
* Because rxpos/rxlen shows something in the ah, we will get
* service guaranteed next time around the event loop
*/
lws_pt_unlock(pt);
lws_server_init_wsi_for_ws(wsi);
lwsl_parser("accepted v%02d connection\n",
wsi->ws->ietf_spec_revision);
/* !!! drop ah unreservedly after ESTABLISHED */
if (wsi->ah->rxpos == wsi->ah->rxlen ) {
lws_header_table_force_to_detachable_state(wsi);
lws_header_table_detach(wsi, 1);
}
return 0;
}
static const unsigned char methods[] = {
WSI_TOKEN_GET_URI,
@ -773,6 +1011,9 @@ lws_http_action(struct lws *wsi)
unsigned int n;
char http_version_str[10];
char http_conn_str[20];
#if defined(LWS_WITH_HTTP2)
char *p;
#endif
int http_version_len;
char *uri_ptr = NULL, *s;
int uri_len = 0, meth;
@ -795,6 +1036,37 @@ lws_http_action(struct lws *wsi)
lwsl_info("Method: '%s' (%d), request for '%s'\n", method_names[meth],
meth, uri_ptr);
#if defined(LWS_WITH_HTTP2)
/*
* with H2 there's also a way to upgrade a stream to something
* else... :method is CONNECT and :protocol says the name of
* the new protocol we want to carry. We have to have sent a
* SETTINGS saying that we support it though.
*/
p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_METHOD);
if (wsi->vhost->set.s[H2SET_ENABLE_CONNECT_PROTOCOL] &&
wsi->http2_substream && p && !strcmp(p, "CONNECT")) {
p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_COLON_PROTOCOL);
if (p && !strcmp(p, "websocket")) {
struct lws *nwsi = lws_get_network_wsi(wsi);
wsi->vhost->conn_stats.ws_upg++;
lwsl_info("Upgrade h2 to ws\n");
wsi->h2_stream_carries_ws = 1;
nwsi->ws_over_h2_count++;
if (lws_process_ws_upgrade(wsi))
goto bail_nuke_ah;
if (nwsi->ws_over_h2_count == 1)
lws_set_timeout(nwsi, NO_PENDING_TIMEOUT, 0);
lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
lwsl_info("Upgraded h2 to ws OK\n");
return 0;
}
}
#endif
if (lws_ensure_user_space(wsi))
goto bail_nuke_ah;
@ -1264,69 +1536,16 @@ transaction_result_n:
#endif
}
static int
lws_server_init_wsi_for_ws(struct lws *wsi)
{
int n;
wsi->state = LWSS_ESTABLISHED;
lws_restart_ws_ping_pong_timer(wsi);
/*
* create the frame buffer for this connection according to the
* size mentioned in the protocol definition. If 0 there, use
* a big default for compatibility
*/
n = (int)wsi->protocol->rx_buffer_size;
if (!n)
n = wsi->context->pt_serv_buf_size;
n += LWS_PRE;
wsi->ws->rx_ubuf = lws_malloc(n + 4 /* 0x0000ffff zlib */, "rx_ubuf");
if (!wsi->ws->rx_ubuf) {
lwsl_err("Out of Mem allocating rx buffer %d\n", n);
return 1;
}
wsi->ws->rx_ubuf_alloc = n;
lwsl_debug("Allocating RX buffer %d\n", n);
#if LWS_POSIX && !defined(LWS_WITH_ESP32)
if (!wsi->parent_carries_io &&
!wsi->h2_stream_carries_ws)
if (setsockopt(wsi->desc.sockfd, SOL_SOCKET, SO_SNDBUF,
(const char *)&n, sizeof n)) {
lwsl_warn("Failed to set SNDBUF to %d", n);
return 1;
}
#endif
/* notify user code that we're ready to roll */
if (wsi->protocol->callback)
if (wsi->protocol->callback(wsi, LWS_CALLBACK_ESTABLISHED,
wsi->user_space,
#ifdef LWS_OPENSSL_SUPPORT
wsi->ssl,
#else
NULL,
#endif
0))
return 1;
return 0;
}
int
lws_handshake_server(struct lws *wsi, unsigned char **buf, size_t len)
{
struct lws_context *context = lws_get_context(wsi);
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
int protocol_len, n = 0, hit, non_space_char_found = 0, m, i;
unsigned char *obuf = *buf;
char protocol_list[128];
char protocol_name[64];
#if defined(LWS_WITH_HTTP2)
char tbuf[128], *p;
#endif
size_t olen = len;
char *p;
int n = 0, m, i;
if (len >= 10000000) {
lwsl_err("%s: assert: len %ld\n", __func__, (long)len);
@ -1349,6 +1568,7 @@ lws_handshake_server(struct lws *wsi, unsigned char **buf, size_t len)
i = (int)len;
m = lws_parse(wsi, *buf, &i);
(*buf) += (int)len - i;
len = i;
if (m) {
if (m == 2) {
/*
@ -1489,27 +1709,6 @@ raw_transition:
goto bail_nuke_ah;
}
#if defined(LWS_WITH_HTTP2)
/*
* with H2 there's also a way to upgrade a stream to something
* else... :method is CONNECT and :protocol says the name of
* the new protocol we want to carry. We have to have sent a
* SETTINGS saying that we support it though.
*/
p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_METHOD);
if (wsi->h2.h2n &&
wsi->h2.h2n->set.s[H2SET_ENABLE_CONNECT_PROTOCOL] &&
p && !strcmp(p, "CONNECT")) {
p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_COLON_PROTOCOL);
if (p && !strcmp(p, "websocket")) {
wsi->vhost->conn_stats.ws_upg++;
lwsl_info("Upgrade h2 to ws\n");
wsi->h2_stream_carries_ws = 1;
goto upgrade_ws;
}
}
#endif
/* no upgrade ack... he remained as HTTP */
lwsl_info("No upgrade\n");
@ -1536,8 +1735,7 @@ upgrade_h2c:
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));
n = lws_b64_decode_string(p, tbuf, sizeof(tbuf));
if (n < 0) {
lwsl_parser("HTTP2_SETTINGS too long\n");
return 1;
@ -1558,18 +1756,17 @@ upgrade_h2c:
/* HTTP2 union */
lws_h2_settings(wsi, &wsi->h2.h2n->set,
(unsigned char *)protocol_list, n);
lws_h2_settings(wsi, &wsi->h2.h2n->set, (unsigned char *)tbuf, n);
lws_hpack_dynamic_size(wsi, wsi->h2.h2n->set.s[
H2SET_HEADER_TABLE_SIZE]);
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 != (int)strlen(protocol_list)) {
strcpy(tbuf, "HTTP/1.1 101 Switching Protocols\x0d\x0a"
"Connection: Upgrade\x0d\x0a"
"Upgrade: h2c\x0d\x0a\x0d\x0a");
m = (int)strlen(tbuf);
n = lws_issue_raw(wsi, (unsigned char *)tbuf, m);
if (n != m) {
lwsl_debug("http2 switch: ERROR writing to socket\n");
return 1;
}
@ -1581,177 +1778,11 @@ upgrade_h2c:
#endif
upgrade_ws:
if (!wsi->protocol)
lwsl_err("NULL protocol at lws_read\n");
/*
* It's either websocket or h2->websocket
*
* Select the first protocol we support from the list
* the client sent us.
*
* Copy it to remove header fragmentation
*/
if (lws_hdr_copy(wsi, protocol_list, sizeof(protocol_list) - 1,
WSI_TOKEN_PROTOCOL) < 0) {
lwsl_err("protocol list too long");
if (lws_process_ws_upgrade(wsi))
goto bail_nuke_ah;
}
protocol_len = lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL);
protocol_list[protocol_len] = '\0';
p = protocol_list;
hit = 0;
while (*p && !hit) {
n = 0;
non_space_char_found = 0;
while (n < (int)sizeof(protocol_name) - 1 &&
*p && *p != ',') {
/* ignore leading spaces */
if (!non_space_char_found && *p == ' ') {
n++;
continue;
}
non_space_char_found = 1;
protocol_name[n++] = *p++;
}
protocol_name[n] = '\0';
if (*p)
p++;
lwsl_info("checking %s\n", protocol_name);
n = 0;
while (wsi->vhost->protocols[n].callback) {
lwsl_info("try %s\n",
wsi->vhost->protocols[n].name);
if (wsi->vhost->protocols[n].name &&
!strcmp(wsi->vhost->protocols[n].name,
protocol_name)) {
wsi->protocol = &wsi->vhost->protocols[n];
hit = 1;
break;
}
n++;
}
}
/* we didn't find a protocol he wanted? */
if (!hit) {
if (lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL)) {
lwsl_info("No protocol from \"%s\" supported\n",
protocol_list);
goto bail_nuke_ah;
}
/*
* some clients only have one protocol and
* do not send the protocol list header...
* allow it and match to the vhost's default
* protocol (which itself defaults to zero)
*/
lwsl_info("defaulting to prot handler %d\n",
wsi->vhost->default_protocol_index);
n = wsi->vhost->default_protocol_index;
wsi->protocol = &wsi->vhost->protocols[
(int)wsi->vhost->default_protocol_index];
}
/* allocate the ws struct for the wsi */
wsi->ws = lws_zalloc(sizeof(*wsi->ws), "ws struct");
if (!wsi->ws) {
lwsl_notice("OOM\n");
goto bail_nuke_ah;
}
/* set it from the parser temp */
wsi->ws->ietf_spec_revision = wsi->rx_frame_type;
/* allocate wsi->user storage */
if (lws_ensure_user_space(wsi))
goto bail_nuke_ah;
/*
* Give the user code a chance to study the request and
* have the opportunity to deny it
*/
if ((wsi->protocol->callback)(wsi,
LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION,
wsi->user_space,
lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL), 0)) {
lwsl_warn("User code denied connection\n");
goto bail_nuke_ah;
}
/*
* Perform the handshake according to the protocol version the
* client announced
*/
switch (wsi->ws->ietf_spec_revision) {
case 13:
#if defined(LWS_WITH_HTTP2)
if (wsi->h2_stream_carries_ws) {
if (lws_h2_ws_handshake(wsi)) {
lwsl_info("h2 ws handshake failed\n");
goto bail_nuke_ah;
}
} else
#endif
{
lwsl_parser("lws_parse calling handshake_04\n");
if (handshake_0405(context, wsi)) {
lwsl_info("hs0405 has failed the connection\n");
goto bail_nuke_ah;
}
}
break;
default:
lwsl_info("Unknown client spec version %d\n",
wsi->ws->ietf_spec_revision);
goto bail_nuke_ah;
}
lws_same_vh_protocol_insert(wsi, n);
/* we are upgrading to ws, so http/1.1 + h2 and keepalive +
* pipelined header considerations about keeping the ah around
* no longer apply. However it's common for the first ws
* protocol data to have been coalesced with the browser
* upgrade request and to already be in the ah rx buffer.
*/
lwsl_info("%s: %p: inheriting ws ah (rxpos:%d, rxlen:%d)\n",
__func__, wsi, wsi->ah->rxpos, wsi->ah->rxlen);
lws_pt_lock(pt, __func__);
if (wsi->h2_stream_carries_ws)
lws_union_transition(wsi, LWSCM_HTTP2_WS_SERVING);
else
lws_union_transition(wsi, LWSCM_WS_SERVING);
/*
* Because rxpos/rxlen shows something in the ah, we will get
* service guaranteed next time around the event loop
*/
lws_pt_unlock(pt);
lws_server_init_wsi_for_ws(wsi);
lwsl_parser("accepted v%02d connection\n",
wsi->ws->ietf_spec_revision);
/* !!! drop ah unreservedly after ESTABLISHED */
if (wsi->ah->rxpos == wsi->ah->rxlen ) {
lws_header_table_force_to_detachable_state(wsi);
lws_header_table_detach(wsi, 1);
}
return 0;
} /* while all chars are handled */
return 0;

View file

@ -54,6 +54,7 @@ lws_calllback_as_writeable(struct lws *wsi)
case LWSCM_WSCL_ISSUE_HTTP_BODY:
n = LWS_CALLBACK_CLIENT_HTTP_WRITEABLE;
break;
case LWSCM_HTTP2_WS_SERVING:
case LWSCM_WS_SERVING:
n = LWS_CALLBACK_SERVER_WRITEABLE;
break;
@ -162,7 +163,7 @@ lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd)
LWS_WRITE_CLOSE);
if (n >= 0) {
wsi->state = LWSS_AWAITING_CLOSE_ACK;
lws_set_timeout(wsi, PENDING_TIMEOUT_CLOSE_ACK, 1);
lws_set_timeout(wsi, PENDING_TIMEOUT_CLOSE_ACK, 5);
lwsl_debug("sent close indication, awaiting ack\n");
goto bail_ok;
@ -187,9 +188,11 @@ lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd)
/* well he is sent, mark him done */
wsi->ws->ping_pending_flag = 0;
if (wsi->ws->payload_is_close)
if (wsi->ws->payload_is_close) {
// assert(0);
/* oh... a close frame was it... then we are done */
goto bail_die;
}
/* otherwise for PING, leave POLLOUT active either way */
goto bail_ok;
@ -412,9 +415,9 @@ user_service_go_again:
wsi2a = wsi->h2.child_list;
while (wsi2a) {
if (wsi2a->h2.requested_POLLOUT)
lwsl_debug(" * %p\n", wsi2a);
lwsl_debug(" * %p %s\n", wsi2a, wsi2a->protocol->name);
else
lwsl_debug(" %p\n", wsi2a);
lwsl_debug(" %p %s\n", wsi2a, wsi2a->protocol->name);
wsi2a = wsi2a->h2.sibling_list;
}
@ -427,10 +430,8 @@ user_service_go_again:
struct lws *w, **wa;
wa = &(*wsi2)->h2.sibling_list;
if (!(*wsi2)->h2.requested_POLLOUT) {
lwsl_debug(" child %p doesn't want POLLOUT\n", *wsi2);
if (!(*wsi2)->h2.requested_POLLOUT)
goto next_child;
}
/*
* we're going to do writable callback for this child.
@ -545,6 +546,57 @@ user_service_go_again:
goto next_child;
}
/* Notify peer that we decided to close */
if (w->state == LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION) {
lwsl_debug("sending close packet\n");
w->waiting_to_send_close_frame = 0;
n = lws_write(w, &w->ws->ping_payload_buf[LWS_PRE],
w->ws->close_in_ping_buffer_len,
LWS_WRITE_CLOSE);
if (n >= 0) {
w->state = LWSS_AWAITING_CLOSE_ACK;
lws_set_timeout(w, PENDING_TIMEOUT_CLOSE_ACK, 5);
lwsl_debug("sent close indication, awaiting ack\n");
}
goto next_child;
}
/* Acknowledge receipt of peer's notification he closed,
* then logically close ourself */
if ((lws_state_is_ws(w->state) && w->ws->ping_pending_flag) ||
(w->state == LWSS_RETURNED_CLOSE_ALREADY &&
w->ws->payload_is_close)) {
if (w->ws->payload_is_close)
write_type = LWS_WRITE_CLOSE | LWS_WRITE_H2_STREAM_END;
n = lws_write(w, &w->ws->ping_payload_buf[LWS_PRE],
w->ws->ping_payload_len, write_type);
if (n < 0)
goto bail_die;
/* well he is sent, mark him done */
w->ws->ping_pending_flag = 0;
if (w->ws->payload_is_close) {
/* oh... a close frame was it... then we are done */
lwsl_debug("Acknowledged peer's close packet\n");
w->ws->payload_is_close = 0;
w->state = LWSS_RETURNED_CLOSE_ALREADY;
lws_close_free_wsi(w, LWS_CLOSE_STATUS_NOSTATUS, "returned close packet");
wa = &wsi->h2.child_list;
goto next_child;
}
lws_callback_on_writable(w);
(w)->h2.requested_POLLOUT = 1;
/* otherwise for PING, leave POLLOUT active either way */
goto next_child;
}
if (lws_calllback_as_writeable(w) || w->h2.send_END_STREAM) {
lwsl_debug("Closing POLLOUT child\n");
lws_close_free_wsi(w, LWS_CLOSE_STATUS_NOSTATUS, "h2 pollout handle");
@ -638,7 +690,7 @@ __lws_service_timeout_check(struct lws *wsi, time_t sec)
if (wsi->protocol &&
wsi->protocol->callback(wsi, LWS_CALLBACK_TIMER,
wsi->user_space, NULL, 0)) {
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS,
__lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS,
"timer cb errored");
return 1;
@ -1382,7 +1434,7 @@ lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd,
#ifdef LWS_OPENSSL_SUPPORT
if (wsi->state == LWSS_SHUTDOWN && lws_is_ssl(wsi) && wsi->ssl) {
n = 0;
switch (lws_tls_shutdown(wsi)) {
switch (__lws_tls_shutdown(wsi)) {
case LWS_SSL_CAPABLE_DONE:
case LWS_SSL_CAPABLE_ERROR:
goto close_and_handled;

View file

@ -278,7 +278,7 @@ lws_tls_server_new_nonblocking(struct lws *wsi, lws_sockfd_type accept_fd)
int
lws_tls_server_abort_connection(struct lws *wsi)
{
lws_tls_shutdown(wsi);
__lws_tls_shutdown(wsi);
SSL_free(wsi->ssl);
return 0;

View file

@ -302,7 +302,7 @@ lws_tls_ctx_from_wsi(struct lws *wsi)
}
enum lws_ssl_capable_status
lws_tls_shutdown(struct lws *wsi)
__lws_tls_shutdown(struct lws *wsi)
{
int n = SSL_shutdown(wsi->ssl);
@ -314,7 +314,7 @@ lws_tls_shutdown(struct lws *wsi)
return LWS_SSL_CAPABLE_DONE;
case 0: /* needs a retry */
lws_change_pollfd(wsi, 0, LWS_POLLIN);
__lws_change_pollfd(wsi, 0, LWS_POLLIN);
return LWS_SSL_CAPABLE_MORE_SERVICE;
default: /* fatal error, or WANT */
@ -322,12 +322,12 @@ lws_tls_shutdown(struct lws *wsi)
if (n != SSL_ERROR_SYSCALL && n != SSL_ERROR_SSL) {
if (SSL_want_read(wsi->ssl)) {
lwsl_debug("(wants read)\n");
lws_change_pollfd(wsi, 0, LWS_POLLIN);
__lws_change_pollfd(wsi, 0, LWS_POLLIN);
return LWS_SSL_CAPABLE_MORE_SERVICE_READ;
}
if (SSL_want_write(wsi->ssl)) {
lwsl_debug("(wants write)\n");
lws_change_pollfd(wsi, 0, LWS_POLLOUT);
__lws_change_pollfd(wsi, 0, LWS_POLLOUT);
return LWS_SSL_CAPABLE_MORE_SERVICE_WRITE;
}
}

View file

@ -463,7 +463,7 @@ lws_tls_ctx_from_wsi(struct lws *wsi)
}
enum lws_ssl_capable_status
lws_tls_shutdown(struct lws *wsi)
__lws_tls_shutdown(struct lws *wsi)
{
int n;
@ -475,7 +475,7 @@ lws_tls_shutdown(struct lws *wsi)
return LWS_SSL_CAPABLE_DONE;
case 0: /* needs a retry */
lws_change_pollfd(wsi, 0, LWS_POLLIN);
__lws_change_pollfd(wsi, 0, LWS_POLLIN);
return LWS_SSL_CAPABLE_MORE_SERVICE;
default: /* fatal error, or WANT */
@ -483,12 +483,12 @@ lws_tls_shutdown(struct lws *wsi)
if (n != SSL_ERROR_SYSCALL && n != SSL_ERROR_SSL) {
if (SSL_want_read(wsi->ssl)) {
lwsl_debug("(wants read)\n");
lws_change_pollfd(wsi, 0, LWS_POLLIN);
__lws_change_pollfd(wsi, 0, LWS_POLLIN);
return LWS_SSL_CAPABLE_MORE_SERVICE_READ;
}
if (SSL_want_write(wsi->ssl)) {
lwsl_debug("(wants write)\n");
lws_change_pollfd(wsi, 0, LWS_POLLOUT);
__lws_change_pollfd(wsi, 0, LWS_POLLOUT);
return LWS_SSL_CAPABLE_MORE_SERVICE_WRITE;
}
}

View file

@ -376,7 +376,7 @@ int main(int argc, char **argv)
#endif
/* tell the library what debug level to emit and to send it to syslog */
lws_set_log_level(debug_level, lwsl_emit_syslog);
lws_set_log_level(debug_level, NULL);
lwsl_notice("libwebsockets test server - license LGPL2.1+SLE\n");
lwsl_notice("(C) Copyright 2010-2017 Andy Green <andy@warmcat.com>\n");