mirror of
https://github.com/python/cpython.git
synced 2025-12-04 08:34:25 +00:00
Fixed issue11082 - Reject str for POST data with a TypeError. Document the need to explicitly encode to bytes when using urlencode.
This commit is contained in:
parent
44028d8663
commit
2933312fe7
4 changed files with 23 additions and 9 deletions
|
|
@ -140,6 +140,7 @@ or on combining URL components into a URL string.
|
||||||
Use the :func:`urllib.parse.urlencode` function to convert such
|
Use the :func:`urllib.parse.urlencode` function to convert such
|
||||||
dictionaries into query strings.
|
dictionaries into query strings.
|
||||||
|
|
||||||
|
|
||||||
.. versionchanged:: 3.2
|
.. versionchanged:: 3.2
|
||||||
Add *encoding* and *errors* parameters.
|
Add *encoding* and *errors* parameters.
|
||||||
|
|
||||||
|
|
@ -506,9 +507,10 @@ task isn't already covered by the URL parsing functions above.
|
||||||
.. function:: urlencode(query, doseq=False, safe='', encoding=None, errors=None)
|
.. function:: urlencode(query, doseq=False, safe='', encoding=None, errors=None)
|
||||||
|
|
||||||
Convert a mapping object or a sequence of two-element tuples, which may
|
Convert a mapping object or a sequence of two-element tuples, which may
|
||||||
either be a :class:`str` or a :class:`bytes`, to a "percent-encoded" string,
|
either be a :class:`str` or a :class:`bytes`, to a "percent-encoded"
|
||||||
suitable to pass to :func:`urlopen` above as the optional *data* argument.
|
string. The resultant string must be converted to bytes using the
|
||||||
This is useful to pass a dictionary of form fields to a ``POST`` request.
|
user-specified encoding before it is sent to :func:`urlopen` as the optional
|
||||||
|
*data* argument.
|
||||||
The resulting string is a series of ``key=value`` pairs separated by ``'&'``
|
The resulting string is a series of ``key=value`` pairs separated by ``'&'``
|
||||||
characters, where both *key* and *value* are quoted using :func:`quote_plus`
|
characters, where both *key* and *value* are quoted using :func:`quote_plus`
|
||||||
above. When a sequence of two-element tuples is used as the *query*
|
above. When a sequence of two-element tuples is used as the *query*
|
||||||
|
|
@ -525,6 +527,9 @@ task isn't already covered by the URL parsing functions above.
|
||||||
To reverse this encoding process, :func:`parse_qs` and :func:`parse_qsl` are
|
To reverse this encoding process, :func:`parse_qs` and :func:`parse_qsl` are
|
||||||
provided in this module to parse query strings into Python data structures.
|
provided in this module to parse query strings into Python data structures.
|
||||||
|
|
||||||
|
Refer to :ref:`urllib examples <urllib-examples>` to find out how urlencode
|
||||||
|
method can be used for generating query string for a URL or data for POST.
|
||||||
|
|
||||||
.. versionchanged:: 3.2
|
.. versionchanged:: 3.2
|
||||||
Query parameter supports bytes and string objects.
|
Query parameter supports bytes and string objects.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -967,7 +967,7 @@ when the Python installation supports SSL. ::
|
||||||
|
|
||||||
>>> import urllib.request
|
>>> import urllib.request
|
||||||
>>> req = urllib.request.Request(url='https://localhost/cgi-bin/test.cgi',
|
>>> req = urllib.request.Request(url='https://localhost/cgi-bin/test.cgi',
|
||||||
... data='This data is passed to stdin of the CGI')
|
... data=b'This data is passed to stdin of the CGI')
|
||||||
>>> f = urllib.request.urlopen(req)
|
>>> f = urllib.request.urlopen(req)
|
||||||
>>> print(f.read().decode('utf-8'))
|
>>> print(f.read().decode('utf-8'))
|
||||||
Got Data: "This data is passed to stdin of the CGI"
|
Got Data: "This data is passed to stdin of the CGI"
|
||||||
|
|
@ -1043,11 +1043,13 @@ containing parameters::
|
||||||
>>> f = urllib.request.urlopen("http://www.musi-cal.com/cgi-bin/query?%s" % params)
|
>>> f = urllib.request.urlopen("http://www.musi-cal.com/cgi-bin/query?%s" % params)
|
||||||
>>> print(f.read().decode('utf-8'))
|
>>> print(f.read().decode('utf-8'))
|
||||||
|
|
||||||
The following example uses the ``POST`` method instead::
|
The following example uses the ``POST`` method instead. Note that params output
|
||||||
|
from urlencode is encoded to bytes before it is sent to urlopen as data::
|
||||||
|
|
||||||
>>> import urllib.request
|
>>> import urllib.request
|
||||||
>>> import urllib.parse
|
>>> import urllib.parse
|
||||||
>>> params = urllib.parse.urlencode({'spam': 1, 'eggs': 2, 'bacon': 0})
|
>>> params = urllib.parse.urlencode({'spam': 1, 'eggs': 2, 'bacon': 0})
|
||||||
|
>>> params = params.encode('utf-8')
|
||||||
>>> f = urllib.request.urlopen("http://www.musi-cal.com/cgi-bin/query", params)
|
>>> f = urllib.request.urlopen("http://www.musi-cal.com/cgi-bin/query", params)
|
||||||
>>> print(f.read().decode('utf-8'))
|
>>> print(f.read().decode('utf-8'))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -794,6 +794,10 @@ class HandlerTests(unittest.TestCase):
|
||||||
http.raise_on_endheaders = True
|
http.raise_on_endheaders = True
|
||||||
self.assertRaises(urllib.error.URLError, h.do_open, http, req)
|
self.assertRaises(urllib.error.URLError, h.do_open, http, req)
|
||||||
|
|
||||||
|
# Check for TypeError on POST data which is str.
|
||||||
|
req = Request("http://example.com/","badpost")
|
||||||
|
self.assertRaises(TypeError, h.do_request_, req)
|
||||||
|
|
||||||
# check adding of standard headers
|
# check adding of standard headers
|
||||||
o.addheaders = [("Spam", "eggs")]
|
o.addheaders = [("Spam", "eggs")]
|
||||||
for data in b"", None: # POST, GET
|
for data in b"", None: # POST, GET
|
||||||
|
|
@ -837,10 +841,11 @@ class HandlerTests(unittest.TestCase):
|
||||||
else:
|
else:
|
||||||
newreq = h.do_request_(req)
|
newreq = h.do_request_(req)
|
||||||
|
|
||||||
# A file object
|
# A file object.
|
||||||
|
# Test only Content-Length attribute of request.
|
||||||
|
|
||||||
file_obj = io.StringIO()
|
file_obj = io.BytesIO()
|
||||||
file_obj.write("Something\nSomething\nSomething\n")
|
file_obj.write(b"Something\nSomething\nSomething\n")
|
||||||
|
|
||||||
for headers in {}, {"Content-Length": 30}:
|
for headers in {}, {"Content-Length": 30}:
|
||||||
req = Request("http://example.com/", file_obj, headers)
|
req = Request("http://example.com/", file_obj, headers)
|
||||||
|
|
@ -863,7 +868,6 @@ class HandlerTests(unittest.TestCase):
|
||||||
newreq = h.do_request_(req)
|
newreq = h.do_request_(req)
|
||||||
self.assertEqual(int(newreq.get_header('Content-length')),16)
|
self.assertEqual(int(newreq.get_header('Content-length')),16)
|
||||||
|
|
||||||
|
|
||||||
def test_http_doubleslash(self):
|
def test_http_doubleslash(self):
|
||||||
# Checks the presence of any unnecessary double slash in url does not
|
# Checks the presence of any unnecessary double slash in url does not
|
||||||
# break anything. Previously, a double slash directly after the host
|
# break anything. Previously, a double slash directly after the host
|
||||||
|
|
|
||||||
|
|
@ -1048,6 +1048,9 @@ class AbstractHTTPHandler(BaseHandler):
|
||||||
|
|
||||||
if request.data is not None: # POST
|
if request.data is not None: # POST
|
||||||
data = request.data
|
data = request.data
|
||||||
|
if isinstance(data, str):
|
||||||
|
raise TypeError("POST data should be bytes"
|
||||||
|
" or an iterable of bytes. It cannot be str.")
|
||||||
if not request.has_header('Content-type'):
|
if not request.has_header('Content-type'):
|
||||||
request.add_unredirected_header(
|
request.add_unredirected_header(
|
||||||
'Content-type',
|
'Content-type',
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue