daydaynatation
daydaynatation

Reputation: 550

Websockets + servant-server

I'm trying to make websockets work with servant-server:

{-# LANGUAGE DataKinds         #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeOperators     #-}

import           Control.Monad.IO.Class
import           Data.Text                (Text)
import           Network.Wai.Handler.Warp (run)
import           Network.WebSockets
import           Servant                  hiding (route)
import           Servant.API.WebSocket

type API = Raw
  :<|> "game" :> WebSocketPending

myAPI :: Proxy API
myAPI = Proxy

server :: Server API
server = serveDirectoryFileServer "site"
  :<|> game "From socket server"

game :: MonadIO m => Text -> PendingConnection -> m ()
game msg pending = do
  conn <- liftIO $ acceptRequest pending
  liftIO $ sendTextData conn msg

app :: Application
app = serve myAPI server

main :: IO ()
main = run 8080 app

The intention is to send a string From socket server back to a client for testing.

In Firefox console:

// Create WebSocket connection.
socket = new WebSocket('ws://localhost:8080/game');

// Connection opened
socket.addEventListener('open', function (event) {
    socket.send('Hello Server!');
});

// Listen for messages
socket.addEventListener('message', function (event) {
    console.log('Message from server ', event.data);
});

I got an error frome firefox:

Firefox can’t establish a connection to the server at ws://localhost:8080/game

Could someone give me a hint?

Update:

Here's the code that worked after re-ordering the API.

type API = "game" :> WebSocketPending
  :<|> Raw

myAPI :: Proxy API
myAPI = Proxy

server :: Server API
server = game
  :<|> serveDirectoryFileServer "site"

game :: MonadIO m => PendingConnection -> m ()
game pending = do
  conn <- liftIO $ acceptRequest pending
  liftIO $ withPingThread conn 30 (return ()) $ do
    forever $ do
      msg <- receiveData conn :: IO Text
      sendTextData conn msg

Upvotes: 2

Views: 702

Answers (1)

Robin Zigmond
Robin Zigmond

Reputation: 18259

The problem turns out to be the order of your API endpoints in the type definition.

The type combinator (:<|>) is described in the docs, as

Union of two APIs, first takes precedence in case of overlap.

and since Raw essentially matches everything, your original definition was treating all requests as "raw" requests, never forwarding to your Websocket server.

Simply switching the order - of the arguments to (:<|>) in both the type and the server definition - fixes this problem, as you observed.

Upvotes: 1

Related Questions