OpenSecurity/install/web.py-0.37/build/lib/web/httpserver.py
changeset 3 65432e6c6042
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/OpenSecurity/install/web.py-0.37/build/lib/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)