@@ -20,12 +20,10 @@ module Language.Javascript.JSaddle.WebSockets (
2020 , jsaddleApp
2121 , jsaddleWithAppOr
2222 , jsaddleAppWithJs
23− , jsaddleAppWithJsOr
2423 , jsaddleAppPartial
2524 , jsaddleJs
2625 , jsaddleJs'
2726 , debug
28− , debugOr
2927 , debugWrapper
3028) where
3129
@@ -60,57 +58,45 @@ import qualified Data.Map as M (empty, insert, lookup)
6058import Data.IORef
6159 (readIORef, newIORef, atomicModifyIORef')
6260import Data.ByteString.Lazy (ByteString)
63−import qualified Data.ByteString.Lazy as LBS
64− (toStrict, stripPrefix)
61+import qualified Data.ByteString.Lazy as LBS (stripPrefix)
6562import Control.Concurrent.MVar
6663 (tryTakeMVar, MVar, tryPutMVar, modifyMVar_, putMVar, takeMVar,
6764 readMVar, newMVar, newEmptyMVar, modifyMVar)
6865import Network.Wai.Handler.Warp
6966 (defaultSettings, setTimeout, setPort, runSettings)
70−import Foreign.Store (readStore, lookupStore, writeStore, Store(..))
67+import Foreign.Store (newStore, readStore, lookupStore, writeStore, Store(..))
7168import Language.Javascript.JSaddle (askJSM)
7269import Control.Monad.IO.Class (MonadIO(..))
7370
7471import Language.Javascript.JSaddle.WebSockets.Compat (getTextMessageByteString)
75−import qualified Data.Text.Encoding as T (decodeUtf8)
7672
7773jsaddleOr :: ConnectionOptions -> JSM () -> Application -> IO Application
7874jsaddleOr opts entryPoint otherApp = do
7975 syncHandlers <- newIORef M.empty
80− asyncHandlers <- newIORef M.empty
8176 let wsApp :: ServerApp
8277 wsApp pending_conn = do
8378 conn <- acceptRequest pending_conn
84− initMsg <- receiveDataMessage conn
85− case getTextMessageByteString initMsg of
86− Just "" -> do
87− rec (processResult, processSyncResult, start) <- runJavaScript (sendTextData conn . encode) $ do
88− syncKey <- T.pack . show . contextId <$> askJSM
89− liftIO $ atomicModifyIORef' syncHandlers (\m -> (M.insert syncKey processSyncResult m, ()))
90− liftIO $ atomicModifyIORef' asyncHandlers (\m -> (M.insert syncKey processResult m, ()))
91− liftIO $ sendTextData conn (encode syncKey)
92− entryPoint
93− start
94− waitTillClosed conn
95− Just syncKey ->
96− M.lookup (T.decodeUtf8 $ LBS.toStrict syncKey) <$> readIORef syncHandlers >>= \case
97− Nothing -> error "jsaddle missing sync message handler"
98− Just processResult ->
99− forever $
100− receiveDataMessage conn >>= \msg -> case getTextMessageByteString msg of
101− Just t ->
102− case decode t of
103− Nothing -> error $ "jsaddle Results decode failed : " <> show t
104− Just r -> processResult r
105− _ -> error "jsaddle WebSocket unexpected binary data"
106− _ -> error "jsaddle WebSocket unexpected binary data"
79+ rec (processResult, processSyncResult, start) <- runJavaScript (sendTextData conn . encode) $ do
80+ syncKey <- T.pack . show . contextId <$> askJSM
81+ liftIO $ atomicModifyIORef' syncHandlers (\m -> (M.insert syncKey processSyncResult m, ()))
82+ liftIO $ sendTextData conn (encode syncKey)
83+ entryPoint
84+ _ <- forkIO . forever $
85+ receiveDataMessage conn >>= \msg -> case getTextMessageByteString msg of
86+ Just t ->
87+ case decode t of
88+ Nothing -> error $ "jsaddle Results decode failed : " <> show t
89+ Just r -> processResult r
90+ _ -> error "jsaddle WebSocket unexpected binary data"
91+ start
92+ waitTillClosed conn
10793
10894 -- Based on Network.WebSocket.forkPingThread
10995 waitTillClosed conn = ignore `handle` go 1
11096 where
11197 go :: Int -> IO ()
11298 go i = do
113− threadDelay (10 * 1000 * 1000)
99+ threadDelay (1 * 1000 * 1000)
114100 sendPing conn (T.pack $ show i)
115101 go (i + 1)
116102
@@ -143,14 +129,9 @@ jsaddleApp = jsaddleAppWithJs $ jsaddleJs False
143129
144130jsaddleAppWithJs :: ByteString -> Application
145131jsaddleAppWithJs js req sendResponse =
146− jsaddleAppWithJsOr js
147− (\_ _ -> sendResponse $ W.responseLBS H.status403 [("Content-Type", "text/plain")] "Forbidden")
148− req sendResponse
149−
150−jsaddleAppWithJsOr :: ByteString -> Application -> Application
151−jsaddleAppWithJsOr js otherApp req sendResponse =
152− fromMaybe (otherApp req sendResponse)
153− (jsaddleAppPartialWithJs js req sendResponse)
132+ fromMaybe
133+ (sendResponse $ W.responseLBS H.status403 [("Content-Type", "text/plain")] "Forbidden")
134+ (jsaddleAppPartialWithJs js req sendResponse)
154135
155136jsaddleWithAppOr :: ConnectionOptions -> JSM () -> Application -> IO Application
156137jsaddleWithAppOr opts entryPoint otherApp = jsaddleOr opts entryPoint $ \req sendResponse ->
@@ -176,7 +157,7 @@ jsaddleJs = jsaddleJs' Nothing
176157-- sed -e 's|\\|\\\\|g' -e 's|^| \\|' -e 's|$|\\n\\|' -e 's|"|\\"|g' data/jsaddle.js | pbcopy
177158jsaddleJs' :: Maybe ByteString -> Bool -> ByteString
178159jsaddleJs' jsaddleUri refreshOnLoad = "\
179− \if(typeof global !== \"undefined\" && typeof require === \"function\") {\n\
160+ \if(typeof global !== \"undefined\") {\n\
180161 \ global.window = global;\n\
181162 \ global.WebSocket = require('ws');\n\
182163 \}\n\
@@ -188,30 +169,20 @@ jsaddleJs' jsaddleUri refreshOnLoad = "\
188169 (jsaddleUri >>= LBS.stripPrefix "http")
189170 <> ";\n\
190171 \\n\
191− \ var ws0 = new WebSocket(wsaddress);\n\
172+ \ var ws = new WebSocket(wsaddress);\n\
192173 \ var syncKey = \"\";\n\
193174 \\n\
194− \ ws0.onopen = function(e) {\n\
195− \ ws0.send(\"\");\n\
196− \ var initialResults = [];\n\
197− \ var ws = {send: function(m) {initialResults.push(m);}};\n\
175+ \ ws.onopen = function(e) {\n\
198176 \ " <> initState <> "\n\
199177 \\n\
200− \ ws0.onmessage = function(e) {\n\
178+ \ ws.onmessage = function(e) {\n\
201179 \ var batch = JSON.parse(e.data);\n\
202180 \ if(inCallback > 0) {\n\
203181 \ asyncBatch = batch;\n\
204182 \ return;\n\
205183 \ }\n\
206184 \ if(typeof batch === \"string\") {\n\
207− \ syncKey = batch;\n\
208− \ var ws1 = new WebSocket(wsaddress);\n\
209− \ ws1.onopen = function(e) {\n\
210− \ ws1.send(syncKey);\n\
211− \ initialResults.forEach(function(m){ ws1.send(m); });\n\
212− \ initialResults = null;\n\
213− \ ws = ws1;\n\
214− \ }\n" <>
185+ \ syncKey = batch;\n" <>
215186 (if refreshOnLoad
216187 then " var xhr = new XMLHttpRequest();\n\
217188 \ xhr.open('POST', '/reload/'+syncKey, true);\n\
@@ -233,7 +204,7 @@ jsaddleJs' jsaddleUri refreshOnLoad = "\
233204 \ return JSON.parse(xhr.response);})()")) <> "\
234205 \ };\n\
235206 \ };\n\
236− \ ws0.onerror = function() {\n\
207+ \ ws.onerror = function() {\n\
237208 \ setTimeout(connect, 1000);\n\
238209 \ };\n\
239210 \}\n\
@@ -252,13 +223,6 @@ debug port f = do
252223 jsaddleOr defaultConnectionOptions (registerContext >> f >> syncPoint) (withRefresh $ jsaddleAppWithJs $ jsaddleJs True)
253224 putStrLn $ "<a href=\"http://localhost:" <> show port <> "\">run</a>"
254225
255−debugOr :: Int -> JSM () -> Application -> IO ()
256−debugOr port f b = do
257− debugWrapper $ \withRefresh registerContext ->
258− runSettings (setPort port (setTimeout 3600 defaultSettings)) =<<
259− jsaddleOr defaultConnectionOptions (registerContext >> f >> syncPoint) (withRefresh $ jsaddleAppWithJsOr (jsaddleJs True) b)
260− putStrLn $ "<a href=\"http://localhost:" <> show port <> "\">run</a>"
261−
262226refreshMiddleware :: ((Response -> IO ResponseReceived) -> IO ResponseReceived) -> Middleware
263227refreshMiddleware refresh otherApp req sendResponse = case (W.requestMethod req, W.pathInfo req) of
264228 ("POST", ["reload", _syncKey]) -> refresh sendResponse