Repository: tornadoweb/tornado

Issue:
Cookies set with secure=False end up getting set as secure cookies
This one had me scratching my head for a little bit.  

It's not actually a tornado bug, so I understand if you don't feel like fixing it, but I think it would make sense for Tornado to handle this so that the behavior is not completely unexpected.

``` python
self.set_cookie('test', '1', secure=False)
```

Ends up writing back the cookie with secure flag.

I did the following 

``` python
>>> morsel = Cookie.SimpleCookie()
>>> morsel['test'] = '1'
>>> morsel['test']['secure'] = False
>>> morsel.output()
'Set-Cookie: test=1; secure'
```

Seems like SimpleCookie only checks for the presence of the key and not the value.  Could perhaps add a simple check to make sure `v != False` in this loop

https://github.com/tornadoweb/tornado/blob/96f81dfa48f73674c196af5da7e4255d2d228774/tornado/web.py#L524-L526



Discussion:

Resolving patch:
diff --git a/tornado/web.py b/tornado/web.py
--- a/tornado/web.py
+++ b/tornado/web.py
@@ -524,6 +524,12 @@ def set_cookie(self, name, value, domain=None, expires=None, path="/",
         for k, v in kwargs.items():
             if k == 'max_age':
                 k = 'max-age'
+
+            # skip falsy values for httponly and secure flags because
+            # SimpleCookie sets them regardless
+            if k in ['httponly', 'secure'] and not v:
+                continue
+
             morsel[k] = v
 
     def clear_cookie(self, name, path="/", domain=None):


Test patch:
diff --git a/tornado/test/web_test.py b/tornado/test/web_test.py
--- a/tornado/test/web_test.py
+++ b/tornado/test/web_test.py
@@ -171,6 +171,13 @@ class SetCookieExpiresDaysHandler(RequestHandler):
             def get(self):
                 self.set_cookie("foo", "bar", expires_days=10)
 
+        class SetCookieFalsyFlags(RequestHandler):
+            def get(self):
+                self.set_cookie("a", "1", secure=True)
+                self.set_cookie("b", "1", secure=False)
+                self.set_cookie("c", "1", httponly=True)
+                self.set_cookie("d", "1", httponly=False)
+
         return [("/set", SetCookieHandler),
                 ("/get", GetCookieHandler),
                 ("/set_domain", SetCookieDomainHandler),
@@ -178,6 +185,7 @@ def get(self):
                 ("/set_overwrite", SetCookieOverwriteHandler),
                 ("/set_max_age", SetCookieMaxAgeHandler),
                 ("/set_expires_days", SetCookieExpiresDaysHandler),
+                ("/set_falsy_flags", SetCookieFalsyFlags)
                 ]
 
     def test_set_cookie(self):
@@ -248,7 +256,15 @@ def test_set_cookie_expires_days(self):
         header_expires = datetime.datetime(
             *email.utils.parsedate(match.groupdict()["expires"])[:6])
         self.assertTrue(abs(timedelta_to_seconds(expires - header_expires)) < 10)
-    
+
+    def test_set_cookie_false_flags(self):
+        response = self.fetch("/set_falsy_flags")
+        headers = sorted(response.headers.get_list("Set-Cookie"))
+        self.assertEqual(headers[0], 'a=1; Path=/; secure')
+        self.assertEqual(headers[1], 'b=1; Path=/')
+        self.assertEqual(headers[2], 'c=1; httponly; Path=/')
+        self.assertEqual(headers[3], 'd=1; Path=/')
+
 
 class AuthRedirectRequestHandler(RequestHandler):
     def initialize(self, login_url):