diff --git a/index.js b/index.js new file mode 100644 index 0000000..30dc41c --- /dev/null +++ b/index.js @@ -0,0 +1,130 @@ +const http = require('http') +const url = require('url') +const fs = require('fs') +const ws = require('ws') +const redis = require('redis') +const mime = require('mime') +const XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest +const host = 'http://127.0.0.1:8080/' + +//redis client +const redis_cli = redis.createClient() +redis_cli.on("error", function (error) { + console.error(error) +}) + +const outpath = ['sym', '', 'manifest.json'] + +//HTTP server +http.createServer(function (req, res) { + const q = url.parse(req.url, true); + let filename = "./public" + q.pathname; + if (filename === "./public/") filename = "./public/index.html"; + let file_type = mime.getType(filename) + //q.pathname.split("/", 2)[1] === "sym" || ) + if (function valid_path(){// + for (let i = 0; i < outpath.length; i++) if (q.pathname.split("/", 2)[1] === outpath[i]) return true + return false + }() === true){ + fs.readFile(filename, function(err, data) { + if (err) { + res.writeHead(404, {'Content-Type': file_type}); + return res.end("404 Not Found"); + } + res.writeHead(200, {'Content-Type': file_type}); + res.write(data); + return res.end(); + }) + }else{ + redis_cli.hget("surl;"+q.pathname.split("/", 2)[1], "url", function(err, obj) { + if(err) console.log(err); + if(obj){ + res.writeHead(302, {'Location': obj}); + return res.end(); + }else{ + res.writeHead(404, {'Content-Type': file_type}); + return res.end("404 this short-url does not exist :/"); + } + }); + } +}).listen(8080); + +//WS server +const wss = new ws.Server({ + port: 8081, + perMessageDeflate: { + zlibDeflateOptions: { + // See zlib defaults. + chunkSize: 1024, + memLevel: 7, + level: 3 + }, + zlibInflateOptions: { + chunkSize: 10 * 1024 + }, + // Other options settable: + clientNoContextTakeover: true, // Defaults to negotiated value. + serverNoContextTakeover: true, // Defaults to negotiated value. + serverMaxWindowBits: 10, // Defaults to negotiated value. + // Below options specified as default values. + concurrencyLimit: 10, // Limits zlib concurrency for perf. + threshold: 1024 // Size (in bytes) below which messages + // should not be compressed. + } +}); + +//WS handler +wss.on('connection', ws => { + ws.on('message', message => { + console.log(`Received message => ${message}`) + let msg = `${message}`.split(";", 2) + if (msg[0] === 'long_url') { + if (msg[1] === '') ws.send('error;url is empty') + else if (msg[1].length > 2000) ws.send('error;your url is too long') + else{ + let xhr = new XMLHttpRequest(); + xhr.onreadystatechange = function() { + if (xhr.readyState === 4) { + switch(xhr.status) { + case 200: + let ran_key = get_key(4) + //write to redis + redis_cli.hmset("surl;"+ran_key, "url", msg[1], "time", Date.now()) + ws.send('surl;'+host+ran_key) + console.log(ran_key+' --> '+msg[1]) + break; + case 404: ws.send('error;site does not exist (404)');break; + case 502: ws.send('error;remote server error (502)');break; + case 500: ws.send('error;remote server error (500)');break; + case 503: ws.send('error;remote server error (503)');break; + default: ws.send('error;no valid url');break; + } + } + } + xhr.open('GET', msg[1], true); + xhr.timeout = 1000; + xhr.ontimeout = function(e) {ws.send('error;url is to slow')} + xhr.send(); + setTimeout(to => {if (xhr.readyState !== 4) ws.send('error;url timed out');xhr.abort()}, 1500) + } + } + }) + ws.send('websocket connected') +}) + +//random key +const forbidden_array = ['sym', 'admin', 'stats'] +function get_key(length) { + let forbidden = false; let output = '' + let characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' + do{ + for (let i = 0; i < length; i++ ) + output += characters.charAt(Math.floor(Math.random() * characters.length)) + for (let i = 0; i < forbidden_array.length; i++) if (output === forbidden_array[i]) forbidden = true + redis_cli.hget("surl;"+output, "url", function(err, obj) { + if(err) console.log(err) + if(obj) forbidden = true + }) + } while (forbidden) + return output +}