Starting the server


This page is part of the Snakelets documentation shipped with Yaki.

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.disabled for 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 to True to enable virtual hosts. Setting it to False disables 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 in virtualhosts.
  • 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 as HTTPD_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).
  • precompileYPages to 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 the tmp directory? 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.logger is the logger that is used for server messages to file server.log (rotating).
  • Snakelets.logger.accesslog is used for logging the web server requests (Apache format) to access.log. The loglevel is set to NOTSET. If you set it to CRITICAL, 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.stdout and Snakelets.logger.stderr are the logger adapters for the standard output and standard error messages. These messages are printed on the console but are also written to server_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.