forked from cory/tildefriends
Delete httpd.js.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4709 ed5197a5-7fde-0310-b194-c3ffbd925b24
This commit is contained in:
parent
4f3f4295ea
commit
0dcc879eb1
22
core/core.js
22
core/core.js
@ -2,7 +2,6 @@ import * as app from './app.js';
|
|||||||
import * as auth from './auth.js';
|
import * as auth from './auth.js';
|
||||||
import * as form from './form.js';
|
import * as form from './form.js';
|
||||||
import * as http from './http.js';
|
import * as http from './http.js';
|
||||||
import * as httpd from './httpd.js';
|
|
||||||
|
|
||||||
let gProcessIndex = 0;
|
let gProcessIndex = 0;
|
||||||
let gProcesses = {};
|
let gProcesses = {};
|
||||||
@ -952,10 +951,9 @@ function stringResponse(response, data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
loadSettings().then(function() {
|
loadSettings().then(function() {
|
||||||
let httpd_impl = (tildefriends.args.httpdc ? httpdc : httpd);
|
httpd.all("/login", auth.handler);
|
||||||
httpd_impl.all("/login", auth.handler);
|
httpd.registerSocketHandler("/app/socket", app.socket);
|
||||||
httpd_impl.registerSocketHandler("/app/socket", app.socket);
|
httpd.all("", function(request, response) {
|
||||||
httpd_impl.all("", function(request, response) {
|
|
||||||
let match;
|
let match;
|
||||||
if (request.uri === "/" || request.uri === "") {
|
if (request.uri === "/" || request.uri === "") {
|
||||||
try {
|
try {
|
||||||
@ -1005,9 +1003,17 @@ loadSettings().then(function() {
|
|||||||
return response.end(data);
|
return response.end(data);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
httpd_impl.start(tildefriends.http_port);
|
let port = httpd.start(tildefriends.http_port);
|
||||||
|
if (tildefriends.args.out_http_port_file) {
|
||||||
|
print("Writing the port file.");
|
||||||
|
File.writeFile(tildefriends.args.out_http_port_file, port.toString() + '\n').then(function(r) {
|
||||||
|
print("Wrote the port file:", tildefriends.args.out_http_port_file, r);
|
||||||
|
}).catch(function() {
|
||||||
|
print("Failed to write the port file.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (tildefriends.args.httpdc && tildefriends.https_port) {
|
if (tildefriends.https_port) {
|
||||||
async function start_tls() {
|
async function start_tls() {
|
||||||
const kCertificatePath = "data/httpd/certificate.pem";
|
const kCertificatePath = "data/httpd/certificate.pem";
|
||||||
const kPrivateKeyPath = "data/httpd/privatekey.pem";
|
const kPrivateKeyPath = "data/httpd/privatekey.pem";
|
||||||
@ -1016,7 +1022,7 @@ loadSettings().then(function() {
|
|||||||
let context = new TlsContext();
|
let context = new TlsContext();
|
||||||
context.setPrivateKey(privateKey);
|
context.setPrivateKey(privateKey);
|
||||||
context.setCertificate(certificate);
|
context.setCertificate(certificate);
|
||||||
httpd_impl.start(tildefriends.https_port, context);
|
httpd.start(tildefriends.https_port, context);
|
||||||
}
|
}
|
||||||
start_tls();
|
start_tls();
|
||||||
}
|
}
|
||||||
|
598
core/httpd.js
598
core/httpd.js
@ -1,598 +0,0 @@
|
|||||||
import * as core from './core.js';
|
|
||||||
|
|
||||||
let gHandlers = [];
|
|
||||||
let gSocketHandlers = [];
|
|
||||||
let gBadRequests = {};
|
|
||||||
|
|
||||||
const kRequestTimeout = 5000;
|
|
||||||
const kStallTimeout = 60000;
|
|
||||||
|
|
||||||
function logError(error) {
|
|
||||||
print("ERROR " + error);
|
|
||||||
if (error.stackTrace) {
|
|
||||||
print(error.stackTrace);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function addHandler(handler) {
|
|
||||||
let added = false;
|
|
||||||
for (let i in gHandlers) {
|
|
||||||
if (gHandlers[i].path == handler.path) {
|
|
||||||
gHandlers[i] = handler;
|
|
||||||
added = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!added) {
|
|
||||||
gHandlers.push(handler);
|
|
||||||
added = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function all(prefix, handler) {
|
|
||||||
addHandler({
|
|
||||||
owner: this,
|
|
||||||
path: prefix,
|
|
||||||
invoke: handler,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function registerSocketHandler(prefix, handler) {
|
|
||||||
gSocketHandlers.push({
|
|
||||||
owner: this,
|
|
||||||
path: prefix,
|
|
||||||
invoke: handler,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function Request(method, uri, version, headers, body, client) {
|
|
||||||
this.method = method;
|
|
||||||
let index = uri.indexOf("?");
|
|
||||||
if (index != -1) {
|
|
||||||
this.uri = uri.slice(0, index);
|
|
||||||
this.query = uri.slice(index + 1);
|
|
||||||
} else {
|
|
||||||
this.uri = uri;
|
|
||||||
this.query = undefined;
|
|
||||||
}
|
|
||||||
this.version = version || '';
|
|
||||||
this.headers = headers;
|
|
||||||
this.client = {peerName: client.peerName, tls: client.tls};
|
|
||||||
this.body = body;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
function findHandler(request) {
|
|
||||||
let matchedHandler = null;
|
|
||||||
for (let name in gHandlers) {
|
|
||||||
let handler = gHandlers[name];
|
|
||||||
if (request.uri == handler.path || request.uri.slice(0, handler.path.length + 1) == handler.path + '/') {
|
|
||||||
matchedHandler = handler;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return matchedHandler;
|
|
||||||
}
|
|
||||||
|
|
||||||
function findSocketHandler(request) {
|
|
||||||
let matchedHandler = null;
|
|
||||||
for (let name in gSocketHandlers) {
|
|
||||||
let handler = gSocketHandlers[name];
|
|
||||||
if (request.uri == handler.path || request.uri.slice(0, handler.path.length + 1) == handler.path + '/') {
|
|
||||||
matchedHandler = handler;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return matchedHandler;
|
|
||||||
}
|
|
||||||
|
|
||||||
function Response(request, client) {
|
|
||||||
let kStatusText = {
|
|
||||||
101: "Switching Protocols",
|
|
||||||
200: 'OK',
|
|
||||||
303: 'See other',
|
|
||||||
304: 'Not Modified',
|
|
||||||
400: 'Bad Request',
|
|
||||||
401: 'Unauthorized',
|
|
||||||
403: 'Forbidden',
|
|
||||||
404: 'File not found',
|
|
||||||
500: 'Internal server error',
|
|
||||||
};
|
|
||||||
let _started = false;
|
|
||||||
let _finished = false;
|
|
||||||
let _keepAlive = false;
|
|
||||||
let _chunked = false;
|
|
||||||
return {
|
|
||||||
writeHead: function(status) {
|
|
||||||
if (_started) {
|
|
||||||
throw new Error("Response.writeHead called multiple times.");
|
|
||||||
}
|
|
||||||
let reason;
|
|
||||||
let headers;
|
|
||||||
if (arguments.length == 3) {
|
|
||||||
reason = arguments[1];
|
|
||||||
headers = arguments[2];
|
|
||||||
} else {
|
|
||||||
reason = kStatusText[status];
|
|
||||||
headers = arguments[1];
|
|
||||||
}
|
|
||||||
let lowerHeaders = {};
|
|
||||||
let requestVersion = request.version.split("/")[1].split(".");
|
|
||||||
let responseVersion = (requestVersion[0] >= 1 && requestVersion[0] >= 1) ? "1.1" : "1.0";
|
|
||||||
let headerString = "HTTP/" + responseVersion + " " + status + " " + reason + "\r\n";
|
|
||||||
headers['Server'] = `Tilde Friends/${version().number}`;
|
|
||||||
for (let i in headers) {
|
|
||||||
headerString += i + ": " + headers[i] + "\r\n";
|
|
||||||
lowerHeaders[i.toLowerCase()] = headers[i];
|
|
||||||
}
|
|
||||||
if ("connection" in lowerHeaders) {
|
|
||||||
_keepAlive = lowerHeaders["connection"].toLowerCase() == "keep-alive";
|
|
||||||
} else {
|
|
||||||
_keepAlive = ((request.version == "HTTP/1.0" && ("connection" in lowerHeaders && lowerHeaders["connection"].toLowerCase() == "keep-alive")) ||
|
|
||||||
(request.version == "HTTP/1.1" && (!("connection" in lowerHeaders) || lowerHeaders["connection"].toLowerCase() != "close")));
|
|
||||||
headerString += "Connection: " + (_keepAlive ? "keep-alive" : "close") + "\r\n";
|
|
||||||
}
|
|
||||||
_chunked = _keepAlive && !("content-length" in lowerHeaders);
|
|
||||||
if (_chunked) {
|
|
||||||
headerString += "Transfer-Encoding: chunked\r\n";
|
|
||||||
}
|
|
||||||
headerString += "\r\n";
|
|
||||||
_started = true;
|
|
||||||
client.write(headerString).catch(function() {});
|
|
||||||
},
|
|
||||||
end: function(data) {
|
|
||||||
if (_finished) {
|
|
||||||
throw new Error("Response.end called multiple times.");
|
|
||||||
}
|
|
||||||
if (data) {
|
|
||||||
if (_chunked) {
|
|
||||||
client.write(data.length.toString(16) + "\r\n" + data + "\r\n" + "0\r\n\r\n").catch(function() {});
|
|
||||||
} else {
|
|
||||||
client.write(data).catch(function() {});
|
|
||||||
}
|
|
||||||
} else if (_chunked) {
|
|
||||||
client.write("0\r\n\r\n").catch(function() {});
|
|
||||||
}
|
|
||||||
_finished = true;
|
|
||||||
if (!_keepAlive) {
|
|
||||||
client.shutdown().catch(function() {});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
reportError: function(error) {
|
|
||||||
if (!_started) {
|
|
||||||
client.write("HTTP/1.1 500 Internal Server Error\r\nContent-Type: text/plain; charset=utf-8\r\n\r\n").catch(function() {});
|
|
||||||
}
|
|
||||||
if (!_finished) {
|
|
||||||
client.write("500 Internal Server Error\r\n\r\n" + error?.stackTrace).catch(function() {});
|
|
||||||
}
|
|
||||||
logError(client.peerName + " - - [" + new Date() + "] " + error);
|
|
||||||
},
|
|
||||||
isConnected: function() { return client.isConnected; },
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleRequest(request, response) {
|
|
||||||
let handler = findHandler(request);
|
|
||||||
|
|
||||||
print(request.client.peerName + " - - [" + new Date() + "] " + request.method + " " + request.uri + " " + request.version + " \"" + request.headers["user-agent"] + "\"");
|
|
||||||
|
|
||||||
if (handler) {
|
|
||||||
try {
|
|
||||||
Promise.resolve(handler.invoke(request, response)).catch(function(error) {
|
|
||||||
response.reportError(error);
|
|
||||||
request.client.close();
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
response.reportError(error);
|
|
||||||
request.client.close();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
response.writeHead(200, {"Content-Type": "text/plain; charset=utf-8"});
|
|
||||||
response.end("No handler found for request: " + request.uri);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleWebSocketRequest(request, response, client) {
|
|
||||||
let buffer = new Uint8Array(0);
|
|
||||||
let frame;
|
|
||||||
let frameOpCode = 0x0;
|
|
||||||
|
|
||||||
let handler = findSocketHandler(request);
|
|
||||||
if (!handler) {
|
|
||||||
client.close();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
response.onMessage = null;
|
|
||||||
|
|
||||||
let extra_headers = handler.invoke(request, response);
|
|
||||||
|
|
||||||
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];
|
|
||||||
}
|
|
||||||
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 {
|
|
||||||
response.onClose();
|
|
||||||
client.close();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
client.onError(function(error) {
|
|
||||||
logError(client.peerName + " - - [" + new Date() + "] " + error);
|
|
||||||
response.onError(error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let headers = {
|
|
||||||
"Upgrade": "websocket",
|
|
||||||
"Connection": "Upgrade",
|
|
||||||
"Sec-WebSocket-Accept": webSocketAcceptResponse(request.headers["sec-websocket-key"]),
|
|
||||||
};
|
|
||||||
if (request.headers["sec-websocket-version"] != "13") {
|
|
||||||
headers["Sec-WebSocket-Version"] = "13";
|
|
||||||
}
|
|
||||||
response.writeHead(101, Object.assign({}, headers, extra_headers));
|
|
||||||
}
|
|
||||||
|
|
||||||
function webSocketAcceptResponse(key) {
|
|
||||||
let kMagic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
|
||||||
return base64Encode(sha1Digest(key + kMagic));
|
|
||||||
}
|
|
||||||
|
|
||||||
function badRequest(client, reason) {
|
|
||||||
let now = new Date();
|
|
||||||
let count = 0;
|
|
||||||
let old = gBadRequests[client.peerName];
|
|
||||||
if (!old) {
|
|
||||||
gBadRequests[client.peerName] = {
|
|
||||||
expire: new Date(now.getTime() + 1 * 60 * 1000),
|
|
||||||
count: 1,
|
|
||||||
reason: reason,
|
|
||||||
};
|
|
||||||
count = 1;
|
|
||||||
} else {
|
|
||||||
old.count++;
|
|
||||||
old.reason = reason;
|
|
||||||
count = old.count;
|
|
||||||
}
|
|
||||||
new Response({version: '1.0'}, client).reportError(reason + ': ' + count);
|
|
||||||
client.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
function allowRequest(client) {
|
|
||||||
let old = gBadRequests[client.peerName];
|
|
||||||
if (old) {
|
|
||||||
let now = new Date();
|
|
||||||
if (old.expire < now) {
|
|
||||||
delete gBadRequests[client.peerName];
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return old.count < 3;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleConnection(client) {
|
|
||||||
if (!allowRequest(client)) {
|
|
||||||
print('Rejecting client for too many bad requests: ', client.peerName, gBadRequests[client.peerName].reason);
|
|
||||||
client.info = 'rejected';
|
|
||||||
client.close();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
client.info = 'accepted';
|
|
||||||
let inputBuffer = new Uint8Array(0);
|
|
||||||
let request;
|
|
||||||
let headers = {};
|
|
||||||
let parsing_header = true;
|
|
||||||
let bodyToRead = -1;
|
|
||||||
let body;
|
|
||||||
let readCount = 0;
|
|
||||||
let isWebsocket = false;
|
|
||||||
|
|
||||||
client.setActivityTimeout(kRequestTimeout);
|
|
||||||
|
|
||||||
function reset() {
|
|
||||||
request = undefined;
|
|
||||||
headers = {};
|
|
||||||
parsing_header = true;
|
|
||||||
bodyToRead = -1;
|
|
||||||
body = undefined;
|
|
||||||
client.info = 'reset';
|
|
||||||
client.setActivityTimeout(kRequestTimeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
function finish() {
|
|
||||||
client.info = 'finishing';
|
|
||||||
let requestObject = new Request(request[0], request[1], request[2], headers, body, client);
|
|
||||||
let response = new Response(requestObject, client);
|
|
||||||
try {
|
|
||||||
handleRequest(requestObject, response)
|
|
||||||
if (client.isConnected) {
|
|
||||||
reset();
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
response.reportError(error);
|
|
||||||
client.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
client.onError(function(error) {
|
|
||||||
logError(client.peerName + " - - [" + new Date() + "] " + error);
|
|
||||||
});
|
|
||||||
|
|
||||||
client.read(function(data) {
|
|
||||||
readCount++;
|
|
||||||
if (data) {
|
|
||||||
let newBuffer = new Uint8Array(inputBuffer.length + data.length);
|
|
||||||
newBuffer.set(inputBuffer, 0);
|
|
||||||
newBuffer.set(data, inputBuffer.length);
|
|
||||||
inputBuffer = newBuffer;
|
|
||||||
|
|
||||||
if (parsing_header)
|
|
||||||
{
|
|
||||||
let result = parseHttpRequest(inputBuffer, inputBuffer.length - data.length);
|
|
||||||
if (result) {
|
|
||||||
if (typeof result === 'number') {
|
|
||||||
if (result == -2) {
|
|
||||||
/* More. */
|
|
||||||
} else {
|
|
||||||
badRequest(client, `Bad request(parse=${result}, length=${inputBuffer.length - data.length}).`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else if (typeof result === 'object') {
|
|
||||||
client.setActivityTimeout(kStallTimeout);
|
|
||||||
request = [
|
|
||||||
result.method,
|
|
||||||
result.path,
|
|
||||||
`HTTP/1.${result.minor_version}`,
|
|
||||||
];
|
|
||||||
|
|
||||||
headers = Object.fromEntries(Object.entries(result.headers).map(x => [x[0].toLowerCase(), x[1]]));
|
|
||||||
parsing_header = false;
|
|
||||||
inputBuffer = inputBuffer.slice(result.bytes_parsed);
|
|
||||||
|
|
||||||
if (!client.tls && tildefriends.https_port && core.globalSettings.http_redirect && !result.path.startsWith('/.well-known/')) {
|
|
||||||
let requestObject = new Request(request[0], request[1], request[2], headers, body, client);
|
|
||||||
let response = new Response(requestObject, client);
|
|
||||||
response.writeHead(303, {"Location": `${core.globalSettings.http_redirect}${result.path}`, "Content-Length": "0"});
|
|
||||||
response.end();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (headers["content-length"] != undefined) {
|
|
||||||
bodyToRead = parseInt(headers["content-length"]);
|
|
||||||
if (bodyToRead > 16 * 1024 * 1024) {
|
|
||||||
badRequest(client, 'Request too large: ' + bodyToRead + '.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
body = new Uint8Array(bodyToRead);
|
|
||||||
client.info = 'waiting for body';
|
|
||||||
} else if (headers["connection"]
|
|
||||||
&& headers["connection"].toLowerCase().split(",").map(x => x.trim()).indexOf("upgrade") != -1
|
|
||||||
&& headers["upgrade"]
|
|
||||||
&& headers["upgrade"].toLowerCase() == "websocket") {
|
|
||||||
isWebsocket = true;
|
|
||||||
client.info = 'websocket';
|
|
||||||
let requestObject = new Request(request[0], request[1], request[2], headers, body, client);
|
|
||||||
let response = new Response(requestObject, client);
|
|
||||||
handleWebSocketRequest(requestObject, response, client);
|
|
||||||
/* Prevent the timeout from disconnecting us. */
|
|
||||||
client.setActivityTimeout();
|
|
||||||
} else {
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!parsing_header && inputBuffer.length)
|
|
||||||
{
|
|
||||||
let offset = body.length - bodyToRead;
|
|
||||||
let length = Math.min(inputBuffer.length, body.length - offset);
|
|
||||||
if (inputBuffer.length > body.length - offset) {
|
|
||||||
body.set(inputBuffer.slice(0, length), offset);
|
|
||||||
inputBuffer = inputBuffer.slice(length);
|
|
||||||
} else {
|
|
||||||
body.set(inputBuffer, offset);
|
|
||||||
inputBuffer = inputBuffer.slice(inputBuffer.length);
|
|
||||||
}
|
|
||||||
bodyToRead -= length;
|
|
||||||
if (bodyToRead <= 0) {
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
client.info = 'EOF';
|
|
||||||
client.close();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let kBacklog = 8;
|
|
||||||
let kHost = platform() == 'haiku' ? 'localhost' : '::';
|
|
||||||
|
|
||||||
function start() {
|
|
||||||
let socket = new Socket();
|
|
||||||
socket.bind(kHost, tildefriends.http_port).then(function(port) {
|
|
||||||
print("bound to", port);
|
|
||||||
print("checking", tildefriends.args.out_http_port_file);
|
|
||||||
if (tildefriends.args.out_http_port_file) {
|
|
||||||
print("going to write the file");
|
|
||||||
File.writeFile(tildefriends.args.out_http_port_file, port.toString() + '\n').then(function(r) {
|
|
||||||
print("wrote port file", tildefriends.args.out_http_port_file, r);
|
|
||||||
}).catch(function() {
|
|
||||||
print("failed to write port file");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
let listenResult = socket.listen(kBacklog, async function() {
|
|
||||||
try {
|
|
||||||
let client = await socket.accept();
|
|
||||||
client.noDelay = true;
|
|
||||||
handleConnection(client);
|
|
||||||
} catch (error) {
|
|
||||||
logError("[" + new Date() + "] accept error " + error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}).catch(function(error) {
|
|
||||||
logError("[" + new Date() + "] bind error " + error);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (tildefriends.https_port) {
|
|
||||||
let tls = {};
|
|
||||||
let secureSocket = new Socket();
|
|
||||||
secureSocket.bind(kHost, tildefriends.https_port).then(function() {
|
|
||||||
return secureSocket.listen(kBacklog, async function() {
|
|
||||||
try {
|
|
||||||
let client = await secureSocket.accept();
|
|
||||||
client.noDelay = true;
|
|
||||||
client.tls = true;
|
|
||||||
const kCertificatePath = "data/httpd/certificate.pem";
|
|
||||||
const kPrivateKeyPath = "data/httpd/privatekey.pem";
|
|
||||||
|
|
||||||
let stat = await Promise.all([
|
|
||||||
await File.stat(kCertificatePath),
|
|
||||||
await File.stat(kPrivateKeyPath),
|
|
||||||
]);
|
|
||||||
if (!tls.context ||
|
|
||||||
tls.certStat.mtime != stat[0].mtime ||
|
|
||||||
tls.certStat.size != stat[0].size ||
|
|
||||||
tls.keyStat.mtime != stat[1].mtime ||
|
|
||||||
tls.keyStat.size != stat[1].size) {
|
|
||||||
print("Reloading " + kCertificatePath + " and " + kPrivateKeyPath);
|
|
||||||
let privateKey = utf8Decode(await File.readFile(kPrivateKeyPath));
|
|
||||||
let certificate = utf8Decode(await File.readFile(kCertificatePath));
|
|
||||||
|
|
||||||
tls.context = new TlsContext();
|
|
||||||
tls.context.setPrivateKey(privateKey);
|
|
||||||
tls.context.setCertificate(certificate);
|
|
||||||
tls.certStat = stat[0];
|
|
||||||
tls.keyStat = stat[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
let result = client.startTls(tls.context);
|
|
||||||
handleConnection(client);
|
|
||||||
return result;
|
|
||||||
} catch (error) {
|
|
||||||
logError("[" + new Date() + "] " + error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}).catch(function(error) {
|
|
||||||
logError("[" + new Date() + "] bind error " + error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export { all, start, registerSocketHandler };
|
|
12
src/http.c
12
src/http.c
@ -544,7 +544,7 @@ static void _http_on_connection(uv_stream_t* stream, int status)
|
|||||||
http->connections[http->connections_count++] = connection;
|
http->connections[http->connections_count++] = connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
void tf_http_listen(tf_http_t* http, int port, tf_tls_context_t* tls)
|
int tf_http_listen(tf_http_t* http, int port, tf_tls_context_t* tls)
|
||||||
{
|
{
|
||||||
tf_http_listener_t* listener = tf_malloc(sizeof(tf_http_listener_t));
|
tf_http_listener_t* listener = tf_malloc(sizeof(tf_http_listener_t));
|
||||||
*listener = (tf_http_listener_t) { .http = http, .tls = tls, .tcp = { .data = listener } };
|
*listener = (tf_http_listener_t) { .http = http, .tls = tls, .tcp = { .data = listener } };
|
||||||
@ -569,6 +569,15 @@ void tf_http_listen(tf_http_t* http, int port, tf_tls_context_t* tls)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int assigned_port = 0;
|
||||||
|
if (r == 0)
|
||||||
|
{
|
||||||
|
struct sockaddr_storage name = { 0 };
|
||||||
|
int size = (int)sizeof(name);
|
||||||
|
r = uv_tcp_getsockname(&listener->tcp, (struct sockaddr*)&name, &size);
|
||||||
|
assigned_port = ntohs(((struct sockaddr_in*)&name)->sin_port);
|
||||||
|
}
|
||||||
|
|
||||||
if (r == 0)
|
if (r == 0)
|
||||||
{
|
{
|
||||||
r = uv_listen((uv_stream_t*)&listener->tcp, 16, _http_on_connection);
|
r = uv_listen((uv_stream_t*)&listener->tcp, 16, _http_on_connection);
|
||||||
@ -583,6 +592,7 @@ void tf_http_listen(tf_http_t* http, int port, tf_tls_context_t* tls)
|
|||||||
http->listeners = tf_realloc(http->listeners, sizeof(tf_http_listener_t*) * (http->listeners_count + 1));
|
http->listeners = tf_realloc(http->listeners, sizeof(tf_http_listener_t*) * (http->listeners_count + 1));
|
||||||
http->listeners[http->listeners_count++] = listener;
|
http->listeners[http->listeners_count++] = listener;
|
||||||
}
|
}
|
||||||
|
return assigned_port;
|
||||||
}
|
}
|
||||||
|
|
||||||
void tf_http_add_handler(tf_http_t* http, const char* pattern, tf_http_callback_t* callback, void* user_data)
|
void tf_http_add_handler(tf_http_t* http, const char* pattern, tf_http_callback_t* callback, void* user_data)
|
||||||
|
@ -36,7 +36,7 @@ typedef struct _tf_http_request_t
|
|||||||
typedef void (tf_http_callback_t)(tf_http_request_t* request);
|
typedef void (tf_http_callback_t)(tf_http_request_t* request);
|
||||||
|
|
||||||
tf_http_t* tf_http_create(uv_loop_t* loop);
|
tf_http_t* tf_http_create(uv_loop_t* loop);
|
||||||
void tf_http_listen(tf_http_t* http, int port, tf_tls_context_t* tls);
|
int tf_http_listen(tf_http_t* http, int port, tf_tls_context_t* tls);
|
||||||
void tf_http_add_handler(tf_http_t* http, const char* pattern, tf_http_callback_t* callback, void* user_data);
|
void tf_http_add_handler(tf_http_t* http, const char* pattern, tf_http_callback_t* callback, void* user_data);
|
||||||
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);
|
||||||
size_t tf_http_get_body(const tf_http_request_t* request, const void** out_data);
|
size_t tf_http_get_body(const tf_http_request_t* request, const void** out_data);
|
||||||
|
@ -293,8 +293,8 @@ static JSValue _httpd_start(JSContext* context, JSValueConst this_val, int argc,
|
|||||||
int port = 0;
|
int port = 0;
|
||||||
JS_ToInt32(context, &port, argv[0]);
|
JS_ToInt32(context, &port, argv[0]);
|
||||||
tf_tls_context_t* tls = tf_tls_context_get(JS_DupValue(context, argv[1]));
|
tf_tls_context_t* tls = tf_tls_context_get(JS_DupValue(context, argv[1]));
|
||||||
tf_http_listen(http, port, tls);
|
int assigned_port = tf_http_listen(http, port, tls);
|
||||||
return JS_UNDEFINED;
|
return JS_NewInt32(context, assigned_port);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _httpd_finalizer(JSRuntime* runtime, JSValue value)
|
void _httpd_finalizer(JSRuntime* runtime, JSValue value)
|
||||||
@ -343,6 +343,6 @@ void tf_httpd_register(JSContext* context)
|
|||||||
JS_SetPropertyStr(context, httpd, "all", JS_NewCFunction(context, _httpd_all, "all", 2));
|
JS_SetPropertyStr(context, httpd, "all", JS_NewCFunction(context, _httpd_all, "all", 2));
|
||||||
JS_SetPropertyStr(context, httpd, "registerSocketHandler", JS_NewCFunction(context, _httpd_register_socket_handler, "register_socket_handler", 2));
|
JS_SetPropertyStr(context, httpd, "registerSocketHandler", JS_NewCFunction(context, _httpd_register_socket_handler, "register_socket_handler", 2));
|
||||||
JS_SetPropertyStr(context, httpd, "start", JS_NewCFunction(context, _httpd_start, "start", 2));
|
JS_SetPropertyStr(context, httpd, "start", JS_NewCFunction(context, _httpd_start, "start", 2));
|
||||||
JS_SetPropertyStr(context, global, "httpdc", httpd);
|
JS_SetPropertyStr(context, global, "httpd", httpd);
|
||||||
JS_FreeValue(context, global);
|
JS_FreeValue(context, global);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user