When I use httplib for my OAUTH in Python, I always get "CannotSendRequest" and then "
Traceback:
File "/usr/local/lib/python2.6/dist-packages/django/core/handlers/base.py" in get_response
92. response = callback(request, *callback_args, **callback_kwargs)
File "/home/ea/ea/hell/life/views.py" in linkedin_auth
274. token = oauth_linkedin.get_unauthorised_request_token()
File "/home/ea/ea/hell/life/oauth_linkedin.py" in get_unauthorised_request_token
52. resp = fetch_response(oauth_request, connection)
File "/home/ea/ea/hell/life/oauth_linkedin.py" in fetch_response
42. connection.request(oauth_request.http_method,url)
File "/usr/lib/python2.6/httplib.py" in request
874. self._send_request(method, url, body, headers)
File "/usr/lib/python2.6/httplib.py" in _send_request
891. self.putrequest(method, url, **skips)
File "/usr/lib/python2.6/httplib.py" in putrequest
778. raise CannotSendRequest()
Exception Type: CannotSendRequest at /linkedin/auth
Exception Value:
And then, sometimes I get: BadStatusLine error instead of this.
It's pretty random. I don't know when or why they happen. It happens more frequently when I'm running the Django development server (and less frequently when in APACHE2...but it still happens at random times). When this error happens, I have to restart my server.
Answer
The error CannotSendRequest
and BadStatusLine
from httplib
often indicate an issue with the HTTP connection. These errors typically arise from:
- Persistent Connections Issues: The connection object (
connection
) may be reused when it's in an invalid or closed state. - Concurrency Problems: If the connection object is shared across multiple threads or requests without proper locking or management.
- Network Instability: Temporary network issues causing the connection to drop unexpectedly.
- Bug in
httplib
or the HTTP Server: Particularly with older Python versions (e.g., Python 2.6).
Since you're using Python 2.6 (a very old and unsupported version), the likelihood of encountering bugs in the HTTP library is higher.
Steps to Mitigate the Problem
-
Ensure a Fresh Connection for Each Request Avoid reusing the same HTTP connection object for multiple requests. Create a new connection object for each HTTP request.
Update your
fetch_response
function to explicitly close the connection after use:import httplib def fetch_response(oauth_request, connection): try: connection.request(oauth_request.http_method, oauth_request.to_url()) response = connection.getresponse() return response.read() finally: # Ensure the connection is closed connection.close()
-
Use a Modern Library Instead of relying on
httplib
, use a more modern and robust HTTP library such asrequests
. This library handles connection pooling, retries, and other complexities internally.Example:
import requests def fetch_response(oauth_request): response = requests.request( method=oauth_request.http_method, url=oauth_request.to_url() ) response.raise_for_status() # Raise an error for HTTP errors return response.text
-
Check for Thread Safety If your application uses multithreading (e.g., Django development server or Apache with WSGI), ensure that the connection object is not shared across threads. Sharing connections without synchronization can cause unpredictable behavior.
-
Upgrade Your Python Version Python 2.6 is outdated and no longer supported. Consider upgrading to Python 3.x to benefit from more stable and well-maintained libraries.
-
Handle
BadStatusLine
Gracefully Implement retry logic to handle temporary connection issues likeBadStatusLine
. Wrap your HTTP call in a retry loop:import time from httplib import BadStatusLine def fetch_response(oauth_request, connection, retries=3): for attempt in range(retries): try: connection.request(oauth_request.http_method, oauth_request.to_url()) response = connection.getresponse() return response.read() except BadStatusLine: if attempt < retries - 1: time.sleep(1) # Small delay before retrying else: raise finally: connection.close()
-
Debugging Tips
- Log the connection state before making the request to understand why it fails.
- Add logging for each request to identify patterns leading to the error.
- Test with a newer Django version on Python 3.x if possible.
-
Development vs Production
- The Django development server is single-threaded and not optimized for concurrent connections. Use a proper WSGI server like Gunicorn or uWSGI in production to minimize such issues.
By following these steps, you can mitigate or eliminate the random CannotSendRequest
and BadStatusLine
errors.