resty icon indicating copy to clipboard operation
resty copied to clipboard

Method changes on redirect

Open OJFord opened this issue 5 years ago • 12 comments
trafficstars

If the redirect policy allows redirection, and the request method was initially POST; on the redirected request it's no longer POST, and the response may be 405 Method Not Allowed (very confusing if the logs aren't showing that redirection happened!) or an unexpected response/effect in the event that the method is allowed.

OJFord avatar Feb 29 '20 19:02 OJFord

@OJFord I think you can take full control of all redirect(s) via policy. By default resty does not assign any redirect policy to the client.

jeevatkm avatar Mar 02 '20 03:03 jeevatkm

@jeevatkm I'm not sure I understand - would it ever be desirable that a POST resulting in a redirect ends up being a GET on the redirected-to resource?

The behaviour I saw by default (not specifying a redirect policy) was the same as when I supplied one that allowed the redirect (DomainCheckRedirectPolicy) - but it wasn't POSTed and as a result I noticed the error because the server responded 405, since it only allowed POST on that endpoint.

OJFord avatar Mar 02 '20 10:03 OJFord

would it ever be desirable that a POST resulting in a redirect ends up being a GET on the redirected-to resource?

@OJFord It certainly possible. Because on the server-side if they redirect POST to GET then HTTP clients simply going to follow the redirects. So the client is not going to take a decision on its own.

Define a custom redirect policy on your client to investigate it.

client.SetRedirectPolicy(RedirectPolicyFunc(func(req *http.Request, via []*http.Request) error {
	// log all the info you want for the investigation       
	return nil
}))

If you do not want to follow the redirect kindly use client.SetRedirectPolicy(NoRedirectPolicy())

jeevatkm avatar Mar 03 '20 06:03 jeevatkm

The redirect response is 301, methods are not supposed to change, but MDN notes:

there are existing user agents that do change their method. 308 was created to remove the ambiguity of the behavior when using non-GET methods.

The response is just:

$ curl --head --request POST "$host/blah"
HTTP/2 301
server: nginx
date: Tue, 03 Mar 2020 10:56:18 GMT
content-type: text/html; charset=utf-8
content-length: 0
location: /blah/
x-content-type-options: nosniff
x-xss-protection: 1; mode=block
vary: Origin
strict-transport-security: max-age=15768000; includeSubDomains

I do want to follow the redirect, I just don't want the method to change.

OJFord avatar Mar 03 '20 11:03 OJFord

@OJFord will check it again in Resty. Just want to clarify, you're using Resty v2 correct?

jeevatkm avatar Apr 06 '20 23:04 jeevatkm

Sorry for the delay - v2, yes.

OJFord avatar Apr 20 '20 12:04 OJFord

I have the same problem, I send a POST request and it returns 405 because it is changed to GET upon redirect.

MrNocTV avatar Mar 29 '23 07:03 MrNocTV

@MrNocTV Yep, as far as I recall I just got around this by not having using the redirected endpoint in the end. Not as easy it sounds because of some other issue with trailing slashes or something (which was why it was redirecting), but that was easier to solve in the end.

OJFord avatar Mar 29 '23 09:03 OJFord

@OJFord Can you please share an example :D ?

MrNocTV avatar Apr 03 '23 02:04 MrNocTV

@MrNocTV Depends on the API you're using, try in the browser or with curl, check the redirect Location, and use that in your code instead of the one that gets redirected. This assumes it's a static/deterministic-in-known-way redirect though; again, depends on API and why it's redirecting.

OJFord avatar Apr 14 '23 12:04 OJFord

I was struggling with a similar problem, until I noticed that that method change seems to be the right behavior, depending on the server's status code.

According to Go's net/http docs:

If the server replies with a redirect, the Client first uses the CheckRedirect function to determine whether the redirect should be followed. If permitted, a 301, 302, or 303 redirect causes subsequent requests to use HTTP method GET (or HEAD if the original request was HEAD), with no body. A 307 or 308 redirect preserves the original HTTP method and body, provided that the Request.GetBody function is defined. The NewRequest function automatically sets GetBody for common standard library body types.

We've just changed the method from 301 to 308 and the issue had its solution.

https://github.com/golang/go/issues/60769#issuecomment-1590003432

Maybe It isn't the same problem, but I think it could help in some way.

henrybarreto avatar Oct 18 '23 20:10 henrybarreto

Oh interesting, thanks for that. I suppose that explains it then - but frustrating if you're working with a third-party API that behaves differently, even if that isn't RFC compliant.

... Actually though RFC2616 implies that the method should be retained ('when automatically redirecting a POST request'); what it does say - and I wonder if the docs quoted above are perhaps a misreading of this? - is that the user agent must not follow the redirect without confirmation, unless it's GET or HEAD. (In the context of a programming library user agent, I suppose 'user confirmation' is something like setting a followRedirects=True flag from its false default.)

Edit: there is also this note:

Note: RFC 1945 and RFC 2068 specify that the client is not allowed to change the method on the redirected request. However, most existing user agent implementations treat 302 as if it were a 303 response, performing a GET on the Location field-value regardless of the original request method. The status codes 303 and 307 have been added for servers that wish to make unambiguously clear which kind of reaction is expected of the client.

But I think that's been misapplied to 301.

OJFord avatar Oct 19 '23 21:10 OJFord