httpdbg 0.7.1__tar.gz → 0.7.3__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.
Files changed (56) hide show
  1. {httpdbg-0.7.1 → httpdbg-0.7.3}/PKG-INFO +3 -3
  2. {httpdbg-0.7.1 → httpdbg-0.7.3}/README.md +2 -2
  3. {httpdbg-0.7.1 → httpdbg-0.7.3}/httpdbg/__init__.py +1 -1
  4. {httpdbg-0.7.1 → httpdbg-0.7.3}/httpdbg/hooks/socket.py +26 -20
  5. {httpdbg-0.7.1 → httpdbg-0.7.3}/httpdbg/records.py +26 -11
  6. {httpdbg-0.7.1 → httpdbg-0.7.3}/httpdbg.egg-info/PKG-INFO +3 -3
  7. {httpdbg-0.7.1 → httpdbg-0.7.3}/tests/test_hook_aiohttp.py +27 -1
  8. {httpdbg-0.7.1 → httpdbg-0.7.3}/tests/test_hook_httpx.py +67 -15
  9. {httpdbg-0.7.1 → httpdbg-0.7.3}/tests/test_hook_requests.py +41 -0
  10. {httpdbg-0.7.1 → httpdbg-0.7.3}/LICENSE +0 -0
  11. {httpdbg-0.7.1 → httpdbg-0.7.3}/httpdbg/__main__.py +0 -0
  12. {httpdbg-0.7.1 → httpdbg-0.7.3}/httpdbg/args.py +0 -0
  13. {httpdbg-0.7.1 → httpdbg-0.7.3}/httpdbg/hooks/__init__.py +0 -0
  14. {httpdbg-0.7.1 → httpdbg-0.7.3}/httpdbg/hooks/aiohttp.py +0 -0
  15. {httpdbg-0.7.1 → httpdbg-0.7.3}/httpdbg/hooks/all.py +0 -0
  16. {httpdbg-0.7.1 → httpdbg-0.7.3}/httpdbg/hooks/httpx.py +0 -0
  17. {httpdbg-0.7.1 → httpdbg-0.7.3}/httpdbg/hooks/requests.py +0 -0
  18. {httpdbg-0.7.1 → httpdbg-0.7.3}/httpdbg/hooks/urllib3.py +0 -0
  19. {httpdbg-0.7.1 → httpdbg-0.7.3}/httpdbg/hooks/utils.py +0 -0
  20. {httpdbg-0.7.1 → httpdbg-0.7.3}/httpdbg/initiator.py +0 -0
  21. {httpdbg-0.7.1 → httpdbg-0.7.3}/httpdbg/mode_console.py +0 -0
  22. {httpdbg-0.7.1 → httpdbg-0.7.3}/httpdbg/mode_module.py +0 -0
  23. {httpdbg-0.7.1 → httpdbg-0.7.3}/httpdbg/mode_script.py +0 -0
  24. {httpdbg-0.7.1 → httpdbg-0.7.3}/httpdbg/server.py +0 -0
  25. {httpdbg-0.7.1 → httpdbg-0.7.3}/httpdbg/utils.py +0 -0
  26. {httpdbg-0.7.1 → httpdbg-0.7.3}/httpdbg/webapp/__init__.py +0 -0
  27. {httpdbg-0.7.1 → httpdbg-0.7.3}/httpdbg/webapp/api.py +0 -0
  28. {httpdbg-0.7.1 → httpdbg-0.7.3}/httpdbg/webapp/app.py +0 -0
  29. {httpdbg-0.7.1 → httpdbg-0.7.3}/httpdbg/webapp/preview.py +0 -0
  30. {httpdbg-0.7.1 → httpdbg-0.7.3}/httpdbg/webapp/static/api.js +0 -0
  31. {httpdbg-0.7.1 → httpdbg-0.7.3}/httpdbg/webapp/static/column.css +0 -0
  32. {httpdbg-0.7.1 → httpdbg-0.7.3}/httpdbg/webapp/static/configuration.js +0 -0
  33. {httpdbg-0.7.1 → httpdbg-0.7.3}/httpdbg/webapp/static/detail.css +0 -0
  34. {httpdbg-0.7.1 → httpdbg-0.7.3}/httpdbg/webapp/static/favicon.ico +0 -0
  35. {httpdbg-0.7.1 → httpdbg-0.7.3}/httpdbg/webapp/static/filter.js +0 -0
  36. {httpdbg-0.7.1 → httpdbg-0.7.3}/httpdbg/webapp/static/index.htm +0 -0
  37. {httpdbg-0.7.1 → httpdbg-0.7.3}/httpdbg/webapp/static/main.js +0 -0
  38. {httpdbg-0.7.1 → httpdbg-0.7.3}/httpdbg/webapp/static/mustache.min.js +0 -0
  39. {httpdbg-0.7.1 → httpdbg-0.7.3}/httpdbg/webapp/static/render.js +0 -0
  40. {httpdbg-0.7.1 → httpdbg-0.7.3}/httpdbg/webapp/static/style.css +0 -0
  41. {httpdbg-0.7.1 → httpdbg-0.7.3}/httpdbg/webapp/static/table.css +0 -0
  42. {httpdbg-0.7.1 → httpdbg-0.7.3}/httpdbg/webapp/static/tooltip.css +0 -0
  43. {httpdbg-0.7.1 → httpdbg-0.7.3}/httpdbg.egg-info/SOURCES.txt +0 -0
  44. {httpdbg-0.7.1 → httpdbg-0.7.3}/httpdbg.egg-info/dependency_links.txt +0 -0
  45. {httpdbg-0.7.1 → httpdbg-0.7.3}/httpdbg.egg-info/entry_points.txt +0 -0
  46. {httpdbg-0.7.1 → httpdbg-0.7.3}/httpdbg.egg-info/top_level.txt +0 -0
  47. {httpdbg-0.7.1 → httpdbg-0.7.3}/pyproject.toml +0 -0
  48. {httpdbg-0.7.1 → httpdbg-0.7.3}/setup.cfg +0 -0
  49. {httpdbg-0.7.1 → httpdbg-0.7.3}/tests/test_api.py +0 -0
  50. {httpdbg-0.7.1 → httpdbg-0.7.3}/tests/test_cli.py +0 -0
  51. {httpdbg-0.7.1 → httpdbg-0.7.3}/tests/test_context_manager.py +0 -0
  52. {httpdbg-0.7.1 → httpdbg-0.7.3}/tests/test_hook_urllib3.py +0 -0
  53. {httpdbg-0.7.1 → httpdbg-0.7.3}/tests/test_mode_console.py +0 -0
  54. {httpdbg-0.7.1 → httpdbg-0.7.3}/tests/test_mode_module_pytest.py +0 -0
  55. {httpdbg-0.7.1 → httpdbg-0.7.3}/tests/test_mode_script.py +0 -0
  56. {httpdbg-0.7.1 → httpdbg-0.7.3}/tests/test_preview.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: httpdbg
