Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Socket
Sign inDemoInstall

nock

Package Overview
Dependencies
Maintainers
4
Versions
431
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

nock - npm Package Compare versions

Comparing version 13.0.0-beta.3 to 13.0.0-beta.4

2

lib/intercept.js

@@ -284,2 +284,4 @@ 'use strict'

// Use filtered interceptors to intercept requests.
// TODO: this shouldn't be a class anymore
// the overrider explicitly overrides methods and attrs on the request so the `assign` below should be removed.
const overrider = new InterceptedRequestRouter({

@@ -286,0 +288,0 @@ req: this,

25

lib/interceptor.js

@@ -74,3 +74,3 @@ 'use strict'

this.delayInMs = 0
this.delayBodyInMs = 0
this.delayConnectionInMs = 0

@@ -602,3 +602,3 @@

delayBody(ms) {
this.delayInMs += ms
this.delayBodyInMs = ms
return this

@@ -614,24 +614,5 @@ }

delayConnection(ms) {
this.delayConnectionInMs += ms
this.delayConnectionInMs = ms
return this
}
/**
* @private
* @returns {number}
*/
getTotalDelay() {
return this.delayInMs + this.delayConnectionInMs
}
/**
* Make the socket idle for a certain number of ms (simulated).
*
* @param {integer} ms - Number of milliseconds to wait
* @return {Interceptor} - the current interceptor for chaining
*/
socketDelay(ms) {
this.socketDelayInMs = ms
return this
}
}
'use strict'
const stream = require('stream')
const util = require('util')

@@ -7,3 +8,2 @@ const zlib = require('zlib')

const common = require('./common')
const DelayedBody = require('./delayed_body')

@@ -75,2 +75,40 @@ function parseJSONRequestBody(req, requestBody) {

// Presents a list of Buffers as a Readable
class ReadableBuffers extends stream.Readable {
constructor(buffers, opts = {}) {
super(opts)
this.buffers = buffers
}
_read(size) {
while (this.buffers.length) {
if (!this.push(this.buffers.shift())) {
return
}
}
this.push(null)
}
}
function convertBodyToStream(body) {
if (common.isStream(body)) {
return body
}
if (body === undefined) {
return new ReadableBuffers([])
}
if (Buffer.isBuffer(body)) {
return new ReadableBuffers([body])
}
if (typeof body !== 'string') {
body = JSON.stringify(body)
}
return new ReadableBuffers([Buffer.from(body)])
}
/**

@@ -97,2 +135,3 @@ * Play back an interceptor using the given request and mock response.

function start() {
interceptor.markConsumed()
interceptor.req = req

@@ -104,4 +143,2 @@ req.headers = req.getHeaders()

if (typeof interceptor.errorMessage !== 'undefined') {
interceptor.markConsumed()
let error

@@ -113,3 +150,5 @@ if (typeof interceptor.errorMessage === 'object') {

}
common.setTimeout(() => emitError(error), interceptor.getTotalDelay())
const delay = interceptor.delayBodyInMs + interceptor.delayConnectionInMs
common.setTimeout(emitError, delay, error)
return

@@ -138,4 +177,4 @@ }

Promise.resolve(fn.call(interceptor, options.path, parsedRequestBody))
.then(responseBody => continueWithResponseBody({ responseBody }))
.catch(err => emitError(err))
.then(continueWithResponseBody)
.catch(emitError)
return

@@ -153,4 +192,4 @@ }

Promise.resolve(fn.call(interceptor, options.path, parsedRequestBody))
.then(fullReplyResult => continueWithFullResponse({ fullReplyResult }))
.catch(err => emitError(err))
.then(continueWithFullResponse)
.catch(emitError)
return

@@ -167,12 +206,2 @@ }

// buffer by buffer and not one single merged buffer)
if (interceptor.delayInMs) {
emitError(
new Error(
'Response delay of the body is currently not supported with content-encoded responses.'
)
)
return
}
const bufferData = Array.isArray(interceptor.body)

@@ -182,3 +211,4 @@ ? interceptor.body

const responseBuffers = bufferData.map(data => Buffer.from(data, 'hex'))
continueWithResponseBody({ responseBuffers })
const responseBody = new ReadableBuffers(responseBuffers)
continueWithResponseBody(responseBody)
return

@@ -208,70 +238,35 @@ }

return continueWithResponseBody({ responseBody })
return continueWithResponseBody(responseBody)
}
function continueWithFullResponse({ fullReplyResult }) {
function continueWithFullResponse(fullReplyResult) {
let responseBody
try {
responseBody = parseFullReplyResult(response, fullReplyResult)
} catch (innerErr) {
emitError(innerErr)
} catch (err) {
emitError(err)
return
}
continueWithResponseBody({ responseBody })
continueWithResponseBody(responseBody)
}
function continueWithResponseBody({ responseBuffers, responseBody }) {
// Transform the response body if it exists (it may not exist
// if we have `responseBuffers` instead)
if (responseBody !== undefined) {
logger('transform the response body')
function prepareResponseHeaders(body) {
const defaultHeaders = [...interceptor.scope._defaultReplyHeaders]
if (interceptor.delayInMs) {
logger(
'delaying the response for',
interceptor.delayInMs,
'milliseconds'
)
// Because setTimeout is called immediately in DelayedBody(), so we
// need count in the delayConnectionInMs.
responseBody = new DelayedBody(
interceptor.getTotalDelay(),
responseBody
)
}
// Include a JSON content type when JSON.stringify is called on the body.
// This is a convenience added by Nock that has no analog in Node. It's added to the
// defaults, so it will be ignored if the caller explicitly provided the header already.
const isJSON =
body !== undefined &&
typeof body !== 'string' &&
!Buffer.isBuffer(body) &&
!common.isStream(body)
if (common.isStream(responseBody)) {
logger('response body is a stream')
responseBody.pause()
responseBody.on('data', function (d) {
response.push(d)
})
responseBody.on('end', function () {
response.push(null)
// https://nodejs.org/dist/latest-v10.x/docs/api/http.html#http_message_complete
response.complete = true
})
responseBody.on('error', function (err) {
response.emit('error', err)
})
} else if (!Buffer.isBuffer(responseBody)) {
if (typeof responseBody === 'string') {
responseBody = Buffer.from(responseBody)
} else {
responseBody = JSON.stringify(responseBody)
response.rawHeaders.push('Content-Type', 'application/json')
}
}
// Why are strings converted to a Buffer, but JSON data is left as a string?
// Related to https://github.com/nock/nock/issues/1542 ?
if (isJSON) {
defaultHeaders.push('Content-Type', 'application/json')
}
interceptor.markConsumed()
response.rawHeaders.push(
...selectDefaultHeaders(
response.rawHeaders,
interceptor.scope._defaultReplyHeaders
)
...selectDefaultHeaders(response.rawHeaders, defaultHeaders)
)

@@ -282,3 +277,3 @@

if (typeof value === 'function') {
response.rawHeaders[i + 1] = value(req, response, responseBody)
response.rawHeaders[i + 1] = value(req, response, body)
}

@@ -288,10 +283,26 @@ })

response.headers = common.headersArrayToObject(response.rawHeaders)
}
respondUsingInterceptor({
responseBody,
responseBuffers,
function continueWithResponseBody(rawBody) {
prepareResponseHeaders(rawBody)
const bodyAsStream = convertBodyToStream(rawBody)
bodyAsStream.pause()
// IncomingMessage extends Readable so we can't simply pipe.
bodyAsStream.on('data', function (chunk) {
response.push(chunk)
})
}
bodyAsStream.on('end', function () {
// https://nodejs.org/dist/latest-v10.x/docs/api/http.html#http_message_complete
response.complete = true
response.push(null)
function respondUsingInterceptor({ responseBody, responseBuffers }) {
interceptor.scope.emit('replied', req, interceptor)
})
bodyAsStream.on('error', function (err) {
response.emit('error', err)
})
const { delayBodyInMs, delayConnectionInMs } = interceptor
function respond() {

@@ -311,51 +322,13 @@ if (req.aborted) {

if (common.isStream(responseBody)) {
logger('resuming response stream')
responseBody.resume()
} else {
responseBuffers = responseBuffers || []
if (typeof responseBody !== 'undefined') {
logger('adding body to buffer list')
responseBuffers.push(responseBody)
}
// Stream the response chunks one at a time.
common.setImmediate(function emitChunk() {
const chunk = responseBuffers.shift()
if (chunk) {
logger('emitting response chunk')
response.push(chunk)
common.setImmediate(emitChunk)
} else {
logger('ending response stream')
response.push(null)
// https://nodejs.org/dist/latest-v10.x/docs/api/http.html#http_message_complete
response.complete = true
interceptor.scope.emit('replied', req, interceptor)
}
})
}
common.setTimeout(() => bodyAsStream.resume(), delayBodyInMs)
}
if (interceptor.socketDelayInMs && interceptor.socketDelayInMs > 0) {
socket.applyDelay(interceptor.socketDelayInMs)
}
if (
interceptor.delayConnectionInMs &&
interceptor.delayConnectionInMs > 0
) {
socket.applyDelay(interceptor.delayConnectionInMs)
common.setTimeout(respond, interceptor.delayConnectionInMs)
} else {
respond()
}
socket.applyDelay(delayConnectionInMs)
common.setTimeout(respond, delayConnectionInMs)
}
// If there are no delays configured on the Interceptor, calling `start` will
// take the request all the way to the 'response' event during a single microtask
// execution. This setImmediate stalls the playback to ensure the correct events
// are emitted first ('socket', 'finish') and any aborts in the in the queue
// or called during a 'finish' listener can be called.
// Calling `start` immediately could take the request all the way to the connection delay
// during a single microtask execution. This setImmediate stalls the playback to ensure the
// correct events are emitted first ('socket', 'finish') and any aborts in the in the queue or
// called during a 'finish' listener can be called.
common.setImmediate(() => {

@@ -362,0 +335,0 @@ if (!req.aborted) {

@@ -23,8 +23,4 @@ 'use strict'

// totalDelay that has already been applied to the current
// request/connection, timeout error will be generated if
// it is timed-out.
this.totalDelayMs = 0
// Maximum allowed delay. Null means unlimited.
this.timeoutMs = null
// Maximum allowed delay. 0 means unlimited.
this.timeout = 0

@@ -52,12 +48,18 @@ const ipv6 = options.family === 6

setTimeout(timeoutMs, fn) {
this.timeoutMs = timeoutMs
this.timeout = timeoutMs
if (fn) {
this.once('timeout', fn)
}
return this
}
/**
* Artificial delay that will trip socket timeouts when appropriate.
*
* Doesn't actually wait for time to pass.
* Timeout events don't necessarily end the request.
* While many clients choose to abort the request upon a timeout, Node itself does not.
*/
applyDelay(delayMs) {
this.totalDelayMs += delayMs
if (this.timeoutMs && this.totalDelayMs > this.timeoutMs) {
if (this.timeout && delayMs > this.timeout) {
debug('socket timeout')

@@ -64,0 +66,0 @@ this.emit('timeout')

@@ -10,3 +10,3 @@ {

],
"version": "13.0.0-beta.3",
"version": "13.0.0-beta.4",
"author": "Pedro Teixeira <pedro.teixeira@gmail.com>",

@@ -13,0 +13,0 @@ "repository": {

@@ -46,6 +46,3 @@ # Nock

- [Repeat response n times](#repeat-response-n-times)
- [Delay the response body](#delay-the-response-body)
- [Delay the response](#delay-the-response)
- [Delay the connection](#delay-the-connection)
- [Socket timeout](#socket-timeout)
- [Chaining](#chaining)

@@ -664,18 +661,5 @@ - [Scope filtering](#scope-filtering)

### Delay the response body
You are able to specify the number of milliseconds that the response body should be delayed. Response header will be replied immediately.
`delayBody(1000)` is equivalent to `delay({body: 1000})`.
```js
nock('http://my.server.com')
.get('/')
.delayBody(2000) // 2 seconds
.reply(200, '<html></html>')
```
NOTE: the [`'response'`](http://nodejs.org/api/http.html#http_event_response) event will occur immediately, but the [IncomingMessage](http://nodejs.org/api/http.html#http_http_incomingmessage) will not emit its `'end'` event until after the delay.
### Delay the response
Nock can simulate response latency to allow you to test timeouts, race conditions, an other timing related scenarios.
You are able to specify the number of milliseconds that your reply should be delayed.

@@ -690,50 +674,51 @@

`delay()` could also be used as
`delay(1000)` is an alias for `delayConnection(1000).delayBody(0)`
`delay({ head: 1000, body: 2000 })` is an alias for `delayConnection(1000).delayBody(2000)`
Both of which are covered in detail below.
```
delay({
head: headDelayInMs,
body: bodyDelayInMs
})
```
#### Delay the connection
for example
You are able to specify the number of milliseconds that your connection should be idle before it starts to receive the response.
To simulate a socket timeout, provide a larger value than the timeout setting on the request.
```js
nock('http://my.server.com')
.get('/')
.delay({
head: 2000, // header will be delayed for 2 seconds, i.e. the whole response will be delayed for 2 seconds.
body: 3000, // body will be delayed for another 3 seconds after header is sent out.
})
.delayConnection(2000) // 2 seconds
.reply(200, '<html></html>')
req = http.request('http://my.server.com', { timeout: 1000 })
```
### Delay the connection
Nock emits timeout events almost immediately by comparing the requested connection delay to the timeout parameter passed to `http.request()` or `http.ClientRequest#setTimeout()`.
This allows you to test timeouts without using fake timers or slowing down your tests.
If the client chooses to _not_ take an action (e.g. abort the request), the request and response will continue on as normal, after real clock time has passed.
`delayConnection(1000)` is equivalent to `delay({ head: 1000 })`.
##### Technical Details
### Socket timeout
Following the `'finish'` event being emitted by `ClientRequest`, Nock will wait for the next event loop iteration before checking if the request has been aborted.
At this point, any connection delay value is compared against any request timeout setting and a [`'timeout'`](https://nodejs.org/api/http.html#http_event_timeout) is emitted when appropriate from the socket and the request objects.
A Node timeout timer is then registered with any connection delay value to delay real time before checking again if the request has been aborted and the [`'response'`](http://nodejs.org/api/http.html#http_event_response) is emitted by the request.
You are able to specify the number of milliseconds that your connection should be idle, to simulate a socket timeout.
A similar method, `.socketDelay()` was removed in version 13. It was thought that having two methods so subtlety similar was confusing.
The discussion can be found at https://github.com/nock/nock/pull/1974.
#### Delay the response body
You are able to specify the number of milliseconds that the response body should be delayed.
This is the time between the headers being received and the body starting to be received.
```js
nock('http://my.server.com')
.get('/')
.socketDelay(2000) // 2 seconds
.delayBody(2000) // 2 seconds
.reply(200, '<html></html>')
```
To test a request like the following:
##### Technical Details
```js
req = http.request('http://my.server.com', res => {
...
})
req.setTimeout(1000, () => { req.abort() })
req.end()
```
Following the [`'response'`](http://nodejs.org/api/http.html#http_event_response) being emitted by `ClientRequest`,
Nock will register a timeout timer with the body delay value to delay real time before the [IncomingMessage](http://nodejs.org/api/http.html#http_http_incomingmessage) emits its first `'data'` or the `'end'` event.
NOTE: the timeout will be fired immediately, and will not leave the simulated connection idle for the specified period of time.
### Chaining

@@ -740,0 +725,0 @@

@@ -206,3 +206,2 @@ // TypeScript Version: 3.5

delayConnection(timeMs: number): this
socketDelay(timeMs: number): this
}

@@ -209,0 +208,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc