8000 Release version 14 by mikicho · Pull Request #2813 · nock/nock · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Release version 14 #2813

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 32 commits into from
Jan 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
ad8c80e
ci(test): use a single `test` job that we can require, independent of…
gr2m Feb 3, 2024
8b8c0c2
feat: native fetch mocking (#2580)
Feb 3, 2024
12aa5b6
fix: add fetch to the restore function (#2582)
Feb 5, 2024
8605dae
ci: run an all release branches (#2589)
gr2m Feb 14, 2024
11adf2c
fix: fetch response status text (#2588)
Feb 17, 2024
991a8f3
fix: fetch support compressed responses (#2591)
Feb 24, 2024
e8044c6
fix: replace debug with node debug (#2592)
Uzlopak Feb 26, 2024
652c689
chore: improve coverage of socket.js (#2593)
Uzlopak Feb 26, 2024
fc3a34f
chore(deps-dev): bump sinon from 15.2.0 to 17.0.1 (#2549)
dependabot[bot] Feb 26, 2024
de560ec
docs: remove discarded build status badge (#2601)
aaharu Mar 8, 2024
b37c57f
fix: mapValues should not update the object in place (#2737)
pranaygp Apr 20, 2024
ff75d09
fix: avoid closing an already closed stream (#2747)
May 30, 2024
5c8c4c2
feat: `@mswjs/interceptors` for mocking (#2517)
Jul 13, 2024
8ad3475
fix format and reduce coverage (#2761)
Jul 14, 2024
7c0e667
Support using AbortSignal to abort requests (#2760)
Jul 14, 2024
de7922c
fix: support lowercase HTTP request methods (#2763)
Jul 24, 2024
6585a4e
upgrade interceptors (#2767)
Aug 5, 2024
2eb636f
stop forward request headers (#2769)
Aug 9, 2024
2066e69
fix: memory leaks due to timer references outliving the timers (#2772)
johnp Aug 15, 2024
61683a6
fix: upgrade interceptors (+trigger release) (#2770)
Aug 15, 2024
5dd29a8
fix(fetch): support fetch redirect (#2779)
Sep 9, 2024
a39587f
fix(fetch): rebase relative redirect URL against `request.url` (#2781)
Sep 11, 2024
2c44339
fix(deps): bump `@mswjs/interceptors` to `^0.35.6` (#2783)
tebriel Sep 16, 2024
fc4c1f4
chore(tests): added test for #2780 (#2784)
Sep 16, 2024
4213d99
fix(fetch): await response listener before resolving the response pro…
Oct 7, 2024
451e69c
fix(fetch): support Content-Encoding response header (#2797)
Nov 14, 2024
a143911
fix(fetch): add support for aborting requests using AbortSignal (#2809)
Nov 21, 2024
be35f23
fix(recorder): recompress fetch body on record (#2810)
Nov 25, 2024
3dcae7f
chore: prepare beta for merge (#2814)
Dec 3, 2024
8b8adc2
chore: deprecate delayConnection (#2812)
Dec 4, 2024
c52a9a0
Michael/merge main (#2818)
Dec 4, 2024
c3a23f0
Merge branch 'main' into beta
Dec 4, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .eslintrc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ env:
node: true

parserOptions:
ecmaVersion: 9
ecmaVersion: 2020
# Override eslint-config-standard, which incorrectly sets this to "module",
# though that setting is only for ES6 modules, not CommonJS modules.
sourceType: 'script'
Expand Down
18 changes: 5 additions & 13 deletions .github/workflows/continuous-integration.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ name: Continuous Integration
- synchronize
push:
branches:
- '*.x'
- main
- beta
- next
permissions:
contents: read

Expand All @@ -22,7 +25,7 @@ jobs:
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 16
node-version: lts/*
cache: 'npm'
- name: Install dependencies
run: npm ci --ignore-scripts --no-audit --no-progress --prefer-offline
Expand Down Expand Up @@ -70,23 +73,13 @@ jobs:
fail-fast: false
matrix:
node-version:
- 10
- 12
- 14
- 16
- 18
- 20
- 22
os:
- macos-latest
- ubuntu-latest
- windows-latest
exclude:
- node-version: 10
os: macos-latest
- node-version: 12
os: macos-latest
- node-version: 14
os: macos-latest
runs-on: ${{ matrix.os }}
timeout-minutes: 5

Expand All @@ -106,7 +99,6 @@ jobs:
run: npm run test
- name: Test jest
run: npm run test:jest
if: matrix.node-version >= 14

# separate job to set as required in branch protection,
# as the build names above change each time Node versions change
Expand Down
4 changes: 2 additions & 2 deletions .nycrc.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
all: true
check-coverage: true

branches: 100
lines: 100
branches: 94.59
lines: 96.89

include:
- lib
Expand Down
57 changes: 9 additions & 48 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,11 @@
# Nock

[![npm](https://img.shields.io/npm/v/nock.svg)][npmjs]
[![Build Status](https://travis-ci.org/nock/nock.svg)][build]
![Coverage Status](http://img.shields.io/badge/coverage-100%25-brightgreen.svg)
[![Backers on Open Collective](https://opencollective.com/nock/backers/badge.svg)](#backers)
[![Sponsors on Open Collective](https://opencollective.com/nock/sponsors/badge.svg)](#sponsors)

[npmjs]: https://www.npmjs.com/package/nock
[build]: https://travis-ci.org/nock/nock

> **Notice**
>
> We have introduced experimental support for fetch. Please share your feedback with us. You can install it by:
>
> ```
> npm install --save-dev nock@beta
> ```

HTTP server mocking and expectations library for Node.js

Expand Down Expand Up @@ -694,46 +684,17 @@ You are able to specify the number of milliseconds that your reply should be del
```js
nock('http://my.server.com')
.get('/')
.delay(2000) // 2 seconds delay will be applied to the response header.
.delay(2000) // 2 seconds delay will be applied to the response body.
.reply(200, '<html></html>')
```

`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 the connection

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('/')
.delayConnection(2000) // 2 seconds
.reply(200, '<html></html>')

req = http.request('http://my.server.com', { timeout: 1000 })
```

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.

##### Technical Details

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.

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.
The `delayConnection` method’s behavior of emitting quick timeout events when the connection delay exceeds the request timeout is now deprecated. Please use the `delay` function instead.

#### 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.
The `delayBody` is now deprecated. Please use the `delay` function instead.

```js
nock('http://my.server.com')
Expand Down Expand Up @@ -1643,10 +1604,10 @@ It does this by manipulating the modules cache of Node in a way that conflicts w

## Debugging

Nock uses [`debug`](https://github.com/visionmedia/debug), so just run with environmental variable `DEBUG` set to `nock.*`.
Nock uses node internals [`debuglog`](https://nodejs.org/api/util.html#utildebuglogsection-callbackg), so just run with environmental variable `NODE_DEBUG` set to `nock:*`.

```console
user@local$ DEBUG=nock.* node my_test.js
user@local$ NODE_DEBUG=nock:* node my_test.js
```

Each step in the matching process is logged this way and can be useful when determining why a request was not intercepted by Nock.
Expand All @@ -1660,11 +1621,11 @@ await got('http://example.com/?foo=bar&baz=foz')
```

```console
user@local$ DEBUG=nock.scope:example.com node my_test.js
user@local$ DEBUG=nock:scope:example.com node my_test.js
...
nock.scope:example.com Interceptor queries: {"foo":"bar"} +1ms
nock.scope:example.com Request queries: {"foo":"bar","baz":"foz"} +0ms
nock.scope:example.com query matching failed +0ms
NOCK:SCOPE:EXAMPLE.COM 103514: Interceptor queries: {"foo":"bar"}
NOCK:SCOPE:EXAMPLE.COM 103514: Request queries: {"foo":"bar","baz":"foz"}
NOCK:SCOPE:EXAMPLE.COM 103514: query matching failed
```

## Contributing
Expand Down
4 changes: 1 addition & 3 deletions lib/back.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,9 @@ const {
removeAll: cleanAll,
} = require('./intercept')
const { loadDefs, define } = require('./scope')

const { back: debug } = require('./debug')
const { format } = require('util')
const path = require('path')
const debug = require('debug')('nock.back')

let _mode = null

Expand Down Expand Up @@ -78,7 +77,6 @@ function Back(fixtureName, options, nockedFn) {
}

debug('context:', context)

// If nockedFn is a function then invoke it, otherwise return a promise resolving to nockDone.
if (typeof nockedFn === 'function') {
nockedFn.call(context, nockDone)
Expand Down
108 changes: 29 additions & 79 deletions lib/common.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
'use strict'

const debug = require('debug')('nock.common')
const { common: debug } = require('./debug')
const timers = require('timers')
const url = require('url')
const util = require('util')
const http = require('http')

/**
* Normalizes the request options so that it always has `host` property.
Expand Down Expand Up @@ -50,82 +51,6 @@ function isUtf8Representable(buffer) {
return reconstructedBuffer.equals(buffer)
}

// Array where all information about all the overridden requests are held.
let requestOverrides = {}

/**
* Overrides the current `request` function of `http` and `https` modules with
* our own version which intercepts issues HTTP/HTTPS requests and forwards them
* to the given `newRequest` function.
*
* @param {Function} newRequest - a function handling requests; it accepts four arguments:
* - proto - a string with the overridden module's protocol name (either `http` or `https`)
* - overriddenRequest - the overridden module's request function already bound to module's object
* - options - the options of the issued request
* - callback - the callback of the issued request
*/
function overrideRequests(newRequest) {
debug('overriding requests')
;['http', 'https'].forEach(function (proto) {
debug('- overriding request for', proto)

const moduleName = proto // 1 to 1 match of protocol and module is fortunate :)
const module = require(proto)
const overriddenRequest = module.request
const overriddenGet = module.get

if (requestOverrides[moduleName]) {
throw new Error(
`Module's request already overridden for ${moduleName} protocol.`,
)
}

// Store the properties of the overridden request so that it can be restored later on.
requestOverrides[moduleName] = {
module,
request: overriddenRequest,
get: overriddenGet,
}
// https://nodejs.org/api/http.html#http_http_request_url_options_callback
module.request = function (input, options, callback) {
return newRequest(proto, overriddenRequest.bind(module), [
input,
options,
callback,
])
}
// https://nodejs.org/api/http.html#http_http_get_options_callback
module.get = function (input, options, callback) {
const req = newRequest(proto, overriddenGet.bind(module), [
input,
options,
callback,
])
req.end()
return req
}

debug('- overridden request for', proto)
})
}

/**
* Restores `request` function of `http` and `https` modules to values they
* held before they were overridden by us.
*/
function restoreOverriddenRequests() {
debug('restoring requests')
Object.entries(requestOverrides).forEach(
([proto, { module, request, get }]) => {
debug('- restoring request for', proto)
module.request = request
module.get = get
debug('- restored request for', proto)
},
)
requestOverrides = {}
}

/**
* In WHATWG URL vernacular, this returns the origin portion of a URL.
* However, the port is not included if it's standard and not already present on the host.
Expand Down Expand Up @@ -621,6 +546,7 @@ function clearTimer(clear, ids) {
}

function removeAllTimers() {
debug('remove all timers')
clearTimer(clearTimeout, timeouts)
clearTimer(clearImmediate, immediates)
}
Expand Down Expand Up @@ -653,6 +579,31 @@ function isRequestDestroyed(req) {
)
}

/**
* @param {Request} request
*/
function convertFetchRequestToClientRequest(request) {
const url = new URL(request.url)
const options = {
...urlToOptions(url),
method: request.method,
host: url.hostname,
port: url.port || (url.protocol === 'https:' ? 443 : 80),
path: url.pathname + url.search,
proto: url.protocol.slice(0, -1),
headers: Object.fromEntries(request.headers.entries()),
}

// By default, Node adds a host header, but for maximum backward compatibility, we are now removing it.
// However, we need to consider leaving the header and fixing the tests.
if (options.headers.host === options.host) {
const { host, ...restHeaders } = options.headers
options.headers = restHeaders
}

return new http.ClientRequest(options)
}

/**
* Returns true if the given value is a plain object and not an Array.
* @param {*} value
Expand Down Expand Up @@ -760,12 +711,11 @@ module.exports = {
normalizeClientRequestArgs,
normalizeOrigin,
normalizeRequestOptions,
overrideRequests,
percentDecode,
percentEncode,
removeAllTimers,
restoreOverriddenRequests,
setImmediate,
setTimeout,
stringifyRequest,
convertFetchRequestToClientRequest,
}
53 changes: 53 additions & 0 deletions lib/create_response.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
'use strict'

const { STATUS_CODES } = require('http')

/**
* Creates a Fetch API `Response` instance from the given
* `http.IncomingMessage` instance.
* Inspired by: https://github.com/mswjs/interceptors/blob/04152ed914f8041272b6e92ed374216b8177e1b2/src/interceptors/ClientRequest/utils/createResponse.ts#L8
*/

/**
* Response status codes for responses that cannot have body.
* @see https://fetch.spec.whatwg.org/#statuses
*/
const responseStatusCodesWithoutBody = [204, 205, 304]

/**
* @param {import('http').IncomingMessage} message
* @param {AbortSignal} signal
*/
function createResponse(message, signal) {
const responseBodyOrNull = responseStatusCodesWithoutBody.includes(
message.statusCode || 200,
)
? null
: new ReadableStream({
start(controller) {
message.on('data', chunk => controller.enqueue(chunk))
message.on('end', () => controller.close())
message.on('error', error => controller.error(error))
signal.addEventListener('abort', () => message.destroy(signal.reason))
},
cancel() {
message.destroy()
},
})

const rawHeaders = new Headers()
for (let i = 0; i < message.rawHeaders.length; i += 2) {
rawHeaders.append(message.rawHeaders[i], message.rawHeaders[i + 1])
}

// @mswjs/interceptors supports rawHeaders. https://github.com/mswjs/interceptors/pull/598
const response = new Response(responseBodyOrNull, {
status: message.statusCode,
statusText: message.statusMessage || STATUS_CODES[message.statusCode],
headers: rawHeaders,
})

return response
}

module.exports = { createResponse }
Loading
Loading
0