** Description changed: [impact] python-twisted errors out with "'NoneType' object has no attribute 'encode' in requestReceived()" when it tries to parse a multipart mime message and python3.7+ is used. This happens because before commit cc3fa20 in cpython, cgi.parse_multipart ignored parts without a name defined in "content-disposition" (or parts without headers for that matter) but after 3.7+ the return of the function can now contain NoneType keys, which fail to encode. [scope] - This bug affects all releases + Even though this bug affects all python3-twisted releases, I'll backport + the fix just to Focal, Groovy and Hirsute. Bionic and Xenial do not have + Python 3.7 as the default interpreter (required to trigger the issue), + and the delta in python3-twisted might be to big to backport as the + current packages do not contemplate _PY37PLUS at all. Fixed upstream with commit 310496249, available since 21.2.0rc1 [test case] 1. Save the following code as webserver.py from twisted.application.internet import TCPServer from twisted.application.service import Application from twisted.web.resource import Resource from twisted.web.server import Site - class Foo(Resource): - def render_POST(self, request): - newdata = request.content.getvalue() - print(newdata) - return '' - + def render_POST(self, request): + newdata = request.content.getvalue() + print(newdata) + return '' root = Resource() root.putChild("foo", Foo()) application = Application("cgi.parse_multipart test") TCPServer(8080, Site(root)).setServiceParent(application) 2. Save the following code as client.py (python3-httplib2 is required) #!/usr/bin/env python import httplib2 - def http_request(url, method, body=None, headers=None, insecure=False): - """Issue an http request.""" - http = httplib2.Http(disable_ssl_certificate_validation=insecure) - if isinstance(url, bytes): - url = url.decode("ascii") - return http.request(url, method, body=body, headers=headers) - + """Issue an http request.""" + http = httplib2.Http(disable_ssl_certificate_validation=insecure) + if isinstance(url, bytes): + url = url.decode("ascii") + return http.request(url, method, body=body, headers=headers) url = "http://localhost:8080" method = "POST" headers = {'Content-Type': 'multipart/form-data; boundary="8825899812428059282"'} emptyh = '--8825899812428059282\n\n--8825899812428059282--' print("== BODY: " + emptyh + "\n") response, content = http_request(url, method, emptyh, headers) 3. Run the server with "twistd3 -y webserver.py" 4. Run the client 5. twistd will fail to encode the key and will drop this traceback in the log file (twistd.log) 2021-02-16T13:41:39+0100 [_GenericHTTPChannelProtocol,7,127.0.0.1] Unhandled Error - Traceback (most recent call last): - File "/usr/lib/python3/dist-packages/twisted/python/log.py", line 103, in callWithLogger - return callWithContext({"system": lp}, func, *args, **kw) - File "/usr/lib/python3/dist-packages/twisted/python/log.py", line 86, in callWithContext - return context.call({ILogContext: newCtx}, func, *args, **kw) - File "/usr/lib/python3/dist-packages/twisted/python/context.py", line 122, in callWithContext - return self.currentContext().callWithContext(ctx, func, *args, **kw) - File "/usr/lib/python3/dist-packages/twisted/python/context.py", line 85, in callWithContext - return func(*args,**kw) - --- <exception caught here> --- - File "/usr/lib/python3/dist-packages/twisted/internet/posixbase.py", line 614, in _doReadOrWrite - why = selectable.doRead() - File "/usr/lib/python3/dist-packages/twisted/internet/tcp.py", line 243, in doRead - return self._dataReceived(data) - File "/usr/lib/python3/dist-packages/twisted/internet/tcp.py", line 249, in _dataReceived - rval = self.protocol.dataReceived(data) - File "/usr/lib/python3/dist-packages/twisted/web/http.py", line 2952, in dataReceived - return self._channel.dataReceived(data) - File "/usr/lib/python3/dist-packages/twisted/web/http.py", line 2245, in dataReceived - return basic.LineReceiver.dataReceived(self, data) - File "/usr/lib/python3/dist-packages/twisted/protocols/basic.py", line 579, in dataReceived - why = self.rawDataReceived(data) - File "/usr/lib/python3/dist-packages/twisted/web/http.py", line 2252, in rawDataReceived - self._transferDecoder.dataReceived(data) - File "/usr/lib/python3/dist-packages/twisted/web/http.py", line 1699, in dataReceived - finishCallback(data[contentLength:]) - File "/usr/lib/python3/dist-packages/twisted/web/http.py", line 2115, in _finishRequestBody - self.allContentReceived() - File "/usr/lib/python3/dist-packages/twisted/web/http.py", line 2224, in allContentReceived - req.requestReceived(command, path, version) - File "/usr/lib/python3/dist-packages/twisted/web/http.py", line 898, in requestReceived - self.args.update({ - File "/usr/lib/python3/dist-packages/twisted/web/http.py", line 899, in <dictcomp> - x.encode('iso-8859-1'): \ - builtins.AttributeError: 'NoneType' object has no attribute 'encode' + Traceback (most recent call last): + File "/usr/lib/python3/dist-packages/twisted/python/log.py", line 103, in callWithLogger + return callWithContext({"system": lp}, func, *args, **kw) + File "/usr/lib/python3/dist-packages/twisted/python/log.py", line 86, in callWithContext + return context.call({ILogContext: newCtx}, func, *args, **kw) + File "/usr/lib/python3/dist-packages/twisted/python/context.py", line 122, in callWithContext + return self.currentContext().callWithContext(ctx, func, *args, **kw) + File "/usr/lib/python3/dist-packages/twisted/python/context.py", line 85, in callWithContext + return func(*args,**kw) + --- <exception caught here> --- + File "/usr/lib/python3/dist-packages/twisted/internet/posixbase.py", line 614, in _doReadOrWrite + why = selectable.doRead() + File "/usr/lib/python3/dist-packages/twisted/internet/tcp.py", line 243, in doRead + return self._dataReceived(data) + File "/usr/lib/python3/dist-packages/twisted/internet/tcp.py", line 249, in _dataReceived + rval = self.protocol.dataReceived(data) + File "/usr/lib/python3/dist-packages/twisted/web/http.py", line 2952, in dataReceived + return self._channel.dataReceived(data) + File "/usr/lib/python3/dist-packages/twisted/web/http.py", line 2245, in dataReceived + return basic.LineReceiver.dataReceived(self, data) + File "/usr/lib/python3/dist-packages/twisted/protocols/basic.py", line 579, in dataReceived + why = self.rawDataReceived(data) + File "/usr/lib/python3/dist-packages/twisted/web/http.py", line 2252, in rawDataReceived + self._transferDecoder.dataReceived(data) + File "/usr/lib/python3/dist-packages/twisted/web/http.py", line 1699, in dataReceived + finishCallback(data[contentLength:]) + File "/usr/lib/python3/dist-packages/twisted/web/http.py", line 2115, in _finishRequestBody + self.allContentReceived() + File "/usr/lib/python3/dist-packages/twisted/web/http.py", line 2224, in allContentReceived + req.requestReceived(command, path, version) + File "/usr/lib/python3/dist-packages/twisted/web/http.py", line 898, in requestReceived + self.args.update({ + File "/usr/lib/python3/dist-packages/twisted/web/http.py", line 899, in <dictcomp> + x.encode('iso-8859-1'): \ + builtins.AttributeError: 'NoneType' object has no attribute 'encode' [regression potential] This affects the returned dictionaries with non-str keys, which were discarded in python3.6 or earlier before they reached twisted, so patching this will make its behavior consistent.
-- You received this bug notification because you are a member of Ubuntu Bugs, which is subscribed to Ubuntu. https://bugs.launchpad.net/bugs/1915819 Title: 'NoneType' object has no attribute 'encode' in requestReceived() when multipart body doesn't include content-disposition To manage notifications about this bug go to: https://bugs.launchpad.net/ubuntu/+source/twisted/+bug/1915819/+subscriptions -- ubuntu-bugs mailing list ubuntu-bugs@lists.ubuntu.com https://lists.ubuntu.com/mailman/listinfo/ubuntu-bugs