source: OpenRLabs-Git/deploy/rlabs-docker/web2py-rlabs/anyserver.py

main
Last change on this file was 42bd667, checked in by David Fuertes <dfuertes@…>, 4 years ago

Historial Limpio

  • Property mode set to 100755
File size: 12.7 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4"""
5This file is part of the web2py Web Framework
6Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu>
7License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
8
9This file is based, although a rewrite, on MIT-licensed code from the Bottle web framework.
10"""
11
12import os
13import sys
14import optparse
15import urllib
16
17path = os.path.dirname(os.path.abspath(__file__))
18os.chdir(path)
19sys.path = [path] + [p for p in sys.path if not p == path]
20
21
22class Servers:
23    @staticmethod
24    def cgi(app, address=None, **options):
25        from wsgiref.handlers import CGIHandler
26        CGIHandler().run(app)  # Just ignore host and port here
27
28    @staticmethod
29    def flup(app, address, **options):
30        import flup.server.fcgi
31        flup.server.fcgi.WSGIServer(app, bindAddress=address).run()
32
33    @staticmethod
34    def wsgiref(app, address, **options):  # pragma: no cover
35        from wsgiref.simple_server import make_server, WSGIRequestHandler
36        options = {}
37        class QuietHandler(WSGIRequestHandler):
38            def log_request(*args, **kw):
39                pass
40        options['handler_class'] = QuietHandler
41        srv = make_server(address[0], address[1], app, **options)
42        srv.serve_forever()
43
44    @staticmethod
45    def cherrypy(app, address, **options):
46        from cheroot.wsgi import Server as WSGIServer
47        server = WSGIServer(address, app)
48        server.start()
49
50    @staticmethod
51    def rocket(app, address, **options):
52        from gluon.rocket import CherryPyWSGIServer
53        server = CherryPyWSGIServer(address, app)
54        server.start()
55
56    @staticmethod
57    def rocket_with_repoze_profiler(app, address, **options):
58        from gluon.rocket import CherryPyWSGIServer
59        from repoze.profile.profiler import AccumulatingProfileMiddleware
60        from gluon.settings import global_settings
61        global_settings.web2py_crontype = 'none'
62        wrapped = AccumulatingProfileMiddleware(
63            app,
64            log_filename='wsgi.prof',
65            discard_first_request=True,
66            flush_at_shutdown=True,
67            path='/__profile__'
68        )
69        server = CherryPyWSGIServer(address, wrapped)
70        server.start()
71
72    @staticmethod
73    def paste(app, address, **options):
74        options = {}
75        from paste import httpserver
76        from paste.translogger import TransLogger
77        httpserver.serve(app, host=address[0], port=address[1], **options)
78
79    @staticmethod
80    def fapws(app, address, **options):
81        import fapws._evwsgi as evwsgi
82        from fapws import base
83        evwsgi.start(address[0], str(address[1]))
84        evwsgi.set_base_module(base)
85
86        def app(environ, start_response):
87            environ['wsgi.multiprocess'] = False
88            return app(environ, start_response)
89        evwsgi.wsgi_cb(('', app))
90        evwsgi.run()
91
92    @staticmethod
93    def gevent(app, address, **options):
94        options = options['options']
95        workers = options.workers
96        from gevent import pywsgi
97        from gevent.pool import Pool
98        pywsgi.WSGIServer(address, app, spawn=workers and Pool(
99            int(options.workers)) or 'default', log=None).serve_forever()
100
101    @staticmethod
102    def bjoern(app, address, **options):
103        import bjoern
104        bjoern.run(app, *address)
105
106    @staticmethod
107    def tornado(app, address, **options):
108        import tornado.wsgi
109        import tornado.httpserver
110        import tornado.ioloop
111        container = tornado.wsgi.WSGIContainer(app)
112        server = tornado.httpserver.HTTPServer(container)
113        server.listen(address=address[0], port=address[1])
114        tornado.ioloop.IOLoop.instance().start()
115
116    @staticmethod
117    def twisted(app, address, **options):
118        from twisted.web import server, wsgi
119        from twisted.python.threadpool import ThreadPool
120        from twisted.internet import reactor
121        thread_pool = ThreadPool()
122        thread_pool.start()
123        reactor.addSystemEventTrigger('after', 'shutdown', thread_pool.stop)
124        factory = server.Site(wsgi.WSGIResource(reactor, thread_pool, app))
125        reactor.listenTCP(address[1], factory, interface=address[0])
126        reactor.run()
127
128    @staticmethod
129    def diesel(app, address, **options):
130        from diesel.protocols.wsgi import WSGIApplication
131        app = WSGIApplication(app, port=address[1])
132        app.run()
133
134    @staticmethod
135    def gunicorn(app, address, **options):
136        options = {}
137        from gunicorn.app.base import Application
138        config = {'bind': "%s:%d" % address}
139        config.update(options)
140        sys.argv = ['anyserver.py']
141
142        class GunicornApplication(Application):
143            def init(self, parser, opts, args):
144                return config
145
146            def load(self):
147                return app
148        g = GunicornApplication()
149        g.run()
150
151    @staticmethod
152    def eventlet(app, address, **options):
153        from eventlet import wsgi, listen
154        wsgi.server(listen(address), app)
155
156    @staticmethod
157    def mongrel2(app, address, **options):
158        import uuid
159        sys.path.append(os.path.abspath(os.path.dirname(__file__)))
160        from mongrel2 import handler
161        conn = handler.Connection(str(uuid.uuid4()),
162                                  "tcp://127.0.0.1:9997",
163                                  "tcp://127.0.0.1:9996")
164        mongrel2_handler(app, conn, debug=False)
165
166    @staticmethod
167    def motor(app, address, **options):
168        #https://github.com/rpedroso/motor
169        import motor
170        app = motor.WSGIContainer(app)
171        http_server = motor.HTTPServer(app)
172        http_server.listen(address=address[0], port=address[1])
173        #http_server.start(2)
174        motor.IOLoop.instance().start()
175
176    @staticmethod
177    def pulsar(app, address, **options):
178        from pulsar.apps import wsgi
179        sys.argv = ['anyserver.py']
180        s = wsgi.WSGIServer(callable=app, bind="%s:%d" % address)
181        s.start()
182
183    @staticmethod
184    def waitress(app, address, **options):
185        from waitress import serve
186        serve(app, host=address[0], port=address[1], _quiet=True)
187
188
189def mongrel2_handler(application, conn, debug=False):
190    """
191    Based on :
192    https://github.com/berry/Mongrel2-WSGI-Handler/blob/master/wsgi-handler.py
193
194    WSGI handler based on the Python wsgiref SimpleHandler.
195    A WSGI application should return a iterable op StringTypes.
196    Any encoding must be handled by the WSGI application itself.
197    """
198    from wsgiref.handlers import SimpleHandler
199    try:
200        import cStringIO as StringIO
201    except:
202        import StringIO
203
204    # TODO - this wsgi handler executes the application and renders a page
205    # in memory completely before returning it as a response to the client.
206    # Thus, it does not "stream" the result back to the client. It should be
207    # possible though. The SimpleHandler accepts file-like stream objects. So,
208    # it should be just a matter of connecting 0MQ requests/response streams to
209    # the SimpleHandler requests and response streams. However, the Python API
210    # for Mongrel2 doesn't seem to support file-like stream objects for requests
211    # and responses. Unless I have missed something.
212
213    while True:
214        if debug:
215            print("WAITING FOR REQUEST")
216
217        # receive a request
218        req = conn.recv()
219        if debug:
220            print("REQUEST BODY: %r\n" % req.body)
221
222        if req.is_disconnect():
223            if debug:
224                print("DISCONNECT")
225            continue  # effectively ignore the disconnect from the client
226
227        # Set a couple of environment attributes a.k.a. header attributes
228        # that are a must according to PEP 333
229        environ = req.headers
230        environ['SERVER_PROTOCOL'] = 'HTTP/1.1'  # SimpleHandler expects a server_protocol, lets assume it is HTTP 1.1
231        environ['REQUEST_METHOD'] = environ['METHOD']
232        if ':' in environ['Host']:
233            environ['SERVER_NAME'] = environ['Host'].split(':')[0]
234            environ['SERVER_PORT'] = environ['Host'].split(':')[1]
235        else:
236            environ['SERVER_NAME'] = environ['Host']
237            environ['SERVER_PORT'] = ''
238        environ['SCRIPT_NAME'] = ''  # empty for now
239        environ['PATH_INFO'] = urllib.unquote(environ['PATH'])
240        if '?' in environ['URI']:
241            environ['QUERY_STRING'] = environ['URI'].split('?')[1]
242        else:
243            environ['QUERY_STRING'] = ''
244        if 'Content-Length' in environ:
245            environ['CONTENT_LENGTH'] = environ[
246                'Content-Length']  # necessary for POST to work with Django
247        environ['wsgi.input'] = req.body
248
249        if debug:
250            print("ENVIRON: %r\n" % environ)
251
252        # SimpleHandler needs file-like stream objects for
253        # requests, errors and responses
254        reqIO = StringIO.StringIO(req.body)
255        errIO = StringIO.StringIO()
256        respIO = StringIO.StringIO()
257
258        # execute the application
259        handler = SimpleHandler(reqIO, respIO, errIO, environ,
260                                multithread=False, multiprocess=False)
261        handler.run(application)
262
263        # Get the response and filter out the response (=data) itself,
264        # the response headers,
265        # the response status code and the response status description
266        response = respIO.getvalue()
267        response = response.split("\r\n")
268        data = response[-1]
269        headers = dict([r.split(": ") for r in response[1:-2]])
270        code = response[0][9:12]
271        status = response[0][13:]
272
273        # strip BOM's from response data
274        # Especially the WSGI handler from Django seems to generate them (2 actually, huh?)
275        # a BOM isn't really necessary and cause HTML parsing errors in Chrome and Safari
276        # See also: http://www.xs4all.nl/~mechiel/projects/bomstrip/
277        # Although I still find this a ugly hack, it does work.
278        data = data.replace('\xef\xbb\xbf', '')
279
280        # Get the generated errors
281        errors = errIO.getvalue()
282
283        # return the response
284        if debug:
285            print("RESPONSE: %r\n" % response)
286        if errors:
287            if debug:
288                print("ERRORS: %r" % errors)
289            data = "%s\r\n\r\n%s" % (data, errors)
290        conn.reply_http(
291            req, data, code=code, status=status, headers=headers)
292
293
294def run(servername, ip, port, softcron=True, logging=False, profiler=None,
295        options=None):
296    if servername == 'gevent':
297        from gevent import monkey
298        monkey.patch_all()
299    elif servername == 'eventlet':
300        import eventlet
301        eventlet.monkey_patch()
302
303    import gluon.main
304
305    if logging:
306        application = gluon.main.appfactory(wsgiapp=gluon.main.wsgibase,
307                                            logfilename='httpserver.log',
308                                            profiler_dir=profiler)
309    else:
310        application = gluon.main.wsgibase
311    if softcron:
312        from gluon.settings import global_settings
313        global_settings.web2py_crontype = 'soft'
314    getattr(Servers, servername)(application, (ip, int(port)), options=options)
315
316
317
318def main():
319    usage = "python anyserver.py -s tornado -i 127.0.0.1 -p 8000 -l -P"
320    try:
321        version = open('VERSION','r')
322    except IOError:
323        version = ''
324    parser = optparse.OptionParser(usage, None, optparse.Option, version)
325    parser.add_option('-l',
326                      '--logging',
327                      action='store_true',
328                      default=False,
329                      dest='logging',
330                      help='log into httpserver.log')
331    parser.add_option('-P',
332                      '--profiler',
333                      default=False,
334                      dest='profiler_dir',
335                      help='profiler dir')
336    servers = ', '.join(x for x in dir(Servers) if not x[0] == '_')
337    parser.add_option('-s',
338                      '--server',
339                      default='rocket',
340                      dest='server',
341                      help='server name (%s)' % servers)
342    parser.add_option('-i',
343                      '--ip',
344                      default='127.0.0.1',
345                      dest='ip',
346                      help='ip address')
347    parser.add_option('-p',
348                      '--port',
349                      default='8000',
350                      dest='port',
351                      help='port number')
352    parser.add_option('-w',
353                      '--workers',
354                      default=None,
355                      dest='workers',
356                      help='number of workers number')
357    (options, args) = parser.parse_args()
358    print('starting %s on %s:%s...' % (
359        options.server, options.ip, options.port))
360    run(options.server, options.ip, options.port,
361        logging=options.logging, profiler=options.profiler_dir,
362        options=options)
363
364if __name__ == '__main__':
365    main()
Note: See TracBrowser for help on using the repository browser.