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