mirror of
https://github.com/titaniumnetwork-dev/Ultraviolet.git
synced 2025-05-15 20:40:01 -04:00
304 lines
No EOL
9.8 KiB
JavaScript
304 lines
No EOL
9.8 KiB
JavaScript
importScripts('/op.bundle.js');
|
|
|
|
const csp = [
|
|
'cross-origin-embedder-policy',
|
|
'cross-origin-opener-policy',
|
|
'cross-origin-resource-policy',
|
|
'content-security-policy',
|
|
'content-security-policy-report-only',
|
|
'expect-ct',
|
|
'feature-policy',
|
|
'origin-isolation',
|
|
'strict-transport-security',
|
|
'upgrade-insecure-requests',
|
|
'x-content-type-options',
|
|
'x-download-options',
|
|
'x-frame-options',
|
|
'x-permitted-cross-domain-policies',
|
|
'x-powered-by',
|
|
'x-xss-protection',
|
|
];
|
|
|
|
const headers = {
|
|
csp: [
|
|
'cross-origin-embedder-policy',
|
|
'cross-origin-opener-policy',
|
|
'cross-origin-resource-policy',
|
|
'content-security-policy',
|
|
'content-security-policy-report-only',
|
|
'expect-ct',
|
|
'feature-policy',
|
|
'origin-isolation',
|
|
'strict-transport-security',
|
|
'upgrade-insecure-requests',
|
|
'x-content-type-options',
|
|
'x-download-options',
|
|
'x-frame-options',
|
|
'x-permitted-cross-domain-policies',
|
|
'x-powered-by',
|
|
'x-xss-protection',
|
|
],
|
|
forward: [
|
|
'accept-encoding',
|
|
'accept',
|
|
'connection',
|
|
'content-length',
|
|
'content-type',
|
|
],
|
|
};
|
|
|
|
const method = {
|
|
empty: ['GET', 'HEAD']
|
|
};
|
|
|
|
const statusCode = {
|
|
empty: [
|
|
204,
|
|
304,
|
|
],
|
|
};
|
|
|
|
Brazen('/bare/v1/', {
|
|
prefix: '/service/',
|
|
});
|
|
|
|
function Brazen(bare, config = {}) {
|
|
if (!bare) throw new Error('No barer server given. Cannot make connections without one.');
|
|
addEventListener('fetch', event => {
|
|
event.respondWith(handler(event.request, config, bare));
|
|
});
|
|
};
|
|
|
|
|
|
async function handler(request, config = {}, bare) {
|
|
try {
|
|
let blob = false;
|
|
const clientRequest = request;
|
|
const _url = new URL(clientRequest.url);
|
|
const rewrite = new Rewriter(config);
|
|
const db = await rewrite.cookie.db();
|
|
let fetchUrl = bare;
|
|
|
|
if (!request.url.startsWith(location.origin) || !_url.pathname.startsWith(rewrite.prefix) || (config.skip || []).includes(request.url)) {
|
|
return fetch(request);
|
|
};
|
|
|
|
rewrite.meta.origin = location.origin;
|
|
rewrite.meta.base = rewrite.meta.url = new URL(rewrite.sourceUrl(request.url));
|
|
|
|
rewrite.html.on('element', (element, type) => {
|
|
if (type !== 'rewrite') return false;
|
|
if (element.tagName !== 'head') return false;
|
|
|
|
element.childNodes.unshift(
|
|
{
|
|
tagName: 'script',
|
|
nodeName: 'script',
|
|
childNodes: [],
|
|
attrs: [
|
|
{ name: 'src', value: '/op.handler.js', skip: true }
|
|
],
|
|
}
|
|
);
|
|
|
|
element.childNodes.unshift(
|
|
{
|
|
tagName: 'script',
|
|
nodeName: 'script',
|
|
childNodes: [],
|
|
attrs: [
|
|
{ name: 'src', value: '/op.bundle.js', skip: true }
|
|
],
|
|
}
|
|
);
|
|
|
|
element.childNodes.unshift(
|
|
{
|
|
tagName: 'script',
|
|
nodeName: 'script',
|
|
childNodes: [
|
|
{
|
|
nodeName: '#text',
|
|
value: `window.__opCookies = atob("${btoa(rewrite.cookie.serialize(element.options.cookies, rewrite.meta, true))}");\nwindow.__opReferrer = atob("${btoa(request.referrer)}");`
|
|
},
|
|
],
|
|
attrs: [],
|
|
skip: true,
|
|
}
|
|
);
|
|
});
|
|
|
|
rewrite.addRewrites();
|
|
|
|
if (rewrite.meta.url.protocol === 'blob:') {
|
|
blob = true;
|
|
rewrite.meta.base = rewrite.meta.url = new URL(rewrite.meta.url.pathname);
|
|
fetchUrl = 'blob:' + location.origin + rewrite.meta.url.pathname;
|
|
};
|
|
|
|
const { url } = rewrite.meta;
|
|
const sendHeaders = Object.fromEntries([...request.headers.entries()]);
|
|
|
|
sendHeaders['user-agent'] = navigator.userAgent;
|
|
|
|
if (request.referrer && request.referrer.startsWith(location.origin)) {
|
|
const referer = new URL(rewrite.sourceUrl(request.referrer));
|
|
|
|
if (rewrite.meta.url.origin !== referer.origin && request.mode === 'cors') {
|
|
sendHeaders.origin = referer.origin;
|
|
};
|
|
|
|
sendHeaders.referer = referer.href;
|
|
};
|
|
|
|
//const cookies = await rewrite.cookie.getCookies(await db.getAll('cookies'), rewrite.meta) || '';
|
|
const cookies = await rewrite.cookie.getCookies(db) || [];
|
|
const cookieStr = rewrite.cookie.serialize(cookies, rewrite.meta, false);
|
|
if (cookieStr) sendHeaders.cookie = cookieStr;
|
|
|
|
const barer = {
|
|
'x-bare-protocol': url.protocol,
|
|
'x-bare-host': url.hostname,
|
|
'x-bare-path': url.pathname + url.search,
|
|
'x-bare-port': url.port,
|
|
'x-bare-headers': JSON.stringify(sendHeaders),
|
|
'x-bare-forward-headers': JSON.stringify(headers.forward),
|
|
};
|
|
|
|
const options = {
|
|
method: request.method,
|
|
headers: !blob ? barer : request.headers,
|
|
redirect: request.redirect,
|
|
credentials: 'omit',
|
|
mode: request.mode === 'cors' ? request.mode : 'same-origin',
|
|
};
|
|
|
|
if (!method.empty.includes(request.method.toUpperCase())) options.body = await request.blob();
|
|
|
|
const remoteRequest = !blob ? new Request(fetchUrl, options) : new Request(fetchUrl);
|
|
const remoteResponse = await fetch(remoteRequest);
|
|
|
|
if (remoteResponse.status === 500) {
|
|
return Promise.reject('Err');
|
|
};
|
|
|
|
const sendData = !blob ? getBarerResponse(remoteResponse) : {
|
|
status: remoteResponse.status,
|
|
statusText: remoteResponse.statusText,
|
|
headers: Object.fromEntries([...remoteResponse.headers.entries()]),
|
|
body: remoteResponse.body,
|
|
};
|
|
|
|
for (const name of headers.csp) {
|
|
if (sendData.headers[name]) delete sendData.headers[name];
|
|
};
|
|
|
|
if (sendData.headers.location) {
|
|
sendData.headers.location = rewrite.rewriteUrl(sendData.headers.location);
|
|
};
|
|
|
|
if (sendData.headers['set-cookie']) {
|
|
Promise.resolve(rewrite.cookie.setCookies(sendData.headers['set-cookie'], db, rewrite.meta)).then(() => {
|
|
self.clients.matchAll().then(function (clients){
|
|
clients.forEach(function(client){
|
|
client.postMessage({
|
|
msg: 'updateCookies',
|
|
url: rewrite.meta.url.href,
|
|
});
|
|
});
|
|
});
|
|
});
|
|
delete sendData.headers['set-cookie'];
|
|
};
|
|
|
|
if (statusCode.empty.includes(sendData.status)) {
|
|
return new Response(null, {
|
|
headers: sendData.headers,
|
|
status: sendData.status,
|
|
statusText: sendData.statusText,
|
|
});
|
|
};
|
|
|
|
|
|
switch(request.destination) {
|
|
case 'script':
|
|
sendData.body = rewrite.js.rewrite(
|
|
await remoteResponse.text()
|
|
);
|
|
break;
|
|
case 'worker':
|
|
sendData.body = `if (!self.__op) importScripts('/op.bundle.js', '/op.handler.js');\n`;
|
|
sendData.body += rewrite.js.rewrite(
|
|
await remoteResponse.text()
|
|
);
|
|
break;
|
|
case 'style':
|
|
sendData.body = rewrite.rewriteCSS(
|
|
await remoteResponse.text()
|
|
);
|
|
break;
|
|
/*
|
|
case 'document':
|
|
case 'iframe':
|
|
sendData.body = rewrite.rewriteHtml(
|
|
await remoteResponse.text(),
|
|
{
|
|
document: true,
|
|
cookies,
|
|
}
|
|
);
|
|
*/
|
|
/*
|
|
default:
|
|
if (request.mode !== 'cors' && isHtml(rewrite.meta.url, (sendData.headers['content-type'] || ''))) {
|
|
sendData.body = rewrite.rewriteHtml(
|
|
await remoteResponse.text(),
|
|
{
|
|
document: true ,
|
|
cookies,
|
|
}
|
|
);
|
|
};
|
|
*/
|
|
case 'iframe':
|
|
case 'document':
|
|
if (isHtml(rewrite.meta.url, (sendData.headers['content-type'] || ''))) {
|
|
sendData.body = rewrite.rewriteHtml(
|
|
await remoteResponse.text(),
|
|
{
|
|
document: true ,
|
|
cookies,
|
|
}
|
|
);
|
|
};
|
|
};
|
|
|
|
// EventSource support
|
|
if (sendHeaders.accept === 'text/event-stream') {
|
|
sendData.headers['content-type'] = 'text/event-stream';
|
|
};
|
|
|
|
return new Response(sendData.body, {
|
|
headers: sendData.headers,
|
|
status: sendData.status,
|
|
statusText: sendData.statusText,
|
|
});
|
|
} catch(e) {
|
|
return new Response(e.toString(), {
|
|
status: 500,
|
|
});
|
|
};
|
|
};
|
|
|
|
function getBarerResponse(response) {
|
|
return {
|
|
headers: JSON.parse(response.headers.get('x-bare-headers')),
|
|
status: +response.headers.get('x-bare-status'),
|
|
statusText: response.headers.get('x-bare-status-text'),
|
|
body: response.body,
|
|
};
|
|
};
|
|
|
|
function isHtml(url, contentType = '') {
|
|
return (Rewriter.mime.contentType((contentType || url.pathname)) || 'text/html').split(';')[0] === 'text/html';
|
|
}; |