Requests / bravado 302 response to POST turns into GET (python)

Viewed 1085

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.

1 Answers

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

Related