Repository: twisted/twisted

Issue:
Wrong Set-Cookie header causes a traceback in Twisted
|[<img alt="itshane's avatar" src="https://avatars.githubusercontent.com/u/1818322?s=50" width="50" height="50">](https://github.com/itshane)| @itshane reported|
|-|-|
|Trac ID|trac#9136|
|Type|defect|
|Created|2017-05-15 08:06:56Z|

When the Set-Cookie header comes as a list which just contains the following "HttpOnly;Secure" string, it causes a "exceptions.ValueError: need more than 1 value to unpack" traceback.

```
Unhandled Error
Traceback (most recent call last):
  File "/usr/lib/python2.7/site-packages/twisted/python/log.py", line 101, in callWithLogger
    return callWithContext({"system": lp}, func, *args, **kw)
  File "/usr/lib/python2.7/site-packages/twisted/python/log.py", line 84, in callWithContext
    return context.call({ILogContext: newCtx}, func, *args, **kw)
  File "/usr/lib/python2.7/site-packages/twisted/python/context.py", line 118, in callWithContext
    return self.currentContext().callWithContext(ctx, func, *args, **kw)
  File "/usr/lib/python2.7/site-packages/twisted/python/context.py", line 81, in callWithContext
    return func(*args,**kw)
--- <exception caught here> ---
  File "/usr/lib/python2.7/site-packages/twisted/internet/posixbase.py", line 597, in _doReadOrWrite
    why = selectable.doRead()
  File "/usr/lib/python2.7/site-packages/twisted/internet/tcp.py", line 209, in doRead
    return self._dataReceived(data)
  File "/usr/lib/python2.7/site-packages/twisted/internet/tcp.py", line 215, in _dataReceived
    rval = self.protocol.dataReceived(data)
  File "/usr/lib/python2.7/site-packages/twisted/protocols/tls.py", line 422, in dataReceived
    self._flushReceiveBIO()
  File "/usr/lib/python2.7/site-packages/twisted/protocols/tls.py", line 392, in _flushReceiveBIO
    ProtocolWrapper.dataReceived(self, bytes)
  File "/usr/lib/python2.7/site-packages/twisted/protocols/policies.py", line 120, in dataReceived
    self.wrappedProtocol.dataReceived(data)
  File "/usr/lib/python2.7/site-packages/twisted/protocols/basic.py", line 571, in dataReceived
    why = self.lineReceived(line)
  File "/usr/lib/python2.7/site-packages/twisted/web/http.py", line 459, in lineReceived
    self.handleEndHeaders()
  File "/usr/lib/python2.7/site-packages/twisted/web/client.py", line 144, in handleEndHeaders
    self.factory.gotHeaders(self.headers)
  File "/usr/lib/python2.7/site-packages/twisted/web/client.py", line 420, in gotHeaders
    k, v = cook.split(b'=', 1)
exceptions.ValueError: need more than 1 value to unpack
```

It would be better just to omit the wrong part of the header, therefore, a response can be gracefully processed.

<details><summary>Searchable metadata</summary>

```
trac-id__9136 9136
type__defect defect
reporter__itshane itshane
priority__normal normal
milestone__None None
branch__ 
branch_author__ 
status__closed closed
resolution__fixed fixed
component__web web
keywords__None None
time__1494835616649140 1494835616649140
changetime__1502610226137736 1502610226137736
version__None None
owner__itshane itshane

```
</details>



Discussion:

Resolving patch:
diff --git a/src/twisted/web/client.py b/src/twisted/web/client.py
--- a/src/twisted/web/client.py
+++ b/src/twisted/web/client.py
@@ -411,14 +411,21 @@ def _cancelTimeout(self, result, timeoutCall):
         return result
 
     def gotHeaders(self, headers):
+        """
+        Parse the response HTTP headers.
+
+        @param headers: The response HTTP headers.
+        @type headers: L{dict}
+        """
         self.response_headers = headers
         if b'set-cookie' in headers:
             for cookie in headers[b'set-cookie']:
-                cookparts = cookie.split(b';')
-                cook = cookparts[0]
-                cook.lstrip()
-                k, v = cook.split(b'=', 1)
-                self.cookies[k.lstrip()] = v.lstrip()
+                if b'=' in cookie:
+                    cookparts = cookie.split(b';')
+                    cook = cookparts[0]
+                    cook.lstrip()
+                    k, v = cook.split(b'=', 1)
+                    self.cookies[k.lstrip()] = v.lstrip()
 
     def gotStatus(self, version, status, message):
         """


Test patch:
diff --git a/src/twisted/web/test/test_webclient.py b/src/twisted/web/test/test_webclient.py
--- a/src/twisted/web/test/test_webclient.py
+++ b/src/twisted/web/test/test_webclient.py
@@ -958,6 +958,7 @@ def testCookieHeaderParsing(self):
             b'Set-Cookie: CUSTOMER=WILE_E_COYOTE; path=/; expires=Wednesday, 09-Nov-99 23:12:40 GMT',
             b'Set-Cookie: PART_NUMBER=ROCKET_LAUNCHER_0001; path=/',
             b'Set-Cookie: SHIPPING=FEDEX; path=/foo',
+            b'Set-Cookie: HttpOnly;Secure',
             b'',
             b'body',
             b'more body',