From d87931745d099bacb7c8ef34a47bccc25222e006 Mon Sep 17 00:00:00 2001 From: Cam Saul <cammsaul@gmail.com> Date: Fri, 19 Apr 2019 20:54:28 -0400 Subject: [PATCH] Set X-Accel-Buffering header in streaming resp so nginx doesn't buffer :speaking_head_in_silhouette: --- src/metabase/async/api_response.clj | 10 ++++------ src/metabase/handler.clj | 1 + src/metabase/middleware/misc.clj | 21 ++++++++++++++++++++- 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/metabase/async/api_response.clj b/src/metabase/async/api_response.clj index 46b5fc9b5c9..16f5730becf 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 d82c3ded88d..bf0614c6717 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 bee301fd081..7f8d8d862c4 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))) -- GitLab