A bit closer to websockets.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4696 ed5197a5-7fde-0310-b194-c3ffbd925b24
This commit is contained in:
parent
9f2f9bd8b0
commit
ccebf831e7
226
core/httpd.js
226
core/httpd.js
@ -203,131 +203,135 @@ function handleWebSocketRequest(request, response, client) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
response.send = function(message, opCode) {
|
if (client) {
|
||||||
if (opCode === undefined) {
|
response.send = function(message, opCode) {
|
||||||
opCode = 0x2;
|
if (opCode === undefined) {
|
||||||
}
|
opCode = 0x2;
|
||||||
if (opCode == 0x1 && (typeof message == "string" || message instanceof String)) {
|
}
|
||||||
message = utf8Encode(message);
|
if (opCode == 0x1 && (typeof message == "string" || message instanceof String)) {
|
||||||
}
|
message = utf8Encode(message);
|
||||||
let fin = true;
|
}
|
||||||
let packet = [(fin ? (1 << 7) : 0) | (opCode & 0xf)];
|
let fin = true;
|
||||||
let mask = false;
|
let packet = [(fin ? (1 << 7) : 0) | (opCode & 0xf)];
|
||||||
if (message.length < 126) {
|
let mask = false;
|
||||||
packet.push((mask ? (1 << 7) : 0) | message.length);
|
if (message.length < 126) {
|
||||||
} else if (message.length < (1 << 16)) {
|
packet.push((mask ? (1 << 7) : 0) | message.length);
|
||||||
packet.push((mask ? (1 << 7) : 0) | 126);
|
} else if (message.length < (1 << 16)) {
|
||||||
packet.push((message.length >> 8) & 0xff);
|
packet.push((mask ? (1 << 7) : 0) | 126);
|
||||||
packet.push(message.length & 0xff);
|
packet.push((message.length >> 8) & 0xff);
|
||||||
} else {
|
packet.push(message.length & 0xff);
|
||||||
let high = 0; //(message.length / (1 ** 32)) & 0xffffffff;
|
} else {
|
||||||
let low = message.length & 0xffffffff;
|
let high = 0; //(message.length / (1 ** 32)) & 0xffffffff;
|
||||||
packet.push((mask ? (1 << 7) : 0) | 127);
|
let low = message.length & 0xffffffff;
|
||||||
packet.push((high >> 24) & 0xff);
|
packet.push((mask ? (1 << 7) : 0) | 127);
|
||||||
packet.push((high >> 16) & 0xff);
|
packet.push((high >> 24) & 0xff);
|
||||||
packet.push((high >> 8) & 0xff);
|
packet.push((high >> 16) & 0xff);
|
||||||
packet.push((high >> 0) & 0xff);
|
packet.push((high >> 8) & 0xff);
|
||||||
packet.push((low >> 24) & 0xff);
|
packet.push((high >> 0) & 0xff);
|
||||||
packet.push((low >> 16) & 0xff);
|
packet.push((low >> 24) & 0xff);
|
||||||
packet.push((low >> 8) & 0xff);
|
packet.push((low >> 16) & 0xff);
|
||||||
packet.push(low & 0xff);
|
packet.push((low >> 8) & 0xff);
|
||||||
}
|
packet.push(low & 0xff);
|
||||||
|
}
|
||||||
|
|
||||||
let array = new Uint8Array(packet.length + message.length);
|
let array = new Uint8Array(packet.length + message.length);
|
||||||
array.set(packet, 0);
|
array.set(packet, 0);
|
||||||
array.set(message, packet.length);
|
array.set(message, packet.length);
|
||||||
try {
|
try {
|
||||||
return client.write(array);
|
return client.write(array);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
client.close();
|
client.close();
|
||||||
throw error;
|
throw error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
response.onMessage = null;
|
response.onMessage = null;
|
||||||
|
|
||||||
let extra_headers = handler.invoke(request, response);
|
let extra_headers = handler.invoke(request, response);
|
||||||
|
|
||||||
client.read(function(data) {
|
if (client) {
|
||||||
if (data) {
|
client.read(function(data) {
|
||||||
let newBuffer = new Uint8Array(buffer.length + data.length);
|
if (data) {
|
||||||
newBuffer.set(buffer, 0);
|
let newBuffer = new Uint8Array(buffer.length + data.length);
|
||||||
newBuffer.set(data, buffer.length);
|
newBuffer.set(buffer, 0);
|
||||||
buffer = newBuffer;
|
newBuffer.set(data, buffer.length);
|
||||||
|
buffer = newBuffer;
|
||||||
|
|
||||||
while (buffer.length >= 2) {
|
while (buffer.length >= 2) {
|
||||||
let bits0 = buffer[0];
|
let bits0 = buffer[0];
|
||||||
let bits1 = buffer[1];
|
let bits1 = buffer[1];
|
||||||
if (bits1 & (1 << 7) == 0) {
|
if (bits1 & (1 << 7) == 0) {
|
||||||
// Unmasked message.
|
// Unmasked message.
|
||||||
client.close();
|
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];
|
|
||||||
}
|
}
|
||||||
maskStart = 4;
|
let opCode = bits0 & 0xf;
|
||||||
} else if (payloadLength == 127) {
|
let fin = bits0 & (1 << 7);
|
||||||
payloadLength = 0;
|
let payloadLength = bits1 & 0x7f;
|
||||||
for (let i = 0; i < 8; i++) {
|
let maskStart = 2;
|
||||||
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) {
|
if (payloadLength == 126) {
|
||||||
let newBuffer = new Uint8Array(frame.length + decoded.length);
|
payloadLength = 0;
|
||||||
newBuffer.set(frame, 0);
|
for (let i = 0; i < 2; i++) {
|
||||||
newBuffer.set(decoded, frame.length);
|
payloadLength <<= 8;
|
||||||
frame = newBuffer;
|
payloadLength |= buffer[2 + i];
|
||||||
} else {
|
|
||||||
frame = decoded;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opCode) {
|
|
||||||
frameOpCode = opCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fin) {
|
|
||||||
if (response.onMessage) {
|
|
||||||
response.onMessage({
|
|
||||||
data: frameOpCode == 0x1 ? utf8Decode(frame) : frame,
|
|
||||||
opCode: frameOpCode,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
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.onError(function(error) {
|
||||||
client.close();
|
logError(client.peerName + " - - [" + new Date() + "] " + error);
|
||||||
}
|
response.onError(error);
|
||||||
});
|
});
|
||||||
client.onError(function(error) {
|
}
|
||||||
logError(client.peerName + " - - [" + new Date() + "] " + error);
|
|
||||||
response.onError(error);
|
|
||||||
});
|
|
||||||
|
|
||||||
let headers = {
|
let headers = {
|
||||||
"Upgrade": "websocket",
|
"Upgrade": "websocket",
|
||||||
|
17
src/http.c
17
src/http.c
@ -521,6 +521,23 @@ static void _http_on_shutdown(uv_shutdown_t* request, int status)
|
|||||||
request->data = NULL;
|
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)
|
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);
|
const char* status_text = _http_status_text(status);
|
||||||
|
@ -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_ref(tf_http_request_t* request);
|
||||||
void tf_http_request_release(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);
|
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);
|
||||||
|
@ -100,14 +100,62 @@ static JSValue _httpd_response_end(JSContext* context, JSValueConst this_val, in
|
|||||||
return JS_UNDEFINED;
|
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)
|
static void _httpd_callback(tf_http_request_t* request)
|
||||||
{
|
{
|
||||||
|
#if 0
|
||||||
if (request->flags & k_tf_http_handler_flag_websocket)
|
if (request->flags & k_tf_http_handler_flag_websocket)
|
||||||
{
|
{
|
||||||
const char* header_connection = tf_http_request_get_header(request, "connection");
|
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_upgrade = tf_http_request_get_header(request, "upgrade");
|
||||||
const char* header_sec_websocket_key = tf_http_request_get_header(request, "sec-websocket-key");
|
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 &&
|
if (header_connection &&
|
||||||
header_upgrade &&
|
header_upgrade &&
|
||||||
header_sec_websocket_key &&
|
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") ||
|
!tf_http_request_get_header(request, "sec-websocket-version") ||
|
||||||
strcmp(tf_http_request_get_header(request, "sec-websocket-version"), "13") != 0;
|
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);
|
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;
|
http_handler_data_t* data = request->user_data;
|
||||||
JSContext* context = data->context;
|
JSContext* context = data->context;
|
||||||
@ -169,6 +217,7 @@ static void _httpd_callback(tf_http_request_t* request)
|
|||||||
JS_SetOpaque(response_object, 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, "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, "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[] =
|
JSValue args[] =
|
||||||
{
|
{
|
||||||
request_object,
|
request_object,
|
||||||
|
Loading…
Reference in New Issue
Block a user