diff --git a/src/metabase/async/api_response.clj b/src/metabase/async/api_response.clj index 46b5fc9b5c97cde83ee7d631b9544f5fd3bdcc33..16f5730becfe4fd818a9b79123c268f86bf33985 100644 --- a/src/metabase/async/api_response.clj +++ b/src/metabase/async/api_response.clj @@ -186,12 +186,10 @@ (log/debug (u/format-color 'green (trs "starting streaming response"))) (write-chan-vals-to-writer! (async-keepalive-channel chan) (io/writer output-stream)))) +;; `defendpoint-async` responses (extend-protocol Sendable ManyToManyChannel (send* [input-chan _ respond _] - (respond (-> (response/response input-chan) - (assoc :content-type "applicaton/json; charset=utf-8") - ;; tell nginx not to batch streaming responses -- otherwise the keepalive bytes aren't written and - ;; the entire purpose is defeated. See - ;; https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_cache - (assoc-in [:headers "X-Accel-Buffering"] "no"))))) + (respond + (assoc (response/response input-chan) + :content-type "applicaton/json; charset=utf-8")))) diff --git a/src/metabase/handler.clj b/src/metabase/handler.clj index d82c3ded88dbc6ef07ff5f7ce4a31c57ae884438..bf0614c6717cb2bb99861707375863df726a80e5 100644 --- a/src/metabase/handler.clj +++ b/src/metabase/handler.clj @@ -40,5 +40,6 @@ mw.misc/bind-user-locale ; Binds *locale* for i18n wrap-cookies ; Parses cookies in the request map and assocs as :cookies mw.misc/add-content-type ; Adds a Content-Type header for any response that doesn't already have one + mw.misc/disable-streaming-buffering ; Add header to streaming (async) responses so ngnix doesn't buffer keepalive bytes mw.misc/wrap-gzip)) ; GZIP response if client can handle it ;; ▲▲▲ PRE-PROCESSING ▲▲▲ happens from BOTTOM-TO-TOP diff --git a/src/metabase/middleware/misc.clj b/src/metabase/middleware/misc.clj index bee301fd081290ec58fe11846e76ce4ddd5b848e..7f8d8d862c44f8df170ff17b69e72dd527bbf758 100644 --- a/src/metabase/middleware/misc.clj +++ b/src/metabase/middleware/misc.clj @@ -8,7 +8,8 @@ [metabase.util.i18n :refer [trs]] [puppetlabs.i18n.core :as puppet-i18n] [ring.middleware.gzip :as ring.gzip]) - (:import [java.io File InputStream])) + (:import clojure.core.async.impl.channels.ManyToManyChannel + [java.io File InputStream])) (defn- add-content-type* [request response] (update-in @@ -98,3 +99,21 @@ request (comp respond (partial wrap-gzip* request)) raise))) + + +;;; ------------------------------------------ Disable Streaming Buffering ------------------------------------------- + +(defn- maybe-add-disable-buffering-header [{:keys [body], :as response}] + (cond-> response + (instance? ManyToManyChannel body) + (assoc-in [:headers "X-Accel-Buffering"] "no"))) + +(defn disable-streaming-buffering + "Tell nginx not to batch streaming responses -- otherwise the keepalive bytes aren't written and + the entire purpose is defeated. See https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_cache" + [handler] + (fn [request respond raise] + (handler + request + (comp respond maybe-add-disable-buffering-header) + raise)))