Repository: benoitc/gunicorn

Issue:
AttributeError when running gunicorn over HTTPS
When running gunicorn with my certificate and keyfile over https, sending any request to the server gives me this error. 

```
Traceback (most recent call last):
  File "/opt/bitnami/python/lib/python2.7/site-packages/gunicorn/arbiter.py", line 515, in spawn_worker
    worker.init_process()
  File "/opt/bitnami/python/lib/python2.7/site-packages/gunicorn/workers/base.py", line 126, in init_process
    self.run()
  File "/opt/bitnami/python/lib/python2.7/site-packages/gunicorn/workers/sync.py", line 119, in run
    self.run_for_one(timeout)
  File "/opt/bitnami/python/lib/python2.7/site-packages/gunicorn/workers/sync.py", line 66, in run_for_one
    self.accept(listener)
  File "/opt/bitnami/python/lib/python2.7/site-packages/gunicorn/workers/sync.py", line 30, in accept
    self.handle(listener, client, addr)
  File "/opt/bitnami/python/lib/python2.7/site-packages/gunicorn/workers/sync.py", line 141, in handle
    self.handle_error(req, client, addr, e)
  File "/opt/bitnami/python/lib/python2.7/site-packages/gunicorn/workers/base.py", line 213, in handle_error
    self.log.exception("Error handling request %s", req.uri)
AttributeError: 'NoneType' object has no attribute 'uri'
```



Discussion:
Hi, thanks for the ticket! I don't reproduce it right now, can you give more detail?

how can I reproduce it? Which versions of Gunicorn are you running?

I'm using 19.4.3. I ran it using the `--certfile` arguments and the `--keyfile` arguments, and provided an SSL Certificate and keyfile. However, whenever attempting to send a request, I simply get the above error.

This traceback appears when a client attempts to connect via HTTP (not HTTPS).

Either we can catch the `SSLError` in the base class `handle_error`:

```
diff --git a/gunicorn/workers/base.py b/gunicorn/workers/base.py
index 9ba8e75..488c2ea 100644
--- a/gunicorn/workers/base.py
+++ b/gunicorn/workers/base.py
@@ -10,6 +10,7 @@ import sys
 import time
 import traceback
 from random import randint
+from ssl import SSLError


 from gunicorn import util
@@ -181,7 +182,8 @@ class Worker(object):
         if isinstance(exc, (InvalidRequestLine, InvalidRequestMethod,
                 InvalidHTTPVersion, InvalidHeader, InvalidHeaderName,
                 LimitRequestLine, LimitRequestHeaders,
-                InvalidProxyLine, ForbiddenProxyRequest)):
+                InvalidProxyLine, ForbiddenProxyRequest,
+                SSLError)):

             status_int = 400
             reason = "Bad Request"
```

Or we should maybe move/change the line that depends on `req.uri` for logging the exception in the corresponding `else` clause.

This is a little verbose, even for debug, maybe:

```
[2016-01-07 13:25:43 +0000] [16469] [DEBUG] Error processing SSL request.
[2016-01-07 13:25:43 +0000] [16469] [DEBUG] Invalid request from ip=127.0.0.1: [SSL: WRONG_VERSION_NUMBER] wrong version number (_ssl.c:1752)
[2016-01-07 13:25:43 +0000] [16469] [DEBUG] Failed to send error message.
```

@tilgovi the second solution is more explicit, isn't it?

@benoitc which? You mean moving the `req.uri` logging? I think I like that better.

We shouldn't be trying to use `req.uri` before we know it's valid, probably. That is easier to reason about.

Excellent, just hit this :)
gunicorn (version 19.4.5)
Python 2.6.6

I'm using the patch at the moment because it at least stops the tracebacks in my output :+1: 

@tilgovi agree. go ahead :) 

For posterity, I also encountered this with gunicorn 19.4.5 running Python 2.7.3.

I'm confused. 
Gunicorn crashes immediately when failing to handle any non-https request after I provide a certfile and keyfile. Just make a GET request from any recent version of Chrome.
This means: Anybody, malicious or not,  can bring down a gunicorn server by trying to use http requests.

This behavior is not being considered a bug or even a priority?

gunicorn (version 19.4.5)
Python 3.5.1

@edwardotis well patches are always welcome. people have been busy. anyway it will be fixed in this week release.

@benoitc Thanks, good to hear. Just surprised that the bug label was avoided during this discussion, when this presents a great security risk to the server.


Resolving patch:
diff --git a/gunicorn/workers/base.py b/gunicorn/workers/base.py
--- a/gunicorn/workers/base.py
+++ b/gunicorn/workers/base.py
@@ -5,12 +5,12 @@
 
 from datetime import datetime
 import os
+from random import randint
 import signal
+from ssl import SSLError
 import sys
 import time
 import traceback
-from random import randint
-
 
 from gunicorn import util
 from gunicorn.workers.workertmp import WorkerTmp
@@ -181,7 +181,8 @@ def handle_error(self, req, client, addr, exc):
         if isinstance(exc, (InvalidRequestLine, InvalidRequestMethod,
                 InvalidHTTPVersion, InvalidHeader, InvalidHeaderName,
                 LimitRequestLine, LimitRequestHeaders,
-                InvalidProxyLine, ForbiddenProxyRequest)):
+                InvalidProxyLine, ForbiddenProxyRequest,
+                SSLError)):
 
             status_int = 400
             reason = "Bad Request"
@@ -206,12 +207,16 @@ def handle_error(self, req, client, addr, exc):
                 reason = "Forbidden"
                 mesg = "Request forbidden"
                 status_int = 403
+            elif isinstance(exc, SSLError):
+                reason = "Forbidden"
+                mesg = "'%s'" % str(exc)
+                status_int = 403
 
             msg = "Invalid request from ip={ip}: {error}"
             self.log.debug(msg.format(ip=addr[0], error=str(exc)))
         else:
-            self.log.exception("Error handling request %s", req.uri)
-
+            if hasattr(req, "uri"):
+                self.log.exception("Error handling request %s", req.uri)
             status_int = 500
             reason = "Internal Server Error"
             mesg = ""


Test patch: