inline/move http.make_* functions

This commit is contained in:
Maximilian Hils 2021-02-04 02:59:01 +01:00
parent 9409bf0368
commit d68c364b35
7 changed files with 99 additions and 114 deletions

View File

@ -7,7 +7,6 @@ from typing import Tuple
import ldap3 import ldap3
import passlib.apache import passlib.apache
import mitmproxy.net.http
from mitmproxy import ctx from mitmproxy import ctx
from mitmproxy import exceptions from mitmproxy import exceptions
from mitmproxy import http from mitmproxy import http
@ -83,15 +82,23 @@ class ProxyAuth:
def auth_required_response(self) -> http.Response: def auth_required_response(self) -> http.Response:
if self.is_proxy_auth(): if self.is_proxy_auth():
return http.make_error_response( status_code = status_codes.PROXY_AUTH_REQUIRED
status_codes.PROXY_AUTH_REQUIRED, headers = {"Proxy-Authenticate": f'Basic realm="{REALM}"'}
headers=mitmproxy.http.Headers(Proxy_Authenticate=f'Basic realm="{REALM}"'),
)
else: else:
return http.make_error_response( status_code = status_codes.UNAUTHORIZED
status_codes.UNAUTHORIZED, headers = {"WWW-Authenticate": f'Basic realm="{REALM}"'}
headers=mitmproxy.http.Headers(WWW_Authenticate=f'Basic realm="{REALM}"'),
) reason = http.status_codes.RESPONSES[status_code]
return http.Response.make(
status_code,
(
f"<html>"
f"<head><title>{status_code} {reason}</title></head>"
f"<body><h1>{status_code} {reason}</h1></body>"
f"</html>"
),
headers
)
def check(self, f: http.HTTPFlow) -> Optional[Tuple[str, str]]: def check(self, f: http.HTTPFlow) -> Optional[Tuple[str, str]]:
""" """

View File

