Python 2.7 - Redirect handler does not pass parameters when redirecting

I have a URL that I click on a website that can be migrated, and when the endpoint is moved, I need POST / GET parameters for reuse. What am I missing for this handler to do this?

class RedirectHandler(urllib2.HTTPRedirectHandler): def http_error_301(self, req, fp, code, msg, headers): result = urllib2.HTTPRedirectHandler.http_error_301( self, req, fp, code, msg, headers) result.status = code return result def http_error_302(self, req, fp, code, msg, headers): result = urllib2.HTTPRedirectHandler.http_error_302( self, req, fp, code, msg, headers) result.status = code return result 

When I look at traffic through a violinist, I notice that the token that is used for authentication is discarded.

(note that I cannot use queries for this solution, it should only be a standard library)

thanks

+8
python urllib2 urllib
source share
1 answer

The story of HTTP statuses HTTP 1.0 and 1.1 302, 303 and 307 is a bit complicated. Basically you see the expected and documented behavior (you can also see this answer for a more detailed description):

The standard implementation of this method does not strictly follow RFC 2616 , which states that 301 and 302 responses to POST requests should not be automatically redirected without user confirmation. In fact, browsers allow you to automatically redirect these responses, changing POST to GET , and the default implementation reproduces this behavior.

And you go right, but redefine the wrong methods. Here's the source urllib2.HTTPRedirectHandler.redirect_request :

 def redirect_request(self, req, fp, code, msg, headers, newurl): """Return a Request or None in response to a redirect. ... Return None if you can't but another Handler might. """ m = req.get_method() if (code in (301, 302, 303, 307) and m in ("GET", "HEAD") or code in (301, 302, 303) and m == "POST"): # ... newurl = newurl.replace(' ', '%20') newheaders = dict((k,v) for k,v in req.headers.items() if k.lower() not in ("content-length", "content-type") ) return Request(newurl, headers=newheaders, origin_req_host=req.get_origin_req_host(), unverifiable=True) else: raise HTTPError(req.get_full_url(), code, msg, headers, fp) 

A few observations here. It does not pass data , so a new GET request. It filters out the content-length and content-type headers, which are necessary for a proper POST . If fact, in my example req.headers is an empty dict, so I turned to req.header_items() (see unredirected_hdrs ). Moreover, it does not handle POST and 307 redirects.

This is where the correct implementation of the redirection handler to redirect the POST and 302 runs. Here, the full CherryPy simulation is done (before pip install cherrypy ).

 #!/usr/bin/env python # -*- coding: utf-8 -*- import urllib2 from urllib2 import HTTPRedirectHandler, Request import cherrypy config = { 'global' : { 'server.socket_host' : '127.0.0.1', 'server.socket_port' : 8080, 'server.thread_pool' : 8 } } class RedirectHandler(HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): if code == 302 and req.get_method() == 'POST': return Request(newurl, headers=dict(req.header_items()), data=req.data, origin_req_host=req.get_origin_req_host(), unverifiable=True) else: return HTTPRedirectHandler.redirect_request(self, req, fp, code, msg, headers, newurl) class App: @cherrypy.expose def index(self): opener = urllib2.build_opener(RedirectHandler()) return opener.open('http://localhost:8080/redirect', data='foo=bar') @cherrypy.expose def redirect(self, **kwargs): print('Before redirect {0}'.format(kwargs)) raise cherrypy.HTTPRedirect('/target', 302) @cherrypy.expose def target(self, **kwargs): return 'Target received {0} {1}'.format(cherrypy.request.method, kwargs) if __name__ == '__main__': cherrypy.quickstart(App(), '/', config) 
+2
source share

All Articles