HTTP socket code with support for GET and POST
This commit is contained in:
parent
ba409cce57
commit
33372945a3
682
core/net/http-socket/http-socket.c
Normal file
682
core/net/http-socket/http-socket.c
Normal file
|
@ -0,0 +1,682 @@
|
|||
/*
|
||||
* Copyright (c) 2013, 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-net.h"
|
||||
#include "ip64-addr.h"
|
||||
#include "http-socket.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define MAX_PATHLEN 80
|
||||
#define MAX_HOSTLEN 40
|
||||
PROCESS(http_socket_process, "HTTP socket process");
|
||||
LIST(socketlist);
|
||||
|
||||
static void removesocket(struct http_socket *s);
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void
|
||||
call_callback(struct http_socket *s, http_socket_event_t e,
|
||||
const uint8_t *data, uint16_t datalen)
|
||||
{
|
||||
if(s->callback != NULL) {
|
||||
s->callback(s, s->callbackptr, e,
|
||||
data, datalen);
|
||||
}
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void
|
||||
parse_header_init(struct http_socket *s)
|
||||
{
|
||||
PT_INIT(&s->headerpt);
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static int
|
||||
parse_header_byte(struct http_socket *s, char c)
|
||||
{
|
||||
PT_BEGIN(&s->headerpt);
|
||||
|
||||
memset(&s->header, -1, sizeof(s->header));
|
||||
|
||||
/* Skip the HTTP response */
|
||||
while(c != ' ') {
|
||||
PT_YIELD(&s->headerpt);
|
||||
}
|
||||
|
||||
/* Skip the space */
|
||||
PT_YIELD(&s->headerpt);
|
||||
/* Read three characters of HTTP status and convert to BCD */
|
||||
s->header.status_code = 0;
|
||||
for(s->header_chars = 0; s->header_chars < 3; s->header_chars++) {
|
||||
s->header.status_code = s->header.status_code << 4 | (c - '0');
|
||||
PT_YIELD(&s->headerpt);
|
||||
}
|
||||
|
||||
if(s->header.status_code == 0x200 || s->header.status_code == 0x206) {
|
||||
/* Read headers until data */
|
||||
|
||||
while(1) {
|
||||
/* Skip characters until end of line */
|
||||
do {
|
||||
while(c != '\r') {
|
||||
s->header_chars++;
|
||||
PT_YIELD(&s->headerpt);
|
||||
}
|
||||
s->header_chars++;
|
||||
PT_YIELD(&s->headerpt);
|
||||
} while(c != '\n');
|
||||
s->header_chars--;
|
||||
PT_YIELD(&s->headerpt);
|
||||
|
||||
if(s->header_chars == 0) {
|
||||
/* This was an empty line, i.e. the end of headers */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Start of line */
|
||||
s->header_chars = 0;
|
||||
|
||||
/* Read header field */
|
||||
while(c != ' ' && c != '\t' && c != ':' && c != '\r' &&
|
||||
s->header_chars < sizeof(s->header_field) - 1) {
|
||||
s->header_field[s->header_chars++] = c;
|
||||
PT_YIELD(&s->headerpt);
|
||||
}
|
||||
s->header_field[s->header_chars] = '\0';
|
||||
/* Skip linear white spaces */
|
||||
while(c == ' ' || c == '\t') {
|
||||
s->header_chars++;
|
||||
PT_YIELD(&s->headerpt);
|
||||
}
|
||||
if(c == ':') {
|
||||
/* Skip the colon */
|
||||
s->header_chars++;
|
||||
PT_YIELD(&s->headerpt);
|
||||
/* Skip linear white spaces */
|
||||
while(c == ' ' || c == '\t') {
|
||||
s->header_chars++;
|
||||
PT_YIELD(&s->headerpt);
|
||||
}
|
||||
if(!strcmp(s->header_field, "Content-Length")) {
|
||||
s->header.content_length = 0;
|
||||
while(isdigit((int)c)) {
|
||||
s->header.content_length = s->header.content_length * 10 + c - '0';
|
||||
s->header_chars++;
|
||||
PT_YIELD(&s->headerpt);
|
||||
}
|
||||
} else if(!strcmp(s->header_field, "Content-Range")) {
|
||||
/* Skip the bytes-unit token */
|
||||
while(c != ' ' && c != '\t') {
|
||||
s->header_chars++;
|
||||
PT_YIELD(&s->headerpt);
|
||||
}
|
||||
/* Skip linear white spaces */
|
||||
while(c == ' ' || c == '\t') {
|
||||
s->header_chars++;
|
||||
PT_YIELD(&s->headerpt);
|
||||
}
|
||||
s->header.content_range.first_byte_pos = 0;
|
||||
while(isdigit((int)c)) {
|
||||
s->header.content_range.first_byte_pos =
|
||||
s->header.content_range.first_byte_pos * 10 + c - '0';
|
||||
s->header_chars++;
|
||||
PT_YIELD(&s->headerpt);
|
||||
}
|
||||
/* Skip linear white spaces */
|
||||
while(c == ' ' || c == '\t') {
|
||||
s->header_chars++;
|
||||
PT_YIELD(&s->headerpt);
|
||||
}
|
||||
if(c == '-') {
|
||||
/* Skip the dash */
|
||||
s->header_chars++;
|
||||
PT_YIELD(&s->headerpt);
|
||||
/* Skip linear white spaces */
|
||||
while(c == ' ' || c == '\t') {
|
||||
s->header_chars++;
|
||||
PT_YIELD(&s->headerpt);
|
||||
}
|
||||
s->header.content_range.last_byte_pos = 0;
|
||||
while(isdigit((int)c)) {
|
||||
s->header.content_range.last_byte_pos =
|
||||
s->header.content_range.last_byte_pos * 10 + c - '0';
|
||||
s->header_chars++;
|
||||
PT_YIELD(&s->headerpt);
|
||||
}
|
||||
/* Skip linear white spaces */
|
||||
while(c == ' ' || c == '\t') {
|
||||
s->header_chars++;
|
||||
PT_YIELD(&s->headerpt);
|
||||
}
|
||||
if(c == '/') {
|
||||
/* Skip the slash */
|
||||
s->header_chars++;
|
||||
PT_YIELD(&s->headerpt);
|
||||
/* Skip linear white spaces */
|
||||
while(c == ' ' || c == '\t') {
|
||||
s->header_chars++;
|
||||
PT_YIELD(&s->headerpt);
|
||||
}
|
||||
if(c != '*') {
|
||||
s->header.content_range.instance_length = 0;
|
||||
while(isdigit((int)c)) {
|
||||
s->header.content_range.instance_length =
|
||||
s->header.content_range.instance_length * 10 + c - '0';
|
||||
s->header_chars++;
|
||||
PT_YIELD(&s->headerpt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* All headers read, now read data */
|
||||
call_callback(s, HTTP_SOCKET_HEADER, (void *)&s->header, sizeof(s->header));
|
||||
|
||||
/* Should exit the pt here to indicate that all headers have been
|
||||
read */
|
||||
PT_EXIT(&s->headerpt);
|
||||
} else {
|
||||
if(s->header.status_code == 0x404) {
|
||||
printf("File not found\n");
|
||||
} else if(s->header.status_code == 0x301 || s->header.status_code == 0x302) {
|
||||
printf("File moved (not handled)\n");
|
||||
}
|
||||
|
||||
call_callback(s, HTTP_SOCKET_ERR, (void *)&s->header, sizeof(s->header));
|
||||
tcp_socket_close(&s->s);
|
||||
removesocket(s);
|
||||
PT_EXIT(&s->headerpt);
|
||||
}
|
||||
|
||||
|
||||
PT_END(&s->headerpt);
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static int
|
||||
input_pt(struct http_socket *s,
|
||||
const uint8_t *inputptr, int inputdatalen)
|
||||
{
|
||||
int i;
|
||||
PT_BEGIN(&s->pt);
|
||||
|
||||
/* Parse the header */
|
||||
s->header_received = 0;
|
||||
do {
|
||||
for(i = 0; i < inputdatalen; i++) {
|
||||
if(!PT_SCHEDULE(parse_header_byte(s, inputptr[i]))) {
|
||||
s->header_received = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
inputdatalen -= i;
|
||||
inputptr += i;
|
||||
|
||||
if(s->header_received == 0) {
|
||||
/* If we have not yet received the full header, we wait for the
|
||||
next packet to arrive. */
|
||||
PT_YIELD(&s->pt);
|
||||
}
|
||||
} while(s->header_received == 0);
|
||||
|
||||
s->bodylen = 0;
|
||||
do {
|
||||
/* Receive the data */
|
||||
call_callback(s, HTTP_SOCKET_DATA, inputptr, inputdatalen);
|
||||
|
||||
/* Close the connection if the expected content length has been received */
|
||||
if(s->header.content_length >= 0 && s->bodylen < s->header.content_length) {
|
||||
s->bodylen += inputdatalen;
|
||||
if(s->bodylen >= s->header.content_length) {
|
||||
tcp_socket_close(&s->s);
|
||||
}
|
||||
}
|
||||
|
||||
PT_YIELD(&s->pt);
|
||||
} while(inputdatalen > 0);
|
||||
|
||||
PT_END(&s->pt);
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void
|
||||
start_timeout_timer(struct http_socket *s)
|
||||
{
|
||||
PROCESS_CONTEXT_BEGIN(&http_socket_process);
|
||||
etimer_set(&s->timeout_timer, HTTP_SOCKET_TIMEOUT);
|
||||
PROCESS_CONTEXT_END(&http_socket_process);
|
||||
s->timeout_timer_started = 1;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static int
|
||||
input(struct tcp_socket *tcps, void *ptr,
|
||||
const uint8_t *inputptr, int inputdatalen)
|
||||
{
|
||||
struct http_socket *s = ptr;
|
||||
|
||||
input_pt(s, inputptr, inputdatalen);
|
||||
start_timeout_timer(s);
|
||||
|
||||
return 0; /* all data consumed */
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
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) {
|
||||
printf("null url\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Don't even try to go further if the URL is empty. */
|
||||
if(strlen(url) == 0) {
|
||||
printf("empty url\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* See if the URL starts with http:// and remove it. Otherwise, we
|
||||
assume it is an implicit http://. */
|
||||
if(strncmp(url, "http://", strlen("http://")) == 0) {
|
||||
urlptr = url + strlen("http://");
|
||||
} else {
|
||||
urlptr = url;
|
||||
}
|
||||
|
||||
/* Find host part of the URL. */
|
||||
if(*urlptr == '[') {
|
||||
/* Handle IPv6 addresses - scan for matching ']' */
|
||||
urlptr++;
|
||||
for(i = 0; i < MAX_HOSTLEN; ++i) {
|
||||
if(*urlptr == ']') {
|
||||
if(host != NULL) {
|
||||
host[i] = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if(host != NULL) {
|
||||
host[i] = *urlptr;
|
||||
}
|
||||
++urlptr;
|
||||
}
|
||||
} else {
|
||||
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 80. */
|
||||
port = 80;
|
||||
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 void
|
||||
removesocket(struct http_socket *s)
|
||||
{
|
||||
etimer_stop(&s->timeout_timer);
|
||||
s->timeout_timer_started = 0;
|
||||
list_remove(socketlist, s);
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void
|
||||
event(struct tcp_socket *tcps, void *ptr,
|
||||
tcp_socket_event_t e)
|
||||
{
|
||||
struct http_socket *s = ptr;
|
||||
char host[MAX_HOSTLEN];
|
||||
char path[MAX_PATHLEN];
|
||||
uint16_t port;
|
||||
char str[42];
|
||||
int len;
|
||||
|
||||
if(e == TCP_SOCKET_CONNECTED) {
|
||||
printf("Connected\n");
|
||||
if(parse_url(s->url, host, &port, path)) {
|
||||
tcp_socket_send_str(tcps, s->postdata != NULL ? "POST " : "GET ");
|
||||
if(s->proxy_port != 0) {
|
||||
/* If we are configured to route through a proxy, we should
|
||||
provide the full URL as the path. */
|
||||
tcp_socket_send_str(tcps, s->url);
|
||||
} else {
|
||||
tcp_socket_send_str(tcps, path);
|
||||
}
|
||||
tcp_socket_send_str(tcps, " HTTP/1.1\r\n");
|
||||
tcp_socket_send_str(tcps, "Connection: close\r\n");
|
||||
tcp_socket_send_str(tcps, "Host: ");
|
||||
tcp_socket_send_str(tcps, host);
|
||||
tcp_socket_send_str(tcps, "\r\n");
|
||||
if(s->postdata != NULL) {
|
||||
if(s->content_type) {
|
||||
tcp_socket_send_str(tcps, "Content-Type: ");
|
||||
tcp_socket_send_str(tcps, s->content_type);
|
||||
tcp_socket_send_str(tcps, "\r\n");
|
||||
}
|
||||
tcp_socket_send_str(tcps, "Content-Length: ");
|
||||
sprintf(str, "%u", s->postdatalen);
|
||||
tcp_socket_send_str(tcps, str);
|
||||
tcp_socket_send_str(tcps, "\r\n");
|
||||
} else if(s->length || s->pos > 0) {
|
||||
tcp_socket_send_str(tcps, "Range: bytes=");
|
||||
if(s->length) {
|
||||
if(s->pos >= 0) {
|
||||
sprintf(str, "%llu-%llu", s->pos, s->pos + s->length - 1);
|
||||
} else {
|
||||
sprintf(str, "-%llu", s->length);
|
||||
}
|
||||
} else {
|
||||
sprintf(str, "%llu-", s->pos);
|
||||
}
|
||||
tcp_socket_send_str(tcps, str);
|
||||
tcp_socket_send_str(tcps, "\r\n");
|
||||
}
|
||||
tcp_socket_send_str(tcps, "\r\n");
|
||||
if(s->postdata != NULL && s->postdatalen) {
|
||||
len = tcp_socket_send(tcps, s->postdata, s->postdatalen);
|
||||
s->postdata += len;
|
||||
s->postdatalen -= len;
|
||||
}
|
||||
}
|
||||
parse_header_init(s);
|
||||
} else if(e == TCP_SOCKET_CLOSED) {
|
||||
call_callback(s, HTTP_SOCKET_CLOSED, NULL, 0);
|
||||
removesocket(s);
|
||||
printf("Closed\n");
|
||||
} else if(e == TCP_SOCKET_TIMEDOUT) {
|
||||
call_callback(s, HTTP_SOCKET_TIMEDOUT, NULL, 0);
|
||||
removesocket(s);
|
||||
printf("Timedout\n");
|
||||
} else if(e == TCP_SOCKET_ABORTED) {
|
||||
call_callback(s, HTTP_SOCKET_ABORTED, NULL, 0);
|
||||
removesocket(s);
|
||||
printf("Aborted\n");
|
||||
} else if(e == TCP_SOCKET_DATA_SENT) {
|
||||
if(s->postdata != NULL && s->postdatalen) {
|
||||
len = tcp_socket_send(tcps, s->postdata, s->postdatalen);
|
||||
s->postdata += len;
|
||||
s->postdatalen -= len;
|
||||
} else {
|
||||
start_timeout_timer(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static int
|
||||
start_request(struct http_socket *s)
|
||||
{
|
||||
uip_ip4addr_t ip4addr;
|
||||
uip_ip6addr_t ip6addr;
|
||||
uip_ip6addr_t *addr;
|
||||
char host[MAX_HOSTLEN];
|
||||
char path[MAX_PATHLEN];
|
||||
uint16_t port;
|
||||
int ret;
|
||||
|
||||
if(parse_url(s->url, host, &port, path)) {
|
||||
|
||||
printf("url %s host %s port %d path %s\n",
|
||||
s->url, host, port, path);
|
||||
|
||||
/* Check if we are to route the request through a proxy. */
|
||||
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(host, &ip6addr) == 0) {
|
||||
/* First check if the host is an IP address. */
|
||||
if(uiplib_ip4addrconv(host, &ip4addr) != 0) {
|
||||
ip64_addr_4to6(&ip4addr, &ip6addr);
|
||||
} else {
|
||||
/* Try to lookup the hostname. If it fails, we initiate a hostname
|
||||
lookup. */
|
||||
ret = resolv_lookup(host, &addr);
|
||||
if(ret == RESOLV_STATUS_UNCACHED ||
|
||||
ret == RESOLV_STATUS_EXPIRED) {
|
||||
resolv_query(host);
|
||||
puts("Resolving host...");
|
||||
return HTTP_SOCKET_OK;
|
||||
}
|
||||
if(addr != NULL) {
|
||||
s->did_tcp_connect = 1;
|
||||
tcp_socket_connect(&s->s, addr, port);
|
||||
return HTTP_SOCKET_OK;
|
||||
} else {
|
||||
return HTTP_SOCKET_ERR;
|
||||
}
|
||||
}
|
||||
}
|
||||
tcp_socket_connect(&s->s, &ip6addr, port);
|
||||
return HTTP_SOCKET_OK;
|
||||
} else {
|
||||
return HTTP_SOCKET_ERR;
|
||||
}
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
PROCESS_THREAD(http_socket_process, ev, data)
|
||||
{
|
||||
PROCESS_BEGIN();
|
||||
|
||||
while(1) {
|
||||
|
||||
PROCESS_WAIT_EVENT();
|
||||
|
||||
if(ev == resolv_event_found && data != NULL) {
|
||||
struct http_socket *s;
|
||||
const char *name = data;
|
||||
/* Either found a hostname, or not. We need to go through the
|
||||
list of http sockets and 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(socketlist);
|
||||
s != NULL;
|
||||
s = list_item_next(s)) {
|
||||
char host[MAX_HOSTLEN];
|
||||
if(s->did_tcp_connect) {
|
||||
/* We already connected, ignored */
|
||||
} else if(parse_url(s->url, host, NULL, NULL) &&
|
||||
strcmp(name, host) == 0) {
|
||||
if(resolv_lookup(name, NULL) == RESOLV_STATUS_CACHED) {
|
||||
/* Hostname found, restart get. */
|
||||
start_request(s);
|
||||
} else {
|
||||
/* Hostname not found, kill connection. */
|
||||
call_callback(s, HTTP_SOCKET_HOSTNAME_NOT_FOUND, NULL, 0);
|
||||
removesocket(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if(ev == PROCESS_EVENT_TIMER) {
|
||||
struct http_socket *s;
|
||||
struct etimer *timeout_timer = data;
|
||||
/*
|
||||
* A socket time-out has occurred. We need to go through the list of HTTP
|
||||
* sockets and figure out to which socket this timer event corresponds,
|
||||
* then close this socket.
|
||||
*/
|
||||
for(s = list_head(socketlist);
|
||||
s != NULL;
|
||||
s = list_item_next(s)) {
|
||||
if(timeout_timer == &s->timeout_timer && s->timeout_timer_started) {
|
||||
tcp_socket_close(&s->s);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PROCESS_END();
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void
|
||||
init(void)
|
||||
{
|
||||
static uint8_t inited = 0;
|
||||
if(inited == 0) {
|
||||
process_start(&http_socket_process, NULL);
|
||||
list_init(socketlist);
|
||||
inited = 1;
|
||||
}
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
void
|
||||
http_socket_init(struct http_socket *s)
|
||||
{
|
||||
init();
|
||||
uip_create_unspecified(&s->proxy_addr);
|
||||
s->proxy_port = 0;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void
|
||||
initialize_socket(struct http_socket *s)
|
||||
{
|
||||
s->pos = 0;
|
||||
s->length = 0;
|
||||
s->postdata = NULL;
|
||||
s->postdatalen = 0;
|
||||
s->timeout_timer_started = 0;
|
||||
PT_INIT(&s->pt);
|
||||
tcp_socket_register(&s->s, s,
|
||||
s->inputbuf, sizeof(s->inputbuf),
|
||||
s->outputbuf, sizeof(s->outputbuf),
|
||||
input, event);
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
int
|
||||
http_socket_get(struct http_socket *s,
|
||||
const char *url,
|
||||
int64_t pos,
|
||||
uint64_t length,
|
||||
http_socket_callback_t callback,
|
||||
void *callbackptr)
|
||||
{
|
||||
initialize_socket(s);
|
||||
strncpy(s->url, url, sizeof(s->url));
|
||||
s->pos = pos;
|
||||
s->length = length;
|
||||
s->callback = callback;
|
||||
s->callbackptr = callbackptr;
|
||||
|
||||
s->did_tcp_connect = 0;
|
||||
|
||||
list_add(socketlist, s);
|
||||
|
||||
return start_request(s);
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
int
|
||||
http_socket_post(struct http_socket *s,
|
||||
const char *url,
|
||||
const void *postdata,
|
||||
uint16_t postdatalen,
|
||||
const char *content_type,
|
||||
http_socket_callback_t callback,
|
||||
void *callbackptr)
|
||||
{
|
||||
initialize_socket(s);
|
||||
strncpy(s->url, url, sizeof(s->url));
|
||||
s->postdata = postdata;
|
||||
s->postdatalen = postdatalen;
|
||||
s->content_type = content_type;
|
||||
|
||||
s->callback = callback;
|
||||
s->callbackptr = callbackptr;
|
||||
|
||||
s->did_tcp_connect = 0;
|
||||
|
||||
list_add(socketlist, s);
|
||||
|
||||
return start_request(s);
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
int
|
||||
http_socket_close(struct http_socket *socket)
|
||||
{
|
||||
struct http_socket *s;
|
||||
for(s = list_head(socketlist);
|
||||
s != NULL;
|
||||
s = list_item_next(s)) {
|
||||
if(s == socket) {
|
||||
tcp_socket_close(&s->s);
|
||||
removesocket(s);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
void
|
||||
http_socket_set_proxy(struct http_socket *s,
|
||||
const uip_ipaddr_t *addr, uint16_t port)
|
||||
{
|
||||
uip_ipaddr_copy(&s->proxy_addr, addr);
|
||||
s->proxy_port = port;
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
121
core/net/http-socket/http-socket.h
Normal file
121
core/net/http-socket/http-socket.h
Normal file
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* Copyright (c) 2013, 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 HTTP_SOCKET_H
|
||||
#define HTTP_SOCKET_H
|
||||
|
||||
#include "tcp-socket.h"
|
||||
|
||||
struct http_socket;
|
||||
|
||||
typedef enum {
|
||||
HTTP_SOCKET_ERR,
|
||||
HTTP_SOCKET_OK,
|
||||
HTTP_SOCKET_HEADER,
|
||||
HTTP_SOCKET_DATA,
|
||||
HTTP_SOCKET_CLOSED,
|
||||
HTTP_SOCKET_TIMEDOUT,
|
||||
HTTP_SOCKET_ABORTED,
|
||||
HTTP_SOCKET_HOSTNAME_NOT_FOUND,
|
||||
} http_socket_event_t;
|
||||
|
||||
struct http_socket_header {
|
||||
uint16_t status_code;
|
||||
int64_t content_length;
|
||||
struct {
|
||||
int64_t first_byte_pos;
|
||||
int64_t last_byte_pos;
|
||||
int64_t instance_length;
|
||||
} content_range;
|
||||
};
|
||||
|
||||
typedef void (* http_socket_callback_t)(struct http_socket *s,
|
||||
void *ptr,
|
||||
http_socket_event_t ev,
|
||||
const uint8_t *data,
|
||||
uint16_t datalen);
|
||||
|
||||
#define MAX(n, m) (((n) < (m)) ? (m) : (n))
|
||||
|
||||
#define HTTP_SOCKET_INPUTBUFSIZE UIP_TCP_MSS
|
||||
#define HTTP_SOCKET_OUTPUTBUFSIZE MAX(UIP_TCP_MSS, 128)
|
||||
|
||||
#define HTTP_SOCKET_URLLEN 128
|
||||
|
||||
#define HTTP_SOCKET_TIMEOUT ((2 * 60 + 30) * CLOCK_SECOND)
|
||||
|
||||
struct http_socket {
|
||||
struct http_socket *next;
|
||||
struct tcp_socket s;
|
||||
uip_ipaddr_t proxy_addr;
|
||||
uint16_t proxy_port;
|
||||
int64_t pos;
|
||||
uint64_t length;
|
||||
const uint8_t *postdata;
|
||||
uint16_t postdatalen;
|
||||
http_socket_callback_t callback;
|
||||
void *callbackptr;
|
||||
int did_tcp_connect;
|
||||
char url[HTTP_SOCKET_URLLEN];
|
||||
uint8_t inputbuf[HTTP_SOCKET_INPUTBUFSIZE];
|
||||
uint8_t outputbuf[HTTP_SOCKET_OUTPUTBUFSIZE];
|
||||
|
||||
struct etimer timeout_timer;
|
||||
uint8_t timeout_timer_started;
|
||||
struct pt pt, headerpt;
|
||||
int header_chars;
|
||||
char header_field[15];
|
||||
struct http_socket_header header;
|
||||
uint8_t header_received;
|
||||
uint64_t bodylen;
|
||||
const char *content_type;
|
||||
};
|
||||
|
||||
void http_socket_init(struct http_socket *s);
|
||||
|
||||
int http_socket_get(struct http_socket *s, const char *url,
|
||||
int64_t pos, uint64_t length,
|
||||
http_socket_callback_t callback,
|
||||
void *callbackptr);
|
||||
|
||||
int http_socket_post(struct http_socket *s, const char *url,
|
||||
const void *postdata,
|
||||
uint16_t postdatalen,
|
||||
const char *content_type,
|
||||
http_socket_callback_t callback,
|
||||
void *callbackptr);
|
||||
|
||||
int http_socket_close(struct http_socket *socket);
|
||||
|
||||
void http_socket_set_proxy(struct http_socket *s,
|
||||
const uip_ipaddr_t *addr, uint16_t port);
|
||||
|
||||
|
||||
#endif /* HTTP_SOCKET_H */
|
7
examples/http-socket/Makefile
Normal file
7
examples/http-socket/Makefile
Normal file
|
@ -0,0 +1,7 @@
|
|||
all: http-example
|
||||
CONTIKI=../..
|
||||
MODULES += core/net/http-socket
|
||||
|
||||
include $(CONTIKI)/Makefile.include
|
||||
|
||||
|
62
examples/http-socket/http-example.c
Normal file
62
examples/http-socket/http-example.c
Normal file
|
@ -0,0 +1,62 @@
|
|||
#include "contiki-net.h"
|
||||
#include "http-socket.h"
|
||||
#include "ip64-addr.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
static struct http_socket s;
|
||||
static int bytes_received = 0;
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
PROCESS(http_example_process, "HTTP Example");
|
||||
AUTOSTART_PROCESSES(&http_example_process);
|
||||
/*---------------------------------------------------------------------------*/
|
||||
static void
|
||||
callback(struct http_socket *s, void *ptr,
|
||||
http_socket_event_t e,
|
||||
const uint8_t *data, uint16_t datalen)
|
||||
{
|
||||
if(e == HTTP_SOCKET_ERR) {
|
||||
printf("HTTP socket error\n");
|
||||
} else if(e == HTTP_SOCKET_TIMEDOUT) {
|
||||
printf("HTTP socket error: timed out\n");
|
||||
} else if(e == HTTP_SOCKET_ABORTED) {
|
||||
printf("HTTP socket error: aborted\n");
|
||||
} else if(e == HTTP_SOCKET_HOSTNAME_NOT_FOUND) {
|
||||
printf("HTTP socket error: hostname not found\n");
|
||||
} else if(e == HTTP_SOCKET_CLOSED) {
|
||||
printf("HTTP socket closed, %d bytes received\n", bytes_received);
|
||||
} else if(e == HTTP_SOCKET_DATA) {
|
||||
bytes_received += datalen;
|
||||
printf("HTTP socket received %d bytes of data\n", datalen);
|
||||
}
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
||||
PROCESS_THREAD(http_example_process, ev, data)
|
||||
{
|
||||
static struct etimer et;
|
||||
uip_ip4addr_t ip4addr;
|
||||
uip_ip6addr_t ip6addr;
|
||||
|
||||
PROCESS_BEGIN();
|
||||
|
||||
uip_ipaddr(&ip4addr, 8,8,8,8);
|
||||
ip64_addr_4to6(&ip4addr, &ip6addr);
|
||||
uip_nameserver_update(&ip6addr, UIP_NAMESERVER_INFINITE_LIFETIME);
|
||||
|
||||
etimer_set(&et, CLOCK_SECOND * 60);
|
||||
PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&et));
|
||||
|
||||
http_socket_init(&s);
|
||||
http_socket_get(&s, "http://www.contiki-os.org/", 0, 0,
|
||||
callback, NULL);
|
||||
|
||||
etimer_set(&et, CLOCK_SECOND);
|
||||
while(1) {
|
||||
PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&et));
|
||||
etimer_reset(&et);
|
||||
}
|
||||
|
||||
PROCESS_END();
|
||||
}
|
||||
/*---------------------------------------------------------------------------*/
|
Loading…
Reference in a new issue