From 0fdbefbb86bcf959875358b732682af65eddacc2 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Fri, 16 Oct 2020 14:49:33 +0200 Subject: client & server: improved error handling & timeouts 🐛 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client.js | 27 +++++++++++++++++---------- server.js | 39 ++++++++++++++++++++++++--------------- 2 files changed, 41 insertions(+), 25 deletions(-) diff --git a/client.js b/client.js index 4614921..2748da1 100644 --- a/client.js +++ b/client.js @@ -3,6 +3,7 @@ const debug = require('debug')('gemini:client') const {parse: parseUrl} = require('url') const pem = require('pem') +const {pipeline: pipe} = require('stream') const connect = require('./connect') const createParser = require('./lib/response-parser') const { @@ -33,20 +34,26 @@ const _request = (pathOrUrl, opt, cb) => { } const res = createParser() - socket.pipe(res) - socket.once('error', (err) => { - socket.unpipe(res) - res.destroy(err) - }) - - const close = () => { - socket.destroy() - res.destroy() + pipe( + socket, + res, + (err) => { + if (err) debug('error receiving response', err) + // Control over the socket has been given to the caller + // already, so we swallow the error here. + if (!timeout) return; + if (!err) cb(new Error('socket closed while waiting for header')) + }, + ) + + const reportTimeout = () => { + socket.destroy(new Error('timeout waiting for header')) } - let timeout = setTimeout(close, 20 * 1000) + let timeout = setTimeout(reportTimeout, 20 * 1000) res.once('header', (header) => { clearTimeout(timeout) + timeout = null debug('received header', header) // prepare res diff --git a/server.js b/server.js index 4bb2cef..1dc5905 100644 --- a/server.js +++ b/server.js @@ -3,6 +3,7 @@ const debug = require('debug')('gemini:server') const {createServer: createTlsServer} = require('tls') const {EventEmitter} = require('events') +const {pipeline: pipe} = require('stream') const createParser = require('./lib/request-parser') const createResponse = require('./lib/response') const { @@ -49,20 +50,26 @@ const createGeminiServer = (opt = {}, onRequest) => { const clientCert = socket.getPeerCertificate() const req = createParser() - socket.pipe(req) - socket.once('error', (err) => { - socket.unpipe(req) - req.destroy(err) - }) + pipe( + socket, + req, + (err) => { + if (err) debug('error receiving request', err) + if (timeout && err) { + debug('socket closed while waiting for header') + } + // todo? https://nodejs.org/api/http.html#http_event_clienterror + }, + ) - const close = () => { - socket.destroy() - req.destroy() + const reportTimeout = () => { + socket.destroy(new Error('timeout waiting for header')) } - let timeout = setTimeout(close, 20 * 1000) + let timeout = setTimeout(reportTimeout, 20 * 1000) req.once('header', (header) => { clearTimeout(timeout) + timeout = null debug('received header', header) // prepare req @@ -77,14 +84,16 @@ const createGeminiServer = (opt = {}, onRequest) => { // prepare res const res = createResponse() - res.pipe(socket) - res.once('error', (err) => { - console.error('error', err) - res.unpipe(socket) - socket.destroy(err) - }) Object.defineProperty(res, 'socket', {value: socket}) + pipe( + res, + socket, + (err) => { + if (err) debug('error sending response', err) + }, + ) + onRequest(req, res) server.emit('request', req, res) }) -- cgit v1.3