jabberd2  2.3.4
websocket.c
Go to the documentation of this file.
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 
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 
59  sw_start = 0,
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];
80 
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 
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 
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 
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 
231 {
232  return libwebsock_close_with_reason(s, sc, WS_CLOSE_NORMAL, NULL);
233 }
234 
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 
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 
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 
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 
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 
316  int i, ret, err;
317  sha1_state_t sha1;
318  unsigned char hash[20];
319 
320  /* if not wrapped yet */
321  if(!(s->flags & SX_WEBSOCKET_WRAPPER)) {
322  /* look for HTTP handshake */
323  if(s->state == state_NONE && sc->state == websocket_PRE && buf->len >= 5 && strncmp("GET /", buf->data, 5) == 0) {
324  _sx_debug(ZONE, "got HTTP handshake");
325  sc->state = websocket_HEADERS;
326  }
327 
328  /* pass buffers through http_parser */
329  if(s->state == state_NONE && sc->state == websocket_HEADERS) {
330  _sx_debug(ZONE, "parsing HTTP headers");
331  if(buf->len > 0) {
332  _sx_debug(ZONE, "loading %d bytes into http_parser %.*s", buf->len, buf->len, buf->data);
333 
334  ret = http_parser_execute(&sc->parser, &settings, buf->data, buf->len);
335 
336  if (sc->parser.upgrade) {
337  /* check for required websocket upgrade headers */
338  char *upgrade = xhash_get(sc->headers, "Upgrade");
339  char *connection = xhash_get(sc->headers, "Connection");
340  char *key = xhash_get(sc->headers, "Sec-WebSocket-Key");
341  char *proto = xhash_get(sc->headers, "Sec-WebSocket-Protocol");
342  int version = j_atoi(xhash_get(sc->headers, "Sec-WebSocket-Version"), -1);
343  if(j_strcmp(upgrade, "websocket") || j_strcmp(connection, "Upgrade") || j_strcmp(proto, "xmpp") || version != 13) {
344  _sx_debug(ZONE, "Upgrade: %s", upgrade);
345  _sx_debug(ZONE, "Connection: %s", connection);
346  _sx_debug(ZONE, "Sec-WebSocket-Key: %s", key);
347  _sx_debug(ZONE, "Sec-WebSocket-Protocol: %s", proto);
348  _sx_debug(ZONE, "Sec-WebSocket-Version: %d", version);
349  _sx_websocket_http_return(s, "400 Bad Request", "");
350  sx_close(s);
351  return -2;
352  }
353 
354  /* we're good to go */
355 
356  sha1_init(&sha1);
357  sha1_append(&sha1, key, j_strlen(key));
358  sha1_append(&sha1, websocket_guid, sizeof(websocket_guid) -1);
359  sha1_finish(&sha1, hash);
360  char * accept = b64_encode(hash, sizeof(hash));
361 
362  /* switch protocols */
363  _sx_websocket_http_return(s, "101 Switching Protocols",
364  "Upgrade: websocket\r\n"
365  "Connection: Upgrade\r\n"
366  "Sec-WebSocket-Accept: %s\r\n"
367  "Sec-WebSocket-Protocol: xmpp\r\n",
368  accept);
369  free(accept);
370 
371  /* and move past headers */
372  sc->state = websocket_ACTIVE;
374 
375  return 0;
376  } else if (ret != buf->len) {
377  /* throw an error */
378  sx_error(s, stream_err_BAD_FORMAT, http_errno_description(sc->parser.http_errno));
379  sx_close(s);
380  return -2;
381  } else if (p->private) {
382  char *http_forward = p->private;
383  _sx_debug(ZONE, "bouncing HTTP request to %s", http_forward);
384  _sx_websocket_http_return(s, "301 Found", "Location: %s\r\nConnection: close\r\n", http_forward);
385  sx_close(s);
386  return -1;
387  }
388 
389  _sx_debug(ZONE, "unhandling HTTP request");
390  sx_kill(s);
391  return -2;
392 
393  }
394 
395  _sx_buffer_clear(buf);
396  /* flag we want to read */
397  s->want_read = 1;
398 
399  return 0;
400  }
401  }
402 
403  /* only bothering if it is active websocket */
404  if(!(s->flags & SX_WEBSOCKET_WRAPPER) || sc->state != websocket_ACTIVE)
405  return 1;
406 
407  _sx_debug(ZONE, "Unwraping WebSocket frame");
408 
409  char *data = buf->data;
410  for (i = 0; i < buf->len;) {
411  libwebsock_frame *frame;
412  if (sc->frame == NULL) {
413  frame = (libwebsock_frame *) calloc(1, sizeof(libwebsock_frame));
414  frame->payload_len = -1;
416  frame->rawdata = (char *) malloc(FRAME_CHUNK_LENGTH);
417  sc->frame = frame;
418  } else {
419  frame = sc->frame;
420  }
421 
422  *(frame->rawdata + frame->rawdata_idx++) = *data++;
423  i++;
424 
425  if (frame->state != sw_loaded_mask) {
426  err = libwebsock_read_header(frame);
427  if (err == -1) {
428  if (sc->state != websocket_CLOSING) {
430  }
431  return -2;
432  }
433  if (err == 0) {
434  continue;
435  }
436  }
437 
438  if (frame->rawdata_idx < frame->size) {
439  if (buf->len - i >= frame->size - frame->rawdata_idx) { //remaining in current vector completes frame. Copy remaining frame size
440  memcpy(frame->rawdata + frame->rawdata_idx, data,
441  frame->size - frame->rawdata_idx);
442  data += frame->size - frame->rawdata_idx;
443  i += frame->size - frame->rawdata_idx;
444  frame->rawdata_idx = frame->size;
445  } else { //not complete frame, copy the rest of this vector into frame.
446  memcpy(frame->rawdata + frame->rawdata_idx, data, buf->len - i);
447  frame->rawdata_idx += buf->len - i;
448  i = buf->len;
449  continue;
450  }
451  }
452 
453  //have full frame at this point
454  _sx_debug(ZONE, "FIN: %d", frame->fin);
455  _sx_debug(ZONE, "Opcode: %d", frame->opcode);
456  _sx_debug(ZONE, "mask_offset: %d", frame->mask_offset);
457  _sx_debug(ZONE, "payload_offset: %d", frame->payload_offset);
458  _sx_debug(ZONE, "rawdata_idx: %d", frame->rawdata_idx);
459  _sx_debug(ZONE, "rawdata_sz: %d", frame->rawdata_sz);
460  _sx_debug(ZONE, "payload_len: %u", frame->payload_len);
461 
462  switch (frame->opcode) {
463  case WS_OPCODE_TEXT:
464  _sx_buffer_set(buf, frame->rawdata + frame->payload_offset, frame->payload_len, frame->rawdata);
465  frame->rawdata = NULL;
466  /* unmask content */
467  for (i = 0; i < buf->len; i++)
468  buf->data[i] ^= frame->mask[i % 4];
469  _sx_debug(ZONE, "payload: %.*s", buf->len, buf->data);
470  /* hack unclosed stream */
471  if (buf->len >= 7 && strncmp(buf->data, "<open", 5) == 0 && strncmp(buf->data + buf->len - 2, "/>", 2) == 0) {
472  buf->len--;
473  buf->data[buf->len - 1] = '>';
474  }
475  break;
476  case WS_OPCODE_CLOSE:
477  libwebsock_close(s, sc);
478  break;
479  case WS_OPCODE_PING:
481  _sx_buffer_clear(buf);
482  break;
483  case WS_OPCODE_PONG:
484  _sx_buffer_clear(buf);
485  s->want_read = 1;
486  return 0;
487  default:
489  break;
490  }
491 
492  free(frame->rawdata);
493  free(frame);
494  sc->frame = NULL;
495 
496  if (sc->state == websocket_CLOSING) {
497  _sx_buffer_clear(buf);
498  return 0;
499  }
500  }
501 
502  return 1;
503 }
504 
507 
508  /* only bothering if it is active websocket */
509  if(!(s->flags & SX_WEBSOCKET_WRAPPER))
510  return 1;
511 
512  _sx_debug(ZONE, "in _sx_websocket_wio");
513 
514  if(buf->len > 0) {
515  _sx_debug(ZONE, "wrapping %d bytes in WebSocket frame", buf->len);
517  if (frame == NULL) {
518  return libwebsock_close_with_reason(s, sc, WS_CLOSE_UNEXPECTED_ERROR, "Internal server error");
519  }
520  _sx_buffer_set(buf, frame->data, frame->len, frame->data);
521  free(frame);
522  }
523  _sx_debug(ZONE, "passing %d bytes frame", buf->len);
524 
525  return 1;
526 }
527 
528 static void _sx_websocket_new(sx_t s, sx_plugin_t p) {
530 
531  if(sc != NULL)
532  return;
533 
534  _sx_debug(ZONE, "preparing for HTTP websocket connect for %d", s->tag);
535 
536  sc = (_sx_websocket_conn_t) calloc(1, sizeof(struct _sx_websocket_conn_st));
537 
538  sc->state = websocket_PRE;
539  sc->p = pool_new();
540  sc->field = spool_new(sc->p);
541  sc->value = spool_new(sc->p);
542  sc->headers = xhash_new(11);
543  sc->parser.data = sc;
544 
545  /* initialize parser */
546  http_parser_init(&sc->parser, HTTP_REQUEST);
547 
548  s->plugin_data[p->index] = (void *) sc;
549 
550  /* bring the plugin online */
551  _sx_chain_io_plugin(s, p);
552 }
553 
557 
558  if(sc == NULL)
559  return;
560 
561  log_debug(ZONE, "cleaning up websocket state");
562 
563  pool_free(sc->p);
564 
565  if (sc->frame) free(((libwebsock_frame *)sc->frame)->rawdata);
566  free(sc->frame);
567  free(sc);
568 
569  s->plugin_data[p->index] = NULL;
570 }
571 
573 int sx_websocket_init(sx_env_t env, sx_plugin_t p, va_list args) {
574 
575  _sx_debug(ZONE, "initialising websocket plugin");
576 
578  p->rio = _sx_websocket_rio;
579  p->wio = _sx_websocket_wio;
581 
582  char *http_forward = va_arg(args, char*);
583  p->private = http_forward;
584 
585  settings.on_headers_complete = _sx_websocket_http_headers_complete;
586  settings.on_header_field = _sx_websocket_http_header_field;
587  settings.on_header_value = _sx_websocket_http_header_value;
588 
589  return 0;
590 }
enum WS_FRAME_STATE state
Definition: websocket.c:78
void(* free)(sx_t s, sx_plugin_t p)
Definition: sx.h:353
void pool_free(pool_t p)
Definition: pool.c:226
Definition: sx.h:113
static void _sx_websocket_http_return(sx_t s, char *status, char *headers_format,...)
Definition: websocket.c:287
_sx_state_t state
Definition: sx.h:315
#define _sx_event(s, e, data)
Definition: sx.h:391
void(* server)(sx_t s, sx_plugin_t p)
Definition: sx.h:356
unsigned int flags
Definition: sx.h:275
#define WS_CLOSE_PROTOCOL_ERROR
Definition: websocket.c:48
jqueue_t wbufq
Definition: sx.h:300
unsigned int mask_offset
Definition: websocket.c:69
char * b64_encode(char *buf, int len)
Definition: base64.c:167
Definition: sx.h:70
an environment
Definition: sx.h:378
int tag
Definition: sx.h:257
a plugin
Definition: sx.h:343
void libwebsock_fail_connection(sx_t s, _sx_websocket_conn_t sc, unsigned short close_code)
Definition: websocket.c:235
int j_atoi(const char *a, int def)
Definition: str.c:87
void _sx_chain_io_plugin(sx_t s, sx_plugin_t p)
Definition: chain.c:25
static int _sx_websocket_http_header_value(http_parser *parser, const char *chars, size_t length)
Definition: websocket.c:265
unsigned int opcode
Definition: websocket.c:68
#define pool_new()
Definition: pool.h:97
static void _sx_websocket_new(sx_t s, sx_plugin_t p)
Definition: websocket.c:528
unsigned int payload_len_short
Definition: websocket.c:74
static int libwebsock_read_header(libwebsock_frame *frame)
Definition: websocket.c:81
static int _sx_websocket_http_headers_complete(http_parser *parser)
Definition: websocket.c:277
static int _sx_websocket_http_header_field(http_parser *parser, const char *chars, size_t length)
Definition: websocket.c:250
holds the state for a single stream
Definition: sx.h:252
char * data
Definition: sx.h:114
void sha1_init(sha1_state_t *ctx)
Definition: sha1.c:28
void jqueue_push(jqueue_t q, void *data, int priority)
Definition: jqueue.c:44
void xhash_put(xht h, const char *key, void *val)
Definition: xhash.c:163
_sx_websocket_state_t state
Definition: plugins.h:179
char * strunescape(pool_t p, char *buf)
Definition: str.c:238
unsigned int rawdata_idx
Definition: websocket.c:71
sx_buf_t _sx_buffer_new(const char *data, int len, _sx_notify_t notify, void *notify_arg)
utility: make a new buffer if len>0 but data is NULL, the buffer will contain that many bytes of garb...
Definition: sx.c:220
#define WS_CLOSE_NORMAL
Definition: websocket.c:46
int j_strcmp(const char *a, const char *b)
Definition: str.c:43
#define WS_OPCODE_CLOSE
Definition: websocket.c:40
void sx_close(sx_t s)
Definition: io.c:498
void sha1_finish(sha1_state_t *ctx, unsigned char hashout[20])
Definition: sha1.c:65
#define log_debug(...)
Definition: log.h:65
const char * spool_print(spool s)
Definition: str.c:186
#define SX_WEBSOCKET_WRAPPER
Definition: plugins.h:34
#define _sx_debug
Definition: sx.h:404
WS_FRAME_STATE
Definition: websocket.c:58
void sx_error(sx_t s, int err, const char *text)
Definition: error.c:94
int libwebsock_close_with_reason(sx_t s, _sx_websocket_conn_t sc, unsigned short code, const char *reason)
Definition: websocket.c:211
#define FRAME_CHUNK_LENGTH
Definition: websocket.c:35
unsigned int payload_offset
Definition: websocket.c:70
struct _libwebsock_frame libwebsock_frame
unsigned char mask[4]
Definition: websocket.c:77
static http_parser_settings settings
Definition: websocket.c:31
int libwebsock_close(sx_t s, _sx_websocket_conn_t sc)
Definition: websocket.c:230
static int _sx_websocket_rio(sx_t s, sx_plugin_t p, sx_buf_t buf)
Definition: websocket.c:314
int sx_websocket_init(sx_env_t env, sx_plugin_t p, va_list args)
args: none
Definition: websocket.c:573
unsigned int len
Definition: sx.h:115
void * private
Definition: sx.h:350
int(* wio)(sx_t s, sx_plugin_t p, sx_buf_t buf)
Definition: sx.h:359
sx_buf_t libwebsock_fragment_buffer(const char *data, unsigned int len, int flags)
Definition: websocket.c:153
void _sx_buffer_clear(sx_buf_t buf)
utility: clear out a buffer, but don't deallocate it
Definition: sx.c:252
unsigned int rawdata_sz
Definition: websocket.c:72
#define WS_CLOSE_UNEXPECTED_ERROR
Definition: websocket.c:56
void sha1_append(sha1_state_t *ctx, const unsigned char *dataIn, int len)
Definition: sha1.c:47
#define WS_OPCODE_TEXT
Definition: websocket.c:38
static int _sx_websocket_wio(sx_t s, sx_plugin_t p, sx_buf_t buf)
Definition: websocket.c:505
unsigned int payload_len
Definition: websocket.c:75
int libwebsock_send_fragment(sx_t s, _sx_websocket_conn_t sc, const char *data, unsigned int len, int flags)
Definition: websocket.c:201
static void _sx_websocket_free(sx_t s, sx_plugin_t p)
cleanup
Definition: websocket.c:555
void sx_kill(sx_t s)
Definition: io.c:513
#define stream_err_BAD_FORMAT
Definition: sx.h:124
void * xhash_get(xht h, const char *key)
Definition: xhash.c:184
unsigned int size
Definition: websocket.c:73
static const char websocket_guid[]
this plugin implements WebSocket C2S access RFC 7395 : An Extensible Messaging and Presence Protocol ...
Definition: websocket.c:29
#define ZONE
Definition: mio_impl.h:76
int want_read
Definition: sx.h:305
void _sx_buffer_set(sx_buf_t buf, char *newdata, int newlength, char *newheap)
utility: reset a sx_buf_t's contents.
Definition: sx.c:299
int(* rio)(sx_t s, sx_plugin_t p, sx_buf_t buf)
Definition: sx.h:360
xht xhash_new(int prime)
Definition: xhash.c:96
int j_strlen(const char *a)
Definition: str.c:79
void spool_escape(spool s, const char *raw, int len)
Definition: str.c:155
struct _sx_websocket_conn_st * _sx_websocket_conn_t
a single conn
int index
Definition: sx.h:348
a single conn
Definition: plugins.h:177
#define WS_OPCODE_PONG
Definition: websocket.c:42
unsigned int fin
Definition: websocket.c:67
int want_write
Definition: sx.h:305
spool spool_new(pool_t p)
Definition: str.c:119
#define WS_FRAGMENT_FIN
Definition: websocket.c:44
#define WS_OPCODE_PING
Definition: websocket.c:41
void ** plugin_data
Definition: sx.h:326
#define MASK_LENGTH
Definition: websocket.c:34
http_parser parser
Definition: plugins.h:178