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