clojurepedestal

Clojure Pedestal root serving as application/octet-stream


I am trying to host static assets along with services in Pedestal 0.5.1. I am using the ::file-path to point to a directory to host the files. This works fine if I navigate directly to the file http://localhost:8888/index.html but if I go to the root of the site http://localhost:8888 it serves the files as application/octet-stream rather than text/html. I adapted the Hello World Sample and it has the same behavior.

src/hello_world/server.clj

(ns hello-world.server
  (:require [io.pedestal.http :as http]
            [io.pedestal.http.route :as route])
  (:gen-class))

(def routes
  (route/expand-routes [[]]))

(def service
  {:env                 :prod
   ::http/join? false
   ::http/routes routes
   ::http/file-path "/tmp/www"       
   ::http/type          :jetty
   ::http/allowed-origins {:creds true :allowed-origins (constantly true)}       
   ::http/port          8888})

(defonce runnable-service (http/create-server service))

(defn -main
  "The entry-point for 'lein run'"
  [& args]
  (println "\nCreating your server...")
  (http/start runnable-service))

Start lein run

$ curl -i localhost:8888
HTTP/1.1 200 OK
Date: Fri, 18 Nov 2016 16:02:56 GMT
Last-Modified: Fri, 18 Nov 2016 15:10:22 GMT
Content-Type: application/octet-stream
Content-Length: 12
Server: Jetty(9.3.8.v20160314)

hello world

$ curl -i localhost:8888/index.html
HTTP/1.1 200 OK
Date: Fri, 18 Nov 2016 16:03:02 GMT
Last-Modified: Fri, 18 Nov 2016 15:10:22 GMT
Content-Type: text/html
Content-Length: 12
Server: Jetty(9.3.8.v20160314)

hello world

Is there some way to fix the "/" route to serve the correct content-type?


Solution

  • To get correct content-types for files served as directory indexes, add the interceptor io.pedestal.http.ring-middlewares/file-info to your Pedestal configuration.

    This requires that you override the default interceptor chain with your own, so you will have to include all of the default interceptors that your app needs.

    For example, your service might look something like this:

    (ns hello-world.service
      (:require
       [io.pedestal.http :as http]
       [io.pedestal.http.ring-middlewares :as middlewares]
       [io.pedestal.http.route :as route]
       [io.pedestal.http.route.definition :refer [defroutes]]))
    
    (defroutes routes
      [[]])
    
    (def service
      {::http/type :jetty
       ::http/port 8080
       ::http/interceptors [http/log-request
                            http/not-found
                            middlewares/session
                            route/query-params
                            (middlewares/file-info)  ; HERE
                            (middlewares/file "/tmp/www")
                            ;; ... insert other interceptors ...
                            (route/router #(deref #'routes) :map-tree)]})
    

    For examples of other default interceptors you might want to include, see default-interceptors.

    Explanation

    This probably does not come up very often in practice because many web applications use a handler function to generate the home page instead of returning a static file.

    For an alternate solution, you could write a route handler for the / route which returns the contents of index.html with the appropriate content-type.

    Pedestal's default interceptor stack includes io.pedestal.http.ring-middlewares/file and io.pedestal.http.ring-middlewares/content-type.

    These interceptors just wrap the Ring middleware functions file-request and content-type-response, respectively.

    file-request returns a java.io.File object as the HTTP response.

    content-type-response examines the request URI to determine the value of the Content-Type header. Since the URI is just / it defaults to application/octet-stream.

    By contrast ring.middleware.file-info (which is deprecated) examines path of the actual File object in the response. See file-info-response.

    io.pedestal.http.ring-middlewares/file-info is the interceptor wrapper around ring.middleware.file-info/file-info-response.