2013-03-02 21:37:28 +00:00
|
|
|
import cStringIO, textwrap, binascii
|
2012-06-23 01:56:17 +00:00
|
|
|
from netlib import http, odict
|
2012-06-18 21:42:32 +00:00
|
|
|
import tutils
|
|
|
|
|
2013-03-02 23:16:09 +00:00
|
|
|
|
2012-06-23 03:07:42 +00:00
|
|
|
def test_httperror():
|
|
|
|
e = http.HttpError(404, "Not found")
|
|
|
|
assert str(e)
|
|
|
|
|
|
|
|
|
2012-06-18 21:42:32 +00:00
|
|
|
def test_has_chunked_encoding():
|
|
|
|
h = odict.ODictCaseless()
|
2012-06-23 01:56:17 +00:00
|
|
|
assert not http.has_chunked_encoding(h)
|
2012-06-18 21:42:32 +00:00
|
|
|
h["transfer-encoding"] = ["chunked"]
|
2012-06-23 01:56:17 +00:00
|
|
|
assert http.has_chunked_encoding(h)
|
2012-06-18 21:42:32 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_read_chunked():
|
|
|
|
s = cStringIO.StringIO("1\r\na\r\n0\r\n")
|
2012-06-23 03:07:42 +00:00
|
|
|
tutils.raises("closed prematurely", http.read_chunked, 500, s, None)
|
2012-06-18 21:42:32 +00:00
|
|
|
|
|
|
|
s = cStringIO.StringIO("1\r\na\r\n0\r\n\r\n")
|
2012-06-23 03:07:42 +00:00
|
|
|
assert http.read_chunked(500, s, None) == "a"
|
|
|
|
|
|
|
|
s = cStringIO.StringIO("\r\n\r\n1\r\na\r\n0\r\n\r\n")
|
|
|
|
assert http.read_chunked(500, s, None) == "a"
|
2012-06-18 21:42:32 +00:00
|
|
|
|
|
|
|
s = cStringIO.StringIO("\r\n")
|
2012-06-23 03:07:42 +00:00
|
|
|
tutils.raises("closed prematurely", http.read_chunked, 500, s, None)
|
2012-06-18 21:42:32 +00:00
|
|
|
|
|
|
|
s = cStringIO.StringIO("1\r\nfoo")
|
2012-06-23 03:07:42 +00:00
|
|
|
tutils.raises("malformed chunked body", http.read_chunked, 500, s, None)
|
2012-06-18 21:42:32 +00:00
|
|
|
|
|
|
|
s = cStringIO.StringIO("foo\r\nfoo")
|
2012-06-23 03:07:42 +00:00
|
|
|
tutils.raises(http.HttpError, http.read_chunked, 500, s, None)
|
|
|
|
|
|
|
|
s = cStringIO.StringIO("5\r\naaaaa\r\n0\r\n\r\n")
|
|
|
|
tutils.raises("too large", http.read_chunked, 500, s, 2)
|
2012-06-18 21:42:32 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_request_connection_close():
|
|
|
|
h = odict.ODictCaseless()
|
2012-06-23 01:56:17 +00:00
|
|
|
assert http.request_connection_close((1, 0), h)
|
|
|
|
assert not http.request_connection_close((1, 1), h)
|
2012-06-18 21:42:32 +00:00
|
|
|
|
|
|
|
h["connection"] = ["keep-alive"]
|
2012-06-23 01:56:17 +00:00
|
|
|
assert not http.request_connection_close((1, 1), h)
|
2012-06-18 21:42:32 +00:00
|
|
|
|
2012-06-23 03:07:42 +00:00
|
|
|
h["connection"] = ["close"]
|
|
|
|
assert http.request_connection_close((1, 1), h)
|
|
|
|
|
|
|
|
|
|
|
|
def test_response_connection_close():
|
|
|
|
h = odict.ODictCaseless()
|
|
|
|
assert http.response_connection_close((1, 1), h)
|
|
|
|
|
|
|
|
h["content-length"] = [10]
|
|
|
|
assert not http.response_connection_close((1, 1), h)
|
|
|
|
|
|
|
|
h["connection"] = ["close"]
|
|
|
|
assert http.response_connection_close((1, 1), h)
|
|
|
|
|
|
|
|
|
|
|
|
def test_read_http_body_response():
|
|
|
|
h = odict.ODictCaseless()
|
|
|
|
h["content-length"] = [7]
|
|
|
|
s = cStringIO.StringIO("testing")
|
2012-07-23 23:39:49 +00:00
|
|
|
assert http.read_http_body_response(s, h, None) == "testing"
|
|
|
|
|
|
|
|
|
|
|
|
h = odict.ODictCaseless()
|
|
|
|
s = cStringIO.StringIO("testing")
|
|
|
|
assert not http.read_http_body_response(s, h, None)
|
|
|
|
|
|
|
|
h = odict.ODictCaseless()
|
|
|
|
h["connection"] = ["close"]
|
|
|
|
s = cStringIO.StringIO("testing")
|
|
|
|
assert http.read_http_body_response(s, h, None) == "testing"
|
|
|
|
|
|
|
|
|
|
|
|
def test_get_header_tokens():
|
|
|
|
h = odict.ODictCaseless()
|
|
|
|
assert http.get_header_tokens(h, "foo") == []
|
|
|
|
h["foo"] = ["bar"]
|
|
|
|
assert http.get_header_tokens(h, "foo") == ["bar"]
|
|
|
|
h["foo"] = ["bar, voing"]
|
|
|
|
assert http.get_header_tokens(h, "foo") == ["bar", "voing"]
|
|
|
|
h["foo"] = ["bar, voing", "oink"]
|
|
|
|
assert http.get_header_tokens(h, "foo") == ["bar", "voing", "oink"]
|
2012-06-23 03:07:42 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_read_http_body_request():
|
|
|
|
h = odict.ODictCaseless()
|
|
|
|
h["expect"] = ["100-continue"]
|
|
|
|
r = cStringIO.StringIO("testing")
|
|
|
|
w = cStringIO.StringIO()
|
|
|
|
assert http.read_http_body_request(r, w, h, (1, 1), None) == ""
|
|
|
|
assert "100 Continue" in w.getvalue()
|
|
|
|
|
2012-06-18 21:42:32 +00:00
|
|
|
|
|
|
|
def test_read_http_body():
|
2012-06-23 03:07:42 +00:00
|
|
|
h = odict.ODictCaseless()
|
2012-06-18 21:42:32 +00:00
|
|
|
s = cStringIO.StringIO("testing")
|
2012-06-23 03:07:42 +00:00
|
|
|
assert http.read_http_body(500, s, h, False, None) == ""
|
2012-06-18 21:42:32 +00:00
|
|
|
|
|
|
|
h["content-length"] = ["foo"]
|
|
|
|
s = cStringIO.StringIO("testing")
|
2012-06-23 03:07:42 +00:00
|
|
|
tutils.raises(http.HttpError, http.read_http_body, 500, s, h, False, None)
|
2012-06-18 21:42:32 +00:00
|
|
|
|
|
|
|
h["content-length"] = [5]
|
|
|
|
s = cStringIO.StringIO("testing")
|
2012-06-23 03:07:42 +00:00
|
|
|
assert len(http.read_http_body(500, s, h, False, None)) == 5
|
2012-06-18 21:42:32 +00:00
|
|
|
s = cStringIO.StringIO("testing")
|
2012-06-23 03:07:42 +00:00
|
|
|
tutils.raises(http.HttpError, http.read_http_body, 500, s, h, False, 4)
|
2012-06-18 21:42:32 +00:00
|
|
|
|
2012-06-23 03:07:42 +00:00
|
|
|
h = odict.ODictCaseless()
|
2012-06-18 21:42:32 +00:00
|
|
|
s = cStringIO.StringIO("testing")
|
2012-06-23 03:07:42 +00:00
|
|
|
assert len(http.read_http_body(500, s, h, True, 4)) == 4
|
2012-06-18 21:42:32 +00:00
|
|
|
s = cStringIO.StringIO("testing")
|
2012-06-23 03:07:42 +00:00
|
|
|
assert len(http.read_http_body(500, s, h, True, 100)) == 7
|
|
|
|
|
|
|
|
h = odict.ODictCaseless()
|
|
|
|
h["transfer-encoding"] = ["chunked"]
|
|
|
|
s = cStringIO.StringIO("5\r\naaaaa\r\n0\r\n\r\n")
|
|
|
|
assert http.read_http_body(500, s, h, True, 100) == "aaaaa"
|
|
|
|
|
2012-06-18 21:42:32 +00:00
|
|
|
|
|
|
|
def test_parse_http_protocol():
|
2012-06-23 01:56:17 +00:00
|
|
|
assert http.parse_http_protocol("HTTP/1.1") == (1, 1)
|
|
|
|
assert http.parse_http_protocol("HTTP/0.0") == (0, 0)
|
2012-07-21 05:27:23 +00:00
|
|
|
assert not http.parse_http_protocol("HTTP/a.1")
|
|
|
|
assert not http.parse_http_protocol("HTTP/1.a")
|
2012-06-23 01:56:17 +00:00
|
|
|
assert not http.parse_http_protocol("foo/0.0")
|
2012-06-24 10:45:40 +00:00
|
|
|
assert not http.parse_http_protocol("HTTP/x")
|
2012-06-18 21:42:32 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_parse_init_connect():
|
2012-06-23 01:56:17 +00:00
|
|
|
assert http.parse_init_connect("CONNECT host.com:443 HTTP/1.0")
|
2013-03-03 09:13:23 +00:00
|
|
|
assert not http.parse_init_connect("C\xfeONNECT host.com:443 HTTP/1.0")
|
2013-03-03 08:36:19 +00:00
|
|
|
assert not http.parse_init_connect("CONNECT \0host.com:443 HTTP/1.0")
|
|
|
|
assert not http.parse_init_connect("CONNECT host.com:444444 HTTP/1.0")
|
2012-06-23 01:56:17 +00:00
|
|
|
assert not http.parse_init_connect("bogus")
|
|
|
|
assert not http.parse_init_connect("GET host.com:443 HTTP/1.0")
|
|
|
|
assert not http.parse_init_connect("CONNECT host.com443 HTTP/1.0")
|
|
|
|
assert not http.parse_init_connect("CONNECT host.com:443 foo/1.0")
|
2013-01-05 07:06:55 +00:00
|
|
|
assert not http.parse_init_connect("CONNECT host.com:foo HTTP/1.0")
|
2012-06-18 21:42:32 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_prase_init_proxy():
|
|
|
|
u = "GET http://foo.com:8888/test HTTP/1.1"
|
2012-06-23 01:56:17 +00:00
|
|
|
m, s, h, po, pa, httpversion = http.parse_init_proxy(u)
|
2012-06-18 21:42:32 +00:00
|
|
|
assert m == "GET"
|
|
|
|
assert s == "http"
|
|
|
|
assert h == "foo.com"
|
|
|
|
assert po == 8888
|
|
|
|
assert pa == "/test"
|
|
|
|
assert httpversion == (1, 1)
|
|
|
|
|
2013-03-03 09:13:23 +00:00
|
|
|
u = "G\xfeET http://foo.com:8888/test HTTP/1.1"
|
|
|
|
assert not http.parse_init_proxy(u)
|
|
|
|
|
2012-06-23 01:56:17 +00:00
|
|
|
assert not http.parse_init_proxy("invalid")
|
|
|
|
assert not http.parse_init_proxy("GET invalid HTTP/1.1")
|
|
|
|
assert not http.parse_init_proxy("GET http://foo.com:8888/test foo/1.1")
|
2012-06-18 21:42:32 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_parse_init_http():
|
|
|
|
u = "GET /test HTTP/1.1"
|
2013-03-03 09:13:23 +00:00
|
|
|
m, u, httpversion = http.parse_init_http(u)
|
2012-06-18 21:42:32 +00:00
|
|
|
assert m == "GET"
|
|
|
|
assert u == "/test"
|
|
|
|
assert httpversion == (1, 1)
|
2013-03-03 09:13:23 +00:00
|
|
|
|
|
|
|
u = "G\xfeET /test HTTP/1.1"
|
|
|
|
assert not http.parse_init_http(u)
|
|
|
|
|
2012-06-23 01:56:17 +00:00
|
|
|
assert not http.parse_init_http("invalid")
|
|
|
|
assert not http.parse_init_http("GET invalid HTTP/1.1")
|
|
|
|
assert not http.parse_init_http("GET /test foo/1.1")
|
2013-03-03 08:36:19 +00:00
|
|
|
assert not http.parse_init_http("GET /test\xc0 HTTP/1.1")
|
2012-06-18 21:42:32 +00:00
|
|
|
|
|
|
|
class TestReadHeaders:
|
2012-07-30 00:50:35 +00:00
|
|
|
def _read(self, data, verbatim=False):
|
|
|
|
if not verbatim:
|
|
|
|
data = textwrap.dedent(data)
|
|
|
|
data = data.strip()
|
|
|
|
s = cStringIO.StringIO(data)
|
|
|
|
return http.read_headers(s)
|
|
|
|
|
2012-06-18 21:42:32 +00:00
|
|
|
def test_read_simple(self):
|
|
|
|
data = """
|
|
|
|
Header: one
|
|
|
|
Header2: two
|
|
|
|
\r\n
|
|
|
|
"""
|
2012-07-30 00:50:35 +00:00
|
|
|
h = self._read(data)
|
2012-06-24 09:49:23 +00:00
|
|
|
assert h.lst == [["Header", "one"], ["Header2", "two"]]
|
2012-06-18 21:42:32 +00:00
|
|
|
|
|
|
|
def test_read_multi(self):
|
|
|
|
data = """
|
|
|
|
Header: one
|
|
|
|
Header: two
|
|
|
|
\r\n
|
|
|
|
"""
|
2012-07-30 00:50:35 +00:00
|
|
|
h = self._read(data)
|
2012-06-24 09:49:23 +00:00
|
|
|
assert h.lst == [["Header", "one"], ["Header", "two"]]
|
2012-06-18 21:42:32 +00:00
|
|
|
|
|
|
|
def test_read_continued(self):
|
|
|
|
data = """
|
|
|
|
Header: one
|
|
|
|
\ttwo
|
|
|
|
Header2: three
|
|
|
|
\r\n
|
|
|
|
"""
|
2012-07-30 00:50:35 +00:00
|
|
|
h = self._read(data)
|
2012-06-24 09:49:23 +00:00
|
|
|
assert h.lst == [["Header", "one\r\n two"], ["Header2", "three"]]
|
2012-06-18 21:42:32 +00:00
|
|
|
|
2012-07-30 00:50:35 +00:00
|
|
|
def test_read_continued_err(self):
|
|
|
|
data = "\tfoo: bar\r\n"
|
|
|
|
assert self._read(data, True) is None
|
|
|
|
|
|
|
|
def test_read_err(self):
|
|
|
|
data = """
|
|
|
|
foo
|
|
|
|
"""
|
|
|
|
assert self._read(data) is None
|
|
|
|
|
2012-06-18 21:42:32 +00:00
|
|
|
|
2012-06-24 10:45:40 +00:00
|
|
|
def test_read_response():
|
|
|
|
def tst(data, method, limit):
|
|
|
|
data = textwrap.dedent(data)
|
|
|
|
r = cStringIO.StringIO(data)
|
|
|
|
return http.read_response(r, method, limit)
|
|
|
|
|
2013-02-23 22:08:43 +00:00
|
|
|
tutils.raises("server disconnect", tst, "", "GET", None)
|
2012-06-24 10:45:40 +00:00
|
|
|
tutils.raises("invalid server response", tst, "foo", "GET", None)
|
|
|
|
data = """
|
|
|
|
HTTP/1.1 200 OK
|
|
|
|
"""
|
|
|
|
assert tst(data, "GET", None) == ((1, 1), 200, 'OK', odict.ODictCaseless(), '')
|
|
|
|
data = """
|
|
|
|
HTTP/1.1 200
|
|
|
|
"""
|
|
|
|
assert tst(data, "GET", None) == ((1, 1), 200, '', odict.ODictCaseless(), '')
|
|
|
|
data = """
|
|
|
|
HTTP/x 200 OK
|
|
|
|
"""
|
|
|
|
tutils.raises("invalid http version", tst, data, "GET", None)
|
|
|
|
data = """
|
|
|
|
HTTP/1.1 xx OK
|
|
|
|
"""
|
|
|
|
tutils.raises("invalid server response", tst, data, "GET", None)
|
|
|
|
|
|
|
|
data = """
|
|
|
|
HTTP/1.1 100 CONTINUE
|
|
|
|
|
|
|
|
HTTP/1.1 200 OK
|
|
|
|
"""
|
|
|
|
assert tst(data, "GET", None) == ((1, 1), 200, 'OK', odict.ODictCaseless(), '')
|
|
|
|
|
|
|
|
data = """
|
|
|
|
HTTP/1.1 200 OK
|
2012-06-24 11:16:06 +00:00
|
|
|
Content-Length: 3
|
2012-06-24 10:45:40 +00:00
|
|
|
|
|
|
|
foo
|
|
|
|
"""
|
2012-06-24 11:16:06 +00:00
|
|
|
assert tst(data, "GET", None)[4] == 'foo'
|
|
|
|
assert tst(data, "HEAD", None)[4] == ''
|
2012-06-24 10:45:40 +00:00
|
|
|
|
2012-07-30 00:50:35 +00:00
|
|
|
data = """
|
|
|
|
HTTP/1.1 200 OK
|
|
|
|
\tContent-Length: 3
|
|
|
|
|
|
|
|
foo
|
|
|
|
"""
|
|
|
|
tutils.raises("invalid headers", tst, data, "GET", None)
|
|
|
|
|
2012-06-24 10:45:40 +00:00
|
|
|
|
2012-06-18 21:42:32 +00:00
|
|
|
def test_parse_url():
|
2012-06-23 01:56:17 +00:00
|
|
|
assert not http.parse_url("")
|
2012-06-18 21:42:32 +00:00
|
|
|
|
|
|
|
u = "http://foo.com:8888/test"
|
2012-06-23 01:56:17 +00:00
|
|
|
s, h, po, pa = http.parse_url(u)
|
2012-06-18 21:42:32 +00:00
|
|
|
assert s == "http"
|
|
|
|
assert h == "foo.com"
|
|
|
|
assert po == 8888
|
|
|
|
assert pa == "/test"
|
|
|
|
|
2012-06-23 01:56:17 +00:00
|
|
|
s, h, po, pa = http.parse_url("http://foo/bar")
|
2012-06-18 21:42:32 +00:00
|
|
|
assert s == "http"
|
|
|
|
assert h == "foo"
|
|
|
|
assert po == 80
|
|
|
|
assert pa == "/bar"
|
|
|
|
|
2012-06-23 01:56:17 +00:00
|
|
|
s, h, po, pa = http.parse_url("http://foo")
|
2012-06-18 21:42:32 +00:00
|
|
|
assert pa == "/"
|
|
|
|
|
2012-06-23 01:56:17 +00:00
|
|
|
s, h, po, pa = http.parse_url("https://foo")
|
2012-06-18 21:42:32 +00:00
|
|
|
assert po == 443
|
|
|
|
|
2012-06-23 01:56:17 +00:00
|
|
|
assert not http.parse_url("https://foo:bar")
|
|
|
|
assert not http.parse_url("https://foo:")
|
2012-06-18 21:42:32 +00:00
|
|
|
|
2013-03-03 01:52:06 +00:00
|
|
|
# Invalid IDNA
|
|
|
|
assert not http.parse_url("http://\xfafoo")
|
2013-03-03 02:12:58 +00:00
|
|
|
# Invalid PATH
|
2013-03-03 01:52:06 +00:00
|
|
|
assert not http.parse_url("http:/\xc6/localhost:56121")
|
2013-03-03 02:12:58 +00:00
|
|
|
# Null byte in host
|
2013-03-03 02:03:57 +00:00
|
|
|
assert not http.parse_url("http://foo\0")
|
2013-03-03 02:12:58 +00:00
|
|
|
# Port out of range
|
2013-03-03 02:08:17 +00:00
|
|
|
assert not http.parse_url("http://foo:999999")
|
2013-03-03 02:12:58 +00:00
|
|
|
# Invalid IPv6 URL - see http://www.ietf.org/rfc/rfc2732.txt
|
|
|
|
assert not http.parse_url('http://lo[calhost')
|
2013-03-02 21:37:28 +00:00
|
|
|
|
|
|
|
def test_parse_http_basic_auth():
|
|
|
|
vals = ("basic", "foo", "bar")
|
|
|
|
assert http.parse_http_basic_auth(http.assemble_http_basic_auth(*vals)) == vals
|
|
|
|
assert not http.parse_http_basic_auth("")
|
|
|
|
assert not http.parse_http_basic_auth("foo bar")
|
|
|
|
v = "basic " + binascii.b2a_base64("foo")
|
|
|
|
assert not http.parse_http_basic_auth(v)
|
|
|
|
|