/* //http-file-upload:d 26-05-24 generate key/certificate ... https://javascript-2020.github.io/generate-https-certificate.html */ var port = 3000; var userdir = ''; var chunksize = 1024*1024; var cwd = ''; var mode = 'https'; var https = require('https'); var http = require('http'); var fs = require('fs'); var fsp = fs.promises; var path = require('path'); var dns = require('dns'); var os = require('os'); var program = { name : 'http-file-upload', version : 'v2.0' }; var ws = wsmod(); var key; var cert; var cacert; var server; var txt = {}; setup(); cwd = cwd||process.cwd(); cmdline.list = [ {id:['p','port'] , fn : cmdline.port}, {id:['d'] , fn : cmdline.dir}, {id:['cwd'] , fn : cmdline.cwd}, {id:['http','https'] , fn : cmdline.mode}, {id:['v','version'] , fn : cmdline.version}, {id:['cert'] , fn : cmdline.cert}, {id:['h','help'] , fn : cmdline.help} ]; function cmdline(){ var ni = process.argv.length; for(var i=0;i{ request.notfound(req,res); return; }); res.setHeader('Content-Description','File Transfer'); res.setHeader('Content-Type','application/octet-stream'); res.setHeader('Content-Disposition',`attachment; filename=${filename}`); fd.pipe(res); }//download request.download.list=async function(req,res){ try{ var list = await fsp.readdir(cwd+userdir,{withFileTypes:true}); } catch(err){ var result = false; var msg = err.toString(); } if(result===false){ res.end(JSON.stringify({error:msg})); return; } var files = []; list.forEach(file=>file.isFile() && files.push(file.name)); res.end(JSON.stringify({files})); }//download.list request.upload=function(req,res){ var filename = req.url.slice(8); if(!filename){ res.writeHead(500); res.end('no filename'); return; } var fd = fs.createWriteStream(filename); fd.on('error',()=>{ res.writeHead(500); res.end('create write stream error : '+filename); return; }); req.pipe(fd); res.writeHead(200); res.end('ok'); }//upload //: function upgrade(req,socket,head){ var con = ws.upgrade.server(req,socket,onmsg); function onmsg(payload,type,con){ var str = payload.toString(); var json = JSON.parse(str); switch(json.type){ case 'upload' : upload(con,json); break; case 'download' : download(con,json); break; case 'download-list' : download.list(con,json); break; }//switch }//onmsg }//upgrade //: function upload(con,json){ con.onrec = rec; var fh; var name; var num; var ct; upload(json); async function upload(json){ ({name,num} = json); var result = chk(con,name); if(result===false)return; ct = 0; console.log('upload',name,num); fh = await open(con,name,'w'); if(fh===false)return; con.send.json({type:'open'}); }//upload async function abort(){ console.log('abort',name); await fh.close(); del(con,name); }//abort async function binary(buffer){ ct++; console.log(name,ct,'/',num); var result = await write(con,fh,buffer); if(!result)return; if(ct===num){ fh.close(); } con.send.json({type:'progress'}); }//binary function rec(payload,type,con){ switch(type){ case 'text' : var str = payload.toString(); var json = JSON.parse(str); switch(json.type){ case 'upload' : upload(json); break; case 'abort' : abort(); break; }//switch break; case 'binary' : binary(payload); break; }//switch }//rec }//upload //: function download(con,json){ console.log('download'); con.onrec = rec; var buffer = Buffer.alloc(chunksize); var fn = {}; start(json); async function start(json){ fn.send = send; fn.abort = abort; var file = json.file; var result = chk(con,file); if(result===false)return; console.log('start',file); var size = await filesize(con,file); if(size===false)return; var num = Math.ceil(size/chunksize); console.log(file,num); var ct = 0; var flag = false; con.send.json({type:'start',size,num}); var fh = await open(con,file,'r'); if(fh===false)return; send(); async function send(){ ct++; console.log(file,ct,'/',num); var buffer = await read(con,fh); if(!buffer)return; con.send.binary(buffer); if(ct===num){ fh.close(); } }//send function abort(){ console.log('abort'); flag = true; fh.close(); }//abort }//start function rec(payload,type,con){ var str = payload.toString(); var json = JSON.parse(str); switch(json.type){ case 'download' : start(json); break; case 'progress' : fn.send(); break; case 'abort' : fn.abort(); break; }//switch }//rec }//download download.list=async function(con,json){ console.log('list'); try{ var list = await fsp.readdir(cwd+userdir,{withFileTypes:true}); } catch(err){ var result = false; var msg = err.toString(); } if(result===false){ con.send.json({type:'error',msg}); return; } var files = []; list.forEach(file=>{if(file.isFile())files.push(file.name)}); con.send.json({type:'list',files}); }//list //: function chk(con,file){ if(chk.file(file)===false){ con.send.json({type:'error',msg:'invalid filename'}); return false; } }//chk chk.file=function(file){ var p1 = path.resolve(userdir); var p2 = path.resolve(userdir,file).substring(0,p1.length); if(p1!==p2){ return false; } return true; }//chk.file async function open(con,file,mode){ try{ var fh = await fsp.open(userdir+file,mode); } catch(err){ console.log(err); var result = false; var msg = err.toString(); } if(result===false){ con.send.json({type:'error',msg}); return false; } return fh; }//open async function del(con,file){ try{ await fsp.unlink(userdir+file); } catch(err){ console.log(err); var result = false; var msg = err.toString(); } }//del async function write(con,fh,buffer){ try{ await fh.write(buffer); } catch(err){ console.log(err); var result = false; var msg = err.toString(); } if(result===false){ con.send.json({type:'error',msg}); return false; } return true; }//write async function filesize(con,file){ try{ var stat = await fsp.stat(userdir+file); } catch(err){ console.log(err); var result = false; var msg = err.toString(); } if(result===false){ con.send.json({type:'error',msg}); return false; } var size = stat.size; return size; }//filesize async function read(con,fh){ var buffer = Buffer.alloc(chunksize); try{ var {bytesRead} = await fh.read(buffer,{length:chunksize}); } catch(err){ console.log(err); var result = false; var msg = err.toString(); } if(result===false){ con.send.json({type:'error',msg}); return; } if(bytesRead!==chunksize){ var buf2 = Buffer.alloc(bytesRead); buffer.copy(buf2,0,0,bytesRead); buffer = buf2; } return buffer; }//read //: function listen(err,ip){ server.listen(port); console.log(' ','serving directory : ',cwd); console.log(' ','server listening : ',mode+' ,','all interfaces , port '+port); console.log(); var nics = require('os').networkInterfaces(); for(var key in nics){ nics[key].forEach(o=>{ if(o.family==='IPv4'){ console.log(' ',o.address); } }); }//for console.log(); //console.log('local ip : '+ip); //console.log(); }//listen function listening(){ console.log(' ',`${mode}://localhost:${port}/`); console.log(); console.log(' ','feedback can be provided :'); console.log(' ','https://chat.stackoverflow.com/rooms/17/javascript','','user:','@matt'); console.log(' ','email : javascript.12.03.2024@gmail.com'); console.log(); }//listening function title(title){ if(process.platform==='win32'){ process.title = title; }else{ process.stdout.write('\x1b]2;'+title+'\x1b\x5c'); } }//title function createdir(){ if(!userdir)return true; var stat = fs.statSync(userdir,{throwIfNoEntry:false}); if(!stat){ try{ fs.mkdirSync(userdir); } catch(err2){ err = err2; } if(err){ console.log('unable to create upload directory'); console.log(cwd+userdir); console.error(err); } return; } if(!stat.isDirectory()){ console.error('invalid upload directory'); console.log(cwd+userdir); return; } return true; }//createdir function loadcert(index){ var files = {}; var filename = process.argv[index+1]; read(filename); var filename = process.argv[index+2]; read(filename); if(!files.cert){ console.log('-cert certificate not found'); process.exit(); } if(!files.key){ console.log('-cert key not found'); process.exit(); } cert = files.cert; key = files.key; write(); function read(filename){ if(!filename){ console.log('-cert requires two files, pem encoded key and certificate'); process.exit(); } try{ var txt = fs.readFileSync(filename,'utf8'); } catch(err){ console.log('-cert requires two files, pem encoded key and certificate'); console.error(err); process.exit(); } var f = false; if(iscert(txt)){ f = true; files.cert = txt; } if(iskey(txt)){ f = true; files.key = txt; } if(!f){ console.error(filename,'not recognised as pem encoded key or certificate'); process.exit(); } }//read function write(){ var txt = fs.readFileSync(__filename,'utf8'); var index = txt.indexOf('//certificate:'); var i1; var i2; i1 = txt.indexOf('key =',index); i1 = txt.indexOf('`',i1); i2 = txt.indexOf('`',i1+1); txt = txt.slice(0,i1+1)+'\n'+key+'\n'+txt.slice(i2); i1 = txt.indexOf('cert =',index); i1 = txt.indexOf('`',i1); i2 = txt.indexOf('`',i1+1); txt = txt.slice(0,i1+1)+'\n'+cert+'\n'+txt.slice(i2); fs.writeFileSync(__filename,txt); }//write }//loadcert function iskey(key){ if(key.indexOf('-----BEGIN RSA PRIVATE KEY-----')!=-1){ return true; } return false; }//iskey function iscert(cert){ if(key.indexOf('-----BEGIN CERTIFICATE-----')!=-1){ //if(key.indexOf('Signature Algorithm:')!=-1){ return true; //} } return false; }//iscert function help(){ console.log(); console.log(program.name,program.version); console.log(); console.log('-p','','set the port that http-file-upload listens on'); console.log('-cwd','','set the working directory for http-file-upload'); console.log('-d','','set the upload/download directory, relative to cwd'); console.log('https|http','set whether http-file-upload uses http or https, default https'); console.log('-v','display current version'); console.log('-cert',' ','update the key/certificate used by the server'); console.log('-h','display this help dialogue'); console.log(); }//help //: //txt.hello: txt.hello=` http-file-upload

