diff mercurial/hgweb/webcommands.py @ 36876:97f44b0720e2

hgweb: port archive command to modern response API Well, I tried to go with PEP 3333's recommendations and only allow our WSGI application to emit data via a response generator. Unfortunately, the "archive" command calls into the zipfile and tarfile modules and these operator on file objects and must send their data to an object with write(). There's no easy way turn these write() calls into a generator. So, we teach our response type how to expose a file object like object that can be used to write() output. We try to keep the API consistent with how things work currently: callers must call a setbody*(), then sendresponse() to trigger sending of headers, and only then can they get a handle on the object to perform writing. This required overloading the return value of @webcommand functions even more. Fortunately, we're almost completely ported off the legacy API. So we should be able to simplify matters in the near future. A test relying on this functionality has also been updated to use the new API. Differential Revision: https://phab.mercurial-scm.org/D2792
author Gregory Szorc <gregory.szorc@gmail.com>
date Sat, 10 Mar 2018 20:16:20 -0800
parents 16499427f6de
children 67fb0dca29bc
line wrap: on
line diff
--- a/mercurial/hgweb/webcommands.py	Sat Mar 10 16:17:51 2018 -0800
+++ b/mercurial/hgweb/webcommands.py	Sat Mar 10 20:16:20 2018 -0800
@@ -19,14 +19,10 @@
     ErrorResponse,
     HTTP_FORBIDDEN,
     HTTP_NOT_FOUND,
-    HTTP_OK,
     get_contact,
     paritygen,
     staticfile,
 )
-from . import (
-    request as requestmod,
-)
 
 from .. import (
     archival,
@@ -64,7 +60,9 @@
     The function can return the ``requestcontext.res`` instance to signal
     that it wants to use this object to generate the response. If an iterable
     is returned, the ``wsgirequest`` instance will be used and the returned
-    content will constitute the response body.
+    content will constitute the response body. ``True`` can be returned to
+    indicate that the function already sent output and the caller doesn't
+    need to do anything more to send the response.
 
     Usage:
 
@@ -1210,21 +1208,24 @@
                     'file(s) not found: %s' % file)
 
     mimetype, artype, extension, encoding = web.archivespecs[type_]
-    headers = [
-        ('Content-Disposition', 'attachment; filename=%s%s' % (name, extension))
-        ]
+
+    web.res.headers['Content-Type'] = mimetype
+    web.res.headers['Content-Disposition'] = 'attachment; filename=%s%s' % (
+        name, extension)
+
     if encoding:
-        headers.append(('Content-Encoding', encoding))
-    req.headers.extend(headers)
-    req.respond(HTTP_OK, mimetype)
+        web.res.headers['Content-Encoding'] = encoding
 
-    bodyfh = requestmod.offsettrackingwriter(req.write)
+    web.res.setbodywillwrite()
+    assert list(web.res.sendresponse()) == []
+
+    bodyfh = web.res.getbodyfile()
 
     archival.archive(web.repo, bodyfh, cnode, artype, prefix=name,
                      matchfn=match,
                      subrepos=web.configbool("web", "archivesubrepos"))
-    return []
 
+    return True
 
 @webcommand('static')
 def static(web, req, tmpl):