diff mercurial/hgweb/server.py @ 2506:d0db3462d568

This patch make several WSGI related alterations. First, it changes the server to be almost a generic WSGI server. Second, it changes request.py to have wsgiapplication and _wsgirequest. wsgiapplication is a class that creates _wsgirequests when called by a WSGI compliant server. It needs to know whether or not it should create hgwebdir or hgweb requests. Lastly, wsgicgi.py is added, and the CGI scripts are altered to use it to launch wsgiapplications in a WSGI compliant way. As a side effect, all the keepalive code has been removed from request.py. This code needs to be moved so that it is exclusively in server.py
author Eric Hopper <hopper@omnifarious.org>
date Tue, 27 Jun 2006 00:09:33 -0700
parents 01b856927970
children 7e01da2bc7f3
line wrap: on
line diff
--- a/mercurial/hgweb/server.py
+++ b/mercurial/hgweb/server.py
@@ -10,7 +10,7 @@ from mercurial.demandload import demandl
 import os, sys, errno
 demandload(globals(), "urllib BaseHTTPServer socket SocketServer")
 demandload(globals(), "mercurial:ui,hg,util,templater")
-demandload(globals(), "hgweb_mod:hgweb hgwebdir_mod:hgwebdir request:hgrequest")
+demandload(globals(), "hgweb_mod:hgweb hgwebdir_mod:hgwebdir request:wsgiapplication")
 from mercurial.i18n import gettext as _
 
 def _splitURI(uri):
@@ -25,6 +25,17 @@ def _splitURI(uri):
         path, query = uri, ''
     return urllib.unquote(path), query
 
+class _error_logger(object):
+    def __init__(self, handler):
+        self.handler = handler
+    def flush(self):
+        pass
+    def write(str):
+        self.writelines(str.split('\n'))
+    def writelines(seq):
+        for msg in seq:
+            self.handler.log_error("HG error:  %s", msg)
+
 class _hgwebhandler(object, BaseHTTPServer.BaseHTTPRequestHandler):
     def __init__(self, *args, **kargs):
         self.protocol_version = 'HTTP/1.1'
@@ -84,10 +95,50 @@ class _hgwebhandler(object, BaseHTTPServ
             if hval:
                 env[hkey] = hval
         env['SERVER_PROTOCOL'] = self.request_version
+        env['wsgi.version'] = (1, 0)
+        env['wsgi.url_scheme'] = 'http'
+        env['wsgi.input'] = self.rfile
+        env['wsgi.errors'] = _error_logger(self)
+        env['wsgi.multithread'] = isinstance(self.server,
+                                             SocketServer.ThreadingMixIn)
+        env['wsgi.multiprocess'] = isinstance(self.server,
+                                              SocketServer.ForkingMixIn)
+        env['wsgi.run_once'] = 0
 
-        req = hgrequest(self.rfile, self.wfile, env)
-        self.send_response(200, "Script output follows")
-        self.close_connection = self.server.make_and_run_handler(req)
+        self.close_connection = True
+        self.saved_status = None
+        self.saved_headers = []
+        self.sent_headers = False
+        req = self.server.reqmaker(env, self._start_response)
+        for data in req:
+            if data:
+                self._write(data)
+
+    def send_headers(self):
+        if not self.saved_status:
+            raise AssertionError("Sending headers before start_response() called")
+        saved_status = self.saved_status.split(None, 1)
+        saved_status[0] = int(saved_status[0])
+        self.send_response(*saved_status)
+        for h in self.saved_headers:
+            self.send_header(*h)
+        self.end_headers()
+        self.sent_headers = True
+
+    def _start_response(self, http_status, headers, exc_info=None):
+        code, msg = http_status.split(None, 1)
+        code = int(code)
+        self.saved_status = http_status
+        self.saved_headers = headers
+        return self._write
+
+    def _write(self, data):
+        if not self.saved_status:
+            raise AssertionError("data written before start_response() called")
+        elif not self.sent_headers:
+            self.send_headers()
+        self.wfile.write(data)
+        self.wfile.flush()
 
 def create_server(ui, repo):
     use_threads = True
@@ -127,8 +178,9 @@ def create_server(ui, repo):
             self.webdir_conf = webdir_conf
             self.webdirmaker = hgwebdir
             self.repoviewmaker = hgweb
+            self.reqmaker = wsgiapplication(self.make_handler)
 
-        def make_and_run_handler(self, req):
+        def make_handler(self):
             if self.webdir_conf:
                 hgwebobj = self.webdirmaker(self.webdir_conf)
             elif self.repo is not None:
@@ -136,8 +188,7 @@ def create_server(ui, repo):
                                                              repo.origroot))
             else:
                 raise hg.RepoError(_('no repo found'))
-            hgwebobj.run(req)
-            return req.will_close
+            return hgwebobj
 
     class IPv6HTTPServer(MercurialHTTPServer):
         address_family = getattr(socket, 'AF_INET6', None)