mercurial/hgweb/server.py
branchstable
changeset 37788 ed5448edcbfa
parent 37156 7de7bd407251
parent 37591 b5ca5d34fe8d
child 38297 9f499d28efb4
equal deleted inserted replaced
37287:fb92df8b634c 37788:ed5448edcbfa
    11 import errno
    11 import errno
    12 import os
    12 import os
    13 import socket
    13 import socket
    14 import sys
    14 import sys
    15 import traceback
    15 import traceback
       
    16 import wsgiref.validate
    16 
    17 
    17 from ..i18n import _
    18 from ..i18n import _
    18 
    19 
    19 from .. import (
    20 from .. import (
    20     encoding,
    21     encoding,
   109             # r-string. This is the easy way out.
   110             # r-string. This is the easy way out.
   110             newline = chr(10)
   111             newline = chr(10)
   111             self.log_error(r"Exception happened during processing "
   112             self.log_error(r"Exception happened during processing "
   112                            r"request '%s':%s%s", self.path, newline, tb)
   113                            r"request '%s':%s%s", self.path, newline, tb)
   113 
   114 
       
   115     def do_PUT(self):
       
   116         self.do_POST()
       
   117 
   114     def do_GET(self):
   118     def do_GET(self):
   115         self.do_POST()
   119         self.do_POST()
   116 
   120 
   117     def do_hgweb(self):
   121     def do_hgweb(self):
   118         self.sent_headers = False
   122         self.sent_headers = False
   130         env[r'GATEWAY_INTERFACE'] = r'CGI/1.1'
   134         env[r'GATEWAY_INTERFACE'] = r'CGI/1.1'
   131         env[r'REQUEST_METHOD'] = self.command
   135         env[r'REQUEST_METHOD'] = self.command
   132         env[r'SERVER_NAME'] = self.server.server_name
   136         env[r'SERVER_NAME'] = self.server.server_name
   133         env[r'SERVER_PORT'] = str(self.server.server_port)
   137         env[r'SERVER_PORT'] = str(self.server.server_port)
   134         env[r'REQUEST_URI'] = self.path
   138         env[r'REQUEST_URI'] = self.path
   135         env[r'SCRIPT_NAME'] = self.server.prefix
   139         env[r'SCRIPT_NAME'] = pycompat.sysstr(self.server.prefix)
   136         env[r'PATH_INFO'] = path[len(self.server.prefix):]
   140         env[r'PATH_INFO'] = pycompat.sysstr(path[len(self.server.prefix):])
   137         env[r'REMOTE_HOST'] = self.client_address[0]
   141         env[r'REMOTE_HOST'] = self.client_address[0]
   138         env[r'REMOTE_ADDR'] = self.client_address[0]
   142         env[r'REMOTE_ADDR'] = self.client_address[0]
   139         if query:
   143         env[r'QUERY_STRING'] = query or r''
   140             env[r'QUERY_STRING'] = query
       
   141 
   144 
   142         if pycompat.ispy3:
   145         if pycompat.ispy3:
   143             if self.headers.get_content_type() is None:
   146             if self.headers.get_content_type() is None:
   144                 env[r'CONTENT_TYPE'] = self.headers.get_default_type()
   147                 env[r'CONTENT_TYPE'] = self.headers.get_default_type()
   145             else:
   148             else:
   146                 env[r'CONTENT_TYPE'] = self.headers.get_content_type()
   149                 env[r'CONTENT_TYPE'] = self.headers.get_content_type()
   147             length = self.headers.get('content-length')
   150             length = self.headers.get(r'content-length')
   148         else:
   151         else:
   149             if self.headers.typeheader is None:
   152             if self.headers.typeheader is None:
   150                 env[r'CONTENT_TYPE'] = self.headers.type
   153                 env[r'CONTENT_TYPE'] = self.headers.type
   151             else:
   154             else:
   152                 env[r'CONTENT_TYPE'] = self.headers.typeheader
   155                 env[r'CONTENT_TYPE'] = self.headers.typeheader
   153             length = self.headers.getheader('content-length')
   156             length = self.headers.getheader(r'content-length')
   154         if length:
   157         if length:
   155             env[r'CONTENT_LENGTH'] = length
   158             env[r'CONTENT_LENGTH'] = length
   156         for header in [h for h in self.headers.keys()
   159         for header in [h for h in self.headers.keys()
   157                        if h not in ('content-type', 'content-length')]:
   160                        if h not in (r'content-type', r'content-length')]:
   158             hkey = r'HTTP_' + header.replace(r'-', r'_').upper()
   161             hkey = r'HTTP_' + header.replace(r'-', r'_').upper()
   159             hval = self.headers.get(header)
   162             hval = self.headers.get(header)
   160             hval = hval.replace(r'\n', r'').strip()
   163             hval = hval.replace(r'\n', r'').strip()
   161             if hval:
   164             if hval:
   162                 env[hkey] = hval
   165                 env[hkey] = hval
   163         env[r'SERVER_PROTOCOL'] = self.request_version
   166         env[r'SERVER_PROTOCOL'] = self.request_version
   164         env[r'wsgi.version'] = (1, 0)
   167         env[r'wsgi.version'] = (1, 0)
   165         env[r'wsgi.url_scheme'] = self.url_scheme
   168         env[r'wsgi.url_scheme'] = pycompat.sysstr(self.url_scheme)
   166         if env.get(r'HTTP_EXPECT', '').lower() == '100-continue':
   169         if env.get(r'HTTP_EXPECT', '').lower() == '100-continue':
   167             self.rfile = common.continuereader(self.rfile, self.wfile.write)
   170             self.rfile = common.continuereader(self.rfile, self.wfile.write)
   168 
   171 
   169         env[r'wsgi.input'] = self.rfile
   172         env[r'wsgi.input'] = self.rfile
   170         env[r'wsgi.errors'] = _error_logger(self)
   173         env[r'wsgi.errors'] = _error_logger(self)
   171         env[r'wsgi.multithread'] = isinstance(self.server,
   174         env[r'wsgi.multithread'] = isinstance(self.server,
   172                                              socketserver.ThreadingMixIn)
   175                                              socketserver.ThreadingMixIn)
   173         env[r'wsgi.multiprocess'] = isinstance(self.server,
   176         env[r'wsgi.multiprocess'] = isinstance(self.server,
   174                                               socketserver.ForkingMixIn)
   177                                               socketserver.ForkingMixIn)
   175         env[r'wsgi.run_once'] = 0
   178         env[r'wsgi.run_once'] = 0
       
   179 
       
   180         wsgiref.validate.check_environ(env)
   176 
   181 
   177         self.saved_status = None
   182         self.saved_status = None
   178         self.saved_headers = []
   183         self.saved_headers = []
   179         self.length = None
   184         self.length = None
   180         self._chunked = None
   185         self._chunked = None
   235     def _done(self):
   240     def _done(self):
   236         if self._chunked:
   241         if self._chunked:
   237             self.wfile.write('0\r\n\r\n')
   242             self.wfile.write('0\r\n\r\n')
   238             self.wfile.flush()
   243             self.wfile.flush()
   239 
   244 
       
   245     def version_string(self):
       
   246         if self.server.serverheader:
       
   247             return self.server.serverheader
       
   248         return httpservermod.basehttprequesthandler.version_string(self)
       
   249 
   240 class _httprequesthandlerssl(_httprequesthandler):
   250 class _httprequesthandlerssl(_httprequesthandler):
   241     """HTTPS handler based on Python's ssl module"""
   251     """HTTPS handler based on Python's ssl module"""
   242 
   252 
   243     url_scheme = 'https'
   253     url_scheme = 'https'
   244 
   254 
   263                                                      cafile=cafile,
   273                                                      cafile=cafile,
   264                                                      requireclientcert=reqcert)
   274                                                      requireclientcert=reqcert)
   265 
   275 
   266     def setup(self):
   276     def setup(self):
   267         self.connection = self.request
   277         self.connection = self.request
   268         self.rfile = socket._fileobject(self.request, "rb", self.rbufsize)
   278         self.rfile = self.request.makefile(r"rb", self.rbufsize)
   269         self.wfile = socket._fileobject(self.request, "wb", self.wbufsize)
   279         self.wfile = self.request.makefile(r"wb", self.wbufsize)
   270 
   280 
   271 try:
   281 try:
   272     import threading
   282     import threading
   273     threading.activeCount() # silence pyflakes and bypass demandimport
   283     threading.activeCount() # silence pyflakes and bypass demandimport
   274     _mixin = socketserver.ThreadingMixIn
   284     _mixin = socketserver.ThreadingMixIn
   279         class _mixin(object):
   289         class _mixin(object):
   280             pass
   290             pass
   281 
   291 
   282 def openlog(opt, default):
   292 def openlog(opt, default):
   283     if opt and opt != '-':
   293     if opt and opt != '-':
   284         return open(opt, 'a')
   294         return open(opt, 'ab')
   285     return default
   295     return default
   286 
   296 
   287 class MercurialHTTPServer(_mixin, httpservermod.httpserver, object):
   297 class MercurialHTTPServer(_mixin, httpservermod.httpserver, object):
   288 
   298 
   289     # SO_REUSEADDR has broken semantics on windows
   299     # SO_REUSEADDR has broken semantics on windows
   307         self.accesslog = alog
   317         self.accesslog = alog
   308         self.errorlog = elog
   318         self.errorlog = elog
   309 
   319 
   310         self.addr, self.port = self.socket.getsockname()[0:2]
   320         self.addr, self.port = self.socket.getsockname()[0:2]
   311         self.fqaddr = socket.getfqdn(addr[0])
   321         self.fqaddr = socket.getfqdn(addr[0])
       
   322 
       
   323         self.serverheader = ui.config('web', 'server-header')
   312 
   324 
   313 class IPv6HTTPServer(MercurialHTTPServer):
   325 class IPv6HTTPServer(MercurialHTTPServer):
   314     address_family = getattr(socket, 'AF_INET6', None)
   326     address_family = getattr(socket, 'AF_INET6', None)
   315     def __init__(self, *args, **kwargs):
   327     def __init__(self, *args, **kwargs):
   316         if self.address_family is None:
   328         if self.address_family is None: