예제 #1
0
 def _setUp(self, request: Any, mocker: MockerFixture) -> None:
     self.mock_fromfd = mocker.patch('socket.fromfd')
     self.mock_selector = mocker.patch('selectors.DefaultSelector')
     self.fileno = 10
     self._addr = ('127.0.0.1', 54382)
     self._conn = self.mock_fromfd.return_value
     self.pac_file = request.param
     if isinstance(self.pac_file, str):
         with open(self.pac_file, 'rb') as f:
             self.expected_response = f.read()
     else:
         self.expected_response = PAC_FILE_CONTENT
     self.flags = FlagParser.initialize(
         pac_file=self.pac_file, threaded=True,
     )
     self.flags.plugins = Plugins.load([
         bytes_(PLUGIN_HTTP_PROXY),
         bytes_(PLUGIN_WEB_SERVER),
         bytes_(PLUGIN_PAC_FILE),
     ])
     self.protocol_handler = HttpProtocolHandler(
         HttpClientConnection(self._conn, self._addr),
         flags=self.flags,
     )
     self.protocol_handler.initialize()
     self._conn.recv.return_value = CRLF.join([
         b'GET / HTTP/1.1',
         CRLF,
     ])
     mock_selector_for_client_read(self)
예제 #2
0
 async def test_proxy_authentication_failed(self) -> None:
     self._conn = self.mock_fromfd.return_value
     mock_selector_for_client_read(self)
     flags = FlagParser.initialize(
         auth_code=base64.b64encode(b'user:pass'),
         threaded=True,
     )
     flags.plugins = Plugins.load([
         bytes_(PLUGIN_HTTP_PROXY),
         bytes_(PLUGIN_WEB_SERVER),
         bytes_(PLUGIN_PROXY_AUTH),
     ])
     self.protocol_handler = HttpProtocolHandler(
         HttpClientConnection(self._conn, self._addr),
         flags=flags,
     )
     self.protocol_handler.initialize()
     self._conn.recv.return_value = CRLF.join([
         b'GET http://abhinavsingh.com HTTP/1.1',
         b'Host: abhinavsingh.com',
         CRLF,
     ])
     await self.protocol_handler._run_once()
     self.assertEqual(
         self.protocol_handler.work.buffer[0],
         PROXY_AUTH_FAILED_RESPONSE_PKT,
     )
예제 #3
0
 def test_enable_devtools(
     self,
     mock_listener: mock.Mock,
     mock_executor_pool: mock.Mock,
     mock_acceptor_pool: mock.Mock,
     mock_event_manager: mock.Mock,
     mock_parse_args: mock.Mock,
     mock_load_plugins: mock.Mock,
     mock_sleep: mock.Mock,
 ) -> None:
     mock_sleep.side_effect = KeyboardInterrupt()
     mock_args = mock_parse_args.return_value
     self.mock_default_args(mock_args)
     mock_args.enable_devtools = True
     mock_args.local_executor = 0
     main(enable_devtools=True, local_executor=0)
     mock_load_plugins.assert_called()
     self.assertEqual(
         mock_load_plugins.call_args_list[0][0][0],
         [
             bytes_(PLUGIN_DEVTOOLS_PROTOCOL),
             bytes_(PLUGIN_WEB_SERVER),
             bytes_(PLUGIN_HTTP_PROXY),
         ],
     )
     mock_parse_args.assert_called_once()
     # Currently --enable-devtools flag alone doesn't enable eventing core
     mock_event_manager.assert_not_called()
     if _env_threadless_compliant():
         mock_executor_pool.assert_called_once()
         mock_executor_pool.return_value.setup.assert_called_once()
     mock_acceptor_pool.assert_called_once()
     mock_acceptor_pool.return_value.setup.assert_called_once()
     mock_listener.return_value.setup.assert_called_once()
