
What is the Ring Server?
The first thing to know is that Ring and the Ring Server are not, I repeat, are not the same thing. Whereas Ring provides a suite of libraries which abstract the underlying implementation details, the Ring Server library provides the ability to start an actual web server to serve a Ring handler.

Whenever we use lein ring server
from the command line, we start the Ring Server (there are other ways, but we'll get to those later in this chapter, and in Chapter 11, Environment Configuration and Deployment). At startup, the Ring Server will execute any registered application initialization hooks, and then start an embedded Jetty server, which serves our application handler. Incoming requests are then processed by Ring, as described in the previous section, until we shut down our app. On shutdown, Ring stops the embedded Jetty server and executes any registered shutdown hooks.
We can see this in the interaction between our hipstr.handler
and hipstr.repl
namespaces. Let's examine the hipstr.handler
namespace, and then we'll see how the hipstr.repl
namespace uses it.
hipstr.handler
The hipstr.handler
namespace is the bootstrap to our application. In this, we define an initialization hook, a shutdown hook, a couple of application routes, and the application handler.
The hipstr.handler/init
defines the initialization hook to be invoke immediately before starting the embedded Jetty server. Typically, we add any kind of application runtime configuration and invoke any other components required for the duration of the application. For example, our application configures various Timbre logging appenders and initiates the session manager.
(defn init "init will be called once when app is deployed as a servlet on an app server such as Tomcat put any initialization code here" [] ;…snipped for brevity… )
This initialization hook is configured for use either in the project configuration or through the REPL (discussed later).
The hipstr.handler/destroy
defines the shutdown hook to invoke immediately before exiting. The application shutdown hook is a function that performs anything our application needs to do before it permanently exits, such as stopping the session manager, or emitting a log statement stating the application is shutting down:
(defn destroy "destroy will be called when your application shuts down, put any clean up code here" [] ;…snipped for brevity…)
The shutdown hook is configured for use either in the project configuration, or through the REPL (again, which we'll discuss shortly).
Routes are what tie a request to a specific Ring handler, that is, which URL should execute which chunk of code. The Compojure library provides us the tools to tie these two things together. We've already played with these earlier in the chapter, when we fooled around with the /About
route to execute the foo-response
handler.
The hipstr.handler/app-routes
defines two special defroutes
: route/resources
and route/not-found
.
(defroutes app-routes (route/resources "/") (route/not-found "Not Found"))
The (route/resources "/")
route defines the URL from which our static resources will be served (found in the hipstr/resources/public directory
). For example, the URL to our hipstr/resources/public/css/screen.css
file would simply be /css/screen.css
. If we changed our resources route to (route/resources "/artifacts/")
, then the URL to the same screen.css
file would be /artifacts/css/screen.css
.
The (route/not-found "Not Found")
defines what should be emitted for a 404 Not Found
HTTP response. The luminus
template simply defaults to Not Found
(in practice, you'll likely want to have a handler that renders something a little more pretty). Ring will take the "Not Found"
parameter and insert it into the <body>
element of an HTML document on our behalf.
The real meat of the hipstr.handler
namespace is the application handler. This is where we package together the various routes and middleware, define how we want our sessions to behave, any access rules to protected routes (for example, authenticated-only pages), and which formats should be serialized/deserialized to /from EDN
in the requests/responses. Let's define the application handler:
(def app (app-handler ;; add your application routes here [home-routes app-routes] ;; add custom middleware here :middleware (load-middleware) ;; timeout sessions after 30 minutes :session-options {:timeout (* 60 30) :timeout-response (redirect "/")} ;; add access rules here :access-rules [] ;; serialize/deserialize the following data formats ;; available formats: ;; :json :json-kw :yaml :yaml-kw :edn :yaml-in-html :formats [:json-kw :edn]))
The app handler
packages our entire application, which will receive the request maps from Ring, and return response maps. As we build on our hipstr example, we will be modifying this function to include our own routes, access rules, and so on.
hipstr.repl
The hipstr.handler
namespace defines what and how our application works, whereas the hipstr.repl
namespace actually consumes it and makes it all run through the Clojure REPL. The hipstr.repl
namespace is considerably more simple than the hipstr.handler
namespace; it merely consists of an atom (to store a Jetty server instance returned from the Ring Server library), a start-server
function, a stop-server
function, and a get-handler
function.
The hipstr.repl/start-server
function attempts to start the Ring Server on a given port, defaulting to port 3000. It also forwards the application handler (returned from get-handler)
along with any runtime options, to the underlying Jetty server (a full list of which is defined at https://github.com/weavejester/ring-server#usage). Here is the code for starting the Ring Server:
;… Snipped for brevity (defonce server (atom nil)) ;… (defn start-server "used for starting the server in development mode from REPL" [& [port]] (let [port (if port (Integer/parseInt port) 3000)] (reset! server (serve (get-handler) {:port port :init init ;#1 :auto-reload? true :destroy destroy ;#2 :join? false})) ;#3 (println (str "You can view the site at http://localhost:" port))))
The :init
and :destroy
keys at #1
and #2
configure the initialization and shutdown hooks, respectively. The :join?
option at #3
will determine if the thread will wait until the underlying Jetty instance stops. When set to false, the Ring Server will return the actual Jetty instance, which we keep a reference to in the server
atom. When running the server through the REPL, it's best to keep this option set to false
, thereby allowing us to stop the server without having to kill our REPL session.
The stop-server
function simply stops the retained Jetty instance, and then destroys it.
(defn stop-server [] (.stop @server) (reset! server nil))
The server is now magically stopped.
The get-handler
function returns our hipstr.handler/app
handler, exposes the static resources directory, and wraps the handler with one last bit of middleware, which adds a couple more headers to the response. The added middleware also returns a 304 Not Modified
response if it detects the document being served hasn't been modified since the previous request:
(defn get-handler [] ;; #'app expands to (var app) so that when we reload our code, ;; the server is forced to re-resolve the symbol in the var ;; rather than having its own copy. When the root binding ;; changes, the server picks it up without having to restart. (-> #'app ; Makes static assets in ; $PROJECT_DIR/resources/public/ available. (wrap-file "resources") ; Content-Type, Content-Length, and Last Modified headers ; for files in body (wrap-file-info)))