http-file-upload v2.0

drag files here or or


`; //txt.worker: txt.worker=` var socket; var onopen; self.onmessage=function(event){ var json = event.data; switch(json.type){ case 'init' : init(json); break; case 'upload' : upload(json); break; case 'download' : download(json); break; case 'download-list' : download.list(json); break; }//switch }//onmessage function init(json){ var url = json.url; socket = new WebSocket(url); socket.onerror = console.error; socket.onmessage = event=>self.postMessage(event.data); socket.onopen = ()=>{ if(onopen){ onopen(); }else{ onopen = true; } }; }//init function onready(json){ var fn = ()=>socket.send(JSON.stringify(json)); if(onopen){ fn(); }else{ onopen = fn; } }//onready //: function upload(json){ onready(json); self.onmessage = event=>{ var data = event.data; var type = datatype(data); switch(type){ case 'object' : socket.send(JSON.stringify(event.data)); break; case 'blob' : socket.send(data); break; }//switch }; }//upload //: function download(json){ onready(json); self.onmessage = event=>socket.send(JSON.stringify(event.data)); }//download download.list=function(json){ onready(json); }//list //: function datatype(value){ var str = Object.prototype.toString.call(value); var i = str.indexOf(' '); var type = str.slice(i+1,-1); type = type.toLowerCase(); return type; }//datatype `; /* wsmod:d 03-09-22 31-10-22 - multiple connections */ module.exports = wsmod; function wsmod(){ var obj = {}; var crypto = require('crypto'); var http = require('http'); var https = require('https'); const two16 = Math.pow(2,16); const two64 = Math.pow(2,64); var op = {}; var frame = {}; obj.frame = frame; var rd = {}; var wt = {}; var pos = {}; var mask = {}; var extlen = {}; var bit = {}; var display = {}; //: obj.client=function(url,onrec,onerror,onclose,callback){ //d if(!callback){ var promise = new Promise(res=>callback=res); } handshake.client(url,complete); return promise; function complete(socket,head){ if(socket===null){ callback(null); return; } var con = upgrade(socket,onrec,onerror,onclose); rec(con,head); callback(con); }//complete }//client //: obj.upgrade=function(socket,onrec,onerror,onclose){return upgrade(socket,onrec,onerror,onclose)} //d function upgrade(socket,onrec,onerror,onclose){ var con = connection(socket,onrec,onerror,onclose); socket.on('data',data=>rec(con,data)); socket.on('error',err=>{ console.log('error'); console.error(err); if(typeof onerror==='function'){ onerror(err); } }); socket.on('close',()=>{ list.remove(con); console.log('closed'); if(typeof onclose==='function'){ onclose(); } }); return con; }//upgrade obj.upgrade.server=function(req,socket,onrec,onerror,onclose){return upgrade.server(req,socket,onrec,onerror,onclose)} //d upgrade.server=function(req,socket,onrec,onerror,onclose){ console.log('upgrade'); if(req.headers['upgrade'].toLowerCase()!=='websocket'){ console.log(req.url,'bad request',req.headers['upgrade']); socket.end('HTTP/1.1 400 Bad Request'); return; } handshake(req,socket); var con = upgrade(socket,onrec,onerror,onclose); return con; }//upgrade.server obj.upgrade.deny=function(socket){return upgrade.deny(socket)} //d upgrade.deny=function(socket){ socket.end( 'HTTP/1.1 401 Web Socket Protocol Handshake\r\n' + 'Upgrade: WebSocket\r\n' + 'Connection: Upgrade\r\n' + '\r\n' ); socket.destroy(); }//deny //: function connection(socket,onrec,onerror,onclose){ //c var con = {}; list.push(con); con.onrec = onrec; con.onerror = onerror; con.onclose = onclose; con.websocket = socket; con.buffer = Buffer.alloc(0); con.send = {}; var payload = { buffer : null, type : null, add : function(frame){ var fragment = rd.payload(frame); payload.buffer = Buffer.concat([payload.buffer,fragment]); var fin = rd.fin(frame); if(fin){ rec(payload.buffer,payload.type,con); payload.reset(); } }, reset : function(){ payload.buffer = Buffer.alloc(0); payload.type = null; } }; con.payload = payload; payload.reset(); //: function rec(payload,type,con){ call('rec',payload,type,con); }//rec //: con.send.frame=function(buffer){ var r = send(con,buffer); return r; }//send con.send.text=function(str){ var r = send.text(con,str); return r; }//send.text con.send.binary=function(buffer){ var r = send.binary(con,buffer); return r; }//binary con.send.ping=function(){ var r = send.ping(con); return r; }//send.ping con.send.json=function(json){ var txt = JSON.stringify(json); var r = send.text(con,txt); return r; }//send.json //: con.close=function(){ close(con); }//close //: socket.on('error',err=>{ call('error',err,con); }); socket.on('close',()=>{ call('close',con); }); //: var fn = {}; fn.rec = []; fn.error = []; fn.close = []; con.on=function(name,userfn){ if(typeof userfn!=='function'){ return; } var list = fn[name]; if(!list){ return; } list.push(userfn); }//on con.remove=function(name,userfn){ var list = fn[name]; if(!list){ return; } var n = list.length; for(var i=0;iuserfn.apply(null,args)); function callfn(fn){ if(typeof fn!=='function'){ return; } fn.apply(null,args); }//callfn }//call return con; }//connection //: var list = []; obj.list = list; list.remove=function(con){ var n = list.length; for(var i=0;i=0;i--){ var con = list[i]; close(con); }//for }//closeall //: function handshake(req,socket){ var headers = [ 'HTTP/1.1 101 Web Socket Protocol Handshake', 'Upgrade: WebSocket', 'Connection: Upgrade' ]; var key = req.headers['sec-websocket-key']; if(key){ var hash = genhash(key) headers.push('Sec-WebSocket-Accept: '+hash); } headers = headers.join('\r\n'); headers += '\r\n\r\n'; socket.write(headers); function genhash(key){ var fn = crypto.createHash('sha1'); var data = fn.update(key+'258EAFA5-E914-47DA-95CA-C5AB0DC85B11','binary'); var b64 = data.digest('base64'); return b64; }//genhash }//handshake handshake.client=function(url,callback){ if(!callback){ var promise = new Promise((res,rej)=>callback=res); } url = new URL(url); var secure = (url.protocol==='wss:'); var hostname = url.hostname; var port = url.port; var path = url.pathname+url.search; var num = Buffer.alloc(16); for(var i=0;i<16;i++){ num[i] = Math.round(Math.random()*255); }//for var b64 = num.toString('base64'); var hdrs = { 'Upgrade' : 'websocket', 'Connection' : 'Upgrade', 'Sec-WebSocket-Version' : 13, 'Sec-WebSocket-Key' : b64, 'Host' : hostname+':'+port } var opts = { hostname : hostname, port : port, method : 'GET', path : path, headers : hdrs, rejectUnauthorized : false } var req = (secure ? https : http).request(opts); req.on('error',err=>console.log(err)); req.on('upgrade',upgrade); req.on('response',response); req.end(); return promise; function upgrade(response,socket,head){ if(validate(response)===false){ callback(null); return; } console.log('upgrade ok'); callback(socket,head); }//upgrade function response(response){ console.log('upgrade failed, response'); response.socket.end(); }//response function validate(response){ if(response.headers.upgrade.toLowerCase()!=='websocket'){ console.log('validate upgrade failed - response.headers.upgrade : '+response.headers.upgrade); response.socket.end(); return false; } var sha1 = crypto.createHash('sha1'); sha1.update(b64+'258EAFA5-E914-47DA-95CA-C5AB0DC85B11'); var expectedKey = sha1.digest('base64'); if(response.headers['sec-websocket-accept']!==expectedKey){ console.log('validate upgrade failed - invalid key : '+response.headers['sec-websocket-accept']); response.socket.end(); return false; } }//validateHandshake }//handshake.client function rec(con,buf){ if(buf.length===0)return; con.buffer = Buffer.concat([con.buffer,buf]); while(frame.available(con.buffer)){ var buf = frame.rd(con.buffer); con.buffer = frame.remove(con.buffer); var opcode = rd.opcode(buf); switch(opcode){ case 0 : op.continuation(con,buf); break; //continuation case 1 : op.text(con,buf); break; //text case 2 : op.binary(con,buf); break; //binary case 8 : op.close(con); break; //close case 9 : op.ping(con); break; //ping case 10 : op.pong(con); break; //pong }//switch }//while }//rec function close(con){ send.close(con); con.websocket.destroy(); list.remove(con); }//close //:- op.continuation=function(con,frame){ if(con.payload.type===null){ close(con); return; } con.payload.add(frame); }//continuation op.text=function(con,frame){ if(con.payload.type!==null){ close(con); return; } con.payload.type = 'text'; con.payload.add(frame); }//text op.binary=function(con,frame){ if(con.payload.type!==null){ close(con); return; } con.payload.type = 'binary'; con.payload.add(frame); }//binary op.close=function(con,frame){ close(con); }//close op.ping=function(con){ console.log('ping received'); var buf = frame.create.pong(); send(con,buf); }//ping op.pong=function(){ console.log('pong received'); }//pong //: function send(con,buffer){ con.websocket.write(buffer); }//send send.text=function(con,txt){ //console.log('send : '+txt); var payload = Buffer.from(txt,'utf8'); var buffer = frame.create(1,1,false,payload); //display.buffer(buffer); send(con,buffer); }//text send.binary=function(con,payload){ var buffer = frame.create(1,2,false,payload); send(con,buffer); }//binary send.ping=function(con){ var buf = frame.create.ping(); send(con,buf); }//ping send.close=function(con){ var buf = frame.create.close(); send(con,buf); }//close //: frame.rd=function(buffer){ var len = pos.endframe(buffer); var frame = Buffer.alloc(len); buffer.copy(frame,0,0,len); return frame; }//rd frame.create=function(fin,opcode,ismasked,payload){ payload = payload||Buffer.alloc(0); var size = frame.size(payload,ismasked); var buffer = Buffer.alloc(size); wt.fin(buffer,fin); wt.opcode(buffer,opcode); wt.ismasked(buffer,ismasked); if(ismasked){ var buf = mask.create(); wt.mask(buffer,buf); mask.mask(payload,buf); } wt.paylen(buffer,payload.length); wt.payload(buffer,payload); return buffer; }//create frame.remove=function(buffer){ //console.log('removeframe'); var len = pos.endframe(buffer); var num = buffer.length-len; var buf = Buffer.alloc(num); buffer.copy(buf,0,len); return buf; }//remove frame.size=function(payload,ismasked){ var len = payload.length; var ext = extlen.len(len); var mask = ismasked?4:0; var size = 2+ext+mask+len; return size; }//size frame.available=function(buffer){ if(buffer.length<2){ return false; } var ismasked = rd.ismasked(buffer); var payloadlength = rd.payloadlength(buffer); var hdr = 2; var ext = extlen.paylen(payloadlength); var hdrlen = hdr+ext; if(buffer.length>n) & 1} //: display.buffer=function(buffer,str){ str = str||'buffer'; console.log(); console.log(str+' : '+buffer.length); console.log(); for(var i=0;i