httpdbg 0.12.0__tar.gz → 0.13.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.
Files changed (68) hide show
  1. {httpdbg-0.12.0 → httpdbg-0.13.0}/PKG-INFO +3 -1
  2. {httpdbg-0.12.0 → httpdbg-0.13.0}/README.md +2 -0
  3. {httpdbg-0.12.0 → httpdbg-0.13.0}/httpdbg/__init__.py +1 -1
  4. {httpdbg-0.12.0 → httpdbg-0.13.0}/httpdbg/hooks/urllib3.py +22 -10
  5. {httpdbg-0.12.0 → httpdbg-0.13.0}/httpdbg/initiator.py +1 -1
  6. {httpdbg-0.12.0 → httpdbg-0.13.0}/httpdbg/utils.py +2 -2
  7. {httpdbg-0.12.0 → httpdbg-0.13.0}/httpdbg/webapp/app.py +1 -1
  8. {httpdbg-0.12.0 → httpdbg-0.13.0}/httpdbg/webapp/static/api.js +9 -4
  9. httpdbg-0.13.0/httpdbg/webapp/static/icons/question-mark-round-line-icon.svg +1 -0
  10. {httpdbg-0.12.0 → httpdbg-0.13.0}/httpdbg/webapp/static/index.htm +61 -21
  11. httpdbg-0.13.0/httpdbg/webapp/static/main.js +79 -0
  12. {httpdbg-0.12.0 → httpdbg-0.13.0}/httpdbg/webapp/static/render.js +42 -0
  13. {httpdbg-0.12.0 → httpdbg-0.13.0}/httpdbg/webapp/static/style.css +23 -0
  14. {httpdbg-0.12.0 → httpdbg-0.13.0}/httpdbg.egg-info/PKG-INFO +3 -1
  15. {httpdbg-0.12.0 → httpdbg-0.13.0}/httpdbg.egg-info/SOURCES.txt +1 -0
  16. {httpdbg-0.12.0 → httpdbg-0.13.0}/tests/test_hook_httpx.py +0 -23
  17. {httpdbg-0.12.0 → httpdbg-0.13.0}/tests/test_hook_requests.py +0 -25
  18. {httpdbg-0.12.0 → httpdbg-0.13.0}/tests/test_hook_urllib3.py +58 -1
  19. httpdbg-0.12.0/httpdbg/webapp/static/main.js +0 -22
  20. {httpdbg-0.12.0 → httpdbg-0.13.0}/LICENSE +0 -0
  21. {httpdbg-0.12.0 → httpdbg-0.13.0}/httpdbg/__main__.py +0 -0
  22. {httpdbg-0.12.0 → httpdbg-0.13.0}/httpdbg/args.py +0 -0
  23. {httpdbg-0.12.0 → httpdbg-0.13.0}/httpdbg/hooks/__init__.py +0 -0
  24. {httpdbg-0.12.0 → httpdbg-0.13.0}/httpdbg/hooks/aiohttp.py +0 -0
  25. {httpdbg-0.12.0 → httpdbg-0.13.0}/httpdbg/hooks/all.py +0 -0
  26. {httpdbg-0.12.0 → httpdbg-0.13.0}/httpdbg/hooks/generic.py +0 -0
  27. {httpdbg-0.12.0 → httpdbg-0.13.0}/httpdbg/hooks/httpx.py +0 -0
  28. {httpdbg-0.12.0 → httpdbg-0.13.0}/httpdbg/hooks/requests.py +0 -0
  29. {httpdbg-0.12.0 → httpdbg-0.13.0}/httpdbg/hooks/socket.py +0 -0
  30. {httpdbg-0.12.0 → httpdbg-0.13.0}/httpdbg/hooks/utils.py +0 -0
  31. {httpdbg-0.12.0 → httpdbg-0.13.0}/httpdbg/mode_console.py +0 -0
  32. {httpdbg-0.12.0 → httpdbg-0.13.0}/httpdbg/mode_module.py +0 -0
  33. {httpdbg-0.12.0 → httpdbg-0.13.0}/httpdbg/mode_script.py +0 -0
  34. {httpdbg-0.12.0 → httpdbg-0.13.0}/httpdbg/preview.py +0 -0
  35. {httpdbg-0.12.0 → httpdbg-0.13.0}/httpdbg/records.py +0 -0
  36. {httpdbg-0.12.0 → httpdbg-0.13.0}/httpdbg/server.py +0 -0
  37. {httpdbg-0.12.0 → httpdbg-0.13.0}/httpdbg/webapp/__init__.py +0 -0
  38. {httpdbg-0.12.0 → httpdbg-0.13.0}/httpdbg/webapp/api.py +0 -0
  39. {httpdbg-0.12.0 → httpdbg-0.13.0}/httpdbg/webapp/static/column.css +0 -0
  40. {httpdbg-0.12.0 → httpdbg-0.13.0}/httpdbg/webapp/static/configuration.js +0 -0
  41. {httpdbg-0.12.0 → httpdbg-0.13.0}/httpdbg/webapp/static/detail.css +0 -0
  42. {httpdbg-0.12.0 → httpdbg-0.13.0}/httpdbg/webapp/static/favicon.ico +0 -0
  43. {httpdbg-0.12.0 → httpdbg-0.13.0}/httpdbg/webapp/static/filter.js +0 -0
  44. {httpdbg-0.12.0 → httpdbg-0.13.0}/httpdbg/webapp/static/icons/download-install-line-icon.svg +0 -0
  45. {httpdbg-0.12.0 → httpdbg-0.13.0}/httpdbg/webapp/static/icons/engine-gears-icon.svg +0 -0
  46. {httpdbg-0.12.0 → httpdbg-0.13.0}/httpdbg/webapp/static/icons/files-papers-outline-icon.svg +0 -0
  47. {httpdbg-0.12.0 → httpdbg-0.13.0}/httpdbg/webapp/static/icons/filter-outline-icon.svg +0 -0
  48. {httpdbg-0.12.0 → httpdbg-0.13.0}/httpdbg/webapp/static/icons/information-mark-circle-outline-icon.svg +0 -0
  49. {httpdbg-0.12.0 → httpdbg-0.13.0}/httpdbg/webapp/static/icons/math-multiplication-icon.svg +0 -0
  50. {httpdbg-0.12.0 → httpdbg-0.13.0}/httpdbg/webapp/static/icons/push-pin-green-icon.svg +0 -0
  51. {httpdbg-0.12.0 → httpdbg-0.13.0}/httpdbg/webapp/static/icons/recycle-bin-line-icon.svg +0 -0
  52. {httpdbg-0.12.0 → httpdbg-0.13.0}/httpdbg/webapp/static/icons/wait-sandclock-icon.svg +0 -0
  53. {httpdbg-0.12.0 → httpdbg-0.13.0}/httpdbg/webapp/static/mustache.min.js +0 -0
  54. {httpdbg-0.12.0 → httpdbg-0.13.0}/httpdbg/webapp/static/table.css +0 -0
  55. {httpdbg-0.12.0 → httpdbg-0.13.0}/httpdbg.egg-info/dependency_links.txt +0 -0
  56. {httpdbg-0.12.0 → httpdbg-0.13.0}/httpdbg.egg-info/entry_points.txt +0 -0
  57. {httpdbg-0.12.0 → httpdbg-0.13.0}/httpdbg.egg-info/top_level.txt +0 -0
  58. {httpdbg-0.12.0 → httpdbg-0.13.0}/pyproject.toml +0 -0
  59. {httpdbg-0.12.0 → httpdbg-0.13.0}/setup.cfg +0 -0
  60. {httpdbg-0.12.0 → httpdbg-0.13.0}/tests/test_api.py +0 -0
  61. {httpdbg-0.12.0 → httpdbg-0.13.0}/tests/test_cli.py +0 -0
  62. {httpdbg-0.12.0 → httpdbg-0.13.0}/tests/test_context_manager.py +0 -0
  63. {httpdbg-0.12.0 → httpdbg-0.13.0}/tests/test_hook_aiohttp.py +0 -0
  64. {httpdbg-0.12.0 → httpdbg-0.13.0}/tests/test_initiator.py +0 -0
  65. {httpdbg-0.12.0 → httpdbg-0.13.0}/tests/test_mode_console.py +0 -0
  66. {httpdbg-0.12.0 → httpdbg-0.13.0}/tests/test_mode_module_pytest.py +0 -0
  67. {httpdbg-0.12.0 → httpdbg-0.13.0}/tests/test_mode_script.py +0 -0
  68. {httpdbg-0.12.0 → httpdbg-0.13.0}/tests/test_preview.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: httpdbg
