From b40e19edca76c0a8b6c385c838bdd2173016bc2a Mon Sep 17 00:00:00 2001 From: Andy Green Date: Wed, 2 Oct 2019 10:49:18 -0700 Subject: [PATCH] h2: end stream with end headers This shouldn't be necessary; just END_HEADERS flag should be enough. But nghttp2 will not talk to us unless we end the stream from our side. Unfortunately ending the stream at the time we sent the headers means we cannot support the long poll half-close scheme. So add a quirk flag to optionally support this behaviour of nghttp2 when the client is creating the connection. --- include/libwebsockets/lws-client.h | 1 + lib/core-net/connect.c | 1 + lib/core-net/private-lib-core-net.h | 1 + lib/roles/h2/http2.c | 31 ++++++++++++++++++++++++++--- 4 files changed, 31 insertions(+), 3 deletions(-) diff --git a/include/libwebsockets/lws-client.h b/include/libwebsockets/lws-client.h index 22a4ec128..3c0393468 100644 --- a/include/libwebsockets/lws-client.h +++ b/include/libwebsockets/lws-client.h @@ -40,6 +40,7 @@ enum lws_client_connect_ssl_connection_flags { LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK = (1 << 2), LCCSCF_ALLOW_EXPIRED = (1 << 3), LCCSCF_ALLOW_INSECURE = (1 << 4), + LCCSCF_H2_QUIRK_NGHTTP2_END_STREAM = (1 << 5), LCCSCF_PIPELINE = (1 << 16), /**< Serialize / pipeline multiple client connections diff --git a/lib/core-net/connect.c b/lib/core-net/connect.c index 6e449c946..ccad6bf91 100644 --- a/lib/core-net/connect.c +++ b/lib/core-net/connect.c @@ -60,6 +60,7 @@ lws_client_connect_via_info(const struct lws_client_connect_info *i) wsi->context = i->context; wsi->desc.sockfd = LWS_SOCK_INVALID; wsi->seq = i->seq; + wsi->flags = i->ssl_connection; if (i->retry_and_idle_policy) wsi->retry_policy = i->retry_and_idle_policy; else diff --git a/lib/core-net/private-lib-core-net.h b/lib/core-net/private-lib-core-net.h index 680326dbf..b841dc1d1 100644 --- a/lib/core-net/private-lib-core-net.h +++ b/lib/core-net/private-lib-core-net.h @@ -682,6 +682,7 @@ struct lws { #if defined(LWS_WITH_CLIENT) int chunk_remaining; + int flags; #endif unsigned int cache_secs; diff --git a/lib/roles/h2/http2.c b/lib/roles/h2/http2.c index 39dd4fe06..b45a00b47 100644 --- a/lib/roles/h2/http2.c +++ b/lib/roles/h2/http2.c @@ -1324,6 +1324,9 @@ lws_h2_parse_end_of_frame(struct lws *wsi) /* pass on the initial headers to SID 1 */ h2n->swsi->http.ah = wsi->http.ah; h2n->swsi->client_h2_substream = 1; +#if defined(LWS_WITH_CLIENT) + h2n->swsi->flags = wsi->flags; +#endif h2n->swsi->protocol = wsi->protocol; if (h2n->swsi->user_space && !h2n->swsi->user_space_externally_allocated) @@ -2184,7 +2187,7 @@ lws_h2_client_handshake(struct lws *wsi) *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; + int n, m; /* * The identifier of a newly established stream MUST be numerically * greater than all streams that the initiating endpoint has opened or @@ -2233,7 +2236,7 @@ lws_h2_client_handshake(struct lws *wsi) if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_COLON_SCHEME, - (unsigned char *)"https", 4, + (unsigned char *)"https", 5, &p, end)) goto fail_length; @@ -2252,6 +2255,18 @@ lws_h2_client_handshake(struct lws *wsi) &p, end)) goto fail_length; + if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HOST, + (unsigned char *)lws_hdr_simple_ptr(wsi, + _WSI_TOKEN_CLIENT_HOST), + lws_hdr_total_length(wsi, _WSI_TOKEN_CLIENT_HOST), + &p, end)) + goto fail_length; + + if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_USER_AGENT, + (unsigned char *)"lwsss", 5, + &p, end)) + goto fail_length; + /* give userland a chance to append, eg, cookies */ if (wsi->protocol->callback(wsi, @@ -2265,7 +2280,17 @@ lws_h2_client_handshake(struct lws *wsi) #if defined(LWS_WITH_DETAILED_LATENCY) wsi->detlat.earliest_write_req_pre_write = lws_now_usecs(); #endif - n = lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS); + + m = LWS_WRITE_HTTP_HEADERS; +#if defined(LWS_WITH_CLIENT) + /* below is not needed in spec, indeed it destroys the long poll + * feature, but required by nghttp2 */ + if (wsi->flags & LCCSCF_H2_QUIRK_NGHTTP2_END_STREAM) + m |= LWS_WRITE_H2_STREAM_END; +#endif + + n = lws_write(wsi, start, p - start, m); + if (n != (p - start)) { lwsl_err("_write returned %d from %ld\n", n, (long)(p - start));