# HG changeset patch # User Brendan Cully # Date 2007-07-10 05:12:28 # Node ID f3802f9f184056f2b051547b3819b67dd3fa7598 # Parent 8c5aca855b5d420a2708289112d83cd28a454941 Add SSL support to hg serve, activated via --certificate option diff --git a/mercurial/commands.py b/mercurial/commands.py --- a/mercurial/commands.py +++ b/mercurial/commands.py @@ -2464,7 +2464,7 @@ def serve(ui, repo, **opts): parentui = ui.parentui or ui optlist = ("name templates style address port ipv6" - " accesslog errorlog webdir_conf") + " accesslog errorlog webdir_conf certificate") for o in optlist.split(): if opts[o]: parentui.setconfig("web", o, str(opts[o])) @@ -3072,7 +3072,8 @@ table = { ('', 'stdio', None, _('for remote clients')), ('t', 'templates', '', _('web templates to use')), ('', 'style', '', _('template style to use')), - ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))], + ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')), + ('', 'certificate', '', _('SSL certificate file'))], _('hg serve [OPTION]...')), "^status|st": (status, diff --git a/mercurial/hgweb/server.py b/mercurial/hgweb/server.py --- a/mercurial/hgweb/server.py +++ b/mercurial/hgweb/server.py @@ -53,13 +53,16 @@ class _hgwebhandler(object, BaseHTTPServ self.log_date_time_string(), format % args)) + def do_write(self): + try: + self.do_hgweb() + except socket.error, inst: + if inst[0] != errno.EPIPE: + raise + def do_POST(self): try: - try: - self.do_hgweb() - except socket.error, inst: - if inst[0] != errno.EPIPE: - raise + self.do_write() except StandardError, inst: self._start_response("500 Internal Server Error", []) self._write("Internal Server Error") @@ -164,6 +167,28 @@ class _hgwebhandler(object, BaseHTTPServ self.wfile.write(data) self.wfile.flush() +class _shgwebhandler(_hgwebhandler): + def setup(self): + self.connection = self.request + self.rfile = socket._fileobject(self.request, "rb", self.rbufsize) + self.wfile = socket._fileobject(self.request, "wb", self.wbufsize) + + def do_write(self): + from OpenSSL.SSL import SysCallError + try: + super(_shgwebhandler, self).do_write() + except SysCallError, inst: + if inst.args[0] != errno.EPIPE: + raise + + def handle_one_request(self): + from OpenSSL.SSL import SysCallError, ZeroReturnError + try: + super(_shgwebhandler, self).handle_one_request() + except (SysCallError, ZeroReturnError): + self.close_connection = True + pass + def create_server(ui, repo): use_threads = True @@ -176,6 +201,7 @@ def create_server(ui, repo): port = int(ui.config("web", "port", 8000)) use_ipv6 = ui.configbool("web", "ipv6") webdir_conf = ui.config("web", "webdir_conf") + ssl_cert = ui.config("web", "certificate") accesslog = openlog(ui.config("web", "accesslog", "-"), sys.stdout) errorlog = openlog(ui.config("web", "errorlog", "-"), sys.stderr) @@ -222,6 +248,19 @@ def create_server(ui, repo): self.addr, self.port = addr, port + if ssl_cert: + try: + from OpenSSL import SSL + ctx = SSL.Context(SSL.SSLv23_METHOD) + except ImportError: + raise util.Abort("SSL support is unavailable") + ctx.use_privatekey_file(ssl_cert) + ctx.use_certificate_file(ssl_cert) + sock = socket.socket(self.address_family, self.socket_type) + self.socket = SSL.Connection(ctx, sock) + self.server_bind() + self.server_activate() + class IPv6HTTPServer(MercurialHTTPServer): address_family = getattr(socket, 'AF_INET6', None) @@ -230,10 +269,15 @@ def create_server(ui, repo): raise hg.RepoError(_('IPv6 not available on this system')) super(IPv6HTTPServer, self).__init__(*args, **kwargs) + if ssl_cert: + handler = _shgwebhandler + else: + handler = _hgwebhandler + try: if use_ipv6: - return IPv6HTTPServer((address, port), _hgwebhandler) + return IPv6HTTPServer((address, port), handler) else: - return MercurialHTTPServer((address, port), _hgwebhandler) + return MercurialHTTPServer((address, port), handler) except socket.error, inst: raise util.Abort(_('cannot start server: %s') % inst.args[1])