Welcome! Please see the About page for a little more info on how this works.

0 votes
in ClojureScript by
If you rapidly enter forms into the browser REPL when connected to Safari, it will ultimately fail (usually within 20 forms, but sometimes within 100) with Safari indicating


Failed to load resource: The network connection was lost.


At this point, the response to the evaluated form will not be sent back to the terminal REPL, and the terminal REPL will block awaiting a promise that will never be fulfilled:


"main" #1 prio=5 os_prio=31 tid=0x00007fa680011800 nid=0x2603 waiting on condition [0x000070000ad7c000]
   java.lang.Thread.State: WAITING (parking)
    at jdk.internal.misc.Unsafe.park(java.base@9.0.4/Native Method)
    - parking to wait for  <0x000000070ad0c030> (a java.util.concurrent.CountDownLatch$Sync)
    at java.util.concurrent.locks.LockSupport.park(java.base@9.0.4/LockSupport.java:194)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(java.base@9.0.4/AbstractQueuedSynchronizer.java:871)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireSharedInterruptibly(java.base@9.0.4/AbstractQueuedSynchronizer.java:1024)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireSharedInterruptibly(java.base@9.0.4/AbstractQueuedSynchronizer.java:1331)
    at java.util.concurrent.CountDownLatch.await(java.base@9.0.4/CountDownLatch.java:232)
    at clojure.core$promise$reify__8144.deref(core.clj:7029)
    at clojure.core$deref.invokeStatic(core.clj:2312)
    at clojure.core$deref.invoke(core.clj:2298)
    at cljs.repl.browser$browser_eval.invokeStatic(browser.clj:261)
    at cljs.repl.browser$browser_eval.invoke(browser.clj:250)
    at cljs.repl.browser.BrowserEnv._evaluate(browser.clj:330)
    at cljs.repl$evaluate_form.invokeStatic(repl.cljc:546)
    at cljs.repl$evaluate_form.invoke(repl.cljc:480)
    at cljs.repl$eval_cljs.invokeStatic(repl.cljc:665)
    at cljs.repl$eval_cljs.invoke(repl.cljc:658)
    at cljs.repl$repl_STAR_$read_eval_print__6488.invoke(repl.cljc:950)
    at cljs.repl$repl_STAR_$fn__6494$fn__6503.invoke(repl.cljc:994)
    at cljs.repl$repl_STAR_$fn__6494.invoke(repl.cljc:993)
    at cljs.compiler$with_core_cljs.invokeStatic(compiler.cljc:1285)
    at cljs.compiler$with_core_cljs.invoke(compiler.cljc:1274)
    at cljs.repl$repl_STAR_.invokeStatic(repl.cljc:953)
    at cljs.repl$repl_STAR_.invoke(repl.cljc:832)
    at cljs.cli$repl_opt.invokeStatic(cli.clj:258)
    at cljs.cli$repl_opt.invoke(cli.clj:247)
    at cljs.cli$main.invokeStatic(cli.clj:577)
    at cljs.cli$main.doInvoke(cli.clj:564)
    at clojure.lang.RestFn.invoke(RestFn.java:410)
    at clojure.lang.AFn.applyToHelper(AFn.java:154)
    at clojure.lang.RestFn.applyTo(RestFn.java:132)
    at clojure.core$apply.invokeStatic(core.clj:659)
    at clojure.core$apply.invoke(core.clj:652)
    at cljs.main$_main.invokeStatic(main.clj:61)
    at cljs.main$_main.doInvoke(main.clj:52)
    at clojure.lang.RestFn.invoke(RestFn.java:397)
    at clojure.lang.AFn.applyToHelper(AFn.java:152)
    at clojure.lang.RestFn.applyTo(RestFn.java:132)
    at clojure.lang.Var.applyTo(Var.java:702)
    at clojure.core$apply.invokeStatic(core.clj:657)
    at clojure.main$main_opt.invokeStatic(main.clj:317)
    at clojure.main$main_opt.invoke(main.clj:313)
    at clojure.main$main.invokeStatic(main.clj:424)
    at clojure.main$main.doInvoke(main.clj:387)
    at clojure.lang.RestFn.applyTo(RestFn.java:137)
    at clojure.lang.Var.applyTo(Var.java:702)
    at clojure.main.main(main.java:37)


This is also reproducible with Safari Technology Preview, but not with Chrome or FireFox.

One way to reproduce the issue is to make a file containing one form per line (say, a list of integers), and to {{cat}} that file into the REPL:


cat forms.txt | clj -m cljs.main -ro '{:launch-browser false}' -r


Then connect to http://localhost:9000 with Safari.

Another way to reproduce the error is to use a program to drive the REPL. Here is one that can be executed in a Clojure REPL:


(require '[clojure.java.io :as io])

(let [cmd  ["clj" "-m" "cljs.main" "-ro" "{:launch-browser false}" "-r"]
      proc (.exec (Runtime/getRuntime) (into-array cmd))
             out (.getInputStream proc)
             err (.getErrorStream proc)
             in (.getOutputStream proc)]
  (future (io/copy out *out*))
  (future (io/copy err *err*))                                
  (loop [n 0]
    (io/copy (io/input-stream (.getBytes (str "(inc " n ")\n"))) in)
    (io/copy (io/input-stream (.getBytes (str "(.log js/console " n ")\n"))) in)
    (.flush in)
    (Thread/sleep (+ 100 (rand-int 200)))
    (recur (inc n))))


Then connect to http://localhost:9000 with Safari.

The above program includes JavaScript console logging, so you can see the count increase in the browser console until failure occurs.

Interestingly, it was empirically found that if you comment out the {{Content-Length}} header here, then the problem goes away:

https://github.com/clojure/clojurescript/blob/7aca40c4b6131b8e08153809a410c06bdfa567ab/src/main/clojure/cljs/repl/server.clj#L166

This was inspired by a description of the way Safari handles this header, and how it can produce the error being seen https://apple.stackexchange.com/a/107868

Note that this is unrelated to the new gzip code; I tried a test disabling the gzip feature and the problem still occurs with it disabled.

1 Answer

0 votes
by
Reference: https://clojure.atlassian.net/browse/CLJS-2629 (reported by mfikes)
...