예제 #4
0
    def test_modify_post_data_plugin(self,
                                     mock_server_conn: mock.Mock) -> None:
        original = b'{"key": "value"}'
        modified = b'{"key": "modified"}'

        self._conn.recv.return_value = build_http_request(
            b'POST',
            b'http://httpbin.org/post',
            headers={
                b'Host': b'httpbin.org',
                b'Content-Type': b'application/x-www-form-urlencoded',
                b'Content-Length': bytes_(len(original)),
            },
            body=original)
        self.mock_selector.return_value.select.side_effect = [
            [(selectors.SelectorKey(fileobj=self._conn,
                                    fd=self._conn.fileno,
                                    events=selectors.EVENT_READ,
                                    data=None), selectors.EVENT_READ)],
        ]

        self.protocol_handler.run_once()
        mock_server_conn.assert_called_with('httpbin.org', DEFAULT_HTTP_PORT)
        mock_server_conn.return_value.queue.assert_called_with(
            build_http_request(b'POST',
                               b'/post',
                               headers={
                                   b'Host': b'httpbin.org',
                                   b'Content-Length': bytes_(len(modified)),
                                   b'Content-Type': b'application/json',
                                   b'Via':
                                   b'1.1 %s' % PROXY_AGENT_HEADER_VALUE,
                               },
                               body=modified))
예제 #5
0
 def _setUp(self, mocker: MockerFixture) -> None:
     self.mock_fromfd = mocker.patch('socket.fromfd')
     self.mock_selector = mocker.patch('selectors.DefaultSelector')
     self.fileno = 10
     self._addr = ('127.0.0.1', 54382)
     self._conn = self.mock_fromfd.return_value
     # Setup a static directory
     self.static_server_dir = os.path.join(tempfile.gettempdir(), 'static')
     self.index_file_path = os.path.join(
         self.static_server_dir, 'index.html',
     )
     self.html_file_content = b'''<html><head></head><body><h1>Proxy.py Testing</h1></body></html>'''
     os.makedirs(self.static_server_dir, exist_ok=True)
     with open(self.index_file_path, 'wb') as f:
         f.write(self.html_file_content)
     #
     flags = FlagParser.initialize(
         enable_static_server=True,
         static_server_dir=self.static_server_dir,
         threaded=True,
     )
     flags.plugins = Plugins.load([
         bytes_(PLUGIN_HTTP_PROXY),
         bytes_(PLUGIN_WEB_SERVER),
     ])
     self.protocol_handler = HttpProtocolHandler(
         HttpClientConnection(self._conn, self._addr),
         flags=flags,
     )
     self.protocol_handler.initialize()
예제 #6
0
    def test_authenticated_proxy_http_tunnel(self,
                                             mock_server_connection: mock.Mock,
                                             mock_fromfd: mock.Mock,
                                             mock_selector: mock.Mock) -> None:
        server = mock_server_connection.return_value
        server.connect.return_value = True
        server.buffer_size.return_value = 0
        self._conn = mock_fromfd.return_value
        self.mock_selector_for_client_read_read_server_write(
            mock_selector, server)

        flags = Proxy.initialize(auth_code=base64.b64encode(b'user:pass'))
        flags.plugins = Proxy.load_plugins(
            [bytes_(PLUGIN_HTTP_PROXY),
             bytes_(PLUGIN_WEB_SERVER)])

        self.protocol_handler = HttpProtocolHandler(TcpClientConnection(
            self._conn, self._addr),
                                                    flags=flags)
        self.protocol_handler.initialize()

        assert self.http_server_port is not None
        self._conn.recv.return_value = CRLF.join([
            b'CONNECT localhost:%d HTTP/1.1' % self.http_server_port,
            b'Host: localhost:%d' % self.http_server_port,
            b'User-Agent: proxy.py/%s' % bytes_(__version__),
            b'Proxy-Connection: Keep-Alive',
            b'Proxy-Authorization: Basic dXNlcjpwYXNz', CRLF
        ])
        self.assert_tunnel_response(mock_server_connection, server)
        self.protocol_handler.client.flush()
        self.assert_data_queued_to_server(server)

        self.protocol_handler.run_once()
        server.flush.assert_called_once()
예제 #7
0
 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 = Proxy.initialize()
     flags.plugins = Proxy.load_plugins([
         bytes_(PLUGIN_HTTP_PROXY),
         bytes_(PLUGIN_WEB_SERVER),
     ])
     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)
예제 #8
0
    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'''<html><head></head><body><h1>Proxy.py Testing</h1></body></html>'''
        os.makedirs(static_server_dir, exist_ok=True)
        with open(index_file_path, 'wb') as f:
            f.write(html_file_content)

        self._conn = mock_fromfd.return_value
        self._conn.recv.return_value = build_http_request(
            b'GET', b'/index.html')

        mock_selector.return_value.select.side_effect = [
            [(selectors.SelectorKey(
                fileobj=self._conn,
                fd=self._conn.fileno,
                events=selectors.EVENT_READ,
                data=None), selectors.EVENT_READ)],
            [(selectors.SelectorKey(
                fileobj=self._conn,
                fd=self._conn.fileno,
                events=selectors.EVENT_WRITE,
                data=None), selectors.EVENT_WRITE)], ]

        flags = Proxy.initialize(
            enable_static_server=True,
            static_server_dir=static_server_dir)
        flags.plugins = Proxy.load_plugins([
            bytes_(PLUGIN_HTTP_PROXY),
            bytes_(PLUGIN_WEB_SERVER),
        ])

        self.protocol_handler = HttpProtocolHandler(
            TcpClientConnection(self._conn, self._addr),
            flags=flags)
        self.protocol_handler.initialize()

        self.protocol_handler.run_once()
        self.protocol_handler.run_once()

        self.assertEqual(mock_selector.return_value.select.call_count, 2)
        self.assertEqual(self._conn.send.call_count, 1)
        encoded_html_file_content = gzip.compress(html_file_content)
        self.assertEqual(self._conn.send.call_args[0][0], build_http_response(
            200, reason=b'OK', headers={
                b'Content-Type': b'text/html',
                b'Cache-Control': b'max-age=86400',
                b'Content-Encoding': b'gzip',
                b'Connection': b'close',
                b'Content-Length': bytes_(len(encoded_html_file_content)),
            },
            body=encoded_html_file_content
        ))
예제 #9
0
 def test_valid_ipv6_in_request_line(self) -> None:
     self.parser.parse(
         bytes_(
             'CONNECT [2001:db8:3333:4444:CCCC:DDDD:EEEE:FFFF]:443 HTTP/1.1\r\n',
         ), )
     self.assertTrue(self.parser.is_https_tunnel)
     self.assertEqual(
         self.parser.host,
         bytes_('[2001:db8:3333:4444:CCCC:DDDD:EEEE:FFFF]', ),
     )
     self.assertEqual(self.parser.port, 443)
예제 #10
0
 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 = Proxy.initialize()
     self.flags.plugins = Proxy.load_plugins([
         bytes_(PLUGIN_HTTP_PROXY),
         bytes_(PLUGIN_WEB_SERVER),
     ])
     self.protocol_handler = HttpProtocolHandler(
         TcpClientConnection(self._conn, self._addr),
         flags=self.flags)
     self.protocol_handler.initialize()
예제 #11
0
 def init_and_make_pac_file_request(self, pac_file: str) -> None:
     flags = Proxy.initialize(pac_file=pac_file)
     flags.plugins = Proxy.load_plugins([
         bytes_(PLUGIN_HTTP_PROXY),
         bytes_(PLUGIN_WEB_SERVER),
         bytes_(PLUGIN_PAC_FILE),
     ])
     self.protocol_handler = HttpProtocolHandler(
         TcpClientConnection(self._conn, self._addr),
         flags=flags)
     self.protocol_handler.initialize()
     self._conn.recv.return_value = CRLF.join([
         b'GET / HTTP/1.1',
         CRLF,
     ])
