changeset 7908:19418a4c6c61

wsgi: make WSGI wrapper follow the size of the result and log when it finished The WSGI protocol allow for explicit writes and for iterator yields, and we thus have to do accounting in both places.
author Mads Kiilerich <mads@kiilerich.com>
date Wed, 09 Oct 2019 12:23:21 +0200
parents b42ee1bdf082
children 08b6aa79f213
files kallithea/lib/middleware/wrapper.py
diffstat 1 files changed, 20 insertions(+), 4 deletions(-) [+]
line wrap: on
line diff
--- a/kallithea/lib/middleware/wrapper.py	Sat Oct 19 23:07:12 2019 +0200
+++ b/kallithea/lib/middleware/wrapper.py	Wed Oct 09 12:23:21 2019 +0200
@@ -38,12 +38,27 @@
 
 class Meter:
 
-    def __init__(self):
+    def __init__(self, start_response):
+        self._start_response = start_response
         self._start = time.time()
+        self._size = 0
 
     def duration(self):
         return time.time() - self._start
 
+    def start_response(self, status, response_headers, exc_info=None):
+        write = self._start_response(status, response_headers, exc_info)
+        def metered_write(s):
+            self.measure(s)
+            write(s)
+        return metered_write
+
+    def measure(self, chunk):
+        self._size += len(chunk)
+
+    def size(self):
+        return self._size
+
 
 class ResultIter:
 
@@ -58,11 +73,12 @@
 
     def next(self):
         chunk = self._next()
+        self._meter.measure(chunk)
         return chunk
 
     def close(self):
         self._result_close()
-        log.info("%s responded after %.3fs", self._description, self._meter.duration())
+        log.info("%s responded after %.3fs with %s bytes", self._description, self._meter.duration(), self._meter.size())
 
 
 class RequestWrapper(object):
@@ -72,13 +88,13 @@
         self.config = config
 
     def __call__(self, environ, start_response):
-        meter = Meter()
+        meter = Meter(start_response)
         description = "Request from %s for %s" % (
             _get_ip_addr(environ),
             safe_unicode(_get_access_path(environ)),
         )
         try:
-            result = self.application(environ, start_response)
+            result = self.application(environ, meter.start_response)
         finally:
             log.info("%s responding after %.3fs", description, meter.duration())
         return ResultIter(result, meter, description)