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;