nock 11.3.6 → 11.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/README.md +9 -0
  2. package/index.js +2 -0
  3. package/lib/common.js +33 -9
  4. package/lib/delayed_body.js +1 -1
  5. package/lib/intercept.js +41 -40
  6. package/lib/intercepted_request_router.js +10 -14
  7. package/lib/interceptor.js +10 -4
  8. package/lib/match_body.js +2 -5
  9. package/lib/playback_interceptor.js +9 -10
  10. package/lib/recorder.js +27 -30
  11. package/lib/scope.js +3 -2
  12. package/lib/socket.js +16 -0
  13. package/package.json +14 -6
  14. package/types/index.d.ts +2 -0
  15. package/.all-contributorsrc +0 -123
  16. package/.eslintignore +0 -3
  17. package/.eslintrc.yml +0 -34
  18. package/.github/FUNDING.yml +0 -1
  19. package/.github/ISSUE_TEMPLATE/01_bug_report.md +0 -26
  20. package/.github/ISSUE_TEMPLATE/02_feature_request.md +0 -20
  21. package/.github/ISSUE_TEMPLATE/03_support.md +0 -10
  22. package/.github/ISSUE_TEMPLATE/04_thanks.md +0 -20
  23. package/.github/lock.yml +0 -19
  24. package/.github/stale.yml +0 -25
  25. package/.github/toc.yml +0 -1
  26. package/.istanbul.yml +0 -5
  27. package/.prettierignore +0 -5
  28. package/.prettierrc.yml +0 -4
  29. package/.travis.yml +0 -47
  30. package/CODE_OF_CONDUCT.md +0 -46
  31. package/CONTRIBUTING.md +0 -164
  32. package/assets/reply_file_1.txt +0 -1
  33. package/assets/reply_file_2.txt.gz +0 -0
  34. package/examples/.eslintrc.yml +0 -3
  35. package/examples/_log.js +0 -12
  36. package/examples/delay-connection.js +0 -15
  37. package/examples/delay-response.js +0 -15
  38. package/examples/net-connect-default-no-mock.js +0 -18
  39. package/examples/net-connect-default-other-mock.js +0 -23
  40. package/examples/net-connect-disabled-different-host.js +0 -26
  41. package/examples/net-connect-mock-same-host-different-path.js +0 -24
  42. package/examples/socket-delay-abort.js +0 -19
  43. package/examples/socket-delay-no-abort.js +0 -15
  44. package/rfcs/rfc-001.md +0 -43
  45. package/types/tests.ts +0 -810
  46. package/types/tsconfig.json +0 -11
  47. package/types/tslint.json +0 -6
package/README.md CHANGED
@@ -58,6 +58,7 @@ For instance, if a module performs HTTP requests to a CouchDB server or makes HT
58
58
  - [Expectations](#expectations)
59
59
  - [.isDone()](#isdone)
60
60
  - [.cleanAll()](#cleanall)
61
+ - [.abortPendingRequests()](#abortpendingrequests)
61
62
  - [.persist()](#persist)
62
63
  - [.pendingMocks()](#pendingmocks)
63
64
  - [.activeMocks()](#activemocks)
@@ -995,6 +996,14 @@ You can cleanup all the prepared mocks (could be useful to cleanup some state af
995
996
  nock.cleanAll()
996
997
  ```
997
998
 
999
+ ### .abortPendingRequests()
1000
+
1001
+ You can abort all current pending request like this:
1002
+
1003
+ ```js
1004
+ nock.abortPendingRequests()
1005
+ ```
1006
+
998
1007
  ### .persist()
999
1008
 
1000
1009
  You can make all the interceptors for a scope persist by calling `.persist()` on it:
package/index.js CHANGED
@@ -12,6 +12,7 @@ const {
12
12
  disableNetConnect,
13
13
  enableNetConnect,
14
14
  removeAll,
15
+ abortPendingRequests,
15
16
  } = require('./lib/intercept')
16
17
  const recorder = require('./lib/recorder')
17
18
  const { Scope, load, loadDefs, define } = require('./lib/scope')
@@ -35,6 +36,7 @@ Object.assign(module.exports, {
35
36
  removeAll()
36
37
  return module.exports
37
38
  },
39
+ abortPendingRequests,
38
40
  load,
39
41
  loadDefs,
40
42
  define,
package/lib/common.js CHANGED
@@ -3,6 +3,7 @@
3
3
  const _ = require('lodash')
4
4
  const debug = require('debug')('nock.common')
5
5
  const url = require('url')
6
+ const timers = require('timers')
6
7
 
7
8
  /**
8
9
  * Normalizes the request options so that it always has `host` property.
@@ -37,23 +38,16 @@ function normalizeRequestOptions(options) {
37
38
  }
38
39
 
39
40
  /**
40
- * Returns false if the data contained in buffer can be reconstructed
41
+ * Returns true if the data contained in buffer can be reconstructed
41
42
  * from its utf8 representation.
42
43
  *
43
- * TODO: Reverse the semantics of this method and refactor calling code
44
- * accordingly. We've inadvertently gotten it flipped.
45
- *
46
44
  * @param {Object} buffer - a Buffer object
47
45
  * @returns {boolean}
48
46
  */
49
47
  function isUtf8Representable(buffer) {
50
- if (!Buffer.isBuffer(buffer)) {
51
- return false
52
- }
53
-
54
48
  const utfEncodedBuffer = buffer.toString('utf8')
55
49
  const reconstructedBuffer = Buffer.from(utfEncodedBuffer, 'utf8')
56
- return !reconstructedBuffer.equals(buffer)
50
+ return reconstructedBuffer.equals(buffer)
57
51
  }
58
52
 
59
53
  // Array where all information about all the overridden requests are held.
@@ -593,6 +587,32 @@ function deepEqual(expected, actual) {
593
587
  return expected === actual
594
588
  }
595
589
 
590
+ const timeouts = []
591
+ const intervals = []
592
+ const immediates = []
593
+
594
+ const wrapTimer = (timer, ids) => (...args) => {
595
+ const id = timer(...args)
596
+ ids.push(id)
597
+ return id
598
+ }
599
+
600
+ const setTimeout = wrapTimer(timers.setTimeout, timeouts)
601
+ const setInterval = wrapTimer(timers.setInterval, intervals)
602
+ const setImmediate = wrapTimer(timers.setImmediate, immediates)
603
+
604
+ function clearTimer(clear, ids) {
605
+ while (ids.length) {
606
+ clear(ids.shift())
607
+ }
608
+ }
609
+
610
+ function removeAllTimers() {
611
+ clearTimer(clearTimeout, timeouts)
612
+ clearTimer(clearInterval, intervals)
613
+ clearTimer(clearImmediate, immediates)
614
+ }
615
+
596
616
  exports.normalizeClientRequestArgs = normalizeClientRequestArgs
597
617
  exports.normalizeRequestOptions = normalizeRequestOptions
598
618
  exports.normalizeOrigin = normalizeOrigin
@@ -615,3 +635,7 @@ exports.matchStringOrRegexp = matchStringOrRegexp
615
635
  exports.formatQueryValue = formatQueryValue
616
636
  exports.isStream = isStream
617
637
  exports.dataEqual = dataEqual
638
+ exports.setTimeout = setTimeout
639
+ exports.setInterval = setInterval
640
+ exports.setImmediate = setImmediate
641
+ exports.removeAllTimers = removeAllTimers
@@ -34,7 +34,7 @@ module.exports = class DelayedBody extends Transform {
34
34
 
35
35
  // TODO: This would be more readable if the stream case were moved into
36
36
  // the `if` statement above.
37
- setTimeout(function() {
37
+ common.setTimeout(function() {
38
38
  if (common.isStream(body) && !ended) {
39
39
  body.once('end', function() {
40
40
  self.end(data)
package/lib/intercept.js CHANGED
@@ -11,7 +11,6 @@ const http = require('http')
11
11
  const _ = require('lodash')
12
12
  const debug = require('debug')('nock.intercept')
13
13
  const globalEmitter = require('./global_emitter')
14
- const timers = require('timers')
15
14
 
16
15
  /**
17
16
  * @name NetConnectNotAllowedError
@@ -90,7 +89,7 @@ function isOff() {
90
89
 
91
90
  function addInterceptor(key, interceptor, scope, scopeOptions, host) {
92
91
  if (!(key in allInterceptors)) {
93
- allInterceptors[key] = { key, scopes: [] }
92
+ allInterceptors[key] = { key, interceptors: [] }
94
93
  }
95
94
  interceptor.__nock_scope = scope
96
95
 
@@ -103,7 +102,7 @@ function addInterceptor(key, interceptor, scope, scopeOptions, host) {
103
102
 
104
103
  if (scopeOptions.allowUnmocked) allInterceptors[key].allowUnmocked = true
105
104
 
106
- allInterceptors[key].scopes.push(interceptor)
105
+ allInterceptors[key].interceptors.push(interceptor)
107
106
  }
108
107
 
109
108
  function remove(interceptor) {
@@ -113,7 +112,7 @@ function remove(interceptor) {
113
112
 
114
113
  const { basePath } = interceptor
115
114
  const interceptors =
116
- (allInterceptors[basePath] && allInterceptors[basePath].scopes) || []
115
+ (allInterceptors[basePath] && allInterceptors[basePath].interceptors) || []
117
116
 
118
117
  // TODO: There is a clearer way to write that we want to delete the first
119
118
  // matching instance. I'm also not sure why we couldn't delete _all_
@@ -125,7 +124,7 @@ function remove(interceptor) {
125
124
 
126
125
  function removeAll() {
127
126
  Object.keys(allInterceptors).forEach(function(key) {
128
- allInterceptors[key].scopes.forEach(function(interceptor) {
127
+ allInterceptors[key].interceptors.forEach(function(interceptor) {
129
128
  interceptor.scope.keyedInterceptors = {}
130
129
  })
131
130
  })
@@ -146,32 +145,29 @@ function interceptorsFor(options) {
146
145
 
147
146
  debug('filtering interceptors for basepath', basePath)
148
147
 
149
- // First try to use filteringScope if any of the interceptors has it defined.
150
- let matchingInterceptor
151
- _.each(allInterceptors, function(interceptor) {
152
- _.each(interceptor.scopes, function(scope) {
153
- const { filteringScope } = scope.__nock_scopeOptions
148
+ // First try to use filteringScope if any of the interceptors has it defined.
149
+ for (const { key, interceptors, allowUnmocked } of Object.values(
150
+ allInterceptors
151
+ )) {
152
+ for (const interceptor of interceptors) {
153
+ const { filteringScope } = interceptor.__nock_scopeOptions
154
154
 
155
- // If scope filtering function is defined and returns a truthy value
156
- // then we have to treat this as a match.
155
+ // If scope filtering function is defined and returns a truthy value then
156
+ // we have to treat this as a match.
157
157
  if (filteringScope && filteringScope(basePath)) {
158
158
  debug('found matching scope interceptor')
159
159
 
160
- // Keep the filtered scope (its key) to signal the rest of the module
161
- // that this wasn't an exact but filtered match.
162
- scope.__nock_filteredScope = scope.__nock_scopeKey
163
- matchingInterceptor = interceptor.scopes
164
- // Break out of _.each for scopes.
165
- return false
160
+ // Keep the filtered scope (its key) to signal the rest of the module
161
+ // that this wasn't an exact but filtered match.
162
+ interceptor.__nock_filteredScope = interceptor.__nock_scopeKey
163
+ return interceptors
166
164
  }
167
- })
165
+ }
168
166
 
169
- if (
170
- !matchingInterceptor &&
171
- common.matchStringOrRegexp(basePath, interceptor.key)
172
- ) {
173
- if (interceptor.scopes.length === 0 && interceptor.allowUnmocked) {
174
- matchingInterceptor = [
167
+ if (common.matchStringOrRegexp(basePath, key)) {
168
+ if (allowUnmocked && interceptors.length === 0) {
169
+ debug('matched base path with allowUnmocked (no matching interceptors)')
170
+ return [
175
171
  {
176
172
  options: { allowUnmocked: true },
177
173
  matchAddress() {
@@ -180,18 +176,17 @@ function interceptorsFor(options) {
180
176
  },
181
177
  ]
182
178
  } else {
183
- matchingInterceptor = interceptor.scopes
179
+ debug(
180
+ `matched base path (${interceptors.length} interceptor${
181
+ interceptors.length > 1 ? 's' : ''
182
+ })`
183
+ )
184
+ return interceptors
184
185
  }
185
- // false to short circuit the .each
186
- return false
187
186
  }
187
+ }
188
188
 
189
- // Returning falsy value here (which will happen if we have found our matching interceptor)
190
- // will break out of _.each for all interceptors.
191
- return !matchingInterceptor
192
- })
193
-
194
- return matchingInterceptor
189
+ return undefined
195
190
  }
196
191
 
197
192
  function removeInterceptor(options) {
@@ -211,11 +206,14 @@ function removeInterceptor(options) {
211
206
  key = `${method} ${baseUrl}${options.path || '/'}`
212
207
  }
213
208
 
214
- if (allInterceptors[baseUrl] && allInterceptors[baseUrl].scopes.length > 0) {
215
- for (let i = 0; i < allInterceptors[baseUrl].scopes.length; i++) {
216
- const interceptor = allInterceptors[baseUrl].scopes[i]
209
+ if (
210
+ allInterceptors[baseUrl] &&
211
+ allInterceptors[baseUrl].interceptors.length > 0
212
+ ) {
213
+ for (let i = 0; i < allInterceptors[baseUrl].interceptors.length; i++) {
214
+ const interceptor = allInterceptors[baseUrl].interceptors[i]
217
215
  if (interceptor._key === key) {
218
- allInterceptors[baseUrl].scopes.splice(i, 1)
216
+ allInterceptors[baseUrl].interceptors.splice(i, 1)
219
217
  interceptor.scope.remove(key, interceptor)
220
218
  break
221
219
  }
@@ -294,7 +292,7 @@ function overrideClientRequest() {
294
292
  if (isOff() || isEnabledForNetConnect(options)) {
295
293
  originalClientRequest.apply(this, arguments)
296
294
  } else {
297
- timers.setImmediate(
295
+ common.setImmediate(
298
296
  function() {
299
297
  const error = new NetConnectNotAllowedError(
300
298
  options.host,
@@ -337,7 +335,9 @@ function isActive() {
337
335
  }
338
336
 
339
337
  function interceptorScopes() {
340
- const nestedInterceptors = Object.values(allInterceptors).map(i => i.scopes)
338
+ const nestedInterceptors = Object.values(allInterceptors).map(
339
+ i => i.interceptors
340
+ )
341
341
  return [].concat(...nestedInterceptors).map(i => i.scope)
342
342
  }
343
343
 
@@ -443,4 +443,5 @@ module.exports = {
443
443
  disableNetConnect,
444
444
  overrideClientRequest,
445
445
  restoreOverriddenClientRequest,
446
+ abortPendingRequests: common.removeAllTimers,
446
447
  }
@@ -1,7 +1,6 @@
1
1
  'use strict'
2
2
 
3
3
  const debug = require('debug')('nock.request_overrider')
4
- const { EventEmitter } = require('events')
5
4
  const {
6
5
  IncomingMessage,
7
6
  ClientRequest,
@@ -9,7 +8,6 @@ const {
9
8
  } = require('http')
10
9
  const { request: originalHttpsRequest } = require('https')
11
10
  const propagate = require('propagate')
12
- const timers = require('timers')
13
11
  const common = require('./common')
14
12
  const globalEmitter = require('./global_emitter')
15
13
  const Socket = require('./socket')
@@ -32,8 +30,8 @@ class InterceptedRequestRouter {
32
30
  }
33
31
  this.interceptors = interceptors
34
32
 
35
- this.response = new IncomingMessage(new EventEmitter())
36
- this.socket = new Socket({ proto: options.proto })
33
+ this.socket = new Socket(options)
34
+ this.response = new IncomingMessage(this.socket)
37
35
  this.playbackStarted = false
38
36
  this.requestBodyBuffers = []
39
37
 
@@ -63,11 +61,8 @@ class InterceptedRequestRouter {
63
61
  // ClientRequest.connection is an alias for ClientRequest.socket
64
62
  // https://nodejs.org/api/http.html#http_request_socket
65
63
  // https://github.com/nodejs/node/blob/b0f75818f39ed4e6bd80eb7c4010c1daf5823ef7/lib/_http_client.js#L640-L641
66
- // IncomingMessage.connection & IncomingMessage.client are aliases for IncomingMessage.socket
67
- // https://nodejs.org/api/http.html#http_response_socket
68
- // https://github.com/nodejs/node/blob/b0f75818f39ed4e6bd80eb7c4010c1daf5823ef7/lib/_http_incoming.js#L44-L69
69
64
  // The same Socket is shared between the request and response to mimic native behavior.
70
- req.socket = req.connection = response.socket = response.client = response.connection = socket
65
+ req.socket = req.connection = socket
71
66
 
72
67
  propagate(['error', 'timeout'], req.socket, req)
73
68
 
@@ -78,7 +73,7 @@ class InterceptedRequestRouter {
78
73
 
79
74
  // https://github.com/nock/nock/issues/256
80
75
  if (options.headers.expect === '100-continue') {
81
- timers.setImmediate(() => {
76
+ common.setImmediate(() => {
82
77
  debug('continue')
83
78
  req.emit('continue')
84
79
  })
@@ -125,7 +120,7 @@ class InterceptedRequestRouter {
125
120
  this.emitError(new Error('Request aborted'))
126
121
  }
127
122
 
128
- timers.setImmediate(function() {
123
+ common.setImmediate(function() {
129
124
  req.emit('drain')
130
125
  })
131
126
 
@@ -236,8 +231,9 @@ class InterceptedRequestRouter {
236
231
  // Re-update `options` with the current value of `req.path` because badly
237
232
  // behaving agents like superagent like to change `req.path` mid-flight.
238
233
  path: req.path,
239
- // similarly, node-http-proxy will modify headers in flight, so we have
234
+ // Similarly, node-http-proxy will modify headers in flight, so we have
240
235
  // to put the headers back into options.
236
+ // https://github.com/nock/nock/pull/1484
241
237
  headers: req.getHeaders(),
242
238
  // Fixes https://github.com/nock/nock/issues/976
243
239
  protocol: `${options.proto}:`,
@@ -250,11 +246,11 @@ class InterceptedRequestRouter {
250
246
  const requestBodyBuffer = Buffer.concat(this.requestBodyBuffers)
251
247
  // When request body is a binary buffer we internally use in its hexadecimal
252
248
  // representation.
253
- const isBinaryRequestBodyBuffer = common.isUtf8Representable(
249
+ const requestBodyIsUtf8Representable = common.isUtf8Representable(
254
250
  requestBodyBuffer
255
251
  )
256
252
  const requestBodyString = requestBodyBuffer.toString(
257
- isBinaryRequestBodyBuffer ? 'hex' : 'utf8'
253
+ requestBodyIsUtf8Representable ? 'utf8' : 'hex'
258
254
  )
259
255
 
260
256
  const matchedInterceptor = interceptors.find(i =>
@@ -269,7 +265,7 @@ class InterceptedRequestRouter {
269
265
  socket,
270
266
  options,
271
267
  requestBodyString,
272
- isBinaryRequestBodyBuffer,
268
+ requestBodyIsUtf8Representable,
273
269
  response,
274
270
  interceptor: matchedInterceptor,
275
271
  })
@@ -31,10 +31,16 @@ module.exports = class Interceptor {
31
31
  constructor(scope, uri, method, requestBody, interceptorOptions) {
32
32
  const uriIsStr = typeof uri === 'string'
33
33
  // Check for leading slash. Uri can be either a string or a regexp, but
34
- // we only need to check strings.
35
- if (uriIsStr && /^[^/*]/.test(uri)) {
34
+ // When enabled filteringScope ignores the passed URL entirely so we skip validation.
35
+
36
+ if (
37
+ !scope.scopeOptions.filteringScope &&
38
+ uriIsStr &&
39
+ !uri.startsWith('/') &&
40
+ !uri.startsWith('*')
41
+ ) {
36
42
  throw Error(
37
- "Non-wildcard URL path strings must begin with a slash (otherwise they won't match anything)"
43
+ `Non-wildcard URL path strings must begin with a slash (otherwise they won't match anything) (got: ${uri})`
38
44
  )
39
45
  }
40
46
 
@@ -224,7 +230,7 @@ module.exports = class Interceptor {
224
230
  }
225
231
 
226
232
  const method = (options.method || 'GET').toUpperCase()
227
- let { path = '' } = options
233
+ let { path = '/' } = options
228
234
  let matches
229
235
  let matchKey
230
236
  const { proto } = options
package/lib/match_body.js CHANGED
@@ -11,11 +11,8 @@ module.exports = function matchBody(options, spec, body) {
11
11
  }
12
12
 
13
13
  if (Buffer.isBuffer(spec)) {
14
- if (common.isUtf8Representable(spec)) {
15
- spec = spec.toString('hex')
16
- } else {
17
- spec = spec.toString('utf8')
18
- }
14
+ const encoding = common.isUtf8Representable(spec) ? 'utf8' : 'hex'
15
+ spec = spec.toString(encoding)
19
16
  }
20
17
 
21
18
  const contentType = (
@@ -1,7 +1,6 @@
1
1
  'use strict'
2
2
 
3
3
  const util = require('util')
4
- const timers = require('timers')
5
4
  const zlib = require('zlib')
6
5
  const debug = require('debug')('nock.playback_interceptor')
7
6
  const _ = require('lodash')
@@ -81,7 +80,7 @@ function playbackInterceptor({
81
80
  socket,
82
81
  options,
83
82
  requestBodyString,
84
- isBinaryRequestBodyBuffer,
83
+ requestBodyIsUtf8Representable,
85
84
  response,
86
85
  interceptor,
87
86
  }) {
@@ -106,7 +105,7 @@ function playbackInterceptor({
106
105
  } else {
107
106
  error = new Error(interceptor.errorMessage)
108
107
  }
109
- timers.setTimeout(() => emitError(error), interceptor.getTotalDelay())
108
+ common.setTimeout(() => emitError(error), interceptor.getTotalDelay())
110
109
  return
111
110
  }
112
111
 
@@ -179,10 +178,10 @@ function playbackInterceptor({
179
178
  // will eventually be JSON stringified.
180
179
  let responseBody = interceptor.body
181
180
 
182
- // If the request was binary then we assume that the response will be binary
183
- // as well. In that case we send the response as a Buffer object as that's
184
- // what the client will expect.
185
- if (isBinaryRequestBodyBuffer && typeof responseBody === 'string') {
181
+ // If the request was not UTF8-representable then we assume that the
182
+ // response won't be either. In that case we send the response as a Buffer
183
+ // object as that's what the client will expect.
184
+ if (!requestBodyIsUtf8Representable && typeof responseBody === 'string') {
186
185
  // Try to create the buffer from the interceptor's body response as hex.
187
186
  responseBody = Buffer.from(responseBody, 'hex')
188
187
 
@@ -311,13 +310,13 @@ function playbackInterceptor({
311
310
  }
312
311
 
313
312
  // Stream the response chunks one at a time.
314
- timers.setImmediate(function emitChunk() {
313
+ common.setImmediate(function emitChunk() {
315
314
  const chunk = responseBuffers.shift()
316
315
 
317
316
  if (chunk) {
318
317
  debug('emitting response chunk')
319
318
  response.push(chunk)
320
- timers.setImmediate(emitChunk)
319
+ common.setImmediate(emitChunk)
321
320
  } else {
322
321
  debug('ending response stream')
323
322
  response.push(null)
@@ -338,7 +337,7 @@ function playbackInterceptor({
338
337
  interceptor.delayConnectionInMs > 0
339
338
  ) {
340
339
  socket.applyDelay(interceptor.delayConnectionInMs)
341
- setTimeout(respond, interceptor.delayConnectionInMs)
340
+ common.setTimeout(respond, interceptor.delayConnectionInMs)
342
341
  } else {
343
342
  respond()
344
343
  }
package/lib/recorder.js CHANGED
@@ -21,9 +21,9 @@ function getMethod(options) {
21
21
  }
22
22
 
23
23
  function getBodyFromChunks(chunks, headers) {
24
- // If we have headers and there is content-encoding it means that
25
- // the body shouldn't be merged but instead persisted as an array
26
- // of hex strings so that the responses can be mocked one by one.
24
+ // If we have headers and there is content-encoding it means that the body
25
+ // shouldn't be merged but instead persisted as an array of hex strings so
26
+ // that the response chunks can be mocked one by one.
27
27
  if (headers && common.isContentEncoded(headers)) {
28
28
  return {
29
29
  body: chunks.map(chunk => chunk.toString('hex')),
@@ -32,30 +32,29 @@ function getBodyFromChunks(chunks, headers) {
32
32
 
33
33
  const mergedBuffer = Buffer.concat(chunks)
34
34
 
35
- // The merged buffer can be one of three things:
36
- // 1. A binary buffer which then has to be recorded as a hex string.
37
- // 2. A string buffer which represents a JSON object.
38
- // 3. A string buffer which doesn't represent a JSON object.
39
-
40
- const isBinary = common.isUtf8Representable(mergedBuffer)
41
- if (isBinary) {
42
- return {
43
- body: mergedBuffer.toString('hex'),
44
- isBinary: true,
45
- }
46
- } else {
35
+ // The merged buffer can be one of three things:
36
+ // 1. A UTF-8-representable string buffer which represents a JSON object.
37
+ // 2. A UTF-8-representable buffer which doesn't represent a JSON object.
38
+ // 3. A non-UTF-8-representable buffer which then has to be recorded as a hex string.
39
+ const isUtf8Representable = common.isUtf8Representable(mergedBuffer)
40
+ if (isUtf8Representable) {
47
41
  const maybeStringifiedJson = mergedBuffer.toString('utf8')
48
42
  try {
49
43
  return {
44
+ isUtf8Representable,
50
45
  body: JSON.parse(maybeStringifiedJson),
51
- isBinary: false,
52
46
  }
53
47
  } catch (err) {
54
48
  return {
49
+ isUtf8Representable,
55
50
  body: maybeStringifiedJson,
56
- isBinary: false,
57
51
  }
58
52
  }
53
+ } else {
54
+ return {
55
+ isUtf8Representable,
56
+ body: mergedBuffer.toString('hex'),
57
+ }
59
58
  }
60
59
  }
61
60
 
@@ -67,28 +66,26 @@ function generateRequestAndResponseObject({
67
66
  dataChunks,
68
67
  reqheaders,
69
68
  }) {
70
- const response = getBodyFromChunks(dataChunks, res.headers)
69
+ const { body, isUtf8Representable } = getBodyFromChunks(
70
+ dataChunks,
71
+ res.headers
72
+ )
71
73
  options.path = req.path
72
74
 
73
- const nockDef = {
75
+ return {
74
76
  scope: getScope(options),
75
77
  method: getMethod(options),
76
78
  path: options.path,
79
+ // Is it deliberate that `getBodyFromChunks()` is called a second time?
77
80
  body: getBodyFromChunks(bodyChunks).body,
78
81
  status: res.statusCode,
79
- response: response.body,
82
+ response: body,
80
83
  rawHeaders: res.rawHeaders,
84
+ reqheaders: reqheaders || undefined,
85
+ // When content-encoding is enabled, isUtf8Representable is `undefined`,
86
+ // so we explicitly check for `false`.
87
+ responseIsBinary: isUtf8Representable === false,
81
88
  }
82
-
83
- if (reqheaders) {
84
- nockDef.reqheaders = reqheaders
85
- }
86
-
87
- if (response.isBinary) {
88
- nockDef.responseIsBinary = true
89
- }
90
-
91
- return nockDef
92
89
  }
93
90
 
94
91
  function generateRequestAndResponse({
package/lib/scope.js CHANGED
@@ -347,11 +347,12 @@ function define(nockDefs) {
347
347
  body = undefined
348
348
  }
349
349
 
350
- // Response is not always JSON as it could be a string or binary data or
351
- // even an array of binary buffers (e.g. when content is encoded)
350
+ // Response is not always JSON as it could be a string or binary data or
351
+ // even an array of binary buffers (e.g. when content is encoded).
352
352
  let response
353
353
  if (!nockDef.response) {
354
354
  response = ''
355
+ // TODO: Rename `responseIsBinary` to `reponseIsUtf8Representable`.
355
356
  } else if (nockDef.responseIsBinary) {
356
357
  response = Buffer.from(nockDef.response, 'hex')
357
358
  } else {
package/lib/socket.js CHANGED
@@ -12,8 +12,10 @@ module.exports = class Socket extends EventEmitter {
12
12
  this.authorized = true
13
13
  }
14
14
 
15
+ this.bufferSize = 0
15
16
  this.writable = true
16
17
  this.readable = true
18
+ this.pending = false
17
19
  this.destroyed = false
18
20
  this.connecting = false
19
21
 
@@ -23,13 +25,27 @@ module.exports = class Socket extends EventEmitter {
23
25
  this.totalDelayMs = 0
24
26
  // Maximum allowed delay. Null means unlimited.
25
27
  this.timeoutMs = null
28
+
29
+ const ipv6 = options.family === 6
30
+ this.remoteFamily = ipv6 ? 'IPv6' : 'IPv4'
31
+ this.localAddress = this.remoteAddress = ipv6 ? '::1' : '127.0.0.1'
32
+ this.localPort = this.remotePort = parseInt(options.port)
26
33
  }
27
34
 
28
35
  setNoDelay() {}
29
36
  setKeepAlive() {}
30
37
  resume() {}
38
+ ref() {}
31
39
  unref() {}
32
40
 
41
+ address() {
42
+ return {
43
+ port: this.remotePort,
44
+ family: this.remoteFamily,
45
+ address: this.remoteAddress,
46
+ }
47
+ }
48
+
33
49
  setTimeout(timeoutMs, fn) {
34
50
  this.timeoutMs = timeoutMs
35
51
  if (fn) {