# -*- coding: utf-8 -*- """ proxy.py ~~~~~~~~ ⚡⚡⚡ Fast, Lightweight, Pluggable, TLS interception capable proxy server focused on Network monitoring, controls & Application development, testing, debugging. :copyright: (c) 2013-present by Abhinav Singh and contributors. :license: BSD, see LICENSE for more details. """ import gzip import os import tempfile import unittest import selectors from unittest import mock from proxy.common.flags import Flags from proxy.core.connection import TcpClientConnection from proxy.http.handler import HttpProtocolHandler from proxy.http.parser import httpParserStates from proxy.common.utils import build_http_response, build_http_request, bytes_, text_ from proxy.common.constants import CRLF, PROXY_PY_DIR from proxy.http.server import HttpWebServerPlugin class TestWebServerPlugin(unittest.TestCase): @mock.patch('selectors.DefaultSelector') @mock.patch('socket.fromfd') def setUp(self, mock_fromfd: mock.Mock, mock_selector: mock.Mock) -> None: self.fileno = 10 self._addr = ('127.0.0.1', 54382) self._conn = mock_fromfd.return_value self.mock_selector = mock_selector self.flags = Flags() self.flags.plugins = Flags.load_plugins([ b'proxy.http.proxy.HttpProxyPlugin', b'proxy.http.server.HttpWebServerPlugin', ]) self.protocol_handler = HttpProtocolHandler( TcpClientConnection(self._conn, self._addr), flags=self.flags) self.protocol_handler.initialize() @mock.patch('selectors.DefaultSelector') @mock.patch('socket.fromfd') def test_pac_file_served_from_disk( self, mock_fromfd: mock.Mock, mock_selector: mock.Mock) -> None: pac_file = os.path.join( os.path.dirname(PROXY_PY_DIR), 'helper', 'proxy.pac') self._conn = mock_fromfd.return_value self.mock_selector_for_client_read(mock_selector) self.init_and_make_pac_file_request(pac_file) self.protocol_handler.run_once() self.assertEqual( self.protocol_handler.request.state, httpParserStates.COMPLETE) with open(pac_file, 'rb') as f: self._conn.send.called_once_with(build_http_response( 200, reason=b'OK', headers={ b'Content-Type': b'application/x-ns-proxy-autoconfig', b'Connection': b'close' }, body=f.read() )) @mock.patch('selectors.DefaultSelector') @mock.patch('socket.fromfd') def test_pac_file_served_from_buffer( self, mock_fromfd: mock.Mock, mock_selector: mock.Mock) -> None: self._conn = mock_fromfd.return_value self.mock_selector_for_client_read(mock_selector) pac_file_content = b'function FindProxyForURL(url, host) { return "PROXY localhost:8899; DIRECT"; }' self.init_and_make_pac_file_request(text_(pac_file_content)) self.protocol_handler.run_once() self.assertEqual( self.protocol_handler.request.state, httpParserStates.COMPLETE) self._conn.send.called_once_with(build_http_response( 200, reason=b'OK', headers={ b'Content-Type': b'application/x-ns-proxy-autoconfig', b'Connection': b'close' }, body=pac_file_content )) @mock.patch('selectors.DefaultSelector') @mock.patch('socket.fromfd') def test_default_web_server_returns_404( self, mock_fromfd: mock.Mock, mock_selector: mock.Mock) -> None: self._conn = mock_fromfd.return_value mock_selector.return_value.select.return_value = [( selectors.SelectorKey( fileobj=self._conn, fd=self._conn.fileno, events=selectors.EVENT_READ, data=None), selectors.EVENT_READ), ] flags = Flags() flags.plugins = Flags.load_plugins([ b'proxy.http.proxy.HttpProxyPlugin', b'proxy.http.server.HttpWebServerPlugin', ]) self.protocol_handler = HttpProtocolHandler( TcpClientConnection(self._conn, self._addr), flags=flags) self.protocol_handler.initialize() self._conn.recv.return_value = CRLF.join([ b'GET /hello HTTP/1.1', CRLF, ]) self.protocol_handler.run_once() self.assertEqual( self.protocol_handler.request.state, httpParserStates.COMPLETE) self.assertEqual( self.protocol_handler.client.buffer[0], HttpWebServerPlugin.DEFAULT_404_RESPONSE) @unittest.skipIf(os.environ.get('GITHUB_ACTIONS', False), 'Disabled on GitHub actions because this test is flaky on GitHub infrastructure.') @mock.patch('selectors.DefaultSelector') @mock.patch('socket.fromfd') def test_static_web_server_serves( self, mock_fromfd: mock.Mock, mock_selector: mock.Mock) -> None: # Setup a static directory static_server_dir = os.path.join(tempfile.gettempdir(), 'static') index_file_path = os.path.join(static_server_dir, 'index.html') html_file_content = b'''