diff --git a/minimal-examples/http-server/minimal-http-server-dynamic/localhost-100y.cert b/minimal-examples/http-server/minimal-http-server-dynamic/localhost-100y.cert new file mode 100644 index 000000000..6f372db40 --- /dev/null +++ b/minimal-examples/http-server/minimal-http-server-dynamic/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-dynamic/localhost-100y.key b/minimal-examples/http-server/minimal-http-server-dynamic/localhost-100y.key new file mode 100644 index 000000000..148f8598e --- /dev/null +++ b/minimal-examples/http-server/minimal-http-server-dynamic/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-dynamic/minimal-http-server-dynamic.c b/minimal-examples/http-server/minimal-http-server-dynamic/minimal-http-server-dynamic.c index b7bdaac1a..351b5c17f 100644 --- a/minimal-examples/http-server/minimal-http-server-dynamic/minimal-http-server-dynamic.c +++ b/minimal-examples/http-server/minimal-http-server-dynamic/minimal-http-server-dynamic.c @@ -26,8 +26,12 @@ * that is unrelated to (shorter than) the lifetime of the network connection. */ struct pss { - char str[128]; - int len; + char path[128]; + + int times; + int budget; + + int content_lines; }; static int interrupted; @@ -37,30 +41,49 @@ callback_dynamic_http(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) { struct pss *pss = (struct pss *)user; - uint8_t buf[LWS_PRE + 256], *start = &buf[LWS_PRE], *p = start, - *end = &buf[sizeof(buf) - 1]; + uint8_t buf[LWS_PRE + 2048], *start = &buf[LWS_PRE], *p = start, + *end = &buf[sizeof(buf) - LWS_PRE - 1]; time_t t; + int n; switch (reason) { case LWS_CALLBACK_HTTP: /* in contains the url part after our mountpoint /dyn, if any */ + lws_snprintf(pss->path, sizeof(pss->path), "%s", (const char *)in); - t = time(NULL); - pss->len = lws_snprintf(pss->str, sizeof(pss->str), - "" - "" - "
Dynamic content for '%s' from mountpoint." - "
Time: %s" - "", (const char *)in, ctime(&t)); - - /* prepare and write http headers */ + /* + * prepare and write http headers... with regards to content- + * length, there are three approaches: + * + * - http/1.0 or connection:close: no need, but no pipelining + * - http/1.1 or connected:keep-alive + * (keep-alive is default for 1.1): content-length required + * - http/2: no need, LWS_WRITE_HTTP_FINAL closes the stream + * + * giving the api below LWS_ILLEGAL_HTTP_CONTENT_LEN instead of + * a content length forces the connection response headers to + * send back "connection: close", disabling keep-alive. + * + * If you know the final content-length, it's always OK to give + * it and keep-alive can work then if otherwise possible. But + * often you don't know it and avoiding having to compute it + * at header-time makes life easier at the server. + */ if (lws_add_http_common_headers(wsi, HTTP_STATUS_OK, - "text/html", pss->len, &p, end)) + "text/html", + LWS_ILLEGAL_HTTP_CONTENT_LEN, /* no content len */ + &p, end)) return 1; if (lws_finalize_write_http_header(wsi, start, &p, end)) return 1; + pss->times = 0; + pss->budget = atoi(in + 1); + pss->content_lines = 0; + if (!pss->budget) + pss->budget = 10; + /* write the body separately */ lws_callback_on_writable(wsi); @@ -68,16 +91,65 @@ callback_dynamic_http(struct lws *wsi, enum lws_callback_reasons reason, case LWS_CALLBACK_HTTP_WRITEABLE: - if (!pss || !pss->len) + if (!pss || pss->times > pss->budget) break; /* - * Use LWS_WRITE_HTTP for intermediate writes, on http/2 - * lws uses this to understand to end the stream with this - * frame + * We send a large reply in pieces of around 2KB each. + * + * For http/1, it's possible to send a large buffer at once, + * but lws will malloc() up a temp buffer to hold any data + * that the kernel didn't accept in one go. This is expensive + * in memory and cpu, so it's better to stage the creation of + * the data to be sent each time. + * + * For http/2, large data frames would block the whole + * connection, not just the stream and are not allowed. Lws + * will call back on writable when the stream both has transmit + * credit and the round-robin fair access for sibling streams + * allows it. + * + * For http/2, we must send the last part with + * LWS_WRITE_HTTP_FINAL to close the stream representing + * this transaction. */ - if (lws_write(wsi, (uint8_t *)pss->str, pss->len, - LWS_WRITE_HTTP_FINAL) != pss->len) + n = LWS_WRITE_HTTP; + if (pss->times == pss->budget) + n = LWS_WRITE_HTTP_FINAL; + + if (!pss->times) { + /* + * the first time, we print some html title + */ + t = time(NULL); + /* + * to work with http/2, we must take care about LWS_PRE + * valid behind the buffer we will send. + */ + p += lws_snprintf((char *)p, end - p, "" + "" + "
Dynamic content for '%s' from mountpoint." + "
Time: %s

" + "", pss->path, ctime(&t)); + } else { + /* + * after the first time, we create bulk content. + * + * Again we take care about LWS_PRE valid behind the + * buffer we will send. + */ + + while (lws_ptr_diff(end, p) > 80) + p += lws_snprintf((char *)p, end - p, + "%d.%d: this is some content... ", + pss->times, pss->content_lines++); + + p += lws_snprintf((char *)p, end - p, "

"); + } + + pss->times++; + if (lws_write(wsi, (uint8_t *)start, lws_ptr_diff(p, start), n) != + lws_ptr_diff(p, start)) return 1; /* @@ -85,8 +157,11 @@ callback_dynamic_http(struct lws *wsi, enum lws_callback_reasons reason, * HTTP/1.1 or HTTP1.0 + KA: wait / process next transaction * HTTP/2: stream ended, parent connection remains up */ - if (lws_http_transaction_completed(wsi)) + if (n == LWS_WRITE_HTTP_FINAL) { + if (lws_http_transaction_completed(wsi)) return -1; + } else + lws_callback_on_writable(wsi); return 0; @@ -173,9 +248,8 @@ int main(int argc, const char **argv) lwsl_user("LWS minimal http server dynamic | 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_DO_SSL_GLOBAL_INIT | + LWS_SERVER_OPTION_EXPLICIT_VHOSTS; /* for testing ah queue, not useful in real world */ if (lws_cmdline_option(argc, argv, "--ah1")) @@ -187,9 +261,35 @@ int main(int argc, const char **argv) return 1; } + /* http on 7681 */ + + info.port = 7681; + info.protocols = protocols; + info.mounts = &mount; + info.vhost_name = "http"; + + if (!lws_create_vhost(context, &info)) { + lwsl_err("Failed to create tls vhost\n"); + goto bail; + } + + /* https on 7682 */ + + info.port = 7682; + info.error_document_404 = "/404.html"; + info.ssl_cert_filepath = "localhost-100y.cert"; + info.ssl_private_key_filepath = "localhost-100y.key"; + info.vhost_name = "localhost"; + + if (!lws_create_vhost(context, &info)) { + lwsl_err("Failed to create tls vhost\n"); + goto bail; + } + while (n >= 0 && !interrupted) n = lws_service(context, 1000); +bail: lws_context_destroy(context); return 0;