I'm trying to launch a webserver serving several sites under several subdomains.
I'm using Pythen with webapp2 and paste. My Server is behind a router that asigns a static IP adress to the server and forwards port 80. The router itself has no static IP adress assigned by my ISP so I'm using DDNS (lets say example.dlinkddns.com) .
In my folder hierarchy each folder represents a subdomain and is a python module.
Like this:
server/app.py
server/www
server/www/__init__.py
server/test
server/test/__init__.py
they should be reachable via www.mydomain.com and test.mydomain.com
I set *.mydomain.com to be a CNAME for example.dlinkddns.com
This is server/app.py:
import sys
import os
import webapp2
from webapp2_extras import routes
from paste import httpserver
DOMAIN = 'mydomain.com'
class Fallback(webapp2.RequestHandler):
def get(self, *args, **kw):
self.response.write('Fallback...\n'+str(args)+'\n'+str(kw))
def main():
dirs = [name for name in os.listdir(".") if os.path.isdir(name)]
dirs.remove('env') # folder created by package virtualenv - needed for paste
rs = []
for subdomain in dirs:
# import subdomain package
exec('import '+subdomain)
# add routes defined for subdomain
exec('rs += [routes.DomainRoute("'+subdomain+'.'+DOMAIN+'", '+subdomain+'.routes)]')
rs += [routes.DomainRoute("<domain:.*>",[webapp2.Route('/<:.*>',Fallback,name='fallback')])]
app = webapp2.WSGIApplication(rs)
httpserver.serve(app, host='0.0.0.0', port='80')
if __name__ == '__main__':
main()
And this is how my www package looks like:
__init__.py
import webapp2
class MainMain(webapp2.RequestHandler):
def get(self,target):
self.response.write('Hello, webapp2! www.mydomain.com at:'+target)
routes = [
webapp2.Route('/<target>', handler=MainMain, name='main-main')
]
The problem now is, when I visit www.mydomain.com the fallback handler seems to match but MainMain handler should.
The output is
Fallback... () {'domain':'0.0.0.0'}.
It looks like my app wouldn't recognize the domains at all. Same thing happens when I visit example.dlinkddns.com.
I also tried it without the fallback handler but then it only showed me the 404 page for every route and every domain...
I also tried to start the server with
httpserver.serve(app, host='192.168.178.33', port='80')
This is the static assigned IP address of my server in my LAN. The the output changes to
Fallback... () {'domain':'192.168.178.33'}.
What am I doing wrong? Thanks for any help!
Related
Let us create an application server and an admin server. Assume that fusionListener and adminListener contain the application and admin logic we want to expose.
from cherrypy._cpserver import Server
fserver = Server()
fserver.socket_port = 10000
fserver.subscribe()
aserver = Server()
aserver.socket_port = 10001
aserver.subscribe()
And then to start them:
cherrypy.engine.start()
cherrypy.engine.block()
The tree.mount parameters ask for:
the code/ business logic as the first parameter
listening url
config parameters
Here is how that looks for the above servers:
cherrypy.tree.mount(fusionListener, r"/fusion.*",fusionConf)
cherrypy.tree.mount(adminListener, r"/admin.*",adminConf)
But where is the parameter for the server itself - which includes the port being listened to?
This is not a well supported case for CherryPy.
The application selection (cherrypy.tree is basically a map of /path -> App) is done before the request dispatch and... long story short, you could use cherrypy.dispatch.VirtualHost and map you sub applications under a main one (that will route depending on the hostname (which the port can be part of). For the listening on multiple ports, can be done, but again this is a very custom arrangement.
I hope this example is illustrative of a possible way to make such feat:
import cherrypy
from cherrypy import dispatch
from cherrypy._cpserver import Server
class AppOne:
#cherrypy.expose
def default(self):
return "DEFAULT from app ONE!"
#cherrypy.expose
def foo(self):
return "FOO from app ONE"
class AppTwo:
#cherrypy.expose
def default(self):
return "DEFAULT from app TWO!"
#cherrypy.expose
def foo(self):
return "FOO from app TWO"
class Root:
def __init__(self):
self.one = AppOne()
self.two = AppTwo()
def bind_two_servers(app_one_port, app_two_port):
# unsubscribe the default server
cherrypy.server.unsubscribe()
s1 = Server()
s2 = Server()
s1.socket_port = app_one_port
s2.socket_port = app_two_port
# subscribe the server to the `cherrypy.engine` bus events
s1.subscribe()
s2.subscribe()
def start_server():
bind_two_servers(8081, 8082)
cherrypy.engine.signals.subscribe()
cherrypy.engine.start()
cherrypy.engine.block()
config = {
'/': {
'request.dispatch': dispatch.VirtualHost(**{
'localhost:8081': '/one',
'localhost:8082': '/two',
})
}
}
cherrypy.tree.mount(Root(), '/', config)
start_server()
This example will serve AppOne when coming from localhost:8081 and AppTwo when coming from localhost:8082.
The problem is that you can't do multiples cherrypy.tree.mount and expect to route into the different applications using the VirtualHost dispatcher, it assumes that the application resolution is done at that point and is only resolving the path of that application.
Having said all of that... I do not recommend this solution, it can get complicated and it would be better to have some other server in front (like nginx) and serve each path on different processes. This could be an alternative, only if you really really want to avoid any extra server or process in your setup.
I'm using python with tornado webserver.
the application works fine but I can't find a way to prevent the user on accessing server files directly via url.
for example I have the following files in the server:
program.py
index.html
main.html
i wanted to prevent the user from accessing the above server files directly via web url
ex: localhost:8080/program.py or /index.html
i only wanted them to access localhost:8080/ or /home
Thanks in advance
from ws4py.client.tornadoclient import TornadoWebSocketClient
import tornado.ioloop
import tornado.web
import tornado.websocket
import tornado.template
SETTING_CLIENT_LISTEN_PORT = 8080
class MainHandler(tornado.web.RequestHandler):
def get(self):
try:
loader = tornado.template.Loader(".")
self.write(loader.load("index.html").generate())
except Exception as e:
print("exception occured", e)
class CWSHandler(tornado.websocket.WebSocketHandler):
global waiters
def open(self):
print('###FUNCTION CWSHandler.open(self) start')
def on_close(self):
print('###FUNCTION CWSHandler.open(self) close')
def on_message(self, message):
print('###FUNCTION CWSHandler.on_message msg:', message)
settings = {
"cookie_secret": "bZJc2sWbQLKos6GkHn/VB9oXwQt8S0R0kRvJ5/xJ89E=",
"login_url": "/",
}
application = tornado.web.Application(handlers=[
(r'/', MainHandler),
(r'/cws', CWSHandler),
(r"/(.*)", tornado.web.StaticFileHandler,{'path':'./'})
], cookie_secret="bZJc2sWbQLKos6GkHn/VB9oXwQt8S0R0kRvJ5/xJ89E=")
if __name__ == "__main__":
server = tornado.httpserver.HTTPServer(application)
server.listen(SETTING_CLIENT_LISTEN_PORT)
try:
tornado.ioloop.IOLoop.instance().start()
server.stop()
except KeyboardInterrupt:
print("Keyboard interupt")
pass
finally:
server.stop()
tornado.ioloop.IOLoop.instance().stop()
The problem is with your urls, specifically:
(r"/(.*)", tornado.web.StaticFileHandler,{'path':'./'})
You have mapped r'/(.*)' to {'path': './'}, which is your project directory. So, if a request comes in like localhost:8080/program.py, it will be matched with this - /(.*) and tornado will then look for a file named program.py in your project directory. If it finds it there, it will serve that file.
You should keep all your static files in a separate directory called static (you can name it anything you want, though) inside your project dir. Then map this directory with the desired url.
Example:
(r"/(.*)", tornado.web.StaticFileHandler,{'path': 'static'})
Or better yet, serve that directory under a /static/ url instead of - .*.
(r"/static/(.*)", tornado.web.StaticFileHandler,{'path': 'static'})
I want to run 2 servers of cherry py on different ports and with a different application.
I manage to run them both, but how do i connect between the app's and the servers ?
I want to be able to go to
http://127.0.0.1:3141/
and get server1
and to
http://127.0.0.1:3145/
and get server2
cherrypy docs example for multiple servers is not good enough for me to understand how to do this.
here it is Multiple servers/ports
my code
import cherrypy
class App1(object):
#cherrypy.expose
def index(self):
return ('server1')
class App2(object):
#cherrypy.expose
def index(self):
return ('server2')
cherrypy.server.unsubscribe()
server1 = cherrypy._cpserver.Server()
server1.socket_port=3141
server1._socket_host="127.0.0.1"
server1.thread_pool=2
server1.subscribe()
server2 = cherrypy._cpserver.Server()
server2.socket_port=3145
server2._socket_host="127.0.0.1"
server2.thread_pool=2
server2.subscribe()
cherrypy.engine.start()
cherrypy.engine.block()
If your apps need to (or can) be served from different paths (e. g. http://127.0.0.1:3141/app1 and http://127.0.0.1:3145/app2), just use tree.mount() for each app. If both apps must be served from the root path, look at VirtualHost for an idea.
After tweaking for a while I was not able to mount each app to a specific port.
The behavior was that each port was responding to each app, so I added a tool to filter port on each app as shown in the example below:
import cherrypy
from cherrypy._cpserver import Server
#cherrypy.tools.register('before_handler')
def port_filter():
if cherrypy.request.app.root.request_port != cherrypy.request.local.port:
raise cherrypy.NotFound()
class App1(object):
def __init__(self, request_port):
self.request_port = request_port
#cherrypy.expose
#cherrypy.tools.port_filter()
def index(self):
return "Hello world from app1"
class App2(object):
def __init__(self, request_port):
self.request_port = request_port
#cherrypy.expose
#cherrypy.tools.port_filter()
def index(self):
return "Hello world from app2"
class MyServer(Server):
def __init__(self, host, port, thread_pool=10):
Server.__init__(self)
self.socket_port = port
self._socket_host = host
self.thread_pool = thread_pool
self.subscribe()
if __name__ == '__main__':
# Unsubscribe default server
cherrypy.server.unsubscribe()
# Setup server for each port
MyServer(host='0.0.0.0', port=80)
MyServer(host='0.0.0.0', port=8080)
# Mount applications to specific port
cherrypy.tree.mount(App1(80), "/app1")
cherrypy.tree.mount(App2(8080), "/app2")
# Start!
cherrypy.engine.start()
cherrypy.engine.block()
I think you need two different directory. In each directory/app you put a config file for the app.
For example i have something like this:
[global]
server.socket_host = "ip_address"
server.socket_port = 8080
server.thread_pool = 10
[/]
tools.staticdir.root = "/path/to/your/app"
tools.encode.on = True
tools.decode.on = True
Have a look here
I have a python app developed on Flask. Everything works fine offline, I tried deploying on CherryPy successfully too. Now, I'm trying to deploy the same on www.pythonanywhere.com.
Here's the deploy.py I use for deploying the Flask app on CherryPy
from cherrypy import wsgiserver
from appname import app
def initiate():
app_list = wsgiserver.WSGIPathInfoDispatcher({'/appname': app})
server = wsgiserver.CherryPyWSGIServer( ('http://username.pythonanywhere.com/'), app_list)
try:
server.start()
except KeyboardInterrupt:
server.stop()
print "Server initiated..."
initiate()
print "Ended"
I created a "manual configuration" app on pythonanywhere.com.
Here's the configuration file (username_pythonanywhere_com_wsgi.py):
import sys
path = '/home/username/appname'
if path not in sys.path:
sys.path.append(path)
import deploy
deploy.initiate()
Now I'm pretty sure that it "almost worked", because in the server logs I could see my "Server initiated..." message.
2013-09-27 09:57:16 +0000 username.pythonanywhere.com - *** Operational MODE: single process ***
Server initiated...
Now the problem, when I try to view my app username.pyhtonanywhere.com/about, it times out.
This I believe is caused due to incorrect port given while starting the CherryPy server (in deploy.py).
Could anyone please tell how I can properly initiate the CherryPy server?
Joe Doherty is right. You want something more like this in you wsgi file:
import sys
sys.path = [ <path to your web app> ] + sys.path
from cherrypy._cpwsgi import CPWSGIApp
from cherrypy._cptree import Application
from <your_web_app> import <your web app class>
config_path = '<path to your cherrypy config>'
application = CPWSGIApp(
Application(<your web app class>(), '', config = config_path)
I stuck everything that should be based on your particular app in <>s.
I've got the following minimal code for a CGI-handling HTTP server, derived from several examples on the inner-tubes:
#!/usr/bin/env python
import BaseHTTPServer
import CGIHTTPServer
import cgitb;
cgitb.enable() # Error reporting
server = BaseHTTPServer.HTTPServer
handler = CGIHTTPServer.CGIHTTPRequestHandler
server_address = ("", 8000)
handler.cgi_directories = [""]
httpd = server(server_address, handler)
httpd.serve_forever()
Yet, when I execute the script and try to run a test script in the same directory via CGI using http://localhost:8000/test.py, I see the text of the script rather than the results of the execution.
Permissions are all set correctly, and the test script itself is not the problem (as I can run it fine using python -m CGIHTTPServer, when the script resides in cgi-bin). I suspect the problem has something to do with the default CGI directories.
How can I get the script to execute?
My suspicions were correct. The examples from which this code is derived showed the wrong way to set the default directory to be the same directory in which the server script resides. To set the default directory in this way, use:
handler.cgi_directories = ["/"]
Caution: This opens up potentially huge security holes if you're not behind any kind of a firewall. This is only an instructive example. Use only with extreme care.
The solution doesn't seem to work (at least for me) if the .cgi_directories requires multiple layers of subdirectories ( ['/db/cgi-bin'] for instance). Subclassing the server and changing the is_cgi def seemed to work. Here's what I added/substituted in your script:
from CGIHTTPServer import _url_collapse_path
class MyCGIHTTPServer(CGIHTTPServer.CGIHTTPRequestHandler):
def is_cgi(self):
collapsed_path = _url_collapse_path(self.path)
for path in self.cgi_directories:
if path in collapsed_path:
dir_sep_index = collapsed_path.rfind(path) + len(path)
head, tail = collapsed_path[:dir_sep_index], collapsed_path[dir_sep_index + 1:]
self.cgi_info = head, tail
return True
return False
server = BaseHTTPServer.HTTPServer
handler = MyCGIHTTPServer
Here is how you would make every .py file on the server a cgi file (you probably don't want that for production/a public server ;):
import BaseHTTPServer
import CGIHTTPServer
import cgitb; cgitb.enable()
server = BaseHTTPServer.HTTPServer
# Treat everything as a cgi file, i.e.
# `handler.cgi_directories = ["*"]` but that is not defined, so we need
class Handler(CGIHTTPServer.CGIHTTPRequestHandler):
def is_cgi(self):
self.cgi_info = '', self.path[1:]
return True
httpd = server(("", 9006), Handler)
httpd.serve_forever()