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