File: minimal-http-server.c

package info (click to toggle)
libwebsockets 4.3.5-2
  • links: PTS
  • area: main
  • in suites: forky, sid
  • size: 31,288 kB
  • sloc: ansic: 194,407; javascript: 1,550; sh: 1,387; cpp: 505; java: 461; perl: 405; xml: 118; makefile: 76; awk: 5
file content (158 lines) | stat: -rw-r--r-- 3,969 bytes parent folder | download | duplicates (2)
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;
}