36 #include "contiki-net.h" 38 #include "websocket.h" 42 #define LOG_MODULE "Websocket" 43 #define LOG_LEVEL LOG_LEVEL_IPV6 45 PROCESS(websocket_process,
"Websockets process");
47 #define MAX_HOSTLEN 64 48 #define MAX_PATHLEN 100 52 #define WEBSOCKET_FIN_BIT 0x80 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 62 #define WEBSOCKET_MASK_BIT 0x80 63 #define WEBSOCKET_LEN_MASK 0x7f 64 struct websocket_frame_hdr {
70 struct websocket_frame_mask {
76 parse_url(
const char *url,
char *host, uint16_t *portptr,
char *path)
88 if(strlen(url) == 0) {
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://");
102 for(i = 0; i < MAX_HOSTLEN; ++i) {
125 if(*urlptr >=
'0' && *urlptr <=
'9') {
126 port = (10 * port) + (*urlptr -
'0');
128 }
while(*urlptr >=
'0' &&
131 if(portptr != NULL) {
135 while(*urlptr !=
'/' && *urlptr != 0) {
144 strncpy(path, file, MAX_PATHLEN);
150 start_get(
struct websocket *s)
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;
157 LOG_INFO(
"Connecting...\n");
158 s->state = WEBSOCKET_STATE_HTTP_REQUEST_SENT;
161 return WEBSOCKET_ERR;
165 call(
struct websocket *s, websocket_result_t r,
166 const uint8_t *data, uint16_t datalen)
168 if(s != NULL && s->callback != NULL) {
169 s->callback(s, r, data, datalen);
184 const char *name = data;
192 if(strcmp(name, websocket_http_client_hostname(&s->s)) == 0) {
196 if(s->state == WEBSOCKET_STATE_DNS_REQUEST_SENT) {
197 LOG_INFO(
"Restarting get\n");
201 if(s->state == WEBSOCKET_STATE_DNS_REQUEST_SENT) {
203 LOG_ERR(
"killing connection\n");
204 call(s, WEBSOCKET_HOSTNAME_NOT_FOUND, NULL, 0);
219 websocket_http_client_aborted(
struct websocket_http_client_state *client_state)
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);
234 websocket_http_client_timedout(
struct websocket_http_client_state *client_state)
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);
250 websocket_http_client_closed(
struct websocket_http_client_state *client_state)
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);
265 websocket_http_client_connected(
struct websocket_http_client_state *client_state)
267 struct websocket *s = (
struct websocket *)
268 ((
char *)client_state - offsetof(
struct websocket, s));
270 LOG_INFO(
"Websocket connected\n");
271 s->state = WEBSOCKET_STATE_WAITING_FOR_HEADER;
272 call(s, WEBSOCKET_CONNECTED, NULL, 0);
280 receive_header_byte(
struct websocket *s, uint8_t byte)
284 struct websocket_frame_hdr *hdr;
287 if(s->state == WEBSOCKET_STATE_RECEIVING_HEADER) {
288 s->headercache[s->headercacheptr] = byte;
290 if(s->headercacheptr >=
sizeof(s->headercache)) {
297 len = s->headercacheptr;
298 hdr = (
struct websocket_frame_hdr *)s->headercache;
307 if(len >= expected_len) {
312 if((hdr->len & WEBSOCKET_LEN_MASK) == 126) {
314 }
else if((hdr->len & WEBSOCKET_LEN_MASK) == 127) {
320 if((hdr->len & WEBSOCKET_MASK_BIT ) != 0) {
326 if(len == expected_len) {
327 s->state = WEBSOCKET_STATE_HEADER_RECEIVED;
338 websocket_http_client_datahandler(
struct websocket_http_client_state *client_state,
339 const uint8_t *data, uint16_t datalen)
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;
347 call(s, WEBSOCKET_CLOSED, NULL, 0);
359 if(s->state == WEBSOCKET_STATE_WAITING_FOR_HEADER) {
360 s->state = WEBSOCKET_STATE_RECEIVING_HEADER;
361 s->headercacheptr = 0;
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]);
372 if(s->state == WEBSOCKET_STATE_HEADER_RECEIVED) {
380 hdr = (
struct websocket_frame_hdr *)s->headercache;
384 s->len = s->left = 0;
388 memset(s->mask, 0,
sizeof(s->mask));
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) +
407 maskptr = (
struct websocket_frame_mask *)&hdr->extlen[4];
413 if((hdr->len & WEBSOCKET_MASK_BIT) == 0) {
416 memcpy(s->mask, &maskptr->mask,
sizeof(s->mask));
423 s->opcode = hdr->opcode & WEBSOCKET_OPCODE_MASK;
425 if(s->opcode == WEBSOCKET_OPCODE_PING) {
428 hdr->opcode = (hdr->opcode & (~WEBSOCKET_OPCODE_MASK)) |
429 WEBSOCKET_OPCODE_PONG;
430 websocket_http_client_send(&s->s, (
const uint8_t*)hdr, 2);
432 websocket_http_client_send(&s->s, (
const uint8_t*)data, s->left);
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) {
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) {
445 hdr->opcode = (hdr->opcode & (~WEBSOCKET_OPCODE_MASK)) |
446 WEBSOCKET_OPCODE_CLOSE;
447 websocket_http_client_send(&s->s, (
const uint8_t*)hdr, 2);
449 websocket_http_client_send(&s->s, (
const uint8_t*)data, s->left);
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) {
461 s->state = WEBSOCKET_STATE_RECEIVING_DATA;
465 len = MIN(s->left, datalen);
467 call(s, WEBSOCKET_DATA, data, len);
476 call(s, WEBSOCKET_DATA_RECEIVED, NULL, s->len);
477 s->state = WEBSOCKET_STATE_WAITING_FOR_HEADER;
482 websocket_http_client_datahandler(client_state,
486 }
else if(s->state == WEBSOCKET_STATE_RECEIVING_DATA) {
489 if(datalen < s->left) {
490 call(s, WEBSOCKET_DATA, data, datalen);
495 call(s, WEBSOCKET_DATA, data, s->left);
502 call(s, WEBSOCKET_DATA_RECEIVED, NULL, s->len);
503 s->state = WEBSOCKET_STATE_WAITING_FOR_HEADER;
507 websocket_http_client_datahandler(client_state,
519 static uint8_t inited = 0;
528 websocket_init(
struct websocket *s)
531 websocket_http_client_init(&s->s);
535 websocket_set_proxy(
struct websocket *s,
536 const uip_ipaddr_t *
addr, uint16_t port)
538 websocket_http_client_set_proxy(&s->s, addr, port);
542 websocket_open(
struct websocket *s,
const char *url,
543 const char *subprotocol,
const char *hdr,
544 websocket_callback c)
547 char host[MAX_HOSTLEN + 1] = {0};
548 char path[MAX_PATHLEN + 1] = {0};
555 return WEBSOCKET_ERR;
558 if(s->state != WEBSOCKET_STATE_CLOSED) {
559 LOG_INFO(
"Open: closing websocket before opening it again.\n");
564 if(parse_url(url, host, &port, path)) {
566 websocket_http_client_register(&s->s, host, port, path, subprotocol, hdr);
570 uiplib_ip6addrconv(host, (uip_ip6addr_t *)&addr) == 0) {
577 s->state = WEBSOCKET_STATE_DNS_REQUEST_SENT;
578 LOG_INFO(
"Resolving host...\n");
592 websocket_close(
struct websocket *s)
594 websocket_http_client_close(&s->s);
595 s->state = WEBSOCKET_STATE_CLOSED;
599 send_data(
struct websocket *s,
const void *data,
600 uint16_t datalen, uint8_t data_type_opcode)
602 uint8_t buf[WEBSOCKET_MAX_MSGLEN + 4 + 4];
606 struct websocket_frame_hdr *hdr;
607 struct websocket_frame_mask *mask;
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) {
614 LOG_ERR(
"send fail: not connected\n");
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),
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);
633 hdr = (
struct websocket_frame_hdr *)&buf[0];
634 hdr->opcode = WEBSOCKET_FIN_BIT | data_type_opcode;
644 hdr->len = 126 | WEBSOCKET_MASK_BIT;
645 hdr->extlen[0] = datalen >> 8;
646 hdr->extlen[1] = datalen & 0xff;
648 mask = (
struct websocket_frame_mask *)&buf[4];
653 memcpy(&buf[8], data, datalen);
654 return websocket_http_client_send(&s->s, buf, 8 + datalen);
658 hdr->len = datalen | WEBSOCKET_MASK_BIT;
660 mask = (
struct websocket_frame_mask *)&buf[2];
665 memcpy(&buf[6], data, datalen);
666 return websocket_http_client_send(&s->s, buf, 6 + datalen);
672 websocket_send_str(
struct websocket *s,
const char *str)
674 return send_data(s, str, strlen(str), WEBSOCKET_OPCODE_TEXT);
678 websocket_send(
struct websocket *s,
const uint8_t *data,
681 return send_data(s, data, datalen, WEBSOCKET_OPCODE_BIN);
685 websocket_ping(
struct websocket *s)
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;
694 if(2 + 4 > websocket_http_client_sendbuflen(&s->s)) {
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;
704 hdr->len = 0 | WEBSOCKET_MASK_BIT;
712 websocket_http_client_send(&s->s, buf, 2 + 4);
717 websocket_queuelen(
struct websocket *s)
719 return websocket_http_client_queuelen(&s->s);
process_event_t resolv_event_found
Event that is broadcasted when a DNS name has been resolved.
#define PROCESS(name, strname)
Declare a process.
#define PROCESS_CONTEXT_END(p)
End a context switch.
Hostname is fresh and usable.
#define PROCESS_WAIT_EVENT()
Wait for an event to be posted to the process.
static uip_ds6_addr_t * addr
Pointer to a nbr cache entry.
#define PROCESS_BEGIN()
Define the beginning of a process.
#define PROCESS_END()
Define the end of a process.
Representation of an IP address.
resolv_status_t resolv_lookup(const char *name, uip_ipaddr_t **ipaddr)
Look up a hostname in the array of known hostnames.
void * list_head(list_t list)
Get a pointer to the first element of a list.
void list_add(list_t list, void *item)
Add an item at the end of a list.
void list_init(list_t list)
Initialize a list.
#define LIST(name)
Declare a linked list.
#define PROCESS_CONTEXT_BEGIN(p)
Switch context to another process.
PROCESS_THREAD(cc2538_rf_process, ev, data)
Implementation of the cc2538 RF driver process.
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.
void * list_item_next(void *item)
Get the next item following this item.
void process_start(struct process *p, process_data_t data)
Start a process.