basic handshake handling
This commit is contained in:
parent
ac6cdf7bc9
commit
641a978cbe
7 changed files with 139 additions and 0 deletions
28
example/example.html
Normal file
28
example/example.html
Normal 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
3
example/example.js
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
const Wormhole = require('../index');
|
||||||
|
|
||||||
|
const wormhole = new Wormhole({ urls: [ '/hello' ] });
|
58
index.js
58
index.js
|
@ -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
12
lib/Socket.js
Normal 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
5
lib/constants.js
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
module.exports = {
|
||||||
|
handshakeGUID: '258EAFA5-E914-47DA-95CA-C5AB0DC85B11',
|
||||||
|
upgradeHeaderRequirement: 'websocket',
|
||||||
|
websocketVersionRequirement: '13'
|
||||||
|
}
|
17
lib/handshake.js
Normal file
17
lib/handshake.js
Normal 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
16
lib/logger.js
Normal 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);
|
||||||
|
}
|
||||||
|
};
|
Loading…
Reference in a new issue