Sling Academy
Home/Python/Python ‘requests’ module: How to force use of IPv4 or IPv6

Python ‘requests’ module: How to force use of IPv4 or IPv6

Last updated: January 02, 2024

Introduction

Understanding how to control the IP version when making HTTP requests in Python can be crucial when dealing with servers that are accessible only via IPv4 or IPv6.

The requests module in Python is a powerful, user-friendly tool for sending HTTP requests. By default, it automatically determines whether to use IPv4 or IPv6 based on the network stack’s configuration. This article guides you on how to explicitly force IPv4 or IPv6 when using the requests module, with practical code examples.

Installing ‘requests’ Module

# Use pip to install the 'requests' module
pip install requests

Basic Request Example

import requests

response = requests.get('https://example.com')
print(response.text)

Understanding Network Stacks

Before forcing a specific IP version, it’s important to know that modern operating systems support a dual network stack that allows the use of both IPv4 and IPv6. However, forcing a specific version can be necessary for certain network conditions or server configurations.

Forcing IPv4

To force the use of IPv4, one can modify the default socket connection behavior. This requires a little more control over the connection process. Here’s a basic example:

import socket
import requests
from requests.packages.urllib3.util.connection import allowed_gai_family

# Override the default function in the urllib3 utility module

original_allowed_gai_family = allowed_gai_family

def forced_ipv4_gai_family():
    return socket.AF_INET

allowed_gai_family = forced_ipv4_gai_family

# Now, all requests via the 'requests' module will use IPv4 only
response = requests.get('https://example.com')
print(response.text)

# Restore the original function after making IPv4 requests
allowed_gai_family = original_allowed_gai_family

Forcing IPv6

Forcing IPv6 is similar to forcing IPv4, but you direct the socket to use the IPv6 family:

import socket
import requests
from requests.packages.urllib3.util.connection import allowed_gai_family

def forced_ipv6_gai_family():
    return socket.AF_INET6

allowed_gai_family = forced_ipv6_gai_family

# Attempts will be made to connect using IPv6
response = requests.get('https://example.com')
print(response.text)

# Don't forget to reset the original function
allowed_gai_family = original_allowed_gai_family

Handling Exceptions

When forcing a specific IP version, it’s possible to encounter exceptions, such as ConnectionError if the requested domain does not support the forced IP version. Make sure to handle these:

import requests
from requests.exceptions import ConnectionError

try:
    response = requests.get('https://ipv4onlysite.com')
    print(response.text)
except ConnectionError as e:
    print('Error connecting to the site:', e)

Advanced Use Case: Custom Adapter

In advanced scenarios, you may want to create a custom adapter to control the IP version for specific domains or request sessions:

import requests
from requests.packages.urllib3.util.connection import allowed_gai_family

# Custom adapter that forces IPv4

class IPv4Adapter(requests.adapters.HTTPAdapter):
    def init_poolmanager(self, *args, **kwargs):
        self.poolmanager = requests.adapters.HTTPAdapter(pool_connections=kwargs.get('connections', self.poolmanager.pool_connections),
                                                        pool_maxsize=kwargs.get('maxsize', self.poolmanager.pool_maxsize),
                                                        max_retries=kwargs.get('retries', self.poolmanager.max_retries),
                                                        pool_block=kwargs.get('block', self.poolmanager.pool_block))
        self.poolmanager.pool_classes_by_scheme[IPv4Adapter.scheme] = requests.packages.urllib3.connectionpool.HTTPConnectionPool
        self.poolmanager.connection_from_host = lambda host, port=None, scheme=None, pool_kwargs=None: self.poolmanager.pool_classes_by_scheme[IPv4Adapter.scheme](host, port, strict=True, timeout=IPv4Adapter.poolmanager.ConnectionTimeout, **(pool_kwargs or {}))

# Add the custom adapter to a session

session = requests.Session()
session.mount('https://example.com', IPv4Adapter())

# The following request to https://example.com will use IPv4 due to the custom adapter

response = session.get('https://example.com')
print(response.text)

Conclusion

The Python requests module is versatile and supports both IPv4 and IPv6 usage. Forcing a specific IP protocol can be critical for compatibility or performance reasons and can be achieved through the techniques described. Always handle exceptions gracefully and reset any global state modifications to avoid side effects in other parts of your application.

Next Article: How to deal with Python InsecureRequestWarning

Previous Article: How to Block Requests Sent by Python ‘requests’ Module?

Series: Python: Network & JSON tutorials

Python

You May Also Like

  • Python Warning: Secure coding is not enabled for restorable state
  • Python TypeError: write() argument must be str, not bytes
  • 4 ways to install Python modules on Windows without admin rights
  • Python TypeError: object of type ‘NoneType’ has no len()
  • Python: How to access command-line arguments (3 approaches)
  • Understanding ‘Never’ type in Python 3.11+ (5 examples)
  • Python: 3 Ways to Retrieve City/Country from IP Address
  • Using Type Aliases in Python: A Practical Guide (with Examples)
  • Python: Defining distinct types using NewType class
  • Using Optional Type in Python (explained with examples)
  • Python: How to Override Methods in Classes
  • Python: Define Generic Types for Lists of Nested Dictionaries
  • Python: Defining type for a list that can contain both numbers and strings
  • Using TypeGuard in Python (Python 3.10+)
  • Python: Using ‘NoReturn’ type with functions
  • Type Casting in Python: The Ultimate Guide (with Examples)
  • Python: Using type hints with class methods and properties
  • Python: Typing a function with default parameters
  • Python: Typing a function that can return multiple types