httpclient.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545
  1. /*
  2. * ----------------------------------------------------------------------------
  3. * "THE BEER-WARE LICENSE" (Revision 42):
  4. * Martin d'Allens <martin.dallens@gmail.com> wrote this file. As long as you retain
  5. * this notice you can do whatever you want with this stuff. If we meet some day,
  6. * and you think this stuff is worth it, you can buy me a beer in return.
  7. * ----------------------------------------------------------------------------
  8. */
  9. /*
  10. * FIXME: sprintf->snprintf everywhere.
  11. * FIXME: support null characters in responses.
  12. */
  13. #include "osapi.h"
  14. #include "user_interface.h"
  15. #include "espconn.h"
  16. #include "mem.h"
  17. #include "limits.h"
  18. #include "httpclient.h"
  19. #include "stdlib.h"
  20. /* Internal state. */
  21. typedef struct request_args_t {
  22. char * hostname;
  23. int port;
  24. bool secure;
  25. char * method;
  26. char * path;
  27. char * headers;
  28. char * post_data;
  29. char * buffer;
  30. int buffer_size;
  31. int timeout;
  32. os_timer_t timeout_timer;
  33. http_callback_t callback_handle;
  34. } request_args_t;
  35. static char * ICACHE_FLASH_ATTR esp_strdup( const char * str )
  36. {
  37. if ( str == NULL )
  38. {
  39. return(NULL);
  40. }
  41. char * new_str = (char *) os_malloc( os_strlen( str ) + 1 ); /* 1 for null character */
  42. if ( new_str == NULL )
  43. {
  44. HTTPCLIENT_DEBUG( "esp_strdup: malloc error" );
  45. return(NULL);
  46. }
  47. os_strcpy( new_str, str );
  48. return(new_str);
  49. }
  50. static int ICACHE_FLASH_ATTR
  51. esp_isupper( char c )
  52. {
  53. return(c >= 'A' && c <= 'Z');
  54. }
  55. static int ICACHE_FLASH_ATTR
  56. esp_isalpha( char c )
  57. {
  58. return( (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') );
  59. }
  60. static int ICACHE_FLASH_ATTR
  61. esp_isspace( char c )
  62. {
  63. return(c == ' ' || c == '\t' || c == '\n' || c == '\12');
  64. }
  65. static int ICACHE_FLASH_ATTR
  66. esp_isdigit( char c )
  67. {
  68. return(c >= '0' && c <= '9');
  69. }
  70. static int ICACHE_FLASH_ATTR http_chunked_decode( const char * chunked, char * decode )
  71. {
  72. int i = 0, j = 0;
  73. int decode_size = 0;
  74. char *str = (char *) chunked;
  75. do
  76. {
  77. char * endstr;
  78. /* [chunk-size] */
  79. i = strtoul( str + j, NULL, 16 );
  80. HTTPCLIENT_DEBUG( "Chunk Size:%d\r\n", i );
  81. if ( i <= 0 )
  82. break;
  83. /* [chunk-size-end-ptr] */
  84. endstr = (char *) os_strstr( str + j, "\r\n" );
  85. /* [chunk-ext] */
  86. j += endstr - (str + j);
  87. /* [CRLF] */
  88. j += 2;
  89. /* [chunk-data] */
  90. decode_size += i;
  91. os_memcpy( (char *) &decode[decode_size - i], (char *) str + j, i );
  92. j += i;
  93. /* [CRLF] */
  94. j += 2;
  95. }
  96. while ( true );
  97. /*
  98. *
  99. * footer CRLF
  100. *
  101. */
  102. return(j);
  103. }
  104. static void ICACHE_FLASH_ATTR http_receive_callback( void * arg, char * buf, unsigned short len )
  105. {
  106. struct espconn * conn = (struct espconn *) arg;
  107. request_args_t * req = (request_args_t *) conn->reverse;
  108. if ( req->buffer == NULL )
  109. {
  110. return;
  111. }
  112. /* Let's do the equivalent of a realloc(). */
  113. const int new_size = req->buffer_size + len;
  114. char * new_buffer;
  115. if ( new_size > BUFFER_SIZE_MAX || NULL == (new_buffer = (char *) os_malloc( new_size ) ) )
  116. {
  117. HTTPCLIENT_DEBUG( "Response too long (%d)\n", new_size );
  118. req->buffer[0] = '\0'; /* Discard the buffer to avoid using an incomplete response. */
  119. if ( req->secure )
  120. espconn_secure_disconnect( conn );
  121. else
  122. espconn_disconnect( conn );
  123. return; /* The disconnect callback will be called. */
  124. }
  125. os_memcpy( new_buffer, req->buffer, req->buffer_size );
  126. os_memcpy( new_buffer + req->buffer_size - 1 /*overwrite the null character*/, buf, len ); /* Append new data. */
  127. new_buffer[new_size - 1] = '\0'; /* Make sure there is an end of string. */
  128. os_free( req->buffer );
  129. req->buffer = new_buffer;
  130. req->buffer_size = new_size;
  131. }
  132. static void ICACHE_FLASH_ATTR http_send_callback( void * arg )
  133. {
  134. struct espconn * conn = (struct espconn *) arg;
  135. request_args_t * req = (request_args_t *) conn->reverse;
  136. if ( req->post_data == NULL )
  137. {
  138. HTTPCLIENT_DEBUG( "All sent\n" );
  139. }
  140. else
  141. {
  142. /* The headers were sent, now send the contents. */
  143. HTTPCLIENT_DEBUG( "Sending request body\n" );
  144. if ( req->secure )
  145. espconn_secure_send( conn, (uint8_t *) req->post_data, strlen( req->post_data ) );
  146. else
  147. espconn_send( conn, (uint8_t *) req->post_data, strlen( req->post_data ) );
  148. os_free( req->post_data );
  149. req->post_data = NULL;
  150. }
  151. }
  152. static void ICACHE_FLASH_ATTR http_connect_callback( void * arg )
  153. {
  154. HTTPCLIENT_DEBUG( "Connected\n" );
  155. struct espconn * conn = (struct espconn *) arg;
  156. request_args_t * req = (request_args_t *) conn->reverse;
  157. espconn_regist_recvcb( conn, http_receive_callback );
  158. espconn_regist_sentcb( conn, http_send_callback );
  159. char post_headers[32] = "";
  160. if ( req->post_data != NULL ) /* If there is data then add Content-Length header. */
  161. {
  162. os_sprintf( post_headers, "Content-Length: %d\r\n", strlen( req->post_data ) );
  163. }
  164. if(req->headers == NULL) /* Avoid NULL pointer, it may cause exception */
  165. {
  166. req->headers = (char *)os_malloc(sizeof(char));
  167. req->headers[0] = '\0';
  168. }
  169. char buf[69 + strlen( req->method ) + strlen( req->path ) + strlen( req->hostname ) +
  170. strlen( req->headers ) + strlen( post_headers )];
  171. int len = os_sprintf( buf,
  172. "%s %s HTTP/1.1\r\n"
  173. "Host: %s:%d\r\n"
  174. "Connection: close\r\n"
  175. "User-Agent: ESP8266\r\n"
  176. "%s"
  177. "%s"
  178. "\r\n",
  179. req->method, req->path, req->hostname, req->port, req->headers, post_headers );
  180. if ( req->secure )
  181. espconn_secure_send( conn, (uint8_t *) buf, len );
  182. else
  183. espconn_send( conn, (uint8_t *) buf, len );
  184. if(req->headers != NULL)
  185. os_free( req->headers );
  186. req->headers = NULL;
  187. HTTPCLIENT_DEBUG( "Sending request header\n" );
  188. }
  189. static void ICACHE_FLASH_ATTR http_disconnect_callback( void * arg )
  190. {
  191. HTTPCLIENT_DEBUG( "Disconnected\n" );
  192. struct espconn *conn = (struct espconn *) arg;
  193. if ( conn == NULL )
  194. {
  195. return;
  196. }
  197. if ( conn->proto.tcp != NULL )
  198. {
  199. os_free( conn->proto.tcp );
  200. }
  201. if ( conn->reverse != NULL )
  202. {
  203. request_args_t * req = (request_args_t *) conn->reverse;
  204. int http_status = -1;
  205. char * body = "";
  206. // Turn off timeout timer
  207. os_timer_disarm( &(req->timeout_timer) );
  208. if ( req->buffer == NULL )
  209. {
  210. HTTPCLIENT_DEBUG( "Buffer probably shouldn't be NULL\n" );
  211. }
  212. else if ( req->buffer[0] != '\0' )
  213. {
  214. /* FIXME: make sure this is not a partial response, using the Content-Length header. */
  215. const char * version_1_0 = "HTTP/1.0 ";
  216. const char * version_1_1 = "HTTP/1.1 ";
  217. if (( os_strncmp( req->buffer, version_1_0, strlen( version_1_0 ) ) != 0 ) &&
  218. ( os_strncmp( req->buffer, version_1_1, strlen( version_1_1 ) ) != 0 ))
  219. {
  220. HTTPCLIENT_DEBUG( "Invalid version in %s\n", req->buffer );
  221. }
  222. else
  223. {
  224. http_status = atoi( req->buffer + strlen( version_1_0 ) );
  225. body = (char *) os_strstr(req->buffer, "\r\n\r\n");
  226. if (NULL == body) {
  227. /* Find missing body */
  228. HTTPCLIENT_DEBUG("Body shouldn't be NULL\n");
  229. /* To avoid NULL body */
  230. body = "";
  231. } else {
  232. /* Skip CR & LF */
  233. body = body + 4;
  234. }
  235. if ( os_strstr( req->buffer, "Transfer-Encoding: chunked" ) )
  236. {
  237. int body_size = req->buffer_size - (body - req->buffer);
  238. char chunked_decode_buffer[body_size];
  239. os_memset( chunked_decode_buffer, 0, body_size );
  240. /* Chuncked data */
  241. http_chunked_decode( body, chunked_decode_buffer );
  242. os_memcpy( body, chunked_decode_buffer, body_size );
  243. }
  244. }
  245. }
  246. if ( req->callback_handle != NULL ) /* Callback is optional. */
  247. {
  248. req->callback_handle( body, http_status, req->buffer );
  249. }
  250. if (req->buffer) {
  251. os_free( req->buffer );
  252. }
  253. os_free( req->hostname );
  254. os_free( req->method );
  255. os_free( req->path );
  256. os_free( req );
  257. }
  258. /* Fix memory leak. */
  259. espconn_delete( conn );
  260. os_free( conn );
  261. }
  262. static void ICACHE_FLASH_ATTR http_error_callback( void *arg, sint8 errType )
  263. {
  264. HTTPCLIENT_DEBUG( "Disconnected with error\n" );
  265. http_disconnect_callback( arg );
  266. }
  267. static void ICACHE_FLASH_ATTR http_timeout_callback( void *arg )
  268. {
  269. HTTPCLIENT_DEBUG( "Connection timeout\n" );
  270. struct espconn * conn = (struct espconn *) arg;
  271. if ( conn == NULL )
  272. {
  273. return;
  274. }
  275. if ( conn->reverse == NULL )
  276. {
  277. return;
  278. }
  279. request_args_t * req = (request_args_t *) conn->reverse;
  280. /* Call disconnect */
  281. if ( req->secure )
  282. espconn_secure_disconnect( conn );
  283. else
  284. espconn_disconnect( conn );
  285. }
  286. static void ICACHE_FLASH_ATTR http_dns_callback( const char * hostname, ip_addr_t * addr, void * arg )
  287. {
  288. request_args_t * req = (request_args_t *) arg;
  289. if ( addr == NULL )
  290. {
  291. HTTPCLIENT_DEBUG( "DNS failed for %s\n", hostname );
  292. if ( req->callback_handle != NULL )
  293. {
  294. req->callback_handle( "", -1, "" );
  295. }
  296. os_free( req );
  297. }
  298. else
  299. {
  300. HTTPCLIENT_DEBUG( "DNS found %s " IPSTR "\n", hostname, IP2STR( addr ) );
  301. struct espconn * conn = (struct espconn *) os_zalloc( sizeof(struct espconn) );
  302. conn->type = ESPCONN_TCP;
  303. conn->state = ESPCONN_NONE;
  304. conn->proto.tcp = (esp_tcp *) os_zalloc( sizeof(esp_tcp) );
  305. conn->proto.tcp->local_port = espconn_port();
  306. conn->proto.tcp->remote_port = req->port;
  307. conn->reverse = req;
  308. os_memcpy( conn->proto.tcp->remote_ip, addr, 4 );
  309. espconn_regist_connectcb( conn, http_connect_callback );
  310. espconn_regist_disconcb( conn, http_disconnect_callback );
  311. espconn_regist_reconcb( conn, http_error_callback );
  312. /* Set connection timeout timer */
  313. os_timer_disarm( &(req->timeout_timer) );
  314. os_timer_setfn( &(req->timeout_timer), (os_timer_func_t *) http_timeout_callback, conn );
  315. os_timer_arm( &(req->timeout_timer), req->timeout, false );
  316. if ( req->secure )
  317. {
  318. espconn_secure_set_size( ESPCONN_CLIENT, 5120 ); /* set SSL buffer size */
  319. espconn_secure_connect( conn );
  320. }
  321. else
  322. {
  323. espconn_connect( conn );
  324. }
  325. }
  326. }
  327. 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 )
  328. {
  329. HTTPCLIENT_DEBUG( "DNS request\n" );
  330. request_args_t * req = (request_args_t *) os_zalloc( sizeof(request_args_t) );
  331. req->hostname = esp_strdup( hostname );
  332. req->port = port;
  333. req->secure = secure;
  334. req->method = esp_strdup( method );
  335. req->path = esp_strdup( path );
  336. req->headers = esp_strdup( headers );
  337. req->post_data = esp_strdup( post_data );
  338. req->buffer_size = 1;
  339. req->buffer = (char *) os_malloc( 1 );
  340. req->buffer[0] = '\0'; /* Empty string. */
  341. req->callback_handle = callback_handle;
  342. req->timeout = HTTP_REQUEST_TIMEOUT_MS;
  343. ip_addr_t addr;
  344. err_t error = espconn_gethostbyname( (struct espconn *) req, /* It seems we don't need a real espconn pointer here. */
  345. hostname, &addr, http_dns_callback );
  346. if ( error == ESPCONN_INPROGRESS )
  347. {
  348. HTTPCLIENT_DEBUG( "DNS pending\n" );
  349. }
  350. else if ( error == ESPCONN_OK )
  351. {
  352. /* Already in the local names table (or hostname was an IP address), execute the callback ourselves. */
  353. http_dns_callback( hostname, &addr, req );
  354. }
  355. else
  356. {
  357. if ( error == ESPCONN_ARG )
  358. {
  359. HTTPCLIENT_DEBUG( "DNS arg error %s\n", hostname );
  360. }else {
  361. HTTPCLIENT_DEBUG( "DNS error code %d\n", error );
  362. }
  363. http_dns_callback( hostname, NULL, req ); /* Handle all DNS errors the same way. */
  364. }
  365. }
  366. /*
  367. * Parse an URL of the form http://host:port/path
  368. * <host> can be a hostname or an IP address
  369. * <port> is optional
  370. */
  371. void ICACHE_FLASH_ATTR http_request( const char * url, const char * method, const char * headers, const char * post_data, http_callback_t callback_handle )
  372. {
  373. /*
  374. * FIXME: handle HTTP auth with http://user:pass@host/
  375. * FIXME: get rid of the #anchor part if present.
  376. */
  377. char hostname[128] = "";
  378. int port = 80;
  379. bool secure = false;
  380. bool is_http = os_strncmp( url, "http://", strlen( "http://" ) ) == 0;
  381. bool is_https = os_strncmp( url, "https://", strlen( "https://" ) ) == 0;
  382. if ( is_http )
  383. url += strlen( "http://" ); /* Get rid of the protocol. */
  384. else if ( is_https )
  385. {
  386. port = 443;
  387. secure = true;
  388. url += strlen( "https://" ); /* Get rid of the protocol. */
  389. }
  390. else
  391. {
  392. HTTPCLIENT_DEBUG( "URL is not HTTP or HTTPS %s\n", url );
  393. return;
  394. }
  395. char * path = os_strchr( url, '/' );
  396. if ( path == NULL )
  397. {
  398. path = os_strchr( url, '\0' ); /* Pointer to end of string. */
  399. }
  400. char * colon = os_strchr( url, ':' );
  401. if ( colon > path )
  402. {
  403. colon = NULL; /* Limit the search to characters before the path. */
  404. }
  405. if (path - url >= sizeof(hostname)) {
  406. HTTPCLIENT_DEBUG( "hostname is too long %s\n", url );
  407. return;
  408. }
  409. if ( colon == NULL ) /* The port is not present. */
  410. {
  411. os_memcpy( hostname, url, path - url );
  412. hostname[path - url] = '\0';
  413. }
  414. else
  415. {
  416. port = atoi( colon + 1 );
  417. if ( port == 0 )
  418. {
  419. HTTPCLIENT_DEBUG( "Port error %s\n", url );
  420. return;
  421. }
  422. os_memcpy( hostname, url, colon - url );
  423. hostname[colon - url] = '\0';
  424. }
  425. if ( path[0] == '\0' ) /* Empty path is not allowed. */
  426. {
  427. path = "/";
  428. }
  429. HTTPCLIENT_DEBUG( "hostname=%s\n", hostname );
  430. HTTPCLIENT_DEBUG( "port=%d\n", port );
  431. HTTPCLIENT_DEBUG( "method=%s\n", method );
  432. HTTPCLIENT_DEBUG( "path=%s\n", path );
  433. http_raw_request( hostname, port, secure, method, path, headers, post_data, callback_handle );
  434. }
  435. /*
  436. * Parse an URL of the form http://host:port/path
  437. * <host> can be a hostname or an IP address
  438. * <port> is optional
  439. */
  440. void ICACHE_FLASH_ATTR http_post( const char * url, const char * headers, const char * post_data, http_callback_t callback_handle )
  441. {
  442. http_request( url, "POST", headers, post_data, callback_handle );
  443. }
  444. void ICACHE_FLASH_ATTR http_get( const char * url, const char * headers, http_callback_t callback_handle )
  445. {
  446. http_request( url, "GET", headers, NULL, callback_handle );
  447. }
  448. void ICACHE_FLASH_ATTR http_delete( const char * url, const char * headers, const char * post_data, http_callback_t callback_handle )
  449. {
  450. http_request( url, "DELETE", headers, post_data, callback_handle );
  451. }
  452. void ICACHE_FLASH_ATTR http_put( const char * url, const char * headers, const char * post_data, http_callback_t callback_handle )
  453. {
  454. http_request( url, "PUT", headers, post_data, callback_handle );
  455. }
  456. void ICACHE_FLASH_ATTR http_callback_example( char * response, int http_status, char * full_response )
  457. {
  458. os_printf( "http_status=%d\n", http_status );
  459. if ( http_status != HTTP_STATUS_GENERIC_ERROR )
  460. {
  461. os_printf( "strlen(full_response)=%d\n", strlen( full_response ) );
  462. os_printf( "response=%s<EOF>\n", response );
  463. }
  464. }