1 /*
2 * jabberd - Jabber Open Source Server
3 * Copyright (c) 2015 Tomasz Sterna
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; version 2 of the License.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA
17 */
18
19 /**
20 * this plugin implements WebSocket C2S access
21 * RFC 7395 : An Extensible Messaging and Presence Protocol (XMPP) Subprotocol for WebSocket
22 * http://tools.ietf.org/html/rfc7395
23 */
24
25 #include "sx.h"
26 #include <stdarg.h>
27 #include <string.h>
28
29 static const char websocket_guid[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
30
31 static http_parser_settings settings;
32
33 /* parts of github.com/payden src/websock.c by Payden Sutherland follow */
34 #define MASK_LENGTH 4
35 #define FRAME_CHUNK_LENGTH 1024
36
37 #define WS_OPCODE_CONTINUE 0x0
38 #define WS_OPCODE_TEXT 0x1
39 #define WS_OPCODE_BINARY 0x2
40 #define WS_OPCODE_CLOSE 0x8
41 #define WS_OPCODE_PING 0x9
42 #define WS_OPCODE_PONG 0xa
43
44 #define WS_FRAGMENT_FIN (1 << 7)
45
46 #define WS_CLOSE_NORMAL 1000
47 #define WS_CLOSE_GOING_AWAY 1001
48 #define WS_CLOSE_PROTOCOL_ERROR 1002
49 #define WS_CLOSE_NOT_ALLOWED 1003
50 #define WS_CLOSE_RESERVED 1004
51 #define WS_CLOSE_NO_CODE 1005
52 #define WS_CLOSE_DIRTY 1006
53 #define WS_CLOSE_WRONG_TYPE 1007
54 #define WS_CLOSE_POLICY_VIOLATION 1008
55 #define WS_CLOSE_MESSAGE_TOO_BIG 1009
56 #define WS_CLOSE_UNEXPECTED_ERROR 1011
57
58 enum WS_FRAME_STATE {
59 sw_start = 0,
60 sw_got_two,
61 sw_got_short_len,
62 sw_got_full_len,
63 sw_loaded_mask
64 };
65
66 typedef struct _libwebsock_frame {
67 unsigned int fin;
68 unsigned int opcode;
69 unsigned int mask_offset;
70 unsigned int payload_offset;
71 unsigned int rawdata_idx;
72 unsigned int rawdata_sz;
73 unsigned int size;
74 unsigned int payload_len_short;
75 unsigned int payload_len;
76 char *rawdata;
77 unsigned char mask[4];
78 enum WS_FRAME_STATE state;
79 } libwebsock_frame;
80
libwebsock_read_header(libwebsock_frame * frame)81 static inline int libwebsock_read_header(libwebsock_frame *frame) {
82 int i, new_size;
83 enum WS_FRAME_STATE state;
84
85 state = frame->state;
86 switch (state) {
87 case sw_start:
88 if (frame->rawdata_idx < 2) {
89 return 0;
90 }
91 frame->state = sw_got_two;
92 case sw_got_two:
93 frame->mask_offset = 2;
94 frame->fin = (*(frame->rawdata) & 0x80) == 0x80 ? 1 : 0;
95 frame->opcode = *(frame->rawdata) & 0xf;
96 frame->payload_len_short = *(frame->rawdata + 1) & 0x7f;
97 frame->state = sw_got_short_len;
98 case sw_got_short_len:
99 switch (frame->payload_len_short) {
100 case 126:
101 if (frame->rawdata_idx < 4) {
102 return 0;
103 }
104 frame->mask_offset += 2;
105 frame->payload_offset = frame->mask_offset + MASK_LENGTH;
106 frame->payload_len = ntohs(
107 *((unsigned short int *) (frame->rawdata + 2)));
108 frame->state = sw_got_full_len;
109 break;
110 case 127:
111 if (frame->rawdata_idx < 10) {
112 return 0;
113 }
114 frame->mask_offset += 8;
115 frame->payload_offset = frame->mask_offset + MASK_LENGTH;
116 frame->payload_len = ntohl(*((unsigned int *) (frame->rawdata + 6)));
117 frame->state = sw_got_full_len;
118 break;
119 default:
120 frame->payload_len = frame->payload_len_short;
121 frame->payload_offset = frame->mask_offset + MASK_LENGTH;
122 frame->state = sw_got_full_len;
123 break;
124 }
125 case sw_got_full_len:
126 if (frame->rawdata_idx < frame->payload_offset) {
127 return 0;
128 }
129 for (i = 0; i < MASK_LENGTH; i++) {
130 frame->mask[i] = *(frame->rawdata + frame->mask_offset + i) & 0xff;
131 }
132 frame->state = sw_loaded_mask;
133 frame->size = frame->payload_offset + frame->payload_len;
134 if (frame->size > frame->rawdata_sz) {
135 new_size = frame->size;
136 new_size--;
137 new_size |= new_size >> 1;
138 new_size |= new_size >> 2;
139 new_size |= new_size >> 4;
140 new_size |= new_size >> 8;
141 new_size |= new_size >> 16;
142 new_size++;
143 frame->rawdata_sz = new_size;
144 frame->rawdata = (char *) realloc(frame->rawdata, new_size);
145 }
146 return 1;
147 case sw_loaded_mask:
148 return 1;
149 }
150 return 0;
151 }
152
libwebsock_fragment_buffer(const char * data,unsigned int len,int flags)153 sx_buf_t libwebsock_fragment_buffer(const char *data, unsigned int len, int flags) {
154 unsigned int *payload_len_32_be;
155 unsigned short int *payload_len_short_be;
156 unsigned char finNopcode, payload_len_small;
157 unsigned int payload_offset = 2;
158 unsigned int frame_size;
159 char *frame;
160
161 finNopcode = flags & 0xff;
162 if (len <= 125) {
163 frame_size = 2 + len;
164 payload_len_small = len & 0xff;
165 } else if (len > 125 && len <= 0xffff) {
166 frame_size = 4 + len;
167 payload_len_small = 126;
168 payload_offset += 2;
169 } else if (len > 0xffff && len <= 0xfffffff0) {
170 frame_size = 10 + len;
171 payload_len_small = 127;
172 payload_offset += 8;
173 } else {
174 _sx_debug(ZONE,
175 "libwebsock does not support frame payload sizes over %u bytes long\n",
176 0xfffffff0);
177 return NULL;
178 }
179 sx_buf_t buf = _sx_buffer_new(NULL, frame_size, NULL, NULL);
180 frame = buf->data;
181 payload_len_small &= 0x7f;
182 *frame = finNopcode;
183 *(frame + 1) = payload_len_small;
184 if (payload_len_small == 126) {
185 len &= 0xffff;
186 payload_len_short_be = (unsigned short *) ((char *) frame + 2);
187 *payload_len_short_be = htons(len);
188 }
189 if (payload_len_small == 127) {
190 payload_len_32_be = (unsigned int *) ((char *) frame + 2);
191 *payload_len_32_be++ = 0;
192 *payload_len_32_be = htonl(len);
193 }
194 memcpy(frame + payload_offset, data, len);
195
196 return buf;
197 }
198
199 int libwebsock_close_with_reason(sx_t s, _sx_websocket_conn_t sc, unsigned short code, const char *reason);
200
libwebsock_send_fragment(sx_t s,_sx_websocket_conn_t sc,const char * data,unsigned int len,int flags)201 int libwebsock_send_fragment(sx_t s, _sx_websocket_conn_t sc, const char *data, unsigned int len, int flags) {
202 sx_buf_t buf = libwebsock_fragment_buffer(data, len, flags);
203 if (buf == NULL) {
204 return libwebsock_close_with_reason(s, sc, WS_CLOSE_UNEXPECTED_ERROR, "Internal server error");
205 }
206 jqueue_push(s->wbufq, buf, 0);
207 s->want_write = 1;
208 return _sx_event(s, event_WANT_WRITE, NULL);
209 }
210
libwebsock_close_with_reason(sx_t s,_sx_websocket_conn_t sc,unsigned short code,const char * reason)211 int libwebsock_close_with_reason(sx_t s, _sx_websocket_conn_t sc, unsigned short code, const char *reason)
212 {
213 unsigned int len;
214 unsigned short code_be;
215 char buf[128]; //w3 spec on WebSockets API (http://dev.w3.org/html5/websockets/) says reason shouldn't be over 123 bytes. I concur.
216 len = 2;
217 code_be = htobe16(code);
218 memcpy(buf, &code_be, 2);
219 if (reason) {
220 len += snprintf(buf + 2, 124, "%s", reason);
221 }
222
223 sc->state = websocket_CLOSING;
224 int ret = libwebsock_send_fragment(s, sc, buf, len, WS_FRAGMENT_FIN | WS_OPCODE_CLOSE);
225
226 sx_close(s);
227 return ret;
228 }
229
libwebsock_close(sx_t s,_sx_websocket_conn_t sc)230 int libwebsock_close(sx_t s, _sx_websocket_conn_t sc)
231 {
232 return libwebsock_close_with_reason(s, sc, WS_CLOSE_NORMAL, NULL);
233 }
234
libwebsock_fail_connection(sx_t s,_sx_websocket_conn_t sc,unsigned short close_code)235 void libwebsock_fail_connection(sx_t s, _sx_websocket_conn_t sc, unsigned short close_code) {
236 char close_frame[4] = { 0x88, 0x02, 0x00, 0x00 };
237 unsigned short *code_be = (unsigned short *) &close_frame[2];
238 *code_be = htobe16(WS_CLOSE_PROTOCOL_ERROR);
239
240 sx_buf_t buf = _sx_buffer_new(NULL, sizeof(close_frame), NULL, NULL);
241 memcpy(buf->data, close_frame, buf->len);
242
243 sc->state = websocket_CLOSING;
244 s->want_write = 1;
245 _sx_event(s, event_WANT_WRITE, NULL);
246
247 sx_close(s);
248 }
249
_sx_websocket_http_header_field(http_parser * parser,const char * chars,size_t length)250 static int _sx_websocket_http_header_field(http_parser *parser, const char *chars, size_t length) {
251 _sx_debug(ZONE, "HTTP header field '%.*s'", length, chars);
252 _sx_websocket_conn_t sc = (_sx_websocket_conn_t) parser->data;
253 if(sc->header_value) {
254 // new field incoming
255 xhash_put(sc->headers,
256 strunescape(sc->p, spool_print(sc->field)),
257 strunescape(sc->p, spool_print(sc->value)));
258 sc->header_value = 0;
259 sc->field = spool_new(sc->p);
260 }
261 spool_escape(sc->field, chars, length);
262 return 0;
263 }
264
_sx_websocket_http_header_value(http_parser * parser,const char * chars,size_t length)265 static int _sx_websocket_http_header_value(http_parser *parser, const char *chars, size_t length) {
266 _sx_debug(ZONE, "HTTP header value '%.*s'", length, chars);
267 _sx_websocket_conn_t sc = (_sx_websocket_conn_t) parser->data;
268 if(!sc->header_value) {
269 // field name complete
270 sc->header_value = 1;
271 sc->value = spool_new(sc->p);
272 }
273 spool_escape(sc->value, chars, length);
274 return 0;
275 }
276
_sx_websocket_http_headers_complete(http_parser * parser)277 static int _sx_websocket_http_headers_complete(http_parser *parser) {
278 _sx_websocket_conn_t sc = (_sx_websocket_conn_t) parser->data;
279 _sx_debug(ZONE, "HTTP headers complete: %d %s HTTP/%d.%d", parser->status_code, http_method_str(parser->method), parser->http_major, parser->http_minor);
280 if (sc->header_value) {
281 /* pull last value by switching to field parser */
282 _sx_websocket_http_header_field(parser, "", 0);
283 }
284 return 1;
285 }
286
_sx_websocket_http_return(sx_t s,char * status,char * headers_format,...)287 static void _sx_websocket_http_return(sx_t s, char *status, char *headers_format, ...) {
288 char* http =
289 "HTTP/1.1 %s\r\n"
290 "%s"
291 "Server: " PACKAGE_STRING "\r\n"
292 "Expires: Fri, 10 Oct 1997 10:10:10 GMT\r\n"
293 "Pragma: no-cache\r\n"
294 "Cache-control: private\r\n"
295 "\r\n";
296
297 /* build additional headers */
298 char headers[1024];
299 va_list args;
300 va_start(args, headers_format);
301 vsnprintf(headers, sizeof(headers), headers_format, args);
302 va_end(args);
303
304 /* build HTTP answer */
305 sx_buf_t buf = _sx_buffer_new(NULL, j_strlen(http) + j_strlen(status) + j_strlen(headers), NULL, NULL);
306 buf->len = sprintf(buf->data, http, status, headers);
307 jqueue_push(s->wbufq, buf, 0);
308
309 /* stuff to write */
310 s->want_write = 1;
311 _sx_event(s, event_WANT_WRITE, NULL);
312 }
313
_sx_websocket_rio(sx_t s,sx_plugin_t p,sx_buf_t buf)314 static int _sx_websocket_rio(sx_t s, sx_plugin_t p, sx_buf_t buf) {
315 _sx_websocket_conn_t sc = (_sx_websocket_conn_t) s->plugin_data[p->index];
316 int i, j, ret, err;
317 char *newbuf;
318 sha1_state_t sha1;
319 unsigned char hash[20];
320
321 /* if not wrapped yet */
322 if(!(s->flags & SX_WEBSOCKET_WRAPPER)) {
323 /* look for HTTP handshake */
324 if(s->state == state_NONE && sc->state == websocket_PRE && buf->len >= 5 && strncmp("GET /", buf->data, 5) == 0) {
325 _sx_debug(ZONE, "got HTTP handshake");
326 sc->state = websocket_HEADERS;
327 }
328
329 /* pass buffers through http_parser */
330 if(s->state == state_NONE && sc->state == websocket_HEADERS) {
331 _sx_debug(ZONE, "parsing HTTP headers");
332 if(buf->len > 0) {
333 _sx_debug(ZONE, "loading %d bytes into http_parser %.*s", buf->len, buf->len, buf->data);
334
335 ret = http_parser_execute(&sc->parser, &settings, buf->data, buf->len);
336
337 if (sc->parser.upgrade) {
338 /* check for required websocket upgrade headers */
339 char *upgrade = xhash_get(sc->headers, "Upgrade");
340 char *connection = xhash_get(sc->headers, "Connection");
341 char *key = xhash_get(sc->headers, "Sec-WebSocket-Key");
342 char *proto = xhash_get(sc->headers, "Sec-WebSocket-Protocol");
343 int version = j_atoi(xhash_get(sc->headers, "Sec-WebSocket-Version"), -1);
344 if(j_strcmp(upgrade, "websocket") || connection == NULL || strcasestr(connection, "Upgrade") == NULL || j_strcmp(proto, "xmpp") || version != 13) {
345 _sx_debug(ZONE, "Upgrade: %s", upgrade);
346 _sx_debug(ZONE, "Connection: %s", connection);
347 _sx_debug(ZONE, "Sec-WebSocket-Key: %s", key);
348 _sx_debug(ZONE, "Sec-WebSocket-Protocol: %s", proto);
349 _sx_debug(ZONE, "Sec-WebSocket-Version: %d", version);
350 _sx_websocket_http_return(s, "400 Bad Request", "");
351 sx_close(s);
352 return -2;
353 }
354
355 /* we're good to go */
356
357 sha1_init(&sha1);
358 sha1_append(&sha1, key, j_strlen(key));
359 sha1_append(&sha1, websocket_guid, sizeof(websocket_guid) -1);
360 sha1_finish(&sha1, hash);
361 char * accept = b64_encode(hash, sizeof(hash));
362
363 /* switch protocols */
364 _sx_websocket_http_return(s, "101 Switching Protocols",
365 "Upgrade: websocket\r\n"
366 "Connection: Upgrade\r\n"
367 "Sec-WebSocket-Accept: %s\r\n"
368 "Sec-WebSocket-Protocol: xmpp\r\n",
369 accept);
370 free(accept);
371
372 /* and move past headers */
373 sc->state = websocket_ACTIVE;
374 s->flags |= SX_WEBSOCKET_WRAPPER;
375
376 return 0;
377 } else if (ret != buf->len) {
378 /* throw an error */
379 sx_error(s, stream_err_BAD_FORMAT, http_errno_description(sc->parser.http_errno));
380 sx_close(s);
381 return -2;
382 } else if (p->private) {
383 char *http_forward = p->private;
384 _sx_debug(ZONE, "bouncing HTTP request to %s", http_forward);
385 _sx_websocket_http_return(s, "301 Found", "Location: %s\r\nConnection: close\r\n", http_forward);
386 sx_close(s);
387 return -1;
388 }
389
390 _sx_debug(ZONE, "unhandling HTTP request");
391 _sx_websocket_http_return(s, "403 Forbidden", "Connection: close\r\n");
392 sx_close(s);
393 return -1;
394 }
395
396 _sx_buffer_clear(buf);
397 /* flag we want to read */
398 s->want_read = 1;
399
400 return 0;
401 }
402 }
403
404 /* only bothering if it is active websocket */
405 if(!(s->flags & SX_WEBSOCKET_WRAPPER) || sc->state != websocket_ACTIVE)
406 return 1;
407
408 _sx_debug(ZONE, "Unwraping WebSocket frame: %d bytes", buf->len);
409
410 char *data = buf->data;
411 for (i = 0; i < buf->len;) {
412 libwebsock_frame *frame;
413 if (sc->frame == NULL) {
414 frame = (libwebsock_frame *) calloc(1, sizeof(libwebsock_frame));
415 frame->payload_len = -1;
416 frame->rawdata_sz = FRAME_CHUNK_LENGTH;
417 frame->rawdata = (char *) malloc(FRAME_CHUNK_LENGTH);
418 sc->frame = frame;
419 } else {
420 frame = sc->frame;
421 }
422
423 *(frame->rawdata + frame->rawdata_idx++) = *data++;
424 i++;
425
426 if (frame->state != sw_loaded_mask) {
427 err = libwebsock_read_header(frame);
428 if (err == -1) {
429 if (sc->state != websocket_CLOSING) {
430 libwebsock_fail_connection(s, sc, WS_CLOSE_PROTOCOL_ERROR);
431 }
432 return -2;
433 }
434 if (err == 0) {
435 continue;
436 }
437 }
438
439 if (frame->rawdata_idx < frame->size) {
440 if (buf->len - i >= frame->size - frame->rawdata_idx) {
441 //remaining in current vector completes frame. Copy remaining frame size
442 memcpy(frame->rawdata + frame->rawdata_idx, data,
443 frame->size - frame->rawdata_idx);
444 data += frame->size - frame->rawdata_idx;
445 i += frame->size - frame->rawdata_idx;
446 frame->rawdata_idx = frame->size;
447 } else {
448 //not complete frame, copy the rest of this vector into frame.
449 memcpy(frame->rawdata + frame->rawdata_idx, data, buf->len - i);
450 frame->rawdata_idx += buf->len - i;
451 i = buf->len;
452 _sx_debug(ZONE, "more frame data to come");
453 continue;
454 }
455 }
456
457 //have full frame at this point
458 _sx_debug(ZONE, "FIN: %d", frame->fin);
459 _sx_debug(ZONE, "Opcode: %x", frame->opcode);
460 _sx_debug(ZONE, "mask_offset: %d", frame->mask_offset);
461 _sx_debug(ZONE, "payload_offset: %d", frame->payload_offset);
462 _sx_debug(ZONE, "rawdata_idx: %d", frame->rawdata_idx);
463 _sx_debug(ZONE, "rawdata_sz: %d", frame->rawdata_sz);
464 _sx_debug(ZONE, "payload_len: %u", frame->payload_len);
465
466 if (frame->opcode != WS_OPCODE_CONTINUE) {
467 sc->opcode = frame->opcode;
468 }
469
470 switch (sc->opcode) {
471 case WS_OPCODE_TEXT:
472 /* unmask content */
473 for (j = 0; j < frame->payload_len; j++)
474 frame->rawdata[frame->payload_offset + j] ^= frame->mask[j % 4];
475 _sx_debug(ZONE, "payload: %.*s", frame->payload_len, frame->rawdata + frame->payload_offset);
476 sc->buf = realloc(sc->buf, sc->buf_len + frame->payload_len);
477 newbuf = sc->buf + sc->buf_len;
478 strncpy(newbuf, frame->rawdata + frame->payload_offset, frame->payload_len);
479 sc->buf_len += frame->payload_len;
480 /* hack unclose <open ... /> */
481 if (frame->payload_len >= 7 && strncmp(newbuf, "<open", 5) == 0 && strncmp(newbuf + frame->payload_len - 2, "/>", 2) == 0) {
482 sc->buf_len--;
483 sc->buf[sc->buf_len - 1] = '>';
484 }
485 break;
486 case WS_OPCODE_CLOSE:
487 libwebsock_close(s, sc);
488 break;
489 case WS_OPCODE_PING:
490 libwebsock_send_fragment(s, sc, frame->rawdata + frame->payload_offset, frame->payload_len, WS_FRAGMENT_FIN | WS_OPCODE_PONG);
491 break;
492 case WS_OPCODE_PONG:
493 s->want_read = 1;
494 default:
495 _sx_debug(ZONE, "unhandled opcode: %x", frame->opcode);
496 break;
497 }
498
499 free(frame->rawdata);
500 free(frame);
501 sc->frame = NULL;
502
503 if (sc->state == websocket_CLOSING) {
504 _sx_buffer_clear(buf);
505 return 0;
506 }
507 }
508
509 _sx_debug(ZONE, "passing buffer: %.*s", sc->buf_len, sc->buf);
510 _sx_buffer_set(buf, sc->buf, sc->buf_len, NULL);
511 sc->buf_len = 0;
512
513 return 1;
514 }
515
_sx_websocket_wio(sx_t s,sx_plugin_t p,sx_buf_t buf)516 static int _sx_websocket_wio(sx_t s, sx_plugin_t p, sx_buf_t buf) {
517 _sx_websocket_conn_t sc = (_sx_websocket_conn_t) s->plugin_data[p->index];
518
519 /* only bothering if it is active websocket */
520 if(!(s->flags & SX_WEBSOCKET_WRAPPER))
521 return 1;
522
523 _sx_debug(ZONE, "in _sx_websocket_wio");
524
525 if(buf->len > 0) {
526 _sx_debug(ZONE, "wrapping %d bytes in WebSocket frame", buf->len);
527 sx_buf_t frame = libwebsock_fragment_buffer(buf->data, buf->len, WS_FRAGMENT_FIN | WS_OPCODE_TEXT);
528 if (frame == NULL) {
529 return libwebsock_close_with_reason(s, sc, WS_CLOSE_UNEXPECTED_ERROR, "Internal server error");
530 }
531 _sx_buffer_set(buf, frame->data, frame->len, frame->data);
532 free(frame);
533 }
534 _sx_debug(ZONE, "passing %d bytes frame", buf->len);
535
536 return 1;
537 }
538
_sx_websocket_new(sx_t s,sx_plugin_t p)539 static void _sx_websocket_new(sx_t s, sx_plugin_t p) {
540 _sx_websocket_conn_t sc = (_sx_websocket_conn_t) s->plugin_data[p->index];
541
542 if(sc != NULL)
543 return;
544
545 _sx_debug(ZONE, "preparing for HTTP websocket connect for %d", s->tag);
546
547 sc = (_sx_websocket_conn_t) calloc(1, sizeof(struct _sx_websocket_conn_st));
548
549 sc->state = websocket_PRE;
550 sc->p = pool_new();
551 sc->field = spool_new(sc->p);
552 sc->value = spool_new(sc->p);
553 sc->headers = xhash_new(11);
554 sc->buf = malloc(1024);
555 sc->parser.data = sc;
556
557 /* initialize parser */
558 http_parser_init(&sc->parser, HTTP_REQUEST);
559
560 s->plugin_data[p->index] = (void *) sc;
561
562 /* bring the plugin online */
563 _sx_chain_io_plugin(s, p);
564 }
565
566 /** cleanup */
_sx_websocket_free(sx_t s,sx_plugin_t p)567 static void _sx_websocket_free(sx_t s, sx_plugin_t p) {
568 _sx_websocket_conn_t sc = (_sx_websocket_conn_t) s->plugin_data[p->index];
569
570 if(sc == NULL)
571 return;
572
573 log_debug(ZONE, "cleaning up websocket state");
574
575 pool_free(sc->p);
576
577 if (sc->frame) free(((libwebsock_frame *)sc->frame)->rawdata);
578 free(sc->frame);
579 free(sc);
580
581 s->plugin_data[p->index] = NULL;
582 }
583
584 /** args: none */
sx_websocket_init(sx_env_t env,sx_plugin_t p,va_list args)585 int sx_websocket_init(sx_env_t env, sx_plugin_t p, va_list args) {
586
587 _sx_debug(ZONE, "initialising websocket plugin");
588
589 p->server = _sx_websocket_new;
590 p->rio = _sx_websocket_rio;
591 p->wio = _sx_websocket_wio;
592 p->free = _sx_websocket_free;
593
594 char *http_forward = va_arg(args, char*);
595 p->private = http_forward;
596
597 settings.on_headers_complete = _sx_websocket_http_headers_complete;
598 settings.on_header_field = _sx_websocket_http_header_field;
599 settings.on_header_value = _sx_websocket_http_header_value;
600
601 return 0;
602 }
603