%PDF- %PDF-
| Direktori : /lib/python3/dist-packages/mitmproxy/addons/ |
| Current File : //lib/python3/dist-packages/mitmproxy/addons/modifyheaders.py |
import re
import typing
from pathlib import Path
from mitmproxy import ctx, exceptions, flowfilter, http
from mitmproxy.net.http import Headers
from mitmproxy.utils import strutils
from mitmproxy.utils.spec import parse_spec
class ModifySpec(typing.NamedTuple):
matches: flowfilter.TFilter
subject: bytes
replacement_str: str
def read_replacement(self) -> bytes:
"""
Process the replacement str. This usually just involves converting it to bytes.
However, if it starts with `@`, we interpret the rest as a file path to read from.
Raises:
- IOError if the file cannot be read.
"""
if self.replacement_str.startswith("@"):
return Path(self.replacement_str[1:]).expanduser().read_bytes()
else:
# We could cache this at some point, but unlikely to be a problem.
return strutils.escaped_str_to_bytes(self.replacement_str)
def parse_modify_spec(option: str, subject_is_regex: bool) -> ModifySpec:
flow_filter, subject_str, replacement = parse_spec(option)
subject = strutils.escaped_str_to_bytes(subject_str)
if subject_is_regex:
try:
re.compile(subject)
except re.error as e:
raise ValueError(f"Invalid regular expression {subject!r} ({e})")
spec = ModifySpec(flow_filter, subject, replacement)
try:
spec.read_replacement()
except OSError as e:
raise ValueError(f"Invalid file path: {replacement[1:]} ({e})")
return spec
class ModifyHeaders:
def __init__(self):
self.replacements: typing.List[ModifySpec] = []
def load(self, loader):
loader.add_option(
"modify_headers", typing.Sequence[str], [],
"""
Header modify pattern of the form "[/flow-filter]/header-name/[@]header-value", where the
separator can be any character. The @ allows to provide a file path that is used to read
the header value string. An empty header-value removes existing header-name headers.
"""
)
def configure(self, updated):
if "modify_headers" in updated:
self.replacements = []
for option in ctx.options.modify_headers:
try:
spec = parse_modify_spec(option, False)
except ValueError as e:
raise exceptions.OptionsError(f"Cannot parse modify_headers option {option}: {e}") from e
self.replacements.append(spec)
def request(self, flow):
if not flow.reply.has_message:
self.run(flow, flow.request.headers)
def response(self, flow):
if not flow.reply.has_message:
self.run(flow, flow.response.headers)
def run(self, flow: http.HTTPFlow, hdrs: Headers) -> None:
# unset all specified headers
for spec in self.replacements:
if spec.matches(flow):
hdrs.pop(spec.subject, None)
# set all specified headers if the replacement string is not empty
for spec in self.replacements:
if spec.matches(flow):
try:
replacement = spec.read_replacement()
except OSError as e:
ctx.log.warn(f"Could not read replacement file: {e}")
continue
else:
if replacement:
hdrs.add(spec.subject, replacement)