diff --git a/Lib/http/server.py b/Lib/http/server.py --- a/Lib/http/server.py +++ b/Lib/http/server.py @@ -12,9 +12,6 @@ This class implements GET and POST requests to cgi-bin scripts. -If the os.fork() function is not present (e.g. on Windows), -subprocess.Popen() is used as a fallback, with slightly altered semantics. - In all cases, the implementation is intentionally naive -- all requests are executed synchronously. @@ -96,6 +93,7 @@ import shutil import socket # For gethostbyaddr() import socketserver +import subprocess import sys import time import urllib.parse @@ -922,9 +920,6 @@ """ - # Determine platform specifics - have_fork = hasattr(os, 'fork') - # Make rfile unbuffered -- we need to read one line and then pass # the rest to a subprocess, so we can't use buffered input. rbufsize = 0 @@ -1024,8 +1019,7 @@ self.send_error(403, "CGI script is not a plain file (%r)" % scriptname) return - ispy = self.is_python(scriptname) - if self.have_fork or not ispy: + if os.name == 'posix' or not self.is_python(scriptfile): if not self.is_executable(scriptfile): self.send_error(403, "CGI script is not executable (%r)" % scriptname) @@ -1098,53 +1092,37 @@ self.send_response(200, "Script output follows") self.flush_headers() + self.wfile.flush() decoded_query = query.replace('+', ' ') - if self.have_fork: - # Unix -- fork as we should - args = [script] - if '=' not in decoded_query: - args.append(decoded_query) - nobody = nobody_uid() - self.wfile.flush() # Always flush before forking - pid = os.fork() - if pid != 0: - # Parent - pid, sts = os.waitpid(pid, 0) - # throw away additional data [see bug #427345] - while select.select([self.rfile], [], [], 0)[0]: - if not self.rfile.read(1): - break - if sts: - self.log_error("CGI script exit status %#x", sts) - return - # Child - try: + cmdline = [scriptfile] + if self.is_python(scriptfile): + interp = sys.executable + if os.name == 'nt' and interp.lower().endswith("w.exe"): + # On Windows, use python.exe, not pythonw.exe + interp = interp[:-5] + interp[-4:] + cmdline = [interp, '-u'] + cmdline + + if '=' not in decoded_query: + cmdline.append(decoded_query) + + self.log_message("command: %s", subprocess.list2cmdline(cmdline)) + + if os.name == 'posix': + def reduce_privilege(): + nobody = nobody_uid() try: os.setuid(nobody) except OSError: pass - os.dup2(self.rfile.fileno(), 0) - os.dup2(self.wfile.fileno(), 1) - os.execve(scriptfile, args, env) - except: - self.server.handle_error(self.request, self.client_address) - os._exit(127) + p = subprocess.Popen(cmdline, stdin=self.rfile, + stdout=self.wfile, + env=env, + preexec_fn=reduce_privilege) + status = p.wait() else: - # Non-Unix -- use subprocess - import subprocess - cmdline = [scriptfile] - if self.is_python(scriptfile): - interp = sys.executable - if interp.lower().endswith("w.exe"): - # On Windows, use python.exe, not pythonw.exe - interp = interp[:-5] + interp[-4:] - cmdline = [interp, '-u'] + cmdline - if '=' not in query: - cmdline.append(query) - self.log_message("command: %s", subprocess.list2cmdline(cmdline)) try: nbytes = int(length) except (TypeError, ValueError): @@ -1153,27 +1131,28 @@ stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, - env = env - ) + env = env) if self.command.lower() == "post" and nbytes > 0: data = self.rfile.read(nbytes) else: data = None - # throw away additional data [see bug #427345] - while select.select([self.rfile._sock], [], [], 0)[0]: - if not self.rfile._sock.recv(1): - break stdout, stderr = p.communicate(data) self.wfile.write(stdout) if stderr: self.log_error('%s', stderr) p.stderr.close() p.stdout.close() - status = p.returncode - if status: - self.log_error("CGI script exit status %#x", status) - else: - self.log_message("CGI script exited OK") + status = p.wait() + + # throw away additional data [see bug #427345] + while select.select([self.rfile._sock], [], [], 0)[0]: + if not self.rfile._sock.recv(1): + break + + if status: + self.log_error("CGI script exit status %#x", status) + else: + self.log_message("CGI script exited OK") def test(HandlerClass = BaseHTTPRequestHandler,