mercurial/hgweb/server.py
changeset 43506 9f70512ae2cf
parent 43503 313e3a279828
child 44877 86a7b7abf28e
child 45034 b1a1702262c9
equal deleted inserted replaced
43505:47fac1692ede 43506:9f70512ae2cf
    41     """Return path and query that has been split from uri
    41     """Return path and query that has been split from uri
    42 
    42 
    43     Just like CGI environment, the path is unquoted, the query is
    43     Just like CGI environment, the path is unquoted, the query is
    44     not.
    44     not.
    45     """
    45     """
    46     if r'?' in uri:
    46     if '?' in uri:
    47         path, query = uri.split(r'?', 1)
    47         path, query = uri.split('?', 1)
    48     else:
    48     else:
    49         path, query = uri, r''
    49         path, query = uri, r''
    50     return urlreq.unquote(path), query
    50     return urlreq.unquote(path), query
    51 
    51 
    52 
    52 
    95         self._log_any(self.server.errorlog, format, *args)
    95         self._log_any(self.server.errorlog, format, *args)
    96 
    96 
    97     def log_message(self, format, *args):
    97     def log_message(self, format, *args):
    98         self._log_any(self.server.accesslog, format, *args)
    98         self._log_any(self.server.accesslog, format, *args)
    99 
    99 
   100     def log_request(self, code=r'-', size=r'-'):
   100     def log_request(self, code='-', size='-'):
   101         xheaders = []
   101         xheaders = []
   102         if util.safehasattr(self, b'headers'):
   102         if util.safehasattr(self, b'headers'):
   103             xheaders = [
   103             xheaders = [
   104                 h for h in self.headers.items() if h[0].startswith(r'x-')
   104                 h for h in self.headers.items() if h[0].startswith('x-')
   105             ]
   105             ]
   106         self.log_message(
   106         self.log_message(
   107             r'"%s" %s %s%s',
   107             '"%s" %s %s%s',
   108             self.requestline,
   108             self.requestline,
   109             str(code),
   109             str(code),
   110             str(size),
   110             str(size),
   111             r''.join([r' %s:%s' % h for h in sorted(xheaders)]),
   111             ''.join([' %s:%s' % h for h in sorted(xheaders)]),
   112         )
   112         )
   113 
   113 
   114     def do_write(self):
   114     def do_write(self):
   115         try:
   115         try:
   116             self.do_hgweb()
   116             self.do_hgweb()
   158         # Ensure the slicing of path below is valid
   158         # Ensure the slicing of path below is valid
   159         if path != self.server.prefix and not path.startswith(
   159         if path != self.server.prefix and not path.startswith(
   160             self.server.prefix + b'/'
   160             self.server.prefix + b'/'
   161         ):
   161         ):
   162             self._start_response(pycompat.strurl(common.statusmessage(404)), [])
   162             self._start_response(pycompat.strurl(common.statusmessage(404)), [])
   163             if self.command == r'POST':
   163             if self.command == 'POST':
   164                 # Paranoia: tell the client we're going to close the
   164                 # Paranoia: tell the client we're going to close the
   165                 # socket so they don't try and reuse a socket that
   165                 # socket so they don't try and reuse a socket that
   166                 # might have a POST body waiting to confuse us. We do
   166                 # might have a POST body waiting to confuse us. We do
   167                 # this by directly munging self.saved_headers because
   167                 # this by directly munging self.saved_headers because
   168                 # self._start_response ignores Connection headers.
   168                 # self._start_response ignores Connection headers.
   169                 self.saved_headers = [(r'Connection', r'Close')]
   169                 self.saved_headers = [('Connection', 'Close')]
   170             self._write(b"Not Found")
   170             self._write(b"Not Found")
   171             self._done()
   171             self._done()
   172             return
   172             return
   173 
   173 
   174         env = {}
   174         env = {}
   175         env[r'GATEWAY_INTERFACE'] = r'CGI/1.1'
   175         env['GATEWAY_INTERFACE'] = 'CGI/1.1'
   176         env[r'REQUEST_METHOD'] = self.command
   176         env['REQUEST_METHOD'] = self.command
   177         env[r'SERVER_NAME'] = self.server.server_name
   177         env['SERVER_NAME'] = self.server.server_name
   178         env[r'SERVER_PORT'] = str(self.server.server_port)
   178         env['SERVER_PORT'] = str(self.server.server_port)
   179         env[r'REQUEST_URI'] = self.path
   179         env['REQUEST_URI'] = self.path
   180         env[r'SCRIPT_NAME'] = pycompat.sysstr(self.server.prefix)
   180         env['SCRIPT_NAME'] = pycompat.sysstr(self.server.prefix)
   181         env[r'PATH_INFO'] = pycompat.sysstr(path[len(self.server.prefix) :])
   181         env['PATH_INFO'] = pycompat.sysstr(path[len(self.server.prefix) :])
   182         env[r'REMOTE_HOST'] = self.client_address[0]
   182         env['REMOTE_HOST'] = self.client_address[0]
   183         env[r'REMOTE_ADDR'] = self.client_address[0]
   183         env['REMOTE_ADDR'] = self.client_address[0]
   184         env[r'QUERY_STRING'] = query or r''
   184         env['QUERY_STRING'] = query or ''
   185 
   185 
   186         if pycompat.ispy3:
   186         if pycompat.ispy3:
   187             if self.headers.get_content_type() is None:
   187             if self.headers.get_content_type() is None:
   188                 env[r'CONTENT_TYPE'] = self.headers.get_default_type()
   188                 env['CONTENT_TYPE'] = self.headers.get_default_type()
   189             else:
   189             else:
   190                 env[r'CONTENT_TYPE'] = self.headers.get_content_type()
   190                 env['CONTENT_TYPE'] = self.headers.get_content_type()
   191             length = self.headers.get(r'content-length')
   191             length = self.headers.get('content-length')
   192         else:
   192         else:
   193             if self.headers.typeheader is None:
   193             if self.headers.typeheader is None:
   194                 env[r'CONTENT_TYPE'] = self.headers.type
   194                 env['CONTENT_TYPE'] = self.headers.type
   195             else:
   195             else:
   196                 env[r'CONTENT_TYPE'] = self.headers.typeheader
   196                 env['CONTENT_TYPE'] = self.headers.typeheader
   197             length = self.headers.getheader(r'content-length')
   197             length = self.headers.getheader('content-length')
   198         if length:
   198         if length:
   199             env[r'CONTENT_LENGTH'] = length
   199             env['CONTENT_LENGTH'] = length
   200         for header in [
   200         for header in [
   201             h
   201             h
   202             for h in self.headers.keys()
   202             for h in self.headers.keys()
   203             if h.lower() not in (r'content-type', r'content-length')
   203             if h.lower() not in ('content-type', 'content-length')
   204         ]:
   204         ]:
   205             hkey = r'HTTP_' + header.replace(r'-', r'_').upper()
   205             hkey = 'HTTP_' + header.replace('-', '_').upper()
   206             hval = self.headers.get(header)
   206             hval = self.headers.get(header)
   207             hval = hval.replace(r'\n', r'').strip()
   207             hval = hval.replace('\n', '').strip()
   208             if hval:
   208             if hval:
   209                 env[hkey] = hval
   209                 env[hkey] = hval
   210         env[r'SERVER_PROTOCOL'] = self.request_version
   210         env['SERVER_PROTOCOL'] = self.request_version
   211         env[r'wsgi.version'] = (1, 0)
   211         env['wsgi.version'] = (1, 0)
   212         env[r'wsgi.url_scheme'] = pycompat.sysstr(self.url_scheme)
   212         env['wsgi.url_scheme'] = pycompat.sysstr(self.url_scheme)
   213         if env.get(r'HTTP_EXPECT', b'').lower() == b'100-continue':
   213         if env.get('HTTP_EXPECT', b'').lower() == b'100-continue':
   214             self.rfile = common.continuereader(self.rfile, self.wfile.write)
   214             self.rfile = common.continuereader(self.rfile, self.wfile.write)
   215 
   215 
   216         env[r'wsgi.input'] = self.rfile
   216         env['wsgi.input'] = self.rfile
   217         env[r'wsgi.errors'] = _error_logger(self)
   217         env['wsgi.errors'] = _error_logger(self)
   218         env[r'wsgi.multithread'] = isinstance(
   218         env['wsgi.multithread'] = isinstance(
   219             self.server, socketserver.ThreadingMixIn
   219             self.server, socketserver.ThreadingMixIn
   220         )
   220         )
   221         if util.safehasattr(socketserver, b'ForkingMixIn'):
   221         if util.safehasattr(socketserver, b'ForkingMixIn'):
   222             env[r'wsgi.multiprocess'] = isinstance(
   222             env['wsgi.multiprocess'] = isinstance(
   223                 self.server, socketserver.ForkingMixIn
   223                 self.server, socketserver.ForkingMixIn
   224             )
   224             )
   225         else:
   225         else:
   226             env[r'wsgi.multiprocess'] = False
   226             env['wsgi.multiprocess'] = False
   227 
   227 
   228         env[r'wsgi.run_once'] = 0
   228         env['wsgi.run_once'] = 0
   229 
   229 
   230         wsgiref.validate.check_environ(env)
   230         wsgiref.validate.check_environ(env)
   231 
   231 
   232         self.saved_status = None
   232         self.saved_status = None
   233         self.saved_headers = []
   233         self.saved_headers = []
   249         self.send_response(*saved_status)
   249         self.send_response(*saved_status)
   250         self.length = None
   250         self.length = None
   251         self._chunked = False
   251         self._chunked = False
   252         for h in self.saved_headers:
   252         for h in self.saved_headers:
   253             self.send_header(*h)
   253             self.send_header(*h)
   254             if h[0].lower() == r'content-length':
   254             if h[0].lower() == 'content-length':
   255                 self.length = int(h[1])
   255                 self.length = int(h[1])
   256         if self.length is None and saved_status[0] != common.HTTP_NOT_MODIFIED:
   256         if self.length is None and saved_status[0] != common.HTTP_NOT_MODIFIED:
   257             self._chunked = (
   257             self._chunked = (
   258                 not self.close_connection
   258                 not self.close_connection and self.request_version == 'HTTP/1.1'
   259                 and self.request_version == r'HTTP/1.1'
       
   260             )
   259             )
   261             if self._chunked:
   260             if self._chunked:
   262                 self.send_header(r'Transfer-Encoding', r'chunked')
   261                 self.send_header('Transfer-Encoding', 'chunked')
   263             else:
   262             else:
   264                 self.send_header(r'Connection', r'close')
   263                 self.send_header('Connection', 'close')
   265         self.end_headers()
   264         self.end_headers()
   266         self.sent_headers = True
   265         self.sent_headers = True
   267 
   266 
   268     def _start_response(self, http_status, headers, exc_info=None):
   267     def _start_response(self, http_status, headers, exc_info=None):
   269         assert isinstance(http_status, str)
   268         assert isinstance(http_status, str)
   270         code, msg = http_status.split(None, 1)
   269         code, msg = http_status.split(None, 1)
   271         code = int(code)
   270         code = int(code)
   272         self.saved_status = http_status
   271         self.saved_status = http_status
   273         bad_headers = (r'connection', r'transfer-encoding')
   272         bad_headers = ('connection', 'transfer-encoding')
   274         self.saved_headers = [
   273         self.saved_headers = [
   275             h for h in headers if h[0].lower() not in bad_headers
   274             h for h in headers if h[0].lower() not in bad_headers
   276         ]
   275         ]
   277         return self._write
   276         return self._write
   278 
   277