GithubHelp home page GithubHelp logo

mswjs / interceptors Goto Github PK

View Code? Open in Web Editor NEW
491.0 8.0 113.0 2.1 MB

Low-level network interception library.

Home Page: https://npm.im/@mswjs/interceptors

License: MIT License

JavaScript 1.87% TypeScript 98.13%
node http https xhr request mock intercept interceptor nodejs request-interception

interceptors's People

Contributors

95th avatar alexk111 avatar atti187 avatar avivasyuta avatar benehiko avatar blaenk avatar dependabot[bot] avatar gribnoysup avatar hamishhossack avatar hpohlmeyer avatar kalopilato avatar kettanaito avatar leonardodino avatar marcosvega91 avatar mathanpec avatar mattcosta7 avatar mikicho avatar msutkowski avatar oscard0m avatar phryneas avatar raon0211 avatar rrogowski avatar scwr avatar sean-hernon avatar simmzl avatar tanem avatar thomas-p-wilson avatar tommos0 avatar tonykhaov avatar weyert avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

interceptors's Issues

Always return full "MockedResponse" instance in the "response" event

Current type annotation is invalid, as Partial<MockedResponse> implies any of the mocked response properties could be missing. That is not true, as the library ensures all the mocked response properties are set (the values can still be undefined for properties like headers or body, but not for status or statusText).

ClientRequestOverride.end() does not accept chunk/encoding

Discovered in mswjs/msw#275

Current behavior

.end() method does not accept arguments like chunk/encoding according to the NodeJS specification.

Because of this, request issuing libraries that post a request body via req.end(body) are failing when it comes to performing an original request (request body is never written, as req.write() is never called).

Expected behavior

.end() behaves according to the aforementioned specification, meaning:

  1. It accepts an optional chunk argument. Appends that chunk to the existing request body.
  2. It accepts an optional encoding argument. Propagates that encoding to the original req.end() call.

Does not work well with jest 27

Jest 27 removes setImmediate, which is used in createClientRequestOverride.

Currently, I get the following in my tests:

ReferenceError: setImmediate is not defined
          at ClientRequestOverride.write (~/node_modules/@mswjs/interceptors/src/interceptors/ClientRequest/createClientRequestOverride.ts:124:7)
          at writeToStream (~/node_modules/node-fetch/lib/index.js:653:8)
          at ~/node_modules/node-fetch/lib/index.js:1627:3

Crash when response is empty

Hi,

Thanks for your work !

Since last version, when the response is empty my tests with msw crashes :
image
image
image

Is the response now mandatory ?

Regards,
Joccd

ClientRequest patch is never restored

The following patch of the ClientRequest class doesn't seem to get restored anywhere:

http.ClientRequest = ClientRequestOverride

Solution

Restore the http.ClientRequest to the pureClientRequest (stored original class) in the cleanup function of interceptClientRequest:

return () => {
debug('restoring modules...')
for (const requestModule of pureModules.values()) {
requestModule.module.get = requestModule.get
requestModule.module.request = requestModule.request
}
pureModules.clear()
}

GitHub

Allow access to original response in the middleware

Hey y'all ๐Ÿ‘‹

First of all thanks for all the hard work on this library, it's nice to see this focused, low-level approach to intercepting requests.

I think there is one feature that is currently doesn't seem to be possible to achieve with this interceptor and I'm wondering if you would be interested in looking into solving this use-case.

I was about to use this lib to monitor outgoing requests of a node.js service, and what do you know there is even an example of this in the readme:

interceptor.use((req) => {
  // Will print to stdout any outgoing requests
  // without affecting their responses
  console.log('%s %s', req.method, req.url.href)
})

One issue here though, it would be valuable to also be able to monitor response statusCode on response end, but there is no way to hook up to original response: req is not a real request instance, it's just normalized request options, so there is no way to attach 'response' event listener, and the second argument ref is the mocked response instance, not the original one (which makes sense, but doesn't help with the issue too).

Effectively if you don't want to use normalized req to send it yourself (and it's currently missing a bunch of http.request options to do it safely) there is no way (as far as I see) to intercept incoming response. If not for monitoring, I can imagine this being useful for e.g., recording and replaying functionality, where you would actually want to conditionally either bypass the interceptor if there are no recordings or respond immediately with with a record if it exists.

One idea of the top of my head is to pass the instance of the ClientRequest to the middleware handler:

export type RequestMiddleware = (
  req: InterceptedRequest,
  ref: IncomingMessage | XMLHttpRequest,
  req: http.ClientRequest,
) => ReturnedResponse | Promise<ReturnedResponse>

This would allow end users of the library to subscribe to response event that will allow them to access original response info.

I'm happy to implement it, but wanted to check first if it's something that aligns with your vision of this library and get your thoughts on the subject.

Include request ID in `debug` calls

That way it's possible to narrow debugging per specific request. Useful when debugging parallel requests.

How

  1. Generate a reproducible request ID for req.url and the timestamp. Do this as soon as the all the prerequisites are known, so debugging is consistent and tight to the request ID through the library's execution.
  2. Create a custom debug() instance and use it everywhere.

Socket.setNoDelay: TypeError: Cannot read property 'apply' of undefined

What

When using a supertest library there's the following exception:

TypeError: Cannot read property 'apply' of undefined
    at callSocketMethod (_http_client.js:708:27)
    at onSocket (_http_client.js:716:7)
    at ClientRequestOverride._deferToConnect (_http_client.js:725:5)
    at ClientRequestOverride.setNoDelay (_http_client.js:758:8)
    at Test.Request.request (/Users/kettanaito/Projects/contrib/msw-supertest/node_modules/superagent/lib/node/index.js:628:7)
    at Test.Request.end (/Users/kettanaito/Projects/contrib/msw-supertest/node_modules/superagent/lib/node/index.js:767:8)
    at Test.end (/Users/kettanaito/Projects/contrib/msw-supertest/node_modules/supertest/lib/test.js:125:7)
    at Object.<anonymous> (/Users/kettanaito/Projects/contrib/msw-supertest/index.js:29:4)
    at Module._compile (internal/modules/cjs/loader.js:778:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:789:10)

Why

The supertest has its own request abstraction, one of the steps of which calls req.socket.setNoDelay() method on the Socket instance of the request. The setNoDelay() method is not polyfilled by NRI, which causes an exception.

Node's _http_client.js may call arbitrary Socket methods during socket's lifecycle.

TypeError: Cannot read property 'URL' of null

This error seems to be coming from node-request-interceptor while I'm running my tests with msw

(node:183) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'URL' of null
    at new XMLHttpRequest (/app/node_modules/jsdom/lib/jsdom/living/xmlhttprequest.js:113:42)
    at /app/node_modules/node-request-interceptor/lib/XMLHttpRequest/XMLHttpRequest/createXMLHttpRequestOverride.js:211:45

That line is marked by HERE below:

