Skip to content

Commit

Permalink
Merging master into v1.3
Browse files Browse the repository at this point in the history
  • Loading branch information
shazow committed Mar 25, 2012
2 parents 751880f + 968263b commit bca8c7a
Show file tree
Hide file tree
Showing 15 changed files with 341 additions and 181 deletions.
3 changes: 2 additions & 1 deletion .coveragerc
Expand Up @@ -5,5 +5,6 @@ omit = urllib3/packages/*
exclude_lines =
.* # Platform-specific.*
except ImportError:
try:.* # Python 3
.*:.* # Python 3
pass
.* # Abstract
18 changes: 18 additions & 0 deletions CHANGES.rst
@@ -1,6 +1,24 @@
Changes
=======

1.3 (2012-03-25)
++++++++++++++++

* Removed pre-1.0 deprecated API.

* Refactored helpers into a ``urllib3.util`` submodule.

* Fixed multipart encoding to support list-of-tuples for keys with multiple
values. (Issue #48)

* Fixed multiple Set-Cookie headers in response not getting merged properly in
Python 3. (Issue #53)

* AppEngine support with Py27. (Issue #61)

* Minor ``encode_multipart_formdata`` fixes related to Python 3 strings vs
bytes.


1.2.2 (2012-02-06)
++++++++++++++++++
Expand Down
4 changes: 2 additions & 2 deletions dummyserver/server.py
Expand Up @@ -83,7 +83,7 @@ def _start_server(self):
else:
http_server = tornado.httpserver.HTTPServer(container)

http_server.listen(self.port)
http_server.listen(self.port, address=self.host)
return http_server

def run(self):
Expand All @@ -106,7 +106,7 @@ def stop(self):
if len(sys.argv) > 1:
url = sys.argv[1]

print("Starting WGI server at: %s" % url)
print("Starting WSGI server at: %s" % url)

scheme, host, port = get_host(url)
t = TornadoServerThread(scheme=scheme, host=host, port=port)
Expand Down
1 change: 1 addition & 0 deletions test/test_collections.py
Expand Up @@ -4,6 +4,7 @@
from urllib3.packages import six
xrange = six.moves.xrange


class TestLRUContainer(unittest.TestCase):
def test_maxsize(self):
d = Container(5)
Expand Down
10 changes: 3 additions & 7 deletions test/test_connectionpool.py
@@ -1,11 +1,7 @@
import unittest

from urllib3.connectionpool import (
connection_from_url,
get_host,
HTTPConnectionPool,
make_headers)

from urllib3.connectionpool import connection_from_url, HTTPConnectionPool
from urllib3.util import get_host, make_headers
from urllib3.exceptions import EmptyPoolError, LocationParseError


Expand Down Expand Up @@ -105,7 +101,7 @@ def test_max_connections(self):
pass

try:
pool.get_url('/', pool_timeout=0.01)
pool.request('GET', '/', pool_timeout=0.01)
self.fail("Managed to get a connection without EmptyPoolError")
except EmptyPoolError:
pass
Expand Down
85 changes: 85 additions & 0 deletions test/test_filepost.py
@@ -0,0 +1,85 @@
import unittest

from urllib3.filepost import encode_multipart_formdata, iter_fields
from urllib3.packages.six import b, u


BOUNDARY = '!! test boundary !!'


class TestIterfields(unittest.TestCase):

def test_dict(self):
for fieldname, value in iter_fields(dict(a='b')):
self.assertEqual((fieldname, value), ('a', 'b'))

self.assertEqual(
list(sorted(iter_fields(dict(a='b', c='d')))),
[('a', 'b'), ('c', 'd')])

def test_tuple_list(self):
for fieldname, value in iter_fields([('a', 'b')]):
self.assertEqual((fieldname, value), ('a', 'b'))

self.assertEqual(
list(iter_fields([('a', 'b'), ('c', 'd')])),
[('a', 'b'), ('c', 'd')])


class TestMultipartEncoding(unittest.TestCase):

def test_input_datastructures(self):
fieldsets = [
dict(k='v', k2='v2'),
[('k', 'v'), ('k2', 'v2')],
]

for fields in fieldsets:
encoded, _ = encode_multipart_formdata(fields, boundary=BOUNDARY)
self.assertEqual(encoded.count(b(BOUNDARY)), 3)


def test_field_encoding(self):
fieldsets = [
[('k', 'v'), ('k2', 'v2')],
[('k', b'v'), (u('k2'), b'v2')],
[('k', b'v'), (u('k2'), 'v2')],
]

for fields in fieldsets:
encoded, content_type = encode_multipart_formdata(fields, boundary=BOUNDARY)

self.assertEqual(encoded,
b'--' + b(BOUNDARY) + b'\r\n'
b'Content-Disposition: form-data; name="k"\r\n'
b'Content-Type: text/plain\r\n'
b'\r\n'
b'v\r\n'
b'--' + b(BOUNDARY) + b'\r\n'
b'Content-Disposition: form-data; name="k2"\r\n'
b'Content-Type: text/plain\r\n'
b'\r\n'
b'v2\r\n'
b'--' + b(BOUNDARY) + b'--\r\n'
, fields)

self.assertEqual(content_type,
b'multipart/form-data; boundary=' + b(BOUNDARY))


def test_filename(self):
fields = [('k', ('somename', b'v'))]

encoded, content_type = encode_multipart_formdata(fields, boundary=BOUNDARY)

self.assertEqual(encoded,
b'--' + b(BOUNDARY) + b'\r\n'
b'Content-Disposition: form-data; name="k"; filename="somename"\r\n'
b'Content-Type: application/octet-stream\r\n'
b'\r\n'
b'v\r\n'
b'--' + b(BOUNDARY) + b'--\r\n'
)

self.assertEqual(content_type,
b'multipart/form-data; boundary=' + b(BOUNDARY))
26 changes: 18 additions & 8 deletions test/with_dummyserver/test_connectionpool.py
Expand Up @@ -60,6 +60,21 @@ def test_upload(self):
r = self.pool.request('POST', '/upload', fields=fields)
self.assertEqual(r.status, 200, r.data)

def test_one_name_multiple_values(self):
fields = [
('foo', 'a'),
('foo', 'b'),
]

# urlencode
r = self.pool.request('GET', '/echo', fields=fields)
self.assertEqual(r.data, b'foo=a&foo=b')

# multipart
r = self.pool.request('POST', '/echo', fields=fields)
self.assertEqual(r.data.count(b'name="foo"'), 2)


def test_unicode_upload(self):
fieldname = u('myfile')
filename = u('\xe2\x99\xa5.txt')
Expand All @@ -86,13 +101,10 @@ def test_timeout(self):
pass

def test_redirect(self):
r = self.pool.request('GET', '/redirect',
fields={'target': '/'},
redirect=False)
r = self.pool.request('GET', '/redirect', fields={'target': '/'}, redirect=False)
self.assertEqual(r.status, 303)

r = self.pool.request('GET', '/redirect',
fields={'target': '/'})
r = self.pool.request('GET', '/redirect', fields={'target': '/'})
self.assertEqual(r.status, 200)
self.assertEqual(r.data, b'Dummy server!')

Expand Down Expand Up @@ -169,9 +181,7 @@ def test_keepalive_close(self):

def test_post_with_urlencode(self):
data = {'banana': 'hammock', 'lol': 'cat'}
r = self.pool.request('POST', '/echo',
fields=data,
encode_multipart=False)
r = self.pool.request('POST', '/echo', fields=data, encode_multipart=False)
self.assertEqual(r.data.decode('utf-8'), urlencode(data))

def test_post_with_multipart(self):
Expand Down
23 changes: 22 additions & 1 deletion test/with_dummyserver/test_socketlevel.py
@@ -1,11 +1,32 @@
from urllib3 import HTTPConnectionPool
from urllib3.poolmanager import ProxyManager, proxy_from_url
from urllib3.poolmanager import proxy_from_url

from dummyserver.testcase import SocketDummyServerTestCase

from threading import Event


class TestCookies(SocketDummyServerTestCase):

def test_multi_setcookie(self):
def multicookie_response_handler(listener):
sock = listener.accept()[0]

buf = b''
while not buf.endswith(b'\r\n\r\n'):
buf += sock.recv(65536)

sock.send(b'HTTP/1.1 200 OK\r\n'
b'Set-Cookie: foo=1\r\n'
b'Set-Cookie: bar=1\r\n'
b'\r\n')

self._start_server(multicookie_response_handler)
pool = HTTPConnectionPool(self.host, self.port)
r = pool.request('GET', '/', retries=0)
self.assertEquals(r.headers, {'set-cookie': 'foo=1, bar=1'})


class TestSocketClosing(SocketDummyServerTestCase):

def test_recovery_when_server_closes_connection(self):
Expand Down
18 changes: 6 additions & 12 deletions urllib3/__init__.py
Expand Up @@ -10,26 +10,20 @@

__author__ = 'Andrey Petrov (andrey.petrov@shazow.net)'
__license__ = 'MIT'
__version__ = '1.2.2'
__version__ = '1.3'


from .connectionpool import (
HTTPConnectionPool,
HTTPSConnectionPool,
connection_from_url,
get_host,
make_headers)


from .exceptions import (
HTTPError,
MaxRetryError,
SSLError,
TimeoutError)
connection_from_url
)

from . import exceptions
from .filepost import encode_multipart_formdata
from .poolmanager import PoolManager, ProxyManager, proxy_from_url
from .response import HTTPResponse
from .filepost import encode_multipart_formdata
from .util import make_headers, get_host


# Set default logging handler to avoid "No handler found" warnings.
Expand Down

0 comments on commit bca8c7a

Please sign in to comment.