diff --git a/docs/workflows.md b/docs/workflows.md index 1b262011b2..b011acd01c 100644 --- a/docs/workflows.md +++ b/docs/workflows.md @@ -286,6 +286,44 @@ not protected by basic auth in case the device is someone elses. Only `GET` requests are supported and will return `405 Method Not Allowed` otherwise. +#### `/cp/devices.json` + +Returns information about other devices found on the network using MDNS. + +* `total`: Total MDNS response count. May be more than in `devices` if internal limits were hit. +* `devices`: List of discovered devices. + * `hostname`: MDNS hostname + * `instance_name`: MDNS instance name. Defaults to human readable board name. + * `port`: Port of CircuitPython Web API + * `ip`: IP address + +Example: +```sh +curl -v -L http://circuitpython.local/cp/devices.json +``` + +```json +{ + "total": 1, + "devices": [ + { + "hostname": "cpy-951032", + "instance_name": "Adafruit Feather ESP32-S2 TFT", + "port": 80, + "ip": "192.168.1.235" + } + ] +} +``` + +#### `/cp/serial/` + + +Serves a basic serial terminal program when a `GET` request is received without the +`Upgrade: websocket` header. Otherwise the socket is upgraded to a WebSocket. See WebSockets below for more detail. + +This is an authenticated endpoint in both modes. + #### `/cp/version.json` Returns information about the device. @@ -323,36 +361,6 @@ curl -v -L http://circuitpython.local/cp/version.json } ``` -#### `/cp/devices.json` - -Returns information about other devices found on the network using MDNS. - -* `total`: Total MDNS response count. May be more than in `devices` if internal limits were hit. -* `devices`: List of discovered devices. - * `hostname`: MDNS hostname - * `instance_name`: MDNS instance name. Defaults to human readable board name. - * `port`: Port of CircuitPython Web API - * `ip`: IP address - -Example: -```sh -curl -v -L http://circuitpython.local/cp/devices.json -``` - -```json -{ - "total": 1, - "devices": [ - { - "hostname": "cpy-951032", - "instance_name": "Adafruit Feather ESP32-S2 TFT", - "port": 80, - "ip": "192.168.1.235" - } - ] -} -``` - ### Static files * `/favicon.ico` - Blinka @@ -361,4 +369,12 @@ curl -v -L http://circuitpython.local/cp/devices.json ### WebSocket -Coming soon! +The CircuitPython serial interactions are available over a WebSocket. A WebSocket begins as a +special HTTP request that gets upgraded to a WebSocket. Authentication happens before upgrading. + +WebSockets are *not* bare sockets once upgraded. Instead they have their own framing format for data. +CircuitPython can handle PING and CLOSE opcodes. All others are treated as TEXT. Data to +CircuitPython is expected to be masked UTF-8, as the spec requires. Data from CircuitPython to the +client is unmasked. It is also unbuffered so the client will get a variety of frame sizes. + +Only one WebSocket at a time is supported. diff --git a/supervisor/shared/web_workflow/static/welcome.html b/supervisor/shared/web_workflow/static/welcome.html index 6ef1ccd767..139e9eba39 100644 --- a/supervisor/shared/web_workflow/static/welcome.html +++ b/supervisor/shared/web_workflow/static/welcome.html @@ -3,11 +3,12 @@ CircuitPython +

 Welcome!

- Welcome to CircuitPython's Web API. Go to the file browser to work with files in the CIRCUITPY drive. Make sure you've set CIRCUITPY_WEB_API_PASSWORD='somepassword' in /.env. Provide the password when the browser prompts for it. Leave the username blank. + Welcome to CircuitPython's Web API. Go to the file browser to work with files in the CIRCUITPY drive. Go to the serial terminal to see code output and interact with the REPL. Make sure you've set CIRCUITPY_WEB_API_PASSWORD='somepassword' in /.env. Provide the password when the browser prompts for it. Leave the username blank.

Device Info

