From ccebf831e7937d2049d6ee2db47a8881d465dd12 Mon Sep 17 00:00:00 2001 From: Cory McWilliams Date: Mon, 25 Dec 2023 22:53:05 +0000 Subject: [PATCH] A bit closer to websockets. git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4696 ed5197a5-7fde-0310-b194-c3ffbd925b24 --- core/httpd.js | 226 +++++++++++++++++++++++++------------------------ src/http.c | 17 ++++ src/http.h | 1 + src/httpd.js.c | 53 +++++++++++- 4 files changed, 184 insertions(+), 113 deletions(-) diff --git a/core/httpd.js b/core/httpd.js index 34780a62..6689a922 100644 --- a/core/httpd.js +++ b/core/httpd.js @@ -203,131 +203,135 @@ function handleWebSocketRequest(request, response, client) { return; } - response.send = function(message, opCode) { - if (opCode === undefined) { - opCode = 0x2; - } - if (opCode == 0x1 && (typeof message == "string" || message instanceof String)) { - message = utf8Encode(message); - } - let fin = true; - let packet = [(fin ? (1 << 7) : 0) | (opCode & 0xf)]; - let mask = false; - if (message.length < 126) { - packet.push((mask ? (1 << 7) : 0) | message.length); - } else if (message.length < (1 << 16)) { - packet.push((mask ? (1 << 7) : 0) | 126); - packet.push((message.length >> 8) & 0xff); - packet.push(message.length & 0xff); - } else { - let high = 0; //(message.length / (1 ** 32)) & 0xffffffff; - let low = message.length & 0xffffffff; - packet.push((mask ? (1 << 7) : 0) | 127); - packet.push((high >> 24) & 0xff); - packet.push((high >> 16) & 0xff); - packet.push((high >> 8) & 0xff); - packet.push((high >> 0) & 0xff); - packet.push((low >> 24) & 0xff); - packet.push((low >> 16) & 0xff); - packet.push((low >> 8) & 0xff); - packet.push(low & 0xff); - } + if (client) { + response.send = function(message, opCode) { + if (opCode === undefined) { + opCode = 0x2; + } + if (opCode == 0x1 && (typeof message == "string" || message instanceof String)) { + message = utf8Encode(message); + } + let fin = true; + let packet = [(fin ? (1 << 7) : 0) | (opCode & 0xf)]; + let mask = false; + if (message.length < 126) { + packet.push((mask ? (1 << 7) : 0) | message.length); + } else if (message.length < (1 << 16)) { + packet.push((mask ? (1 << 7) : 0) | 126); + packet.push((message.length >> 8) & 0xff); + packet.push(message.length & 0xff); + } else { + let high = 0; //(message.length / (1 ** 32)) & 0xffffffff; + let low = message.length & 0xffffffff; + packet.push((mask ? (1 << 7) : 0) | 127); + packet.push((high >> 24) & 0xff); + packet.push((high >> 16) & 0xff); + packet.push((high >> 8) & 0xff); + packet.push((high >> 0) & 0xff); + packet.push((low >> 24) & 0xff); + packet.push((low >> 16) & 0xff); + packet.push((low >> 8) & 0xff); + packet.push(low & 0xff); + } - let array = new Uint8Array(packet.length + message.length); - array.set(packet, 0); - array.set(message, packet.length); - try { - return client.write(array); - } catch (error) { - client.close(); - throw error; + let array = new Uint8Array(packet.length + message.length); + array.set(packet, 0); + array.set(message, packet.length); + try { + return client.write(array); + } catch (error) { + client.close(); + throw error; + } } } response.onMessage = null; let extra_headers = handler.invoke(request, response); - client.read(function(data) { - if (data) { - let newBuffer = new Uint8Array(buffer.length + data.length); - newBuffer.set(buffer, 0); - newBuffer.set(data, buffer.length); - buffer = newBuffer; + if (client) { + client.read(function(data) { + if (data) { + let newBuffer = new Uint8Array(buffer.length + data.length); + newBuffer.set(buffer, 0); + newBuffer.set(data, buffer.length); + buffer = newBuffer; - while (buffer.length >= 2) { - let bits0 = buffer[0]; - let bits1 = buffer[1]; - if (bits1 & (1 << 7) == 0) { - // Unmasked message. - client.close(); - } - let opCode = bits0 & 0xf; - let fin = bits0 & (1 << 7); - let payloadLength = bits1 & 0x7f; - let maskStart = 2; - - if (payloadLength == 126) { - payloadLength = 0; - for (let i = 0; i < 2; i++) { - payloadLength <<= 8; - payloadLength |= buffer[2 + i]; + while (buffer.length >= 2) { + let bits0 = buffer[0]; + let bits1 = buffer[1]; + if (bits1 & (1 << 7) == 0) { + // Unmasked message. + client.close(); } - maskStart = 4; - } else if (payloadLength == 127) { - payloadLength = 0; - for (let i = 0; i < 8; i++) { - payloadLength <<= 8; - payloadLength |= buffer[2 + i]; - } - maskStart = 10; - } - let havePayload = buffer.length >= payloadLength + 2 + 4; - if (havePayload) { - let mask = - buffer[maskStart + 0] | - buffer[maskStart + 1] << 8 | - buffer[maskStart + 2] << 16 | - buffer[maskStart + 3] << 24; - let dataStart = maskStart + 4; - let payload = buffer.slice(dataStart, dataStart + payloadLength); - let decoded = maskBytes(payload, mask); - buffer = buffer.slice(dataStart + payloadLength); + let opCode = bits0 & 0xf; + let fin = bits0 & (1 << 7); + let payloadLength = bits1 & 0x7f; + let maskStart = 2; - if (frame) { - let newBuffer = new Uint8Array(frame.length + decoded.length); - newBuffer.set(frame, 0); - newBuffer.set(decoded, frame.length); - frame = newBuffer; - } else { - frame = decoded; - } - - if (opCode) { - frameOpCode = opCode; - } - - if (fin) { - if (response.onMessage) { - response.onMessage({ - data: frameOpCode == 0x1 ? utf8Decode(frame) : frame, - opCode: frameOpCode, - }); + if (payloadLength == 126) { + payloadLength = 0; + for (let i = 0; i < 2; i++) { + payloadLength <<= 8; + payloadLength |= buffer[2 + i]; } - frame = undefined; + maskStart = 4; + } else if (payloadLength == 127) { + payloadLength = 0; + for (let i = 0; i < 8; i++) { + payloadLength <<= 8; + payloadLength |= buffer[2 + i]; + } + maskStart = 10; + } + let havePayload = buffer.length >= payloadLength + 2 + 4; + if (havePayload) { + let mask = + buffer[maskStart + 0] | + buffer[maskStart + 1] << 8 | + buffer[maskStart + 2] << 16 | + buffer[maskStart + 3] << 24; + let dataStart = maskStart + 4; + let payload = buffer.slice(dataStart, dataStart + payloadLength); + let decoded = maskBytes(payload, mask); + buffer = buffer.slice(dataStart + payloadLength); + + if (frame) { + let newBuffer = new Uint8Array(frame.length + decoded.length); + newBuffer.set(frame, 0); + newBuffer.set(decoded, frame.length); + frame = newBuffer; + } else { + frame = decoded; + } + + if (opCode) { + frameOpCode = opCode; + } + + if (fin) { + if (response.onMessage) { + response.onMessage({ + data: frameOpCode == 0x1 ? utf8Decode(frame) : frame, + opCode: frameOpCode, + }); + } + frame = undefined; + } + } else { + break; } - } else { - break; } + } else { + response.onClose(); + client.close(); } - } else { - response.onClose(); - client.close(); - } - }); - client.onError(function(error) { - logError(client.peerName + " - - [" + new Date() + "] " + error); - response.onError(error); - }); + }); + client.onError(function(error) { + logError(client.peerName + " - - [" + new Date() + "] " + error); + response.onError(error); + }); + } let headers = { "Upgrade": "websocket", diff --git a/src/http.c b/src/http.c index 2b5efa03..c84009db 100644 --- a/src/http.c +++ b/src/http.c @@ -521,6 +521,23 @@ static void _http_on_shutdown(uv_shutdown_t* request, int status) request->data = NULL; } +static void _http_write(tf_http_connection_t* connection, const void* data, size_t size) +{ + uv_write_t* write = tf_malloc(sizeof(uv_write_t) + size); + *write = (uv_write_t) { .data = connection }; + memcpy(write + 1, data, size); + int r = uv_write(write, (uv_stream_t*)&connection->tcp, &(uv_buf_t) { .base = (void*)(write + 1), .len = size }, 1, _http_on_write); + if (r) + { + tf_printf("uv_write: %s\n", uv_strerror(r)); + } +} + +void tf_http_request_send(tf_http_request_t* request, const void* data, size_t size) +{ + _http_write(request->connection, data, size); +} + void tf_http_respond(tf_http_request_t* request, int status, const char** headers, int headers_count, const void* body, size_t content_length) { const char* status_text = _http_status_text(status); diff --git a/src/http.h b/src/http.h index bf2e446e..71b1fe4b 100644 --- a/src/http.h +++ b/src/http.h @@ -46,3 +46,4 @@ void tf_http_destroy(tf_http_t* http); void tf_http_request_ref(tf_http_request_t* request); void tf_http_request_release(tf_http_request_t* request); const char* tf_http_request_get_header(tf_http_request_t* request, const char* name); +void tf_http_request_send(tf_http_request_t* request, const void* data, size_t size); diff --git a/src/httpd.js.c b/src/httpd.js.c index 8d32a1ce..3ebbe27d 100644 --- a/src/httpd.js.c +++ b/src/httpd.js.c @@ -100,14 +100,62 @@ static JSValue _httpd_response_end(JSContext* context, JSValueConst this_val, in return JS_UNDEFINED; } +static JSValue _httpd_response_send(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) +{ + tf_http_request_t* request = JS_GetOpaque(this_val, _httpd_request_class_id); + int opcode = 0x1; + JS_ToInt32(context, &opcode, argv[1]); + uint64_t length = 0; + size_t length_size = 0; + const char* message = JS_ToCStringLen(context, &length_size, argv[0]); + length = length_size; + uint8_t* copy = tf_malloc(length + 16); + bool fin = true; + size_t header = 1; + copy[0] = (fin ? (1 << 7) : 0) | (opcode & 0xf); + if (length < 126) + { + copy[1] = length; + header += 2; + } + else if (length < (1 << 16)) + { + copy[1] = 126; + copy[2] = (length >> 8) & 0xff; + copy[3] = (length >> 0) & 0xff; + header += 4; + } + else + { + uint32_t high = (length >> 32) & 0xffffffff; + uint32_t low = (length >> 0) & 0xffffffff; + copy[1] = 127; + copy[2] = (high >> 24) & 0xff; + copy[3] = (high >> 16) & 0xff; + copy[4] = (high >> 8) & 0xff; + copy[5] = (high >> 0) & 0xff; + copy[6] = (low >> 24) & 0xff; + copy[7] = (low >> 16) & 0xff; + copy[8] = (low >> 8) & 0xff; + copy[9] = (low >> 0) & 0xff; + header += 10; + } + memcpy(copy + header, message, length); + tf_printf("SEND [%.*s]\n", (int)length, message); + tf_http_request_send(request, copy, header + length); + tf_free(copy); + JS_FreeCString(context, message); + return JS_UNDEFINED; +} + static void _httpd_callback(tf_http_request_t* request) { +#if 0 if (request->flags & k_tf_http_handler_flag_websocket) { const char* header_connection = tf_http_request_get_header(request, "connection"); const char* header_upgrade = tf_http_request_get_header(request, "upgrade"); const char* header_sec_websocket_key = tf_http_request_get_header(request, "sec-websocket-key"); - tf_printf("\n%s\n%s\n%s\n\n", header_connection, header_upgrade, header_sec_websocket_key); if (header_connection && header_upgrade && header_sec_websocket_key && @@ -136,9 +184,9 @@ static void _httpd_callback(tf_http_request_t* request) !tf_http_request_get_header(request, "sec-websocket-version") || strcmp(tf_http_request_get_header(request, "sec-websocket-version"), "13") != 0; tf_http_respond(request, 101, headers, send_version ? k_headers_count : (k_headers_count - 1), NULL, 0); - return; } } +#endif http_handler_data_t* data = request->user_data; JSContext* context = data->context; @@ -169,6 +217,7 @@ static void _httpd_callback(tf_http_request_t* request) JS_SetOpaque(response_object, request); JS_SetPropertyStr(context, response_object, "writeHead", JS_NewCFunction(context, _httpd_response_write_head, "writeHead", 2)); JS_SetPropertyStr(context, response_object, "end", JS_NewCFunction(context, _httpd_response_end, "end", 1)); + JS_SetPropertyStr(context, response_object, "send", JS_NewCFunction(context, _httpd_response_send, "send", 2)); JSValue args[] = { request_object,