diff --git a/Makefile b/Makefile index be82a23..242fe99 100644 --- a/Makefile +++ b/Makefile @@ -55,11 +55,13 @@ APPGEN ?= $(SDK_BASE)/tools/gen_appbin.py TARGET = httpd # which modules (subdirectories) of the project to include in compiling -MODULES = user sbmp/library -EXTRA_INCDIR = include libesphttpd/include sbmp/library user/ +MODULES = user sbmp/library esphttpclient +EXTRA_INCDIR = include libesphttpd/include sbmp/library user esphttpclient # libraries used in this project, mainly provided by the SDK -LIBS = c gcc hal phy pp net80211 wpa main lwip crypto +LIBS = c gcc hal phy pp net80211 wpa main lwip crypto +#ssl + #Add in esphttpd lib LIBS += esphttpd diff --git a/esp_iot_sdk_v1.5.2/include/osapi.h b/esp_iot_sdk_v1.5.2/include/osapi.h index 5cb3485..5cac8dd 100644 --- a/esp_iot_sdk_v1.5.2/include/osapi.h +++ b/esp_iot_sdk_v1.5.2/include/osapi.h @@ -32,6 +32,7 @@ #define os_timer_setfn ets_timer_setfn #define os_sprintf ets_sprintf +#define os_snprintf ets_snprintf #ifdef USE_OPTIMIZE_PRINTF #define os_printf(fmt, ...) do { \ diff --git a/esp_meas.pro b/esp_meas.pro index ef8c867..bc698fe 100644 --- a/esp_meas.pro +++ b/esp_meas.pro @@ -9,6 +9,7 @@ INCLUDEPATH = . \ esp_iot_sdk_v1.5.2/include \ include \ user \ + esphttpclient \ libesphttpd/include \ libesphttpd/espfs \ libesphttpd/core \ @@ -56,14 +57,16 @@ SOURCES += \ sbmp/library/payload_parser.c \ user/sampling.c \ user/ftoa.c \ - user/routes.c \ - user/page_status.c \ - user/page_waveform.c \ - user/utils.c \ - sbmp/library/payload_builder.c \ - user/page_about.c \ - user/cgi_reset.c \ - user/cgi_ping.c + user/routes.c \ + user/page_status.c \ + user/page_waveform.c \ + user/utils.c \ + sbmp/library/payload_builder.c \ + user/page_about.c \ + user/cgi_reset.c \ + user/cgi_ping.c \ + esphttpclient/test/httpclient_test.c \ + esphttpclient/httpclient.c HEADERS += \ include/uart_hw.h \ @@ -140,16 +143,18 @@ HEADERS += \ user/sbmp_config.h \ sbmp/library/sbmp_config.example.h \ user/ftoa.h \ - user/routes.h \ - user/page_status.h \ - user/page_waveform.h \ - libesphttpd/include/espmissingprotos.h \ - user/utils.h \ - sbmp/library/payload_builder.h \ - user/page_about.h \ - user/fw_version.h \ - user/cgi_reset.h \ - user/cgi_ping.h + user/routes.h \ + user/page_status.h \ + user/page_waveform.h \ + libesphttpd/include/espmissingprotos.h \ + user/utils.h \ + sbmp/library/payload_builder.h \ + user/page_about.h \ + user/fw_version.h \ + user/cgi_reset.h \ + user/cgi_ping.h \ + esphttpclient/espmissingincludes.h \ + esphttpclient/httpclient.h DISTFILES += \ style.astylerc \ diff --git a/esphttpclient/README.md b/esphttpclient/README.md new file mode 100644 index 0000000..b49d3f6 --- /dev/null +++ b/esphttpclient/README.md @@ -0,0 +1,78 @@ +# esphttpclient + +This is a short library for ESP8266(EX) chips to make HTTP requests. + +## Features + + * Easy to use. + * Supports multiple requests in parallel. + * Supports GET and POST requests. + * Tested with Espressif SDK v1.0.0 + +## Building +If you don't have a toolchain yet, install one with then get Espressif's SDK. + +### The submodule way +If your project looks like esphttpd from Sprite_tm: +```bash +git clone http://git.spritesserver.nl/esphttpd.git/ +cd esphttpd +git submodule add https://github.com/Caerbannog/esphttpclient.git lib/esphttpclient +git submodule update --init +``` + +Now append `lib/esphttpclient` to the following `Makefile` line and you should be ready: +``` +MODULES = driver user lib/esphttpclient +``` +In case you want to use SSL don't forget to add `ssl` to `LIBS` in the `Makefile` +``` +LIBS = c gcc hal pp phy net80211 lwip wpa main ssl +``` + +### The dirty way +Alternatively you could create a simple project: +```bash +git clone https://github.com/esp8266/source-code-examples.git +cd source-code-examples/basic_example +# Set your Wifi credentials in user_config.h +# I could not test this because of the UART baud rate (74880) +``` + +Then download this library and move the files to `user/`: +```bash +git clone https://github.com/Caerbannog/esphttpclient.git +mv esphttpclient/*.* user/ +``` + +## Usage +Include `httpclient.h` from `user_main.c` then call one of these functions: +```c +void http_get(const char * url, const char * headers, http_callback user_callback); +void http_post(const char * url, const char * post_data, const char * headers, http_callback user_callback); + +void http_callback_example(char * response_body, int http_status, char * response_headers, int body_size) +{ + os_printf("http_status=%d\n", http_status); + if (http_status != HTTP_STATUS_GENERIC_ERROR) { + os_printf("strlen(headers)=%d\n", strlen(response_headers)); + os_printf("body_size=%d\n", body_size); + os_printf("body=%s\n", response_body); + } +} +``` + +## Example +The following code performs a single request, then calls `http_callback_example` to display your public IP address. +```c +http_get("http://wtfismyip.com/text", "", http_callback_example); +``` + +The output looks like this: +``` +http_status=200 +strlen(full_response)=244 +body_size=15 +response_body=208.97.177.124 + +``` diff --git a/esphttpclient/httpclient.c b/esphttpclient/httpclient.c new file mode 100644 index 0000000..38b3595 --- /dev/null +++ b/esphttpclient/httpclient.c @@ -0,0 +1,487 @@ +/* + * ---------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * Martin d'Allens wrote this file. As long as you retain + * this notice you can do whatever you want with this stuff. If we meet some day, + * and you think this stuff is worth it, you can buy me a beer in return. + * ---------------------------------------------------------------------------- + */ + +// FIXME: sprintf->snprintf everywhere. + +#include + +#include "httpclient.h" +#include + +// Internal state. +typedef struct { + char *path; + int port; + char *post_data; + char *headers; + char *hostname; + char *buffer; + int buffer_size; + bool secure; + httpclient_cb user_callback; +} request_args; + +static char *FLASH_FN esp_strdup(const char *str) +{ + if (str == NULL) { + return NULL; + } + char *new_str = (char *)malloc(strlen(str) + 1); // 1 for null character + if (new_str == NULL) { + error("esp_strdup: malloc error"); + return NULL; + } + strcpy(new_str, str); + return new_str; +} + +static int FLASH_FN esp_isupper(char c) +{ + return (c >= 'A' && c <= 'Z'); +} + +static int FLASH_FN esp_isalpha(char c) +{ + return ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')); +} + + +static int FLASH_FN esp_isspace(char c) +{ + return (c == ' ' || c == '\t' || c == '\n' || c == '\r'); +} + +static int FLASH_FN esp_isdigit(char c) +{ + return (c >= '0' && c <= '9'); +} + +/* + * Convert a string to a long integer. + * + * Ignores `locale' stuff. Assumes that the upper and lower case + * alphabets and digits are each contiguous. + */ +static long FLASH_FN esp_strtol(const char *nptr, char **endptr, int base) +{ + const char *s = nptr; + unsigned long acc; + int c; + unsigned long cutoff; + int neg = 0, any, cutlim; + + /* + * Skip white space and pick up leading +/- sign if any. + * If base is 0, allow 0x for hex and 0 for octal, else + * assume decimal; if base is already 16, allow 0x. + */ + do { + c = *s++; + } while (esp_isspace((char)c)); + if (c == '-') { + neg = 1; + c = *s++; + } else if (c == '+') + c = *s++; + if ((base == 0 || base == 16) && + c == '0' && (*s == 'x' || *s == 'X')) { + c = s[1]; + s += 2; + base = 16; + } else if ((base == 0 || base == 2) && + c == '0' && (*s == 'b' || *s == 'B')) { + c = s[1]; + s += 2; + base = 2; + } + if (base == 0) + base = c == '0' ? 8 : 10; + + /* + * Compute the cutoff value between legal numbers and illegal + * numbers. That is the largest legal value, divided by the + * base. An input number that is greater than this value, if + * followed by a legal input character, is too big. One that + * is equal to this value may be valid or not; the limit + * between valid and invalid numbers is then based on the last + * digit. For instance, if the range for longs is + * [-2147483648..2147483647] and the input base is 10, + * cutoff will be set to 214748364 and cutlim to either + * 7 (neg==0) or 8 (neg==1), meaning that if we have accumulated + * a value > 214748364, or equal but the next digit is > 7 (or 8), + * the number is too big, and we will return a range error. + * + * Set any if any `digits' consumed; make it negative to indicate + * overflow. + */ + cutoff = neg ? -(unsigned long)LONG_MIN : LONG_MAX; + cutlim = cutoff % (unsigned long)base; + cutoff /= (unsigned long)base; + for (acc = 0, any = 0;; c = *s++) { + if (esp_isdigit((char)c)) + c -= '0'; + else if (esp_isalpha((char)c)) + c -= esp_isupper((char)c) ? 'A' - 10 : 'a' - 10; + else + break; + if (c >= base) + break; + if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim)) + any = -1; + else { + any = 1; + acc *= base; + acc += c; + } + } + if (any < 0) { + acc = neg ? LONG_MIN : LONG_MAX; +// errno = ERANGE; + } else if (neg) + acc = -acc; + if (endptr != 0) + *endptr = (char *)(any ? s - 1 : nptr); + return (acc); +} + +static int FLASH_FN chunked_decode(char *chunked, int size) +{ + char *src = chunked; + char *end = chunked + size; + int i, dst = 0; + + do { + char *endstr = NULL; + //[chunk-size] + i = esp_strtol(src, &endstr, 16); + dbg("Chunk Size:%d\r\n", i); + if (i <= 0) + break; + //[chunk-size-end-ptr] + src = (char *)strstr(src, "\r\n") + 2; + //[chunk-data] + memmove(&chunked[dst], src, (size_t)i); + src += i + 2; /* CRLF */ + dst += i; + } while (src < end); + + // + //footer CRLF + // + + /* decoded size */ + return dst; +} + +static void FLASH_FN receive_callback(void * arg, char *buf, unsigned short len) +{ + struct espconn * conn = (struct espconn *)arg; + request_args * req = (request_args *)conn->reserve; + + if (req->buffer == NULL) { + return; + } + + // Let's do the equivalent of a realloc(). + const int new_size = req->buffer_size + len; + char *new_buffer; + if (new_size > BUFFER_SIZE_MAX || NULL == (new_buffer = (char *)malloc(new_size))) { + error("Response too long (%d)", new_size); + req->buffer[0] = '\0'; // Discard the buffer to avoid using an incomplete response. + if (req->secure) { +#ifdef USE_SECURE + espconn_secure_disconnect(conn); +#endif + } else { + espconn_disconnect(conn); + } + return; // The disconnect callback will be called. + } + + memcpy(new_buffer, req->buffer, req->buffer_size); + memcpy(new_buffer + req->buffer_size - 1 /*overwrite the null character*/, buf, len); // Append new data. + new_buffer[new_size - 1] = '\0'; // Make sure there is an end of string. + + free(req->buffer); + req->buffer = new_buffer; + req->buffer_size = new_size; +} + + +static void FLASH_FN sent_callback(void * arg) +{ + struct espconn * conn = (struct espconn *)arg; + request_args * req = (request_args *)conn->reserve; + + if (req->post_data == NULL) { + //dbg("All sent"); + } else { + // The headers were sent, now send the contents. + //dbg("Sending request body"); + if (req->secure){ +#ifdef USE_SECURE + espconn_secure_sent(conn, (uint8_t *)req->post_data, strlen(req->post_data)); +#endif + } else { + espconn_sent(conn, (uint8_t *)req->post_data, strlen(req->post_data)); + } + free(req->post_data); + req->post_data = NULL; + } +} + +static void FLASH_FN connect_callback(void * arg) +{ + info("Connected!"); + struct espconn * conn = (struct espconn *)arg; + request_args * req = (request_args *)conn->reserve; + + espconn_regist_recvcb(conn, receive_callback); + espconn_regist_sentcb(conn, sent_callback); + + const char *method = "GET"; + char post_headers[32] = ""; + + 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)); + } + + char buf[69 + strlen(method) + strlen(req->path) + strlen(req->hostname) + strlen(req->headers) + strlen(post_headers)]; + + int len = sprintf(buf, + "%s %s HTTP/1.1\r\n" + "Host: %s:%d\r\n" + "Connection: close\r\n" + "User-Agent: ESP8266\r\n" + "%s" + "%s" + "\r\n", + method, req->path, req->hostname, req->port, req->headers, post_headers); + + dbg("Sending request"); + if (req->secure) { +#ifdef USE_SECURE + espconn_secure_sent(conn, (uint8_t *)buf, len); +#endif + } else { + espconn_sent(conn, (uint8_t *)buf, len); + } + free(req->headers); + req->headers = NULL; +} + +static void FLASH_FN disconnect_callback(void * arg) +{ + dbg("Disconnected"); + struct espconn *conn = (struct espconn *)arg; + + if (conn == NULL) { + return; + } + + if (conn->reserve != NULL) { + request_args * req = (request_args *)conn->reserve; + int http_status = -1; + int body_size = 0; + char *body = ""; + if (req->buffer == NULL) { + error("Buffer shouldn't be NULL"); + } else if (req->buffer[0] != '\0') { + // FIXME: make sure this is not a partial response, using the Content-Length header. + + const char *version10 = "HTTP/1.0 "; + const char *version11 = "HTTP/1.1 "; + if (!strstarts(req->buffer, version10) && !strstarts(req->buffer, version11)) { + error("Invalid version in %s", req->buffer); + } else { + http_status = atoi(req->buffer + strlen(version10)); + /* find body and zero terminate headers */ + body = (char *)strstr(req->buffer, "\r\n\r\n") + 2; + *body++ = '\0'; + *body++ = '\0'; + + body_size = req->buffer_size - (body - req->buffer); + + if (strstr(req->buffer, "Transfer-Encoding: chunked")) { + body_size = chunked_decode(body, body_size); + } + } + } + + info("Request completed."); + if (req->user_callback != NULL) { // Callback is optional. + req->user_callback(body, http_status, req->buffer, body_size); + } + + free(req->buffer); + free(req->hostname); + free(req->path); + free(req); + } + espconn_delete(conn); + if (conn->proto.tcp != NULL) { + free(conn->proto.tcp); + } + free(conn); +} + +static void FLASH_FN error_callback(void *arg, sint8 errType) +{ + error("Disconnected with error, type %d", errType); + disconnect_callback(arg); +} + +static void FLASH_FN dns_callback(const char *hostname, ip_addr_t *addr, void *arg) +{ + request_args * req = (request_args *)arg; + + if (addr == NULL) { + error("DNS failed for %s", hostname); + if (req->user_callback != NULL) { + req->user_callback("", -1, "", 0); + } + free(req); + } else { + info("DNS found %s " IPSTR, hostname, IP2STR(addr)); + + struct espconn * conn = (struct espconn *)malloc(sizeof(struct espconn)); + conn->type = ESPCONN_TCP; + conn->state = ESPCONN_NONE; + conn->proto.tcp = (esp_tcp *)malloc(sizeof(esp_tcp)); + conn->proto.tcp->local_port = espconn_port(); + conn->proto.tcp->remote_port = req->port; + conn->reserve = req; + + memcpy(conn->proto.tcp->remote_ip, addr, 4); + + espconn_regist_connectcb(conn, connect_callback); + espconn_regist_disconcb(conn, disconnect_callback); + espconn_regist_reconcb(conn, error_callback); + + if (req->secure) { +#ifdef USE_SECURE + espconn_secure_set_size(ESPCONN_CLIENT, 5120); // set SSL buffer size + espconn_secure_connect(conn); +#endif + } else { + espconn_connect(conn); + } + } +} + +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) +{ + 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); + 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; + + 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 + * can be a hostname or an IP address + * 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: get rid of the #anchor part if present. + + char hostname[128] = ""; + int port = 80; + bool secure = false; + + bool is_http = strstarts(url, "http://"); + bool is_https = strstarts(url, "https://"); + + if (is_http) + url += strlen("http://"); // Get rid of the protocol. + else if (is_https) { + port = 443; + secure = true; + url += strlen("https://"); // Get rid of the protocol. + } else { + error("URL is not HTTP or HTTPS %s", url); + return; + } + + char *path = strchr(url, '/'); + if (path == NULL) { + path = strchr(url, '\0'); // Pointer to end of string. + } + + char *colon = strchr(url, ':'); + if (colon > path) { + colon = NULL; // Limit the search to characters before the path. + } + + if (colon == NULL) { // The port is not present. + memcpy(hostname, url, path - url); + hostname[path - url] = '\0'; + } else { + port = atoi(colon + 1); + if (port == 0) { + error("Port error %s\n", url); + return; + } + + memcpy(hostname, url, colon - url); + hostname[colon - url] = '\0'; + } + + + if (path[0] == '\0') { // Empty path is not allowed. + path = "/"; + } + + http_raw_request(hostname, port, secure, path, post_data, headers, user_callback); +} + +void FLASH_FN http_get(const char *url, const char *headers, httpclient_cb user_callback) +{ + http_post(url, NULL, headers, user_callback); +} + +void FLASH_FN http_callback_example(char *response_body, int http_status, char *response_headers, int body_size) +{ + dbg("[HTTPCLIENT] http_status=%d", http_status); + if (http_status != HTTP_STATUS_GENERIC_ERROR) { + dbg("strlen(headers)=%d", (int)strlen(response_headers)); + dbg("body_size=%d", body_size); + dbg("body=%s", response_body); // FIXME: this does not handle binary data. + } +} diff --git a/esphttpclient/httpclient.h b/esphttpclient/httpclient.h new file mode 100644 index 0000000..5afc2fa --- /dev/null +++ b/esphttpclient/httpclient.h @@ -0,0 +1,62 @@ +/* + * ---------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * Martin d'Allens wrote this file. As long as you retain + * this notice you can do whatever you want with this stuff. If we meet some day, + * and you think this stuff is worth it, you can buy me a beer in return. + * ---------------------------------------------------------------------------- + */ + +#ifndef HTTPCLIENT_H +#define HTTPCLIENT_H + +#include + +#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 this if ssl is needed. Also link the ssl lib */ +//#define USE_SECURE + +/** + * "full_response" is a string containing all response headers and the response body. + * "response_body and "http_status" are extracted from "full_response" for convenience. + * + * A successful request corresponds to an HTTP status code of 200 (OK). + * More info at http://en.wikipedia.org/wiki/List_of_HTTP_status_codes + */ +typedef void (* httpclient_cb)(char * response_body, int http_status, char * response_headers, int body_size); + +/** + * Download a web page from its URL. + * Try: + * http_get("http://wtfismyip.com/text", http_callback_example); + */ +void http_get(const char * url, const char *headers, httpclient_cb user_callback); + +/** + * Post data to a web form. + * The data should be encoded as application/x-www-form-urlencoded. + * Try: + * 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); + +/** + * Call this function to skip URL parsing if the arguments are already in separate variables. + */ +void http_raw_request( + 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. + */ +void http_callback_example(char *response_body, int http_status, char *response_headers, int body_size); + +#endif diff --git a/esphttpclient/test/httpclient_test.c b/esphttpclient/test/httpclient_test.c new file mode 100644 index 0000000..0887111 --- /dev/null +++ b/esphttpclient/test/httpclient_test.c @@ -0,0 +1,90 @@ +/* + * ---------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * Martin d'Allens wrote this file. As long as you retain + * this notice you can do whatever you want with this stuff. If we meet some day, + * and you think this stuff is worth it, you can buy me a beer in return. + * ---------------------------------------------------------------------------- + */ + +#include +#include "httpclient.h" + + +static int test_count = 0; +static ETSTimer test_timer; + +static void ICACHE_FLASH_ATTR test_timer_cb() +{ + dbg("\nTEST %d ", test_count); + + switch(test_count) { + case 0: + dbg("=> IP address before network ready\n"); + http_get("http://173.194.45.65", "", http_callback_example); + // This test will fail. The DHCP request returns directly, but the TCP connection hangs. + // FIXME: wait for network to be ready before connecting? + break; + case 1: + dbg("=> Simple GET\n"); + http_get("http://wtfismyip.com/text", "", http_callback_example); + break; + case 2: + dbg("=> Response too long\n"); + http_get("http://www.wikipedia.org/", "", http_callback_example); + break; + case 3: + dbg("=> Connection refused\n"); + http_get("http://att.com:1234/", "", http_callback_example); + break; + case 4: + dbg("=> Empty response, wrong protocol\n"); + http_get("http://google.com:443/", "", http_callback_example); + break; + case 5: + dbg("=> Invalid DNS\n"); + http_get("http://invalid.dns/", "", http_callback_example); + break; + case 6: + dbg("=> Connection time out\n"); + http_get("http://google.com:1234/", "", http_callback_example); + break; + case 7: + dbg("=> Simple POST\n"); + http_post("http://httpbin.org/post", "first_word=hello&second_word=world", "Content-Type: application/x-www-form-urlencoded\r\n", http_callback_example); + break; + case 8: + dbg("=> Moved\n"); + http_get("http://wikipedia.org/", "", http_callback_example); + break; + case 9: + dbg("=> IP address, 404\n"); + http_get("http://54.200.182.206/", "", http_callback_example); + break; + case 10: + dbg("=> Concurrent requests\n"); + http_get("http://wtfismyip.com/text", "", http_callback_example); + http_post("http://httpbin.org/post", "first_word=hello&second_word=world", "Content-Type: application/x-www-form-urlencoded\r\n", http_callback_example); + http_get("http://wtfismyip.com/text", "", http_callback_example); + http_post("http://httpbin.org/post", "first_word=hello&second_word=world", "Content-Type: application/x-www-form-urlencoded\r\n", http_callback_example); + http_get("http://wtfismyip.com/text", "", http_callback_example); + http_post("http://httpbin.org/post", "first_word=hello&second_word=world", "Content-Type: application/x-www-form-urlencoded\r\n", http_callback_example); + http_get("http://wtfismyip.com/text", "", http_callback_example); + // FIXME: this test sometimes fails with "DNS error code -1" + break; + default: + dbg("=> DONE\n"); + return; + } + test_count++; + os_timer_arm(&test_timer, 3000, 0); +} + +void ICACHE_FLASH_ATTR http_test() +{ + // FIXME: what happens when no Wifi network is available? + + os_timer_disarm(&test_timer); + os_timer_setfn(&test_timer, test_timer_cb, NULL); + os_timer_arm(&test_timer, 0, 0); // Start immediately. +} diff --git a/libesphttpd/include/platform.h b/libesphttpd/include/platform.h index 5fcfaed..d88f461 100644 --- a/libesphttpd/include/platform.h +++ b/libesphttpd/include/platform.h @@ -22,6 +22,7 @@ typedef RtosConnType* ConnTypePtr; #define streq(a, b) (strcmp(a, b) == 0) #define strneq(a, b, n) (strncmp(a, b, n) == 0) +#define strstarts(a, b) (strncmp(a, b, (int)strlen(b)) == 0) #define malloc(x) os_malloc(x) #define free(x) os_free(x) diff --git a/user/user_main.c b/user/user_main.c index 4b368b6..2755a3d 100644 --- a/user/user_main.c +++ b/user/user_main.c @@ -23,6 +23,8 @@ #include "routes.h" #include "fw_version.h" +#include "httpclient.h" + extern HttpdBuiltInUrl builtInUrls[]; static ETSTimer prSecondTimer; @@ -35,6 +37,8 @@ static void ICACHE_FLASH_ATTR prSecondTimerCb(void *arg) static u8 cnt = 0; static u32 last = 0; + static u8 cnt2 = 0; + if (++cnt == 3) { cnt = 0; u32 heap = system_get_free_heap_size(); @@ -42,6 +46,17 @@ static void ICACHE_FLASH_ATTR prSecondTimerCb(void *arg) last = heap; } + if (++cnt2 == 15) { + cnt2 = 0; + + dbg("=> Simple GET"); + error("=> Simple GET"); + warn("=> Simple GET"); + info("=> Simple GET"); + http_get("http://data.ondrovo.com/f/hello.txt", "", http_callback_example); + } + + // we will also try to set up a SBMP connection if (sbmp_ep_handshake_status(dlnk_ep) != SBMP_HSK_SUCCESS) { sbmp_ep_start_handshake(dlnk_ep);