@ -1,4 +1,4 @@
import html import re
import re import re
import time import time
import urllib.parse import urllib.parse
@ -13,14 +13,13 @@ from typing import Optional
from typing import Tuple from typing import Tuple
from typing import Union from typing import Union
from mitmproxy.net.http import url
from mitmproxy import flow from mitmproxy import flow
from mitmproxy import version
from mitmproxy.coretypes import multidict from mitmproxy.coretypes import multidict
from mitmproxy.coretypes import serializable from mitmproxy.coretypes import serializable
from mitmproxy.net import http, encoding from mitmproxy.net import encoding
from mitmproxy.net.http import cookies, multipart from mitmproxy.net.http import cookies, multipart
from mitmproxy.net.http import status_codes from mitmproxy.net.http import status_codes
from mitmproxy.net.http import url
from mitmproxy.net.http.headers import assemble_content_type, parse_content_type from mitmproxy.net.http.headers import assemble_content_type, parse_content_type
from mitmproxy.proxy import context from mitmproxy.proxy import context
from mitmproxy.utils import human from mitmproxy.utils import human
@ -1158,69 +1157,10 @@ class HTTPFlow(flow.Flow):
return f return f
def make_error_response( __all__ = [
status_code: int, "HTTPFlow",
message: str = "", "Message",
headers: Optional[Headers] = None, "Request",
) -> Response: "Response",
body: bytes = """ "Headers",
<html> ]
<head>
<title>{status_code} {reason}</title>
</head>
<body>
<h1>{status_code} {reason}</h1>
<p>{message}</p>
</body>
</html>
""".strip().format(
status_code=status_code,
reason=http.status_codes.RESPONSES.get(status_code, "Unknown"),
message=html.escape(message),
).encode("utf8", "replace")
if not headers:
headers = Headers(
Server=version.MITMPROXY,
Connection="close",
Content_Length=str(len(body)),
Content_Type="text/html"
)
return Response.make(status_code, body, headers)
def make_connect_request(address: Tuple[str, int]) -> Request:
return Request(
host=address[0],
port=address[1],
method=b"CONNECT",
scheme=b"",
authority=f"{address[0]}:{address[1]}".encode(),
path=b"",
http_version=b"HTTP/1.1",
headers=Headers(),
content=b"",
trailers=None,
timestamp_start=time.time(),
timestamp_end=time.time(),
)
def make_connect_response(http_version):
# Do not send any response headers as it breaks proxying non-80 ports on
# Android emulators using the -http-proxy option.
return Response(
http_version,
200,
b"Connection established",
Headers(),
b"",
None,
time.time(),
time.time(),
)
def make_expect_continue_response():
return Response.make(100)

View File

@ -449,7 +449,18 @@ class HttpStream(layer.Layer):
def handle_connect_finish(self): def handle_connect_finish(self):
if not self.flow.response: if not self.flow.response:
self.flow.response = http.make_connect_response(self.flow.request.data.http_version) # Do not send any response headers as it breaks proxying non-80 ports on
# Android emulators using the -http-proxy option.
self.flow.response = http.Response(
self.flow.request.data.http_version,
200,
b"Connection established",
http.Headers(),
b"",
None,
time.time(),
time.time(),
)
if 200 <= self.flow.response.status_code < 300: if 200 <= self.flow.response.status_code < 300:
yield SendHttp(ResponseHeaders(self.stream_id, self.flow.response), self.context.client) yield SendHttp(ResponseHeaders(self.stream_id, self.flow.response), self.context.client)

View File

@ -1,11 +1,12 @@
import abc import abc
import html
from typing import Union, Optional, Callable, Type from typing import Union, Optional, Callable, Type
import h11 import h11
from h11._readers import ChunkedReader, ContentLengthReader, Http10Reader from h11._readers import ChunkedReader, ContentLengthReader, Http10Reader
from h11._receivebuffer import ReceiveBuffer from h11._receivebuffer import ReceiveBuffer
from mitmproxy import http from mitmproxy import http, version
from mitmproxy.net.http import http1, status_codes from mitmproxy.net.http import http1, status_codes
from mitmproxy.proxy import commands, events, layer from mitmproxy.proxy import commands, events, layer
from mitmproxy.proxy.context import Connection, ConnectionState, Context from mitmproxy.proxy.context import Connection, ConnectionState, Context
@ -145,13 +146,13 @@ class Http1Connection(HttpConnection, metaclass=abc.ABCMeta):
yield from self.make_pipe() yield from self.make_pipe()
return return
connection_done = ( connection_done = (
http1.expected_http_body_size(self.request, self.response) == -1 http1.expected_http_body_size(self.request, self.response) == -1
or http1.connection_close(self.request.http_version, self.request.headers) or http1.connection_close(self.request.http_version, self.request.headers)
or http1.connection_close(self.response.http_version, self.response.headers) or http1.connection_close(self.response.http_version, self.response.headers)
# If we proxy HTTP/2 to HTTP/1, we only use upstream connections for one request. # If we proxy HTTP/2 to HTTP/1, we only use upstream connections for one request.
# This simplifies our connection management quite a bit as we can rely on # This simplifies our connection management quite a bit as we can rely on
# the proxyserver's max-connection-per-server throttling. # the proxyserver's max-connection-per-server throttling.
or (self.request.is_http2 and isinstance(self, Http1Client)) or (self.request.is_http2 and isinstance(self, Http1Client))
) )
if connection_done: if connection_done:
yield commands.CloseConnection(self.conn) yield commands.CloseConnection(self.conn)
@ -211,7 +212,7 @@ class Http1Server(Http1Connection):
yield from self.mark_done(response=True) yield from self.mark_done(response=True)
elif isinstance(event, ResponseProtocolError): elif isinstance(event, ResponseProtocolError):
if not self.response: if not self.response:
resp = http.make_error_response(event.code, event.message) resp = make_error_response(event.code, event.message)
raw = http1.assemble_response(resp) raw = http1.assemble_response(resp)
yield commands.SendData(self.conn, raw) yield commands.SendData(self.conn, raw)
yield commands.CloseConnection(self.conn) yield commands.CloseConnection(self.conn)
@ -362,6 +363,37 @@ def make_body_reader(expected_size: Optional[int]) -> TBodyReader:
return ContentLengthReader(expected_size) return ContentLengthReader(expected_size)
def make_error_response(
status_code: int,
message: str = "",
) -> http.Response:
body: bytes = """
<html>
<head>
<title>{status_code} {reason}</title>
</head>
<body>
<h1>{status_code} {reason}</h1>
<p>{message}</p>
</body>
</html>
""".strip().format(
status_code=status_code,
reason=http.status_codes.RESPONSES.get(status_code, "Unknown"),
message=html.escape(message),
).encode("utf8", "replace")
return http.Response.make(
status_code,
body,
http.Headers(
Server=version.MITMPROXY,
Connection="close",
Content_Type="text/html",
)
)
__all__ = [ __all__ = [
"Http1Client", "Http1Client",
"Http1Server", "Http1Server",

View File

@ -1,3 +1,4 @@
import time
from typing import Optional, Tuple from typing import Optional, Tuple
from h11._receivebuffer import ReceiveBuffer from h11._receivebuffer import ReceiveBuffer
@ -44,7 +45,20 @@ class HttpUpstreamProxy(tunnel.TunnelLayer):
if not self.send_connect: if not self.send_connect:
return (yield from super().start_handshake()) return (yield from super().start_handshake())
assert self.conn.address assert self.conn.address
req = http.make_connect_request(self.conn.address) req = http.Request(
host=self.conn.address[0],
port=self.conn.address[1],
method=b"CONNECT",
scheme=b"",
authority=f"{self.conn.address[0]}:{self.conn.address[1]}".encode(),
path=b"",
http_version=b"HTTP/1.1",
headers=http.Headers(),
content=b"",
trailers=None,
timestamp_start=time.time(),
timestamp_end=time.time(),
)
raw = http1.assemble_request(req) raw = http1.assemble_request(req)
yield commands.SendData(self.tunnel_connection, raw) yield commands.SendData(self.tunnel_connection, raw)

View File

@ -5,6 +5,7 @@ from mitmproxy.proxy.commands import SendData
from mitmproxy.proxy.events import DataReceived from mitmproxy.proxy.events import DataReceived
from mitmproxy.proxy.layers.http import Http1Server, ReceiveHttp, RequestHeaders, RequestEndOfMessage, \ from mitmproxy.proxy.layers.http import Http1Server, ReceiveHttp, RequestHeaders, RequestEndOfMessage, \
ResponseHeaders, ResponseEndOfMessage, RequestData, Http1Client, ResponseData ResponseHeaders, ResponseEndOfMessage, RequestData, Http1Client, ResponseData
from mitmproxy.proxy.layers.http._http1 import make_error_response
from test.mitmproxy.proxy.tutils import Placeholder, Playbook from test.mitmproxy.proxy.tutils import Placeholder, Playbook
@ -199,3 +200,7 @@ class TestClient:
>> RequestHeaders(3, req, True) >> RequestHeaders(3, req, True)
<< SendData(tctx.server, Placeholder(bytes)) << SendData(tctx.server, Placeholder(bytes))
) )
def test_make_error_response():
assert make_error_response(543, 'foobar')

View File

@ -197,27 +197,3 @@ class TestHTTPFlow:
def test_timestamp_start(self): def test_timestamp_start(self):
f = tflow.tflow() f = tflow.tflow()
assert f.timestamp_start == f.request.timestamp_start assert f.timestamp_start == f.request.timestamp_start
def test_make_error_response():
resp = http.make_error_response(543, 'foobar', Headers())
assert resp
def test_make_connect_request():
req = http.make_connect_request(('invalidhost', 1234))
assert req.first_line_format == 'authority'
assert req.method == 'CONNECT'
assert req.http_version == 'HTTP/1.1'
def test_make_connect_response():
resp = http.make_connect_response('foobar')
assert resp.http_version == 'foobar'
assert resp.status_code == 200
def test_expect_continue_response():
resp = http.make_expect_continue_response()
assert resp.http_version == 'HTTP/1.1'
assert resp.status_code == 100