chrome cdp 를 local에서 켜고, 연결을 docker 에서 할때 mitmproxy 사용해서 해결

Updated on

Chrome 은 localhost, 127.0.0.1이 아니면 접근이 불가능 하다.

그래서 옛날에는

netsh advfirewall firewall delete rule name="Allow Port 9222"
netsh interface portproxy delete v4tov4 listenport=9222 listenaddress=0.0.0.0
timeout 3
netsh advfirewall firewall add rule name="Allow Port 9222" dir=in action=allow protocol=TCP localport=9222
start /b cmd /c call "C:\Program Files\Google\Chrome\Application\chrome.exe" --remote-debugging-port=9222 --lang=ko-KR --no-default-browser-check --no-first-run --disable-dev-shm-usage --window-size=2560,1440 --start-maximized --disable-infobars
timeout 5
netsh interface portproxy add v4tov4 listenport=9222 listenaddress=0.0.0.0 connectport=9222 connectaddress=127.0.0.1

이 window shell 명령어를 통해서 해결하고는 했는데, 문제가 크롬이 켜진 이후에 netsh 로 연결을 열어줬어야 했고.

그러다보니, 항상 크롬이 켜진 이후라서 5초라는 딜레이가 발생했었다.

근데 이 문제를 더 간단히 해결하기 위해서 발견한 것이 mitmproxy 라는 건데. https://github.com/mitmproxy/mitmproxy https://mitmproxy.org/downloads/# 매우 좋은 것 같다.

from mitmproxy import http
import json

TARGET_HOST = "127.0.0.1"
TARGET_PORT = 9222

PROXY_HOST = "host.docker.internal"
PROXY_PORT = 9223

def request(flow: http.HTTPFlow) -> None:
    """HTTP 요청을 수정하여 실제 Chrome 디버그 서버로 포워딩"""
    try:
        print("HTTP request intercepted")
        flow.request.headers["Host"] = "localhost"
        flow.request.host = TARGET_HOST
        flow.request.port = TARGET_PORT
    except Exception as e:
        print(f"Request modification error: {e}")

def websocket_handshake(flow: http.HTTPFlow) -> None:
    """WebSocket 핸드셰이크 요청을 수정하여 실제 Chrome 디버그 서버로 포워딩"""
    try:
        print("WebSocket handshake intercepted")
        flow.request.headers["Host"] = "localhost"
        flow.request.host = TARGET_HOST
        flow.request.port = TARGET_PORT
    except Exception as e:
        print(f"WebSocket handshake modification error: {e}")

def response(flow: http.HTTPFlow) -> None:
    """HTTP 응답을 수정하여 webSocketDebuggerUrl을 mitmproxy 포트으로 변경"""
    try:
        content_type = flow.response.headers.get("Content-Type", "")
        if "application/json" in content_type:
            data = json.loads(flow.response.text)
            if "webSocketDebuggerUrl" in data:
                original_ws_url = data["webSocketDebuggerUrl"]
                # 원래의 WS URL을 변경
                new_ws_url = original_ws_url.replace(
                    f"ws://{TARGET_HOST}:{TARGET_PORT}",
                    f"ws://{PROXY_HOST}:{PROXY_PORT}"
                )
                data["webSocketDebuggerUrl"] = new_ws_url
                flow.response.text = json.dumps(data)
                print(f"Modified webSocketDebuggerUrl: {new_ws_url}")
    except json.JSONDecodeError:
        print("Failed to decode JSON response")
    except Exception as e:
        print(f"Error modifying response: {e}")

"""
mitmproxy --mode reverse:http://127.0.0.1:9222/ --listen-port 9223 -s modify_host.py
"""

위 코드가 파이썬으로 만들어진 코드이고,

mitmproxy --mode reverse:http://127.0.0.1:9222/ --listen-port 9223 -s modify_host.py 명령어를 통해서 mitmproxy 를 실행시키면 된다.

그러면, 도커 안에서 도는 프로세스에서 puppeteer나 playwright를 연결할때.

host.docker.internal:9223 으로 연결되게 타겟해놓게 되면 실제 연결하게 되면 로컬에서 켠 브라우저에서 작동하게 된다.

C:\Program Files\Google\Chrome\Application\chrome.exe" --remote-debugging-port=9222 --lang=ko-KR --no-default-browser-check --no-first-run --disable-dev-shm-usage --window-size=2560,1440 --start-maximized --disable-infobars

이게 왜 유용하냐면, 실제로 개발이나 서버에서는 도커에서 돌아가기 마련인데, 생각보다 이게 정확히 작동하는지에 대해서 체크가 어려울때가 있다.

그래서 개발할때는 빠르게 개발해야하기 때문에 이 방식으로 개발을 하고, 실제로 서버에서 돌아갈때는 또 서버에서 잘 도는지 체크를 해주면 된다.

puppeteer-screen-recorder 같은거로 체크했었음.