Contiki-NG
Loading...
Searching...
No Matches
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#include "resolv.h"
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
45PROCESS(websocket_process, "Websockets process");
46
47#define MAX_HOSTLEN 64
48#define MAX_PATHLEN 100
49
50LIST(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
64struct websocket_frame_hdr {
65 uint8_t opcode;
66 uint8_t len;
67 uint8_t extlen[4];
68};
69
70struct websocket_frame_mask {
71 uint8_t mask[4];
72};
73
74/*---------------------------------------------------------------------------*/
75static int
76parse_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/*---------------------------------------------------------------------------*/
149static int
150start_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/*---------------------------------------------------------------------------*/
164void
165call(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/*---------------------------------------------------------------------------*/
173PROCESS_THREAD(websocket_process, ev, data)
174{
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 */
218void
219websocket_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 */
233void
234websocket_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 */
249void
250websocket_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 */
264void
265websocket_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. */
279static int
280receive_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 */
337void
338websocket_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/*---------------------------------------------------------------------------*/
516static void
517init(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/*---------------------------------------------------------------------------*/
527void
528websocket_init(struct websocket *s)
529{
530 init();
531 websocket_http_client_init(&s->s);
532}
533/*---------------------------------------------------------------------------*/
534void
535websocket_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/*---------------------------------------------------------------------------*/
541websocket_result_t
542websocket_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/*---------------------------------------------------------------------------*/
591void
592websocket_close(struct websocket *s)
593{
594 websocket_http_client_close(&s->s);
595 s->state = WEBSOCKET_STATE_CLOSED;
596}
597/*---------------------------------------------------------------------------*/
598static int
599send_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/*---------------------------------------------------------------------------*/
671int
672websocket_send_str(struct websocket *s, const char *str)
673{
674 return send_data(s, str, strlen(str), WEBSOCKET_OPCODE_TEXT);
675}
676/*---------------------------------------------------------------------------*/
677int
678websocket_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/*---------------------------------------------------------------------------*/
684int
685websocket_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/*---------------------------------------------------------------------------*/
716int
717websocket_queuelen(struct websocket *s)
718{
719 return websocket_http_client_queuelen(&s->s);
720}
721/*---------------------------------------------------------------------------*/
static void list_init(list_t list)
Initialize a list.
Definition list.h:152
#define LIST(name)
Declare a linked list.
Definition list.h:90
static void * list_item_next(const void *item)
Get the next item following this item.
Definition list.h:294
void list_add(list_t list, void *item)
Add an item at the end of a list.
Definition list.c:71
static void * list_head(const_list_t list)
Get a pointer to the first element of a list.
Definition list.h:169
#define PROCESS(name, strname)
Declare a process.
Definition process.h:308
#define PROCESS_WAIT_EVENT()
Wait for an event to be posted to the process.
Definition process.h:142
#define PROCESS_BEGIN()
Define the beginning of a process.
Definition process.h:121
#define PROCESS_END()
Define the end of a process.
Definition process.h:132
void process_start(struct process *p, process_data_t data)
Start a process.
Definition process.c:121
#define PROCESS_CONTEXT_BEGIN(p)
Switch context to another process.
Definition process.h:428
#define PROCESS_THREAD(name, ev, data)
Define the body of a process.
Definition process.h:274
#define PROCESS_CONTEXT_END(p)
End a context switch.
Definition process.h:442
void resolv_query(const char *name)
Queues a name so that a question for the name will be sent out.
Definition resolv.c:1189
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:1263
process_event_t resolv_event_found
Event that is broadcasted when a DNS name has been resolved.
Definition resolv.c:243
Header file for the logging system.
uIP DNS resolver code header file.
@ RESOLV_STATUS_CACHED
Hostname is fresh and usable.
Definition resolv.h:54
static uip_ds6_addr_t * addr
Pointer to a nbr cache entry.
Definition uip-nd6.c:107
Representation of an IP address.
Definition uip.h:95