Update checked-in dependencies
This commit is contained in:
parent
35f1961385
commit
17223bdff7
20 changed files with 1756 additions and 646 deletions
229
node_modules/nock/lib/intercepted_request_router.js
generated
vendored
229
node_modules/nock/lib/intercepted_request_router.js
generated
vendored
|
|
@ -13,6 +13,20 @@ const globalEmitter = require('./global_emitter')
|
|||
const Socket = require('./socket')
|
||||
const { playbackInterceptor } = require('./playback_interceptor')
|
||||
|
||||
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')
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a group of interceptors, appropriately route an outgoing request.
|
||||
* Identify which interceptor ought to respond, if any, then delegate to
|
||||
|
|
@ -39,16 +53,24 @@ class InterceptedRequestRouter {
|
|||
}
|
||||
|
||||
this.response = new IncomingMessage(this.socket)
|
||||
this.playbackStarted = false
|
||||
this.requestBodyBuffers = []
|
||||
this.playbackStarted = false
|
||||
|
||||
// For parity with Node, it's important the socket event is emitted before we begin playback.
|
||||
// This flag is set when playback is triggered if we haven't yet gotten the
|
||||
// socket event to indicate that playback should start as soon as it comes in.
|
||||
this.readyToStartPlaybackOnSocketEvent = false
|
||||
|
||||
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, response, socket, options } = this
|
||||
|
||||
response.req = req
|
||||
const { req, options } = this
|
||||
|
||||
for (const [name, val] of Object.entries(options.headers)) {
|
||||
req.setHeader(name.toLowerCase(), val)
|
||||
|
|
@ -65,18 +87,9 @@ class InterceptedRequestRouter {
|
|||
req.path = options.path
|
||||
req.method = options.method
|
||||
|
||||
// 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(['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)
|
||||
|
||||
// https://github.com/nock/nock/issues/256
|
||||
if (options.headers.expect === '100-continue') {
|
||||
|
|
@ -85,53 +98,79 @@ class InterceptedRequestRouter {
|
|||
req.emit('continue')
|
||||
})
|
||||
}
|
||||
|
||||
// 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(() => {
|
||||
req.emit('socket', socket)
|
||||
|
||||
// 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')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
emitError(error) {
|
||||
const { req } = this
|
||||
process.nextTick(() => {
|
||||
req.emit('error', error)
|
||||
})
|
||||
}
|
||||
connectSocket() {
|
||||
const { req, socket } = this
|
||||
|
||||
handleWrite(buffer, encoding, callback) {
|
||||
debug('write', arguments)
|
||||
const { req } = this
|
||||
|
||||
if (!req.aborted) {
|
||||
if (buffer) {
|
||||
if (!Buffer.isBuffer(buffer)) {
|
||||
buffer = Buffer.from(buffer, encoding)
|
||||
}
|
||||
this.requestBodyBuffers.push(buffer)
|
||||
}
|
||||
// can't use instanceof Function because some test runners
|
||||
// run tests in vm.runInNewContext where Function is not same
|
||||
// as that in the current context
|
||||
// https://github.com/nock/nock/pull/1754#issuecomment-571531407
|
||||
if (typeof callback === 'function') {
|
||||
callback()
|
||||
}
|
||||
} else {
|
||||
this.emitError(new Error('Request aborted'))
|
||||
if (common.isRequestDestroyed(req)) {
|
||||
return
|
||||
}
|
||||
|
||||
common.setImmediate(function() {
|
||||
// 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(['error', 'timeout'], socket, req)
|
||||
socket.on('close', () => socketOnClose(req))
|
||||
|
||||
socket.connecting = false
|
||||
req.emit('socket', socket)
|
||||
|
||||
// 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()
|
||||
}
|
||||
}
|
||||
|
||||
// from docs: When write function is called with empty string or buffer, it does nothing and waits for more input.
|
||||
// However, actually implementation checks the state of finished and aborted before checking if the first arg is empty.
|
||||
handleWrite(buffer, encoding, callback) {
|
||||
debug('request write')
|
||||
const { req } = this
|
||||
|
||||
if (req.finished) {
|
||||
const err = new Error('write after end')
|
||||
err.code = 'ERR_STREAM_WRITE_AFTER_END'
|
||||
process.nextTick(() => req.emit('error', err))
|
||||
|
||||
// It seems odd to return `true` here, not sure why you'd want to have
|
||||
// the stream potentially written to more, but it's what Node does.
|
||||
// https://github.com/nodejs/node/blob/a9270dcbeba4316b1e179b77ecb6c46af5aa8c20/lib/_http_outgoing.js#L662-L665
|
||||
return true
|
||||
}
|
||||
|
||||
if (req.socket && req.socket.destroyed) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (!buffer || buffer.length === 0) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (!Buffer.isBuffer(buffer)) {
|
||||
buffer = Buffer.from(buffer, encoding)
|
||||
}
|
||||
this.requestBodyBuffers.push(buffer)
|
||||
|
||||
// can't use instanceof Function because some test runners
|
||||
// run tests in vm.runInNewContext where Function is not same
|
||||
// as that in the current context
|
||||
// https://github.com/nock/nock/pull/1754#issuecomment-571531407
|
||||
if (typeof callback === 'function') {
|
||||
callback()
|
||||
}
|
||||
|
||||
common.setImmediate(function () {
|
||||
req.emit('drain')
|
||||
})
|
||||
|
||||
|
|
@ -139,10 +178,10 @@ class InterceptedRequestRouter {
|
|||
}
|
||||
|
||||
handleEnd(chunk, encoding, callback) {
|
||||
debug('req.end')
|
||||
debug('request end')
|
||||
const { req } = this
|
||||
|
||||
// handle the different overloaded param signatures
|
||||
// handle the different overloaded arg signatures
|
||||
if (typeof chunk === 'function') {
|
||||
callback = chunk
|
||||
chunk = null
|
||||
|
|
@ -155,51 +194,18 @@ class InterceptedRequestRouter {
|
|||
req.once('finish', callback)
|
||||
}
|
||||
|
||||
if (!req.aborted && !this.playbackStarted) {
|
||||
if (chunk) {
|
||||
req.write(chunk, encoding)
|
||||
this.startPlayback()
|
||||
}
|
||||
if (req.aborted) {
|
||||
this.emitError(new Error('Request aborted'))
|
||||
}
|
||||
req.finished = true
|
||||
this.maybeStartPlayback()
|
||||
|
||||
return req
|
||||
}
|
||||
|
||||
handleFlushHeaders() {
|
||||
debug('req.flushHeaders')
|
||||
const { req } = this
|
||||
|
||||
if (!req.aborted && !this.playbackStarted) {
|
||||
this.startPlayback()
|
||||
}
|
||||
if (req.aborted) {
|
||||
this.emitError(new Error('Request aborted'))
|
||||
}
|
||||
}
|
||||
|
||||
handleAbort() {
|
||||
debug('req.abort')
|
||||
const { req, response, socket } = this
|
||||
|
||||
if (req.aborted) {
|
||||
return
|
||||
}
|
||||
req.aborted = Date.now()
|
||||
if (!this.playbackStarted) {
|
||||
this.startPlayback()
|
||||
}
|
||||
const err = new Error()
|
||||
err.code = 'aborted'
|
||||
response.emit('close', err)
|
||||
|
||||
socket.destroy()
|
||||
|
||||
req.emit('abort')
|
||||
|
||||
const connResetError = new Error('socket hang up')
|
||||
connResetError.code = 'ECONNRESET'
|
||||
this.emitError(connResetError)
|
||||
debug('request flushHeaders')
|
||||
this.maybeStartPlayback()
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -233,6 +239,21 @@ class InterceptedRequestRouter {
|
|||
}
|
||||
}
|
||||
|
||||
maybeStartPlayback() {
|
||||
const { req, socket, playbackStarted } = this
|
||||
|
||||
// In order to get the events in the right order we need to delay playback
|
||||
// if we get here before the `socket` event is emitted.
|
||||
if (socket.connecting) {
|
||||
this.readyToStartPlaybackOnSocketEvent = true
|
||||
return
|
||||
}
|
||||
|
||||
if (!common.isRequestDestroyed(req) && !playbackStarted) {
|
||||
this.startPlayback()
|
||||
}
|
||||
}
|
||||
|
||||
startPlayback() {
|
||||
debug('ending')
|
||||
this.playbackStarted = true
|
||||
|
|
@ -270,11 +291,14 @@ class InterceptedRequestRouter {
|
|||
)
|
||||
|
||||
if (matchedInterceptor) {
|
||||
debug('interceptor identified, starting mocking')
|
||||
matchedInterceptor.scope.logger(
|
||||
'interceptor identified, starting mocking'
|
||||
)
|
||||
|
||||
matchedInterceptor.markConsumed()
|
||||
|
||||
// wait to emit the finish event until we know for sure an Interceptor is going to playback.
|
||||
// otherwise an unmocked request might emit finish twice.
|
||||
req.finished = true
|
||||
req.emit('finish')
|
||||
|
||||
playbackInterceptor({
|
||||
|
|
@ -304,14 +328,11 @@ class InterceptedRequestRouter {
|
|||
// We send the raw buffer as we received it, not as we interpreted it.
|
||||
newReq.end(requestBodyBuffer)
|
||||
} 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue