Skip to content

Instantly share code, notes, and snippets.

@chadselph
Created October 20, 2011 03:22
Show Gist options
  • Save chadselph/1300341 to your computer and use it in GitHub Desktop.
Save chadselph/1300341 to your computer and use it in GitHub Desktop.
requests gevent demo
"""
requests uses a singleton AuthManager, which when used with gevent's monkey patching
(or presumably, the threading module) can be set by one thread, and read by another.
The AuthManager maps urls to credentials which assumes for any url you will always
request it with the same credentials. This turned out to be false for us, and thus,
the bug. Here's code to reliably reproduce it.
"""
import base64
import gevent.monkey
from gevent.pywsgi import WSGIServer
gevent.monkey.patch_all()
import requests
port = 8899
def cycle_auths(n):
# cycle between user1 and user2
auths = [('user1', 'pass1', 'forced_basic'), ('user2', 'pass2', 'forced_basic')]
for i in xrange(n):
yield auths[i % len(auths)]
def app(env, start_response):
if not env.get('HTTP_AUTHORIZATION'):
# force requests to send the auth!
start_response("401 Unauthorized", [])
return ""
start_response("200 OK", [])
b64auth = env['HTTP_AUTHORIZATION'][6:]
user, password = base64.b64decode(b64auth).split(':')
print user, password, "should be", env['wsgi.input'].read()
return ""
g = gevent.Greenlet(WSGIServer(('', port), app).serve_forever).start()
gevent.sleep(2) # wait for the server to start
def mk_request(auth):
r = requests.post('http://localhost:{port}'.format(port=port),
data=auth[0], auth=auth)
threads = [gevent.spawn(mk_request, auth) for auth in cycle_auths(20)]
gevent.joinall(threads)
gevent.sleep(2)
# show that in a non-race situation, the auth will be correct
thread = [gevent.spawn(mk_request, ('user3', 'pass3'))]
gevent.joinall(thread)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment