Last active
June 8, 2022 22:57
-
-
Save tommelo/1af033ca38b442359fb006d8813f14bb to your computer and use it in GitHub Desktop.
Promisify net.Socket
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const { connect } = require('./socket'); | |
async function asyncSocket() { | |
let socket; | |
try { | |
socket = await connect('localhost', 3000, 30); | |
await socket.write('ping'); | |
const message = await socket.recv(); | |
console.log(message); // "pong" | |
} catch(e) { | |
console.error(e); | |
} finally { | |
if(socket) socket.close(); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const { EOL } = require('os'); | |
const { createConnection } = require('net'); | |
const SocketError = { | |
IDLE_TIMEOUT: 'IDLE_TIMEOUT', | |
TRANSMISSION_ERROR: 'TRANSMISSION_ERROR', | |
CONNECTION_CLOSED: 'CONNECTION_CLOSED' | |
}; | |
/** | |
* Creates a function to handle the socket 'close' event. | |
* Receiving a close event while reading or writing | |
* to the remote server could be a transmission error | |
* or the remote server terminating the connection. | |
* | |
* @param {Function} reject the reject handler | |
* @returns {Function} callback the calback function | |
*/ | |
const onCloseEvent = reject => error => reject( | |
error | |
? { code: SocketError.TRANSMISSION_ERROR } | |
: { code: SocketError.CONNECTION_CLOSED } | |
); | |
/** | |
* Creates a function to handle idle timeouts. | |
* | |
* @param {Function} reject the reject handler | |
* @returns {Function} callback the calback function | |
*/ | |
const onTimeoutEvent = reject => () => reject({ code: SocketError.IDLE_TIMEOUT }); | |
/** | |
* Creates a function to handle write results. | |
* | |
* @param {Function} reject handler function for write failures | |
* @returns {Function} callback the calback function | |
*/ | |
const onWriteEvent = (resolve, reject) => error => error ? reject(error) : resolve(); | |
/** | |
* Creates a function to handle 'data' events. | |
* The promise will be fulfilled(resolve method) whenever | |
* the remote host send a end of line. | |
* | |
* @param {Function} resolve handler function for successful reads | |
* @returns {Function} callback the calback function | |
*/ | |
const onDataEvent = resolve => { | |
const messages = []; | |
return data => { | |
const message = data.toString(); | |
messages.push(message); | |
if (EOL === message.slice(-1)) | |
resolve(messages.join("")); | |
} | |
} | |
/** | |
* Creates a socket connection with the given remote host. | |
* The function creates a net.Socket wrapper object that | |
* enables using async/await on read/write operations instead | |
* of listening for data or error events. | |
* | |
* @param {string} host The remote host | |
* @param {number} port Port number | |
* @param {number} timeout Idle timeout in seconds | |
* @returns {Promise} promise A promise object that resolves into a net.Socket | |
* wrapper with only read, write and close operations when | |
* the promise is fulfilled. | |
* In case of connection errors, the promise will be rejected. | |
*/ | |
function connect(host, port, timeout) { | |
const connection = createConnection({ host, port, timeout: timeout * 1000 }); | |
/** | |
* net.Socket wrapper containing only | |
* write, read and close functions. | |
*/ | |
const socket = Object.freeze({ | |
/** | |
* Writes to the remote host. | |
* | |
* @param {string} message message to be sent | |
* @returns {Promise} promise promise object | |
*/ | |
write: message => new Promise((resolve, reject) => { | |
connection.removeAllListeners('data'); | |
connection.once('close', onCloseEvent(reject)); | |
connection.write(message, onWriteEvent(resolve, reject)); | |
}), | |
/** | |
* Reads from the remote host. | |
* | |
* @returns {Promise} promise promise object | |
*/ | |
recv: () => new Promise((resolve, reject) => { | |
connection.once('timeout', onTimeoutEvent(reject)); | |
connection.once('close', onCloseEvent(reject)); | |
connection.on('data', onDataEvent(resolve)); | |
}), | |
/** | |
* Closes the socket connection | |
*/ | |
close: () => connection.destroy() | |
}); | |
return new Promise((resolve, reject) => { | |
connection.once('error', e => reject(e)); | |
connection.once('ready', () => resolve(socket)); | |
}); | |
} | |
module.exports = { connect }; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment