httpdbg 0.13.3__tar.gz → 0.13.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. {httpdbg-0.13.3 → httpdbg-0.13.4}/PKG-INFO +1 -1
  2. {httpdbg-0.13.3 → httpdbg-0.13.4}/httpdbg/__init__.py +1 -1
  3. httpdbg-0.13.4/httpdbg/preview.py +53 -0
  4. {httpdbg-0.13.3 → httpdbg-0.13.4}/httpdbg/webapp/static/render.js +43 -1
  5. {httpdbg-0.13.3 → httpdbg-0.13.4}/httpdbg.egg-info/PKG-INFO +1 -1
  6. httpdbg-0.13.4/tests/test_preview.py +56 -0
  7. httpdbg-0.13.3/httpdbg/preview.py +0 -88
  8. httpdbg-0.13.3/tests/test_preview.py +0 -96
  9. {httpdbg-0.13.3 → httpdbg-0.13.4}/LICENSE +0 -0
  10. {httpdbg-0.13.3 → httpdbg-0.13.4}/README.md +0 -0
  11. {httpdbg-0.13.3 → httpdbg-0.13.4}/httpdbg/__main__.py +0 -0
  12. {httpdbg-0.13.3 → httpdbg-0.13.4}/httpdbg/args.py +0 -0
  13. {httpdbg-0.13.3 → httpdbg-0.13.4}/httpdbg/hooks/__init__.py +0 -0
  14. {httpdbg-0.13.3 → httpdbg-0.13.4}/httpdbg/hooks/aiohttp.py +0 -0
  15. {httpdbg-0.13.3 → httpdbg-0.13.4}/httpdbg/hooks/all.py +0 -0
  16. {httpdbg-0.13.3 → httpdbg-0.13.4}/httpdbg/hooks/generic.py +0 -0
  17. {httpdbg-0.13.3 → httpdbg-0.13.4}/httpdbg/hooks/httpx.py +0 -0
  18. {httpdbg-0.13.3 → httpdbg-0.13.4}/httpdbg/hooks/requests.py +0 -0
  19. {httpdbg-0.13.3 → httpdbg-0.13.4}/httpdbg/hooks/socket.py +0 -0
  20. {httpdbg-0.13.3 → httpdbg-0.13.4}/httpdbg/hooks/urllib3.py +0 -0
  21. {httpdbg-0.13.3 → httpdbg-0.13.4}/httpdbg/hooks/utils.py +0 -0
  22. {httpdbg-0.13.3 → httpdbg-0.13.4}/httpdbg/initiator.py +0 -0
  23. {httpdbg-0.13.3 → httpdbg-0.13.4}/httpdbg/mode_console.py +0 -0
  24. {httpdbg-0.13.3 → httpdbg-0.13.4}/httpdbg/mode_module.py +0 -0
  25. {httpdbg-0.13.3 → httpdbg-0.13.4}/httpdbg/mode_script.py +0 -0
  26. {httpdbg-0.13.3 → httpdbg-0.13.4}/httpdbg/records.py +0 -0
  27. {httpdbg-0.13.3 → httpdbg-0.13.4}/httpdbg/server.py +0 -0
  28. {httpdbg-0.13.3 → httpdbg-0.13.4}/httpdbg/utils.py +0 -0
  29. {httpdbg-0.13.3 → httpdbg-0.13.4}/httpdbg/webapp/__init__.py +0 -0
  30. {httpdbg-0.13.3 → httpdbg-0.13.4}/httpdbg/webapp/api.py +0 -0
  31. {httpdbg-0.13.3 → httpdbg-0.13.4}/httpdbg/webapp/app.py +0 -0
  32. {httpdbg-0.13.3 → httpdbg-0.13.4}/httpdbg/webapp/static/api.js +0 -0
  33. {httpdbg-0.13.3 → httpdbg-0.13.4}/httpdbg/webapp/static/column.css +0 -0
  34. {httpdbg-0.13.3 → httpdbg-0.13.4}/httpdbg/webapp/static/configuration.js +0 -0
  35. {httpdbg-0.13.3 → httpdbg-0.13.4}/httpdbg/webapp/static/detail.css +0 -0
  36. {httpdbg-0.13.3 → httpdbg-0.13.4}/httpdbg/webapp/static/favicon.ico +0 -0
  37. {httpdbg-0.13.3 → httpdbg-0.13.4}/httpdbg/webapp/static/filter.js +0 -0
  38. {httpdbg-0.13.3 → httpdbg-0.13.4}/httpdbg/webapp/static/icons/download-install-line-icon.svg +0 -0
  39. {httpdbg-0.13.3 → httpdbg-0.13.4}/httpdbg/webapp/static/icons/engine-gears-icon.svg +0 -0
  40. {httpdbg-0.13.3 → httpdbg-0.13.4}/httpdbg/webapp/static/icons/files-papers-outline-icon.svg +0 -0
  41. {httpdbg-0.13.3 → httpdbg-0.13.4}/httpdbg/webapp/static/icons/filter-outline-icon.svg +0 -0
  42. {httpdbg-0.13.3 → httpdbg-0.13.4}/httpdbg/webapp/static/icons/information-mark-circle-outline-icon.svg +0 -0
  43. {httpdbg-0.13.3 → httpdbg-0.13.4}/httpdbg/webapp/static/icons/math-multiplication-icon.svg +0 -0
  44. {httpdbg-0.13.3 → httpdbg-0.13.4}/httpdbg/webapp/static/icons/push-pin-green-icon.svg +0 -0
  45. {httpdbg-0.13.3 → httpdbg-0.13.4}/httpdbg/webapp/static/icons/question-mark-round-line-icon.svg +0 -0
  46. {httpdbg-0.13.3 → httpdbg-0.13.4}/httpdbg/webapp/static/icons/recycle-bin-line-icon.svg +0 -0
  47. {httpdbg-0.13.3 → httpdbg-0.13.4}/httpdbg/webapp/static/icons/wait-sandclock-icon.svg +0 -0
  48. {httpdbg-0.13.3 → httpdbg-0.13.4}/httpdbg/webapp/static/index.htm +0 -0
  49. {httpdbg-0.13.3 → httpdbg-0.13.4}/httpdbg/webapp/static/main.js +0 -0
  50. {httpdbg-0.13.3 → httpdbg-0.13.4}/httpdbg/webapp/static/mustache.min.js +0 -0
  51. {httpdbg-0.13.3 → httpdbg-0.13.4}/httpdbg/webapp/static/style.css +0 -0
  52. {httpdbg-0.13.3 → httpdbg-0.13.4}/httpdbg/webapp/static/table.css +0 -0
  53. {httpdbg-0.13.3 → httpdbg-0.13.4}/httpdbg.egg-info/SOURCES.txt +0 -0
  54. {httpdbg-0.13.3 → httpdbg-0.13.4}/httpdbg.egg-info/dependency_links.txt +0 -0
  55. {httpdbg-0.13.3 → httpdbg-0.13.4}/httpdbg.egg-info/entry_points.txt +0 -0
  56. {httpdbg-0.13.3 → httpdbg-0.13.4}/httpdbg.egg-info/top_level.txt +0 -0
  57. {httpdbg-0.13.3 → httpdbg-0.13.4}/pyproject.toml +0 -0
  58. {httpdbg-0.13.3 → httpdbg-0.13.4}/setup.cfg +0 -0
  59. {httpdbg-0.13.3 → httpdbg-0.13.4}/tests/test_api.py +0 -0
  60. {httpdbg-0.13.3 → httpdbg-0.13.4}/tests/test_cli.py +0 -0
  61. {httpdbg-0.13.3 → httpdbg-0.13.4}/tests/test_context_manager.py +0 -0
  62. {httpdbg-0.13.3 → httpdbg-0.13.4}/tests/test_hook_aiohttp.py +0 -0
  63. {httpdbg-0.13.3 → httpdbg-0.13.4}/tests/test_hook_httpx.py +0 -0
  64. {httpdbg-0.13.3 → httpdbg-0.13.4}/tests/test_hook_requests.py +0 -0
  65. {httpdbg-0.13.3 → httpdbg-0.13.4}/tests/test_hook_urllib3.py +0 -0
  66. {httpdbg-0.13.3 → httpdbg-0.13.4}/tests/test_initiator.py +0 -0
  67. {httpdbg-0.13.3 → httpdbg-0.13.4}/tests/test_mode_console.py +0 -0
  68. {httpdbg-0.13.3 → httpdbg-0.13.4}/tests/test_mode_module_pytest.py +0 -0
  69. {httpdbg-0.13.3 → httpdbg-0.13.4}/tests/test_mode_script.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: httpdbg
3
- Version: 0.13.3
3
+ Version: 0.13.4
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
@@ -3,6 +3,6 @@ from httpdbg.hooks.all import httpdbg
3
3
  from httpdbg.records import HTTPRecords
4
4
 
5
5
 
6
- VERSION = "0.13.3"
6
+ VERSION = "0.13.4"
7
7
 
8
8
  __all__ = ["httpdbg", "HTTPRecords"]
@@ -0,0 +1,53 @@
1
+ # -*- coding: utf-8 -*-
2
+ from typing import Tuple, Dict, Union
3
+
4
+
5
+ def parse_content_type(content_type: str) -> Tuple[str, Dict[str, str]]:
6
+ s = content_type.split(";")
7
+ media_type = s[0]
8
+ directives = {}
9
+ for directive in s[1:]:
10
+ sp = directive.split("=")
11
+ if len(sp) == 2:
12
+ directives[sp[0].strip()] = sp[1].strip()
13
+ return media_type, directives
14
+
15
+
16
+ def generate_preview(
17
+ raw_data: bytes, content_type: str, content_encoding: str
18
+ ) -> Dict[str, Union[str, bool]]:
19
+ body: Dict[str, Union[str, bool]] = {}
20
+
21
+ body["content_type"] = content_type
22
+
23
+ if content_encoding.lower() == "br":
24
+ try:
25
+ import brotli # type: ignore
26
+
27
+ raw_data = brotli.decompress(raw_data)
28
+ except Exception:
29
+ # if there is no brotli library available to decompress the data we keep the compressed data
30
+ pass
31
+ elif content_encoding.lower() == "gzip":
32
+ try:
33
+ import zlib
34
+
35
+ raw_data = zlib.decompressobj(16 + zlib.MAX_WBITS).decompress(raw_data)
36
+ except Exception:
37
+ pass
38
+ # TODO deflate
39
+
40
+ if "image/" in content_type.lower():
41
+ body["image"] = True
42
+ else:
43
+ if isinstance(raw_data, str):
44
+ body["text"] = raw_data
45
+ elif isinstance(raw_data, bytes):
46
+ try:
47
+ body["text"] = raw_data.decode(
48
+ parse_content_type(content_type)[1].get("charset", "utf-8")
49
+ )
50
+ except Exception:
51
+ pass
52
+
53
+ return body
@@ -127,6 +127,12 @@ function fill_content(request_id, name) {
127
127
 
128
128
  if (request.body && request.body.text) {
129
129
  request.body.raw_text = "global.requests['" + request_id + "'].data.request.body.text";
130
+
131
+ const parsed_text = parse_raw_text(request.body.text, request.body.content_type);
132
+ if (parsed_text) {
133
+ request.body.parsed = parsed_text;
134
+ }
135
+
130
136
  if (request.body.parsed) {
131
137
  request.body.parsed_text = "global.requests['" + request_id + "'].data.request.body.parsed";
132
138
  }
@@ -140,6 +146,12 @@ function fill_content(request_id, name) {
140
146
 
141
147
  if (response.body && response.body.text) {
142
148
  response.body.raw_text = "global.requests['" + request_id + "'].data.response.body.text";
149
+
150
+ const parsed_text = parse_raw_text(response.body.text, response.body.content_type);
151
+ if (parsed_text) {
152
+ response.body.parsed = parsed_text;
153
+ }
154
+
143
155
  if (response.body.parsed) {
144
156
  response.body.parsed_text = "global.requests['" + request_id + "'].data.response.body.parsed";
145
157
  }
@@ -314,7 +326,7 @@ function select_next_request() {
314
326
 
315
327
  if (request == undefined) {
316
328
  // maybe there are some requests under another initiator
317
- var next_initiator = document.querySelector(".active-row").parentElement.nextElementSibling;
329
+ var next_initiator = document.querySelector(".active-row").parentElement.nextElementSibling;
318
330
 
319
331
  if (next_initiator != undefined) {
320
332
  request = next_initiator.querySelector(".request");
@@ -324,4 +336,34 @@ function select_next_request() {
324
336
  if (request != undefined) {
325
337
  request.click();
326
338
  }
339
+ }
340
+
341
+ function parse_raw_text(raw_text, content_type) {
342
+ var parsed_text="";
343
+
344
+ if (content_type.toLowerCase().includes("json")) {
345
+ try {
346
+ parsed_text = JSON.stringify(JSON.parse(raw_text), null, " ");
347
+ } catch {
348
+ return;
349
+ }
350
+ } else if (content_type.toLowerCase().includes("x-www-form-urlencoded")) {
351
+ try {
352
+ var params = new URLSearchParams(raw_text);
353
+
354
+ for (const key of params.keys()) {
355
+ params.getAll(key).forEach((value) => {
356
+ try {
357
+ parsed_text += key + ": " + JSON.stringify(JSON.parse(value), null, " ") + "\n";
358
+ } catch {
359
+ parsed_text += key + ": " + value + "\n";
360
+ }
361
+ });
362
+ }
363
+ } catch {
364
+ return;
365
+ }
366
+ }
367
+
368
+ return parsed_text;
327
369
  }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: httpdbg
3
- Version: 0.13.3
3
+ Version: 0.13.4
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
@@ -0,0 +1,56 @@
1
+ # -*- coding: utf-8 -*-
2
+ import pytest
3
+
4
+ from httpdbg.preview import generate_preview
5
+
6
+
7
+ @pytest.mark.preview
8
+ def test_preview_unknown_type_text():
9
+ body = generate_preview("a text", "unknown/type", "")
10
+ assert body == {"text": "a text", "content_type": "unknown/type"}
11
+
12
+
13
+ @pytest.mark.preview
14
+ def test_preview_unknown_type_bytes():
15
+ body = generate_preview(b"a text", "unknown/type", "")
16
+ assert body == {"text": "a text", "content_type": "unknown/type"}
17
+
18
+
19
+ @pytest.mark.preview
20
+ @pytest.mark.parametrize("content_type", ["text/json", "application/json", "text", ""])
21
+ def test_preview_text(content_type):
22
+ raw_data = '{"a": "1", "b": {"c": 2} }'
23
+ body = generate_preview(raw_data, content_type, "")
24
+
25
+ assert body == {
26
+ "text": raw_data,
27
+ "content_type": content_type,
28
+ }
29
+
30
+
31
+ @pytest.mark.preview
32
+ def test_preview_image():
33
+ body = generate_preview(None, "image/png", "")
34
+
35
+ assert body == {
36
+ "image": True,
37
+ "content_type": "image/png",
38
+ }
39
+
40
+
41
+ def test_preview_application_no_content_type_text():
42
+ body = generate_preview("just text", "", "")
43
+
44
+ assert body == {
45
+ "text": "just text",
46
+ "content_type": "",
47
+ }
48
+
49
+
50
+ def test_preview_application_no_content_type_bytes():
51
+ body = generate_preview("just éà bytes".encode("utf-8"), "", "")
52
+
53
+ assert body == {
54
+ "text": "just éà bytes",
55
+ "content_type": "",
56
+ }
@@ -1,88 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- import json
3
- import urllib
4
- from typing import Tuple, Dict, Union
5
- from xml.dom import minidom
6
-
7
-
8
- def parse_content_type(content_type: str) -> Tuple[str, Dict[str, str]]:
9
- s = content_type.split(";")
10
- media_type = s[0]
11
- directives = {}
12
- for directive in s[1:]:
13
- sp = directive.split("=")
14
- if len(sp) == 2:
15
- directives[sp[0].strip()] = sp[1].strip()
16
- return media_type, directives
17
-
18
-
19
- def generate_preview(
20
- raw_data: bytes, content_type: str, content_encoding: str
21
- ) -> Dict[str, Union[str, bool]]:
22
- body: Dict[str, Union[str, bool]] = {}
23
-
24
- if content_encoding.lower() == "br":
25
- try:
26
- import brotli # type: ignore
27
-
28
- raw_data = brotli.decompress(raw_data)
29
- except Exception:
30
- # if there is no brotli library available to decompress the data we keep the compressed data
31
- pass
32
- elif content_encoding.lower() == "gzip":
33
- try:
34
- import zlib
35
-
36
- raw_data = zlib.decompressobj(16 + zlib.MAX_WBITS).decompress(raw_data)
37
- except Exception:
38
- pass
39
- # TODO deflate
40
-
41
- if "image/" in content_type.lower():
42
- body["image"] = True
43
- else:
44
- if isinstance(raw_data, str):
45
- body["text"] = raw_data
46
- elif isinstance(raw_data, bytes):
47
- try:
48
- body["text"] = raw_data.decode(
49
- parse_content_type(content_type)[1].get("charset", "utf-8")
50
- )
51
- except Exception:
52
- pass
53
-
54
- if body.get("text"):
55
- if isinstance(body["text"], str):
56
- # json
57
- try:
58
- body["parsed"] = json.dumps(
59
- json.loads(body["text"]), sort_keys=True, indent=4
60
- )
61
- except Exception:
62
- # xml, ...
63
- try:
64
- lines = minidom.parseString(body["text"]).toprettyxml(
65
- indent=" "
66
- )
67
- body["parsed"] = "\n".join(
68
- [line for line in lines.split("\n") if line.strip() != ""]
69
- )
70
- except Exception:
71
- # query string
72
- try:
73
- if (
74
- parse_content_type(content_type)[0]
75
- == "application/x-www-form-urlencoded"
76
- ):
77
- qs = []
78
- for key, value in urllib.parse.parse_qsl(
79
- body["text"], strict_parsing=True
80
- ):
81
- if value:
82
- qs.append(f"{key}={value}")
83
- if qs:
84
- body["parsed"] = "\n\n".join(qs)
85
- except Exception:
86
- pass
87
-
88
- return body
@@ -1,96 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- import pytest
3
-
4
- from httpdbg.preview import generate_preview
5
-
6
-
7
- @pytest.mark.preview
8
- def test_preview_unknown_type_text():
9
- body = generate_preview("a text", "unknown/type", "")
10
- assert body == {"text": "a text"}
11
-
12
-
13
- @pytest.mark.preview
14
- def test_preview_unknown_type_bytes():
15
- body = generate_preview(b"a text", "unknown/type", "")
16
- assert body == {"text": "a text"}
17
-
18
-
19
- @pytest.mark.preview
20
- @pytest.mark.parametrize(
21
- "content_type",
22
- ["text/html", "application/xhtml+xml", "application/xml", "text", ""],
23
- )
24
- def test_preview_xml(content_type):
25
- raw_data = "<html><head><title>:-)</title></head><body>empty</body></html>"
26
- body = generate_preview(raw_data, content_type, "")
27
- parsed = '<?xml version="1.0" ?>\n<html>\n <head>\n <title>:-)</title>\n </head>\n <body>empty</body>\n</html>'
28
-
29
- assert body == {
30
- "text": raw_data,
31
- "parsed": parsed,
32
- }
33
-
34
-
35
- @pytest.mark.preview
36
- @pytest.mark.parametrize("content_type", ["text/json", "application/json", "text", ""])
37
- def test_preview_json(content_type):
38
- raw_data = '{"a": "1", "b": {"c": 2} }'
39
- body = generate_preview(raw_data, "application/json", "")
40
- parsed = '{\n "a": "1",\n "b": {\n "c": 2\n }\n}'
41
-
42
- assert body == {
43
- "text": raw_data,
44
- "parsed": parsed,
45
- }
46
-
47
-
48
- @pytest.mark.preview
49
- def test_preview_query_string():
50
- raw_data = "a=1&b=2&c=%7B%27er%27%3A+43434%7D"
51
- body = generate_preview(
52
- raw_data,
53
- "application/x-www-form-urlencoded",
54
- "",
55
- )
56
- parsed = "a=1\n\nb=2\n\nc={'er': 43434}"
57
-
58
- assert body == {
59
- "text": raw_data,
60
- "parsed": parsed,
61
- }
62
-
63
-
64
- @pytest.mark.preview
65
- def test_preview_query_string_not_urlencoded_mimetype():
66
- raw_data = "a=1&b=2&c=%7B%27er%27%3A+43434%7D"
67
- body = generate_preview(raw_data, "test/plain", "")
68
-
69
- assert body == {
70
- "text": raw_data,
71
- }
72
-
73
-
74
- @pytest.mark.preview
75
- def test_preview_image():
76
- body = generate_preview(None, "image/png", "")
77
-
78
- assert body == {
79
- "image": True,
80
- }
81
-
82
-
83
- def test_preview_application_no_content_type_text():
84
- body = generate_preview("just text", "", "")
85
-
86
- assert body == {
87
- "text": "just text",
88
- }
89
-
90
-
91
- def test_preview_application_no_content_type_bytes():
92
- body = generate_preview("just éà bytes".encode("utf-8"), "", "")
93
-
94
- assert body == {
95
- "text": "just éà bytes",
96
- }
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