2015-12-29 12:25:37 +01:00
|
|
|
/*
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
* "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"
|
2016-04-09 13:54:10 +02:00
|
|
|
#include "stdlib.h"
|
2015-12-29 12:25:37 +01:00
|
|
|
|
|
|
|
/* 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);
|
|
|
|
}
|
2016-05-26 10:36:20 +02:00
|
|
|
char * new_str = (char *) malloc( os_strlen( str ) + 1 ); /* 1 for null character */
|
2015-12-29 12:25:37 +01:00
|
|
|
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');
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
{
|
2016-04-09 13:54:10 +02:00
|
|
|
char * endstr;
|
2015-12-29 12:25:37 +01:00
|
|
|
/* [chunk-size] */
|
2016-04-09 13:54:10 +02:00
|
|
|
i = strtoul( str + j, NULL, 16 );
|
2015-12-29 12:25:37 +01:00
|
|
|
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;
|
2016-05-26 10:36:20 +02:00
|
|
|
if ( new_size > BUFFER_SIZE_MAX || NULL == (new_buffer = (char *) malloc( new_size ) ) )
|
2015-12-29 12:25:37 +01:00
|
|
|
{
|
|
|
|
HTTPCLIENT_DEBUG( "Response too long (%d)\n", new_size );
|
|
|
|
req->buffer[0] = '\0'; /* Discard the buffer to avoid using an incomplete response. */
|
Initial pass at switching to RTOS SDK.
This compiles, links, and starts the RTOS without crashing and burning.
Lua environment does not yet start due to the different task architecture.
Known pain points:
- task implementation needs to be rewritten for RTOS (next up on my TODO)
- secure espconn does not exist, all secure espconn stuff has been #if 0'd
- lwip now built from within the RTOS SDK, but does not appear to include
MDNS support. Investigation needed.
- there is no access to FRC1 NMI, not sure if we ever actually used that
however. Also #if 0'd out for now.
- new timing constraints introduced by the RTOS, all use of ets_delay_us()
and os_delay_us() needs to be reviewed (the tsl2561 driver in particular).
- even more confusion with ets_ vs os_ vs c_ vs non-prefixed versions.
In the long run everything should be switched to non-prefixed versions.
- system_set_os_print() not available, needs to be reimplemented
- all the RTOS rodata is loaded into RAM, as it apparently uses some
constants while the flash isn't mapped, so our exception handler can't
work its magic. This should be narrowed down to the minimum possible
at some point.
- with each task having its own stack in RTOS, we probably need change
flash-page buffers from the stack to the heap in a bunch of places.
A single, shared, page buffer *might* be possible if we limit ourselves
to running NodeMCU in a single task.
- there's a ton of junk in the sdk-overrides now; over time the core code
should be updated to not need those shims
2016-05-24 07:05:01 +02:00
|
|
|
#if 0
|
2015-12-29 12:25:37 +01:00
|
|
|
if ( req->secure )
|
|
|
|
espconn_secure_disconnect( conn );
|
|
|
|
else
|
Initial pass at switching to RTOS SDK.
This compiles, links, and starts the RTOS without crashing and burning.
Lua environment does not yet start due to the different task architecture.
Known pain points:
- task implementation needs to be rewritten for RTOS (next up on my TODO)
- secure espconn does not exist, all secure espconn stuff has been #if 0'd
- lwip now built from within the RTOS SDK, but does not appear to include
MDNS support. Investigation needed.
- there is no access to FRC1 NMI, not sure if we ever actually used that
however. Also #if 0'd out for now.
- new timing constraints introduced by the RTOS, all use of ets_delay_us()
and os_delay_us() needs to be reviewed (the tsl2561 driver in particular).
- even more confusion with ets_ vs os_ vs c_ vs non-prefixed versions.
In the long run everything should be switched to non-prefixed versions.
- system_set_os_print() not available, needs to be reimplemented
- all the RTOS rodata is loaded into RAM, as it apparently uses some
constants while the flash isn't mapped, so our exception handler can't
work its magic. This should be narrowed down to the minimum possible
at some point.
- with each task having its own stack in RTOS, we probably need change
flash-page buffers from the stack to the heap in a bunch of places.
A single, shared, page buffer *might* be possible if we limit ourselves
to running NodeMCU in a single task.
- there's a ton of junk in the sdk-overrides now; over time the core code
should be updated to not need those shims
2016-05-24 07:05:01 +02:00
|
|
|
#endif
|
2015-12-29 12:25:37 +01:00
|
|
|
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. */
|
|
|
|
|
2016-05-26 10:36:20 +02:00
|
|
|
free( req->buffer );
|
2015-12-29 12:25:37 +01:00
|
|
|
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" );
|
Initial pass at switching to RTOS SDK.
This compiles, links, and starts the RTOS without crashing and burning.
Lua environment does not yet start due to the different task architecture.
Known pain points:
- task implementation needs to be rewritten for RTOS (next up on my TODO)
- secure espconn does not exist, all secure espconn stuff has been #if 0'd
- lwip now built from within the RTOS SDK, but does not appear to include
MDNS support. Investigation needed.
- there is no access to FRC1 NMI, not sure if we ever actually used that
however. Also #if 0'd out for now.
- new timing constraints introduced by the RTOS, all use of ets_delay_us()
and os_delay_us() needs to be reviewed (the tsl2561 driver in particular).
- even more confusion with ets_ vs os_ vs c_ vs non-prefixed versions.
In the long run everything should be switched to non-prefixed versions.
- system_set_os_print() not available, needs to be reimplemented
- all the RTOS rodata is loaded into RAM, as it apparently uses some
constants while the flash isn't mapped, so our exception handler can't
work its magic. This should be narrowed down to the minimum possible
at some point.
- with each task having its own stack in RTOS, we probably need change
flash-page buffers from the stack to the heap in a bunch of places.
A single, shared, page buffer *might* be possible if we limit ourselves
to running NodeMCU in a single task.
- there's a ton of junk in the sdk-overrides now; over time the core code
should be updated to not need those shims
2016-05-24 07:05:01 +02:00
|
|
|
#if 0
|
2015-12-29 12:25:37 +01:00
|
|
|
if ( req->secure )
|
|
|
|
espconn_secure_send( conn, (uint8_t *) req->post_data, strlen( req->post_data ) );
|
|
|
|
else
|
Initial pass at switching to RTOS SDK.
This compiles, links, and starts the RTOS without crashing and burning.
Lua environment does not yet start due to the different task architecture.
Known pain points:
- task implementation needs to be rewritten for RTOS (next up on my TODO)
- secure espconn does not exist, all secure espconn stuff has been #if 0'd
- lwip now built from within the RTOS SDK, but does not appear to include
MDNS support. Investigation needed.
- there is no access to FRC1 NMI, not sure if we ever actually used that
however. Also #if 0'd out for now.
- new timing constraints introduced by the RTOS, all use of ets_delay_us()
and os_delay_us() needs to be reviewed (the tsl2561 driver in particular).
- even more confusion with ets_ vs os_ vs c_ vs non-prefixed versions.
In the long run everything should be switched to non-prefixed versions.
- system_set_os_print() not available, needs to be reimplemented
- all the RTOS rodata is loaded into RAM, as it apparently uses some
constants while the flash isn't mapped, so our exception handler can't
work its magic. This should be narrowed down to the minimum possible
at some point.
- with each task having its own stack in RTOS, we probably need change
flash-page buffers from the stack to the heap in a bunch of places.
A single, shared, page buffer *might* be possible if we limit ourselves
to running NodeMCU in a single task.
- there's a ton of junk in the sdk-overrides now; over time the core code
should be updated to not need those shims
2016-05-24 07:05:01 +02:00
|
|
|
#endif
|
2015-12-29 12:25:37 +01:00
|
|
|
espconn_send( conn, (uint8_t *) req->post_data, strlen( req->post_data ) );
|
2016-05-26 10:36:20 +02:00
|
|
|
free( req->post_data );
|
2015-12-29 12:25:37 +01:00
|
|
|
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 */
|
|
|
|
{
|
2016-05-26 10:36:20 +02:00
|
|
|
req->headers = (char *)malloc(sizeof(char));
|
2015-12-29 12:25:37 +01:00
|
|
|
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 );
|
|
|
|
|
Initial pass at switching to RTOS SDK.
This compiles, links, and starts the RTOS without crashing and burning.
Lua environment does not yet start due to the different task architecture.
Known pain points:
- task implementation needs to be rewritten for RTOS (next up on my TODO)
- secure espconn does not exist, all secure espconn stuff has been #if 0'd
- lwip now built from within the RTOS SDK, but does not appear to include
MDNS support. Investigation needed.
- there is no access to FRC1 NMI, not sure if we ever actually used that
however. Also #if 0'd out for now.
- new timing constraints introduced by the RTOS, all use of ets_delay_us()
and os_delay_us() needs to be reviewed (the tsl2561 driver in particular).
- even more confusion with ets_ vs os_ vs c_ vs non-prefixed versions.
In the long run everything should be switched to non-prefixed versions.
- system_set_os_print() not available, needs to be reimplemented
- all the RTOS rodata is loaded into RAM, as it apparently uses some
constants while the flash isn't mapped, so our exception handler can't
work its magic. This should be narrowed down to the minimum possible
at some point.
- with each task having its own stack in RTOS, we probably need change
flash-page buffers from the stack to the heap in a bunch of places.
A single, shared, page buffer *might* be possible if we limit ourselves
to running NodeMCU in a single task.
- there's a ton of junk in the sdk-overrides now; over time the core code
should be updated to not need those shims
2016-05-24 07:05:01 +02:00
|
|
|
#if 0
|
2015-12-29 12:25:37 +01:00
|
|
|
if ( req->secure )
|
|
|
|
espconn_secure_send( conn, (uint8_t *) buf, len );
|
|
|
|
else
|
Initial pass at switching to RTOS SDK.
This compiles, links, and starts the RTOS without crashing and burning.
Lua environment does not yet start due to the different task architecture.
Known pain points:
- task implementation needs to be rewritten for RTOS (next up on my TODO)
- secure espconn does not exist, all secure espconn stuff has been #if 0'd
- lwip now built from within the RTOS SDK, but does not appear to include
MDNS support. Investigation needed.
- there is no access to FRC1 NMI, not sure if we ever actually used that
however. Also #if 0'd out for now.
- new timing constraints introduced by the RTOS, all use of ets_delay_us()
and os_delay_us() needs to be reviewed (the tsl2561 driver in particular).
- even more confusion with ets_ vs os_ vs c_ vs non-prefixed versions.
In the long run everything should be switched to non-prefixed versions.
- system_set_os_print() not available, needs to be reimplemented
- all the RTOS rodata is loaded into RAM, as it apparently uses some
constants while the flash isn't mapped, so our exception handler can't
work its magic. This should be narrowed down to the minimum possible
at some point.
- with each task having its own stack in RTOS, we probably need change
flash-page buffers from the stack to the heap in a bunch of places.
A single, shared, page buffer *might* be possible if we limit ourselves
to running NodeMCU in a single task.
- there's a ton of junk in the sdk-overrides now; over time the core code
should be updated to not need those shims
2016-05-24 07:05:01 +02:00
|
|
|
#endif
|
2015-12-29 12:25:37 +01:00
|
|
|
espconn_send( conn, (uint8_t *) buf, len );
|
|
|
|
if(req->headers != NULL)
|
2016-05-26 10:36:20 +02:00
|
|
|
free( req->headers );
|
2015-12-29 12:25:37 +01:00
|
|
|
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 )
|
|
|
|
{
|
2016-05-26 10:36:20 +02:00
|
|
|
free( conn->proto.tcp );
|
2015-12-29 12:25:37 +01:00
|
|
|
}
|
|
|
|
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 )
|
|
|
|
{
|
2016-04-09 13:54:10 +02:00
|
|
|
HTTPCLIENT_DEBUG( "Buffer probably shouldn't be NULL\n" );
|
2015-12-29 12:25:37 +01:00
|
|
|
}
|
|
|
|
else if ( req->buffer[0] != '\0' )
|
|
|
|
{
|
|
|
|
/* FIXME: make sure this is not a partial response, using the Content-Length header. */
|
2016-01-26 09:06:11 +01:00
|
|
|
const char * version_1_0 = "HTTP/1.0 ";
|
|
|
|
const char * version_1_1 = "HTTP/1.1 ";
|
|
|
|
if (( os_strncmp( req->buffer, version_1_0, strlen( version_1_0 ) ) != 0 ) &&
|
|
|
|
( os_strncmp( req->buffer, version_1_1, strlen( version_1_1 ) ) != 0 ))
|
2015-12-29 12:25:37 +01:00
|
|
|
{
|
|
|
|
HTTPCLIENT_DEBUG( "Invalid version in %s\n", req->buffer );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-01-26 09:06:11 +01:00
|
|
|
http_status = atoi( req->buffer + strlen( version_1_0 ) );
|
2016-04-09 13:54:10 +02:00
|
|
|
body = (char *) os_strstr(req->buffer, "\r\n\r\n");
|
|
|
|
|
|
|
|
if (NULL == body) {
|
|
|
|
/* Find missing body */
|
|
|
|
HTTPCLIENT_DEBUG("Body shouldn't be NULL\n");
|
|
|
|
/* To avoid NULL body */
|
|
|
|
body = "";
|
|
|
|
} else {
|
|
|
|
/* Skip CR & LF */
|
|
|
|
body = body + 4;
|
|
|
|
}
|
|
|
|
|
2015-12-29 12:25:37 +01:00
|
|
|
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 );
|
|
|
|
}
|
2016-04-09 13:54:10 +02:00
|
|
|
if (req->buffer) {
|
2016-05-26 10:36:20 +02:00
|
|
|
free( req->buffer );
|
2016-04-09 13:54:10 +02:00
|
|
|
}
|
2016-05-26 10:36:20 +02:00
|
|
|
free( req->hostname );
|
|
|
|
free( req->method );
|
|
|
|
free( req->path );
|
|
|
|
free( req );
|
2015-12-29 12:25:37 +01:00
|
|
|
}
|
|
|
|
/* Fix memory leak. */
|
|
|
|
espconn_delete( conn );
|
2016-05-26 10:36:20 +02:00
|
|
|
free( conn );
|
2015-12-29 12:25:37 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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 */
|
Initial pass at switching to RTOS SDK.
This compiles, links, and starts the RTOS without crashing and burning.
Lua environment does not yet start due to the different task architecture.
Known pain points:
- task implementation needs to be rewritten for RTOS (next up on my TODO)
- secure espconn does not exist, all secure espconn stuff has been #if 0'd
- lwip now built from within the RTOS SDK, but does not appear to include
MDNS support. Investigation needed.
- there is no access to FRC1 NMI, not sure if we ever actually used that
however. Also #if 0'd out for now.
- new timing constraints introduced by the RTOS, all use of ets_delay_us()
and os_delay_us() needs to be reviewed (the tsl2561 driver in particular).
- even more confusion with ets_ vs os_ vs c_ vs non-prefixed versions.
In the long run everything should be switched to non-prefixed versions.
- system_set_os_print() not available, needs to be reimplemented
- all the RTOS rodata is loaded into RAM, as it apparently uses some
constants while the flash isn't mapped, so our exception handler can't
work its magic. This should be narrowed down to the minimum possible
at some point.
- with each task having its own stack in RTOS, we probably need change
flash-page buffers from the stack to the heap in a bunch of places.
A single, shared, page buffer *might* be possible if we limit ourselves
to running NodeMCU in a single task.
- there's a ton of junk in the sdk-overrides now; over time the core code
should be updated to not need those shims
2016-05-24 07:05:01 +02:00
|
|
|
#if 0
|
2015-12-29 12:25:37 +01:00
|
|
|
if ( req->secure )
|
|
|
|
espconn_secure_disconnect( conn );
|
|
|
|
else
|
Initial pass at switching to RTOS SDK.
This compiles, links, and starts the RTOS without crashing and burning.
Lua environment does not yet start due to the different task architecture.
Known pain points:
- task implementation needs to be rewritten for RTOS (next up on my TODO)
- secure espconn does not exist, all secure espconn stuff has been #if 0'd
- lwip now built from within the RTOS SDK, but does not appear to include
MDNS support. Investigation needed.
- there is no access to FRC1 NMI, not sure if we ever actually used that
however. Also #if 0'd out for now.
- new timing constraints introduced by the RTOS, all use of ets_delay_us()
and os_delay_us() needs to be reviewed (the tsl2561 driver in particular).
- even more confusion with ets_ vs os_ vs c_ vs non-prefixed versions.
In the long run everything should be switched to non-prefixed versions.
- system_set_os_print() not available, needs to be reimplemented
- all the RTOS rodata is loaded into RAM, as it apparently uses some
constants while the flash isn't mapped, so our exception handler can't
work its magic. This should be narrowed down to the minimum possible
at some point.
- with each task having its own stack in RTOS, we probably need change
flash-page buffers from the stack to the heap in a bunch of places.
A single, shared, page buffer *might* be possible if we limit ourselves
to running NodeMCU in a single task.
- there's a ton of junk in the sdk-overrides now; over time the core code
should be updated to not need those shims
2016-05-24 07:05:01 +02:00
|
|
|
#endif
|
2015-12-29 12:25:37 +01:00
|
|
|
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, "" );
|
|
|
|
}
|
2016-05-26 10:36:20 +02:00
|
|
|
free( req );
|
2015-12-29 12:25:37 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
HTTPCLIENT_DEBUG( "DNS found %s " IPSTR "\n", hostname, IP2STR( addr ) );
|
|
|
|
|
2016-05-26 10:36:20 +02:00
|
|
|
struct espconn * conn = (struct espconn *) zalloc( sizeof(struct espconn) );
|
2015-12-29 12:25:37 +01:00
|
|
|
conn->type = ESPCONN_TCP;
|
|
|
|
conn->state = ESPCONN_NONE;
|
2016-05-26 10:36:20 +02:00
|
|
|
conn->proto.tcp = (esp_tcp *) zalloc( sizeof(esp_tcp) );
|
2015-12-29 12:25:37 +01:00
|
|
|
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 );
|
|
|
|
|
Initial pass at switching to RTOS SDK.
This compiles, links, and starts the RTOS without crashing and burning.
Lua environment does not yet start due to the different task architecture.
Known pain points:
- task implementation needs to be rewritten for RTOS (next up on my TODO)
- secure espconn does not exist, all secure espconn stuff has been #if 0'd
- lwip now built from within the RTOS SDK, but does not appear to include
MDNS support. Investigation needed.
- there is no access to FRC1 NMI, not sure if we ever actually used that
however. Also #if 0'd out for now.
- new timing constraints introduced by the RTOS, all use of ets_delay_us()
and os_delay_us() needs to be reviewed (the tsl2561 driver in particular).
- even more confusion with ets_ vs os_ vs c_ vs non-prefixed versions.
In the long run everything should be switched to non-prefixed versions.
- system_set_os_print() not available, needs to be reimplemented
- all the RTOS rodata is loaded into RAM, as it apparently uses some
constants while the flash isn't mapped, so our exception handler can't
work its magic. This should be narrowed down to the minimum possible
at some point.
- with each task having its own stack in RTOS, we probably need change
flash-page buffers from the stack to the heap in a bunch of places.
A single, shared, page buffer *might* be possible if we limit ourselves
to running NodeMCU in a single task.
- there's a ton of junk in the sdk-overrides now; over time the core code
should be updated to not need those shims
2016-05-24 07:05:01 +02:00
|
|
|
#if 0
|
2015-12-29 12:25:37 +01:00
|
|
|
if ( req->secure )
|
|
|
|
{
|
|
|
|
espconn_secure_set_size( ESPCONN_CLIENT, 5120 ); /* set SSL buffer size */
|
|
|
|
espconn_secure_connect( conn );
|
|
|
|
}
|
|
|
|
else
|
Initial pass at switching to RTOS SDK.
This compiles, links, and starts the RTOS without crashing and burning.
Lua environment does not yet start due to the different task architecture.
Known pain points:
- task implementation needs to be rewritten for RTOS (next up on my TODO)
- secure espconn does not exist, all secure espconn stuff has been #if 0'd
- lwip now built from within the RTOS SDK, but does not appear to include
MDNS support. Investigation needed.
- there is no access to FRC1 NMI, not sure if we ever actually used that
however. Also #if 0'd out for now.
- new timing constraints introduced by the RTOS, all use of ets_delay_us()
and os_delay_us() needs to be reviewed (the tsl2561 driver in particular).
- even more confusion with ets_ vs os_ vs c_ vs non-prefixed versions.
In the long run everything should be switched to non-prefixed versions.
- system_set_os_print() not available, needs to be reimplemented
- all the RTOS rodata is loaded into RAM, as it apparently uses some
constants while the flash isn't mapped, so our exception handler can't
work its magic. This should be narrowed down to the minimum possible
at some point.
- with each task having its own stack in RTOS, we probably need change
flash-page buffers from the stack to the heap in a bunch of places.
A single, shared, page buffer *might* be possible if we limit ourselves
to running NodeMCU in a single task.
- there's a ton of junk in the sdk-overrides now; over time the core code
should be updated to not need those shims
2016-05-24 07:05:01 +02:00
|
|
|
#endif
|
2015-12-29 12:25:37 +01:00
|
|
|
{
|
|
|
|
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" );
|
|
|
|
|
2016-05-26 10:36:20 +02:00
|
|
|
request_args_t * req = (request_args_t *) zalloc( sizeof(request_args_t) );
|
2015-12-29 12:25:37 +01:00
|
|
|
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;
|
2016-05-26 10:36:20 +02:00
|
|
|
req->buffer = (char *) malloc( 1 );
|
2015-12-29 12:25:37 +01:00
|
|
|
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. */
|
|
|
|
}
|
|
|
|
|
2016-03-07 02:25:05 +01:00
|
|
|
if (path - url >= sizeof(hostname)) {
|
|
|
|
HTTPCLIENT_DEBUG( "hostname is too long %s\n", url );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-12-29 12:25:37 +01:00
|
|
|
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 );
|
|
|
|
}
|
|
|
|
}
|