3
- Version: 0.7.1
3
+ Version: 0.7.3
4
4
  Summary: A very simple tool to debug HTTP(S) client requests
5
5
  Author-email: cle-b <cle@tictac.pm>
6
6
  License: Apache-2.0
@@ -86,8 +86,8 @@ pyhttpdbg -m pip install httpdbg --upgrade
86
86
  |------------|-------------|-------------------------------------------------------------------|
87
87
  | requests | supported | - |
88
88
  | urllib3 | supported | - |
89
- | httpx | supported | Async HTTPS requests not intercepted |
90
- | aiohttp | supported | HTTP requests not intercepted on Windows |
89
+ | httpx | supported | Async HTTP requests not intercepted on Windows (HTTPS OK) |
90
+ | aiohttp | supported | Async HTTP requests not intercepted on Windows (HTTPS OK) |
91
91
 
92
92
  ## configuration
93
93
 
@@ -66,8 +66,8 @@ pyhttpdbg -m pip install httpdbg --upgrade
66
66
  |------------|-------------|-------------------------------------------------------------------|
67
67
  | requests | supported | - |
68
68
  | urllib3 | supported | - |
69
- | httpx | supported | Async HTTPS requests not intercepted |
70
- | aiohttp | supported | HTTP requests not intercepted on Windows |
69
+ | httpx | supported | Async HTTP requests not intercepted on Windows (HTTPS OK) |
70
+ | aiohttp | supported | Async HTTP requests not intercepted on Windows (HTTPS OK) |
71
71
 
72
72
  ## configuration
73
73
 
@@ -2,6 +2,6 @@
2
2
  from httpdbg.hooks.all import httpdbg
3
3
  from httpdbg.utils import logger
4
4
 
5
- VERSION = "0.7.1"
5
+ VERSION = "0.7.3"
6
6
 
7
7
  __all__ = ["httpdbg", "logger"]
@@ -32,7 +32,9 @@ def set_hook_for_socket_connect(records, method):
32
32
  try:
33
33
  r = method(self, address)
34
34
  except Exception as ex:
35
- if not isinstance(ex, BlockingIOError): # for async
35
+ if not isinstance(
36
+ ex, (BlockingIOError, OSError)
37
+ ): # BlockingIOError for async, OSError for ipv6
36
38
  record = HTTPRecord()
37
39
  record.initiator = get_initiator(records._initiators)
38
40
 
@@ -61,11 +63,13 @@ def set_hook_for_ssl_wrap_socket(records, method):
61
63
 
62
64
  raise
63
65
 
66
+ logger.info(
67
+ f"WRAP_SOCKET - {type(sock)}={id(sock)} {type(sslsocket)}={id(sslsocket)}"
68
+ )
69
+
64
70
  socketdata = records.move_socket_data(sslsocket, sock)
65
71
  if socketdata:
66
- logger.info(
67
- f"WRAP_SOCKET - sock={sock} sockid={id(sock)} sslsocket={sslsocket} sslsocketid={id(sslsocket)} socketdata={socketdata}"
68
- )
72
+ logger.info(f"WRAP_SOCKET * - socketdata={socketdata}")
69
73
 
70
74
  return sslsocket
71
75
 
@@ -86,11 +90,13 @@ def set_hook_for_sslcontext_wrap_socket(records, method):
86
90
 
87
91
  raise
88
92
 
93
+ logger.info(
94
+ f"WRAP_SOCKET (SSLContext) - {type(self)}={id(self)} {type(sock)}={id(sock)} {type(sslsocket)}={id(sslsocket)}"
95
+ )
96
+
89
97
  socketdata = records.move_socket_data(sslsocket, sock)
90
98
  if socketdata:
91
- logger.info(
92
- f"WRAP_SOCKET (SSLContext) - socket={sock} socketid={id(sock)} sslsocket={sslsocket} sslsocketid={id(sslsocket)} socketdata={socketdata}"
93
- )
99
+ logger.info(f"WRAP_SOCKET (SSLContext) * - socketdata={socketdata}")
94
100
 
95
101
  return sslsocket
96
102
 
@@ -111,11 +117,13 @@ def set_hook_for_socket_wrap_bio(records, method):
111
117
 
112
118
  raise
113
119
 
114
- socketdata = records.move_socket_data(sslobject, self)
120
+ logger.info(
121
+ f"WRAP_SOCKET_BIO - {type(self)}={id(self)} {type(sslobject)}={id(sslobject)}"
122
+ )
123
+
124
+ socketdata = records.get_socket_data(sslobject, self)
115
125
  if socketdata:
116
- logger.info(
117
- f"WRAP_SOCKET_BIO - sock={self} sockid={id(self)} sslobject={sslobject} sslobjectid={id(sslobject)} socketdata={socketdata}"
118
- )
126
+ logger.info(f"WRAP_SOCKET_BIO * - socketdata={socketdata}")
119
127
 
120
128
  return sslobject
121
129
 
@@ -171,7 +179,7 @@ def set_hook_for_socket_recv(records, method):
171
179
 
172
180
  def set_hook_for_socket_sendall(records, method):
173
181
  def hook(self, bytes, *args, **kwargs):
174
- socketdata = records.get_socket_data(self)
182
+ socketdata = records.get_socket_data(self, request=True)
175
183
  if socketdata:
176
184
  logger.info(
177
185
  f"SENDALL - self={self} id={id(self)} socketdata={socketdata} bytes={(b''+bytes)[:20]} type={type(bytes)} len={len(bytes)} args={args} kwargs={kwargs}"
@@ -206,7 +214,7 @@ def set_hook_for_socket_sendall(records, method):
206
214
 
207
215
  def set_hook_for_socket_send(records, method):
208
216
  def hook(self, bytes, *args, **kwargs):
209
- socketdata = records.get_socket_data(self)
217
+ socketdata = records.get_socket_data(self, request=True)
210
218
  if socketdata:
211
219
  logger.info(
212
220
  f"SEND - self={self} id={id(self)} socketdata={socketdata} bytes={(b''+bytes)[:20]} args={args} kwargs={kwargs}"
@@ -264,11 +272,10 @@ def set_hook_for_asyncio_create_connection(records, method):
264
272
 
265
273
  def set_hook_for_sslobject_write(records, method):
266
274
  def hook(self, buf, *args, **kwargs):
267
- socketdata = records.get_socket_data(self)
275
+ logger.info(f"WRITE - {type(self)}={id(self)} buf={(b'' + buf)[:20]}")
276
+ socketdata = records.get_socket_data(self, request=True)
268
277
  if socketdata:
269
- logger.info(
270
- f"WRITE - self={self} id={id(self)} socketdata={socketdata} buf={(b'' + buf)[:20]} args={args} kwargs={kwargs}"
271
- )
278
+ logger.info(f"WRITE * - socketdata={socketdata}")
272
279
 
273
280
  try:
274
281
  size = method(self, buf, *args, **kwargs)
@@ -299,11 +306,10 @@ def set_hook_for_sslobject_write(records, method):
299
306
 
300
307
  def set_hook_for_sslobject_read(records, method):
301
308
  def hook(self, *args, **kwargs):
309
+ logger.info(f"READ - {type(self)}={id(self)}")
302
310
  socketdata = records.get_socket_data(self)
303
311
  if socketdata:
304
- logger.info(
305
- f"READ - self={self} id={id(self)} socketdata={socketdata} args={args} kwargs={kwargs}"
306
- )
312
+ logger.info(f"READ * - socketdata={socketdata}")
307
313
 
308
314
  try:
309
315
  r = method(self, *args, **kwargs)
@@ -64,14 +64,25 @@ class HTTPRecords:
64
64
  def __len__(self):
65
65
  return len(self.requests)
66
66
 
67
- def get_socket_data(self, obj, extra_sock=None, force_new=False):
68
- socket_data = None
67
+ def get_socket_data(self, obj, extra_sock=None, force_new=False, request=None):
68
+ socketdata = None
69
69
 
70
70
  if force_new:
71
71
  self.del_socket_data(obj)
72
72
 
73
73
  if id(obj) in self._sockets:
74
- socket_data = self._sockets[id(obj)]
74
+ socketdata = self._sockets[id(obj)]
75
+ if request:
76
+ if (
77
+ socketdata
78
+ and socketdata.record
79
+ and socketdata.record.response.rawdata
80
+ ):
81
+ # the socket is reused for a new request
82
+ self._sockets[id(obj)] = SocketRawData(
83
+ id(obj), socketdata.address, socketdata.ssl
84
+ )
85
+ socketdata = self._sockets[id(obj)]
75
86
  else:
76
87
  if isinstance(obj, socket.socket):
77
88
  try:
@@ -79,33 +90,37 @@ class HTTPRecords:
79
90
  self._sockets[id(obj)] = SocketRawData(
80
91
  id(obj), address, isinstance(obj, ssl.SSLSocket)
81
92
  )
82
- socket_data = self._sockets[id(obj)]
93
+ socketdata = self._sockets[id(obj)]
83
94
  except OSError:
84
95
  # OSError: [WinError 10022] An invalid argument was supplied
85
96
  pass
86
97
  else:
87
98
  if extra_sock:
88
99
  try:
89
- address = extra_sock.getsockname()
100
+ address = (
101
+ extra_sock.getsockname()
102
+ if hasattr(extra_sock, "getsockname")
103
+ else ("", 0) # wrap_bio
104
+ )
90
105
  self._sockets[id(obj)] = SocketRawData(
91
106
  id(obj),
92
107
  address,
93
108
  isinstance(obj, (ssl.SSLObject, ssl.SSLSocket)),
94
109
  )
95
- socket_data = self._sockets[id(obj)]
110
+ socketdata = self._sockets[id(obj)]
96
111
  except OSError:
97
112
  # OSError: [WinError 10022] An invalid argument was supplied
98
113
  pass
99
114
 
100
- return socket_data
115
+ return socketdata
101
116
 
102
117
  def move_socket_data(self, dest, ori):
103
118
  if id(ori) in self._sockets:
104
- socket_data = self.get_socket_data(ori)
105
- if socket_data:
106
- self._sockets[id(dest)] = socket_data
119
+ socketdata = self.get_socket_data(ori)
120
+ if socketdata:
121
+ self._sockets[id(dest)] = socketdata
107
122
  if isinstance(dest, (ssl.SSLSocket, ssl.SSLObject)):
108
- socket_data.ssl = True
123
+ socketdata.ssl = True
109
124
  self.del_socket_data(ori)
110
125
 
111
126
  def del_socket_data(self, obj):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: httpdbg
3
- Version: 0.7.1
3
+ Version: 0.7.3
4
4
  Summary: A very simple tool to debug HTTP(S) client requests
5
5
  Author-email: cle-b <cle@tictac.pm>
6
6
  License: Apache-2.0
@@ -86,8 +86,8 @@ pyhttpdbg -m pip install httpdbg --upgrade
86
86
  |------------|-------------|-------------------------------------------------------------------|
87
87
  | requests | supported | - |
88
88
  | urllib3 | supported | - |
89
- | httpx | supported | Async HTTPS requests not intercepted |
90
- | aiohttp | supported | HTTP requests not intercepted on Windows |
89
+ | httpx | supported | Async HTTP requests not intercepted on Windows (HTTPS OK) |
90
+ | aiohttp | supported | Async HTTP requests not intercepted on Windows (HTTPS OK) |
91
91
 
92
92
  ## configuration
93
93
 
@@ -321,7 +321,7 @@ async def test_aiohttp_exception_asyncclient():
321
321
  platform.system().lower() == "windows",
322
322
  reason="Async HTTP requests not intercepted on Windows",
323
323
  )
324
- async def test_aiohttp_get_empty_request_content(httpbin_both):
324
+ async def test_aiohttp_get_empty_request_content_asyncclient(httpbin_both):
325
325
  with httpdbg() as records:
326
326
  async with aiohttp.ClientSession(
327
327
  connector=aiohttp.TCPConnector(verify_ssl=False)
@@ -333,3 +333,29 @@ async def test_aiohttp_get_empty_request_content(httpbin_both):
333
333
 
334
334
  assert http_record.url == f"{httpbin_both.url}/get"
335
335
  assert http_record.request.content == b""
336
+
337
+
338
+ @pytest.mark.aiohttp
339
+ @pytest.mark.asyncio
340
+ @pytest.mark.xfail(
341
+ platform.system().lower() == "windows",
342
+ reason="Async HTTP requests not intercepted on Windows",
343
+ )
344
+ async def test_aiohttp_many_requests_session_asyncclient():
345
+ with httpdbg() as records:
346
+ async with aiohttp.ClientSession() as session:
347
+ await session.get("https://httpbin.org/get")
348
+ await session.get("https://httpbin.org/get/abc")
349
+ await session.post("https://httpbin.org/post", data="abc")
350
+ await session.get("https://httpbin.org/get")
351
+
352
+ assert len(records) == 4
353
+
354
+ assert records[0].url == "https://httpbin.org/get"
355
+ assert records[0].request.content == b""
356
+ assert records[1].url == "https://httpbin.org/get/abc"
357
+ assert records[1].request.content == b""
358
+ assert records[2].url == "https://httpbin.org/post"
359
+ assert records[2].request.content == b"abc"
360
+ assert records[3].url == "https://httpbin.org/get"
361
+ assert records[3].request.content == b""
@@ -232,15 +232,53 @@ def test_httpx_get_empty_request_content(httpbin_both):
232
232
  assert http_record.request.content == b""
233
233
 
234
234
 
235
+ @pytest.mark.httpx
236
+ def test_httpx_many_requests():
237
+ with httpdbg() as records:
238
+ httpx.get("https://httpbin.org/get")
239
+ httpx.get("https://httpbin.org/get/abc")
240
+ httpx.post("https://httpbin.org/post", data="abc")
241
+ httpx.get("https://httpbin.org/get")
242
+
243
+ assert len(records) == 4
244
+
245
+ assert records[0].url == "https://httpbin.org/get"
246
+ assert records[0].request.content == b""
247
+ assert records[1].url == "https://httpbin.org/get/abc"
248
+ assert records[1].request.content == b""
249
+ assert records[2].url == "https://httpbin.org/post"
250
+ assert records[2].request.content == b"abc"
251
+ assert records[3].url == "https://httpbin.org/get"
252
+ assert records[3].request.content == b""
253
+
254
+
255
+ @pytest.mark.httpx
256
+ def test_httpx_many_requests_session():
257
+ with httpdbg() as records:
258
+ with httpx.Client() as session:
259
+ session.get("https://httpbin.org/get")
260
+ session.get("https://httpbin.org/get/abc")
261
+ session.post("https://httpbin.org/post", data="abc")
262
+ session.get("https://httpbin.org/get")
263
+
264
+ assert len(records) == 4
265
+
266
+ assert records[0].url == "https://httpbin.org/get"
267
+ assert records[0].request.content == b""
268
+ assert records[1].url == "https://httpbin.org/get/abc"
269
+ assert records[1].request.content == b""
270
+ assert records[2].url == "https://httpbin.org/post"
271
+ assert records[2].request.content == b"abc"
272
+ assert records[3].url == "https://httpbin.org/get"
273
+ assert records[3].request.content == b""
274
+
275
+
235
276
  @pytest.mark.httpx
236
277
  @pytest.mark.asyncio
237
278
  @pytest.mark.xfail(
238
279
  platform.system().lower() == "windows",
239
280
  reason="Async HTTP requests not intercepted on Windows",
240
281
  )
241
- @pytest.mark.xfail(
242
- reason="Async HTTPS requests not intercepted",
243
- )
244
282
  async def test_httpx_asyncclient(httpbin_both):
245
283
  with httpdbg() as records:
246
284
  async with httpx.AsyncClient(verify=False) as client:
@@ -285,9 +323,6 @@ async def test_httpx_initiator_asyncclient(httpbin):
285
323
  platform.system().lower() == "windows",
286
324
  reason="Async HTTP requests not intercepted on Windows",
287
325
  )
288
- @pytest.mark.xfail(
289
- reason="Async HTTPS requests not intercepted",
290
- )
291
326
  async def test_httpx_request_asyncclient(httpbin_both):
292
327
  with httpdbg() as records:
293
328
  async with httpx.AsyncClient(verify=False) as client:
@@ -310,9 +345,6 @@ async def test_httpx_request_asyncclient(httpbin_both):
310
345
  platform.system().lower() == "windows",
311
346
  reason="Async HTTP requests not intercepted on Windows",
312
347
  )
313
- @pytest.mark.xfail(
314
- reason="Async HTTPS requests not intercepted",
315
- )
316
348
  async def test_httpx_response_asyncclient(httpbin_both):
317
349
  with httpdbg() as records:
318
350
  async with httpx.AsyncClient(verify=False) as client:
@@ -370,9 +402,6 @@ async def test_httpx_cookies_asyncclient(httpbin):
370
402
  platform.system().lower() == "windows",
371
403
  reason="Async HTTP requests not intercepted on Windows",
372
404
  )
373
- @pytest.mark.xfail(
374
- reason="Async HTTPS requests not intercepted",
375
- )
376
405
  async def test_httpx_cookies_asyncclient_secure(httpbin_secure):
377
406
  with httpdbg() as records:
378
407
  async with httpx.AsyncClient(verify=False) as client:
@@ -426,9 +455,6 @@ async def test_httpx_exception_asyncclient():
426
455
  platform.system().lower() == "windows",
427
456
  reason="Async HTTP requests not intercepted on Windows",
428
457
  )
429
- @pytest.mark.xfail(
430
- reason="Async HTTPS requests not intercepted",
431
- )
432
458
  async def test_httpx_get_empty_request_content_asyncclient(httpbin_both):
433
459
  with httpdbg() as records:
434
460
  async with httpx.AsyncClient(verify=False) as client:
@@ -439,3 +465,29 @@ async def test_httpx_get_empty_request_content_asyncclient(httpbin_both):
439
465
 
440
466
  assert http_record.url == f"{httpbin_both.url}/get"
441
467
  assert http_record.request.content == b""
468
+
469
+
470
+ @pytest.mark.httpx
471
+ @pytest.mark.asyncio
472
+ @pytest.mark.xfail(
473
+ platform.system().lower() == "windows",
474
+ reason="Async HTTP requests not intercepted on Windows",
475
+ )
476
+ async def test_httpx_many_requests_session_asyncclient():
477
+ with httpdbg() as records:
478
+ async with httpx.AsyncClient() as session:
479
+ await session.get("https://httpbin.org/get")
480
+ await session.get("https://httpbin.org/get/abc")
481
+ await session.post("https://httpbin.org/post", data="abc")
482
+ await session.get("https://httpbin.org/get")
483
+
484
+ assert len(records) == 4
485
+
486
+ assert records[0].url == "https://httpbin.org/get"
487
+ assert records[0].request.content == b""
488
+ assert records[1].url == "https://httpbin.org/get/abc"
489
+ assert records[1].request.content == b""
490
+ assert records[2].url == "https://httpbin.org/post"
491
+ assert records[2].request.content == b"abc"
492
+ assert records[3].url == "https://httpbin.org/get"
493
+ assert records[3].request.content == b""
@@ -217,3 +217,44 @@ def test_requests_get_empty_request_content(httpbin_both):
217
217
 
