Download Install Tutorial Docs FAQ Tools WikiLicense Team IRC Planet Involvement Shop Book

root/branches/cherrypy-3.0.x/cherrypy/_cpmodpy.py

Revision 2044 (checked in by fumanchu, 2 weeks ago)

Fix for #844 (mod_python timeout w/ CherryPy 3.1).

  • Property svn:eol-style set to native
Line 
1 """Native adapter for serving CherryPy via mod_python
2
3 Basic usage:
4
5 ##########################################
6 # Application in a module called myapp.py
7 ##########################################
8
9 import cherrypy
10
11 class Root:
12     @cherrypy.expose
13     def index(self):
14         return 'Hi there, Ho there, Hey there'
15
16
17 # We will use this method from the mod_python configuration
18 # as the entry point to our application
19 def setup_server():
20     cherrypy.config.update({'environment': 'production',
21                             'log.screen': False,
22                             'show_tracebacks': False})
23     cherrypy.tree.mount(Root())
24
25 ##########################################
26 # mod_python settings for apache2
27 # This should reside in your httpd.conf
28 # or a file that will be loaded at
29 # apache startup
30 ##########################################
31
32 # Start
33 DocumentRoot "/"
34 Listen 8080
35 LoadModule python_module /usr/lib/apache2/modules/mod_python.so
36
37 <Location "/">
38         PythonPath "sys.path+['/path/to/my/application']"
39         SetHandler python-program
40         PythonHandler cherrypy._cpmodpy::handler
41         PythonOption cherrypy.setup myapp::setup_server
42         PythonDebug On
43 </Location>
44 # End
45
46 The actual path to your mod_python.so is dependant of your
47 environment. In this case we suppose a global mod_python
48 installation on a Linux distribution such as Ubuntu.
49
50 We do set the PythonPath configuration setting so that
51 your application can be found by from the user running
52 the apache2 instance. Of course if your application
53 resides in the global site-package this won't be needed.
54
55 Then restart apache2 and access http://localhost:8080
56 """
57
58 import StringIO
59
60 import cherrypy
61 from cherrypy._cperror import format_exc, bare_error
62 from cherrypy.lib import http
63
64
65
66 # ------------------------------ Request-handling
67
68
69 def setup(req):
70     # Run any setup function defined by a "PythonOption cherrypy.setup" directive.
71     options = req.get_options()
72     if 'cherrypy.setup' in options:
73         modname, fname = options['cherrypy.setup'].split('::')
74         mod = __import__(modname, globals(), locals(), [fname])
75         func = getattr(mod, fname)
76         func()
77    
78     cherrypy.config.update({'log.screen': False,
79                             "tools.ignore_headers.on": True,
80                             "tools.ignore_headers.headers": ['Range'],
81                             })
82    
83     if cherrypy.engine.state == cherrypy._cpengine.STOPPED:
84         cherrypy.engine.start(blocking=False)
85     elif cherrypy.engine.state == cherrypy._cpengine.STARTING:
86         cherrypy.engine.wait()
87    
88     def cherrypy_cleanup(data):
89         cherrypy.engine.stop()
90     try:
91         from mod_python import apache
92         # apache.register_cleanup wasn't available until 3.1.4.
93         apache.register_cleanup(cherrypy_cleanup)
94     except AttributeError:
95         req.server.register_cleanup(req, cherrypy_cleanup)
96
97
98 class _ReadOnlyRequest:
99     expose = ('read', 'readline', 'readlines')
100     def __init__(self, req):
101         for method in self.expose:
102             self.__dict__[method] = getattr(req, method)
103
104
105 recursive = False
106
107 _isSetUp = False
108 def handler(req):
109     from mod_python import apache
110     try:
111         global _isSetUp
112         if not _isSetUp:
113             setup(req)
114             _isSetUp = True
115        
116         # Obtain a Request object from CherryPy
117         local = req.connection.local_addr
118         local = http.Host(local[0], local[1], req.connection.local_host or "")
119         remote = req.connection.remote_addr
120         remote = http.Host(remote[0], remote[1], req.connection.remote_host or "")
121        
122         scheme = req.parsed_uri[0] or 'http'
123         req.get_basic_auth_pw()
124        
125         try:
126             # apache.mpm_query only became available in mod_python 3.1
127             q = apache.mpm_query
128             threaded = q(apache.AP_MPMQ_IS_THREADED)
129             forked = q(apache.AP_MPMQ_IS_FORKED)
130         except AttributeError:
131             bad_value = ("You must provide a PythonOption '%s', "
132                          "either 'on' or 'off', when running a version "
133                          "of mod_python < 3.1")
134            
135             threaded = options.get('multithread', '').lower()
136             if threaded == 'on':
137                 threaded = True
138             elif threaded == 'off':
139                 threaded = False
140             else:
141                 raise ValueError(bad_value % "multithread")
142            
143             forked = options.get('multiprocess', '').lower()
144             if forked == 'on':
145                 forked = True
146             elif forked == 'off':
147                 forked = False
148             else:
149                 raise ValueError(bad_value % "multiprocess")
150        
151         sn = cherrypy.tree.script_name(req.uri or "/")
152         if sn is None:
153             send_response(req, '404 Not Found', [], '')
154         else:
155             app = cherrypy.tree.apps[sn]
156             method = req.method
157             path = req.uri
158             qs = req.args or ""
159             sproto = req.protocol
160             headers = req.headers_in.items()
161             rfile = _ReadOnlyRequest(req)
162             prev = None
163            
164             redirections = []
165             while True:
166                 request = cherrypy.engine.request(local, remote, scheme)
167                 request.login = req.user
168                 request.multithread = bool(threaded)
169                 request.multiprocess = bool(forked)
170                 request.app = app
171                 request.prev = prev
172                
173                 # Run the CherryPy Request object and obtain the response
174                 try:
175                     response = request.run(method, path, qs, sproto, headers, rfile)
176                     break
177                 except cherrypy.InternalRedirect, ir:
178                     cherrypy.engine.release()
179                     prev = request
180                    
181                     if not recursive:
182                         if ir.path in redirections:
183                             raise RuntimeError("InternalRedirector visited the "
184                                                "same URL twice: %r" % ir.path)
185                         else:
186                             # Add the *previous* path_info + qs to redirections.
187                             if qs:
188                                 qs = "?" + qs
189                             redirections.append(sn + path + qs)
190                    
191                     # Munge environment and try again.
192                     method = "GET"
193                     path = ir.path
194                     qs = ir.query_string
195                     rfile = StringIO.StringIO()
196            
197             send_response(req, response.status, response.header_list, response.body)
198             cherrypy.engine.release()
199     except:
200         tb = format_exc()
201         cherrypy.log(tb)
202         s, h, b = bare_error()
203         send_response(req, s, h, b)
204     return apache.OK
205
206 def send_response(req, status, headers, body):
207     # Set response status
208     req.status = int(status[:3])
209    
210     # Set response headers
211     req.content_type = "text/plain"
212     for header, value in headers:
213         if header.lower() == 'content-type':
214             req.content_type = value
215             continue
216         req.headers_out.add(header, value)
217    
218     # Set response body
219     if isinstance(body, basestring):
220         req.write(body)
221     else:
222         for seg in body:
223             req.write(seg)
224
225
226
227 # --------------- Startup tools for CherryPy + mod_python --------------- #
228
229
230 import os
231 import re
232
233
234 def read_process(cmd, args=""):
235     pipein, pipeout = os.popen4("%s %s" % (cmd, args))
236     try:
237         firstline = pipeout.readline()
238         if (re.search(r"(not recognized|No such file|not found)", firstline,
239                       re.IGNORECASE)):
240             raise IOError('%s must be on your system path.' % cmd)
241         output = firstline + pipeout.read()
242     finally:
243         pipeout.close()
244     return output
245
246
247 class ModPythonServer(object):
248    
249     template = """
250 # Apache2 server configuration file for running CherryPy with mod_python.
251
252 DocumentRoot "/"
253 Listen %(port)s
254 LoadModule python_module modules/mod_python.so
255
256 <Location %(loc)s>
257     SetHandler python-program
258     PythonHandler %(handler)s
259     PythonDebug On
260 %(opts)s
261 </Location>
262 """
263    
264     def __init__(self, loc="/", port=80, opts=None, apache_path="apache",
265                  handler="cherrypy._cpmodpy::handler"):
266         self.loc = loc
267         self.port = port
268         self.opts = opts
269         self.apache_path = apache_path
270         self.handler = handler
271    
272     def start(self):
273         opts = "".join(["    PythonOption %s %s\n" % (k, v)
274                         for k, v in self.opts])
275         conf_data = self.template % {"port": self.port,
276                                      "loc": self.loc,
277                                      "opts": opts,
278                                      "handler": self.handler,
279                                      }
280        
281         mpconf = os.path.join(os.path.dirname(__file__), "cpmodpy.conf")
282         f = open(mpconf, 'wb')
283         try:
284             f.write(conf_data)
285         finally:
286             f.close()
287        
288         response = read_process(self.apache_path, "-k start -f %s" % mpconf)
289         self.ready = True
290         return response
291    
292     def stop(self):
293         os.popen("apache -k stop")
294         self.ready = False
295
Note: See TracBrowser for help on using the browser.

Hosted by WebFaction

Log in as guest/cpguest to create tickets