forked from cory/tildefriends
105 lines
2.7 KiB
JavaScript
105 lines
2.7 KiB
JavaScript
/**
|
|
* TODOC
|
|
* TODO: document so we can improve this
|
|
* @param {*} url
|
|
* @returns
|
|
*/
|
|
function parseUrl(url) {
|
|
// XXX: Hack.
|
|
let match = url.match(new RegExp("(\\w+)://([^/:]+)(?::(\\d+))?(.*)"));
|
|
return {
|
|
protocol: match[1],
|
|
host: match[2],
|
|
path: match[4],
|
|
port: match[3] ? parseInt(match[3]) : match[1] == "http" ? 80 : 443,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* TODOC
|
|
* @param {*} data
|
|
* @returns
|
|
*/
|
|
function parseResponse(data) {
|
|
let firstLine;
|
|
let headers = {};
|
|
while (true) {
|
|
let endLine = data.indexOf('\r\n');
|
|
let line = data.substring(0, endLine);
|
|
data = data.substring(endLine + 2);
|
|
if (!line.length) {
|
|
break;
|
|
} else if (!firstLine) {
|
|
firstLine = line;
|
|
} else {
|
|
let colon = line.indexOf(":");
|
|
headers[line.substring(colon)] = line.substring(colon + 1);
|
|
}
|
|
}
|
|
return {body: data};
|
|
}
|
|
|
|
/**
|
|
* TODOC
|
|
* @param {*} url
|
|
* @param {*} options
|
|
* @param {*} allowed_hosts
|
|
* @returns
|
|
*/
|
|
export function fetch(url, options, allowed_hosts) {
|
|
let parsed = parseUrl(url);
|
|
return new Promise(function(resolve, reject) {
|
|
if ((allowed_hosts ?? []).indexOf(parsed.host) == -1) {
|
|
throw new Error(`fetch() request to host ${parsed.host} is not allowed.`);
|
|
}
|
|
let socket = new Socket();
|
|
let buffer = new Uint8Array(0);
|
|
|
|
return socket.connect(parsed.host, parsed.port).then(function() {
|
|
socket.read(function(data) {
|
|
if (data && data.length) {
|
|
let newBuffer = new Uint8Array(buffer.length + data.length);
|
|
newBuffer.set(buffer, 0);
|
|
newBuffer.set(data, buffer.length);
|
|
buffer = newBuffer;
|
|
} else {
|
|
let result = parseHttpResponse(buffer);
|
|
if (!result) {
|
|
reject(new Exception('Parse failed.'));
|
|
}
|
|
if (typeof result == 'number') {
|
|
if (result == -2) {
|
|
reject('Incomplete request.');
|
|
} else {
|
|
reject('Bad request.');
|
|
}
|
|
} else if (typeof result == 'object') {
|
|
resolve({
|
|
body: buffer.slice(result.bytes_parsed),
|
|
status: result.status,
|
|
message: result.message,
|
|
headers: result.headers,
|
|
});
|
|
} else {
|
|
reject(new Exception('Unexpected parse result.'));
|
|
}
|
|
resolve(parseResponse(utf8Decode(buffer)));
|
|
}
|
|
});
|
|
|
|
if (parsed.port == 443) {
|
|
return socket.startTls();
|
|
}
|
|
}).then(function() {
|
|
let body = typeof options?.body == 'string' ? utf8Encode(options.body) : (options.body || new Uint8Array(0));
|
|
let headers = utf8Encode(`${options?.method ?? 'GET'} ${parsed.path} HTTP/1.0\r\nHost: ${parsed.host}\r\nConnection: close\r\nContent-Length: ${body.length}\r\n\r\n`);
|
|
let fullRequest = new Uint8Array(headers.length + body.length);
|
|
fullRequest.set(headers, 0);
|
|
fullRequest.set(body, headers.length);
|
|
socket.write(fullRequest);
|
|
}).catch(function(error) {
|
|
reject(error);
|
|
});
|
|
});
|
|
}
|