nock
Advanced tools
Comparing version 13.0.0-beta.4 to 13.0.0-beta.5
@@ -16,2 +16,16 @@ 'use strict' | ||
function socketOnClose(req) { | ||
debug('socket close') | ||
if (!req.res && !req.socket._hadError) { | ||
// If we don't have a response then we know that the socket | ||
// ended prematurely and we need to emit an error on the request. | ||
req.socket._hadError = true | ||
const err = new Error('socket hang up') | ||
err.code = 'ECONNRESET' | ||
req.emit('error', err) | ||
} | ||
req.emit('close') | ||
} | ||
/** | ||
@@ -52,6 +66,11 @@ * Given a group of interceptors, appropriately route an outgoing request. | ||
this.attachToReq() | ||
// Emit a fake socket event on the next tick to mimic what would happen on a real request. | ||
// Some clients listen for a 'socket' event to be emitted before calling end(), | ||
// which causes Nock to hang. | ||
process.nextTick(() => this.connectSocket()) | ||
} | ||
attachToReq() { | ||
const { req, socket, options } = this | ||
const { req, options } = this | ||
@@ -73,14 +92,5 @@ for (const [name, val] of Object.entries(options.headers)) { | ||
// ClientRequest.connection is an alias for ClientRequest.socket | ||
// https://nodejs.org/api/http.html#http_request_socket | ||
// https://github.com/nodejs/node/blob/b0f75818f39ed4e6bd80eb7c4010c1daf5823ef7/lib/_http_client.js#L640-L641 | ||
// The same Socket is shared between the request and response to mimic native behavior. | ||
req.socket = req.connection = socket | ||
propagate(['close', 'error', 'timeout'], req.socket, req) | ||
req.write = (...args) => this.handleWrite(...args) | ||
req.end = (...args) => this.handleEnd(...args) | ||
req.flushHeaders = (...args) => this.handleFlushHeaders(...args) | ||
req.abort = (...args) => this.handleAbort(...args) | ||
@@ -94,33 +104,35 @@ // https://github.com/nock/nock/issues/256 | ||
} | ||
} | ||
// Emit a fake socket event on the next tick to mimic what would happen on a real request. | ||
// Some clients listen for a 'socket' event to be emitted before calling end(), | ||
// which causes nock to hang. | ||
process.nextTick(() => { | ||
if (req.aborted) { | ||
return | ||
} | ||
connectSocket() { | ||
const { req, socket } = this | ||
socket.connecting = false | ||
req.emit('socket', socket) | ||
// Until Node 14 is the minimum, we need to look at both flags to see if the request has been cancelled. | ||
if (req.destroyed || req.aborted) { | ||
return | ||
} | ||
// https://nodejs.org/api/net.html#net_event_connect | ||
socket.emit('connect') | ||
// ClientRequest.connection is an alias for ClientRequest.socket | ||
// https://nodejs.org/api/http.html#http_request_socket | ||
// https://github.com/nodejs/node/blob/b0f75818f39ed4e6bd80eb7c4010c1daf5823ef7/lib/_http_client.js#L640-L641 | ||
// The same Socket is shared between the request and response to mimic native behavior. | ||
req.socket = req.connection = socket | ||
// https://nodejs.org/api/tls.html#tls_event_secureconnect | ||
if (socket.authorized) { | ||
socket.emit('secureConnect') | ||
} | ||
propagate(['error', 'timeout'], socket, req) | ||
socket.on('close', () => socketOnClose(req)) | ||
if (this.readyToStartPlaybackOnSocketEvent) { | ||
this.maybeStartPlayback() | ||
} | ||
}) | ||
} | ||
socket.connecting = false | ||
req.emit('socket', socket) | ||
emitError(error) { | ||
const { req } = this | ||
process.nextTick(() => { | ||
req.emit('error', error) | ||
}) | ||
// https://nodejs.org/api/net.html#net_event_connect | ||
socket.emit('connect') | ||
// https://nodejs.org/api/tls.html#tls_event_secureconnect | ||
if (socket.authorized) { | ||
socket.emit('secureConnect') | ||
} | ||
if (this.readyToStartPlaybackOnSocketEvent) { | ||
this.maybeStartPlayback() | ||
} | ||
} | ||
@@ -131,3 +143,3 @@ | ||
handleWrite(buffer, encoding, callback) { | ||
debug('write', arguments) | ||
debug('request write') | ||
const { req } = this | ||
@@ -138,3 +150,3 @@ | ||
err.code = 'ERR_STREAM_WRITE_AFTER_END' | ||
this.emitError(err) | ||
process.nextTick(() => req.emit('error', err)) | ||
@@ -147,3 +159,3 @@ // It seems odd to return `true` here, not sure why you'd want to have | ||
if (req.aborted) { | ||
if (req.socket && req.socket.destroyed) { | ||
return false | ||
@@ -177,3 +189,3 @@ } | ||
handleEnd(chunk, encoding, callback) { | ||
debug('req.end') | ||
debug('request end') | ||
const { req } = this | ||
@@ -202,38 +214,6 @@ | ||
handleFlushHeaders() { | ||
debug('req.flushHeaders') | ||
debug('request flushHeaders') | ||
this.maybeStartPlayback() | ||
} | ||
handleAbort() { | ||
debug('req.abort') | ||
const { req, socket } = this | ||
if (req.aborted) { | ||
return | ||
} | ||
// Historically, `aborted` was undefined or a timestamp. | ||
// Node changed this behavior in v11 to always be a bool. | ||
req.aborted = true | ||
req.destroyed = true | ||
// the order of these next three steps is important to match order of events Node would emit. | ||
process.nextTick(() => req.emit('abort')) | ||
if (!socket.connecting) { | ||
if (!req.res) { | ||
// If we don't have a response then we know that the socket | ||
// ended prematurely and we need to emit an error on the request. | ||
// Node doesn't actually emit this during the `abort` method. | ||
// Instead it listens to the socket's `end` and `close` events, however, | ||
// Nock's socket doesn't have all the complexities and events to | ||
// replicated that directly. This is an easy way to achieve the same behavior. | ||
const connResetError = new Error('socket hang up') | ||
connResetError.code = 'ECONNRESET' | ||
this.emitError(connResetError) | ||
} | ||
socket.destroy() | ||
} | ||
} | ||
/** | ||
@@ -280,3 +260,4 @@ * Set request headers of the given request. This is needed both during the | ||
if (!req.aborted && !playbackStarted) { | ||
// Until Node 14 is the minimum, we need to look at both flags to see if the request has been cancelled. | ||
if (!req.destroyed && !req.aborted && !playbackStarted) { | ||
this.startPlayback() | ||
@@ -358,10 +339,7 @@ } | ||
} else { | ||
const err = new Error( | ||
`Nock: No match for request ${common.stringifyRequest( | ||
options, | ||
requestBodyString | ||
)}` | ||
) | ||
const reqStr = common.stringifyRequest(options, requestBodyString) | ||
const err = new Error(`Nock: No match for request ${reqStr}`) | ||
err.code = 'ERR_NOCK_NO_MATCH' | ||
err.statusCode = err.status = 404 | ||
this.emitError(err) | ||
req.destroy(err) | ||
} | ||
@@ -368,0 +346,0 @@ } |
@@ -126,8 +126,2 @@ 'use strict' | ||
function emitError(error) { | ||
process.nextTick(() => { | ||
req.emit('error', error) | ||
}) | ||
} | ||
function start() { | ||
@@ -149,3 +143,3 @@ interceptor.markConsumed() | ||
const delay = interceptor.delayBodyInMs + interceptor.delayConnectionInMs | ||
common.setTimeout(emitError, delay, error) | ||
common.setTimeout(() => req.destroy(error), delay) | ||
return | ||
@@ -175,3 +169,3 @@ } | ||
.then(continueWithResponseBody) | ||
.catch(emitError) | ||
.catch(err => req.destroy(err)) | ||
return | ||
@@ -190,3 +184,3 @@ } | ||
.then(continueWithFullResponse) | ||
.catch(emitError) | ||
.catch(err => req.destroy(err)) | ||
return | ||
@@ -241,3 +235,3 @@ } | ||
} catch (err) { | ||
emitError(err) | ||
req.destroy(err) | ||
return | ||
@@ -244,0 +238,0 @@ } |
@@ -23,2 +23,5 @@ 'use strict' | ||
// Undocumented flag used by ClientRequest to ensure errors aren't double-fired | ||
this._hadError = false | ||
// Maximum allowed delay. 0 means unlimited. | ||
@@ -87,2 +90,3 @@ this.timeout = 0 | ||
debug('socket destroy') | ||
this.destroyed = true | ||
@@ -93,2 +97,3 @@ this.readable = this.writable = false | ||
if (err) { | ||
this._hadError = true | ||
this.emit('error', err) | ||
@@ -95,0 +100,0 @@ } |
@@ -10,3 +10,3 @@ { | ||
], | ||
"version": "13.0.0-beta.4", | ||
"version": "13.0.0-beta.5", | ||
"author": "Pedro Teixeira <pedro.teixeira@gmail.com>", | ||
@@ -13,0 +13,0 @@ "repository": { |
@@ -47,2 +47,6 @@ # Nock | ||
- [Delay the response](#delay-the-response) | ||
- [Delay the connection](#delay-the-connection) | ||
- [Technical Details](#technical-details) | ||
- [Delay the response body](#delay-the-response-body) | ||
- [Technical Details](#technical-details-1) | ||
- [Chaining](#chaining) | ||
@@ -49,0 +53,0 @@ - [Scope filtering](#scope-filtering) |
1592
172593
3381