Merge pull request #887 from vowstar/dev-all-in-one

Add HTTP client module and documents.
This commit is contained in:
Johny Mattsson 2016-01-15 17:44:41 +11:00
commit 52e121f469
8 changed files with 1049 additions and 1 deletions

View File

@ -289,6 +289,7 @@ Comment-out the #define statement for unused modules. Example:
// #define LUA_USE_MODULES_BMP085 // #define LUA_USE_MODULES_BMP085
#define LUA_USE_MODULES_TSL2561 #define LUA_USE_MODULES_TSL2561
// #define LUA_USE_MODULES_HX711 // #define LUA_USE_MODULES_HX711
#define LUA_USE_MODULES_HTTP
#endif /* LUA_USE_MODULES */ #endif /* LUA_USE_MODULES */
``` ```
@ -628,6 +629,47 @@ json_text = cjson.encode(value)
-- Returns: '[true,{"foo":"bar"}]' -- Returns: '[true,{"foo":"bar"}]'
``` ```
####HTTP client
```lua
-- Support HTTP and HTTPS, For example
-- HTTP POST Example with JSON header and body
http.post("http://somewhere.acceptjson.com/",
"Content-Type: application/json\r\n",
"{\"hello\":\"world\"}",
function(code, data)
print(code)
print(data)
end)
-- HTTPS GET Example with NULL header
http.get("https://www.vowstar.com/nodemcu/","",
function(code, data)
print(code)
print(data)
end)
-- You will get
-- > 200
-- hello nodemcu
-- HTTPS DELETE Example with NULL header and body
http.delete("https://10.0.0.2:443","","",
function(code, data)
print(code)
print(data)
end)
-- HTTPS PUT Example with NULL header and body
http.put("https://testput.somewhere/somewhereyouput.php","","",
function(code, data)
print(code)
print(data)
end)
-- HTTP RAW Request Example, use more HTTP/HTTPS request method
http.request("http://www.apple.com:80/library/test/success.html","GET","","",
function(code, data)
print(code)
print(data)
end)
```
####Read an HX711 load cell ADC. ####Read an HX711 load cell ADC.
Note: currently only chanel A with gain 128 is supported. Note: currently only chanel A with gain 128 is supported.
The HX711 is an inexpensive 24bit ADC with programmable 128x, 64x, and 32x gain. The HX711 is an inexpensive 24bit ADC with programmable 128x, 64x, and 32x gain.

View File

@ -41,7 +41,8 @@ SUBDIRS= \
cjson \ cjson \
crypto \ crypto \
dhtlib \ dhtlib \
tsl2561 tsl2561 \
http
endif # } PDIR endif # } PDIR
@ -86,6 +87,7 @@ COMPONENTS_eagle.app.v6 = \
crypto/libcrypto.a \ crypto/libcrypto.a \
dhtlib/libdhtlib.a \ dhtlib/libdhtlib.a \
tsl2561/tsl2561lib.a \ tsl2561/tsl2561lib.a \
http/libhttp.a \
modules/libmodules.a \ modules/libmodules.a \
# Inspect the modules library and work out which modules need to be linked. # Inspect the modules library and work out which modules need to be linked.

47
app/http/Makefile Normal file
View File

@ -0,0 +1,47 @@
#############################################################
# Required variables for each makefile
# Discard this section from all parent makefiles
# Expected variables (with automatic defaults):
# CSRCS (all "C" files in the dir)
# SUBDIRS (all subdirs with a Makefile)
# GEN_LIBS - list of libs to be generated ()
# GEN_IMAGES - list of images to be generated ()
# COMPONENTS_xxx - a list of libs/objs in the form
# subdir/lib to be extracted and rolled up into
# a generated lib/image xxx.a ()
#
ifndef PDIR
GEN_LIBS = libhttp.a
endif
#############################################################
# Configuration i.e. compile options etc.
# Target specific stuff (defines etc.) goes in here!
# Generally values applying to a tree are captured in the
# makefile at its root level - these are then overridden
# for a subtree within the makefile rooted therein
#
#DEFINES +=
#############################################################
# Recursion Magic - Don't touch this!!
#
# Each subtree potentially has an include directory
# corresponding to the common APIs applicable to modules
# rooted at that subtree. Accordingly, the INCLUDE PATH
# of a module can only contain the include directories up
# its parent path, and not its siblings
#
# Required for each makefile to inherit from the parent
#
INCLUDES := $(INCLUDES) -I $(PDIR)include
INCLUDES += -I ./
INCLUDES += -I ./include
INCLUDES += -I ../include
INCLUDES += -I ../../include
PDIR := ../$(PDIR)
sinclude $(PDIR)Makefile

