2

As I was writing a handler for a webservice in bravado (through a request.session()) I came across this peculiarity:

A POST request turns into a GET request as it's being redirected. Causing my POST to be mangled and I can not get the thing to work.

EDIT: The actual requests call is made from bravado as explained in: How do you set a custom http client in Bravado? Which I needed for setting the additional client side certificates.

So the requests call/session itself is being obscured by bravado.

This issue (the post/get transition by requests) has been discussed in other threads where it is considered according to spec, but a weirdness nonetheless.

In the end I just hacked into requests in the following segments in session.py:

def rebuild_method(self, prepared_request, response):
    """When being redirected we may want to change the method of the request
    based on certain specs or browser behavior.
    """


    method = prepared_request.method

    # http://tools.ietf.org/html/rfc7231#section-6.4.4
    if response.status_code == codes.see_other and method != 'HEAD':
        method = 'GET'


    # Do what the browsers do, despite standards...
    # First, turn 302s into GETs.
    if response.status_code == codes.found and method != 'HEAD':
        if method == 'POST':
            #print ( '\n\nDeliberately not changing to GET\n\n' )
        else:
            method = 'GET'

    # Second, if a POST is responded to with a 301, turn it into a GET.
    # This bizarre behaviour is explained in Issue 1704.
    if response.status_code == codes.moved and method == 'POST':
        method = 'GET'


    prepared_request.method = method

Where I simply not change to get.

And also here:

def resolve_redirects(self, resp, req, stream=False, timeout=None,
                      verify=True, cert=None, proxies=None, yield_requests=False, **adapter_kwargs):

Where I commented out:

##            # https://github.com/requests/requests/issues/1084
##            if resp.status_code not in (codes.temporary_redirect, codes.permanent_redirect):
##                # https://github.com/requests/requests/issues/3490
##                print(prepared_request.headers)
##                purged_headers = ('Content-Length', 'Content-Type', 'Transfer-Encoding')
##                for header in purged_headers:
##                    prepared_request.headers.pop(header, None)
##                prepared_request.body = None

Surely this is the ugly hack, but what should I have done? Hope anyone has a pointer on this.

  • Did you figure it out? I guess I have the same problem – Sil Bouwman Nov 21 '19 at 10:47
  • yes, sorry for the late reply. I fixed it by hard coding it in the requests library. A bit like 'when this redirect is triggered by url/site www.xxx.something then treat is as a post'. I was in contact with the requests creator about this. He thought it was an interesting option to add it as an option to the call, to not rewrite the type of call. but they did not want to change the core for it. – Rene Oudeman Jan 07 '20 at 20:03

1 Answers1

1

If you don't want to change requests library, you can check for redirects in your code instead, here is an example:

url = 'https://httpbin.org/redirect-to?url=http%3A%2F%2Fhttpbin.org%2Fpost'
data = {'name': 'Hello'}
while True:
    rep = requests.post(url, data=data, allow_redirects=False)
    if rep.status_code in [301, 302]:
        url = rep.headers.get('location')
        print 'redirect to', url
        continue
    break

print rep.text

Of course you have to handle infinite loop possibility and other http status codes

ahmed
  • 5,430
  • 1
  • 20
  • 36