Skip to content

Instantly share code, notes, and snippets.

@kyle-rb
Created December 16, 2018 08:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kyle-rb/9a6d54a26ca07d32f8f9be22e7e807a0 to your computer and use it in GitHub Desktop.
Save kyle-rb/9a6d54a26ca07d32f8f9be22e7e807a0 to your computer and use it in GitHub Desktop.
const http = require('http');
const https = require('https');
const URL = require('url').URL;
const fs = require('fs');
const badRequestResponse = '<html><head><title>400 Bad Request</title><body><h1>400 Bad Request</h1><h2>Required URL parameters are missing</h2></body></html>';
const notFoundResponse = '<html><head><title>404 Not Found</title><body><h1>404 Not Found</h1><h2>The requested page was not found on the server</h2></body></html>';
const MIME_TYPES = {
'mp4': 'video/mp4', 'm4v':'video/mp4', 'webm': 'video/webm', 'mkv': 'video/x-matroska',
'png': 'image/png', 'jpg':'image/jpeg', 'jpeg': 'image/jpeg', 'gif': 'image/gif',
'ico': 'image/x-icon', 'svg':'image/svg+xml', 'webp': 'image/webp',
'mp3': 'audio/mpeg', 'wav':'audio/wav',
'txt': 'text/plain', 'html':'text/html', 'css': 'text/css', 'js': 'text/javascript',
'xml': 'text/xml' // possibly supposed to be application/xml, but it's debatable
}; // all the filetypes that might need to be served, I guess
const server = http.createServer(function(request, response) {
if (request.url === '/') { // if request is for the root
request.url += 'index.html';
}
let url = new URL('http://localhost:8000' + request.url); // need domain in url for constructor
if (url.pathname === '/req/') {
if (url.searchParams.get('url')) {
makeRequestAndRespond(decodeURIComponent(url.searchParams.get('url')), response);
}
else {
response.writeHead(400, { 'Content-Type': MIME_TYPES.html }); // 400 bad request
response.end(badRequestResponse);
}
}
else { // otherwise this is a regular static file request
let fileExtension = url.pathname.split('.').pop();
let mimeType = MIME_TYPES[fileExtension] || 'application/octet-stream';
let fileSystemPath = '.' + url.pathname;
fs.lstat(fileSystemPath, function(err, stats) { // make sure file exists
if (err || stats.isDirectory()) {
response.writeHead(404, { 'Content-Type': MIME_TYPES.html }); // 404 not found
response.end(notFoundResponse);
return;
}
response.writeHead(200, { 'Content-Type': mimeType });
let fileStream = fs.createReadStream(fileSystemPath)
.on('open', function() {
fileStream.pipe(response);
})
.on('error', function(err) {
console.log(err);
response.end(err);
});
});
}
});
server.listen(8000, '0.0.0.0', function() {
console.log('server running at http://0.0.0.0:8000');
});
/*
* Make a request to a remote page and pass that remote response through to the local response that
* was passed in to the function.
*/
function makeRequestAndRespond(urlString, localResponse) {
let url;
try {
url = new URL(urlString);
}
catch (e) { // if the URL is invalid, 400 response
response.writeHead(400, { 'Content-Type': MIME_TYPES.html }); // 400 bad request
response.end(badRequestResponse);
return;
}
let requestModule = http;
if (url.protocol === 'https:') { // secure requests require using https module
requestModule = https;
}
let requestOptions = {
protocol: url.protocol,
hostname: url.hostname,
port: url.port,
path: url.pathname + url.search,
headers: { referer: urlString }
};
return requestModule.get(requestOptions, function(remoteResponse) {
var body = '';
remoteResponse.on('data', function(d) {
body += d; // continuously update stream with data
});
remoteResponse.on('end', function() {
localResponse.writeHead(remoteResponse.statusCode);
localResponse.end(body);
});
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment