Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes15kdownloads
serverplayback.py306 linesDownload Raw Back to addons
1import hashlib2import logging3import urllib4from collections.abc import Hashable5from collections.abc import Sequence6from typing import Any7 8import mitmproxy.types9from mitmproxy import command10from mitmproxy import ctx11from mitmproxy import exceptions12from mitmproxy import flow13from mitmproxy import hooks14from mitmproxy import http15from mitmproxy import io16 17logger = logging.getLogger(__name__)18 19HASH_OPTIONS = [20    "server_replay_ignore_content",21    "server_replay_ignore_host",22    "server_replay_ignore_params",23    "server_replay_ignore_payload_params",24    "server_replay_ignore_port",25    "server_replay_use_headers",26]27 28 29class ServerPlayback:30    flowmap: dict[Hashable, list[http.HTTPFlow]]31    configured: bool32 33    def __init__(self):34        self.flowmap = {}35        self.configured = False36 37    def load(self, loader):38        loader.add_option(39            "server_replay_kill_extra",40            bool,41            False,42            "Kill extra requests during replay (for which no replayable response was found)."43            "[Deprecated, prefer to use server_replay_extra='kill']",44        )45        loader.add_option(46            "server_replay_extra",47            str,48            "forward",49            "Behaviour for extra requests during replay for which no replayable response was found. "50            "Setting a numeric string value will return an empty HTTP response with the respective status code.",51            choices=["forward", "kill", "204", "400", "404", "500"],52        )53        loader.add_option(54            "server_replay_reuse",55            bool,56            False,57            """58            Don't remove flows from server replay state after use. This makes it59            possible to replay same response multiple times.60            """,61        )62        loader.add_option(63            "server_replay_nopop",64            bool,65            False,66            """67            Deprecated alias for `server_replay_reuse`.68            """,69        )70        loader.add_option(71            "server_replay_refresh",72            bool,73            True,74            """75            Refresh server replay responses by adjusting date, expires and76            last-modified headers, as well as adjusting cookie expiration.77            """,78        )79        loader.add_option(80            "server_replay_use_headers",81            Sequence[str],82            [],83            """84            Request headers that need to match while searching for a saved flow85            to replay.86            """,87        )88        loader.add_option(89            "server_replay",90            Sequence[str],91            [],92            "Replay server responses from a saved file.",93        )94        loader.add_option(95            "server_replay_ignore_content",96            bool,97            False,98            "Ignore request content while searching for a saved flow to replay.",99        )100        loader.add_option(101            "server_replay_ignore_params",102            Sequence[str],103            [],104            """105            Request parameters to be ignored while searching for a saved flow106            to replay.107            """,108        )109        loader.add_option(110            "server_replay_ignore_payload_params",111            Sequence[str],112            [],113            """114            Request payload parameters (application/x-www-form-urlencoded or115            multipart/form-data) to be ignored while searching for a saved flow116            to replay.117            """,118        )119        loader.add_option(120            "server_replay_ignore_host",121            bool,122            False,123            """124            Ignore request destination host while searching for a saved flow125            to replay.126            """,127        )128        loader.add_option(129            "server_replay_ignore_port",130            bool,131            False,132            """133            Ignore request destination port while searching for a saved flow134            to replay.135            """,136        )137 138    @command.command("replay.server")139    def load_flows(self, flows: Sequence[flow.Flow]) -> None:140        """141        Replay server responses from flows.142        """143        self.flowmap = {}144        self.add_flows(flows)145 146    @command.command("replay.server.add")147    def add_flows(self, flows: Sequence[flow.Flow]) -> None:148        """149        Add responses from flows to server replay list.150        """151        for f in flows:152            if isinstance(f, http.HTTPFlow):153                lst = self.flowmap.setdefault(self._hash(f), [])154                lst.append(f)155        ctx.master.addons.trigger(hooks.UpdateHook([]))156 157    @command.command("replay.server.file")158    def load_file(self, path: mitmproxy.types.Path) -> None:159        try:160            flows = io.read_flows_from_paths([path])161        except exceptions.FlowReadException as e:162            raise exceptions.CommandError(str(e))163        self.load_flows(flows)164 165    @command.command("replay.server.stop")166    def clear(self) -> None:167        """168        Stop server replay.169        """170        self.flowmap = {}171        ctx.master.addons.trigger(hooks.UpdateHook([]))172 173    @command.command("replay.server.count")174    def count(self) -> int:175        return sum(len(i) for i in self.flowmap.values())176 177    def _hash(self, flow: http.HTTPFlow) -> Hashable:178        """179        Calculates a loose hash of the flow request.180        """181        r = flow.request182        _, _, path, _, query, _ = urllib.parse.urlparse(r.url)183        queriesArray = urllib.parse.parse_qsl(query, keep_blank_values=True)184 185        key: list[Any] = [str(r.scheme), str(r.method), str(path)]186        if not ctx.options.server_replay_ignore_content:187            if ctx.options.server_replay_ignore_payload_params and r.multipart_form:188                key.extend(189                    (k, v)190                    for k, v in r.multipart_form.items(multi=True)191                    if k.decode(errors="replace")192                    not in ctx.options.server_replay_ignore_payload_params193                )194            elif ctx.options.server_replay_ignore_payload_params and r.urlencoded_form:195                key.extend(196                    (k, v)197                    for k, v in r.urlencoded_form.items(multi=True)198                    if k not in ctx.options.server_replay_ignore_payload_params199                )200            else:201                key.append(str(r.raw_content))202 203        if not ctx.options.server_replay_ignore_host:204            key.append(r.pretty_host)205        if not ctx.options.server_replay_ignore_port:206            key.append(r.port)207 208        filtered = []209        ignore_params = ctx.options.server_replay_ignore_params or []210        for p in queriesArray:211            if p[0] not in ignore_params:212                filtered.append(p)213        for p in filtered:214            key.append(p[0])215            key.append(p[1])216 217        if ctx.options.server_replay_use_headers:218            headers = []219            for i in ctx.options.server_replay_use_headers:220                v = r.headers.get(i)221                headers.append((i, v))222            key.append(headers)223        return hashlib.sha256(repr(key).encode("utf8", "surrogateescape")).digest()224 225    def next_flow(self, flow: http.HTTPFlow) -> http.HTTPFlow | None:226        """227        Returns the next flow object, or None if no matching flow was228        found.229        """230        hash = self._hash(flow)231        if hash in self.flowmap:232            if ctx.options.server_replay_reuse or ctx.options.server_replay_nopop:233                return next(234                    (flow for flow in self.flowmap[hash] if flow.response), None235                )236            else:237                ret = self.flowmap[hash].pop(0)238                while not ret.response:239                    if self.flowmap[hash]:240                        ret = self.flowmap[hash].pop(0)241                    else:242                        del self.flowmap[hash]243                        return None244                if not self.flowmap[hash]:245                    del self.flowmap[hash]246                return ret247        else:248            return None249 250    def configure(self, updated):251        if ctx.options.server_replay_kill_extra:252            logger.warning(253                "server_replay_kill_extra has been deprecated, "254                "please update your config to use server_replay_extra='kill'."255            )256        if ctx.options.server_replay_nopop:  # pragma: no cover257            logger.error(258                "server_replay_nopop has been renamed to server_replay_reuse, please update your config."259            )260        if not self.configured and ctx.options.server_replay:261            self.configured = True262            try:263                flows = io.read_flows_from_paths(ctx.options.server_replay)264            except exceptions.FlowReadException as e:265                raise exceptions.OptionsError(str(e))266            self.load_flows(flows)267        if any(option in updated for option in HASH_OPTIONS):268            self.recompute_hashes()269 270    def recompute_hashes(self) -> None:271        """272        Rebuild flowmap if the hashing method has changed during execution,273        see https://github.com/mitmproxy/mitmproxy/issues/4506274        """275        flows = [flow for lst in self.flowmap.values() for flow in lst]276        self.load_flows(flows)277 278    def request(self, f: http.HTTPFlow) -> None:279        if self.flowmap:280            rflow = self.next_flow(f)281            if rflow:282                assert rflow.response283                response = rflow.response.copy()284                if ctx.options.server_replay_refresh:285                    response.refresh()286                f.response = response287                f.is_replay = "response"288            elif (289                ctx.options.server_replay_kill_extra290                or ctx.options.server_replay_extra == "kill"291            ):292                logging.warning(293                    "server_playback: killed non-replay request {}".format(294                        f.request.url295                    )296                )297                f.kill()298            elif ctx.options.server_replay_extra != "forward":299                logging.warning(300                    "server_playback: returned {} non-replay request {}".format(301                        ctx.options.server_replay_extra, f.request.url302                    )303                )304                f.response = http.Response.make(int(ctx.options.server_replay_extra))305                f.is_replay = "response"306 
codekingpro/portable-devtools · Team Ai