1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
|
/*
* lws-minimal-http-server-h2-long-poll
*
* Written in 2010-2019 by Andy Green <andy@warmcat.com>
*
* This file is made available under the Creative Commons CC0 1.0
* Universal Public Domain Dedication.
*
* This demonstrates an h2 server that supports "long poll"
* immortal client connections. For simplicity it doesn't serve
* any regular files, you can add a mount to do it if you want.
*
* The protocol keeps the long poll h2 stream open, and sends
* the time on the stream once per minute. Normally idle h2
* connections are closed by default within 30s, so this demonstrates
* the stream and network connection are operating as "immortal"
* on both sides.
*
* See http-client/minimal-http-client-h2-long-poll for the
* client example that connects and transitions the stream to the
* immortal long poll mode.
*/
#include <libwebsockets.h>
#include <string.h>
#include <signal.h>
static int interrupted;
struct pss {
struct lws *wsi;
lws_sorted_usec_list_t sul;
char pending;
};
static const lws_retry_bo_t retry = {
.secs_since_valid_ping = 5,
.secs_since_valid_hangup = 10,
};
static void
sul_cb(lws_sorted_usec_list_t *sul)
{
struct pss *pss = (struct pss *)lws_container_of(sul, struct pss, sul);
pss->pending = 1;
lws_callback_on_writable(pss->wsi);
/* interval 1min... longer than any normal timeout */
lws_sul_schedule(lws_get_context(pss->wsi), 0, &pss->sul, sul_cb,
60 * LWS_US_PER_SEC);
}
static int
callback_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 + LWS_RECOMMENDED_MIN_HEADER_SPACE],
*start = &buf[LWS_PRE], *p = start,
*end = buf + sizeof(buf) - 1;
int m, n;
switch (reason) {
case LWS_CALLBACK_HTTP:
lwsl_user("%s: connect\n", __func__);
pss->wsi = wsi;
if (lws_add_http_common_headers(wsi, HTTP_STATUS_OK,
"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;
sul_cb(&pss->sul);
return 0;
case LWS_CALLBACK_CLOSED_HTTP:
if (!pss)
break;
lws_sul_cancel(&pss->sul);
break;
case LWS_CALLBACK_HTTP_WRITEABLE:
if (!pss->pending)
break;
n = lws_snprintf((char *)p, sizeof(buf) - LWS_PRE, "%llu",
(unsigned long long)lws_now_usecs());
m = lws_write(wsi, p, (unsigned int)n, LWS_WRITE_HTTP);
if (m < n) {
lwsl_err("ERROR %d writing to socket\n", n);
return -1;
}
break;
default:
break;
}
return lws_callback_http_dummy(wsi, reason, user, in, len);
}
static struct lws_protocols protocols[] = {
{ "http", callback_http, sizeof(struct pss), 0, 0, NULL, 0 },
LWS_PROTOCOL_LIST_TERM
};
void sigint_handler(int sig)
{
interrupted = 1;
}
int main(int argc, const char **argv)
{
struct lws_context_creation_info info;
struct lws_context *context;
const char *p;
int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE;
signal(SIGINT, sigint_handler);
if ((p = lws_cmdline_option(argc, argv, "-d")))
logs = atoi(p);
lws_set_log_level(logs, NULL);
lwsl_user("LWS minimal http server h2 long poll\n");
memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
info.port = 7681;
#if defined(LWS_WITH_TLS)
info.ssl_cert_filepath = "localhost-100y.cert";
info.ssl_private_key_filepath = "localhost-100y.key";
#endif
info.protocols = protocols;
info.options =
LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT |
LWS_SERVER_OPTION_VH_H2_HALF_CLOSED_LONG_POLL |
LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE;
/* the default validity check is 5m / 5m10s... -v = 5s / 10s */
if (lws_cmdline_option(argc, argv, "-v"))
info.retry_and_idle_policy = &retry;
context = lws_create_context(&info);
if (!context) {
lwsl_err("lws init failed\n");
return 1;
}
while (n >= 0 && !interrupted)
n = lws_service(context, 0);
lws_context_destroy(context);
return 0;
}
|