예제 #12
0
    async def test_authenticated_proxy_http_get(self) -> None:
        self._conn = self.mock_fromfd.return_value
        mock_selector_for_client_read(self)

        server = self.mock_server_connection.return_value
        server.connect.return_value = True
        server.buffer_size.return_value = 0

        flags = FlagParser.initialize(
            auth_code=base64.b64encode(b'user:pass'),
            threaded=True,
        )
        flags.plugins = Plugins.load([
            bytes_(PLUGIN_HTTP_PROXY),
            bytes_(PLUGIN_WEB_SERVER),
        ])

        self.protocol_handler = HttpProtocolHandler(
            HttpClientConnection(self._conn, self._addr),
            flags=flags,
        )
        self.protocol_handler.initialize()
        assert self.http_server_port is not None

        self._conn.recv.return_value = b'GET http://localhost:%d HTTP/1.1' % self.http_server_port
        await self.protocol_handler._run_once()
        self.assertEqual(
            self.protocol_handler.request.state,
            httpParserStates.INITIALIZED,
        )

        self._conn.recv.return_value = CRLF
        await self.protocol_handler._run_once()
        self.assertEqual(
            self.protocol_handler.request.state,
            httpParserStates.LINE_RCVD,
        )

        assert self.http_server_port is not None
        self._conn.recv.return_value = CRLF.join([
            b'User-Agent: proxy.py/%s' % bytes_(__version__),
            b'Host: localhost:%d' % self.http_server_port,
            b'Accept: */*',
            httpHeaders.PROXY_CONNECTION + b': Keep-Alive',
            httpHeaders.PROXY_AUTHORIZATION + b': Basic dXNlcjpwYXNz',
            CRLF,
        ])
        await self.assert_data_queued(server)
예제 #13
0
    def test_proposed_rest_api_plugin(self,
                                      mock_server_conn: mock.Mock) -> None:
        path = b'/v1/users/'
        self._conn.recv.return_value = build_http_request(
            b'GET',
            b'http://%s%s' % (ProposedRestApiPlugin.API_SERVER, path),
            headers={
                b'Host': ProposedRestApiPlugin.API_SERVER,
            })
        self.mock_selector.return_value.select.side_effect = [
            [(selectors.SelectorKey(fileobj=self._conn,
                                    fd=self._conn.fileno,
                                    events=selectors.EVENT_READ,
                                    data=None), selectors.EVENT_READ)],
        ]
        self.protocol_handler.run_once()

        mock_server_conn.assert_not_called()
        self.assertEqual(
            self.protocol_handler.client.buffer[0].tobytes(),
            build_http_response(
                httpStatusCodes.OK,
                reason=b'OK',
                headers={b'Content-Type': b'application/json'},
                body=bytes_(
                    json.dumps(ProposedRestApiPlugin.REST_API_SPEC[path]))))
예제 #14
0
    def test_http_get(self, mock_server_connection: mock.Mock) -> None:
        server = mock_server_connection.return_value
        server.connect.return_value = True
        server.buffer_size.return_value = 0
        self.mock_selector_for_client_read_read_server_write(
            self.mock_selector, server)

        # Send request line
        assert self.http_server_port is not None
        self._conn.recv.return_value = (b'GET http://localhost:%d HTTP/1.1' %
                                        self.http_server_port) + CRLF
        self.protocol_handler.run_once()
        self.assertEqual(self.protocol_handler.request.state,
                         httpParserStates.LINE_RCVD)
        self.assertNotEqual(self.protocol_handler.request.state,
                            httpParserStates.COMPLETE)

        # Send headers and blank line, thus completing HTTP request
        assert self.http_server_port is not None
        self._conn.recv.return_value = CRLF.join([
            b'User-Agent: proxy.py/%s' % bytes_(__version__),
            b'Host: localhost:%d' % self.http_server_port, b'Accept: */*',
            b'Proxy-Connection: Keep-Alive', CRLF
        ])
        self.assert_data_queued(mock_server_connection, server)
        self.protocol_handler.run_once()
        server.flush.assert_called_once()
예제 #15
0
 def _setUp(self, mocker: MockerFixture) -> None:
     self.mock_fromfd = mocker.patch('socket.fromfd')
     self.mock_selector = mocker.patch('selectors.DefaultSelector')
     self.fileno = 10
     self._addr = ('127.0.0.1', 54382)
     self._conn = self.mock_fromfd.return_value
     self.flags = FlagParser.initialize(threaded=True)
     self.flags.plugins = Plugins.load([
         bytes_(PLUGIN_HTTP_PROXY),
         bytes_(PLUGIN_WEB_SERVER),
     ])
     self.protocol_handler = HttpProtocolHandler(
         HttpClientConnection(self._conn, self._addr),
         flags=self.flags,
     )
     self.protocol_handler.initialize()
