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