comparison mercurial/hgweb/server.py @ 4860:f3802f9f1840

Add SSL support to hg serve, activated via --certificate option
author Brendan Cully <brendan@kublai.com>
date Mon, 09 Jul 2007 22:12:28 -0700
parents 63b9d2deed48
children 192cd95c2ba8
comparison
equal deleted inserted replaced
4859:8c5aca855b5d 4860:f3802f9f1840
51 accesslog = self.server.accesslog 51 accesslog = self.server.accesslog
52 accesslog.write("%s - - [%s] %s\n" % (self.client_address[0], 52 accesslog.write("%s - - [%s] %s\n" % (self.client_address[0],
53 self.log_date_time_string(), 53 self.log_date_time_string(),
54 format % args)) 54 format % args))
55 55
56 def do_write(self):
57 try:
58 self.do_hgweb()
59 except socket.error, inst:
60 if inst[0] != errno.EPIPE:
61 raise
62
56 def do_POST(self): 63 def do_POST(self):
57 try: 64 try:
58 try: 65 self.do_write()
59 self.do_hgweb()
60 except socket.error, inst:
61 if inst[0] != errno.EPIPE:
62 raise
63 except StandardError, inst: 66 except StandardError, inst:
64 self._start_response("500 Internal Server Error", []) 67 self._start_response("500 Internal Server Error", [])
65 self._write("Internal Server Error") 68 self._write("Internal Server Error")
66 tb = "".join(traceback.format_exception(*sys.exc_info())) 69 tb = "".join(traceback.format_exception(*sys.exc_info()))
67 self.log_error("Exception happened during processing request '%s':\n%s", 70 self.log_error("Exception happened during processing request '%s':\n%s",
162 raise AssertionError("Content-length header sent, but more bytes than specified are being written.") 165 raise AssertionError("Content-length header sent, but more bytes than specified are being written.")
163 self.length = self.length - len(data) 166 self.length = self.length - len(data)
164 self.wfile.write(data) 167 self.wfile.write(data)
165 self.wfile.flush() 168 self.wfile.flush()
166 169
170 class _shgwebhandler(_hgwebhandler):
171 def setup(self):
172 self.connection = self.request
173 self.rfile = socket._fileobject(self.request, "rb", self.rbufsize)
174 self.wfile = socket._fileobject(self.request, "wb", self.wbufsize)
175
176 def do_write(self):
177 from OpenSSL.SSL import SysCallError
178 try:
179 super(_shgwebhandler, self).do_write()
180 except SysCallError, inst:
181 if inst.args[0] != errno.EPIPE:
182 raise
183
184 def handle_one_request(self):
185 from OpenSSL.SSL import SysCallError, ZeroReturnError
186 try:
187 super(_shgwebhandler, self).handle_one_request()
188 except (SysCallError, ZeroReturnError):
189 self.close_connection = True
190 pass
191
167 def create_server(ui, repo): 192 def create_server(ui, repo):
168 use_threads = True 193 use_threads = True
169 194
170 def openlog(opt, default): 195 def openlog(opt, default):
171 if opt and opt != '-': 196 if opt and opt != '-':
174 199
175 address = ui.config("web", "address", "") 200 address = ui.config("web", "address", "")
176 port = int(ui.config("web", "port", 8000)) 201 port = int(ui.config("web", "port", 8000))
177 use_ipv6 = ui.configbool("web", "ipv6") 202 use_ipv6 = ui.configbool("web", "ipv6")
178 webdir_conf = ui.config("web", "webdir_conf") 203 webdir_conf = ui.config("web", "webdir_conf")
204 ssl_cert = ui.config("web", "certificate")
179 accesslog = openlog(ui.config("web", "accesslog", "-"), sys.stdout) 205 accesslog = openlog(ui.config("web", "accesslog", "-"), sys.stdout)
180 errorlog = openlog(ui.config("web", "errorlog", "-"), sys.stderr) 206 errorlog = openlog(ui.config("web", "errorlog", "-"), sys.stderr)
181 207
182 if use_threads: 208 if use_threads:
183 try: 209 try:
220 if addr in ('', '::'): 246 if addr in ('', '::'):
221 addr = socket.gethostname() 247 addr = socket.gethostname()
222 248
223 self.addr, self.port = addr, port 249 self.addr, self.port = addr, port
224 250
251 if ssl_cert:
252 try:
253 from OpenSSL import SSL
254 ctx = SSL.Context(SSL.SSLv23_METHOD)
255 except ImportError:
256 raise util.Abort("SSL support is unavailable")
257 ctx.use_privatekey_file(ssl_cert)
258 ctx.use_certificate_file(ssl_cert)
259 sock = socket.socket(self.address_family, self.socket_type)
260 self.socket = SSL.Connection(ctx, sock)
261 self.server_bind()
262 self.server_activate()
263
225 class IPv6HTTPServer(MercurialHTTPServer): 264 class IPv6HTTPServer(MercurialHTTPServer):
226 address_family = getattr(socket, 'AF_INET6', None) 265 address_family = getattr(socket, 'AF_INET6', None)
227 266
228 def __init__(self, *args, **kwargs): 267 def __init__(self, *args, **kwargs):
229 if self.address_family is None: 268 if self.address_family is None:
230 raise hg.RepoError(_('IPv6 not available on this system')) 269 raise hg.RepoError(_('IPv6 not available on this system'))
231 super(IPv6HTTPServer, self).__init__(*args, **kwargs) 270 super(IPv6HTTPServer, self).__init__(*args, **kwargs)
232 271
272 if ssl_cert:
273 handler = _shgwebhandler
274 else:
275 handler = _hgwebhandler
276
233 try: 277 try:
234 if use_ipv6: 278 if use_ipv6:
235 return IPv6HTTPServer((address, port), _hgwebhandler) 279 return IPv6HTTPServer((address, port), handler)
236 else: 280 else:
237 return MercurialHTTPServer((address, port), _hgwebhandler) 281 return MercurialHTTPServer((address, port), handler)
238 except socket.error, inst: 282 except socket.error, inst:
239 raise util.Abort(_('cannot start server: %s') % inst.args[1]) 283 raise util.Abort(_('cannot start server: %s') % inst.args[1])