예제 #16
0
 def assert_data_queued(self, mock_server_connection: mock.Mock,
                        server: mock.Mock) -> None:
     self.protocol_handler.run_once()
     self.assertEqual(self.protocol_handler.request.state,
                      httpParserStates.COMPLETE)
     mock_server_connection.assert_called_once()
     server.connect.assert_called_once()
     server.closed = False
     assert self.http_server_port is not None
     pkt = CRLF.join([
         b'GET / HTTP/1.1',
         b'User-Agent: proxy.py/%s' % bytes_(__version__),
         b'Host: localhost:%d' % self.http_server_port, b'Accept: */*',
         b'Via: 1.1 proxy.py v%s' % bytes_(__version__), CRLF
     ])
     server.queue.assert_called_once_with(pkt)
     server.buffer_size.return_value = len(pkt)
예제 #17
0
 def test_unique_plugin_from_bytes(self) -> None:
     self.flags = Proxy.initialize([],
                                   plugins=[
                                       bytes_(PLUGIN_HTTP_PROXY),
                                   ])
     self.assert_plugins({'HttpProtocolHandlerPlugin': [
         HttpProxyPlugin,
     ]})
예제 #18
0
 def handle_upstream_chunk(self, chunk: memoryview) -> memoryview:
     self.response.parse(chunk.tobytes())
     if self.response.state == httpParserStates.COMPLETE:
         self.handle_response_data()
         new_content_length = len(self.response.body)
         bytes_header_name = b'content-length'
         self.response.add_header(bytes_header_name, bytes_(new_content_length))
         self.client.queue(memoryview(self.response.build_response()))
     return memoryview(b'')
예제 #19
0
    def test_authenticated_proxy_http_get(self,
                                          mock_server_connection: mock.Mock,
                                          mock_fromfd: mock.Mock,
                                          mock_selector: mock.Mock) -> None:
        self._conn = mock_fromfd.return_value
        self.mock_selector_for_client_read(mock_selector)

        server = mock_server_connection.return_value
        server.connect.return_value = True
        server.buffer_size.return_value = 0

        flags = Proxy.initialize(auth_code=base64.b64encode(b'user:pass'))
        flags.plugins = Proxy.load_plugins([
            bytes_(PLUGIN_HTTP_PROXY),
            bytes_(PLUGIN_WEB_SERVER),
        ])

        self.protocol_handler = HttpProtocolHandler(TcpClientConnection(
            self._conn, self._addr),
                                                    flags=flags)
        self.protocol_handler.initialize()
        assert self.http_server_port is not None

        self._conn.recv.return_value = b'GET http://localhost:%d HTTP/1.1' % self.http_server_port
        self.protocol_handler.run_once()
        self.assertEqual(self.protocol_handler.request.state,
                         httpParserStates.INITIALIZED)

        self._conn.recv.return_value = CRLF
        self.protocol_handler.run_once()
        self.assertEqual(self.protocol_handler.request.state,
                         httpParserStates.LINE_RCVD)

        assert self.http_server_port is not None
        self._conn.recv.return_value = CRLF.join([
            b'User-Agent: proxy.py/%s' % bytes_(__version__),
            b'Host: localhost:%d' % self.http_server_port, b'Accept: */*',
            b'Proxy-Connection: Keep-Alive',
            b'Proxy-Authorization: Basic dXNlcjpwYXNz', CRLF
        ])
        self.assert_data_queued(mock_server_connection, server)
예제 #20
0
 def test_proxy_authentication_failed(self, mock_fromfd: mock.Mock,
                                      mock_selector: mock.Mock) -> None:
     self._conn = mock_fromfd.return_value
     self.mock_selector_for_client_read(mock_selector)
     flags = Proxy.initialize(auth_code=base64.b64encode(b'user:pass'))
     flags.plugins = Proxy.load_plugins([
         bytes_(PLUGIN_HTTP_PROXY),
         bytes_(PLUGIN_WEB_SERVER),
         bytes_(PLUGIN_PROXY_AUTH),
     ])
     self.protocol_handler = HttpProtocolHandler(TcpClientConnection(
         self._conn, self._addr),
                                                 flags=flags)
     self.protocol_handler.initialize()
     self._conn.recv.return_value = CRLF.join([
         b'GET http://abhinavsingh.com HTTP/1.1', b'Host: abhinavsingh.com',
         CRLF
     ])
     self.protocol_handler.run_once()
     self.assertEqual(self.protocol_handler.client.buffer[0],
                      ProxyAuthenticationFailed.RESPONSE_PKT)
 def test_build_response_adds_content_length_header(self) -> None:
     body = b'Hello world!!!'
     self.assertEqual(
         build_http_response(200,
                             reason=b'OK',
                             protocol_version=b'HTTP/1.1',
                             headers={b'key': b'value'},
                             body=body),
         CRLF.join([
             b'HTTP/1.1 200 OK', b'key: value',
             b'Content-Length: ' + bytes_(len(body)), CRLF
         ]) + body)
