1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/OpenSecurity/install/web.py-0.37/web/httpserver.py Mon Dec 02 14:02:05 2013 +0100
1.3 @@ -0,0 +1,319 @@
1.4 +__all__ = ["runsimple"]
1.5 +
1.6 +import sys, os
1.7 +from SimpleHTTPServer import SimpleHTTPRequestHandler
1.8 +import urllib
1.9 +import posixpath
1.10 +
1.11 +import webapi as web
1.12 +import net
1.13 +import utils
1.14 +
1.15 +def runbasic(func, server_address=("0.0.0.0", 8080)):
1.16 + """
1.17 + Runs a simple HTTP server hosting WSGI app `func`. The directory `static/`
1.18 + is hosted statically.
1.19 +
1.20 + Based on [WsgiServer][ws] from [Colin Stewart][cs].
1.21 +
1.22 + [ws]: http://www.owlfish.com/software/wsgiutils/documentation/wsgi-server-api.html
1.23 + [cs]: http://www.owlfish.com/
1.24 + """
1.25 + # Copyright (c) 2004 Colin Stewart (http://www.owlfish.com/)
1.26 + # Modified somewhat for simplicity
1.27 + # Used under the modified BSD license:
1.28 + # http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5
1.29 +
1.30 + import SimpleHTTPServer, SocketServer, BaseHTTPServer, urlparse
1.31 + import socket, errno
1.32 + import traceback
1.33 +
1.34 + class WSGIHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
1.35 + def run_wsgi_app(self):
1.36 + protocol, host, path, parameters, query, fragment = \
1.37 + urlparse.urlparse('http://dummyhost%s' % self.path)
1.38 +
1.39 + # we only use path, query
1.40 + env = {'wsgi.version': (1, 0)
1.41 + ,'wsgi.url_scheme': 'http'
1.42 + ,'wsgi.input': self.rfile
1.43 + ,'wsgi.errors': sys.stderr
1.44 + ,'wsgi.multithread': 1
1.45 + ,'wsgi.multiprocess': 0
1.46 + ,'wsgi.run_once': 0
1.47 + ,'REQUEST_METHOD': self.command
1.48 + ,'REQUEST_URI': self.path
1.49 + ,'PATH_INFO': path
1.50 + ,'QUERY_STRING': query
1.51 + ,'CONTENT_TYPE': self.headers.get('Content-Type', '')
1.52 + ,'CONTENT_LENGTH': self.headers.get('Content-Length', '')
1.53 + ,'REMOTE_ADDR': self.client_address[0]
1.54 + ,'SERVER_NAME': self.server.server_address[0]
1.55 + ,'SERVER_PORT': str(self.server.server_address[1])
1.56 + ,'SERVER_PROTOCOL': self.request_version
1.57 + }
1.58 +
1.59 + for http_header, http_value in self.headers.items():
1.60 + env ['HTTP_%s' % http_header.replace('-', '_').upper()] = \
1.61 + http_value
1.62 +
1.63 + # Setup the state
1.64 + self.wsgi_sent_headers = 0
1.65 + self.wsgi_headers = []
1.66 +
1.67 + try:
1.68 + # We have there environment, now invoke the application
1.69 + result = self.server.app(env, self.wsgi_start_response)
1.70 + try:
1.71 + try:
1.72 + for data in result:
1.73 + if data:
1.74 + self.wsgi_write_data(data)
1.75 + finally:
1.76 + if hasattr(result, 'close'):
1.77 + result.close()
1.78 + except socket.error, socket_err:
1.79 + # Catch common network errors and suppress them
1.80 + if (socket_err.args[0] in \
1.81 + (errno.ECONNABORTED, errno.EPIPE)):
1.82 + return
1.83 + except socket.timeout, socket_timeout:
1.84 + return
1.85 + except:
1.86 + print >> web.debug, traceback.format_exc(),
1.87 +
1.88 + if (not self.wsgi_sent_headers):
1.89 + # We must write out something!
1.90 + self.wsgi_write_data(" ")
1.91 + return
1.92 +
1.93 + do_POST = run_wsgi_app
1.94 + do_PUT = run_wsgi_app
1.95 + do_DELETE = run_wsgi_app
1.96 +
1.97 + def do_GET(self):
1.98 + if self.path.startswith('/static/'):
1.99 + SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)
1.100 + else:
1.101 + self.run_wsgi_app()
1.102 +
1.103 + def wsgi_start_response(self, response_status, response_headers,
1.104 + exc_info=None):
1.105 + if (self.wsgi_sent_headers):
1.106 + raise Exception \
1.107 + ("Headers already sent and start_response called again!")
1.108 + # Should really take a copy to avoid changes in the application....
1.109 + self.wsgi_headers = (response_status, response_headers)
1.110 + return self.wsgi_write_data
1.111 +
1.112 + def wsgi_write_data(self, data):
1.113 + if (not self.wsgi_sent_headers):
1.114 + status, headers = self.wsgi_headers
1.115 + # Need to send header prior to data
1.116 + status_code = status[:status.find(' ')]
1.117 + status_msg = status[status.find(' ') + 1:]
1.118 + self.send_response(int(status_code), status_msg)
1.119 + for header, value in headers:
1.120 + self.send_header(header, value)
1.121 + self.end_headers()
1.122 + self.wsgi_sent_headers = 1
1.123 + # Send the data
1.124 + self.wfile.write(data)
1.125 +
1.126 + class WSGIServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer):
1.127 + def __init__(self, func, server_address):
1.128 + BaseHTTPServer.HTTPServer.__init__(self,
1.129 + server_address,
1.130 + WSGIHandler)
1.131 + self.app = func
1.132 + self.serverShuttingDown = 0
1.133 +
1.134 + print "http://%s:%d/" % server_address
1.135 + WSGIServer(func, server_address).serve_forever()
1.136 +
1.137 +# The WSGIServer instance.
1.138 +# Made global so that it can be stopped in embedded mode.
1.139 +server = None
1.140 +
1.141 +def runsimple(func, server_address=("0.0.0.0", 8080)):
1.142 + """
1.143 + Runs [CherryPy][cp] WSGI server hosting WSGI app `func`.
1.144 + The directory `static/` is hosted statically.
1.145 +
1.146 + [cp]: http://www.cherrypy.org
1.147 + """
1.148 + global server
1.149 + func = StaticMiddleware(func)
1.150 + func = LogMiddleware(func)
1.151 +
1.152 + server = WSGIServer(server_address, func)
1.153 +
1.154 + if server.ssl_adapter:
1.155 + print "https://%s:%d/" % server_address
1.156 + else:
1.157 + print "http://%s:%d/" % server_address
1.158 +
1.159 + try:
1.160 + server.start()
1.161 + except (KeyboardInterrupt, SystemExit):
1.162 + server.stop()
1.163 + server = None
1.164 +
1.165 +def WSGIServer(server_address, wsgi_app):
1.166 + """Creates CherryPy WSGI server listening at `server_address` to serve `wsgi_app`.
1.167 + This function can be overwritten to customize the webserver or use a different webserver.
1.168 + """
1.169 + import wsgiserver
1.170 +
1.171 + # Default values of wsgiserver.ssl_adapters uses cherrypy.wsgiserver
1.172 + # prefix. Overwriting it make it work with web.wsgiserver.
1.173 + wsgiserver.ssl_adapters = {
1.174 + 'builtin': 'web.wsgiserver.ssl_builtin.BuiltinSSLAdapter',
1.175 + 'pyopenssl': 'web.wsgiserver.ssl_pyopenssl.pyOpenSSLAdapter',
1.176 + }
1.177 +
1.178 + server = wsgiserver.CherryPyWSGIServer(server_address, wsgi_app, server_name="localhost")
1.179 +
1.180 + def create_ssl_adapter(cert, key):
1.181 + # wsgiserver tries to import submodules as cherrypy.wsgiserver.foo.
1.182 + # That doesn't work as not it is web.wsgiserver.
1.183 + # Patching sys.modules temporarily to make it work.
1.184 + import types
1.185 + cherrypy = types.ModuleType('cherrypy')
1.186 + cherrypy.wsgiserver = wsgiserver
1.187 + sys.modules['cherrypy'] = cherrypy
1.188 + sys.modules['cherrypy.wsgiserver'] = wsgiserver
1.189 +
1.190 + from wsgiserver.ssl_pyopenssl import pyOpenSSLAdapter
1.191 + adapter = pyOpenSSLAdapter(cert, key)
1.192 +
1.193 + # We are done with our work. Cleanup the patches.
1.194 + del sys.modules['cherrypy']
1.195 + del sys.modules['cherrypy.wsgiserver']
1.196 +
1.197 + return adapter
1.198 +
1.199 + # SSL backward compatibility
1.200 + if (server.ssl_adapter is None and
1.201 + getattr(server, 'ssl_certificate', None) and
1.202 + getattr(server, 'ssl_private_key', None)):
1.203 + server.ssl_adapter = create_ssl_adapter(server.ssl_certificate, server.ssl_private_key)
1.204 +
1.205 + server.nodelay = not sys.platform.startswith('java') # TCP_NODELAY isn't supported on the JVM
1.206 + return server
1.207 +
1.208 +class StaticApp(SimpleHTTPRequestHandler):
1.209 + """WSGI application for serving static files."""
1.210 + def __init__(self, environ, start_response):
1.211 + self.headers = []
1.212 + self.environ = environ
1.213 + self.start_response = start_response
1.214 +
1.215 + def send_response(self, status, msg=""):
1.216 + self.status = str(status) + " " + msg
1.217 +
1.218 + def send_header(self, name, value):
1.219 + self.headers.append((name, value))
1.220 +
1.221 + def end_headers(self):
1.222 + pass
1.223 +
1.224 + def log_message(*a): pass
1.225 +
1.226 + def __iter__(self):
1.227 + environ = self.environ
1.228 +
1.229 + self.path = environ.get('PATH_INFO', '')
1.230 + self.client_address = environ.get('REMOTE_ADDR','-'), \
1.231 + environ.get('REMOTE_PORT','-')
1.232 + self.command = environ.get('REQUEST_METHOD', '-')
1.233 +
1.234 + from cStringIO import StringIO
1.235 + self.wfile = StringIO() # for capturing error
1.236 +
1.237 + try:
1.238 + path = self.translate_path(self.path)
1.239 + etag = '"%s"' % os.path.getmtime(path)
1.240 + client_etag = environ.get('HTTP_IF_NONE_MATCH')
1.241 + self.send_header('ETag', etag)
1.242 + if etag == client_etag:
1.243 + self.send_response(304, "Not Modified")
1.244 + self.start_response(self.status, self.headers)
1.245 + raise StopIteration
1.246 + except OSError:
1.247 + pass # Probably a 404
1.248 +
1.249 + f = self.send_head()
1.250 + self.start_response(self.status, self.headers)
1.251 +
1.252 + if f:
1.253 + block_size = 16 * 1024
1.254 + while True:
1.255 + buf = f.read(block_size)
1.256 + if not buf:
1.257 + break
1.258 + yield buf
1.259 + f.close()
1.260 + else:
1.261 + value = self.wfile.getvalue()
1.262 + yield value
1.263 +
1.264 +class StaticMiddleware:
1.265 + """WSGI middleware for serving static files."""
1.266 + def __init__(self, app, prefix='/static/'):
1.267 + self.app = app
1.268 + self.prefix = prefix
1.269 +
1.270 + def __call__(self, environ, start_response):
1.271 + path = environ.get('PATH_INFO', '')
1.272 + path = self.normpath(path)
1.273 +
1.274 + if path.startswith(self.prefix):
1.275 + return StaticApp(environ, start_response)
1.276 + else:
1.277 + return self.app(environ, start_response)
1.278 +
1.279 + def normpath(self, path):
1.280 + path2 = posixpath.normpath(urllib.unquote(path))
1.281 + if path.endswith("/"):
1.282 + path2 += "/"
1.283 + return path2
1.284 +
1.285 +
1.286 +class LogMiddleware:
1.287 + """WSGI middleware for logging the status."""
1.288 + def __init__(self, app):
1.289 + self.app = app
1.290 + self.format = '%s - - [%s] "%s %s %s" - %s'
1.291 +
1.292 + from BaseHTTPServer import BaseHTTPRequestHandler
1.293 + import StringIO
1.294 + f = StringIO.StringIO()
1.295 +
1.296 + class FakeSocket:
1.297 + def makefile(self, *a):
1.298 + return f
1.299 +
1.300 + # take log_date_time_string method from BaseHTTPRequestHandler
1.301 + self.log_date_time_string = BaseHTTPRequestHandler(FakeSocket(), None, None).log_date_time_string
1.302 +
1.303 + def __call__(self, environ, start_response):
1.304 + def xstart_response(status, response_headers, *args):
1.305 + out = start_response(status, response_headers, *args)
1.306 + self.log(status, environ)
1.307 + return out
1.308 +
1.309 + return self.app(environ, xstart_response)
1.310 +
1.311 + def log(self, status, environ):
1.312 + outfile = environ.get('wsgi.errors', web.debug)
1.313 + req = environ.get('PATH_INFO', '_')
1.314 + protocol = environ.get('ACTUAL_SERVER_PROTOCOL', '-')
1.315 + method = environ.get('REQUEST_METHOD', '-')
1.316 + host = "%s:%s" % (environ.get('REMOTE_ADDR','-'),
1.317 + environ.get('REMOTE_PORT','-'))
1.318 +
1.319 + time = self.log_date_time_string()
1.320 +
1.321 + msg = self.format % (host, time, protocol, method, req, status)
1.322 + print >> outfile, utils.safestr(msg)