628
app/http/httpclient.c Normal file
View File

@ -0,0 +1,628 @@
/*
* ----------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* Martin d'Allens <martin.dallens@gmail.com> 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.
* FIXME: support null characters in responses.
*/
#include "osapi.h"
#include "user_interface.h"
#include "espconn.h"
#include "mem.h"
#include "limits.h"
#include "httpclient.h"
/* Internal state. */
typedef struct request_args_t {
char * hostname;
int port;
bool secure;
char * method;
char * path;
char * headers;
char * post_data;
char * buffer;
int buffer_size;
int timeout;
os_timer_t timeout_timer;
http_callback_t callback_handle;
} request_args_t;
static char * ICACHE_FLASH_ATTR esp_strdup( const char * str )
{
if ( str == NULL )
{
return(NULL);
}
char * new_str = (char *) os_malloc( os_strlen( str ) + 1 ); /* 1 for null character */
if ( new_str == NULL )
{
HTTPCLIENT_DEBUG( "esp_strdup: malloc error" );
return(NULL);
}
os_strcpy( new_str, str );
return(new_str);
}
static int ICACHE_FLASH_ATTR
esp_isupper( char c )
{
return(c >= 'A' && c <= 'Z');
}
static int ICACHE_FLASH_ATTR
esp_isalpha( char c )
{
return( (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') );
}
static int ICACHE_FLASH_ATTR
esp_isspace( char c )
{
return(c == ' ' || c == '\t' || c == '\n' || c == '\12');
}
static int ICACHE_FLASH_ATTR
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.
*/
long ICACHE_FLASH_ATTR
esp_strtol( nptr, endptr, base )
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( 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( c ) )
c -= '0';
else if ( esp_isalpha( c ) )
c -= esp_isupper( 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 ICACHE_FLASH_ATTR http_chunked_decode( const char * chunked, char * decode )
{
int i = 0, j = 0;
int decode_size = 0;
char *str = (char *) chunked;
do
{
char * endstr = NULL;
/* [chunk-size] */
i = esp_strtol( str + j, endstr, 16 );
HTTPCLIENT_DEBUG( "Chunk Size:%d\r\n", i );
if ( i <= 0 )
break;
/* [chunk-size-end-ptr] */
endstr = (char *) os_strstr( str + j, "\r\n" );
/* [chunk-ext] */
j += endstr - (str + j);
/* [CRLF] */
j += 2;
/* [chunk-data] */
decode_size += i;
os_memcpy( (char *) &decode[decode_size - i], (char *) str + j, i );
j += i;
/* [CRLF] */
j += 2;
}
while ( true );
/*
*
* footer CRLF
*
*/
return(j);
}
static void ICACHE_FLASH_ATTR http_receive_callback( void * arg, char * buf, unsigned short len )
{
struct espconn * conn = (struct espconn *) arg;
request_args_t * req = (request_args_t *) conn->reverse;
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 *) os_malloc( new_size ) ) )
{
HTTPCLIENT_DEBUG( "Response too long (%d)\n", new_size );
req->buffer[0] = '\0'; /* Discard the buffer to avoid using an incomplete response. */
if ( req->secure )
espconn_secure_disconnect( conn );
else
espconn_disconnect( conn );
return; /* The disconnect callback will be called. */
}
os_memcpy( new_buffer, req->buffer, req->buffer_size );
os_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. */
os_free( req->buffer );
req->buffer = new_buffer;
req->buffer_size = new_size;
}
static void ICACHE_FLASH_ATTR http_send_callback( void * arg )
{
struct espconn * conn = (struct espconn *) arg;
request_args_t * req = (request_args_t *) conn->reverse;
if ( req->post_data == NULL )
{
HTTPCLIENT_DEBUG( "All sent\n" );
}
else
{
/* The headers were sent, now send the contents. */
HTTPCLIENT_DEBUG( "Sending request body\n" );
if ( req->secure )
espconn_secure_send( conn, (uint8_t *) req->post_data, strlen( req->post_data ) );
else
espconn_send( conn, (uint8_t *) req->post_data, strlen( req->post_data ) );
os_free( req->post_data );
req->post_data = NULL;
}
}
static void ICACHE_FLASH_ATTR http_connect_callback( void * arg )
{
HTTPCLIENT_DEBUG( "Connected\n" );
struct espconn * conn = (struct espconn *) arg;
request_args_t * req = (request_args_t *) conn->reverse;
espconn_regist_recvcb( conn, http_receive_callback );
espconn_regist_sentcb( conn, http_send_callback );
char post_headers[32] = "";
if ( req->post_data != NULL ) /* If there is data then add Content-Length header. */
{
os_sprintf( post_headers, "Content-Length: %d\r\n", strlen( req->post_data ) );
}
if(req->headers == NULL) /* Avoid NULL pointer, it may cause exception */
{
req->headers = (char *)os_malloc(sizeof(char));
req->headers[0] = '\0';
}
char buf[69 + strlen( req->method ) + strlen( req->path ) + strlen( req->hostname ) +
strlen( req->headers ) + strlen( post_headers )];
int len = os_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",
req->method, req->path, req->hostname, req->port, req->headers, post_headers );
if ( req->secure )
espconn_secure_send( conn, (uint8_t *) buf, len );
else
espconn_send( conn, (uint8_t *) buf, len );
if(req->headers != NULL)
os_free( req->headers );
req->headers = NULL;
HTTPCLIENT_DEBUG( "Sending request header\n" );
}
static void ICACHE_FLASH_ATTR http_disconnect_callback( void * arg )
{
HTTPCLIENT_DEBUG( "Disconnected\n" );
struct espconn *conn = (struct espconn *) arg;
if ( conn == NULL )
{
return;
}
if ( conn->proto.tcp != NULL )
{
os_free( conn->proto.tcp );
}
if ( conn->reverse != NULL )
{
request_args_t * req = (request_args_t *) conn->reverse;
int http_status = -1;
char * body = "";
// Turn off timeout timer
os_timer_disarm( &(req->timeout_timer) );
if ( req->buffer == NULL )
{
HTTPCLIENT_DEBUG( "Buffer shouldn't be NULL\n" );
}
else if ( req->buffer[0] != '\0' )
{
/* FIXME: make sure this is not a partial response, using the Content-Length header. */
const char * version = "HTTP/1.1 ";
if ( os_strncmp( req->buffer, version, strlen( version ) ) != 0 )
{
HTTPCLIENT_DEBUG( "Invalid version in %s\n", req->buffer );
}
else
{
http_status = atoi( req->buffer + strlen( version ) );
body = (char *) os_strstr( req->buffer, "\r\n\r\n" ) + 4;
if ( os_strstr( req->buffer, "Transfer-Encoding: chunked" ) )
{
int body_size = req->buffer_size - (body - req->buffer);
char chunked_decode_buffer[body_size];
os_memset( chunked_decode_buffer, 0, body_size );
/* Chuncked data */
http_chunked_decode( body, chunked_decode_buffer );
os_memcpy( body, chunked_decode_buffer, body_size );
}
}
}
if ( req->callback_handle != NULL ) /* Callback is optional. */
{
req->callback_handle( body, http_status, req->buffer );
}
os_free( req->buffer );
os_free( req->hostname );
os_free( req->method );
os_free( req->path );
os_free( req );
}
/* Fix memory leak. */
espconn_delete( conn );
os_free( conn );
}
static void ICACHE_FLASH_ATTR http_error_callback( void *arg, sint8 errType )
{
HTTPCLIENT_DEBUG( "Disconnected with error\n" );
http_disconnect_callback( arg );
}
static void ICACHE_FLASH_ATTR http_timeout_callback( void *arg )
{
HTTPCLIENT_DEBUG( "Connection timeout\n" );
struct espconn * conn = (struct espconn *) arg;
if ( conn == NULL )
{
return;
}
if ( conn->reverse == NULL )
{
return;
}
request_args_t * req = (request_args_t *) conn->reverse;
/* Call disconnect */
if ( req->secure )
espconn_secure_disconnect( conn );
else
espconn_disconnect( conn );
}
static void ICACHE_FLASH_ATTR http_dns_callback( const char * hostname, ip_addr_t * addr, void * arg )
{
request_args_t * req = (request_args_t *) arg;
if ( addr == NULL )
{
HTTPCLIENT_DEBUG( "DNS failed for %s\n", hostname );
if ( req->callback_handle != NULL )
{
req->callback_handle( "", -1, "" );
}
os_free( req );
}
else
{
HTTPCLIENT_DEBUG( "DNS found %s " IPSTR "\n", hostname, IP2STR( addr ) );
struct espconn * conn = (struct espconn *) os_malloc( sizeof(struct espconn) );
conn->type = ESPCONN_TCP;
conn->state = ESPCONN_NONE;
conn->proto.tcp = (esp_tcp *) os_malloc( sizeof(esp_tcp) );
conn->proto.tcp->local_port = espconn_port();
conn->proto.tcp->remote_port = req->port;
conn->reverse = req;
os_memcpy( conn->proto.tcp->remote_ip, addr, 4 );
espconn_regist_connectcb( conn, http_connect_callback );
espconn_regist_disconcb( conn, http_disconnect_callback );
espconn_regist_reconcb( conn, http_error_callback );
/* Set connection timeout timer */
os_timer_disarm( &(req->timeout_timer) );
os_timer_setfn( &(req->timeout_timer), (os_timer_func_t *) http_timeout_callback, conn );
os_timer_arm( &(req->timeout_timer), req->timeout, false );
if ( req->secure )
{
espconn_secure_set_size( ESPCONN_CLIENT, 5120 ); /* set SSL buffer size */
espconn_secure_connect( conn );
}
else
{
espconn_connect( conn );
}
}
}
void ICACHE_FLASH_ATTR http_raw_request( const char * hostname, int port, bool secure, const char * method, const char * path, const char * headers, const char * post_data, http_callback_t callback_handle )
{
HTTPCLIENT_DEBUG( "DNS request\n" );
request_args_t * req = (request_args_t *) os_malloc( sizeof(request_args_t) );
req->hostname = esp_strdup( hostname );
req->port = port;
req->secure = secure;
req->method = esp_strdup( method );
req->path = esp_strdup( path );
req->headers = esp_strdup( headers );
req->post_data = esp_strdup( post_data );
req->buffer_size = 1;
req->buffer = (char *) os_malloc( 1 );
req->buffer[0] = '\0'; /* Empty string. */
req->callback_handle = callback_handle;
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, http_dns_callback );
if ( error == ESPCONN_INPROGRESS )
{
HTTPCLIENT_DEBUG( "DNS pending\n" );
}
else if ( error == ESPCONN_OK )
{
/* Already in the local names table (or hostname was an IP address), execute the callback ourselves. */
http_dns_callback( hostname, &addr, req );
}
else
{
if ( error == ESPCONN_ARG )
{
HTTPCLIENT_DEBUG( "DNS arg error %s\n", hostname );
}else {
HTTPCLIENT_DEBUG( "DNS error code %d\n", error );
}
http_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 ICACHE_FLASH_ATTR http_request( const char * url, const char * method, const char * headers, const char * post_data, http_callback_t callback_handle )
{
/*
* 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 = os_strncmp( url, "http://", strlen( "http://" ) ) == 0;
bool is_https = os_strncmp( url, "https://", strlen( "https://" ) ) == 0;
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
{
HTTPCLIENT_DEBUG( "URL is not HTTP or HTTPS %s\n", url );
return;
}
char * path = os_strchr( url, '/' );
if ( path == NULL )
{
path = os_strchr( url, '\0' ); /* Pointer to end of string. */
}
char * colon = os_strchr( url, ':' );
if ( colon > path )
{
colon = NULL; /* Limit the search to characters before the path. */
}
if ( colon == NULL ) /* The port is not present. */
{
os_memcpy( hostname, url, path - url );
hostname[path - url] = '\0';
}
else
{
port = atoi( colon + 1 );
if ( port == 0 )
{
HTTPCLIENT_DEBUG( "Port error %s\n", url );
return;
}
os_memcpy( hostname, url, colon - url );
hostname[colon - url] = '\0';
}
if ( path[0] == '\0' ) /* Empty path is not allowed. */
{
path = "/";
}
HTTPCLIENT_DEBUG( "hostname=%s\n", hostname );
HTTPCLIENT_DEBUG( "port=%d\n", port );
HTTPCLIENT_DEBUG( "method=%s\n", method );
HTTPCLIENT_DEBUG( "path=%s\n", path );
http_raw_request( hostname, port, secure, method, path, headers, post_data, callback_handle );
}
/*
* Parse an URL of the form http://host:port/path
* <host> can be a hostname or an IP address
* <port> is optional
*/
void ICACHE_FLASH_ATTR http_post( const char * url, const char * headers, const char * post_data, http_callback_t callback_handle )
{
http_request( url, "POST", headers, post_data, callback_handle );
}
void ICACHE_FLASH_ATTR http_get( const char * url, const char * headers, http_callback_t callback_handle )
{
http_request( url, "GET", headers, NULL, callback_handle );
}
void ICACHE_FLASH_ATTR http_delete( const char * url, const char * headers, const char * post_data, http_callback_t callback_handle )
{
http_request( url, "DELETE", headers, post_data, callback_handle );
}
void ICACHE_FLASH_ATTR http_put( const char * url, const char * headers, const char * post_data, http_callback_t callback_handle )
{
http_request( url, "PUT", headers, post_data, callback_handle );
}
void ICACHE_FLASH_ATTR http_callback_example( char * response, int http_status, char * full_response )
{
os_printf( "http_status=%d\n", http_status );
if ( http_status != HTTP_STATUS_GENERIC_ERROR )
{
os_printf( "strlen(full_response)=%d\n", strlen( full_response ) );
os_printf( "response=%s<EOF>\n", response );
}
}

97
app/http/httpclient.h Normal file
View File

@ -0,0 +1,97 @@
/*
* ----------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* Martin d'Allens <martin.dallens@gmail.com> 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__
#if defined(GLOBAL_DEBUG_ON)
#define HTTPCLIENT_DEBUG_ON
#endif
#if defined(HTTPCLIENT_DEBUG_ON)
#define HTTPCLIENT_DEBUG(format, ...) os_printf(format, ##__VA_ARGS__)
#else
#define HTTPCLIENT_DEBUG(format, ...)
#endif
#if defined(USES_SDK_BEFORE_V140)
#define espconn_send espconn_sent
#define espconn_secure_send espconn_secure_sent
#endif
/*
* In case of TCP or DNS error the callback is called with this status.
*/
#define HTTP_STATUS_GENERIC_ERROR (-1)
/*
* Size of http responses that will cause an error.
*/
#define BUFFER_SIZE_MAX (1024)
/*
* Timeout of http request.
*/
#define HTTP_REQUEST_TIMEOUT_MS (10000)
/*
* "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 (* http_callback_t)(char * response_body, int http_status, char * full_response);
/*
* Call this function to skip URL parsing if the arguments are already in separate variables.
*/
void ICACHE_FLASH_ATTR http_raw_request(const char * hostname, int port, bool secure, const char * method, const char * path, const char * headers, const char * post_data, http_callback_t callback_handle);
/*
* Request data from URL use custom method.
* The data should be encoded as any format.
* Try:
* http_request("http://httpbin.org/post", "OPTIONS", "Content-type: text/plain", "Hello world", http_callback_example);
*/
void ICACHE_FLASH_ATTR http_request(const char * url, const char * method, const char * headers, const char * post_data, http_callback_t callback_handle);
/*
* Post data to a web form.
* The data should be encoded as any format.
* Try:
* http_post("http://httpbin.org/post", "Content-type: application/json", "{\"hello\": \"world\"}", http_callback_example);
*/
void ICACHE_FLASH_ATTR http_post(const char * url, const char * headers, const char * post_data, http_callback_t callback_handle);
/*
* Download a web page from its URL.
* Try:
* http_get("http://wtfismyip.com/text", NULL, http_callback_example);
*/
void ICACHE_FLASH_ATTR http_get(const char * url, const char * headers, http_callback_t callback_handle);
/*
* Delete a web page from its URL.
* Try:
* http_delete("http://wtfismyip.com/text", NULL, http_callback_example);
*/
void ICACHE_FLASH_ATTR http_delete(const char * url, const char * headers, const char * post_data, http_callback_t callback_handle);
/*
* Update data to a web form.
* The data should be encoded as any format.
* Try:
* http_put("http://httpbin.org/post", "Content-type: application/json", "{\"hello\": \"world\"}", http_callback_example);
*/
void ICACHE_FLASH_ATTR http_put(const char * url, const char * headers, const char * post_data, http_callback_t callback_handle);
/*
* Output on the UART.
*/
void http_callback_example(char * response, int http_status, char * full_response);
#endif // __HTTPCLIENT_H__

