I'm launching a tornado web server in python3, here's some simplified startup code:
import tornado.ioloop, tornado.web
root = os.path.dirname(__file__)
startPage = 'index.html'
class allStops(tornado.web.RequestHandler):
def get(self):
self.write('yo man')
def make_app():
return tornado.web.Application([
(r"/API/allStops", allStops),
(r"/(.*)", tornado.web.StaticFileHandler, {"path": root, "default_filename": startPage})
])
if __name__ == "__main__":
app = make_app()
app.listen(5005)
tornado.ioloop.IOLoop.current().start()
The webserver works fine. I have various files and folders around index.html. So when we open localhost:5005
in browser we see the contents of the index.html (a static website), and with the links in it can access all other things. I can also just access them directly by putting in URL.
That's where my problem lies. If this program is in launch.py
then I'm able to see the whole program from the browser through localhost:5050/launch.py
. I want to disallow that. I also want to prevent browser access to contents of a particular subfolder. But at the same time, I don't want to restrict access to all the other files and folders: the default allow-all state is good. How can I make this happen?
Subclass StaticFileHandler
and then validate the filenames in validate_absolute_path
method. Raise 404 or 403 errors for files you don't want to serve.
from tornado import web
from tornado.web import HTTPError
class MyStaticFileHandler(web.StaticFileHandler):
def validate_absolute_path(self, root, absolute_path):
if absolute_path.endswith('.py'):
# raise 403 Forbidden error for all files ending with .py
raise HTTPError(403)
return super().validate_absolute_path(root, absolute_path)
And then use this class in your url handler.
If you set debug=False
, then Tornado will automatically show an appropriate message related to the status code instead of the exception.
If you want to send a custom error template, you can do this:
...
def validate_absolute_path(self, root, absolute_path):
if absolute_path.endswith('.py'):
self.set_status(404)
# send a custom 404 response from an html file
with open("path/to/404.html", "r") as f:
self.write(f.read())
return None
return super().validate_absolute_path(root, absolute_path)