Skip to content

Latest commit

 

History

History
96 lines (78 loc) · 3.42 KB

7-01_ringintroduction.asciidoc

File metadata and controls

96 lines (78 loc) · 3.42 KB

Introduction to Ring

by Adam Bard

Problem

You need to write an HTTP service with Clojure.

Solution

Clojure has no built-in HTTP server, but the de facto standard for serving basic, synchronous HTTP requests is the Ring library.

To follow along with this recipe, clone the https://github.com/clojure-cookbook/ringtest repository and overwrite src/ringtest.clj:

(ns ringtest
  (:require
    [ring.adapter.jetty :as jetty]
    clojure.pprint))

;; Echo (with pretty-print) the request received
(defn handler [request]
  {:status 200
   :headers {"content-type" "text/clojure"}
   :body (with-out-str (clojure.pprint/pprint request))})

(defn -main []
  ;; Run the server on port 3000
  (jetty/run-jetty handler {:port 3000}))

Discussion

Ring is the basis for most web applications in Clojure. It provides a low-level and straightforward request/response API, where requests and responses are plain old Clojure maps.

Ring applications are architected around handlers: functions that accept requests and return responses. The preceding example defines a single handler that just echoes the response it receives.

A basic response map consists of three keys: :status, the status code of the response; :headers, an optional string-string map of the response headers you want; and :body, the string you want as your response body. Here, :status is 200 and :body is a pretty-printed string of the request. Therefore, the following sample response from hitting the URL http://localhost:3000/test/path/?qs=1 on the author’s machine demonstrates the structure of a request:

{:ssl-client-cert nil,
 :remote-addr "0:0:0:0:0:0:0:1",
 :scheme :http,
 :request-method :get,
 :query-string "qs=1",
 :content-type nil,
 :uri "/test/path/",
 :server-name "localhost",
 :headers
 {"accept-encoding" "gzip,deflate,sdch",
  "connection" "keep-alive",
  "user-agent"
  "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_4) AppleWebKit/537.36
  (KHTML, like Gecko) Chrome/28.0.1500.71 Safari/537.36",
  "accept-language" "en-US,en;q=0.8",
  "accept"
  "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
  "host" "localhost:3000",
  "cookie" ""},
 :content-length nil,
 :server-port 3000,
 :character-encoding nil,
 :body #<HttpInput org.eclipse.jetty.server.HttpInput@43efe432>}

You can see that this is comprehensive, but low level, with the salient features of the request parsed into Clojure data structures without additional abstraction. Typically, additional code or libraries are used to extract meaningful information from this data structure.

The jetty adapter is used to run an embedded Jetty server. Ring also comes with adapters to run as a servlet in any Java servlet container.

Note that the call to run-jetty is synchronous and will not return as long as the server is running. If you call it from the REPL, you should wrap it in a future (or use some other concurrency mechanism) so the server runs on another thread and your REPL does not become unresponsive.

See Also