1d4afb5ceSopenharmony_ci/*
2d4afb5ceSopenharmony_ci * libwebsockets-test-server - libwebsockets test implementation
3d4afb5ceSopenharmony_ci *
4d4afb5ceSopenharmony_ci * Written in 2010-2019 by Andy Green <andy@warmcat.com>
5d4afb5ceSopenharmony_ci *
6d4afb5ceSopenharmony_ci * This file is made available under the Creative Commons CC0 1.0
7d4afb5ceSopenharmony_ci * Universal Public Domain Dedication.
8d4afb5ceSopenharmony_ci *
9d4afb5ceSopenharmony_ci * The person who associated a work with this deed has dedicated
10d4afb5ceSopenharmony_ci * the work to the public domain by waiving all of his or her rights
11d4afb5ceSopenharmony_ci * to the work worldwide under copyright law, including all related
12d4afb5ceSopenharmony_ci * and neighboring rights, to the extent allowed by law. You can copy,
13d4afb5ceSopenharmony_ci * modify, distribute and perform the work, even for commercial purposes,
14d4afb5ceSopenharmony_ci * all without asking permission.
15d4afb5ceSopenharmony_ci *
16d4afb5ceSopenharmony_ci * The test apps are intended to be adapted for use in your code, which
17d4afb5ceSopenharmony_ci * may be proprietary.  So unlike the library itself, they are licensed
18d4afb5ceSopenharmony_ci * Public Domain.
19d4afb5ceSopenharmony_ci */
20d4afb5ceSopenharmony_ci
21d4afb5ceSopenharmony_ci#if !defined (LWS_PLUGIN_STATIC)
22d4afb5ceSopenharmony_ci#if !defined(LWS_DLL)
23d4afb5ceSopenharmony_ci#define LWS_DLL
24d4afb5ceSopenharmony_ci#endif
25d4afb5ceSopenharmony_ci#if !defined(LWS_INTERNAL)
26d4afb5ceSopenharmony_ci#define LWS_INTERNAL
27d4afb5ceSopenharmony_ci#endif
28d4afb5ceSopenharmony_ci#include <libwebsockets.h>
29d4afb5ceSopenharmony_ci#endif
30d4afb5ceSopenharmony_ci
31d4afb5ceSopenharmony_ci#include <time.h>
32d4afb5ceSopenharmony_ci#include <string.h>
33d4afb5ceSopenharmony_ci#ifdef WIN32
34d4afb5ceSopenharmony_ci#include <io.h>
35d4afb5ceSopenharmony_ci#include <gettimeofday.h>
36d4afb5ceSopenharmony_ci#endif
37d4afb5ceSopenharmony_ci
38d4afb5ceSopenharmony_ci
39d4afb5ceSopenharmony_citypedef enum {
40d4afb5ceSopenharmony_ci	WALK_NONE,
41d4afb5ceSopenharmony_ci	WALK_INITIAL,
42d4afb5ceSopenharmony_ci	WALK_LIST,
43d4afb5ceSopenharmony_ci	WALK_FINAL
44d4afb5ceSopenharmony_ci} e_walk;
45d4afb5ceSopenharmony_ci
46d4afb5ceSopenharmony_cistruct per_session_data__lws_status {
47d4afb5ceSopenharmony_ci	struct per_session_data__lws_status *next;
48d4afb5ceSopenharmony_ci	struct lws *wsi;
49d4afb5ceSopenharmony_ci	time_t time_est;
50d4afb5ceSopenharmony_ci	char user_agent[256];
51d4afb5ceSopenharmony_ci
52d4afb5ceSopenharmony_ci	e_walk walk;
53d4afb5ceSopenharmony_ci	struct per_session_data__lws_status *walk_next;
54d4afb5ceSopenharmony_ci	unsigned char subsequent:1;
55d4afb5ceSopenharmony_ci	unsigned char changed_partway:1;
56d4afb5ceSopenharmony_ci	unsigned char wss_over_h2:1;
57d4afb5ceSopenharmony_ci};
58d4afb5ceSopenharmony_ci
59d4afb5ceSopenharmony_cistruct per_vhost_data__lws_status {
60d4afb5ceSopenharmony_ci	struct per_session_data__lws_status *live_pss_list;
61d4afb5ceSopenharmony_ci	struct lws_context *context;
62d4afb5ceSopenharmony_ci	struct lws_vhost *vhost;
63d4afb5ceSopenharmony_ci	const struct lws_protocols *protocol;
64d4afb5ceSopenharmony_ci	int count_live_pss;
65d4afb5ceSopenharmony_ci};
66d4afb5ceSopenharmony_ci
67d4afb5ceSopenharmony_cistatic void
68d4afb5ceSopenharmony_citrigger_resend(struct per_vhost_data__lws_status *vhd)
69d4afb5ceSopenharmony_ci{
70d4afb5ceSopenharmony_ci	lws_start_foreach_ll(struct per_session_data__lws_status *, pss,
71d4afb5ceSopenharmony_ci			     vhd->live_pss_list) {
72d4afb5ceSopenharmony_ci		if (pss->walk == WALK_NONE) {
73d4afb5ceSopenharmony_ci			pss->subsequent = 0;
74d4afb5ceSopenharmony_ci			pss->walk_next = vhd->live_pss_list;
75d4afb5ceSopenharmony_ci			pss->walk = WALK_INITIAL;
76d4afb5ceSopenharmony_ci		} else
77d4afb5ceSopenharmony_ci			pss->changed_partway = 1;
78d4afb5ceSopenharmony_ci	} lws_end_foreach_ll(pss, next);
79d4afb5ceSopenharmony_ci
80d4afb5ceSopenharmony_ci	lws_callback_on_writable_all_protocol(vhd->context, vhd->protocol);
81d4afb5ceSopenharmony_ci}
82d4afb5ceSopenharmony_ci
83d4afb5ceSopenharmony_ci/* lws-status protocol */
84d4afb5ceSopenharmony_ci
85d4afb5ceSopenharmony_ciint
86d4afb5ceSopenharmony_cicallback_lws_status(struct lws *wsi, enum lws_callback_reasons reason,
87d4afb5ceSopenharmony_ci		    void *user, void *in, size_t len)
88d4afb5ceSopenharmony_ci{
89d4afb5ceSopenharmony_ci	struct per_session_data__lws_status *pss =
90d4afb5ceSopenharmony_ci			(struct per_session_data__lws_status *)user;
91d4afb5ceSopenharmony_ci	struct per_vhost_data__lws_status *vhd =
92d4afb5ceSopenharmony_ci			(struct per_vhost_data__lws_status *)
93d4afb5ceSopenharmony_ci			lws_protocol_vh_priv_get(lws_get_vhost(wsi),
94d4afb5ceSopenharmony_ci					lws_get_protocol(wsi));
95d4afb5ceSopenharmony_ci	char buf[LWS_PRE + 384], ip[24], *start = buf + LWS_PRE - 1, *p = start,
96d4afb5ceSopenharmony_ci	     *end = buf + sizeof(buf) - 1;
97d4afb5ceSopenharmony_ci	int n, m;
98d4afb5ceSopenharmony_ci
99d4afb5ceSopenharmony_ci	switch (reason) {
100d4afb5ceSopenharmony_ci
101d4afb5ceSopenharmony_ci	case LWS_CALLBACK_PROTOCOL_INIT:
102d4afb5ceSopenharmony_ci		vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi),
103d4afb5ceSopenharmony_ci				lws_get_protocol(wsi),
104d4afb5ceSopenharmony_ci				sizeof(struct per_vhost_data__lws_status));
105d4afb5ceSopenharmony_ci		if (!vhd) {
106d4afb5ceSopenharmony_ci			lwsl_notice("%s: PROTOCOL_INIT failed\n", __func__);
107d4afb5ceSopenharmony_ci			return 0;
108d4afb5ceSopenharmony_ci		}
109d4afb5ceSopenharmony_ci		vhd->context = lws_get_context(wsi);
110d4afb5ceSopenharmony_ci		vhd->protocol = lws_get_protocol(wsi);
111d4afb5ceSopenharmony_ci		vhd->vhost = lws_get_vhost(wsi);
112d4afb5ceSopenharmony_ci		break;
113d4afb5ceSopenharmony_ci
114d4afb5ceSopenharmony_ci	case LWS_CALLBACK_ESTABLISHED:
115d4afb5ceSopenharmony_ci
116d4afb5ceSopenharmony_ci		/*
117d4afb5ceSopenharmony_ci		 * This shows how to stage sending a single ws message in
118d4afb5ceSopenharmony_ci		 * multiple fragments.  In this case, it lets us trade off
119d4afb5ceSopenharmony_ci		 * memory needed to make the data vs time to send it.
120d4afb5ceSopenharmony_ci		 */
121d4afb5ceSopenharmony_ci
122d4afb5ceSopenharmony_ci		vhd->count_live_pss++;
123d4afb5ceSopenharmony_ci		pss->next = vhd->live_pss_list;
124d4afb5ceSopenharmony_ci		vhd->live_pss_list = pss;
125d4afb5ceSopenharmony_ci
126d4afb5ceSopenharmony_ci		pss->wss_over_h2 = !!len;
127d4afb5ceSopenharmony_ci
128d4afb5ceSopenharmony_ci		time(&pss->time_est);
129d4afb5ceSopenharmony_ci		pss->wsi = wsi;
130d4afb5ceSopenharmony_ci
131d4afb5ceSopenharmony_ci#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS)
132d4afb5ceSopenharmony_ci		if (lws_hdr_copy(wsi, pss->user_agent, sizeof(pss->user_agent),
133d4afb5ceSopenharmony_ci			     WSI_TOKEN_HTTP_USER_AGENT) < 0) /* too big */
134d4afb5ceSopenharmony_ci#endif
135d4afb5ceSopenharmony_ci			strcpy(pss->user_agent, "unknown");
136d4afb5ceSopenharmony_ci		trigger_resend(vhd);
137d4afb5ceSopenharmony_ci		break;
138d4afb5ceSopenharmony_ci
139d4afb5ceSopenharmony_ci	case LWS_CALLBACK_SERVER_WRITEABLE:
140d4afb5ceSopenharmony_ci		switch (pss->walk) {
141d4afb5ceSopenharmony_ci		case WALK_INITIAL:
142d4afb5ceSopenharmony_ci			n = LWS_WRITE_TEXT | LWS_WRITE_NO_FIN;
143d4afb5ceSopenharmony_ci			p += lws_snprintf(p, lws_ptr_diff_size_t(end, p),
144d4afb5ceSopenharmony_ci				      "{ \"version\":\"%s\","
145d4afb5ceSopenharmony_ci				      " \"wss_over_h2\":\"%d\","
146d4afb5ceSopenharmony_ci				      " \"hostname\":\"%s\","
147d4afb5ceSopenharmony_ci				      " \"wsi\":\"%d\", \"conns\":[",
148d4afb5ceSopenharmony_ci				      lws_get_library_version(),
149d4afb5ceSopenharmony_ci				      pss->wss_over_h2,
150d4afb5ceSopenharmony_ci				      lws_canonical_hostname(vhd->context),
151d4afb5ceSopenharmony_ci				      vhd->count_live_pss);
152d4afb5ceSopenharmony_ci			pss->walk = WALK_LIST;
153d4afb5ceSopenharmony_ci			pss->walk_next = vhd->live_pss_list;
154d4afb5ceSopenharmony_ci			break;
155d4afb5ceSopenharmony_ci		case WALK_LIST:
156d4afb5ceSopenharmony_ci			n = LWS_WRITE_CONTINUATION | LWS_WRITE_NO_FIN;
157d4afb5ceSopenharmony_ci			if (!pss->walk_next)
158d4afb5ceSopenharmony_ci				goto walk_final;
159d4afb5ceSopenharmony_ci
160d4afb5ceSopenharmony_ci			if (pss->subsequent)
161d4afb5ceSopenharmony_ci				*p++ = ',';
162d4afb5ceSopenharmony_ci			pss->subsequent = 1;
163d4afb5ceSopenharmony_ci
164d4afb5ceSopenharmony_ci			m = 0;
165d4afb5ceSopenharmony_ci			lws_start_foreach_ll(struct per_session_data__lws_status *,
166d4afb5ceSopenharmony_ci					     pss2, vhd->live_pss_list) {
167d4afb5ceSopenharmony_ci				if (pss2 == pss->walk_next) {
168d4afb5ceSopenharmony_ci					m = 1;
169d4afb5ceSopenharmony_ci					break;
170d4afb5ceSopenharmony_ci				}
171d4afb5ceSopenharmony_ci			} lws_end_foreach_ll(pss2, next);
172d4afb5ceSopenharmony_ci
173d4afb5ceSopenharmony_ci			if (!m) {
174d4afb5ceSopenharmony_ci				/* our next guy went away */
175d4afb5ceSopenharmony_ci				pss->walk = WALK_FINAL;
176d4afb5ceSopenharmony_ci				pss->changed_partway = 1;
177d4afb5ceSopenharmony_ci				break;
178d4afb5ceSopenharmony_ci			}
179d4afb5ceSopenharmony_ci
180d4afb5ceSopenharmony_ci			strcpy(ip, "unknown");
181d4afb5ceSopenharmony_ci			lws_get_peer_simple(pss->walk_next->wsi, ip, sizeof(ip));
182d4afb5ceSopenharmony_ci			p += lws_snprintf(p, lws_ptr_diff_size_t(end, p),
183d4afb5ceSopenharmony_ci					"{\"peer\":\"%s\",\"time\":\"%ld\","
184d4afb5ceSopenharmony_ci					"\"ua\":\"%s\"}",
185d4afb5ceSopenharmony_ci					ip, (unsigned long)pss->walk_next->time_est,
186d4afb5ceSopenharmony_ci					pss->walk_next->user_agent);
187d4afb5ceSopenharmony_ci			pss->walk_next = pss->walk_next->next;
188d4afb5ceSopenharmony_ci			if (!pss->walk_next)
189d4afb5ceSopenharmony_ci				pss->walk = WALK_FINAL;
190d4afb5ceSopenharmony_ci			break;
191d4afb5ceSopenharmony_ci		case WALK_FINAL:
192d4afb5ceSopenharmony_ciwalk_final:
193d4afb5ceSopenharmony_ci			n = LWS_WRITE_CONTINUATION;
194d4afb5ceSopenharmony_ci			p += lws_snprintf(p, 4, "]}");
195d4afb5ceSopenharmony_ci			if (pss->changed_partway) {
196d4afb5ceSopenharmony_ci				pss->changed_partway = 0;
197d4afb5ceSopenharmony_ci				pss->subsequent = 0;
198d4afb5ceSopenharmony_ci				pss->walk_next = vhd->live_pss_list;
199d4afb5ceSopenharmony_ci				pss->walk = WALK_INITIAL;
200d4afb5ceSopenharmony_ci			} else
201d4afb5ceSopenharmony_ci				pss->walk = WALK_NONE;
202d4afb5ceSopenharmony_ci			break;
203d4afb5ceSopenharmony_ci		default:
204d4afb5ceSopenharmony_ci			return 0;
205d4afb5ceSopenharmony_ci		}
206d4afb5ceSopenharmony_ci
207d4afb5ceSopenharmony_ci		m = lws_write(wsi, (unsigned char *)start, lws_ptr_diff_size_t(p, start), (unsigned int)n);
208d4afb5ceSopenharmony_ci		if (m < 0) {
209d4afb5ceSopenharmony_ci			lwsl_err("ERROR %d writing to di socket\n", m);
210d4afb5ceSopenharmony_ci			return -1;
211d4afb5ceSopenharmony_ci		}
212d4afb5ceSopenharmony_ci
213d4afb5ceSopenharmony_ci		if (pss->walk != WALK_NONE)
214d4afb5ceSopenharmony_ci			lws_callback_on_writable(wsi);
215d4afb5ceSopenharmony_ci		break;
216d4afb5ceSopenharmony_ci
217d4afb5ceSopenharmony_ci	case LWS_CALLBACK_RECEIVE:
218d4afb5ceSopenharmony_ci		lwsl_notice("pmd test: RX len %d\n", (int)len);
219d4afb5ceSopenharmony_ci		break;
220d4afb5ceSopenharmony_ci
221d4afb5ceSopenharmony_ci	case LWS_CALLBACK_CLOSED:
222d4afb5ceSopenharmony_ci		// lwsl_debug("****** LWS_CALLBACK_CLOSED\n");
223d4afb5ceSopenharmony_ci		lws_start_foreach_llp(struct per_session_data__lws_status **,
224d4afb5ceSopenharmony_ci			ppss, vhd->live_pss_list) {
225d4afb5ceSopenharmony_ci			if (*ppss == pss) {
226d4afb5ceSopenharmony_ci				*ppss = pss->next;
227d4afb5ceSopenharmony_ci				break;
228d4afb5ceSopenharmony_ci			}
229d4afb5ceSopenharmony_ci		} lws_end_foreach_llp(ppss, next);
230d4afb5ceSopenharmony_ci
231d4afb5ceSopenharmony_ci		trigger_resend(vhd);
232d4afb5ceSopenharmony_ci		break;
233d4afb5ceSopenharmony_ci
234d4afb5ceSopenharmony_ci	default:
235d4afb5ceSopenharmony_ci		break;
236d4afb5ceSopenharmony_ci	}
237d4afb5ceSopenharmony_ci
238d4afb5ceSopenharmony_ci	return 0;
239d4afb5ceSopenharmony_ci}
240d4afb5ceSopenharmony_ci
241d4afb5ceSopenharmony_ci#define LWS_PLUGIN_PROTOCOL_LWS_STATUS \
242d4afb5ceSopenharmony_ci	{ \
243d4afb5ceSopenharmony_ci		"lws-status", \
244d4afb5ceSopenharmony_ci		callback_lws_status, \
245d4afb5ceSopenharmony_ci		sizeof(struct per_session_data__lws_status), \
246d4afb5ceSopenharmony_ci		512, /* rx buf size must be >= permessage-deflate rx size */ \
247d4afb5ceSopenharmony_ci		0, NULL, 0 \
248d4afb5ceSopenharmony_ci	}
249d4afb5ceSopenharmony_ci
250d4afb5ceSopenharmony_ci#if !defined (LWS_PLUGIN_STATIC)
251d4afb5ceSopenharmony_ci
252d4afb5ceSopenharmony_ciLWS_VISIBLE const struct lws_protocols lws_status_protocols[] = {
253d4afb5ceSopenharmony_ci	LWS_PLUGIN_PROTOCOL_LWS_STATUS
254d4afb5ceSopenharmony_ci};
255d4afb5ceSopenharmony_ci
256d4afb5ceSopenharmony_ciLWS_VISIBLE const lws_plugin_protocol_t lws_status = {
257d4afb5ceSopenharmony_ci	.hdr = {
258d4afb5ceSopenharmony_ci		"lws status",
259d4afb5ceSopenharmony_ci		"lws_protocol_plugin",
260d4afb5ceSopenharmony_ci		LWS_BUILD_HASH,
261d4afb5ceSopenharmony_ci		LWS_PLUGIN_API_MAGIC
262d4afb5ceSopenharmony_ci	},
263d4afb5ceSopenharmony_ci
264d4afb5ceSopenharmony_ci	.protocols = lws_status_protocols,
265d4afb5ceSopenharmony_ci	.count_protocols = LWS_ARRAY_SIZE(lws_status_protocols),
266d4afb5ceSopenharmony_ci	.extensions = NULL,
267d4afb5ceSopenharmony_ci	.count_extensions = 0,
268d4afb5ceSopenharmony_ci};
269d4afb5ceSopenharmony_ci
270d4afb5ceSopenharmony_ci#endif
271