Serve static files in wsgi testing environment - python

I am building a wsgi application. The production environment is Apache mod_wsgi and configured appropriately. For development, I use wsgiref.simple_server to serve the wsgi app locally. However, I would like my dev server to serve static content also with minimal overhead. For purposes of answering the question, assume I want to serve static file "/favicon.ico".
I have come across the package "static": http://lukearno.com/projects/static/ but found the documentation a bit lacking, and haven't been able to configure it to serve both static content and my application.
Here is a sample wsgi application.
from cgi import parse_qs
def application(environ, start_response):
qs = parse_qs(environ['QUERY_STRING'])
response_body, content_type = ('Hello World', ('Content-type', 'text/plain'))
content_length = 0
for s in response_body:
content_length += len(s)
status = '200 OK'
response_headers = [content_type,
('Content-Length', str(content_length))]
start_response(status, response_headers)
return response_body
if __name__ == '__main__':
from wsgiref.simple_server import make_server
# Instantiate the WSGI web server.
httpd = make_server('192.168.1.1', # The host name.
8080, # A port number where to wait for the request.
application # Our application object name, in this case a function.
)
httpd.serve_forever()
Note: This may be a duplicate of unanswered question Serving static content with python wsgiref? from December. I wanted to add more than just a comment and did not want to dig up a dead thread.

Let me propose the werkzeug package. It comes with a SharedDataMiddleware that solves precisely this task.

Related

Allow urls containing "www." on openshift hosted website

I have a (very) simple flask app hosted on open-shift.
It has one route:
#app.route('/')
def display_content():
return render_template("content.html.jnj2")
and a simple wsgi file (as described in the open-shift flask setup tutorial):
from wsgiref.simple_server import make_server
httpd = make_server('localhost', 8051, application)
httpd.serve_forever()
This works fine when I navigate to "myappname-mydomain.rhcloud.com", but gives an "ERR_NAME_NOT_RESOLVED" when I navigate to "www.myappname-mydomain.rhcloud.com".
I've done some googling etc, can't see anyone else with a similar problem.. I'm not aware of having changed any open-shift settings or anything.
Your app-domain.rhcloud.com address that is provided by OpenShift does NOT include a cname for www.app-domain.rhcloud.com, that's why it's not working. You can use your app-domain.rhcloud.com, or you can map your own alias like example.com or www.example.com using this guide: https://developers.openshift.com/en/managing-domains-ssl.html#using-a-custom-domain

Setup gevent with Lighttpd, weird stuff

I have wary odd problem. I configured Lighttpd to pass /test to fastcgi backend.
just added this in config
fastcgi.server = ("/test" =>
("127.0.0.1" =>
(
"host" => "127.0.0.1",
"port" => 7101,
"docroot" => "/",
"check-local" => "disable"
)
)
)
Now, when i start flup example, and hit 127.0.0.1:80/test everything work fine. Tested uWSGI to, still fine.
flup example:
#!/usr/bin/env python
from flup.server.fcgi import WSGIServer
def myapp(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/plain')])
return ['Hello World']
WSGIServer(myapp, bindAddress = ('127.0.0.1',7101)).run()
Now, only problem is when I start gevent it won't work. Lighttpd mod_fastcgi says that backend just blocked.
Funny part is when I alter handler to return just string, cause WSGI require iterable, and hit 127.0.0.1:7101 from my browser it work as expected. This should be WSGIServer, how can it work this way?
Here is gevent code:
#!/usr/bin/python
"""WSGI server example"""
from gevent.wsgi import WSGIServer
def app(env, start_response):
start_response('200 OK', [('Content-Type','text/html')])
#return ["Hello World", StopIteration] # this is WSGI test, not working
return "Hello World"
# when set like this, frontend :80 still wont work (500 Internal error),
# but 127.0.0.1:7101 work like standard http
if __name__ == '__main__':
WSGIServer(('', 7101), app).serve_forever()
Bottom line is , why only gevent wont work in this setup, and both flup and uWSGI are working? Is there some secret setting not mention in official example here.
Because gevent.wsgi.WSGIServer is not a fcgi server, it's only http server. Your can proxy your requests from lighttpd to gevent as http, or use wsgi.
U can see that flup here states it SPEAK FastCGI (not HTTP), and uWSGI here says "Born as a WSGI-only server".
Now Gevent says here "Fast WSGI server based on libevent-http", that confused me, but then I try gunicorn, and it steel failed.
Then i found here "Gunicorn 'Green Unicorn' is a Python WSGI HTTP Server for UNIX". That means that gevent and gunicorn WSGI handlers are HTTP request not FastCGI, but ,as Fedor Gogolev said, for your handlers they are WSGI servers.
So for Flup and uWSGI u configure lighttpd (or any other web server) to use fastcgi module, but for gunicorn and gevent u use proxy module, and for them u don't have to use frontend at all!If don't have static stuff to serve or other reason u can omit frontend cause gunicorn state it is wary fast and stable.

Using WSGI on Twisted

Can I use Twisted and mod_wsgi together, to try some gain in performance?
Since I am not starting a reactor.listenTCP(...), how do I use the async methods of twisted?:
What I have tried:
> server.wsgi
def application(environ, start_response):
status = '200 OK'
output = 'Pong!'
response_headers = [('Content-type', 'text/plain'),
('Content-Length', str(len(output)))]
start_response(status, response_headers)
# How do I call a twisted async method from here?!
# like deferToThread(object.send, environ).
return [output]
resource = WSGIResource(reactor, reactor.getThreadPool(), application)
You can't.
If you want to use Twisted as your WSGI container, then use Twisted. If you want to use Apache, then use Apache. However, if you use Apache as your WSGI container, then you will not be able to use features from Twisted, because Twisted's event loop is not compatible with the way Apache does network I/O.
What you're doing in the code example is doubly meaningless, as WSGIResource is the glue between Twisted's HTTP server and WSGI; even if you could somehow jam Twisted into a running Apache HTTPD process via mod_wsgi, you would not need a WSGIResource, since apache would be filling that role.

Obtaining Client IP address from a WSGI app using Eventlet

I'm currently writing a basic dispatch model server based on the Python Eventlet library (http://eventlet.net/doc/). Having looked at the WSGI docs on Eventlet (http://eventlet.net/doc/modules/wsgi.html), I can see that the eventlet.wsgi.server function logs the x-forwarded-for header in addition to the client IP address.
However, the way to obtain this is to attach a file-like object (the default which is sys.stderr) and then have the server pipe that to that object.
I would like to be able to obtain the client IP from within the application itself (i.e. the function that has start_response and environ as parameters). Indeed, an environ key would be perfect for this. Is there a way to obtain the IP address simply (i.e. through the environ dictionary or similar), without having to resort to redirecting the log object somehow?
What you want is in the wsgi environ, specifically environ['REMOTE_ADDR'].
However, if there is a proxy involved, then REMOTE_ADDR will be the address of the proxy, and the client address will be included (most likely) in HTTP_X_FORWARDED_FOR.
Here's a function that should do what you want, for most cases (all credit to Sævar):
def get_client_address(environ):
try:
return environ['HTTP_X_FORWARDED_FOR'].split(',')[-1].strip()
except KeyError:
return environ['REMOTE_ADDR']
You can easily see what is included in the wsgi environ by writing a simple wsgi app and pointing a browser at it, for example:
from eventlet import wsgi
import eventlet
from pprint import pformat
def show_env(env, start_response):
start_response('200 OK', [('Content-Type', 'text/plain')])
return ['%s\r\n' % pformat(env)]
wsgi.server(eventlet.listen(('', 8090)), show_env)
And combining the two ...
from eventlet import wsgi
import eventlet
from pprint import pformat
def get_client_address(environ):
try:
return environ['HTTP_X_FORWARDED_FOR'].split(',')[-1].strip()
except KeyError:
return environ['REMOTE_ADDR']
def show_env(env, start_response):
start_response('200 OK', [('Content-Type', 'text/plain')])
return ['%s\r\n\r\nClient Address: %s\r\n' % (pformat(env), get_client_address(env))]
wsgi.server(eventlet.listen(('', 8090)), show_env)

uwsgi + python + nginx + willy nilly file execution

I'm using uwsgi on Nginx to run some Python code.
I'd like to bind uwsgi to a directory and make it render any .py file that I call from the server in the browser. I'm thinking like PHP, here (/index.php executes that file, /login.php executes that file).
Is this a possibility? Or am I only able to explicitly specify a single module/app/file in uwsgi?
Here is my init syntax:
/opt/uwsgi/uwsgi -s 127.0.0.1:9001 -M 4 -t 30 -A 4 -p 4 -d /var/log/uwsgi.log --pidfile /var/run/uwsgi.pid --pythonpath /srv/www
I thought that would allow /srv/www to act as the folder where any .py files are executed.
Here is my nginx config:
server {
listen 80;
server_name DONT_NEED_THIS;
access_log /srv/www/logs/access.log;
error_log /srv/www/logs/error.log;
location / {
root /srv/www;
# added lines
include uwsgi_params;
uwsgi_pass 127.0.0.1:9001;
}
As it stands, when I try to call web root (ie www.site.com/) I get a:
wsgi application not found
With the following index.py file:
import sys
import os
sys.path.append(os.path.abspath(os.path.dirname(__file__)))
def application(environ, start_response):
status = '200 OK'
output = 'Hello World!'
response_headers = [('Content-type', 'text/plain'),
('Content-Length', str(len(output)))]
start_response(status, response_headers)
return [output]
Any ideas?
Thanks!
WSGI is not like PHP. You can't just point uwsgi to a directory with a bunch of .py files. In fact, never, ever make your python modules available in a public directory, accessible from the server. You need to hook uwsgi up to a WSGI application, preferably a framework. Read more about WSGI here. Check out bottle which is small, simple WSGI framework. It has great docs, and it's easy to get started with. There are actually tons of great web frameworks for Python though, so feel free to look around :)
You may want to read this thread:
http://lists.unbit.it/pipermail/uwsgi/2011-March/001657.html

Categories