[PATCH 1 of 1 v2] serve: use chunked encoding in hgweb responses

Mads Kiilerich mads at kiilerich.com
Sun Jan 13 17:03:39 CST 2013


# HG changeset patch
# User Mads Kiilerich <mads at kiilerich.com>
# Date 1358116000 -3600
# Node ID cc9c9033808f0fef919a2326be26e0db9592c469
# Parent  b37663b0f4a8106f60c13d229a34b442cf81ba11
serve: use chunked encoding in hgweb responses

'hg serve' used to close connections when sending a response with unknown
length ... such as a bundle or archive.

Now chunked encoding will be used for responses with unknown length, and the
connection do thus not have to be closed to indicate the end of the response.

Chunked encoding is only used if the length is unknown, if the connection
wouldn't be closed for other reasons, AND if it is a HTTP 1.1 request.

This will not benefit other users of hgweb ... but it can serve as an example
that it can be done.

diff --git a/mercurial/hgweb/server.py b/mercurial/hgweb/server.py
--- a/mercurial/hgweb/server.py
+++ b/mercurial/hgweb/server.py
@@ -133,10 +133,12 @@ class _httprequesthandler(BaseHTTPServer
         self.saved_headers = []
         self.sent_headers = False
         self.length = None
+        self._chunked = None
         for chunk in self.server.application(env, self._start_response):
             self._write(chunk)
         if not self.sent_headers:
             self.send_headers()
+        self._done()
 
     def send_headers(self):
         if not self.saved_status:
@@ -145,16 +147,19 @@ class _httprequesthandler(BaseHTTPServer
         saved_status = self.saved_status.split(None, 1)
         saved_status[0] = int(saved_status[0])
         self.send_response(*saved_status)
-        should_close = True
+        self.length = None
+        self._chunked = False
         for h in self.saved_headers:
             self.send_header(*h)
             if h[0].lower() == 'content-length':
-                should_close = False
                 self.length = int(h[1])
-        # The value of the Connection header is a list of case-insensitive
-        # tokens separated by commas and optional whitespace.
-        if should_close:
-            self.send_header('Connection', 'close')
+        if self.length is None:
+            self._chunked = (not self.close_connection and
+                             self.request_version == "HTTP/1.1")
+            if self._chunked:
+                self.send_header('Transfer-Encoding', 'chunked')
+            else:
+                self.send_header('Connection', 'close')
         self.end_headers()
         self.sent_headers = True
 
@@ -177,9 +182,16 @@ class _httprequesthandler(BaseHTTPServer
                 raise AssertionError("Content-length header sent, but more "
                                      "bytes than specified are being written.")
             self.length = self.length - len(data)
+        elif self._chunked and data:
+            data = '%x\r\n%s\r\n' % (len(data), data)
         self.wfile.write(data)
         self.wfile.flush()
 
+    def _done(self):
+        if self._chunked:
+            self.wfile.write('0\r\n\r\n')
+            self.wfile.flush()
+
 class _httprequesthandleropenssl(_httprequesthandler):
     """HTTPS handler based on pyOpenSSL"""
 
diff --git a/tests/test-https.t b/tests/test-https.t
--- a/tests/test-https.t
+++ b/tests/test-https.t
@@ -124,7 +124,6 @@ clone via pull
   adding manifests
   adding file changes
   added 1 changesets with 4 changes to 4 files
-  warning: localhost certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting)
   updating to branch default
   4 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg verify -R copy-pull
@@ -152,7 +151,6 @@ pull without cacert
   adding manifests
   adding file changes
   added 1 changesets with 1 changes to 1 files
-  warning: localhost certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting)
   changegroup hook: HG_NODE=5fed3813f7f5e1824344fdc9cf8f63bb662c292d HG_SOURCE=pull HG_URL=https://localhost:$HGPORT/
   (run 'hg update' to get a working copy)
   $ cd ..


More information about the Mercurial-devel mailing list