Contiki-NG
websocket.c
1 /*
2  * Copyright (c) 2012, Thingsquare, http://www.thingsquare.com/.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the copyright holder nor the names of its
14  * contributors may be used to endorse or promote products derived
15  * from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
20  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
21  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
22  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
28  * OF THE POSSIBILITY OF SUCH DAMAGE.
29  *
30  */
31 
32 #include <stdio.h>
33 #include <stddef.h>
34 #include <string.h>
35 
36 #include "contiki-net.h"
37 
38 #include "websocket.h"
39 
40 /* Log configuration */
41 #include "sys/log.h"
42 #define LOG_MODULE "Websocket"
43 #define LOG_LEVEL LOG_LEVEL_IPV6
44 
45 PROCESS(websocket_process, "Websockets process");
46 
47 #define MAX_HOSTLEN 64
48 #define MAX_PATHLEN 100
49 
50 LIST(websocketlist);
51 
52 #define WEBSOCKET_FIN_BIT 0x80
53 
54 #define WEBSOCKET_OPCODE_MASK 0x0f
55 #define WEBSOCKET_OPCODE_CONT 0x00
56 #define WEBSOCKET_OPCODE_TEXT 0x01
57 #define WEBSOCKET_OPCODE_BIN 0x02
58 #define WEBSOCKET_OPCODE_CLOSE 0x08
59 #define WEBSOCKET_OPCODE_PING 0x09
60 #define WEBSOCKET_OPCODE_PONG 0x0a
61 
62 #define WEBSOCKET_MASK_BIT 0x80
63 #define WEBSOCKET_LEN_MASK 0x7f
64 struct websocket_frame_hdr {
65  uint8_t opcode;
66  uint8_t len;
67  uint8_t extlen[4];
68 };
69 
70 struct websocket_frame_mask {
71  uint8_t mask[4];
72 };
73 
74 /*---------------------------------------------------------------------------*/
75 static int
76 parse_url(const char *url, char *host, uint16_t *portptr, char *path)
77 {
78  const char *urlptr;
79  int i;
80  const char *file;
81  uint16_t port;
82 
83  if(url == NULL) {
84  return 0;
85  }
86 
87  /* Don't even try to go further if the URL is empty. */
88  if(strlen(url) == 0) {
89  return 0;
90  }
91 
92  /* See if the URL starts with http:// or ws:// and remove it. */
93  if(strncmp(url, "http://", strlen("http://")) == 0) {
94  urlptr = url + strlen("http://");
95  } else if(strncmp(url, "ws://", strlen("ws://")) == 0) {
96  urlptr = url + strlen("ws://");
97  } else {
98  urlptr = url;
99  }
100 
101  /* Find host part of the URL. */
102  for(i = 0; i < MAX_HOSTLEN; ++i) {
103  if(*urlptr == 0 ||
104  *urlptr == '/' ||
105  *urlptr == ' ' ||
106  *urlptr == ':') {
107  if(host != NULL) {
108  host[i] = 0;
109  }
110  break;
111  }
112  if(host != NULL) {
113  host[i] = *urlptr;
114  }
115  ++urlptr;
116  }
117 
118  /* Find the port. Default is 0, which lets the underlying transport
119  select its default port. */
120  port = 0;
121  if(*urlptr == ':') {
122  port = 0;
123  do {
124  ++urlptr;
125  if(*urlptr >= '0' && *urlptr <= '9') {
126  port = (10 * port) + (*urlptr - '0');
127  }
128  } while(*urlptr >= '0' &&
129  *urlptr <= '9');
130  }
131  if(portptr != NULL) {
132  *portptr = port;
133  }
134  /* Find file part of the URL. */
135  while(*urlptr != '/' && *urlptr != 0) {
136  ++urlptr;
137  }
138  if(*urlptr == '/') {
139  file = urlptr;
140  } else {
141  file = "/";
142  }
143  if(path != NULL) {
144  strncpy(path, file, MAX_PATHLEN);
145  }
146  return 1;
147 }
148 /*---------------------------------------------------------------------------*/
149 static int
150 start_get(struct websocket *s)
151 {
152  if(websocket_http_client_get(&(s->s)) == 0) {
153  LOG_ERR("Out of memory error\n");
154  s->state = WEBSOCKET_STATE_CLOSED;
155  return WEBSOCKET_ERR;
156  } else {
157  LOG_INFO("Connecting...\n");
158  s->state = WEBSOCKET_STATE_HTTP_REQUEST_SENT;
159  return WEBSOCKET_OK;
160  }
161  return WEBSOCKET_ERR;
162 }
163 /*---------------------------------------------------------------------------*/
164 void
165 call(struct websocket *s, websocket_result_t r,
166  const uint8_t *data, uint16_t datalen)
167 {
168  if(s != NULL && s->callback != NULL) {
169  s->callback(s, r, data, datalen);
170  }
171 }
172 /*---------------------------------------------------------------------------*/
173 PROCESS_THREAD(websocket_process, ev, data)
174 {
175  PROCESS_BEGIN();
176 
177  while(1) {
178 
180 
181  if(ev == resolv_event_found && data != NULL) {
182  int ret;
183  struct websocket *s;
184  const char *name = data;
185  /* Either found a hostname, or not. We need to go through the
186  list of websocketsand figure out to which connection this
187  reply corresponds, then either restart the HTTP get, or kill
188  it (if no hostname was found). */
189  for(s = list_head(websocketlist);
190  s != NULL;
191  s = list_item_next(s)) {
192  if(strcmp(name, websocket_http_client_hostname(&s->s)) == 0) {
193  ret = resolv_lookup(name, NULL);
194  if(ret == RESOLV_STATUS_CACHED) {
195  /* Hostname found, restart get. */
196  if(s->state == WEBSOCKET_STATE_DNS_REQUEST_SENT) {
197  LOG_INFO("Restarting get\n");
198  start_get(s);
199  }
200  } else {
201  if(s->state == WEBSOCKET_STATE_DNS_REQUEST_SENT) {
202  /* Hostname not found, kill connection. */
203  LOG_ERR("killing connection\n");
204  call(s, WEBSOCKET_HOSTNAME_NOT_FOUND, NULL, 0);
205  }
206  }
207  }
208  }
209  }
210  }
211 
212  PROCESS_END();
213 }
214 /*---------------------------------------------------------------------------*/
215 /* Callback function. Called from the webclient when the HTTP
216  * connection was abruptly aborted.
217  */
218 void
219 websocket_http_client_aborted(struct websocket_http_client_state *client_state)
220 {
221  if(client_state != NULL) {
222  struct websocket *s = (struct websocket *)
223  ((char *)client_state - offsetof(struct websocket, s));
224  LOG_WARN("Websocket reset\n");
225  s->state = WEBSOCKET_STATE_CLOSED;
226  call(s, WEBSOCKET_RESET, NULL, 0);
227  }
228 }
229 /*---------------------------------------------------------------------------*/
230 /* Callback function. Called from the webclient when the HTTP
231  * connection timed out.
232  */
233 void
234 websocket_http_client_timedout(struct websocket_http_client_state *client_state)
235 {
236  if(client_state != NULL) {
237  struct websocket *s = (struct websocket *)
238  ((char *)client_state - offsetof(struct websocket, s));
239  LOG_WARN("Websocket timed out\n");
240  s->state = WEBSOCKET_STATE_CLOSED;
241  call(s, WEBSOCKET_TIMEDOUT, NULL, 0);
242  }
243 }
244 /*---------------------------------------------------------------------------*/
245 /* Callback function. Called from the webclient when the HTTP
246  * connection was closed after a request from the "websocket_http_client_close()"
247  * function. .
248  */
249 void
250 websocket_http_client_closed(struct websocket_http_client_state *client_state)
251 {
252  if(client_state != NULL) {
253  struct websocket *s = (struct websocket *)
254  ((char *)client_state - offsetof(struct websocket, s));
255  LOG_INFO("Websocket closed.\n");
256  s->state = WEBSOCKET_STATE_CLOSED;
257  call(s, WEBSOCKET_CLOSED, NULL, 0);
258  }
259 }
260 /*---------------------------------------------------------------------------*/
261 /* Callback function. Called from the webclient when the HTTP
262  * connection is connected.
263  */
264 void
265 websocket_http_client_connected(struct websocket_http_client_state *client_state)
266 {
267  struct websocket *s = (struct websocket *)
268  ((char *)client_state - offsetof(struct websocket, s));
269 
270  LOG_INFO("Websocket connected\n");
271  s->state = WEBSOCKET_STATE_WAITING_FOR_HEADER;
272  call(s, WEBSOCKET_CONNECTED, NULL, 0);
273 }
274 /*---------------------------------------------------------------------------*/
275 /* The websocket header may potentially be split into multiple TCP
276  segments. This function eats one byte each, puts it into
277  s->headercache, and checks whether or not the full header has been
278  received. */
279 static int
280 receive_header_byte(struct websocket *s, uint8_t byte)
281 {
282  int len;
283  int expected_len;
284  struct websocket_frame_hdr *hdr;
285 
286  /* Take the next byte of data and place it in the header cache. */
287  if(s->state == WEBSOCKET_STATE_RECEIVING_HEADER) {
288  s->headercache[s->headercacheptr] = byte;
289  s->headercacheptr++;
290  if(s->headercacheptr >= sizeof(s->headercache)) {
291  /* Something bad happened: we ad read 10 bytes and had not yet
292  found a reasonable header, so we close the socket. */
293  websocket_close(s);
294  }
295  }
296 
297  len = s->headercacheptr;
298  hdr = (struct websocket_frame_hdr *)s->headercache;
299 
300  /* Check the header that we have received to see if it is long
301  enough. */
302 
303  /* We start with expecting a length of at least two bytes (opcode +
304  1 length byte). */
305  expected_len = 2;
306 
307  if(len >= expected_len) {
308 
309  /* We check how many more bytes we should expect to see. The
310  length byte determines how many length bytes are included in
311  the header. */
312  if((hdr->len & WEBSOCKET_LEN_MASK) == 126) {
313  expected_len += 2;
314  } else if((hdr->len & WEBSOCKET_LEN_MASK) == 127) {
315  expected_len += 4;
316  }
317 
318  /* If the option has the mask bit set, we should expect to see 4
319  mask bytes at the end of the header. */
320  if((hdr->len & WEBSOCKET_MASK_BIT ) != 0) {
321  expected_len += 4;
322  }
323 
324  /* Now we know how long our header if expected to be. If it is
325  this long, we are done and we set the state to reflect this. */
326  if(len == expected_len) {
327  s->state = WEBSOCKET_STATE_HEADER_RECEIVED;
328  return 1;
329  }
330  }
331  return 0;
332 }
333 /*---------------------------------------------------------------------------*/
334 /* Callback function. Called from the webclient module when HTTP data
335  * has arrived.
336  */
337 void
338 websocket_http_client_datahandler(struct websocket_http_client_state *client_state,
339  const uint8_t *data, uint16_t datalen)
340 {
341  struct websocket *s = (struct websocket *)
342  ((char *)client_state - offsetof(struct websocket, s));
343  struct websocket_frame_hdr *hdr;
344  struct websocket_frame_mask *maskptr;
345 
346  if(data == NULL) {
347  call(s, WEBSOCKET_CLOSED, NULL, 0);
348  } else {
349  /* This function is a state machine that does different things
350  depending on the state. If we are waiting for header (the
351  default state), we change to the RECEIVING_HEADER state when we
352  get the first byte. If we are receiving header, we put all
353  bytes we have into a header buffer until the full header has
354  been received. If we have received the header, we parse it. If
355  we have received and parsed the header, we are ready to receive
356  data. Finally, if there is data left in the incoming packet, we
357  repeat the process. */
358 
359  if(s->state == WEBSOCKET_STATE_WAITING_FOR_HEADER) {
360  s->state = WEBSOCKET_STATE_RECEIVING_HEADER;
361  s->headercacheptr = 0;
362  }
363 
364  if(s->state == WEBSOCKET_STATE_RECEIVING_HEADER) {
365  while(datalen > 0 && s->state == WEBSOCKET_STATE_RECEIVING_HEADER) {
366  receive_header_byte(s, data[0]);
367  data++;
368  datalen--;
369  }
370  }
371 
372  if(s->state == WEBSOCKET_STATE_HEADER_RECEIVED) {
373  /* If this is the start of an incoming websocket data frame, we
374  decode the header and check if we should act on in. If not, we
375  pipe the data to the application through a callback handler. If
376  data arrives in multiple packets, it is up to the application to
377  put it back together again. */
378 
379  /* The websocket header is at the start of the incoming data. */
380  hdr = (struct websocket_frame_hdr *)s->headercache;
381 
382  /* The s->left field holds the length of the application data
383  * chunk that we are about to receive. */
384  s->len = s->left = 0;
385 
386  /* The s->mask field holds the bitmask of the data chunk, if
387  * any. */
388  memset(s->mask, 0, sizeof(s->mask));
389 
390  /* We first read out the length of the application data
391  chunk. The length may be encoded over multiple bytes. If the
392  length is >= 126 bytes, it is encoded as two or more
393  bytes. The first length field determines if it is in 2 or 4
394  bytes. We also keep track of where the bitmask is held - its
395  place also differs depending on how the length is encoded. */
396  maskptr = (struct websocket_frame_mask *)hdr->extlen;
397  if((hdr->len & WEBSOCKET_LEN_MASK) < 126) {
398  s->len = s->left = hdr->len & WEBSOCKET_LEN_MASK;
399  } else if(hdr->len == 126) {
400  s->len = s->left = (hdr->extlen[0] << 8) + hdr->extlen[1];
401  maskptr = (struct websocket_frame_mask *)&hdr->extlen[2];
402  } else if(hdr->len == 127) {
403  s->len = s->left = ((uint32_t)hdr->extlen[0] << 24) +
404  ((uint32_t)hdr->extlen[1] << 16) +
405  ((uint32_t)hdr->extlen[2] << 8) +
406  hdr->extlen[3];
407  maskptr = (struct websocket_frame_mask *)&hdr->extlen[4];
408  }
409 
410  /* Set user_data to point to the first byte of application data.
411  See if the application data chunk is masked or not. If it is,
412  we copy the bitmask into the s->mask field. */
413  if((hdr->len & WEBSOCKET_MASK_BIT) == 0) {
414  /* LOG_INFO("No mask\n");*/
415  } else {
416  memcpy(s->mask, &maskptr->mask, sizeof(s->mask));
417  /* LOG_INFO("There was a mask, %02x %02x %02x %02x\n",
418  s->mask[0], s->mask[1], s->mask[2], s->mask[3]);*/
419  }
420 
421  /* Remember the opcode of the application chunk, put it in the
422  * s->opcode field. */
423  s->opcode = hdr->opcode & WEBSOCKET_OPCODE_MASK;
424 
425  if(s->opcode == WEBSOCKET_OPCODE_PING) {
426  /* If the opcode is ping, we change the opcode to a pong, and
427  * send the data back. */
428  hdr->opcode = (hdr->opcode & (~WEBSOCKET_OPCODE_MASK)) |
429  WEBSOCKET_OPCODE_PONG;
430  websocket_http_client_send(&s->s, (const uint8_t*)hdr, 2);
431  if(s->left > 0) {
432  websocket_http_client_send(&s->s, (const uint8_t*)data, s->left);
433  }
434  LOG_INFO("Got ping\n");
435  call(s, WEBSOCKET_PINGED, NULL, 0);
436  s->state = WEBSOCKET_STATE_WAITING_FOR_HEADER;
437  } else if(s->opcode == WEBSOCKET_OPCODE_PONG) {
438  /* If the opcode is pong, we call the application to let it
439  know we got a pong. */
440  LOG_INFO("Got pong\n");
441  call(s, WEBSOCKET_PONG_RECEIVED, NULL, 0);
442  s->state = WEBSOCKET_STATE_WAITING_FOR_HEADER;
443  } else if(s->opcode == WEBSOCKET_OPCODE_CLOSE) {
444  /* If the opcode is a close, we send a close frame back. */
445  hdr->opcode = (hdr->opcode & (~WEBSOCKET_OPCODE_MASK)) |
446  WEBSOCKET_OPCODE_CLOSE;
447  websocket_http_client_send(&s->s, (const uint8_t*)hdr, 2);
448  if(s->left > 0) {
449  websocket_http_client_send(&s->s, (const uint8_t*)data, s->left);
450  }
451  LOG_INFO("Got close, sending close\n");
452  s->state = WEBSOCKET_STATE_WAITING_FOR_HEADER;
453  websocket_http_client_close(&s->s);
454  } else if(s->opcode == WEBSOCKET_OPCODE_BIN ||
455  s->opcode == WEBSOCKET_OPCODE_TEXT) {
456 
457  /* If the opcode is bin or text, and there is application
458  * layer data in the packet, we call the application to
459  * process it. */
460  if(s->left > 0) {
461  s->state = WEBSOCKET_STATE_RECEIVING_DATA;
462  if(datalen > 0) {
463  int len;
464 
465  len = MIN(s->left, datalen);
466  /* XXX todo: mask if needed. */
467  call(s, WEBSOCKET_DATA, data, len);
468  data += len;
469  s->left -= len;
470  datalen -= len;
471  }
472  }
473  }
474 
475  if(s->left == 0) {
476  call(s, WEBSOCKET_DATA_RECEIVED, NULL, s->len);
477  s->state = WEBSOCKET_STATE_WAITING_FOR_HEADER;
478 
479  /* Need to keep parsing the incoming data to check for more
480  frames, if the incoming datalen is > than s->left. */
481  if(datalen > 0) {
482  websocket_http_client_datahandler(client_state,
483  data, datalen);
484  }
485  }
486  } else if(s->state == WEBSOCKET_STATE_RECEIVING_DATA) {
487  /* XXX todo: mask if needed. */
488  if(datalen > 0) {
489  if(datalen < s->left) {
490  call(s, WEBSOCKET_DATA, data, datalen);
491  s->left -= datalen;
492  data += datalen;
493  datalen = 0;
494  } else {
495  call(s, WEBSOCKET_DATA, data, s->left);
496  data += s->left;
497  datalen -= s->left;
498  s->left = 0;
499  }
500  }
501  if(s->left == 0) {
502  call(s, WEBSOCKET_DATA_RECEIVED, NULL, s->len);
503  s->state = WEBSOCKET_STATE_WAITING_FOR_HEADER;
504  /* Need to keep parsing the incoming data to check for more
505  frames, if the incoming datalen is > than len. */
506  if(datalen > 0) {
507  websocket_http_client_datahandler(client_state,
508  data, datalen);
509 
510  }
511  }
512  }
513  }
514 }
515 /*---------------------------------------------------------------------------*/
516 static void
517 init(void)
518 {
519  static uint8_t inited = 0;
520  if(!inited) {
521  process_start(&websocket_process, NULL);
522  list_init(websocketlist);
523  inited = 1;
524  }
525 }
526 /*---------------------------------------------------------------------------*/
527 void
528 websocket_init(struct websocket *s)
529 {
530  init();
531  websocket_http_client_init(&s->s);
532 }
533 /*---------------------------------------------------------------------------*/
534 void
535 websocket_set_proxy(struct websocket *s,
536  const uip_ipaddr_t *addr, uint16_t port)
537 {
538  websocket_http_client_set_proxy(&s->s, addr, port);
539 }
540 /*---------------------------------------------------------------------------*/
541 websocket_result_t
542 websocket_open(struct websocket *s, const char *url,
543  const char *subprotocol, const char *hdr,
544  websocket_callback c)
545 {
546  int ret;
547  char host[MAX_HOSTLEN + 1] = {0};
548  char path[MAX_PATHLEN + 1] = {0};
549  uint16_t port;
550  uip_ipaddr_t addr;
551 
552  init();
553 
554  if(s == NULL) {
555  return WEBSOCKET_ERR;
556  }
557 
558  if(s->state != WEBSOCKET_STATE_CLOSED) {
559  LOG_INFO("Open: closing websocket before opening it again.\n");
560  websocket_close(s);
561  }
562  s->callback = c;
563 
564  if(parse_url(url, host, &port, path)) {
565  list_add(websocketlist, s);
566  websocket_http_client_register(&s->s, host, port, path, subprotocol, hdr);
567 
568  /* First check if the host is an IP address. */
569  if(uiplib_ip4addrconv(host, (uip_ip4addr_t *)&addr) == 0 &&
570  uiplib_ip6addrconv(host, (uip_ip6addr_t *)&addr) == 0) {
571  /* Try to lookup the hostname. If it fails, we initiate a hostname
572  lookup and print out an informative message on the
573  statusbar. */
574  ret = resolv_lookup(host, NULL);
575  if(ret != RESOLV_STATUS_CACHED) {
576  resolv_query(host);
577  s->state = WEBSOCKET_STATE_DNS_REQUEST_SENT;
578  LOG_INFO("Resolving host...\n");
579  return WEBSOCKET_OK;
580  }
581  }
582 
583  PROCESS_CONTEXT_BEGIN(&websocket_process);
584  ret = start_get(s);
586  return ret;
587  }
588  return -1;
589 }
590 /*---------------------------------------------------------------------------*/
591 void
592 websocket_close(struct websocket *s)
593 {
594  websocket_http_client_close(&s->s);
595  s->state = WEBSOCKET_STATE_CLOSED;
596 }
597 /*---------------------------------------------------------------------------*/
598 static int
599 send_data(struct websocket *s, const void *data,
600  uint16_t datalen, uint8_t data_type_opcode)
601 {
602  uint8_t buf[WEBSOCKET_MAX_MSGLEN + 4 + 4]; /* The extra + 4 + 4 here
603  comes from the size of
604  the websocket framing
605  header. */
606  struct websocket_frame_hdr *hdr;
607  struct websocket_frame_mask *mask;
608 
609  LOG_INFO("send data len %d %.*s\n", datalen, datalen, (char *)data);
610  if(s->state == WEBSOCKET_STATE_CLOSED ||
611  s->state == WEBSOCKET_STATE_DNS_REQUEST_SENT ||
612  s->state == WEBSOCKET_STATE_HTTP_REQUEST_SENT) {
613  /* Trying to send data on a non-connected websocket. */
614  LOG_ERR("send fail: not connected\n");
615  return -1;
616  }
617 
618  /* We need to have 4 + 4 additional bytes for the websocket framing
619  header. */
620  if(4 + 4 + datalen > websocket_http_client_sendbuflen(&s->s)) {
621  LOG_ERR("too few bytes left (%d left, %d needed)\n",
622  websocket_http_client_sendbuflen(&s->s),
623  4 + 4 + datalen);
624  return -1;
625  }
626 
627  if(datalen > sizeof(buf) - 4 - 4) {
628  LOG_ERR("trying to send too large data chunk %d > %d\n",
629  datalen, (int)sizeof(buf) - 4 - 4);
630  return -1;
631  }
632 
633  hdr = (struct websocket_frame_hdr *)&buf[0];
634  hdr->opcode = WEBSOCKET_FIN_BIT | data_type_opcode;
635 
636  /* If the datalen is larger than 125 bytes, we need to send the data
637  length as two bytes. If the data length would be larger than 64k,
638  we should send the length as 4 bytes, but since we specify the
639  datalen as an unsigned 16-bit int, we do not handle the 64k case
640  here. */
641  if(datalen > 125) {
642  /* Data from client must always have the mask bit set, and a data
643  mask sent right after the header. */
644  hdr->len = 126 | WEBSOCKET_MASK_BIT;
645  hdr->extlen[0] = datalen >> 8;
646  hdr->extlen[1] = datalen & 0xff;
647 
648  mask = (struct websocket_frame_mask *)&buf[4];
649  mask->mask[0] =
650  mask->mask[1] =
651  mask->mask[2] =
652  mask->mask[3] = 0;
653  memcpy(&buf[8], data, datalen);
654  return websocket_http_client_send(&s->s, buf, 8 + datalen);
655  } else {
656  /* Data from client must always have the mask bit set, and a data
657  mask sent right after the header. */
658  hdr->len = datalen | WEBSOCKET_MASK_BIT;
659 
660  mask = (struct websocket_frame_mask *)&buf[2];
661  mask->mask[0] =
662  mask->mask[1] =
663  mask->mask[2] =
664  mask->mask[3] = 0;
665  memcpy(&buf[6], data, datalen);
666  return websocket_http_client_send(&s->s, buf, 6 + datalen);
667  }
668  return -1;
669 }
670 /*---------------------------------------------------------------------------*/
671 int
672 websocket_send_str(struct websocket *s, const char *str)
673 {
674  return send_data(s, str, strlen(str), WEBSOCKET_OPCODE_TEXT);
675 }
676 /*---------------------------------------------------------------------------*/
677 int
678 websocket_send(struct websocket *s, const uint8_t *data,
679  uint16_t datalen)
680 {
681  return send_data(s, data, datalen, WEBSOCKET_OPCODE_BIN);
682 }
683 /*---------------------------------------------------------------------------*/
684 int
685 websocket_ping(struct websocket *s)
686 {
687  uint8_t buf[sizeof(struct websocket_frame_hdr) +
688  sizeof(struct websocket_frame_mask)];
689  struct websocket_frame_hdr *hdr;
690  struct websocket_frame_mask *mask;
691 
692  /* We need 2 + 4 additional bytes for the websocket framing
693  header. */
694  if(2 + 4 > websocket_http_client_sendbuflen(&s->s)) {
695  return -1;
696  }
697 
698  hdr = (struct websocket_frame_hdr *)&buf[0];
699  mask = (struct websocket_frame_mask *)&buf[2];
700  hdr->opcode = WEBSOCKET_FIN_BIT | WEBSOCKET_OPCODE_PING;
701 
702  /* Data from client must always have the mask bit set, and a data
703  mask sent right after the header. */
704  hdr->len = 0 | WEBSOCKET_MASK_BIT;
705 
706  /* XXX: We just set a dummy mask of 0 for now and hope that this
707  works. */
708  mask->mask[0] =
709  mask->mask[1] =
710  mask->mask[2] =
711  mask->mask[3] = 0;
712  websocket_http_client_send(&s->s, buf, 2 + 4);
713  return 1;
714 }
715 /*---------------------------------------------------------------------------*/
716 int
717 websocket_queuelen(struct websocket *s)
718 {
719  return websocket_http_client_queuelen(&s->s);
720 }
721 /*---------------------------------------------------------------------------*/
process_event_t resolv_event_found
Event that is broadcasted when a DNS name has been resolved.
Definition: resolv.c:276
#define PROCESS(name, strname)
Declare a process.
Definition: process.h:307
#define PROCESS_CONTEXT_END(p)
End a context switch.
Definition: process.h:440
Hostname is fresh and usable.
Definition: resolv.h:63
#define PROCESS_WAIT_EVENT()
Wait for an event to be posted to the process.
Definition: process.h:141
static uip_ds6_addr_t * addr
Pointer to a nbr cache entry.
Definition: uip-nd6.c:107
#define PROCESS_BEGIN()
Define the beginning of a process.
Definition: process.h:120
#define PROCESS_END()
Define the end of a process.
Definition: process.h:131
Representation of an IP address.
Definition: uip.h:95
resolv_status_t resolv_lookup(const char *name, uip_ipaddr_t **ipaddr)
Look up a hostname in the array of known hostnames.
Definition: resolv.c:1311
void * list_head(list_t list)
Get a pointer to the first element of a list.
Definition: list.c:82
void list_add(list_t list, void *item)
Add an item at the end of a list.
Definition: list.c:142
void list_init(list_t list)
Initialize a list.
Definition: list.c:65
#define LIST(name)
Declare a linked list.
Definition: list.h:89
#define PROCESS_CONTEXT_BEGIN(p)
Switch context to another process.
Definition: process.h:426
PROCESS_THREAD(cc2538_rf_process, ev, data)
Implementation of the cc2538 RF driver process.
Definition: cc2538-rf.c:1097
Header file for the logging system
void resolv_query(const char *name)
Queues a name so that a question for the name will be sent out.
Definition: resolv.c:1232
void * list_item_next(void *item)
Get the next item following this item.
Definition: list.c:322
void process_start(struct process *p, process_data_t data)
Start a process.
Definition: process.c:99