mirror of
https://github.com/Grasscutters/mitmproxy.git
synced 2024-11-30 03:14:22 +00:00
move bits around
This commit is contained in:
parent
bd5ee21284
commit
f50deb7b76
2
netlib/http/__init__.py
Normal file
2
netlib/http/__init__.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
from exceptions import *
|
||||||
|
from semantics import *
|
@ -1,6 +1,7 @@
|
|||||||
from __future__ import (absolute_import, print_function, division)
|
from __future__ import (absolute_import, print_function, division)
|
||||||
from argparse import Action, ArgumentTypeError
|
from argparse import Action, ArgumentTypeError
|
||||||
from . import http
|
|
||||||
|
from .. import http
|
||||||
|
|
||||||
|
|
||||||
class NullProxyAuth(object):
|
class NullProxyAuth(object):
|
||||||
@ -46,7 +47,7 @@ class BasicProxyAuth(NullProxyAuth):
|
|||||||
auth_value = headers.get(self.AUTH_HEADER, [])
|
auth_value = headers.get(self.AUTH_HEADER, [])
|
||||||
if not auth_value:
|
if not auth_value:
|
||||||
return False
|
return False
|
||||||
parts = http.parse_http_basic_auth(auth_value[0])
|
parts = http.http1.parse_http_basic_auth(auth_value[0])
|
||||||
if not parts:
|
if not parts:
|
||||||
return False
|
return False
|
||||||
scheme, username, password = parts
|
scheme, username, password = parts
|
@ -1,3 +1,7 @@
|
|||||||
|
import re
|
||||||
|
|
||||||
|
from .. import odict
|
||||||
|
|
||||||
"""
|
"""
|
||||||
A flexible module for cookie parsing and manipulation.
|
A flexible module for cookie parsing and manipulation.
|
||||||
|
|
||||||
@ -22,10 +26,6 @@ variants. Serialization follows RFC6265.
|
|||||||
# TODO
|
# TODO
|
||||||
# - Disallow LHS-only Cookie values
|
# - Disallow LHS-only Cookie values
|
||||||
|
|
||||||
import re
|
|
||||||
|
|
||||||
import odict
|
|
||||||
|
|
||||||
|
|
||||||
def _read_until(s, start, term):
|
def _read_until(s, start, term):
|
||||||
"""
|
"""
|
9
netlib/http/exceptions.py
Normal file
9
netlib/http/exceptions.py
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
class HttpError(Exception):
|
||||||
|
|
||||||
|
def __init__(self, code, message):
|
||||||
|
super(HttpError, self).__init__(message)
|
||||||
|
self.code = code
|
||||||
|
|
||||||
|
|
||||||
|
class HttpErrorConnClosed(HttpError):
|
||||||
|
pass
|
1
netlib/http/http1/__init__.py
Normal file
1
netlib/http/http1/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
from protocol import *
|
@ -1,37 +1,13 @@
|
|||||||
from __future__ import (absolute_import, print_function, division)
|
from __future__ import (absolute_import, print_function, division)
|
||||||
|
import binascii
|
||||||
import collections
|
import collections
|
||||||
import string
|
import string
|
||||||
import urlparse
|
|
||||||
import binascii
|
|
||||||
import sys
|
import sys
|
||||||
from . import odict, utils, tcp, http_semantics, http_status
|
import urlparse
|
||||||
|
|
||||||
|
from netlib import odict, utils, tcp, http
|
||||||
class HttpError(Exception):
|
from .. import status_codes
|
||||||
|
from ..exceptions import *
|
||||||
def __init__(self, code, message):
|
|
||||||
super(HttpError, self).__init__(message)
|
|
||||||
self.code = code
|
|
||||||
|
|
||||||
|
|
||||||
class HttpErrorConnClosed(HttpError):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def _is_valid_port(port):
|
|
||||||
if not 0 <= port <= 65535:
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def _is_valid_host(host):
|
|
||||||
try:
|
|
||||||
host.decode("idna")
|
|
||||||
except ValueError:
|
|
||||||
return False
|
|
||||||
if "\0" in host:
|
|
||||||
return None
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def get_request_line(fp):
|
def get_request_line(fp):
|
||||||
@ -44,51 +20,6 @@ def get_request_line(fp):
|
|||||||
line = fp.readline()
|
line = fp.readline()
|
||||||
return line
|
return line
|
||||||
|
|
||||||
|
|
||||||
def parse_url(url):
|
|
||||||
"""
|
|
||||||
Returns a (scheme, host, port, path) tuple, or None on error.
|
|
||||||
|
|
||||||
Checks that:
|
|
||||||
port is an integer 0-65535
|
|
||||||
host is a valid IDNA-encoded hostname with no null-bytes
|
|
||||||
path is valid ASCII
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
scheme, netloc, path, params, query, fragment = urlparse.urlparse(url)
|
|
||||||
except ValueError:
|
|
||||||
return None
|
|
||||||
if not scheme:
|
|
||||||
return None
|
|
||||||
if '@' in netloc:
|
|
||||||
# FIXME: Consider what to do with the discarded credentials here Most
|
|
||||||
# probably we should extend the signature to return these as a separate
|
|
||||||
# value.
|
|
||||||
_, netloc = string.rsplit(netloc, '@', maxsplit=1)
|
|
||||||
if ':' in netloc:
|
|
||||||
host, port = string.rsplit(netloc, ':', maxsplit=1)
|
|
||||||
try:
|
|
||||||
port = int(port)
|
|
||||||
except ValueError:
|
|
||||||
return None
|
|
||||||
else:
|
|
||||||
host = netloc
|
|
||||||
if scheme == "https":
|
|
||||||
port = 443
|
|
||||||
else:
|
|
||||||
port = 80
|
|
||||||
path = urlparse.urlunparse(('', '', path, params, query, fragment))
|
|
||||||
if not path.startswith("/"):
|
|
||||||
path = "/" + path
|
|
||||||
if not _is_valid_host(host):
|
|
||||||
return None
|
|
||||||
if not utils.isascii(path):
|
|
||||||
return None
|
|
||||||
if not _is_valid_port(port):
|
|
||||||
return None
|
|
||||||
return scheme, host, port, path
|
|
||||||
|
|
||||||
|
|
||||||
def read_headers(fp):
|
def read_headers(fp):
|
||||||
"""
|
"""
|
||||||
Read a set of headers from a file pointer. Stop once a blank line is
|
Read a set of headers from a file pointer. Stop once a blank line is
|
||||||
@ -193,6 +124,7 @@ def parse_http_protocol(s):
|
|||||||
|
|
||||||
|
|
||||||
def parse_http_basic_auth(s):
|
def parse_http_basic_auth(s):
|
||||||
|
# TODO: check if this is HTTP/1 only - otherwise move it to netlib.http.semantics
|
||||||
words = s.split()
|
words = s.split()
|
||||||
if len(words) != 2:
|
if len(words) != 2:
|
||||||
return None
|
return None
|
||||||
@ -208,6 +140,7 @@ def parse_http_basic_auth(s):
|
|||||||
|
|
||||||
|
|
||||||
def assemble_http_basic_auth(scheme, username, password):
|
def assemble_http_basic_auth(scheme, username, password):
|
||||||
|
# TODO: check if this is HTTP/1 only - otherwise move it to netlib.http.semantics
|
||||||
v = binascii.b2a_base64(username + ":" + password)
|
v = binascii.b2a_base64(username + ":" + password)
|
||||||
return scheme + " " + v
|
return scheme + " " + v
|
||||||
|
|
||||||
@ -245,9 +178,9 @@ def parse_init_connect(line):
|
|||||||
port = int(port)
|
port = int(port)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return None
|
return None
|
||||||
if not _is_valid_port(port):
|
if not http.is_valid_port(port):
|
||||||
return None
|
return None
|
||||||
if not _is_valid_host(host):
|
if not http.is_valid_host(host):
|
||||||
return None
|
return None
|
||||||
return host, port, httpversion
|
return host, port, httpversion
|
||||||
|
|
||||||
@ -258,7 +191,7 @@ def parse_init_proxy(line):
|
|||||||
return None
|
return None
|
||||||
method, url, httpversion = v
|
method, url, httpversion = v
|
||||||
|
|
||||||
parts = parse_url(url)
|
parts = http.parse_url(url)
|
||||||
if not parts:
|
if not parts:
|
||||||
return None
|
return None
|
||||||
scheme, host, port, path = parts
|
scheme, host, port, path = parts
|
||||||
@ -421,6 +354,7 @@ def expected_http_body_size(headers, is_request, request_method, response_code):
|
|||||||
return -1
|
return -1
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: make this a regular class - just like Response
|
||||||
Request = collections.namedtuple(
|
Request = collections.namedtuple(
|
||||||
"Request",
|
"Request",
|
||||||
[
|
[
|
||||||
@ -529,7 +463,7 @@ def read_request(rfile, include_body=True, body_size_limit=None, wfile=None):
|
|||||||
|
|
||||||
def read_response(rfile, request_method, body_size_limit, include_body=True):
|
def read_response(rfile, request_method, body_size_limit, include_body=True):
|
||||||
"""
|
"""
|
||||||
Return an (httpversion, code, msg, headers, content) tuple.
|
Returns an http.Response
|
||||||
|
|
||||||
By default, both response header and body are read.
|
By default, both response header and body are read.
|
||||||
If include_body=False is specified, content may be one of the
|
If include_body=False is specified, content may be one of the
|
||||||
@ -538,6 +472,7 @@ def read_response(rfile, request_method, body_size_limit, include_body=True):
|
|||||||
- "", if the response must not have a response body (e.g. it's a
|
- "", if the response must not have a response body (e.g. it's a
|
||||||
response to a HEAD request)
|
response to a HEAD request)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
line = rfile.readline()
|
line = rfile.readline()
|
||||||
# Possible leftover from previous message
|
# Possible leftover from previous message
|
||||||
if line == "\r\n" or line == "\n":
|
if line == "\r\n" or line == "\n":
|
||||||
@ -568,7 +503,7 @@ def read_response(rfile, request_method, body_size_limit, include_body=True):
|
|||||||
# if include_body==False then a None content means the body should be
|
# if include_body==False then a None content means the body should be
|
||||||
# read separately
|
# read separately
|
||||||
content = None
|
content = None
|
||||||
return http_semantics.Response(httpversion, code, msg, headers, content)
|
return http.Response(httpversion, code, msg, headers, content)
|
||||||
|
|
||||||
|
|
||||||
def request_preamble(method, resource, http_major="1", http_minor="1"):
|
def request_preamble(method, resource, http_major="1", http_minor="1"):
|
||||||
@ -579,5 +514,5 @@ def request_preamble(method, resource, http_major="1", http_minor="1"):
|
|||||||
|
|
||||||
def response_preamble(code, message=None, http_major="1", http_minor="1"):
|
def response_preamble(code, message=None, http_major="1", http_minor="1"):
|
||||||
if message is None:
|
if message is None:
|
||||||
message = http_status.RESPONSES.get(code)
|
message = status_codes.RESPONSES.get(code)
|
||||||
return 'HTTP/%s.%s %s %s' % (http_major, http_minor, code, message)
|
return 'HTTP/%s.%s %s %s' % (http_major, http_minor, code, message)
|
94
netlib/http/semantics.py
Normal file
94
netlib/http/semantics.py
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
from __future__ import (absolute_import, print_function, division)
|
||||||
|
import binascii
|
||||||
|
import collections
|
||||||
|
import string
|
||||||
|
import sys
|
||||||
|
import urlparse
|
||||||
|
|
||||||
|
from .. import utils
|
||||||
|
|
||||||
|
class Response(object):
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
httpversion,
|
||||||
|
status_code,
|
||||||
|
msg,
|
||||||
|
headers,
|
||||||
|
content,
|
||||||
|
sslinfo=None,
|
||||||
|
):
|
||||||
|
self.httpversion = httpversion
|
||||||
|
self.status_code = status_code
|
||||||
|
self.msg = msg
|
||||||
|
self.headers = headers
|
||||||
|
self.content = content
|
||||||
|
self.sslinfo = sslinfo
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
return self.__dict__ == other.__dict__
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "Response(%s - %s)" % (self.status_code, self.msg)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def is_valid_port(port):
|
||||||
|
if not 0 <= port <= 65535:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def is_valid_host(host):
|
||||||
|
try:
|
||||||
|
host.decode("idna")
|
||||||
|
except ValueError:
|
||||||
|
return False
|
||||||
|
if "\0" in host:
|
||||||
|
return None
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def parse_url(url):
|
||||||
|
"""
|
||||||
|
Returns a (scheme, host, port, path) tuple, or None on error.
|
||||||
|
|
||||||
|
Checks that:
|
||||||
|
port is an integer 0-65535
|
||||||
|
host is a valid IDNA-encoded hostname with no null-bytes
|
||||||
|
path is valid ASCII
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
scheme, netloc, path, params, query, fragment = urlparse.urlparse(url)
|
||||||
|
except ValueError:
|
||||||
|
return None
|
||||||
|
if not scheme:
|
||||||
|
return None
|
||||||
|
if '@' in netloc:
|
||||||
|
# FIXME: Consider what to do with the discarded credentials here Most
|
||||||
|
# probably we should extend the signature to return these as a separate
|
||||||
|
# value.
|
||||||
|
_, netloc = string.rsplit(netloc, '@', maxsplit=1)
|
||||||
|
if ':' in netloc:
|
||||||
|
host, port = string.rsplit(netloc, ':', maxsplit=1)
|
||||||
|
try:
|
||||||
|
port = int(port)
|
||||||
|
except ValueError:
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
host = netloc
|
||||||
|
if scheme == "https":
|
||||||
|
port = 443
|
||||||
|
else:
|
||||||
|
port = 80
|
||||||
|
path = urlparse.urlunparse(('', '', path, params, query, fragment))
|
||||||
|
if not path.startswith("/"):
|
||||||
|
path = "/" + path
|
||||||
|
if not is_valid_host(host):
|
||||||
|
return None
|
||||||
|
if not utils.isascii(path):
|
||||||
|
return None
|
||||||
|
if not is_valid_port(port):
|
||||||
|
return None
|
||||||
|
return scheme, host, port, path
|
@ -1,23 +0,0 @@
|
|||||||
class Response(object):
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
httpversion,
|
|
||||||
status_code,
|
|
||||||
msg,
|
|
||||||
headers,
|
|
||||||
content,
|
|
||||||
sslinfo=None,
|
|
||||||
):
|
|
||||||
self.httpversion = httpversion
|
|
||||||
self.status_code = status_code
|
|
||||||
self.msg = msg
|
|
||||||
self.headers = headers
|
|
||||||
self.content = content
|
|
||||||
self.sslinfo = sslinfo
|
|
||||||
|
|
||||||
def __eq__(self, other):
|
|
||||||
return self.__dict__ == other.__dict__
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "Response(%s - %s)" % (self.status_code, self.msg)
|
|
@ -6,7 +6,7 @@ import struct
|
|||||||
import io
|
import io
|
||||||
|
|
||||||
from .protocol import Masker
|
from .protocol import Masker
|
||||||
from .. import utils, odict, tcp
|
from netlib import utils, odict, tcp
|
||||||
|
|
||||||
DEFAULT = object()
|
DEFAULT = object()
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ import os
|
|||||||
import struct
|
import struct
|
||||||
import io
|
import io
|
||||||
|
|
||||||
from .. import utils, odict, tcp
|
from netlib import utils, odict, tcp
|
||||||
|
|
||||||
# Colleciton of utility functions that implement small portions of the RFC6455
|
# Colleciton of utility functions that implement small portions of the RFC6455
|
||||||
# WebSockets Protocol Useful for building WebSocket clients and servers.
|
# WebSockets Protocol Useful for building WebSocket clients and servers.
|
||||||
|
0
test/http/http1/__init__.py
Normal file
0
test/http/http1/__init__.py
Normal file
@ -1,20 +1,17 @@
|
|||||||
import cStringIO
|
import cStringIO
|
||||||
import textwrap
|
import textwrap
|
||||||
import binascii
|
import binascii
|
||||||
from netlib import http, http_semantics, odict, tcp
|
|
||||||
from . import tutils, tservers
|
|
||||||
|
|
||||||
|
from netlib import http, odict, tcp
|
||||||
def test_httperror():
|
from netlib.http.http1 import protocol
|
||||||
e = http.HttpError(404, "Not found")
|
from ... import tutils, tservers
|
||||||
assert str(e)
|
|
||||||
|
|
||||||
|
|
||||||
def test_has_chunked_encoding():
|
def test_has_chunked_encoding():
|
||||||
h = odict.ODictCaseless()
|
h = odict.ODictCaseless()
|
||||||
assert not http.has_chunked_encoding(h)
|
assert not protocol.has_chunked_encoding(h)
|
||||||
h["transfer-encoding"] = ["chunked"]
|
h["transfer-encoding"] = ["chunked"]
|
||||||
assert http.has_chunked_encoding(h)
|
assert protocol.has_chunked_encoding(h)
|
||||||
|
|
||||||
|
|
||||||
def test_read_chunked():
|
def test_read_chunked():
|
||||||
@ -25,74 +22,74 @@ def test_read_chunked():
|
|||||||
|
|
||||||
tutils.raises(
|
tutils.raises(
|
||||||
"malformed chunked body",
|
"malformed chunked body",
|
||||||
http.read_http_body,
|
protocol.read_http_body,
|
||||||
s, h, None, "GET", None, True
|
s, h, None, "GET", None, True
|
||||||
)
|
)
|
||||||
|
|
||||||
s = cStringIO.StringIO("1\r\na\r\n0\r\n\r\n")
|
s = cStringIO.StringIO("1\r\na\r\n0\r\n\r\n")
|
||||||
assert http.read_http_body(s, h, None, "GET", None, True) == "a"
|
assert protocol.read_http_body(s, h, None, "GET", None, True) == "a"
|
||||||
|
|
||||||
s = cStringIO.StringIO("\r\n\r\n1\r\na\r\n0\r\n\r\n")
|
s = cStringIO.StringIO("\r\n\r\n1\r\na\r\n0\r\n\r\n")
|
||||||
assert http.read_http_body(s, h, None, "GET", None, True) == "a"
|
assert protocol.read_http_body(s, h, None, "GET", None, True) == "a"
|
||||||
|
|
||||||
s = cStringIO.StringIO("\r\n")
|
s = cStringIO.StringIO("\r\n")
|
||||||
tutils.raises(
|
tutils.raises(
|
||||||
"closed prematurely",
|
"closed prematurely",
|
||||||
http.read_http_body,
|
protocol.read_http_body,
|
||||||
s, h, None, "GET", None, True
|
s, h, None, "GET", None, True
|
||||||
)
|
)
|
||||||
|
|
||||||
s = cStringIO.StringIO("1\r\nfoo")
|
s = cStringIO.StringIO("1\r\nfoo")
|
||||||
tutils.raises(
|
tutils.raises(
|
||||||
"malformed chunked body",
|
"malformed chunked body",
|
||||||
http.read_http_body,
|
protocol.read_http_body,
|
||||||
s, h, None, "GET", None, True
|
s, h, None, "GET", None, True
|
||||||
)
|
)
|
||||||
|
|
||||||
s = cStringIO.StringIO("foo\r\nfoo")
|
s = cStringIO.StringIO("foo\r\nfoo")
|
||||||
tutils.raises(
|
tutils.raises(
|
||||||
http.HttpError,
|
protocol.HttpError,
|
||||||
http.read_http_body,
|
protocol.read_http_body,
|
||||||
s, h, None, "GET", None, True
|
s, h, None, "GET", None, True
|
||||||
)
|
)
|
||||||
|
|
||||||
s = cStringIO.StringIO("5\r\naaaaa\r\n0\r\n\r\n")
|
s = cStringIO.StringIO("5\r\naaaaa\r\n0\r\n\r\n")
|
||||||
tutils.raises("too large", http.read_http_body, s, h, 2, "GET", None, True)
|
tutils.raises("too large", protocol.read_http_body, s, h, 2, "GET", None, True)
|
||||||
|
|
||||||
|
|
||||||
def test_connection_close():
|
def test_connection_close():
|
||||||
h = odict.ODictCaseless()
|
h = odict.ODictCaseless()
|
||||||
assert http.connection_close((1, 0), h)
|
assert protocol.connection_close((1, 0), h)
|
||||||
assert not http.connection_close((1, 1), h)
|
assert not protocol.connection_close((1, 1), h)
|
||||||
|
|
||||||
h["connection"] = ["keep-alive"]
|
h["connection"] = ["keep-alive"]
|
||||||
assert not http.connection_close((1, 1), h)
|
assert not protocol.connection_close((1, 1), h)
|
||||||
|
|
||||||
h["connection"] = ["close"]
|
h["connection"] = ["close"]
|
||||||
assert http.connection_close((1, 1), h)
|
assert protocol.connection_close((1, 1), h)
|
||||||
|
|
||||||
|
|
||||||
def test_get_header_tokens():
|
def test_get_header_tokens():
|
||||||
h = odict.ODictCaseless()
|
h = odict.ODictCaseless()
|
||||||
assert http.get_header_tokens(h, "foo") == []
|
assert protocol.get_header_tokens(h, "foo") == []
|
||||||
h["foo"] = ["bar"]
|
h["foo"] = ["bar"]
|
||||||
assert http.get_header_tokens(h, "foo") == ["bar"]
|
assert protocol.get_header_tokens(h, "foo") == ["bar"]
|
||||||
h["foo"] = ["bar, voing"]
|
h["foo"] = ["bar, voing"]
|
||||||
assert http.get_header_tokens(h, "foo") == ["bar", "voing"]
|
assert protocol.get_header_tokens(h, "foo") == ["bar", "voing"]
|
||||||
h["foo"] = ["bar, voing", "oink"]
|
h["foo"] = ["bar, voing", "oink"]
|
||||||
assert http.get_header_tokens(h, "foo") == ["bar", "voing", "oink"]
|
assert protocol.get_header_tokens(h, "foo") == ["bar", "voing", "oink"]
|
||||||
|
|
||||||
|
|
||||||
def test_read_http_body_request():
|
def test_read_http_body_request():
|
||||||
h = odict.ODictCaseless()
|
h = odict.ODictCaseless()
|
||||||
r = cStringIO.StringIO("testing")
|
r = cStringIO.StringIO("testing")
|
||||||
assert http.read_http_body(r, h, None, "GET", None, True) == ""
|
assert protocol.read_http_body(r, h, None, "GET", None, True) == ""
|
||||||
|
|
||||||
|
|
||||||
def test_read_http_body_response():
|
def test_read_http_body_response():
|
||||||
h = odict.ODictCaseless()
|
h = odict.ODictCaseless()
|
||||||
s = tcp.Reader(cStringIO.StringIO("testing"))
|
s = tcp.Reader(cStringIO.StringIO("testing"))
|
||||||
assert http.read_http_body(s, h, None, "GET", 200, False) == "testing"
|
assert protocol.read_http_body(s, h, None, "GET", 200, False) == "testing"
|
||||||
|
|
||||||
|
|
||||||
def test_read_http_body():
|
def test_read_http_body():
|
||||||
@ -100,14 +97,14 @@ def test_read_http_body():
|
|||||||
h = odict.ODictCaseless()
|
h = odict.ODictCaseless()
|
||||||
h["content-length"] = [7]
|
h["content-length"] = [7]
|
||||||
s = cStringIO.StringIO("testing")
|
s = cStringIO.StringIO("testing")
|
||||||
assert http.read_http_body(s, h, None, "GET", 200, False) == "testing"
|
assert protocol.read_http_body(s, h, None, "GET", 200, False) == "testing"
|
||||||
|
|
||||||
# test content length: invalid header
|
# test content length: invalid header
|
||||||
h["content-length"] = ["foo"]
|
h["content-length"] = ["foo"]
|
||||||
s = cStringIO.StringIO("testing")
|
s = cStringIO.StringIO("testing")
|
||||||
tutils.raises(
|
tutils.raises(
|
||||||
http.HttpError,
|
protocol.HttpError,
|
||||||
http.read_http_body,
|
protocol.read_http_body,
|
||||||
s, h, None, "GET", 200, False
|
s, h, None, "GET", 200, False
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -115,8 +112,8 @@ def test_read_http_body():
|
|||||||
h["content-length"] = [-1]
|
h["content-length"] = [-1]
|
||||||
s = cStringIO.StringIO("testing")
|
s = cStringIO.StringIO("testing")
|
||||||
tutils.raises(
|
tutils.raises(
|
||||||
http.HttpError,
|
protocol.HttpError,
|
||||||
http.read_http_body,
|
protocol.read_http_body,
|
||||||
s, h, None, "GET", 200, False
|
s, h, None, "GET", 200, False
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -124,25 +121,25 @@ def test_read_http_body():
|
|||||||
h["content-length"] = [5]
|
h["content-length"] = [5]
|
||||||
s = cStringIO.StringIO("testing")
|
s = cStringIO.StringIO("testing")
|
||||||
tutils.raises(
|
tutils.raises(
|
||||||
http.HttpError,
|
protocol.HttpError,
|
||||||
http.read_http_body,
|
protocol.read_http_body,
|
||||||
s, h, 4, "GET", 200, False
|
s, h, 4, "GET", 200, False
|
||||||
)
|
)
|
||||||
|
|
||||||
# test content length: content length < actual content
|
# test content length: content length < actual content
|
||||||
s = cStringIO.StringIO("testing")
|
s = cStringIO.StringIO("testing")
|
||||||
assert len(http.read_http_body(s, h, None, "GET", 200, False)) == 5
|
assert len(protocol.read_http_body(s, h, None, "GET", 200, False)) == 5
|
||||||
|
|
||||||
# test no content length: limit > actual content
|
# test no content length: limit > actual content
|
||||||
h = odict.ODictCaseless()
|
h = odict.ODictCaseless()
|
||||||
s = tcp.Reader(cStringIO.StringIO("testing"))
|
s = tcp.Reader(cStringIO.StringIO("testing"))
|
||||||
assert len(http.read_http_body(s, h, 100, "GET", 200, False)) == 7
|
assert len(protocol.read_http_body(s, h, 100, "GET", 200, False)) == 7
|
||||||
|
|
||||||
# test no content length: limit < actual content
|
# test no content length: limit < actual content
|
||||||
s = tcp.Reader(cStringIO.StringIO("testing"))
|
s = tcp.Reader(cStringIO.StringIO("testing"))
|
||||||
tutils.raises(
|
tutils.raises(
|
||||||
http.HttpError,
|
protocol.HttpError,
|
||||||
http.read_http_body,
|
protocol.read_http_body,
|
||||||
s, h, 4, "GET", 200, False
|
s, h, 4, "GET", 200, False
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -150,54 +147,54 @@ def test_read_http_body():
|
|||||||
h = odict.ODictCaseless()
|
h = odict.ODictCaseless()
|
||||||
h["transfer-encoding"] = ["chunked"]
|
h["transfer-encoding"] = ["chunked"]
|
||||||
s = tcp.Reader(cStringIO.StringIO("5\r\naaaaa\r\n0\r\n\r\n"))
|
s = tcp.Reader(cStringIO.StringIO("5\r\naaaaa\r\n0\r\n\r\n"))
|
||||||
assert http.read_http_body(s, h, 100, "GET", 200, False) == "aaaaa"
|
assert protocol.read_http_body(s, h, 100, "GET", 200, False) == "aaaaa"
|
||||||
|
|
||||||
|
|
||||||
def test_expected_http_body_size():
|
def test_expected_http_body_size():
|
||||||
# gibber in the content-length field
|
# gibber in the content-length field
|
||||||
h = odict.ODictCaseless()
|
h = odict.ODictCaseless()
|
||||||
h["content-length"] = ["foo"]
|
h["content-length"] = ["foo"]
|
||||||
assert http.expected_http_body_size(h, False, "GET", 200) is None
|
assert protocol.expected_http_body_size(h, False, "GET", 200) is None
|
||||||
# negative number in the content-length field
|
# negative number in the content-length field
|
||||||
h = odict.ODictCaseless()
|
h = odict.ODictCaseless()
|
||||||
h["content-length"] = ["-7"]
|
h["content-length"] = ["-7"]
|
||||||
assert http.expected_http_body_size(h, False, "GET", 200) is None
|
assert protocol.expected_http_body_size(h, False, "GET", 200) is None
|
||||||
# explicit length
|
# explicit length
|
||||||
h = odict.ODictCaseless()
|
h = odict.ODictCaseless()
|
||||||
h["content-length"] = ["5"]
|
h["content-length"] = ["5"]
|
||||||
assert http.expected_http_body_size(h, False, "GET", 200) == 5
|
assert protocol.expected_http_body_size(h, False, "GET", 200) == 5
|
||||||
# no length
|
# no length
|
||||||
h = odict.ODictCaseless()
|
h = odict.ODictCaseless()
|
||||||
assert http.expected_http_body_size(h, False, "GET", 200) == -1
|
assert protocol.expected_http_body_size(h, False, "GET", 200) == -1
|
||||||
# no length request
|
# no length request
|
||||||
h = odict.ODictCaseless()
|
h = odict.ODictCaseless()
|
||||||
assert http.expected_http_body_size(h, True, "GET", None) == 0
|
assert protocol.expected_http_body_size(h, True, "GET", None) == 0
|
||||||
|
|
||||||
|
|
||||||
def test_parse_http_protocol():
|
def test_parse_http_protocol():
|
||||||
assert http.parse_http_protocol("HTTP/1.1") == (1, 1)
|
assert protocol.parse_http_protocol("HTTP/1.1") == (1, 1)
|
||||||
assert http.parse_http_protocol("HTTP/0.0") == (0, 0)
|
assert protocol.parse_http_protocol("HTTP/0.0") == (0, 0)
|
||||||
assert not http.parse_http_protocol("HTTP/a.1")
|
assert not protocol.parse_http_protocol("HTTP/a.1")
|
||||||
assert not http.parse_http_protocol("HTTP/1.a")
|
assert not protocol.parse_http_protocol("HTTP/1.a")
|
||||||
assert not http.parse_http_protocol("foo/0.0")
|
assert not protocol.parse_http_protocol("foo/0.0")
|
||||||
assert not http.parse_http_protocol("HTTP/x")
|
assert not protocol.parse_http_protocol("HTTP/x")
|
||||||
|
|
||||||
|
|
||||||
def test_parse_init_connect():
|
def test_parse_init_connect():
|
||||||
assert http.parse_init_connect("CONNECT host.com:443 HTTP/1.0")
|
assert protocol.parse_init_connect("CONNECT host.com:443 HTTP/1.0")
|
||||||
assert not http.parse_init_connect("C\xfeONNECT host.com:443 HTTP/1.0")
|
assert not protocol.parse_init_connect("C\xfeONNECT host.com:443 HTTP/1.0")
|
||||||
assert not http.parse_init_connect("CONNECT \0host.com:443 HTTP/1.0")
|
assert not protocol.parse_init_connect("CONNECT \0host.com:443 HTTP/1.0")
|
||||||
assert not http.parse_init_connect("CONNECT host.com:444444 HTTP/1.0")
|
assert not protocol.parse_init_connect("CONNECT host.com:444444 HTTP/1.0")
|
||||||
assert not http.parse_init_connect("bogus")
|
assert not protocol.parse_init_connect("bogus")
|
||||||
assert not http.parse_init_connect("GET host.com:443 HTTP/1.0")
|
assert not protocol.parse_init_connect("GET host.com:443 HTTP/1.0")
|
||||||
assert not http.parse_init_connect("CONNECT host.com443 HTTP/1.0")
|
assert not protocol.parse_init_connect("CONNECT host.com443 HTTP/1.0")
|
||||||
assert not http.parse_init_connect("CONNECT host.com:443 foo/1.0")
|
assert not protocol.parse_init_connect("CONNECT host.com:443 foo/1.0")
|
||||||
assert not http.parse_init_connect("CONNECT host.com:foo HTTP/1.0")
|
assert not protocol.parse_init_connect("CONNECT host.com:foo HTTP/1.0")
|
||||||
|
|
||||||
|
|
||||||
def test_parse_init_proxy():
|
def test_parse_init_proxy():
|
||||||
u = "GET http://foo.com:8888/test HTTP/1.1"
|
u = "GET http://foo.com:8888/test HTTP/1.1"
|
||||||
m, s, h, po, pa, httpversion = http.parse_init_proxy(u)
|
m, s, h, po, pa, httpversion = protocol.parse_init_proxy(u)
|
||||||
assert m == "GET"
|
assert m == "GET"
|
||||||
assert s == "http"
|
assert s == "http"
|
||||||
assert h == "foo.com"
|
assert h == "foo.com"
|
||||||
@ -206,27 +203,27 @@ def test_parse_init_proxy():
|
|||||||
assert httpversion == (1, 1)
|
assert httpversion == (1, 1)
|
||||||
|
|
||||||
u = "G\xfeET http://foo.com:8888/test HTTP/1.1"
|
u = "G\xfeET http://foo.com:8888/test HTTP/1.1"
|
||||||
assert not http.parse_init_proxy(u)
|
assert not protocol.parse_init_proxy(u)
|
||||||
|
|
||||||
assert not http.parse_init_proxy("invalid")
|
assert not protocol.parse_init_proxy("invalid")
|
||||||
assert not http.parse_init_proxy("GET invalid HTTP/1.1")
|
assert not protocol.parse_init_proxy("GET invalid HTTP/1.1")
|
||||||
assert not http.parse_init_proxy("GET http://foo.com:8888/test foo/1.1")
|
assert not protocol.parse_init_proxy("GET http://foo.com:8888/test foo/1.1")
|
||||||
|
|
||||||
|
|
||||||
def test_parse_init_http():
|
def test_parse_init_http():
|
||||||
u = "GET /test HTTP/1.1"
|
u = "GET /test HTTP/1.1"
|
||||||
m, u, httpversion = http.parse_init_http(u)
|
m, u, httpversion = protocol.parse_init_http(u)
|
||||||
assert m == "GET"
|
assert m == "GET"
|
||||||
assert u == "/test"
|
assert u == "/test"
|
||||||
assert httpversion == (1, 1)
|
assert httpversion == (1, 1)
|
||||||
|
|
||||||
u = "G\xfeET /test HTTP/1.1"
|
u = "G\xfeET /test HTTP/1.1"
|
||||||
assert not http.parse_init_http(u)
|
assert not protocol.parse_init_http(u)
|
||||||
|
|
||||||
assert not http.parse_init_http("invalid")
|
assert not protocol.parse_init_http("invalid")
|
||||||
assert not http.parse_init_http("GET invalid HTTP/1.1")
|
assert not protocol.parse_init_http("GET invalid HTTP/1.1")
|
||||||
assert not http.parse_init_http("GET /test foo/1.1")
|
assert not protocol.parse_init_http("GET /test foo/1.1")
|
||||||
assert not http.parse_init_http("GET /test\xc0 HTTP/1.1")
|
assert not protocol.parse_init_http("GET /test\xc0 HTTP/1.1")
|
||||||
|
|
||||||
|
|
||||||
class TestReadHeaders:
|
class TestReadHeaders:
|
||||||
@ -236,7 +233,7 @@ class TestReadHeaders:
|
|||||||
data = textwrap.dedent(data)
|
data = textwrap.dedent(data)
|
||||||
data = data.strip()
|
data = data.strip()
|
||||||
s = cStringIO.StringIO(data)
|
s = cStringIO.StringIO(data)
|
||||||
return http.read_headers(s)
|
return protocol.read_headers(s)
|
||||||
|
|
||||||
def test_read_simple(self):
|
def test_read_simple(self):
|
||||||
data = """
|
data = """
|
||||||
@ -290,7 +287,7 @@ class TestReadResponseNoContentLength(tservers.ServerTestBase):
|
|||||||
def test_no_content_length(self):
|
def test_no_content_length(self):
|
||||||
c = tcp.TCPClient(("127.0.0.1", self.port))
|
c = tcp.TCPClient(("127.0.0.1", self.port))
|
||||||
c.connect()
|
c.connect()
|
||||||
resp = http.read_response(c.rfile, "GET", None)
|
resp = protocol.read_response(c.rfile, "GET", None)
|
||||||
assert resp.content == "bar\r\n\r\n"
|
assert resp.content == "bar\r\n\r\n"
|
||||||
|
|
||||||
|
|
||||||
@ -298,7 +295,7 @@ def test_read_response():
|
|||||||
def tst(data, method, limit, include_body=True):
|
def tst(data, method, limit, include_body=True):
|
||||||
data = textwrap.dedent(data)
|
data = textwrap.dedent(data)
|
||||||
r = cStringIO.StringIO(data)
|
r = cStringIO.StringIO(data)
|
||||||
return http.read_response(
|
return protocol.read_response(
|
||||||
r, method, limit, include_body=include_body
|
r, method, limit, include_body=include_body
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -307,13 +304,13 @@ def test_read_response():
|
|||||||
data = """
|
data = """
|
||||||
HTTP/1.1 200 OK
|
HTTP/1.1 200 OK
|
||||||
"""
|
"""
|
||||||
assert tst(data, "GET", None) == http_semantics.Response(
|
assert tst(data, "GET", None) == http.Response(
|
||||||
(1, 1), 200, 'OK', odict.ODictCaseless(), ''
|
(1, 1), 200, 'OK', odict.ODictCaseless(), ''
|
||||||
)
|
)
|
||||||
data = """
|
data = """
|
||||||
HTTP/1.1 200
|
HTTP/1.1 200
|
||||||
"""
|
"""
|
||||||
assert tst(data, "GET", None) == http_semantics.Response(
|
assert tst(data, "GET", None) == http.Response(
|
||||||
(1, 1), 200, '', odict.ODictCaseless(), ''
|
(1, 1), 200, '', odict.ODictCaseless(), ''
|
||||||
)
|
)
|
||||||
data = """
|
data = """
|
||||||
@ -330,7 +327,7 @@ def test_read_response():
|
|||||||
|
|
||||||
HTTP/1.1 200 OK
|
HTTP/1.1 200 OK
|
||||||
"""
|
"""
|
||||||
assert tst(data, "GET", None) == http_semantics.Response(
|
assert tst(data, "GET", None) == http.Response(
|
||||||
(1, 1), 100, 'CONTINUE', odict.ODictCaseless(), ''
|
(1, 1), 100, 'CONTINUE', odict.ODictCaseless(), ''
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -360,71 +357,28 @@ def test_read_response():
|
|||||||
assert tst(data, "GET", None, include_body=False).content is None
|
assert tst(data, "GET", None, include_body=False).content is None
|
||||||
|
|
||||||
|
|
||||||
def test_parse_url():
|
|
||||||
assert not http.parse_url("")
|
|
||||||
|
|
||||||
u = "http://foo.com:8888/test"
|
|
||||||
s, h, po, pa = http.parse_url(u)
|
|
||||||
assert s == "http"
|
|
||||||
assert h == "foo.com"
|
|
||||||
assert po == 8888
|
|
||||||
assert pa == "/test"
|
|
||||||
|
|
||||||
s, h, po, pa = http.parse_url("http://foo/bar")
|
|
||||||
assert s == "http"
|
|
||||||
assert h == "foo"
|
|
||||||
assert po == 80
|
|
||||||
assert pa == "/bar"
|
|
||||||
|
|
||||||
s, h, po, pa = http.parse_url("http://user:pass@foo/bar")
|
|
||||||
assert s == "http"
|
|
||||||
assert h == "foo"
|
|
||||||
assert po == 80
|
|
||||||
assert pa == "/bar"
|
|
||||||
|
|
||||||
s, h, po, pa = http.parse_url("http://foo")
|
|
||||||
assert pa == "/"
|
|
||||||
|
|
||||||
s, h, po, pa = http.parse_url("https://foo")
|
|
||||||
assert po == 443
|
|
||||||
|
|
||||||
assert not http.parse_url("https://foo:bar")
|
|
||||||
assert not http.parse_url("https://foo:")
|
|
||||||
|
|
||||||
# Invalid IDNA
|
|
||||||
assert not http.parse_url("http://\xfafoo")
|
|
||||||
# Invalid PATH
|
|
||||||
assert not http.parse_url("http:/\xc6/localhost:56121")
|
|
||||||
# Null byte in host
|
|
||||||
assert not http.parse_url("http://foo\0")
|
|
||||||
# Port out of range
|
|
||||||
assert not http.parse_url("http://foo:999999")
|
|
||||||
# Invalid IPv6 URL - see http://www.ietf.org/rfc/rfc2732.txt
|
|
||||||
assert not http.parse_url('http://lo[calhost')
|
|
||||||
|
|
||||||
|
|
||||||
def test_parse_http_basic_auth():
|
def test_parse_http_basic_auth():
|
||||||
vals = ("basic", "foo", "bar")
|
vals = ("basic", "foo", "bar")
|
||||||
assert http.parse_http_basic_auth(
|
assert protocol.parse_http_basic_auth(
|
||||||
http.assemble_http_basic_auth(*vals)
|
protocol.assemble_http_basic_auth(*vals)
|
||||||
) == vals
|
) == vals
|
||||||
assert not http.parse_http_basic_auth("")
|
assert not protocol.parse_http_basic_auth("")
|
||||||
assert not http.parse_http_basic_auth("foo bar")
|
assert not protocol.parse_http_basic_auth("foo bar")
|
||||||
v = "basic " + binascii.b2a_base64("foo")
|
v = "basic " + binascii.b2a_base64("foo")
|
||||||
assert not http.parse_http_basic_auth(v)
|
assert not protocol.parse_http_basic_auth(v)
|
||||||
|
|
||||||
|
|
||||||
def test_get_request_line():
|
def test_get_request_line():
|
||||||
r = cStringIO.StringIO("\nfoo")
|
r = cStringIO.StringIO("\nfoo")
|
||||||
assert http.get_request_line(r) == "foo"
|
assert protocol.get_request_line(r) == "foo"
|
||||||
assert not http.get_request_line(r)
|
assert not protocol.get_request_line(r)
|
||||||
|
|
||||||
|
|
||||||
class TestReadRequest():
|
class TestReadRequest():
|
||||||
|
|
||||||
def tst(self, data, **kwargs):
|
def tst(self, data, **kwargs):
|
||||||
r = cStringIO.StringIO(data)
|
r = cStringIO.StringIO(data)
|
||||||
return http.read_request(r, **kwargs)
|
return protocol.read_request(r, **kwargs)
|
||||||
|
|
||||||
def test_invalid(self):
|
def test_invalid(self):
|
||||||
tutils.raises(
|
tutils.raises(
|
||||||
@ -485,7 +439,7 @@ class TestReadRequest():
|
|||||||
"Expect: 100-continue\r\n\r\n"
|
"Expect: 100-continue\r\n\r\n"
|
||||||
"foobar",
|
"foobar",
|
||||||
)
|
)
|
||||||
v = http.read_request(r, wfile=w)
|
v = protocol.read_request(r, wfile=w)
|
||||||
assert w.getvalue() == "HTTP/1.1 100 Continue\r\n\r\n"
|
assert w.getvalue() == "HTTP/1.1 100 Continue\r\n\r\n"
|
||||||
assert v.content == "foo"
|
assert v.content == "foo"
|
||||||
assert r.read(3) == "bar"
|
assert r.read(3) == "bar"
|
0
test/http/http2/__init__.py
Normal file
0
test/http/http2/__init__.py
Normal file
@ -2,7 +2,7 @@ import cStringIO
|
|||||||
from test import tutils
|
from test import tutils
|
||||||
from nose.tools import assert_equal
|
from nose.tools import assert_equal
|
||||||
from netlib import tcp
|
from netlib import tcp
|
||||||
from netlib.http2.frame import *
|
from netlib.http.http2.frame import *
|
||||||
|
|
||||||
|
|
||||||
def hex_to_file(data):
|
def hex_to_file(data):
|
@ -1,10 +1,9 @@
|
|||||||
import OpenSSL
|
import OpenSSL
|
||||||
|
|
||||||
from netlib import http2
|
|
||||||
from netlib import tcp
|
from netlib import tcp
|
||||||
from netlib.http2.frame import *
|
from netlib.http import http2
|
||||||
from test import tutils
|
from netlib.http.http2.frame import *
|
||||||
from .. import tservers
|
from ... import tutils, tservers
|
||||||
|
|
||||||
|
|
||||||
class EchoHandler(tcp.BaseHandler):
|
class EchoHandler(tcp.BaseHandler):
|
@ -1,11 +1,12 @@
|
|||||||
from netlib import odict, http_auth, http
|
from netlib import odict, http
|
||||||
import tutils
|
from netlib.http import authentication
|
||||||
|
from .. import tutils
|
||||||
|
|
||||||
|
|
||||||
class TestPassManNonAnon:
|
class TestPassManNonAnon:
|
||||||
|
|
||||||
def test_simple(self):
|
def test_simple(self):
|
||||||
p = http_auth.PassManNonAnon()
|
p = authentication.PassManNonAnon()
|
||||||
assert not p.test("", "")
|
assert not p.test("", "")
|
||||||
assert p.test("user", "")
|
assert p.test("user", "")
|
||||||
|
|
||||||
@ -15,14 +16,14 @@ class TestPassManHtpasswd:
|
|||||||
def test_file_errors(self):
|
def test_file_errors(self):
|
||||||
tutils.raises(
|
tutils.raises(
|
||||||
"malformed htpasswd file",
|
"malformed htpasswd file",
|
||||||
http_auth.PassManHtpasswd,
|
authentication.PassManHtpasswd,
|
||||||
tutils.test_data.path("data/server.crt"))
|
tutils.test_data.path("data/server.crt"))
|
||||||
|
|
||||||
def test_simple(self):
|
def test_simple(self):
|
||||||
pm = http_auth.PassManHtpasswd(tutils.test_data.path("data/htpasswd"))
|
pm = authentication.PassManHtpasswd(tutils.test_data.path("data/htpasswd"))
|
||||||
|
|
||||||
vals = ("basic", "test", "test")
|
vals = ("basic", "test", "test")
|
||||||
http.assemble_http_basic_auth(*vals)
|
http.http1.assemble_http_basic_auth(*vals)
|
||||||
assert pm.test("test", "test")
|
assert pm.test("test", "test")
|
||||||
assert not pm.test("test", "foo")
|
assert not pm.test("test", "foo")
|
||||||
assert not pm.test("foo", "test")
|
assert not pm.test("foo", "test")
|
||||||
@ -33,7 +34,7 @@ class TestPassManHtpasswd:
|
|||||||
class TestPassManSingleUser:
|
class TestPassManSingleUser:
|
||||||
|
|
||||||
def test_simple(self):
|
def test_simple(self):
|
||||||
pm = http_auth.PassManSingleUser("test", "test")
|
pm = authentication.PassManSingleUser("test", "test")
|
||||||
assert pm.test("test", "test")
|
assert pm.test("test", "test")
|
||||||
assert not pm.test("test", "foo")
|
assert not pm.test("test", "foo")
|
||||||
assert not pm.test("foo", "test")
|
assert not pm.test("foo", "test")
|
||||||
@ -42,7 +43,7 @@ class TestPassManSingleUser:
|
|||||||
class TestNullProxyAuth:
|
class TestNullProxyAuth:
|
||||||
|
|
||||||
def test_simple(self):
|
def test_simple(self):
|
||||||
na = http_auth.NullProxyAuth(http_auth.PassManNonAnon())
|
na = authentication.NullProxyAuth(authentication.PassManNonAnon())
|
||||||
assert not na.auth_challenge_headers()
|
assert not na.auth_challenge_headers()
|
||||||
assert na.authenticate("foo")
|
assert na.authenticate("foo")
|
||||||
na.clean({})
|
na.clean({})
|
||||||
@ -51,17 +52,17 @@ class TestNullProxyAuth:
|
|||||||
class TestBasicProxyAuth:
|
class TestBasicProxyAuth:
|
||||||
|
|
||||||
def test_simple(self):
|
def test_simple(self):
|
||||||
ba = http_auth.BasicProxyAuth(http_auth.PassManNonAnon(), "test")
|
ba = authentication.BasicProxyAuth(authentication.PassManNonAnon(), "test")
|
||||||
h = odict.ODictCaseless()
|
h = odict.ODictCaseless()
|
||||||
assert ba.auth_challenge_headers()
|
assert ba.auth_challenge_headers()
|
||||||
assert not ba.authenticate(h)
|
assert not ba.authenticate(h)
|
||||||
|
|
||||||
def test_authenticate_clean(self):
|
def test_authenticate_clean(self):
|
||||||
ba = http_auth.BasicProxyAuth(http_auth.PassManNonAnon(), "test")
|
ba = authentication.BasicProxyAuth(authentication.PassManNonAnon(), "test")
|
||||||
|
|
||||||
hdrs = odict.ODictCaseless()
|
hdrs = odict.ODictCaseless()
|
||||||
vals = ("basic", "foo", "bar")
|
vals = ("basic", "foo", "bar")
|
||||||
hdrs[ba.AUTH_HEADER] = [http.assemble_http_basic_auth(*vals)]
|
hdrs[ba.AUTH_HEADER] = [http.http1.assemble_http_basic_auth(*vals)]
|
||||||
assert ba.authenticate(hdrs)
|
assert ba.authenticate(hdrs)
|
||||||
|
|
||||||
ba.clean(hdrs)
|
ba.clean(hdrs)
|
||||||
@ -74,12 +75,12 @@ class TestBasicProxyAuth:
|
|||||||
assert not ba.authenticate(hdrs)
|
assert not ba.authenticate(hdrs)
|
||||||
|
|
||||||
vals = ("foo", "foo", "bar")
|
vals = ("foo", "foo", "bar")
|
||||||
hdrs[ba.AUTH_HEADER] = [http.assemble_http_basic_auth(*vals)]
|
hdrs[ba.AUTH_HEADER] = [http.http1.assemble_http_basic_auth(*vals)]
|
||||||
assert not ba.authenticate(hdrs)
|
assert not ba.authenticate(hdrs)
|
||||||
|
|
||||||
ba = http_auth.BasicProxyAuth(http_auth.PassMan(), "test")
|
ba = authentication.BasicProxyAuth(authentication.PassMan(), "test")
|
||||||
vals = ("basic", "foo", "bar")
|
vals = ("basic", "foo", "bar")
|
||||||
hdrs[ba.AUTH_HEADER] = [http.assemble_http_basic_auth(*vals)]
|
hdrs[ba.AUTH_HEADER] = [http.http1.assemble_http_basic_auth(*vals)]
|
||||||
assert not ba.authenticate(hdrs)
|
assert not ba.authenticate(hdrs)
|
||||||
|
|
||||||
|
|
||||||
@ -91,19 +92,19 @@ class TestAuthAction:
|
|||||||
|
|
||||||
def test_nonanonymous(self):
|
def test_nonanonymous(self):
|
||||||
m = Bunch()
|
m = Bunch()
|
||||||
aa = http_auth.NonanonymousAuthAction(None, "authenticator")
|
aa = authentication.NonanonymousAuthAction(None, "authenticator")
|
||||||
aa(None, m, None, None)
|
aa(None, m, None, None)
|
||||||
assert m.authenticator
|
assert m.authenticator
|
||||||
|
|
||||||
def test_singleuser(self):
|
def test_singleuser(self):
|
||||||
m = Bunch()
|
m = Bunch()
|
||||||
aa = http_auth.SingleuserAuthAction(None, "authenticator")
|
aa = authentication.SingleuserAuthAction(None, "authenticator")
|
||||||
aa(None, m, "foo:bar", None)
|
aa(None, m, "foo:bar", None)
|
||||||
assert m.authenticator
|
assert m.authenticator
|
||||||
tutils.raises("invalid", aa, None, m, "foo", None)
|
tutils.raises("invalid", aa, None, m, "foo", None)
|
||||||
|
|
||||||
def test_httppasswd(self):
|
def test_httppasswd(self):
|
||||||
m = Bunch()
|
m = Bunch()
|
||||||
aa = http_auth.HtpasswdAuthAction(None, "authenticator")
|
aa = authentication.HtpasswdAuthAction(None, "authenticator")
|
||||||
aa(None, m, tutils.test_data.path("data/htpasswd"), None)
|
aa(None, m, tutils.test_data.path("data/htpasswd"), None)
|
||||||
assert m.authenticator
|
assert m.authenticator
|
@ -1,6 +1,6 @@
|
|||||||
import nose.tools
|
import nose.tools
|
||||||
|
|
||||||
from netlib import http_cookies
|
from netlib.http import cookies
|
||||||
|
|
||||||
|
|
||||||
def test_read_token():
|
def test_read_token():
|
||||||
@ -13,7 +13,7 @@ def test_read_token():
|
|||||||
[(" foo=bar", 1), ("foo", 4)],
|
[(" foo=bar", 1), ("foo", 4)],
|
||||||
]
|
]
|
||||||
for q, a in tokens:
|
for q, a in tokens:
|
||||||
nose.tools.eq_(http_cookies._read_token(*q), a)
|
nose.tools.eq_(cookies._read_token(*q), a)
|
||||||
|
|
||||||
|
|
||||||
def test_read_quoted_string():
|
def test_read_quoted_string():
|
||||||
@ -25,7 +25,7 @@ def test_read_quoted_string():
|
|||||||
[('"fo\\\"" x', 0), ("fo\"", 6)],
|
[('"fo\\\"" x', 0), ("fo\"", 6)],
|
||||||
]
|
]
|
||||||
for q, a in tokens:
|
for q, a in tokens:
|
||||||
nose.tools.eq_(http_cookies._read_quoted_string(*q), a)
|
nose.tools.eq_(cookies._read_quoted_string(*q), a)
|
||||||
|
|
||||||
|
|
||||||
def test_read_pairs():
|
def test_read_pairs():
|
||||||
@ -60,7 +60,7 @@ def test_read_pairs():
|
|||||||
],
|
],
|
||||||
]
|
]
|
||||||
for s, lst in vals:
|
for s, lst in vals:
|
||||||
ret, off = http_cookies._read_pairs(s)
|
ret, off = cookies._read_pairs(s)
|
||||||
nose.tools.eq_(ret, lst)
|
nose.tools.eq_(ret, lst)
|
||||||
|
|
||||||
|
|
||||||
@ -108,10 +108,10 @@ def test_pairs_roundtrips():
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
for s, lst in pairs:
|
for s, lst in pairs:
|
||||||
ret, off = http_cookies._read_pairs(s)
|
ret, off = cookies._read_pairs(s)
|
||||||
nose.tools.eq_(ret, lst)
|
nose.tools.eq_(ret, lst)
|
||||||
s2 = http_cookies._format_pairs(lst)
|
s2 = cookies._format_pairs(lst)
|
||||||
ret, off = http_cookies._read_pairs(s2)
|
ret, off = cookies._read_pairs(s2)
|
||||||
nose.tools.eq_(ret, lst)
|
nose.tools.eq_(ret, lst)
|
||||||
|
|
||||||
|
|
||||||
@ -127,10 +127,10 @@ def test_cookie_roundtrips():
|
|||||||
],
|
],
|
||||||
]
|
]
|
||||||
for s, lst in pairs:
|
for s, lst in pairs:
|
||||||
ret = http_cookies.parse_cookie_header(s)
|
ret = cookies.parse_cookie_header(s)
|
||||||
nose.tools.eq_(ret.lst, lst)
|
nose.tools.eq_(ret.lst, lst)
|
||||||
s2 = http_cookies.format_cookie_header(ret)
|
s2 = cookies.format_cookie_header(ret)
|
||||||
ret = http_cookies.parse_cookie_header(s2)
|
ret = cookies.parse_cookie_header(s2)
|
||||||
nose.tools.eq_(ret.lst, lst)
|
nose.tools.eq_(ret.lst, lst)
|
||||||
|
|
||||||
|
|
||||||
@ -180,10 +180,10 @@ def test_parse_set_cookie_pairs():
|
|||||||
],
|
],
|
||||||
]
|
]
|
||||||
for s, lst in pairs:
|
for s, lst in pairs:
|
||||||
ret = http_cookies._parse_set_cookie_pairs(s)
|
ret = cookies._parse_set_cookie_pairs(s)
|
||||||
nose.tools.eq_(ret, lst)
|
nose.tools.eq_(ret, lst)
|
||||||
s2 = http_cookies._format_set_cookie_pairs(ret)
|
s2 = cookies._format_set_cookie_pairs(ret)
|
||||||
ret2 = http_cookies._parse_set_cookie_pairs(s2)
|
ret2 = cookies._parse_set_cookie_pairs(s2)
|
||||||
nose.tools.eq_(ret2, lst)
|
nose.tools.eq_(ret2, lst)
|
||||||
|
|
||||||
|
|
||||||
@ -205,13 +205,13 @@ def test_parse_set_cookie_header():
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
for s, expected in vals:
|
for s, expected in vals:
|
||||||
ret = http_cookies.parse_set_cookie_header(s)
|
ret = cookies.parse_set_cookie_header(s)
|
||||||
if expected:
|
if expected:
|
||||||
assert ret[0] == expected[0]
|
assert ret[0] == expected[0]
|
||||||
assert ret[1] == expected[1]
|
assert ret[1] == expected[1]
|
||||||
nose.tools.eq_(ret[2].lst, expected[2])
|
nose.tools.eq_(ret[2].lst, expected[2])
|
||||||
s2 = http_cookies.format_set_cookie_header(*ret)
|
s2 = cookies.format_set_cookie_header(*ret)
|
||||||
ret2 = http_cookies.parse_set_cookie_header(s2)
|
ret2 = cookies.parse_set_cookie_header(s2)
|
||||||
assert ret2[0] == expected[0]
|
assert ret2[0] == expected[0]
|
||||||
assert ret2[1] == expected[1]
|
assert ret2[1] == expected[1]
|
||||||
nose.tools.eq_(ret2[2].lst, expected[2])
|
nose.tools.eq_(ret2[2].lst, expected[2])
|
54
test/http/test_semantics.py
Normal file
54
test/http/test_semantics.py
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import cStringIO
|
||||||
|
import textwrap
|
||||||
|
import binascii
|
||||||
|
|
||||||
|
from netlib import http, odict, tcp
|
||||||
|
from netlib.http import http1
|
||||||
|
from .. import tutils, tservers
|
||||||
|
|
||||||
|
def test_httperror():
|
||||||
|
e = http.exceptions.HttpError(404, "Not found")
|
||||||
|
assert str(e)
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_url():
|
||||||
|
assert not http.parse_url("")
|
||||||
|
|
||||||
|
u = "http://foo.com:8888/test"
|
||||||
|
s, h, po, pa = http.parse_url(u)
|
||||||
|
assert s == "http"
|
||||||
|
assert h == "foo.com"
|
||||||
|
assert po == 8888
|
||||||
|
assert pa == "/test"
|
||||||
|
|
||||||
|
s, h, po, pa = http.parse_url("http://foo/bar")
|
||||||
|
assert s == "http"
|
||||||
|
assert h == "foo"
|
||||||
|
assert po == 80
|
||||||
|
assert pa == "/bar"
|
||||||
|
|
||||||
|
s, h, po, pa = http.parse_url("http://user:pass@foo/bar")
|
||||||
|
assert s == "http"
|
||||||
|
assert h == "foo"
|
||||||
|
assert po == 80
|
||||||
|
assert pa == "/bar"
|
||||||
|
|
||||||
|
s, h, po, pa = http.parse_url("http://foo")
|
||||||
|
assert pa == "/"
|
||||||
|
|
||||||
|
s, h, po, pa = http.parse_url("https://foo")
|
||||||
|
assert po == 443
|
||||||
|
|
||||||
|
assert not http.parse_url("https://foo:bar")
|
||||||
|
assert not http.parse_url("https://foo:")
|
||||||
|
|
||||||
|
# Invalid IDNA
|
||||||
|
assert not http.parse_url("http://\xfafoo")
|
||||||
|
# Invalid PATH
|
||||||
|
assert not http.parse_url("http:/\xc6/localhost:56121")
|
||||||
|
# Null byte in host
|
||||||
|
assert not http.parse_url("http://foo\0")
|
||||||
|
# Port out of range
|
||||||
|
assert not http.parse_url("http://foo:999999")
|
||||||
|
# Invalid IPv6 URL - see http://www.ietf.org/rfc/rfc2732.txt
|
||||||
|
assert not http.parse_url('http://lo[calhost')
|
6
test/http/test_user_agents.py
Normal file
6
test/http/test_user_agents.py
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
from netlib.http import user_agents
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_shortcut():
|
||||||
|
assert user_agents.get_by_shortcut("c")[0] == "chrome"
|
||||||
|
assert not user_agents.get_by_shortcut("_")
|
@ -1,6 +0,0 @@
|
|||||||
from netlib import http_uastrings
|
|
||||||
|
|
||||||
|
|
||||||
def test_get_shortcut():
|
|
||||||
assert http_uastrings.get_by_shortcut("c")[0] == "chrome"
|
|
||||||
assert not http_uastrings.get_by_shortcut("_")
|
|
0
test/websockets/__init__.py
Normal file
0
test/websockets/__init__.py
Normal file
@ -2,8 +2,9 @@ import os
|
|||||||
|
|
||||||
from nose.tools import raises
|
from nose.tools import raises
|
||||||
|
|
||||||
from netlib import tcp, websockets, http
|
from netlib import tcp, http, websockets
|
||||||
from . import tutils, tservers
|
from netlib.http.exceptions import *
|
||||||
|
from .. import tutils, tservers
|
||||||
|
|
||||||
|
|
||||||
class WebSocketsEchoHandler(tcp.BaseHandler):
|
class WebSocketsEchoHandler(tcp.BaseHandler):
|
||||||
@ -31,10 +32,10 @@ class WebSocketsEchoHandler(tcp.BaseHandler):
|
|||||||
frame.to_file(self.wfile)
|
frame.to_file(self.wfile)
|
||||||
|
|
||||||
def handshake(self):
|
def handshake(self):
|
||||||
req = http.read_request(self.rfile)
|
req = http.http1.read_request(self.rfile)
|
||||||
key = self.protocol.check_client_handshake(req.headers)
|
key = self.protocol.check_client_handshake(req.headers)
|
||||||
|
|
||||||
self.wfile.write(http.response_preamble(101) + "\r\n")
|
self.wfile.write(http.http1.response_preamble(101) + "\r\n")
|
||||||
headers = self.protocol.server_handshake_headers(key)
|
headers = self.protocol.server_handshake_headers(key)
|
||||||
self.wfile.write(headers.format() + "\r\n")
|
self.wfile.write(headers.format() + "\r\n")
|
||||||
self.wfile.flush()
|
self.wfile.flush()
|
||||||
@ -55,14 +56,14 @@ class WebSocketsClient(tcp.TCPClient):
|
|||||||
def connect(self):
|
def connect(self):
|
||||||
super(WebSocketsClient, self).connect()
|
super(WebSocketsClient, self).connect()
|
||||||
|
|
||||||
preamble = http.request_preamble("GET", "/")
|
preamble = http.http1.protocol.request_preamble("GET", "/")
|
||||||
self.wfile.write(preamble + "\r\n")
|
self.wfile.write(preamble + "\r\n")
|
||||||
headers = self.protocol.client_handshake_headers()
|
headers = self.protocol.client_handshake_headers()
|
||||||
self.client_nonce = headers.get_first("sec-websocket-key")
|
self.client_nonce = headers.get_first("sec-websocket-key")
|
||||||
self.wfile.write(headers.format() + "\r\n")
|
self.wfile.write(headers.format() + "\r\n")
|
||||||
self.wfile.flush()
|
self.wfile.flush()
|
||||||
|
|
||||||
resp = http.read_response(self.rfile, "get", None)
|
resp = http.http1.protocol.read_response(self.rfile, "get", None)
|
||||||
server_nonce = self.protocol.check_server_handshake(resp.headers)
|
server_nonce = self.protocol.check_server_handshake(resp.headers)
|
||||||
|
|
||||||
if not server_nonce == self.protocol.create_server_nonce(
|
if not server_nonce == self.protocol.create_server_nonce(
|
||||||
@ -150,10 +151,10 @@ class TestWebSockets(tservers.ServerTestBase):
|
|||||||
class BadHandshakeHandler(WebSocketsEchoHandler):
|
class BadHandshakeHandler(WebSocketsEchoHandler):
|
||||||
|
|
||||||
def handshake(self):
|
def handshake(self):
|
||||||
client_hs = http.read_request(self.rfile)
|
client_hs = http.http1.protocol.read_request(self.rfile)
|
||||||
self.protocol.check_client_handshake(client_hs.headers)
|
self.protocol.check_client_handshake(client_hs.headers)
|
||||||
|
|
||||||
self.wfile.write(http.response_preamble(101) + "\r\n")
|
self.wfile.write(http.http1.protocol.response_preamble(101) + "\r\n")
|
||||||
headers = self.protocol.server_handshake_headers("malformed key")
|
headers = self.protocol.server_handshake_headers("malformed key")
|
||||||
self.wfile.write(headers.format() + "\r\n")
|
self.wfile.write(headers.format() + "\r\n")
|
||||||
self.wfile.flush()
|
self.wfile.flush()
|
Loading…
Reference in New Issue
Block a user