Repository: benoitc/gunicorn

Issue:
does not work HTTPS on gunicorn 19.3.0 and tornado 4.3
I'm not sure this is a problem on gunicorn, but please ask a question here.
I'm working on an implementation simple tornado app and it works on gunicorn, also it listen HTTPS. It worked great with gunicorn 18.0 + tornado 3.0.
However, after doing I upgrade gunicorn to 19.3.0 for using `ca-certs` option (also upgrade tornado to 4..2.1), it does not work fine.

If someone point out the cause of this is which one gunicorn or tornado, it would be great help to me.

here is a starting command line.

```
$ gunicorn --certfile=/home/hiro/201510/server.crt --keyfile=/home/hiro/201510/server.key -b 0.0.0.0:16189 -w 1 -k "tornado" 'httpproxy:get_service("tcp://0.0.0.0:5555")'
```

and stacktrace is following:

```
[2015-10-27 20:29:04 +0000] [4360] [INFO] Booting worker with pid: 4360
ERROR:tornado.application:Exception in callback (<gunicorn.sock.TCPSocket object at 0x2a6bc50>, <function null_wrapper at 0x2c91488>)
Traceback (most recent call last):
  File "/home/hiro/.virtualenvs/CP275_asyncproxy/lib/python2.7/site-packages/tornado/ioloop.py", line 866, in start
    handler_func(fd_obj, events)
  File "/home/hiro/.virtualenvs/CP275_asyncproxy/lib/python2.7/site-packages/tornado/stack_context.py", line 275, in null_wrapper
    return fn(*args, **kwargs)
  File "/home/hiro/.virtualenvs/CP275_asyncproxy/lib/python2.7/site-packages/tornado/netutil.py", line 265, in accept_handler
    callback(connection, address)
  File "/home/hiro/.virtualenvs/CP275_asyncproxy/lib/python2.7/site-packages/tornado/tcpserver.py", line 239, in _handle_connection
    do_handshake_on_connect=False)
  File "/home/hiro/.virtualenvs/CP275_asyncproxy/lib/python2.7/site-packages/tornado/netutil.py", line 501, in ssl_wrap_socket
    context = ssl_options_to_context(ssl_options)
  File "/home/hiro/.virtualenvs/CP275_asyncproxy/lib/python2.7/site-packages/tornado/netutil.py", line 471, in ssl_options_to_context
    assert all(k in _SSL_CONTEXT_KEYWORDS for k in ssl_options), ssl_options
AssertionError: {'do_handshake_on_connect': False, 'certfile': '/home/hiro/201510/server.crt', 'suppress_ragged_eofs': True, 'ciphers': 'TLSv1', 'ssl_version': 3, 'cert_reqs': 0, 'ca_certs': None, 'keyfile': '/home/hiro/201510/server.key'}
```

`_SSL_CONTEXT_KEYWORDS` declared in `netutil.py` is following:

```
_SSL_CONTEXT_KEYWORDS = frozenset(['ssl_version', 'certfile', 'keyfile',
                                   'cert_reqs', 'ca_certs', 'ciphers'])
```



Discussion:
seems like tornado is now limiting the number of settings. `suppress_ragged_eofs` is added by gunicorn here but not accepted. A patch is coming :)


Resolving patch:
diff --git a/gunicorn/workers/gtornado.py b/gunicorn/workers/gtornado.py
--- a/gunicorn/workers/gtornado.py
+++ b/gunicorn/workers/gtornado.py
@@ -3,6 +3,7 @@
 # This file is part of gunicorn released under the MIT license.
 # See the NOTICE for more information.
 
+import copy
 import os
 import sys
 
@@ -102,8 +103,13 @@ def on_close(instance, server_conn):
             server_class = _HTTPServer
 
         if self.cfg.is_ssl:
+             _ssl_opt = copy.deepcopy(self.cfg.ssl_options)
+             # tornado refuses initialization if ssl_options contains following
+             # options
+             del _ssl_opt["do_handshake_on_connect"]
+             del _ssl_opt["suppress_ragged_eofs"]
             server = server_class(app, io_loop=self.ioloop,
-                    ssl_options=self.cfg.ssl_options)
+                    ssl_options=_ssl_opt)
         else:
             server = server_class(app, io_loop=self.ioloop)
 


Test patch: