186 lines
5.2 KiB
Python
186 lines
5.2 KiB
Python
"""Diff engine for comparing HTTP entries."""
|
|
|
|
import difflib
|
|
|
|
from http_log_explorer.models import DiffResult, HTTPEntry
|
|
|
|
|
|
class DiffEngine:
|
|
"""Engine for comparing HTTP request/response pairs."""
|
|
|
|
def diff(self, entry1: HTTPEntry, entry2: HTTPEntry) -> DiffResult:
|
|
"""Compare two HTTP entries.
|
|
|
|
Args:
|
|
entry1: First HTTPEntry
|
|
entry2: Second HTTPEntry
|
|
|
|
Returns:
|
|
DiffResult with differences
|
|
"""
|
|
result = DiffResult(
|
|
entry1_id=entry1.id,
|
|
entry2_id=entry2.id,
|
|
)
|
|
|
|
result.url_changed = entry1.request.url != entry2.request.url
|
|
|
|
result.status_changed = entry1.response.status != entry2.response.status
|
|
result.status1 = entry1.response.status
|
|
result.status2 = entry2.response.status
|
|
|
|
result.request_headers_diff = self.headers_diff(
|
|
entry1.request.headers,
|
|
entry2.request.headers,
|
|
)
|
|
|
|
result.response_headers_diff = self.headers_diff(
|
|
entry1.response.headers,
|
|
entry2.response.headers,
|
|
)
|
|
|
|
result.request_body_diff = self.body_diff(
|
|
entry1.request.body,
|
|
entry2.request.body,
|
|
)
|
|
|
|
result.response_body_diff = self.body_diff(
|
|
entry1.response.body,
|
|
entry2.response.body,
|
|
)
|
|
|
|
return result
|
|
|
|
def headers_diff(
|
|
self, headers1: dict[str, str], headers2: dict[str, str]
|
|
) -> list[str]:
|
|
"""Compare two header dictionaries.
|
|
|
|
Args:
|
|
headers1: First headers dict
|
|
headers2: Second headers dict
|
|
|
|
Returns:
|
|
List of diff lines
|
|
"""
|
|
all_keys = set(headers1.keys()) | set(headers2.keys())
|
|
diff_lines: list[str] = []
|
|
|
|
for key in sorted(all_keys):
|
|
val1 = headers1.get(key)
|
|
val2 = headers2.get(key)
|
|
|
|
if val1 != val2:
|
|
if val1 is None:
|
|
diff_lines.append(f"+ {key}: {val2}")
|
|
elif val2 is None:
|
|
diff_lines.append(f"- {key}: {val1}")
|
|
else:
|
|
diff_lines.append(f"- {key}: {val1}")
|
|
diff_lines.append(f"+ {key}: {val2}")
|
|
|
|
return diff_lines
|
|
|
|
def body_diff(
|
|
self, body1: str | None, body2: str | None
|
|
) -> list[str]:
|
|
"""Compare two body strings.
|
|
|
|
Args:
|
|
body1: First body
|
|
body2: Second body
|
|
|
|
Returns:
|
|
List of diff lines (unified format)
|
|
"""
|
|
if body1 == body2:
|
|
return []
|
|
|
|
b1 = body1 or ""
|
|
b2 = body2 or ""
|
|
|
|
lines1 = b1.splitlines(keepends=True)
|
|
lines2 = b2.splitlines(keepends=True)
|
|
|
|
if not lines1 and not lines2:
|
|
return []
|
|
|
|
diff = list(difflib.unified_diff(
|
|
lines1,
|
|
lines2,
|
|
fromfile="before",
|
|
tofile="after",
|
|
lineterm="",
|
|
))
|
|
|
|
return diff
|
|
|
|
def unified_diff_output(self, diff_result: DiffResult) -> str:
|
|
"""Generate a human-readable unified diff output.
|
|
|
|
Args:
|
|
diff_result: The diff result
|
|
|
|
Returns:
|
|
Formatted string with all differences
|
|
"""
|
|
lines: list[str] = []
|
|
lines.append(f"=== Diff: {diff_result.entry1_id} vs {diff_result.entry2_id} ===")
|
|
lines.append("")
|
|
|
|
if diff_result.url_changed:
|
|
lines.append(f"URL changed: {diff_result.url_changed}")
|
|
|
|
if diff_result.status_changed:
|
|
lines.append(f"Status: {diff_result.status1} -> {diff_result.status2}")
|
|
|
|
if diff_result.request_headers_diff:
|
|
lines.append("")
|
|
lines.append("--- Request Headers ---")
|
|
lines.extend(diff_result.request_headers_diff)
|
|
|
|
if diff_result.request_body_diff:
|
|
lines.append("")
|
|
lines.append("--- Request Body ---")
|
|
lines.extend(diff_result.request_body_diff)
|
|
|
|
if diff_result.response_headers_diff:
|
|
lines.append("")
|
|
lines.append("--- Response Headers ---")
|
|
lines.extend(diff_result.response_headers_diff)
|
|
|
|
if diff_result.response_body_diff:
|
|
lines.append("")
|
|
lines.append("--- Response Body ---")
|
|
lines.extend(diff_result.response_body_diff)
|
|
|
|
if not any([
|
|
diff_result.url_changed,
|
|
diff_result.status_changed,
|
|
diff_result.request_headers_diff,
|
|
diff_result.request_body_diff,
|
|
diff_result.response_headers_diff,
|
|
diff_result.response_body_diff,
|
|
]):
|
|
lines.append("No differences found.")
|
|
|
|
return "\n".join(lines)
|
|
|
|
def has_differences(self, diff_result: DiffResult) -> bool:
|
|
"""Check if there are any differences.
|
|
|
|
Args:
|
|
diff_result: The diff result
|
|
|
|
Returns:
|
|
True if there are any differences
|
|
"""
|
|
return bool(
|
|
diff_result.url_changed
|
|
or diff_result.status_changed
|
|
or diff_result.request_headers_diff
|
|
or diff_result.request_body_diff
|
|
or diff_result.response_headers_diff
|
|
or diff_result.response_body_diff
|
|
)
|