
The http.client.HTTPConnection
class is a powerful tool for making HTTP requests in Python. To begin using it, you’ll need to create an instance of the class. Here’s how you can set up a basic HTTPConnection object:
conn = http.client.HTTPConnection("www.example.com")
import http.client
conn = http.client.HTTPConnection("www.example.com")
import http.client
conn = http.client.HTTPConnection("www.example.com")
This creates a connection to the specified host. By default, it uses port 80 for HTTP connections. If you need to connect to a different port, you can specify it as follows:
conn = http.client.HTTPConnection("www.example.com", 8080)
conn = http.client.HTTPConnection("www.example.com", 8080)
conn = http.client.HTTPConnection("www.example.com", 8080)
For more advanced usage, the HTTPConnection
constructor accepts several optional parameters:
- Sets a timeout (in seconds) for blocking operations
- Specifies the (host, port) to use as the source address for the connection
- Sets the block size for the connection’s buffering
Here’s an example that incorporates these parameters:
conn = http.client.HTTPConnection(
source_address=("192.168.1.2", 0),
conn = http.client.HTTPConnection(
"www.example.com",
8080,
timeout=10,
source_address=("192.168.1.2", 0),
blocksize=8192
)
conn = http.client.HTTPConnection(
"www.example.com",
8080,
timeout=10,
source_address=("192.168.1.2", 0),
blocksize=8192
)
If you need to connect to an HTTPS server, you should use the HTTPSConnection
class instead:
secure_conn = http.client.HTTPSConnection("www.example.com")
secure_conn = http.client.HTTPSConnection("www.example.com")
secure_conn = http.client.HTTPSConnection("www.example.com")
The HTTPSConnection
class accepts additional parameters for SSL/TLS configuration, such as context
for specifying an SSL context and check_hostname
for enabling or disabling hostname checking.
Once you’ve created your connection object, you can use it to send requests and receive responses. Remember to close the connection when you’re done:
# Use the connection here
try:
# Use the connection here
pass
finally:
conn.close()
try:
# Use the connection here
pass
finally:
conn.close()
Alternatively, you can use a context manager to ensure the connection is properly closed:
with http.client.HTTPConnection("www.example.com") as conn:
# Use the connection here
with http.client.HTTPConnection("www.example.com") as conn:
# Use the connection here
pass
with http.client.HTTPConnection("www.example.com") as conn:
# Use the connection here
pass
This approach automatically closes the connection when you exit the with
block, even if an exception occurs.
Sending HTTP Requests
Now that we have set up our HTTPConnection object, let’s explore how to send HTTP requests using it. The http.client module provides methods for sending various types of HTTP requests, including GET, POST, PUT, and DELETE.
To send a request, you typically follow these steps:
- Call the appropriate method to set up the request (e.g., request())
- Send any request body data (if applicable)
- Call the getresponse() method to get the server’s response
Let’s start with a simple GET request:
conn = http.client.HTTPConnection("www.example.com")
conn.request("GET", "/path/to/resource")
response = conn.getresponse()
import http.client
conn = http.client.HTTPConnection("www.example.com")
conn.request("GET", "/path/to/resource")
response = conn.getresponse()
import http.client
conn = http.client.HTTPConnection("www.example.com")
conn.request("GET", "/path/to/resource")
response = conn.getresponse()
For a POST request with data, you can do the following:
conn = http.client.HTTPConnection("www.example.com")
headers = {"Content-Type": "application/json"}
data = json.dumps({"key": "value"})
conn.request("POST", "/api/endpoint", body=data, headers=headers)
response = conn.getresponse()
import http.client
import json
conn = http.client.HTTPConnection("www.example.com")
headers = {"Content-Type": "application/json"}
data = json.dumps({"key": "value"})
conn.request("POST", "/api/endpoint", body=data, headers=headers)
response = conn.getresponse()
import http.client
import json
conn = http.client.HTTPConnection("www.example.com")
headers = {"Content-Type": "application/json"}
data = json.dumps({"key": "value"})
conn.request("POST", "/api/endpoint", body=data, headers=headers)
response = conn.getresponse()
You can also use the putheader() method to add headers individually before sending the request:
conn = http.client.HTTPConnection("www.example.com")
conn.putrequest("POST", "/api/endpoint")
conn.putheader("Content-Type", "application/json")
conn.putheader("Authorization", "Bearer your_token_here")
conn.send(json.dumps({"key": "value"}).encode())
response = conn.getresponse()
conn = http.client.HTTPConnection("www.example.com")
conn.putrequest("POST", "/api/endpoint")
conn.putheader("Content-Type", "application/json")
conn.putheader("Authorization", "Bearer your_token_here")
conn.endheaders()
conn.send(json.dumps({"key": "value"}).encode())
response = conn.getresponse()
conn = http.client.HTTPConnection("www.example.com")
conn.putrequest("POST", "/api/endpoint")
conn.putheader("Content-Type", "application/json")
conn.putheader("Authorization", "Bearer your_token_here")
conn.endheaders()
conn.send(json.dumps({"key": "value"}).encode())
response = conn.getresponse()
For sending large amounts of data, you can use the send() method multiple times:
conn = http.client.HTTPConnection("www.example.com")
conn.putrequest("POST", "/upload")
conn.putheader("Content-Type", "application/octet-stream")
with open("large_file.bin", "rb") as f:
response = conn.getresponse()
conn = http.client.HTTPConnection("www.example.com")
conn.putrequest("POST", "/upload")
conn.putheader("Content-Type", "application/octet-stream")
conn.endheaders()
with open("large_file.bin", "rb") as f:
while True:
chunk = f.read(8192)
if not chunk:
break
conn.send(chunk)
response = conn.getresponse()
conn = http.client.HTTPConnection("www.example.com")
conn.putrequest("POST", "/upload")
conn.putheader("Content-Type", "application/octet-stream")
conn.endheaders()
with open("large_file.bin", "rb") as f:
while True:
chunk = f.read(8192)
if not chunk:
break
conn.send(chunk)
response = conn.getresponse()
To handle redirects manually, you can check the response status and send a new request if needed:
conn = http.client.HTTPConnection("www.example.com")
conn.request("GET", "/redirect")
response = conn.getresponse()
if response.status == 301 or response.status == 302:
new_location = response.getheader("Location")
conn = http.client.HTTPConnection(new_location)
response = conn.getresponse()
conn = http.client.HTTPConnection("www.example.com")
conn.request("GET", "/redirect")
response = conn.getresponse()
if response.status == 301 or response.status == 302:
new_location = response.getheader("Location")
conn.close()
conn = http.client.HTTPConnection(new_location)
conn.request("GET", "/")
response = conn.getresponse()
conn = http.client.HTTPConnection("www.example.com")
conn.request("GET", "/redirect")
response = conn.getresponse()
if response.status == 301 or response.status == 302:
new_location = response.getheader("Location")
conn.close()
conn = http.client.HTTPConnection(new_location)
conn.request("GET", "/")
response = conn.getresponse()
Remember to always close the connection when you are done:
conn.close()
By using these methods, you can send various types of HTTP requests and handle different scenarios when working with the http.client.HTTPConnection class.
Handling HTTP Responses
Once you’ve sent a request using the http.client.HTTPConnection object, you’ll need to handle the response. The getresponse() method returns an HTTPResponse object, which provides several methods and attributes for accessing the response data.
Here’s how you can work with the response:
conn = http.client.HTTPConnection("www.example.com")
response = conn.getresponse()
# Get the status code and reason
print(f"Status: {response.status} {response.reason}")
print(f"Response body: {body.decode()}")
for header, value in response.getheaders():
print(f"{header}: {value}")
import http.client
conn = http.client.HTTPConnection("www.example.com")
conn.request("GET", "/")
response = conn.getresponse()
# Get the status code and reason
print(f"Status: {response.status} {response.reason}")
# Read the response body
body = response.read()
print(f"Response body: {body.decode()}")
# Get response headers
for header, value in response.getheaders():
print(f"{header}: {value}")
conn.close()
import http.client
conn = http.client.HTTPConnection("www.example.com")
conn.request("GET", "/")
response = conn.getresponse()
# Get the status code and reason
print(f"Status: {response.status} {response.reason}")
# Read the response body
body = response.read()
print(f"Response body: {body.decode()}")
# Get response headers
for header, value in response.getheaders():
print(f"{header}: {value}")
conn.close()
Let’s break down the different aspects of handling HTTP responses:
- The status attribute gives you the numeric status code, while reason provides a text description.
- Use the read() method to get the entire response body as bytes. You can then decode it to a string if needed.
- The getheaders() method returns a list of (header, value) tuples. You can also use getheader(name) to get a specific header value.
For larger responses, you might want to read the body in chunks to avoid memory issues:
with open("large_response.txt", "wb") as f:
chunk = response.read(chunk_size)
chunk_size = 1024
with open("large_response.txt", "wb") as f:
while True:
chunk = response.read(chunk_size)
if not chunk:
break
f.write(chunk)
chunk_size = 1024
with open("large_response.txt", "wb") as f:
while True:
chunk = response.read(chunk_size)
if not chunk:
break
f.write(chunk)
You can also use the readline() or readlines() methods to read the response line by line:
print(line.decode().strip())
for line in response:
print(line.decode().strip())
for line in response:
print(line.decode().strip())
If you are working with JSON responses, you can easily parse them using the json module:
conn.request("GET", "/api/data")
response = conn.getresponse()
data = json.loads(response.read().decode())
import json
conn.request("GET", "/api/data")
response = conn.getresponse()
data = json.loads(response.read().decode())
print(data)
import json
conn.request("GET", "/api/data")
response = conn.getresponse()
data = json.loads(response.read().decode())
print(data)
To handle different status codes, you can use conditional statements:
if response.status == 200:
print("Request successful")
elif response.status == 404:
print("Resource not found")
elif response.status >= 500:
print("Server error occurred")
print(f"Unexpected status code: {response.status}")
if response.status == 200:
print("Request successful")
# Process the response
elif response.status == 404:
print("Resource not found")
elif response.status >= 500:
print("Server error occurred")
else:
print(f"Unexpected status code: {response.status}")
if response.status == 200:
print("Request successful")
# Process the response
elif response.status == 404:
print("Resource not found")
elif response.status >= 500:
print("Server error occurred")
else:
print(f"Unexpected status code: {response.status}")
For responses with chunked transfer encoding, you can use the HTTPResponse object as an iterator:
conn.request("GET", "/chunked")
response = conn.getresponse()
if response.getheader("Transfer-Encoding") == "chunked":
print(f"Received chunk: {chunk.decode()}")
conn.request("GET", "/chunked")
response = conn.getresponse()
if response.getheader("Transfer-Encoding") == "chunked":
for chunk in response:
print(f"Received chunk: {chunk.decode()}")
conn.request("GET", "/chunked")
response = conn.getresponse()
if response.getheader("Transfer-Encoding") == "chunked":
for chunk in response:
print(f"Received chunk: {chunk.decode()}")
Remember to always close the connection when you are done processing the response:
conn.close()
By effectively handling HTTP responses, you can extract the necessary information, process the data, and handle various scenarios in your application.
Working with Headers
Setting Request Headers
You can set request headers using the putheader()
method or by passing a dictionary to the request()
method:
conn = http.client.HTTPConnection("api.example.com")
# Method 1: Using putheader()
conn.putrequest("GET", "/data")
conn.putheader("User-Agent", "MyApp/1.0")
conn.putheader("Accept", "application/json")
# Method 2: Using request() with a headers dictionary
"User-Agent": "MyApp/1.0",
"Accept": "application/json",
"Authorization": "Bearer mytoken123"
conn.request("GET", "/data", headers=headers)
response = conn.getresponse()
import http.client
conn = http.client.HTTPConnection("api.example.com")
# Method 1: Using putheader()
conn.putrequest("GET", "/data")
conn.putheader("User-Agent", "MyApp/1.0")
conn.putheader("Accept", "application/json")
conn.endheaders()
# Method 2: Using request() with a headers dictionary
headers = {
"User-Agent": "MyApp/1.0",
"Accept": "application/json",
"Authorization": "Bearer mytoken123"
}
conn.request("GET", "/data", headers=headers)
response = conn.getresponse()
import http.client
conn = http.client.HTTPConnection("api.example.com")
# Method 1: Using putheader()
conn.putrequest("GET", "/data")
conn.putheader("User-Agent", "MyApp/1.0")
conn.putheader("Accept", "application/json")
conn.endheaders()
# Method 2: Using request() with a headers dictionary
headers = {
"User-Agent": "MyApp/1.0",
"Accept": "application/json",
"Authorization": "Bearer mytoken123"
}
conn.request("GET", "/data", headers=headers)
response = conn.getresponse()
Reading Response Headers
To access response headers, you can use the getheader()
and getheaders()
methods of the HTTPResponse object:
content_type = response.getheader("Content-Type")
print(f"Content-Type: {content_type}")
all_headers = response.getheaders()
for header, value in all_headers:
print(f"{header}: {value}")
# Get a specific header
content_type = response.getheader("Content-Type")
print(f"Content-Type: {content_type}")
# Get all headers
all_headers = response.getheaders()
for header, value in all_headers:
print(f"{header}: {value}")
# Get a specific header
content_type = response.getheader("Content-Type")
print(f"Content-Type: {content_type}")
# Get all headers
all_headers = response.getheaders()
for header, value in all_headers:
print(f"{header}: {value}")
Working with Custom Headers
Custom headers can be useful for passing additional information or implementing authentication:
data = json.dumps({"username": "johndoe", "password": "secret"})
"Content-Type": "application/json",
"X-API-Key": "your-api-key-here",
"X-Custom-Header": "Some-Value"
conn.request("POST", "/login", body=data, headers=headers)
response = conn.getresponse()
# Check if the server acknowledged our custom header
if response.getheader("X-Custom-Header-Received"):
print("Server received our custom header")
import json
data = json.dumps({"username": "johndoe", "password": "secret"})
headers = {
"Content-Type": "application/json",
"X-API-Key": "your-api-key-here",
"X-Custom-Header": "Some-Value"
}
conn.request("POST", "/login", body=data, headers=headers)
response = conn.getresponse()
# Check if the server acknowledged our custom header
if response.getheader("X-Custom-Header-Received"):
print("Server received our custom header")
import json
data = json.dumps({"username": "johndoe", "password": "secret"})
headers = {
"Content-Type": "application/json",
"X-API-Key": "your-api-key-here",
"X-Custom-Header": "Some-Value"
}
conn.request("POST", "/login", body=data, headers=headers)
response = conn.getresponse()
# Check if the server acknowledged our custom header
if response.getheader("X-Custom-Header-Received"):
print("Server received our custom header")
Handling Content-Type Headers
The Content-Type header is important for interpreting the response body correctly:
conn.request("GET", "/data")
response = conn.getresponse()
content_type = response.getheader("Content-Type")
if content_type == "application/json":
data = json.loads(response.read().decode())
print(f"Received JSON data: {data}")
elif content_type == "text/html":
html_content = response.read().decode()
print(f"Received HTML content: {html_content[:100]}...")
print(f"Unsupported content type: {content_type}")
conn.request("GET", "/data")
response = conn.getresponse()
content_type = response.getheader("Content-Type")
if content_type == "application/json":
data = json.loads(response.read().decode())
print(f"Received JSON data: {data}")
elif content_type == "text/html":
html_content = response.read().decode()
print(f"Received HTML content: {html_content[:100]}...")
else:
print(f"Unsupported content type: {content_type}")
conn.request("GET", "/data")
response = conn.getresponse()
content_type = response.getheader("Content-Type")
if content_type == "application/json":
data = json.loads(response.read().decode())
print(f"Received JSON data: {data}")
elif content_type == "text/html":
html_content = response.read().decode()
print(f"Received HTML content: {html_content[:100]}...")
else:
print(f"Unsupported content type: {content_type}")
Dealing with Cookies
While http.client doesn’t have built-in cookie handling, you can manually manage cookies using headers:
headers = {"Cookie": "session_id=abc123; user_id=12345"}
conn.request("GET", "/dashboard", headers=headers)
# Receiving and storing cookies
response = conn.getresponse()
cookies = response.getheader("Set-Cookie")
print(f"Received cookies: {cookies}")
# Store these cookies for future requests
# Sending cookies
headers = {"Cookie": "session_id=abc123; user_id=12345"}
conn.request("GET", "/dashboard", headers=headers)
# Receiving and storing cookies
response = conn.getresponse()
cookies = response.getheader("Set-Cookie")
if cookies:
print(f"Received cookies: {cookies}")
# Store these cookies for future requests
# Sending cookies
headers = {"Cookie": "session_id=abc123; user_id=12345"}
conn.request("GET", "/dashboard", headers=headers)
# Receiving and storing cookies
response = conn.getresponse()
cookies = response.getheader("Set-Cookie")
if cookies:
print(f"Received cookies: {cookies}")
# Store these cookies for future requests
Handling Redirects with Headers
You can use the Location header to handle redirects manually:
conn.request("GET", "/old-page")
response = conn.getresponse()
if response.status in (301, 302, 303, 307, 308):
new_location = response.getheader("Location")
print(f"Redirecting to: {new_location}")
conn = http.client.HTTPConnection(new_location)
response = conn.getresponse()
conn.request("GET", "/old-page")
response = conn.getresponse()
if response.status in (301, 302, 303, 307, 308):
new_location = response.getheader("Location")
print(f"Redirecting to: {new_location}")
conn.close()
conn = http.client.HTTPConnection(new_location)
conn.request("GET", "/")
response = conn.getresponse()
conn.request("GET", "/old-page")
response = conn.getresponse()
if response.status in (301, 302, 303, 307, 308):
new_location = response.getheader("Location")
print(f"Redirecting to: {new_location}")
conn.close()
conn = http.client.HTTPConnection(new_location)
conn.request("GET", "/")
response = conn.getresponse()
By effectively working with headers, you can control various aspects of HTTP communication, implement authentication, handle different content types, and manage session information in your applications using http.client.HTTPConnection.
Managing Cookies
Managing cookies with http.client.HTTPConnection requires manual handling, as the library doesn’t provide built-in cookie management. However, you can implement cookie handling by working with the appropriate headers. Here’s how you can manage cookies effectively:
Sending Cookies
To send cookies with your request, you need to include them in the “Cookie” header:
conn = http.client.HTTPConnection("www.example.com")
headers = {"Cookie": "session_id=abc123; user_preference=dark_mode"}
conn.request("GET", "/dashboard", headers=headers)
response = conn.getresponse()
import http.client
conn = http.client.HTTPConnection("www.example.com")
headers = {"Cookie": "session_id=abc123; user_preference=dark_mode"}
conn.request("GET", "/dashboard", headers=headers)
response = conn.getresponse()
import http.client
conn = http.client.HTTPConnection("www.example.com")
headers = {"Cookie": "session_id=abc123; user_preference=dark_mode"}
conn.request("GET", "/dashboard", headers=headers)
response = conn.getresponse()
Receiving and Storing Cookies
When a server sends cookies, they come in the “Set-Cookie” header. You’ll need to parse this header and store the cookies for future use:
response = conn.getresponse()
set_cookie_headers = response.getheader("Set-Cookie")
for cookie_string in set_cookie_headers.split(","):
name, value = cookie_string.strip().split("=", 1)
cookies[name] = value.split(";")[0] # Ignore additional attributes
print("Received cookies:", cookies)
response = conn.getresponse()
set_cookie_headers = response.getheader("Set-Cookie")
if set_cookie_headers:
cookies = {}
for cookie_string in set_cookie_headers.split(","):
name, value = cookie_string.strip().split("=", 1)
cookies[name] = value.split(";")[0] # Ignore additional attributes
print("Received cookies:", cookies)
response = conn.getresponse()
set_cookie_headers = response.getheader("Set-Cookie")
if set_cookie_headers:
cookies = {}
for cookie_string in set_cookie_headers.split(","):
name, value = cookie_string.strip().split("=", 1)
cookies[name] = value.split(";")[0] # Ignore additional attributes
print("Received cookies:", cookies)
Maintaining a Cookie Jar
To manage cookies across multiple requests, you can create a simple cookie jar:
def add_cookies(self, set_cookie_header):
for cookie_string in set_cookie_header.split(","):
name, value = cookie_string.strip().split("=", 1)
self.cookies[name] = value.split(";")[0]
def get_cookie_header(self):
return "; ".join([f"{name}={value}" for name, value in self.cookies.items()])
conn = http.client.HTTPConnection("www.example.com")
conn.request("GET", "/login")
response = conn.getresponse()
cookie_jar.add_cookies(response.getheader("Set-Cookie"))
# Subsequent request using stored cookies
headers = {"Cookie": cookie_jar.get_cookie_header()}
conn.request("GET", "/dashboard", headers=headers)
response = conn.getresponse()
cookie_jar.add_cookies(response.getheader("Set-Cookie"))
class CookieJar:
def __init__(self):
self.cookies = {}
def add_cookies(self, set_cookie_header):
if set_cookie_header:
for cookie_string in set_cookie_header.split(","):
name, value = cookie_string.strip().split("=", 1)
self.cookies[name] = value.split(";")[0]
def get_cookie_header(self):
return "; ".join([f"{name}={value}" for name, value in self.cookies.items()])
# Usage
cookie_jar = CookieJar()
# First request
conn = http.client.HTTPConnection("www.example.com")
conn.request("GET", "/login")
response = conn.getresponse()
cookie_jar.add_cookies(response.getheader("Set-Cookie"))
# Subsequent request using stored cookies
headers = {"Cookie": cookie_jar.get_cookie_header()}
conn.request("GET", "/dashboard", headers=headers)
response = conn.getresponse()
cookie_jar.add_cookies(response.getheader("Set-Cookie"))
class CookieJar:
def __init__(self):
self.cookies = {}
def add_cookies(self, set_cookie_header):
if set_cookie_header:
for cookie_string in set_cookie_header.split(","):
name, value = cookie_string.strip().split("=", 1)
self.cookies[name] = value.split(";")[0]
def get_cookie_header(self):
return "; ".join([f"{name}={value}" for name, value in self.cookies.items()])
# Usage
cookie_jar = CookieJar()
# First request
conn = http.client.HTTPConnection("www.example.com")
conn.request("GET", "/login")
response = conn.getresponse()
cookie_jar.add_cookies(response.getheader("Set-Cookie"))
# Subsequent request using stored cookies
headers = {"Cookie": cookie_jar.get_cookie_header()}
conn.request("GET", "/dashboard", headers=headers)
response = conn.getresponse()
cookie_jar.add_cookies(response.getheader("Set-Cookie"))
Handling Cookie Expiration and Attributes
For a more robust cookie management system, you should think handling cookie expiration and attributes like “HttpOnly”, “Secure”, and “Domain”. Here’s an example of a more advanced CookieJar class:
from http.cookies import SimpleCookie
def add_cookies(self, set_cookie_header):
cookie.load(set_cookie_header)
for key, morsel in cookie.items():
expires = morsel.get("expires")
expires = time.mktime(time.strptime(expires, "%a, %d-%b-%Y %H:%M:%S GMT"))
"domain": morsel.get("domain"),
"path": morsel.get("path"),
"secure": morsel.get("secure"),
"httponly": morsel.get("httponly")
def get_cookie_header(self, domain, path, secure):
for name, cookie in self.cookies.items():
if cookie["expires"] and cookie["expires"] < now:
if cookie["domain"] and not domain.endswith(cookie["domain"]):
if cookie["path"] and not path.startswith(cookie["path"]):
if cookie["secure"] and not secure:
cookies.append(f"{name}={cookie['value']}")
return "; ".join(cookies)
advanced_jar = AdvancedCookieJar()
conn = http.client.HTTPConnection("www.example.com")
conn.request("GET", "/login")
response = conn.getresponse()
advanced_jar.add_cookies(response.getheader("Set-Cookie"))
headers = {"Cookie": advanced_jar.get_cookie_header("www.example.com", "/dashboard", False)}
conn.request("GET", "/dashboard", headers=headers)
response = conn.getresponse()
advanced_jar.add_cookies(response.getheader("Set-Cookie"))
import time
from http.cookies import SimpleCookie
class AdvancedCookieJar:
def __init__(self):
self.cookies = {}
def add_cookies(self, set_cookie_header):
if set_cookie_header:
cookie = SimpleCookie()
cookie.load(set_cookie_header)
for key, morsel in cookie.items():
expires = morsel.get("expires")
if expires:
expires = time.mktime(time.strptime(expires, "%a, %d-%b-%Y %H:%M:%S GMT"))
self.cookies[key] = {
"value": morsel.value,
"expires": expires,
"domain": morsel.get("domain"),
"path": morsel.get("path"),
"secure": morsel.get("secure"),
"httponly": morsel.get("httponly")
}
def get_cookie_header(self, domain, path, secure):
now = time.time()
cookies = []
for name, cookie in self.cookies.items():
if cookie["expires"] and cookie["expires"] < now:
continue
if cookie["domain"] and not domain.endswith(cookie["domain"]):
continue
if cookie["path"] and not path.startswith(cookie["path"]):
continue
if cookie["secure"] and not secure:
continue
cookies.append(f"{name}={cookie['value']}")
return "; ".join(cookies)
# Usage
advanced_jar = AdvancedCookieJar()
conn = http.client.HTTPConnection("www.example.com")
# First request
conn.request("GET", "/login")
response = conn.getresponse()
advanced_jar.add_cookies(response.getheader("Set-Cookie"))
# Subsequent request
headers = {"Cookie": advanced_jar.get_cookie_header("www.example.com", "/dashboard", False)}
conn.request("GET", "/dashboard", headers=headers)
response = conn.getresponse()
advanced_jar.add_cookies(response.getheader("Set-Cookie"))
import time
from http.cookies import SimpleCookie
class AdvancedCookieJar:
def __init__(self):
self.cookies = {}
def add_cookies(self, set_cookie_header):
if set_cookie_header:
cookie = SimpleCookie()
cookie.load(set_cookie_header)
for key, morsel in cookie.items():
expires = morsel.get("expires")
if expires:
expires = time.mktime(time.strptime(expires, "%a, %d-%b-%Y %H:%M:%S GMT"))
self.cookies[key] = {
"value": morsel.value,
"expires": expires,
"domain": morsel.get("domain"),
"path": morsel.get("path"),
"secure": morsel.get("secure"),
"httponly": morsel.get("httponly")
}
def get_cookie_header(self, domain, path, secure):
now = time.time()
cookies = []
for name, cookie in self.cookies.items():
if cookie["expires"] and cookie["expires"] < now:
continue
if cookie["domain"] and not domain.endswith(cookie["domain"]):
continue
if cookie["path"] and not path.startswith(cookie["path"]):
continue
if cookie["secure"] and not secure:
continue
cookies.append(f"{name}={cookie['value']}")
return "; ".join(cookies)
# Usage
advanced_jar = AdvancedCookieJar()
conn = http.client.HTTPConnection("www.example.com")
# First request
conn.request("GET", "/login")
response = conn.getresponse()
advanced_jar.add_cookies(response.getheader("Set-Cookie"))
# Subsequent request
headers = {"Cookie": advanced_jar.get_cookie_header("www.example.com", "/dashboard", False)}
conn.request("GET", "/dashboard", headers=headers)
response = conn.getresponse()
advanced_jar.add_cookies(response.getheader("Set-Cookie"))
This advanced implementation takes into account cookie expiration, domain, path, and secure attributes, providing a more accurate and secure way of managing cookies when using http.client.HTTPConnection.
Implementing Retry and Timeout Mechanisms
When working with HTTP connections, it’s important to implement retry and timeout mechanisms to handle network issues and ensure robust communication. The http.client module doesn’t provide built-in retry functionality, but you can implement it yourself. Here’s how you can add retry and timeout mechanisms to your HTTP requests:
Setting Timeouts
You can set a timeout when creating the HTTPConnection object:
conn = http.client.HTTPConnection("www.example.com", timeout=10)
import http.client
conn = http.client.HTTPConnection("www.example.com", timeout=10)
import http.client
conn = http.client.HTTPConnection("www.example.com", timeout=10)
This sets a timeout of 10 seconds for all blocking operations on this connection. You can also set timeouts for individual requests:
conn.request("GET", "/", timeout=5)
response = conn.getresponse()
conn.request("GET", "/", timeout=5)
response = conn.getresponse()
conn.request("GET", "/", timeout=5)
response = conn.getresponse()
Implementing Retry Logic
Here’s an example of a function that implements retry logic with exponential backoff:
from urllib.parse import urlparse
def make_request_with_retry(url, method="GET", body=None, headers=None, max_retries=3, initial_delay=1):
parsed_url = urlparse(url)
path = parsed_url.path or "/"
for attempt in range(max_retries):
conn = http.client.HTTPConnection(parsed_url.netloc, timeout=10)
conn.request(method, path, body=body, headers=headers)
response = conn.getresponse()
except (http.client.HTTPException, TimeoutError, ConnectionError) as e:
if attempt == max_retries - 1:
delay = initial_delay * (2 ** attempt)
print(f"Request failed. Retrying in {delay} seconds...")
response = make_request_with_retry("http://www.example.com/api/data")
print(f"Status: {response.status}")
print(response.read().decode())
print(f"Request failed after multiple retries: {e}")
import time
import http.client
from urllib.parse import urlparse
def make_request_with_retry(url, method="GET", body=None, headers=None, max_retries=3, initial_delay=1):
parsed_url = urlparse(url)
path = parsed_url.path or "/"
for attempt in range(max_retries):
try:
conn = http.client.HTTPConnection(parsed_url.netloc, timeout=10)
conn.request(method, path, body=body, headers=headers)
response = conn.getresponse()
return response
except (http.client.HTTPException, TimeoutError, ConnectionError) as e:
if attempt == max_retries - 1:
raise
delay = initial_delay * (2 ** attempt)
print(f"Request failed. Retrying in {delay} seconds...")
time.sleep(delay)
finally:
conn.close()
# Usage
try:
response = make_request_with_retry("http://www.example.com/api/data")
print(f"Status: {response.status}")
print(response.read().decode())
except Exception as e:
print(f"Request failed after multiple retries: {e}")
import time
import http.client
from urllib.parse import urlparse
def make_request_with_retry(url, method="GET", body=None, headers=None, max_retries=3, initial_delay=1):
parsed_url = urlparse(url)
path = parsed_url.path or "/"
for attempt in range(max_retries):
try:
conn = http.client.HTTPConnection(parsed_url.netloc, timeout=10)
conn.request(method, path, body=body, headers=headers)
response = conn.getresponse()
return response
except (http.client.HTTPException, TimeoutError, ConnectionError) as e:
if attempt == max_retries - 1:
raise
delay = initial_delay * (2 ** attempt)
print(f"Request failed. Retrying in {delay} seconds...")
time.sleep(delay)
finally:
conn.close()
# Usage
try:
response = make_request_with_retry("http://www.example.com/api/data")
print(f"Status: {response.status}")
print(response.read().decode())
except Exception as e:
print(f"Request failed after multiple retries: {e}")
This function attempts to make a request up to max_retries
times, with an exponential backoff delay between retries. It handles common exceptions that might occur during the request.
Handling Specific Status Codes
You might want to retry on specific status codes, such as 429 (Too Many Requests) or 503 (Service Unavailable). Here’s an example:
def make_request_with_status_retry(url, method="GET", body=None, headers=None, max_retries=3, initial_delay=1):
parsed_url = urlparse(url)
path = parsed_url.path or "/"
for attempt in range(max_retries):
conn = http.client.HTTPConnection(parsed_url.netloc, timeout=10)
conn.request(method, path, body=body, headers=headers)
response = conn.getresponse()
if response.status in (429, 503):
retry_after = int(response.getheader("Retry-After", 0))
delay = max(initial_delay * (2 ** attempt), retry_after)
print(f"Received status {response.status}. Retrying in {delay} seconds...")
except (http.client.HTTPException, TimeoutError, ConnectionError) as e:
if attempt == max_retries - 1:
delay = initial_delay * (2 ** attempt)
print(f"Request failed. Retrying in {delay} seconds...")
response = make_request_with_status_retry("http://www.example.com/api/data")
print(f"Status: {response.status}")
print(response.read().decode())
print(f"Request failed after multiple retries: {e}")
def make_request_with_status_retry(url, method="GET", body=None, headers=None, max_retries=3, initial_delay=1):
parsed_url = urlparse(url)
path = parsed_url.path or "/"
for attempt in range(max_retries):
try:
conn = http.client.HTTPConnection(parsed_url.netloc, timeout=10)
conn.request(method, path, body=body, headers=headers)
response = conn.getresponse()
if response.status in (429, 503):
retry_after = int(response.getheader("Retry-After", 0))
delay = max(initial_delay * (2 ** attempt), retry_after)
print(f"Received status {response.status}. Retrying in {delay} seconds...")
time.sleep(delay)
continue
return response
except (http.client.HTTPException, TimeoutError, ConnectionError) as e:
if attempt == max_retries - 1:
raise
delay = initial_delay * (2 ** attempt)
print(f"Request failed. Retrying in {delay} seconds...")
time.sleep(delay)
finally:
conn.close()
# Usage
try:
response = make_request_with_status_retry("http://www.example.com/api/data")
print(f"Status: {response.status}")
print(response.read().decode())
except Exception as e:
print(f"Request failed after multiple retries: {e}")
def make_request_with_status_retry(url, method="GET", body=None, headers=None, max_retries=3, initial_delay=1):
parsed_url = urlparse(url)
path = parsed_url.path or "/"
for attempt in range(max_retries):
try:
conn = http.client.HTTPConnection(parsed_url.netloc, timeout=10)
conn.request(method, path, body=body, headers=headers)
response = conn.getresponse()
if response.status in (429, 503):
retry_after = int(response.getheader("Retry-After", 0))
delay = max(initial_delay * (2 ** attempt), retry_after)
print(f"Received status {response.status}. Retrying in {delay} seconds...")
time.sleep(delay)
continue
return response
except (http.client.HTTPException, TimeoutError, ConnectionError) as e:
if attempt == max_retries - 1:
raise
delay = initial_delay * (2 ** attempt)
print(f"Request failed. Retrying in {delay} seconds...")
time.sleep(delay)
finally:
conn.close()
# Usage
try:
response = make_request_with_status_retry("http://www.example.com/api/data")
print(f"Status: {response.status}")
print(response.read().decode())
except Exception as e:
print(f"Request failed after multiple retries: {e}")
This version checks for specific status codes and respects the “Retry-After” header if present.
Using a Retry Decorator
For a more reusable solution, you can create a decorator to add retry functionality to any function:
def retry(max_retries=3, initial_delay=1, backoff_factor=2, exceptions=(Exception,)):
def wrapper(*args, **kwargs):
for attempt in range(max_retries):
return func(*args, **kwargs)
if attempt == max_retries - 1:
print(f"Attempt {attempt + 1} failed. Retrying in {delay} seconds...")
@retry(max_retries=3, exceptions=(http.client.HTTPException, TimeoutError, ConnectionError))
conn = http.client.HTTPConnection(urlparse(url).netloc, timeout=10)
conn.request("GET", urlparse(url).path)
response = conn.getresponse()
data = fetch_data("http://www.example.com/api/data")
print(f"Failed to fetch data: {e}")
import functools
import time
def retry(max_retries=3, initial_delay=1, backoff_factor=2, exceptions=(Exception,)):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
delay = initial_delay
for attempt in range(max_retries):
try:
return func(*args, **kwargs)
except exceptions as e:
if attempt == max_retries - 1:
raise
print(f"Attempt {attempt + 1} failed. Retrying in {delay} seconds...")
time.sleep(delay)
delay *= backoff_factor
return wrapper
return decorator
# Usage
@retry(max_retries=3, exceptions=(http.client.HTTPException, TimeoutError, ConnectionError))
def fetch_data(url):
conn = http.client.HTTPConnection(urlparse(url).netloc, timeout=10)
try:
conn.request("GET", urlparse(url).path)
response = conn.getresponse()
return response.read()
finally:
conn.close()
try:
data = fetch_data("http://www.example.com/api/data")
print(data.decode())
except Exception as e:
print(f"Failed to fetch data: {e}")
import functools
import time
def retry(max_retries=3, initial_delay=1, backoff_factor=2, exceptions=(Exception,)):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
delay = initial_delay
for attempt in range(max_retries):
try:
return func(*args, **kwargs)
except exceptions as e:
if attempt == max_retries - 1:
raise
print(f"Attempt {attempt + 1} failed. Retrying in {delay} seconds...")
time.sleep(delay)
delay *= backoff_factor
return wrapper
return decorator
# Usage
@retry(max_retries=3, exceptions=(http.client.HTTPException, TimeoutError, ConnectionError))
def fetch_data(url):
conn = http.client.HTTPConnection(urlparse(url).netloc, timeout=10)
try:
conn.request("GET", urlparse(url).path)
response = conn.getresponse()
return response.read()
finally:
conn.close()
try:
data = fetch_data("http://www.example.com/api/data")
print(data.decode())
except Exception as e:
print(f"Failed to fetch data: {e}")
This decorator allows you to easily add retry functionality to any function that makes HTTP requests or performs other operations that might need retrying.
By implementing these retry and timeout mechanisms, you can make your HTTP requests using http.client.HTTPConnection more resilient to network issues and temporary server problems.