diff options
author | Kun Huang <gareth@unitedstack.com> | 2013-07-07 16:45:56 +0800 |
---|---|---|
committer | Darrell Bishop <darrell@swiftstack.com> | 2013-07-12 15:18:49 -0700 |
commit | 7d88d14def2541227fb4d3f96016a51b0bafa36b (patch) | |
tree | adc3117986b3b0a95f0c549f30d3b98b3894f210 | |
parent | 65e5ef7b8137062a9a82b3fc99f33074f1e7c2e7 (diff) | |
download | python-swiftclient-7d88d14def2541227fb4d3f96016a51b0bafa36b.tar.gz |
Refuse carriage return in header value
See bug #1188896.
Comparing with Curl and Django, they both refuse carriage returns in
header values, so the request() method on the HTTP(S)Connection instance
returned by swiftclient.client.http_connection() will raise an
InvalidHeadersException if any of the headers to be sent contain a
newline.
Drive-by fix for a couple of header values which were integers instead
of strings (Content-Length getting set to zero).
Fixes bug #1188896
Change-Id: Ic6afdb92882284f843aacb06d20f682ddcb47151
-rw-r--r-- | swiftclient/client.py | 20 | ||||
-rw-r--r-- | tests/test_swiftclient.py | 14 |
2 files changed, 31 insertions, 3 deletions
diff --git a/swiftclient/client.py b/swiftclient/client.py index 03dad31..2ad392f 100644 --- a/swiftclient/client.py +++ b/swiftclient/client.py @@ -76,6 +76,17 @@ def quote(value, safe='/'): return value +def validate_headers(headers): + if headers: + for key, value in headers.iteritems(): + if '\n' in value: + raise InvalidHeadersException("%r header contained a " + "newline" % key) + if '\r' in value: + raise InvalidHeadersException("%r header contained a " + "carriage return" % key) + + def encode_utf8(value): if isinstance(value, unicode): value = value.encode('utf8') @@ -91,6 +102,10 @@ except ImportError: from json import loads as json_loads +class InvalidHeadersException(Exception): + pass + + class ClientException(Exception): def __init__(self, msg, http_scheme='', http_host='', http_port='', @@ -187,6 +202,7 @@ def http_connection(url, proxy=None, ssl_compression=True): @wraps(func) def request_escaped(method, url, body=None, headers=None): + validate_headers(headers) url = encode_utf8(url) if body: body = encode_utf8(body) @@ -635,7 +651,7 @@ def put_container(url, token, container, headers=None, http_conn=None, headers = {} headers['X-Auth-Token'] = token if not 'content-length' in (k.lower() for k in headers): - headers['Content-Length'] = 0 + headers['Content-Length'] = '0' conn.request(method, path, '', headers) resp = conn.getresponse() body = resp.read() @@ -675,7 +691,7 @@ def post_container(url, token, container, headers, http_conn=None, method = 'POST' headers['X-Auth-Token'] = token if not 'content-length' in (k.lower() for k in headers): - headers['Content-Length'] = 0 + headers['Content-Length'] = '0' conn.request(method, path, '', headers) resp = conn.getresponse() body = resp.read() diff --git a/tests/test_swiftclient.py b/tests/test_swiftclient.py index e0fa400..2b4782a 100644 --- a/tests/test_swiftclient.py +++ b/tests/test_swiftclient.py @@ -193,6 +193,18 @@ class TestHttpHelpers(MockHttpTest): url = 'ftp://www.test.com' self.assertRaises(c.ClientException, c.http_connection, url) + def test_validate_headers(self): + headers = {'key': 'value'} + self.assertEquals(c.validate_headers(headers), None) + + headers = {'key': 'value1\nvalue2'} + self.assertRaises(c.InvalidHeadersException, c.validate_headers, + headers) + + headers = {'key': 'value1\rvalue2'} + self.assertRaises(c.InvalidHeadersException, c.validate_headers, + headers) + # TODO: following tests are placeholders, need more tests, better coverage @@ -595,7 +607,7 @@ class TestPostObject(MockHttpTest): u'\u5929\u7a7a\u4e2d\u7684\u4e4c\u4e91', u'\u5929\u7a7a\u4e2d\u7684\u4e4c\u4e91') headers = {'X-Header1': u'\u5929\u7a7a\u4e2d\u7684\u4e4c\u4e91', - 'X-2': 1, 'X-3': {'a': 'b'}, 'a-b': '.x:yz mn:kl:qr'} + 'X-2': '1', 'X-3': {'a': 'b'}, 'a-b': '.x:yz mn:kl:qr'} resp = MockHttpResponse() conn[1].getresponse = resp.fake_response |