Exploring Advanced Features of http.client.HTTPConnection

Exploring Advanced Features of http.client.HTTPConnection

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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import http.client
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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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 )
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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
try:
# Use the connection here
pass
finally:
conn.close()
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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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
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:

  1. Call the appropriate method to set up the request (e.g., request())
  2. Send any request body data (if applicable)
  3. Call the getresponse() method to get the server’s response

Let’s start with a simple GET request:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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()
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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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()
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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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()
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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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()
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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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()
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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
conn.close()
conn.close()
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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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()
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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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)
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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
for line in response:
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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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)
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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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}")
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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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()}")
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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
conn.close()
conn.close()
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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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()
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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
# 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}")
# 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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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")
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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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}")
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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
# 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
# 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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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()
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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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()
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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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)
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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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"))
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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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"))
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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import http.client
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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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}")
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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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}")
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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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}")
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.

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *