update @actions/tool-cache, install semver, nock
This commit is contained in:
parent
74d434c5ca
commit
4c6749115a
678 changed files with 39259 additions and 14619 deletions
320
node_modules/nock/lib/intercepted_request_router.js
generated
vendored
Normal file
320
node_modules/nock/lib/intercepted_request_router.js
generated
vendored
Normal file
|
|
@ -0,0 +1,320 @@
|
|||
'use strict'
|
||||
|
||||
const debug = require('debug')('nock.request_overrider')
|
||||
const {
|
||||
IncomingMessage,
|
||||
ClientRequest,
|
||||
request: originalHttpRequest,
|
||||
} = require('http')
|
||||
const { request: originalHttpsRequest } = require('https')
|
||||
const propagate = require('propagate')
|
||||
const common = require('./common')
|
||||
const globalEmitter = require('./global_emitter')
|
||||
const Socket = require('./socket')
|
||||
const { playbackInterceptor } = require('./playback_interceptor')
|
||||
|
||||
/**
|
||||
* Given a group of interceptors, appropriately route an outgoing request.
|
||||
* Identify which interceptor ought to respond, if any, then delegate to
|
||||
* `playbackInterceptor()` to consume the request itself.
|
||||
*/
|
||||
class InterceptedRequestRouter {
|
||||
constructor({ req, options, interceptors }) {
|
||||
this.req = req
|
||||
this.options = {
|
||||
// We may be changing the options object and we don't want those changes
|
||||
// affecting the user so we use a clone of the object.
|
||||
...options,
|
||||
// We use lower-case header field names throughout Nock.
|
||||
headers: common.headersFieldNamesToLowerCase(options.headers || {}),
|
||||
}
|
||||
this.interceptors = interceptors
|
||||
|
||||
this.socket = new Socket(options)
|
||||
|
||||
// support setting `timeout` using request `options`
|
||||
// https://nodejs.org/docs/latest-v12.x/api/http.html#http_http_request_url_options_callback
|
||||
if (options.timeout) {
|
||||
this.socket.setTimeout(options.timeout)
|
||||
}
|
||||
|
||||
this.response = new IncomingMessage(this.socket)
|
||||
this.playbackStarted = false
|
||||
this.requestBodyBuffers = []
|
||||
|
||||
this.attachToReq()
|
||||
}
|
||||
|
||||
attachToReq() {
|
||||
const { req, response, socket, options } = this
|
||||
|
||||
response.req = req
|
||||
|
||||
for (const [name, val] of Object.entries(options.headers)) {
|
||||
req.setHeader(name.toLowerCase(), val)
|
||||
}
|
||||
|
||||
if (options.auth && !options.headers.authorization) {
|
||||
req.setHeader(
|
||||
// We use lower-case header field names throughout Nock.
|
||||
'authorization',
|
||||
`Basic ${Buffer.from(options.auth).toString('base64')}`
|
||||
)
|
||||
}
|
||||
|
||||
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') {
|
||||
common.setImmediate(() => {
|
||||
debug('continue')
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
||||
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'))
|
||||
}
|
||||
|
||||
common.setImmediate(function() {
|
||||
req.emit('drain')
|
||||
})
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
handleEnd(chunk, encoding, callback) {
|
||||
debug('req.end')
|
||||
const { req } = this
|
||||
|
||||
// handle the different overloaded param signatures
|
||||
if (typeof chunk === 'function') {
|
||||
callback = chunk
|
||||
chunk = null
|
||||
} else if (typeof encoding === 'function') {
|
||||
callback = encoding
|
||||
encoding = null
|
||||
}
|
||||
|
||||
if (typeof callback === 'function') {
|
||||
req.once('finish', callback)
|
||||
}
|
||||
|
||||
if (!req.aborted && !this.playbackStarted) {
|
||||
req.write(chunk, encoding)
|
||||
this.startPlayback()
|
||||
}
|
||||
if (req.aborted) {
|
||||
this.emitError(new Error('Request aborted'))
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
/**
|
||||
* Set request headers of the given request. This is needed both during the
|
||||
* routing phase, in case header filters were specified, and during the
|
||||
* interceptor-playback phase, to correctly pass mocked request headers.
|
||||
* TODO There are some problems with this; see https://github.com/nock/nock/issues/1718
|
||||
*/
|
||||
setHostHeaderUsingInterceptor(interceptor) {
|
||||
const { req, options } = this
|
||||
|
||||
// If a filtered scope is being used we have to use scope's host in the
|
||||
// header, otherwise 'host' header won't match.
|
||||
// NOTE: We use lower-case header field names throughout Nock.
|
||||
const HOST_HEADER = 'host'
|
||||
if (interceptor.__nock_filteredScope && interceptor.__nock_scopeHost) {
|
||||
options.headers[HOST_HEADER] = interceptor.__nock_scopeHost
|
||||
req.setHeader(HOST_HEADER, interceptor.__nock_scopeHost)
|
||||
} else {
|
||||
// For all other cases, we always add host header equal to the requested
|
||||
// host unless it was already defined.
|
||||
if (options.host && !req.getHeader(HOST_HEADER)) {
|
||||
let hostHeader = options.host
|
||||
|
||||
if (options.port === 80 || options.port === 443) {
|
||||
hostHeader = hostHeader.split(':')[0]
|
||||
}
|
||||
|
||||
req.setHeader(HOST_HEADER, hostHeader)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
startPlayback() {
|
||||
debug('ending')
|
||||
this.playbackStarted = true
|
||||
|
||||
const { req, response, socket, options, interceptors } = this
|
||||
|
||||
Object.assign(options, {
|
||||
// Re-update `options` with the current value of `req.path` because badly
|
||||
// behaving agents like superagent like to change `req.path` mid-flight.
|
||||
path: req.path,
|
||||
// Similarly, node-http-proxy will modify headers in flight, so we have
|
||||
// to put the headers back into options.
|
||||
// https://github.com/nock/nock/pull/1484
|
||||
headers: req.getHeaders(),
|
||||
// Fixes https://github.com/nock/nock/issues/976
|
||||
protocol: `${options.proto}:`,
|
||||
})
|
||||
|
||||
interceptors.forEach(interceptor => {
|
||||
this.setHostHeaderUsingInterceptor(interceptor)
|
||||
})
|
||||
|
||||
const requestBodyBuffer = Buffer.concat(this.requestBodyBuffers)
|
||||
// When request body is a binary buffer we internally use in its hexadecimal
|
||||
// representation.
|
||||
const requestBodyIsUtf8Representable = common.isUtf8Representable(
|
||||
requestBodyBuffer
|
||||
)
|
||||
const requestBodyString = requestBodyBuffer.toString(
|
||||
requestBodyIsUtf8Representable ? 'utf8' : 'hex'
|
||||
)
|
||||
|
||||
const matchedInterceptor = interceptors.find(i =>
|
||||
i.match(req, options, requestBodyString)
|
||||
)
|
||||
|
||||
if (matchedInterceptor) {
|
||||
debug('interceptor identified, starting mocking')
|
||||
|
||||
// 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({
|
||||
req,
|
||||
socket,
|
||||
options,
|
||||
requestBodyString,
|
||||
requestBodyIsUtf8Representable,
|
||||
response,
|
||||
interceptor: matchedInterceptor,
|
||||
})
|
||||
} else {
|
||||
globalEmitter.emit('no match', req, options, requestBodyString)
|
||||
|
||||
// Try to find a hostname match that allows unmocked.
|
||||
const allowUnmocked = interceptors.some(
|
||||
i => i.matchHostName(options) && i.options.allowUnmocked
|
||||
)
|
||||
|
||||
if (allowUnmocked && req instanceof ClientRequest) {
|
||||
const newReq =
|
||||
options.proto === 'https'
|
||||
? originalHttpsRequest(options)
|
||||
: originalHttpRequest(options)
|
||||
|
||||
propagate(newReq, req)
|
||||
// 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
|
||||
)}`
|
||||
)
|
||||
err.statusCode = err.status = 404
|
||||
this.emitError(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { InterceptedRequestRouter }
|
||||
Loading…
Add table
Add a link
Reference in a new issue