3
- Version: 0.12.0
3
+ Version: 0.13.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
@@ -22,6 +22,8 @@ License-File: LICENSE
22
22
 
23
23
  A very simple tool to debug HTTP(S) client requests.
24
24
 
25
+ ![](ui.png)
26
+
25
27
  ## installation
26
28
 
27
29
  ```
@@ -2,6 +2,8 @@
2
2
 
3
3
  A very simple tool to debug HTTP(S) client requests.
4
4
 
5
+ ![](ui.png)
6
+
5
7
  ## installation
6
8
 
7
9
  ```
@@ -3,6 +3,6 @@ from httpdbg.hooks.all import httpdbg
3
3
  from httpdbg.records import HTTPRecords
4
4
 
5
5
 
6
- VERSION = "0.12.0"
6
+ VERSION = "0.13.0"
7
7
 
8
8
  __all__ = ["httpdbg", "HTTPRecords"]
@@ -43,12 +43,18 @@ def hook_urllib3(records: HTTPRecords) -> Generator[None, None, None]:
43
43
  try:
44
44
  import urllib3
45
45
 
46
- urllib3.PoolManager.request = decorate(
47
- records, urllib3.PoolManager.request, set_hook_for_urllib3
48
- )
49
- urllib3.HTTPConnectionPool.request = decorate(
50
- records, urllib3.HTTPConnectionPool.request, set_hook_for_urllib3
51
- )
46
+ # v1
47
+ if hasattr(urllib3, "PoolManager"):
48
+ urllib3.PoolManager.request = decorate(
49
+ records, urllib3.PoolManager.request, set_hook_for_urllib3
50
+ )
51
+ if hasattr(urllib3, "HTTPConnectionPool"):
52
+ urllib3.HTTPConnectionPool.request = decorate(
53
+ records, urllib3.HTTPConnectionPool.request, set_hook_for_urllib3
54
+ )
55
+ # v2
56
+ if hasattr(urllib3, "request"):
57
+ urllib3.request = decorate(records, urllib3.request, set_hook_for_urllib3)
52
58
 
53
59
  hooks = True
54
60
  except ImportError:
@@ -57,7 +63,13 @@ def hook_urllib3(records: HTTPRecords) -> Generator[None, None, None]:
57
63
  yield
58
64
 
59
65
  if hooks:
60
- urllib3.PoolManager.request = undecorate(urllib3.PoolManager.request)
61
- urllib3.HTTPConnectionPool.request = undecorate(
62
- urllib3.HTTPConnectionPool.request
63
- )
66
+ # v1
67
+ if hasattr(urllib3, "PoolManager"):
68
+ urllib3.PoolManager.request = undecorate(urllib3.PoolManager.request)
69
+ if hasattr(urllib3, "HTTPConnectionPool"):
70
+ urllib3.HTTPConnectionPool.request = undecorate(
71
+ urllib3.HTTPConnectionPool.request
72
+ )
73
+ # v2
74
+ if hasattr(urllib3, "request"):
75
+ urllib3.request = undecorate(urllib3.request)
@@ -29,7 +29,7 @@ class Initiator(object):
29
29
  self.stack = stack
30
30
 
31
31
  def __eq__(self, other) -> bool:
32
- if type(other) == Initiator:
32
+ if type(other) is Initiator:
33
33
  return (
34
34
  self.short_label == other.short_label
35
35
  and self.long_label == other.long_label
@@ -50,7 +50,7 @@ class HTTPDBGCookie(object):
50
50
  return {"name": self.name, "value": self.value, "attributes": self.attributes}
51
51
 
52
52
  def __eq__(self, other) -> bool:
53
- if type(other) == HTTPDBGCookie:
53
+ if type(other) is HTTPDBGCookie:
54
54
  return (
55
55
  (self.name == other.name)
56
56
  and (self.value == other.value)
@@ -78,7 +78,7 @@ class HTTPDBGHeader(object):
78
78
  return {"name": self.name, "value": self.value}
79
79
 
80
80
  def __eq__(self, other) -> bool:
81
- if type(other) == HTTPDBGHeader:
81
+ if type(other) is HTTPDBGHeader:
82
82
  return (self.name == other.name) and (self.value == other.value)
83
83
  else:
84
84
  return False
@@ -85,7 +85,7 @@ class HttpbgHTTPRequestHandler(BaseHTTPRequestHandler):
85
85
  os.path.realpath(base_path) + "/static/icons/*.svg"
86
86
  ):
87
87
  icon_path = icon.replace(os.path.realpath(base_path) + "/", "")
88
- icons += f"<link rel='preload' href='{icon_path}-+-$**HTTPDBG_VERSION**$' as='image' type='image/svg+xml' />\n"
88
+ icons += f" <link rel='preload' href='{icon_path}-+-$**HTTPDBG_VERSION**$' as='image' type='image/svg+xml' />\n"
89
89
 
90
90
  filecontent = filecontent.replace(
91
91
  b"$**PRELOAD_ICONS**$", icons.encode("utf-8")
@@ -3,7 +3,8 @@
3
3
  const global = {
4
4
  "k7": null,
5
5
  "requests": {},
6
- "connected": false
6
+ "connected": false,
7
+ "tabindex": 100
7
8
  }
8
9
 
9
10
  function save_request(request_id, request) {
@@ -12,16 +13,20 @@ function save_request(request_id, request) {
12
13
  if (request.pin == undefined) {
13
14
  request.pin = "";
14
15
  }
16
+ if (request.tabindex == undefined) {
17
+ global.tabindex += 1;
18
+ request.tabindex = global.tabindex;
19
+ }
15
20
  global.requests[request_id] = request;
16
21
  if (global.requests[request_id].in_progress) {
17
- global.requests[request_id].status_code_view = '<img class="icon" src="static/icons/wait-sandclock-icon.svg-+-$**HTTPDBG_VERSION**$" />';
22
+ global.requests[request_id].status_code_view = '<img class="icon" src="static/icons/wait-sandclock-icon.svg-+-$**HTTPDBG_VERSION**$" alt="loading"/>';
18
23
  } else {
19
24
  switch (global.requests[request_id].status_code) {
20
25
  case 0:
21
- global.requests[request_id].status_code_view = '<img class="icon" src="static/icons/wait-sandclock-icon.svg-+-$**HTTPDBG_VERSION**$/" />';
26
+ global.requests[request_id].status_code_view = '<img class="icon" src="static/icons/wait-sandclock-icon.svg-+-$**HTTPDBG_VERSION**$/" alt="loading"/>';
22
27
  break;
23
28
  case -1:
24
- global.requests[request_id].status_code_view = '<img class="icon" src="static/icons/math-multiplication-icon.svg-+-$**HTTPDBG_VERSION**$/" />';
29
+ global.requests[request_id].status_code_view = '<img class="icon" src="static/icons/math-multiplication-icon.svg-+-$**HTTPDBG_VERSION**$/" alt="load failed"/>';
25
30
  break;
26
31
  default:
27
32
  global.requests[request_id].status_code_view = global.requests[request_id].status_code;
@@ -0,0 +1 @@
1
+ <?xml version="1.0" encoding="utf-8"?><svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 122.88 122.88" style="enable-background:new 0 0 122.88 122.88" xml:space="preserve"><g><path d="M109.72,61.44v-0.01h0.02c0-13.31-5.42-25.38-14.17-34.13c-8.75-8.74-20.81-14.16-34.1-14.16v0.02h-0.03h-0.01v-0.02 c-13.3,0-25.38,5.42-34.13,14.17c-8.74,8.75-14.16,20.81-14.16,34.11h0.02v0.03v0.01h-0.02c0,8.88,2.41,17.22,6.59,24.36 c8.38,14.31,23.92,23.92,41.68,23.93v-0.02h0.03h0.01v0.02c13.31,0,25.38-5.42,34.13-14.17c1.44-1.44,2.78-2.95,4-4.52 C105.93,82.88,109.72,72.6,109.72,61.44L109.72,61.44z M68.38,73.86H51.86v-1.65c0-2.8,0.3-5.09,0.94-6.85 c0.64-1.77,1.59-3.35,2.85-4.81c1.27-1.45,4.11-4,8.51-7.66c2.34-1.92,3.52-3.67,3.52-5.25c0-1.61-0.48-2.84-1.41-3.73 c-0.94-0.88-2.37-1.33-4.3-1.33c-2.07,0-3.76,0.69-5.1,2.04c-1.35,1.36-2.21,3.75-2.58,7.14l-16.89-2.1 c0.58-6.2,2.84-11.2,6.76-14.98C48.1,30.89,54.12,29,62.24,29c6.32,0,11.43,1.33,15.32,3.95c5.29,3.57,7.92,8.33,7.92,14.26 c0,2.48-0.69,4.86-2.04,7.15c-1.36,2.3-4.15,5.09-8.36,8.39c-2.92,2.34-4.77,4.19-5.54,5.6C68.77,69.76,68.38,71.6,68.38,73.86 L68.38,73.86z M51.27,78.26h17.72v15.61H51.27V78.26L51.27,78.26z M122.85,61.43h0.03v0.01c0,14.19-4.82,27.27-12.92,37.68 c-1.59,2.04-3.29,3.97-5.08,5.76c-11.1,11.1-26.45,17.97-43.43,17.98v0.03h-0.01h-0.03v-0.03c-22.59-0.01-42.34-12.22-52.99-30.4 c-5.33-9.1-8.39-19.7-8.39-31H0v-0.01v-0.03h0.03C0.03,44.44,6.9,29.09,18,18C29.09,6.9,44.45,0.03,61.43,0.03V0h0.01h0.03v0.03 c16.97,0.01,32.32,6.87,43.41,17.97C115.98,29.09,122.85,44.45,122.85,61.43L122.85,61.43z"/></g></svg>
@@ -29,16 +29,22 @@
29
29
  <header>
30
30
  <nav id="navbar">
31
31
  <div class="menu-left">
32
- <button id="trash" onclick="clean(true)">
33
- <img class="icon" src="static/icons/recycle-bin-line-icon.svg-+-$**HTTPDBG_VERSION**$" />
32
+ <button id="trash" type="button" onclick="clean(true)">
33
+ <img class="icon" src="static/icons/recycle-bin-line-icon.svg-+-$**HTTPDBG_VERSION**$"
34
+ alt="clean the requests list" />
34
35
  </button>
35
36
  <input type="checkbox" id="onlynew" name="cleannew" checked>
36
- <label for="clean">force clean for new session</label>
37
+ <label for="onlynew">force clean for new session</label>
37
38
  </div>
38
39
  <div class="menu-right">
39
40
  httpdbg
40
- <button id="config" onclick="config()">
41
- <img class="icon" src="static/icons/engine-gears-icon.svg-+-$**HTTPDBG_VERSION**$" />
41
+ <button onclick="config()" title="open configuration">
42
+ <img class="icon" src="static/icons/engine-gears-icon.svg-+-$**HTTPDBG_VERSION**$"
43
+ alt="open configuration" />
44
+ </button>
45
+ <button onclick="help()" title="open help">
46
+ <img class="icon" src="static/icons/question-mark-round-line-icon.svg-+-$**HTTPDBG_VERSION**$"
47
+ alt="open help" />
42
48
  </button>
43
49
  </div>
44
50
  </nav>
@@ -65,6 +71,37 @@
65
71
  <br>
66
72
  </div>
67
73
 
74
+ <div id="help">
75
+ <h4>Help</h4>
76
+
77
+ <h5>Shorcuts</h5>
78
+ <ul>
79
+ <li>Press <kbd>O</kbd> <kbd>C</kbd> to open the configuration panel.</li>
80
+ <li>Press <kbd>O</kbd> <kbd>H</kbd> to open this help.</li>
81
+ </ul>
82
+
83
+ <h5>List of requests</h5>
84
+ Use <kbd>Tab</kbd> to browser the list of requests.
85
+ <ul>
86
+ <li>Press <kbd>Enter</kbd> or <kbd>S</kbd> to select a request.</li>
87
+ <li>Press <kbd>C</kbd> to compare this request to the select one.</li>
88
+ </ul>
89
+
90
+ <h5>Request panel</h5>
91
+ <ul>
92
+ <li>Press <kbd>R</kbd> <kbd>H</kbd> to open the headers tab.</li>
93
+ <li>Press <kbd>R</kbd> <kbd>C</kbd> to open the cookies tab.</li>
94
+ <li>Press <kbd>R</kbd> <kbd>P</kbd> to open the request tab.</li>
95
+ <li>Press <kbd>R</kbd> <kbd>T</kbd> to open the response tab.</li>
96
+ <li>Press <kbd>R</kbd> <kbd>E</kbd> to open the exception tab.</li>
97
+ <li>Press <kbd>R</kbd> <kbd>S</kbd> to open the stack tab.</li>
98
+ </ul>
99
+
100
+ <h4>About</h4>
101
+ httpdbg v$**HTTPDBG_VERSION**$ - <a href="https://pypi.org/project/httpdbg/">PyPI</a> <a
102
+ href="https://github.com/cle-b/httpdbg">Source code</a>
103
+ </div>
104
+
68
105
  <div class="grid-container">
69
106
 
70
107
  <div class="grid-column grid-column-left">
@@ -78,9 +115,13 @@
78
115
  <th class="method" title="method"></th>
79
116
  <th class="url">
80
117
  url
81
- <img class="icon" src="static/icons/filter-outline-icon.svg-+-$**HTTPDBG_VERSION**$" />
82
- <input type="text" value="" id="filter-url" onkeyup="update_filter_url()"
83
- oncut="update_filter_url()" onpaste="update_filter_url()"
118
+ <img class="icon" src="static/icons/filter-outline-icon.svg-+-$**HTTPDBG_VERSION**$"
119
+ alt="filter for url" />
120
+ <label for="filter-url" class="visuallyhidden">
121
+ filter for url
122
+ </label>
123
+ <input type="text" value="" id="filter-url" title="filter for url"
124
+ onkeyup="update_filter_url()" oncut="update_filter_url()" onpaste="update_filter_url()"
84
125
  onchange="update_filter_url()">
85
126
  </th>
86
127
  </tr>
@@ -100,11 +141,14 @@
100
141
  <tr id="request-{{id}}"
101
142
  data-filter-url="{{url}}"
102
143
  onclick="select_request(arguments[0], '{{id}}')"
144
+ onkeypress="shortcut_request(event, '{{id}}')"
103
145
  class="request"
104
- data-qa-request="{{id}}">
146
+ data-qa-request="{{id}}"
147
+ tabindex="{{tabindex}}">
105
148
  <td class="pin" onclick="event.stopPropagation();">
149
+ <label for="pin{{id}}" class="visuallyhidden"pin request</label>
106
150
  <input class="pin-checkbox" type="checkbox" id="pin{{id}}" onclick="pin_request('{{id}}',this.checked)" {{pin}}>
107
- <label class="pin-icon" for="pin{{id}}"><img class="icon" src="static/icons/push-pin-green-icon.svg-+-$**HTTPDBG_VERSION**$"/></label>
151
+ <label class="pin-icon" for="pin{{id}}"><img class="icon" src="static/icons/push-pin-green-icon.svg-+-$**HTTPDBG_VERSION**$" alt="click to pin"/></label>
108
152
  </td>
109
153
  <td class="status"><span title="{{reason}}" data-qa-request-status>{{{status_code_view}}}</span></td>
110
154
  <td class="method"><span title="{{verb}}" data-qa-request-method>{{verb}}</span></td>
@@ -125,17 +169,16 @@
125
169
  <th>
126
170
  <div class="tab">
127
171
  <button id="btn-tab-headers" class="tablinks"
128
- onclick="opentab(event.currentTarget, 'tabHeaders')">headers</button>
172
+ onclick="opentab_headers()">headers</button>
129
173
  <button id="btn-tab-cookies" class="tablinks"
130
- onclick="opentab(event.currentTarget, 'tabCookies')">cookies</button>
174
+ onclick="opentab_cookies()">cookies</button>
131
175
  <button id="btn-tab-request" class="tablinks"
132
- onclick="opentab(event.currentTarget, 'tabRequest')">request</button>
176
+ onclick="opentab_request()">request</button>
133
177
  <button id="btn-tab-response" class="tablinks"
134
- onclick="opentab(event.currentTarget, 'tabResponse')">response</button>
178
+ onclick="opentab_response()">response</button>
135
179
  <button id="btn-tab-exception" class="tablinks"
136
- onclick="opentab(event.currentTarget, 'tabException')">exception</button>
137
- <button id="btn-tab-stack" class="tablinks"
138
- onclick="opentab(event.currentTarget, 'tabStack')">stack</button>
180
+ onclick="opentab_exception()">exception</button>
181
+ <button id="btn-tab-stack" class="tablinks" onclick="opentab_stack()">stack</button>
139
182
  </div>
140
183
  </th>
141
184
  </tr>
@@ -225,9 +268,6 @@
225
268
  <script id="template_body" type="x-tmpl-mustache">
226
269
  {{^body}}
227
270
  -- no content --
228
- {{#stream}}
229
- (because stream mode on)
230
- {{/stream}}
231
271
  {{/body}}
232
272
 
233
273
  {{#body}}
@@ -307,7 +347,7 @@
307
347
  update_filter_url();
308
348
  pol_new_data();
309
349
  enable_refresh();
310
- opentab(document.getElementById("btn-tab-headers"), "tabHeaders");
350
+ opentab_headers();
311
351
  </script>
312
352
  </body>
313
353
 
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+
3
+ let httpdbgApp = {
4
+ "config": {
5
+ "hide_initiator": {
6
+ "checkbox": "cinitiator",
7
+ "param": "hi",
8
+ "css": ".initiator {display: none;}",
9
+ "value": false
10
+ },
11
+ "hide_netloc": {
12
+ "checkbox": "curl",
13
+ "param": "hn",
14
+ "css": ".netloc {display: none;}",
15
+ "value": false
16
+ }
17
+ }
18
+ }
19
+
20
+ function wait_for(ms) {
21
+ return new Promise(resolve => setTimeout(resolve, ms));
22
+ }
23
+
24
+ // use keys sequences as shortcut
25
+ var keys_history= "";
26
+ window.addEventListener('keydown', (event) => {
27
+
28
+ if (document.activeElement) {
29
+ if (document.activeElement.tagName.toLowerCase() === "input") {
30
+ if (document.activeElement.getAttribute("type").toLowerCase() === "text") {
31
+ return;
32
+ }
33
+ }
34
+ }
35
+
36
+ keys_history += event.key.toLowerCase();
37
+
38
+ if (keys_history.endsWith("rh")) {
39
+ opentab_headers();
40
+ }
41
+
42
+ if (keys_history.endsWith("rc")) {
43
+ opentab_cookies();
44
+ }
45
+
46
+ if (keys_history.endsWith("rp")) {
47
+ opentab_request();
48
+ }
49
+
50
+ if (keys_history.endsWith("rt")) {
51
+ opentab_response();
52
+ }
53
+
54
+ if (keys_history.endsWith("re")) {
55
+ opentab_exception();
56
+ }
57
+
58
+ if (keys_history.endsWith("rs")) {
59
+ opentab_stack();
60
+ }
61
+
62
+ if (keys_history.endsWith("oc")) {
63
+ config();
64
+ }
65
+
66
+ if (keys_history.endsWith("oh")) {
67
+ help();
68
+ }
69
+
70
+ keys_history = keys_history.substr(-8, 8)
71
+ });
72
+
73
+ function help() {
74
+ if (document.getElementById("help").style.display == "block") {
75
+ document.getElementById("help").style.display = "none";
76
+ } else {
77
+ document.getElementById("help").style.display = "block";
78
+ }
79
+ }
@@ -64,6 +64,23 @@ function unselect_request(request_id) {
64
64
  }
65
65
  }
66
66
 
67
+ function shortcut_request(event, request_id) {
68
+ var key_pressed = event.key.toLowerCase()
69
+ // select the request
70
+ if ((key_pressed === "s") || (key_pressed === "enter")) {
71
+ show_request(request_id);
72
+ }
73
+ // compare the request to the one already selected
74
+ if (key_pressed === "c") {
75
+ if ((document.getElementsByClassName("active-row").length > 0)) {
76
+ compare_to_request(request_id);
77
+ } else {
78
+ // if no request is already selected, just select the new one
79
+ show_request(request_id);
80
+ }
81
+ }
82
+ }
83
+
67
84
  function remove_class(classname) {
68
85
  var elts = document.getElementsByClassName(classname);
69
86
  [].forEach.call(elts, function (el) {
@@ -154,6 +171,31 @@ function opentab(btn, tabname) {
154
171
  btn.className += " active";
155
172
  }
156
173
 
174
+ function opentab_headers() {
175
+ opentab(document.getElementById("btn-tab-headers"), "tabHeaders");
176
+ }
177
+
178
+ function opentab_cookies() {
179
+ opentab(document.getElementById("btn-tab-cookies"), "tabCookies");
180
+ }
181
+
182
+ function opentab_request() {
183
+ opentab(document.getElementById("btn-tab-request"), "tabRequest");
184
+ }
185
+
186
+ function opentab_response() {
187
+ opentab(document.getElementById("btn-tab-response"), "tabResponse");
188
+ }
189
+
190
+ function opentab_exception() {
191
+ opentab(document.getElementById("btn-tab-exception"), "tabException");
192
+ }
193
+
194
+ function opentab_stack() {
195
+ opentab(document.getElementById("btn-tab-stack"), "tabStack");
196
+ }
197
+
198
+
157
199
  async function disable_link_if_server_disconnected() {
158
200
  var sheet = document.getElementById("serverstatuscss").sheet;
159
201
 
@@ -19,6 +19,7 @@
19
19
  --link-server-disconnected: #444444;
20
20
  --preview-code-border: #777777;
21
21
  --column-border: #dadada;
22
+ --kdb-border: #000000;
22
23
  }
23
24
 
24
25
  body {
@@ -58,6 +59,10 @@ a:link {
58
59
  display: none;
59
60
  }
60
61
 
62
+ #help {
63
+ display: none;
64
+ }
65
+
61
66
  .previewcode {
62
67
  display: block;
63
68
  overflow-x: scroll;
@@ -78,4 +83,22 @@ a:link {
78
83
  width: 16px;
79
84
  height: 16px;
80
85
  vertical-align: middle;
86
+ }
87
+
88
+ .visuallyhidden {
89
+ border: 0;
90
+ clip: rect(0 0 0 0);
91
+ height: 1px;
92
+ margin: -1px;
93
+ overflow: hidden;
94
+ padding: 0;
95
+ position: absolute;
96
+ width: 1px;
97
+ }
98
+
99
+ kbd {
100
+ padding: 1px 2px 0;
101
+ border-radius: 3px;
102
+ border: 1px solid black;
103
+ font-family: monospace;
81
104
  }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: httpdbg
3
- Version: 0.12.0
3
+ Version: 0.13.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
@@ -22,6 +22,8 @@ License-File: LICENSE
22
22
 
23
23
  A very simple tool to debug HTTP(S) client requests.
24
24
 
25
+ ![](ui.png)
26
+
25
27
  ## installation
26
28
 
27
29
  ```
@@ -49,6 +49,7 @@ httpdbg/webapp/static/icons/filter-outline-icon.svg
49
49
  httpdbg/webapp/static/icons/information-mark-circle-outline-icon.svg
50
50
  httpdbg/webapp/static/icons/math-multiplication-icon.svg
51
51
  httpdbg/webapp/static/icons/push-pin-green-icon.svg
52
+ httpdbg/webapp/static/icons/question-mark-round-line-icon.svg
52
53
  httpdbg/webapp/static/icons/recycle-bin-line-icon.svg
53
54
  httpdbg/webapp/static/icons/wait-sandclock-icon.svg
54
55
  tests/test_api.py
@@ -1,5 +1,4 @@
1
1
  # -*- coding: utf-8 -*-
2
- import platform
3
2
  import sys
4
3
  import urllib.parse
5
4
 
@@ -125,28 +124,6 @@ def test_httpx_cookies_secure(httpbin_secure):
125
124
  )
126
125
 
127
126
 
128
- @pytest.mark.httpx
129
- @pytest.mark.xfail(
130
- platform.system().lower() == "windows",
131
- reason="An established connection was aborted by the software in your host machine",
132
- )
133
- def test_httpx_stream(httpbin):
134
- request_content = b"key=value"
135
- response_content = bytes()
136
- with httpdbg() as records:
137
- with httpx.stream("POST", f"{httpbin.url}", data={"key": "value"}) as r:
138
- for data in r.iter_bytes():
139
- response_content += data
140
-
141
- assert response_content != bytes()
142
-
143
- assert len(records) == 1
144
-
145
- http_record = records[0]
146
- assert http_record.request.content == request_content
147
- assert http_record.response.content == response_content
148
-
149
-
150
127
  @pytest.mark.httpx
151
128
  def test_httpx_redirect(httpbin_both):
152
129
  redirect_url = f"{httpbin_both.url}/redirect-to?{urllib.parse.urlencode({'url': httpbin_both.url})}/get"
@@ -1,5 +1,4 @@
1
1
  # -*- coding: utf-8 -*-
2
- import platform
3
2
  import pytest
4
3
  import requests
5
4
 
@@ -125,30 +124,6 @@ def test_requests_cookies_secure(httpbin_secure):
125
124
  )
126
125
 
127
126
 
128
- @pytest.mark.requests
129
- @pytest.mark.xfail(
130
- platform.system().lower() == "windows",
131
- reason="An established connection was aborted by the software in your host machine",
132
- )
133
- def test_requests_stream(httpbin_both):
134
- request_content = b"key=value"
135
- response_content = bytes()
136
- with httpdbg() as records:
137
- with requests.post(
138
- f"{httpbin_both.url}", data={"key": "value"}, verify=False
139
- ) as r:
140
- for data in r.iter_content():
141
- response_content += data
142
-
143
- assert response_content != bytes()
144
-
145
- assert len(records) == 1
146
-
147
- http_record = records[0]
148
- assert http_record.request.content == request_content
149
- assert http_record.response.content == response_content
150
-
151
-
152
127
  @pytest.mark.requests
153
128
  def test_requests_redirect(httpbin_both):
154
129
  with httpdbg() as records:
@@ -1,4 +1,7 @@
1
1
  # -*- coding: utf-8 -*-
2
+ from packaging import version
3
+ import pkg_resources
4
+
2
5
  import pytest
3
6
  import urllib3
4
7
 
@@ -28,7 +31,7 @@ def test_urllib3_initiator(httpbin, monkeypatch):
28
31
  with monkeypatch.context() as m:
29
32
  m.delenv("PYTEST_CURRENT_TEST")
30
33
  with httpdbg() as records:
31
- with urllib3.PoolManager(cert_reqs="CERT_NONE") as http:
34
+ with urllib3.PoolManager() as http:
32
35
  http.request("GET", f"{httpbin.url}/get")
33
36
  with urllib3.HTTPConnectionPool(f"{httpbin.url[7:]}") as http:
34
37
  http.request("GET", "/get")
@@ -48,6 +51,35 @@ def test_urllib3_initiator(httpbin, monkeypatch):
48
51
  assert 'http.request("GET", "/get") <===' in "".join(records[1].initiator.stack)
49
52
 
50
53
 
54
+ @pytest.mark.initiator
55
+ @pytest.mark.urllib3
56
+ def test_urllib3_initiator_secure(httpbin_secure, monkeypatch):
57
+ with monkeypatch.context() as m:
58
+ m.delenv("PYTEST_CURRENT_TEST")
59
+ with httpdbg() as records:
60
+ with urllib3.PoolManager(cert_reqs="CERT_NONE") as http:
61
+ http.request("GET", f"{httpbin_secure.url}/get")
62
+ with urllib3.HTTPSConnectionPool(
63
+ f"{httpbin_secure.url[8:]}", cert_reqs="CERT_NONE"
64
+ ) as http:
65
+ http.request("GET", "/get")
66
+
67
+ assert len(records) == 2
68
+
69
+ assert (
70
+ records[0].initiator.short_label
71
+ == 'http.request("GET", f"{httpbin_secure.url}/get")'
72
+ )
73
+ assert records[0].initiator.long_label is None
74
+ assert 'http.request("GET", f"{httpbin_secure.url}/get") <===' in "".join(
75
+ records[0].initiator.stack
76
+ )
77
+
78
+ assert records[1].initiator.short_label == 'http.request("GET", "/get")'
79
+ assert records[1].initiator.long_label is None
80
+ assert 'http.request("GET", "/get") <===' in "".join(records[1].initiator.stack)
81
+
82
+
51
83
  @pytest.mark.urllib3
52
84
  def test_urllib3_request(httpbin_both):
53
85
  with httpdbg() as records:
@@ -201,3 +233,28 @@ def test_urllib3_get_empty_request_content(httpbin_both):
201
233
 
202
234
  assert http_record.url == f"{httpbin_both.url}/get"
203
235
  assert http_record.request.content == b""
236
+
237
+
238
+ @pytest.mark.initiator
239
+ @pytest.mark.urllib3
240
+ @pytest.mark.skipif(
241
+ version.parse(pkg_resources.get_distribution("urllib3").version)
242
+ < version.parse("2.0.0"),
243
+ reason="only urllib3 v2",
244
+ )
245
+ def test_urllib3_v2_request(httpbin, monkeypatch):
246
+ with monkeypatch.context() as m:
247
+ m.delenv("PYTEST_CURRENT_TEST")
248
+ with httpdbg() as records:
249
+ urllib3.request("GET", f"{httpbin.url}/get")
250
+
251
+ assert len(records) == 1
252
+
253
+ assert (
254
+ records[0].initiator.short_label
255
+ == 'urllib3.request("GET", f"{httpbin.url}/get")'
256
+ )
257
+ assert records[0].initiator.long_label is None
258
+ assert 'urllib3.request("GET", f"{httpbin.url}/get") <===' in "".join(
259
+ records[0].initiator.stack
260
+ )
@@ -1,22 +0,0 @@
1
- "use strict";
2
-
3
- let httpdbgApp = {
4
- "config": {
5
- "hide_initiator": {
6
- "checkbox": "cinitiator",
7
- "param": "hi",
8
- "css": ".initiator {display: none;}",
9
- "value": false
10
- },
11
- "hide_netloc": {
12
- "checkbox": "curl",
13
- "param": "hn",
14
- "css": ".netloc {display: none;}",
15
- "value": false
16
- }
17
- }
18
- }
19
-
20
- function wait_for(ms) {
21
- return new Promise(resolve => setTimeout(resolve, ms));
22
- }
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