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.
Related
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?
Below is my handler code where tornado is allowing to do get request where as getting method not allowed error.
i am missing something obivious?
class CustomerHandler(web.RequestHandler):
def get(self, customer_id):
data = retrieve_customer_data_from_customer_database(customer_id)
print(data)
self.write(data)
self.finish()
def put(self, data):
customer_data = data
data = json.loads(customer_data)
customer_id = customer_data['id']
update_customer_data(customer_id, data)
result_out = {}
result_out['status'] = True
self.write(json.dumps(result_out))
self.finish()
Check again indentation. Also, the data you are looking for is probably in body of the request. Here's a simple example:
import tornado.ioloop
import tornado.web
import json
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world")
def put(self):
body = json.loads(self.request.body)
# do some stuff here
self.write("{} your ID is {}".format(body['name'], body['id']))
if __name__ == "__main__":
application = tornado.web.Application([
(r"/", MainHandler),
])
application.listen(8888)
tornado.ioloop.IOLoop.current().start()
And the test:
$ curl http://localhost:8888/ -XPUT -d '{"id": 123, "name": "John"}'
John your ID is 123
The issue was there was extra "/" I was using in put request url while calling from frontend, that's why method not allowed error was there. although error message doesn't suggest what exactly error is.
Hope this will help someone.
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.
I am attempting to have the url input be (some kind of url)/page/(whatever page number of the template we want). I am having trouble with this, and am not sure what the issue is. The first part of my code goes as follows:
from wsgiref.simple_server import make_server
from wsgiref.util import setup_testing_defaults
routing_table = {}
def route(url, func):
routing_table[url] = func
def find_path(url):
if url in routing_table:
return routing_table[url]
else:
return None
def app(environ, start_response):
setup_testing_defaults(environ)
handler = find_path(environ['PATH_INFO'])
if handler is None:
status = '404 Not Found'
body = "<html><body><h1>Page Not Found</h1></body></html>"
else:
status = '200 OK'
body = handler()
headers = [('Content-type', 'text/html: charset=utf-8')]
start_response(status, headers)
return [body.encode("utf-8")]
def run(ip, port):
myserver = make_server(ip, port, app)
print("Serving testings of wsgi at http://%s:%s" % (ip, port))
myserver.serve_forever()
The next part of the code is where I believe the main issue is occurring at page(page_id):
import test
import re
def index():
return "This is the main page"
def hello():
return "Hi, how are you?"
def page(page_id):
return "This is page number: %d" % page_id
if __name__ == '__main__':
test.route("/", index)
test.route("/Hello", hello)
test.route('/page/<page_id>', page)
test.run("127.0.0.1", 8000)
My thinking is that we need to import the template, and have the logic defined within the templates, themselves. However, when I attempt to do this, I am unable to "from python import Template" and utilize the template(myTemplates.tpl). I believe my syntax may be incorrect, but python.org has shown no suggestions, thus far.
In find_path you are simply comparing the given string to one of the URLs on the routing table
if url in routing_table:
So the only page you could actually reach for the '/page/<page_id>' route is the literal '/page/<page_id>'.
What you would need to do is parse the URL to see if it matches the format you are passing in, not compare the static string. Makes sense right?
In that case you might wanna look into regular expressions: https://docs.python.org/2/library/re.html
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