26
26
27
27
#include <string.h>
28
28
29
+ #include "extmod/vfs.h"
30
+ #include "extmod/vfs_fat.h"
31
+
32
+ #include "py/mpstate.h"
33
+
29
34
#include "shared-bindings/wifi/Radio.h"
30
35
#include "supervisor/shared/translate/translate.h"
31
36
#include "supervisor/shared/web_workflow/web_workflow.h"
@@ -180,6 +185,8 @@ void supervisor_start_web_workflow(void) {
180
185
// - JSON list of MDNS results
181
186
// GET /cp/version.json
182
187
// - JSON version info
188
+ // GET /cp/serial.txt
189
+ // - Most recent 1k of serial output.
183
190
// GET /
184
191
// - Super basic editor
185
192
// GET /ws/circuitpython
@@ -188,6 +195,44 @@ void supervisor_start_web_workflow(void) {
188
195
#endif
189
196
}
190
197
198
+ static void _send_chunk (socketpool_socket_obj_t * socket , const char * chunk ) {
199
+ char encoded_len [sizeof (size_t ) * 2 + 1 ];
200
+ int len = snprintf (encoded_len , sizeof (encoded_len ), "%X" , strlen (chunk ));
201
+ int sent = socketpool_socket_send (socket , (const uint8_t * )encoded_len , len );
202
+ if (sent < len ) {
203
+ ESP_LOGE (TAG , "short send %d %d" , sent , len );
204
+ }
205
+ sent = socketpool_socket_send (socket , (const uint8_t * )"\r\n" , 2 );
206
+ if (sent < 2 ) {
207
+ ESP_LOGE (TAG , "short send %d %d" , sent , 2 );
208
+ }
209
+ sent = socketpool_socket_send (socket , (const uint8_t * )chunk , strlen (chunk ));
210
+ if (sent < (int )strlen (chunk )) {
211
+ ESP_LOGE (TAG , "short send %d %d" , sent , strlen (chunk ));
212
+ }
213
+ sent = socketpool_socket_send (socket , (const uint8_t * )"\r\n" , 2 );
214
+ if (sent < 2 ) {
215
+ ESP_LOGE (TAG , "short send %d %d" , sent , 2 );
216
+ }
217
+ }
218
+
219
+ static void _send_str (socketpool_socket_obj_t * socket , const char * str ) {
220
+ int sent = socketpool_socket_send (socket , (const uint8_t * )str , strlen (str ));
221
+ if (sent < (int )strlen (str )) {
222
+ ESP_LOGE (TAG , "short send %d %d" , sent , strlen (str ));
223
+ }
224
+ }
225
+
226
+ static bool _endswith (const char * str , const char * suffix ) {
227
+ if (str == NULL || suffix == NULL ) {
228
+ return false;
229
+ }
230
+ if (strlen (suffix ) > strlen (str )) {
231
+ return false;
232
+ }
233
+ return strcmp (str + (strlen (str ) - strlen (suffix )), suffix ) == 0 ;
234
+ }
235
+
191
236
static void _process_request (socketpool_socket_obj_t * socket , _request * request ) {
192
237
bool more = true;
193
238
bool error = false;
@@ -284,7 +329,9 @@ static void _process_request(socketpool_socket_obj_t *socket, _request *request)
284
329
break ;
285
330
}
286
331
case STATE_BODY :
332
+ ESP_LOGW (TAG , "BODY %c" , c );
287
333
request -> done = true;
334
+ request -> state = STATE_METHOD ;
288
335
more = false;
289
336
break ;
290
337
}
@@ -312,6 +359,117 @@ static void _process_request(socketpool_socket_obj_t *socket, _request *request)
312
359
const char * two_lines = "\r\n\r\n" ;
313
360
sent += socketpool_socket_send (socket , (const uint8_t * )two_lines , strlen (two_lines ));
314
361
ESP_LOGW (TAG , "sent %d %d" , sent , err );
362
+ } else if (memcmp (request -> path , "/fs/" , 4 ) == 0 ) {
363
+ ESP_LOGW (TAG , "filesystem %s %s" , request -> method , request -> path + 3 );
364
+ const char * path = request -> path + 3 ;
365
+ size_t pathlen = strlen (path );
366
+ FATFS * fs = & ((fs_user_mount_t * )MP_STATE_VM (vfs_mount_table )-> obj )-> fatfs ;
367
+ // Trailing / is a directory.
368
+ if (path [pathlen - 1 ] == '/' ) {
369
+ if (strcmp (request -> method , "GET" ) == 0 ) {
370
+ FF_DIR dir ;
371
+ FRESULT res = f_opendir (fs , & dir , path );
372
+ if (res != FR_OK ) {
373
+ // TODO: Send 404
374
+ }
375
+ const char * ok_response = "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n"
376
+ "Content-Type: text/html\r\n\r\n" ;
377
+ socketpool_socket_send (socket , (const uint8_t * )ok_response , strlen (ok_response ));
378
+ _send_chunk (socket , "<!DOCTYPE html><html><head><title>" );
379
+ _send_chunk (socket , path );
380
+ _send_chunk (socket , "</title><meta charset=\"UTF-8\"></head><body><h1>" );
381
+ _send_chunk (socket , path );
382
+ _send_chunk (socket , "</h1><pre>" );
383
+ if (strlen (path ) > 1 ) {
384
+ _send_chunk (socket , "🗀\t<a href=\"..\">..</a><br/>" );
385
+ }
386
+
387
+ FILINFO file_info ;
388
+ char * fn = file_info .fname ;
389
+ res = f_readdir (& dir , & file_info );
390
+ while (res == FR_OK && fn [0 ] != 0 ) {
391
+ // uint64_t truncated_time = timeutils_mktime(1980 + (file_info.fdate >> 9),
392
+ // (file_info.fdate >> 5) & 0xf,
393
+ // file_info.fdate & 0x1f,
394
+ // file_info.ftime >> 11,
395
+ // (file_info.ftime >> 5) & 0x1f,
396
+ // (file_info.ftime & 0x1f) * 2) * 1000000000ULL;
397
+ // entry->truncated_time = truncated_time;
398
+ // if ((file_info.fattrib & AM_DIR) != 0) {
399
+ // entry->flags = 1; // Directory
400
+ // entry->file_size = 0;
401
+ // } else {
402
+ // entry->flags = 0;
403
+ // entry->file_size = file_info.fsize;
404
+ // }
405
+ // _send_chunk(socket, "<li>");
406
+ if ((file_info .fattrib & AM_DIR ) != 0 ) {
407
+ _send_chunk (socket , "🗀\t" );
408
+ } else if (_endswith (path , ".txt" ) || _endswith (path , ".py" )) {
409
+ _send_chunk (socket , "🖹\t" );
410
+ } else {
411
+ _send_chunk (socket , "⭳\t" );
412
+ }
413
+ _send_chunk (socket , "<a href=\"" );
414
+ _send_chunk (socket , request -> path );
415
+ _send_chunk (socket , file_info .fname );
416
+ if ((file_info .fattrib & AM_DIR ) != 0 ) {
417
+ _send_chunk (socket , "/" );
418
+ }
419
+ _send_chunk (socket , "\">" );
420
+
421
+ _send_chunk (socket , file_info .fname );
422
+ _send_chunk (socket , "</a><a>✏️</a><a>🗑️</a><br/>" );
423
+ res = f_readdir (& dir , & file_info );
424
+ }
425
+ _send_chunk (socket , "</pre>Upload:<input type=\"file\" multiple></body></html>" );
426
+ _send_chunk (socket , "" );
427
+ f_closedir (& dir );
428
+ }
429
+
430
+ } else { // Dealing with a file.
431
+ if (strcmp (request -> method , "GET" ) == 0 ) {
432
+ FIL active_file ;
433
+ FRESULT result = f_open (fs , & active_file , path , FA_READ );
434
+
435
+ if (result != FR_OK ) {
436
+ // TODO: 404
437
+ }
438
+ uint32_t total_length = f_size (& active_file );
439
+ char encoded_len [10 ];
440
+ snprintf (encoded_len , sizeof (encoded_len ), "%d" , total_length );
441
+
442
+ _send_str (socket , "HTTP/1.1 200 OK\r\nContent-Length: " );
443
+ _send_str (socket , encoded_len );
444
+ _send_str (socket , "\r\n" );
445
+ if (_endswith (path , ".txt" ) || _endswith (path , ".py" )) {
446
+ _send_str (socket , "Content-Type: text/plain\r\n" );
447
+ } else {
448
+ _send_str (socket , "Content-Type: application/octet-stream\r\n" );
449
+ }
450
+ _send_str (socket , "\r\n" );
451
+
452
+ uint32_t total_read = 0 ;
453
+ while (total_read < total_length ) {
454
+ uint8_t data_buffer [64 ];
455
+ size_t quantity_read ;
456
+ f_read (& active_file , data_buffer , 64 , & quantity_read );
457
+ total_read += quantity_read ;
458
+ uint32_t send_offset = 0 ;
459
+ while (send_offset < quantity_read ) {
460
+ int sent = socketpool_socket_send (socket , data_buffer + send_offset , quantity_read - send_offset );
461
+ if (sent < 0 ) {
462
+ ESP_LOGE (TAG , "file send error %d" , sent );
463
+ break ;
464
+ }
465
+ send_offset += sent ;
466
+ }
467
+ }
468
+ ESP_LOGW (TAG , "file return done" );
469
+
470
+ f_close (& active_file );
471
+ }
472
+ }
315
473
} else {
316
474
const char * ok_response = "HTTP/1.1 200 OK\r\nContent-Length: 11\r\nContent-Type: text/plain\r\n\r\nHello World" ;
317
475
int sent = socketpool_socket_send (socket , (const uint8_t * )ok_response , strlen (ok_response ));
0 commit comments