denoland / deno

A modern runtime for JavaScript and TypeScript.

Home Page:https://deno.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

fetch(..., {redirect: 'manual'}) does not work propertly

jakajancar opened this issue · comments

https://httpbin.org/status/302 is a url that does a few redirects, ending at https://httpbin.org/get.

fetch('https://httpbin.org/status/302')
  .then(res => console.log(res.status, res.headers))

Follows redirects, prints status 200, headers, as expected.

fetch('https://httpbin.org/status/302', {redirect: 'manual'})
  .then(res => console.log(res.status, res.headers))

Prints status 0, empty headers.

Expected: status 302, headers including Location.

I'm pretty sure this is currently the intended behaviour? https://fetch.spec.whatwg.org/#concept-request-redirect-mode says

"manual"
Retrieves an opaque-redirect filtered response when a request is met with a redirect so that the redirect can be followed manually.

An opaque-redirect filtered response is a filtered response whose type is "opaqueredirect", status is 0, status message is the empty byte sequence, header list is empty, and body is null.

Fixing #3767 should basically provide the same user accessible API as I understand it. I think want @jakajancar wants is something like the noredirect mode that I temporary implemented in a feature branch a while back.

https://stackoverflow.com/a/42717388 explains why redirect: manual actually exists.

Also, @bartlomieju I'm not really available to help with deno right now, as I unfortunately have school work that is higher priority. I am sorry about this, because I know that there are a few fetch issues that have yet to be resolved, but there's not much I can do right now.

Thanks @serverhiccups for the context.

My thinking is the server will always have slightly different use-cases and does not need the same CORS limitations as the browser. I see these options:

  1. Do nothing, maintain CORS limitations.

    This means one needs to fallback to Deno.connect and re-build HTTP stuff, IMO bad.

  2. Maintain API, but loosen CORS restrictions

    Probably the simplest. What works in browser will work in Deno (perhaps slightly differently, missing preflights). What works in Deno will not necessarily work in browser. Not sure if anyone is relying on Deno to fail in some cases.

  3. Extend API:

    1. For specific use-cases (e.g. your redirect: noredirect), maybe something similar for safe methods/headers.

    2. Support a new mode, say "no-security" flag that removes all limitations of the Fetch API that stem from CORS concerns.

no-security

If there is a real benefit to emulate browser security behavior on the server-side, and indeed if such concepts as Origin even make sense, and if Deno has the capacity to do this, then I would propose 3.2:

The modes currently are:

"same-origin"
Used to ensure requests are made to same-origin URLs. Fetch will return a network error if the request is not made to a same-origin URL.
"cors"
Makes the request a CORS request. Fetch will return a network error if the requested resource does not understand the CORS protocol.
"no-cors"
Restricts requests to using CORS-safelisted methods and CORS-safelisted request-headers. Upon success, fetch will return an opaque filtered response.
"navigate"
This is a special mode used only when navigating between documents.
"websocket"
This is a special mode used only when establishing a WebSocket connection.

Such "no-security" mode would:

  1. Consider all methods as "CORS-safelisted methods" (instead of just GET, HEAD, POST)
  2. Consider no methods "forbidden methods" (instead of CONNECT, TRACE, TRACK)
  3. Consider all headers "CORS-safelisted request-headers" (ignoring key, value or value length)
  4. Consider no header names "forbidden response-header name"
  5. ... and so on and so on ...

This would be great for testing APIs as well as making requests that are perfectly OK from the server/app, but not the browser context.

Loose

If Deno emulating browser limitations fully is either useless OR non-realistic, I would just go with option 2 and remove these limitations.

Maybe this can be said to be mode: "no-security" and the only supported mode, so we don't pretend to support the browser modes, and those can be implemented later if needed.

I'm a little confused about what you are trying to achieve here.
Are you trying to handle the redirection yourself? Because currently in Deno it's not possible to do that with the current (and mostly standards-compliant) fetch API. Even if there was a no-security mode in our implementation I don't think it would allow you to handle redirection yourself (or at least if it did it would be very confusing).
If you are trying to handle the redirection yourself, we need something like the no-redirect mode discussed here: #3667. If I remember correctly, we decided not to add it because it would be non-standard.

To answer the original issue, redirect: manual does work properly and as intended.
In terms of no-security, I think it's an interesting idea, but something separate from handling redirects. Perhaps you should open a separate issue for it.

Yes exactly. Handle redirection myself (or, not really handle it, just check that it happened and correct Location header was returned).

fetch(url, {redirect: 'manual', mode: 'no-security'})

... returning the full 302 response is my suggestion.

I do like the idea of no-security. However, I feel that that maybe the mode setting might not be the right way of actually implementing it. Perhaps something like opaque: never. That way we can avoid changing behaviour of the fetch API in a way that might be confusing while providing the required functionality (because redirect: manual + opaque: never is basically equivelant to redirect: noredirect).

I'm not sure how much work would be involved in implementing this and whether or not we want to diverge from the spec (pinging @bartlomieju), but if it's only a little I might be able to get it done.

I think you are looking too narrowly. What if I want to do a PUT without preflight?

All the CORS stuff needs a solution, not just redirect.

Hey, I'm affected by this issue as well. I'm trying to log into a service using fetch, but I can't access its headers since it redirects to the main page with a 302 status code.

When using node-fetch in Node, you can set the redirect option to manual, which makes it so it simply doesn't redirect and allows you to access the headers, status, etc. In Deno this gives you a status code of 0, with a body of null and empty headers. I know this isn't how the standard fetch does it, but it's a lot more useful this way.

Is there a way to access this information using fetch? Or any other solution for this?