WIP doesn't flush response before closing

This commit is contained in:
Scott Shawcroft 2022-06-14 17:02:34 -07:00
parent 301f3e0456
commit 61d0f7947b
No known key found for this signature in database
GPG Key ID: 0DFD512649C052DA
8 changed files with 263 additions and 52 deletions

View File

@ -40,6 +40,9 @@
#include "common-hal/pulseio/PulseIn.h"
#endif
#if CIRCUITPY_WEB_WORKFLOW
#include "supervisor/shared/web_workflow/web_workflow.h"
#endif
void port_background_task(void) {
// Zero delay in case FreeRTOS wants to switch to something else.
@ -47,6 +50,10 @@ void port_background_task(void) {
#if CIRCUITPY_PULSEIO
pulsein_background();
#endif
#if CIRCUITPY_WEB_WORKFLOW
supervisor_web_workflow_background();
#endif
}
void port_start_background_task(void) {

View File

@ -37,6 +37,10 @@
#include "components/lwip/lwip/src/include/lwip/sys.h"
#include "components/lwip/lwip/src/include/lwip/netdb.h"
#include "esp_log.h"
static const char *TAG = "socket";
STATIC socketpool_socket_obj_t *open_socket_handles[CONFIG_LWIP_MAX_SOCKETS];
void socket_user_reset(void) {
@ -62,8 +66,7 @@ bool register_open_socket(socketpool_socket_obj_t *self) {
return false;
}
socketpool_socket_obj_t *common_hal_socketpool_socket_accept(socketpool_socket_obj_t *self,
uint8_t *ip, uint32_t *port) {
int socketpool_socket_accept(socketpool_socket_obj_t *self, uint8_t *ip, uint32_t *port) {
struct sockaddr_in accept_addr;
socklen_t socklen = sizeof(accept_addr);
int newsoc = -1;
@ -81,7 +84,10 @@ socketpool_socket_obj_t *common_hal_socketpool_socket_accept(socketpool_socket_o
newsoc = lwip_accept(self->num, (struct sockaddr *)&accept_addr, &socklen);
// In non-blocking mode, fail instead of timing out
if (newsoc == -1 && self->timeout_ms == 0) {
mp_raise_OSError(MP_EAGAIN);
if (errno != EAGAIN) {
ESP_LOGE(TAG, "accept failed %d", errno);
}
return -MP_EAGAIN;
}
}
@ -90,8 +96,17 @@ socketpool_socket_obj_t *common_hal_socketpool_socket_accept(socketpool_socket_o
memcpy((void *)ip, (void *)&accept_addr.sin_addr.s_addr, sizeof(accept_addr.sin_addr.s_addr));
*port = accept_addr.sin_port;
} else {
mp_raise_OSError(ETIMEDOUT);
return -ETIMEDOUT;
}
if (newsoc < 0) {
return -MP_EBADF;
}
return newsoc;
}
socketpool_socket_obj_t *common_hal_socketpool_socket_accept(socketpool_socket_obj_t *self,
uint8_t *ip, uint32_t *port) {
int newsoc = socketpool_socket_accept(self, ip, port);
if (newsoc > 0) {
// Create the socket
@ -108,7 +123,7 @@ socketpool_socket_obj_t *common_hal_socketpool_socket_accept(socketpool_socket_o
lwip_fcntl(newsoc, F_SETFL, O_NONBLOCK);
return sock;
} else {
mp_raise_OSError(MP_EBADF);
mp_raise_OSError(-newsoc);
return NULL;
}
}
@ -125,17 +140,22 @@ bool common_hal_socketpool_socket_bind(socketpool_socket_obj_t *self,
if (err != 0) {
mp_raise_RuntimeError(translate("Cannot set socket options"));
}
int result = lwip_bind(self->num, (struct sockaddr *)&bind_addr, sizeof(bind_addr)) == 0;
return result;
int result = lwip_bind(self->num, (struct sockaddr *)&bind_addr, sizeof(bind_addr));
ESP_LOGE(TAG, "bind result %d", result);
return result == 0;
}
void common_hal_socketpool_socket_close(socketpool_socket_obj_t *self) {
void socketpool_socket_close(socketpool_socket_obj_t *self) {
self->connected = false;
if (self->num >= 0) {
lwip_shutdown(self->num, 0);
lwip_shutdown(self->num, SHUT_RDWR);
lwip_close(self->num);
self->num = -1;
}
}
void common_hal_socketpool_socket_close(socketpool_socket_obj_t *self) {
socketpool_socket_close(self);
// Remove socket record
for (size_t i = 0; i < MP_ARRAY_SIZE(open_socket_handles); i++) {
if (open_socket_handles[i] == self) {
@ -199,7 +219,9 @@ bool common_hal_socketpool_socket_get_connected(socketpool_socket_obj_t *self) {
}
bool common_hal_socketpool_socket_listen(socketpool_socket_obj_t *self, int backlog) {
return lwip_listen(self->num, backlog) == 0;
int result = lwip_listen(self->num, backlog);
ESP_LOGE(TAG, "listen result %d", result);
return result == 0;
}
mp_uint_t common_hal_socketpool_socket_recvfrom_into(socketpool_socket_obj_t *self,
@ -242,7 +264,8 @@ mp_uint_t common_hal_socketpool_socket_recvfrom_into(socketpool_socket_obj_t *se
return received;
}
mp_uint_t common_hal_socketpool_socket_recv_into(socketpool_socket_obj_t *self, const uint8_t *buf, uint32_t len) {
int socketpool_socket_recv_into(socketpool_socket_obj_t *self,
const uint8_t *buf, uint32_t len) {
int received = 0;
bool timed_out = false;
@ -261,31 +284,52 @@ mp_uint_t common_hal_socketpool_socket_recv_into(socketpool_socket_obj_t *self,
// In non-blocking mode, fail instead of looping
if (received == -1 && self->timeout_ms == 0) {
mp_raise_OSError(MP_EAGAIN);
if (errno != EAGAIN) {
ESP_LOGE(TAG, "recv %d", errno);
}
return -MP_EAGAIN;
}
}
} else {
mp_raise_OSError(MP_EBADF);
return -MP_EBADF;
}
if (timed_out) {
mp_raise_OSError(ETIMEDOUT);
return -ETIMEDOUT;
}
return received;
}
mp_uint_t common_hal_socketpool_socket_send(socketpool_socket_obj_t *self, const uint8_t *buf, uint32_t len) {
mp_uint_t common_hal_socketpool_socket_recv_into(socketpool_socket_obj_t *self, const uint8_t *buf, uint32_t len) {
int received = socketpool_socket_recv_into(self, buf, len);
if (received < 0) {
mp_raise_OSError(received);
}
return received;
}
int socketpool_socket_send(socketpool_socket_obj_t *self, const uint8_t *buf, uint32_t len) {
int sent = -1;
if (self->num != -1) {
// LWIP Socket
// TODO: deal with potential failure/add timeout?
sent = lwip_send(self->num, buf, len, 0);
} else {
mp_raise_OSError(MP_EBADF);
sent = -MP_EBADF;
}
if (sent < 0) {
mp_raise_OSError(errno);
return -errno;
}
return sent;
}
mp_uint_t common_hal_socketpool_socket_send(socketpool_socket_obj_t *self, const uint8_t *buf, uint32_t len) {
int sent = socketpool_socket_send(self, buf, len);
if (sent < 0) {
mp_raise_OSError(-sent);
}
return sent;
}

View File

@ -40,29 +40,9 @@ void common_hal_socketpool_socketpool_construct(socketpool_socketpool_obj_t *sel
}
}
void socketpool_socket(socketpool_socketpool_obj_t *self,
socketpool_socketpool_addressfamily_t family, socketpool_socketpool_sock_t type
bool socketpool_socket(socketpool_socketpool_obj_t *self,
socketpool_socketpool_addressfamily_t family, socketpool_socketpool_sock_t type,
socketpool_socket_obj_t *sock) {
sock->type = socket_type;
sock->family = addr_family;
sock->ipproto = ipproto;
sock->pool = self;
sock->timeout_ms = (uint)-1;
// Create LWIP socket
int socknum = -1;
socknum = lwip_socket(sock->family, sock->type, sock->ipproto);
if (socknum < 0 || !register_open_socket(sock)) {
mp_raise_RuntimeError(translate("Out of sockets"));
}
sock->num = socknum;
// Sockets should be nonblocking in most cases
lwip_fcntl(socknum, F_SETFL, O_NONBLOCK);
}
socketpool_socket_obj_t *common_hal_socketpool_socket(socketpool_socketpool_obj_t *self,
socketpool_socketpool_addressfamily_t family, socketpool_socketpool_sock_t type) {
int addr_family;
int ipproto;
if (family == SOCKETPOOL_AF_INET) {
@ -81,15 +61,37 @@ socketpool_socket_obj_t *common_hal_socketpool_socket(socketpool_socketpool_obj_
} else { // SOCKETPOOL_SOCK_RAW
socket_type = SOCK_RAW;
}
sock->type = socket_type;
sock->family = addr_family;
sock->ipproto = ipproto;
sock->pool = self;
sock->timeout_ms = (uint)-1;
if (addr_family == AF_INET6 || ipproto == IPPROTO_IPV6) {
// Create LWIP socket
int socknum = -1;
socknum = lwip_socket(sock->family, sock->type, sock->ipproto);
if (socknum < 0) {
return false;
}
sock->num = socknum;
// Sockets should be nonblocking in most cases
lwip_fcntl(socknum, F_SETFL, O_NONBLOCK);
return true;
}
socketpool_socket_obj_t *common_hal_socketpool_socket(socketpool_socketpool_obj_t *self,
socketpool_socketpool_addressfamily_t family, socketpool_socketpool_sock_t type) {
if (family != SOCKETPOOL_AF_INET) {
mp_raise_NotImplementedError(translate("Only IPv4 sockets supported"));
}
socketpool_socket_obj_t *sock = m_new_obj_with_finaliser(socketpool_socket_obj_t);
sock->base.type = &socketpool_socket_type;
socketpool_socket(self, family, type, sock);
if (!socketpool_socket(self, family, type, sock) ||
!register_open_socket(sock)) {
mp_raise_RuntimeError(translate("Out of sockets"));
}
return sock;
}

View File

@ -75,7 +75,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(socketpool_socket___exit___obj, 4, 4,
//| creating a new socket of type SOCK_STREAM.
//| Returns a tuple of (new_socket, remote_address)"""
//|
STATIC mp_obj_t socketpool_socket_accept(mp_obj_t self_in) {
STATIC mp_obj_t _socketpool_socket_accept(mp_obj_t self_in) {
socketpool_socket_obj_t *self = MP_OBJ_TO_PTR(self_in);
uint8_t ip[4];
uint32_t port;
@ -87,7 +87,7 @@ STATIC mp_obj_t socketpool_socket_accept(mp_obj_t self_in) {
tuple_contents[1] = netutils_format_inet_addr(ip, port, NETUTILS_BIG);
return mp_obj_new_tuple(2, tuple_contents);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(socketpool_socket_accept_obj, socketpool_socket_accept);
STATIC MP_DEFINE_CONST_FUN_OBJ_1(socketpool_socket_accept_obj, _socketpool_socket_accept);
//| def bind(self, address: Tuple[str, int]) -> None:
//| """Bind a socket to an address
@ -120,12 +120,12 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(socketpool_socket_bind_obj, socketpool_socket_b
//| def close(self) -> None:
//| """Closes this Socket and makes its resources available to its SocketPool."""
//|
STATIC mp_obj_t socketpool_socket_close(mp_obj_t self_in) {
STATIC mp_obj_t _socketpool_socket_close(mp_obj_t self_in) {
socketpool_socket_obj_t *self = MP_OBJ_TO_PTR(self_in);
common_hal_socketpool_socket_close(self);
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(socketpool_socket_close_obj, socketpool_socket_close);
STATIC MP_DEFINE_CONST_FUN_OBJ_1(socketpool_socket_close_obj, _socketpool_socket_close);
//| def connect(self, address: Tuple[str, int]) -> None:
//| """Connect a socket to a remote address
@ -208,7 +208,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(socketpool_socket_recvfrom_into_obj, socketpool
//| :param int bufsize: optionally, a maximum number of bytes to read."""
//| ...
//|
STATIC mp_obj_t socketpool_socket_recv_into(size_t n_args, const mp_obj_t *args) {
STATIC mp_obj_t _socketpool_socket_recv_into(size_t n_args, const mp_obj_t *args) {
socketpool_socket_obj_t *self = MP_OBJ_TO_PTR(args[0]);
if (common_hal_socketpool_socket_get_closed(self)) {
// Bad file number.
@ -238,7 +238,7 @@ STATIC mp_obj_t socketpool_socket_recv_into(size_t n_args, const mp_obj_t *args)
mp_int_t ret = common_hal_socketpool_socket_recv_into(self, (byte *)bufinfo.buf, len);
return mp_obj_new_int_from_uint(ret);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(socketpool_socket_recv_into_obj, 2, 3, socketpool_socket_recv_into);
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(socketpool_socket_recv_into_obj, 2, 3, _socketpool_socket_recv_into);
//| def send(self, bytes: ReadableBuffer) -> int:
//| """Send some bytes to the connected remote address.
@ -247,7 +247,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(socketpool_socket_recv_into_obj, 2, 3
//| :param ~bytes bytes: some bytes to send"""
//| ...
//|
STATIC mp_obj_t socketpool_socket_send(mp_obj_t self_in, mp_obj_t buf_in) {
STATIC mp_obj_t _socketpool_socket_send(mp_obj_t self_in, mp_obj_t buf_in) {
socketpool_socket_obj_t *self = MP_OBJ_TO_PTR(self_in);
if (common_hal_socketpool_socket_get_closed(self)) {
// Bad file number.
@ -264,7 +264,7 @@ STATIC mp_obj_t socketpool_socket_send(mp_obj_t self_in, mp_obj_t buf_in) {
}
return mp_obj_new_int_from_uint(ret);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(socketpool_socket_send_obj, socketpool_socket_send);
STATIC MP_DEFINE_CONST_FUN_OBJ_2(socketpool_socket_send_obj, _socketpool_socket_send);
//| def sendto(self, bytes: ReadableBuffer, address: Tuple[str, int]) -> int:
//| """Send some bytes to a specific address.

View File

@ -47,4 +47,11 @@ mp_uint_t common_hal_socketpool_socket_sendto(socketpool_socket_obj_t *self,
const char *host, size_t hostlen, uint32_t port, const uint8_t *buf, uint32_t len);
void common_hal_socketpool_socket_settimeout(socketpool_socket_obj_t *self, uint32_t timeout_ms);
// Non-allocating versions for internal use.
int socketpool_socket_accept(socketpool_socket_obj_t *self, uint8_t *ip, uint32_t *port);
void socketpool_socket_close(socketpool_socket_obj_t *self);
int socketpool_socket_send(socketpool_socket_obj_t *self, const uint8_t *buf, uint32_t len);
int socketpool_socket_recv_into(socketpool_socket_obj_t *self,
const uint8_t *buf, uint32_t len);
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_SOCKETPOOL_SOCKET_H

View File

@ -52,4 +52,10 @@ socketpool_socket_obj_t *common_hal_socketpool_socket(socketpool_socketpool_obj_
mp_obj_t common_hal_socketpool_socketpool_gethostbyname(socketpool_socketpool_obj_t *self,
const char *host);
// Non-allocating version for internal use. These sockets are not registered and, therefore, not
// closed automatically.
bool socketpool_socket(socketpool_socketpool_obj_t *self,
socketpool_socketpool_addressfamily_t family, socketpool_socketpool_sock_t type,
socketpool_socket_obj_t *sock);
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_SOCKETPOOL_SOCKETPOOL_H

View File

@ -30,6 +30,10 @@
#include "supervisor/shared/translate/translate.h"
#include "supervisor/shared/web_workflow/web_workflow.h"
#include "shared-bindings/socketpool/__init__.h"
#include "shared-bindings/socketpool/Socket.h"
#include "shared-bindings/socketpool/SocketPool.h"
#if CIRCUITPY_WIFI
#include "shared-bindings/wifi/__init__.h"
#endif
@ -38,10 +42,35 @@
#include "shared-module/dotenv/__init__.h"
#endif
#include "esp_log.h"
static const char *TAG = "CP webserver";
enum request_state {
STATE_METHOD,
STATE_PATH,
STATE_VERSION,
STATE_HEADER_KEY,
STATE_HEADER_VALUE,
STATE_BODY
};
typedef struct {
enum request_state state;
char method[6];
char path[256];
uint32_t content_length;
size_t offset;
} _request;
static wifi_radio_error_t wifi_status = WIFI_RADIO_ERROR_NONE;
static socketpool_socketpool_obj_t pool;
static socketpool_socket_obj_t listening;
static socketpool_socket_obj_t active;
static _request active_request;
void supervisor_web_workflow_status(void) {
serial_write_compressed(translate("Wi-Fi: "));
@ -66,6 +95,13 @@ void supervisor_web_workflow_status(void) {
void supervisor_start_web_workflow(void) {
#if CIRCUITPY_WEB_WORKFLOW && CIRCUITPY_WIFI
if (common_hal_wifi_radio_get_enabled(&common_hal_wifi_radio_obj) &&
wifi_radio_get_ipv4_address(&common_hal_wifi_radio_obj) != 0) {
// Already started.
return;
}
char ssid[33];
char password[64];
mp_int_t ssid_len = 0;
@ -102,11 +138,19 @@ void supervisor_start_web_workflow(void) {
listening.base.type = &socketpool_socket_type;
socketpool_socket(&pool, SOCKETPOOL_AF_INET, SOCKETPOOL_SOCK_STREAM, &listening);
common_hal_socketpool_socket_settimeout(&listening, 0);
// Bind to any ip.
// TODO: Make this port .env configurable.
common_hal_socketpool_socket_bind(&listening, "", 0, 80);
const char *ip = "192.168.1.94";
common_hal_socketpool_socket_bind(&listening, ip, strlen(ip), 80);
common_hal_socketpool_socket_listen(&listening, 1);
ESP_LOGW(TAG, "listening on socket %d", listening.num);
active.base.type = &socketpool_socket_type;
active.num = -1;
active.connected = false;
// Accept a connection and start parsing:
// * HTTP method
// * HTTP path
@ -135,5 +179,105 @@ void supervisor_start_web_workflow(void) {
#endif
}
static void _process_request(socketpool_socket_obj_t *socket, _request *request) {
bool more = true;
bool error = false;
uint8_t c;
while (more && !error) {
int len = socketpool_socket_recv_into(socket, &c, 1);
if (len != 1) {
if (len != -EAGAIN && len != -EBADF) {
ESP_LOGW(TAG, "received %d", len);
}
more = false;
break;
}
ESP_LOGI(TAG, "%c", c);
switch (request->state) {
case STATE_METHOD: {
if (c == ' ') {
request->method[request->offset] = '\0';
request->offset = 0;
request->state = STATE_PATH;
} else if (request->offset > sizeof(request->method) - 1) {
// Skip methods that are too long.
} else {
request->method[request->offset] = c;
request->offset++;
}
break;
}
case STATE_PATH: {
if (c == ' ') {
request->path[request->offset] = '\0';
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 {
request->path[request->offset] = c;
request->offset++;
}
break;
}
case STATE_VERSION:
break;
case STATE_HEADER_KEY:
break;
case STATE_HEADER_VALUE:
break;
case STATE_BODY:
break;
}
}
if (error) {
const char *error_response = "HTTP/1.1 501 Not Implemented";
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);
}
}
void supervisor_web_workflow_background(void) {
// Otherwise, see if we have another socket to accept.
if (!common_hal_socketpool_socket_get_connected(&active) &&
!common_hal_socketpool_socket_get_closed(&listening) &&
listening.num > 0) {
uint32_t ip;
uint32_t port;
int newsoc = socketpool_socket_accept(&listening, (uint8_t *)&ip, &port);
if (newsoc != -11) {
ESP_LOGI(TAG, "newsoc %d", newsoc);
}
if (newsoc > 0) {
ESP_LOGE(TAG, "Accepted socket %d", newsoc);
// TODO: Don't do this because it uses the private struct layout.
// Create the socket
active.num = newsoc;
active.pool = &pool;
active.connected = true;
common_hal_socketpool_socket_settimeout(&active, 0);
active_request.state = STATE_METHOD;
active_request.offset = 0;
lwip_fcntl(newsoc, F_SETFL, O_NONBLOCK);
}
}
// If we have a request in progress, continue working on it.
if (common_hal_socketpool_socket_get_connected(&active)) {
_process_request(&active, &active_request);
}
}
void supervisor_stop_web_workflow(void) {
}

View File

@ -28,8 +28,9 @@
#include <stdbool.h>
void supervisor_wifi_background(void);
void supervisor_wifi_init(void);
// This background function should be called repeatedly. It cannot be done based
// on events.
void supervisor_web_workflow_background(void);
void supervisor_web_workflow_status(void);
void supervisor_start_web_workflow(void);
void supervisor_stop_web_workflow(void);