"""Traffic analyzer for filtering HTTP entries.""" import re from collections.abc import Callable from http_log_explorer.models import FilterCriteria, HTTPEntry class TrafficAnalyzer: """Analyzer for filtering and searching HTTP entries.""" def __init__(self, entries: list[HTTPEntry]) -> None: """Initialize with HTTP entries. Args: entries: List of HTTPEntry objects to analyze """ self.entries = entries def filter(self, criteria: FilterCriteria) -> list[HTTPEntry]: """Filter entries based on criteria. Args: criteria: FilterCriteria object with filtering rules Returns: Filtered list of HTTPEntry objects """ predicates: list[Callable[[HTTPEntry], bool]] = [] if criteria.methods: predicates.append(lambda e: e.request.method in criteria.methods) if criteria.status_codes: predicates.append(lambda e: e.response.status in criteria.status_codes) if criteria.url_pattern: pattern = re.compile(criteria.url_pattern) predicates.append(lambda e: bool(pattern.search(e.request.url))) if criteria.content_types: predicates.append(lambda e: bool(e.content_type and any(ct in e.content_type for ct in criteria.content_types))) if criteria.start_time: predicates.append(lambda e: bool(e.timestamp and e.timestamp >= criteria.start_time)) if criteria.end_time: predicates.append(lambda e: bool(e.timestamp and e.timestamp <= criteria.end_time)) if criteria.min_response_time_ms is not None: predicates.append(lambda e: bool(e.duration_ms and e.duration_ms >= criteria.min_response_time_ms)) if criteria.max_response_time_ms is not None: predicates.append(lambda e: bool(e.duration_ms and e.duration_ms <= criteria.max_response_time_ms)) if criteria.request_body_contains: predicates.append( lambda e: bool(e.request.body and criteria.request_body_contains in e.request.body) ) if criteria.response_body_contains: predicates.append( lambda e: bool(e.response.body and criteria.response_body_contains in e.response.body) ) if not predicates: return list(self.entries) return [entry for entry in self.entries if all(pred(entry) for pred in predicates)] def by_method(self, methods: list[str]) -> list[HTTPEntry]: """Filter by HTTP methods. Args: methods: List of methods (GET, POST, PUT, DELETE, etc.) Returns: Filtered entries """ criteria = FilterCriteria(methods=methods) return self.filter(criteria) def by_status(self, status_codes: list[int]) -> list[HTTPEntry]: """Filter by status codes. Args: status_codes: List of status codes to include Returns: Filtered entries """ criteria = FilterCriteria(status_codes=status_codes) return self.filter(criteria) def by_url(self, url_pattern: str) -> list[HTTPEntry]: """Filter by URL pattern. Args: url_pattern: Regular expression pattern to match URLs Returns: Filtered entries """ criteria = FilterCriteria(url_pattern=url_pattern) return self.filter(criteria) def by_content_type(self, content_types: list[str]) -> list[HTTPEntry]: """Filter by content types. Args: content_types: List of content type substrings to match Returns: Filtered entries """ criteria = FilterCriteria(content_types=content_types) return self.filter(criteria) def by_status_range(self, min_status: int, max_status: int) -> list[HTTPEntry]: """Filter by status code range. Args: min_status: Minimum status code (inclusive) max_status: Maximum status code (inclusive) Returns: Filtered entries """ all_in_range = list(range(min_status, max_status + 1)) return self.by_status(all_in_range) def successful_requests(self) -> list[HTTPEntry]: """Get all 2xx responses. Returns: Entries with 2xx status codes """ return self.by_status_range(200, 299) def client_errors(self) -> list[HTTPEntry]: """Get all 4xx responses. Returns: Entries with 4xx status codes """ return self.by_status_range(400, 499) def server_errors(self) -> list[HTTPEntry]: """Get all 5xx responses. Returns: Entries with 5xx status codes """ return self.by_status_range(500, 599) def search(self, query: str, case_sensitive: bool = False) -> list[HTTPEntry]: """Search across URL, request body, and response body. Args: query: Search string case_sensitive: Whether search should be case sensitive Returns: Entries matching the query """ search_query = query if case_sensitive else query.lower() def matches(entry: HTTPEntry) -> bool: url = entry.request.url if case_sensitive else entry.request.url.lower() if search_query in url: return True if entry.request.body: body = entry.request.body if case_sensitive else entry.request.body.lower() if search_query in body: return True if entry.response.body: body = entry.response.body if case_sensitive else entry.response.body.lower() if search_query in body: return True return False return [e for e in self.entries if matches(e)] def get_entry_by_id(self, entry_id: str) -> HTTPEntry | None: """Get a specific entry by its ID. Args: entry_id: The entry ID to find Returns: The HTTPEntry or None if not found """ for entry in self.entries: if entry.id == entry_id: return entry return None