218
218
  assert http_record.url == f"{httpbin_both.url}/get"
219
219
  assert http_record.request.content == b""
220
+
221
+
222
+ @pytest.mark.requests
223
+ def test_requests_many_requests():
224
+ with httpdbg() as records:
225
+ requests.get("https://httpbin.org/get")
226
+ requests.get("https://httpbin.org/get/abc")
227
+ requests.post("https://httpbin.org/post", data="abc")
228
+ requests.get("https://httpbin.org/get")
229
+
230
+ assert len(records) == 4
231
+
232
+ assert records[0].url == "https://httpbin.org/get"
233
+ assert records[0].request.content == b""
234
+ assert records[1].url == "https://httpbin.org/get/abc"
235
+ assert records[1].request.content == b""
236
+ assert records[2].url == "https://httpbin.org/post"
237
+ assert records[2].request.content == b"abc"
238
+ assert records[3].url == "https://httpbin.org/get"
239
+ assert records[3].request.content == b""
240
+
241
+
242
+ @pytest.mark.requests
243
+ def test_requests_many_requests_session():
244
+ with httpdbg() as records:
245
+ with requests.Session() as session:
246
+ session.get("https://httpbin.org/get")
247
+ session.get("https://httpbin.org/get/abc")
248
+ session.post("https://httpbin.org/post", data="abc")
249
+ session.get("https://httpbin.org/get")
250
+
251
+ assert len(records) == 4
252
+
253
+ assert records[0].url == "https://httpbin.org/get"
254
+ assert records[0].request.content == b""
255
+ assert records[1].url == "https://httpbin.org/get/abc"
256
+ assert records[1].request.content == b""
257
+ assert records[2].url == "https://httpbin.org/post"
258
+ assert records[2].request.content == b"abc"
259
+ assert records[3].url == "https://httpbin.org/get"
260
+ assert records[3].request.content == b""
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes