I wrote a flask app as kind of a proxy, to analyse the data passing through it and provide a web page where I get the result. All seems to went well when using the default development server that comes with flask, say using:
app.run()
But when I tried to deploy the app to a server, for example tornado or wsgiref.simple_server in Python standard library using:
from tornado.wsgi import WSGIContainer
from tornado.httpserver import HTTPServer
from tornado.ioloop import IOLoop
from app import app
http_server = HTTPServer(WSGIContainer(app))
http_server.listen(5000)
IOLoop.instance().start()
or
from wsgiref.simple_server import make_server
from app import app
httpd = make_server('', 5000, app)
httpd.serve_forever()
This two ways result in getting 404 error on requests got 200 OK previously.
The requests my app gets, since it serves as a proxy, are with absolute urls in the request lines like POST http://example.com/test HTTP/1.1. When I'm using the development server, this request is handled by the function registered under /test normally, something like:
#app.route('/test', methods=['GET', 'POST'])
def handle_test():
...
and the request.url, as I checked, is http://example.com/test.
When using the other two ways, the request is handled by the error handler with code 404, and the handle_test() function is never invoked. The request.url, which seems to cause the problem, is http://example.com/http://example.com/test, definitely not what I want.
So I want to know:
What changed the url to the wrong one, and when did this happen.
Why the app behaves differently on default development server and servers like tornado.
Of course, how to get rid of this problem.
Thanks!
Tornado does not currently support proxy-style requests; see https://github.com/tornadoweb/tornado/issues/1036
Tornado's WSGIContainer is also a poor choice for proxies because of its single-threaded concurrency model (see http://www.tornadoweb.org/en/stable/wsgi.html#tornado.wsgi.WSGIContainer). Even if the aforementioned bug were fixed, your proxy would perform poorly. I recommend either using a multithreaded WSGI server (like gunicorn or uwsgi, although I don't know whether they support proxy-style requests) or rewriting the proxy as a native Tornado app (without flask) to take advantage of asynchronous features.
Related
I'm setting up a Flask server, and want to use a Twisted ReverseProxyResource to proxy over another local server. In Flask, I have a current_user.is_authenticated boolean which I use to protect pages. How can I lock the ReverseProxyResource using this variable, so that it cannot be accessed when a user is logged out? The twisted.web.guard module requires me to set up another HTTP authentication system entirely, and the library doesn't seem to have any other built-in solution.
I've set up some demo code, (based off a previous question) attempting to place the Flask server inside of the Twisted reactor. I'm using the ReverseProxyResource to allow for two-way communication with a Shellinabox server on port 4200.
from flask import Flask
from twisted.internet import reactor
from twisted.web.proxy import ReverseProxyResource
from twisted.web.resource import Resource
from twisted.web.server import Site
from twisted.web.wsgi import WSGIResource
app = Flask(__name__)
#app.route('/example')
def index():
return 'Flask within Twisted?'
flask_site = WSGIResource(reactor, reactor.getThreadPool(), app)
root = Resource()
site_example = ReverseProxyResource('localhost', 4200, b'')
root.putChild(b'ssh', site_example)
reactor.listenTCP(8081, Site(root))
reactor.run()
I'd be okay switching to another reverse proxy, but only Twisted has worked so far. I'm also not wanting to switch to the Klein framework due to what I already have established in Flask.
I am a new Flask user and i have a problem. I want to redirect all url from http to https but I am having this error:
The connection was reset
This is my Flask code:
#! /usr/bin/python
# -*- coding:utf-8 -*-
from flask import *
from OpenSSL import SSL
import psycopg2
import os
from datetime import timedelta
import sys
from flask_sslify import SSLify
reload(sys)
sys.setdefaultencoding('utf8')
db_conn = psycopg2.connect("dbname=billjobs host=192.168.42.96 port=50434 user=username password=password")
app = Flask(__name__)
db = db_conn.cursor()
app.permanent_session_lifetime = timedelta(seconds=900)
sslify = SSLify(app)
app.secret_key='\xatYK\x1ba\x1dz\xa6-D\x9d\x97\x83\xfa\xcf\xcbd\xfa\xfb\x1a|\x08\x1af'
context = ('ssl.crt','ssl.key')
#app.route('/')
def pre_log():
return render_template('index.html')
if __name__ == '__main__':
app.run(host="192.168.42.186", ssl_context=context, debug=False)
If I enter directly the address https://192.168.42.186:5000 it work but with http only its not
Thanks for helping me in advance
You cannot do this using ssl_context and Werkzung (default server of Flask) now. A functionality to allow this was proposed and rejected in 2014: auto http to https redirect; citing:
That requires running another HTTP server. Werkzeug is not capable of that and IMO it's out of scope. run_simple should only be used for development anyway.
So what's going on is your Flask application calls run_simple underneath, passing ssl_context and some other variables. SSLify has no impact on your routing as long as you use ssl_context, because sole presence of this variable makes Werkzung host only using https schema. To get redirection from http to https, you need either to set up another server, listening at http and redirecting to https or migrate to other, more advanced server which allows redirection easily.
I recommend to migrate to Apache or gunicorn. Flask provides comprehensive instructions on deployment: Deployment Options. Keep in mind that built-in server of Flask (Werkzung) is not suitable for production, as authors of Flask write:
While lightweight and easy to use, Flask’s built-in server is not
suitable for production as it doesn’t scale well and by default serves
only one request at a time.
Using Apache you could redirect all http requests using VirtualHost rule, listening at 80:
<VirtualHost *:80>
ServerName mysite.example.com
DocumentRoot /usr/local/apache2/htdocs
Redirect /secure https://mysite.example.com/secure
</VirtualHost>
See more on this on Redirect Request to SSL Apache wiki.
Is it possible to have a single flask app with routes on two different ports? My Flask app needs to listen for webhooks and due to some security biz it can't receive foreign POST requests on the default port. Is it possible to do something like this?
#app.route('/hook/<sourcename>', methods=["POST"], port=5051)
def handle_hook(sourcename):
print 'asdf'
If you don't need any socket code inside C plugins, gevent could help, e.g. with
import gevent
from gevent.pywsgi import WSGIServer
app = Flask(__name__)
https_server = WSGIServer((HOST, HTTPS_PORT), app, keyfile=PRIVKEY, certfile=CERT)
https_server.start()
http_server = WSGIServer((HOST, HTTP_PORT), app)
http_server.start()
while True:
gevent.sleep(60)
A server by default only listens to a single port. Wouldn't it make more sense, since the additional port requires additional functionality, to implement a front-end server on the second port that proxies the POST request locally? There are many well-documented ways to do this such as this one
I am starting with web development. I am trying to develop and webapp using the Instagram API and Django. I was looking that a lot of people it's using Tornado Web Server for Real Time Subscriptions. So I am using Webfaction as a host and found this code so I can wrap my Django project with the "WSGI Container" that Tornado Web Server provides:
import os
import tornado.httpserver
import tornado.ioloop
import tornado.wsgi
import tornado.web
import sys
import django.core.handlers.wsgi
sys.path.append('/path/to/project')
class HelloHandler(tornado.web.RequestHandler):
def get(self):
self.write('Hello from tornado')
def main():
os.environ['DJANGO_SETTINGS_MODULE'] = 'myproject.settings' # path to your settings module
wsgi_app = tornado.wsgi.WSGIContainer(django.core.handlers.wsgi.WSGIHandler())
tornado_app = tornado.web.Application(
[
('/hello-tornado', HelloHandler),
('.*', tornado.web.FallbackHandler, dict(fallback=wsgi_app)),
]
)
http_server = tornado.httpserver.HTTPServer(tornado_app)
http_server.listen(8080)
tornado.ioloop.IOLoop.instance().start()
if __name__ == "__main__":
main()
So I run this python script inside my Webfaction server and everytime I try to access "http://mywebsite.com/hello-tornado/" does not seem to work. I know I am running that Tornado web server on that port but do not know how too access from the browser or something like that. What I am doing wrong here? Thanks for your help and patience. Will cyber high-five for every answer.
EDIT: What I am really trying to do is that I want to receive all the calls from the subscriptions that I make with the Instagram RealTime Subscription API through Tornado, for that I have a callback url "http://mysite.com/sub" and I want to be able to receive through Tornado.
You are starting the server at port 8080, Web browsers use port 80 by default, try using: http://mywebsite.com:8080/hello-tornado
if you want to use port 80 and you already have a web server running in the box you can try following Ali-Akber Saifee suggestion, or run the WSGI application directly from the server, using something like mod_python (http://www.modpython.org), you will lose the ability to run Tornado code, but Django will work.
You have to create a custom app (listening on port), note the port that is assigned to your app then configure tornado to serve on that port: http_server.listen(my port)
You can also avoid tornado and start directly by installing a django app.
I want a simple python web server for the following use case:
I want to write a simple server that will accept HTTP requests from my application running on Google App Engine.
The server will accept HTTP requests, and then send iphone notifications. (Basically, I need this extra server to account for the lack of socket support in google app engine).
I guess I need the server to be able to maintain this persistent connection with Apple's Push Notification Service. So I'll need to have some sort of thread always open for this. So I need some sort of web server that can accept the request pass it off to the other thread with the persistent connection to APNS.
Maybe multiple processes and one of pythons queuing tools to communicate between them? Accept the HTTP request, then enqueue a message to the other process?
I was wondering what someone with a bit of experience would suggest. I'm starting to think that maybe even writing my own simple server is a good option (http://fragments.turtlemeat.com/pythonwebserver.php).
One option would be the (appropriately named) SimpleHTTPServer, which is part of the Python standard library. Another, more flexible but more complicated option would be to write your server in Twisted.
I've been writing simple http servers using gevent and bottle -- an example:
#!/usr/bin/env python
import gevent.monkey
gevent.monkey.patch_all()
import bottle
bottle.debug(True)
import gevent.wsgi
from bottle import route, run, request, response, static_file, abort
#route('/echo')
def echo():
s = request.GET.get('s', 'o hai')
return '<html><head><title>echo server</title></head><body>%s</body></html>\r\n' % (s)
#route('/static/:filename')
def send_static(filename):
root = os.getcwd() + '/static'
return static_file(filename, root=root)
if __name__ == '__main__':
app = bottle.app()
wsgi_server = gevent.wsgi.WSGIServer(('0.0.0.0', 8000), app)
print 'Starting wsgi search on port 8000'
wsgi_server.serve_forever()
So you could write a simple server that sticks a job into a Queue (see gevent.queue) and have another worker greenlet that handles reading requests from the queue and processing them...