|
|
@ -10,6 +10,7 @@ |
|
|
|
// FIXME: sprintf->snprintf everywhere.
|
|
|
|
// FIXME: sprintf->snprintf everywhere.
|
|
|
|
|
|
|
|
|
|
|
|
#include <esp8266.h> |
|
|
|
#include <esp8266.h> |
|
|
|
|
|
|
|
#include <httpd.h> |
|
|
|
|
|
|
|
|
|
|
|
#include "httpclient.h" |
|
|
|
#include "httpclient.h" |
|
|
|
#include <limits.h> |
|
|
|
#include <limits.h> |
|
|
@ -27,6 +28,7 @@ typedef struct { |
|
|
|
httpclient_cb user_callback; |
|
|
|
httpclient_cb user_callback; |
|
|
|
int timeout; |
|
|
|
int timeout; |
|
|
|
os_timer_t timeout_timer; |
|
|
|
os_timer_t timeout_timer; |
|
|
|
|
|
|
|
http_method method; |
|
|
|
} request_args; |
|
|
|
} request_args; |
|
|
|
|
|
|
|
|
|
|
|
static char *FLASH_FN esp_strdup(const char *str) |
|
|
|
static char *FLASH_FN esp_strdup(const char *str) |
|
|
@ -247,14 +249,18 @@ static void FLASH_FN connect_callback(void * arg) |
|
|
|
espconn_regist_recvcb(conn, receive_callback); |
|
|
|
espconn_regist_recvcb(conn, receive_callback); |
|
|
|
espconn_regist_sentcb(conn, sent_callback); |
|
|
|
espconn_regist_sentcb(conn, sent_callback); |
|
|
|
|
|
|
|
|
|
|
|
const char *method = "GET"; |
|
|
|
|
|
|
|
char post_headers[32] = ""; |
|
|
|
char post_headers[32] = ""; |
|
|
|
|
|
|
|
|
|
|
|
if (req->post_data != NULL) { // If there is data this is a POST request.
|
|
|
|
if (req->post_data != NULL) { // If there is data this is a POST request.
|
|
|
|
method = "POST"; |
|
|
|
|
|
|
|
sprintf(post_headers, "Content-Length: %d\r\n", strlen(req->post_data)); |
|
|
|
sprintf(post_headers, "Content-Length: %d\r\n", strlen(req->post_data)); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (req->method == HTTP_GET) { |
|
|
|
|
|
|
|
req->method = HTTP_POST; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const char* method = http_method_str(req->method); |
|
|
|
|
|
|
|
|
|
|
|
if (req->headers == NULL) { /* Avoid NULL pointer, it may cause exception */ |
|
|
|
if (req->headers == NULL) { /* Avoid NULL pointer, it may cause exception */ |
|
|
|
req->headers = (char *)malloc(sizeof(char)); |
|
|
|
req->headers = (char *)malloc(sizeof(char)); |
|
|
|
req->headers[0] = '\0'; |
|
|
|
req->headers[0] = '\0'; |
|
|
@ -438,68 +444,30 @@ static void FLASH_FN dns_callback(const char *hostname, ip_addr_t *addr, void *a |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void FLASH_FN http_raw_request(const char *hostname, int port, bool secure, const char *path, const char *post_data, const char *headers, httpclient_cb user_callback) |
|
|
|
bool FLASH_FN http_request( |
|
|
|
|
|
|
|
const char *url, |
|
|
|
|
|
|
|
http_method method, |
|
|
|
|
|
|
|
const char *body, |
|
|
|
|
|
|
|
const char *headers, |
|
|
|
|
|
|
|
httpclient_cb user_callback) |
|
|
|
{ |
|
|
|
{ |
|
|
|
info("HTTP request: %s:%d%s", hostname, port, path); |
|
|
|
// --- prepare port, secure... ---
|
|
|
|
|
|
|
|
|
|
|
|
request_args * req = (request_args *)malloc(sizeof(request_args)); |
|
|
|
|
|
|
|
req->hostname = esp_strdup(hostname); |
|
|
|
|
|
|
|
req->path = esp_strdup(path); |
|
|
|
|
|
|
|
req->port = port; |
|
|
|
|
|
|
|
req->secure = secure; |
|
|
|
|
|
|
|
req->headers = esp_strdup(headers); |
|
|
|
|
|
|
|
req->post_data = esp_strdup(post_data); |
|
|
|
|
|
|
|
req->buffer_size = 1; |
|
|
|
|
|
|
|
req->buffer = (char *)malloc(1); |
|
|
|
|
|
|
|
req->buffer[0] = '\0'; // Empty string.
|
|
|
|
|
|
|
|
req->user_callback = user_callback; |
|
|
|
|
|
|
|
req->timeout = HTTP_REQUEST_TIMEOUT_MS; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ip_addr_t addr; |
|
|
|
|
|
|
|
err_t error = espconn_gethostbyname((struct espconn *)req, // It seems we don't need a real espconn pointer here.
|
|
|
|
|
|
|
|
hostname, &addr, dns_callback); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (error == ESPCONN_INPROGRESS) { |
|
|
|
|
|
|
|
dbg("DNS pending"); |
|
|
|
|
|
|
|
} else if (error == ESPCONN_OK) { |
|
|
|
|
|
|
|
// Already in the local names table (or hostname was an IP address), execute the callback ourselves.
|
|
|
|
|
|
|
|
dns_callback(hostname, &addr, req); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
if (error == ESPCONN_ARG) { |
|
|
|
|
|
|
|
error("DNS arg error %s", hostname); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
error("DNS error code %d", error); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
dns_callback(hostname, NULL, req); // Handle all DNS errors the same way.
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
|
|
* Parse an URL of the form http://host:port/path
|
|
|
|
|
|
|
|
* <host> can be a hostname or an IP address |
|
|
|
|
|
|
|
* <port> is optional |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
void FLASH_FN http_post(const char *url, const char *post_data, const char *headers, httpclient_cb user_callback) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// FIXME: handle HTTP auth with http://user:pass@host/
|
|
|
|
// FIXME: handle HTTP auth with http://user:pass@host/
|
|
|
|
// FIXME: get rid of the #anchor part if present.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
char hostname[128] = ""; |
|
|
|
char hostname[128] = ""; |
|
|
|
int port = 80; |
|
|
|
int port = 80; |
|
|
|
bool secure = false; |
|
|
|
bool secure = false; |
|
|
|
|
|
|
|
|
|
|
|
bool is_http = strstarts(url, "http://"); |
|
|
|
if (strstarts(url, "http://")) |
|
|
|
bool is_https = strstarts(url, "https://"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (is_http) |
|
|
|
|
|
|
|
url += strlen("http://"); // Get rid of the protocol.
|
|
|
|
url += strlen("http://"); // Get rid of the protocol.
|
|
|
|
else if (is_https) { |
|
|
|
else if (strstarts(url, "https://")) { |
|
|
|
port = 443; |
|
|
|
port = 443; |
|
|
|
secure = true; |
|
|
|
secure = true; |
|
|
|
url += strlen("https://"); // Get rid of the protocol.
|
|
|
|
url += strlen("https://"); // Get rid of the protocol.
|
|
|
|
} else { |
|
|
|
} else { |
|
|
|
error("URL is not HTTP or HTTPS %s", url); |
|
|
|
error("Invalid URL protocol: %s", url); |
|
|
|
return; |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
char *path = strchr(url, '/'); |
|
|
|
char *path = strchr(url, '/'); |
|
|
@ -513,38 +481,91 @@ void FLASH_FN http_post(const char *url, const char *post_data, const char *head |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (colon == NULL) { // The port is not present.
|
|
|
|
if (colon == NULL) { // The port is not present.
|
|
|
|
memcpy(hostname, url, path - url); |
|
|
|
memcpy(hostname, url, (size_t)(path - url)); |
|
|
|
hostname[path - url] = '\0'; |
|
|
|
hostname[path - url] = '\0'; |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
port = atoi(colon + 1); |
|
|
|
port = atoi(colon + 1); |
|
|
|
if (port == 0) { |
|
|
|
if (port == 0) { |
|
|
|
error("Port error %s\n", url); |
|
|
|
error("Port error %s\n", url); |
|
|
|
return; |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
memcpy(hostname, url, colon - url); |
|
|
|
memcpy(hostname, url, (size_t)(colon - url)); |
|
|
|
hostname[colon - url] = '\0'; |
|
|
|
hostname[colon - url] = '\0'; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (path[0] == '\0') { // Empty path is not allowed.
|
|
|
|
if (path[0] == '\0') { // Empty path is not allowed.
|
|
|
|
path = "/"; |
|
|
|
path = "/"; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
http_raw_request(hostname, port, secure, path, post_data, headers, user_callback); |
|
|
|
// ---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
info("HTTP request: %s:%d%s", hostname, port, path); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
request_args * req = (request_args *)malloc(sizeof(request_args)); |
|
|
|
|
|
|
|
req->hostname = esp_strdup(hostname); |
|
|
|
|
|
|
|
req->path = esp_strdup(path); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// remove #anchor
|
|
|
|
|
|
|
|
char *hash = strchr(req->path, '#'); |
|
|
|
|
|
|
|
if (hash != NULL) *hash = '\0'; // remove the hash part
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
req->port = port; |
|
|
|
|
|
|
|
req->secure = secure; |
|
|
|
|
|
|
|
req->headers = esp_strdup(headers); |
|
|
|
|
|
|
|
req->post_data = esp_strdup(body); |
|
|
|
|
|
|
|
req->buffer_size = 1; |
|
|
|
|
|
|
|
req->buffer = (char *)malloc(1); |
|
|
|
|
|
|
|
req->buffer[0] = '\0'; // Empty string.
|
|
|
|
|
|
|
|
req->user_callback = user_callback; |
|
|
|
|
|
|
|
req->timeout = HTTP_REQUEST_TIMEOUT_MS; |
|
|
|
|
|
|
|
req->method = method; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ip_addr_t addr; |
|
|
|
|
|
|
|
err_t error = espconn_gethostbyname((struct espconn *)req, // It seems we don't need a real espconn pointer here.
|
|
|
|
|
|
|
|
hostname, &addr, dns_callback); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (error == ESPCONN_INPROGRESS) { |
|
|
|
|
|
|
|
dbg("DNS pending"); |
|
|
|
|
|
|
|
} else if (error == ESPCONN_OK) { |
|
|
|
|
|
|
|
// Already in the local names table (or hostname was an IP address), execute the callback ourselves.
|
|
|
|
|
|
|
|
dns_callback(hostname, &addr, req); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
if (error == ESPCONN_ARG) { |
|
|
|
|
|
|
|
error("DNS arg error %s", hostname); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
error("DNS error code %d", error); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
dns_callback(hostname, NULL, req); // Handle all DNS errors the same way.
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return true; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void FLASH_FN http_get(const char *url, const char *headers, httpclient_cb user_callback) |
|
|
|
|
|
|
|
|
|
|
|
bool FLASH_FN http_post(const char *url, const char *body, const char *headers, httpclient_cb user_callback) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
return http_request(url, HTTP_POST, body, headers, user_callback); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool http_get(const char *url, const char *headers, httpclient_cb user_callback) |
|
|
|
{ |
|
|
|
{ |
|
|
|
http_post(url, NULL, headers, user_callback); |
|
|
|
return http_request(url, HTTP_GET, NULL, headers, user_callback); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool http_put(const char *url, const char *body, const char *headers, httpclient_cb user_callback) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
return http_request(url, HTTP_PUT, body, headers, user_callback); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void FLASH_FN http_callback_example(char *response_body, int http_status, char *response_headers, int body_size) |
|
|
|
void FLASH_FN http_callback_example(char *response_body, int http_status, char *response_headers, int body_size) |
|
|
|
{ |
|
|
|
{ |
|
|
|
dbg("http_status=%d", http_status); |
|
|
|
dbg("Response: code %d", http_status); |
|
|
|
if (http_status != HTTP_STATUS_GENERIC_ERROR) { |
|
|
|
if (http_status != HTTP_STATUS_GENERIC_ERROR) { |
|
|
|
dbg("strlen(headers)=%d", (int)strlen(response_headers)); |
|
|
|
dbg("len(headers) = %d, len(body) = %d", (int)strlen(response_headers), body_size); |
|
|
|
dbg("body_size=%d", body_size); |
|
|
|
dbg("body: %s<EOF>", response_body); // FIXME: this does not handle binary data.
|
|
|
|
dbg("body=%s<EOF>", response_body); // FIXME: this does not handle binary data.
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|