예제 #22
0
 def test_modify_post_data_plugin(self) -> None:
     original = b'{"key": "value"}'
     modified = b'{"key": "modified"}'
     self.client_ssl_connection.recv.return_value = build_http_request(
         b'POST',
         b'/',
         headers={
             b'Host': b'uni.corn',
             b'Content-Type': b'application/x-www-form-urlencoded',
             b'Content-Length': bytes_(len(original)),
         },
         body=original)
     self.protocol_handler.run_once()
     self.server.queue.assert_called_with(
         build_http_request(b'POST',
                            b'/',
                            headers={
                                b'Host': b'uni.corn',
                                b'Content-Length': bytes_(len(modified)),
                                b'Content-Type': b'application/json',
                            },
                            body=modified))
예제 #23
0
    def test_http_tunnel(self, mock_server_connection: mock.Mock) -> None:
        server = mock_server_connection.return_value
        server.connect.return_value = True

        def has_buffer() -> bool:
            return cast(bool, server.queue.called)

        server.has_buffer.side_effect = has_buffer
        self.mock_selector.return_value.select.side_effect = [
            [
                (selectors.SelectorKey(fileobj=self._conn,
                                       fd=self._conn.fileno,
                                       events=selectors.EVENT_READ,
                                       data=None), selectors.EVENT_READ),
            ],
            [
                (selectors.SelectorKey(fileobj=self._conn,
                                       fd=self._conn.fileno,
                                       events=0,
                                       data=None), selectors.EVENT_WRITE),
            ],
            [
                (selectors.SelectorKey(fileobj=self._conn,
                                       fd=self._conn.fileno,
                                       events=selectors.EVENT_READ,
                                       data=None), selectors.EVENT_READ),
            ],
            [
                (selectors.SelectorKey(fileobj=server.connection,
                                       fd=server.connection.fileno,
                                       events=0,
                                       data=None), selectors.EVENT_WRITE),
            ],
        ]

        assert self.http_server_port is not None
        self._conn.recv.return_value = CRLF.join([
            b'CONNECT localhost:%d HTTP/1.1' % self.http_server_port,
            b'Host: localhost:%d' % self.http_server_port,
            b'User-Agent: proxy.py/%s' % bytes_(__version__),
            b'Proxy-Connection: Keep-Alive', CRLF
        ])
        self.assert_tunnel_response(mock_server_connection, server)

        # Dispatch tunnel established response to client
        self.protocol_handler.run_once()
        self.assert_data_queued_to_server(server)

        self.protocol_handler.run_once()
        self.assertEqual(server.queue.call_count, 1)
        server.flush.assert_called_once()
예제 #24
0
    def test_static_web_server_serves_404(
            self,
            mock_fromfd: mock.Mock,
            mock_selector: mock.Mock) -> None:
        self._conn = mock_fromfd.return_value
        self._conn.recv.return_value = build_http_request(
            b'GET', b'/not-found.html')

        mock_selector.return_value.select.side_effect = [
            [(selectors.SelectorKey(
                fileobj=self._conn,
                fd=self._conn.fileno,
                events=selectors.EVENT_READ,
                data=None), selectors.EVENT_READ)],
            [(selectors.SelectorKey(
                fileobj=self._conn,
                fd=self._conn.fileno,
                events=selectors.EVENT_WRITE,
                data=None), selectors.EVENT_WRITE)], ]

        flags = Proxy.initialize(enable_static_server=True)
        flags.plugins = Proxy.load_plugins([
            bytes_(PLUGIN_HTTP_PROXY),
            bytes_(PLUGIN_WEB_SERVER),
        ])

        self.protocol_handler = HttpProtocolHandler(
            TcpClientConnection(self._conn, self._addr),
            flags=flags)
        self.protocol_handler.initialize()

        self.protocol_handler.run_once()
        self.protocol_handler.run_once()

        self.assertEqual(mock_selector.return_value.select.call_count, 2)
        self.assertEqual(self._conn.send.call_count, 1)
        self.assertEqual(self._conn.send.call_args[0][0],
                         HttpWebServerPlugin.DEFAULT_404_RESPONSE)