Board:
Version:
diff --git a/supervisor/shared/web_workflow/web_workflow.c b/supervisor/shared/web_workflow/web_workflow.c index c7bf673fdc..3f9920d1c7 100644 --- a/supervisor/shared/web_workflow/web_workflow.c +++ b/supervisor/shared/web_workflow/web_workflow.c @@ -1019,14 +1019,13 @@ static bool _reply(socketpool_socket_obj_t *socket, _request *request) { } else if (strcmp(path, "/version.json") == 0) { _reply_with_version_json(socket, request); } else if (strcmp(path, "/serial/") == 0) { - if (false && !request->authenticated) { + if (!request->authenticated) { if (_api_password[0] != '\0') { _reply_unauthorized(socket, request); } else { _reply_forbidden(socket, request); } } else if (request->websocket) { - ESP_LOGI(TAG, "websocket!"); _reply_websocket_upgrade(socket, request); } else { _REPLY_STATIC(socket, request, serial_html); @@ -1059,7 +1058,6 @@ static bool _reply(socketpool_socket_obj_t *socket, _request *request) { } static void _reset_request(_request *request) { - ESP_LOGI(TAG, "reset request"); request->state = STATE_METHOD; request->origin[0] = '\0'; request->content_length = 0; diff --git a/supervisor/shared/web_workflow/websocket.c b/supervisor/shared/web_workflow/websocket.c index 7a2f37b168..8d3941b434 100644 --- a/supervisor/shared/web_workflow/websocket.c +++ b/supervisor/shared/web_workflow/websocket.c @@ -48,7 +48,6 @@ void websocket_init(void) { } void websocket_handoff(socketpool_socket_obj_t *socket) { - ESP_LOGI(TAG, "socket handed off"); cp_serial.socket = *socket; cp_serial.closed = false; cp_serial.opcode = 0; @@ -57,7 +56,6 @@ void websocket_handoff(socketpool_socket_obj_t *socket) { // Mark the original socket object as closed without telling the lower level. socket->connected = false; socket->num = -1; - ESP_LOGI(TAG, "socket hand off done"); } bool websocket_connected(void) { @@ -90,7 +88,6 @@ static void _read_next_frame_header(void) { if (cp_serial.frame_index == 0 && _read_byte(&h)) { cp_serial.frame_index++; cp_serial.opcode = h & 0xf; - ESP_LOGI(TAG, "fin %d opcode %x", h >> 7, cp_serial.opcode); } if (cp_serial.frame_index == 1 && _read_byte(&h)) { cp_serial.frame_index++; @@ -108,8 +105,6 @@ static void _read_next_frame_header(void) { if (cp_serial.masked) { cp_serial.frame_len += 4; } - - ESP_LOGI(TAG, "mask %d length %x", cp_serial.masked, len); } while (cp_serial.frame_index >= 2 && cp_serial.frame_index < (cp_serial.payload_len_size + 2) && @@ -133,7 +128,6 @@ static void _read_next_frame_header(void) { if (cp_serial.frame_index == cp_serial.frame_len) { uint8_t opcode = 0x8; // CLOSE if (cp_serial.opcode == 0x9) { - ESP_LOGI(TAG, "websocket ping"); opcode = 0xA; // PONG } else { // Set the TCP socket to send immediately so that we send the payload back before @@ -160,7 +154,6 @@ static void _read_next_frame_header(void) { if (cp_serial.payload_remaining == 0) { cp_serial.frame_index = 0; if (cp_serial.opcode == 0x8) { - ESP_LOGI(TAG, "websocket closed"); cp_serial.closed = true; common_hal_socketpool_socket_close(&cp_serial.socket); @@ -199,7 +192,6 @@ bool websocket_available(void) { char websocket_read_char(void) { uint8_t c; _read_next_payload_byte(&c); - ESP_LOGI(TAG, "read %c", c); return c; } @@ -232,7 +224,6 @@ static void _websocket_send(_websocket *ws, const char *text, size_t len) { char copy[len]; memcpy(copy, text, len); copy[len] = '\0'; - ESP_LOGI(TAG, "sent over websocket: %s", copy); } void websocket_write(const char *text, size_t len) {