WIP adding devices.json and auth

This commit is contained in:
Scott Shawcroft 2022-06-16 17:03:52 -07:00
parent 77cecdbe2a
commit 4f0a7aedfd
No known key found for this signature in database
GPG Key ID: 0DFD512649C052DA
8 changed files with 370 additions and 145 deletions

View File

@ -13,5 +13,5 @@ LONGINT_IMPL = MPZ
CFLAGS += -DCFG_TUD_TASK_QUEUE_SZ=32
CIRCUITPY_ESP_FLASH_MODE=dio
CIRCUITPY_ESP_FLASH_FREQ=80m
CIRCUITPY_ESP_FLASH_FREQ=40m
CIRCUITPY_ESP_FLASH_SIZE=16MB

View File

@ -35,7 +35,7 @@
STATIC bool inited = false;
void mdns_server_construct(mdns_server_obj_t *self) {
void mdns_server_construct(mdns_server_obj_t *self, bool workflow) {
if (inited) {
return;
}
@ -46,19 +46,21 @@ void mdns_server_construct(mdns_server_obj_t *self) {
snprintf(self->default_hostname, sizeof(self->default_hostname), "cpy-%02x%02x%02x", mac[3], mac[4], mac[5]);
common_hal_mdns_server_set_hostname(self, self->default_hostname);
// Set a delegated entry to ourselves. This allows us to respond to "circuitpython.local"
// queries as well.
// TODO: Allow for disabling this with `supervisor.disable_web_workflow()`.
mdns_ip_addr_t our_ip;
esp_netif_get_ip_info(common_hal_wifi_radio_obj.netif, &common_hal_wifi_radio_obj.ip_info);
our_ip.next = NULL;
our_ip.addr.type = ESP_IPADDR_TYPE_V4;
our_ip.addr.u_addr.ip4 = common_hal_wifi_radio_obj.ip_info.ip;
our_ip.addr.u_addr.ip6.addr[1] = 0;
our_ip.addr.u_addr.ip6.addr[2] = 0;
our_ip.addr.u_addr.ip6.addr[3] = 0;
our_ip.addr.u_addr.ip6.zone = 0;
mdns_delegate_hostname_add("circuitpython", &our_ip);
if (workflow) {
// Set a delegated entry to ourselves. This allows us to respond to "circuitpython.local"
// queries as well.
// TODO: Allow for disabling this with `supervisor.disable_web_workflow()`.
mdns_ip_addr_t our_ip;
esp_netif_get_ip_info(common_hal_wifi_radio_obj.netif, &common_hal_wifi_radio_obj.ip_info);
our_ip.next = NULL;
our_ip.addr.type = ESP_IPADDR_TYPE_V4;
our_ip.addr.u_addr.ip4 = common_hal_wifi_radio_obj.ip_info.ip;
our_ip.addr.u_addr.ip6.addr[1] = 0;
our_ip.addr.u_addr.ip6.addr[2] = 0;
our_ip.addr.u_addr.ip6.addr[3] = 0;
our_ip.addr.u_addr.ip6.zone = 0;
mdns_delegate_hostname_add("circuitpython", &our_ip);
}
}
void common_hal_mdns_server_construct(mdns_server_obj_t *self, mp_obj_t network_interface) {
@ -69,7 +71,7 @@ void common_hal_mdns_server_construct(mdns_server_obj_t *self, mp_obj_t network_
if (inited) {
mp_raise_RuntimeError(translate("mDNS already initialized"));
}
mdns_server_construct(self);
mdns_server_construct(self, false);
}
void common_hal_mdns_server_deinit(mdns_server_obj_t *self) {
@ -104,6 +106,48 @@ void common_hal_mdns_server_set_instance_name(mdns_server_obj_t *self, const cha
self->instance_name = instance_name;
}
size_t mdns_server_find(mdns_server_obj_t *self, const char *service_type, const char *protocol,
mp_float_t timeout, mdns_remoteservice_obj_t *out, size_t out_len) {
mdns_search_once_t *search = mdns_query_async_new(NULL, service_type, protocol, MDNS_TYPE_PTR, timeout * 1000, 255, NULL);
if (search == NULL) {
return 0;
}
mdns_result_t *results;
while (!mdns_query_async_get_results(search, 1, &results)) {
RUN_BACKGROUND_TASKS;
}
mdns_query_async_delete(search);
// Count how many results we got.
// TODO: Remove this loop when moving off 4.4. Newer APIs will give us num_results
// back directly.
mdns_result_t *next = results;
uint8_t num_results = 0;
while (next != NULL) {
num_results++;
next = next->next;
}
next = results;
// Don't error if we're out of memory. Instead, truncate the tuple.
uint8_t added = 0;
while (next != NULL && added < out_len) {
mdns_remoteservice_obj_t *service = &out[added];
service->result = next;
service->base.type = &mdns_remoteservice_type;
next = next->next;
// Break the linked list so we free each result separately.
service->result->next = NULL;
added++;
}
if (added < out_len) {
// Free the remaining results from the IDF because we don't have
// enough space in Python.
mdns_query_results_free(next);
}
return num_results;
}
mp_obj_t common_hal_mdns_server_find(mdns_server_obj_t *self, const char *service_type, const char *protocol, mp_float_t timeout) {
mdns_search_once_t *search = mdns_query_async_new(NULL, service_type, protocol, MDNS_TYPE_PTR, timeout * 1000, 255, NULL);
if (search == NULL) {

View File

@ -48,6 +48,10 @@
#define MAC_ADDRESS_LENGTH 6
#include "esp_log.h"
static const char *TAG = "radio";
static void set_mode_station(wifi_radio_obj_t *self, bool state) {
wifi_mode_t next_mode;
if (state) {
@ -236,11 +240,12 @@ wifi_radio_error_t common_hal_wifi_radio_connect(wifi_radio_obj_t *self, uint8_t
if (!common_hal_wifi_radio_get_enabled(self)) {
mp_raise_RuntimeError(translate("wifi is not enabled"));
}
ESP_LOGI(TAG, "connect");
wifi_config_t *config = &self->sta_config;
size_t timeout_ms = timeout * 1000;
uint32_t start_time = common_hal_time_monotonic_ms();
uint32_t end_time = start_time + timeout_ms;
// size_t timeout_ms = timeout * 1000;
// uint32_t start_time = common_hal_time_monotonic_ms();
// uint32_t end_time = start_time + timeout_ms;
EventBits_t bits;
// can't block since both bits are false after wifi_init
@ -304,7 +309,9 @@ wifi_radio_error_t common_hal_wifi_radio_connect(wifi_radio_obj_t *self, uint8_t
esp_wifi_set_config(ESP_IF_WIFI_STA, config);
self->starting_retries = 5;
self->retries_left = 5;
ESP_LOGI(TAG, "wifi connect");
esp_wifi_connect();
ESP_LOGI(TAG, "wifi connect done");
do {
RUN_BACKGROUND_TASKS;
@ -314,10 +321,11 @@ wifi_radio_error_t common_hal_wifi_radio_connect(wifi_radio_obj_t *self, uint8_t
pdTRUE,
0);
// Don't retry anymore if we're over our time budget.
if (self->retries_left > 0 && common_hal_time_monotonic_ms() > end_time) {
self->retries_left = 0;
}
// if (self->retries_left > 0 && common_hal_time_monotonic_ms() > end_time) {
// self->retries_left = 0;
// }
} while ((bits & (WIFI_CONNECTED_BIT | WIFI_DISCONNECTED_BIT)) == 0 && !mp_hal_is_interrupted());
ESP_LOGI(TAG, "connect done");
if ((bits & WIFI_DISCONNECTED_BIT) != 0) {
if (self->last_disconnect_reason == WIFI_REASON_AUTH_FAIL) {
return WIFI_RADIO_ERROR_AUTH_FAIL;

View File

@ -44,7 +44,7 @@ wifi_radio_obj_t common_hal_wifi_radio_obj;
#include "supervisor/workflow.h"
static const char *TAG = "wifi";
static const char *TAG = "CP wifi";
static void event_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data) {

View File

@ -297,7 +297,7 @@ CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=2304
CONFIG_ESP_MAIN_TASK_STACK_SIZE=8192
CONFIG_ESP_MINIMAL_SHARED_STACK_SIZE=2048
CONFIG_ESP_INT_WDT=y
CONFIG_ESP_INT_WDT_TIMEOUT_MS=300
CONFIG_ESP_INT_WDT_TIMEOUT_MS=1000
CONFIG_ESP_INT_WDT_CHECK_CPU1=y
# CONFIG_ESP_TASK_WDT is not set
# CONFIG_ESP_PANIC_HANDLER_IRAM is not set
@ -805,9 +805,9 @@ CONFIG_LOG_BOOTLOADER_LEVEL_INFO=y
CONFIG_LOG_BOOTLOADER_LEVEL=3
# CONFIG_APP_ROLLBACK_ENABLE is not set
# CONFIG_FLASH_ENCRYPTION_ENABLED is not set
# CONFIG_FLASHMODE_QIO is not set
CONFIG_FLASHMODE_QIO=y
# CONFIG_FLASHMODE_QOUT is not set
CONFIG_FLASHMODE_DIO=y
# CONFIG_FLASHMODE_DIO is not set
# CONFIG_FLASHMODE_DOUT is not set
# CONFIG_MONITOR_BAUD_9600B is not set
# CONFIG_MONITOR_BAUD_57600B is not set

View File

@ -136,7 +136,7 @@ MP_PROPERTY_GETSET(mdns_server_instance_name_obj,
//| :param float/int timeout: Time to wait for responses"""
//| ...
//|
STATIC mp_obj_t mdns_server_find(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
STATIC mp_obj_t _mdns_server_find(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
mdns_server_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
check_for_deinit(self);
@ -156,7 +156,7 @@ STATIC mp_obj_t mdns_server_find(mp_uint_t n_args, const mp_obj_t *pos_args, mp_
return common_hal_mdns_server_find(self, service_type, protocol, timeout);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(mdns_server_find_obj, 1, mdns_server_find);
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(mdns_server_find_obj, 1, _mdns_server_find);
//| def advertise_service(self, *, service_type: str, protocol: str, port: int) -> None:
//| """Respond to queries for the given service with the given port.

View File

@ -30,6 +30,8 @@
#include "common-hal/mdns/Server.h"
#include "shared-bindings/mdns/RemoteService.h"
extern const mp_obj_type_t mdns_server_type;
void common_hal_mdns_server_construct(mdns_server_obj_t *self, mp_obj_t network_interface);
@ -43,4 +45,6 @@ mp_obj_t common_hal_mdns_server_find(mdns_server_obj_t *self, const char *servic
void common_hal_mdns_server_advertise_service(mdns_server_obj_t *self, const char *service_type, const char *protocol, mp_int_t port);
// For internal use.
void mdns_server_construct(mdns_server_obj_t *self);
void mdns_server_construct(mdns_server_obj_t *self, bool workflow);
size_t mdns_server_find(mdns_server_obj_t *self, const char *service_type, const char *protocol,
mp_float_t timeout, mdns_remoteservice_obj_t *out, size_t out_len);

View File

@ -28,13 +28,14 @@
#include "extmod/vfs.h"
#include "extmod/vfs_fat.h"
#include "genhdr/mpversion.h"
#include "py/mpstate.h"
#include "shared-bindings/wifi/Radio.h"
#include "supervisor/shared/translate/translate.h"
#include "supervisor/shared/web_workflow/web_workflow.h"
#include "shared-bindings/mdns/RemoteService.h"
#include "shared-bindings/mdns/Server.h"
#include "shared-bindings/socketpool/__init__.h"
#include "shared-bindings/socketpool/Socket.h"
@ -71,6 +72,7 @@ typedef struct {
size_t offset;
bool redirect;
bool done;
bool authenticated;
} _request;
static wifi_radio_error_t wifi_status = WIFI_RADIO_ERROR_NONE;
@ -83,6 +85,72 @@ static socketpool_socket_obj_t active;
static _request active_request;
static char _api_password[64];
// in_len is the number of bytes to encode. out_len is the number of bytes we
// have to do it.
static bool _base64_in_place(char *buf, size_t in_len, size_t out_len) {
size_t triples = (((in_len - 1) / 3) + 1);
size_t encoded_len = triples * 4;
if (encoded_len + 1 > out_len) {
return false;
}
ESP_LOGI(TAG, "triples %d", triples);
// First pass, we convert input buffer to numeric base 64 values
char *in = buf + (triples - 1) * 3;
char *out = buf + (triples - 1) * 4;
int r = in_len % 3;
int partial = 0;
if (r != 0) {
out[0] = (in[0] & 0xFC) >> 2;
out[1] = (in[0] & 0x03) << 4;
if (r == 2) {
out[1] |= (in[1] & 0xF0) >> 4;
out[2] = (in[1] & 0x0F) << 2;
} else {
out[2] = 64;
}
out[3] = 64;
in -= 3;
out -= 4;
partial = 1;
} else {
ESP_LOGI(TAG, "no partial");
}
buf[encoded_len] = '\0';
for (size_t i = 0; i < triples - partial; i++) {
ESP_LOGI(TAG, "in %d %d %d", in[0], in[1], in[2]);
out[0] = (in[0] & 0xFC) >> 2;
out[1] = (in[0] & 0x03) << 4 | (in[1] & 0xF0) >> 4;
out[2] = (in[1] & 0x0F) << 2 | (in[2] & 0xC0) >> 6;
out[3] = in[2] & 0x3F;
ESP_LOGI(TAG, "out %d %d %d %d", out[0], out[1], out[2], out[3]);
in -= 3;
out -= 4;
}
// Second pass, we convert number base 64 values to actual base64 ascii encoding
out = buf;
for (mp_uint_t j = 0; j < encoded_len; j++) {
if (*out < 26) {
*out += 'A';
} else if (*out < 52) {
*out += 'a' - 26;
} else if (*out < 62) {
*out += '0' - 52;
} else if (*out == 62) {
*out = '+';
} else if (*out == 63) {
*out = '/';
} else {
*out = '=';
}
out++;
}
return true;
}
void supervisor_web_workflow_status(void) {
serial_write_compressed(translate("Wi-Fi: "));
@ -145,7 +213,8 @@ void supervisor_start_web_workflow(void) {
return;
}
mdns_server_construct(&mdns);
mdns_server_construct(&mdns, true);
common_hal_mdns_server_set_instance_name(&mdns, MICROPY_HW_BOARD_NAME);
common_hal_mdns_server_advertise_service(&mdns, "_circuitpython", "_tcp", 80);
pool.base.type = &socketpool_socketpool_type;
@ -160,6 +229,16 @@ void supervisor_start_web_workflow(void) {
common_hal_socketpool_socket_bind(&listening, ip, strlen(ip), 80);
common_hal_socketpool_socket_listen(&listening, 1);
mp_int_t api_password_len = dotenv_get_key("/.env", "CIRCUITPY_WEB_API_PASSWORD", _api_password + 1, sizeof(_api_password) - 2);
if (api_password_len > 0) {
_api_password[0] = ':';
_api_password[api_password_len + 1] = '\0';
ESP_LOGW(TAG, "password before: %s", _api_password);
_base64_in_place(_api_password, api_password_len + 1, sizeof(_api_password));
ESP_LOGW(TAG, "password encoded: %s", _api_password);
}
ESP_LOGW(TAG, "listening on socket %d", listening.num);
active.base.type = &socketpool_socket_type;
@ -234,6 +313,37 @@ static bool _endswith(const char *str, const char *suffix) {
return strcmp(str + (strlen(str) - strlen(suffix)), suffix) == 0;
}
static void _reply_missing(socketpool_socket_obj_t *socket) {
const char *response = "HTTP/1.1 401 Unauthorized\r\n"
"Content-Length: 0\r\n"
"WWW-Authenticate: Basic realm=\"CircuitPython\"\r\n\r\n";
_send_str(socket, response);
}
static void _reply_forbidden(socketpool_socket_obj_t *socket) {
const char *response = "HTTP/1.1 403 Forbidden\r\n"
"Content-Length: 0\r\n\r\n";
_send_str(socket, response);
}
static void _reply_unauthorized(socketpool_socket_obj_t *socket) {
const char *response = "HTTP/1.1 401 Unauthorized\r\n"
"Content-Length: 0\r\n"
"WWW-Authenticate: Basic realm=\"CircuitPython\"\r\n\r\n";
_send_str(socket, response);
}
static void _reply_redirect(socketpool_socket_obj_t *socket, const char *path) {
const char *redirect_response = "HTTP/1.1 301 Moved Permanently\r\nConnection: close\r\nContent-Length: 0\r\nLocation: http://";
int nodelay = 1;
lwip_setsockopt(socket->num, IPPROTO_TCP, TCP_NODELAY, &nodelay, sizeof(nodelay));
_send_str(socket, redirect_response);
_send_str(socket, common_hal_mdns_server_get_instance_name(&mdns));
_send_str(socket, ".local");
_send_str(socket, path);
_send_str(socket, "\r\n\r\n");
}
static void _process_request(socketpool_socket_obj_t *socket, _request *request) {
bool more = true;
bool error = false;
@ -348,129 +458,188 @@ static void _process_request(socketpool_socket_obj_t *socket, _request *request)
return;
}
if (request->redirect) {
const char *redirect_response = "HTTP/1.1 301 Moved Permanently\r\nConnection: close\r\nContent-Length: 0\r\nLocation: http://";
int nodelay = 1;
int err = lwip_setsockopt(socket->num, IPPROTO_TCP, TCP_NODELAY, &nodelay, sizeof(nodelay));
int sent = socketpool_socket_send(socket, (const uint8_t *)redirect_response, strlen(redirect_response));
const char *instance_name = common_hal_mdns_server_get_instance_name(&mdns);
sent += socketpool_socket_send(socket, (const uint8_t *)instance_name, strlen(instance_name));
const char *local = ".local";
sent += socketpool_socket_send(socket, (const uint8_t *)local, strlen(local));
sent += socketpool_socket_send(socket, (const uint8_t *)request->path, strlen(request->path));
const char *two_lines = "\r\n\r\n";
sent += socketpool_socket_send(socket, (const uint8_t *)two_lines, strlen(two_lines));
ESP_LOGW(TAG, "sent %d %d", sent, err);
_reply_redirect(socket, request->path);
} else if (memcmp(request->path, "/fs/", 4) == 0) {
ESP_LOGW(TAG, "filesystem %s %s", request->method, request->path + 3);
const char *path = request->path + 3;
size_t pathlen = strlen(path);
FATFS *fs = &((fs_user_mount_t *)MP_STATE_VM(vfs_mount_table)->obj)->fatfs;
// Trailing / is a directory.
if (path[pathlen - 1] == '/') {
if (strcmp(request->method, "GET") == 0) {
FF_DIR dir;
FRESULT res = f_opendir(fs, &dir, path);
if (res != FR_OK) {
// TODO: Send 404
}
const char *ok_response = "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n"
"Content-Type: text/html\r\n\r\n";
socketpool_socket_send(socket, (const uint8_t *)ok_response, strlen(ok_response));
_send_chunk(socket, "<!DOCTYPE html><html><head><title>");
_send_chunk(socket, path);
_send_chunk(socket, "</title><meta charset=\"UTF-8\"></head><body><h1>");
_send_chunk(socket, path);
_send_chunk(socket, "</h1><pre>");
if (strlen(path) > 1) {
_send_chunk(socket, "🗀\t<a href=\"..\">..</a><br/>");
}
FILINFO file_info;
char *fn = file_info.fname;
res = f_readdir(&dir, &file_info);
while (res == FR_OK && fn[0] != 0) {
// uint64_t truncated_time = timeutils_mktime(1980 + (file_info.fdate >> 9),
// (file_info.fdate >> 5) & 0xf,
// file_info.fdate & 0x1f,
// file_info.ftime >> 11,
// (file_info.ftime >> 5) & 0x1f,
// (file_info.ftime & 0x1f) * 2) * 1000000000ULL;
// entry->truncated_time = truncated_time;
// if ((file_info.fattrib & AM_DIR) != 0) {
// entry->flags = 1; // Directory
// entry->file_size = 0;
// } else {
// entry->flags = 0;
// entry->file_size = file_info.fsize;
// }
// _send_chunk(socket, "<li>");
if ((file_info.fattrib & AM_DIR) != 0) {
_send_chunk(socket, "🗀\t");
} else if (_endswith(path, ".txt") || _endswith(path, ".py")) {
_send_chunk(socket, "🖹\t");
} else {
_send_chunk(socket, "\t");
if (!request->authenticated) {
if (_api_password[0] != '\0') {
_reply_unauthorized(socket);
} else {
_reply_forbidden(socket);
}
} else {
ESP_LOGW(TAG, "filesystem %s %s", request->method, request->path + 3);
const char *path = request->path + 3;
size_t pathlen = strlen(path);
FATFS *fs = &((fs_user_mount_t *)MP_STATE_VM(vfs_mount_table)->obj)->fatfs;
// Trailing / is a directory.
if (path[pathlen - 1] == '/') {
if (strcmp(request->method, "GET") == 0) {
FF_DIR dir;
FRESULT res = f_opendir(fs, &dir, path);
if (res != FR_OK) {
_reply_missing(socket);
}
_send_chunk(socket, "<a href=\"");
_send_chunk(socket, request->path);
_send_chunk(socket, file_info.fname);
if ((file_info.fattrib & AM_DIR) != 0) {
_send_chunk(socket, "/");
const char *ok_response = "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n"
"Content-Type: text/html\r\n\r\n";
socketpool_socket_send(socket, (const uint8_t *)ok_response, strlen(ok_response));
_send_chunk(socket, "<!DOCTYPE html><html><head><title>");
_send_chunk(socket, path);
_send_chunk(socket, "</title><meta charset=\"UTF-8\"></head><body><h1>");
_send_chunk(socket, path);
_send_chunk(socket, "</h1><pre>");
if (strlen(path) > 1) {
_send_chunk(socket, "🗀\t<a href=\"..\">..</a><br/>");
}
_send_chunk(socket, "\">");
_send_chunk(socket, file_info.fname);
_send_chunk(socket, "</a><a>✏️</a><a>🗑️</a><br/>");
FILINFO file_info;
char *fn = file_info.fname;
res = f_readdir(&dir, &file_info);
}
_send_chunk(socket, "</pre>Upload:<input type=\"file\" multiple></body></html>");
_send_chunk(socket, "");
f_closedir(&dir);
}
} else { // Dealing with a file.
if (strcmp(request->method, "GET") == 0) {
FIL active_file;
FRESULT result = f_open(fs, &active_file, path, FA_READ);
if (result != FR_OK) {
// TODO: 404
}
uint32_t total_length = f_size(&active_file);
char encoded_len[10];
snprintf(encoded_len, sizeof(encoded_len), "%d", total_length);
_send_str(socket, "HTTP/1.1 200 OK\r\nContent-Length: ");
_send_str(socket, encoded_len);
_send_str(socket, "\r\n");
if (_endswith(path, ".txt") || _endswith(path, ".py")) {
_send_str(socket, "Content-Type: text/plain\r\n");
} else {
_send_str(socket, "Content-Type: application/octet-stream\r\n");
}
_send_str(socket, "\r\n");
uint32_t total_read = 0;
while (total_read < total_length) {
uint8_t data_buffer[64];
size_t quantity_read;
f_read(&active_file, data_buffer, 64, &quantity_read);
total_read += quantity_read;
uint32_t send_offset = 0;
while (send_offset < quantity_read) {
int sent = socketpool_socket_send(socket, data_buffer + send_offset, quantity_read - send_offset);
if (sent < 0) {
ESP_LOGE(TAG, "file send error %d", sent);
break;
while (res == FR_OK && fn[0] != 0) {
// uint64_t truncated_time = timeutils_mktime(1980 + (file_info.fdate >> 9),
// (file_info.fdate >> 5) & 0xf,
// file_info.fdate & 0x1f,
// file_info.ftime >> 11,
// (file_info.ftime >> 5) & 0x1f,
// (file_info.ftime & 0x1f) * 2) * 1000000000ULL;
// entry->truncated_time = truncated_time;
// if ((file_info.fattrib & AM_DIR) != 0) {
// entry->flags = 1; // Directory
// entry->file_size = 0;
// } else {
// entry->flags = 0;
// entry->file_size = file_info.fsize;
// }
// _send_chunk(socket, "<li>");
if ((file_info.fattrib & AM_DIR) != 0) {
_send_chunk(socket, "🗀\t");
} else if (_endswith(file_info.fname, ".txt") ||
_endswith(file_info.fname, ".py")) {
_send_chunk(socket, "🖹\t");
} else {
_send_chunk(socket, "\t");
}
send_offset += sent;
}
}
ESP_LOGW(TAG, "file return done");
_send_chunk(socket, "<a href=\"");
_send_chunk(socket, request->path);
_send_chunk(socket, file_info.fname);
if ((file_info.fattrib & AM_DIR) != 0) {
_send_chunk(socket, "/");
}
_send_chunk(socket, "\">");
f_close(&active_file);
_send_chunk(socket, file_info.fname);
_send_chunk(socket, "</a><a>✏️</a><a>🗑️</a><br/>");
res = f_readdir(&dir, &file_info);
}
_send_chunk(socket, "</pre>Upload:<input type=\"file\" multiple></body></html>");
_send_chunk(socket, "");
f_closedir(&dir);
}
} else { // Dealing with a file.
if (strcmp(request->method, "GET") == 0) {
FIL active_file;
FRESULT result = f_open(fs, &active_file, path, FA_READ);
if (result != FR_OK) {
// TODO: 404
}
uint32_t total_length = f_size(&active_file);
char encoded_len[10];
snprintf(encoded_len, sizeof(encoded_len), "%d", total_length);
_send_str(socket, "HTTP/1.1 200 OK\r\nContent-Length: ");
_send_str(socket, encoded_len);
_send_str(socket, "\r\n");
if (_endswith(path, ".txt") || _endswith(path, ".py")) {
_send_str(socket, "Content-Type: text/plain\r\n");
} else {
_send_str(socket, "Content-Type: application/octet-stream\r\n");
}
_send_str(socket, "\r\n");
uint32_t total_read = 0;
while (total_read < total_length) {
uint8_t data_buffer[64];
size_t quantity_read;
f_read(&active_file, data_buffer, 64, &quantity_read);
total_read += quantity_read;
uint32_t send_offset = 0;
while (send_offset < quantity_read) {
int sent = socketpool_socket_send(socket, data_buffer + send_offset, quantity_read - send_offset);
if (sent < 0) {
ESP_LOGE(TAG, "file send error %d", sent);
break;
}
send_offset += sent;
}
}
ESP_LOGW(TAG, "file return done");
f_close(&active_file);
}
}
}
} else if (memcmp(request->path, "/cp/", 4) == 0) {
ESP_LOGW(TAG, "circuitpython %s %s", request->method, request->path + 3);
const char *path = request->path + 3;
if (strcmp(request->method, "GET") != 0) {
// Return error if not a GET
}
if (strcmp(path, "/devices.json") == 0) {
mdns_remoteservice_obj_t found_devices[32];
size_t total_results = mdns_server_find(&mdns, "_circuitpython", "_tcp", 1, found_devices, MP_ARRAY_SIZE(found_devices));
size_t count = MIN(total_results, MP_ARRAY_SIZE(found_devices));
const char *ok_response = "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\nContent-Type: application/json\r\n\r\n";
socketpool_socket_send(socket, (const uint8_t *)ok_response, strlen(ok_response));
_send_chunk(socket, "{\"total\": ");
char total_encoded[4];
snprintf(total_encoded, sizeof(total_encoded), "%d", total_results);
_send_chunk(socket, total_encoded);
_send_chunk(socket, ", \"devices\": [");
ESP_LOGW(TAG, "%d total devices", total_results);
for (size_t i = 0; i < count; i++) {
if (i > 0) {
_send_chunk(socket, ",");
}
_send_chunk(socket, "{\"hostname\": \"");
_send_chunk(socket, common_hal_mdns_remoteservice_get_hostname(&found_devices[i]));
_send_chunk(socket, "\", \"instance_name\": \"");
_send_chunk(socket, common_hal_mdns_remoteservice_get_instance_name(&found_devices[i]));
_send_chunk(socket, "\", \"port\": ");
char port_encoded[4];
int port = common_hal_mdns_remoteservice_get_port(&found_devices[i]);
snprintf(port_encoded, sizeof(port_encoded), "%d", port);
_send_chunk(socket, port_encoded);
_send_chunk(socket, "}");
common_hal_mdns_remoteservice_deinit(&found_devices[i]);
}
_send_chunk(socket, "]}");
// Empty chunk signals the end of the response.
_send_chunk(socket, "");
} else if (strcmp(path, "/version.json") == 0) {
const char *ok_response = "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\nContent-Type: application/json\r\n\r\n";
socketpool_socket_send(socket, (const uint8_t *)ok_response, strlen(ok_response));
_send_chunk(socket, "{\"web_api_version\": 1, \"version\": \"");
_send_chunk(socket, MICROPY_GIT_TAG);
_send_chunk(socket, "\", \"build_date\": \"");
_send_chunk(socket, MICROPY_BUILD_DATE);
_send_chunk(socket, "\", \"board_name\": \"");
_send_chunk(socket, MICROPY_HW_BOARD_NAME);
_send_chunk(socket, "\", \"mcu_name\": \"");
_send_chunk(socket, MICROPY_HW_MCU_NAME);
_send_chunk(socket, "\", \"board_id\": \"");
_send_chunk(socket, CIRCUITPY_BOARD_ID);
_send_chunk(socket, "\", \"creator_id\": ");
char encoded_id[8];
snprintf(encoded_id, sizeof(encoded_id), "%d", CIRCUITPY_CREATOR_ID);
_send_chunk(socket, encoded_id);
_send_chunk(socket, ", \"creation_id\": ");
snprintf(encoded_id, sizeof(encoded_id), "%d", CIRCUITPY_CREATION_ID);
_send_chunk(socket, encoded_id);
_send_chunk(socket, "}");
// Empty chunk signals the end of the response.
_send_chunk(socket, "");
}
} else {
const char *ok_response = "HTTP/1.1 200 OK\r\nContent-Length: 11\r\nContent-Type: text/plain\r\n\r\nHello World";
int sent = socketpool_socket_send(socket, (const uint8_t *)ok_response, strlen(ok_response));