basic handshake handling

This commit is contained in:
hippoz 2021-01-26 12:04:53 +02:00
parent ac6cdf7bc9
commit 641a978cbe
Signed by: hippoz
GPG key ID: 7C52899193467641
7 changed files with 139 additions and 0 deletions

28
example/example.html Normal file
View file

@ -0,0 +1,28 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Wormhole test</title>
<script>
// Create WebSocket connection.
const socket = new WebSocket('ws://localhost:8080/hello');
// Connection opened
socket.addEventListener('open', function (event) {
socket.send('Hello Server!');
});
socket.addEventListener('error', console.error)
// Listen for messages
socket.addEventListener('message', function (event) {
console.log('Message from server ', event.data);
});
</script>
</head>
<body>
</body>
</html>

3
example/example.js Normal file
View file

@ -0,0 +1,3 @@
const Wormhole = require('../index');
const wormhole = new Wormhole({ urls: [ '/hello' ] });

View file

@ -0,0 +1,58 @@
const http = require('http');
const { createLog } = require('./lib/logger');
const handshake = require('./lib/handshake');
const constants = require('./lib/constants');
const Socket = require('./lib/Socket');
const handshakeLog = createLog([ 'Wormhole', 'Handshake' ]);
class Wormhole {
constructor({ urls=[ '/bruh' ], port=8080 }) {
this._urls = urls;
this._port = port;
this._httpServer = http.createServer((req, res) => {
if (req.method === 'GET' && req.url && this._urls.includes(req.url)) {
handshakeLog(`Got connection request to ${req.url} on port ${this._port}`);
const failConnection = (status=400) => {
res.writeHead(status);
res.end();
console.trace();
};
const websocketKey = req.headers['sec-websocket-key'];
const upgradeHeader = req.headers['upgrade'];
const websocketVersion = req.headers['sec-websocket-version'];
if (upgradeHeader !== constants.upgradeHeaderRequirement) return failConnection();
if (websocketVersion !== constants.websocketVersionRequirement) return failConnection();
const websocketAccept = handshake.generateWebsocketAcceptValue(websocketKey);
handshakeLog(websocketKey, websocketAccept);
if (websocketAccept) {
res.writeHead(101, {
'Upgrade': 'websocket',
'Connection': 'Upgrade',
'Sec-WebSocket-Accept': websocketAccept
});
res.end();
const socket = new Socket({ socket: res.socket, initalState: 'CONNECTED' });
return true;
}
return failConnection();
}
});
this._httpServer.listen(port);
}
}
module.exports = Wormhole;

12
lib/Socket.js Normal file
View file

@ -0,0 +1,12 @@
class Socket {
constructor({ initialState='CONNECTING', socket }) {
this._state = initialState;
this._socket = socket;
this._socket.on('data', (e) => {
console.log(e.toString());
});
}
}
module.exports = Socket;

5
lib/constants.js Normal file
View file

@ -0,0 +1,5 @@
module.exports = {
handshakeGUID: '258EAFA5-E914-47DA-95CA-C5AB0DC85B11',
upgradeHeaderRequirement: 'websocket',
websocketVersionRequirement: '13'
}

17
lib/handshake.js Normal file
View file

@ -0,0 +1,17 @@
const crypto = require('crypto');
const { handshakeGUID } = require('./constants');
const generateWebsocketAcceptValue = (websocketKey) => {
if (typeof websocketKey !== 'string' || typeof handshakeGUID !== 'string') {
// TODO: maybe throw error?
return;
}
const concatenated = websocketKey + handshakeGUID;
const sha1HashedInBase64 = crypto.createHash('sha1').update(concatenated, 'binary').digest('base64');
return sha1HashedInBase64;
};
module.exports = { generateWebsocketAcceptValue };

16
lib/logger.js Normal file
View file

@ -0,0 +1,16 @@
const logDebug = true;
module.exports = {
createLog: (components) => (...args) => {
let compString = '';
for (const i in components) {
const component = components[i];
if (i >= (components.length - 1)) {
compString += `[${component}]`;
} else {
compString += `[${component}] `;
}
}
logDebug && console.log(compString, ...args);
}
};