comparison mercurial/hgweb/server.py @ 37788:ed5448edcbfa stable 4.6rc0

merge with default to begin 4.6 freeze # no-check-commit because of many vendored packages
author Augie Fackler <augie@google.com>
date Wed, 18 Apr 2018 15:32:08 -0400
parents 7de7bd407251 b5ca5d34fe8d
children 9f499d28efb4
comparison
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: