httpdbg 0.31.6__tar.gz → 0.32.1__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.
- {httpdbg-0.31.6 → httpdbg-0.32.1}/PKG-INFO +10 -16
- {httpdbg-0.31.6 → httpdbg-0.32.1}/README.md +9 -13
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg/__init__.py +1 -1
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg/env.py +1 -1
- httpdbg-0.32.1/httpdbg/hooks/external.py +80 -0
- httpdbg-0.32.1/httpdbg/hooks/template_sitecustomize.py +65 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg/mode_script.py +15 -13
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg.egg-info/PKG-INFO +10 -16
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg.egg-info/SOURCES.txt +2 -1
- {httpdbg-0.31.6 → httpdbg-0.32.1}/pyproject.toml +0 -3
- {httpdbg-0.31.6 → httpdbg-0.32.1}/tests/test_mode_script.py +2 -2
- httpdbg-0.32.1/tests/test_multiprocess.py +52 -0
- httpdbg-0.31.6/httpdbg/hooks/external.py +0 -61
- httpdbg-0.31.6/httpdbg.egg-info/requires.txt +0 -3
- {httpdbg-0.31.6 → httpdbg-0.32.1}/LICENSE +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg/__main__.py +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg/args.py +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg/exception.py +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg/hooks/__init__.py +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg/hooks/aiohttp.py +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg/hooks/all.py +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg/hooks/generic.py +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg/hooks/httpx.py +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg/hooks/pytest.py +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg/hooks/requests.py +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg/hooks/socket.py +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg/hooks/unittest.py +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg/hooks/urllib3.py +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg/hooks/utils.py +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg/initiator.py +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg/log.py +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg/mode_console.py +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg/mode_module.py +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg/preview.py +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg/records.py +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg/server.py +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg/utils.py +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg/webapp/__init__.py +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg/webapp/api.py +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg/webapp/app.py +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg/webapp/static/api.js +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg/webapp/static/column.css +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg/webapp/static/configuration.css +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg/webapp/static/configuration.js +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg/webapp/static/detail.css +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg/webapp/static/favicon.ico +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg/webapp/static/filter.js +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg/webapp/static/icons/chevron-direction-top-outline-icon.svg +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg/webapp/static/icons/download-install-line-icon.svg +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg/webapp/static/icons/engine-gears-icon.svg +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg/webapp/static/icons/eye-blind-icon.svg +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg/webapp/static/icons/eye-icon.svg +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg/webapp/static/icons/files-papers-outline-icon.svg +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg/webapp/static/icons/filter-outline-icon.svg +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg/webapp/static/icons/information-mark-circle-outline-icon.svg +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg/webapp/static/icons/lock-line-icon.svg +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg/webapp/static/icons/lock-open-line-icon.svg +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg/webapp/static/icons/math-multiplication-icon.svg +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg/webapp/static/icons/push-pin-green-icon.svg +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg/webapp/static/icons/question-mark-round-line-icon.svg +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg/webapp/static/icons/recycle-bin-line-icon.svg +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg/webapp/static/icons/wait-sandclock-icon.svg +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg/webapp/static/index.htm +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg/webapp/static/main.js +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg/webapp/static/mustache.min.js +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg/webapp/static/render.js +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg/webapp/static/style.css +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg/webapp/static/table.css +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg.egg-info/dependency_links.txt +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg.egg-info/entry_points.txt +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg.egg-info/top_level.txt +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/setup.cfg +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/tests/test_api.py +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/tests/test_cli.py +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/tests/test_context_manager.py +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/tests/test_hook_aiohttp.py +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/tests/test_hook_httpx.py +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/tests/test_hook_pytest.py +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/tests/test_hook_requests.py +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/tests/test_hook_unittest.py +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/tests/test_hook_urllib3.py +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/tests/test_initiator.py +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/tests/test_mode_console.py +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/tests/test_mode_module_pytest.py +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/tests/test_preview.py +0 -0
- {httpdbg-0.31.6 → httpdbg-0.32.1}/tests/test_server.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: httpdbg
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.32.1
|
|
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
|
|
@@ -23,8 +23,6 @@ Classifier: Topic :: Utilities
|
|
|
23
23
|
Requires-Python: >=3.9
|
|
24
24
|
Description-Content-Type: text/markdown
|
|
25
25
|
License-File: LICENSE
|
|
26
|
-
Provides-Extra: pytest
|
|
27
|
-
Requires-Dist: pytest-httpdbg>=0.6.0; extra == "pytest"
|
|
28
26
|
|
|
29
27
|
# httpdbg
|
|
30
28
|
|
|
@@ -71,30 +69,26 @@ You can trace all the HTTP requests performed by a script
|
|
|
71
69
|
pyhttpdbg --script filename.py [arg1 --arg2 ...]
|
|
72
70
|
```
|
|
73
71
|
|
|
74
|
-
###
|
|
75
|
-
|
|
76
|
-
You can trace all the HTTP requests performed during your tests
|
|
72
|
+
### module
|
|
77
73
|
|
|
78
|
-
|
|
79
|
-
pyhttpdbg -m pytest [arg1 --arg2 ...]
|
|
80
|
-
```
|
|
74
|
+
You can trace all the HTTP requests performed by a library module run as a script using the `-m` command line argument.
|
|
81
75
|
|
|
82
|
-
|
|
76
|
+
For example, you can view which HTTP requests are performed by `pip` when you install a package.
|
|
83
77
|
|
|
84
78
|
```console
|
|
85
|
-
pip install
|
|
79
|
+
pyhttpdbg -m pip install hookdns --upgrade
|
|
86
80
|
```
|
|
87
81
|
|
|
88
|
-
###
|
|
89
|
-
|
|
90
|
-
You can trace all the HTTP requests performed by a library module run as a script using the `-m` command line argument.
|
|
82
|
+
### test frameworks
|
|
91
83
|
|
|
92
|
-
|
|
84
|
+
You can trace all HTTP requests made during your tests (pytest, unittest).
|
|
93
85
|
|
|
94
86
|
```console
|
|
95
|
-
pyhttpdbg -m
|
|
87
|
+
pyhttpdbg -m pytest [arg1 --arg2 ...]
|
|
96
88
|
```
|
|
97
89
|
|
|
90
|
+
In that case, the requests will be grouped by test, and any requests made within a fixture or the setup/teardown methods will be identified by a tag.
|
|
91
|
+
|
|
98
92
|
## Initiators
|
|
99
93
|
|
|
100
94
|
An initiator is the function/method that is at the origin of the HTTP requests. By default, we already support some packages but you can add your own initiators.
|
|
@@ -43,30 +43,26 @@ You can trace all the HTTP requests performed by a script
|
|
|
43
43
|
pyhttpdbg --script filename.py [arg1 --arg2 ...]
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
-
###
|
|
47
|
-
|
|
48
|
-
You can trace all the HTTP requests performed during your tests
|
|
46
|
+
### module
|
|
49
47
|
|
|
50
|
-
|
|
51
|
-
pyhttpdbg -m pytest [arg1 --arg2 ...]
|
|
52
|
-
```
|
|
48
|
+
You can trace all the HTTP requests performed by a library module run as a script using the `-m` command line argument.
|
|
53
49
|
|
|
54
|
-
|
|
50
|
+
For example, you can view which HTTP requests are performed by `pip` when you install a package.
|
|
55
51
|
|
|
56
52
|
```console
|
|
57
|
-
pip install
|
|
53
|
+
pyhttpdbg -m pip install hookdns --upgrade
|
|
58
54
|
```
|
|
59
55
|
|
|
60
|
-
###
|
|
61
|
-
|
|
62
|
-
You can trace all the HTTP requests performed by a library module run as a script using the `-m` command line argument.
|
|
56
|
+
### test frameworks
|
|
63
57
|
|
|
64
|
-
|
|
58
|
+
You can trace all HTTP requests made during your tests (pytest, unittest).
|
|
65
59
|
|
|
66
60
|
```console
|
|
67
|
-
pyhttpdbg -m
|
|
61
|
+
pyhttpdbg -m pytest [arg1 --arg2 ...]
|
|
68
62
|
```
|
|
69
63
|
|
|
64
|
+
In that case, the requests will be grouped by test, and any requests made within a fixture or the setup/teardown methods will be identified by a tag.
|
|
65
|
+
|
|
70
66
|
## Initiators
|
|
71
67
|
|
|
72
68
|
An initiator is the function/method that is at the origin of the HTTP requests. By default, we already support some packages but you can add your own initiators.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
HTTPDBG_CURRENT_GROUP = "HTTPDBG_CURRENT_GROUP"
|
|
2
2
|
HTTPDBG_CURRENT_INITIATOR = "HTTPDBG_CURRENT_INITIATOR"
|
|
3
3
|
HTTPDBG_CURRENT_TAG = "HTTPDBG_CURRENT_TAG"
|
|
4
|
-
|
|
4
|
+
HTTPDBG_MULTIPROCESS_DIR = "HTTPDBG_MULTIPROCESS_DIR"
|
|
5
5
|
|
|
6
6
|
HTTPDBG_LOG_LEVEL = "HTTPDBG_LOG_LEVEL"
|
|
7
7
|
HTTPDBG_LOG_PATH = "HTTPDBG_LOG_PATH"
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
from contextlib import contextmanager
|
|
3
|
+
from typing import Generator
|
|
4
|
+
import glob
|
|
5
|
+
import os
|
|
6
|
+
import pickle
|
|
7
|
+
import shutil
|
|
8
|
+
import tempfile
|
|
9
|
+
import time
|
|
10
|
+
import threading
|
|
11
|
+
|
|
12
|
+
from httpdbg.env import HTTPDBG_MULTIPROCESS_DIR
|
|
13
|
+
from httpdbg.log import logger
|
|
14
|
+
from httpdbg.records import HTTPRecords
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@contextmanager
|
|
18
|
+
def watcher_external(records: HTTPRecords) -> Generator[HTTPRecords, None, None]:
|
|
19
|
+
if HTTPDBG_MULTIPROCESS_DIR not in os.environ:
|
|
20
|
+
with tempfile.TemporaryDirectory(prefix="httpdbg_") as httpdbg_multiprocess_dir:
|
|
21
|
+
|
|
22
|
+
logger().info(f"watcher_external {httpdbg_multiprocess_dir}")
|
|
23
|
+
os.environ[HTTPDBG_MULTIPROCESS_DIR] = httpdbg_multiprocess_dir
|
|
24
|
+
|
|
25
|
+
# we use a custom sitecustomize.py script to record the request in the subprocesses.
|
|
26
|
+
# It doesn't work if the subprocess is created using the "fork" method
|
|
27
|
+
# instead of the "spawn" method.
|
|
28
|
+
template = os.path.join(
|
|
29
|
+
os.path.dirname(os.path.abspath(__file__)),
|
|
30
|
+
"template_sitecustomize.py",
|
|
31
|
+
)
|
|
32
|
+
sitecustomize = os.path.join(httpdbg_multiprocess_dir, "sitecustomize.py")
|
|
33
|
+
|
|
34
|
+
shutil.copy(template, sitecustomize)
|
|
35
|
+
|
|
36
|
+
if "PYTHONPATH" in os.environ:
|
|
37
|
+
os.environ["PYTHONPATH"] = (
|
|
38
|
+
f"{httpdbg_multiprocess_dir}:{os.environ['PYTHONPATH']}"
|
|
39
|
+
)
|
|
40
|
+
else:
|
|
41
|
+
os.environ["PYTHONPATH"] = httpdbg_multiprocess_dir
|
|
42
|
+
|
|
43
|
+
try:
|
|
44
|
+
watcher = WatcherSubprocessDirThread(
|
|
45
|
+
records, httpdbg_multiprocess_dir, 1.0
|
|
46
|
+
)
|
|
47
|
+
watcher.start()
|
|
48
|
+
|
|
49
|
+
yield records
|
|
50
|
+
finally:
|
|
51
|
+
watcher.shutdown()
|
|
52
|
+
else:
|
|
53
|
+
yield records
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class WatcherSubprocessDirThread(threading.Thread):
|
|
57
|
+
def __init__(self, records: HTTPRecords, directory: str, delay: float) -> None:
|
|
58
|
+
self.records: HTTPRecords = records
|
|
59
|
+
self.directory: str = directory
|
|
60
|
+
self.delay: float = delay
|
|
61
|
+
self._running: bool = True
|
|
62
|
+
threading.Thread.__init__(self)
|
|
63
|
+
|
|
64
|
+
def load_dump(self):
|
|
65
|
+
for dump in glob.glob(os.path.join(self.directory, "*.httpdbgrecords")):
|
|
66
|
+
with open(dump, "rb") as dumpfile:
|
|
67
|
+
newrecords: HTTPRecords = pickle.load(dumpfile)
|
|
68
|
+
self.records.requests.update(newrecords.requests)
|
|
69
|
+
self.records.initiators.update(newrecords.initiators)
|
|
70
|
+
self.records.groups.update(newrecords.groups)
|
|
71
|
+
|
|
72
|
+
def run(self):
|
|
73
|
+
while self._running:
|
|
74
|
+
self.load_dump()
|
|
75
|
+
time.sleep(self.delay)
|
|
76
|
+
|
|
77
|
+
def shutdown(self):
|
|
78
|
+
self._running = False
|
|
79
|
+
self.join(timeout=5)
|
|
80
|
+
self.load_dump()
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import atexit
|
|
2
|
+
import os
|
|
3
|
+
import pickle
|
|
4
|
+
import sys
|
|
5
|
+
import time
|
|
6
|
+
import threading
|
|
7
|
+
from typing import Union
|
|
8
|
+
import uuid
|
|
9
|
+
|
|
10
|
+
from httpdbg.hooks.all import httprecord
|
|
11
|
+
from httpdbg import HTTPRecords
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class HttpdbgRecorder:
|
|
15
|
+
"""Records the HTTP requests in subprocesses and saves them to disk."""
|
|
16
|
+
|
|
17
|
+
def __init__(self):
|
|
18
|
+
self._running: bool = False
|
|
19
|
+
self.fname: str = os.path.join(
|
|
20
|
+
os.environ["HTTPDBG_MULTIPROCESS_DIR"], str(uuid.uuid1())
|
|
21
|
+
)
|
|
22
|
+
self.context = None
|
|
23
|
+
self.records: Union[HTTPRecords, None] = None
|
|
24
|
+
|
|
25
|
+
def _save_to_disk_loop(self):
|
|
26
|
+
while self._running:
|
|
27
|
+
self.save_to_disk()
|
|
28
|
+
time.sleep(1)
|
|
29
|
+
|
|
30
|
+
def save_to_disk(self):
|
|
31
|
+
if self.records and len(self.records.requests) > 0:
|
|
32
|
+
with open(f"{self.fname}.httpdbgrecords.tmp", "wb") as f:
|
|
33
|
+
pickle.dump(self.records, f)
|
|
34
|
+
os.replace(
|
|
35
|
+
f"{self.fname}.httpdbgrecords.tmp", f"{self.fname}.httpdbgrecords"
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
def start(self):
|
|
39
|
+
"""Start recording."""
|
|
40
|
+
self.context = httprecord()
|
|
41
|
+
self.records = self.context.__enter__()
|
|
42
|
+
self._running = True
|
|
43
|
+
self._thread = threading.Thread(target=self._save_to_disk_loop, daemon=True)
|
|
44
|
+
self._thread.start()
|
|
45
|
+
|
|
46
|
+
def stop(self):
|
|
47
|
+
"""Stop recording."""
|
|
48
|
+
self._running = False
|
|
49
|
+
if self._thread:
|
|
50
|
+
self._thread.join(timeout=5)
|
|
51
|
+
self.save_to_disk()
|
|
52
|
+
if self.context:
|
|
53
|
+
self.context.__exit__(None, None, None)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
if "HTTPDBG_MULTIPROCESS_DIR" in os.environ:
|
|
57
|
+
|
|
58
|
+
if not hasattr(sys, "_httpdbg_enabled"):
|
|
59
|
+
sys._httpdbg_enabled = True # type: ignore[attr-defined]
|
|
60
|
+
|
|
61
|
+
# start recording when the process starts and stop it when process exists
|
|
62
|
+
httprecorder = HttpdbgRecorder()
|
|
63
|
+
httprecorder.start()
|
|
64
|
+
|
|
65
|
+
atexit.register(httprecorder.stop)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
|
-
import
|
|
2
|
+
import os
|
|
3
|
+
import runpy
|
|
3
4
|
import sys
|
|
4
5
|
import traceback
|
|
5
6
|
from typing import List
|
|
@@ -8,24 +9,25 @@ from httpdbg.log import logger
|
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
def run_script(argv: List[str]) -> None:
|
|
11
|
-
|
|
12
|
+
|
|
12
13
|
if len(argv) == 0:
|
|
13
14
|
exit("script mode - error - python file required, but none set")
|
|
15
|
+
|
|
14
16
|
try:
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
spec.loader.exec_module(module) # type: ignore
|
|
23
|
-
except Exception:
|
|
24
|
-
traceback.print_exc()
|
|
17
|
+
original_sys_path = sys.path
|
|
18
|
+
original_sys_argv = sys.argv
|
|
19
|
+
|
|
20
|
+
sys.path.insert(0, os.path.abspath(argv[0]))
|
|
21
|
+
sys.argv = argv
|
|
22
|
+
|
|
23
|
+
runpy.run_path(argv[0], run_name="__main__")
|
|
25
24
|
except SystemExit as ex:
|
|
26
25
|
# in some case, for example when executing unittest tests from a script,
|
|
27
26
|
# the SystemExit exception is raised by the script itself so we catch it.
|
|
28
27
|
# TODO propagate the SystemExit value when httpdbg terminates.
|
|
29
28
|
logger().info(f"run_script - exception SystemExit - {str(ex)}")
|
|
29
|
+
except Exception:
|
|
30
|
+
traceback.print_exc()
|
|
30
31
|
finally:
|
|
31
|
-
sys.
|
|
32
|
+
sys.path = original_sys_path
|
|
33
|
+
sys.argv = original_sys_argv
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: httpdbg
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.32.1
|
|
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
|
|
@@ -23,8 +23,6 @@ Classifier: Topic :: Utilities
|
|
|
23
23
|
Requires-Python: >=3.9
|
|
24
24
|
Description-Content-Type: text/markdown
|
|
25
25
|
License-File: LICENSE
|
|
26
|
-
Provides-Extra: pytest
|
|
27
|
-
Requires-Dist: pytest-httpdbg>=0.6.0; extra == "pytest"
|
|
28
26
|
|
|
29
27
|
# httpdbg
|
|
30
28
|
|
|
@@ -71,30 +69,26 @@ You can trace all the HTTP requests performed by a script
|
|
|
71
69
|
pyhttpdbg --script filename.py [arg1 --arg2 ...]
|
|
72
70
|
```
|
|
73
71
|
|
|
74
|
-
###
|
|
75
|
-
|
|
76
|
-
You can trace all the HTTP requests performed during your tests
|
|
72
|
+
### module
|
|
77
73
|
|
|
78
|
-
|
|
79
|
-
pyhttpdbg -m pytest [arg1 --arg2 ...]
|
|
80
|
-
```
|
|
74
|
+
You can trace all the HTTP requests performed by a library module run as a script using the `-m` command line argument.
|
|
81
75
|
|
|
82
|
-
|
|
76
|
+
For example, you can view which HTTP requests are performed by `pip` when you install a package.
|
|
83
77
|
|
|
84
78
|
```console
|
|
85
|
-
pip install
|
|
79
|
+
pyhttpdbg -m pip install hookdns --upgrade
|
|
86
80
|
```
|
|
87
81
|
|
|
88
|
-
###
|
|
89
|
-
|
|
90
|
-
You can trace all the HTTP requests performed by a library module run as a script using the `-m` command line argument.
|
|
82
|
+
### test frameworks
|
|
91
83
|
|
|
92
|
-
|
|
84
|
+
You can trace all HTTP requests made during your tests (pytest, unittest).
|
|
93
85
|
|
|
94
86
|
```console
|
|
95
|
-
pyhttpdbg -m
|
|
87
|
+
pyhttpdbg -m pytest [arg1 --arg2 ...]
|
|
96
88
|
```
|
|
97
89
|
|
|
90
|
+
In that case, the requests will be grouped by test, and any requests made within a fixture or the setup/teardown methods will be identified by a tag.
|
|
91
|
+
|
|
98
92
|
## Initiators
|
|
99
93
|
|
|
100
94
|
An initiator is the function/method that is at the origin of the HTTP requests. By default, we already support some packages but you can add your own initiators.
|
|
@@ -20,7 +20,6 @@ httpdbg.egg-info/PKG-INFO
|
|
|
20
20
|
httpdbg.egg-info/SOURCES.txt
|
|
21
21
|
httpdbg.egg-info/dependency_links.txt
|
|
22
22
|
httpdbg.egg-info/entry_points.txt
|
|
23
|
-
httpdbg.egg-info/requires.txt
|
|
24
23
|
httpdbg.egg-info/top_level.txt
|
|
25
24
|
httpdbg/hooks/__init__.py
|
|
26
25
|
httpdbg/hooks/aiohttp.py
|
|
@@ -31,6 +30,7 @@ httpdbg/hooks/httpx.py
|
|
|
31
30
|
httpdbg/hooks/pytest.py
|
|
32
31
|
httpdbg/hooks/requests.py
|
|
33
32
|
httpdbg/hooks/socket.py
|
|
33
|
+
httpdbg/hooks/template_sitecustomize.py
|
|
34
34
|
httpdbg/hooks/unittest.py
|
|
35
35
|
httpdbg/hooks/urllib3.py
|
|
36
36
|
httpdbg/hooks/utils.py
|
|
@@ -78,5 +78,6 @@ tests/test_initiator.py
|
|
|
78
78
|
tests/test_mode_console.py
|
|
79
79
|
tests/test_mode_module_pytest.py
|
|
80
80
|
tests/test_mode_script.py
|
|
81
|
+
tests/test_multiprocess.py
|
|
81
82
|
tests/test_preview.py
|
|
82
83
|
tests/test_server.py
|
|
@@ -46,9 +46,6 @@ no_implicit_optional = false
|
|
|
46
46
|
disable_error_code= ["method-assign",]
|
|
47
47
|
files = ["httpdbg"]
|
|
48
48
|
|
|
49
|
-
[project.optional-dependencies]
|
|
50
|
-
pytest = ["pytest-httpdbg>=0.6.0"]
|
|
51
|
-
|
|
52
49
|
[project.urls]
|
|
53
50
|
Source = "https://github.com/cle-b/httpdbg/"
|
|
54
51
|
Documentation = "https://httpdbg.readthedocs.io/"
|
|
@@ -67,8 +67,8 @@ def test_run_script_no_args(httpbin, capsys):
|
|
|
67
67
|
|
|
68
68
|
@pytest.mark.script
|
|
69
69
|
def test_run_script_not_a_python_script(httpbin, capsys):
|
|
70
|
-
|
|
71
|
-
|
|
70
|
+
# should not raise an exception
|
|
71
|
+
run_script(["not_a_path_to_a_python_script"])
|
|
72
72
|
|
|
73
73
|
|
|
74
74
|
@pytest.mark.api
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import platform
|
|
3
|
+
|
|
4
|
+
import pytest
|
|
5
|
+
|
|
6
|
+
from httpdbg import httprecord
|
|
7
|
+
from httpdbg.env import HTTPDBG_MULTIPROCESS_DIR
|
|
8
|
+
from httpdbg.mode_script import run_script
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@pytest.mark.script
|
|
12
|
+
def test_run_script_multithread(httpbin):
|
|
13
|
+
|
|
14
|
+
if HTTPDBG_MULTIPROCESS_DIR in os.environ:
|
|
15
|
+
del os.environ[HTTPDBG_MULTIPROCESS_DIR]
|
|
16
|
+
|
|
17
|
+
with httprecord() as records:
|
|
18
|
+
os.environ["HTTPDBG_TEST_MULTITHREAD_BASE_URL"] = httpbin.url
|
|
19
|
+
script_to_run = os.path.join(
|
|
20
|
+
os.path.dirname(os.path.realpath(__file__)), "demo_run_multithread.py"
|
|
21
|
+
)
|
|
22
|
+
run_script([script_to_run, httpbin.url])
|
|
23
|
+
|
|
24
|
+
assert len(records) == 3
|
|
25
|
+
pids = []
|
|
26
|
+
for record in records:
|
|
27
|
+
pids.append(record.url.split("/")[-1])
|
|
28
|
+
assert len(set(pids)) == 1
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@pytest.mark.script
|
|
32
|
+
@pytest.mark.xfail(
|
|
33
|
+
platform.system().lower() == "windows",
|
|
34
|
+
reason="the test fails on the CI but the feature works on windows",
|
|
35
|
+
)
|
|
36
|
+
def test_run_script_multiprocess(httpbin):
|
|
37
|
+
|
|
38
|
+
if HTTPDBG_MULTIPROCESS_DIR in os.environ:
|
|
39
|
+
del os.environ[HTTPDBG_MULTIPROCESS_DIR]
|
|
40
|
+
|
|
41
|
+
with httprecord() as records:
|
|
42
|
+
os.environ["HTTPDBG_TEST_MULTIPROCESS_BASE_URL"] = httpbin.url
|
|
43
|
+
script_to_run = os.path.join(
|
|
44
|
+
os.path.dirname(os.path.realpath(__file__)), "demo_run_multiprocess.py"
|
|
45
|
+
)
|
|
46
|
+
run_script([script_to_run, httpbin.url])
|
|
47
|
+
|
|
48
|
+
assert len(records) == 3
|
|
49
|
+
pids = []
|
|
50
|
+
for record in records:
|
|
51
|
+
pids.append(record.url.split("/")[-1])
|
|
52
|
+
assert len(set(pids)) == 3
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
from contextlib import contextmanager
|
|
3
|
-
from typing import Generator
|
|
4
|
-
import glob
|
|
5
|
-
import os
|
|
6
|
-
import pickle
|
|
7
|
-
import tempfile
|
|
8
|
-
import time
|
|
9
|
-
import threading
|
|
10
|
-
|
|
11
|
-
from httpdbg.env import HTTPDBG_SUBPROCESS_DIR
|
|
12
|
-
from httpdbg.records import HTTPRecords
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
@contextmanager
|
|
16
|
-
def watcher_external(records: HTTPRecords) -> Generator[HTTPRecords, None, None]:
|
|
17
|
-
try:
|
|
18
|
-
watcher = None
|
|
19
|
-
if HTTPDBG_SUBPROCESS_DIR not in os.environ:
|
|
20
|
-
with tempfile.TemporaryDirectory(
|
|
21
|
-
prefix="httpdbg"
|
|
22
|
-
) as httpdbg_subprocess_dir:
|
|
23
|
-
os.environ[HTTPDBG_SUBPROCESS_DIR] = httpdbg_subprocess_dir
|
|
24
|
-
|
|
25
|
-
watcher = WatcherSubprocessDirThread(records)
|
|
26
|
-
watcher.start()
|
|
27
|
-
|
|
28
|
-
yield records
|
|
29
|
-
|
|
30
|
-
watcher.shutdown()
|
|
31
|
-
watcher.join()
|
|
32
|
-
else:
|
|
33
|
-
yield records
|
|
34
|
-
except Exception as ex:
|
|
35
|
-
if watcher:
|
|
36
|
-
watcher.shutdown()
|
|
37
|
-
raise ex
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
class WatcherSubprocessDirThread(threading.Thread):
|
|
41
|
-
def __init__(self, records: HTTPRecords, delay: int = 2) -> None:
|
|
42
|
-
self.stop = False
|
|
43
|
-
self.records = records
|
|
44
|
-
self.delay = delay
|
|
45
|
-
threading.Thread.__init__(self)
|
|
46
|
-
|
|
47
|
-
def load_dump(self):
|
|
48
|
-
for dump in glob.glob(f"{os.environ[HTTPDBG_SUBPROCESS_DIR]}/*.httpdbgrecords"):
|
|
49
|
-
with open(dump, "rb") as dumpfile:
|
|
50
|
-
newrecords: HTTPRecords = pickle.load(dumpfile)
|
|
51
|
-
self.records.requests.update(newrecords.requests)
|
|
52
|
-
self.records.initiators.update(newrecords.initiators)
|
|
53
|
-
self.records.groups.update(newrecords.groups)
|
|
54
|
-
|
|
55
|
-
def run(self):
|
|
56
|
-
while not self.stop:
|
|
57
|
-
time.sleep(self.delay)
|
|
58
|
-
self.load_dump()
|
|
59
|
-
|
|
60
|
-
def shutdown(self):
|
|
61
|
-
self.stop = True
|
|
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
|
|
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
|
{httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg/webapp/static/icons/chevron-direction-top-outline-icon.svg
RENAMED
|
File without changes
|
{httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg/webapp/static/icons/download-install-line-icon.svg
RENAMED
|
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
|
{httpdbg-0.31.6 → httpdbg-0.32.1}/httpdbg/webapp/static/icons/question-mark-round-line-icon.svg
RENAMED
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|