• Home
  • History
  • Annotate
Name Date Size #Lines LOC

..03-May-2022-

forcediphttpsadapter/H01-Nov-2021-181127

forcediphttpsadapter.egg-info/H03-May-2022-1312

PKG-INFOH A D01-Nov-2021793 1312

README.mdH A D17-May-20213.2 KiB8664

setup.cfgH A D01-Nov-202138 53

setup.pyH A D01-Nov-20211.1 KiB4425

README.md

1This module implements a set of requests TransportAdapter, PoolManager,
2ConnectionPool and HTTPSConnection with one goal only:
3
4* to use a specific IP address when connecting via SSL to a web service without
5running into SNI trouble.
6
7The usual technique to force an IP address on an HTTP connection with Requests
8is (assuming I want http://example.com/some/path on IP 1.2.3.4):
9
10```
11requests.get("http://1.2.3.4/some/path", headers={'Host': 'example.com'})
12```
13
14this is useful if I want to specifically test how 1.2.3.4 is responding; for
15instance, if example.com is DNS round-robined to several IP
16addresses and I want to hit one of them specifically.
17
18This also works for https requests if using Python <2.7.9 because older
19versions don't do SNI and thus don't pass the requested hostname as part of the
20SSL handshake.
21
22However, Python >=2.7.9 and >=3.4.x conveniently added SNI support, breaking
23this way of connecting to the IP, because the IP address embedded in the URL
24*is* passed as part of the SSL handshake, causing errors (mainly, the server
25returns a 400 Bad Request because the SNI host 1.2.3.4 doesn't match the one in
26the HTTP headers example.com).
27
28The "easiest" way to achieve this is to force the IP address at the lowest
29possible level, namely when we do socket.create_connection. The rest of the
30"stack" is given the actual hostname. So the sequence is:
31
321. Open a socket to 1.2.3.4
332. SSL wrap this socket using the hostname.
343. Do the rest of the HTTPS traffic, headers and all over this socket.
35
36Unfortunately Requests hides the socket.create_connection call in the deep
37recesses of urllib3, so the specified chain of classes is needed to propagate
38the given dest_ip value all the way down the stack.
39
40Because this applies to a very limited set of circumstances, the overridden
41code is very simplistic and eschews many of the nice checks Requests does for
42you.
43
44Specifically:
45
46- It ONLY handles HTTPS.
47- It does NO certificate verification (which would be pointless)
48- Only tested with Requests 2.2.1 and 2.9.1.
49- Does NOT work with the ancient urllib3 (1.7.1) shipped with Ubuntu 14.04.
50  Should not be an issue because Ubunt 14.04 has older Python which doesn't do
51  SNI.
52
53
54How to use it
55=============
56
57First install it:
58
59```
60pip install forcediphttpsadapter
61```
62
63Then, it's like any other transport adapter. Just pass the IP address that
64connections to the given URL prefix should use.
65
66```
67from forcediphttpsadapter.adapters import ForcedIPHTTPSAdapter
68
69session = requests.Session()
70session.mount("https://example.com", ForcedIPHTTPSAdapter(dest_ip='1.2.3.4'))
71response = session.get(
72    '/some/path', headers={'Host': 'example.com'}, verify=False)
73```
74
75Note this module will ImportError if there's no sane requests/urllib
76combination available so the adapter won't work, and it's up to the caller to
77decide what to do. The caller can, for instance, check the Python version and
78if it's &lt;2.7.9 decide to use the old "http://$IP/ technique. If Python is
79&gt;=2.7.9 and the adapter doesn't work, unfortunately, there's nothing that can
80be done :(
81
82An example.py script is provided, it illustrates how to
83import the module, how to decide whether to use the adapter
84or the old technique, and how to define the IP and mount
85the adapter on a session.
86