comparison 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
comparison
equal deleted inserted replaced
2505:01b856927970 2506:d0db3462d568
8 8
9 from mercurial.demandload import demandload 9 from mercurial.demandload import demandload
10 import os, sys, errno 10 import os, sys, errno
11 demandload(globals(), "urllib BaseHTTPServer socket SocketServer") 11 demandload(globals(), "urllib BaseHTTPServer socket SocketServer")
12 demandload(globals(), "mercurial:ui,hg,util,templater") 12 demandload(globals(), "mercurial:ui,hg,util,templater")
13 demandload(globals(), "hgweb_mod:hgweb hgwebdir_mod:hgwebdir request:hgrequest") 13 demandload(globals(), "hgweb_mod:hgweb hgwebdir_mod:hgwebdir request:wsgiapplication")
14 from mercurial.i18n import gettext as _ 14 from mercurial.i18n import gettext as _
15 15
16 def _splitURI(uri): 16 def _splitURI(uri):
17 """ Return path and query splited from uri 17 """ Return path and query splited from uri
18 18
22 if '?' in uri: 22 if '?' in uri:
23 path, query = uri.split('?', 1) 23 path, query = uri.split('?', 1)
24 else: 24 else:
25 path, query = uri, '' 25 path, query = uri, ''
26 return urllib.unquote(path), query 26 return urllib.unquote(path), query
27
28 class _error_logger(object):
29 def __init__(self, handler):
30 self.handler = handler
31 def flush(self):
32 pass
33 def write(str):
34 self.writelines(str.split('\n'))
35 def writelines(seq):
36 for msg in seq:
37 self.handler.log_error("HG error: %s", msg)
27 38
28 class _hgwebhandler(object, BaseHTTPServer.BaseHTTPRequestHandler): 39 class _hgwebhandler(object, BaseHTTPServer.BaseHTTPRequestHandler):
29 def __init__(self, *args, **kargs): 40 def __init__(self, *args, **kargs):
30 self.protocol_version = 'HTTP/1.1' 41 self.protocol_version = 'HTTP/1.1'
31 BaseHTTPServer.BaseHTTPRequestHandler.__init__(self, *args, **kargs) 42 BaseHTTPServer.BaseHTTPRequestHandler.__init__(self, *args, **kargs)
82 hval = self.headers.getheader(header) 93 hval = self.headers.getheader(header)
83 hval = hval.replace('\n', '').strip() 94 hval = hval.replace('\n', '').strip()
84 if hval: 95 if hval:
85 env[hkey] = hval 96 env[hkey] = hval
86 env['SERVER_PROTOCOL'] = self.request_version 97 env['SERVER_PROTOCOL'] = self.request_version
87 98 env['wsgi.version'] = (1, 0)
88 req = hgrequest(self.rfile, self.wfile, env) 99 env['wsgi.url_scheme'] = 'http'
89 self.send_response(200, "Script output follows") 100 env['wsgi.input'] = self.rfile
90 self.close_connection = self.server.make_and_run_handler(req) 101 env['wsgi.errors'] = _error_logger(self)
102 env['wsgi.multithread'] = isinstance(self.server,
103 SocketServer.ThreadingMixIn)
104 env['wsgi.multiprocess'] = isinstance(self.server,
105 SocketServer.ForkingMixIn)
106 env['wsgi.run_once'] = 0
107
108 self.close_connection = True
109 self.saved_status = None
110 self.saved_headers = []
111 self.sent_headers = False
112 req = self.server.reqmaker(env, self._start_response)
113 for data in req:
114 if data:
115 self._write(data)
116
117 def send_headers(self):
118 if not self.saved_status:
119 raise AssertionError("Sending headers before start_response() called")
120 saved_status = self.saved_status.split(None, 1)
121 saved_status[0] = int(saved_status[0])
122 self.send_response(*saved_status)
123 for h in self.saved_headers:
124 self.send_header(*h)
125 self.end_headers()
126 self.sent_headers = True
127
128 def _start_response(self, http_status, headers, exc_info=None):
129 code, msg = http_status.split(None, 1)
130 code = int(code)
131 self.saved_status = http_status
132 self.saved_headers = headers
133 return self._write
134
135 def _write(self, data):
136 if not self.saved_status:
137 raise AssertionError("data written before start_response() called")
138 elif not self.sent_headers:
139 self.send_headers()
140 self.wfile.write(data)
141 self.wfile.flush()
91 142
92 def create_server(ui, repo): 143 def create_server(ui, repo):
93 use_threads = True 144 use_threads = True
94 145
95 def openlog(opt, default): 146 def openlog(opt, default):
125 self.errorlog = errorlog 176 self.errorlog = errorlog
126 self.repo = repo 177 self.repo = repo
127 self.webdir_conf = webdir_conf 178 self.webdir_conf = webdir_conf
128 self.webdirmaker = hgwebdir 179 self.webdirmaker = hgwebdir
129 self.repoviewmaker = hgweb 180 self.repoviewmaker = hgweb
130 181 self.reqmaker = wsgiapplication(self.make_handler)
131 def make_and_run_handler(self, req): 182
183 def make_handler(self):
132 if self.webdir_conf: 184 if self.webdir_conf:
133 hgwebobj = self.webdirmaker(self.webdir_conf) 185 hgwebobj = self.webdirmaker(self.webdir_conf)
134 elif self.repo is not None: 186 elif self.repo is not None:
135 hgwebobj = self.repoviewmaker(repo.__class__(repo.ui, 187 hgwebobj = self.repoviewmaker(repo.__class__(repo.ui,
136 repo.origroot)) 188 repo.origroot))
137 else: 189 else:
138 raise hg.RepoError(_('no repo found')) 190 raise hg.RepoError(_('no repo found'))
139 hgwebobj.run(req) 191 return hgwebobj
140 return req.will_close
141 192
142 class IPv6HTTPServer(MercurialHTTPServer): 193 class IPv6HTTPServer(MercurialHTTPServer):
143 address_family = getattr(socket, 'AF_INET6', None) 194 address_family = getattr(socket, 'AF_INET6', None)
144 195
145 def __init__(self, *args, **kwargs): 196 def __init__(self, *args, **kwargs):