Fixed enough thing sto be able to authenticate and get data from Strava.

git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4347 ed5197a5-7fde-0310-b194-c3ffbd925b24
This commit is contained in:
Cory McWilliams 2023-07-16 21:04:48 +00:00
parent 5074246462
commit 6ef466f3ed
2 changed files with 42 additions and 19 deletions

View File

@ -1,6 +1,7 @@
import * as app from './app.js'; 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 httpd from './httpd.js'; import * as httpd from './httpd.js';
let gProcessIndex = 0; let gProcessIndex = 0;
@ -66,6 +67,11 @@ const k_global_settings = {
default_value: undefined, default_value: undefined,
description: 'If connecting by HTTP and HTTPS is configured, Location header prefix (ie, "https://example.com")', description: 'If connecting by HTTP and HTTPS is configured, Location header prefix (ie, "https://example.com")',
}, },
fetch_hosts: {
type: 'string',
default_value: undefined,
description: 'Comma-separated list of host names to which HTTP fetch requests are allowed. None if empty.',
},
}; };
let gGlobalSettings = { let gGlobalSettings = {
@ -404,6 +410,9 @@ async function getProcessBlob(blobId, key, options) {
return ssb.privateMessageDecrypt(process.credentials.session.name, id, message); return ssb.privateMessageDecrypt(process.credentials.session.name, id, message);
} }
}; };
imports.fetch = function(url, options) {
return http.fetch(url, options, gGlobalSettings.fetch_hosts);
}
if (process.credentials && if (process.credentials &&
process.credentials.session && process.credentials.session &&
@ -583,12 +592,12 @@ function guessTypeFromMagicBytes(data) {
} }
} }
function sendData(response, data, type, headers) { function sendData(response, data, type, headers, status_code) {
if (data) { if (data) {
response.writeHead(200, Object.assign({"Content-Type": type || guessTypeFromMagicBytes(data) || "application/binary", "Content-Length": data.byteLength}, headers || {})); response.writeHead(status_code ?? 200, Object.assign({"Content-Type": type || guessTypeFromMagicBytes(data) || "application/binary", "Content-Length": data.byteLength}, headers || {}));
response.end(data); response.end(data);
} else { } else {
response.writeHead(404, Object.assign({"Content-Type": "text/plain; charset=utf-8", "Content-Length": "File not found".length}, headers || {})); response.writeHead(status_code ?? 404, Object.assign({"Content-Type": "text/plain; charset=utf-8", "Content-Length": "File not found".length}, headers || {}));
response.end("File not found"); response.end("File not found");
} }
} }
@ -604,7 +613,8 @@ async function getBlobOrContent(id) {
} }
let g_handler_index = 0; let g_handler_index = 0;
async function useAppHandler(response, handler_blob_id, path, query, headers) { async function useAppHandler(response, handler_blob_id, path, query, headers, packageOwner, packageName) {
print('useAppHandler', packageOwner, packageName);
let do_resolve; let do_resolve;
let promise = new Promise(async function(resolve, reject) { let promise = new Promise(async function(resolve, reject) {
do_resolve = resolve; do_resolve = resolve;
@ -622,6 +632,8 @@ async function useAppHandler(response, handler_blob_id, path, query, headers) {
respond: do_resolve, respond: do_resolve,
}, },
credentials: auth.query(headers), credentials: auth.query(headers),
packageOwner: packageOwner,
packageName: packageName,
}); });
await process.ready; await process.ready;
@ -784,7 +796,11 @@ async function blobHandler(request, response, blobId, uri) {
let match; let match;
let id; let id;
let app_id = blobId; let app_id = blobId;
let packageOwner;
let packageName;
if (match = /^\/\~(\w+)\/(\w+)$/.exec(blobId)) { if (match = /^\/\~(\w+)\/(\w+)$/.exec(blobId)) {
packageOwner = match[1];
packageName = match[2];
let db = new Database(match[1]); let db = new Database(match[1]);
app_id = await db.get('path:' + match[2]); app_id = await db.get('path:' + match[2]);
} }
@ -794,7 +810,7 @@ async function blobHandler(request, response, blobId, uri) {
if (!id && app_object.files['handler.js']) { if (!id && app_object.files['handler.js']) {
let answer; let answer;
try { try {
answer = await useAppHandler(response, app_id, uri.substring(1), request.query ? form.decodeForm(request.query) : undefined, request.headers); answer = await useAppHandler(response, app_id, uri.substring(1), request.query ? form.decodeForm(request.query) : undefined, request.headers, packageOwner, packageName);
} catch (error) { } catch (error) {
data = utf8Encode(`Internal Server Error\n\n${error?.message}\n${error?.stack}`); data = utf8Encode(`Internal Server Error\n\n${error?.message}\n${error?.stack}`);
response.writeHead(500, {'Content-Type': 'text/plain; charset=utf-8', 'Content-Length': data.length}); response.writeHead(500, {'Content-Type': 'text/plain; charset=utf-8', 'Content-Length': data.length});
@ -804,10 +820,10 @@ async function blobHandler(request, response, blobId, uri) {
if (answer && typeof answer.data == 'string') { if (answer && typeof answer.data == 'string') {
answer.data = utf8Encode(answer.data); answer.data = utf8Encode(answer.data);
} }
sendData(response, answer?.data, answer?.content_type, { sendData(response, answer?.data, answer?.content_type, Object.assign(answer?.headers ?? {}, {
'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Origin': '*',
'Content-Security-Policy': 'sandbox', 'Content-Security-Policy': 'sandbox',
}); }), answer.status_code);
} else if (id) { } else if (id) {
if (request.headers['if-none-match'] && request.headers['if-none-match'] == '"' + id + '"') { if (request.headers['if-none-match'] && request.headers['if-none-match'] == '"' + id + '"') {
let headers = { let headers = {

View File

@ -1,43 +1,45 @@
function parseUrl(url) { function parseUrl(url) {
// XXX: Hack. // XXX: Hack.
let match = url.match(new RegExp("(\\w+)://([^/]+)?(.*)")); let match = url.match(new RegExp("(\\w+)://([^/:]+)(?::(\\d+))?(.*)"));
return { return {
protocol: match[1], protocol: match[1],
host: match[2], host: match[2],
path: match[3], path: match[4],
port: match[1] == "http" ? 80 : 443, port: match[3] ? parseInt(match[3]) : match[1] == "http" ? 80 : 443,
}; };
} }
function parseResponse(data) { function parseResponse(data) {
let firstLine; let firstLine;
let headers = {}; let headers = {};
while (true) { while (true) {
let endLine = data.indexOf('\r\n'); let endLine = data.indexOf('\r\n');
let line = data.substring(0, endLine); let line = data.substring(0, endLine);
if (!firstLine) { data = data.substring(endLine + 2);
firstLine = line; if (!line.length) {
} else if (!line.length) {
break; break;
} else if (!firstLine) {
firstLine = line;
} else { } else {
let colon = line.indexOf(":"); let colon = line.indexOf(":");
headers[line.substring(colon)] = line.substring(colon + 1); headers[line.substring(colon)] = line.substring(colon + 1);
} }
data = data.substring(endLine + 2);
} }
return {body: data}; return {body: data};
} }
export function fetch(url, options) { export function fetch(url, options, allowed_hosts) {
let parsed = parseUrl(url); let parsed = parseUrl(url);
return new Promise(function(resolve, reject) { 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 socket = new Socket();
let buffer = new Uint8Array(0) let buffer = new Uint8Array(0);
return socket.connect(parsed.host, parsed.port).then(function() { return socket.connect(parsed.host, parsed.port).then(function() {
socket.read(function(data) { socket.read(function(data) {
if (data) { if (data && data.length) {
let newBuffer = new Uint8Array(buffer.length + data.length); let newBuffer = new Uint8Array(buffer.length + data.length);
newBuffer.set(buffer, 0); newBuffer.set(buffer, 0);
newBuffer.set(data, buffer.length); newBuffer.set(data, buffer.length);
@ -51,7 +53,12 @@ export function fetch(url, options) {
return socket.startTls(); return socket.startTls();
} }
}).then(function() { }).then(function() {
socket.write(`${options?.method ?? 'GET'} ${parsed.path} HTTP/1.0\r\nHost: ${parsed.host}\r\nConnection: close\r\n\r\n`); 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);
socket.shutdown(); socket.shutdown();
}).catch(function(error) { }).catch(function(error) {
reject(error); reject(error);