Requirements
You'll need at least Python 2.5 to run Snakelets and Yaki.
Please consider installing a
sendfile(2)system call extension module such as the one available on Pypi, which will boost performance significantly when handling static files. Snakelets will automatically use it if it is installed.
Out-of-the-box startup
It is possible to start the Snakelets server out-of-the-box without changing anything. If you don't enable the virtual host feature, Snakelets will scan the webapps directory and will load all web applications it finds on the current host.
Yaki runs as the main app (named ROOT), which is used as the web application for the root context '/'.
You can just start the app.py script without configuring anything, and it will launch Snakelets and Yaki on port 9080, making it available to you alone on http://127.0.0.1:9080/.
A number of web apps that originally shipped with Snakelets are available under
webapps.disabledfor your perusal.
Apache
...is not needed: Snakelets contains its own multithreaded web server. But if you still want to use Apache, lighttpd or nginx, you can use mod_proxy or its equivalent for forwarding requests to a running Snakelets server behind it (which is the recommended configuration, since you may need to run additional services).
Edit your Apache config file. Make sure that mod_proxy is loaded in the LoadModule section.
Configure the forwarding to the Snakelets server:
ProxyRequests Off ProxyPass /snake/ http://localhost:9080/snake/ ProxyPassReverse /snake/ http://localhost:9080/snake/
This example forwards all requests starting with /snake/ to Snakelets. You can also configure a dedicated virtual host, for example:
<VirtualHost snakelets.host.domain> ServerName snakelets.host.domain ProxyRequests Off ProxyPass / http://localhost:9080/ ProxyPassReverse / http://localhost:9080/ </VirtualHost>
Now all requests to this hostname will be passed to Snakelets. To make sure Snakelets understands this when it generates URLs internally, you should edit app.py to read:
bindname='localhost' serverURLprefix='/snake/' externalPort=80
If you use the virtualhost mapping, the serverURLprefix should be set to an empty string.
Finally, if you are using mod_cache, you should tell it to not cache Snakelets urls, which can be done by the following:
<IfModule mod_cache.c> CacheDisable /snake </IfModule>
lighttpd
The following is a simplified example of a lighttpd configuration for Yaki:
## modules that are generally useful
server.modules = (
...
"mod_proxy",
"mod_rewrite",
"mod_redirect",
...
)
## a domain.com vhost with reverse proxy to Yaki
$HTTP["host"] =~ "^(((the|www).)?domain.com)$" {
## the actual proxy entry
$HTTP["url"] =~ "^/*" {
proxy.server = ( "" => ( ( "host" => "127.0.0.1", "port" => 9080 ) ) )
}
## a few sample redirects that usually come in handy
url.redirect = (
"^/?$" => "http://%1/space/",
"^/space$" => "http://%1/space/"
)
}
nginx
And the same for nginx:
server {
listen 80 default; ## listen for ipv4
listen [::]:80 default ipv6only=on; ## listen for ipv6
server_name _; ## our default
server_name_in_redirect off;
# usual defaults
index index.html;
sendfile on;
tcp_nodelay on;
keepalive_timeout 75 20;
# send these to Yaki
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_connect_timeout 90;
proxy_send_timeout 90;
proxy_read_timeout 90;
proxy_buffers 32 4k;
gzip on;
gzip_min_length 1000;
gzip_proxied expired no-cache no-store private auth;
gzip_types text/plain application/xml text/html text/css text/javascript application/x-javascript application/javascript;
gzip_disable "MSIE [1-6]\.";
client_max_body_size 50m;
client_body_buffer_size 128k;
# the actual proxying
location = / {
proxy_pass http://127.0.0.1:9080;
}
# sample rewrites again
rewrite ^/?$ /space break;
}
Varnish
This is a somewhat more elaborated example, since Varnish is a more sophisticated beast:
# we assume Yaki will be the default back-end
backend default {
.host = "127.0.0.1";
.port = "9080";
}
# redefine the receive subroutine to forward the original client IP
# and help somewhat with the default static paths
sub vcl_recv {
if (req.http.x-forwarded-for) {
set req.http.X-Forwarded-For =
req.http.X-Forwarded-For ", " client.ip;
} else {
set req.http.X-Forwarded-For = client.ip;
}
# these are where there are more static assets
if (req.request == "GET" && (
req.url ~ "^/themes/" ||
req.url ~ "^/media/" ||
req.url ~ "^/static/" ||
req.url ~ "^/attachment/"
)) {
unset req.http.cookie;
unset req.http.Authorization;
return (lookup);
}
if (req.request != "GET" &&
req.request != "HEAD" &&
req.request != "PUT" &&
req.request != "POST" &&
req.request != "TRACE" &&
req.request != "OPTIONS" &&
req.request != "DELETE") {
/* Non-RFC2616 or CONNECT which is weird. */
return (pipe);
}
if (req.request != "GET" && req.request != "HEAD") {
/* We only deal with GET and HEAD by default */
return (pass);
}
if (req.http.Authorization || req.http.Cookie) {
/* Not cacheable by default */
return (pass);
}
return (lookup);
}
Virtual Hosts
To enable this feature you have tell the server what web applications to load and to what host names they must be bound in webapps/__init__.py (the webapp module init file), which contains the following configuration items:
ENABLED- set this toTrueto enable virtual hosts. Setting it toFalsedisables this feature and reverts back to out-of-the-box startup (see above).defaultenabledwebapps- a list of webapps that will be loaded for the default config (if vhosts is disabled). Use['*']as a wildcard to enable all available webapps.virtualhosts- a mapping of host names to a sequence of web application names that will be connected to the specified hostname. If a web application is not mentioned for any virtual host, it won't be loaded. A web app may be connected to multiple vhosts.webroots- a mapping of host names to the name of the web app that will be mapped in the URL root ( '/' ) of the server on that virtual host. The web root hosts must be known virtualhosts specified invirtualhosts.aliases- a mapping of vhost-alias name to real-vhost name (this avoids duplicate loading of webapps).defaultvhost- the name of the default virtual host that will be used when the browser doesn't send a 'Host' header.
Every vhost can have a different list of webapps that are deployed on it, but a webapp can also be deployed on multiple vhosts at the same time. However, all deployed instances will be separate, unrelated instances of the webapp - if you deploy a webapp on multiple vhosts, it will be created for each vhost, and the init function will be invoked once for every copy.
Web applications that you configured in the virtual host config are installed automatically, whereas any other web applications in the webapps directory are ignored.
Startup Parameters
When you run app.py, it instantiates the internal multi-threaded web server in snakeserver.server.main with the following parameters:
HTTPD_PORT- where it will listen locally (default=9090). Note: on most operating systems you have to be root (have admin rights) to be able to use ports below 1024.externalPort- where the server is visible from the outside world (default=same asHTTPD_PORT). If you're running behind a forwarding proxy you may need to set this.bindname- hostname the server will bind on, None (default) means only the current host.serverURLprefix- URL prefix for all urls that this server uses (for instance,/snakelets). Default is '' (empty). Slashes will be added/stripped automatically if required.debugRequests- print incoming requests and headers (defaults to False).precompileYPagesto find possible errors early? Default is True (boolean). You may want to set this to False to allow faster startup times, but then you won't find out if an Ypage can't compile until the page is actually requested.writePageSource- should generated Ypage source code be written to a file in thetmpdirectory? Default is False (boolean). You may want to set this to True for easier Ypage debugging.serverRootDir- root directory for the Snakelet server (i.e. the directory that contains the logging config, the webapps and userlibs directories etc). Default is None. If not specified, the current directory is used.runAsUser- username that you want the server process to run as (used if you need to start the server as root)runAsGroup- groupname that you want the server process to run as (used if you need to start the server as root)
Monitoring and Restarting
It is also possible to use the monitor.py script. This script is designed to run on Linux, and will check if the server is active. If it's not active (or hanging) the monitor script will restart the Snakelets server (as a daemon process in the background).
You can invoke the script from cron periodically to check and restart the server if necessary.
Logging
The app server uses the standard Python 2.3+ logging module to log messages. Log files appear in the var/log directory. Logging configuration is in the logging.cfg file. There are a few predefined loggers, some of which use log rotation:
Snakelets.loggeris the logger that is used for server messages to fileserver.log(rotating).Snakelets.logger.accesslogis used for logging the web server requests (Apache format) toaccess.log. The loglevel is set toNOTSET. If you set it toCRITICAL, no access logging is performed, which improves performance and can be helpful if you're running Snakelets behind a reverse proxy and/or have no need for HTTP logs.Snakelets.logger.stdoutandSnakelets.logger.stderrare the logger adapters for the standard output and standard error messages. These messages are printed on the console but are also written toserver_console.log.
You can use the logging facility in your own code by doing:
import logging log=logging.getLogger("Snakelets.logger") log.debug("my debug message")
User libraries / modules
If you want to use a library or module from within several webapps, you don't have to include it in every webapp directory. There is a special userlibs folder in which you can place modules and packages that you want to use. Snakelets adds this directory to the module search path, so you can import anything in it in your webapps without using nasty prefixes.
You can also easily upgrade libraries this way, just put the new version in userlibs instead of the older version and all your webapps that import it will instantly use the new code the next time you start the server.
Also, Yaki ships with a modified Snakelets version that will prepend userlibs to the module search path, thereby making it easy to overlay updated (or more stable) versions of system-level libraries.
