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