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