| | 1 | # Copyright (c) 2005, 2006 Allan Saddi <allan@saddi.com> |
|---|
| | 2 | # All rights reserved. |
|---|
| | 3 | # |
|---|
| | 4 | # Redistribution and use in source and binary forms, with or without |
|---|
| | 5 | # modification, are permitted provided that the following conditions |
|---|
| | 6 | # are met: |
|---|
| | 7 | # 1. Redistributions of source code must retain the above copyright |
|---|
| | 8 | # notice, this list of conditions and the following disclaimer. |
|---|
| | 9 | # 2. Redistributions in binary form must reproduce the above copyright |
|---|
| | 10 | # notice, this list of conditions and the following disclaimer in the |
|---|
| | 11 | # documentation and/or other materials provided with the distribution. |
|---|
| | 12 | # |
|---|
| | 13 | # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
|---|
| | 14 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|---|
| | 15 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
|---|
| | 16 | # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
|---|
| | 17 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
|---|
| | 18 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
|---|
| | 19 | # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
|---|
| | 20 | # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
|---|
| | 21 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
|---|
| | 22 | # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
|---|
| | 23 | # SUCH DAMAGE. |
|---|
| | 24 | # |
|---|
| | 25 | # $Id$ |
|---|
| | 26 | |
|---|
| | 27 | """ |
|---|
| | 28 | fcgi - a FastCGI/WSGI gateway. |
|---|
| | 29 | |
|---|
| | 30 | For more information about FastCGI, see <http://www.fastcgi.com/>. |
|---|
| | 31 | |
|---|
| | 32 | For more information about the Web Server Gateway Interface, see |
|---|
| | 33 | <http://www.python.org/peps/pep-0333.html>. |
|---|
| | 34 | |
|---|
| | 35 | Example usage: |
|---|
| | 36 | |
|---|
| | 37 | #!/usr/bin/env python |
|---|
| | 38 | from myapplication import app # Assume app is your WSGI application object |
|---|
| | 39 | from fcgi import WSGIServer |
|---|
| | 40 | WSGIServer(app).run() |
|---|
| | 41 | |
|---|
| | 42 | See the documentation for WSGIServer for more information. |
|---|
| | 43 | |
|---|
| | 44 | On most platforms, fcgi will fallback to regular CGI behavior if run in a |
|---|
| | 45 | non-FastCGI context. If you want to force CGI behavior, set the environment |
|---|
| | 46 | variable FCGI_FORCE_CGI to "Y" or "y". |
|---|
| | 47 | """ |
|---|
| | 48 | |
|---|
| | 49 | __author__ = 'Allan Saddi <allan@saddi.com>' |
|---|
| | 50 | __version__ = '$Revision$' |
|---|
| | 51 | |
|---|
| | 52 | import os |
|---|
| | 53 | |
|---|
| | 54 | from flup.server.fcgi_base import BaseFCGIServer, FCGI_RESPONDER, \ |
|---|
| | 55 | FCGI_MAX_CONNS, FCGI_MAX_REQS, FCGI_MPXS_CONNS |
|---|
| | 56 | from flup.server.singleserver import SingleServer |
|---|
| | 57 | |
|---|
| | 58 | __all__ = ['WSGIServer'] |
|---|
| | 59 | |
|---|
| | 60 | class WSGIServer(BaseFCGIServer, SingleServer): |
|---|
| | 61 | """ |
|---|
| | 62 | FastCGI server that supports the Web Server Gateway Interface. See |
|---|
| | 63 | <http://www.python.org/peps/pep-0333.html>. |
|---|
| | 64 | """ |
|---|
| | 65 | def __init__(self, application, environ=None, |
|---|
| | 66 | bindAddress=None, umask=None, multiplexed=False, |
|---|
| | 67 | debug=True, roles=(FCGI_RESPONDER,), **kw): |
|---|
| | 68 | """ |
|---|
| | 69 | environ, if present, must be a dictionary-like object. Its |
|---|
| | 70 | contents will be copied into application's environ. Useful |
|---|
| | 71 | for passing application-specific variables. |
|---|
| | 72 | |
|---|
| | 73 | bindAddress, if present, must either be a string or a 2-tuple. If |
|---|
| | 74 | present, run() will open its own listening socket. You would use |
|---|
| | 75 | this if you wanted to run your application as an 'external' FastCGI |
|---|
| | 76 | app. (i.e. the webserver would no longer be responsible for starting |
|---|
| | 77 | your app) If a string, it will be interpreted as a filename and a UNIX |
|---|
| | 78 | socket will be opened. If a tuple, the first element, a string, |
|---|
| | 79 | is the interface name/IP to bind to, and the second element (an int) |
|---|
| | 80 | is the port number. |
|---|
| | 81 | """ |
|---|
| | 82 | BaseFCGIServer.__init__(self, application, |
|---|
| | 83 | environ=environ, |
|---|
| | 84 | multithreaded=False, |
|---|
| | 85 | multiprocess=False, |
|---|
| | 86 | bindAddress=bindAddress, |
|---|
| | 87 | umask=umask, |
|---|
| | 88 | multiplexed=multiplexed, |
|---|
| | 89 | debug=debug, |
|---|
| | 90 | roles=roles) |
|---|
| | 91 | for key in ('jobClass', 'jobArgs'): |
|---|
| | 92 | if kw.has_key(key): |
|---|
| | 93 | del kw[key] |
|---|
| | 94 | SingleServer.__init__(self, jobClass=self._connectionClass, |
|---|
| | 95 | jobArgs=(self,), **kw) |
|---|
| | 96 | self.capability = { |
|---|
| | 97 | FCGI_MAX_CONNS: 1, |
|---|
| | 98 | FCGI_MAX_REQS: 1, |
|---|
| | 99 | FCGI_MPXS_CONNS: 0 |
|---|
| | 100 | } |
|---|
| | 101 | |
|---|
| | 102 | def _isClientAllowed(self, addr): |
|---|
| | 103 | return self._web_server_addrs is None or \ |
|---|
| | 104 | (len(addr) == 2 and addr[0] in self._web_server_addrs) |
|---|
| | 105 | |
|---|
| | 106 | def run(self): |
|---|
| | 107 | """ |
|---|
| | 108 | The main loop. Exits on SIGHUP, SIGINT, SIGTERM. Returns True if |
|---|
| | 109 | SIGHUP was received, False otherwise. |
|---|
| | 110 | """ |
|---|
| | 111 | self._web_server_addrs = os.environ.get('FCGI_WEB_SERVER_ADDRS') |
|---|
| | 112 | if self._web_server_addrs is not None: |
|---|
| | 113 | self._web_server_addrs = map(lambda x: x.strip(), |
|---|
| | 114 | self._web_server_addrs.split(',')) |
|---|
| | 115 | |
|---|
| | 116 | sock = self._setupSocket() |
|---|
| | 117 | |
|---|
| | 118 | ret = SingleServer.run(self, sock) |
|---|
| | 119 | |
|---|
| | 120 | self._cleanupSocket(sock) |
|---|
| | 121 | |
|---|
| | 122 | return ret |
|---|
| | 123 | |
|---|
| | 124 | def factory(global_conf, host=None, port=None, **local): |
|---|
| | 125 | import paste_factory |
|---|
| | 126 | return paste_factory.helper(WSGIServer, global_conf, host, port, **local) |
|---|
| | 127 | |
|---|
| | 128 | if __name__ == '__main__': |
|---|
| | 129 | def test_app(environ, start_response): |
|---|
| | 130 | """Probably not the most efficient example.""" |
|---|
| | 131 | import cgi |
|---|
| | 132 | start_response('200 OK', [('Content-Type', 'text/html')]) |
|---|
| | 133 | yield '<html><head><title>Hello World!</title></head>\n' \ |
|---|
| | 134 | '<body>\n' \ |
|---|
| | 135 | '<p>Hello World!</p>\n' \ |
|---|
| | 136 | '<table border="1">' |
|---|
| | 137 | names = environ.keys() |
|---|
| | 138 | names.sort() |
|---|
| | 139 | for name in names: |
|---|
| | 140 | yield '<tr><td>%s</td><td>%s</td></tr>\n' % ( |
|---|
| | 141 | name, cgi.escape(`environ[name]`)) |
|---|
| | 142 | |
|---|
| | 143 | form = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ, |
|---|
| | 144 | keep_blank_values=1) |
|---|
| | 145 | if form.list: |
|---|
| | 146 | yield '<tr><th colspan="2">Form data</th></tr>' |
|---|
| | 147 | |
|---|
| | 148 | for field in form.list: |
|---|
| | 149 | yield '<tr><td>%s</td><td>%s</td></tr>\n' % ( |
|---|
| | 150 | field.name, field.value) |
|---|
| | 151 | |
|---|
| | 152 | yield '</table>\n' \ |
|---|
| | 153 | '</body></html>\n' |
|---|
| | 154 | |
|---|
| | 155 | from wsgiref import validate |
|---|
| | 156 | test_app = validate.validator(test_app) |
|---|
| | 157 | WSGIServer(test_app).run() |