mirror of
https://github.com/titaniumnetwork-dev/Ultraviolet.git
synced 2025-05-16 13:00:01 -04:00
uv
This commit is contained in:
parent
b9b6aee734
commit
82f5f76588
66 changed files with 74967 additions and 1 deletions
229
example/index.test.js
Normal file
229
example/index.test.js
Normal file
|
@ -0,0 +1,229 @@
|
|||
import http from "http";
|
||||
import https from "https";
|
||||
import httpStatic from "node-static";
|
||||
import path from "path";
|
||||
import { readFileSync, createReadStream } from "fs";
|
||||
import webpack from "webpack";
|
||||
|
||||
const __dirname = path.resolve(path.dirname(decodeURI(new URL(import.meta.url).pathname))).slice(3);
|
||||
const file = new httpStatic.Server(path.join(__dirname, './static/'));
|
||||
|
||||
const server = https.createServer({
|
||||
key: readFileSync(path.join(__dirname, './ssl.key')),
|
||||
cert: readFileSync(path.join(__dirname, './ssl.cert')),
|
||||
});
|
||||
|
||||
server.on('request', (req, res) => {
|
||||
|
||||
if (req.url.startsWith('/service/')) {
|
||||
res.writeHead(200, { "Content-Type": "text/html", "Cache-Control": 'no-cache' });
|
||||
createReadStream(path.join(__dirname, './load.html')).pipe(res);
|
||||
return;
|
||||
};
|
||||
|
||||
if (!req.url.startsWith('/bare/v1/')) return file.serve(req, res);
|
||||
|
||||
|
||||
try {
|
||||
const headers = JSON.parse(req.headers['x-bare-headers']);
|
||||
const forward = JSON.parse((req.headers['x-bare-forward-headers'] || '[]'));
|
||||
const url = new URL(req.headers['x-bare-protocol'] + '//' + req.headers['x-bare-host'] + ':' + req.headers['x-bare-port'] + req.headers['x-bare-path']);
|
||||
|
||||
for (const header of forward) {
|
||||
if (req.headers[header]) headers[header] = req.headers[header];
|
||||
};
|
||||
|
||||
const remoteRequest = (url.protocol === 'https:' ? https : http).request(
|
||||
url,
|
||||
{
|
||||
headers: headers,
|
||||
method: req.method,
|
||||
}
|
||||
);
|
||||
|
||||
remoteRequest.on('response', remoteResponse => {
|
||||
remoteResponse.headers['x-bare-headers'] = JSON.stringify(remoteResponse.headers);
|
||||
remoteResponse.headers['x-bare-status'] = remoteResponse.statusCode.toString();
|
||||
remoteResponse.headers['x-bare-status-text'] = remoteResponse.statusMessage;
|
||||
remoteResponse.headers['cache-control'] = 'no-cache';
|
||||
|
||||
const headers = {
|
||||
'x-bare-headers': JSON.stringify(remoteResponse.headers),
|
||||
'x-bare-status': remoteResponse.statusCode.toString(),
|
||||
'x-bare-status-text': remoteResponse.statusMessage,
|
||||
'cache-control': 'no-cache',
|
||||
};
|
||||
|
||||
if (remoteResponse.headers['content-encoding']) headers['content-encoding'] = remoteResponse.headers['content-encoding'];
|
||||
if (remoteResponse.headers['content-length']) headers['content-length'] = remoteResponse.headers['content-length'];
|
||||
|
||||
res.writeHead(200, headers);
|
||||
remoteResponse.pipe(res);
|
||||
});
|
||||
|
||||
remoteRequest.on('error', e => {
|
||||
res.writeHead(500, {});
|
||||
res.end();
|
||||
});
|
||||
|
||||
req.pipe(remoteRequest);
|
||||
} catch(e) {
|
||||
res.writeHead(500, {});
|
||||
res.end();
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
const impl = {
|
||||
'accept-encoding': 'Accept-Encoding',
|
||||
'accept-language': 'Accept-Language',
|
||||
'accept': 'Accept',
|
||||
'sec-websocket-extensions': 'Sec-WebSocket-Extensions',
|
||||
'sec-websocket-key': 'Sec-WebSocket-Key',
|
||||
'sec-websocket-version': 'Sec-WebSocket-Version'
|
||||
};
|
||||
|
||||
server.on('upgrade', (req, socket, head) => {
|
||||
if (!req.url.startsWith('/bare/v1/') || !req.headers['sec-websocket-protocol']) return socket.end();
|
||||
try {
|
||||
const [ bare, data ] = req.headers['sec-websocket-protocol'].split(/,\s*/g);
|
||||
const {
|
||||
remote,
|
||||
headers,
|
||||
forward_headers: forward,
|
||||
} = JSON.parse(decodeProtocol(data));
|
||||
|
||||
for (const header of forward) {
|
||||
if (req.headers[header]) headers[(impl[header] || header)] = req.headers[header];
|
||||
};
|
||||
|
||||
const url = new URL(remote.protocol + '//' + remote.host + ':' + remote.port + remote.path);
|
||||
const remoteRequest = (url.protocol === 'https:' ? https : http).request(
|
||||
url,
|
||||
{
|
||||
headers,
|
||||
method: req.method,
|
||||
}
|
||||
);
|
||||
|
||||
remoteRequest.on('upgrade', (remoteResponse, remoteSocket, remoteHead) => {
|
||||
let handshake = 'HTTP/1.1 101 Web Socket Protocol Handshake\r\n';
|
||||
if (remoteResponse.headers['sec-websocket-accept']) handshake += `Sec-WebSocket-Accept: ${remoteResponse.headers['sec-websocket-accept']}\r\n`;
|
||||
if (remoteResponse.headers['sec-websocket-extensions']) handshake += `Sec-WebSocket-Extensions: ${remoteResponse.headers['sec-websocket-extensions']}\r\n`;
|
||||
handshake += `Sec-WebSocket-Protocol: bare\r\n`;
|
||||
if (remoteResponse.headers['connection']) handshake += `Connection: ${remoteResponse.headers['connection']}\r\n`;
|
||||
if (remoteResponse.headers['upgrade']) handshake += `Upgrade: ${remoteResponse.headers['upgrade']}\r\n`;
|
||||
handshake += '\r\n';
|
||||
socket.write(handshake);
|
||||
socket.write(remoteHead);
|
||||
remoteSocket.on('close', () => socket.end());
|
||||
socket.on('close', () => remoteSocket.end());
|
||||
remoteSocket.on('error', () => socket.end());
|
||||
socket.on('error', () => remoteSocket.end());
|
||||
|
||||
remoteSocket.pipe(socket);
|
||||
socket.pipe(remoteSocket);
|
||||
});
|
||||
|
||||
remoteRequest.on('error', () => socket.end());
|
||||
|
||||
remoteRequest.end();
|
||||
|
||||
} catch(e) {
|
||||
console.log(e);
|
||||
socket.end();
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
server.listen(443);
|
||||
|
||||
|
||||
const valid_chars = "!#$%&'*+-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz|~";
|
||||
const reserved_chars = "%";
|
||||
|
||||
function encodeProtocol(protocol){
|
||||
protocol = protocol.toString();
|
||||
|
||||
let result = '';
|
||||
|
||||
for(let i = 0; i < protocol.length; i++){
|
||||
const char = protocol[i];
|
||||
|
||||
if(valid_chars.includes(char) && !reserved_chars.includes(char)){
|
||||
result += char;
|
||||
}else{
|
||||
const code = char.charCodeAt();
|
||||
result += '%' + code.toString(16).padStart(2, 0);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function decodeProtocol(protocol){
|
||||
if(typeof protocol != 'string')throw new TypeError('protocol must be a string');
|
||||
|
||||
let result = '';
|
||||
|
||||
for(let i = 0; i < protocol.length; i++){
|
||||
const char = protocol[i];
|
||||
|
||||
if(char == '%'){
|
||||
const code = parseInt(protocol.slice(i + 1, i + 3), 16);
|
||||
const decoded = String.fromCharCode(code);
|
||||
|
||||
result += decoded;
|
||||
i += 2;
|
||||
}else{
|
||||
result += char;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
function parseRawHeaders(rawHeaders = []) {
|
||||
const obj = {};
|
||||
|
||||
for (let i = 0; i < rawHeaders.length; i+=2) {
|
||||
const name = rawHeaders[i] || '';
|
||||
const lowerCaseName = name.toLowerCase();
|
||||
const value = rawHeaders[i + 1] || '';
|
||||
|
||||
if (lowerCaseName in obj) {
|
||||
if (Array.isArray(obj[lowerCaseName].value)) {
|
||||
obj[lowerCaseName].value.push(value);
|
||||
} else {
|
||||
obj[lowerCaseName].value = [ obj[lowerCaseName].value, value ];
|
||||
};
|
||||
} else {
|
||||
obj[lowerCaseName] = { name, value };
|
||||
};
|
||||
};
|
||||
|
||||
return obj;
|
||||
};
|
||||
|
||||
function compileParsedHeaders(headers = {}, prefix = false) {
|
||||
const compiled = {};
|
||||
|
||||
for (const key in headers) {
|
||||
const { name, value } = headers[key];
|
||||
compiled[(prefix ? 'x-op-' : '') + name] = value;
|
||||
};
|
||||
|
||||
return compiled;
|
||||
};
|
||||
|
||||
webpack({
|
||||
mode: 'none',
|
||||
entry: path.join(__dirname, '../lib/index.js'),
|
||||
output: {
|
||||
path: __dirname,
|
||||
filename: './static/op.bundle.js',
|
||||
}
|
||||
}, (err, i) =>
|
||||
console.log(!err ? 'Ultraviolet bundled!' : 'Err')
|
||||
);
|
Loading…
Add table
Add a link
Reference in a new issue