View File

@ -23,6 +23,7 @@
//#define LUA_USE_MODULES_ENDUSER_SETUP // USE_DNS in dhcpserver.h needs to be enabled for this module to work. //#define LUA_USE_MODULES_ENDUSER_SETUP // USE_DNS in dhcpserver.h needs to be enabled for this module to work.
#define LUA_USE_MODULES_FILE #define LUA_USE_MODULES_FILE
#define LUA_USE_MODULES_GPIO #define LUA_USE_MODULES_GPIO
#define LUA_USE_MODULES_HTTP
//#define LUA_USE_MODULES_HX711 //#define LUA_USE_MODULES_HX711
#define LUA_USE_MODULES_I2C #define LUA_USE_MODULES_I2C
#define LUA_USE_MODULES_MQTT #define LUA_USE_MODULES_MQTT

View File

@ -50,6 +50,7 @@ INCLUDES += -I ../spiffs
INCLUDES += -I ../smart INCLUDES += -I ../smart
INCLUDES += -I ../cjson INCLUDES += -I ../cjson
INCLUDES += -I ../dhtlib INCLUDES += -I ../dhtlib
INCLUDES += -I ../http
PDIR := ../$(PDIR) PDIR := ../$(PDIR)
sinclude $(PDIR)Makefile sinclude $(PDIR)Makefile

