hexdb 1.6.0__tar.gz → 2.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {hexdb-1.6.0 → hexdb-2.0.0}/PKG-INFO +1 -1
- {hexdb-1.6.0 → hexdb-2.0.0}/hexdb/__init__.py +23 -3
- {hexdb-1.6.0 → hexdb-2.0.0}/hexdb.egg-info/PKG-INFO +1 -1
- {hexdb-1.6.0 → hexdb-2.0.0}/pyproject.toml +1 -1
- {hexdb-1.6.0 → hexdb-2.0.0}/tests/test_client.py +61 -0
- {hexdb-1.6.0 → hexdb-2.0.0}/README.md +0 -0
- {hexdb-1.6.0 → hexdb-2.0.0}/hexdb.egg-info/SOURCES.txt +0 -0
- {hexdb-1.6.0 → hexdb-2.0.0}/hexdb.egg-info/dependency_links.txt +0 -0
- {hexdb-1.6.0 → hexdb-2.0.0}/hexdb.egg-info/top_level.txt +0 -0
- {hexdb-1.6.0 → hexdb-2.0.0}/setup.cfg +0 -0
|
@@ -26,7 +26,7 @@ import urllib.request
|
|
|
26
26
|
from typing import Any, BinaryIO, Callable, Dict, Iterator, List, Optional, Sequence, Tuple, Union
|
|
27
27
|
|
|
28
28
|
__all__ = ["HexDB", "HexDBError", "Tessellation", "Stream", "Files"]
|
|
29
|
-
__version__ = "
|
|
29
|
+
__version__ = "2.0.0"
|
|
30
30
|
|
|
31
31
|
Json = Any
|
|
32
32
|
Document = Dict[str, Any]
|
|
@@ -46,6 +46,22 @@ class HexDBError(Exception):
|
|
|
46
46
|
return f"HexDBError({self.status}, {self.code!r}, {self.message!r})"
|
|
47
47
|
|
|
48
48
|
|
|
49
|
+
class _SameOriginAuth(urllib.request.HTTPRedirectHandler):
|
|
50
|
+
"""Follow redirects, but send the API key only to the server it was given for."""
|
|
51
|
+
|
|
52
|
+
def redirect_request(self, req, fp, code, msg, headers, newurl):
|
|
53
|
+
new = super().redirect_request(req, fp, code, msg, headers, newurl)
|
|
54
|
+
if new is not None and _origin(newurl) != _origin(req.full_url):
|
|
55
|
+
new.headers.pop("Authorization", None)
|
|
56
|
+
new.unredirected_hdrs.pop("Authorization", None)
|
|
57
|
+
return new
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _origin(url: str) -> Tuple[str, str]:
|
|
61
|
+
parts = urllib.parse.urlsplit(url)
|
|
62
|
+
return (parts.scheme.lower(), parts.netloc.lower())
|
|
63
|
+
|
|
64
|
+
|
|
49
65
|
def _quote(text: str) -> str:
|
|
50
66
|
return urllib.parse.quote(text, safe="")
|
|
51
67
|
|
|
@@ -67,6 +83,10 @@ class HexDB:
|
|
|
67
83
|
self.retries = retries
|
|
68
84
|
self.timeout = timeout
|
|
69
85
|
self._context = ssl.create_default_context(cafile=ca_file) if ca_file else None
|
|
86
|
+
handlers: List[Any] = [_SameOriginAuth()]
|
|
87
|
+
if self._context is not None:
|
|
88
|
+
handlers.append(urllib.request.HTTPSHandler(context=self._context))
|
|
89
|
+
self._opener = urllib.request.build_opener(*handlers)
|
|
70
90
|
|
|
71
91
|
# -- transport ---------------------------------------------------------
|
|
72
92
|
|
|
@@ -84,7 +104,7 @@ class HexDB:
|
|
|
84
104
|
while True:
|
|
85
105
|
req = urllib.request.Request(self.base + path, data=data, method=method, headers=headers)
|
|
86
106
|
try:
|
|
87
|
-
with
|
|
107
|
+
with self._opener.open(req, timeout=timeout or self.timeout) as response:
|
|
88
108
|
if response.status == 204:
|
|
89
109
|
return None
|
|
90
110
|
text = response.read().decode("utf-8")
|
|
@@ -115,7 +135,7 @@ class HexDB:
|
|
|
115
135
|
headers["Authorization"] = f"Bearer {self.token}"
|
|
116
136
|
req = urllib.request.Request(self.base + path, data=data, method=method, headers=headers)
|
|
117
137
|
try:
|
|
118
|
-
response =
|
|
138
|
+
response = self._opener.open(req, timeout=timeout or self.timeout)
|
|
119
139
|
except urllib.error.HTTPError as e:
|
|
120
140
|
text = e.read().decode("utf-8", "replace")
|
|
121
141
|
e.close()
|
|
@@ -146,3 +146,64 @@ class ClientTest(unittest.TestCase):
|
|
|
146
146
|
|
|
147
147
|
if __name__ == "__main__":
|
|
148
148
|
unittest.main()
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
class RedirectTests(unittest.TestCase):
|
|
152
|
+
"""The API key isn't sent to another server on a redirect."""
|
|
153
|
+
|
|
154
|
+
def test_api_key_stays_with_its_server(self):
|
|
155
|
+
import http.server
|
|
156
|
+
import threading
|
|
157
|
+
|
|
158
|
+
seen = {}
|
|
159
|
+
|
|
160
|
+
def server(handler):
|
|
161
|
+
httpd = http.server.HTTPServer(("127.0.0.1", 0), handler)
|
|
162
|
+
threading.Thread(target=httpd.serve_forever, daemon=True).start()
|
|
163
|
+
return httpd
|
|
164
|
+
|
|
165
|
+
class Other(http.server.BaseHTTPRequestHandler):
|
|
166
|
+
def do_GET(self):
|
|
167
|
+
seen["other"] = self.headers.get("Authorization")
|
|
168
|
+
self.send_response(200)
|
|
169
|
+
self.send_header("Content-Type", "application/json")
|
|
170
|
+
self.end_headers()
|
|
171
|
+
self.wfile.write(b"{}")
|
|
172
|
+
|
|
173
|
+
def log_message(self, *args):
|
|
174
|
+
pass
|
|
175
|
+
|
|
176
|
+
other = server(Other)
|
|
177
|
+
target = "http://127.0.0.1:%d/landing" % other.server_address[1]
|
|
178
|
+
|
|
179
|
+
class First(http.server.BaseHTTPRequestHandler):
|
|
180
|
+
def do_GET(self):
|
|
181
|
+
seen.setdefault("first", self.headers.get("Authorization"))
|
|
182
|
+
if self.path == "/same":
|
|
183
|
+
self.send_response(302)
|
|
184
|
+
self.send_header("Location", "/landing")
|
|
185
|
+
elif self.path == "/landing":
|
|
186
|
+
seen["same"] = self.headers.get("Authorization")
|
|
187
|
+
self.send_response(200)
|
|
188
|
+
self.send_header("Content-Type", "application/json")
|
|
189
|
+
self.end_headers()
|
|
190
|
+
self.wfile.write(b"{}")
|
|
191
|
+
return
|
|
192
|
+
else:
|
|
193
|
+
self.send_response(302)
|
|
194
|
+
self.send_header("Location", target)
|
|
195
|
+
self.end_headers()
|
|
196
|
+
|
|
197
|
+
def log_message(self, *args):
|
|
198
|
+
pass
|
|
199
|
+
|
|
200
|
+
first = server(First)
|
|
201
|
+
client = HexDB("http://127.0.0.1:%d" % first.server_address[1], "hxk_secret")
|
|
202
|
+
client.request("GET", "/same")
|
|
203
|
+
self.assertEqual(seen["same"], "Bearer hxk_secret", "kept on the same server")
|
|
204
|
+
client.request("GET", "/elsewhere")
|
|
205
|
+
self.assertEqual(seen["first"], "Bearer hxk_secret")
|
|
206
|
+
self.assertIsNone(seen["other"], "dropped for another server")
|
|
207
|
+
first.shutdown()
|
|
208
|
+
other.shutdown()
|
|
209
|
+
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|