WSGI & Python subprocess - python

I would like to use WSGI (Web Server Gateway Interface) instead of CGI (Common Gateway Interface) because I've been reading that it's more efficient as it doesn't spawn a new process for each request.
Here is a hello world app using WSGI:
wsgi.py
def application(environ, start_response):
content = "Hello, World!"
status = '200 OK'
header = [('Content-type', 'text/html')]
start_response(status, header)
return [content.encode('utf-8')]
I would like to know if it's OK to separate wsgi.py from the application by doing a subprocess from wsgi.py to the application? So the WSGI hello world app will look something like this:
app.py
import json
response = {
'status': '200 OK',
'header': [('Content-type', 'text/html')],
'content': "Hello, World!"
}
response = json.loads(response)
print(response)
wsgi.py
import sys
import subprocess
import json
def application(environ, start_response):
# Do a subprocess to the hello world app
script = '/path/to/my/app.py'
arg1 = json.loads(environ)
response = subprocess.check_output([sys.executable, script, arg1])
response = json.dumps(response)
status = response['status']
header = response['header']
content = response['content']
start_response(status, header)
return [content.encode('utf-8')]
Does the subprocess from wsgi.py to the application defeat the purpose of using WSGI? Is this setup basically the same as using CGI where it spawns a new process for each request?

Related

Print or return the request arguments/form data

I am using wsgi-request-logger https://github.com/pklaus/wsgi-request-logger in a flask application and need it to also log the request parameters (ie. the arguments that would be sent with the request).
Using request.form or request.args doesn't work and returns -
RuntimeError: Working outside of request context.
val['params'] = url_decode(environ.get('QUERY_STRING', ''))
print val['params']
This does not work and returns MultiDict([]) (tried it in middleware and the views.py file, it returns the same thing for both cases).
if environ['REQUEST_METHOD'] == 'POST':
print parse_form_data(environ)[1]
This returns MultiDict[] too.
I don't get what I am missing here. Help would be great.
Code which calls the middleware. I edited the middleware a bit and changed the files name to request_logger_wsgi as Im testing it with a local clone right now.
#!flask/bin/python
from app import app
from request_logger_wsgi import WSGILogger, ApacheFormatters
from logging.handlers import TimedRotatingFileHandler
def application(environ, start_response):
response_body = 'The request method was %s' % environ['REQUEST_METHOD']
response_body = response_body.encode('utf-8')
response_headers = [('Content-Type', 'text/plain'),
('Content-Length', str(len(response_body)))]
start_response('200 OK', response_headers)
print response_body, "231321"
return [response_body]
handlers = [ TimedRotatingFileHandler('access.log', 'd', 7) , ]
app.wsgi_app = WSGILogger(app.wsgi_app, handlers, ApacheFormatters.format_log)
app.run(debug=True)
You should post more code of your application, otherwise it's very difficult to help.
You can't use Flask's request object in the WSGI layer. The wsgi-request-logger runs before Flask, that's why there is no request context yet.
You other code was probably run in a module and you used os.environ, which is different from the WSGI environment.
What you actually have to do is to create a custom formatter and tell
def query_formatter(status_code, environ, content_length):
return "{0} {1} {2}".format(dt.now().isoformat(), status_code,
environ.get('QUERY_STRING', ''))
And then set the formatter:
app = WSGILogger(application, handlers, query_formatter)
It would however be better to reuse one of the Apache formatters instead:
import requestlogger
def apache_query_formatter(status_code, environ, content_length):
return requestlogger.ApacheFormatters.format_NCSA_log(
status_code, environ, content_length) + environ.get('QUERY_STRING', '')
This formatter will use the NCSA format and append the query string. There are probably better formats for the log message, but this should get you started.

Python - stop WSGI application after specific request

I need to create an application that ends after receiving a specific request. I use wsgiref.simple_server and run handling request in separate thread.
There is my code:
from wsgiref.simple_server import make_server
import re
import threading
import urllib2
def webapp(environ, start_response):
path = environ.get('PATH_INFO', '').lstrip('/')
for regex, callback in urls:
match = re.search(regex, path)
if match is not None:
environ['app.url_args'] = match.groups()
return callback(environ, start_response)
return not_found(environ, start_response)
KEEP_RUNNING = True
srv = make_server('localhost', 8081, webapp)
def t_serve():
while KEEP_RUNNING:
srv.handle_request()
def main():
t = threading.Thread(target=t_serve)
t.start()
print 'Service is running.'
t.join()
def index(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/html')])
return ['Service works']
def t_stop_service():
print 'Service shutdown'
urllib2.urlopen('http://localhost:8081/')
def stop_service(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/html')])
global KEEP_RUNNING
KEEP_RUNNING = False
threading.Thread(target=t_stop_service).start()
return ['Service is stopping']
def not_found(environ, start_response):
"""Called if no URL matches."""
start_response('404 NOT FOUND', [('Content-Type', 'text/html')])
return ['<h1>Not Found</h1>']
urls = [
(r'^$', index),
(r'^stop/?$', stop_service)
]
if __name__ == '__main__':
main()
After I make request "stop" request processing ends, but the process of the program is not ending. How to fix it?
Depends on the WSGI server whether it will work, with good quality ones it usually doesn't for good reasons, but have you tried using sys.exit() from the request handler?
If you must, you could also use os._exit(1). This will cause Python to exit immediately, although it will not allow any cleanup code to run.

uWSGI AJAX, reading a request

Hi I'm trying to get an ajax response from a wsgi server behind nginx (if that matters). I think I'm having issues reading the request as it would seem the variable request_body_size is always 0; I get a return value of "No Request" in the alert box when I would like to see "test string".
I cant find many docs on this or examples of this working, so if anyone knows how to fix this I would be grateful.
I have this python code in the WSGI script:
import os, sys
from cgi import parse_qs
def application(environ, start_response):
try:
request_body_size = int(environ.get('CONTENT_LENGTH', 0))
except (ValueError):
request_body_size = 0
context = {}
if request_body_size != 0:
request_body = environ['wsgi.input'].read(request_body_size)
output = parse_qs(request_body)
else:
output = "No request"
status = '200 OK'
response_headers = [('Content-type', 'text/html'),('Content-Length', str(len(output)))]
start_response(status, response_headers)
return [output]
and this is the js making the request (jquery):
function test_request()
{
var test_string = "test string";
$.ajax({
type: 'GET',
url:"/ajax",
data: test_string,
success:function(result){alert(result)}}
);
}
The ajax() method converts the 'data' value to a QUERY_STRING when the method is GET. Using POST should address the issue (or you can read environ['QUERY_STRING'])

How to run make_server from the simple_server in python on multiprocess?

I have one simple program of wsgi.
from wsgiref.simple_server import make_server
import time
def application(environ, start_response):
response_body = 'Hello World'
status = '200 OK'
response_headers = [('Content-Type', 'text/plain'),
('Content-Length', str(len(response_body)))]
start_response(status, response_headers)
if environ['PATH_INFO'] != '/favicon.ico':
print "Time :", int(time.time())
if int(time.time()) % 2:
print "Even"
time.sleep(10)
else:
print "Odd"
return [response_body]
httpd = make_server('localhost', 8000, application)
httpd.serve_forever()
So as per the code if the timestamp is Even then it will send response after 10 second. But if the timestamp is Odd then it will send response directly without sleep.
So my question is if i will send 2 request and if first request will send the request in Even mode then my second request will be serve after completing first one.
I check the solution and found that 'multiprocesscan solve this problem. I set the apache configuration withmultiprocess. Then I get the response forOddwithout completingEven` request.
I check how to set the multiprocess with make_server method of simple_server module. When I run the python /usr/lib64/python2.7/wsgiref/simple_server.py I get the output and last few lines are
wsgi.errors = <open file '<stderr>', mode 'w' at 0x7f22ba2a1270>
wsgi.file_wrapper = <class wsgiref.util.FileWrapper at 0x1647600>
wsgi.input = <socket._fileobject object at 0x1569cd0>
wsgi.multiprocess = False
wsgi.multithread = True
wsgi.run_once = False
wsgi.url_scheme = 'http'
wsgi.version = (1, 0)
So I was search how to set this make_server multiprocess so make_server can handle more then 1 request if any request is in progress.
Thx in advance.
If you're using Apache/mod_wsgi than you don't need the make_server/serve_forever stuff. Apache will handle that for you (as it is the web server). It will handle the processes and run the application callback function.
Make sure your Apache and mod_wsgi configuration allows multiprocess/multithread. Good reference is available here

HTTPS to HTTP using CherryPy

Is it possible for CherryPy to redirect HTTP to HTTPS. Lets for example say the code below is http://example.com if someone visits via https://example.com I want them to be redirected to the plain HTTP URL (301 redirect maybe?) how do I accomplish this?
#!/usr/bin/env python
from pprint import pformat
from cherrypy import wsgiserver
def app(environ, start_response):
status = '200 OK'
response_headers = [('Content-type', 'text/plain')]
start_response(status, response_headers)
return [pformat(environ)]
server = wsgiserver.CherryPyWSGIServer(('0.0.0.0', 80), app)
try:
server.start()
except KeyboardInterrupt:
server.stop()
You can check the request.scheme if it is "https" then you can raise a redirect.
See https://github.com/cherrypy/cherrypy/blob/f185ecd005d7fdbafb0ed83b0e49f05ac76e43fd/cherrypy/_cprequest.py#L218
Andrew Cox's link is broken again, here's the updated link to it. I don't have enough points to comment on his answer, hence the new answer.
https://cherrypy.readthedocs.org/en/3.3.0/refman/_cprequest.html#cherrypy._cprequest.Request.scheme

Categories