mirror of
https://github.com/warmcat/libwebsockets.git
synced 2025-03-09 00:00:04 +01:00
client: h2
This adds h2 http support for the client api. The public client api requires no changes, it will detect by ALPN if the server can handle http/2, if so, it will use it. Multiple client connections using the lws api will be mapped on to the same single http/2 + tls socket using http/2 streams that are serviced simultaneously where possible.
This commit is contained in:
parent
2d3fc52b73
commit
2a9b6f54c6
20 changed files with 742 additions and 164 deletions
|
@ -1104,6 +1104,18 @@ If lws learns from the first response header that keepalive is not possible,
|
|||
then it marks itself with that information and detaches any queued clients
|
||||
to make their own individual connections as a fallback.
|
||||
|
||||
Lws can also intelligently combine multiple ongoing client connections to
|
||||
the same host and port into a single http/2 connection with multiple
|
||||
streams if the server supports it.
|
||||
|
||||
Unlike http/1 pipelining, with http/2 the client connections all occur
|
||||
simultaneously using h2 stream multiplexing inside the one tcp + tls
|
||||
connection.
|
||||
|
||||
You can turn off the h2 client support either by not building lws with
|
||||
`-DLWS_WITH_HTTP2=1` or giving the `LCCSCF_NOT_H2` flag in the client
|
||||
connection info struct `ssl_connection` member.
|
||||
|
||||
@section vhosts Using lws vhosts
|
||||
|
||||
If you set LWS_SERVER_OPTION_EXPLICIT_VHOSTS options flag when you create
|
||||
|
|
|
@ -79,25 +79,52 @@ lws_client_connect_2(struct lws *wsi)
|
|||
struct lws *w = lws_container_of(d, struct lws,
|
||||
dll_active_client_conns);
|
||||
|
||||
if (w->ah && !strcmp(adsin, lws_hdr_simple_ptr(w,
|
||||
_WSI_TOKEN_CLIENT_PEER_ADDRESS)) &&
|
||||
if (w->client_hostname_copy &&
|
||||
!strcmp(adsin, w->client_hostname_copy) &&
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
(wsi->use_ssl & (LCCSCF_NOT_H2 | LCCSCF_USE_SSL)) ==
|
||||
(w->use_ssl & (LCCSCF_NOT_H2 | LCCSCF_USE_SSL)) &&
|
||||
#endif
|
||||
wsi->c_port == w->c_port) {
|
||||
|
||||
/* someone else is already connected to the right guy */
|
||||
|
||||
/* do we know for a fact pipelining won't fly? */
|
||||
if (w->keepalive_rejected) {
|
||||
lwsl_info("defeating pipelining due to no KA on server\n");
|
||||
lwsl_notice("defeating pipelining due to no KA on server\n");
|
||||
goto create_new_conn;
|
||||
}
|
||||
#if defined (LWS_WITH_HTTP2)
|
||||
/*
|
||||
* h2: in usable state already: just use it without
|
||||
* going through the queue
|
||||
*/
|
||||
if (w->client_h2_alpn &&
|
||||
(w->state == LWSS_HTTP2_CLIENT_WAITING_TO_SEND_HEADERS ||
|
||||
w->state == LWSS_HTTP2_CLIENT_ESTABLISHED)) {
|
||||
|
||||
lwsl_info("%s: just join h2 directly\n",
|
||||
__func__);
|
||||
|
||||
lws_wsi_h2_adopt(w, wsi);
|
||||
lws_vhost_unlock(wsi->vhost);
|
||||
|
||||
return wsi;
|
||||
}
|
||||
#endif
|
||||
|
||||
lwsl_info("applying %p to txn queue on %p (%d)\n", wsi, w,
|
||||
w->state);
|
||||
/*
|
||||
* ...let's add ourselves to his transaction queue...
|
||||
*/
|
||||
lws_dll_lws_add_front(&wsi->dll_client_transaction_queue,
|
||||
&w->dll_client_transaction_queue_head);
|
||||
|
||||
/*
|
||||
* pipeline our headers out on him, and wait for our
|
||||
* turn at client transaction_complete to take over
|
||||
* parsing the rx.
|
||||
* h1: pipeline our headers out on him,
|
||||
* and wait for our turn at client transaction_complete
|
||||
* to take over parsing the rx.
|
||||
*/
|
||||
|
||||
wsi_piggy = w;
|
||||
|
@ -111,6 +138,17 @@ lws_client_connect_2(struct lws *wsi)
|
|||
|
||||
create_new_conn:
|
||||
|
||||
/*
|
||||
* clients who will create their own fresh connection keep a copy of
|
||||
* the hostname they originally connected to, in case other connections
|
||||
* want to use it too
|
||||
*/
|
||||
|
||||
if (!wsi->client_hostname_copy)
|
||||
wsi->client_hostname_copy =
|
||||
strdup(lws_hdr_simple_ptr(wsi,
|
||||
_WSI_TOKEN_CLIENT_PEER_ADDRESS));
|
||||
|
||||
/*
|
||||
* start off allowing ipv6 on connection if vhost allows it
|
||||
*/
|
||||
|
@ -165,6 +203,8 @@ create_new_conn:
|
|||
|
||||
#ifdef LWS_WITH_IPV6
|
||||
if (wsi->ipv6) {
|
||||
struct sockaddr_in6 *sa6 =
|
||||
((struct sockaddr_in6 *)result->ai_addr);
|
||||
|
||||
if (n) {
|
||||
/* lws_getaddrinfo46 failed, there is no usable result */
|
||||
|
@ -193,11 +233,10 @@ create_new_conn:
|
|||
break;
|
||||
|
||||
case AF_INET6:
|
||||
memcpy(&sa46.sa6.sin6_addr,
|
||||
&((struct sockaddr_in6 *)result->ai_addr)->sin6_addr,
|
||||
memcpy(&sa46.sa6.sin6_addr, &sa6->sin6_addr,
|
||||
sizeof(struct in6_addr));
|
||||
sa46.sa6.sin6_scope_id = ((struct sockaddr_in6 *)result->ai_addr)->sin6_scope_id;
|
||||
sa46.sa6.sin6_flowinfo = ((struct sockaddr_in6 *)result->ai_addr)->sin6_flowinfo;
|
||||
sa46.sa6.sin6_scope_id = sa6->sin6_scope_id;
|
||||
sa46.sa6.sin6_flowinfo = sa6->sin6_flowinfo;
|
||||
break;
|
||||
default:
|
||||
lwsl_err("Unknown address family\n");
|
||||
|
@ -507,7 +546,9 @@ oom4:
|
|||
lws_header_table_force_to_detachable_state(wsi);
|
||||
|
||||
if (wsi->mode == LWSCM_HTTP_CLIENT ||
|
||||
wsi->mode == LWSCM_HTTP2_CLIENT ||
|
||||
wsi->mode == LWSCM_HTTP_CLIENT_ACCEPTED ||
|
||||
wsi->mode == LWSCM_HTTP2_CLIENT_ACCEPTED ||
|
||||
wsi->mode == LWSCM_WSCL_WAITING_CONNECT) {
|
||||
wsi->protocol->callback(wsi,
|
||||
LWS_CALLBACK_CLIENT_CONNECTION_ERROR,
|
||||
|
@ -520,6 +561,7 @@ oom4:
|
|||
lws_remove_from_timeout_list(wsi);
|
||||
lws_header_table_detach(wsi, 0);
|
||||
lws_client_stash_destroy(wsi);
|
||||
lws_free_set_NULL(wsi->client_hostname_copy);
|
||||
lws_free(wsi);
|
||||
|
||||
return NULL;
|
||||
|
@ -887,7 +929,7 @@ lws_client_connect_via_info(struct lws_client_connect_info *i)
|
|||
* which protocol we are associated with since we can give it a
|
||||
* list.
|
||||
*/
|
||||
if ((i->method || i->local_protocol_name) && local) {
|
||||
if (/*(i->method || i->local_protocol_name) && */local) {
|
||||
lwsl_info("binding to %s\n", local);
|
||||
p = lws_vhost_name_to_protocol(wsi->vhost, local);
|
||||
if (p)
|
||||
|
@ -907,8 +949,11 @@ lws_client_connect_via_info(struct lws_client_connect_info *i)
|
|||
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
wsi->use_ssl = i->ssl_connection;
|
||||
|
||||
if (!i->method) /* !!! disallow ws for h2 right now */
|
||||
wsi->use_ssl |= LCCSCF_NOT_H2;
|
||||
#else
|
||||
if (i->ssl_connection) {
|
||||
if (i->ssl_connection & LCCSCF_USE_SSL) {
|
||||
lwsl_err("libwebsockets not configured for ssl\n");
|
||||
goto bail;
|
||||
}
|
||||
|
|
|
@ -351,13 +351,13 @@ start_ws_handshake:
|
|||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
/* we can retry this... just cook the SSL BIO the first time */
|
||||
|
||||
if (wsi->use_ssl && !wsi->ssl &&
|
||||
if ((wsi->use_ssl & LCCSCF_USE_SSL) && !wsi->ssl &&
|
||||
lws_ssl_client_bio_create(wsi) < 0) {
|
||||
cce = "bio_create failed";
|
||||
goto bail3;
|
||||
}
|
||||
|
||||
if (wsi->use_ssl) {
|
||||
if (wsi->use_ssl & LCCSCF_USE_SSL) {
|
||||
n = lws_ssl_client_connect1(wsi);
|
||||
if (!n)
|
||||
return 0;
|
||||
|
@ -372,7 +372,7 @@ start_ws_handshake:
|
|||
|
||||
case LWSCM_WSCL_WAITING_SSL:
|
||||
|
||||
if (wsi->use_ssl) {
|
||||
if (wsi->use_ssl & LCCSCF_USE_SSL) {
|
||||
n = lws_ssl_client_connect2(wsi, ebuf, sizeof(ebuf));
|
||||
if (!n)
|
||||
return 0;
|
||||
|
@ -383,7 +383,30 @@ start_ws_handshake:
|
|||
} else
|
||||
wsi->ssl = NULL;
|
||||
#endif
|
||||
#if defined (LWS_WITH_HTTP2)
|
||||
if (wsi->client_h2_alpn) {
|
||||
/*
|
||||
* We connected to the server and set up tls, and
|
||||
* negotiated "h2".
|
||||
*
|
||||
* So this is it, we are an h2 master client connection
|
||||
* now, not an h1 client connection.
|
||||
*/
|
||||
lwsl_info("client connection upgraded to h2\n");
|
||||
lws_h2_configure_if_upgraded(wsi);
|
||||
|
||||
lws_union_transition(wsi, LWSCM_HTTP2_CLIENT);
|
||||
wsi->state = LWSS_HTTP2_CLIENT_SEND_SETTINGS;
|
||||
|
||||
/* send the H2 preface to legitimize the connection */
|
||||
if (lws_h2_issue_preface(wsi)) {
|
||||
cce = "error sending h2 preface";
|
||||
goto bail3;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
wsi->mode = LWSCM_WSCL_ISSUE_HANDSHAKE2;
|
||||
lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_CLIENT_HS_SEND,
|
||||
context->timeout_secs);
|
||||
|
@ -705,7 +728,10 @@ lws_client_interpret_server_handshake(struct lws *wsi)
|
|||
if (!wsi->do_ws) {
|
||||
/* we are being an http client...
|
||||
*/
|
||||
lws_union_transition(wsi, LWSCM_HTTP_CLIENT_ACCEPTED);
|
||||
if (wsi->client_h2_alpn)
|
||||
lws_union_transition(wsi, LWSCM_HTTP2_CLIENT_ACCEPTED);
|
||||
else
|
||||
lws_union_transition(wsi, LWSCM_HTTP_CLIENT_ACCEPTED);
|
||||
wsi->state = LWSS_CLIENT_HTTP_ESTABLISHED;
|
||||
wsi->ah = ah;
|
||||
ah->http_response = 0;
|
||||
|
@ -722,27 +748,27 @@ lws_client_interpret_server_handshake(struct lws *wsi)
|
|||
* content-type:.text/html
|
||||
* content-length:.17703
|
||||
* set-cookie:.test=LWS_1456736240_336776_COOKIE;Max-Age=360000
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
wsi->http.connection_type = HTTP_CONNECTION_KEEP_ALIVE;
|
||||
p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP);
|
||||
if (wsi->do_ws && !p) {
|
||||
lwsl_info("no URI\n");
|
||||
cce = "HS: URI missing";
|
||||
goto bail3;
|
||||
}
|
||||
if (!p) {
|
||||
p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP1_0);
|
||||
wsi->http.connection_type = HTTP_CONNECTION_CLOSE;
|
||||
}
|
||||
if (!p) {
|
||||
cce = "HS: URI missing";
|
||||
lwsl_info("no URI\n");
|
||||
goto bail3;
|
||||
}
|
||||
if (!wsi->client_h2_substream) {
|
||||
p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP);
|
||||
if (wsi->do_ws && !p) {
|
||||
lwsl_info("no URI\n");
|
||||
cce = "HS: URI missing";
|
||||
goto bail3;
|
||||
}
|
||||
if (!p) {
|
||||
p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP1_0);
|
||||
wsi->http.connection_type = HTTP_CONNECTION_CLOSE;
|
||||
}
|
||||
if (!p) {
|
||||
cce = "HS: URI missing";
|
||||
lwsl_info("no URI\n");
|
||||
goto bail3;
|
||||
}
|
||||
} else
|
||||
p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_STATUS);
|
||||
n = atoi(p);
|
||||
if (ah)
|
||||
ah->http_response = n;
|
||||
|
@ -757,7 +783,7 @@ lws_client_interpret_server_handshake(struct lws *wsi)
|
|||
/* Relative reference absolute path */
|
||||
if (p[0] == '/') {
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
ssl = wsi->use_ssl;
|
||||
ssl = wsi->use_ssl & LCCSCF_USE_SSL;
|
||||
#endif
|
||||
ads = lws_hdr_simple_ptr(wsi,
|
||||
_WSI_TOKEN_CLIENT_PEER_ADDRESS);
|
||||
|
@ -780,7 +806,7 @@ lws_client_interpret_server_handshake(struct lws *wsi)
|
|||
/* This doesn't try to calculate an absolute path,
|
||||
* that will be left to the server */
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
ssl = wsi->use_ssl;
|
||||
ssl = wsi->use_ssl & LCCSCF_USE_SSL;
|
||||
#endif
|
||||
ads = lws_hdr_simple_ptr(wsi,
|
||||
_WSI_TOKEN_CLIENT_PEER_ADDRESS);
|
||||
|
@ -798,7 +824,7 @@ lws_client_interpret_server_handshake(struct lws *wsi)
|
|||
}
|
||||
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
if (wsi->use_ssl && !ssl) {
|
||||
if ((wsi->use_ssl & LCCSCF_USE_SSL) && !ssl) {
|
||||
cce = "HS: Redirect attempted SSL downgrade";
|
||||
goto bail3;
|
||||
}
|
||||
|
@ -823,9 +849,9 @@ lws_client_interpret_server_handshake(struct lws *wsi)
|
|||
|
||||
if (!wsi->do_ws) {
|
||||
|
||||
/* if keepalive is allowed, enable the queued pipeline guys */
|
||||
/* if h1 KA is allowed, enable the queued pipeline guys */
|
||||
|
||||
if (w == wsi) { /* ie, coming to this for the first time */
|
||||
if (!wsi->client_h2_alpn && !wsi->client_h2_substream && w == wsi) { /* ie, coming to this for the first time */
|
||||
if (wsi->http.connection_type == HTTP_CONNECTION_KEEP_ALIVE)
|
||||
wsi->keepalive_active = 1;
|
||||
else {
|
||||
|
@ -949,6 +975,13 @@ lws_client_interpret_server_handshake(struct lws *wsi)
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (wsi->client_h2_substream) {/* !!! client ws-over-h2 not there yet */
|
||||
lwsl_warn("%s: client ws-over-h2 upgrade not supported yet\n",
|
||||
__func__);
|
||||
cce = "HS: h2 / ws upgrade unsupported";
|
||||
goto bail3;
|
||||
}
|
||||
|
||||
if (p && !strncmp(p, "401", 3)) {
|
||||
lwsl_warn(
|
||||
"lws_client_handshake: got bad HTTP response '%s'\n", p);
|
||||
|
@ -1079,7 +1112,8 @@ lws_client_interpret_server_handshake(struct lws *wsi)
|
|||
|
||||
if (!wsi->vhost->protocols[n].callback) {
|
||||
if (wsi->protocol)
|
||||
lwsl_err("Failed to match protocol %s\n", wsi->protocol->name);
|
||||
lwsl_err("Failed to match protocol %s\n",
|
||||
wsi->protocol->name);
|
||||
else
|
||||
lwsl_err("No protocol on client\n");
|
||||
goto bail2;
|
||||
|
|
|
@ -69,8 +69,13 @@ lws_read(struct lws *wsi, unsigned char *buf, lws_filepos_t len)
|
|||
int m;
|
||||
#endif
|
||||
|
||||
// lwsl_notice("%s: state %d\n", __func__, wsi->state);
|
||||
|
||||
switch (wsi->state) {
|
||||
#if defined(LWS_WITH_HTTP2)
|
||||
case LWSS_HTTP2_CLIENT_ESTABLISHED:
|
||||
case LWSS_HTTP2_CLIENT_SEND_SETTINGS:
|
||||
case LWSS_HTTP2_CLIENT_WAITING_TO_SEND_HEADERS:
|
||||
case LWSS_HTTP2_AWAIT_CLIENT_PREFACE:
|
||||
case LWSS_HTTP2_ESTABLISHED_PRE_SETTINGS:
|
||||
case LWSS_HTTP2_ESTABLISHED:
|
||||
|
|
|
@ -38,7 +38,7 @@ lws_add_http_header_by_name(struct lws *wsi, const unsigned char *name,
|
|||
unsigned char **p, unsigned char *end)
|
||||
{
|
||||
#ifdef LWS_WITH_HTTP2
|
||||
if (wsi->mode == LWSCM_HTTP2_SERVING)
|
||||
if (wsi->mode == LWSCM_HTTP2_SERVING || wsi->mode == LWSCM_HTTP2_CLIENT_ACCEPTED)
|
||||
return lws_add_http2_header_by_name(wsi, name,
|
||||
value, length, p, end);
|
||||
#else
|
||||
|
@ -66,7 +66,7 @@ int lws_finalize_http_header(struct lws *wsi, unsigned char **p,
|
|||
unsigned char *end)
|
||||
{
|
||||
#ifdef LWS_WITH_HTTP2
|
||||
if (wsi->mode == LWSCM_HTTP2_SERVING)
|
||||
if (wsi->mode == LWSCM_HTTP2_SERVING || wsi->mode == LWSCM_HTTP2_CLIENT_ACCEPTED)
|
||||
return 0;
|
||||
#else
|
||||
(void)wsi;
|
||||
|
@ -105,7 +105,7 @@ lws_add_http_header_by_token(struct lws *wsi, enum lws_token_indexes token,
|
|||
{
|
||||
const unsigned char *name;
|
||||
#ifdef LWS_WITH_HTTP2
|
||||
if (wsi->mode == LWSCM_HTTP2_SERVING)
|
||||
if (wsi->mode == LWSCM_HTTP2_SERVING || wsi->mode == LWSCM_HTTP2_CLIENT_ACCEPTED)
|
||||
return lws_add_http2_header_by_token(wsi, token, value,
|
||||
length, p, end);
|
||||
#endif
|
||||
|
|
|
@ -118,6 +118,17 @@ lws_h2_dump_settings(struct http2_settings *set)
|
|||
}
|
||||
#endif
|
||||
|
||||
static struct lws_h2_protocol_send *
|
||||
lws_h2_new_pps(enum lws_h2_protocol_send_type type)
|
||||
{
|
||||
struct lws_h2_protocol_send *pps = lws_malloc(sizeof(*pps), "pps");
|
||||
|
||||
if (pps)
|
||||
pps->type = type;
|
||||
|
||||
return pps;
|
||||
}
|
||||
|
||||
void lws_h2_init(struct lws *wsi)
|
||||
{
|
||||
wsi->h2.h2n->set = wsi->vhost->set;
|
||||
|
@ -153,6 +164,7 @@ lws_wsi_server_new(struct lws_vhost *vh, struct lws *parent_wsi,
|
|||
* connection error (Section 5.4.1) of type PROTOCOL_ERROR.
|
||||
*/
|
||||
if (sid <= h2n->highest_sid_opened) {
|
||||
lwsl_info("%s: tried to open lower sid %d\n", __func__, sid);
|
||||
lws_h2_goaway(nwsi, H2_ERR_PROTOCOL_ERROR, "Bad sid");
|
||||
return NULL;
|
||||
}
|
||||
|
@ -214,6 +226,86 @@ bail1:
|
|||
return NULL;
|
||||
}
|
||||
|
||||
struct lws *
|
||||
lws_wsi_h2_adopt(struct lws *parent_wsi, struct lws *wsi)
|
||||
{
|
||||
struct lws *nwsi = lws_get_network_wsi(parent_wsi);
|
||||
|
||||
/* no more children allowed by parent */
|
||||
if (parent_wsi->h2.child_count + 1 >
|
||||
parent_wsi->h2.h2n->set.s[H2SET_MAX_CONCURRENT_STREAMS]) {
|
||||
lwsl_notice("reached concurrent stream limit\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* sid is set just before issuing the headers, ensuring monoticity */
|
||||
|
||||
wsi->seen_nonpseudoheader = 0;
|
||||
wsi->client_h2_substream = 1;
|
||||
wsi->h2.initialized = 1;
|
||||
|
||||
wsi->h2.parent_wsi = parent_wsi;
|
||||
/* new guy's sibling is whoever was the first child before */
|
||||
wsi->h2.sibling_list = parent_wsi->h2.child_list;
|
||||
/* first child is now the new guy */
|
||||
parent_wsi->h2.child_list = wsi;
|
||||
parent_wsi->h2.child_count++;
|
||||
|
||||
wsi->h2.my_priority = 16;
|
||||
wsi->h2.tx_cr = nwsi->h2.h2n->set.s[H2SET_INITIAL_WINDOW_SIZE];
|
||||
wsi->h2.peer_tx_cr_est = nwsi->vhost->set.s[H2SET_INITIAL_WINDOW_SIZE];
|
||||
|
||||
if (lws_ensure_user_space(wsi))
|
||||
goto bail1;
|
||||
|
||||
wsi->state = LWSS_HTTP2_CLIENT_WAITING_TO_SEND_HEADERS;
|
||||
wsi->mode = LWSCM_HTTP2_CLIENT_ACCEPTED;
|
||||
lws_callback_on_writable(wsi);
|
||||
|
||||
wsi->vhost->conn_stats.h2_subs++;
|
||||
|
||||
return wsi;
|
||||
|
||||
bail1:
|
||||
/* undo the insert */
|
||||
parent_wsi->h2.child_list = wsi->h2.sibling_list;
|
||||
parent_wsi->h2.child_count--;
|
||||
|
||||
if (wsi->user_space)
|
||||
lws_free_set_NULL(wsi->user_space);
|
||||
wsi->protocol->callback(wsi, LWS_CALLBACK_WSI_DESTROY, NULL, NULL, 0);
|
||||
lws_free(wsi);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
int lws_h2_issue_preface(struct lws *wsi)
|
||||
{
|
||||
struct lws_h2_netconn *h2n = wsi->h2.h2n;
|
||||
struct lws_h2_protocol_send *pps;
|
||||
|
||||
if (lws_issue_raw(wsi, (uint8_t *)preface, strlen(preface)) !=
|
||||
(int)strlen(preface))
|
||||
return 1;
|
||||
|
||||
wsi->state = LWSS_HTTP2_CLIENT_ESTABLISHED;
|
||||
wsi->mode = LWSCM_HTTP2_CLIENT_ACCEPTED;
|
||||
h2n->count = 0;
|
||||
wsi->h2.tx_cr = 65535;
|
||||
|
||||
/*
|
||||
* we must send a settings frame
|
||||
*/
|
||||
pps = lws_h2_new_pps(LWS_H2_PPS_MY_SETTINGS);
|
||||
if (!pps)
|
||||
return 1;
|
||||
lws_pps_schedule(wsi, pps);
|
||||
lwsl_info("%s: h2 client sending settings\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct lws *
|
||||
lws_h2_wsi_from_id(struct lws *parent_wsi, unsigned int sid)
|
||||
{
|
||||
|
@ -253,17 +345,6 @@ lws_pps_schedule(struct lws *wsi, struct lws_h2_protocol_send *pps)
|
|||
lws_callback_on_writable(wsi);
|
||||
}
|
||||
|
||||
static struct lws_h2_protocol_send *
|
||||
lws_h2_new_pps(enum lws_h2_protocol_send_type type)
|
||||
{
|
||||
struct lws_h2_protocol_send *pps = lws_malloc(sizeof(*pps), "pps");
|
||||
|
||||
if (pps)
|
||||
pps->type = type;
|
||||
|
||||
return pps;
|
||||
}
|
||||
|
||||
int
|
||||
lws_h2_goaway(struct lws *wsi, uint32_t err, const char *reason)
|
||||
{
|
||||
|
@ -519,7 +600,7 @@ int lws_h2_do_pps_send(struct lws *wsi)
|
|||
struct lws_h2_protocol_send *pps = NULL;
|
||||
struct lws *cwsi;
|
||||
uint8_t set[LWS_PRE + 64], *p = &set[LWS_PRE], *q;
|
||||
int n, m = 0;
|
||||
int n, m = 0, flags = 0;
|
||||
|
||||
if (!h2n)
|
||||
return 1;
|
||||
|
@ -542,6 +623,7 @@ int lws_h2_do_pps_send(struct lws *wsi)
|
|||
switch (pps->type) {
|
||||
|
||||
case LWS_H2_PPS_MY_SETTINGS:
|
||||
|
||||
/*
|
||||
* if any of our settings varies from h2 "default defaults"
|
||||
* then we must inform the peer
|
||||
|
@ -554,7 +636,7 @@ int lws_h2_do_pps_send(struct lws *wsi)
|
|||
m += sizeof(h2n->one_setting);
|
||||
}
|
||||
n = lws_h2_frame_write(wsi, LWS_H2_FRAME_TYPE_SETTINGS,
|
||||
0, LWS_H2_STREAM_ID_MASTER, m,
|
||||
flags, LWS_H2_STREAM_ID_MASTER, m,
|
||||
&set[LWS_PRE]);
|
||||
if (n != m) {
|
||||
lwsl_info("send %d %d\n", n, m);
|
||||
|
@ -883,17 +965,9 @@ lws_h2_parse_frame_header(struct lws *wsi)
|
|||
"Settings length error");
|
||||
break;
|
||||
}
|
||||
lwsl_info("scheduled settings ack PPS\n");
|
||||
/* non-ACK coming in means we must ACK it */
|
||||
|
||||
|
||||
if (h2n->type == LWS_H2_FRAME_TYPE_COUNT)
|
||||
return 0;
|
||||
|
||||
pps = lws_h2_new_pps(LWS_H2_PPS_ACK_SETTINGS);
|
||||
if (!pps)
|
||||
return 1;
|
||||
lws_pps_schedule(wsi, pps);
|
||||
break;
|
||||
}
|
||||
/* came to us with ACK set... not allowed to have payload */
|
||||
|
@ -943,6 +1017,16 @@ lws_h2_parse_frame_header(struct lws *wsi)
|
|||
return 1;
|
||||
}
|
||||
|
||||
#if !defined(LWS_NO_CLIENT)
|
||||
if (wsi->client_h2_alpn) {
|
||||
if (h2n->sid) {
|
||||
h2n->swsi = lws_h2_wsi_from_id(wsi, h2n->sid);
|
||||
lwsl_info("HEADERS: nwsi %p: sid %d mapped to wsi %p\n", wsi, h2n->sid, h2n->swsi);
|
||||
}
|
||||
goto update_end_headers;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!h2n->swsi) {
|
||||
/* no more children allowed by parent */
|
||||
if (wsi->h2.child_count + 1 >
|
||||
|
@ -1020,6 +1104,7 @@ update_end_headers:
|
|||
/* no END_HEADERS means CONTINUATION must come */
|
||||
h2n->swsi->h2.END_HEADERS =
|
||||
!!(h2n->flags & LWS_H2_FLAG_END_HEADERS);
|
||||
lwsl_info("%p: END_HEADERS %d\n", h2n->swsi, h2n->swsi->h2.END_HEADERS);
|
||||
if (h2n->swsi->h2.END_HEADERS)
|
||||
h2n->cont_exp = 0;
|
||||
lwsl_debug("END_HEADERS %d\n", h2n->swsi->h2.END_HEADERS);
|
||||
|
@ -1089,6 +1174,89 @@ lws_h2_parse_end_of_frame(struct lws *wsi)
|
|||
}
|
||||
|
||||
switch (h2n->type) {
|
||||
|
||||
case LWS_H2_FRAME_TYPE_SETTINGS:
|
||||
|
||||
#if !defined(LWS_NO_CLIENT)
|
||||
if (wsi->client_h2_alpn &&
|
||||
!(h2n->flags & LWS_H2_FLAG_SETTINGS_ACK)) {
|
||||
|
||||
/* migrate original client ask on to substream 1 */
|
||||
|
||||
wsi->http.fop_fd = NULL;
|
||||
|
||||
/*
|
||||
* we need to treat the headers from the upgrade as the
|
||||
* first job. So these need to get shifted to sid 1.
|
||||
*/
|
||||
h2n->swsi = lws_wsi_server_new(wsi->vhost, wsi, 1);
|
||||
if (!h2n->swsi)
|
||||
return 1;
|
||||
h2n->sid = 1;
|
||||
|
||||
assert(lws_h2_wsi_from_id(wsi, 1) == h2n->swsi);
|
||||
|
||||
wsi->state = LWSS_HTTP2_CLIENT_WAITING_TO_SEND_HEADERS;
|
||||
wsi->mode = LWSCM_HTTP2_CLIENT_ACCEPTED;
|
||||
|
||||
h2n->swsi->state = LWSS_HTTP2_CLIENT_WAITING_TO_SEND_HEADERS;
|
||||
h2n->swsi->mode = LWSCM_HTTP2_CLIENT_ACCEPTED;
|
||||
|
||||
/* pass on the initial headers to SID 1 */
|
||||
h2n->swsi->ah = wsi->ah;
|
||||
h2n->swsi->client_h2_substream = 1;
|
||||
|
||||
h2n->swsi->protocol = wsi->protocol;
|
||||
h2n->swsi->user_space = wsi->user_space;
|
||||
h2n->swsi->user_space_externally_allocated =
|
||||
wsi->user_space_externally_allocated;
|
||||
|
||||
wsi->user_space = NULL;
|
||||
|
||||
if (h2n->swsi->ah)
|
||||
h2n->swsi->ah->wsi = h2n->swsi;
|
||||
wsi->ah = NULL;
|
||||
|
||||
lwsl_info("%s: MIGRATING nwsi %p: swsi %p\n", __func__,
|
||||
wsi, h2n->swsi);
|
||||
h2n->swsi->h2.tx_cr =
|
||||
h2n->set.s[H2SET_INITIAL_WINDOW_SIZE];
|
||||
lwsl_info("initial tx credit on conn %p: %d\n",
|
||||
h2n->swsi, h2n->swsi->h2.tx_cr);
|
||||
h2n->swsi->h2.initialized = 1;
|
||||
|
||||
lws_callback_on_writable(h2n->swsi);
|
||||
|
||||
pps = lws_h2_new_pps(LWS_H2_PPS_ACK_SETTINGS);
|
||||
if (!pps)
|
||||
return 1;
|
||||
lws_pps_schedule(wsi, pps);
|
||||
lwsl_info("%s: scheduled settings ack PPS\n", __func__);
|
||||
|
||||
/* also attach any queued guys */
|
||||
|
||||
/* we have a transaction queue that wants to pipeline */
|
||||
lws_vhost_lock(wsi->vhost);
|
||||
lws_start_foreach_dll_safe(struct lws_dll_lws *, d, d1,
|
||||
wsi->dll_client_transaction_queue_head.next) {
|
||||
struct lws *w = lws_container_of(d, struct lws,
|
||||
dll_client_transaction_queue);
|
||||
|
||||
if (w->mode == LWSCM_WSCL_ISSUE_HANDSHAKE2) {
|
||||
lwsl_info("%s: client pipeq %p to be h2\n",
|
||||
__func__, w);
|
||||
/* remove ourselves from the client queue */
|
||||
lws_dll_lws_remove(&w->dll_client_transaction_queue);
|
||||
|
||||
/* attach ourselves as an h2 stream */
|
||||
lws_wsi_h2_adopt(wsi, w);
|
||||
}
|
||||
} lws_end_foreach_dll_safe(d, d1);
|
||||
lws_vhost_unlock(wsi->vhost);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
|
||||
case LWS_H2_FRAME_TYPE_CONTINUATION:
|
||||
case LWS_H2_FRAME_TYPE_HEADERS:
|
||||
|
||||
|
@ -1130,6 +1298,14 @@ lws_h2_parse_end_of_frame(struct lws *wsi)
|
|||
lwsl_info("http req, wsi=%p, h2n->swsi=%p\n", wsi, h2n->swsi);
|
||||
h2n->swsi->hdr_parsing_completed = 1;
|
||||
|
||||
if (h2n->swsi->client_h2_substream) {
|
||||
if (lws_client_interpret_server_handshake(h2n->swsi)) {
|
||||
lws_h2_rst_stream(h2n->swsi, H2_ERR_STREAM_CLOSED,
|
||||
"protocol CLI_EST closed it");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (lws_hdr_extant(h2n->swsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) {
|
||||
h2n->swsi->http.rx_content_length = atoll(
|
||||
lws_hdr_simple_ptr(h2n->swsi,
|
||||
|
@ -1185,6 +1361,11 @@ lws_h2_parse_end_of_frame(struct lws *wsi)
|
|||
break;
|
||||
}
|
||||
|
||||
if (h2n->swsi->client_h2_substream) {
|
||||
lwsl_info("%s: headers: client path\n", __func__);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!lws_hdr_total_length(h2n->swsi, WSI_TOKEN_HTTP_COLON_PATH) ||
|
||||
!lws_hdr_total_length(h2n->swsi, WSI_TOKEN_HTTP_COLON_METHOD) ||
|
||||
!lws_hdr_total_length(h2n->swsi, WSI_TOKEN_HTTP_COLON_SCHEME) ||
|
||||
|
@ -1238,6 +1419,25 @@ lws_h2_parse_end_of_frame(struct lws *wsi)
|
|||
if (h2n->swsi->h2.END_STREAM &&
|
||||
h2n->swsi->h2.h2_state == LWS_H2_STATE_HALF_CLOSED_LOCAL)
|
||||
lws_h2_state(h2n->swsi, LWS_H2_STATE_CLOSED);
|
||||
|
||||
if (h2n->swsi->client_h2_substream &&
|
||||
h2n->flags & LWS_H2_FLAG_END_STREAM) {
|
||||
lwsl_info("%s: %p: DATA: end stream\n", __func__, h2n->swsi);
|
||||
|
||||
if (h2n->swsi->h2.h2_state == LWS_H2_STATE_OPEN)
|
||||
lws_h2_state(h2n->swsi, LWS_H2_STATE_HALF_CLOSED_REMOTE);
|
||||
|
||||
if (h2n->swsi->h2.h2_state == LWS_H2_STATE_HALF_CLOSED_LOCAL) {
|
||||
lws_h2_state(h2n->swsi, LWS_H2_STATE_CLOSED);
|
||||
|
||||
lws_h2_rst_stream(h2n->swsi, H2_ERR_NO_ERROR,
|
||||
"client done");
|
||||
|
||||
if (lws_http_transaction_completed_client(h2n->swsi))
|
||||
lwsl_debug("tx completed returned close\n");
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case LWS_H2_FRAME_TYPE_PING:
|
||||
|
@ -1350,7 +1550,7 @@ lws_h2_parser(struct lws *wsi, unsigned char *in, lws_filepos_t inlen,
|
|||
struct lws_h2_netconn *h2n = wsi->h2.h2n;
|
||||
struct lws_h2_protocol_send *pps;
|
||||
unsigned char c, *oldin = in;
|
||||
int n;
|
||||
int n, m;
|
||||
|
||||
if (!h2n)
|
||||
goto fail;
|
||||
|
@ -1359,6 +1559,8 @@ lws_h2_parser(struct lws *wsi, unsigned char *in, lws_filepos_t inlen,
|
|||
|
||||
c = *in++;
|
||||
|
||||
// lwsl_notice("%s: 0x%x\n", __func__, c);
|
||||
|
||||
switch (wsi->state) {
|
||||
case LWSS_HTTP2_AWAIT_CLIENT_PREFACE:
|
||||
if (preface[h2n->count++] != c)
|
||||
|
@ -1383,6 +1585,8 @@ lws_h2_parser(struct lws *wsi, unsigned char *in, lws_filepos_t inlen,
|
|||
lws_pps_schedule(wsi, pps);
|
||||
break;
|
||||
|
||||
case LWSS_HTTP2_CLIENT_WAITING_TO_SEND_HEADERS:
|
||||
case LWSS_HTTP2_CLIENT_ESTABLISHED:
|
||||
case LWSS_HTTP2_ESTABLISHED_PRE_SETTINGS:
|
||||
case LWSS_HTTP2_ESTABLISHED:
|
||||
if (h2n->frame_state != LWS_H2_FRAME_HEADER_LENGTH)
|
||||
|
@ -1509,35 +1713,63 @@ lws_h2_parser(struct lws *wsi, unsigned char *in, lws_filepos_t inlen,
|
|||
break;
|
||||
}
|
||||
|
||||
h2n->swsi->outer_will_close = 1;
|
||||
/*
|
||||
* choose the length for this go so that we end at
|
||||
* the frame boundary, in the case there is already
|
||||
* more waiting leave it for next time around
|
||||
* We operate on a frame. The RX we have at
|
||||
* hand may exceed the current frame.
|
||||
*/
|
||||
|
||||
n = (int)inlen + 1;
|
||||
if (n > (int)(h2n->length - h2n->count + 1)) {
|
||||
n = h2n->length - h2n->count + 1;
|
||||
lwsl_debug("---- restricting len to %d vs %ld\n", n, (long)inlen + 1);
|
||||
}
|
||||
n = lws_read(h2n->swsi, in - 1, n);
|
||||
h2n->swsi->outer_will_close = 0;
|
||||
/*
|
||||
* can return 0 in POST body with content len
|
||||
* exhausted somehow.
|
||||
*/
|
||||
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;
|
||||
h2n->inside += n;
|
||||
h2n->count += n - 1;
|
||||
if (h2n->swsi->client_h2_substream) {
|
||||
|
||||
m = user_callback_handle_rxflow(
|
||||
h2n->swsi->protocol->callback,
|
||||
h2n->swsi, LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ,
|
||||
h2n->swsi->user_space, in - 1, n);
|
||||
|
||||
in += n - 1;
|
||||
h2n->inside += n;
|
||||
h2n->count += n - 1;
|
||||
inlen -= n - 1;
|
||||
|
||||
if (m) {
|
||||
lwsl_info("RECEIVE_CLIENT_HTTP closed it\n");
|
||||
goto close_swsi_and_return;
|
||||
}
|
||||
|
||||
break;
|
||||
} else {
|
||||
|
||||
h2n->swsi->outer_will_close = 1;
|
||||
/*
|
||||
* choose the length for this go so that we end at
|
||||
* the frame boundary, in the case there is already
|
||||
* more waiting leave it for next time around
|
||||
*/
|
||||
|
||||
n = lws_read(h2n->swsi, in - 1, n);
|
||||
h2n->swsi->outer_will_close = 0;
|
||||
/*
|
||||
* can return 0 in POST body with content len
|
||||
* exhausted somehow.
|
||||
*/
|
||||
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;
|
||||
h2n->inside += n;
|
||||
h2n->count += n - 1;
|
||||
}
|
||||
|
||||
/* account for both network and stream wsi windows */
|
||||
|
||||
|
@ -1697,6 +1929,103 @@ fail:
|
|||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
lws_h2_client_handshake(struct lws *wsi)
|
||||
{
|
||||
uint8_t buf[LWS_PRE + 1024], *start = &buf[LWS_PRE],
|
||||
*p = start, *end = &buf[sizeof(buf) - 1];
|
||||
char *meth = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_METHOD),
|
||||
*uri = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_URI);
|
||||
struct lws *nwsi = lws_get_network_wsi(wsi);
|
||||
struct lws_h2_protocol_send *pps;
|
||||
int n;
|
||||
/*
|
||||
* The identifier of a newly established stream MUST be numerically
|
||||
* greater than all streams that the initiating endpoint has opened or
|
||||
* reserved. This governs streams that are opened using a HEADERS frame
|
||||
* and streams that are reserved using PUSH_PROMISE. An endpoint that
|
||||
* receives an unexpected stream identifier MUST respond with a
|
||||
* connection error (Section 5.4.1) of type PROTOCOL_ERROR.
|
||||
*/
|
||||
int sid = nwsi->h2.h2n->highest_sid_opened + 2;
|
||||
|
||||
nwsi->h2.h2n->highest_sid_opened = sid;
|
||||
wsi->h2.my_sid = sid;
|
||||
|
||||
lwsl_info("%s: CLIENT_WAITING_TO_SEND_HEADERS: pollout (sid %d)\n",
|
||||
__func__, wsi->h2.my_sid);
|
||||
|
||||
pps = lws_h2_new_pps(LWS_H2_PPS_UPDATE_WINDOW);
|
||||
if (!pps)
|
||||
return 1;
|
||||
pps->u.update_window.sid = sid;
|
||||
pps->u.update_window.credit = 4 * 65536;
|
||||
wsi->h2.peer_tx_cr_est += pps->u.update_window.credit;
|
||||
lws_pps_schedule(wsi, pps);
|
||||
|
||||
pps = lws_h2_new_pps(LWS_H2_PPS_UPDATE_WINDOW);
|
||||
if (!pps)
|
||||
return 1;
|
||||
pps->u.update_window.sid = 0;
|
||||
pps->u.update_window.credit = 4 * 65536;
|
||||
wsi->h2.peer_tx_cr_est += pps->u.update_window.credit;
|
||||
lws_pps_schedule(wsi, pps);
|
||||
|
||||
/* it's time for us to send our client stream headers */
|
||||
|
||||
if (!meth)
|
||||
meth = "GET";
|
||||
|
||||
if (lws_add_http_header_by_token(wsi,
|
||||
WSI_TOKEN_HTTP_COLON_METHOD,
|
||||
(unsigned char *)meth,
|
||||
strlen(meth), &p, end))
|
||||
return -1;
|
||||
|
||||
if (lws_add_http_header_by_token(wsi,
|
||||
WSI_TOKEN_HTTP_COLON_SCHEME,
|
||||
(unsigned char *)"http", 4,
|
||||
&p, end))
|
||||
return -1;
|
||||
|
||||
if (lws_add_http_header_by_token(wsi,
|
||||
WSI_TOKEN_HTTP_COLON_PATH,
|
||||
(unsigned char *)uri,
|
||||
lws_hdr_total_length(wsi, _WSI_TOKEN_CLIENT_URI),
|
||||
&p, end))
|
||||
return -1;
|
||||
|
||||
if (lws_add_http_header_by_token(wsi,
|
||||
WSI_TOKEN_HTTP_COLON_AUTHORITY,
|
||||
(unsigned char *)lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ORIGIN),
|
||||
lws_hdr_total_length(wsi, _WSI_TOKEN_CLIENT_ORIGIN),
|
||||
&p, end))
|
||||
return -1;
|
||||
|
||||
/* give userland a chance to append, eg, cookies */
|
||||
|
||||
if (wsi->protocol->callback(wsi,
|
||||
LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER,
|
||||
wsi->user_space, &p, (end - p) - 12))
|
||||
return -1;
|
||||
|
||||
if (lws_finalize_http_header(wsi, &p, end))
|
||||
return -1;
|
||||
|
||||
n = lws_write(wsi, start, p - start,
|
||||
LWS_WRITE_HTTP_HEADERS);
|
||||
if (n != (p - start)) {
|
||||
lwsl_err("_write returned %d from %ld\n", n,
|
||||
(long)(p - start));
|
||||
return -1;
|
||||
}
|
||||
|
||||
lws_h2_state(wsi, LWS_H2_STATE_OPEN);
|
||||
wsi->state = LWSS_HTTP2_CLIENT_ESTABLISHED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
lws_h2_ws_handshake(struct lws *wsi)
|
||||
{
|
||||
|
|
|
@ -119,7 +119,7 @@ int lws_h2_configure_if_upgraded(struct lws *wsi)
|
|||
cstr[len] = '\0';
|
||||
|
||||
lwsl_info("negotiated '%s' using ALPN\n", cstr);
|
||||
wsi->use_ssl = 1;
|
||||
wsi->use_ssl |= LCCSCF_USE_SSL;
|
||||
if (strncmp((char *)name, "http/1.1", 8) == 0)
|
||||
return 0;
|
||||
|
||||
|
|
|
@ -540,6 +540,8 @@ __lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason, const char *
|
|||
lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_API_CLOSE, 1);
|
||||
|
||||
#if !defined(LWS_NO_CLIENT)
|
||||
|
||||
lws_free_set_NULL(wsi->client_hostname_copy);
|
||||
/* we are no longer an active client connection that can piggyback */
|
||||
lws_dll_lws_remove(&wsi->dll_active_client_conns);
|
||||
|
||||
|
@ -770,7 +772,7 @@ just_kill_connection:
|
|||
} lws_end_foreach_llp(w, h2.sibling_list);
|
||||
}
|
||||
|
||||
if (wsi->upgraded_to_http2 || wsi->http2_substream) {
|
||||
if (wsi->upgraded_to_http2 || wsi->http2_substream || wsi->client_h2_substream) {
|
||||
lwsl_info("closing %p: parent %p\n", wsi, wsi->h2.parent_wsi);
|
||||
|
||||
if (wsi->h2.child_list) {
|
||||
|
@ -805,7 +807,8 @@ just_kill_connection:
|
|||
wsi->h2.h2n->pps = NULL;
|
||||
}
|
||||
|
||||
if (wsi->http2_substream && wsi->h2.parent_wsi) {
|
||||
if ((wsi->client_h2_substream || 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) {
|
||||
|
@ -1372,7 +1375,7 @@ lws_get_network_wsi(struct lws *wsi)
|
|||
return NULL;
|
||||
|
||||
#if defined(LWS_WITH_HTTP2)
|
||||
if (!wsi->http2_substream)
|
||||
if (!wsi->http2_substream && !wsi->client_h2_substream)
|
||||
return wsi;
|
||||
|
||||
while (wsi->h2.parent_wsi)
|
||||
|
@ -2183,7 +2186,7 @@ LWS_VISIBLE int
|
|||
lws_is_ssl(struct lws *wsi)
|
||||
{
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
return wsi->use_ssl;
|
||||
return wsi->use_ssl & LCCSCF_USE_SSL;
|
||||
#else
|
||||
(void)wsi;
|
||||
return 0;
|
||||
|
@ -3109,7 +3112,7 @@ lws_json_dump_vhost(const struct lws_vhost *vh, char *buf, int len)
|
|||
,
|
||||
vh->name, vh->listen_port,
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
vh->use_ssl,
|
||||
vh->use_ssl & LCCSCF_USE_SSL,
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
|
|
|
@ -3282,7 +3282,12 @@ enum lws_client_connect_ssl_connection_flags {
|
|||
|
||||
LCCSCF_PIPELINE = (1 << 16),
|
||||
/**< Serialize / pipeline multiple client connections
|
||||
* on a single connection where possible. */
|
||||
* on a single connection where possible.
|
||||
*
|
||||
* HTTP/1.0: possible if Keep-Alive: yes sent by server
|
||||
* HTTP/1.1: always possible... uses pipelining
|
||||
* HTTP/2: always possible... uses parallel streams
|
||||
* */
|
||||
};
|
||||
|
||||
/** struct lws_client_connect_info - parameters to connect with when using
|
||||
|
|
13
lib/output.c
13
lib/output.c
|
@ -53,6 +53,8 @@ int lws_issue_raw(struct lws *wsi, unsigned char *buf, size_t len)
|
|||
int m;
|
||||
#endif
|
||||
|
||||
// lwsl_hexdump_notice(buf, len);
|
||||
|
||||
/*
|
||||
* Detect if we got called twice without going through the
|
||||
* event loop to handle pending. This would be caused by either
|
||||
|
@ -510,10 +512,11 @@ send_raw:
|
|||
case LWS_WRITE_PING:
|
||||
#ifdef LWS_WITH_HTTP2
|
||||
/*
|
||||
* ws-over-h2 ends up here after the ws framing applied
|
||||
* ws-over-h2 also ends up here after the ws framing applied
|
||||
*/
|
||||
if (wsi->mode == LWSCM_HTTP2_SERVING ||
|
||||
wsi->mode == LWSCM_HTTP2_WS_SERVING) {
|
||||
wsi->mode == LWSCM_HTTP2_WS_SERVING ||
|
||||
wsi->mode == LWSCM_HTTP2_CLIENT_ACCEPTED) {
|
||||
unsigned char flags = 0;
|
||||
|
||||
n = LWS_H2_FRAME_TYPE_DATA;
|
||||
|
@ -626,7 +629,7 @@ LWS_VISIBLE int lws_serve_http_file_fragment(struct lws *wsi)
|
|||
|
||||
lwsl_debug("wsi->http2_substream %d\n", wsi->http2_substream);
|
||||
|
||||
while (!lws_send_pipe_choked(wsi)) {
|
||||
do {
|
||||
|
||||
if (wsi->trunc_len) {
|
||||
if (lws_issue_raw(wsi, wsi->trunc_alloc +
|
||||
|
@ -698,7 +701,7 @@ LWS_VISIBLE int lws_serve_http_file_fragment(struct lws *wsi)
|
|||
#if defined(LWS_WITH_HTTP2)
|
||||
m = lws_h2_tx_cr_get(wsi);
|
||||
if (!m) {
|
||||
lwsl_info("%s: came here with no tx credit", __func__);
|
||||
lwsl_info("%s: came here with no tx credit\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
if ((lws_filepos_t)m < poss)
|
||||
|
@ -842,7 +845,7 @@ all_sent:
|
|||
|
||||
return 1; /* >0 indicates completed */
|
||||
}
|
||||
}
|
||||
} while (0); // while (!lws_send_pipe_choked(wsi))
|
||||
|
||||
lws_callback_on_writable(wsi);
|
||||
|
||||
|
|
14
lib/pollfd.c
14
lib/pollfd.c
|
@ -465,10 +465,16 @@ lws_callback_on_writable(struct lws *wsi)
|
|||
lwsl_info("%s: %p (mode %d)\n", __func__, wsi, wsi->mode);
|
||||
|
||||
if (wsi->mode != LWSCM_HTTP2_SERVING &&
|
||||
wsi->mode != LWSCM_HTTP2_CLIENT &&
|
||||
wsi->mode != LWSCM_HTTP2_CLIENT_ACCEPTED &&
|
||||
wsi->mode != LWSCM_HTTP2_WS_SERVING)
|
||||
goto network_sock;
|
||||
|
||||
if (wsi->h2.requested_POLLOUT) {
|
||||
if (wsi->h2.requested_POLLOUT
|
||||
#if !defined(LWS_NO_CLIENT)
|
||||
&& !wsi->client_h2_alpn
|
||||
#endif
|
||||
) {
|
||||
lwsl_info("already pending writable\n");
|
||||
return 1;
|
||||
}
|
||||
|
@ -506,7 +512,11 @@ lws_callback_on_writable(struct lws *wsi)
|
|||
/* for network action, act only on the network wsi */
|
||||
|
||||
wsi = network_wsi;
|
||||
if (already)
|
||||
if (already && !wsi->client_h2_alpn
|
||||
#if !defined(LWS_NO_CLIENT)
|
||||
&& !wsi->client_h2_substream
|
||||
#endif
|
||||
)
|
||||
return 1;
|
||||
network_sock:
|
||||
#endif
|
||||
|
|
|
@ -521,6 +521,10 @@ enum lws_connection_states {
|
|||
|
||||
LWSS_HTTP_DEFERRING_ACTION = _LSF_CCB | 19 |
|
||||
_LSF_POLLOUT,
|
||||
|
||||
LWSS_HTTP2_CLIENT_SEND_SETTINGS = 20 | _LSF_POLLOUT,
|
||||
LWSS_HTTP2_CLIENT_WAITING_TO_SEND_HEADERS = 21 | _LSF_POLLOUT,
|
||||
LWSS_HTTP2_CLIENT_ESTABLISHED = 22 | _LSF_POLLOUT,
|
||||
};
|
||||
|
||||
#define lws_state_is_ws(s) (!!((s) & _LSF_WEBSOCKET))
|
||||
|
@ -595,6 +599,8 @@ enum connection_mode {
|
|||
/* HTTP Client related */
|
||||
LWSCM_HTTP_CLIENT = LWSCM_FLAG_IMPLIES_CALLBACK_CLOSED_CLIENT_HTTP,
|
||||
LWSCM_HTTP_CLIENT_ACCEPTED, /* actual HTTP service going on */
|
||||
LWSCM_HTTP2_CLIENT,
|
||||
LWSCM_HTTP2_CLIENT_ACCEPTED,
|
||||
LWSCM_WSCL_WAITING_CONNECT,
|
||||
LWSCM_WSCL_WAITING_PROXY_REPLY,
|
||||
LWSCM_WSCL_ISSUE_HANDSHAKE,
|
||||
|
@ -1200,6 +1206,7 @@ struct lws_context {
|
|||
unsigned int protocol_init_done:1;
|
||||
unsigned int ssl_gate_accepts:1;
|
||||
unsigned int doing_protocol_init;
|
||||
unsigned int done_protocol_destroy_cb;
|
||||
/*
|
||||
* set to the Thread ID that's doing the service loop just before entry
|
||||
* to poll indicates service thread likely idling in poll()
|
||||
|
@ -1554,6 +1561,7 @@ enum lws_h2_states {
|
|||
|
||||
#define LWS_H2_STREAM_ID_MASTER 0
|
||||
#define LWS_H2_SETTINGS_LEN 6
|
||||
#define LWS_H2_FLAG_SETTINGS_ACK 1
|
||||
|
||||
enum http2_hpack_state {
|
||||
HPKS_TYPE,
|
||||
|
@ -1916,6 +1924,7 @@ struct lws {
|
|||
unsigned char *preamble_rx;
|
||||
#ifndef LWS_NO_CLIENT
|
||||
struct client_info_stash *stash;
|
||||
char *client_hostname_copy;
|
||||
struct lws_dll_lws dll_active_client_conns;
|
||||
struct lws_dll_lws dll_client_transaction_queue_head;
|
||||
struct lws_dll_lws dll_client_transaction_queue;
|
||||
|
@ -2011,6 +2020,8 @@ struct lws {
|
|||
unsigned int keepalive_active:1;
|
||||
unsigned int keepalive_rejected:1;
|
||||
unsigned int client_pipeline:1;
|
||||
unsigned int client_h2_alpn:1;
|
||||
unsigned int client_h2_substream:1;
|
||||
#endif
|
||||
#ifdef LWS_WITH_HTTP_PROXY
|
||||
unsigned int perform_rewrite:1;
|
||||
|
@ -2019,7 +2030,7 @@ struct lws {
|
|||
unsigned int extension_data_pending:1;
|
||||
#endif
|
||||
#ifdef LWS_OPENSSL_SUPPORT
|
||||
unsigned int use_ssl:4;
|
||||
unsigned int use_ssl;
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
unsigned int sock_send_blocking:1;
|
||||
|
@ -2281,6 +2292,11 @@ lws_pps_schedule(struct lws *wsi, struct lws_h2_protocol_send *pss);
|
|||
LWS_EXTERN const struct http2_settings lws_h2_defaults;
|
||||
LWS_EXTERN int
|
||||
lws_h2_ws_handshake(struct lws *wsi);
|
||||
LWS_EXTERN int lws_h2_issue_preface(struct lws *wsi);
|
||||
LWS_EXTERN int
|
||||
lws_h2_client_handshake(struct lws *wsi);
|
||||
LWS_EXTERN struct lws *
|
||||
lws_wsi_h2_adopt(struct lws *parent_wsi, struct lws *wsi);
|
||||
#else
|
||||
#define lws_h2_configure_if_upgraded(x)
|
||||
#endif
|
||||
|
|
|
@ -52,6 +52,8 @@ lws_calllback_as_writeable(struct lws *wsi)
|
|||
n = LWS_CALLBACK_CLIENT_WRITEABLE;
|
||||
break;
|
||||
case LWSCM_WSCL_ISSUE_HTTP_BODY:
|
||||
case LWSCM_HTTP2_CLIENT:
|
||||
case LWSCM_HTTP2_CLIENT_ACCEPTED:
|
||||
n = LWS_CALLBACK_CLIENT_HTTP_WRITEABLE;
|
||||
break;
|
||||
case LWSCM_HTTP2_WS_SERVING:
|
||||
|
@ -84,6 +86,9 @@ lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd)
|
|||
struct lws_tokens eff_buf;
|
||||
int ret, m;
|
||||
#endif
|
||||
|
||||
lwsl_info("%s: %p\n", __func__, wsi);
|
||||
|
||||
vwsi->leave_pollout_active = 0;
|
||||
vwsi->handling_pollout = 1;
|
||||
/*
|
||||
|
@ -124,7 +129,11 @@ lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd)
|
|||
/*
|
||||
* Priority 2: protocol packets
|
||||
*/
|
||||
if (wsi->upgraded_to_http2 && wsi->h2.h2n->pps) {
|
||||
if ((wsi->upgraded_to_http2
|
||||
#if !defined(LWS_NO_CLIENT)
|
||||
|| wsi->client_h2_alpn
|
||||
#endif
|
||||
) && wsi->h2.h2n->pps) {
|
||||
lwsl_info("servicing pps\n");
|
||||
if (lws_h2_do_pps_send(wsi)) {
|
||||
wsi->socket_is_permanently_unusable = 1;
|
||||
|
@ -376,6 +385,7 @@ user_service:
|
|||
}
|
||||
|
||||
if (wsi->mode != LWSCM_WSCL_ISSUE_HTTP_BODY &&
|
||||
wsi->mode != LWSCM_HTTP2_CLIENT_ACCEPTED &&
|
||||
!wsi->hdr_parsing_completed)
|
||||
goto bail_ok;
|
||||
|
||||
|
@ -399,11 +409,15 @@ user_service_go_again:
|
|||
*/
|
||||
|
||||
if (wsi->mode != LWSCM_HTTP2_SERVING &&
|
||||
wsi->mode != LWSCM_HTTP2_WS_SERVING) {
|
||||
wsi->mode != LWSCM_HTTP2_WS_SERVING &&
|
||||
wsi->mode != LWSCM_HTTP2_CLIENT &&
|
||||
wsi->mode != LWSCM_HTTP2_CLIENT_ACCEPTED) {
|
||||
lwsl_info("%s: non http2\n", __func__);
|
||||
goto notify;
|
||||
}
|
||||
|
||||
wsi = lws_get_network_wsi(wsi);
|
||||
|
||||
wsi->h2.requested_POLLOUT = 0;
|
||||
if (!wsi->h2.initialized) {
|
||||
lwsl_info("pollout on uninitialized http2 conn\n");
|
||||
|
@ -465,8 +479,7 @@ user_service_go_again:
|
|||
}
|
||||
|
||||
w->h2.requested_POLLOUT = 0;
|
||||
lwsl_info("%s: child %p (state %d)\n", __func__, (*wsi2),
|
||||
(*wsi2)->state);
|
||||
lwsl_info("%s: child %p (state %d)\n", __func__, w, w->state);
|
||||
|
||||
/* if we arrived here, even by looping, we checked choked */
|
||||
w->could_have_pending = 0;
|
||||
|
@ -477,13 +490,20 @@ user_service_go_again:
|
|||
n = lws_write(w, (uint8_t *)w->h2.pending_status_body +
|
||||
LWS_PRE,
|
||||
strlen(w->h2.pending_status_body +
|
||||
LWS_PRE), LWS_WRITE_HTTP_FINAL);
|
||||
LWS_PRE), LWS_WRITE_HTTP_FINAL);
|
||||
lws_free_set_NULL(w->h2.pending_status_body);
|
||||
lws_close_free_wsi(w, LWS_CLOSE_STATUS_NOSTATUS, "h2 end stream 1");
|
||||
wa = &wsi->h2.child_list;
|
||||
goto next_child;
|
||||
}
|
||||
|
||||
if (w->state == LWSS_HTTP2_CLIENT_WAITING_TO_SEND_HEADERS) {
|
||||
if (lws_h2_client_handshake(w))
|
||||
return -1;
|
||||
|
||||
goto next_child;
|
||||
}
|
||||
|
||||
if (w->state == LWSS_HTTP2_DEFERRING_ACTION) {
|
||||
|
||||
/*
|
||||
|
@ -602,7 +622,7 @@ user_service_go_again:
|
|||
}
|
||||
|
||||
if (lws_calllback_as_writeable(w) || w->h2.send_END_STREAM) {
|
||||
lwsl_debug("Closing POLLOUT child\n");
|
||||
lwsl_info("Closing POLLOUT child (end stream %d)\n", w->h2.send_END_STREAM);
|
||||
lws_close_free_wsi(w, LWS_CLOSE_STATUS_NOSTATUS, "h2 pollout handle");
|
||||
wa = &wsi->h2.child_list;
|
||||
}
|
||||
|
@ -1284,8 +1304,7 @@ lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd,
|
|||
*/
|
||||
|
||||
wsi = NULL;
|
||||
lws_start_foreach_ll(struct lws_vhost *, v,
|
||||
context->vhost_list) {
|
||||
lws_start_foreach_ll(struct lws_vhost *, v, context->vhost_list) {
|
||||
struct lws_timed_vh_protocol *nx;
|
||||
if (v->timed_vh_protocol_list) {
|
||||
lws_start_foreach_ll(struct lws_timed_vh_protocol *,
|
||||
|
@ -1539,6 +1558,10 @@ lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd,
|
|||
case LWSCM_HTTP2_SERVING:
|
||||
case LWSCM_HTTP2_WS_SERVING:
|
||||
case LWSCM_HTTP_CLIENT_ACCEPTED:
|
||||
case LWSCM_HTTP2_CLIENT_ACCEPTED:
|
||||
case LWSCM_HTTP2_CLIENT:
|
||||
|
||||
// lwsl_notice("%s: mode %d, state %d\n", __func__, wsi->mode, wsi->state);
|
||||
|
||||
/* 1: something requested a callback when it was OK to write */
|
||||
|
||||
|
@ -1669,8 +1692,8 @@ read:
|
|||
break;
|
||||
}
|
||||
|
||||
if (wsi->ah) {
|
||||
lwsl_info("%s: %p: inherited ah rx\n", __func__, wsi);
|
||||
if (wsi->ah && wsi->ah->rxlen - wsi->ah->rxpos) {
|
||||
lwsl_info("%s: %p: inherited ah rx %d\n", __func__, wsi, wsi->ah->rxlen - wsi->ah->rxpos);
|
||||
eff_buf.token_len = wsi->ah->rxlen -
|
||||
wsi->ah->rxpos;
|
||||
eff_buf.token = (char *)wsi->ah->rx +
|
||||
|
@ -1732,7 +1755,8 @@ read:
|
|||
n = 0;
|
||||
goto handled;
|
||||
case LWS_SSL_CAPABLE_ERROR:
|
||||
lwsl_info("Closing when error\n");
|
||||
lwsl_info("%s: LWS_SSL_CAPABLE_ERROR\n",
|
||||
__func__);
|
||||
goto close_and_handled;
|
||||
}
|
||||
// lwsl_notice("Actual RX %d\n", eff_buf.token_len);
|
||||
|
@ -1741,7 +1765,8 @@ read:
|
|||
|
||||
drain:
|
||||
#ifndef LWS_NO_CLIENT
|
||||
if (wsi->mode == LWSCM_HTTP_CLIENT_ACCEPTED &&
|
||||
if ((wsi->mode == LWSCM_HTTP_CLIENT_ACCEPTED ||
|
||||
wsi->mode == LWSS_HTTP2_CLIENT_ESTABLISHED) &&
|
||||
!wsi->told_user_closed) {
|
||||
|
||||
/*
|
||||
|
@ -1816,8 +1841,12 @@ drain:
|
|||
eff_buf.token_len = 0;
|
||||
} while (m);
|
||||
|
||||
if (wsi->ah) {
|
||||
lwsl_debug("%s: %p: detaching\n", __func__, wsi);
|
||||
if (wsi->ah
|
||||
#if !defined(LWS_NO_CLIENT)
|
||||
&& !wsi->client_h2_alpn
|
||||
#endif
|
||||
) {
|
||||
lwsl_notice("%s: %p: detaching ah\n", __func__, wsi);
|
||||
lws_header_table_force_to_detachable_state(wsi);
|
||||
lws_header_table_detach(wsi, 0);
|
||||
}
|
||||
|
|
|
@ -33,11 +33,21 @@ struct alpn_ctx {
|
|||
};
|
||||
|
||||
static struct alpn_ctx protos = { (unsigned char *)
|
||||
"\x08http/1.1", 3 + 9 };
|
||||
#if defined(LWS_WITH_HTTP2)
|
||||
"\x02h2"
|
||||
#endif
|
||||
"\x08http/1.1", 3 +
|
||||
#if defined(LWS_WITH_HTTP2)
|
||||
3 +
|
||||
#endif
|
||||
9 };
|
||||
|
||||
static struct alpn_ctx protos_h1 = { (unsigned char *)"\x08http/1.1", 3 + 9 };
|
||||
|
||||
int
|
||||
lws_ssl_client_bio_create(struct lws *wsi)
|
||||
{
|
||||
struct alpn_ctx *apro = &protos;
|
||||
X509_VERIFY_PARAM *param;
|
||||
char hostname[128], *p;
|
||||
|
||||
|
@ -65,7 +75,10 @@ lws_ssl_client_bio_create(struct lws *wsi)
|
|||
if (!wsi->ssl)
|
||||
return -1;
|
||||
|
||||
SSL_set_alpn_select_cb(wsi->ssl, &protos);
|
||||
if (wsi->use_ssl & LCCSCF_NOT_H2)
|
||||
apro = &protos_h1;
|
||||
|
||||
SSL_set_alpn_select_cb(wsi->ssl, apro);
|
||||
|
||||
if (wsi->vhost->ssl_info_event_mask)
|
||||
SSL_set_info_callback(wsi->ssl, lws_ssl_info_callback);
|
||||
|
@ -104,7 +117,10 @@ lws_tls_client_connect(struct lws *wsi)
|
|||
|
||||
if (n == 1) {
|
||||
SSL_get0_alpn_selected(wsi->ssl, &prot, &len);
|
||||
|
||||
#if !defined(LWS_NO_CLIENT)
|
||||
if (prot && !strcmp((char *)prot, "h2"))
|
||||
wsi->client_h2_alpn = 1;
|
||||
#endif
|
||||
if (prot && !strcmp((char *)prot, "http/1.1"))
|
||||
/*
|
||||
* If alpn asserts it is http/1.1, KA is mandatory.
|
||||
|
|
|
@ -88,6 +88,9 @@ OpenSSL_client_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
|
|||
|
||||
#if defined(LWS_HAVE_SSL_set_alpn_protos) && defined(LWS_HAVE_SSL_get0_alpn_selected)
|
||||
static const unsigned char client_alpn_protocols[] = {
|
||||
#if defined(LWS_WITH_HTTP2)
|
||||
2, 'h', '2',
|
||||
#endif
|
||||
8, 'h', 't', 't', 'p', '/', '1', '.', '1'
|
||||
};
|
||||
#endif
|
||||
|
@ -133,6 +136,10 @@ lws_ssl_client_bio_create(struct lws *wsi)
|
|||
}
|
||||
|
||||
#if defined(LWS_HAVE_SSL_set_alpn_protos) && defined(LWS_HAVE_SSL_get0_alpn_selected)
|
||||
if (wsi->use_ssl & LCCSCF_NOT_H2) {
|
||||
plist += 3;
|
||||
n -= 3;
|
||||
}
|
||||
SSL_set_alpn_protos(wsi->ssl, plist, n);
|
||||
#endif
|
||||
|
||||
|
@ -234,7 +241,12 @@ lws_tls_client_connect(struct lws *wsi)
|
|||
len = sizeof(a) - 1;
|
||||
memcpy(a, (const char *)prot, len);
|
||||
a[len] = '\0';
|
||||
|
||||
#if !defined(LWS_NO_CLIENT)
|
||||
if (prot && !strcmp(a, "h2")) {
|
||||
lwsl_info("%s: upgraded to H2\n", __func__);
|
||||
wsi->client_h2_alpn = 1;
|
||||
}
|
||||
#endif
|
||||
if (prot && !strcmp(a, "http/1.1"))
|
||||
/*
|
||||
* If alpn asserts it is http/1.1, KA is mandatory.
|
||||
|
@ -245,7 +257,7 @@ lws_tls_client_connect(struct lws *wsi)
|
|||
*/
|
||||
wsi->keepalive_active = 1;
|
||||
|
||||
lwsl_notice("client connect OK\n");
|
||||
lwsl_info("client connect OK\n");
|
||||
#endif
|
||||
return LWS_SSL_CAPABLE_DONE;
|
||||
}
|
||||
|
|
|
@ -257,6 +257,8 @@ lws_ssl_capable_read(struct lws *wsi, unsigned char *buf, int len)
|
|||
|
||||
lws_restart_ws_ping_pong_timer(wsi);
|
||||
|
||||
// lwsl_hexdump_err(buf, n);
|
||||
|
||||
/*
|
||||
* if it was our buffer that limited what we read,
|
||||
* check if SSL has additional data pending inside SSL buffers.
|
||||
|
|
|
@ -23,8 +23,9 @@
|
|||
* info.ssl_connection member (this is independent of whether the connection
|
||||
* is in ssl mode or not).
|
||||
*
|
||||
* Pipelined connections are slower (2.3s vs 1.6s for 8 connections), since the
|
||||
* transfers are serialized, but it is much less resource-intensive.
|
||||
* HTTP/1.0: Pipelining only possible if Keep-Alive: yes sent by server
|
||||
* HTTP/1.1: always possible... serializes requests
|
||||
* HTTP/2: always possible... all requests sent as individual streams in parallel
|
||||
*/
|
||||
|
||||
#include <libwebsockets.h>
|
||||
|
@ -34,6 +35,7 @@
|
|||
#include <time.h>
|
||||
|
||||
#define COUNT 8
|
||||
//#define STAGGERED_CONNECTIONS
|
||||
|
||||
struct user {
|
||||
int index;
|
||||
|
@ -58,7 +60,7 @@ callback_http(struct lws *wsi, enum lws_callback_reasons reason,
|
|||
client_wsi[u->index] = NULL;
|
||||
failed++;
|
||||
if (++completed == COUNT) {
|
||||
lwsl_user("Done: failed: %d\n", failed);
|
||||
lwsl_err("Done: failed: %d\n", failed);
|
||||
interrupted = 1;
|
||||
}
|
||||
break;
|
||||
|
@ -93,9 +95,13 @@ callback_http(struct lws *wsi, enum lws_callback_reasons reason,
|
|||
return 0; /* don't passthru */
|
||||
|
||||
case LWS_CALLBACK_COMPLETED_CLIENT_HTTP:
|
||||
lwsl_user("LWS_CALLBACK_COMPLETED_CLIENT_HTTP %d\n", u->index);
|
||||
client_wsi[u->index] = NULL;
|
||||
if (++completed == COUNT) {
|
||||
lwsl_user("Done: failed: %d\n", failed);
|
||||
if (!failed)
|
||||
lwsl_user("Done: all OK\n");
|
||||
else
|
||||
lwsl_err("Done: failed: %d\n", failed);
|
||||
interrupted = 1;
|
||||
/* so we exit immediately */
|
||||
lws_cancel_service(lws_get_context(wsi));
|
||||
|
@ -120,13 +126,65 @@ sigint_handler(int sig)
|
|||
interrupted = 1;
|
||||
}
|
||||
|
||||
unsigned long long us(void)
|
||||
{
|
||||
struct timeval t;
|
||||
|
||||
gettimeofday(&t, NULL);
|
||||
|
||||
return (t.tv_sec * 1000000ull) + t.tv_usec;
|
||||
}
|
||||
|
||||
static void
|
||||
lws_try_client_connection(struct lws_context *context, int m)
|
||||
{
|
||||
struct lws_client_connect_info i;
|
||||
|
||||
memset(&i, 0, sizeof i); /* otherwise uninitialized garbage */
|
||||
i.context = context;
|
||||
|
||||
#if 0
|
||||
i.port = 7681;
|
||||
i.address = "localhost";
|
||||
#else
|
||||
i.port = 443;
|
||||
i.address = "warmcat.com";
|
||||
#endif
|
||||
i.path = "/";
|
||||
i.host = i.address;
|
||||
i.origin = i.address;
|
||||
i.ssl_connection = LCCSCF_PIPELINE | /* enables h1 or h2 connection sharing */
|
||||
// LCCSCF_NOT_H2 | /* forces http/1 */
|
||||
LCCSCF_ALLOW_SELFSIGNED | /* allow selfsigned cert */
|
||||
LCCSCF_USE_SSL;
|
||||
i.method = "GET";
|
||||
|
||||
i.protocol = protocols[0].name;
|
||||
|
||||
i.pwsi = &client_wsi[m];
|
||||
user[m].index = m;
|
||||
i.userdata = &user[m];
|
||||
|
||||
if (!lws_client_connect_via_info(&i)) {
|
||||
failed++;
|
||||
if (++completed == COUNT) {
|
||||
lwsl_user("Done: failed: %d\n", failed);
|
||||
interrupted = 1;
|
||||
}
|
||||
} else
|
||||
lwsl_user("started connection %d\n", m);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct lws_context_creation_info info;
|
||||
struct lws_client_connect_info i;
|
||||
struct lws_context *context;
|
||||
struct timeval start, end;
|
||||
int n = 0;
|
||||
unsigned long long start
|
||||
#if defined(STAGGERED_CONNECTIONS)
|
||||
, next
|
||||
#endif
|
||||
;
|
||||
int n = 0, m;
|
||||
|
||||
signal(SIGINT, sigint_handler);
|
||||
lws_set_log_level(LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE
|
||||
|
@ -160,44 +218,40 @@ int main(int argc, char **argv)
|
|||
return 1;
|
||||
}
|
||||
|
||||
memset(&i, 0, sizeof i); /* otherwise uninitialized garbage */
|
||||
i.context = context;
|
||||
#if !defined(STAGGERED_CONNECTIONS)
|
||||
/*
|
||||
* just pile on all the connections at once, testing the queueing
|
||||
*/
|
||||
for (m = 0; m < (int)LWS_ARRAY_SIZE(client_wsi); m++)
|
||||
lws_try_client_connection(context, m);
|
||||
#else
|
||||
next =
|
||||
#endif
|
||||
start = us();
|
||||
m = 0;
|
||||
while (n >= 0 && !interrupted) {
|
||||
|
||||
i.port = 443;
|
||||
i.address = "warmcat.com";
|
||||
i.path = "/";
|
||||
i.host = i.address;
|
||||
i.origin = i.address;
|
||||
i.ssl_connection = LCCSCF_PIPELINE /* enables http1.1 pipelining */ |
|
||||
LCCSCF_USE_SSL;
|
||||
i.method = "GET";
|
||||
#if defined(STAGGERED_CONNECTIONS)
|
||||
/*
|
||||
* open the connections at 100ms intervals, with the last
|
||||
* one being after 1s, testing queueing, and direct H2 stream
|
||||
* addition stability
|
||||
*/
|
||||
if (us() > next && m < (int)LWS_ARRAY_SIZE(client_wsi)) {
|
||||
|
||||
i.protocol = protocols[0].name;
|
||||
lws_try_client_connection(context, m++);
|
||||
|
||||
gettimeofday(&start, NULL);
|
||||
|
||||
for (n = 0; n < (int)LWS_ARRAY_SIZE(client_wsi); n++) {
|
||||
i.pwsi = &client_wsi[n];
|
||||
user[n].index = n;
|
||||
i.userdata = &user[n];
|
||||
|
||||
if (!lws_client_connect_via_info(&i)) {
|
||||
failed++;
|
||||
if (++completed == COUNT) {
|
||||
lwsl_user("Done: failed: %d\n", failed);
|
||||
interrupted = 1;
|
||||
}
|
||||
if (m == (int)LWS_ARRAY_SIZE(client_wsi) - 1)
|
||||
next = us() + 1000000;
|
||||
else
|
||||
next = us() + 100000;
|
||||
}
|
||||
#endif
|
||||
|
||||
n = lws_service(context, 1000);
|
||||
}
|
||||
|
||||
while (n >= 0 && !interrupted)
|
||||
n = lws_service(context, 1000);
|
||||
|
||||
gettimeofday(&end, NULL);
|
||||
|
||||
lwsl_user("Duration: %lldms\n",
|
||||
(((end.tv_sec * 1000000ll) + end.tv_usec) -
|
||||
((start.tv_sec * 1000000ll) + start.tv_usec)) / 1000);
|
||||
lwsl_user("Duration: %lldms\n", (us() - start) / 1000);
|
||||
|
||||
lws_context_destroy(context);
|
||||
lwsl_user("Completed\n");
|
||||
|
|
|
@ -61,6 +61,7 @@ callback_http(struct lws *wsi, enum lws_callback_reasons reason,
|
|||
return 0; /* don't passthru */
|
||||
|
||||
case LWS_CALLBACK_COMPLETED_CLIENT_HTTP:
|
||||
lwsl_user("LWS_CALLBACK_COMPLETED_CLIENT_HTTP\n");
|
||||
client_wsi = NULL;
|
||||
break;
|
||||
|
||||
|
@ -132,7 +133,7 @@ int main(int argc, char **argv)
|
|||
i.path = "/";
|
||||
i.host = i.address;
|
||||
i.origin = i.address;
|
||||
i.ssl_connection = 1;
|
||||
i.ssl_connection = /* LCCSCF_NOT_H2 | */ LCCSCF_USE_SSL;
|
||||
i.method = "GET";
|
||||
|
||||
i.protocol = protocols[0].name;
|
||||
|
|
|
@ -82,7 +82,7 @@ int main(int argc, char **argv)
|
|||
/* for LLL_ verbosity above NOTICE to be built into lws,
|
||||
* lws must have been configured and built with
|
||||
* -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */
|
||||
/* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */
|
||||
| LLL_INFO /* | LLL_PARSER */ /* | LLL_HEADER */
|
||||
/* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */
|
||||
/* | LLL_DEBUG */, NULL);
|
||||
|
||||
|
@ -114,7 +114,7 @@ int main(int argc, char **argv)
|
|||
i.path = "/";
|
||||
i.host = i.address;
|
||||
i.origin = i.address;
|
||||
i.ssl_connection = 1;
|
||||
i.ssl_connection = LCCSCF_USE_SSL;
|
||||
|
||||
i.protocol = protocols[0].name; /* "dumb-increment-protocol" */
|
||||
i.pwsi = &client_wsi;
|
||||
|
|
|
@ -442,7 +442,9 @@ int main(int argc, char **argv)
|
|||
|
||||
lwsl_notice(" Using resource path \"%s\"\n", resource_path);
|
||||
|
||||
#if UV_VERSION_MAJOR > 0
|
||||
uv_loop_init(&loop);
|
||||
#endif
|
||||
#if defined(TEST_DYNAMIC_VHOST)
|
||||
uv_timer_init(&loop, &timeout_watcher);
|
||||
#endif
|
||||
|
@ -561,9 +563,9 @@ bail:
|
|||
#endif
|
||||
|
||||
/* nothing left in the foreign loop, destroy it */
|
||||
|
||||
#if UV_VERSION_MAJOR > 0
|
||||
uv_loop_close(&loop);
|
||||
|
||||
#endif
|
||||
lwsl_notice("libwebsockets-test-server exited cleanly\n");
|
||||
|
||||
#ifndef _WIN32
|
||||
|
|
Loading…
Add table
Reference in a new issue