working xively report

Former-commit-id: eea29fc09155b1be61b49b02e319554f1040991a
master
Ondřej Hruška 9 years ago
parent c883c02788
commit f64bbb94d7
  1. 145
      esphttpclient/httpclient.c
  2. 32
      esphttpclient/httpclient.h
  3. 24
      libesphttpd/core/httpd.c
  4. 17
      libesphttpd/include/httpd.h
  5. 24
      user/reporting.c

@ -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.
} }
} }

@ -11,6 +11,7 @@
#define HTTPCLIENT_H #define HTTPCLIENT_H
#include <esp8266.h> #include <esp8266.h>
#include <httpd.h>
#define HTTP_STATUS_GENERIC_ERROR -1 // In case of TCP or DNS error the callback is called with this status. #define HTTP_STATUS_GENERIC_ERROR -1 // In case of TCP or DNS error the callback is called with this status.
#define BUFFER_SIZE_MAX 5000 // Size of http responses that will cause an error. #define BUFFER_SIZE_MAX 5000 // Size of http responses that will cause an error.
@ -30,31 +31,36 @@
typedef void (* httpclient_cb)(char *response_body, int http_status, char *response_headers, int body_size); typedef void (* httpclient_cb)(char *response_body, int http_status, char *response_headers, int body_size);
/** /**
* Download a web page from its URL. * @brief Download a web page from its URL.
*
* Try: * Try:
* http_get("http://wtfismyip.com/text", http_callback_example); * http_get("http://wtfismyip.com/text", http_callback_example);
*/ */
void http_get(const char * url, const char *headers, httpclient_cb user_callback); bool http_get(const char * url, const char *headers, httpclient_cb user_callback);
/** /**
* Post data to a web form. * @brief Post data to a web form.
*
* The data should be encoded as application/x-www-form-urlencoded. * The data should be encoded as application/x-www-form-urlencoded.
*
* Try: * Try:
* http_post("http://httpbin.org/post", "first_word=hello&second_word=world", http_callback_example); * http_post("http://httpbin.org/post", "first_word=hello&second_word=world", http_callback_example);
*/ */
void http_post(const char *url, const char *post_data, const char *headers, httpclient_cb user_callback); bool http_post(const char *url, const char *post_data, const char *headers, httpclient_cb user_callback);
/** Like POST, but with the PUT method. */
bool http_put(const char *url, const char *body, const char *headers, httpclient_cb user_callback);
/** /**
* Call this function to skip URL parsing if the arguments are already in separate variables. * @brief Send a HTTP request
* @param url : protocol://host[:port][/path]
* @param method : get, post, ...
* @param body : request body. If GET & body != NULL, method changes to POST.
* @param headers : additional headers string. Must end with \r\n
* @param user_callback : callback for parsing the response
* @return success (in sending)
*/ */
void http_raw_request( bool http_request(const char *url, http_method method, const char *body, const char *headers, httpclient_cb user_callback);
const char * hostname,
int port,
bool secure,
const char *path,
const char *post_data,
const char *headers,
httpclient_cb user_callback);
/** /**
* Output on the UART. * Output on the UART.

@ -115,6 +115,20 @@ const char ICACHE_FLASH_ATTR *httpdGetMimetype(const char *filepath) {
return mimeTypes[i].mimetype; return mimeTypes[i].mimetype;
} }
const char* ICACHE_FLASH_ATTR http_method_str(http_method m)
{
switch (m) {
default:
case HTTP_GET: return "GET";
case HTTP_POST: return "POST";
case HTTP_OPTIONS: return "OPTIONS";
case HTTP_PUT: return "PUT";
case HTTP_DELETE: return "DELETE";
case HTTP_PATCH: return "PATCH";
case HTTP_HEAD: return "HEAD";
}
}
//Looks up the connData info for a specific connection //Looks up the connData info for a specific connection
static HttpdConnData ICACHE_FLASH_ATTR *httpdFindConnData(ConnTypePtr conn, const char *remIp, int remPort) { static HttpdConnData ICACHE_FLASH_ATTR *httpdFindConnData(ConnTypePtr conn, const char *remIp, int remPort) {
for (int i=0; i<HTTPD_MAX_CONNECTIONS; i++) { for (int i=0; i<HTTPD_MAX_CONNECTIONS; i++) {
@ -521,7 +535,7 @@ static void ICACHE_FLASH_ATTR httpdProcessRequest(HttpdConnData *conn) {
return; //Shouldn't happen return; //Shouldn't happen
} }
if (conn->requestType == HTTPD_METHOD_OPTIONS /*&& conn->priv->chunkHdr[0] != 0*/) { if (conn->requestType == HTTP_OPTIONS /*&& conn->priv->chunkHdr[0] != 0*/) {
// we have a CORS preflight // we have a CORS preflight
httpdStartResponse(conn, 200); httpdStartResponse(conn, 200);
httpdHeader(conn, "Access-Control-Allow-Headers", conn->priv->corsToken); httpdHeader(conn, "Access-Control-Allow-Headers", conn->priv->corsToken);
@ -589,16 +603,16 @@ static void ICACHE_FLASH_ATTR httpdParseHeader(char *h, HttpdConnData *conn) {
char firstLine=0; char firstLine=0;
if (strstarts(h, "GET ")) { if (strstarts(h, "GET ")) {
conn->requestType = HTTPD_METHOD_GET; conn->requestType = HTTP_GET;
firstLine=1; firstLine=1;
} else if (strstarts(h, "POST ")) { } else if (strstarts(h, "POST ")) {
conn->requestType = HTTPD_METHOD_POST; conn->requestType = HTTP_POST;
firstLine=1; firstLine=1;
} else if (strstarts(h, "PUT ")) { } else if (strstarts(h, "PUT ")) {
conn->requestType = HTTPD_METHOD_PUT; conn->requestType = HTTP_PUT;
firstLine=1; firstLine=1;
} else if (strstarts(h, "OPTIONS ")) { } else if (strstarts(h, "OPTIONS ")) {
conn->requestType = HTTPD_METHOD_OPTIONS; conn->requestType = HTTP_OPTIONS;
firstLine=1; firstLine=1;
} else if (strstarts(h, "Host:")) { } else if (strstarts(h, "Host:")) {
i=5; i=5;

@ -13,15 +13,16 @@ typedef enum {
} httpd_cgi_state; } httpd_cgi_state;
typedef enum { typedef enum {
HTTPD_METHOD_GET = 1, HTTP_GET = 1,
HTTPD_METHOD_POST = 2, HTTP_POST = 2,
HTTPD_METHOD_OPTIONS = 3, HTTP_OPTIONS = 3,
HTTPD_METHOD_PUT = 4, HTTP_PUT = 4,
HTTPD_METHOD_DELETE = 5, HTTP_DELETE = 5,
HTTPD_METHOD_PATCH = 6, HTTP_PATCH = 6,
HTTPD_METHOD_HEAD = 7, HTTP_HEAD = 7,
} http_method; } http_method;
typedef struct HttpdPriv HttpdPriv; typedef struct HttpdPriv HttpdPriv;
typedef struct HttpdConnData HttpdConnData; typedef struct HttpdConnData HttpdConnData;
typedef struct HttpdPostData HttpdPostData; typedef struct HttpdPostData HttpdPostData;
@ -95,7 +96,7 @@ typedef struct {
#define ROUTE_END() {NULL, NULL, NULL, NULL} #define ROUTE_END() {NULL, NULL, NULL, NULL}
const char *http_method_str(http_method m);
httpd_cgi_state cgiRedirect(HttpdConnData *connData); httpd_cgi_state cgiRedirect(HttpdConnData *connData);
httpd_cgi_state cgiRedirectToHostname(HttpdConnData *connData); httpd_cgi_state cgiRedirectToHostname(HttpdConnData *connData);

@ -2,6 +2,7 @@
#include "datalink.h" #include "datalink.h"
#include "serial.h" #include "serial.h"
#include "httpclient.h" #include "httpclient.h"
#include "ftoa.h"
#define RPT_CONF_MAGIC 0x24C595D5 #define RPT_CONF_MAGIC 0x24C595D5
@ -114,9 +115,28 @@ static void FLASH_FN compare_ref_cb(SBMP_Endpoint *ep, SBMP_Datagram *dg, void *
static void FLASH_FN do_send_report(void) static void FLASH_FN do_send_report(void)
{ {
info("Sending report..."); info("Sending report...");
char buf[100];
char *bb = buf;
char url_buf[200];
char hdrs_buf[100];
switch (rpt_conf.service) { switch (rpt_conf.service) {
case RPT_XIVELY: case RPT_XIVELY:;
warn("------- TODO: REPORT TO XIVELY -------"); bb += sprintf(bb, "deviation,");
bb += my_ftoa(bb, rpt_result.deviation, 2);
bb += sprintf(bb, "\nI_rms,");
bb += my_ftoa(bb, rpt_result.i_rms, 2);
// URL
sprintf(url_buf, "http://api.xively.com/v2/feeds/%s.csv", rpt_conf.feed);
// Key
sprintf(hdrs_buf, "X-ApiKey: %s\r\n", rpt_conf.key);
http_put(url_buf, buf, hdrs_buf, http_callback_example);
break; break;
case RPT_THINGSPEAK: case RPT_THINGSPEAK:

Loading…
Cancel
Save