Skip to content

Commit

Permalink
enforce order by date based on group strategy (#169)
Browse files Browse the repository at this point in the history
  • Loading branch information
cle-b authored Jan 5, 2025
1 parent 6e7de42 commit e283766
Show file tree
Hide file tree
Showing 14 changed files with 340 additions and 204 deletions.
15 changes: 9 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,16 +120,19 @@ options:

### web interace

Clic on the **⚙** button on the top right of the page.
Some options are available to customize the UI:

Some options are available:
* Hide the netloc in the url
* Hide the initiator rows
* Hide the tags
* Change the strategy to group the requests.
* Hide the scheme and the network location in the url.
* Hide the group rows.
* Hide the tags.
* ...

You can also pin a request or delete all unpinned requests.

To keep your configuration, bookmark the page with the full search query.

Fox example, if you want to hide the initiator rows by default, the url will be:
Fox example, if you want to hide the group rows by default, the url will be:
```
http://localhost:4909/?hi=on
```
Expand Down
2 changes: 1 addition & 1 deletion httpdbg/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
from httpdbg.records import HTTPRecords


__version__ = "0.30.0"
__version__ = "0.31.0"

__all__ = ["httprecord", "HTTPRecords"]
21 changes: 18 additions & 3 deletions httpdbg/initiator.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# -*- coding: utf-8 -*-
from collections.abc import Callable
from contextlib import contextmanager
import datetime
import os
import platform
import traceback
Expand Down Expand Up @@ -31,6 +33,7 @@ def __init__(
self.label = label
self.short_stack = short_stack
self.stack = stack
self.tbegin: datetime.datetime = datetime.datetime.now(datetime.timezone.utc)

def __eq__(self, other) -> bool:
if type(other) is Initiator:
Expand All @@ -49,12 +52,14 @@ def to_json(self, full: bool = True) -> dict:
"label": self.label,
"short_stack": self.short_stack,
"stack": "\n".join(self.stack),
"tbegin": self.tbegin.isoformat(),
}
else:
json = {
"id": self.id,
"label": self.label,
"short_stack": self.short_stack,
"tbegin": self.tbegin.isoformat(),
}
return json

Expand All @@ -64,9 +69,15 @@ def __init__(self, label: str, full_label: str):
self.id: str = get_new_uuid()
self.label: str = label
self.full_label: str = full_label
self.tbegin: datetime.datetime = datetime.datetime.now(datetime.timezone.utc)

def to_json(self) -> dict:
return {"id": self.id, "label": self.label, "full_label": self.full_label}
return {
"id": self.id,
"label": self.label,
"full_label": self.full_label,
"tbegin": self.tbegin.isoformat(),
}


def compatible_path(path: str) -> str:
Expand Down Expand Up @@ -193,9 +204,13 @@ def extract_short_stack_from_file(

@contextmanager
def httpdbg_initiator(
records, extracted_stack: traceback.StackSummary, original_method, *args, **kwargs
records: "HTTPRecords",
extracted_stack: traceback.StackSummary,
original_method: Callable,
*args,
**kwargs,
) -> Generator[Union[Tuple[Initiator, Group], None], None, None]:
envname = f"{HTTPDBG_CURRENT_INITIATOR}_{records.id}"
envname = f"{HTTPDBG_CURRENT_INITIATOR}_{records.session.id}"

if not os.environ.get(envname):
# temporary set a fake initiator env variable to avoid a recursion error
Expand Down
13 changes: 11 additions & 2 deletions httpdbg/records.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import os
import socket
import ssl
import sys
import traceback
from urllib.parse import urlparse
from typing import Dict, List, Tuple, Union
Expand Down Expand Up @@ -293,13 +294,21 @@ def last_update(self) -> datetime.datetime:
return max(self.request.last_update, self.response.last_update)


class HTTPRecordsSessionInfo:

def __init__(self):
self.id: str = get_new_uuid()
self.command_line: str = " ".join(sys.argv[1:]) if sys.argv[1:] else "console"
self.tbegin: datetime.datetime = datetime.datetime.now(datetime.timezone.utc)


class HTTPRecords:
def __init__(self) -> None:
self.reset()

def reset(self) -> None:
logger().info("HTTPRecords.reset")
self.id = get_new_uuid()
self.session: HTTPRecordsSessionInfo = HTTPRecordsSessionInfo()
self.requests: Dict[str, HTTPRecord] = {}
self.requests_already_loaded = 0
self.initiators: Dict[str, Initiator] = {}
Expand All @@ -317,7 +326,7 @@ def __len__(self) -> int:
return len(self.requests)

def get_initiator(self) -> str:
envname = f"{HTTPDBG_CURRENT_INITIATOR}_{self.id}"
envname = f"{HTTPDBG_CURRENT_INITIATOR}_{self.session.id}"

if envname in os.environ:
initiator = self.initiators[os.environ[envname]]
Expand Down
11 changes: 8 additions & 3 deletions httpdbg/webapp/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import traceback
from typing import Any
from typing import Dict
from typing import Union

from httpdbg.records import HTTPRecords
from httpdbg.records import HTTPRecord
Expand Down Expand Up @@ -65,13 +66,17 @@ def default(self, req: HTTPRecord) -> Dict[str, Any]:


class RequestListPayload(JSONEncoder):
def default(self, records):
def default(self, records: HTTPRecords):
assert isinstance(
records, HTTPRecords
), "This encoder works only for HTTPRecords object."

payload = {
"id": records.id,
payload: Dict[str, Dict[str, Union[str, dict]]] = {
"session": {
"id": records.session.id,
"command_line": records.session.command_line,
"tbegin": records.session.tbegin.isoformat(),
},
"requests": {},
"initiators": {},
"groups": {},
Expand Down
30 changes: 15 additions & 15 deletions httpdbg/webapp/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from httpdbg import __version__
from httpdbg.log import logger
from httpdbg.webapp.api import RequestListPayload, RequestPayload
from httpdbg.records import HTTPRecords


@contextmanager
Expand All @@ -22,7 +23,10 @@ def silently_catch_error():


class HttpbgHTTPRequestHandler(BaseHTTPRequestHandler):
def __init__(self, records, *args, **kwargs):

def __init__(
self: "HttpbgHTTPRequestHandler", records: HTTPRecords, *args, **kwargs
):
self.records = records
super().__init__(*args, **kwargs)

Expand All @@ -42,7 +46,7 @@ def do_GET(self):
if serve(url):
break

def serve_static(self, url):
def serve_static(self: "HttpbgHTTPRequestHandler", url):
base_path = os.path.dirname(os.path.realpath(__file__))

if not (
Expand Down Expand Up @@ -99,13 +103,13 @@ def serve_static(self, url):

return True

def serve_requests(self, url):
def serve_requests(self: "HttpbgHTTPRequestHandler", url):
if not (url.path.lower() == "/requests"):
return False

query = parse_qs(url.query)

if query.get("id", [""])[0] == self.records.id:
if query.get("id", [""])[0] == self.records.session.id:
self.records.requests_already_loaded = int(
query.get("requests_already_loaded", [0])[0]
)
Expand All @@ -122,7 +126,7 @@ def serve_requests(self, url):

return True

def serve_request(self, url):
def serve_request(self: "HttpbgHTTPRequestHandler", url):
regexp = r"/request/([\w\-]+)"

if re.fullmatch(regexp, url.path) is None:
Expand All @@ -145,7 +149,7 @@ def serve_request(self, url):

return True

def serve_request_content_up(self, url):
def serve_request_content_up(self: "HttpbgHTTPRequestHandler", url):
regexp = r"/request/([\w\-]+)/up"

if re.fullmatch(regexp, url.path) is None:
Expand All @@ -162,15 +166,11 @@ def serve_request_content_up(self, url):
self.send_header("Content-type", "application/octet-stream")
self.send_header_no_cache()
self.end_headers()
self.wfile.write(
req.request.content.encode("utf-8")
if isinstance(req.request.content, str)
else req.request.content
)
self.wfile.write(req.request.content)

return True

def serve_request_content_down(self, url):
def serve_request_content_down(self: "HttpbgHTTPRequestHandler", url):
regexp = r"/request/([\w\-]+)/down"

if re.fullmatch(regexp, url.path) is None:
Expand All @@ -191,19 +191,19 @@ def serve_request_content_down(self, url):

return True

def serve_not_found(self, *kwargs):
def serve_not_found(self: "HttpbgHTTPRequestHandler", *kwargs):
self.send_response(404)
self.send_header_no_cache()
self.end_headers()
self.wfile.write(b"404 Not found")

return True

def log_message(self, format, *args):
def log_message(self: "HttpbgHTTPRequestHandler", format, *args):
pass

def send_header_no_cache(self):
self.send_header("Cache-Control", "max-age=0, no-cache, no-store, private")

def send_header_with_cache(self, seconds):
def send_header_with_cache(self: "HttpbgHTTPRequestHandler", seconds):
self.send_header("Cache-Control", f"max-age={seconds}")
50 changes: 27 additions & 23 deletions httpdbg/webapp/static/api.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
"use strict";

const global = {
"k7": null,
"requests": {},
"initiators": {},
"groups": {},
"connected": false,
"group_collapse": []
session: null,
sessions: {},
requests: {},
initiators: {},
groups: {},
connected: false,
group_collapse: [],
groupby: "default"
}

function save_request(request_id, request) {
function save_request(request_id, request, session_id) {
request.loaded = false;
request.to_refresh = true;
if (request.pin == undefined) {
Expand All @@ -18,26 +20,27 @@ function save_request(request_id, request) {
if (request.filter == undefined) {
request.filter = "---";
}
global.requests[request_id] = request;
request.session_id = session_id;
request.initiator = global.initiators[request.initiator_id];

global.requests[request_id].initiator = global.initiators[request.initiator_id];

if (global.requests[request_id].in_progress) {
global.requests[request_id].status_code_view = '<img class="icon" src="static/icons/wait-sandclock-icon.svg-+-$**HTTPDBG_VERSION**$" alt="loading"/>';
if (request.in_progress) {
request.status_code_view = '<img class="icon" src="static/icons/wait-sandclock-icon.svg-+-$**HTTPDBG_VERSION**$" alt="loading"/>';
} else {
switch (global.requests[request_id].status_code) {
switch (request.status_code) {
case 0:
global.requests[request_id].status_code_view = '<img class="icon" src="static/icons/wait-sandclock-icon.svg-+-$**HTTPDBG_VERSION**$/" alt="loading"/>';
request.status_code_view = '<img class="icon" src="static/icons/wait-sandclock-icon.svg-+-$**HTTPDBG_VERSION**$/" alt="loading"/>';
break;
case -1:
global.requests[request_id].status_code_view = '<img class="icon" src="static/icons/math-multiplication-icon.svg-+-$**HTTPDBG_VERSION**$/" alt="load failed"/>';
request.status_code_view = '<img class="icon" src="static/icons/math-multiplication-icon.svg-+-$**HTTPDBG_VERSION**$/" alt="load failed"/>';
break;
default:
global.requests[request_id].status_code_view = global.requests[request_id].status_code;
request.status_code_view = request.status_code;
break;
}
}

global.requests[request_id] = request;

if (!request.pin) {
get_request(request_id);
}
Expand All @@ -47,12 +50,12 @@ async function get_all_requests() {

var requests_already_loaded = 0
for (const [request_id, request] of Object.entries(global.requests)) {
if (request.loaded) {
if (request.loaded && (global.session == request.session_id)) {
requests_already_loaded += 1;
}
}
var url = "/requests?" + new URLSearchParams({
"id": global.k7,
"id": global.session,
"requests_already_loaded": requests_already_loaded,
})

Expand All @@ -61,9 +64,10 @@ async function get_all_requests() {
.then(data => {
global.connected = true;

if (data.id != global.k7) {
global.k7 = data.id;
clean();
if (data.session.id != global.session) {
clean();
global.session = data.session.id;
global.sessions[data.session.id] = data.session;
};

// for the initiators and the groups, we can just save them without any verification
Expand All @@ -74,11 +78,11 @@ async function get_all_requests() {
for (const [request_id, request] of Object.entries(data.requests)) {
if (!(request_id in global.requests)) {
// this is a new request
save_request(request_id, request);
save_request(request_id, request, data.session.id);
} else {
if (global.requests[request_id].last_update < request.last_update) {
// this request has been updated (probably a "big" file)
save_request(request_id, request);
save_request(request_id, request, data.session.id);
}
};
};
Expand Down
8 changes: 0 additions & 8 deletions httpdbg/webapp/static/column.css
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,3 @@
width: 50vw;
resize: horizontal;
}


.grid-container-configuration {
display: grid;
grid-auto-flow: column;
grid-auto-columns: 1fr;

}
Loading

0 comments on commit e283766

Please sign in to comment.