PageRenderTime 50ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/plugins/org.python.pydev.jython/Lib/CGIHTTPServer.py

https://github.com/MrGreen123/Pydev
Python | 305 lines | 279 code | 6 blank | 20 comment | 21 complexity | e43aeb91d319321ffe4cad32bc05cd00 MD5 | raw file
  1. """CGI-savvy HTTP Server.
  2. This module builds on SimpleHTTPServer by implementing GET and POST
  3. requests to cgi-bin scripts.
  4. If the os.fork() function is not present (e.g. on Windows),
  5. os.popen2() is used as a fallback, with slightly altered semantics; if
  6. that function is not present either (e.g. on Macintosh), only Python
  7. scripts are supported, and they are executed by the current process.
  8. In all cases, the implementation is intentionally naive -- all
  9. requests are executed sychronously.
  10. SECURITY WARNING: DON'T USE THIS CODE UNLESS YOU ARE INSIDE A FIREWALL
  11. -- it may execute arbitrary Python code or external programs.
  12. """
  13. __version__ = "0.4"
  14. __all__ = ["CGIHTTPRequestHandler"]
  15. import os
  16. import sys
  17. import urllib
  18. import BaseHTTPServer
  19. import SimpleHTTPServer
  20. class CGIHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
  21. """Complete HTTP server with GET, HEAD and POST commands.
  22. GET and HEAD also support running CGI scripts.
  23. The POST command is *only* implemented for CGI scripts.
  24. """
  25. # Determine platform specifics
  26. have_fork = hasattr(os, 'fork')
  27. have_popen2 = hasattr(os, 'popen2')
  28. # Make rfile unbuffered -- we need to read one line and then pass
  29. # the rest to a subprocess, so we can't use buffered input.
  30. rbufsize = 0
  31. def do_POST(self):
  32. """Serve a POST request.
  33. This is only implemented for CGI scripts.
  34. """
  35. if self.is_cgi():
  36. self.run_cgi()
  37. else:
  38. self.send_error(501, "Can only POST to CGI scripts")
  39. def send_head(self):
  40. """Version of send_head that support CGI scripts"""
  41. if self.is_cgi():
  42. return self.run_cgi()
  43. else:
  44. return SimpleHTTPServer.SimpleHTTPRequestHandler.send_head(self)
  45. def is_cgi(self):
  46. """Test whether self.path corresponds to a CGI script.
  47. Return a tuple (dir, rest) if self.path requires running a
  48. CGI script, None if not. Note that rest begins with a
  49. slash if it is not empty.
  50. The default implementation tests whether the path
  51. begins with one of the strings in the list
  52. self.cgi_directories (and the next character is a '/'
  53. or the end of the string).
  54. """
  55. path = self.path
  56. for x in self.cgi_directories:
  57. i = len(x)
  58. if path[:i] == x and (not path[i:] or path[i] == '/'):
  59. self.cgi_info = path[:i], path[i+1:]
  60. return 1
  61. return 0
  62. cgi_directories = ['/cgi-bin', '/htbin']
  63. def is_executable(self, path):
  64. """Test whether argument path is an executable file."""
  65. return executable(path)
  66. def is_python(self, path):
  67. """Test whether argument path is a Python script."""
  68. head, tail = os.path.splitext(path)
  69. return tail.lower() in (".py", ".pyw")
  70. def run_cgi(self):
  71. """Execute a CGI script."""
  72. dir, rest = self.cgi_info
  73. i = rest.rfind('?')
  74. if i >= 0:
  75. rest, query = rest[:i], rest[i+1:]
  76. else:
  77. query = ''
  78. i = rest.find('/')
  79. if i >= 0:
  80. script, rest = rest[:i], rest[i:]
  81. else:
  82. script, rest = rest, ''
  83. scriptname = dir + '/' + script
  84. scriptfile = self.translate_path(scriptname)
  85. if not os.path.exists(scriptfile):
  86. self.send_error(404, "No such CGI script (%s)" % `scriptname`)
  87. return
  88. if not os.path.isfile(scriptfile):
  89. self.send_error(403, "CGI script is not a plain file (%s)" %
  90. `scriptname`)
  91. return
  92. ispy = self.is_python(scriptname)
  93. if not ispy:
  94. if not (self.have_fork or self.have_popen2):
  95. self.send_error(403, "CGI script is not a Python script (%s)" %
  96. `scriptname`)
  97. return
  98. if not self.is_executable(scriptfile):
  99. self.send_error(403, "CGI script is not executable (%s)" %
  100. `scriptname`)
  101. return
  102. # Reference: http://hoohoo.ncsa.uiuc.edu/cgi/env.html
  103. # XXX Much of the following could be prepared ahead of time!
  104. env = {}
  105. env['SERVER_SOFTWARE'] = self.version_string()
  106. env['SERVER_NAME'] = self.server.server_name
  107. env['GATEWAY_INTERFACE'] = 'CGI/1.1'
  108. env['SERVER_PROTOCOL'] = self.protocol_version
  109. env['SERVER_PORT'] = str(self.server.server_port)
  110. env['REQUEST_METHOD'] = self.command
  111. uqrest = urllib.unquote(rest)
  112. env['PATH_INFO'] = uqrest
  113. env['PATH_TRANSLATED'] = self.translate_path(uqrest)
  114. env['SCRIPT_NAME'] = scriptname
  115. if query:
  116. env['QUERY_STRING'] = query
  117. host = self.address_string()
  118. if host != self.client_address[0]:
  119. env['REMOTE_HOST'] = host
  120. env['REMOTE_ADDR'] = self.client_address[0]
  121. # XXX AUTH_TYPE
  122. # XXX REMOTE_USER
  123. # XXX REMOTE_IDENT
  124. if self.headers.typeheader is None:
  125. env['CONTENT_TYPE'] = self.headers.type
  126. else:
  127. env['CONTENT_TYPE'] = self.headers.typeheader
  128. length = self.headers.getheader('content-length')
  129. if length:
  130. env['CONTENT_LENGTH'] = length
  131. accept = []
  132. for line in self.headers.getallmatchingheaders('accept'):
  133. if line[:1] in "\t\n\r ":
  134. accept.append(line.strip())
  135. else:
  136. accept = accept + line[7:].split(',')
  137. env['HTTP_ACCEPT'] = ','.join(accept)
  138. ua = self.headers.getheader('user-agent')
  139. if ua:
  140. env['HTTP_USER_AGENT'] = ua
  141. co = filter(None, self.headers.getheaders('cookie'))
  142. if co:
  143. env['HTTP_COOKIE'] = ', '.join(co)
  144. # XXX Other HTTP_* headers
  145. if not self.have_fork:
  146. # Since we're setting the env in the parent, provide empty
  147. # values to override previously set values
  148. for k in ('QUERY_STRING', 'REMOTE_HOST', 'CONTENT_LENGTH',
  149. 'HTTP_USER_AGENT', 'HTTP_COOKIE'):
  150. env.setdefault(k, "")
  151. self.send_response(200, "Script output follows")
  152. decoded_query = query.replace('+', ' ')
  153. if self.have_fork:
  154. # Unix -- fork as we should
  155. args = [script]
  156. if '=' not in decoded_query:
  157. args.append(decoded_query)
  158. nobody = nobody_uid()
  159. self.wfile.flush() # Always flush before forking
  160. pid = os.fork()
  161. if pid != 0:
  162. # Parent
  163. pid, sts = os.waitpid(pid, 0)
  164. if sts:
  165. self.log_error("CGI script exit status %#x", sts)
  166. return
  167. # Child
  168. try:
  169. try:
  170. os.setuid(nobody)
  171. except os.error:
  172. pass
  173. os.dup2(self.rfile.fileno(), 0)
  174. os.dup2(self.wfile.fileno(), 1)
  175. os.execve(scriptfile, args, env)
  176. except:
  177. self.server.handle_error(self.request, self.client_address)
  178. os._exit(127)
  179. elif self.have_popen2:
  180. # Windows -- use popen2 to create a subprocess
  181. import shutil
  182. os.environ.update(env)
  183. cmdline = scriptfile
  184. if self.is_python(scriptfile):
  185. interp = sys.executable
  186. if interp.lower().endswith("w.exe"):
  187. # On Windows, use python.exe, not python.exe
  188. interp = interp[:-5] = interp[-4:]
  189. cmdline = "%s %s" % (interp, cmdline)
  190. if '=' not in query and '"' not in query:
  191. cmdline = '%s "%s"' % (cmdline, query)
  192. self.log_error("command: %s", cmdline)
  193. try:
  194. nbytes = int(length)
  195. except:
  196. nbytes = 0
  197. fi, fo = os.popen2(cmdline)
  198. if self.command.lower() == "post" and nbytes > 0:
  199. data = self.rfile.read(nbytes)
  200. fi.write(data)
  201. fi.close()
  202. shutil.copyfileobj(fo, self.wfile)
  203. sts = fo.close()
  204. if sts:
  205. self.log_error("CGI script exit status %#x", sts)
  206. else:
  207. self.log_error("CGI script exited OK")
  208. else:
  209. # Other O.S. -- execute script in this process
  210. os.environ.update(env)
  211. save_argv = sys.argv
  212. save_stdin = sys.stdin
  213. save_stdout = sys.stdout
  214. save_stderr = sys.stderr
  215. try:
  216. try:
  217. sys.argv = [scriptfile]
  218. if '=' not in decoded_query:
  219. sys.argv.append(decoded_query)
  220. sys.stdout = self.wfile
  221. sys.stdin = self.rfile
  222. execfile(scriptfile, {"__name__": "__main__"})
  223. finally:
  224. sys.argv = save_argv
  225. sys.stdin = save_stdin
  226. sys.stdout = save_stdout
  227. sys.stderr = save_stderr
  228. except SystemExit, sts:
  229. self.log_error("CGI script exit status %s", str(sts))
  230. else:
  231. self.log_error("CGI script exited OK")
  232. nobody = None
  233. def nobody_uid():
  234. """Internal routine to get nobody's uid"""
  235. global nobody
  236. if nobody:
  237. return nobody
  238. try:
  239. import pwd
  240. except ImportError:
  241. return -1
  242. try:
  243. nobody = pwd.getpwnam('nobody')[2]
  244. except KeyError:
  245. nobody = 1 + max(map(lambda x: x[2], pwd.getpwall()))
  246. return nobody
  247. def executable(path):
  248. """Test for executable file."""
  249. try:
  250. st = os.stat(path)
  251. except os.error:
  252. return 0
  253. return st[0] & 0111 != 0
  254. def test(HandlerClass = CGIHTTPRequestHandler,
  255. ServerClass = BaseHTTPServer.HTTPServer):
  256. SimpleHTTPServer.test(HandlerClass, ServerClass)
  257. if __name__ == '__main__':
  258. test()