module.exports = function createXMLHttpRequest(window) {
  class XMLHttpRequest extends XMLHttpRequestEventTarget.interface {
    constructor() { // eslint-disable-line constructor-super
      const theThis = Object.create(new.target.prototype);
      XMLHttpRequestEventTarget.setup(theThis);
      theThis.upload = XMLHttpRequestUpload.create();
      theThis.upload._ownerDocument = window.document;

      theThis[xhrSymbols.flag] = {
        synchronous: false,
        withCredentials: false,
        mimeType: null,
        auth: null,
        method: undefined,
        responseType: "",
        requestHeaders: {},
        referrer: theThis._ownerDocument.URL,      // <----------------- HERE
        uri: "",
        timeout: 0,
        body: undefined,
        formData: false,
        preflight: false,
        requestManager: theThis._ownerDocument._requestManager,
        strictSSL: window._resourceLoader._strictSSL,
        proxy: window._resourceLoader._proxy,
        cookieJar: theThis._ownerDocument._cookieJar,
        encoding: theThis._ownerDocument._encoding,
        origin: theThis._ownerDocument.origin,
        userAgent: window.navigator.userAgent
      };

Is there something I should do differently to avoid this?

React native - Can't find variable: Buffer

Hi all! I just have enough time to log the issue: this is something introduced by #75 ; Buffer is one of those global vars that don't exists in RN. This is not a critical issue, as even on the user's side a simple global.Buffer = bufferPolyfill will do to fix the issue. Another alternative is to relay on https://www.npmjs.com/package/buffer directly from this package

Running into issues with Got and another

Hey, so working getting a mock backend of sorts setup. Currently, I'm using Got and node-request-interceptor, all on the latest versions. The first issue I'm running into here is not being able to find the withDefaultInterceptors module in node-request-interceptor/presets/default, I get the error Cannot find module 'node-request-interceptor/presets/default' or its corresponding type declarations.. However, if I change the path to node-request-interceptor/lib/presets/default, no more errors are thrown and it finds the module. Maybe I'm doing something wrong here, not sure. Here's the the mock backend -

import { RequestInterceptor } from 'node-request-interceptor';
// import withDefaultInterceptors from 'node-request-interceptor/lib/presets/default'; // this bad
import withDefaultInterceptors from 'node-request-interceptor/lib/presets/default'; // this good
import { apiUrl } from '../config.json';

export default class MockBackend {
    public logger;
    public interceptor;

    constructor(logger) {
        this.logger = logger;
        this.interceptor = new RequestInterceptor(withDefaultInterceptors);
    }

    intercept() {
        this.interceptor.use((req) => {
            if ([apiUrl].includes(req.url.origin)) {
                return {
                    status: 301,
                    headers: {
                        'x-powered-by': 'node-request-interceptor',
                    },
                    body: JSON.stringify({
                        message: 'Hey, I am a mocked response',
                    }),
                }
            }
        })
    }

}

Anywho, next issue, whenever I'm using Got, it throws an error,

(node:8924) UnhandledPromiseRejectionWarning: TypeError [ERR_INVALID_ARG_TYPE]: The "timeout" argument must be of type number. Received an instance of Object
    at validateNumber (internal/validators.js:122:11)
    at getTimerDuration (internal/timers.js:376:3)
    at new ClientRequest (_http_client.js:167:20)
    at request (https.js:314:10)
    at Object.proxiedOriginalRequest (C:\Users\Lyon\Documents\GitHub\squadops-discordbot-v5\node_modules\node-request-interceptor\lib\interceptors\ClientRequest\index.js:54:36)
    at ClientRequestOverride.<anonymous> (C:\Users\Lyon\Documents\GitHub\squadops-discordbot-v5\node_modules\node-request-interceptor\lib\interceptors\ClientRequest\ClientRequestOverride.js:235:39)
    at step (C:\Users\Lyon\Documents\GitHub\squadops-discordbot-v5\node_modules\node-request-interceptor\lib\interceptors\ClientRequest\ClientRequestOverride.js:33:23)
    at Object.next (C:\Users\Lyon\Documents\GitHub\squadops-discordbot-v5\node_modules\node-request-interceptor\lib\interceptors\ClientRequest\ClientRequestOverride.js:14:53)
    at fulfilled (C:\Users\Lyon\Documents\GitHub\squadops-discordbot-v5\node_modules\node-request-interceptor\lib\interceptors\ClientRequest\ClientRequestOverride.js:5:58)
    at processTicksAndRejections (internal/process/task_queues.js:97:5)
(node:8924) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhan
dled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 2)

I have no idea why it does this, clearly. Here is the file relating that calls this -

import { apiUrl } from '../config.json';
import got from 'got';

export default class OpsService {
    public logger;
    public path = apiUrl;

    constructor(logger: any) {
        this.logger = logger;

    }

    async getSession(sess_id) {
        const requestPath = `${this.path}/sessions/${sess_id}`;
        this.logger.warn('Ops Service',`Executing call to ${requestPath}`);
        const body = await got.get(requestPath);
        console.log(body);
    }
}

requestPath is valid, otherwise Got would have thrown an error. Kind of clueless here as to why it throws this error. Any help would be much appreciated.

Jest@27 compatibility

Issues

  • JSDOM is no longer the default test environment.
  • setImmediate is not defined (#123)
  • TypeError [ERR_INVALID_ARG_TYPE]: The "options.agent" property must be one of Agent-like Object, undefined, or false. Received an instance of Object
  • Test functions cannot both take a 'done' callback and return something. Either use a 'done' callback, or return a promise. (previously less strict).
  • ReferenceError: browserType.launch: setImmediate is not defined when running in-browser tests.

XMLHttpRequest: readystatechange listener is called twice for unhandled requests

Code

https://github.com/mswjs/node-request-interceptor/blob/b19c82db06fc9c92a8606bd02ea967a1391d7837/src/interceptors/XMLHttpRequest/XMLHttpRequestOverride.ts#L358-L364

See the workaround test:

https://github.com/mswjs/node-request-interceptor/blob/b19c82db06fc9c92a8606bd02ea967a1391d7837/test/compliance/XMLHttpRequest/events-order.test.ts#L75-L88

Expected behavior

The order of the XMLHttpRequest events must be this:

['loadstart', 1],
['readystatechange', 2],
['readystatechange', 4],
['load', 4],
['loadend', 4],

There must be no duplicate ['readystatechange', 4] event.

Why this is happening?

XMLHttpRequest polyfill from JSDOM executes the attached readystatechange listeners as a part of XMLHttpRequest life-cycle, which is correct. However, somehow the way that the listener is attached makes it being called twice.

xhr.upload is null

I've got code that looks like this:

const xhr = new XMLHttpRequest()
xhr.upload.addEventListener("progress", (e) => {
  // ...
})

When I run my test I get this error:

    TypeError: Cannot read property 'addEventListener' of null

      40 |         const xhr = new XMLHttpRequest()
      41 |         console.log(xhr)
    > 42 |         xhr.upload.addEventListener("progress", (e) => {

That console.log reveals that the upload property of the class is set to null.

   XMLHttpRequestOverride {
      upload: null
    }

If someone points me in the right direction, I'd be open to filling in this gap.

interceptor.on('response'...) not catching any events

i have the following code in my project:

const interceptor = new (createInterceptor as any)({
    modules: nodeInterceptors,
});
interceptor.apply();

interceptor.on('request', (request: IsomorphicRequest) => {
    this.logger.log(
        '=============request============\n\n%s\n\n========================',
        JSON.stringify(request)
    );
});
interceptor.on(
    'response',
    (req: IsomorphicRequest, res: IsomorphicResponse) => {
        this.logger.log(
            '=============request============\n\n%s\n=============response============\n\n%s\n\n========================',
            JSON.stringify(req),
            JSON.stringify(res)
        );
    }
);

process.on('disconnect', () => {
    interceptor.restore();
});

the listener on request works fine, and logs every request. But, the listener on response is never logging anything. am i missing any setup/config?

Support "x-msw-bypass" request header

Current behavior

Request with the x-msw-bypass header is matched against the request middleware. This means that the bypass header does not affect the mocking behavior as it should.

Expected behavior

Whenever encountered a request with the x-msw-bypass header, there must be no matching against the request middleware. Such request should be performed as-is, regardless if there's a middleware that can handle it.

Motivation

To preserve the same behavior present in the ctx.fetch() in MSW.

TypeError [ERR_INVALID_PROTOCOL]: Protocol "http:" not supported. Expected "https:"

Error Message

env

  • Node 14
  • testing react application using msw, msw/node.

messages

    TypeError [ERR_INVALID_PROTOCOL]: Protocol "http:" not supported. Expected "https:"

      at Object.proxiedOriginalRequest (node_modules/node-request-interceptor/lib/http/override.js:55:36)
      at ClientRequestOverride.<anonymous> (node_modules/node-request-interceptor/lib/http/ClientRequest/ClientRequestOverride.js:244:39)
      at step (node_modules/node-request-interceptor/lib/http/ClientRequest/ClientRequestOverride.js:33:23)
      at Object.next (node_modules/node-request-interceptor/lib/http/ClientRequest/ClientRequestOverride.js:14:53)
      at fulfilled (node_modules/node-request-interceptor/lib/http/ClientRequest/ClientRequestOverride.js:5:58)

Screen Shot 2020-08-16 at 0 57 25

Why this happen?

This happens when mock https request.

This error occurs because there is no cert property in options.
so options.protocol always set as http:

https://github.com/mswjs/node-request-interceptor/blob/master/src/utils/getUrlByRequestOptions.ts#L22-L23

    console.log(options); // No 'cert' property in `options` object.

    // Assume HTTPS if using an SSL certificate.
    options.protocol = options.cert ? 'https:' : DEFAULT_PROTOCOL

Attachment

console.log(options) result

Possible Solution

I think, options.protocol value should be decided by options.uri.protocol === 'https:'

Example, Reproduce

https://github.com/gwanduke/example-node-request-interceptor-issue-45

App.js

...
    fetch('https://example.com')
...

App.test.js

setupServer(
  rest.get('https://example.com', (req, res, ctx) => {
    return res(
      ctx.json({})
    );
  })
);

test('render', async () => {
    render(<App />);
});

Requests to relative URL fail

Steps to reproduce

  1. Perform a fetch('/login') request in Node-like environment.
  2. See the following error from the XMLHttpRequest class:
    TypeError: Invalid URL: /login

      2152 |             this.readyState = this.LOADING;
      2153 |             this.data = data || '';
    > 2154 |             const url = new URL(this.url);
           |                         ^
      2155 |             const req = {
      2156 |                 url: cleanUrl(url),
      2157 |                 method: this.method,

XMLHttpRequest: getResponseHeader() is case sensitive

Hi all! I've had an issue testing an auth integration with MSW and traced it to case sensitivity in the getResponseHeader function when retrieving the header value. I've created a PR with more detail here: #89

It looks like the current implementation was intentional so I'm keen to hear more if my PR is a breaking change for anyone? Either way I hope there's some way around this!

Node request interceptor is getting skipped for the response interceptor

When attaching a debugger to the following code (with breakpoints set at the request and response interceptors):

export class NodeHttpClient implements Provider<AxiosLike> {
	value() {
		const interceptor = createInterceptor({
			modules: nodeInterceptors,
			resolver(request) {},
		});

		interceptor.on('request', (request) => {
			console.log(request);
			return request;
		});

		interceptor.on('response', (request, response) => {
                        console.log(request, response);
                        return response;
		});

		const instance = axios.create();
		instance.defaults.adapter = require('axios/lib/adapters/http');
		interceptor.apply();
		return instance;
	}
}

it appears as if the request interceptor is skipped and execution is started with the response interceptor when a request is initiated. I've tried this with other interceptor modules and they all behave the same way.

I'd appreciate any help looking into this or a direction to be pointed towards to resolve this, especially if it's something minor I'm overlooking.

Thank you!

React native jest runtime

Sorry about this, just an heads up on #37
This, while it correctly allow me to run msw on the application, it broke my tests. Apparently, while running my jest tests, the overrideModules.native.ts is also loaded, which is not the behaviour I was expecting.

I'll look more into this tonight.

axios requests, when using http adapter, can't handle response body as object

I'd been using axios just fine to basically build a much simpler nock-type interface using node-request-interceptor (fantastic lib btw! thanks for providing it), but then i realized that in my real tests, which will be consuming the interface i'm making, i'm using

  1. inside jest
  2. using axios.defaults.adapter = require('axios/lib/adapters/http');.

so once i remembered and i set that adapter line (2^), my code, which was working beautifully, started throwing:

TypeError [ERR_INVALID_ARG_TYPE]: The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type object

Here's a repro file, which throws unless you assign a string to body: or if you comment out the axios.defaults.adapter line.

'use strict';

const { RequestInterceptor } = require('node-request-interceptor');
const withDefaultInterceptors = require('node-request-interceptor/lib/presets/default').default;

const interceptor = new RequestInterceptor(withDefaultInterceptors);

function handleResponse(request) {
    return {
        status: 200,
        headers: {
            some: 'header'
        },
        body: {
            valueFromObject: 'thisWorkedWithTheOtherAxiosAdapter'
        }
    };
}

interceptor.use(handleResponse);

const axios = require('axios');
axios.defaults.adapter = require('axios/lib/adapters/http');

describe('Repo my test that is breaking', () => {
    it('Returns the expecting string error', async () => {
        const iWontGetHere = await axios('http://theurlshouldntmatter.com');

        console.log(iWontGetHere);
    });
});

Thank you in advance! I tried JSON.stringify()ing the object, but then it escapes the JSON characters and iWontGetHere.data becomes a string rather than the object i would be expecting

XMLHttpRequest does not inherit the "timeout" option

When intercepting an XMLHttpRequest instance, NRI doesn't respect its timeout option. This causes tools that utilize timeout to fail, as performing such requests originally omits the previously assigned timeout.

EXTERNAL_TOOL (timeout) -> NRI -> captured, perform original -> original (NO timeout)

Expected behavior

XMLHttpRequest instances inherit and propagate the timeout property. This only affects XHR issues in async mode.

Wrong response, when responseType equals "json"

Hello

I use mock-service-worker for writing tests in my application and I faced a problem. My application use rxjs/ajax for making network request and rxjs/ajax expects xhrRequest.response to be a javascript object, when xhrRequest.responseType equals "json"(this behavior is described here https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/response), but mock-service-worker, that using node-request-interceptor, returns xhrRequest.response type of string.

This behavior makes my application work differently in tests and in the browser. Is this behavior a bug in node-request-interceptor?

Getting "TypeError [ERR_INVALID_ARG_TYPE]: The "timeout" argument must be of type number. Received an instance of Object" when using "got"

When using msw on node for requests created by got I'll get the following error.

    at validateNumber (internal/validators.js:129:11)
    at getTimerDuration (internal/timers.js:381:3)
    at new ClientRequest (_http_client.js:170:20)
    at request (http.js:50:10)
    at Object.proxiedOriginalRequest (./node_modules/node-request-interceptor/src/interceptors/ClientRequest/index.ts:67:29)
    at ClientRequestOverride.<anonymous> (./node_modules/node-request-interceptor/src/interceptors/ClientRequest/ClientRequestOverride.ts:268:15)
    at step (./node_modules/node-request-interceptor/lib/interceptors/ClientRequest/ClientRequestOverride.js:33:23)
    at Object.next (./node_modules/node-request-interceptor/lib/interceptors/ClientRequest/ClientRequestOverride.js:14:53)
    at fulfilled (./node_modules/node-request-interceptor/lib/interceptors/ClientRequest/ClientRequestOverride.js:5:58)
    at processTicksAndRejections (internal/process/task_queues.js:93:5)

The initial requested created by me doesn't even contain a timeout value.
I've created a minimal repo to showcase the error: https://github.com/KnisterPeter/node-request-interceptor-with-got-issue

ClientRequest headers are not normalized

What

Current implementation proxies options.headers to the intercepted request instance:

https://github.com/mswjs/node-request-interceptor/blob/e743f0f41faf96d0a53c459ba09ddf99946bf547/src/http/ClientRequest/ClientRequestOverride.ts#L118-L123

Why

Different request issuing libraries format headers differently. For instance:

  • node-fetch capitalizes all headers names;
  • whatwg-fetch converts header names to start with lowercase.

This difference makes it problematic to properly asserts an intercepted request headers in tests.

How

Headers names must be formatted to start with lowercase.

Improve debugging experience

Why

Current build setup produces JavaScript artifacts that are hard to debug (compile code is not readable).

How

The build process should be minimal (stripping off type declarations). Code transformation shouldn't be needed, as the target version of node (>=10) should support all the features used in the library's source code.

Two concurrent requests to the same host override each other

Steps to reproduce

http.request('http://httpbin.org/get')
http.request('http://httpbin.org/get', { headers: { 'foo': 'bar' } })

Current behavior

When accessed in the middleware function, the second request will not have the headers present, as the first request interferes with it.

Expected behavior

Each of the hosts must be processed separately.

Clues

  • Request identity is not respected
  • Watch out for mutable entities, make sure they never persist between requests

Distribute interceptors properly

Expected behavior

  • Exported under node-request-interceptor/interceptors/[name]
  • Distributed in a format that would allow them to be imported from both ESM and CJS context (currently importing an interceptor directly from a ESM context doesn't compile, it strips away named export of SocketPolyfill).

Cloning request options with a [Object: null prototype] value throws an exception

When cloning an object like this:

cloneObject({
  key: Object.create(null)
})

The internal cloneObject function throws the following exception:

TypeError: Cannot read property 'name' of undefined

Objects with null prototype don't have a constructor. The cloneObject function must check the constructor's existence before accessing it.

Intercepting all requests

Hello,

I'm trying to intercept all network requests on a web app. I'd like to record everything that shows up in the Networks tab in the browser developer tools (except for web sockets).

I've added interceptors to my web app:

const interceptor = createInterceptor({
    modules: browserInterceptors,
    resolver(req, ref) {
        console.log(req.url);

        console.log(ref);
    },
});

interceptor.apply();

I have 2 buttons that make requests:

<button
    onClick={async () => {
        const data = await fetch(
            'https://pokeapi.co/api/v2/pokemon/1'
        );
        const json = await data.json();
        console.info({ json });
    }}
>
    Fetch Request
</button>
<button
    onClick={async () => {
        const oReq = new XMLHttpRequest();
        oReq.open('GET', 'https://pokeapi.co/api/v2/pokemon/1');
        oReq.send();
    }}
>
    XHR
</button>

Problems

  1. The XHR request interceptor is working. The fetch isn't. I'm getting this error for fetch: TypeError: Failed to execute 'json' on 'Response': body stream already read
    • Looks like #113 is related?
  2. Other network requests that are not using XHR are not being intercepted (should they?).

Things I've checked

  1. Using the browserInteceptors module, fetch and XMLHttpRequest are being shimmed. I checked this by running fetch and XMLHttpRequest in the console and the output was not XMLHttpRequest() { [native code] }.

Original HTTPS request misses the "url" request option

Current behavior

Given an HTTPS request constructed like this:

https.request(new URL('...'), { ... })

The request's URL is never set on the original call to https.request:

if (url.protocol === 'https:') {
request = pureMethod(options)

The normalized options in this case contain the url property, which is an unknown property for the function call.

The presence of the unexpected "url" property may be related to how the request options are constructed in the used library (@auth0/nextjs-auth0). This library does not append the "url" property on the request options.

Expected behavior

The original HTTPS request is performed properly, its request's URL is preserved.

This can be achieved the same way the original HTTP requests are handled:

request = pureMethod(url.toString(), options)

By providing the first explicit option (stringified request URL due to the incompatibility between the standard URL class and the import('url').URL that https module relies on), and passing the rest of the request options as the second argument.

XMLHttpRequest: Ensure events order according to the specification

Current behavior

Some of the events are grouped together, not representing the valid flow of an XHR request:

https://github.com/mswjs/node-request-interceptor/blob/50e5aa72ee102a8bec8e2f38e3efc9efa26d5e74/src/interceptors/XMLHttpRequest/XMLHttpRequestOverride.ts#L288-L290

Expected behavior

The XMLHttpRequest events must be dispatched in the order described in the specification to guarantee compatibility for the users.

node-request-interceptor doesn't play nicely with aws-sdk when interceptor is enabled

๐Ÿ‘‹ again (excited to be one of the first users of the low-level lib)

as part of my product, i'm trying to allow users to "lock" the state of their microservice, and cache the responses of any outbound requests. but during this "locking", i get in an intermediate state, where i need some requests to go through transparently (aws), but the rest to be intercepted (requests to e.g. our other services, public APIs, etc)

i have a feeling aws-sdk is doing something sketchy here, but when i just have the interceptor enabled, here's the error i get trying to allow the aws requests to go through (I have some matching logic that returns undefined for them):

(node:74163) UnhandledPromiseRejectionWarning: TypeError [ERR_INVALID_PROTOCOL]: Protocol "http:" not supported. Expected "https:"
    at new ClientRequest (_http_client.js:120:11)
    at request (https.js:289:10)
    at Object.proxiedOriginalRequest (/Users/scottyjacobson/node_modules/node-request-interceptor/lib/interceptors/ClientRequest/index.js:54:36)
    at ClientRequestOverride.<anonymous> (/Users/scottyjacobson/node_modules/node-request-interceptor/lib/interceptors/ClientRequest/ClientRequestOverride.js:241:39)
    at step (/Users/scottyjacobson/node_modules/node-request-interceptor/lib/interceptors/ClientRequest/ClientRequestOverride.js:33:23)
    at Object.next (/Users/scottyjacobson/node_modules/node-request-interceptor/lib/interceptors/ClientRequest/ClientRequestOverride.js:14:53)
    at fulfilled (/Users/scottyjacobson/node_modules/node-request-interceptor/lib/interceptors/ClientRequest/ClientRequestOverride.js:5:58)
    at process._tickCallback (internal/process/next_tick.js:68:7)
    at Function.Module.runMain (internal/modules/cjs/loader.js:834:11)
    at startup (internal/bootstrap/node.js:283:19)
(node:74163) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:74163) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

reproduction steps:

'use strict'

const { RequestInterceptor } = require('node-request-interceptor')
const withDefaultInterceptors = require('node-request-interceptor/lib/presets/default')
  .default

const interceptor = new RequestInterceptor(withDefaultInterceptors)


interceptor.use(() => {
    return undefined
});

const AWS = require('aws-sdk');
AWS.config.update({region:'us-east-1'});

const s3 = new AWS.S3();

s3.getObject({
    Bucket: 'this.bucket.dont.matter',
    Key: 'neither.does.this'
}).promise();

package.json:

{
  "name": "request-interceptor-59",
  "version": "1.0.0",
  "main": "index.js",
  "author": "",
  "license": "ISC",
  "dependencies": {
    "aws-sdk": "^2.759.0",
    "node-request-interceptor": "^0.5.1"
  }
}

i know there's 218937423894 moving pieces in the aws-sdk, and i wish i could get you a more isolated repro case, but the inner workings of aws-sdk's use of http vs https (and yours for that matter) is slightly out of my pay grade. happy to help hunt down the issue if you know what direction to go looking in!

thanks!
Scotty

Respect "username" and "password" when constructing a URL from RequestOptions

What

When constructing a URL instance out of RequestOptions we need to respect options.username and options.password to be set in the respective properties of the URL instance.

Why

To propagate the authentication data to the URL instance.

How

  • Introduce a separate getUrlByRequestOptions utility.
  • Map the username and password properties to the created URL instance.

NextJS: The "superCtor" argument must be of type function

Hi, I am using msw with nextjs and I came across an error when working on the api route. The error log points to this library so I'm here.

Steps to reproduce

here is a minimal repo: https://github.com/dpyzo0o/next-msw-bug-demo

  1. yarn dev, which enables msw. Then try to click download image, the first time it will probably succeed (sometimes it also fails the first time). Then do a hard refresh of the page and click download image again. This time it will error out, the error message shown below points to node-request-interceptor

Screen Shot 2020-08-19 at 17 16 28

  1. If you build the app with yarn build && yarn start, which basically disables msw, it will never throw this error and everything works.

I'd really appreciate it if you could help me.

multiple interceptors causing restore problem

We know that there is a problem when using multiple interceptors. restore function will remove monkey patches even if there are other interceptors. The last one is mswjs/msw#637

The solution of this problem is not really easy but what you think if we add a counter on the interceptor? I know that is not a complete solution but could be a first start

--- a/src/createInterceptor.ts
+++ b/src/createInterceptor.ts
@@ -63,12 +63,15 @@ export interface InterceptorApi {
   restore(): void
 }
 
+let interceptosCounter = 0
+
 export function createInterceptor(options: InterceptorOptions): InterceptorApi {
   const observer = new StrictEventEmitter<InterceptorEventsMap>()
   let cleanupFns: InterceptorCleanupFn[] = []
 
   return {
     apply() {
+      interceptosCounter++
       cleanupFns = options.modules.map((interceptor) => {
         return interceptor(observer, options.resolver)
       })
@@ -78,14 +81,16 @@ export function createInterceptor(options: InterceptorOptions): InterceptorApi {
     },
     restore() {
       observer.removeAllListeners()
-
+      interceptosCounter--
       if (cleanupFns.length === 0) {
         throw new Error(
           `Failed to restore patched modules: no patches found. Did you forget to run ".apply()"?`
         )
       }
 
-      cleanupFns.forEach((restore) => restore())
+      if (interceptosCounter === 0) {
+        cleanupFns.forEach((restore) => restore())
+      }
     },
   }
 }

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.