230
app/modules/http.c Normal file
View File

@ -0,0 +1,230 @@
/******************************************************************************
* HTTP module for NodeMCU
* vowstar@gmail.com
* 2015-12-29
*******************************************************************************/
#include "module.h"
#include "lauxlib.h"
#include "platform.h"
#include "cpu_esp8266.h"
#include "httpclient.h"
static int http_callback_registry = LUA_NOREF;
static lua_State * http_client_L = NULL;
static void http_callback( char * response, int http_status, char * full_response )
{
#if defined(HTTPCLIENT_DEBUG_ON)
c_printf( "http_status=%d\n", http_status );
if ( http_status != HTTP_STATUS_GENERIC_ERROR )
{
c_printf( "strlen(full_response)=%d\n", strlen( full_response ) );
c_printf( "response=%s<EOF>\n", response );
}
#endif
if (http_callback_registry != LUA_NOREF)
{
lua_rawgeti(http_client_L, LUA_REGISTRYINDEX, http_callback_registry);
lua_pushnumber(http_client_L, http_status);
if ( http_status != HTTP_STATUS_GENERIC_ERROR )
{
lua_pushstring(http_client_L, response);
}
else
{
lua_pushnil(http_client_L);
}
lua_call(http_client_L, 2, 0); // With 2 arguments and 0 result
luaL_unref(http_client_L, LUA_REGISTRYINDEX, http_callback_registry);
http_callback_registry = LUA_NOREF;
}
}
// Lua: http.request( url, method, header, body, function(status, reponse) end )
static int http_lapi_request( lua_State *L )
{
int length;
http_client_L = L;
const char * url = luaL_checklstring(L, 1, &length);
const char * method = luaL_checklstring(L, 2, &length);
const char * headers = NULL;
const char * body = NULL;
// Check parameter
if ((url == NULL) || (method == NULL))
{
return luaL_error( L, "wrong arg type" );
}
if (lua_isstring(L, 3))
{
headers = luaL_checklstring(L, 3, &length);
}
if (lua_isstring(L, 4))
{
body = luaL_checklstring(L, 4, &length);
}
if (lua_type(L, 5) == LUA_TFUNCTION || lua_type(L, 5) == LUA_TLIGHTFUNCTION) {
lua_pushvalue(L, 5); // copy argument (func) to the top of stack
if (http_callback_registry != LUA_NOREF)
luaL_unref(L, LUA_REGISTRYINDEX, http_callback_registry);
http_callback_registry = luaL_ref(L, LUA_REGISTRYINDEX);
}
http_request(url, method, headers, body, http_callback);
return 0;
}
// Lua: http.post( url, header, body, function(status, reponse) end )
static int http_lapi_post( lua_State *L )
{
int length;
http_client_L = L;
const char * url = luaL_checklstring(L, 1, &length);
const char * headers = NULL;
const char * body = NULL;
// Check parameter
if ((url == NULL))
{
return luaL_error( L, "wrong arg type" );
}
if (lua_isstring(L, 2))
{
headers = luaL_checklstring(L, 2, &length);
}
if (lua_isstring(L, 3))
{
body = luaL_checklstring(L, 3, &length);
}
if (lua_type(L, 4) == LUA_TFUNCTION || lua_type(L, 4) == LUA_TLIGHTFUNCTION) {
lua_pushvalue(L, 4); // copy argument (func) to the top of stack
if (http_callback_registry != LUA_NOREF)
luaL_unref(L, LUA_REGISTRYINDEX, http_callback_registry);
http_callback_registry = luaL_ref(L, LUA_REGISTRYINDEX);
}
http_post(url, headers, body, http_callback);
return 0;
}
// Lua: http.put( url, header, body, function(status, reponse) end )
static int http_lapi_put( lua_State *L )
{
int length;
http_client_L = L;
const char * url = luaL_checklstring(L, 1, &length);
const char * headers = NULL;
const char * body = NULL;
// Check parameter
if ((url == NULL))
{
return luaL_error( L, "wrong arg type" );
}
if (lua_isstring(L, 2))
{
headers = luaL_checklstring(L, 2, &length);
}
if (lua_isstring(L, 3))
{
body = luaL_checklstring(L, 3, &length);
}
if (lua_type(L, 4) == LUA_TFUNCTION || lua_type(L, 4) == LUA_TLIGHTFUNCTION) {
lua_pushvalue(L, 4); // copy argument (func) to the top of stack
if (http_callback_registry != LUA_NOREF)
luaL_unref(L, LUA_REGISTRYINDEX, http_callback_registry);
http_callback_registry = luaL_ref(L, LUA_REGISTRYINDEX);
}
http_put(url, headers, body, http_callback);
return 0;
}
// Lua: http.delete( url, header, body, function(status, reponse) end )
static int http_lapi_delete( lua_State *L )
{
int length;
http_client_L = L;
const char * url = luaL_checklstring(L, 1, &length);
const char * headers = NULL;
const char * body = NULL;
// Check parameter
if ((url == NULL))
{
return luaL_error( L, "wrong arg type" );
}
if (lua_isstring(L, 2))
{
headers = luaL_checklstring(L, 2, &length);
}
if (lua_isstring(L, 3))
{
body = luaL_checklstring(L, 3, &length);
}
if (lua_type(L, 4) == LUA_TFUNCTION || lua_type(L, 4) == LUA_TLIGHTFUNCTION) {
lua_pushvalue(L, 4); // copy argument (func) to the top of stack
if (http_callback_registry != LUA_NOREF)
luaL_unref(L, LUA_REGISTRYINDEX, http_callback_registry);
http_callback_registry = luaL_ref(L, LUA_REGISTRYINDEX);
}
http_delete(url, headers, body, http_callback);
return 0;
}
// Lua: http.get( url, header, function(status, reponse) end )
static int http_lapi_get( lua_State *L )
{
int length;
http_client_L = L;
const char * url = luaL_checklstring(L, 1, &length);
const char * headers = NULL;
// Check parameter
if ((url == NULL))
{
return luaL_error( L, "wrong arg type" );
}
if (lua_isstring(L, 2))
{
headers = luaL_checklstring(L, 2, &length);
}
if (lua_type(L, 3) == LUA_TFUNCTION || lua_type(L, 3) == LUA_TLIGHTFUNCTION) {
lua_pushvalue(L, 3); // copy argument (func) to the top of stack
if (http_callback_registry != LUA_NOREF)
luaL_unref(L, LUA_REGISTRYINDEX, http_callback_registry);
http_callback_registry = luaL_ref(L, LUA_REGISTRYINDEX);
}
http_get(url, headers, http_callback);
return 0;
}
// Module function map
static const LUA_REG_TYPE http_map[] = {
{ LSTRKEY( "request" ), LFUNCVAL( http_lapi_request ) },
{ LSTRKEY( "post" ), LFUNCVAL( http_lapi_post ) },
{ LSTRKEY( "put" ), LFUNCVAL( http_lapi_put ) },
{ LSTRKEY( "delete" ), LFUNCVAL( http_lapi_delete ) },
{ LSTRKEY( "get" ), LFUNCVAL( http_lapi_get ) },
{ LSTRKEY( "OK" ), LNUMVAL( 0 ) },
{ LSTRKEY( "ERROR" ), LNUMVAL( HTTP_STATUS_GENERIC_ERROR ) },
{ LNILKEY, LNILVAL }
};
NODEMCU_MODULE(HTTP, "http", http_map, NULL);