Skip to content

Async handlers work only with Jetty #220

@dpiliouras

Description

@dpiliouras

There are two issues preventing async handlers from working in any Servlet container, other than Jetty (e.g. Tomcat). Thankfully, one of them is super easy to fix, but the other I'm having trouble with and I would appreciate your help.

  1. The web.xml emitted should contain a async-supported tag - especially when the [:ring :async?] option is true. This is very easily fixable by adding [:async-supported (:async? ring-options false)]], right before closing the :servlet tag in the make-web-xml function (here). If I manage to fix point 2 (see below), I can include this in the PR, otherwise it's such a trivial change that I don't really see the point of a PR just for that.

  2. The compile-listener function calls generate-handler in order to resolve the actual user handler(here). As you can see if the [:ring :servlet-path-info?] option is true (or missing), the user-handler is wrapped in another function, which however takes a single argument, and therefore won't work with the three arguments passed to it (per the async ring.util.servlet/make-service-method). If the [:ring :servlet-path-info?] option is explicitly set to false, then it works as expected (assuming of course that the actual handler allows for 3 args). Now, you might be tempted to think that this is also easily fixable, but trust me, it isn't...I tried adding a 3-arg arity to that function returned by generate-handler, and everything compiles/builds/deploys no problem, but then when I try to use it on a real project, I get compile-syntax errors like this:

:clojure.main/message
 "Syntax error (ClassNotFoundException) compiling at (com/foo/gw/listener.clj:1:519).\nleiningen.ring.war\n",
 :clojure.main/triage
 {:clojure.error/phase :compile-syntax-check,
  :clojure.error/line 1,
  :clojure.error/column 519,
  :clojure.error/source "listener.clj",
  :clojure.error/path "com/foo/gw/listener.clj",
  :clojure.error/class java.lang.ClassNotFoundException,
  :clojure.error/cause "leiningen.ring.war"},

Here is the code in case you have doubts that the syntax is correct:

(defn- with-context-path-info [req context-path]
  (assoc req
    :context context-path
    :path-info (-> (:uri req) (subs (count context-path)) not-empty (or "/"))))

(defn generate-handler [project handler-sym]
  (if (get-in project [:ring :servlet-path-info?] true)
    `(let [handler# ~(generate-resolve handler-sym)]
       (fn
         ([request# respond# raise#]
          (let [context# (.getContextPath
                           ^javax.servlet.http.HttpServletRequest
                           (:servlet-request request#))]
            (-> request#
                (with-context-path-info context#)
                (handler# respond# raise#))))
         ([request#]
          (let [context# (.getContextPath
                           ^javax.servlet.http.HttpServletRequest
                           (:servlet-request request#))]
            (-> request#
                (with-context-path-info context#)
                (handler#))))))
    (generate-resolve handler-sym)))

I also tried generating two distinct functions based on the :async? option, but I got the exact same compile errors:

(if (get-in project [:ring :async?])  
  (fn [req respond raise] ...) 
  (fn [req] ...))

If you can provide any form of insight as to why such a simple change (adding an arity) would cause compile-syntax errors, that would be fantastic. Many thanks in advance...

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions