httpdbg 0.31.4__tar.gz → 0.31.6__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.4 → httpdbg-0.31.6}/PKG-INFO +1 -1
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg/__init__.py +1 -1
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg/hooks/aiohttp.py +5 -4
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg/hooks/httpx.py +10 -8
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg/hooks/requests.py +5 -4
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg/hooks/socket.py +92 -34
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg/hooks/urllib3.py +5 -4
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg/initiator.py +64 -33
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg/records.py +3 -24
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg.egg-info/PKG-INFO +1 -1
- httpdbg-0.31.6/tests/test_context_manager.py +164 -0
- httpdbg-0.31.4/tests/test_context_manager.py +0 -56
- {httpdbg-0.31.4 → httpdbg-0.31.6}/LICENSE +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/README.md +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg/__main__.py +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg/args.py +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg/env.py +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg/exception.py +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg/hooks/__init__.py +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg/hooks/all.py +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg/hooks/external.py +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg/hooks/generic.py +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg/hooks/pytest.py +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg/hooks/unittest.py +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg/hooks/utils.py +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg/log.py +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg/mode_console.py +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg/mode_module.py +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg/mode_script.py +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg/preview.py +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg/server.py +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg/utils.py +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg/webapp/__init__.py +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg/webapp/api.py +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg/webapp/app.py +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg/webapp/static/api.js +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg/webapp/static/column.css +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg/webapp/static/configuration.css +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg/webapp/static/configuration.js +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg/webapp/static/detail.css +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg/webapp/static/favicon.ico +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg/webapp/static/filter.js +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg/webapp/static/icons/chevron-direction-top-outline-icon.svg +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg/webapp/static/icons/download-install-line-icon.svg +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg/webapp/static/icons/engine-gears-icon.svg +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg/webapp/static/icons/eye-blind-icon.svg +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg/webapp/static/icons/eye-icon.svg +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg/webapp/static/icons/files-papers-outline-icon.svg +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg/webapp/static/icons/filter-outline-icon.svg +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg/webapp/static/icons/information-mark-circle-outline-icon.svg +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg/webapp/static/icons/lock-line-icon.svg +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg/webapp/static/icons/lock-open-line-icon.svg +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg/webapp/static/icons/math-multiplication-icon.svg +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg/webapp/static/icons/push-pin-green-icon.svg +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg/webapp/static/icons/question-mark-round-line-icon.svg +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg/webapp/static/icons/recycle-bin-line-icon.svg +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg/webapp/static/icons/wait-sandclock-icon.svg +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg/webapp/static/index.htm +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg/webapp/static/main.js +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg/webapp/static/mustache.min.js +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg/webapp/static/render.js +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg/webapp/static/style.css +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg/webapp/static/table.css +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg.egg-info/SOURCES.txt +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg.egg-info/dependency_links.txt +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg.egg-info/entry_points.txt +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg.egg-info/requires.txt +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/httpdbg.egg-info/top_level.txt +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/pyproject.toml +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/setup.cfg +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/tests/test_api.py +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/tests/test_cli.py +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/tests/test_hook_aiohttp.py +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/tests/test_hook_httpx.py +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/tests/test_hook_pytest.py +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/tests/test_hook_requests.py +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/tests/test_hook_unittest.py +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/tests/test_hook_urllib3.py +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/tests/test_initiator.py +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/tests/test_mode_console.py +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/tests/test_mode_module_pytest.py +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/tests/test_mode_script.py +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/tests/test_preview.py +0 -0
- {httpdbg-0.31.4 → httpdbg-0.31.6}/tests/test_server.py +0 -0
|
@@ -25,10 +25,11 @@ def set_hook_for_aiohttp_async(records: HTTPRecords, method: Callable):
|
|
|
25
25
|
|
|
26
26
|
if "str_or_url" in callargs:
|
|
27
27
|
if initiator_and_group:
|
|
28
|
-
initiator, group = initiator_and_group
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
initiator, group, is_new = initiator_and_group
|
|
29
|
+
if is_new:
|
|
30
|
+
records.add_new_record_exception(
|
|
31
|
+
initiator, group, str(callargs["str_or_url"]), ex
|
|
32
|
+
)
|
|
32
33
|
raise
|
|
33
34
|
|
|
34
35
|
return hook
|
|
@@ -25,10 +25,11 @@ def set_hook_for_httpx_async(records: HTTPRecords, method: Callable):
|
|
|
25
25
|
|
|
26
26
|
if "url" in callargs:
|
|
27
27
|
if initiator_and_group:
|
|
28
|
-
initiator, group = initiator_and_group
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
initiator, group, is_new = initiator_and_group
|
|
29
|
+
if is_new:
|
|
30
|
+
records.add_new_record_exception(
|
|
31
|
+
initiator, group, str(callargs["url"]), ex
|
|
32
|
+
)
|
|
32
33
|
raise
|
|
33
34
|
|
|
34
35
|
return hook
|
|
@@ -48,10 +49,11 @@ def set_hook_for_httpx(records: HTTPRecords, method: Callable):
|
|
|
48
49
|
|
|
49
50
|
if "url" in callargs:
|
|
50
51
|
if initiator_and_group:
|
|
51
|
-
initiator, group = initiator_and_group
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
52
|
+
initiator, group, is_new = initiator_and_group
|
|
53
|
+
if is_new:
|
|
54
|
+
records.add_new_record_exception(
|
|
55
|
+
initiator, group, str(callargs["url"]), ex
|
|
56
|
+
)
|
|
55
57
|
raise
|
|
56
58
|
|
|
57
59
|
return hook
|
|
@@ -25,10 +25,11 @@ def set_hook_for_requests(records: HTTPRecords, method: Callable):
|
|
|
25
25
|
|
|
26
26
|
if "url" in callargs:
|
|
27
27
|
if initiator_and_group:
|
|
28
|
-
initiator, group = initiator_and_group
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
initiator, group, is_new = initiator_and_group
|
|
29
|
+
if is_new:
|
|
30
|
+
records.add_new_record_exception(
|
|
31
|
+
initiator, group, str(callargs["url"]), ex
|
|
32
|
+
)
|
|
32
33
|
raise
|
|
33
34
|
|
|
34
35
|
return hook
|
|
@@ -3,15 +3,18 @@ import asyncio
|
|
|
3
3
|
import asyncio.proactor_events
|
|
4
4
|
from collections.abc import Callable
|
|
5
5
|
from contextlib import contextmanager
|
|
6
|
+
import datetime
|
|
6
7
|
import platform
|
|
7
8
|
import socket
|
|
8
9
|
import ssl
|
|
9
10
|
import sys
|
|
11
|
+
import traceback
|
|
10
12
|
from typing import Generator
|
|
11
13
|
|
|
12
14
|
from httpdbg.hooks.utils import getcallargs
|
|
13
15
|
from httpdbg.hooks.utils import decorate
|
|
14
16
|
from httpdbg.hooks.utils import undecorate
|
|
17
|
+
from httpdbg.initiator import httpdbg_initiator
|
|
15
18
|
from httpdbg.records import HTTPRecord
|
|
16
19
|
from httpdbg.records import HTTPRecords
|
|
17
20
|
from httpdbg.log import logger
|
|
@@ -33,20 +36,29 @@ def set_hook_for_socket_init(records: HTTPRecords, method: Callable):
|
|
|
33
36
|
# what: A connection to a remote socket is initiated.
|
|
34
37
|
# action: A new entry is added to the temporary raw socket storage list.
|
|
35
38
|
def set_hook_for_socket_connect(records: HTTPRecords, method: Callable):
|
|
36
|
-
def hook(self,
|
|
39
|
+
def hook(self, *args, **kwargs):
|
|
40
|
+
tbegin: datetime.datetime = datetime.datetime.now(datetime.timezone.utc)
|
|
37
41
|
socketdata = records.get_socket_data(self, force_new=True)
|
|
38
42
|
if socketdata:
|
|
39
43
|
logger().info(
|
|
40
|
-
f"CONNECT - self={self} id={id(self)} socketdata={socketdata}
|
|
44
|
+
f"CONNECT - self={self} id={id(self)} socketdata={socketdata} args={args} kwargs={kwargs}"
|
|
41
45
|
)
|
|
42
46
|
try:
|
|
43
|
-
r = method(self,
|
|
47
|
+
r = method(self, *args, **kwargs)
|
|
44
48
|
except Exception as ex:
|
|
45
49
|
if not isinstance(
|
|
46
50
|
ex, (BlockingIOError, OSError)
|
|
47
51
|
): # BlockingIOError for async, OSError for ipv6
|
|
48
|
-
|
|
49
|
-
|
|
52
|
+
with httpdbg_initiator(
|
|
53
|
+
records, traceback.extract_stack(), method, *args, **kwargs
|
|
54
|
+
) as initiator_and_group:
|
|
55
|
+
initiator, group, is_new = initiator_and_group
|
|
56
|
+
if is_new:
|
|
57
|
+
initiator.tbegin = tbegin
|
|
58
|
+
group.tbegin = tbegin
|
|
59
|
+
records.add_new_record_exception(
|
|
60
|
+
initiator, group, "http:///", ex
|
|
61
|
+
)
|
|
50
62
|
raise
|
|
51
63
|
|
|
52
64
|
return r
|
|
@@ -60,11 +72,18 @@ def set_hook_for_socket_connect(records: HTTPRecords, method: Callable):
|
|
|
60
72
|
# action: Link the socket and the sslsocket
|
|
61
73
|
def set_hook_for_ssl_wrap_socket(records: HTTPRecords, method: Callable):
|
|
62
74
|
def hook(sock, *args, **kwargs):
|
|
75
|
+
tbegin: datetime.datetime = datetime.datetime.now(datetime.timezone.utc)
|
|
63
76
|
try:
|
|
64
77
|
sslsocket = method(sock, *args, **kwargs)
|
|
65
78
|
except Exception as ex:
|
|
66
|
-
|
|
67
|
-
|
|
79
|
+
with httpdbg_initiator(
|
|
80
|
+
records, traceback.extract_stack(), method, *args, **kwargs
|
|
81
|
+
) as initiator_and_group:
|
|
82
|
+
initiator, group, is_new = initiator_and_group
|
|
83
|
+
if is_new:
|
|
84
|
+
initiator.tbegin = tbegin
|
|
85
|
+
group.tbegin = tbegin
|
|
86
|
+
records.add_new_record_exception(initiator, group, "http:///", ex)
|
|
68
87
|
raise
|
|
69
88
|
|
|
70
89
|
logger().info(
|
|
@@ -85,11 +104,18 @@ def set_hook_for_ssl_wrap_socket(records: HTTPRecords, method: Callable):
|
|
|
85
104
|
# action: Link the socket and the sslsocket
|
|
86
105
|
def set_hook_for_sslcontext_wrap_socket(records: HTTPRecords, method: Callable):
|
|
87
106
|
def hook(self, sock, *args, **kwargs):
|
|
107
|
+
tbegin: datetime.datetime = datetime.datetime.now(datetime.timezone.utc)
|
|
88
108
|
try:
|
|
89
109
|
sslsocket = method(self, sock, *args, **kwargs)
|
|
90
110
|
except Exception as ex:
|
|
91
|
-
|
|
92
|
-
|
|
111
|
+
with httpdbg_initiator(
|
|
112
|
+
records, traceback.extract_stack(), method, *args, **kwargs
|
|
113
|
+
) as initiator_and_group:
|
|
114
|
+
initiator, group, is_new = initiator_and_group
|
|
115
|
+
if is_new:
|
|
116
|
+
initiator.tbegin = tbegin
|
|
117
|
+
group.tbegin = tbegin
|
|
118
|
+
records.add_new_record_exception(initiator, group, "http:///", ex)
|
|
93
119
|
raise
|
|
94
120
|
|
|
95
121
|
logger().info(
|
|
@@ -110,11 +136,18 @@ def set_hook_for_sslcontext_wrap_socket(records: HTTPRecords, method: Callable):
|
|
|
110
136
|
# action: Record a new SocketRawData if necessary
|
|
111
137
|
def set_hook_for_socket_wrap_bio(records: HTTPRecords, method: Callable):
|
|
112
138
|
def hook(self, *args, **kwargs):
|
|
139
|
+
tbegin: datetime.datetime = datetime.datetime.now(datetime.timezone.utc)
|
|
113
140
|
try:
|
|
114
141
|
sslobject = method(self, *args, **kwargs)
|
|
115
142
|
except Exception as ex:
|
|
116
|
-
|
|
117
|
-
|
|
143
|
+
with httpdbg_initiator(
|
|
144
|
+
records, traceback.extract_stack(), method, *args, **kwargs
|
|
145
|
+
) as initiator_and_group:
|
|
146
|
+
initiator, group, is_new = initiator_and_group
|
|
147
|
+
if is_new:
|
|
148
|
+
initiator.tbegin = tbegin
|
|
149
|
+
group.tbegin = tbegin
|
|
150
|
+
records.add_new_record_exception(initiator, group, "http:///", ex)
|
|
118
151
|
raise
|
|
119
152
|
|
|
120
153
|
logger().info(
|
|
@@ -196,7 +229,6 @@ def set_hook_for_socket_sendall(records: HTTPRecords, method: Callable):
|
|
|
196
229
|
logger().info(
|
|
197
230
|
f"SENDALL - self={self} id={id(self)} socketdata={socketdata} bytes={(b''+bytes)[:20]} type={type(bytes)} len={len(bytes)} args={args} kwargs={kwargs}"
|
|
198
231
|
)
|
|
199
|
-
|
|
200
232
|
if socketdata:
|
|
201
233
|
if socketdata.record:
|
|
202
234
|
socketdata.record.request.rawdata += bytes
|
|
@@ -205,15 +237,23 @@ def set_hook_for_socket_sendall(records: HTTPRecords, method: Callable):
|
|
|
205
237
|
http_detected = socketdata.http_detected()
|
|
206
238
|
if http_detected:
|
|
207
239
|
logger().info("SENDALL - http detected")
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
240
|
+
with httpdbg_initiator(
|
|
241
|
+
records, traceback.extract_stack(), method, *args, **kwargs
|
|
242
|
+
) as initiator_and_group:
|
|
243
|
+
initiator, group, is_new = initiator_and_group
|
|
244
|
+
if is_new:
|
|
245
|
+
tbegin = socketdata.tbegin - datetime.timedelta(
|
|
246
|
+
milliseconds=1
|
|
247
|
+
)
|
|
248
|
+
initiator.tbegin = tbegin
|
|
249
|
+
group.tbegin = tbegin
|
|
250
|
+
socketdata.record = HTTPRecord(tbegin=socketdata.tbegin)
|
|
251
|
+
socketdata.record.address = socketdata.address
|
|
252
|
+
socketdata.record.ssl = socketdata.ssl
|
|
253
|
+
socketdata.record.request.rawdata = socketdata.rawdata
|
|
254
|
+
records.requests[socketdata.record.id] = socketdata.record
|
|
214
255
|
elif http_detected is False: # if None, there is nothing to do
|
|
215
256
|
records._sockets[id(self)] = None
|
|
216
|
-
|
|
217
257
|
try:
|
|
218
258
|
return method(self, bytes, *args, **kwargs)
|
|
219
259
|
except Exception as ex:
|
|
@@ -250,12 +290,21 @@ def set_hook_for_socket_send(records: HTTPRecords, method: Callable):
|
|
|
250
290
|
socketdata.rawdata += bytes[:size]
|
|
251
291
|
http_detected = socketdata.http_detected()
|
|
252
292
|
if http_detected:
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
293
|
+
with httpdbg_initiator(
|
|
294
|
+
records, traceback.extract_stack(), method, *args, **kwargs
|
|
295
|
+
) as initiator_and_group:
|
|
296
|
+
initiator, group, is_new = initiator_and_group
|
|
297
|
+
if is_new:
|
|
298
|
+
tbegin = socketdata.tbegin - datetime.timedelta(
|
|
299
|
+
milliseconds=1
|
|
300
|
+
)
|
|
301
|
+
initiator.tbegin = tbegin
|
|
302
|
+
group.tbegin = tbegin
|
|
303
|
+
socketdata.record = HTTPRecord(tbegin=socketdata.tbegin)
|
|
304
|
+
socketdata.record.address = socketdata.address
|
|
305
|
+
socketdata.record.ssl = socketdata.ssl
|
|
306
|
+
socketdata.record.request.rawdata = socketdata.rawdata
|
|
307
|
+
records.requests[socketdata.record.id] = socketdata.record
|
|
259
308
|
elif http_detected is False: # if None, there is nothing to do
|
|
260
309
|
records._sockets[id(self)] = None
|
|
261
310
|
return size
|
|
@@ -314,12 +363,21 @@ def set_hook_for_sslobject_write(records: HTTPRecords, method: Callable):
|
|
|
314
363
|
socketdata.rawdata += bytes(buf[:size])
|
|
315
364
|
http_detected = socketdata.http_detected()
|
|
316
365
|
if http_detected:
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
366
|
+
with httpdbg_initiator(
|
|
367
|
+
records, traceback.extract_stack(), method, *args, **kwargs
|
|
368
|
+
) as initiator_and_group:
|
|
369
|
+
initiator, group, is_new = initiator_and_group
|
|
370
|
+
if is_new:
|
|
371
|
+
tbegin = socketdata.tbegin - datetime.timedelta(
|
|
372
|
+
milliseconds=1
|
|
373
|
+
)
|
|
374
|
+
initiator.tbegin = tbegin
|
|
375
|
+
group.tbegin = tbegin
|
|
376
|
+
socketdata.record = HTTPRecord(tbegin=socketdata.tbegin)
|
|
377
|
+
socketdata.record.address = socketdata.address
|
|
378
|
+
socketdata.record.ssl = socketdata.ssl
|
|
379
|
+
socketdata.record.request.rawdata = socketdata.rawdata
|
|
380
|
+
records.requests[socketdata.record.id] = socketdata.record
|
|
323
381
|
elif http_detected is False: # if None, there is nothing to do
|
|
324
382
|
records.sockets[id(self)] = None
|
|
325
383
|
return size
|
|
@@ -376,8 +434,8 @@ def hook_socket(records: HTTPRecords) -> Generator[None, None, None]:
|
|
|
376
434
|
socket.socket.send = decorate(records, socket.socket.send, set_hook_for_socket_send)
|
|
377
435
|
|
|
378
436
|
if (sys.version_info.major == 3) and (sys.version_info.minor < 12):
|
|
379
|
-
ssl.wrap_socket = decorate(
|
|
380
|
-
records, ssl.wrap_socket, set_hook_for_ssl_wrap_socket
|
|
437
|
+
ssl.wrap_socket = decorate( # type: ignore[attr-defined]
|
|
438
|
+
records, ssl.wrap_socket, set_hook_for_ssl_wrap_socket # type: ignore[attr-defined]
|
|
381
439
|
)
|
|
382
440
|
|
|
383
441
|
ssl.SSLContext.wrap_socket = decorate(
|
|
@@ -436,7 +494,7 @@ def hook_socket(records: HTTPRecords) -> Generator[None, None, None]:
|
|
|
436
494
|
socket.socket.send = undecorate(socket.socket.send)
|
|
437
495
|
|
|
438
496
|
if (sys.version_info.major == 3) and (sys.version_info.minor < 12):
|
|
439
|
-
ssl.wrap_socket = undecorate(ssl.wrap_socket)
|
|
497
|
+
ssl.wrap_socket = undecorate(ssl.wrap_socket) # type: ignore[attr-defined]
|
|
440
498
|
|
|
441
499
|
ssl.SSLContext.wrap_socket = undecorate(ssl.SSLContext.wrap_socket)
|
|
442
500
|
ssl.SSLContext.wrap_bio = undecorate(ssl.SSLContext.wrap_bio)
|
|
@@ -25,10 +25,11 @@ def set_hook_for_urllib3(records: HTTPRecords, method: Callable):
|
|
|
25
25
|
|
|
26
26
|
if "url" in callargs:
|
|
27
27
|
if initiator_and_group:
|
|
28
|
-
initiator, group = initiator_and_group
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
initiator, group, is_new = initiator_and_group
|
|
29
|
+
if is_new:
|
|
30
|
+
records.add_new_record_exception(
|
|
31
|
+
initiator, group, str(callargs["url"]), ex
|
|
32
|
+
)
|
|
32
33
|
raise
|
|
33
34
|
|
|
34
35
|
return hook
|
|
@@ -112,7 +112,9 @@ def get_current_instruction(
|
|
|
112
112
|
n_stack -= 1
|
|
113
113
|
framesummary = extracted_stack[n_stack - 1]
|
|
114
114
|
|
|
115
|
-
while "httpdbg/hooks" in framesummary.filename
|
|
115
|
+
while ("httpdbg/hooks" in framesummary.filename) or (
|
|
116
|
+
"httpdbg\\hooks" in framesummary.filename
|
|
117
|
+
):
|
|
116
118
|
n_stack -= 1
|
|
117
119
|
framesummary = extracted_stack[n_stack - 1]
|
|
118
120
|
|
|
@@ -209,44 +211,73 @@ def httpdbg_initiator(
|
|
|
209
211
|
original_method: Callable,
|
|
210
212
|
*args,
|
|
211
213
|
**kwargs,
|
|
212
|
-
) -> Generator[Union[Tuple[Initiator, Group], None], None, None]:
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
214
|
+
) -> Generator[Union[Tuple[Initiator, Group, bool], None], None, None]:
|
|
215
|
+
# An initiator is considered set if the environment variable exists and the initiator
|
|
216
|
+
# is recorded.If the environment variable exists but the initiatir is not recorded,
|
|
217
|
+
# it means httpdbg was called reentrantly, and we need to record the initiator again
|
|
218
|
+
# using the same id.
|
|
219
|
+
initiator_already_set = (HTTPDBG_CURRENT_INITIATOR in os.environ) and (
|
|
220
|
+
os.environ[HTTPDBG_CURRENT_INITIATOR] in records.initiators
|
|
221
|
+
)
|
|
222
|
+
try:
|
|
223
|
+
if not initiator_already_set:
|
|
224
|
+
original_initiator_id = None
|
|
225
|
+
if HTTPDBG_CURRENT_INITIATOR not in os.environ:
|
|
226
|
+
# temporary set a fake initiator env variable to avoid a recursion error
|
|
227
|
+
# RecursionError: maximum recursion depth exceeded while calling a Python object
|
|
228
|
+
# TL;DR When we construct the short_stack string, a recursion error occurs if there
|
|
229
|
+
# is an object from a class where a hooked method is called in __repr__ or __str__.
|
|
230
|
+
os.environ[HTTPDBG_CURRENT_INITIATOR] = "blabla"
|
|
231
|
+
else:
|
|
232
|
+
original_initiator_id = os.environ[HTTPDBG_CURRENT_INITIATOR]
|
|
233
|
+
|
|
234
|
+
# in any case, we create an Initiator
|
|
235
|
+
|
|
236
|
+
callargs = getcallargs(original_method, *args, **kwargs)
|
|
237
|
+
instruction, short_stack, stack = get_current_instruction(extracted_stack)
|
|
238
|
+
|
|
239
|
+
str_module = (
|
|
240
|
+
f"{original_method.__module__}."
|
|
241
|
+
if hasattr(original_method, "__module__")
|
|
242
|
+
else ""
|
|
243
|
+
)
|
|
244
|
+
str_name = (
|
|
245
|
+
f"{original_method.__name__}"
|
|
246
|
+
if hasattr(original_method, "__name__")
|
|
247
|
+
else "***"
|
|
248
|
+
)
|
|
249
|
+
short_stack += f"----------\n{str_module}{str_name}(\n"
|
|
250
|
+
for k, v in callargs.items():
|
|
251
|
+
short_stack += f" {k}={v}\n"
|
|
252
|
+
short_stack += ")"
|
|
224
253
|
|
|
225
|
-
|
|
226
|
-
f"----------\n{original_method.__module__}.{original_method.__name__}(\n"
|
|
227
|
-
)
|
|
228
|
-
for k, v in callargs.items():
|
|
229
|
-
short_stack += f" {k}={v}\n"
|
|
230
|
-
short_stack += ")"
|
|
254
|
+
current_initiator = Initiator(instruction, short_stack, stack)
|
|
231
255
|
|
|
232
|
-
|
|
233
|
-
|
|
256
|
+
if original_initiator_id:
|
|
257
|
+
# if we already have an initiator id (reentrant call) we change the initiator id
|
|
258
|
+
# to keep the same
|
|
259
|
+
current_initiator.id = original_initiator_id
|
|
234
260
|
|
|
235
|
-
|
|
261
|
+
records.initiators[current_initiator.id] = current_initiator
|
|
236
262
|
|
|
237
|
-
|
|
238
|
-
with httpdbg_group(
|
|
239
|
-
records, instruction, short_stack
|
|
240
|
-
) as group: # by default we group the requests by initiator
|
|
241
|
-
yield current_initiator, group
|
|
242
|
-
except Exception:
|
|
243
|
-
del os.environ[envname]
|
|
244
|
-
raise
|
|
263
|
+
os.environ[HTTPDBG_CURRENT_INITIATOR] = current_initiator.id
|
|
245
264
|
|
|
246
|
-
|
|
265
|
+
else:
|
|
266
|
+
current_initiator = records.initiators[
|
|
267
|
+
os.environ[HTTPDBG_CURRENT_INITIATOR]
|
|
268
|
+
]
|
|
269
|
+
|
|
270
|
+
with httpdbg_group(
|
|
271
|
+
records, current_initiator.label, current_initiator.short_stack
|
|
272
|
+
) as group: # by default we group the requests by initiator
|
|
273
|
+
yield current_initiator, group, initiator_already_set is False
|
|
274
|
+
except Exception:
|
|
275
|
+
if (not initiator_already_set) and (HTTPDBG_CURRENT_INITIATOR in os.environ):
|
|
276
|
+
del os.environ[HTTPDBG_CURRENT_INITIATOR]
|
|
277
|
+
raise
|
|
247
278
|
|
|
248
|
-
|
|
249
|
-
|
|
279
|
+
if (not initiator_already_set) and (HTTPDBG_CURRENT_INITIATOR in os.environ):
|
|
280
|
+
del os.environ[HTTPDBG_CURRENT_INITIATOR]
|
|
250
281
|
|
|
251
282
|
|
|
252
283
|
@contextmanager
|
|
@@ -6,7 +6,6 @@ import os
|
|
|
6
6
|
import socket
|
|
7
7
|
import ssl
|
|
8
8
|
import sys
|
|
9
|
-
import traceback
|
|
10
9
|
from urllib.parse import urlparse
|
|
11
10
|
from typing import Dict, List, Tuple, Union
|
|
12
11
|
|
|
@@ -15,7 +14,6 @@ from httpdbg.env import HTTPDBG_CURRENT_INITIATOR
|
|
|
15
14
|
from httpdbg.env import HTTPDBG_CURRENT_TAG
|
|
16
15
|
from httpdbg.utils import HTTPDBGCookie
|
|
17
16
|
from httpdbg.utils import HTTPDBGHeader
|
|
18
|
-
from httpdbg.initiator import in_lib
|
|
19
17
|
from httpdbg.initiator import Group
|
|
20
18
|
from httpdbg.initiator import Initiator
|
|
21
19
|
from httpdbg.preview import generate_preview
|
|
@@ -217,7 +215,9 @@ class HTTPRecord:
|
|
|
217
215
|
self.id = get_new_uuid()
|
|
218
216
|
self.address: Tuple[str, int] = ("", 0)
|
|
219
217
|
self._url: Union[str, None] = None
|
|
220
|
-
self.initiator_id: Union[str, None] =
|
|
218
|
+
self.initiator_id: Union[str, None] = os.environ.get(
|
|
219
|
+
f"{HTTPDBG_CURRENT_INITIATOR}"
|
|
220
|
+
)
|
|
221
221
|
self.exception: Union[Exception, None] = None
|
|
222
222
|
self.request: HTTPRecordRequest = HTTPRecordRequest()
|
|
223
223
|
self.response: HTTPRecordResponse = HTTPRecordResponse()
|
|
@@ -325,27 +325,6 @@ class HTTPRecords:
|
|
|
325
325
|
def __len__(self) -> int:
|
|
326
326
|
return len(self.requests)
|
|
327
327
|
|
|
328
|
-
def get_initiator(self) -> str:
|
|
329
|
-
envname = f"{HTTPDBG_CURRENT_INITIATOR}_{self.session.id}"
|
|
330
|
-
|
|
331
|
-
if envname in os.environ:
|
|
332
|
-
initiator = self.initiators[os.environ[envname]]
|
|
333
|
-
else:
|
|
334
|
-
fullstack = traceback.format_stack()
|
|
335
|
-
stack: List[str] = []
|
|
336
|
-
for line in fullstack[6:]:
|
|
337
|
-
if in_lib(line):
|
|
338
|
-
break
|
|
339
|
-
stack.append(line)
|
|
340
|
-
long_label = stack[-1]
|
|
341
|
-
label = long_label.split("\n")[1]
|
|
342
|
-
initiator = Initiator(label, long_label, stack)
|
|
343
|
-
|
|
344
|
-
if initiator.id not in self.initiators:
|
|
345
|
-
self.initiators[initiator.id] = initiator
|
|
346
|
-
|
|
347
|
-
return initiator.id
|
|
348
|
-
|
|
349
328
|
def get_socket_data(
|
|
350
329
|
self, obj, extra_sock=None, force_new=False, request=None
|
|
351
330
|
) -> Union[SocketRawData, None]:
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
from httpdbg import httprecord
|
|
3
|
+
|
|
4
|
+
import pytest
|
|
5
|
+
import requests
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@pytest.mark.cm
|
|
9
|
+
@pytest.mark.api
|
|
10
|
+
def test_context_manager(httpbin):
|
|
11
|
+
requests.get(httpbin.url + "/get")
|
|
12
|
+
|
|
13
|
+
with httprecord() as records:
|
|
14
|
+
requests.get(httpbin.url + "/get")
|
|
15
|
+
|
|
16
|
+
assert len(records.requests) == 1
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@pytest.mark.cm
|
|
20
|
+
@pytest.mark.api
|
|
21
|
+
def test_context_manager_two_calls(httpbin):
|
|
22
|
+
requests.get(httpbin.url + "/get")
|
|
23
|
+
|
|
24
|
+
with httprecord() as records:
|
|
25
|
+
requests.get(httpbin.url + "/get")
|
|
26
|
+
|
|
27
|
+
assert len(records.requests) == 1
|
|
28
|
+
for _, req in records.requests.items():
|
|
29
|
+
assert req.method.lower() == "get"
|
|
30
|
+
|
|
31
|
+
with httprecord() as records2:
|
|
32
|
+
requests.post(httpbin.url + "/post")
|
|
33
|
+
|
|
34
|
+
assert len(records2.requests) == 1
|
|
35
|
+
for _, req in records2.requests.items():
|
|
36
|
+
assert req.method.lower() == "post"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@pytest.mark.cm
|
|
40
|
+
def test_context_manager_reentrant(httpbin):
|
|
41
|
+
requests.get(httpbin.url + "/get")
|
|
42
|
+
|
|
43
|
+
with httprecord() as records1:
|
|
44
|
+
requests.get(httpbin.url + "/get/a1")
|
|
45
|
+
with httprecord() as records2:
|
|
46
|
+
requests.get(httpbin.url + "/get/b1")
|
|
47
|
+
requests.get(httpbin.url + "/get/b2")
|
|
48
|
+
with httprecord() as records3:
|
|
49
|
+
requests.get(httpbin.url + "/get/c1")
|
|
50
|
+
requests.get(httpbin.url + "/get/a2")
|
|
51
|
+
|
|
52
|
+
requests.get(httpbin.url + "/get")
|
|
53
|
+
|
|
54
|
+
assert len(records1.requests) == 5
|
|
55
|
+
assert len(records2.requests) == 3
|
|
56
|
+
assert len(records3.requests) == 1
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@pytest.mark.cm
|
|
60
|
+
@pytest.mark.initiator
|
|
61
|
+
def test_context_manager_reentrant_initiator(httpbin):
|
|
62
|
+
requests.get(httpbin.url + "/get")
|
|
63
|
+
|
|
64
|
+
with httprecord() as records1:
|
|
65
|
+
requests.get(httpbin.url + "/get/a1")
|
|
66
|
+
with httprecord() as records2:
|
|
67
|
+
requests.get(httpbin.url + "/get/b1")
|
|
68
|
+
requests.get(httpbin.url + "/get/b2")
|
|
69
|
+
with httprecord() as records3:
|
|
70
|
+
requests.get(httpbin.url + "/get/c1")
|
|
71
|
+
requests.get(httpbin.url + "/get/a2")
|
|
72
|
+
|
|
73
|
+
requests.get(httpbin.url + "/get")
|
|
74
|
+
|
|
75
|
+
assert (
|
|
76
|
+
records1.initiators[records1[0].initiator_id].label
|
|
77
|
+
== 'requests.get(httpbin.url + "/get/a1")'
|
|
78
|
+
)
|
|
79
|
+
assert (
|
|
80
|
+
records1.initiators[records1[1].initiator_id].label
|
|
81
|
+
== 'requests.get(httpbin.url + "/get/b1")'
|
|
82
|
+
)
|
|
83
|
+
assert (
|
|
84
|
+
records1.initiators[records1[2].initiator_id].label
|
|
85
|
+
== 'requests.get(httpbin.url + "/get/b2")'
|
|
86
|
+
)
|
|
87
|
+
assert (
|
|
88
|
+
records1.initiators[records1[3].initiator_id].label
|
|
89
|
+
== 'requests.get(httpbin.url + "/get/c1")'
|
|
90
|
+
)
|
|
91
|
+
assert (
|
|
92
|
+
records1.initiators[records1[4].initiator_id].label
|
|
93
|
+
== 'requests.get(httpbin.url + "/get/a2")'
|
|
94
|
+
)
|
|
95
|
+
assert (
|
|
96
|
+
records2.initiators[records2[0].initiator_id].label
|
|
97
|
+
== 'requests.get(httpbin.url + "/get/b1")'
|
|
98
|
+
)
|
|
99
|
+
assert (
|
|
100
|
+
records2.initiators[records2[1].initiator_id].label
|
|
101
|
+
== 'requests.get(httpbin.url + "/get/b2")'
|
|
102
|
+
)
|
|
103
|
+
assert (
|
|
104
|
+
records2.initiators[records2[2].initiator_id].label
|
|
105
|
+
== 'requests.get(httpbin.url + "/get/c1")'
|
|
106
|
+
)
|
|
107
|
+
assert (
|
|
108
|
+
records3.initiators[records3[0].initiator_id].label
|
|
109
|
+
== 'requests.get(httpbin.url + "/get/c1")'
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
@pytest.mark.cm
|
|
114
|
+
@pytest.mark.group
|
|
115
|
+
def test_context_manager_reentrant_group(httpbin):
|
|
116
|
+
requests.get(httpbin.url + "/get")
|
|
117
|
+
|
|
118
|
+
with httprecord() as records1:
|
|
119
|
+
requests.get(httpbin.url + "/get/a1")
|
|
120
|
+
with httprecord() as records2:
|
|
121
|
+
requests.get(httpbin.url + "/get/b1")
|
|
122
|
+
requests.get(httpbin.url + "/get/b2")
|
|
123
|
+
with httprecord() as records3:
|
|
124
|
+
requests.get(httpbin.url + "/get/c1")
|
|
125
|
+
requests.get(httpbin.url + "/get/a2")
|
|
126
|
+
|
|
127
|
+
requests.get(httpbin.url + "/get")
|
|
128
|
+
|
|
129
|
+
assert (
|
|
130
|
+
records1.groups[records1[0].group_id].label
|
|
131
|
+
== 'requests.get(httpbin.url + "/get/a1")'
|
|
132
|
+
)
|
|
133
|
+
assert (
|
|
134
|
+
records1.groups[records1[1].group_id].label
|
|
135
|
+
== 'requests.get(httpbin.url + "/get/b1")'
|
|
136
|
+
)
|
|
137
|
+
assert (
|
|
138
|
+
records1.groups[records1[2].group_id].label
|
|
139
|
+
== 'requests.get(httpbin.url + "/get/b2")'
|
|
140
|
+
)
|
|
141
|
+
assert (
|
|
142
|
+
records1.groups[records1[3].group_id].label
|
|
143
|
+
== 'requests.get(httpbin.url + "/get/c1")'
|
|
144
|
+
)
|
|
145
|
+
assert (
|
|
146
|
+
records1.groups[records1[4].group_id].label
|
|
147
|
+
== 'requests.get(httpbin.url + "/get/a2")'
|
|
148
|
+
)
|
|
149
|
+
assert (
|
|
150
|
+
records2.groups[records2[0].group_id].label
|
|
151
|
+
== 'requests.get(httpbin.url + "/get/b1")'
|
|
152
|
+
)
|
|
153
|
+
assert (
|
|
154
|
+
records2.groups[records2[1].group_id].label
|
|
155
|
+
== 'requests.get(httpbin.url + "/get/b2")'
|
|
156
|
+
)
|
|
157
|
+
assert (
|
|
158
|
+
records2.groups[records2[2].group_id].label
|
|
159
|
+
== 'requests.get(httpbin.url + "/get/c1")'
|
|
160
|
+
)
|
|
161
|
+
assert (
|
|
162
|
+
records3.groups[records3[0].group_id].label
|
|
163
|
+
== 'requests.get(httpbin.url + "/get/c1")'
|
|
164
|
+
)
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
from httpdbg import httprecord
|
|
3
|
-
|
|
4
|
-
import pytest
|
|
5
|
-
import requests
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
@pytest.mark.cm
|
|
9
|
-
@pytest.mark.api
|
|
10
|
-
def test_context_manager(httpbin):
|
|
11
|
-
requests.get(httpbin.url + "/get")
|
|
12
|
-
|
|
13
|
-
with httprecord() as records:
|
|
14
|
-
requests.get(httpbin.url + "/get")
|
|
15
|
-
|
|
16
|
-
assert len(records.requests) == 1
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
@pytest.mark.cm
|
|
20
|
-
@pytest.mark.api
|
|
21
|
-
def test_context_manager_two_calls(httpbin):
|
|
22
|
-
requests.get(httpbin.url + "/get")
|
|
23
|
-
|
|
24
|
-
with httprecord() as records:
|
|
25
|
-
requests.get(httpbin.url + "/get")
|
|
26
|
-
|
|
27
|
-
assert len(records.requests) == 1
|
|
28
|
-
for _, req in records.requests.items():
|
|
29
|
-
assert req.method.lower() == "get"
|
|
30
|
-
|
|
31
|
-
with httprecord() as records2:
|
|
32
|
-
requests.post(httpbin.url + "/post")
|
|
33
|
-
|
|
34
|
-
assert len(records2.requests) == 1
|
|
35
|
-
for _, req in records2.requests.items():
|
|
36
|
-
assert req.method.lower() == "post"
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
@pytest.mark.cm
|
|
40
|
-
def test_context_manager_reentrant(httpbin):
|
|
41
|
-
requests.get(httpbin.url + "/get")
|
|
42
|
-
|
|
43
|
-
with httprecord() as records1:
|
|
44
|
-
requests.get(httpbin.url + "/get/a1")
|
|
45
|
-
with httprecord() as records2:
|
|
46
|
-
requests.get(httpbin.url + "/get/b1")
|
|
47
|
-
requests.get(httpbin.url + "/get/b2")
|
|
48
|
-
with httprecord() as records3:
|
|
49
|
-
requests.get(httpbin.url + "/get/c1")
|
|
50
|
-
requests.get(httpbin.url + "/get/a2")
|
|
51
|
-
|
|
52
|
-
requests.get(httpbin.url + "/get")
|
|
53
|
-
|
|
54
|
-
assert len(records1.requests) == 5
|
|
55
|
-
assert len(records2.requests) == 3
|
|
56
|
-
assert len(records3.requests) == 1
|
|
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.4 → httpdbg-0.31.6}/httpdbg/webapp/static/icons/chevron-direction-top-outline-icon.svg
RENAMED
|
File without changes
|
{httpdbg-0.31.4 → httpdbg-0.31.6}/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.4 → httpdbg-0.31.6}/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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|