예제 #25
0
    async def test_authenticated_proxy_http_tunnel(self) -> None:
        server = self.mock_server_connection.return_value
        server.connect.return_value = True
        server.buffer_size.return_value = 0
        self._conn = self.mock_fromfd.return_value
        self.mock_selector_for_client_read_and_server_write(server)

        flags = FlagParser.initialize(
            auth_code=base64.b64encode(b'user:pass'),
            threaded=True,
        )
        flags.plugins = Plugins.load([
            bytes_(PLUGIN_HTTP_PROXY),
            bytes_(PLUGIN_WEB_SERVER),
        ])

        self.protocol_handler = HttpProtocolHandler(
            HttpClientConnection(self._conn, self._addr),
            flags=flags,
        )
        self.protocol_handler.initialize()

        assert self.http_server_port is not None
        self._conn.recv.return_value = CRLF.join([
            b'CONNECT localhost:%d HTTP/1.1' % self.http_server_port,
            b'Host: localhost:%d' % self.http_server_port,
            b'User-Agent: proxy.py/%s' % bytes_(__version__),
            httpHeaders.PROXY_CONNECTION + b': Keep-Alive',
            httpHeaders.PROXY_AUTHORIZATION + b': Basic dXNlcjpwYXNz',
            CRLF,
        ])
        await self.assert_tunnel_response(server)
        self.protocol_handler.work.flush()
        await self.assert_data_queued_to_server(server)

        await self.protocol_handler._run_once()
        server.flush.assert_called_once()
예제 #26
0
    def test_entry_point(
            self,
            mock_listener_pool: mock.Mock,
            mock_executor_pool: mock.Mock,
            mock_acceptor_pool: mock.Mock,
            mock_event_manager: mock.Mock,
            mock_initialize: mock.Mock,
            mock_sleep: mock.Mock,
            mock_open: mock.Mock,
            mock_exists: mock.Mock,
            mock_remove: mock.Mock,
    ) -> None:
        pid_file = os.path.join(tempfile.gettempdir(), 'pid')
        mock_sleep.side_effect = KeyboardInterrupt()
        mock_initialize.return_value.unix_socket_path = None
        mock_initialize.return_value.local_executor = 0
        mock_initialize.return_value.enable_events = False
        mock_initialize.return_value.pid_file = pid_file
        mock_initialize.return_value.port_file = None
        mock_initialize.return_value.enable_ssh_tunnel = False
        entry_point()
        mock_event_manager.assert_not_called()
        mock_listener_pool.assert_called_once_with(
            flags=mock_initialize.return_value,
        )
        mock_executor_pool.assert_called_once_with(
            flags=mock_initialize.return_value,
            event_queue=None,
            executor_klass=RemoteFdExecutor,
        )
        mock_acceptor_pool.assert_called_once_with(
            flags=mock_initialize.return_value,
            listeners=mock_listener_pool.return_value,
            executor_queues=mock_executor_pool.return_value.work_queues,
            executor_pids=mock_executor_pool.return_value.work_pids,
            executor_locks=mock_executor_pool.return_value.work_locks,
            event_queue=None,
        )
        mock_acceptor_pool.return_value.setup.assert_called_once()
        mock_acceptor_pool.return_value.shutdown.assert_called_once()
        mock_listener_pool.return_value.shutdown.assert_called_once()
        mock_sleep.assert_called()

        mock_open.assert_called_with(pid_file, 'wb')
        mock_open.return_value.__enter__.return_value.write.assert_called_with(
            bytes_(os.getpid()),
        )
        mock_exists.assert_called_with(pid_file)
        mock_remove.assert_called_with(pid_file)
