mirror of
https://github.com/oliverschmidt/contiki.git
synced 2024-12-22 10:30:13 +00:00
Merge pull request #1918 from adamdunkels/pr-websocket-2
Contiki websocket client
This commit is contained in:
commit
95f69d92a8
@ -29,6 +29,9 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#define DEBUG DEBUG_NONE
|
||||
#include "net/ip/uip-debug.h"
|
||||
|
||||
#include "contiki.h"
|
||||
#include "sys/cc.h"
|
||||
#include "contiki-net.h"
|
||||
@ -37,10 +40,8 @@
|
||||
|
||||
#include "tcp-socket.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
static void relisten(struct tcp_socket *s);
|
||||
|
||||
LIST(socketlist);
|
||||
@ -80,7 +81,7 @@ acked(struct tcp_socket *s)
|
||||
s->output_data_maxlen - s->output_data_send_nxt);
|
||||
}
|
||||
if(s->output_data_len < s->output_data_send_nxt) {
|
||||
printf("tcp: acked assertion failed s->output_data_len (%d) < s->output_data_send_nxt (%d)\n",
|
||||
PRINTF("tcp: acked assertion failed s->output_data_len (%d) < s->output_data_send_nxt (%d)\n",
|
||||
s->output_data_len,
|
||||
s->output_data_send_nxt);
|
||||
tcp_markconn(uip_conn, NULL);
|
||||
@ -121,7 +122,7 @@ newdata(struct tcp_socket *s)
|
||||
bytesleft = 0;
|
||||
}
|
||||
if(bytesleft > 0) {
|
||||
printf("tcp: newdata, bytesleft > 0 (%d) not implemented\n", bytesleft);
|
||||
PRINTF("tcp: newdata, bytesleft > 0 (%d) not implemented\n", bytesleft);
|
||||
}
|
||||
dataptr += copylen;
|
||||
len -= copylen;
|
||||
@ -356,6 +357,8 @@ tcp_socket_send(struct tcp_socket *s,
|
||||
s->output_senddata_len = s->output_data_len;
|
||||
}
|
||||
|
||||
tcpip_poll_tcp(s->c);
|
||||
|
||||
return len;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
@ -398,3 +401,9 @@ tcp_socket_max_sendlen(struct tcp_socket *s)
|
||||
return s->output_data_maxlen - s->output_data_len;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
int
|
||||
tcp_socket_queuelen(struct tcp_socket *s)
|
||||
{
|
||||
return s->output_data_len;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
@ -284,4 +284,16 @@ int tcp_socket_unregister(struct tcp_socket *s);
|
||||
*/
|
||||
int tcp_socket_max_sendlen(struct tcp_socket *s);
|
||||
|
||||
/**
|
||||
* \brief The number of bytes waiting to be sent
|
||||
* \param s A pointer to a TCP socket
|
||||
* \return The number of bytes that have not yet been acknowledged by the receiver.
|
||||
*
|
||||
* This function queries the TCP socket and returns the
|
||||
* number of bytes that are currently not yet known to
|
||||
* have been successfully received by the receiver.
|
||||
*
|
||||
*/
|
||||
int tcp_socket_queuelen(struct tcp_socket *s);
|
||||
|
||||
#endif /* TCP_SOCKET_H */
|
||||
|
347
core/net/ipv6/websocket-http-client.c
Normal file
347
core/net/ipv6/websocket-http-client.c
Normal file
@ -0,0 +1,347 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Thingsquare, http://www.thingsquare.com/.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "websocket-http-client.h"
|
||||
#include "net/ip/uiplib.h"
|
||||
#include "net/ip/resolv.h"
|
||||
|
||||
#include "ip64-addr.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#define DEBUG DEBUG_NONE
|
||||
#include "net/ip/uip-debug.h"
|
||||
|
||||
enum {
|
||||
STATE_WAITING_FOR_HEADER,
|
||||
STATE_WAITING_FOR_CONNECTED,
|
||||
STATE_STEADY_STATE,
|
||||
};
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void
|
||||
send_get(struct websocket_http_client_state *s)
|
||||
{
|
||||
struct tcp_socket *tcps;
|
||||
|
||||
tcps = &s->s;
|
||||
tcp_socket_send_str(tcps, "GET ");
|
||||
tcp_socket_send_str(tcps, s->file);
|
||||
tcp_socket_send_str(tcps, " HTTP/1.1\r\n");
|
||||
tcp_socket_send_str(tcps, "Host: ");
|
||||
tcp_socket_send_str(tcps, s->host);
|
||||
tcp_socket_send_str(tcps, "\r\n");
|
||||
if(strlen(s->header) > 0) {
|
||||
tcp_socket_send_str(tcps, s->header);
|
||||
}
|
||||
/* The Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw== header is
|
||||
supposed to be a random value, encoded as base64, that is SHA1
|
||||
hashed by the server and returned in a HTTP header. This is used
|
||||
to make sure that we are not seeing some cached version of this
|
||||
conversation. But we have no SHA1 code by default in Contiki, so
|
||||
we can't check the return value. Therefore we just use a
|
||||
hardcoded value here. */
|
||||
tcp_socket_send_str(tcps,
|
||||
"Connection: Upgrade\r\n"
|
||||
"Upgrade: websocket\r\n"
|
||||
"Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n"
|
||||
"Sec-WebSocket-Version: 13\r\n"
|
||||
"Sec-WebSocket-Protocol:");
|
||||
tcp_socket_send_str(tcps, s->subprotocol);
|
||||
tcp_socket_send_str(tcps, "\r\n");
|
||||
tcp_socket_send_str(tcps, "\r\n");
|
||||
PRINTF("websocket-http-client: send_get(): output buffer left %d\n", tcp_socket_max_sendlen(tcps));
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void
|
||||
send_connect(struct websocket_http_client_state *s)
|
||||
{
|
||||
struct tcp_socket *tcps;
|
||||
char buf[20];
|
||||
|
||||
tcps = &s->s;
|
||||
tcp_socket_send_str(tcps, "CONNECT ");
|
||||
tcp_socket_send_str(tcps, s->host);
|
||||
tcp_socket_send_str(tcps, ":");
|
||||
sprintf(buf, "%d", s->port);
|
||||
tcp_socket_send_str(tcps, buf);
|
||||
tcp_socket_send_str(tcps, " HTTP/1.1\r\n");
|
||||
tcp_socket_send_str(tcps, "Host: ");
|
||||
tcp_socket_send_str(tcps, s->host);
|
||||
tcp_socket_send_str(tcps, "\r\n");
|
||||
tcp_socket_send_str(tcps, "Proxy-Connection: Keep-Alive\r\n\r\n");
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void
|
||||
event(struct tcp_socket *tcps, void *ptr,
|
||||
tcp_socket_event_t e)
|
||||
{
|
||||
struct websocket_http_client_state *s = ptr;
|
||||
|
||||
if(e == TCP_SOCKET_CONNECTED) {
|
||||
if(s->proxy_port != 0) {
|
||||
send_connect(s);
|
||||
} else {
|
||||
send_get(s);
|
||||
}
|
||||
} else if(e == TCP_SOCKET_CLOSED) {
|
||||
websocket_http_client_closed(s);
|
||||
} else if(e == TCP_SOCKET_TIMEDOUT) {
|
||||
websocket_http_client_timedout(s);
|
||||
} else if(e == TCP_SOCKET_ABORTED) {
|
||||
websocket_http_client_aborted(s);
|
||||
} else if(e == TCP_SOCKET_DATA_SENT) {
|
||||
/* We could feed this information up to the websocket.c layer, but
|
||||
we currently do not do that. */
|
||||
}
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static int
|
||||
parse_header_byte(struct websocket_http_client_state *s,
|
||||
uint8_t b)
|
||||
{
|
||||
static const char *endmarker = "\r\n\r\n";
|
||||
|
||||
PT_BEGIN(&s->parse_header_pt);
|
||||
|
||||
/* Skip the first part of the HTTP response */
|
||||
while(b != ' ') {
|
||||
PT_YIELD(&s->parse_header_pt);
|
||||
}
|
||||
|
||||
/* Skip the space that follow the first part */
|
||||
PT_YIELD(&s->parse_header_pt);
|
||||
|
||||
/* Read the first three bytes that constistute the HTTP status
|
||||
code. We store the HTTP status code as an integer in the
|
||||
s->http_status field. */
|
||||
s->http_status = (b - '0');
|
||||
PT_YIELD(&s->parse_header_pt);
|
||||
s->http_status = s->http_status * 10 + (b - '0');
|
||||
PT_YIELD(&s->parse_header_pt);
|
||||
s->http_status = s->http_status * 10 + (b - '0');
|
||||
|
||||
if((s->proxy_port != 0 && !(s->http_status == 200 || s->http_status == 101)) ||
|
||||
(s->proxy_port == 0 && s->http_status != 101)) {
|
||||
/* This is a websocket request, so the server should have answered
|
||||
with a 101 Switching protocols response. */
|
||||
PRINTF("Websocket HTTP client didn't get the 101 status code (got %d), closing connection\n",
|
||||
s->http_status);
|
||||
websocket_http_client_close(s);
|
||||
while(1) {
|
||||
PT_YIELD(&s->parse_header_pt);
|
||||
}
|
||||
}
|
||||
|
||||
/* Keep eating header bytes until we reach the end of it. The end is
|
||||
indicated by the string "\r\n\r\n". We don't actually look at any
|
||||
of the headers.
|
||||
|
||||
The s->i variable contains the number of consecutive bytes
|
||||
matched. If we match the total length of the string, we stop.
|
||||
*/
|
||||
|
||||
s->i = 0;
|
||||
do {
|
||||
PT_YIELD(&s->parse_header_pt);
|
||||
if(b == (uint8_t)endmarker[s->i]) {
|
||||
s->i++;
|
||||
} else {
|
||||
s->i = 0;
|
||||
}
|
||||
} while(s->i < strlen(endmarker));
|
||||
|
||||
if(s->proxy_port != 0 && s->state == STATE_WAITING_FOR_HEADER) {
|
||||
send_get(s);
|
||||
s->state = STATE_WAITING_FOR_CONNECTED;
|
||||
} else {
|
||||
s->state = STATE_STEADY_STATE;
|
||||
websocket_http_client_connected(s);
|
||||
}
|
||||
PT_END(&s->parse_header_pt);
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static int
|
||||
input(struct tcp_socket *tcps, void *ptr,
|
||||
const uint8_t *inputptr, int inputdatalen)
|
||||
{
|
||||
struct websocket_http_client_state *s = ptr;
|
||||
|
||||
if(s->state == STATE_WAITING_FOR_HEADER ||
|
||||
s->state == STATE_WAITING_FOR_CONNECTED) {
|
||||
int i;
|
||||
for(i = 0; i < inputdatalen; i++) {
|
||||
parse_header_byte(s, inputptr[i]);
|
||||
if(s->state == STATE_STEADY_STATE) {
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(i < inputdatalen && s->state == STATE_STEADY_STATE) {
|
||||
websocket_http_client_datahandler(s, &inputptr[i], inputdatalen - i);
|
||||
}
|
||||
} else {
|
||||
websocket_http_client_datahandler(s, inputptr, inputdatalen);
|
||||
}
|
||||
|
||||
return 0; /* all data consumed */
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
int
|
||||
websocket_http_client_register(struct websocket_http_client_state *s,
|
||||
const char *host,
|
||||
uint16_t port,
|
||||
const char *file,
|
||||
const char *subprotocol,
|
||||
const char *header)
|
||||
{
|
||||
if(host == NULL) {
|
||||
return -1;
|
||||
}
|
||||
strncpy(s->host, host, sizeof(s->host));
|
||||
|
||||
if(file == NULL) {
|
||||
return -1;
|
||||
}
|
||||
strncpy(s->file, file, sizeof(s->file));
|
||||
|
||||
if(subprotocol == NULL) {
|
||||
return -1;
|
||||
}
|
||||
strncpy(s->subprotocol, subprotocol, sizeof(s->subprotocol));
|
||||
|
||||
if(header == NULL) {
|
||||
strncpy(s->header, "", sizeof(s->header));
|
||||
} else {
|
||||
strncpy(s->header, header, sizeof(s->header));
|
||||
}
|
||||
|
||||
if(port == 0) {
|
||||
s->port = 80;
|
||||
} else {
|
||||
s->port = port;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
int
|
||||
websocket_http_client_get(struct websocket_http_client_state *s)
|
||||
{
|
||||
uip_ip4addr_t ip4addr;
|
||||
uip_ip6addr_t ip6addr;
|
||||
uip_ip6addr_t *addr;
|
||||
uint16_t port;
|
||||
|
||||
PRINTF("websocket_http_client_get: connecting to %s with file %s subprotocol %s header %s\n",
|
||||
s->host, s->file, s->subprotocol, s->header);
|
||||
|
||||
|
||||
s->state = STATE_WAITING_FOR_HEADER;
|
||||
|
||||
if(tcp_socket_register(&s->s, s,
|
||||
s->inputbuf, sizeof(s->inputbuf),
|
||||
s->outputbuf, sizeof(s->outputbuf),
|
||||
input, event) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
port = s->port;
|
||||
if(s->proxy_port != 0) {
|
||||
/* The proxy address should be an IPv6 address. */
|
||||
uip_ipaddr_copy(&ip6addr, &s->proxy_addr);
|
||||
port = s->proxy_port;
|
||||
} else if(uiplib_ip6addrconv(s->host, &ip6addr) == 0) {
|
||||
/* First check if the host is an IP address. */
|
||||
if(uiplib_ip4addrconv(s->host, &ip4addr) != 0) {
|
||||
ip64_addr_4to6(&ip4addr, &ip6addr);
|
||||
} else {
|
||||
/* Try to lookup the hostname. If it fails, we initiate a hostname
|
||||
lookup. */
|
||||
if(resolv_lookup(s->host, &addr) != RESOLV_STATUS_CACHED) {
|
||||
return -1;
|
||||
}
|
||||
return tcp_socket_connect(&s->s, addr, s->port);
|
||||
}
|
||||
}
|
||||
return tcp_socket_connect(&s->s, &ip6addr, port);
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
int
|
||||
websocket_http_client_send(struct websocket_http_client_state *s,
|
||||
const uint8_t *data,
|
||||
uint16_t datalen)
|
||||
{
|
||||
if(s->state == STATE_STEADY_STATE) {
|
||||
return tcp_socket_send(&s->s, data, datalen);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
int
|
||||
websocket_http_client_sendbuflen(struct websocket_http_client_state *s)
|
||||
{
|
||||
return tcp_socket_max_sendlen(&s->s);
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
void
|
||||
websocket_http_client_close(struct websocket_http_client_state *s)
|
||||
{
|
||||
tcp_socket_close(&s->s);
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
const char *
|
||||
websocket_http_client_hostname(struct websocket_http_client_state *s)
|
||||
{
|
||||
return s->host;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
void
|
||||
websocket_http_client_init(struct websocket_http_client_state *s)
|
||||
{
|
||||
uip_create_unspecified(&s->proxy_addr);
|
||||
s->proxy_port = 0;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
void
|
||||
websocket_http_client_set_proxy(struct websocket_http_client_state *s,
|
||||
const uip_ipaddr_t *addr, uint16_t port)
|
||||
{
|
||||
uip_ipaddr_copy(&s->proxy_addr, addr);
|
||||
s->proxy_port = port;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
int
|
||||
websocket_http_client_queuelen(struct websocket_http_client_state *s)
|
||||
{
|
||||
return tcp_socket_queuelen(&s->s);
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
123
core/net/ipv6/websocket-http-client.h
Normal file
123
core/net/ipv6/websocket-http-client.h
Normal file
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Thingsquare, http://www.thingsquare.com/.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
#ifndef WEBSOCKET_HTTP_CLIENT_H_
|
||||
#define WEBSOCKET_HTTP_CLIENT_H_
|
||||
|
||||
#include "contiki.h"
|
||||
#include "tcp-socket.h"
|
||||
|
||||
#ifdef WEBSOCKET_HTTP_CLIENT_CONF_INPUTBUFSIZE
|
||||
#define WEBSOCKET_HTTP_CLIENT_INPUTBUFSIZE WEBSOCKET_HTTP_CLIENT_CONF_INPUTBUFSIZE
|
||||
#else /* WEBSOCKET_HTTP_CLIENT_CONF_INPUTBUFSIZE */
|
||||
#define WEBSOCKET_HTTP_CLIENT_INPUTBUFSIZE 100
|
||||
#endif /* WEBSOCKET_HTTP_CLIENT_CONF_INPUTBUFSIZE */
|
||||
|
||||
#ifdef WEBSOCKET_HTTP_CLIENT_CONF_OUTPUTBUFSIZE
|
||||
#define WEBSOCKET_HTTP_CLIENT_OUTPUTBUFSIZE WEBSOCKET_HTTP_CLIENT_CONF_OUTPUTBUFSIZE
|
||||
#else /* WEBSOCKET_HTTP_CLIENT_CONF_OUTPUTBUFSIZE */
|
||||
#define WEBSOCKET_HTTP_CLIENT_OUTPUTBUFSIZE 300
|
||||
#endif /* WEBSOCKET_HTTP_CLIENT_CONF_OUTPUTBUFSIZE */
|
||||
|
||||
#ifdef WEBSOCKET_HTTP_CLIENT_CONF_MAX_HOSTLEN
|
||||
#define WEBSOCKET_HTTP_CLIENT_MAX_HOSTLEN WEBSOCKET_HTTP_CLIENT_CONF_MAX_HOSTLEN
|
||||
#else /* WEBSOCKET_HTTP_CLIENT_CONF_MAX_HOSTLEN */
|
||||
#define WEBSOCKET_HTTP_CLIENT_MAX_HOSTLEN 32
|
||||
#endif /* WEBSOCKET_HTTP_CLIENT_CONF_MAX_HOSTLEN */
|
||||
|
||||
#ifdef WEBSOCKET_HTTP_CLIENT_CONF_MAX_FILELEN
|
||||
#define WEBSOCKET_HTTP_CLIENT_MAX_FILELEN WEBSOCKET_HTTP_CLIENT_CONF_MAX_FILELEN
|
||||
#else /* WEBSOCKET_HTTP_CLIENT_CONF_MAX_FILELEN */
|
||||
#define WEBSOCKET_HTTP_CLIENT_MAX_FILELEN 32
|
||||
#endif /* WEBSOCKET_HTTP_CLIENT_CONF_MAX_FILELEN */
|
||||
|
||||
#ifdef WEBSOCKET_HTTP_CLIENT_CONF_MAX_SUBPROTOCOLLEN
|
||||
#define WEBSOCKET_HTTP_CLIENT_MAX_SUBPROTOCOLLEN WEBSOCKET_HTTP_CLIENT_CONF_MAX_SUBPROTOCOLLEN
|
||||
#else /* WEBSOCKET_HTTP_CLIENT_CONF_MAX_SUBPROTOCOLLEN */
|
||||
#define WEBSOCKET_HTTP_CLIENT_MAX_SUBPROTOCOLLEN 24
|
||||
#endif /* WEBSOCKET_HTTP_CLIENT_CONF_MAX_SUBPROTOCOLLEN */
|
||||
|
||||
#ifdef WEBSOCKET_HTTP_CLIENT_CONF_MAX_HEADERLEN
|
||||
#define WEBSOCKET_HTTP_CLIENT_MAX_HEADERLEN WEBSOCKET_HTTP_CLIENT_CONF_MAX_HEADERLEN
|
||||
#else /* WEBSOCKET_HTTP_CLIENT_CONF_MAX_HEADERLEN */
|
||||
#define WEBSOCKET_HTTP_CLIENT_MAX_HEADERLEN 128
|
||||
#endif /* WEBSOCKET_HTTP_CLIENT_CONF_MAX_HEADERLEN */
|
||||
|
||||
struct websocket_http_client_state {
|
||||
struct tcp_socket s;
|
||||
uint8_t inputbuf[WEBSOCKET_HTTP_CLIENT_INPUTBUFSIZE];
|
||||
uint8_t outputbuf[WEBSOCKET_HTTP_CLIENT_OUTPUTBUFSIZE];
|
||||
char host[WEBSOCKET_HTTP_CLIENT_MAX_HOSTLEN];
|
||||
char file[WEBSOCKET_HTTP_CLIENT_MAX_FILELEN];
|
||||
char subprotocol[WEBSOCKET_HTTP_CLIENT_MAX_SUBPROTOCOLLEN];
|
||||
char header[WEBSOCKET_HTTP_CLIENT_MAX_HEADERLEN];
|
||||
uint16_t port;
|
||||
|
||||
int state;
|
||||
struct pt parse_header_pt;
|
||||
int http_status;
|
||||
int i;
|
||||
|
||||
uip_ipaddr_t proxy_addr;
|
||||
uint16_t proxy_port;
|
||||
};
|
||||
|
||||
void websocket_http_client_init(struct websocket_http_client_state *s);
|
||||
void websocket_http_client_set_proxy(struct websocket_http_client_state *s,
|
||||
const uip_ipaddr_t *addr, uint16_t port);
|
||||
|
||||
int websocket_http_client_register(struct websocket_http_client_state *s,
|
||||
const char *host,
|
||||
uint16_t port,
|
||||
const char *file,
|
||||
const char *subprotocol,
|
||||
const char *hdr);
|
||||
int websocket_http_client_get(struct websocket_http_client_state *s);
|
||||
int websocket_http_client_send(struct websocket_http_client_state *s,
|
||||
const uint8_t *data,
|
||||
uint16_t datalen);
|
||||
int websocket_http_client_sendbuflen(struct websocket_http_client_state *s);
|
||||
|
||||
void websocket_http_client_close(struct websocket_http_client_state *s);
|
||||
|
||||
const char *websocket_http_client_hostname(struct websocket_http_client_state *s);
|
||||
|
||||
int websocket_http_client_queuelen(struct websocket_http_client_state *s);
|
||||
|
||||
/* Callback functions that have to be implemented by the application
|
||||
program. */
|
||||
void websocket_http_client_datahandler(struct websocket_http_client_state *s,
|
||||
const uint8_t *data, uint16_t len);
|
||||
void websocket_http_client_connected(struct websocket_http_client_state *s);
|
||||
void websocket_http_client_timedout(struct websocket_http_client_state *s);
|
||||
void websocket_http_client_aborted(struct websocket_http_client_state *s);
|
||||
void websocket_http_client_closed(struct websocket_http_client_state *s);
|
||||
|
||||
#endif /* WEBSOCKET_HTTP_CLIENT_H_ */
|
724
core/net/ipv6/websocket.c
Normal file
724
core/net/ipv6/websocket.c
Normal file
@ -0,0 +1,724 @@
|
||||
/*
|
||||
* Copyright (c) 2012, Thingsquare, http://www.thingsquare.com/.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "contiki-net.h"
|
||||
#include "lib/petsciiconv.h"
|
||||
|
||||
#include "websocket.h"
|
||||
|
||||
PROCESS(websocket_process, "Websockets process");
|
||||
|
||||
#define MAX_HOSTLEN 64
|
||||
#define MAX_PATHLEN 100
|
||||
|
||||
LIST(websocketlist);
|
||||
|
||||
#define WEBSOCKET_FIN_BIT 0x80
|
||||
|
||||
#define WEBSOCKET_OPCODE_MASK 0x0f
|
||||
#define WEBSOCKET_OPCODE_CONT 0x00
|
||||
#define WEBSOCKET_OPCODE_TEXT 0x01
|
||||
#define WEBSOCKET_OPCODE_BIN 0x02
|
||||
#define WEBSOCKET_OPCODE_CLOSE 0x08
|
||||
#define WEBSOCKET_OPCODE_PING 0x09
|
||||
#define WEBSOCKET_OPCODE_PONG 0x0a
|
||||
|
||||
#define WEBSOCKET_MASK_BIT 0x80
|
||||
#define WEBSOCKET_LEN_MASK 0x7f
|
||||
struct websocket_frame_hdr {
|
||||
uint8_t opcode;
|
||||
uint8_t len;
|
||||
uint8_t extlen[4];
|
||||
};
|
||||
|
||||
struct websocket_frame_mask {
|
||||
uint8_t mask[4];
|
||||
};
|
||||
|
||||
#define DEBUG DEBUG_NONE
|
||||
#include "net/ip/uip-debug.h"
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static int
|
||||
parse_url(const char *url, char *host, uint16_t *portptr, char *path)
|
||||
{
|
||||
const char *urlptr;
|
||||
int i;
|
||||
const char *file;
|
||||
uint16_t port;
|
||||
|
||||
if(url == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Don't even try to go further if the URL is empty. */
|
||||
if(strlen(url) == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* See if the URL starts with http:// or ws:// and remove it. */
|
||||
if(strncmp(url, "http://", strlen("http://")) == 0) {
|
||||
urlptr = url + strlen("http://");
|
||||
} else if(strncmp(url, "ws://", strlen("ws://")) == 0) {
|
||||
urlptr = url + strlen("ws://");
|
||||
} else {
|
||||
urlptr = url;
|
||||
}
|
||||
|
||||
/* Find host part of the URL. */
|
||||
for(i = 0; i < MAX_HOSTLEN; ++i) {
|
||||
if(*urlptr == 0 ||
|
||||
*urlptr == '/' ||
|
||||
*urlptr == ' ' ||
|
||||
*urlptr == ':') {
|
||||
if(host != NULL) {
|
||||
host[i] = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if(host != NULL) {
|
||||
host[i] = *urlptr;
|
||||
}
|
||||
++urlptr;
|
||||
}
|
||||
|
||||
/* Find the port. Default is 0, which lets the underlying transport
|
||||
select its default port. */
|
||||
port = 0;
|
||||
if(*urlptr == ':') {
|
||||
port = 0;
|
||||
do {
|
||||
++urlptr;
|
||||
if(*urlptr >= '0' && *urlptr <= '9') {
|
||||
port = (10 * port) + (*urlptr - '0');
|
||||
}
|
||||
} while(*urlptr >= '0' &&
|
||||
*urlptr <= '9');
|
||||
}
|
||||
if(portptr != NULL) {
|
||||
*portptr = port;
|
||||
}
|
||||
/* Find file part of the URL. */
|
||||
while(*urlptr != '/' && *urlptr != 0) {
|
||||
++urlptr;
|
||||
}
|
||||
if(*urlptr == '/') {
|
||||
file = urlptr;
|
||||
} else {
|
||||
file = "/";
|
||||
}
|
||||
if(path != NULL) {
|
||||
strncpy(path, file, MAX_PATHLEN);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static int
|
||||
start_get(struct websocket *s)
|
||||
{
|
||||
if(websocket_http_client_get(&(s->s)) == 0) {
|
||||
PRINTF("Out of memory error\n");
|
||||
s->state = WEBSOCKET_STATE_CLOSED;
|
||||
return WEBSOCKET_ERR;
|
||||
} else {
|
||||
PRINTF("Connecting...\n");
|
||||
s->state = WEBSOCKET_STATE_HTTP_REQUEST_SENT;
|
||||
return WEBSOCKET_OK;
|
||||
}
|
||||
return WEBSOCKET_ERR;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
void
|
||||
call(struct websocket *s, websocket_result_t r,
|
||||
const uint8_t *data, uint16_t datalen)
|
||||
{
|
||||
if(s != NULL && s->callback != NULL) {
|
||||
s->callback(s, r, data, datalen);
|
||||
}
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
PROCESS_THREAD(websocket_process, ev, data)
|
||||
{
|
||||
PROCESS_BEGIN();
|
||||
|
||||
while(1) {
|
||||
|
||||
PROCESS_WAIT_EVENT();
|
||||
|
||||
if(ev == resolv_event_found && data != NULL) {
|
||||
int ret;
|
||||
struct websocket *s;
|
||||
const char *name = data;
|
||||
/* Either found a hostname, or not. We need to go through the
|
||||
list of websocketsand figure out to which connection this
|
||||
reply corresponds, then either restart the HTTP get, or kill
|
||||
it (if no hostname was found). */
|
||||
for(s = list_head(websocketlist);
|
||||
s != NULL;
|
||||
s = list_item_next(s)) {
|
||||
if(strcmp(name, websocket_http_client_hostname(&s->s)) == 0) {
|
||||
ret = resolv_lookup(name, NULL);
|
||||
if(ret == RESOLV_STATUS_CACHED) {
|
||||
/* Hostname found, restart get. */
|
||||
if(s->state == WEBSOCKET_STATE_DNS_REQUEST_SENT) {
|
||||
PRINTF("Restarting get\n");
|
||||
start_get(s);
|
||||
}
|
||||
} else {
|
||||
if(s->state == WEBSOCKET_STATE_DNS_REQUEST_SENT) {
|
||||
/* Hostname not found, kill connection. */
|
||||
/* PRINTF("XXX killing connection\n");*/
|
||||
call(s, WEBSOCKET_HOSTNAME_NOT_FOUND, NULL, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PROCESS_END();
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Callback function. Called from the webclient when the HTTP
|
||||
* connection was abruptly aborted.
|
||||
*/
|
||||
void
|
||||
websocket_http_client_aborted(struct websocket_http_client_state *client_state)
|
||||
{
|
||||
if(client_state != NULL) {
|
||||
struct websocket *s = (struct websocket *)
|
||||
((char *)client_state - offsetof(struct websocket, s));
|
||||
PRINTF("Websocket reset\n");
|
||||
s->state = WEBSOCKET_STATE_CLOSED;
|
||||
call(s, WEBSOCKET_RESET, NULL, 0);
|
||||
}
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Callback function. Called from the webclient when the HTTP
|
||||
* connection timed out.
|
||||
*/
|
||||
void
|
||||
websocket_http_client_timedout(struct websocket_http_client_state *client_state)
|
||||
{
|
||||
if(client_state != NULL) {
|
||||
struct websocket *s = (struct websocket *)
|
||||
((char *)client_state - offsetof(struct websocket, s));
|
||||
PRINTF("Websocket timed out\n");
|
||||
s->state = WEBSOCKET_STATE_CLOSED;
|
||||
call(s, WEBSOCKET_TIMEDOUT, NULL, 0);
|
||||
}
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Callback function. Called from the webclient when the HTTP
|
||||
* connection was closed after a request from the "websocket_http_client_close()"
|
||||
* function. .
|
||||
*/
|
||||
void
|
||||
websocket_http_client_closed(struct websocket_http_client_state *client_state)
|
||||
{
|
||||
if(client_state != NULL) {
|
||||
struct websocket *s = (struct websocket *)
|
||||
((char *)client_state - offsetof(struct websocket, s));
|
||||
PRINTF("Websocket closed.\n");
|
||||
s->state = WEBSOCKET_STATE_CLOSED;
|
||||
call(s, WEBSOCKET_CLOSED, NULL, 0);
|
||||
}
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Callback function. Called from the webclient when the HTTP
|
||||
* connection is connected.
|
||||
*/
|
||||
void
|
||||
websocket_http_client_connected(struct websocket_http_client_state *client_state)
|
||||
{
|
||||
struct websocket *s = (struct websocket *)
|
||||
((char *)client_state - offsetof(struct websocket, s));
|
||||
|
||||
PRINTF("Websocket connected\n");
|
||||
s->state = WEBSOCKET_STATE_WAITING_FOR_HEADER;
|
||||
call(s, WEBSOCKET_CONNECTED, NULL, 0);
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* The websocket header may potentially be split into multiple TCP
|
||||
segments. This function eats one byte each, puts it into
|
||||
s->headercache, and checks whether or not the full header has been
|
||||
received. */
|
||||
static int
|
||||
receive_header_byte(struct websocket *s, uint8_t byte)
|
||||
{
|
||||
int len;
|
||||
int expected_len;
|
||||
struct websocket_frame_hdr *hdr;
|
||||
|
||||
/* Take the next byte of data and place it in the header cache. */
|
||||
if(s->state == WEBSOCKET_STATE_RECEIVING_HEADER) {
|
||||
s->headercache[s->headercacheptr] = byte;
|
||||
s->headercacheptr++;
|
||||
if(s->headercacheptr >= sizeof(s->headercache)) {
|
||||
/* Something bad happened: we ad read 10 bytes and had not yet
|
||||
found a reasonable header, so we close the socket. */
|
||||
websocket_close(s);
|
||||
}
|
||||
}
|
||||
|
||||
len = s->headercacheptr;
|
||||
hdr = (struct websocket_frame_hdr *)s->headercache;
|
||||
|
||||
/* Check the header that we have received to see if it is long
|
||||
enough. */
|
||||
|
||||
/* We start with expecting a length of at least two bytes (opcode +
|
||||
1 length byte). */
|
||||
expected_len = 2;
|
||||
|
||||
if(len >= expected_len) {
|
||||
|
||||
/* We check how many more bytes we should expect to see. The
|
||||
length byte determines how many length bytes are included in
|
||||
the header. */
|
||||
if((hdr->len & WEBSOCKET_LEN_MASK) == 126) {
|
||||
expected_len += 2;
|
||||
} else if((hdr->len & WEBSOCKET_LEN_MASK) == 127) {
|
||||
expected_len += 4;
|
||||
}
|
||||
|
||||
/* If the option has the mask bit set, we should expect to see 4
|
||||
mask bytes at the end of the header. */
|
||||
if((hdr->len & WEBSOCKET_MASK_BIT ) != 0) {
|
||||
expected_len += 4;
|
||||
}
|
||||
|
||||
/* Now we know how long our header if expected to be. If it is
|
||||
this long, we are done and we set the state to reflect this. */
|
||||
if(len == expected_len) {
|
||||
s->state = WEBSOCKET_STATE_HEADER_RECEIVED;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Callback function. Called from the webclient module when HTTP data
|
||||
* has arrived.
|
||||
*/
|
||||
void
|
||||
websocket_http_client_datahandler(struct websocket_http_client_state *client_state,
|
||||
const uint8_t *data, uint16_t datalen)
|
||||
{
|
||||
struct websocket *s = (struct websocket *)
|
||||
((char *)client_state - offsetof(struct websocket, s));
|
||||
struct websocket_frame_hdr *hdr;
|
||||
struct websocket_frame_mask *maskptr;
|
||||
|
||||
if(data == NULL) {
|
||||
call(s, WEBSOCKET_CLOSED, NULL, 0);
|
||||
} else {
|
||||
/* This function is a state machine that does different things
|
||||
depending on the state. If we are waiting for header (the
|
||||
default state), we change to the RECEIVING_HEADER state when we
|
||||
get the first byte. If we are receiving header, we put all
|
||||
bytes we have into a header buffer until the full header has
|
||||
been received. If we have received the header, we parse it. If
|
||||
we have received and parsed the header, we are ready to receive
|
||||
data. Finally, if there is data left in the incoming packet, we
|
||||
repeat the process. */
|
||||
|
||||
if(s->state == WEBSOCKET_STATE_WAITING_FOR_HEADER) {
|
||||
s->state = WEBSOCKET_STATE_RECEIVING_HEADER;
|
||||
s->headercacheptr = 0;
|
||||
}
|
||||
|
||||
if(s->state == WEBSOCKET_STATE_RECEIVING_HEADER) {
|
||||
while(datalen > 0 && s->state == WEBSOCKET_STATE_RECEIVING_HEADER) {
|
||||
receive_header_byte(s, data[0]);
|
||||
data++;
|
||||
datalen--;
|
||||
}
|
||||
}
|
||||
|
||||
if(s->state == WEBSOCKET_STATE_HEADER_RECEIVED) {
|
||||
/* If this is the start of an incoming websocket data frame, we
|
||||
decode the header and check if we should act on in. If not, we
|
||||
pipe the data to the application through a callback handler. If
|
||||
data arrives in multiple packets, it is up to the application to
|
||||
put it back together again. */
|
||||
|
||||
/* The websocket header is at the start of the incoming data. */
|
||||
hdr = (struct websocket_frame_hdr *)s->headercache;
|
||||
|
||||
/* The s->left field holds the length of the application data
|
||||
* chunk that we are about to receive. */
|
||||
s->len = s->left = 0;
|
||||
|
||||
/* The s->mask field holds the bitmask of the data chunk, if
|
||||
* any. */
|
||||
memset(s->mask, 0, sizeof(s->mask));
|
||||
|
||||
/* We first read out the length of the application data
|
||||
chunk. The length may be encoded over multiple bytes. If the
|
||||
length is >= 126 bytes, it is encoded as two or more
|
||||
bytes. The first length field determines if it is in 2 or 4
|
||||
bytes. We also keep track of where the bitmask is held - its
|
||||
place also differs depending on how the length is encoded. */
|
||||
maskptr = (struct websocket_frame_mask *)hdr->extlen;
|
||||
if((hdr->len & WEBSOCKET_LEN_MASK) < 126) {
|
||||
s->len = s->left = hdr->len & WEBSOCKET_LEN_MASK;
|
||||
} else if(hdr->len == 126) {
|
||||
s->len = s->left = (hdr->extlen[0] << 8) + hdr->extlen[1];
|
||||
maskptr = (struct websocket_frame_mask *)&hdr->extlen[2];
|
||||
} else if(hdr->len == 127) {
|
||||
s->len = s->left = ((uint32_t)hdr->extlen[0] << 24) +
|
||||
((uint32_t)hdr->extlen[1] << 16) +
|
||||
((uint32_t)hdr->extlen[2] << 8) +
|
||||
hdr->extlen[3];
|
||||
maskptr = (struct websocket_frame_mask *)&hdr->extlen[4];
|
||||
}
|
||||
|
||||
/* Set user_data to point to the first byte of application data.
|
||||
See if the application data chunk is masked or not. If it is,
|
||||
we copy the bitmask into the s->mask field. */
|
||||
if((hdr->len & WEBSOCKET_MASK_BIT) == 0) {
|
||||
/* PRINTF("No mask\n");*/
|
||||
} else {
|
||||
memcpy(s->mask, &maskptr->mask, sizeof(s->mask));
|
||||
/* PRINTF("There was a mask, %02x %02x %02x %02x\n",
|
||||
s->mask[0], s->mask[1], s->mask[2], s->mask[3]);*/
|
||||
}
|
||||
|
||||
/* Remember the opcode of the application chunk, put it in the
|
||||
* s->opcode field. */
|
||||
s->opcode = hdr->opcode & WEBSOCKET_OPCODE_MASK;
|
||||
|
||||
if(s->opcode == WEBSOCKET_OPCODE_PING) {
|
||||
/* If the opcode is ping, we change the opcode to a pong, and
|
||||
* send the data back. */
|
||||
hdr->opcode = (hdr->opcode & (~WEBSOCKET_OPCODE_MASK)) |
|
||||
WEBSOCKET_OPCODE_PONG;
|
||||
websocket_http_client_send(&s->s, (const uint8_t*)hdr, 2);
|
||||
if(s->left > 0) {
|
||||
websocket_http_client_send(&s->s, (const uint8_t*)data, s->left);
|
||||
}
|
||||
PRINTF("Got ping\n");
|
||||
call(s, WEBSOCKET_PINGED, NULL, 0);
|
||||
s->state = WEBSOCKET_STATE_WAITING_FOR_HEADER;
|
||||
} else if(s->opcode == WEBSOCKET_OPCODE_PONG) {
|
||||
/* If the opcode is pong, we call the application to let it
|
||||
know we got a pong. */
|
||||
PRINTF("Got pong\n");
|
||||
call(s, WEBSOCKET_PONG_RECEIVED, NULL, 0);
|
||||
s->state = WEBSOCKET_STATE_WAITING_FOR_HEADER;
|
||||
} else if(s->opcode == WEBSOCKET_OPCODE_CLOSE) {
|
||||
/* If the opcode is a close, we send a close frame back. */
|
||||
hdr->opcode = (hdr->opcode & (~WEBSOCKET_OPCODE_MASK)) |
|
||||
WEBSOCKET_OPCODE_CLOSE;
|
||||
websocket_http_client_send(&s->s, (const uint8_t*)hdr, 2);
|
||||
if(s->left > 0) {
|
||||
websocket_http_client_send(&s->s, (const uint8_t*)data, s->left);
|
||||
}
|
||||
PRINTF("websocket: got close, sending close\n");
|
||||
s->state = WEBSOCKET_STATE_WAITING_FOR_HEADER;
|
||||
websocket_http_client_close(&s->s);
|
||||
} else if(s->opcode == WEBSOCKET_OPCODE_BIN ||
|
||||
s->opcode == WEBSOCKET_OPCODE_TEXT) {
|
||||
|
||||
/* If the opcode is bin or text, and there is application
|
||||
* layer data in the packet, we call the application to
|
||||
* process it. */
|
||||
if(s->left > 0) {
|
||||
s->state = WEBSOCKET_STATE_RECEIVING_DATA;
|
||||
if(datalen > 0) {
|
||||
int len;
|
||||
|
||||
len = MIN(s->left, datalen);
|
||||
/* XXX todo: mask if needed. */
|
||||
call(s, WEBSOCKET_DATA, data, len);
|
||||
data += len;
|
||||
s->left -= len;
|
||||
datalen -= len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(s->left == 0) {
|
||||
call(s, WEBSOCKET_DATA_RECEIVED, NULL, s->len);
|
||||
s->state = WEBSOCKET_STATE_WAITING_FOR_HEADER;
|
||||
|
||||
/* Need to keep parsing the incoming data to check for more
|
||||
frames, if the incoming datalen is > than s->left. */
|
||||
if(datalen > 0) {
|
||||
PRINTF("XXX 1 again\n");
|
||||
websocket_http_client_datahandler(client_state,
|
||||
data, datalen);
|
||||
}
|
||||
}
|
||||
} else if(s->state == WEBSOCKET_STATE_RECEIVING_DATA) {
|
||||
/* XXX todo: mask if needed. */
|
||||
/* PRINTF("Calling with s->left %d datalen %d\n",
|
||||
s->left, datalen);*/
|
||||
if(datalen > 0) {
|
||||
if(datalen < s->left) {
|
||||
call(s, WEBSOCKET_DATA, data, datalen);
|
||||
s->left -= datalen;
|
||||
data += datalen;
|
||||
datalen = 0;
|
||||
} else {
|
||||
call(s, WEBSOCKET_DATA, data, s->left);
|
||||
data += s->left;
|
||||
datalen -= s->left;
|
||||
s->left = 0;
|
||||
}
|
||||
}
|
||||
if(s->left == 0) {
|
||||
call(s, WEBSOCKET_DATA_RECEIVED, NULL, s->len);
|
||||
s->state = WEBSOCKET_STATE_WAITING_FOR_HEADER;
|
||||
/* Need to keep parsing the incoming data to check for more
|
||||
frames, if the incoming datalen is > than len. */
|
||||
if(datalen > 0) {
|
||||
PRINTF("XXX 2 again (datalen %d s->left %d)\n", datalen, (int)s->left);
|
||||
websocket_http_client_datahandler(client_state,
|
||||
data, datalen);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void
|
||||
init(void)
|
||||
{
|
||||
static uint8_t inited = 0;
|
||||
if(!inited) {
|
||||
process_start(&websocket_process, NULL);
|
||||
list_init(websocketlist);
|
||||
inited = 1;
|
||||
}
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
void
|
||||
websocket_init(struct websocket *s)
|
||||
{
|
||||
init();
|
||||
websocket_http_client_init(&s->s);
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
void
|
||||
websocket_set_proxy(struct websocket *s,
|
||||
const uip_ipaddr_t *addr, uint16_t port)
|
||||
{
|
||||
websocket_http_client_set_proxy(&s->s, addr, port);
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
websocket_result_t
|
||||
websocket_open(struct websocket *s, const char *url,
|
||||
const char *subprotocol, const char *hdr,
|
||||
websocket_callback c)
|
||||
{
|
||||
int ret;
|
||||
char host[MAX_HOSTLEN];
|
||||
char path[MAX_PATHLEN];
|
||||
uint16_t port;
|
||||
uip_ipaddr_t addr;
|
||||
|
||||
init();
|
||||
|
||||
if(s == NULL) {
|
||||
return WEBSOCKET_ERR;
|
||||
}
|
||||
|
||||
if(s->state != WEBSOCKET_STATE_CLOSED) {
|
||||
PRINTF("websocket_open: closing websocket before opening it again.\n");
|
||||
websocket_close(s);
|
||||
}
|
||||
s->callback = c;
|
||||
|
||||
if(parse_url(url, host, &port, path)) {
|
||||
list_add(websocketlist, s);
|
||||
websocket_http_client_register(&s->s, host, port, path, subprotocol, hdr);
|
||||
|
||||
/* First check if the host is an IP address. */
|
||||
if(uiplib_ip4addrconv(host, (uip_ip4addr_t *)&addr) == 0 &&
|
||||
uiplib_ip6addrconv(host, (uip_ip6addr_t *)&addr) == 0) {
|
||||
/* Try to lookup the hostname. If it fails, we initiate a hostname
|
||||
lookup and print out an informative message on the
|
||||
statusbar. */
|
||||
ret = resolv_lookup(host, NULL);
|
||||
if(ret != RESOLV_STATUS_CACHED) {
|
||||
resolv_query(host);
|
||||
s->state = WEBSOCKET_STATE_DNS_REQUEST_SENT;
|
||||
PRINTF("Resolving host...\n");
|
||||
return WEBSOCKET_OK;
|
||||
}
|
||||
}
|
||||
|
||||
PROCESS_CONTEXT_BEGIN(&websocket_process);
|
||||
ret = start_get(s);
|
||||
PROCESS_CONTEXT_END();
|
||||
return ret;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
void
|
||||
websocket_close(struct websocket *s)
|
||||
{
|
||||
websocket_http_client_close(&s->s);
|
||||
s->state = WEBSOCKET_STATE_CLOSED;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static int
|
||||
send_data(struct websocket *s, const void *data,
|
||||
uint16_t datalen, uint8_t data_type_opcode)
|
||||
{
|
||||
uint8_t buf[WEBSOCKET_MAX_MSGLEN + 4 + 4]; /* The extra + 4 + 4 here
|
||||
comes from the size of
|
||||
the websocket framing
|
||||
header. */
|
||||
struct websocket_frame_hdr *hdr;
|
||||
struct websocket_frame_mask *mask;
|
||||
|
||||
PRINTF("websocket send data len %d %.*s\n", datalen, datalen, (char *)data);
|
||||
if(s->state == WEBSOCKET_STATE_CLOSED ||
|
||||
s->state == WEBSOCKET_STATE_DNS_REQUEST_SENT ||
|
||||
s->state == WEBSOCKET_STATE_HTTP_REQUEST_SENT) {
|
||||
/* Trying to send data on a non-connected websocket. */
|
||||
PRINTF("websocket send fail: not connected\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* We need to have 4 + 4 additional bytes for the websocket framing
|
||||
header. */
|
||||
if(4 + 4 + datalen > websocket_http_client_sendbuflen(&s->s)) {
|
||||
PRINTF("websocket: too few bytes left (%d left, %d needed)\n",
|
||||
websocket_http_client_sendbuflen(&s->s),
|
||||
4 + 4 + datalen);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(datalen > sizeof(buf) - 4 - 4) {
|
||||
PRINTF("websocket: trying to send too large data chunk %d > %d\n",
|
||||
datalen, sizeof(buf) - 4 - 4);
|
||||
return -1;
|
||||
}
|
||||
|
||||
hdr = (struct websocket_frame_hdr *)&buf[0];
|
||||
hdr->opcode = WEBSOCKET_FIN_BIT | data_type_opcode;
|
||||
|
||||
/* If the datalen is larger than 125 bytes, we need to send the data
|
||||
length as two bytes. If the data length would be larger than 64k,
|
||||
we should send the length as 4 bytes, but since we specify the
|
||||
datalen as an unsigned 16-bit int, we do not handle the 64k case
|
||||
here. */
|
||||
if(datalen > 125) {
|
||||
/* Data from client must always have the mask bit set, and a data
|
||||
mask sent right after the header. */
|
||||
hdr->len = 126 | WEBSOCKET_MASK_BIT;
|
||||
hdr->extlen[0] = datalen >> 8;
|
||||
hdr->extlen[1] = datalen & 0xff;
|
||||
|
||||
mask = (struct websocket_frame_mask *)&buf[4];
|
||||
mask->mask[0] =
|
||||
mask->mask[1] =
|
||||
mask->mask[2] =
|
||||
mask->mask[3] = 0;
|
||||
memcpy(&buf[8], data, datalen);
|
||||
return websocket_http_client_send(&s->s, buf, 8 + datalen);
|
||||
} else {
|
||||
/* Data from client must always have the mask bit set, and a data
|
||||
mask sent right after the header. */
|
||||
hdr->len = datalen | WEBSOCKET_MASK_BIT;
|
||||
|
||||
mask = (struct websocket_frame_mask *)&buf[2];
|
||||
mask->mask[0] =
|
||||
mask->mask[1] =
|
||||
mask->mask[2] =
|
||||
mask->mask[3] = 0;
|
||||
memcpy(&buf[6], data, datalen);
|
||||
return websocket_http_client_send(&s->s, buf, 6 + datalen);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
int
|
||||
websocket_send_str(struct websocket *s, const char *str)
|
||||
{
|
||||
return send_data(s, str, strlen(str), WEBSOCKET_OPCODE_TEXT);
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
int
|
||||
websocket_send(struct websocket *s, const uint8_t *data,
|
||||
uint16_t datalen)
|
||||
{
|
||||
return send_data(s, data, datalen, WEBSOCKET_OPCODE_BIN);
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
int
|
||||
websocket_ping(struct websocket *s)
|
||||
{
|
||||
uint8_t buf[sizeof(struct websocket_frame_hdr) +
|
||||
sizeof(struct websocket_frame_mask)];
|
||||
struct websocket_frame_hdr *hdr;
|
||||
struct websocket_frame_mask *mask;
|
||||
|
||||
/* We need 2 + 4 additional bytes for the websocket framing
|
||||
header. */
|
||||
if(2 + 4 > websocket_http_client_sendbuflen(&s->s)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
hdr = (struct websocket_frame_hdr *)&buf[0];
|
||||
mask = (struct websocket_frame_mask *)&buf[2];
|
||||
hdr->opcode = WEBSOCKET_FIN_BIT | WEBSOCKET_OPCODE_PING;
|
||||
|
||||
/* Data from client must always have the mask bit set, and a data
|
||||
mask sent right after the header. */
|
||||
hdr->len = 0 | WEBSOCKET_MASK_BIT;
|
||||
|
||||
/* XXX: We just set a dummy mask of 0 for now and hope that this
|
||||
works. */
|
||||
mask->mask[0] =
|
||||
mask->mask[1] =
|
||||
mask->mask[2] =
|
||||
mask->mask[3] = 0;
|
||||
websocket_http_client_send(&s->s, buf, 2 + 4);
|
||||
return 1;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
int
|
||||
websocket_queuelen(struct websocket *s)
|
||||
{
|
||||
return websocket_http_client_queuelen(&s->s);
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
113
core/net/ipv6/websocket.h
Normal file
113
core/net/ipv6/websocket.h
Normal file
@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright (c) 2012, Thingsquare, http://www.thingsquare.com/.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
#ifndef WEBSOCKET_H
|
||||
#define WEBSOCKET_H
|
||||
|
||||
#include "websocket-http-client.h"
|
||||
|
||||
typedef enum {
|
||||
WEBSOCKET_ERR = 0,
|
||||
WEBSOCKET_OK = 1,
|
||||
WEBSOCKET_IN_PROGRESS = 2,
|
||||
WEBSOCKET_HOSTNAME_NOT_FOUND = 3,
|
||||
WEBSOCKET_CONNECTED = 4,
|
||||
WEBSOCKET_DATA = 5,
|
||||
WEBSOCKET_RESET = 6,
|
||||
WEBSOCKET_TIMEDOUT = 7,
|
||||
WEBSOCKET_CLOSED = 8,
|
||||
WEBSOCKET_PINGED = 9,
|
||||
WEBSOCKET_DATA_RECEIVED = 10,
|
||||
WEBSOCKET_PONG_RECEIVED = 11,
|
||||
} websocket_result_t;
|
||||
|
||||
struct websocket;
|
||||
|
||||
typedef void (* websocket_callback)(struct websocket *s,
|
||||
websocket_result_t result,
|
||||
const uint8_t *data,
|
||||
uint16_t datalen);
|
||||
#ifdef WEBSOCKET_CONF_MAX_MSGLEN
|
||||
#define WEBSOCKET_MAX_MSGLEN WEBSOCKET_CONF_MAX_MSGLEN
|
||||
#else /* WEBSOCKET_CONF_MAX_MSGLEN */
|
||||
#define WEBSOCKET_MAX_MSGLEN 200
|
||||
#endif /* WEBSOCKET_CONF_MAX_MSGLEN */
|
||||
|
||||
struct websocket {
|
||||
struct websocket *next; /* Must be first. */
|
||||
struct websocket_http_client_state s;
|
||||
websocket_callback callback;
|
||||
|
||||
uint8_t mask[4];
|
||||
uint32_t left, len;
|
||||
uint8_t opcode;
|
||||
|
||||
uint8_t state;
|
||||
|
||||
uint8_t headercacheptr;
|
||||
uint8_t headercache[10]; /* The maximum websocket header + mask is 6
|
||||
+ 4 bytes long */
|
||||
};
|
||||
|
||||
enum {
|
||||
WEBSOCKET_STATE_CLOSED = 0,
|
||||
WEBSOCKET_STATE_DNS_REQUEST_SENT = 1,
|
||||
WEBSOCKET_STATE_HTTP_REQUEST_SENT = 2,
|
||||
WEBSOCKET_STATE_WAITING_FOR_HEADER = 3,
|
||||
WEBSOCKET_STATE_RECEIVING_HEADER = 4,
|
||||
WEBSOCKET_STATE_HEADER_RECEIVED = 5,
|
||||
WEBSOCKET_STATE_RECEIVING_DATA = 6,
|
||||
};
|
||||
|
||||
|
||||
void websocket_init(struct websocket *s);
|
||||
|
||||
void websocket_set_proxy(struct websocket *s,
|
||||
const uip_ipaddr_t *addr, uint16_t port);
|
||||
|
||||
websocket_result_t websocket_open(struct websocket *s,
|
||||
const char *url,
|
||||
const char *subprotocol,
|
||||
const char *hdr,
|
||||
websocket_callback c);
|
||||
|
||||
int websocket_send(struct websocket *s,
|
||||
const uint8_t *data, uint16_t datalen);
|
||||
|
||||
int websocket_send_str(struct websocket *s,
|
||||
const char *strptr);
|
||||
|
||||
void websocket_close(struct websocket *s);
|
||||
|
||||
int websocket_ping(struct websocket *s);
|
||||
|
||||
int websocket_queuelen(struct websocket *s);
|
||||
|
||||
#endif /* WEBSOCKET_H */
|
6
examples/websockets/Makefile
Normal file
6
examples/websockets/Makefile
Normal file
@ -0,0 +1,6 @@
|
||||
all: websocket-example
|
||||
CONTIKI=../..
|
||||
|
||||
include $(CONTIKI)/Makefile.include
|
||||
|
||||
|
6
examples/websockets/node/Makefile
Normal file
6
examples/websockets/node/Makefile
Normal file
@ -0,0 +1,6 @@
|
||||
install:
|
||||
npm install websocket
|
||||
|
||||
|
||||
run:
|
||||
nodejs example-server.js
|
57
examples/websockets/node/example-server.js
Normal file
57
examples/websockets/node/example-server.js
Normal file
@ -0,0 +1,57 @@
|
||||
"use strict";
|
||||
|
||||
var serverPort = 8080;
|
||||
|
||||
var websocket = require('websocket').server;
|
||||
var http = require('http');
|
||||
|
||||
var server = http.createServer(function(request, response) {
|
||||
response.writeHead(200, {'Content-Type': 'text/plain'});
|
||||
response.write('This is a websocket server, not intended for http\n');
|
||||
response.end();
|
||||
});
|
||||
|
||||
server.listen(serverPort, function() {
|
||||
console.log('Server is listening on port ' + serverPort);
|
||||
});
|
||||
|
||||
var wsServer = new websocket({
|
||||
httpServer: server
|
||||
});
|
||||
|
||||
|
||||
var connections = [];
|
||||
|
||||
function broadcastMessage(message) {
|
||||
for (var i = 0; i < connections.length; i++) {
|
||||
connections[i].sendUTF(message);
|
||||
}
|
||||
}
|
||||
|
||||
wsServer.on('request', function(request) {
|
||||
/* Save the connection */
|
||||
var connection = request.accept(null, request.origin);
|
||||
|
||||
/* Store the connection in the list of connections */
|
||||
var connectionIndex = connections.push(connection) - 1;
|
||||
|
||||
console.log('Connection from ' + connection.remoteAddress + '.');
|
||||
|
||||
broadcastMessage('Connection from ' + connection.remoteAddress + '.');
|
||||
|
||||
connection.on('message', function(message) {
|
||||
if (message.type === 'utf8') {
|
||||
console.log((new Date()) + ' Message received: ' +
|
||||
message.utf8Data);
|
||||
broadcastMessage(message.utf8Data);
|
||||
}
|
||||
});
|
||||
|
||||
// user disconnected
|
||||
connection.on('close', function(connection) {
|
||||
console.log((new Date()) + ' Connection lost: ' +
|
||||
connection.remoteAddress);
|
||||
connections.splice(connectionIndex, 1);
|
||||
});
|
||||
|
||||
});
|
93
examples/websockets/websocket-example.c
Normal file
93
examples/websockets/websocket-example.c
Normal file
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (c) 2012, Thingsquare, http://www.thingsquare.com/.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
#include "contiki.h"
|
||||
|
||||
#include "websocket.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
static struct websocket s;
|
||||
|
||||
static void callback(struct websocket *s, websocket_result_t r,
|
||||
const uint8_t *data, uint16_t datalen);
|
||||
|
||||
#define RECONNECT_INTERVAL 10 * CLOCK_SECOND
|
||||
static struct ctimer reconnect_timer;
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
PROCESS(websocket_example_process, "Websocket Example");
|
||||
AUTOSTART_PROCESSES(&websocket_example_process);
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void
|
||||
reconnect_callback(void *ptr)
|
||||
{
|
||||
websocket_open(&s, "ws://172.16.0.1:8080/",
|
||||
"contiki", NULL, callback);
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void
|
||||
callback(struct websocket *s, websocket_result_t r,
|
||||
const uint8_t *data, uint16_t datalen)
|
||||
{
|
||||
if(r == WEBSOCKET_CLOSED ||
|
||||
r == WEBSOCKET_RESET ||
|
||||
r == WEBSOCKET_HOSTNAME_NOT_FOUND ||
|
||||
r == WEBSOCKET_TIMEDOUT) {
|
||||
ctimer_set(&reconnect_timer, RECONNECT_INTERVAL, reconnect_callback, s);
|
||||
} else if(r == WEBSOCKET_CONNECTED) {
|
||||
websocket_send_str(s, "Connected");
|
||||
} else if(r == WEBSOCKET_DATA) {
|
||||
printf("websocket-example: Received data '%.*s' (len %d)\n", datalen,
|
||||
data, datalen);
|
||||
}
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
PROCESS_THREAD(websocket_example_process, ev, data)
|
||||
{
|
||||
static struct etimer et;
|
||||
PROCESS_BEGIN();
|
||||
|
||||
ctimer_set(&reconnect_timer, RECONNECT_INTERVAL, reconnect_callback, &s);
|
||||
|
||||
websocket_init(&s);
|
||||
while(1) {
|
||||
etimer_set(&et, CLOCK_SECOND / 8);
|
||||
PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&et));
|
||||
char buf[] = "012345678";
|
||||
static int count;
|
||||
buf[0] = (count % 9) + '0';
|
||||
count++;
|
||||
websocket_send_str(&s, buf);
|
||||
}
|
||||
|
||||
PROCESS_END();
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
Loading…
Reference in New Issue
Block a user