Merge branch 'trunk' into 9496-names-traceback
This commit is contained in:
commit
fecfc159fe
|
@ -873,7 +873,8 @@ class Request:
|
||||||
@type version: C{bytes}
|
@type version: C{bytes}
|
||||||
@param version: The HTTP version of this request.
|
@param version: The HTTP version of this request.
|
||||||
"""
|
"""
|
||||||
self.content.seek(0,0)
|
clength = self.content.tell()
|
||||||
|
self.content.seek(0, 0)
|
||||||
self.args = {}
|
self.args = {}
|
||||||
|
|
||||||
self.method, self.uri = command, path
|
self.method, self.uri = command, path
|
||||||
|
@ -889,16 +890,16 @@ class Request:
|
||||||
# Argument processing
|
# Argument processing
|
||||||
args = self.args
|
args = self.args
|
||||||
ctype = self.requestHeaders.getRawHeaders(b'content-type')
|
ctype = self.requestHeaders.getRawHeaders(b'content-type')
|
||||||
clength = self.requestHeaders.getRawHeaders(b'content-length')
|
|
||||||
if ctype is not None:
|
if ctype is not None:
|
||||||
ctype = ctype[0]
|
ctype = ctype[0]
|
||||||
|
|
||||||
if clength is not None:
|
|
||||||
clength = clength[0]
|
|
||||||
|
|
||||||
if self.method == b"POST" and ctype and clength:
|
if self.method == b"POST" and ctype and clength:
|
||||||
mfd = b'multipart/form-data'
|
mfd = b'multipart/form-data'
|
||||||
key, pdict = _parseHeader(ctype)
|
key, pdict = _parseHeader(ctype)
|
||||||
|
# This weird CONTENT-LENGTH param is required by
|
||||||
|
# cgi.parse_multipart() in some versions of Python 3.7+, see
|
||||||
|
# bpo-29979. It looks like this will be relaxed and backported, see
|
||||||
|
# https://github.com/python/cpython/pull/8530.
|
||||||
pdict["CONTENT-LENGTH"] = clength
|
pdict["CONTENT-LENGTH"] = clength
|
||||||
if key == b'application/x-www-form-urlencoded':
|
if key == b'application/x-www-form-urlencoded':
|
||||||
args.update(parse_qs(self.content.read(), 1))
|
args.update(parse_qs(self.content.read(), 1))
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
twisted.web.http.Request now correctly parses multipart-encoded form data submitted as a chunked request on Python 3.7+.
|
|
@ -1426,6 +1426,73 @@ class ChunkingTests(unittest.TestCase, ResponseTestMixin):
|
||||||
b"Transfer-Encoding: chunked",
|
b"Transfer-Encoding: chunked",
|
||||||
b"5\r\nHello\r\n6\r\nWorld!\r\n")])
|
b"5\r\nHello\r\n6\r\nWorld!\r\n")])
|
||||||
|
|
||||||
|
def runChunkedRequest(self, httpRequest, requestFactory=None,
|
||||||
|
chunkSize=1):
|
||||||
|
"""
|
||||||
|
Execute a web request based on plain text content, chunking
|
||||||
|
the request payload.
|
||||||
|
|
||||||
|
This is a stripped-down, chunking version of ParsingTests.runRequest.
|
||||||
|
"""
|
||||||
|
channel = http.HTTPChannel()
|
||||||
|
|
||||||
|
if requestFactory:
|
||||||
|
channel.requestFactory = _makeRequestProxyFactory(requestFactory)
|
||||||
|
|
||||||
|
httpRequest = httpRequest.replace(b"\n", b"\r\n")
|
||||||
|
header, body = httpRequest.split(b"\r\n\r\n", 1)
|
||||||
|
|
||||||
|
transport = StringTransport()
|
||||||
|
|
||||||
|
channel.makeConnection(transport)
|
||||||
|
channel.dataReceived(header+b"\r\n\r\n")
|
||||||
|
|
||||||
|
for pos in range(len(body)//chunkSize+1):
|
||||||
|
if channel.transport.disconnecting:
|
||||||
|
break
|
||||||
|
channel.dataReceived(b"".join(
|
||||||
|
http.toChunk(body[pos*chunkSize:(pos+1)*chunkSize])))
|
||||||
|
|
||||||
|
channel.dataReceived(b"".join(http.toChunk(b"")))
|
||||||
|
channel.connectionLost(IOError("all done"))
|
||||||
|
|
||||||
|
return channel
|
||||||
|
|
||||||
|
def test_multipartFormData(self):
|
||||||
|
"""
|
||||||
|
Test that chunked uploads are actually processed into args.
|
||||||
|
|
||||||
|
This is essentially a copy of ParsingTests.test_multipartFormData,
|
||||||
|
just with chunking put in.
|
||||||
|
|
||||||
|
This fails as of twisted version 18.9.0 because of bug #9678.
|
||||||
|
"""
|
||||||
|
processed = []
|
||||||
|
|
||||||
|
class MyRequest(http.Request):
|
||||||
|
def process(self):
|
||||||
|
processed.append(self)
|
||||||
|
self.write(b"done")
|
||||||
|
self.finish()
|
||||||
|
req = b'''\
|
||||||
|
POST / HTTP/1.0
|
||||||
|
Content-Type: multipart/form-data; boundary=AaB03x
|
||||||
|
Transfer-Encoding: chunked
|
||||||
|
|
||||||
|
--AaB03x
|
||||||
|
Content-Type: text/plain
|
||||||
|
Content-Disposition: form-data; name="text"
|
||||||
|
Content-Transfer-Encoding: quoted-printable
|
||||||
|
|
||||||
|
abasdfg
|
||||||
|
--AaB03x--
|
||||||
|
'''
|
||||||
|
channel = self.runChunkedRequest(req, MyRequest, chunkSize=5)
|
||||||
|
self.assertEqual(channel.transport.value(),
|
||||||
|
b"HTTP/1.0 200 OK\r\n\r\ndone")
|
||||||
|
self.assertEqual(len(processed), 1)
|
||||||
|
self.assertEqual(processed[0].args, {b"text": [b"abasdfg"]})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ParsingTests(unittest.TestCase):
|
class ParsingTests(unittest.TestCase):
|
||||||
|
|
6
tox.ini
6
tox.ini
|
@ -48,9 +48,9 @@ extras =
|
||||||
deps =
|
deps =
|
||||||
py27-alldeps-{posix,macos}: pysqlite
|
py27-alldeps-{posix,macos}: pysqlite
|
||||||
|
|
||||||
; Coverage 5.0 does not work with codecov.
|
{withcov}: coverage
|
||||||
{withcov}: coverage<5.0
|
|
||||||
{coverage-prepare,codecov-publish}: coverage<5.0
|
{coverage-prepare,codecov-publish}: coverage
|
||||||
|
|
||||||
{codecov-push,codecov-publish}: codecov
|
{codecov-push,codecov-publish}: codecov
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue