220 lines
5.6 KiB
C
220 lines
5.6 KiB
C
|
#include <stdlib.h>
|
||
|
#include <stdint.h>
|
||
|
#include <stdio.h>
|
||
|
#include <string.h>
|
||
|
#include <unistd.h>
|
||
|
#include <ctype.h>
|
||
|
|
||
|
#include "lwip/err.h"
|
||
|
#include "lwip/sockets.h"
|
||
|
#include "lwip/sys.h"
|
||
|
#include "lwip/netdb.h"
|
||
|
#include "lwip/dns.h"
|
||
|
|
||
|
#include "http_buffered_client.h"
|
||
|
|
||
|
#define MAX_REQUEST_SIZE (152 / sizeof(uint32_t))
|
||
|
#define vTaskDelayMs(ms) vTaskDelay((ms) / portTICK_PERIOD_MS)
|
||
|
|
||
|
typedef void (*handle_http_token)(char *);
|
||
|
|
||
|
struct http_token_table {
|
||
|
char * token;
|
||
|
handle_http_token http_tock_cb;
|
||
|
};
|
||
|
|
||
|
// Response struct
|
||
|
struct HTTP_response {
|
||
|
unsigned int response_code;
|
||
|
unsigned int length;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* \addtogroup http_buffer_client_internal
|
||
|
* Http Request
|
||
|
*/
|
||
|
const char *req =
|
||
|
"GET %s HTTP/1.1\r\n"
|
||
|
"Host: %s \r\n"
|
||
|
"User-Agent: esp-open-rtos/0.1 esp8266\r\n"
|
||
|
"Connection: close\r\n"
|
||
|
"\r\n";
|
||
|
|
||
|
static uint32_t request[MAX_REQUEST_SIZE];
|
||
|
|
||
|
static const struct addrinfo hints = {
|
||
|
.ai_family = AF_UNSPEC,
|
||
|
.ai_socktype = SOCK_STREAM,
|
||
|
};
|
||
|
|
||
|
static struct HTTP_response http_reponse;
|
||
|
|
||
|
// HTTP Header Token, add here function and then register it in HTTP Table callback
|
||
|
static void http_handle_cb_ContentLength(char *token)
|
||
|
{
|
||
|
token += 16; // strlen("Content-Length:"), skip useless part
|
||
|
while (*token) {
|
||
|
if (isdigit((int) *token))
|
||
|
http_reponse.length = (unsigned int) strtol(token, &token, 10);
|
||
|
else
|
||
|
token++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static inline void parse_http_header_HTTP_STATUS(char *token)
|
||
|
{
|
||
|
token += 8; // Skip HTTP/1.0
|
||
|
|
||
|
while (*token) {
|
||
|
if (isdigit((int) *token))
|
||
|
http_reponse.response_code = (unsigned int) strtol(token, &token, 10);
|
||
|
else
|
||
|
token++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// HTTP Token Hanling callback
|
||
|
struct http_token_table HTTP_HEADER_TOKEN[] = {
|
||
|
{ .token = "Content-Length", .http_tock_cb = http_handle_cb_ContentLength },
|
||
|
};
|
||
|
|
||
|
static inline void parse_http_header(char *header)
|
||
|
{
|
||
|
char *str1, *str2, *token, *subtoken, *saveptr1, *saveptr2;
|
||
|
const char line_split[] = "\r\n", sub_chart[] = ":";
|
||
|
unsigned int j, i;
|
||
|
|
||
|
for (j = 1, str1 = header;; j++, str1 = NULL) {
|
||
|
token = strtok_r(str1, line_split, &saveptr1);
|
||
|
if (token == NULL)
|
||
|
break;
|
||
|
|
||
|
str2 = token;
|
||
|
subtoken = strtok_r(str2, sub_chart, &saveptr2);
|
||
|
if (subtoken == NULL)
|
||
|
break;
|
||
|
|
||
|
if (j == 1) {
|
||
|
// Is HTTP Header, response, HTTP Version and status
|
||
|
parse_http_header_HTTP_STATUS(token);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < sizeof(HTTP_HEADER_TOKEN) / sizeof(struct http_token_table); i++)
|
||
|
if (!strcmp(subtoken, HTTP_HEADER_TOKEN[i].token))
|
||
|
HTTP_HEADER_TOKEN[i].http_tock_cb(token);
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
HTTP_Client_State HttpClient_dowload(Http_client_info *info)
|
||
|
{
|
||
|
struct addrinfo *res;
|
||
|
unsigned int tot_http_pdu_rd, read_byte, full;
|
||
|
int err, sock;
|
||
|
char *wrt_ptr;
|
||
|
|
||
|
err = getaddrinfo(info->server, info->port, &hints, &res);
|
||
|
|
||
|
if (err != 0 || res == NULL) {
|
||
|
if (res)
|
||
|
freeaddrinfo(res);
|
||
|
return HTTP_DNS_LOOKUP_FALLIED;
|
||
|
}
|
||
|
|
||
|
sock = socket(res->ai_family, res->ai_socktype, 0);
|
||
|
if (sock < 0) {
|
||
|
freeaddrinfo(res);
|
||
|
return HTTP_SOCKET_ALLOCATION_FALLIED;
|
||
|
}
|
||
|
|
||
|
if (connect(sock, res->ai_addr, res->ai_addrlen) != 0) {
|
||
|
close(sock);
|
||
|
freeaddrinfo(res);
|
||
|
return HTTP_SOCKET_CONNECTION_FALLIED;
|
||
|
}
|
||
|
|
||
|
// Release address memory
|
||
|
freeaddrinfo(res);
|
||
|
|
||
|
// Alloc memory for request
|
||
|
sprintf((char *) request, req, info->path, info->server);
|
||
|
if (write(sock, (char *) request, strlen((char *) request)) < 0) {
|
||
|
close(sock);
|
||
|
return HTTP_REQUEST_SEND_FALLIED;
|
||
|
}
|
||
|
|
||
|
tot_http_pdu_rd = 0;
|
||
|
wrt_ptr = info->buffer;
|
||
|
full = 0;
|
||
|
|
||
|
// Ping wdog
|
||
|
vTaskDelayMs(250);
|
||
|
do {
|
||
|
int free_buff_space;
|
||
|
|
||
|
free_buff_space = info->buffer_size - full;
|
||
|
read_byte = read(sock, wrt_ptr, free_buff_space);
|
||
|
|
||
|
// Update buffer property
|
||
|
wrt_ptr += read_byte;
|
||
|
full += read_byte;
|
||
|
|
||
|
if (tot_http_pdu_rd == 0) {
|
||
|
// Is fist chunk, then it contains http header, parse it.
|
||
|
unsigned int header_len, pdu_size;
|
||
|
char *header, *pdu;
|
||
|
|
||
|
pdu = strstr(info->buffer, "\r\n\r\n");
|
||
|
|
||
|
if (pdu != NULL)
|
||
|
pdu += 4; // Offset by 4 bytes to start of content
|
||
|
else // Not all HTTP Header has been read, then continue read
|
||
|
continue;
|
||
|
|
||
|
header_len = pdu - info->buffer + 4;
|
||
|
|
||
|
header = malloc(header_len + 1); // NULL string terminator
|
||
|
|
||
|
memset(header, 0, header_len + 1);
|
||
|
memcpy(header, info->buffer, header_len);
|
||
|
|
||
|
parse_http_header(header);
|
||
|
// Release useless memory
|
||
|
free(header);
|
||
|
|
||
|
// Move memory
|
||
|
pdu_size = wrt_ptr - pdu;
|
||
|
|
||
|
memmove(info->buffer, pdu, pdu_size);
|
||
|
wrt_ptr = (info->buffer + pdu_size);
|
||
|
|
||
|
full = pdu_size;
|
||
|
tot_http_pdu_rd = pdu_size;
|
||
|
|
||
|
if (http_reponse.response_code != HTTP_OK)
|
||
|
goto err_label;
|
||
|
|
||
|
continue;
|
||
|
}
|
||
|
tot_http_pdu_rd += read_byte;
|
||
|
|
||
|
if (full == info->buffer_size) {
|
||
|
info->buffer_full_cb(info->buffer, full);
|
||
|
|
||
|
memset(info->buffer, 0, info->buffer_size);
|
||
|
wrt_ptr = info->buffer;
|
||
|
full = 0;
|
||
|
vTaskDelayMs(50);
|
||
|
}
|
||
|
} while (read_byte > 0);
|
||
|
|
||
|
info->final_cb(info->buffer, full);
|
||
|
if (tot_http_pdu_rd != http_reponse.length)
|
||
|
http_reponse.response_code = HTTP_DOWLOAD_SIZE_NOT_MATCH;
|
||
|
|
||
|
err_label:
|
||
|
close(sock);
|
||
|
return http_reponse.response_code;
|
||
|
} /* HttpClient_dowload */
|