From 0882cbf82396f380a80fa6f692e19eeddc8ecea3 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Wed, 15 Jun 2022 12:10:42 -0700 Subject: [PATCH] web server kinda works --- ports/espressif/common-hal/mdns/Server.c | 19 ++-- .../espressif/common-hal/socketpool/Socket.c | 5 +- shared-bindings/mdns/Server.h | 3 + supervisor/shared/web_workflow/web_workflow.c | 98 +++++++++++++++++-- 4 files changed, 110 insertions(+), 15 deletions(-) diff --git a/ports/espressif/common-hal/mdns/Server.c b/ports/espressif/common-hal/mdns/Server.c index 9da67a9145..38deac9c3d 100644 --- a/ports/espressif/common-hal/mdns/Server.c +++ b/ports/espressif/common-hal/mdns/Server.c @@ -35,13 +35,9 @@ STATIC bool inited = false; -void common_hal_mdns_server_construct(mdns_server_obj_t *self, mp_obj_t network_interface) { - if (network_interface != MP_OBJ_FROM_PTR(&common_hal_wifi_radio_obj)) { - mp_raise_ValueError(translate("mDNS only works with built-in WiFi")); - return; - } +void mdns_server_construct(mdns_server_obj_t *self) { if (inited) { - mp_raise_RuntimeError(translate("mDNS already initialized")); + return; } mdns_init(); @@ -65,6 +61,17 @@ void common_hal_mdns_server_construct(mdns_server_obj_t *self, mp_obj_t network_ mdns_delegate_hostname_add("circuitpython", &our_ip); } +void common_hal_mdns_server_construct(mdns_server_obj_t *self, mp_obj_t network_interface) { + if (network_interface != MP_OBJ_FROM_PTR(&common_hal_wifi_radio_obj)) { + mp_raise_ValueError(translate("mDNS only works with built-in WiFi")); + return; + } + if (inited) { + mp_raise_RuntimeError(translate("mDNS already initialized")); + } + mdns_server_construct(self); +} + void common_hal_mdns_server_deinit(mdns_server_obj_t *self) { inited = false; mdns_free(); diff --git a/ports/espressif/common-hal/socketpool/Socket.c b/ports/espressif/common-hal/socketpool/Socket.c index 86944f8b00..fc42345e4a 100644 --- a/ports/espressif/common-hal/socketpool/Socket.c +++ b/ports/espressif/common-hal/socketpool/Socket.c @@ -284,7 +284,10 @@ int socketpool_socket_recv_into(socketpool_socket_obj_t *self, // In non-blocking mode, fail instead of looping if (received == -1 && self->timeout_ms == 0) { - if (errno != EAGAIN) { + if (errno == ENOTCONN) { + self->connected = false; + return -MP_ENOTCONN; + } else if (errno != EAGAIN) { ESP_LOGE(TAG, "recv %d", errno); } return -MP_EAGAIN; diff --git a/shared-bindings/mdns/Server.h b/shared-bindings/mdns/Server.h index a178e9bd2d..2146dca311 100644 --- a/shared-bindings/mdns/Server.h +++ b/shared-bindings/mdns/Server.h @@ -41,3 +41,6 @@ const char *common_hal_mdns_server_get_instance_name(mdns_server_obj_t *self); void common_hal_mdns_server_set_instance_name(mdns_server_obj_t *self, const char *instance_name); mp_obj_t common_hal_mdns_server_find(mdns_server_obj_t *self, const char *service_type, const char *protocol, mp_float_t timeout); 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); diff --git a/supervisor/shared/web_workflow/web_workflow.c b/supervisor/shared/web_workflow/web_workflow.c index 2ca25c6b72..fc37dea2db 100644 --- a/supervisor/shared/web_workflow/web_workflow.c +++ b/supervisor/shared/web_workflow/web_workflow.c @@ -30,6 +30,7 @@ #include "supervisor/shared/translate/translate.h" #include "supervisor/shared/web_workflow/web_workflow.h" +#include "shared-bindings/mdns/Server.h" #include "shared-bindings/socketpool/__init__.h" #include "shared-bindings/socketpool/Socket.h" #include "shared-bindings/socketpool/SocketPool.h" @@ -59,12 +60,18 @@ typedef struct { enum request_state state; char method[6]; char path[256]; + char header_key[64]; + char header_value[64]; uint32_t content_length; size_t offset; + bool redirect; + bool done; } _request; static wifi_radio_error_t wifi_status = WIFI_RADIO_ERROR_NONE; +static mdns_server_obj_t mdns; + static socketpool_socketpool_obj_t pool; static socketpool_socket_obj_t listening; static socketpool_socket_obj_t active; @@ -133,6 +140,8 @@ void supervisor_start_web_workflow(void) { return; } + mdns_server_construct(&mdns); + pool.base.type = &socketpool_socketpool_type; common_hal_socketpool_socketpool_construct(&pool, &common_hal_wifi_radio_obj); @@ -183,6 +192,7 @@ static void _process_request(socketpool_socket_obj_t *socket, _request *request) bool more = true; bool error = false; uint8_t c; + // This code assumes header lines are terminated with \r\n while (more && !error) { int len = socketpool_socket_recv_into(socket, &c, 1); if (len != 1) { @@ -192,7 +202,7 @@ static void _process_request(socketpool_socket_obj_t *socket, _request *request) more = false; break; } - ESP_LOGI(TAG, "%c", c); + // ESP_LOGI(TAG, "%c", c); switch (request->state) { case STATE_METHOD: { if (c == ' ') { @@ -213,7 +223,6 @@ static void _process_request(socketpool_socket_obj_t *socket, _request *request) request->offset = 0; ESP_LOGW(TAG, "Request %s %s", request->method, request->path); request->state = STATE_VERSION; - error = true; } else if (request->offset > sizeof(request->path) - 1) { // Skip methods that are too long. } else { @@ -222,25 +231,93 @@ static void _process_request(socketpool_socket_obj_t *socket, _request *request) } break; } - case STATE_VERSION: + case STATE_VERSION: { + const char *supported_version = "HTTP/1.1\r"; + error = supported_version[request->offset] != c; + request->offset++; + if (request->offset == strlen(supported_version)) { + ESP_LOGW(TAG, "Version ok"); + request->state = STATE_HEADER_KEY; + request->offset = 0; + } break; - case STATE_HEADER_KEY: + } + case STATE_HEADER_KEY: { + if (c == '\r') { + request->state = STATE_BODY; + ESP_LOGW(TAG, "Body"); + } else if (c == '\n') { + // Consume the \n + } else if (c == ':') { + request->header_key[request->offset] = '\0'; + request->offset = 0; + request->state = STATE_HEADER_VALUE; + } else if (request->offset > sizeof(request->header_key) - 1) { + // Skip methods that are too long. + } else { + request->header_key[request->offset] = c; + request->offset++; + } break; - case STATE_HEADER_VALUE: + } + case STATE_HEADER_VALUE: { + if (request->offset == 0) { + error = c != ' '; + request->offset++; + } else if (c == '\r') { + request->header_value[request->offset - 1] = '\0'; + request->offset = 0; + request->state = STATE_HEADER_KEY; + if (strcmp(request->header_key, "Authorization") == 0) { + ESP_LOGW(TAG, "Authorization"); + } else if (strcmp(request->header_key, "Host") == 0) { + ESP_LOGW(TAG, "Host header check '%s'", request->header_value); + request->redirect = strcmp(request->header_value, "circuitpython.local") == 0; + } + ESP_LOGW(TAG, "Header %s %s", request->header_key, request->header_value); + } else if (request->offset > sizeof(request->header_value) - 1) { + // Skip methods that are too long. + } else { + request->header_value[request->offset - 1] = c; + request->offset++; + } break; + } case STATE_BODY: + request->done = true; + more = false; break; } } if (error) { - const char *error_response = "HTTP/1.1 501 Not Implemented"; + const char *error_response = "HTTP/1.1 501 Not Implemented\r\n\r\n"; 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 *)error_response, strlen(error_response)); ESP_LOGW(TAG, "sent %d %d", sent, err); - vTaskDelay(0); - socketpool_socket_close(socket); } + if (!request->done) { + 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); + } 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)); + ESP_LOGW(TAG, "sent ok %d", sent); + } + request->done = false; } @@ -249,6 +326,9 @@ void supervisor_web_workflow_background(void) { if (!common_hal_socketpool_socket_get_connected(&active) && !common_hal_socketpool_socket_get_closed(&listening) && listening.num > 0) { + if (!common_hal_socketpool_socket_get_closed(&active)) { + common_hal_socketpool_socket_close(&active); + } uint32_t ip; uint32_t port; int newsoc = socketpool_socket_accept(&listening, (uint8_t *)&ip, &port); @@ -268,6 +348,8 @@ void supervisor_web_workflow_background(void) { active_request.state = STATE_METHOD; active_request.offset = 0; + active_request.done = false; + active_request.redirect = false; lwip_fcntl(newsoc, F_SETFL, O_NONBLOCK); }