Python Requests, How to Specify Port for Outgoing Traffic

Python Requests, how to specify port for outgoing traffic?

requests is built on urllib3, which offers the ability to set a source address for connections; when you set the source address to ('', port_number) you tell it to use the default host name but pick a specific port.

You can set these options on the pool manager, and you tell requests to use a different pool manager by creating a new transport adapter:

from requests.adapters import HTTPAdapter
from requests.packages.urllib3.poolmanager import PoolManager

class SourcePortAdapter(HTTPAdapter):
""""Transport adapter" that allows us to set the source port."""
def __init__(self, port, *args, **kwargs):
self._source_port = port
super(SourcePortAdapter, self).__init__(*args, **kwargs)

def init_poolmanager(self, connections, maxsize, block=False):
self.poolmanager = PoolManager(
num_pools=connections, maxsize=maxsize,
block=block, source_address=('', self._source_port))

Use this adapter in a session object, the following mounts the adapter for all HTTP and HTTPS connections, using 54321 as the source port:

s = requests.Session()
s.mount('http://', SourcePortAdapter(54321))
s.mount('https://', SourcePortAdapter(54321))

You can only set the one source port, limiting you to one active connection at a time. If you need to rotate between ports, register multiple adapters (one per URL) or re-register the catch-all mounts each time.

See the create_connection() utility function documentation for the details on the source_address option:

If source_address is set it must be a tuple of (host, port) for the socket to bind as a source address before making the connection. An host of '' or port 0 tells the OS to use the default.

Set port for outgoing TCP requests in Python?

This question is not Python specific but a design issue. A server application should not need any static information about possible clients.

If you need information from the client, one of the first actions after the initial handshake between server and client is to exchange the needed information from a client to a server.

E.g.

  1. Server starts listening on Port 3000
  2. Client connects to Port 127.0.0.1:3000
  3. Client starts listening on Port 4000
  4. Client sends port 4000 to the Server and awaits its connection

Setting host and port for requests.Session() objects

From the requests documentation about sessions:

The Session object allows you to persist certain parameters across
requests. It also persists cookies across all requests made from the
Session instance, and will use urllib3’s connection pooling. So if
you’re making several requests to the same host, the underlying TCP
connection will be reused, which can result in a significant
performance increase (see HTTP persistent connection).

So while you give the url each time. Under the hood requests will not be creating a new TCP socket connection for each request. It will reuse the TCP socket connection to this host and port from the connection pool.

The main idea behind sessions is to share parameters and cookies between each request without having to set them individually in each request. and under the hood it will persist the connection. but you still need to give the full URL in each call

Send http request through specific network interface

I found a way using pycurl. This works like a charm.

import pycurl
from io import BytesIO
import json

def curl_post(url, data, iface=None):
c = pycurl.Curl()
buffer = BytesIO()
c.setopt(pycurl.URL, url)
c.setopt(pycurl.POST, True)
c.setopt(pycurl.HTTPHEADER, ['Content-Type: application/json'])
c.setopt(pycurl.TIMEOUT, 10)
c.setopt(pycurl.WRITEFUNCTION, buffer.write)
c.setopt(pycurl.POSTFIELDS, data)
if iface:
c.setopt(pycurl.INTERFACE, iface)
c.perform()

# Json response
resp = buffer.getvalue().decode('UTF-8')

# Check response is a JSON if not there was an error
try:
resp = json.loads(resp)
except json.decoder.JSONDecodeError:
pass

buffer.close()
c.close()
return resp

if __name__ == '__main__':
dat = {"id": 52, "configuration": [{"eno1": {"address": "192.168.1.1"}}]}
res = curl_post("http://127.0.0.1:5000/network_configuration/", json.dumps(dat), "wlp2")
print(res)

I'm leaving the question opened hopping that someone can give an answer using requests.



Related Topics



Leave a reply



Submit