예제 #27
0
 async def test_default_web_server_returns_404(self) -> None:
     self._conn = self.mock_fromfd.return_value
     self.mock_selector.return_value.select.return_value = [
         (
             selectors.SelectorKey(
                 fileobj=self._conn.fileno(),
                 fd=self._conn.fileno(),
                 events=selectors.EVENT_READ,
                 data=None,
             ),
             selectors.EVENT_READ,
         ),
     ]
     flags = FlagParser.initialize(threaded=True)
     flags.plugins = Plugins.load([
         bytes_(PLUGIN_HTTP_PROXY),
         bytes_(PLUGIN_WEB_SERVER),
     ])
     self.protocol_handler = HttpProtocolHandler(
         HttpClientConnection(self._conn, self._addr),
         flags=flags,
     )
     self.protocol_handler.initialize()
     self._conn.recv.return_value = CRLF.join([
         b'GET /hello HTTP/1.1',
         CRLF,
     ])
     await self.protocol_handler._run_once()
     self.assertEqual(
         self.protocol_handler.request.state,
         httpParserStates.COMPLETE,
     )
     self.assertEqual(
         self.protocol_handler.work.buffer[0],
         NOT_FOUND_RESPONSE_PKT,
     )
예제 #28
0
    async def test_static_web_server_serves(self) -> None:
        self._conn.recv.return_value = build_http_request(
            b'GET', b'/index.html',
        )
        self.mock_selector.return_value.select.side_effect = [
            [(
                selectors.SelectorKey(
                    fileobj=self._conn.fileno(),
                    fd=self._conn.fileno(),
                    events=selectors.EVENT_READ,
                    data=None,
                ),
                selectors.EVENT_READ,
            )],
            [(
                selectors.SelectorKey(
                    fileobj=self._conn.fileno(),
                    fd=self._conn.fileno(),
                    events=selectors.EVENT_WRITE,
                    data=None,
                ),
                selectors.EVENT_WRITE,
            )],
        ]
        await self.protocol_handler._run_once()
        await self.protocol_handler._run_once()

        self.assertEqual(self.mock_selector.return_value.select.call_count, 2)
        self.assertEqual(self._conn.send.call_count, 1)
        encoded_html_file_content = gzip.compress(self.html_file_content)

        # parse response and verify
        response = HttpParser(httpParserTypes.RESPONSE_PARSER)
        response.parse(self._conn.send.call_args[0][0])
        self.assertEqual(response.code, b'200')
        self.assertEqual(response.header(b'content-type'), b'text/html')
        self.assertEqual(response.header(b'cache-control'), b'max-age=86400')
        self.assertEqual(response.header(b'content-encoding'), b'gzip')
        self.assertEqual(response.header(b'connection'), b'close')
        self.assertEqual(
            response.header(b'content-length'),
            bytes_(len(encoded_html_file_content)),
        )
        assert response.body
        self.assertEqual(
            gzip.decompress(response.body),
            self.html_file_content,
        )
예제 #29
0
    def assert_data_queued_to_server(self, server: mock.Mock) -> None:
        assert self.http_server_port is not None
        self.assertEqual(self._conn.send.call_args[0][0],
                         HttpProxyPlugin.PROXY_TUNNEL_ESTABLISHED_RESPONSE_PKT)

        pkt = CRLF.join([
            b'GET / HTTP/1.1',
            b'Host: localhost:%d' % self.http_server_port,
            b'User-Agent: proxy.py/%s' % bytes_(__version__), CRLF
        ])

        self._conn.recv.return_value = pkt
        self.protocol_handler.run_once()

        server.queue.assert_called_once_with(pkt)
        server.buffer_size.return_value = len(pkt)
        server.flush.assert_not_called()
예제 #30
0
 def handle_client_request(self,
                           request: HttpParser) -> Optional[HttpParser]:
     if request.method == httpMethods.GET:
         path_str = text_(request.path)
         downloaded = 0
         match = re.search('(?:^|\\?|&)downloaded=(\\d+)(?:&|$)', path_str)
         if match:
             downloaded = int(match.group(1))
         match = re.search('(?:^|\\?|&)uploaded=(\\d+)(?:&|$)', path_str)
         if match:
             uploaded = int(downloaded *
                            1.2 if downloaded > 0 else int(match.group(1)) *
                            1.2)
             path_str = path_str[:match.start(1)] + str(
                 uploaded) + path_str[match.end(1):]
             request.path = bytes_(path_str)
     return request