@@ -90,27 +90,45 @@ static const char dns_body[] = { 0x00, 0x01, 0x00, 0x01,
90
90
91
91
static const char http_html_gz_filename [] = "enduser_setup.html.gz" ;
92
92
static const char http_html_filename [] = "enduser_setup.html" ;
93
- static const char http_header_200 [] = "HTTP/1.1 200 OK\r\nCache-control:no-cache\r\nConnection:close\r\nContent-Type:text/html\r\n" ; /* Note single \r\n here! */
93
+ static const char http_header_200 [] = "HTTP/1.1 200 OK\r\nCache-control:no-cache\r\nConnection:close\r\nContent-Type:text/html; charset=utf-8 \r\n" ; /* Note single \r\n here! */
94
94
static const char http_header_204 [] = "HTTP/1.1 204 No Content\r\nContent-Length:0\r\nConnection:close\r\n\r\n" ;
95
95
static const char http_header_302 [] = "HTTP/1.1 302 Moved\r\nLocation: /\r\nContent-Length:0\r\nConnection:close\r\n\r\n" ;
96
96
static const char http_header_302_trying [] = "HTTP/1.1 302 Moved\r\nLocation: /?trying=true\r\nContent-Length:0\r\nConnection:close\r\n\r\n" ;
97
97
static const char http_header_400 [] = "HTTP/1.1 400 Bad request\r\nContent-Length:0\r\nConnection:close\r\n\r\n" ;
98
- static const char http_header_404 [] = "HTTP/1.1 404 Not found\r\nContent-Length:0 \r\nConnection:close\r\n\r\n" ;
98
+ static const char http_header_404 [] = "HTTP/1.1 404 Not found\r\nContent-Length:10 \r\nConnection:close\r\n\r\nNot found \n" ;
99
99
static const char http_header_405 [] = "HTTP/1.1 405 Method Not Allowed\r\nContent-Length:0\r\nConnection:close\r\n\r\n" ;
100
- static const char http_header_500 [] = "HTTP/1.1 500 Internal Error\r\nContent-Length:0 \r\nConnection:close\r\n\r\n" ;
100
+ static const char http_header_500 [] = "HTTP/1.1 500 Internal Error\r\nContent-Length:6 \r\nConnection:close\r\n\r\nError \n" ;
101
101
102
102
static const char http_header_content_len_fmt [] = "Content-length:%5d\r\n\r\n" ;
103
103
static const char http_html_gzip_contentencoding [] = "Content-Encoding: gzip\r\n" ;
104
104
105
105
/* Externally defined: static const char enduser_setup_html_default[] = ... */
106
106
#include "enduser_setup/enduser_setup.html.gz.def.h"
107
107
108
+ // The tcp_arg can be either a pointer to the scan_listener_t or http_request_buffer_t.
109
+ // The enum defines which one it is.
110
+ typedef enum {
111
+ SCAN_LISTENER_STRUCT_TYPE = 1 ,
112
+ HTTP_REQUEST_BUFFER_STRUCT_TYPE = 2
113
+ } struct_type_t ;
114
+
115
+ typedef struct {
116
+ struct_type_t struct_type ;
117
+ } tcp_arg_t ;
118
+
108
119
typedef struct scan_listener
109
120
{
121
+ struct_type_t struct_type ;
110
122
struct tcp_pcb * conn ;
111
123
struct scan_listener * next ;
112
124
} scan_listener_t ;
113
125
126
+ typedef struct {
127
+ struct_type_t struct_type ;
128
+ size_t length ;
129
+ char data [0 ];
130
+ } http_request_buffer_t ;
131
+
114
132
typedef struct
115
133
{
116
134
struct espconn * espconn_dns_udp ;
@@ -398,8 +416,8 @@ static err_t close_once_sent (void *arg, struct tcp_pcb *pcb, u16_t len)
398
416
399
417
/**
400
418
* Get length of param value
401
- *
402
- * This is being called with a fragment of the parameters passed in the
419
+ *
420
+ * This is being called with a fragment of the parameters passed in the
403
421
* URL for GET requests or part of the body of a POST request.
404
422
* The string will look like one of these
405
423
* "SecretPassword HTTP/1.1"
@@ -1124,14 +1142,13 @@ static void enduser_setup_handle_POST(struct tcp_pcb *http_client, char* data, s
1124
1142
if (strncmp (data + 5 , "/setwifi " , 9 ) == 0 ) // User clicked the submit button
1125
1143
{
1126
1144
char * body = strstr (data , "\r\n\r\n" );
1127
- char * content_length_str = strstr (data , "Content-Length: " );
1128
- if ( body == NULL || content_length_str == NULL )
1145
+ if ( body == NULL )
1129
1146
{
1130
1147
enduser_setup_http_serve_header (http_client , http_header_400 , LITLEN (http_header_400 ));
1131
1148
return ;
1132
1149
}
1133
- int bodylength = atoi (content_length_str + 16 );
1134
1150
body += 4 ; // length of the double CRLF found above
1151
+ int bodylength = (data + data_len ) - body ;
1135
1152
switch (enduser_setup_http_handle_credentials (body , bodylength ))
1136
1153
{
1137
1154
case 0 : {
@@ -1148,6 +1165,8 @@ static void enduser_setup_handle_POST(struct tcp_pcb *http_client, char* data, s
1148
1165
ENDUSER_SETUP_ERROR_VOID ("http_recvcb failed. Failed to handle wifi credentials." , ENDUSER_SETUP_ERR_UNKOWN_ERROR , ENDUSER_SETUP_ERR_NONFATAL );
1149
1166
break ;
1150
1167
}
1168
+ } else {
1169
+ enduser_setup_http_serve_header (http_client , http_header_404 , LITLEN (http_header_404 ));
1151
1170
}
1152
1171
}
1153
1172
@@ -1333,27 +1352,154 @@ static err_t enduser_setup_http_recvcb(void *arg, struct tcp_pcb *http_client, s
1333
1352
return ERR_ABRT ;
1334
1353
}
1335
1354
1355
+ tcp_arg_t * tcp_arg_ptr = arg ;
1356
+
1336
1357
if (!p ) /* remote side closed, close our end too */
1337
1358
{
1338
1359
ENDUSER_SETUP_DEBUG ("connection closed" );
1339
- scan_listener_t * l = arg ; /* if it's waiting for scan, we have a ptr here */
1340
- if (l )
1341
- remove_scan_listener (l );
1360
+ if (tcp_arg_ptr ) {
1361
+ if (tcp_arg_ptr -> struct_type == SCAN_LISTENER_STRUCT_TYPE ) {
1362
+ remove_scan_listener ((scan_listener_t * )tcp_arg_ptr );
1363
+ } else if (tcp_arg_ptr -> struct_type == HTTP_REQUEST_BUFFER_STRUCT_TYPE ) {
1364
+ free (tcp_arg_ptr );
1365
+ }
1366
+ }
1342
1367
1343
1368
deferred_close (http_client );
1344
1369
return ERR_OK ;
1345
1370
}
1346
1371
1347
- char * data = calloc (1 , p -> tot_len + 1 );
1348
- if (!data )
1349
- return ERR_MEM ;
1372
+ http_request_buffer_t * hrb ;
1373
+ if (!tcp_arg_ptr ) {
1374
+ hrb = calloc (1 , sizeof (* hrb ));
1375
+ if (!hrb ) {
1376
+ goto general_fail ;
1377
+ }
1378
+ hrb -> struct_type = HTTP_REQUEST_BUFFER_STRUCT_TYPE ;
1379
+ tcp_arg (http_client , hrb );
1380
+ } else if (tcp_arg_ptr -> struct_type == HTTP_REQUEST_BUFFER_STRUCT_TYPE ) {
1381
+ hrb = (http_request_buffer_t * ) tcp_arg_ptr ;
1382
+ } else {
1383
+ goto general_fail ;
1384
+ }
1385
+
1386
+ // Append the new data
1387
+ size_t newlen = hrb -> length + p -> tot_len ;
1388
+ void * old_hrb = hrb ;
1389
+ hrb = realloc (hrb , sizeof (* hrb ) + newlen + 1 );
1390
+ tcp_arg (http_client , hrb );
1391
+ if (!hrb ) {
1392
+ free (old_hrb );
1393
+ goto general_fail ;
1394
+ }
1395
+
1396
+ pbuf_copy_partial (p , hrb -> data + hrb -> length , p -> tot_len , 0 );
1397
+ hrb -> data [newlen ] = 0 ;
1398
+ hrb -> length = newlen ;
1350
1399
1351
- unsigned data_len = pbuf_copy_partial (p , data , p -> tot_len , 0 );
1352
1400
tcp_recved (http_client , p -> tot_len );
1353
1401
pbuf_free (p );
1354
1402
1403
+ // see if we have the whole request.
1404
+ // Rely on the fact that the header should not contain a null character
1405
+ char * end_of_header = strstr (hrb -> data , "\r\n\r\n" );
1406
+ if (end_of_header == 0 ) {
1407
+ return ERR_OK ;
1408
+ }
1409
+
1410
+ end_of_header += 4 ;
1411
+
1412
+ // We have the entire header, now see if there is any content. If we don't find the
1413
+ // content-length header, then there is no content and we can process immediately.
1414
+ // The content-length header can also be missing if the browser is using chunked
1415
+ // encoding.
1416
+
1417
+ bool is_chunked = FALSE;
1418
+ for (const char * hdr = hrb -> data ; hdr && hdr < end_of_header ; hdr = strchr (hdr , '\n' )) {
1419
+ hdr += 1 ; // Skip the \n
1420
+ if (strncasecmp (hdr , "transfer-encoding:" , 18 ) == 0 ) {
1421
+ const char * field = hdr + 18 ;
1422
+
1423
+ while (* field != '\n' ) {
1424
+ if (memcmp (field , "chunked" , 7 ) == 0 ) {
1425
+ is_chunked = TRUE;
1426
+ break ;
1427
+ }
1428
+ field ++ ;
1429
+ }
1430
+ }
1431
+ if (strncasecmp (hdr , "Content-length:" , 15 ) == 0 ) {
1432
+ // There is a content-length header
1433
+ const char * field = hdr + 15 ;
1434
+ size_t extra = strtol (field + 1 , 0 , 10 );
1435
+ if (extra + (end_of_header - hrb -> data ) > hrb -> length ) {
1436
+ return ERR_OK ;
1437
+ }
1438
+ }
1439
+ }
1440
+
1441
+ if (is_chunked ) {
1442
+ // More complex to determine if the whole body has arrived
1443
+ // Format is one or more chunks each preceded by their length (in hex)
1444
+ // A zero length chunk ends the body
1445
+ const char * ptr = end_of_header ;
1446
+ bool seen_end = FALSE;
1447
+
1448
+ while (ptr < hrb -> data + hrb -> length && ptr > hrb -> data ) {
1449
+ size_t chunk_len = strtol (ptr , 0 , 16 );
1450
+ // Skip to end of chunk length (note that there can be parameters after the length)
1451
+ ptr = strchr (ptr , '\n' );
1452
+ if (!ptr ) {
1453
+ // Don't have the entire chunk header
1454
+ return ERR_OK ;
1455
+ }
1456
+ ptr ++ ;
1457
+ ptr += chunk_len ;
1458
+ if (chunk_len == 0 ) {
1459
+ seen_end = TRUE;
1460
+ break ;
1461
+ }
1462
+ if (ptr + 2 > hrb -> data + hrb -> length ) {
1463
+ // We don't have the CRLF yet
1464
+ return ERR_OK ;
1465
+ }
1466
+ if (memcmp (ptr , "\r\n" , 2 )) {
1467
+ // Bail out here -- something bad happened
1468
+ goto general_fail ;
1469
+ }
1470
+ ptr += 2 ;
1471
+ }
1472
+ if (!seen_end ) {
1473
+ // Still waiting for the end chunk
1474
+ return ERR_OK ;
1475
+ }
1476
+
1477
+ // Now rewrite the buffer to eliminate all the chunk headers
1478
+ const char * src = end_of_header ;
1479
+ char * dst = end_of_header ;
1480
+
1481
+ while (src < hrb -> data + hrb -> length && src > hrb -> data ) {
1482
+ size_t chunk_len = strtol (src , 0 , 16 );
1483
+ src = strchr (src , '\n' );
1484
+ src ++ ;
1485
+ if (chunk_len == 0 ) {
1486
+ break ;
1487
+ }
1488
+ memmove (dst , src , chunk_len );
1489
+ dst += chunk_len ;
1490
+ src += chunk_len + 2 ;
1491
+ }
1492
+ * dst = '\0' ; // Move the null termination down
1493
+ hrb -> length = dst - hrb -> data ; // Adjust the length down
1494
+ }
1495
+
1355
1496
err_t ret = ERR_OK ;
1356
1497
1498
+ char * data = hrb -> data ;
1499
+ size_t data_len = hrb -> length ;
1500
+
1501
+ tcp_arg (http_client , 0 ); // Forget the data pointer.
1502
+
1357
1503
#if ENDUSER_SETUP_DEBUG_SHOW_HTTP_REQUEST
1358
1504
ENDUSER_SETUP_DEBUG (data );
1359
1505
#endif
@@ -1364,7 +1510,7 @@ static err_t enduser_setup_http_recvcb(void *arg, struct tcp_pcb *http_client, s
1364
1510
{
1365
1511
if (enduser_setup_http_serve_html (http_client ) != 0 )
1366
1512
{
1367
- ENDUSER_SETUP_ERROR ( "http_recvcb failed. Unable to send HTML." , ENDUSER_SETUP_ERR_UNKOWN_ERROR , ENDUSER_SETUP_ERR_NONFATAL ) ;
1513
+ goto general_fail ;
1368
1514
}
1369
1515
else
1370
1516
{
@@ -1376,27 +1522,29 @@ static err_t enduser_setup_http_recvcb(void *arg, struct tcp_pcb *http_client, s
1376
1522
/* Don't do an AP Scan while station is trying to connect to Wi-Fi */
1377
1523
if (state -> connecting == 0 )
1378
1524
{
1379
- scan_listener_t * l = malloc (sizeof (scan_listener_t ));
1380
- if (!l )
1525
+ scan_listener_t * sl = malloc (sizeof (scan_listener_t ));
1526
+ if (!sl )
1381
1527
{
1382
1528
ENDUSER_SETUP_ERROR ("out of memory" , ENDUSER_SETUP_ERR_OUT_OF_MEMORY , ENDUSER_SETUP_ERR_NONFATAL );
1383
1529
}
1384
1530
1531
+ sl -> struct_type = SCAN_LISTENER_STRUCT_TYPE ;
1532
+
1385
1533
bool already = (state -> scan_listeners != NULL );
1386
1534
1387
- tcp_arg (http_client , l );
1535
+ tcp_arg (http_client , sl );
1388
1536
/* TODO: check if also need a tcp_err() cb, or if recv() is enough */
1389
- l -> conn = http_client ;
1390
- l -> next = state -> scan_listeners ;
1391
- state -> scan_listeners = l ;
1537
+ sl -> conn = http_client ;
1538
+ sl -> next = state -> scan_listeners ;
1539
+ state -> scan_listeners = sl ;
1392
1540
1393
1541
if (!already )
1394
1542
{
1395
1543
if (!wifi_station_scan (NULL , on_scan_done ))
1396
1544
{
1397
1545
enduser_setup_http_serve_header (http_client , http_header_500 , LITLEN (http_header_500 ));
1398
- deferred_close (l -> conn );
1399
- l -> conn = 0 ;
1546
+ deferred_close (sl -> conn );
1547
+ sl -> conn = 0 ;
1400
1548
free_scan_listeners ();
1401
1549
}
1402
1550
}
@@ -1410,13 +1558,12 @@ static err_t enduser_setup_http_recvcb(void *arg, struct tcp_pcb *http_client, s
1410
1558
}
1411
1559
else if (strncmp (data + 4 , "/status.json" , 12 ) == 0 )
1412
1560
{
1413
- enduser_setup_serve_status_as_json (http_client );
1561
+ enduser_setup_serve_status_as_json (http_client );
1414
1562
}
1415
1563
else if (strncmp (data + 4 , "/status" , 7 ) == 0 )
1416
1564
{
1417
1565
enduser_setup_serve_status (http_client );
1418
1566
}
1419
-
1420
1567
else if (strncmp (data + 4 , "/update?" , 8 ) == 0 )
1421
1568
{
1422
1569
switch (enduser_setup_http_handle_credentials (data , data_len ))
@@ -1459,8 +1606,11 @@ static err_t enduser_setup_http_recvcb(void *arg, struct tcp_pcb *http_client, s
1459
1606
deferred_close (http_client );
1460
1607
1461
1608
free_out :
1462
- free (data );
1609
+ free (hrb );
1463
1610
return ret ;
1611
+
1612
+ general_fail :
1613
+ ENDUSER_SETUP_ERROR ("http_recvcb failed. Unable to send HTML." , ENDUSER_SETUP_ERR_UNKOWN_ERROR , ENDUSER_SETUP_ERR_NONFATAL );
1464
1614
}
1465
1615
1466
1616
@@ -1476,6 +1626,7 @@ static err_t enduser_setup_http_connectcb(void *arg, struct tcp_pcb *pcb, err_t
1476
1626
}
1477
1627
1478
1628
tcp_accepted (state -> http_pcb );
1629
+ tcp_arg (pcb , 0 ); // Initialize to known value
1479
1630
tcp_recv (pcb , enduser_setup_http_recvcb );
1480
1631
tcp_nagle_disable (pcb );
1481
1632
return ERR_OK ;
0 commit comments