|
@@ -20,6 +20,8 @@
|
|
|
#include "httpclient.h"
|
|
|
#include "stdlib.h"
|
|
|
|
|
|
+#define REDIRECTION_FOLLOW_MAX 20
|
|
|
+
|
|
|
/* Internal state. */
|
|
|
typedef struct request_args_t {
|
|
|
char * hostname;
|
|
@@ -31,6 +33,7 @@ typedef struct request_args_t {
|
|
|
char * post_data;
|
|
|
char * buffer;
|
|
|
int buffer_size;
|
|
|
+ int redirect_follow_count;
|
|
|
int timeout;
|
|
|
os_timer_t timeout_timer;
|
|
|
http_callback_t callback_handle;
|
|
@@ -307,26 +310,92 @@ static void ICACHE_FLASH_ATTR http_disconnect_callback( void * arg )
|
|
|
else
|
|
|
{
|
|
|
http_status = atoi( req->buffer + strlen( version_1_0 ) );
|
|
|
- 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;
|
|
|
+
|
|
|
+ char *locationOffset = (char *) os_strstr( req->buffer, "Location:" );
|
|
|
+ if ( locationOffset == NULL ) {
|
|
|
+ locationOffset = (char *) os_strstr( req->buffer, "location:" );
|
|
|
}
|
|
|
-
|
|
|
- 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 ( locationOffset != NULL && http_status >= 300 && http_status <= 308 ) {
|
|
|
+ if (req->redirect_follow_count < REDIRECTION_FOLLOW_MAX) {
|
|
|
+ locationOffset += strlen("location:");
|
|
|
+
|
|
|
+ while (*locationOffset == ' ') { // skip url leading white-space
|
|
|
+ locationOffset++;
|
|
|
+ }
|
|
|
+
|
|
|
+ char *locationOffsetEnd = (char *) os_strstr(locationOffset, "\r\n");
|
|
|
+ if ( locationOffsetEnd == NULL ) {
|
|
|
+ HTTPCLIENT_DEBUG( "Found Location header but was incomplete\n" );
|
|
|
+ http_status = -1;
|
|
|
+ } else {
|
|
|
+ *locationOffsetEnd = '\0';
|
|
|
+ req->redirect_follow_count++;
|
|
|
+
|
|
|
+ // Check if url is absolute
|
|
|
+ bool url_has_protocol =
|
|
|
+ os_strncmp( locationOffset, "http://", strlen( "http://" ) ) == 0 ||
|
|
|
+ os_strncmp( locationOffset, "https://", strlen( "https://" ) ) == 0;
|
|
|
+
|
|
|
+ if ( url_has_protocol ) {
|
|
|
+ http_request( locationOffset, req->method, req->headers,
|
|
|
+ req->post_data, req->callback_handle, req->redirect_follow_count );
|
|
|
+ } else {
|
|
|
+ if ( os_strncmp( locationOffset, "/", 1 ) == 0) { // relative and full path
|
|
|
+ http_raw_request( req->hostname, req->port, req->secure, req->method,
|
|
|
+ locationOffset, req->headers, req->post_data, req->callback_handle, req->redirect_follow_count );
|
|
|
+ } else { // relative and relative path
|
|
|
+
|
|
|
+ // find last /
|
|
|
+ const char *pathFolderEnd = strrchr(req->path, '/');
|
|
|
+
|
|
|
+ int pathFolderLength = pathFolderEnd - req->path;
|
|
|
+ pathFolderLength++; // use the '/'
|
|
|
+ int locationLength = strlen(locationOffset);
|
|
|
+ locationLength++; // use the '\0'
|
|
|
+
|
|
|
+ // append pathFolder with given relative path
|
|
|
+ char *completeRelativePath = (char *) os_malloc(pathFolderLength + locationLength);
|
|
|
+ os_memcpy( completeRelativePath, req->path, pathFolderLength );
|
|
|
+ os_memcpy( completeRelativePath + pathFolderLength, locationOffset, locationLength);
|
|
|
+
|
|
|
+ http_raw_request( req->hostname, req->port, req->secure, req->method,
|
|
|
+ completeRelativePath, req->headers, req->post_data, req->callback_handle, req->redirect_follow_count );
|
|
|
+
|
|
|
+ os_free( completeRelativePath );
|
|
|
+ }
|
|
|
+ }
|
|
|
+ http_free_req( req );
|
|
|
+ espconn_delete( conn );
|
|
|
+ os_free( conn );
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ HTTPCLIENT_DEBUG("Too many redirections\n");
|
|
|
+ http_status = -1;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+
|
|
|
+ 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 );
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -419,7 +488,7 @@ static void ICACHE_FLASH_ATTR http_dns_callback( const char * hostname, ip_addr_
|
|
|
}
|
|
|
|
|
|
|
|
|
-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 )
|
|
|
+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, int redirect_follow_count )
|
|
|
{
|
|
|
HTTPCLIENT_DEBUG( "DNS request\n" );
|
|
|
|
|
@@ -436,6 +505,7 @@ void ICACHE_FLASH_ATTR http_raw_request( const char * hostname, int port, bool s
|
|
|
req->buffer[0] = '\0'; /* Empty string. */
|
|
|
req->callback_handle = callback_handle;
|
|
|
req->timeout = HTTP_REQUEST_TIMEOUT_MS;
|
|
|
+ req->redirect_follow_count = redirect_follow_count;
|
|
|
|
|
|
ip_addr_t addr;
|
|
|
err_t error = espconn_gethostbyname( (struct espconn *) req, /* It seems we don't need a real espconn pointer here. */
|
|
@@ -468,7 +538,7 @@ void ICACHE_FLASH_ATTR http_raw_request( const char * hostname, int port, bool s
|
|
|
* <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 )
|
|
|
+void ICACHE_FLASH_ATTR http_request( const char * url, const char * method, const char * headers, const char * post_data, http_callback_t callback_handle, int redirect_follow_count )
|
|
|
{
|
|
|
/*
|
|
|
* FIXME: handle HTTP auth with http://user:pass@host/
|
|
@@ -541,7 +611,7 @@ void ICACHE_FLASH_ATTR http_request( const char * url, const char * method, cons
|
|
|
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 );
|
|
|
+ http_raw_request( hostname, port, secure, method, path, headers, post_data, callback_handle, redirect_follow_count);
|
|
|
}
|
|
|
|
|
|
|
|
@@ -552,25 +622,25 @@ void ICACHE_FLASH_ATTR http_request( const char * url, const char * method, cons
|
|
|
*/
|
|
|
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 );
|
|
|
+ http_request( url, "POST", headers, post_data, callback_handle, 0 );
|
|
|
}
|
|
|
|
|
|
|
|
|
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 );
|
|
|
+ http_request( url, "GET", headers, NULL, callback_handle, 0 );
|
|
|
}
|
|
|
|
|
|
|
|
|
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 );
|
|
|
+ http_request( url, "DELETE", headers, post_data, callback_handle, 0 );
|
|
|
}
|
|
|
|
|
|
|
|
|
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 );
|
|
|
+ http_request( url, "PUT", headers, post_data, callback_handle, 0 );
|
|
|
}
|
|
|
|
|
|
|