diff --git a/include/libwebsockets/lws-http.h b/include/libwebsockets/lws-http.h index 77fa49cc2..22b195b9f 100644 --- a/include/libwebsockets/lws-http.h +++ b/include/libwebsockets/lws-http.h @@ -658,13 +658,22 @@ lws_http_transaction_completed(struct lws *wsi); * it to be reused by another connection * \param wsi: http connection * - * Returns 1 if the HTTP connection must close now - * Returns 0 and resets connection to wait for new HTTP header / - * transaction if possible + * If the wsi has an ah headers struct attached, detach it. */ LWS_VISIBLE LWS_EXTERN int lws_http_headers_detach(struct lws *wsi); +/** + * lws_http_mark_sse() - called to indicate this http stream is now doing SSE + * + * \param wsi: http connection + * + * Cancel any timeout on the wsi, and for h2, mark the network connection as + * containing an immortal stream for the duration the SSE stream is open. + */ +LWS_VISIBLE LWS_EXTERN int +lws_http_mark_sse(struct lws *wsi); + /** * lws_http_compression_apply() - apply an http compression transform * diff --git a/lib/core-net/private.h b/lib/core-net/private.h index 3688e0697..3d4b4c420 100644 --- a/lib/core-net/private.h +++ b/lib/core-net/private.h @@ -539,6 +539,7 @@ struct lws { unsigned int http2_substream:1; unsigned int upgraded_to_http2:1; unsigned int h2_stream_carries_ws:1; + unsigned int h2_stream_carries_sse:1; unsigned int seen_nonpseudoheader:1; unsigned int listener:1; unsigned int user_space_externally_allocated:1; @@ -618,7 +619,7 @@ struct lws { #if defined(LWS_WITH_STATS) && defined(LWS_WITH_TLS) char seen_rx; #endif - uint8_t ws_over_h2_count; + uint8_t immortal_substream_count; /* volatile to make sure code is aware other thread can change */ volatile char handling_pollout; volatile char leave_pollout_active; diff --git a/lib/core-net/wsi-timeout.c b/lib/core-net/wsi-timeout.c index bf4104725..a3dd4e44c 100644 --- a/lib/core-net/wsi-timeout.c +++ b/lib/core-net/wsi-timeout.c @@ -184,6 +184,8 @@ lws_set_timeout(struct lws *wsi, enum pending_timeout reason, int secs) { struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + // lwsl_info("%s: %p: %d %d\n", __func__, wsi, reason, secs); + if (secs == LWS_TO_KILL_SYNC) { lws_remove_from_timeout_list(wsi); lwsl_debug("synchronously killing %p\n", wsi); diff --git a/lib/core-net/wsi.c b/lib/core-net/wsi.c index 2b93331a8..75eb8d951 100644 --- a/lib/core-net/wsi.c +++ b/lib/core-net/wsi.c @@ -881,3 +881,21 @@ lws_bind_protocol(struct lws *wsi, const struct lws_protocols *p, return 0; } + +int +lws_http_mark_sse(struct lws *wsi) +{ + lws_http_headers_detach(wsi); + lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); + + if (wsi->http2_substream) { + struct lws *nwsi = lws_get_network_wsi(wsi); + + wsi->h2_stream_carries_sse = 1; + nwsi->immortal_substream_count++; + if (nwsi->immortal_substream_count == 1) + lws_set_timeout(nwsi, NO_PENDING_TIMEOUT, 0); + } + + return 0; +} diff --git a/lib/roles/h2/http2.c b/lib/roles/h2/http2.c index 7650e8fe3..39fe8a322 100644 --- a/lib/roles/h2/http2.c +++ b/lib/roles/h2/http2.c @@ -822,7 +822,7 @@ lws_h2_parse_frame_header(struct lws *wsi) } /* let the network wsi live a bit longer if subs are active */ - if (!wsi->ws_over_h2_count) + if (!wsi->immortal_substream_count) lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE, 31); if (h2n->sid) @@ -1806,7 +1806,7 @@ lws_h2_parser(struct lws *wsi, unsigned char *in, lws_filepos_t inlen, * subs are active... our frame may take a long * time to chew through */ - if (!wsi->ws_over_h2_count) + if (!wsi->immortal_substream_count) lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE, 31); diff --git a/lib/roles/h2/ops-h2.c b/lib/roles/h2/ops-h2.c index 573eb83fc..1c0b32eac 100644 --- a/lib/roles/h2/ops-h2.c +++ b/lib/roles/h2/ops-h2.c @@ -491,11 +491,11 @@ rops_check_upgrades_h2(struct lws *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++; + nwsi->immortal_substream_count++; if (lws_process_ws_upgrade(wsi)) return LWS_UPG_RET_BAIL; - if (nwsi->ws_over_h2_count == 1) + if (nwsi->immortal_substream_count == 1) lws_set_timeout(nwsi, NO_PENDING_TIMEOUT, 0); lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); @@ -675,12 +675,12 @@ rops_close_kill_connection_h2(struct lws *wsi, enum lws_close_status reason) lws_free_set_NULL(wsi->h2.pending_status_body); } - if (wsi->h2_stream_carries_ws) { + if (wsi->h2_stream_carries_ws || wsi->h2_stream_carries_sse) { struct lws *nwsi = lws_get_network_wsi(wsi); - nwsi->ws_over_h2_count++; + nwsi->immortal_substream_count--; /* if no ws, then put a timeout on the parent wsi */ - if (!nwsi->ws_over_h2_count) + if (!nwsi->immortal_substream_count) __lws_set_timeout(nwsi, PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE, 31); } diff --git a/minimal-examples/http-server/minimal-http-server-sse-ring/minimal-http-server-sse-ring.c b/minimal-examples/http-server/minimal-http-server-sse-ring/minimal-http-server-sse-ring.c index 3176cd70d..ac9d34f9b 100644 --- a/minimal-examples/http-server/minimal-http-server-sse-ring/minimal-http-server-sse-ring.c +++ b/minimal-examples/http-server/minimal-http-server-sse-ring/minimal-http-server-sse-ring.c @@ -216,21 +216,13 @@ callback_sse(struct lws *wsi, enum lws_callback_reasons reason, void *user, pss->wsi = wsi; /* - * Drop the ah that contains the headers associated with - * this connection... ah are a scarce resource, if we don't - * drop it lws will forcibly drop the whole connection to free - * the ah after 5 minutes or so. - * - * If the content of any http headers are important to you for - * deciding what to send, copy them out to the pss before - * doing the below to drop the ah. + * This tells lws we are no longer a normal http stream, + * but are an "immortal" (plus or minus whatever timeout you + * set on it afterwards) SSE stream. In http/2 case that also + * stops idle timeouts being applied to the network connection + * while this wsi is still open. */ - lws_http_headers_detach(wsi); - - /* Unlike a normal http connection, we don't want any specific - * timeout. We want to stay up until the client drops us */ - - lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); + lws_http_mark_sse(wsi); /* write the body separately */ diff --git a/minimal-examples/http-server/minimal-http-server-sse/README.md b/minimal-examples/http-server/minimal-http-server-sse/README.md index 7a423f480..cc8f47802 100644 --- a/minimal-examples/http-server/minimal-http-server-sse/README.md +++ b/minimal-examples/http-server/minimal-http-server-sse/README.md @@ -11,6 +11,8 @@ content over Server Side Events. ## usage +You can give -s to listen using https on port :443 + ``` $ ./lws-minimal-http-server-sse [2018/04/20 06:09:56:9974] USER: LWS minimal http Server-Side Events | visit http://localhost:7681 diff --git a/minimal-examples/http-server/minimal-http-server-sse/localhost-100y.cert b/minimal-examples/http-server/minimal-http-server-sse/localhost-100y.cert new file mode 100644 index 000000000..6f372db40 --- /dev/null +++ b/minimal-examples/http-server/minimal-http-server-sse/localhost-100y.cert @@ -0,0 +1,34 @@ +-----BEGIN CERTIFICATE----- +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 +e2Hl/7M0c/lbZ4FvXgARlex2rkgS0Ka06HE= +-----END CERTIFICATE----- diff --git a/minimal-examples/http-server/minimal-http-server-sse/localhost-100y.key b/minimal-examples/http-server/minimal-http-server-sse/localhost-100y.key new file mode 100644 index 000000000..148f8598e --- /dev/null +++ b/minimal-examples/http-server/minimal-http-server-sse/localhost-100y.key @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +fBrpEY1IATtPq1taBZZogRqI3rOkkPk= +-----END PRIVATE KEY----- diff --git a/minimal-examples/http-server/minimal-http-server-sse/minimal-http-server-sse.c b/minimal-examples/http-server/minimal-http-server-sse/minimal-http-server-sse.c index 74414df0e..4a16a2a7f 100644 --- a/minimal-examples/http-server/minimal-http-server-sse/minimal-http-server-sse.c +++ b/minimal-examples/http-server/minimal-http-server-sse/minimal-http-server-sse.c @@ -65,22 +65,14 @@ callback_sse(struct lws *wsi, enum lws_callback_reasons reason, void *user, if (lws_finalize_write_http_header(wsi, start, &p, end)) return 1; - /* Unlike a normal http connection, we don't want any specific - * timeout. We want to stay up until the client drops us */ - - lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); - /* - * Drop the ah that contains the headers associated with - * this connection... ah are a scarce resource, if we don't - * drop it lws will forcibly drop the whole connection to free - * the ah after 5 minutes or so. - * - * If the content of any http headers are important to you for - * deciding what to send, copy them out to the pss before - * doing the below to drop the ah. + * This tells lws we are no longer a normal http stream, + * but are an "immortal" (plus or minus whatever timeout you + * set on it afterwards) SSE stream. In http/2 case that also + * stops idle timeouts being applied to the network connection + * while this wsi is still open. */ - lws_http_headers_detach(wsi); + lws_http_mark_sse(wsi); /* write the body separately */ @@ -204,11 +196,18 @@ int main(int argc, const char **argv) lwsl_user("LWS minimal http Server-Side Events | visit http://localhost:7681\n"); memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.port = 7681; + info.protocols = protocols; info.mounts = &mount; info.options = LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE; + info.port = 7681; + if (lws_cmdline_option(argc, argv, "-s")) { + info.port = 443; + info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; + info.ssl_cert_filepath = "localhost-100y.cert"; + info.ssl_private_key_filepath = "localhost-100y.key"; + } context = lws_create_context(&info); if (!context) {