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