Skip to content
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

Chrome Dev tools XHR #412

Closed
malykhinvi opened this issue Oct 2, 2020 · 12 comments · Fixed by #417
Closed

Chrome Dev tools XHR #412

malykhinvi opened this issue Oct 2, 2020 · 12 comments · Fixed by #417
Assignees
Labels
bug Something isn't working scope:browser Related to MSW running in a browser

Comments

@malykhinvi
Copy link

Describe the bug

Not sure if it is even a msw problem, but maybe someone knows the answer. For some reason, when msw is enabled, I can not set up a filter to see only XHR requests in Chrome network tab. See how it looks like below (note, that static files are displayed even though the XHR filter is on.)

Environment

  • msw: 0.21.2
  • nodejs: 12.16.1
  • npm: 6.14.8
  • Chrome Version 85.0.4183.121 (Official Build) (64-bit)

To Reproduce

Steps to reproduce the behavior:

  1. Set up msw for browser as described in official docs
  2. Open Chrome Dev Tools
  3. Turn on filter XHR
  4. Reload the page.
  5. You will see all requests.

Expected behavior

Only XHR requests are displayed

Screenshots

Screenshot 2020-10-02 at 15 33 02

@malykhinvi malykhinvi added the bug Something isn't working label Oct 2, 2020
@marcosvega91
Copy link
Member

Hi @malykhinvi thanks for reaching us 😄 .

I have to be honest, I didn't notice that 😆 . BTW I can say that Service Workers are like a proxy. So in your DevTools you should see requests two times. I'm pretty sure it is a normal. You should consider also that the Service Workers will listen for fetch events. Also static files are intercepted by worker, but for those files the request is not forwarded to MSW

@tomadaniel
Copy link

Just installed msw and it's the first thing I noticed. Static assets are displayed twice, once where they should and once as XHR requests. It's causing some confusion.

@kettanaito
Copy link
Member

That's the behavior caused by the current implementation of request handling in the worker. As the worker persists between pages, when the app loads the worker handles all requests. Since you rarely want to mock a static assets (which may render your site broken), the worker skips such requests and performs then as usual.

We can get rid of such requests duplication if it's possible to short-circuit a request handling in the fetch event of the Service Worker. So that we don't return anything in case of a static asset, stating that such request should be performed as is (outside of the worker).

@kettanaito
Copy link
Member

It seems that short circuiting within the fetch event handler function in the worker bypasses the request (it gets performed as-is) without it displaying in the XHR tab. I've issued a pull request with the fix, expect it in the nearest future.

@kettanaito kettanaito added the scope:browser Related to MSW running in a browser label Oct 7, 2020
@malykhinvi
Copy link
Author

Although I see a problem in Chrome dev tools, this is not the case for Firefox. It filters XHR requests just fine. Maybe it is something with dev tools?

@kettanaito
Copy link
Member

Service Worker implementation and thus behavior may differ in different browsers. There's nothing we can or should do about this, unfortunately.

@kettanaito
Copy link
Member

kettanaito commented Oct 8, 2020

During the work on this I've found out a few details that I'd like to share.

Short answer

No, this won't be possible to do due to the Service Worker specification.

Long answer

The worker's "fetch" event triggers for any HTTP request from the client once the worker is active. The fetch event handler is a synchronous function, which may call event.respondWith() to respond with any Response, but it must call it synchronously:

self.addEventListener('fetch', function (event) {
  event.respondWith(anyPromise) // OK
  
  await somePromise()
  event.respondWith(...) // INVALID STATE
})

So there's the first behavior we learn:

  • event.respondWith() must be called synchronously in the "fetch" event handler.

To fetch data in this worker's event handler you can use the fetch() API. Since it's async, we can only use it within the respondWith() Promise:

event.respondWith(async () => {
  return fetch('...').then(data => data.json())
})

Whenever you perform a fetch() call within the worker's scope it gets registered as the XHR in the Network tab. That is the behavior one cannot change. Thus, the next discovery:

  • fetch() calls in the worker's scope are always registered as XHR

Now let's analyze what happens to a request. Specifically, to a static asset request that shouldn't be mocked. Here's the journey it undergoes:

  1. Client loads its JavaScript code.
  2. The JS code registers the worker.
  3. The JS code performs an HTTP request.
  4. Request is caught by the "fetch" event of the worker.
  5. Worker signals to MSW to see if this request should be mocked (async action).
  6. If not, it performs request as is via event.respondWith(fetch(event.request)).

Since the step 5 is async, it must be nested in the event.respondWith() promise. If the request shouldn't be mocked, to perform an original request one needs to call fetch(event.request), which always produces an XHR request. This means that the requests that should be bypassed are performed as XHR and you can see them twice in your browser's Network tab.

There are much more nuances to the async worker behavior. For instance, there is a time window between steps 2 and 3 and if a request happens there it's always bypassed, as that means "the worker is not ready to signal to MSW". Those requests will not be listed as XHR, because the fetch event handler short circuits on those requests outside of the event.respondWith() function call.

If this request duplication in the Network is confusing, this is how you can think of it:

  1. First (non XHR) request is what your app makes.
  2. Second (XHR) request is what the worker makes in order to fetch the original (bypassed) respond data and use it to respond to your first request.

@malykhinvi
Copy link
Author

@kettanaito thanks for your investigation and for a very detailed response! Do you have any idea, why it is possible to filter XHR requests in FF dev tools?

@kettanaito
Copy link
Member

kettanaito commented Oct 8, 2020

As I've mentioned, Service Worker implementation may differ in browsers. I suppose Firefox can understand that this XHR originates from the worker, and hides it in the list of all XHR. Chrome can understand that as well, but doesn't hide those requests. I support Chrome's behavior here, as the worker indeed performs an extra request to use for the actual response, thus it makes sense to list that extra request.

@tomadaniel
Copy link

Really good explanation, it makes sense now. Thank you!

@kettanaito
Copy link
Member

I'm closing the issue because there isn't much we can do on this behavior. The PR that slightly improves the requests bypassing will land in the next minor version. Thanks for raising this!

@mucsi96
Copy link
Contributor

mucsi96 commented Nov 22, 2023

Would it be possible to make the short-circuiting configurable? So for example the consumer can configure everything to be sort-circuited except calls starting with /api.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working scope:browser Related to MSW running in a browser
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants