@@ -20,12 +20,12 @@ import qualified Data.ByteString.Lazy as LBS
2020import Data.CaseInsensitive
2121import Data.Char
2222import Data.String.Conversions
23+import Foreign.StablePtr
2324import GHCJS.Foreign.Callback
2425import GHCJS.Prim
2526import Network.HTTP.Client
2627import Network.HTTP.Client.Internal as HttpClient
2728import Network.HTTP.Types
28−
2929import Servant.Client.PerformRequest.Base
3030
3131newtype JSXMLHttpRequest = JSXMLHttpRequest JSVal
@@ -58,17 +58,29 @@ foreign import javascript unsafe "new $1()"
5858-- Performs the xhr and blocks until the response was received
5959performXhr :: JSXMLHttpRequest -> Request -> IO ()
6060performXhr xhr request = do
61− waiter <- newEmptyMVar
62− callback <- onReadyStateChange xhr $ do
63− state <- readyState xhr
64− case state of
65− 4 -> putMVar waiter ()
66− _ -> return ()
67− openXhr xhr (cs $ method request) (toUrl request) True
68− setHeaders xhr (requestHeaders request)
69− sendXhr xhr (toBody request)
70− takeMVar waiter
71− releaseCallback callback
61+ waiter <- newEmptyMVar
62+
63+ bracket (acquire waiter) releaseCallback $ \_callback -> do
64+ t <- myThreadId
65+ s <- newStablePtr t
66+
67+ openXhr xhr (cs $ method request) (toUrl request) True
68+ setHeaders xhr (requestHeaders request)
69+ sendXhr xhr (toBody request)
70+ takeMVar waiter
71+
72+ freeStablePtr s
73+ where
74+ acquire waiter = onReadyStateChange xhr $ do
75+ state <- readyState xhr
76+ case state of
77+ -- onReadyStateChange's callback can fire state 4
78+ -- (which means "request finished and response is ready")
79+ -- multiple times. By using tryPutMVar, only the first time
80+ -- state 4 is fired will cause an MVar to be put. Subsequent
81+ -- fires are ignored.
82+ 4 -> tryPutMVar waiter () >> return ()
83+ _ -> return ()
7284
7385onReadyStateChange :: JSXMLHttpRequest -> IO () -> IO (Callback (IO ()))
7486onReadyStateChange xhr action = do