]> git.proxmox.com Git - ceph.git/blob - ceph/src/civetweb/examples/_obsolete/ws_server/ws_server.c
buildsys: switch source download to quincy
[ceph.git] / ceph / src / civetweb / examples / _obsolete / ws_server / ws_server.c
1 // Copyright (c) 2004-2012 Sergey Lyubka
2 // This file is a part of civetweb project, http://github.com/bel2125/civetweb
3 //
4 // v 0.1 Contributed by William Greathouse 9-Sep-2013
5
6 #include <stdio.h>
7 #include <string.h>
8 #include <time.h>
9 #include <unistd.h>
10
11 #include "civetweb.h"
12
13 // simple structure for keeping track of websocket connection
14 struct ws_connection {
15 struct mg_connection *conn;
16 int update;
17 int closing;
18 };
19
20 // time base and structure periodic updates to client for demo
21 #define BASETIME 100000 /* 0.1 seconds */
22 struct progress {
23 int limit;
24 int increment;
25 int period;
26 int value;
27 };
28
29 // up to 16 independent client connections
30 #define CONNECTIONS 16
31 static struct ws_connection ws_conn[CONNECTIONS];
32
33
34 // ws_server_thread()
35 // Simple demo server thread. Sends periodic updates to connected clients
36 static void *ws_server_thread(void *parm)
37 {
38 int wsd = (long)parm;
39 struct mg_connection *conn = ws_conn[wsd].conn;
40 int timer = 0;
41 char tstr[32];
42 int i;
43 struct progress meter[] = {
44 /* first meter 0 to 1000, by 5 every 0.1 second */
45 { 1000, 5, 1, 0 },
46 /* second meter 0 to 500, by 10 every 0.5 second */
47 { 500, 10, 5, 0 },
48 /* third meter 0 to 100, by 10 every 1.0 second */
49 { 100, 10, 10, 0},
50 /* end of list */
51 { 0, 0, 0, 0}
52 };
53
54 fprintf(stderr, "ws_server_thread %d\n", wsd);
55
56 /* Send initial meter updates */
57 for (i=0; meter[i].period != 0; i++) {
58 if (meter[i].value >= meter[i].limit)
59 meter[i].value = 0;
60 if (meter[i].value >= meter[i].limit)
61 meter[i].value = meter[i].limit;
62 sprintf(tstr, "meter%d:%d,%d", i+1,
63 meter[i].value, meter[i].limit);
64 mg_websocket_write(conn, WEBSOCKET_OPCODE_TEXT, tstr, strlen(tstr));
65 }
66
67 /* While the connection is open, send periodic updates */
68 while(!ws_conn[wsd].closing) {
69 usleep(100000); /* 0.1 second */
70 timer++;
71
72 /* Send meter updates */
73 if (ws_conn[wsd].update) {
74 for (i=0; meter[i].period != 0; i++) {
75 if (timer%meter[i].period == 0) {
76 if (meter[i].value >= meter[i].limit)
77 meter[i].value = 0;
78 else
79 meter[i].value += meter[i].increment;
80 if (meter[i].value >= meter[i].limit)
81 meter[i].value = meter[i].limit;
82 // if we are closing, server should not send new data
83 if (!ws_conn[wsd].closing) {
84 sprintf(tstr, "meter%d:%d,%d", i+1,
85 meter[i].value, meter[i].limit);
86 mg_websocket_write(conn, WEBSOCKET_OPCODE_TEXT, tstr, strlen(tstr));
87 }
88 }
89 }
90 }
91
92 /* Send periodic PING to assure websocket remains connected, except if we are closing */
93 if (timer%100 == 0 && !ws_conn[wsd].closing)
94 mg_websocket_write(conn, WEBSOCKET_OPCODE_PING, NULL, 0);
95 }
96
97 fprintf(stderr, "ws_server_thread %d exiting\n", wsd);
98
99 // reset connection information to allow reuse by new client
100 ws_conn[wsd].conn = NULL;
101 ws_conn[wsd].update = 0;
102 ws_conn[wsd].closing = 2;
103
104 return NULL;
105 }
106
107 // websocket_connect_handler()
108 // On new client connection, find next available server connection and store
109 // new connection information. If no more server connections are available
110 // tell civetweb to not accept the client request.
111 static int websocket_connect_handler(const struct mg_connection *conn)
112 {
113 int i;
114
115 fprintf(stderr, "connect handler\n");
116
117 for(i=0; i < CONNECTIONS; ++i) {
118 if (ws_conn[i].conn == NULL) {
119 fprintf(stderr, "...prep for server %d\n", i);
120 ws_conn[i].conn = (struct mg_connection *)conn;
121 ws_conn[i].closing = 0;
122 ws_conn[i].update = 0;
123 break;
124 }
125 }
126 if (i >= CONNECTIONS) {
127 fprintf(stderr, "Refused connection: Max connections exceeded\n");
128 return 1;
129 }
130
131 return 0;
132 }
133
134 // websocket_ready_handler()
135 // Once websocket negotiation is complete, start a server for the connection
136 static void websocket_ready_handler(struct mg_connection *conn)
137 {
138 int i;
139
140 fprintf(stderr, "ready handler\n");
141
142 for(i=0; i < CONNECTIONS; ++i) {
143 if (ws_conn[i].conn == conn) {
144 fprintf(stderr, "...start server %d\n", i);
145 mg_start_thread(ws_server_thread, (void *)(long)i);
146 break;
147 }
148 }
149 }
150
151 // websocket_close_handler()
152 // When websocket is closed, tell the associated server to shut down
153 static void websocket_close_handler(struct mg_connection *conn)
154 {
155 int i;
156
157 //fprintf(stderr, "close handler\n"); /* called for every close, not just websockets */
158
159 for(i=0; i < CONNECTIONS; ++i) {
160 if (ws_conn[i].conn == conn) {
161 fprintf(stderr, "...close server %d\n", i);
162 ws_conn[i].closing = 1;
163 }
164 }
165 }
166
167 // Arguments:
168 // flags: first byte of websocket frame, see websocket RFC,
169 // http://tools.ietf.org/html/rfc6455, section 5.2
170 // data, data_len: payload data. Mask, if any, is already applied.
171 static int websocket_data_handler(struct mg_connection *conn, int flags,
172 char *data, size_t data_len)
173 {
174 int i;
175 int wsd;
176
177 for(i=0; i < CONNECTIONS; ++i) {
178 if (ws_conn[i].conn == conn) {
179 wsd = i;
180 break;
181 }
182 }
183 if (i >= CONNECTIONS) {
184 fprintf(stderr, "Received websocket data from unknown connection\n");
185 return 1;
186 }
187
188 if (flags & 0x80) {
189 flags &= 0x7f;
190 switch (flags) {
191 case WEBSOCKET_OPCODE_CONTINUATION:
192 fprintf(stderr, "CONTINUATION...\n");
193 break;
194 case WEBSOCKET_OPCODE_TEXT:
195 fprintf(stderr, "TEXT: %-.*s\n", (int)data_len, data);
196 /*** interpret data as commands here ***/
197 if (strncmp("update on", data, data_len)== 0) {
198 /* turn on updates */
199 ws_conn[wsd].update = 1;
200 /* echo back */
201 mg_websocket_write(conn, WEBSOCKET_OPCODE_TEXT, data, data_len);
202 } else if (strncmp("update off", data, data_len)== 0) {
203 /* turn off updates */
204 ws_conn[wsd].update = 0;
205 /* echo back */
206 mg_websocket_write(conn, WEBSOCKET_OPCODE_TEXT, data, data_len);
207 }
208 break;
209 case WEBSOCKET_OPCODE_BINARY:
210 fprintf(stderr, "BINARY...\n");
211 break;
212 case WEBSOCKET_OPCODE_CONNECTION_CLOSE:
213 fprintf(stderr, "CLOSE...\n");
214 /* If client initiated close, respond with close message in acknowlegement */
215 if (!ws_conn[wsd].closing) {
216 mg_websocket_write(conn, WEBSOCKET_OPCODE_CONNECTION_CLOSE, data, data_len);
217 ws_conn[wsd].closing = 1; /* we should not send addional messages when close requested/acknowledged */
218 }
219 return 0; /* time to close the connection */
220 break;
221 case WEBSOCKET_OPCODE_PING:
222 /* client sent PING, respond with PONG */
223 mg_websocket_write(conn, WEBSOCKET_OPCODE_PONG, data, data_len);
224 break;
225 case WEBSOCKET_OPCODE_PONG:
226 /* received PONG to our PING, no action */
227 break;
228 default:
229 fprintf(stderr, "Unknown flags: %02x\n", flags);
230 break;
231 }
232 }
233
234 return 1; /* keep connection open */
235 }
236
237
238 int main(void)
239 {
240 char server_name[40];
241 struct mg_context *ctx;
242 struct mg_callbacks callbacks;
243 const char *options[] = {
244 "listening_ports", "8080",
245 "document_root", "docroot",
246 NULL
247 };
248
249 /* get simple greeting for the web server */
250 snprintf(server_name, sizeof(server_name),
251 "Civetweb websocket server v. %s",
252 mg_version());
253
254 memset(&callbacks, 0, sizeof(callbacks));
255 callbacks.websocket_connect = websocket_connect_handler;
256 callbacks.websocket_ready = websocket_ready_handler;
257 callbacks.websocket_data = websocket_data_handler;
258 callbacks.connection_close = websocket_close_handler;
259
260 ctx = mg_start(&callbacks, NULL, options);
261
262 /* show the greeting and some basic information */
263 printf("%s started on port(s) %s with web root [%s]\n",
264 server_name, mg_get_option(ctx, "listening_ports"),
265 mg_get_option(ctx, "document_root"));
266
267 getchar(); // Wait until user hits "enter"
268 mg_stop(ctx);
269
270 return 0;
271 }