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