clojure/ring/jetty: I am using > lein ring server.

2019-03-13 17:54发布

问题:

When I was calling the jetty handler directly, I was able to pass in a configurator like so:

(def header-buffer-size 8388608)
(defn start [port]
  (ring/run-jetty
   (var app)
   {:port port
    :join? false
    :host "127.0.0.1"
    :configurator
    (fn [jetty]
      (doseq [connector (.getConnectors jetty)]
        (.setHeaderBufferSize connector header-buffer-size)))}))

I had to do this because I kept getting a FULL HEAD error when posting. Now I refactored things to use > lein ring server directly, which gets called from the command line.

> lein ring server

This uses some configuration specified in my project.clj:

:ring {:handler caribou.api.core/app
       :servlet-name "caribou-api"
       :init caribou.api.core/init
       :port 33443}

This works great, but now I am getting the FULL HEAD issue again. So I tried to add a configurator in there:

:ring {:handler caribou.api.core/app
       :servlet-name "caribou-api"
       :init caribou.api.core/init
       :configurator
       (fn [jetty]
         (doseq [connector (.getConnectors jetty)]
           (.setHeaderBufferSize connector 8388608)))
       :port 33443})

And this fails with this stacktrace:

Exception in thread "main" java.lang.ClassCastException: 
clojure.lang.PersistentList cannot be cast to clojure.lang.IFn
  at ring.adapter.jetty$run_jetty.invoke(jetty.clj:66)
  at ring.server.standalone$serve$fn__833.invoke(standalone.clj:78)
  at ring.server.standalone$try_port.invoke(standalone.clj:12)
  at ring.server.standalone$serve.doInvoke(standalone.clj:75)
  at clojure.lang.RestFn.invoke(RestFn.java:423)
  at ring.server.leiningen$serve.invoke(leiningen.clj:20)

I figured this had to do with putting the function directly in the map like that, so I defined it outside the project (in caribou.api.core) and tried referring to it like I do the rest of the functions defined elsewhere:

;; in caribou/api/core.clj
(def header-buffer-size 8388608)
(defn full-head-avoidance
  [jetty]
  (doseq [connector (.getConnectors jetty)]
    (.setHeaderBufferSize connector header-buffer-size)))

;; in project.clj
:ring {:handler caribou.api.core/app
       :servlet-name "caribou-api"
       :init caribou.api.core/init
       :configurator caribou.api.core/full-head-avoidance
       :port 33443})

This spins up the app, but I still get the 413: FULL HEAD error when posting. Any ideas? Thanks!

回答1:

Stuff written inside a defproject form is not evaluated by default, but you can use ~ (unquote) in defproject when you need it to:

(defproject foo "1.2.3"
  ...
  :some-fn-key ~(fn [& args] ...))

In this particular situation, you'd unquote the fn form defining your configurator function (which without unquoting is being used as a list containing a bunch of symbols, rather than compiled into a function).

Alternatively, you can define the configurator outside the defproject form and refer to its name inside defproject as in your second approach, but then you need to unquote the name inside defproject -- otherwise it'll be treated as a symbol. NB. in the latter case no exception is produced, because symbols are in fact callable and accept arbitrary arguments (but only ever return nil when called with something other than a map or a set; with maps or sets, they look themselves up in their arguments).



回答2:

The answer is not to use ring server and just start jetty directly with ring/ring-jetty-adapter. That way I can still pass in the configurator, which BTW has changed to this:

(defn full-head-avoidance
  [jetty]
  (doseq [connector (.getConnectors jetty)]
    (.setRequestHeaderSize connector header-buffer-size)))