mirror of
https://github.com/Grasscutters/mitmproxy.git
synced 2025-02-07 10:40:09 +00:00
update docs, mostly revert 2f44b26b4c
This commit is contained in:
parent
f2570c773a
commit
2a6337343a
@ -29,46 +29,45 @@ The new header will be added to all responses passing through the proxy.
|
|||||||
Called once on startup, before any other events.
|
Called once on startup, before any other events.
|
||||||
|
|
||||||
|
|
||||||
### clientconnect(ScriptContext, ClientConnect)
|
### clientconnect(ScriptContext, ConnectionHandler)
|
||||||
|
|
||||||
Called when a client initiates a connection to the proxy. Note that
|
Called when a client initiates a connection to the proxy. Note that
|
||||||
a connection can correspond to multiple HTTP requests.
|
a connection can correspond to multiple HTTP requests.
|
||||||
|
|
||||||
|
### serverconnect(ScriptContext, ConnectionHandler)
|
||||||
### serverconnect(ScriptContext, ServerConnection)
|
|
||||||
|
|
||||||
Called when the proxy initiates a connection to the target server. Note that
|
Called when the proxy initiates a connection to the target server. Note that
|
||||||
a connection can correspond to multiple HTTP requests.
|
a connection can correspond to multiple HTTP requests.
|
||||||
|
|
||||||
### request(ScriptContext, Flow)
|
### request(ScriptContext, HTTPFlow)
|
||||||
|
|
||||||
Called when a client request has been received. The __Flow__ object is
|
Called when a client request has been received. The __HTTPFlow__ object is
|
||||||
guaranteed to have a non-None __request__ attribute.
|
guaranteed to have a non-None __request__ attribute.
|
||||||
|
|
||||||
### responseheaders(ScriptContext, Flow)
|
### responseheaders(ScriptContext, HTTPFlow)
|
||||||
|
|
||||||
Called when the headers of a server response have been received.
|
Called when the headers of a server response have been received.
|
||||||
This will always be called before the response hook.
|
This will always be called before the response hook.
|
||||||
The __Flow__ object is guaranteed to have non-None __request__ and
|
The __HTTPFlow__ object is guaranteed to have non-None __request__ and
|
||||||
__response__ attributes. __response.content__ will not be valid,
|
__response__ attributes. __response.content__ will be None,
|
||||||
as the response body has not been read yet.
|
as the response body has not been read yet.
|
||||||
|
|
||||||
### response(ScriptContext, Flow)
|
### response(ScriptContext, HTTPFlow)
|
||||||
|
|
||||||
Called when a server response has been received. The __Flow__ object is
|
Called when a server response has been received. The __HTTPFlow__ object is
|
||||||
guaranteed to have non-None __request__ and __response__ attributes.
|
guaranteed to have non-None __request__ and __response__ attributes.
|
||||||
Note that if response streaming is enabled for this response,
|
Note that if response streaming is enabled for this response,
|
||||||
__response.content__ will not contain the response body.
|
__response.content__ will not contain the response body.
|
||||||
|
|
||||||
### error(ScriptContext, Flow)
|
### error(ScriptContext, HTTPFlow)
|
||||||
|
|
||||||
Called when a flow error has occurred, e.g. invalid server responses, or
|
Called when a flow error has occurred, e.g. invalid server responses, or
|
||||||
interrupted connections. This is distinct from a valid server HTTP error
|
interrupted connections. This is distinct from a valid server HTTP error
|
||||||
response, which is simply a response with an HTTP error code. The __Flow__
|
response, which is simply a response with an HTTP error code. The __HTTPFlow__
|
||||||
object is guaranteed to have non-None __request__ and __error__ attributes.
|
object is guaranteed to have non-None __request__ and __error__ attributes.
|
||||||
|
|
||||||
|
|
||||||
### clientdisconnect(ScriptContext, ClientDisconnect)
|
### clientdisconnect(ScriptContext, ConnectionHandler)
|
||||||
|
|
||||||
Called when a client disconnects from the proxy.
|
Called when a client disconnects from the proxy.
|
||||||
|
|
||||||
@ -96,22 +95,10 @@ The main classes you will deal with in writing mitmproxy scripts are:
|
|||||||
<th>libmproxy.proxy.connection.ServerConnection</th>
|
<th>libmproxy.proxy.connection.ServerConnection</th>
|
||||||
<td>Describes a server connection.</td>
|
<td>Describes a server connection.</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
|
||||||
<th>libmproxy.protocol.primitives.Error</th>
|
|
||||||
<td>A communications error.</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
<tr>
|
||||||
<th>libmproxy.protocol.http.HTTPFlow</th>
|
<th>libmproxy.protocol.http.HTTPFlow</th>
|
||||||
<td>A collection of objects representing a single HTTP transaction.</td>
|
<td>A collection of objects representing a single HTTP transaction.</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
|
||||||
<th>libmproxy.flow.ODict</th>
|
|
||||||
|
|
||||||
<td>A dictionary-like object for managing sets of key/value data. There
|
|
||||||
is also a variant called CaselessODict that ignores key case for some
|
|
||||||
calls (used mainly for headers).
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
<tr>
|
||||||
<th>libmproxy.protocol.http.HTTPResponse</th>
|
<th>libmproxy.protocol.http.HTTPResponse</th>
|
||||||
<td>An HTTP response.</td>
|
<td>An HTTP response.</td>
|
||||||
@ -120,10 +107,22 @@ The main classes you will deal with in writing mitmproxy scripts are:
|
|||||||
<th>libmproxy.protocol.http.HTTPRequest</th>
|
<th>libmproxy.protocol.http.HTTPRequest</th>
|
||||||
<td>An HTTP request.</td>
|
<td>An HTTP request.</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>libmproxy.protocol.primitives.Error</th>
|
||||||
|
<td>A communications error.</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>libmproxy.script.ScriptContext</th>
|
<th>libmproxy.script.ScriptContext</th>
|
||||||
<td> A handle for interacting with mitmproxy's from within scripts.</td>
|
<td> A handle for interacting with mitmproxy's from within scripts.</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>libmproxy.flow.ODict</th>
|
||||||
|
|
||||||
|
<td>A dictionary-like object for managing sets of key/value data. There
|
||||||
|
is also a variant called CaselessODict that ignores key case for some
|
||||||
|
calls (used mainly for headers).
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>libmproxy.certutils.SSLCert</th>
|
<th>libmproxy.certutils.SSLCert</th>
|
||||||
<td>Exposes information SSL certificates.</td>
|
<td>Exposes information SSL certificates.</td>
|
||||||
@ -161,9 +160,9 @@ flows from a file (see the "scripted data transformation" example on the
|
|||||||
one-shot script on a single flow through the _|_ (pipe) shortcut in mitmproxy.
|
one-shot script on a single flow through the _|_ (pipe) shortcut in mitmproxy.
|
||||||
|
|
||||||
In this case, there are no client connections, and the events are run in the
|
In this case, there are no client connections, and the events are run in the
|
||||||
following order: __start__, __request__, __response__, __error__, __done__. If
|
following order: __start__, __request__, __responseheaders__, __response__, __error__, __done__. If
|
||||||
the flow doesn't have a __response__ or __error__ associated with it, the
|
the flow doesn't have a __response__ or __error__ associated with it, the
|
||||||
matching event will be skipped.
|
matching events will be skipped.
|
||||||
|
|
||||||
## Spaces in the script path
|
## Spaces in the script path
|
||||||
By default, spaces are interpreted as separator between the inline script and its arguments (e.g. <code>-s "foo.py
|
By default, spaces are interpreted as separator between the inline script and its arguments (e.g. <code>-s "foo.py
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
def response(context, flow):
|
def response(ctx, flow):
|
||||||
flow.response.headers["newheader"] = ["foo"]
|
flow.response.headers["newheader"] = ["foo"]
|
@ -1,4 +1,4 @@
|
|||||||
def request(ctx, flow):
|
def request(ctx, flow):
|
||||||
f = ctx.duplicate_flow(flow)
|
f = ctx.duplicate_flow(flow)
|
||||||
f.request.path = "/changed"
|
f.request.path = "/changed"
|
||||||
ctx.replay_request(f)
|
ctx.replay_request(f)
|
@ -3,11 +3,14 @@
|
|||||||
This example shows how to build a proxy based on mitmproxy's Flow
|
This example shows how to build a proxy based on mitmproxy's Flow
|
||||||
primitives.
|
primitives.
|
||||||
|
|
||||||
|
Heads Up: In the majority of cases, you want to use inline scripts.
|
||||||
|
|
||||||
Note that request and response messages are not automatically replied to,
|
Note that request and response messages are not automatically replied to,
|
||||||
so we need to implement handlers to do this.
|
so we need to implement handlers to do this.
|
||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
from libmproxy import proxy, flow
|
from libmproxy import flow, proxy
|
||||||
|
from libmproxy.proxy.server import ProxyServer
|
||||||
|
|
||||||
class MyMaster(flow.FlowMaster):
|
class MyMaster(flow.FlowMaster):
|
||||||
def run(self):
|
def run(self):
|
||||||
@ -31,9 +34,9 @@ class MyMaster(flow.FlowMaster):
|
|||||||
|
|
||||||
|
|
||||||
config = proxy.ProxyConfig(
|
config = proxy.ProxyConfig(
|
||||||
cacert = os.path.expanduser("~/.mitmproxy/mitmproxy-ca.pem")
|
ca_file = os.path.expanduser("~/.mitmproxy/mitmproxy-ca.pem")
|
||||||
)
|
)
|
||||||
state = flow.State()
|
state = flow.State()
|
||||||
server = proxy.ProxyServer(config, 8080)
|
server = ProxyServer(config, 8080)
|
||||||
m = MyMaster(server, state)
|
m = MyMaster(server, state)
|
||||||
m.run()
|
m.run()
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
|
|
||||||
def request(context, flow):
|
def request(ctx, flow):
|
||||||
if "application/x-www-form-urlencoded" in flow.request.headers["content-type"]:
|
if "application/x-www-form-urlencoded" in flow.request.headers["content-type"]:
|
||||||
frm = flow.request.form_urlencoded
|
form = flow.request.get_form_urlencoded()
|
||||||
frm["mitmproxy"] = ["rocks"]
|
form["mitmproxy"] = ["rocks"]
|
||||||
flow.request.form_urlencoded = frm
|
flow.request.set_form_urlencoded(form)
|
||||||
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
|||||||
|
|
||||||
def request(context, flow):
|
def request(ctx, flow):
|
||||||
q = flow.request.query
|
q = flow.request.get_query()
|
||||||
if q:
|
if q:
|
||||||
q["mitmproxy"] = ["rocks"]
|
q["mitmproxy"] = ["rocks"]
|
||||||
flow.request.query = q
|
flow.request.set_query(q)
|
||||||
|
|
@ -1,6 +1,7 @@
|
|||||||
import time
|
import time
|
||||||
from libmproxy.script import concurrent
|
from libmproxy.script import concurrent
|
||||||
|
|
||||||
|
|
||||||
@concurrent
|
@concurrent
|
||||||
def request(context, flow):
|
def request(context, flow):
|
||||||
print "handle request: %s%s" % (flow.request.host, flow.request.path)
|
print "handle request: %s%s" % (flow.request.host, flow.request.path)
|
||||||
|
@ -6,7 +6,9 @@ This example shows two ways to redirect flows to other destinations.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def request(context, flow):
|
def request(ctx, flow):
|
||||||
|
# pretty_host(hostheader=True) takes the Host: header of the request into account,
|
||||||
|
# which is useful in transparent mode where we usually only have the IP otherwise.
|
||||||
if flow.request.pretty_host(hostheader=True).endswith("example.com"):
|
if flow.request.pretty_host(hostheader=True).endswith("example.com"):
|
||||||
resp = HTTPResponse(
|
resp = HTTPResponse(
|
||||||
[1, 1], 200, "OK",
|
[1, 1], 200, "OK",
|
||||||
|
@ -5,8 +5,10 @@ implement functionality similar to the "sticky cookies" option. This is at
|
|||||||
a lower level than the Flow mechanism, so we're dealing directly with
|
a lower level than the Flow mechanism, so we're dealing directly with
|
||||||
request and response objects.
|
request and response objects.
|
||||||
"""
|
"""
|
||||||
from libmproxy import controller, proxy
|
|
||||||
import os
|
import os
|
||||||
|
from libmproxy import controller, proxy
|
||||||
|
from libmproxy.proxy.server import ProxyServer
|
||||||
|
|
||||||
|
|
||||||
class StickyMaster(controller.Master):
|
class StickyMaster(controller.Master):
|
||||||
def __init__(self, server):
|
def __init__(self, server):
|
||||||
@ -35,8 +37,8 @@ class StickyMaster(controller.Master):
|
|||||||
|
|
||||||
|
|
||||||
config = proxy.ProxyConfig(
|
config = proxy.ProxyConfig(
|
||||||
cacert = os.path.expanduser("~/.mitmproxy/mitmproxy-ca.pem")
|
ca_file = os.path.expanduser("~/.mitmproxy/mitmproxy-ca.pem")
|
||||||
)
|
)
|
||||||
server = proxy.ProxyServer(config, 8080)
|
server = ProxyServer(config, 8080)
|
||||||
m = StickyMaster(server)
|
m = StickyMaster(server)
|
||||||
m.run()
|
m.run()
|
||||||
|
@ -7,14 +7,14 @@ def start(ctx, argv):
|
|||||||
"""
|
"""
|
||||||
ctx.log("start")
|
ctx.log("start")
|
||||||
|
|
||||||
def clientconnect(ctx, client_connect):
|
def clientconnect(ctx, conn_handler):
|
||||||
"""
|
"""
|
||||||
Called when a client initiates a connection to the proxy. Note that a
|
Called when a client initiates a connection to the proxy. Note that a
|
||||||
connection can correspond to multiple HTTP requests
|
connection can correspond to multiple HTTP requests
|
||||||
"""
|
"""
|
||||||
ctx.log("clientconnect")
|
ctx.log("clientconnect")
|
||||||
|
|
||||||
def serverconnect(ctx, server_connection):
|
def serverconnect(ctx, conn_handler):
|
||||||
"""
|
"""
|
||||||
Called when the proxy initiates a connection to the target server. Note that a
|
Called when the proxy initiates a connection to the target server. Note that a
|
||||||
connection can correspond to multiple HTTP requests
|
connection can correspond to multiple HTTP requests
|
||||||
@ -50,7 +50,7 @@ def error(ctx, flow):
|
|||||||
"""
|
"""
|
||||||
ctx.log("error")
|
ctx.log("error")
|
||||||
|
|
||||||
def clientdisconnect(ctx, client_disconnect):
|
def clientdisconnect(ctx, conn_handler):
|
||||||
"""
|
"""
|
||||||
Called when a client disconnects from the proxy.
|
Called when a client disconnects from the proxy.
|
||||||
"""
|
"""
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import cStringIO
|
import cStringIO
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
def response(context, flow):
|
def response(ctx, flow):
|
||||||
if flow.response.headers["content-type"] == ["image/png"]:
|
if flow.response.headers["content-type"] == ["image/png"]:
|
||||||
s = cStringIO.StringIO(flow.response.content)
|
s = cStringIO.StringIO(flow.response.content)
|
||||||
img = Image.open(s).rotate(180)
|
img = Image.open(s).rotate(180)
|
||||||
|
@ -554,17 +554,17 @@ class FlowView(common.WWrap):
|
|||||||
conn.headers = flow.ODictCaseless(lst)
|
conn.headers = flow.ODictCaseless(lst)
|
||||||
|
|
||||||
def set_query(self, lst, conn):
|
def set_query(self, lst, conn):
|
||||||
conn.query = flow.ODict(lst)
|
conn.set_query(flow.ODict(lst))
|
||||||
|
|
||||||
def set_path_components(self, lst, conn):
|
def set_path_components(self, lst, conn):
|
||||||
conn.path_components = [i[0] for i in lst]
|
conn.set_path_components([i[0] for i in lst])
|
||||||
|
|
||||||
def set_form(self, lst, conn):
|
def set_form(self, lst, conn):
|
||||||
conn.form_urlencoded = flow.ODict(lst)
|
conn.set_form_urlencoded(flow.ODict(lst))
|
||||||
|
|
||||||
def edit_form(self, conn):
|
def edit_form(self, conn):
|
||||||
self.master.view_grideditor(
|
self.master.view_grideditor(
|
||||||
grideditor.URLEncodedFormEditor(self.master, conn.form_urlencoded.lst, self.set_form, conn)
|
grideditor.URLEncodedFormEditor(self.master, conn.get_form_urlencoded().lst, self.set_form, conn)
|
||||||
)
|
)
|
||||||
|
|
||||||
def edit_form_confirm(self, key, conn):
|
def edit_form_confirm(self, key, conn):
|
||||||
@ -589,7 +589,7 @@ class FlowView(common.WWrap):
|
|||||||
c = self.master.spawn_editor(conn.content or "")
|
c = self.master.spawn_editor(conn.content or "")
|
||||||
conn.content = c.rstrip("\n") # what?
|
conn.content = c.rstrip("\n") # what?
|
||||||
elif part == "f":
|
elif part == "f":
|
||||||
if not conn.form_urlencoded and conn.content:
|
if not conn.get_form_urlencoded() and conn.content:
|
||||||
self.master.prompt_onekey(
|
self.master.prompt_onekey(
|
||||||
"Existing body is not a URL-encoded form. Clear and edit?",
|
"Existing body is not a URL-encoded form. Clear and edit?",
|
||||||
[
|
[
|
||||||
@ -604,11 +604,11 @@ class FlowView(common.WWrap):
|
|||||||
elif part == "h":
|
elif part == "h":
|
||||||
self.master.view_grideditor(grideditor.HeaderEditor(self.master, conn.headers.lst, self.set_headers, conn))
|
self.master.view_grideditor(grideditor.HeaderEditor(self.master, conn.headers.lst, self.set_headers, conn))
|
||||||
elif part == "p":
|
elif part == "p":
|
||||||
p = conn.path_components
|
p = conn.get_path_components()
|
||||||
p = [[i] for i in p]
|
p = [[i] for i in p]
|
||||||
self.master.view_grideditor(grideditor.PathEditor(self.master, p, self.set_path_components, conn))
|
self.master.view_grideditor(grideditor.PathEditor(self.master, p, self.set_path_components, conn))
|
||||||
elif part == "q":
|
elif part == "q":
|
||||||
self.master.view_grideditor(grideditor.QueryEditor(self.master, conn.query.lst, self.set_query, conn))
|
self.master.view_grideditor(grideditor.QueryEditor(self.master, conn.get_query().lst, self.set_query, conn))
|
||||||
elif part == "u" and self.state.view_flow_mode == common.VIEW_FLOW_REQUEST:
|
elif part == "u" and self.state.view_flow_mode == common.VIEW_FLOW_REQUEST:
|
||||||
self.master.prompt_edit("URL", conn.url, self.set_url)
|
self.master.prompt_edit("URL", conn.url, self.set_url)
|
||||||
elif part == "m" and self.state.view_flow_mode == common.VIEW_FLOW_REQUEST:
|
elif part == "m" and self.state.view_flow_mode == common.VIEW_FLOW_REQUEST:
|
||||||
|
@ -29,13 +29,13 @@ def get_line(fp):
|
|||||||
def send_connect_request(conn, host, port, update_state=True):
|
def send_connect_request(conn, host, port, update_state=True):
|
||||||
upstream_request = HTTPRequest("authority", "CONNECT", None, host, port, None,
|
upstream_request = HTTPRequest("authority", "CONNECT", None, host, port, None,
|
||||||
(1, 1), ODictCaseless(), "")
|
(1, 1), ODictCaseless(), "")
|
||||||
conn.send(upstream_request._assemble())
|
conn.send(upstream_request.assemble())
|
||||||
resp = HTTPResponse.from_stream(conn.rfile, upstream_request.method)
|
resp = HTTPResponse.from_stream(conn.rfile, upstream_request.method)
|
||||||
if resp.code != 200:
|
if resp.code != 200:
|
||||||
raise proxy.ProxyError(resp.code,
|
raise proxy.ProxyError(resp.code,
|
||||||
"Cannot establish SSL " +
|
"Cannot establish SSL " +
|
||||||
"connection with upstream proxy: \r\n" +
|
"connection with upstream proxy: \r\n" +
|
||||||
str(resp._assemble()))
|
str(resp.assemble()))
|
||||||
if update_state:
|
if update_state:
|
||||||
conn.state.append(("http", {
|
conn.state.append(("http", {
|
||||||
"state": "connect",
|
"state": "connect",
|
||||||
@ -73,6 +73,9 @@ class decoded(object):
|
|||||||
|
|
||||||
|
|
||||||
class HTTPMessage(stateobject.SimpleStateObject):
|
class HTTPMessage(stateobject.SimpleStateObject):
|
||||||
|
"""
|
||||||
|
Base class for HTTPRequest and HTTPResponse
|
||||||
|
"""
|
||||||
def __init__(self, httpversion, headers, content, timestamp_start=None,
|
def __init__(self, httpversion, headers, content, timestamp_start=None,
|
||||||
timestamp_end=None):
|
timestamp_end=None):
|
||||||
self.httpversion = httpversion
|
self.httpversion = httpversion
|
||||||
@ -162,31 +165,31 @@ class HTTPMessage(stateobject.SimpleStateObject):
|
|||||||
"""
|
"""
|
||||||
Parse an HTTP message from a file stream
|
Parse an HTTP message from a file stream
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError # pragma: nocover
|
raise NotImplementedError() # pragma: nocover
|
||||||
|
|
||||||
def _assemble_first_line(self):
|
def _assemble_first_line(self):
|
||||||
"""
|
"""
|
||||||
Returns the assembled request/response line
|
Returns the assembled request/response line
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError # pragma: nocover
|
raise NotImplementedError() # pragma: nocover
|
||||||
|
|
||||||
def _assemble_headers(self):
|
def _assemble_headers(self):
|
||||||
"""
|
"""
|
||||||
Returns the assembled headers
|
Returns the assembled headers
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError # pragma: nocover
|
raise NotImplementedError() # pragma: nocover
|
||||||
|
|
||||||
def _assemble_head(self):
|
def _assemble_head(self):
|
||||||
"""
|
"""
|
||||||
Returns the assembled request/response line plus headers
|
Returns the assembled request/response line plus headers
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError # pragma: nocover
|
raise NotImplementedError() # pragma: nocover
|
||||||
|
|
||||||
def _assemble(self):
|
def assemble(self):
|
||||||
"""
|
"""
|
||||||
Returns the assembled request/response
|
Returns the assembled request/response
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError # pragma: nocover
|
raise NotImplementedError() # pragma: nocover
|
||||||
|
|
||||||
|
|
||||||
class HTTPRequest(HTTPMessage):
|
class HTTPRequest(HTTPMessage):
|
||||||
@ -195,7 +198,17 @@ class HTTPRequest(HTTPMessage):
|
|||||||
|
|
||||||
Exposes the following attributes:
|
Exposes the following attributes:
|
||||||
|
|
||||||
flow: Flow object the request belongs to
|
method: HTTP method
|
||||||
|
|
||||||
|
scheme: URL scheme (http/https) (absolute-form only)
|
||||||
|
|
||||||
|
host: Host portion of the URL (absolute-form and authority-form only)
|
||||||
|
|
||||||
|
port: Destination port (absolute-form and authority-form only)
|
||||||
|
|
||||||
|
path: Path portion of the URL (not present in authority-form)
|
||||||
|
|
||||||
|
httpversion: HTTP version tuple, e.g. (1,1)
|
||||||
|
|
||||||
headers: ODictCaseless object
|
headers: ODictCaseless object
|
||||||
|
|
||||||
@ -211,18 +224,6 @@ class HTTPRequest(HTTPMessage):
|
|||||||
|
|
||||||
form_out: The request form which mitmproxy has send out to the destination
|
form_out: The request form which mitmproxy has send out to the destination
|
||||||
|
|
||||||
method: HTTP method
|
|
||||||
|
|
||||||
scheme: URL scheme (http/https) (absolute-form only)
|
|
||||||
|
|
||||||
host: Host portion of the URL (absolute-form and authority-form only)
|
|
||||||
|
|
||||||
port: Destination port (absolute-form and authority-form only)
|
|
||||||
|
|
||||||
path: Path portion of the URL (not present in authority-form)
|
|
||||||
|
|
||||||
httpversion: HTTP version tuple
|
|
||||||
|
|
||||||
timestamp_start: Timestamp indicating when request transmission started
|
timestamp_start: Timestamp indicating when request transmission started
|
||||||
|
|
||||||
timestamp_end: Timestamp indicating when request transmission ended
|
timestamp_end: Timestamp indicating when request transmission ended
|
||||||
@ -364,7 +365,7 @@ class HTTPRequest(HTTPMessage):
|
|||||||
def _assemble_head(self, form=None):
|
def _assemble_head(self, form=None):
|
||||||
return "%s\r\n%s\r\n" % (self._assemble_first_line(form), self._assemble_headers())
|
return "%s\r\n%s\r\n" % (self._assemble_first_line(form), self._assemble_headers())
|
||||||
|
|
||||||
def _assemble(self, form=None):
|
def assemble(self, form=None):
|
||||||
"""
|
"""
|
||||||
Assembles the request for transmission to the server. We make some
|
Assembles the request for transmission to the server. We make some
|
||||||
modifications to make sure interception works properly.
|
modifications to make sure interception works properly.
|
||||||
@ -417,8 +418,7 @@ class HTTPRequest(HTTPMessage):
|
|||||||
"""
|
"""
|
||||||
self.headers["Host"] = [self.host]
|
self.headers["Host"] = [self.host]
|
||||||
|
|
||||||
@property
|
def get_form_urlencoded(self):
|
||||||
def form_urlencoded(self):
|
|
||||||
"""
|
"""
|
||||||
Retrieves the URL-encoded form data, returning an ODict object.
|
Retrieves the URL-encoded form data, returning an ODict object.
|
||||||
Returns an empty ODict if there is no data or the content-type
|
Returns an empty ODict if there is no data or the content-type
|
||||||
@ -428,8 +428,7 @@ class HTTPRequest(HTTPMessage):
|
|||||||
return ODict(utils.urldecode(self.content))
|
return ODict(utils.urldecode(self.content))
|
||||||
return ODict([])
|
return ODict([])
|
||||||
|
|
||||||
@form_urlencoded.setter
|
def set_form_urlencoded(self, odict):
|
||||||
def form_urlencoded(self, odict):
|
|
||||||
"""
|
"""
|
||||||
Sets the body to the URL-encoded form data, and adds the
|
Sets the body to the URL-encoded form data, and adds the
|
||||||
appropriate content-type header. Note that this will destory the
|
appropriate content-type header. Note that this will destory the
|
||||||
@ -440,8 +439,7 @@ class HTTPRequest(HTTPMessage):
|
|||||||
self.headers["Content-Type"] = [HDR_FORM_URLENCODED]
|
self.headers["Content-Type"] = [HDR_FORM_URLENCODED]
|
||||||
self.content = utils.urlencode(odict.lst)
|
self.content = utils.urlencode(odict.lst)
|
||||||
|
|
||||||
@property
|
def get_path_components(self):
|
||||||
def path_components(self):
|
|
||||||
"""
|
"""
|
||||||
Returns the path components of the URL as a list of strings.
|
Returns the path components of the URL as a list of strings.
|
||||||
|
|
||||||
@ -450,8 +448,7 @@ class HTTPRequest(HTTPMessage):
|
|||||||
_, _, path, _, _, _ = urlparse.urlparse(self.url)
|
_, _, path, _, _, _ = urlparse.urlparse(self.url)
|
||||||
return [urllib.unquote(i) for i in path.split("/") if i]
|
return [urllib.unquote(i) for i in path.split("/") if i]
|
||||||
|
|
||||||
@path_components.setter
|
def set_path_components(self, lst):
|
||||||
def path_components(self, lst):
|
|
||||||
"""
|
"""
|
||||||
Takes a list of strings, and sets the path component of the URL.
|
Takes a list of strings, and sets the path component of the URL.
|
||||||
|
|
||||||
@ -462,8 +459,7 @@ class HTTPRequest(HTTPMessage):
|
|||||||
scheme, netloc, _, params, query, fragment = urlparse.urlparse(self.url)
|
scheme, netloc, _, params, query, fragment = urlparse.urlparse(self.url)
|
||||||
self.url = urlparse.urlunparse([scheme, netloc, path, params, query, fragment])
|
self.url = urlparse.urlunparse([scheme, netloc, path, params, query, fragment])
|
||||||
|
|
||||||
@property
|
def get_query(self):
|
||||||
def query(self):
|
|
||||||
"""
|
"""
|
||||||
Gets the request query string. Returns an ODict object.
|
Gets the request query string. Returns an ODict object.
|
||||||
"""
|
"""
|
||||||
@ -472,8 +468,7 @@ class HTTPRequest(HTTPMessage):
|
|||||||
return ODict(utils.urldecode(query))
|
return ODict(utils.urldecode(query))
|
||||||
return ODict([])
|
return ODict([])
|
||||||
|
|
||||||
@query.setter
|
def set_query(self, odict):
|
||||||
def query(self, odict):
|
|
||||||
"""
|
"""
|
||||||
Takes an ODict object, and sets the request query string.
|
Takes an ODict object, and sets the request query string.
|
||||||
"""
|
"""
|
||||||
@ -528,8 +523,7 @@ class HTTPRequest(HTTPMessage):
|
|||||||
raise ValueError("Invalid URL: %s" % url)
|
raise ValueError("Invalid URL: %s" % url)
|
||||||
self.scheme, self.host, self.port, self.path = parts
|
self.scheme, self.host, self.port, self.path = parts
|
||||||
|
|
||||||
@property
|
def get_cookies(self):
|
||||||
def cookies(self):
|
|
||||||
cookie_headers = self.headers.get("cookie")
|
cookie_headers = self.headers.get("cookie")
|
||||||
if not cookie_headers:
|
if not cookie_headers:
|
||||||
return None
|
return None
|
||||||
@ -560,7 +554,7 @@ class HTTPResponse(HTTPMessage):
|
|||||||
|
|
||||||
Exposes the following attributes:
|
Exposes the following attributes:
|
||||||
|
|
||||||
flow: Flow object the request belongs to
|
httpversion: HTTP version tuple, e.g. (1,1)
|
||||||
|
|
||||||
code: HTTP response code
|
code: HTTP response code
|
||||||
|
|
||||||
@ -572,8 +566,6 @@ class HTTPResponse(HTTPMessage):
|
|||||||
is content associated, but not present. CONTENT_MISSING evaluates
|
is content associated, but not present. CONTENT_MISSING evaluates
|
||||||
to False to make checking for the presence of content natural.
|
to False to make checking for the presence of content natural.
|
||||||
|
|
||||||
httpversion: HTTP version tuple
|
|
||||||
|
|
||||||
timestamp_start: Timestamp indicating when request transmission started
|
timestamp_start: Timestamp indicating when request transmission started
|
||||||
|
|
||||||
timestamp_end: Timestamp indicating when request transmission ended
|
timestamp_end: Timestamp indicating when request transmission ended
|
||||||
@ -661,7 +653,7 @@ class HTTPResponse(HTTPMessage):
|
|||||||
return '%s\r\n%s\r\n' % (
|
return '%s\r\n%s\r\n' % (
|
||||||
self._assemble_first_line(), self._assemble_headers(preserve_transfer_encoding=preserve_transfer_encoding))
|
self._assemble_first_line(), self._assemble_headers(preserve_transfer_encoding=preserve_transfer_encoding))
|
||||||
|
|
||||||
def _assemble(self):
|
def assemble(self):
|
||||||
"""
|
"""
|
||||||
Assembles the response for transmission to the client. We make some
|
Assembles the response for transmission to the client. We make some
|
||||||
modifications to make sure interception works properly.
|
modifications to make sure interception works properly.
|
||||||
@ -726,8 +718,7 @@ class HTTPResponse(HTTPMessage):
|
|||||||
if c:
|
if c:
|
||||||
self.headers["set-cookie"] = c
|
self.headers["set-cookie"] = c
|
||||||
|
|
||||||
@property
|
def get_cookies(self):
|
||||||
def cookies(self):
|
|
||||||
cookie_headers = self.headers.get("set-cookie")
|
cookie_headers = self.headers.get("set-cookie")
|
||||||
if not cookie_headers:
|
if not cookie_headers:
|
||||||
return None
|
return None
|
||||||
@ -745,12 +736,14 @@ class HTTPResponse(HTTPMessage):
|
|||||||
|
|
||||||
class HTTPFlow(Flow):
|
class HTTPFlow(Flow):
|
||||||
"""
|
"""
|
||||||
A Flow is a collection of objects representing a single HTTP
|
A HTTPFlow is a collection of objects representing a single HTTP
|
||||||
transaction. The main attributes are:
|
transaction. The main attributes are:
|
||||||
|
|
||||||
request: HTTPRequest object
|
request: HTTPRequest object
|
||||||
response: HTTPResponse object
|
response: HTTPResponse object
|
||||||
error: Error object
|
error: Error object
|
||||||
|
server_conn: ServerConnection object
|
||||||
|
client_conn: ClientConnection object
|
||||||
|
|
||||||
Note that it's possible for a Flow to have both a response and an error
|
Note that it's possible for a Flow to have both a response and an error
|
||||||
object. This might happen, for instance, when a response was received
|
object. This might happen, for instance, when a response was received
|
||||||
@ -866,6 +859,10 @@ class HttpAuthenticationError(Exception):
|
|||||||
|
|
||||||
|
|
||||||
class HTTPHandler(ProtocolHandler):
|
class HTTPHandler(ProtocolHandler):
|
||||||
|
"""
|
||||||
|
HTTPHandler implements mitmproxys understanding of the HTTP protocol.
|
||||||
|
|
||||||
|
"""
|
||||||
def __init__(self, c):
|
def __init__(self, c):
|
||||||
super(HTTPHandler, self).__init__(c)
|
super(HTTPHandler, self).__init__(c)
|
||||||
self.expected_form_in = c.config.http_form_in
|
self.expected_form_in = c.config.http_form_in
|
||||||
@ -878,7 +875,7 @@ class HTTPHandler(ProtocolHandler):
|
|||||||
|
|
||||||
def get_response_from_server(self, request, include_body=True):
|
def get_response_from_server(self, request, include_body=True):
|
||||||
self.c.establish_server_connection()
|
self.c.establish_server_connection()
|
||||||
request_raw = request._assemble()
|
request_raw = request.assemble()
|
||||||
|
|
||||||
for i in range(2):
|
for i in range(2):
|
||||||
try:
|
try:
|
||||||
@ -957,7 +954,7 @@ class HTTPHandler(ProtocolHandler):
|
|||||||
if not flow.response.stream:
|
if not flow.response.stream:
|
||||||
# no streaming:
|
# no streaming:
|
||||||
# we already received the full response from the server and can send it to the client straight away.
|
# we already received the full response from the server and can send it to the client straight away.
|
||||||
self.c.client_conn.send(flow.response._assemble())
|
self.c.client_conn.send(flow.response.assemble())
|
||||||
else:
|
else:
|
||||||
# streaming:
|
# streaming:
|
||||||
# First send the body and then transfer the response incrementally:
|
# First send the body and then transfer the response incrementally:
|
||||||
@ -1225,7 +1222,7 @@ class RequestReplayThread(threading.Thread):
|
|||||||
server.establish_ssl(self.config.clientcerts, sni=r.host)
|
server.establish_ssl(self.config.clientcerts, sni=r.host)
|
||||||
r.form_out = "relative"
|
r.form_out = "relative"
|
||||||
|
|
||||||
server.send(r._assemble())
|
server.send(r.assemble())
|
||||||
self.flow.response = HTTPResponse.from_stream(server.rfile, r.method,
|
self.flow.response = HTTPResponse.from_stream(server.rfile, r.method,
|
||||||
body_size_limit=self.config.body_size_limit)
|
body_size_limit=self.config.body_size_limit)
|
||||||
self.channel.ask("response", self.flow)
|
self.channel.ask("response", self.flow)
|
||||||
|
@ -12,9 +12,9 @@ class Error(stateobject.SimpleStateObject):
|
|||||||
"""
|
"""
|
||||||
An Error.
|
An Error.
|
||||||
|
|
||||||
This is distinct from an HTTP error response (say, a code 500), which
|
This is distinct from an protocol error response (say, a HTTP code 500), which
|
||||||
is represented by a normal Response object. This class is responsible
|
is represented by a normal HTTPResponse object. This class is responsible
|
||||||
for indicating errors that fall outside of normal HTTP communications,
|
for indicating errors that fall outside of normal protocol communications,
|
||||||
like interrupted connections, timeouts, protocol errors.
|
like interrupted connections, timeouts, protocol errors.
|
||||||
|
|
||||||
Exposes the following attributes:
|
Exposes the following attributes:
|
||||||
@ -52,6 +52,10 @@ class Error(stateobject.SimpleStateObject):
|
|||||||
|
|
||||||
|
|
||||||
class Flow(stateobject.SimpleStateObject):
|
class Flow(stateobject.SimpleStateObject):
|
||||||
|
"""
|
||||||
|
A Flow is a collection of objects representing a single transaction.
|
||||||
|
This class is usually subclassed for each protocol, e.g. HTTPFlow.
|
||||||
|
"""
|
||||||
def __init__(self, conntype, client_conn, server_conn, live=None):
|
def __init__(self, conntype, client_conn, server_conn, live=None):
|
||||||
self.conntype = conntype
|
self.conntype = conntype
|
||||||
self.client_conn = client_conn
|
self.client_conn = client_conn
|
||||||
@ -117,6 +121,10 @@ class Flow(stateobject.SimpleStateObject):
|
|||||||
|
|
||||||
|
|
||||||
class ProtocolHandler(object):
|
class ProtocolHandler(object):
|
||||||
|
"""
|
||||||
|
A ProtocolHandler implements an application-layer protocol, e.g. HTTP.
|
||||||
|
See: libmproxy.protocol.http.HTTPHandler
|
||||||
|
"""
|
||||||
def __init__(self, c):
|
def __init__(self, c):
|
||||||
self.c = c
|
self.c = c
|
||||||
"""@type: libmproxy.proxy.server.ConnectionHandler"""
|
"""@type: libmproxy.proxy.server.ConnectionHandler"""
|
||||||
@ -148,13 +156,14 @@ class ProtocolHandler(object):
|
|||||||
|
|
||||||
class LiveConnection(object):
|
class LiveConnection(object):
|
||||||
"""
|
"""
|
||||||
This facade allows protocol handlers to interface with a live connection,
|
This facade allows interested parties (FlowMaster, inline scripts) to interface with a live connection,
|
||||||
without requiring the expose the ConnectionHandler.
|
without requiring to expose the internals of the ConnectionHandler.
|
||||||
"""
|
"""
|
||||||
def __init__(self, c):
|
def __init__(self, c):
|
||||||
self.c = c
|
self.c = c
|
||||||
self._backup_server_conn = None
|
|
||||||
"""@type: libmproxy.proxy.server.ConnectionHandler"""
|
"""@type: libmproxy.proxy.server.ConnectionHandler"""
|
||||||
|
self._backup_server_conn = None
|
||||||
|
"""@type: libmproxy.proxy.connection.ServerConnection"""
|
||||||
|
|
||||||
def change_server(self, address, ssl=False, force=False, persistent_change=False):
|
def change_server(self, address, ssl=False, force=False, persistent_change=False):
|
||||||
address = netlib.tcp.Address.wrap(address)
|
address = netlib.tcp.Address.wrap(address)
|
||||||
|
@ -1 +1,2 @@
|
|||||||
from .primitives import *
|
from .primitives import *
|
||||||
|
from .config import ProxyConfig
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
import os
|
import os
|
||||||
from .. import utils, platform
|
|
||||||
import re
|
import re
|
||||||
from netlib import http_auth, certutils
|
from netlib import http_auth, certutils
|
||||||
|
from .. import utils, platform
|
||||||
from .primitives import ConstUpstreamServerResolver, TransparentUpstreamServerResolver
|
from .primitives import ConstUpstreamServerResolver, TransparentUpstreamServerResolver
|
||||||
|
|
||||||
TRANSPARENT_SSL_PORTS = [443, 8443]
|
TRANSPARENT_SSL_PORTS = [443, 8443]
|
||||||
@ -11,7 +11,7 @@ CONF_DIR = "~/.mitmproxy"
|
|||||||
|
|
||||||
|
|
||||||
class ProxyConfig:
|
class ProxyConfig:
|
||||||
def __init__(self, confdir=CONF_DIR, clientcerts=None,
|
def __init__(self, confdir=CONF_DIR, ca_file=None, clientcerts=None,
|
||||||
no_upstream_cert=False, body_size_limit=None,
|
no_upstream_cert=False, body_size_limit=None,
|
||||||
mode=None, upstream_server=None, http_form_in=None, http_form_out=None,
|
mode=None, upstream_server=None, http_form_in=None, http_form_out=None,
|
||||||
authenticator=None, ignore=[],
|
authenticator=None, ignore=[],
|
||||||
@ -44,7 +44,7 @@ class ProxyConfig:
|
|||||||
self.ignore = [re.compile(i, re.IGNORECASE) for i in ignore]
|
self.ignore = [re.compile(i, re.IGNORECASE) for i in ignore]
|
||||||
self.authenticator = authenticator
|
self.authenticator = authenticator
|
||||||
self.confdir = os.path.expanduser(confdir)
|
self.confdir = os.path.expanduser(confdir)
|
||||||
self.ca_file = os.path.join(self.confdir, CONF_BASENAME + "-ca.pem")
|
self.ca_file = ca_file or os.path.join(self.confdir, CONF_BASENAME + "-ca.pem")
|
||||||
self.certstore = certutils.CertStore.from_store(self.confdir, CONF_BASENAME)
|
self.certstore = certutils.CertStore.from_store(self.confdir, CONF_BASENAME)
|
||||||
for spec, cert in certs:
|
for spec, cert in certs:
|
||||||
self.certstore.add_cert_file(spec, cert)
|
self.certstore.add_cert_file(spec, cert)
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
import re
|
|
||||||
|
|
||||||
import socket
|
import socket
|
||||||
from OpenSSL import SSL
|
from OpenSSL import SSL
|
||||||
|
@ -481,7 +481,7 @@ class TestSerialize:
|
|||||||
|
|
||||||
f2 = l[0]
|
f2 = l[0]
|
||||||
assert f2._get_state() == f._get_state()
|
assert f2._get_state() == f._get_state()
|
||||||
assert f2.request._assemble() == f.request._assemble()
|
assert f2.request.assemble() == f.request.assemble()
|
||||||
|
|
||||||
def test_load_flows(self):
|
def test_load_flows(self):
|
||||||
r = self._treader()
|
r = self._treader()
|
||||||
@ -757,18 +757,18 @@ class TestRequest:
|
|||||||
r.url = u
|
r.url = u
|
||||||
tutils.raises(ValueError, setattr, r, "url", "")
|
tutils.raises(ValueError, setattr, r, "url", "")
|
||||||
assert r.url == u
|
assert r.url == u
|
||||||
assert r._assemble()
|
assert r.assemble()
|
||||||
assert r.size() == len(r._assemble())
|
assert r.size() == len(r.assemble())
|
||||||
|
|
||||||
r2 = r.copy()
|
r2 = r.copy()
|
||||||
assert r == r2
|
assert r == r2
|
||||||
|
|
||||||
r.content = None
|
r.content = None
|
||||||
assert r._assemble()
|
assert r.assemble()
|
||||||
assert r.size() == len(r._assemble())
|
assert r.size() == len(r.assemble())
|
||||||
|
|
||||||
r.content = CONTENT_MISSING
|
r.content = CONTENT_MISSING
|
||||||
tutils.raises("Cannot assemble flow with CONTENT_MISSING", r._assemble)
|
tutils.raises("Cannot assemble flow with CONTENT_MISSING", r.assemble)
|
||||||
|
|
||||||
def test_get_url(self):
|
def test_get_url(self):
|
||||||
r = tutils.treq()
|
r = tutils.treq()
|
||||||
@ -794,58 +794,58 @@ class TestRequest:
|
|||||||
def test_path_components(self):
|
def test_path_components(self):
|
||||||
r = tutils.treq()
|
r = tutils.treq()
|
||||||
r.path = "/"
|
r.path = "/"
|
||||||
assert r.path_components == []
|
assert r.get_path_components() == []
|
||||||
r.path = "/foo/bar"
|
r.path = "/foo/bar"
|
||||||
assert r.path_components == ["foo", "bar"]
|
assert r.get_path_components() == ["foo", "bar"]
|
||||||
q = flow.ODict()
|
q = flow.ODict()
|
||||||
q["test"] = ["123"]
|
q["test"] = ["123"]
|
||||||
r.query = q
|
r.set_query(q)
|
||||||
assert r.path_components == ["foo", "bar"]
|
assert r.get_path_components() == ["foo", "bar"]
|
||||||
|
|
||||||
r.path_components = []
|
r.set_path_components([])
|
||||||
assert r.path_components == []
|
assert r.get_path_components() == []
|
||||||
r.path_components = ["foo"]
|
r.set_path_components(["foo"])
|
||||||
assert r.path_components == ["foo"]
|
assert r.get_path_components() == ["foo"]
|
||||||
r.path_components = ["/oo"]
|
r.set_path_components(["/oo"])
|
||||||
assert r.path_components == ["/oo"]
|
assert r.get_path_components() == ["/oo"]
|
||||||
assert "%2F" in r.path
|
assert "%2F" in r.path
|
||||||
|
|
||||||
def test_getset_form_urlencoded(self):
|
def test_getset_form_urlencoded(self):
|
||||||
d = flow.ODict([("one", "two"), ("three", "four")])
|
d = flow.ODict([("one", "two"), ("three", "four")])
|
||||||
r = tutils.treq(content=utils.urlencode(d.lst))
|
r = tutils.treq(content=utils.urlencode(d.lst))
|
||||||
r.headers["content-type"] = [protocol.http.HDR_FORM_URLENCODED]
|
r.headers["content-type"] = [protocol.http.HDR_FORM_URLENCODED]
|
||||||
assert r.form_urlencoded == d
|
assert r.get_form_urlencoded() == d
|
||||||
|
|
||||||
d = flow.ODict([("x", "y")])
|
d = flow.ODict([("x", "y")])
|
||||||
r.form_urlencoded = d
|
r.set_form_urlencoded(d)
|
||||||
assert r.form_urlencoded == d
|
assert r.get_form_urlencoded() == d
|
||||||
|
|
||||||
r.headers["content-type"] = ["foo"]
|
r.headers["content-type"] = ["foo"]
|
||||||
assert not r.form_urlencoded
|
assert not r.get_form_urlencoded()
|
||||||
|
|
||||||
def test_getset_query(self):
|
def test_getset_query(self):
|
||||||
h = flow.ODictCaseless()
|
h = flow.ODictCaseless()
|
||||||
|
|
||||||
r = tutils.treq()
|
r = tutils.treq()
|
||||||
r.path = "/foo?x=y&a=b"
|
r.path = "/foo?x=y&a=b"
|
||||||
q = r.query
|
q = r.get_query()
|
||||||
assert q.lst == [("x", "y"), ("a", "b")]
|
assert q.lst == [("x", "y"), ("a", "b")]
|
||||||
|
|
||||||
r.path = "/"
|
r.path = "/"
|
||||||
q = r.query
|
q = r.get_query()
|
||||||
assert not q
|
assert not q
|
||||||
|
|
||||||
r.path = "/?adsfa"
|
r.path = "/?adsfa"
|
||||||
q = r.query
|
q = r.get_query()
|
||||||
assert q.lst == [("adsfa", "")]
|
assert q.lst == [("adsfa", "")]
|
||||||
|
|
||||||
r.path = "/foo?x=y&a=b"
|
r.path = "/foo?x=y&a=b"
|
||||||
assert r.query
|
assert r.get_query()
|
||||||
r.query = flow.ODict([])
|
r.set_query(flow.ODict([]))
|
||||||
assert not r.query
|
assert not r.get_query()
|
||||||
qv = flow.ODict([("a", "b"), ("c", "d")])
|
qv = flow.ODict([("a", "b"), ("c", "d")])
|
||||||
r.query = qv
|
r.set_query(qv)
|
||||||
assert r.query == qv
|
assert r.get_query() == qv
|
||||||
|
|
||||||
def test_anticache(self):
|
def test_anticache(self):
|
||||||
h = flow.ODictCaseless()
|
h = flow.ODictCaseless()
|
||||||
@ -916,14 +916,14 @@ class TestRequest:
|
|||||||
h = flow.ODictCaseless()
|
h = flow.ODictCaseless()
|
||||||
r = tutils.treq()
|
r = tutils.treq()
|
||||||
r.headers = h
|
r.headers = h
|
||||||
assert r.cookies is None
|
assert r.get_cookies() is None
|
||||||
|
|
||||||
def test_get_cookies_single(self):
|
def test_get_cookies_single(self):
|
||||||
h = flow.ODictCaseless()
|
h = flow.ODictCaseless()
|
||||||
h["Cookie"] = ["cookiename=cookievalue"]
|
h["Cookie"] = ["cookiename=cookievalue"]
|
||||||
r = tutils.treq()
|
r = tutils.treq()
|
||||||
r.headers = h
|
r.headers = h
|
||||||
result = r.cookies
|
result = r.get_cookies()
|
||||||
assert len(result)==1
|
assert len(result)==1
|
||||||
assert result['cookiename']==('cookievalue',{})
|
assert result['cookiename']==('cookievalue',{})
|
||||||
|
|
||||||
@ -932,7 +932,7 @@ class TestRequest:
|
|||||||
h["Cookie"] = ["cookiename=cookievalue;othercookiename=othercookievalue"]
|
h["Cookie"] = ["cookiename=cookievalue;othercookiename=othercookievalue"]
|
||||||
r = tutils.treq()
|
r = tutils.treq()
|
||||||
r.headers = h
|
r.headers = h
|
||||||
result = r.cookies
|
result = r.get_cookies()
|
||||||
assert len(result)==2
|
assert len(result)==2
|
||||||
assert result['cookiename']==('cookievalue',{})
|
assert result['cookiename']==('cookievalue',{})
|
||||||
assert result['othercookiename']==('othercookievalue',{})
|
assert result['othercookiename']==('othercookievalue',{})
|
||||||
@ -942,7 +942,7 @@ class TestRequest:
|
|||||||
h["Cookie"] = ["cookiename=coo=kievalue;othercookiename=othercookievalue"]
|
h["Cookie"] = ["cookiename=coo=kievalue;othercookiename=othercookievalue"]
|
||||||
r = tutils.treq()
|
r = tutils.treq()
|
||||||
r.headers = h
|
r.headers = h
|
||||||
result = r.cookies
|
result = r.get_cookies()
|
||||||
assert len(result)==2
|
assert len(result)==2
|
||||||
assert result['cookiename']==('coo=kievalue',{})
|
assert result['cookiename']==('coo=kievalue',{})
|
||||||
assert result['othercookiename']==('othercookievalue',{})
|
assert result['othercookiename']==('othercookievalue',{})
|
||||||
@ -966,18 +966,18 @@ class TestResponse:
|
|||||||
def test_simple(self):
|
def test_simple(self):
|
||||||
f = tutils.tflow(resp=True)
|
f = tutils.tflow(resp=True)
|
||||||
resp = f.response
|
resp = f.response
|
||||||
assert resp._assemble()
|
assert resp.assemble()
|
||||||
assert resp.size() == len(resp._assemble())
|
assert resp.size() == len(resp.assemble())
|
||||||
|
|
||||||
resp2 = resp.copy()
|
resp2 = resp.copy()
|
||||||
assert resp2 == resp
|
assert resp2 == resp
|
||||||
|
|
||||||
resp.content = None
|
resp.content = None
|
||||||
assert resp._assemble()
|
assert resp.assemble()
|
||||||
assert resp.size() == len(resp._assemble())
|
assert resp.size() == len(resp.assemble())
|
||||||
|
|
||||||
resp.content = CONTENT_MISSING
|
resp.content = CONTENT_MISSING
|
||||||
tutils.raises("Cannot assemble flow with CONTENT_MISSING", resp._assemble)
|
tutils.raises("Cannot assemble flow with CONTENT_MISSING", resp.assemble)
|
||||||
|
|
||||||
def test_refresh(self):
|
def test_refresh(self):
|
||||||
r = tutils.tresp()
|
r = tutils.tresp()
|
||||||
@ -1052,14 +1052,14 @@ class TestResponse:
|
|||||||
h = flow.ODictCaseless()
|
h = flow.ODictCaseless()
|
||||||
resp = tutils.tresp()
|
resp = tutils.tresp()
|
||||||
resp.headers = h
|
resp.headers = h
|
||||||
assert not resp.cookies
|
assert not resp.get_cookies()
|
||||||
|
|
||||||
def test_get_cookies_simple(self):
|
def test_get_cookies_simple(self):
|
||||||
h = flow.ODictCaseless()
|
h = flow.ODictCaseless()
|
||||||
h["Set-Cookie"] = ["cookiename=cookievalue"]
|
h["Set-Cookie"] = ["cookiename=cookievalue"]
|
||||||
resp = tutils.tresp()
|
resp = tutils.tresp()
|
||||||
resp.headers = h
|
resp.headers = h
|
||||||
result = resp.cookies
|
result = resp.get_cookies()
|
||||||
assert len(result)==1
|
assert len(result)==1
|
||||||
assert "cookiename" in result
|
assert "cookiename" in result
|
||||||
assert result["cookiename"] == ("cookievalue", {})
|
assert result["cookiename"] == ("cookievalue", {})
|
||||||
@ -1069,7 +1069,7 @@ class TestResponse:
|
|||||||
h["Set-Cookie"] = ["cookiename=cookievalue;domain=example.com;expires=Wed Oct 21 16:29:41 2015;path=/; HttpOnly"]
|
h["Set-Cookie"] = ["cookiename=cookievalue;domain=example.com;expires=Wed Oct 21 16:29:41 2015;path=/; HttpOnly"]
|
||||||
resp = tutils.tresp()
|
resp = tutils.tresp()
|
||||||
resp.headers = h
|
resp.headers = h
|
||||||
result = resp.cookies
|
result = resp.get_cookies()
|
||||||
assert len(result)==1
|
assert len(result)==1
|
||||||
assert "cookiename" in result
|
assert "cookiename" in result
|
||||||
assert result["cookiename"][0] == "cookievalue"
|
assert result["cookiename"][0] == "cookievalue"
|
||||||
@ -1084,7 +1084,7 @@ class TestResponse:
|
|||||||
h["Set-Cookie"] = ["cookiename=; Expires=Thu, 01-Jan-1970 00:00:01 GMT; path=/"]
|
h["Set-Cookie"] = ["cookiename=; Expires=Thu, 01-Jan-1970 00:00:01 GMT; path=/"]
|
||||||
resp = tutils.tresp()
|
resp = tutils.tresp()
|
||||||
resp.headers = h
|
resp.headers = h
|
||||||
result = resp.cookies
|
result = resp.get_cookies()
|
||||||
assert len(result)==1
|
assert len(result)==1
|
||||||
assert "cookiename" in result
|
assert "cookiename" in result
|
||||||
assert result["cookiename"][0] == ""
|
assert result["cookiename"][0] == ""
|
||||||
@ -1095,7 +1095,7 @@ class TestResponse:
|
|||||||
h["Set-Cookie"] = ["cookiename=cookievalue","othercookie=othervalue"]
|
h["Set-Cookie"] = ["cookiename=cookievalue","othercookie=othervalue"]
|
||||||
resp = tutils.tresp()
|
resp = tutils.tresp()
|
||||||
resp.headers = h
|
resp.headers = h
|
||||||
result = resp.cookies
|
result = resp.get_cookies()
|
||||||
assert len(result)==2
|
assert len(result)==2
|
||||||
assert "cookiename" in result
|
assert "cookiename" in result
|
||||||
assert result["cookiename"] == ("cookievalue", {})
|
assert result["cookiename"] == ("cookievalue", {})
|
||||||
|
@ -31,7 +31,7 @@ class TestHTTPRequest:
|
|||||||
f.request.host = f.server_conn.address.host
|
f.request.host = f.server_conn.address.host
|
||||||
f.request.port = f.server_conn.address.port
|
f.request.port = f.server_conn.address.port
|
||||||
f.request.scheme = "http"
|
f.request.scheme = "http"
|
||||||
assert f.request._assemble() == "OPTIONS * HTTP/1.1\r\nHost: address:22\r\n\r\n"
|
assert f.request.assemble() == "OPTIONS * HTTP/1.1\r\nHost: address:22\r\n\r\n"
|
||||||
|
|
||||||
def test_origin_form(self):
|
def test_origin_form(self):
|
||||||
s = StringIO("GET /foo\xff HTTP/1.1")
|
s = StringIO("GET /foo\xff HTTP/1.1")
|
||||||
@ -59,7 +59,7 @@ class TestHTTPRequest:
|
|||||||
s = StringIO("CONNECT address:22 HTTP/1.1")
|
s = StringIO("CONNECT address:22 HTTP/1.1")
|
||||||
r = HTTPRequest.from_stream(s)
|
r = HTTPRequest.from_stream(s)
|
||||||
r.scheme, r.host, r.port = "http", "address", 22
|
r.scheme, r.host, r.port = "http", "address", 22
|
||||||
assert r._assemble() == "CONNECT address:22 HTTP/1.1\r\nHost: address:22\r\n\r\n"
|
assert r.assemble() == "CONNECT address:22 HTTP/1.1\r\nHost: address:22\r\n\r\n"
|
||||||
assert r.pretty_url(False) == "address:22"
|
assert r.pretty_url(False) == "address:22"
|
||||||
|
|
||||||
def test_absolute_form(self):
|
def test_absolute_form(self):
|
||||||
@ -67,11 +67,11 @@ class TestHTTPRequest:
|
|||||||
tutils.raises("Bad HTTP request line", HTTPRequest.from_stream, s)
|
tutils.raises("Bad HTTP request line", HTTPRequest.from_stream, s)
|
||||||
s = StringIO("GET http://address:22/ HTTP/1.1")
|
s = StringIO("GET http://address:22/ HTTP/1.1")
|
||||||
r = HTTPRequest.from_stream(s)
|
r = HTTPRequest.from_stream(s)
|
||||||
assert r._assemble() == "GET http://address:22/ HTTP/1.1\r\nHost: address:22\r\n\r\n"
|
assert r.assemble() == "GET http://address:22/ HTTP/1.1\r\nHost: address:22\r\n\r\n"
|
||||||
|
|
||||||
def test_assemble_unknown_form(self):
|
def test_assemble_unknown_form(self):
|
||||||
r = tutils.treq()
|
r = tutils.treq()
|
||||||
tutils.raises("Invalid request form", r._assemble, "antiauthority")
|
tutils.raises("Invalid request form", r.assemble, "antiauthority")
|
||||||
|
|
||||||
def test_set_url(self):
|
def test_set_url(self):
|
||||||
r = tutils.treq_absolute()
|
r = tutils.treq_absolute()
|
||||||
|
@ -28,7 +28,7 @@ class TestServerConnection:
|
|||||||
f = tutils.tflow()
|
f = tutils.tflow()
|
||||||
f.server_conn = sc
|
f.server_conn = sc
|
||||||
f.request.path = "/p/200:da"
|
f.request.path = "/p/200:da"
|
||||||
sc.send(f.request._assemble())
|
sc.send(f.request.assemble())
|
||||||
assert http.read_response(sc.rfile, f.request.method, 1000)
|
assert http.read_response(sc.rfile, f.request.method, 1000)
|
||||||
assert self.d.last_log()
|
assert self.d.last_log()
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user