Repository: sanic-org/sanic

Issue:
keepalive message recorded in debug mode
ENVIRONMENT
==========
centos7_x64
chrome: 58.0.3029.110
sanic: 0.5.4
python: 3.5.3 or 3.6.1

CODE
====
```
from sanic import Sanic
from sanic.response import text,json

app = Sanic(__name__)

@app.route('/')
async def test(request):
    return text('hello world')

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8080, debug=True)
```

ISSUE DESCRIPTION
=============
when issue http request from chrome the first time,  Sanic RequestTimeout exception thrown out in 60s after we get the correct response from the server, I am not sure this is the expected behavior or not, I didn't see any reference about this error in the document:

```
2017-05-31 02:50:34 - (sanic)[INFO]: Goin' Fast @ http://0.0.0.0:8080
2017-05-31 02:50:34 - (sanic)[INFO]: Starting worker [3277]
I print when a request is received by the server
2017-05-31 02:50:38 - (network)[INFO][192.168.31.164:53576]: GET http://192.168.31.227:8080/number/123  200 13

2017-05-31 02:51:38 - (sanic)[ERROR]: NoneType: None
2017-05-31 02:51:38 - (network)[INFO][]: NoneNone  408 22
```

COMMENT
========
After capturing the tcpdump packets, a HTTP 408 message send by Sanic to browser when it reaches SANIC_REQUEST_TIMEOUT(60s), also it can be seen in the codes event_loop will call connection_timeout() after waiting for that time,  so I assume this is the expected behavior of Sanic.  please help confirm it is true or not?  If it is true, then it had better mention this symptom in the document and perhaps add a switch to turn off send back this exception to client.



Discussion:

Resolving patch:
diff --git a/sanic/server.py b/sanic/server.py
--- a/sanic/server.py
+++ b/sanic/server.py
@@ -139,8 +139,10 @@ def connection_timeout(self):
                 self._request_stream_task.cancel()
             if self._request_handler_task:
                 self._request_handler_task.cancel()
-            exception = RequestTimeout('Request Timeout')
-            self.write_error(exception)
+            try:
+                raise RequestTimeout('Request Timeout')
+            except RequestTimeout as exception:
+                self.write_error(exception)
 
     # -------------------------------------------- #
     # Parsing
@@ -317,6 +319,7 @@ async def stream_response(self, response):
                 self.cleanup()
 
     def write_error(self, exception):
+        response = None
         try:
             response = self.error_handler.response(self.request, exception)
             version = self.request.version if self.request else '1.1'
@@ -331,20 +334,23 @@ def write_error(self, exception):
                 from_error=True)
         finally:
             if self.has_log:
-                extra = {
-                    'status': response.status,
-                    'host': '',
-                    'request': str(self.request) + str(self.url)
-                }
-                if response and isinstance(response, HTTPResponse):
+                extra = dict()
+                if isinstance(response, HTTPResponse):
+                    extra['status'] = response.status
                     extra['byte'] = len(response.body)
                 else:
+                    extra['status'] = 0
                     extra['byte'] = -1
                 if self.request:
                     extra['host'] = '%s:%d' % self.request.ip,
                     extra['request'] = '%s %s' % (self.request.method,
                                                   self.url)
-                netlog.info('', extra=extra)
+                else:
+                    extra['host'] = 'UNKNOWN'
+                    extra['request'] = 'nil'
+                if self.parser and not (self.keep_alive
+                                        and extra['status'] == 408):
+                    netlog.info('', extra=extra)
             self.transport.close()
 
     def bail_out(self, message, from_error=False):


Test patch: