Static files

Serve static files on disk

Project directory layout:

config
public
  favicon.ico
  robots.txt
  404.html
  500.html
  img
    myimage.png
  css
    mystyle.css
  js
    myscript.js
src
build.sbt

Xitrum automatically serves static files inside public directory. URLs to them are in the form:

/img/myimage.png
/css/mystyle.css
/css/mystyle.min.css

To refer to them:

<img src={publicUrl("img/myimage.png")} />

To serve normal file in development environment and its minimized version in production environment (mystyle.css and mystyle.min.css as above):

<img src={publicUrl("css", "mystyle.css", "mystyle.min.css")} />

To send a static file on disk from your action, use respondFile.

respondFile("/absolute/path")
respondFile("path/relative/to/the/current/working/directory")

To optimize static file serving speed, you can avoid unnecessary file existence check with regex filter. If request url does not match pathRegex, Xitrum will respond 404 for that request.

See pathRegex in config/xitrum.conf.

index.html fallback

If there’s no route (no action) for URL /foo/bar (or /foo/bar/), Xitrum will try to look for static file public/foo/bar/index.html (in the “public” directory). If the file exists, Xitrum will respond it to the client.

404 and 500

404.html and 500.html in public directory are used when there’s no matching route and there’s error processing request, respectively. If you want to use your own error handler:

import xitrum.Action
import xitrum.annotation.{Error404, Error500}

@Error404
class My404ErrorHandlerAction extends Action {
  def execute() {
    if (isAjax)
      jsRespond("alert(" + jsEscape("Not Found") + ")")
    else
      renderInlineView("Not Found")
  }
}

@Error500
class My500ErrorHandlerAction extends Action {
  def execute() {
    if (isAjax)
      jsRespond("alert(" + jsEscape("Internal Server Error") + ")")
    else
      renderInlineView("Internal Server Error")
  }
}

Response status is set to 404 or 500 before the actions are executed, so you don’t have to set yourself.

Serve resource files in classpath with WebJars convention

WebJars

WebJars provides a lot of web libraries that you can declare as a dependency in your project.

For example, if you want to use Underscore.js, declare in your project’s build.sbt like this:

libraryDependencies += "org.webjars" % "underscorejs" % "1.6.0-3"

Then in your .jade template file:

script(src={webJarsUrl("underscorejs/1.6.0", "underscore.js", "underscore-min.js")})

Xitrum will automatically use underscore.js for development environment and underscore-min.js for production environment.

The result will look like this:

/webjars/underscorejs/1.6.0/underscore.js?XOKgP8_KIpqz9yUqZ1aVzw

If you want to use the same file for both environments:

script(src={webJarsUrl("underscorejs/1.6.0/underscore.js")})

Dependencies of a dependency are automatically downloaded. If you see that the version you want is not selected (you can confirm by running sbt xitrum-package and see files in the created directory target/xitrum/lib), you can force it with dependencyOverrides. For example, if you see that jQuery 2.x is selected, but you want to support Internet Explorer 6, 7, or 8, you need to force jQuery 1.x like this:

dependencyOverrides += "org.webjars" % "jquery" % "1.11.3"

Save resource file inside .jar file with WebJars convention

If you are a library developer and want to serve myimage.png from your library, which is a .jar file in classpath, save myimage.png in your .jar file with WebJars convention, example:

META-INF/resources/webjars/mylib/1.0/myimage.png

To serve it:

<img src={webJarsUrl("mylib/1.0/myimage.png")} />

In both development and production environments, the URL will be:

/webjars/mylib/1.0/myimage.png?xyz123

Respond a file in classpath

To respond a file inside an classpath element (a .jar file or a directory), even when the file is not saved with WebJars convention:

respondResource("path/relative/to/the/classpath/element")

Ex:

respondResource("akka/actor/Actor.class")
respondResource("META-INF/resources/webjars/underscorejs/1.6.0/underscore.js")
respondResource("META-INF/resources/webjars/underscorejs/1.6.0/underscore-min.js")

Client side cache with ETag and max-age

Xitrum automatically adds Etag for static files on disk and in classpath.

ETags for small files are MD5 of file content. They are cached for later use. Keys of cache entries are (file path, modified time). Because modified time on different servers may differ, each web server in a cluster has its own local ETag cache.

For big files, only modified time is used as ETag. This is not perfect because not identical file on different servers may have different ETag, but it is still better than no ETag at all.

publicUrl and webJarsUrl automatically add ETag to the URLs they generate. For example:

webJarsUrl("jquery/2.1.1/jquery.min.js")
=> /webjars/jquery/2.1.1/jquery.min.js?0CHJg71ucpG0OlzB-y6-mQ

Xitrum also sets max-age and Expires headers to one year. Don’t worry that browsers do not pickup a latest file when you change it. Because when a file on disk changes, its modified time changes, thus the URLs generated by publicUrl and webJarsUrl also change. Its ETag cache is also updated because the cache key changes.

GZIP

Xitrum automatically gzips textual responses. It checks the Content-Type header to determine if a response is textual: text/html, xml/application etc.

Xitrum always gzips static textual files, but for dynamic textual responses, for overall performance reason it does not gzips response smaller than 1 KB.

Server side cache

To avoid loading files from disk, Xitrum caches small static files (not only textual) in memory with LRU (Least Recently Used) expiration. See small_static_file_size_in_kb and max_cached_small_static_files in config/xitrum.conf.