httpdbg 0.28.3__tar.gz → 0.29.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.
- {httpdbg-0.28.3 → httpdbg-0.29.0}/PKG-INFO +2 -2
- {httpdbg-0.28.3 → httpdbg-0.29.0}/httpdbg/__init__.py +1 -1
- {httpdbg-0.28.3 → httpdbg-0.29.0}/httpdbg/env.py +1 -0
- {httpdbg-0.28.3 → httpdbg-0.29.0}/httpdbg/hooks/external.py +3 -1
- {httpdbg-0.28.3 → httpdbg-0.29.0}/httpdbg/hooks/pytest.py +29 -1
- {httpdbg-0.28.3 → httpdbg-0.29.0}/httpdbg/hooks/socket.py +5 -5
- {httpdbg-0.28.3 → httpdbg-0.29.0}/httpdbg/initiator.py +55 -16
- {httpdbg-0.28.3 → httpdbg-0.29.0}/httpdbg/records.py +20 -23
- {httpdbg-0.28.3 → httpdbg-0.29.0}/httpdbg/webapp/api.py +16 -3
- {httpdbg-0.28.3 → httpdbg-0.29.0}/httpdbg/webapp/static/api.js +11 -2
- {httpdbg-0.28.3 → httpdbg-0.29.0}/httpdbg/webapp/static/configuration.js +3 -3
- {httpdbg-0.28.3 → httpdbg-0.29.0}/httpdbg/webapp/static/filter.js +14 -14
- {httpdbg-0.28.3 → httpdbg-0.29.0}/httpdbg/webapp/static/index.htm +10 -10
- {httpdbg-0.28.3 → httpdbg-0.29.0}/httpdbg/webapp/static/main.js +1 -1
- {httpdbg-0.28.3 → httpdbg-0.29.0}/httpdbg/webapp/static/render.js +17 -11
- {httpdbg-0.28.3 → httpdbg-0.29.0}/httpdbg/webapp/static/style.css +1 -1
- {httpdbg-0.28.3 → httpdbg-0.29.0}/httpdbg/webapp/static/table.css +6 -6
- {httpdbg-0.28.3 → httpdbg-0.29.0}/httpdbg.egg-info/PKG-INFO +2 -2
- httpdbg-0.29.0/httpdbg.egg-info/requires.txt +3 -0
- {httpdbg-0.28.3 → httpdbg-0.29.0}/pyproject.toml +1 -1
- {httpdbg-0.28.3 → httpdbg-0.29.0}/tests/test_hook_aiohttp.py +3 -15
- {httpdbg-0.28.3 → httpdbg-0.29.0}/tests/test_hook_httpx.py +6 -19
- {httpdbg-0.28.3 → httpdbg-0.29.0}/tests/test_hook_requests.py +3 -6
- {httpdbg-0.28.3 → httpdbg-0.29.0}/tests/test_hook_urllib3.py +32 -30
- httpdbg-0.29.0/tests/test_initiator.py +167 -0
- {httpdbg-0.28.3 → httpdbg-0.29.0}/tests/test_mode_module_pytest.py +54 -5
- {httpdbg-0.28.3 → httpdbg-0.29.0}/tests/test_mode_script.py +5 -12
- httpdbg-0.28.3/httpdbg.egg-info/requires.txt +0 -3
- httpdbg-0.28.3/tests/test_initiator.py +0 -172
- {httpdbg-0.28.3 → httpdbg-0.29.0}/LICENSE +0 -0
- {httpdbg-0.28.3 → httpdbg-0.29.0}/README.md +0 -0
- {httpdbg-0.28.3 → httpdbg-0.29.0}/httpdbg/__main__.py +0 -0
- {httpdbg-0.28.3 → httpdbg-0.29.0}/httpdbg/args.py +0 -0
- {httpdbg-0.28.3 → httpdbg-0.29.0}/httpdbg/exception.py +0 -0
- {httpdbg-0.28.3 → httpdbg-0.29.0}/httpdbg/hooks/__init__.py +0 -0
- {httpdbg-0.28.3 → httpdbg-0.29.0}/httpdbg/hooks/aiohttp.py +0 -0
- {httpdbg-0.28.3 → httpdbg-0.29.0}/httpdbg/hooks/all.py +0 -0
- {httpdbg-0.28.3 → httpdbg-0.29.0}/httpdbg/hooks/generic.py +0 -0
- {httpdbg-0.28.3 → httpdbg-0.29.0}/httpdbg/hooks/httpx.py +0 -0
- {httpdbg-0.28.3 → httpdbg-0.29.0}/httpdbg/hooks/requests.py +0 -0
- {httpdbg-0.28.3 → httpdbg-0.29.0}/httpdbg/hooks/urllib3.py +0 -0
- {httpdbg-0.28.3 → httpdbg-0.29.0}/httpdbg/hooks/utils.py +0 -0
- {httpdbg-0.28.3 → httpdbg-0.29.0}/httpdbg/log.py +0 -0
- {httpdbg-0.28.3 → httpdbg-0.29.0}/httpdbg/mode_console.py +0 -0
- {httpdbg-0.28.3 → httpdbg-0.29.0}/httpdbg/mode_module.py +0 -0
- {httpdbg-0.28.3 → httpdbg-0.29.0}/httpdbg/mode_script.py +0 -0
- {httpdbg-0.28.3 → httpdbg-0.29.0}/httpdbg/preview.py +0 -0
- {httpdbg-0.28.3 → httpdbg-0.29.0}/httpdbg/server.py +0 -0
- {httpdbg-0.28.3 → httpdbg-0.29.0}/httpdbg/utils.py +0 -0
- {httpdbg-0.28.3 → httpdbg-0.29.0}/httpdbg/webapp/__init__.py +0 -0
- {httpdbg-0.28.3 → httpdbg-0.29.0}/httpdbg/webapp/app.py +0 -0
- {httpdbg-0.28.3 → httpdbg-0.29.0}/httpdbg/webapp/static/column.css +0 -0
- {httpdbg-0.28.3 → httpdbg-0.29.0}/httpdbg/webapp/static/detail.css +0 -0
- {httpdbg-0.28.3 → httpdbg-0.29.0}/httpdbg/webapp/static/favicon.ico +0 -0
- {httpdbg-0.28.3 → httpdbg-0.29.0}/httpdbg/webapp/static/icons/chevron-direction-top-outline-icon.svg +0 -0
- {httpdbg-0.28.3 → httpdbg-0.29.0}/httpdbg/webapp/static/icons/download-install-line-icon.svg +0 -0
- {httpdbg-0.28.3 → httpdbg-0.29.0}/httpdbg/webapp/static/icons/engine-gears-icon.svg +0 -0
- {httpdbg-0.28.3 → httpdbg-0.29.0}/httpdbg/webapp/static/icons/files-papers-outline-icon.svg +0 -0
- {httpdbg-0.28.3 → httpdbg-0.29.0}/httpdbg/webapp/static/icons/filter-outline-icon.svg +0 -0
- {httpdbg-0.28.3 → httpdbg-0.29.0}/httpdbg/webapp/static/icons/information-mark-circle-outline-icon.svg +0 -0
- {httpdbg-0.28.3 → httpdbg-0.29.0}/httpdbg/webapp/static/icons/math-multiplication-icon.svg +0 -0
- {httpdbg-0.28.3 → httpdbg-0.29.0}/httpdbg/webapp/static/icons/push-pin-green-icon.svg +0 -0
- {httpdbg-0.28.3 → httpdbg-0.29.0}/httpdbg/webapp/static/icons/question-mark-round-line-icon.svg +0 -0
- {httpdbg-0.28.3 → httpdbg-0.29.0}/httpdbg/webapp/static/icons/recycle-bin-line-icon.svg +0 -0
- {httpdbg-0.28.3 → httpdbg-0.29.0}/httpdbg/webapp/static/icons/wait-sandclock-icon.svg +0 -0
- {httpdbg-0.28.3 → httpdbg-0.29.0}/httpdbg/webapp/static/mustache.min.js +0 -0
- {httpdbg-0.28.3 → httpdbg-0.29.0}/httpdbg.egg-info/SOURCES.txt +0 -0
- {httpdbg-0.28.3 → httpdbg-0.29.0}/httpdbg.egg-info/dependency_links.txt +0 -0
- {httpdbg-0.28.3 → httpdbg-0.29.0}/httpdbg.egg-info/entry_points.txt +0 -0
- {httpdbg-0.28.3 → httpdbg-0.29.0}/httpdbg.egg-info/top_level.txt +0 -0
- {httpdbg-0.28.3 → httpdbg-0.29.0}/setup.cfg +0 -0
- {httpdbg-0.28.3 → httpdbg-0.29.0}/tests/test_api.py +0 -0
- {httpdbg-0.28.3 → httpdbg-0.29.0}/tests/test_cli.py +0 -0
- {httpdbg-0.28.3 → httpdbg-0.29.0}/tests/test_context_manager.py +0 -0
- {httpdbg-0.28.3 → httpdbg-0.29.0}/tests/test_mode_console.py +0 -0
- {httpdbg-0.28.3 → httpdbg-0.29.0}/tests/test_preview.py +0 -0
- {httpdbg-0.28.3 → httpdbg-0.29.0}/tests/test_server.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: httpdbg
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.29.0
|
|
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
|
|
@@ -24,7 +24,7 @@ Requires-Python: >=3.9
|
|
|
24
24
|
Description-Content-Type: text/markdown
|
|
25
25
|
License-File: LICENSE
|
|
26
26
|
Provides-Extra: pytest
|
|
27
|
-
Requires-Dist: pytest-httpdbg>=0.
|
|
27
|
+
Requires-Dist: pytest-httpdbg>=0.6.0; extra == "pytest"
|
|
28
28
|
|
|
29
29
|
# httpdbg
|
|
30
30
|
|
|
@@ -46,8 +46,10 @@ class WatcherSubprocessDirThread(threading.Thread):
|
|
|
46
46
|
def load_dump(self):
|
|
47
47
|
for dump in glob.glob(f"{os.environ[HTTPDBG_SUBPROCESS_DIR]}/*.httpdbgrecords"):
|
|
48
48
|
with open(dump, "rb") as dumpfile:
|
|
49
|
-
newrecords = pickle.load(dumpfile)
|
|
49
|
+
newrecords: HTTPRecords = pickle.load(dumpfile)
|
|
50
50
|
self.records.requests.update(newrecords.requests)
|
|
51
|
+
self.records.initiators.update(newrecords.initiators)
|
|
52
|
+
self.records.groups.update(newrecords.groups)
|
|
51
53
|
|
|
52
54
|
def run(self):
|
|
53
55
|
while not self.stop:
|
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
|
+
from collections.abc import Callable
|
|
2
3
|
from contextlib import contextmanager
|
|
3
4
|
from typing import Generator
|
|
4
5
|
|
|
5
6
|
from httpdbg.hooks.utils import decorate
|
|
6
7
|
from httpdbg.hooks.utils import getcallargs
|
|
7
8
|
from httpdbg.hooks.utils import undecorate
|
|
9
|
+
from httpdbg.initiator import httpdbg_group
|
|
8
10
|
from httpdbg.initiator import httpdbg_tag
|
|
11
|
+
from httpdbg.log import logger
|
|
9
12
|
from httpdbg.records import HTTPRecords
|
|
10
13
|
|
|
11
14
|
|
|
12
|
-
def set_hook_for_pytest_fixture(_, method):
|
|
15
|
+
def set_hook_for_pytest_fixture(_: HTTPRecords, method: Callable):
|
|
13
16
|
def hook(*args, **kwargs):
|
|
17
|
+
logger().info("SET_HOOK_FOR_PYTEST_FIXTURE")
|
|
14
18
|
callargs = getcallargs(method, *args, **kwargs)
|
|
15
19
|
|
|
16
20
|
if "fixturefunc" in callargs:
|
|
@@ -22,11 +26,28 @@ def set_hook_for_pytest_fixture(_, method):
|
|
|
22
26
|
return hook
|
|
23
27
|
|
|
24
28
|
|
|
29
|
+
def set_hook_for_pytest_runtest(records: HTTPRecords, method: Callable):
|
|
30
|
+
def hook(*args, **kwargs):
|
|
31
|
+
logger().info("SET_HOOK_FOR_PYTEST_RUNTEST")
|
|
32
|
+
callargs = getcallargs(method, *args, **kwargs)
|
|
33
|
+
|
|
34
|
+
if "item" in callargs:
|
|
35
|
+
full_label = getattr(callargs["item"], "nodeid", "unknown test")
|
|
36
|
+
label = getattr(callargs["item"], "name", "unknown test")
|
|
37
|
+
with httpdbg_group(records, label, full_label):
|
|
38
|
+
return method(*args, **kwargs)
|
|
39
|
+
else:
|
|
40
|
+
return method(*args, **kwargs)
|
|
41
|
+
|
|
42
|
+
return hook
|
|
43
|
+
|
|
44
|
+
|
|
25
45
|
@contextmanager
|
|
26
46
|
def hook_pytest(records: HTTPRecords) -> Generator[None, None, None]:
|
|
27
47
|
hooks = False
|
|
28
48
|
try:
|
|
29
49
|
import _pytest.fixtures
|
|
50
|
+
import _pytest.runner
|
|
30
51
|
|
|
31
52
|
_pytest.fixtures.call_fixture_func = decorate(
|
|
32
53
|
records, _pytest.fixtures.call_fixture_func, set_hook_for_pytest_fixture
|
|
@@ -36,6 +57,11 @@ def hook_pytest(records: HTTPRecords) -> Generator[None, None, None]:
|
|
|
36
57
|
_pytest.fixtures._teardown_yield_fixture,
|
|
37
58
|
set_hook_for_pytest_fixture,
|
|
38
59
|
)
|
|
60
|
+
|
|
61
|
+
_pytest.runner.runtestprotocol = decorate(
|
|
62
|
+
records, _pytest.runner.runtestprotocol, set_hook_for_pytest_runtest
|
|
63
|
+
)
|
|
64
|
+
|
|
39
65
|
hooks = True
|
|
40
66
|
except ImportError:
|
|
41
67
|
pass
|
|
@@ -49,3 +75,5 @@ def hook_pytest(records: HTTPRecords) -> Generator[None, None, None]:
|
|
|
49
75
|
_pytest.fixtures._teardown_yield_fixture = undecorate(
|
|
50
76
|
_pytest.fixtures._teardown_yield_fixture
|
|
51
77
|
)
|
|
78
|
+
|
|
79
|
+
_pytest.runner.runtestprotocol = undecorate(_pytest.runner.runtestprotocol)
|
|
@@ -206,7 +206,7 @@ def set_hook_for_socket_sendall(records: HTTPRecords, method: Callable):
|
|
|
206
206
|
if http_detected:
|
|
207
207
|
logger().info("SENDALL - http detected")
|
|
208
208
|
socketdata.record = HTTPRecord(tbegin=socketdata.tbegin)
|
|
209
|
-
socketdata.record.
|
|
209
|
+
socketdata.record.initiator_id = records.get_initiator()
|
|
210
210
|
socketdata.record.address = socketdata.address
|
|
211
211
|
socketdata.record.ssl = socketdata.ssl
|
|
212
212
|
socketdata.record.request.rawdata = socketdata.rawdata
|
|
@@ -251,7 +251,7 @@ def set_hook_for_socket_send(records: HTTPRecords, method: Callable):
|
|
|
251
251
|
http_detected = socketdata.http_detected()
|
|
252
252
|
if http_detected:
|
|
253
253
|
socketdata.record = HTTPRecord(tbegin=socketdata.tbegin)
|
|
254
|
-
socketdata.record.
|
|
254
|
+
socketdata.record.initiator_id = records.get_initiator()
|
|
255
255
|
socketdata.record.address = socketdata.address
|
|
256
256
|
socketdata.record.ssl = socketdata.ssl
|
|
257
257
|
socketdata.record.request.rawdata = socketdata.rawdata
|
|
@@ -315,7 +315,7 @@ def set_hook_for_sslobject_write(records: HTTPRecords, method: Callable):
|
|
|
315
315
|
http_detected = socketdata.http_detected()
|
|
316
316
|
if http_detected:
|
|
317
317
|
socketdata.record = HTTPRecord(tbegin=socketdata.tbegin)
|
|
318
|
-
socketdata.record.
|
|
318
|
+
socketdata.record.initiator_id = records.get_initiator()
|
|
319
319
|
socketdata.record.address = socketdata.address
|
|
320
320
|
socketdata.record.ssl = socketdata.ssl
|
|
321
321
|
socketdata.record.request.rawdata = socketdata.rawdata
|
|
@@ -413,7 +413,7 @@ def hook_socket(records: HTTPRecords) -> Generator[None, None, None]:
|
|
|
413
413
|
)
|
|
414
414
|
|
|
415
415
|
# only for async HTTP requests (not HTTPS) on Windows
|
|
416
|
-
if
|
|
416
|
+
if platform.system().lower() == "windows":
|
|
417
417
|
asyncio.proactor_events._ProactorReadPipeTransport._data_received = decorate( # type: ignore
|
|
418
418
|
records,
|
|
419
419
|
asyncio.proactor_events._ProactorReadPipeTransport._data_received, # type: ignore
|
|
@@ -455,7 +455,7 @@ def hook_socket(records: HTTPRecords) -> Generator[None, None, None]:
|
|
|
455
455
|
)
|
|
456
456
|
|
|
457
457
|
# only for async HTTP requests (not HTTPS) on Windows
|
|
458
|
-
if
|
|
458
|
+
if platform.system().lower() == "windows":
|
|
459
459
|
asyncio.proactor_events._ProactorReadPipeTransport._data_received = undecorate( # type: ignore
|
|
460
460
|
asyncio.proactor_events._ProactorReadPipeTransport._data_received # type: ignore
|
|
461
461
|
)
|
|
@@ -7,7 +7,12 @@ from typing import Generator
|
|
|
7
7
|
from typing import List
|
|
8
8
|
from typing import Tuple
|
|
9
9
|
from typing import Union
|
|
10
|
+
from typing import TYPE_CHECKING
|
|
10
11
|
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from httpdbg.records import HTTPRecords
|
|
14
|
+
|
|
15
|
+
from httpdbg.env import HTTPDBG_CURRENT_GROUP
|
|
11
16
|
from httpdbg.env import HTTPDBG_CURRENT_INITIATOR
|
|
12
17
|
from httpdbg.env import HTTPDBG_CURRENT_TAG
|
|
13
18
|
from httpdbg.hooks.utils import getcallargs
|
|
@@ -15,26 +20,23 @@ from httpdbg.utils import get_new_uuid
|
|
|
15
20
|
from httpdbg.log import logger
|
|
16
21
|
|
|
17
22
|
|
|
18
|
-
class Initiator
|
|
23
|
+
class Initiator:
|
|
19
24
|
def __init__(
|
|
20
25
|
self,
|
|
21
26
|
id: str,
|
|
22
|
-
|
|
23
|
-
long_label: Union[str, None],
|
|
27
|
+
label: str,
|
|
24
28
|
short_stack: str,
|
|
25
29
|
stack: List[str],
|
|
26
30
|
):
|
|
27
31
|
self.id = id
|
|
28
|
-
self.
|
|
29
|
-
self.long_label = long_label
|
|
32
|
+
self.label = label
|
|
30
33
|
self.short_stack = short_stack
|
|
31
34
|
self.stack = stack
|
|
32
35
|
|
|
33
36
|
def __eq__(self, other) -> bool:
|
|
34
37
|
if type(other) is Initiator:
|
|
35
38
|
return (
|
|
36
|
-
self.
|
|
37
|
-
and self.long_label == other.long_label
|
|
39
|
+
self.label == other.label
|
|
38
40
|
and self.short_stack == other.short_stack
|
|
39
41
|
and self.stack == other.stack
|
|
40
42
|
)
|
|
@@ -45,21 +47,29 @@ class Initiator(object):
|
|
|
45
47
|
if full:
|
|
46
48
|
json = {
|
|
47
49
|
"id": self.id,
|
|
48
|
-
"
|
|
49
|
-
"long_label": self.long_label,
|
|
50
|
+
"label": self.label,
|
|
50
51
|
"short_stack": self.short_stack,
|
|
51
52
|
"stack": "\n".join(self.stack),
|
|
52
53
|
}
|
|
53
54
|
else:
|
|
54
55
|
json = {
|
|
55
56
|
"id": self.id,
|
|
56
|
-
"
|
|
57
|
-
"long_label": self.long_label,
|
|
57
|
+
"label": self.label,
|
|
58
58
|
"short_stack": self.short_stack,
|
|
59
59
|
}
|
|
60
60
|
return json
|
|
61
61
|
|
|
62
62
|
|
|
63
|
+
class Group:
|
|
64
|
+
def __init__(self, id: str, label: str, full_label: str):
|
|
65
|
+
self.id = id
|
|
66
|
+
self.label = label
|
|
67
|
+
self.full_label = full_label
|
|
68
|
+
|
|
69
|
+
def to_json(self) -> dict:
|
|
70
|
+
return {"id": self.id, "label": self.label, "full_label": self.full_label}
|
|
71
|
+
|
|
72
|
+
|
|
63
73
|
def compatible_path(path: str) -> str:
|
|
64
74
|
p = path
|
|
65
75
|
if platform.system().lower() == "windows":
|
|
@@ -205,15 +215,16 @@ def httpdbg_initiator(
|
|
|
205
215
|
short_stack += f" {k}={v}\n"
|
|
206
216
|
short_stack += ")"
|
|
207
217
|
|
|
208
|
-
current_initiator = Initiator(
|
|
209
|
-
|
|
210
|
-
)
|
|
211
|
-
records._initiators[current_initiator.id] = current_initiator
|
|
218
|
+
current_initiator = Initiator(get_new_uuid(), instruction, short_stack, stack)
|
|
219
|
+
records.initiators[current_initiator.id] = current_initiator
|
|
212
220
|
|
|
213
221
|
os.environ[envname] = current_initiator.id
|
|
214
222
|
|
|
215
223
|
try:
|
|
216
|
-
|
|
224
|
+
with httpdbg_group(
|
|
225
|
+
records, instruction, short_stack
|
|
226
|
+
): # by default we group the requests by initiator
|
|
227
|
+
yield current_initiator
|
|
217
228
|
except Exception:
|
|
218
229
|
del os.environ[envname]
|
|
219
230
|
raise
|
|
@@ -233,6 +244,7 @@ def httpdbg_tag(tag: str) -> Generator[None, None, None]:
|
|
|
233
244
|
os.environ[HTTPDBG_CURRENT_TAG] = tag
|
|
234
245
|
|
|
235
246
|
try:
|
|
247
|
+
logger().info(f"httpdbg_tag {tag}")
|
|
236
248
|
yield
|
|
237
249
|
except Exception:
|
|
238
250
|
if (not tag_already_set) and (HTTPDBG_CURRENT_TAG in os.environ):
|
|
@@ -241,3 +253,30 @@ def httpdbg_tag(tag: str) -> Generator[None, None, None]:
|
|
|
241
253
|
|
|
242
254
|
if (not tag_already_set) and (HTTPDBG_CURRENT_TAG in os.environ):
|
|
243
255
|
del os.environ[HTTPDBG_CURRENT_TAG]
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
@contextmanager
|
|
259
|
+
def httpdbg_group(
|
|
260
|
+
records: "HTTPRecords", label: str, full_label: str
|
|
261
|
+
) -> Generator[None, None, None]:
|
|
262
|
+
|
|
263
|
+
group_already_set = HTTPDBG_CURRENT_GROUP in os.environ
|
|
264
|
+
|
|
265
|
+
if not group_already_set:
|
|
266
|
+
logger().info("httpdbg_group (new)")
|
|
267
|
+
group_id = get_new_uuid()
|
|
268
|
+
records.groups[group_id] = Group(group_id, label, full_label)
|
|
269
|
+
os.environ[HTTPDBG_CURRENT_GROUP] = group_id
|
|
270
|
+
|
|
271
|
+
try:
|
|
272
|
+
logger().info(
|
|
273
|
+
f"httpdbg_group {records.groups} group_id={os.environ[HTTPDBG_CURRENT_GROUP]} label={label} full_label={full_label}"
|
|
274
|
+
)
|
|
275
|
+
yield
|
|
276
|
+
except Exception:
|
|
277
|
+
if (not group_already_set) and (HTTPDBG_CURRENT_GROUP in os.environ):
|
|
278
|
+
del os.environ[HTTPDBG_CURRENT_GROUP]
|
|
279
|
+
raise
|
|
280
|
+
|
|
281
|
+
if (not group_already_set) and (HTTPDBG_CURRENT_GROUP in os.environ):
|
|
282
|
+
del os.environ[HTTPDBG_CURRENT_GROUP]
|
|
@@ -9,11 +9,13 @@ import traceback
|
|
|
9
9
|
from urllib.parse import urlparse
|
|
10
10
|
from typing import Dict, List, Tuple, Union
|
|
11
11
|
|
|
12
|
+
from httpdbg.env import HTTPDBG_CURRENT_GROUP
|
|
12
13
|
from httpdbg.env import HTTPDBG_CURRENT_INITIATOR
|
|
13
14
|
from httpdbg.env import HTTPDBG_CURRENT_TAG
|
|
14
15
|
from httpdbg.utils import HTTPDBGCookie
|
|
15
16
|
from httpdbg.utils import HTTPDBGHeader
|
|
16
17
|
from httpdbg.initiator import in_lib
|
|
18
|
+
from httpdbg.initiator import Group
|
|
17
19
|
from httpdbg.initiator import Initiator
|
|
18
20
|
from httpdbg.preview import generate_preview
|
|
19
21
|
from httpdbg.utils import get_new_uuid
|
|
@@ -214,13 +216,14 @@ class HTTPRecord:
|
|
|
214
216
|
self.id = get_new_uuid()
|
|
215
217
|
self.address: Tuple[str, int] = ("", 0)
|
|
216
218
|
self._url: Union[str, None] = None
|
|
217
|
-
self.
|
|
219
|
+
self.initiator_id: Union[str, None] = None
|
|
218
220
|
self.exception: Union[Exception, None] = None
|
|
219
221
|
self.request: HTTPRecordRequest = HTTPRecordRequest()
|
|
220
222
|
self.response: HTTPRecordResponse = HTTPRecordResponse()
|
|
221
223
|
self.ssl: Union[bool, None] = None
|
|
222
224
|
self.tbegin: datetime.datetime = datetime.datetime.now(datetime.timezone.utc)
|
|
223
|
-
self.tag = os.environ.get(HTTPDBG_CURRENT_TAG)
|
|
225
|
+
self.tag: Union[str, None] = os.environ.get(HTTPDBG_CURRENT_TAG)
|
|
226
|
+
self.group_id: Union[str, None] = os.environ.get(HTTPDBG_CURRENT_GROUP)
|
|
224
227
|
if tbegin:
|
|
225
228
|
self.tbegin = tbegin
|
|
226
229
|
|
|
@@ -295,10 +298,12 @@ class HTTPRecords:
|
|
|
295
298
|
self.reset()
|
|
296
299
|
|
|
297
300
|
def reset(self) -> None:
|
|
301
|
+
logger().info("HTTPRecords.reset")
|
|
298
302
|
self.id = get_new_uuid()
|
|
299
303
|
self.requests: Dict[str, HTTPRecord] = {}
|
|
300
304
|
self.requests_already_loaded = 0
|
|
301
|
-
self.
|
|
305
|
+
self.initiators: Dict[str, Initiator] = {}
|
|
306
|
+
self.groups: Dict[str, Group] = {}
|
|
302
307
|
self._sockets: Dict[int, SocketRawData] = {}
|
|
303
308
|
|
|
304
309
|
@property
|
|
@@ -311,11 +316,11 @@ class HTTPRecords:
|
|
|
311
316
|
def __len__(self) -> int:
|
|
312
317
|
return len(self.requests)
|
|
313
318
|
|
|
314
|
-
def get_initiator(self) ->
|
|
319
|
+
def get_initiator(self) -> str:
|
|
315
320
|
envname = f"{HTTPDBG_CURRENT_INITIATOR}_{self.id}"
|
|
316
321
|
|
|
317
322
|
if envname in os.environ:
|
|
318
|
-
initiator = self.
|
|
323
|
+
initiator = self.initiators[os.environ[envname]]
|
|
319
324
|
else:
|
|
320
325
|
fullstack = traceback.format_stack()
|
|
321
326
|
stack: List[str] = []
|
|
@@ -324,23 +329,13 @@ class HTTPRecords:
|
|
|
324
329
|
break
|
|
325
330
|
stack.append(line)
|
|
326
331
|
long_label = stack[-1]
|
|
327
|
-
|
|
328
|
-
initiator = Initiator(get_new_uuid(),
|
|
329
|
-
|
|
330
|
-
if
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
short_label = long_label.split("::")[-1]
|
|
335
|
-
initiator = Initiator(
|
|
336
|
-
f"{long_label}{self.id}",
|
|
337
|
-
short_label,
|
|
338
|
-
long_label,
|
|
339
|
-
initiator.short_stack,
|
|
340
|
-
initiator.stack,
|
|
341
|
-
)
|
|
342
|
-
|
|
343
|
-
return initiator
|
|
332
|
+
label = long_label.split("\n")[1]
|
|
333
|
+
initiator = Initiator(get_new_uuid(), label, long_label, stack)
|
|
334
|
+
|
|
335
|
+
if initiator.id not in self.initiators:
|
|
336
|
+
self.initiators[initiator.id] = initiator
|
|
337
|
+
|
|
338
|
+
return initiator.id
|
|
344
339
|
|
|
345
340
|
def get_socket_data(
|
|
346
341
|
self, obj, extra_sock=None, force_new=False, request=None
|
|
@@ -417,9 +412,11 @@ class HTTPRecords:
|
|
|
417
412
|
def add_new_record_exception(
|
|
418
413
|
self, initiator: Initiator, url: str, exception: Exception
|
|
419
414
|
) -> HTTPRecord:
|
|
415
|
+
if initiator.id not in self.initiators:
|
|
416
|
+
self.initiators[initiator.id] = initiator
|
|
420
417
|
new_record = HTTPRecord()
|
|
421
418
|
new_record.url = url
|
|
422
|
-
new_record.
|
|
419
|
+
new_record.initiator_id = initiator.id
|
|
423
420
|
new_record.exception = exception
|
|
424
421
|
self.requests[new_record.id] = new_record
|
|
425
422
|
return new_record
|
|
@@ -21,7 +21,8 @@ class RequestPayload(JSONEncoder):
|
|
|
21
21
|
"response": None,
|
|
22
22
|
"exception": None,
|
|
23
23
|
"tag": req.tag,
|
|
24
|
-
"
|
|
24
|
+
"initiator_id": req.initiator_id,
|
|
25
|
+
"group_id": req.group_id,
|
|
25
26
|
"in_progress": req.in_progress,
|
|
26
27
|
}
|
|
27
28
|
|
|
@@ -64,7 +65,12 @@ class RequestListPayload(JSONEncoder):
|
|
|
64
65
|
records, HTTPRecords
|
|
65
66
|
), "This encoder works only for HTTPRecords object."
|
|
66
67
|
|
|
67
|
-
payload = {
|
|
68
|
+
payload = {
|
|
69
|
+
"id": records.id,
|
|
70
|
+
"requests": {},
|
|
71
|
+
"initiators": {},
|
|
72
|
+
"groups": {},
|
|
73
|
+
}
|
|
68
74
|
|
|
69
75
|
for id, req in records.requests.items():
|
|
70
76
|
payload["requests"][id] = {
|
|
@@ -76,10 +82,17 @@ class RequestListPayload(JSONEncoder):
|
|
|
76
82
|
"reason": req.reason,
|
|
77
83
|
"verb": req.method,
|
|
78
84
|
"tag": req.tag,
|
|
79
|
-
"
|
|
85
|
+
"initiator_id": req.initiator_id,
|
|
86
|
+
"group_id": req.group_id,
|
|
80
87
|
"in_progress": req.in_progress,
|
|
81
88
|
"tbegin": req.tbegin.isoformat(),
|
|
82
89
|
"last_update": req.last_update.isoformat(),
|
|
83
90
|
}
|
|
84
91
|
|
|
92
|
+
for id, initiator in records.initiators.items():
|
|
93
|
+
payload["initiators"][id] = initiator.to_json()
|
|
94
|
+
|
|
95
|
+
for id, group in records.groups.items():
|
|
96
|
+
payload["groups"][id] = group.to_json()
|
|
97
|
+
|
|
85
98
|
return payload
|
|
@@ -3,8 +3,10 @@
|
|
|
3
3
|
const global = {
|
|
4
4
|
"k7": null,
|
|
5
5
|
"requests": {},
|
|
6
|
+
"initiators": {},
|
|
7
|
+
"groups": {},
|
|
6
8
|
"connected": false,
|
|
7
|
-
"
|
|
9
|
+
"group_collapse": []
|
|
8
10
|
}
|
|
9
11
|
|
|
10
12
|
function save_request(request_id, request) {
|
|
@@ -18,6 +20,8 @@ function save_request(request_id, request) {
|
|
|
18
20
|
}
|
|
19
21
|
global.requests[request_id] = request;
|
|
20
22
|
|
|
23
|
+
global.requests[request_id].initiator = global.initiators[request.initiator_id];
|
|
24
|
+
|
|
21
25
|
if (global.requests[request_id].in_progress) {
|
|
22
26
|
global.requests[request_id].status_code_view = '<img class="icon" src="static/icons/wait-sandclock-icon.svg-+-$**HTTPDBG_VERSION**$" alt="loading"/>';
|
|
23
27
|
} else {
|
|
@@ -62,6 +66,11 @@ async function get_all_requests() {
|
|
|
62
66
|
clean();
|
|
63
67
|
};
|
|
64
68
|
|
|
69
|
+
// for the initiators and the groups, we can just save them without any verification
|
|
70
|
+
Object.assign(global.initiators, data.initiators);
|
|
71
|
+
Object.assign(global.groups, data.groups);
|
|
72
|
+
|
|
73
|
+
// for the requests, we may have to update them
|
|
65
74
|
for (const [request_id, request] of Object.entries(data.requests)) {
|
|
66
75
|
if (!(request_id in global.requests)) {
|
|
67
76
|
// this is a new request
|
|
@@ -109,7 +118,7 @@ async function get_request(request_id) {
|
|
|
109
118
|
}
|
|
110
119
|
|
|
111
120
|
// the full stack is not present in request summary
|
|
112
|
-
global.requests[request_id].
|
|
121
|
+
global.requests[request_id].initiator_id = data.initiator_id;
|
|
113
122
|
|
|
114
123
|
global.requests[request_id].to_refresh = true;
|
|
115
124
|
|
|
@@ -33,7 +33,7 @@ function apply_config() {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
apply_config_rule_css(httpdbgApp.config.hide_netloc, sheet);
|
|
36
|
-
apply_config_rule_css(httpdbgApp.config.
|
|
36
|
+
apply_config_rule_css(httpdbgApp.config.hide_group, sheet);
|
|
37
37
|
apply_config_rule_css(httpdbgApp.config.hide_tag, sheet);
|
|
38
38
|
|
|
39
39
|
apply_config_rule_click(httpdbgApp.config.details_wrap_default);
|
|
@@ -48,7 +48,7 @@ function load_config_from_url(apply) {
|
|
|
48
48
|
const params = new URLSearchParams(window.location.search);
|
|
49
49
|
|
|
50
50
|
load_config_rule_from_url(httpdbgApp.config.hide_netloc, params);
|
|
51
|
-
load_config_rule_from_url(httpdbgApp.config.
|
|
51
|
+
load_config_rule_from_url(httpdbgApp.config.hide_group, params);
|
|
52
52
|
load_config_rule_from_url(httpdbgApp.config.hide_tag, params);
|
|
53
53
|
load_config_rule_from_url(httpdbgApp.config.details_wrap_default, params);
|
|
54
54
|
load_config_rule_from_url(httpdbgApp.config.details_raw_default, params);
|
|
@@ -66,7 +66,7 @@ function load_config_from_form(apply) {
|
|
|
66
66
|
const params = new URLSearchParams(window.location.search);
|
|
67
67
|
|
|
68
68
|
load_config_rule_from_form(httpdbgApp.config.hide_netloc);
|
|
69
|
-
load_config_rule_from_form(httpdbgApp.config.
|
|
69
|
+
load_config_rule_from_form(httpdbgApp.config.hide_group);
|
|
70
70
|
load_config_rule_from_form(httpdbgApp.config.hide_tag);
|
|
71
71
|
load_config_rule_from_form(httpdbgApp.config.details_wrap_default);
|
|
72
72
|
load_config_rule_from_form(httpdbgApp.config.details_raw_default);
|
|
@@ -12,46 +12,46 @@ function update_filter_url() {
|
|
|
12
12
|
if (value == "") {
|
|
13
13
|
// if there is no filter, all rows are visible
|
|
14
14
|
sheet.insertRule("[data-filter] {display: table-row;}");
|
|
15
|
-
sheet.insertRule(".
|
|
15
|
+
sheet.insertRule(".group-header {display: table-row-group;}");
|
|
16
16
|
} else {
|
|
17
17
|
// if there is a filter, only the requests that match the filter are visible
|
|
18
18
|
sheet.insertRule('[data-filter*="' + value + '"] {display: table-row;}');
|
|
19
19
|
if (CSS.supports("selector(:has(*))")) {
|
|
20
|
-
//
|
|
21
|
-
sheet.insertRule('.
|
|
20
|
+
// a group row is visible only if it contains at least one visible request
|
|
21
|
+
sheet.insertRule('.group-header:has([data-filter*="' + value + '"]) {display: table-row-group;}');
|
|
22
22
|
// if there is a filter and no request visible, the filter icon is red
|
|
23
23
|
sheet.insertRule('.requests-table:not(:has([data-filter*="' + value + '"])):has(tbody) .icon-filter {filter: grayscale(0%) !important;}');
|
|
24
24
|
} else {
|
|
25
|
-
// in case :has is not supported, the
|
|
26
|
-
sheet.insertRule(".
|
|
25
|
+
// in case :has is not supported, the group rows are always visible
|
|
26
|
+
sheet.insertRule(".group-header {display: table-row-group;}");
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
filter_requests_count();
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
function
|
|
33
|
+
function collapse_group(group_id, collapse) {
|
|
34
34
|
if (collapse) {
|
|
35
|
-
global.
|
|
35
|
+
global.group_collapse.push(group_id);
|
|
36
36
|
} else {
|
|
37
|
-
let index = global.
|
|
37
|
+
let index = global.group_collapse.indexOf(group_id);
|
|
38
38
|
if (index !== -1) {
|
|
39
|
-
global.
|
|
39
|
+
global.group_collapse.splice(index, 1);
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
update_collapse_group();
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
function
|
|
47
|
-
var sheet = document.getElementById("
|
|
46
|
+
function update_collapse_group() {
|
|
47
|
+
var sheet = document.getElementById("collapsegroupcss").sheet;
|
|
48
48
|
|
|
49
49
|
while (sheet.cssRules.length > 0) {
|
|
50
50
|
sheet.deleteRule(0);
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
for (let
|
|
54
|
-
sheet.insertRule('[data-
|
|
53
|
+
for (let group_id of global.group_collapse) {
|
|
54
|
+
sheet.insertRule('[data-group="' + group_id + '"] {display: none;}');
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
57
|
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
<style type="text/css" id="filter"></style>
|
|
18
18
|
<style type="text/css" id="configurationcss"></style>
|
|
19
19
|
<style type="text/css" id="serverstatuscss"></style>
|
|
20
|
-
<style type="text/css" id="
|
|
20
|
+
<style type="text/css" id="collapsegroupcss"></style>
|
|
21
21
|
<script src="static/mustache.min.js-+-$**HTTPDBG_VERSION**$"></script>
|
|
22
22
|
<script src="static/main.js-+-$**HTTPDBG_VERSION**$"></script>
|
|
23
23
|
<script src="static/api.js-+-$**HTTPDBG_VERSION**$"></script>
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
<br>
|
|
68
68
|
|
|
69
69
|
<input type="checkbox" id="cinitiator" name="hi" onchange="load_config_from_form(true)">
|
|
70
|
-
<label for="cinitiator">Hide the
|
|
70
|
+
<label for="cinitiator">Hide the group rows</label>
|
|
71
71
|
|
|
72
72
|
<br>
|
|
73
73
|
|
|
@@ -158,15 +158,15 @@
|
|
|
158
158
|
</tr>
|
|
159
159
|
</thead>
|
|
160
160
|
|
|
161
|
-
<script id="
|
|
162
|
-
<tbody id="
|
|
163
|
-
<tr class="
|
|
161
|
+
<script id="template_group" type="x-tmpl-mustache">
|
|
162
|
+
<tbody id="group-{{id}}" name="group" class="group-header">
|
|
163
|
+
<tr class="group">
|
|
164
164
|
<td class="collapse" onclick="event.stopPropagation();">
|
|
165
|
-
<input class="collapse-checkbox" type="checkbox" id="collapse{{
|
|
166
|
-
<label class="collapse-icon" for="collapse{{
|
|
165
|
+
<input class="collapse-checkbox" type="checkbox" id="collapse{{id}}" onclick="collapse_group('{{id}}',this.checked)">
|
|
166
|
+
<label class="collapse-icon" for="collapse{{id}}"><img class="icon" src="static/icons/chevron-direction-top-outline-icon.svg-+-$**HTTPDBG_VERSION**$" alt="click to collapse/expand"/></label>
|
|
167
167
|
</td>
|
|
168
|
-
<td colspan="3" class="
|
|
169
|
-
{{
|
|
168
|
+
<td colspan="3" class="group-label" title="{{full_label}}">
|
|
169
|
+
{{label}}
|
|
170
170
|
</td>
|
|
171
171
|
</tr>
|
|
172
172
|
</tbody>
|
|
@@ -175,7 +175,7 @@
|
|
|
175
175
|
<script id="template_request" type="x-tmpl-mustache">
|
|
176
176
|
<tr id="request-{{id}}"
|
|
177
177
|
data-filter="{{filter}}"
|
|
178
|
-
data-
|
|
178
|
+
data-group="{{group_id}}"
|
|
179
179
|
onclick="select_request(arguments[0], '{{id}}')"
|
|
180
180
|
onkeypress="shortcut_request(event, '{{id}}')"
|
|
181
181
|
class="request"
|