websockets-0.10.0.0: A sensible and clean way to write WebSocket-capable servers in Haskell.

Safe Haskell | None
Language | Haskell98

Network.WebSockets

Contents

- Incoming connections and handshaking
- Main connection type
- Options for connections
- Sending and receiving messages
- HTTP Types
- WebSocket message types
- Exceptions
- Running a standalone server
- Utilities for writing your own server
- Running a client
- Utilities

Synopsis

- data PendingConnection
- pendingRequest :: PendingConnection -> RequestHead
- acceptRequest :: PendingConnection -> IO Connection
- data AcceptRequest = AcceptRequest {
  - acceptSubprotocol :: !(Maybe ByteString)
  - acceptHeaders :: !Headers
  }
- defaultAcceptRequest :: AcceptRequest
- acceptRequestWith :: PendingConnection -> AcceptRequest -> IO Connection
- rejectRequest :: PendingConnection -> ByteString -> IO ()
- data Connection
- data ConnectionOptions = ConnectionOptions {
  - connectionOnPong :: !(IO ())
  }
- defaultConnectionOptions :: ConnectionOptions
- receive :: Connection -> IO Message
- receiveDataMessage :: Connection -> IO DataMessage
- receiveData :: WebSocketsData a => Connection -> IO a
- send :: Connection -> Message -> IO ()
- sendDataMessage :: Connection -> DataMessage -> IO ()
- sendTextData :: WebSocketsData a => Connection -> a -> IO ()
- sendTextDatas :: WebSocketsData a => Connection -> [a] -> IO ()
- sendBinaryData :: WebSocketsData a => Connection -> a -> IO ()
- sendClose :: WebSocketsData a => Connection -> a -> IO ()
- sendPing :: WebSocketsData a => Connection -> a -> IO ()
- type Headers = [(CI ByteString, ByteString)]
- data Request = Request RequestHead ByteString
- data RequestHead = RequestHead {
  - requestPath :: !ByteString
  - requestHeaders :: Headers
  - requestSecure :: Bool
  }
- getRequestSubprotocols :: RequestHead -> [ByteString]
- data Response = Response ResponseHead ByteString
- data ResponseHead = ResponseHead {
  - responseCode :: !Int
  - responseMessage :: !ByteString
  - responseHeaders :: Headers
  }
- data Message
  - = ControlMessage ControlMessage
  - | DataMessage DataMessage
- data ControlMessage
  - = Close Word16 ByteString
  - | Ping ByteString
  - | Pong ByteString
- data DataMessage
  - = Text ByteString
  - | Binary ByteString
- class WebSocketsData a where
- data HandshakeException
  - = NotSupported
  - | MalformedRequest RequestHead String
  - | MalformedResponse ResponseHead String
  - | RequestRejected Request String
  - | OtherHandshakeException String
- data ConnectionException
  - = CloseRequest Word16 ByteString
  - | ConnectionClosed
  - | ParseException String
- type ServerApp = PendingConnection -> IO ()
- runServer :: String -> Int -> ServerApp -> IO ()
- runServerWith :: String -> Int -> ConnectionOptions -> ServerApp -> IO ()
- makeListenSocket :: String -> Int -> IO Socket
- makePendingConnection :: Socket -> ConnectionOptions -> IO PendingConnection
- makePendingConnectionFromStream :: Stream -> ConnectionOptions -> IO PendingConnection
- type ClientApp a = Connection -> IO a
- runClient :: String -> Int -> String -> ClientApp a -> IO a
- runClientWith :: String -> Int -> String -> ConnectionOptions -> Headers -> ClientApp a -> IO a
- runClientWithSocket :: Socket -> String -> String -> ConnectionOptions -> Headers -> ClientApp a -> IO a
- runClientWithStream :: Stream -> String -> String -> ConnectionOptions -> Headers -> ClientApp a -> IO a
- forkPingThread :: Connection -> Int -> IO ()

# Incoming connections and handshaking

data PendingConnection #

A new client connected to the server. We haven't accepted the connection yet, though.

pendingRequest :: PendingConnection -> RequestHead #

Useful for e.g. inspecting the request path.

acceptRequest :: PendingConnection -> IO Connection #

Accept a pending connection, turning it into a `Connection`.

data AcceptRequest #

This datatype allows you to set options for `acceptRequestWith`. It is strongly recommended to use `defaultAcceptRequest` and then modify the various fields, that way new fields introduced in the library do not break your code.

Constructors

AcceptRequest |
Fields acceptSubprotocol :: !(Maybe ByteString) The subprotocol to speak with the client. If `pendingSubprotcols` is non-empty, `acceptSubprotocol` must be one of the subprotocols from the list. acceptHeaders :: !Headers Extra headers to send with the response.

defaultAcceptRequest :: AcceptRequest #

acceptRequestWith :: PendingConnection -> AcceptRequest -> IO Connection #

This function is like `acceptRequest` but allows you to set custom options using the `AcceptRequest` datatype.

rejectRequest :: PendingConnection -> ByteString -> IO () #

# Main connection type

data Connection #

# Options for connections

data ConnectionOptions #

Set options for a `Connection`.

Constructors

ConnectionOptions |
Fields connectionOnPong :: !(IO ()) Whenever a `pong` is received, this IO action is executed. It can be used to tickle connections or fire missiles.

defaultConnectionOptions :: ConnectionOptions #

# Sending and receiving messages

receive :: Connection -> IO Message #

receiveDataMessage :: Connection -> IO DataMessage #

Receive an application message. Automatically respond to control messages.

When the peer sends a close control message, an exception of type `CloseRequest` is thrown. The peer can send a close control message either to initiate a close or in response to a close message we have sent to the peer. In either case the `CloseRequest` exception will be thrown. The RFC specifies that the server is responsible for closing the TCP connection, which should happen after receiving the `CloseRequest` exception from this function.

This will throw `ConnectionClosed` if the TCP connection dies unexpectedly.

receiveData :: WebSocketsData a => Connection -> IO a #

Receive a message, converting it to whatever format is needed.

send :: Connection -> Message -> IO () #

sendDataMessage :: Connection -> DataMessage -> IO () #

Send a `DataMessage`

sendTextData :: WebSocketsData a => Connection -> a -> IO () #

Send a message as text

sendTextDatas :: WebSocketsData a => Connection -> [a] -> IO () #

Send a collection of messages as text

sendBinaryData :: WebSocketsData a => Connection -> a -> IO () #

Send a message as binary data

sendClose :: WebSocketsData a => Connection -> a -> IO () #

Send a friendly close message. Note that after sending this message, you should still continue calling `receiveDataMessage` to process any in-flight messages. The peer will eventually respond with a close control message of its own which will cause `receiveDataMessage` to throw the `CloseRequest` exception. This exception is when you can finally consider the connection closed.

sendPing :: WebSocketsData a => Connection -> a -> IO () #

Send a ping

# HTTP Types

type Headers = [(CI ByteString, ByteString)] #

Request headers

data Request #

A request with a body

Constructors

Request RequestHead ByteString |

Instances

Show Request # |
Methods showsPrec :: Int -> Request -> ShowS # show :: Request -> String # showList :: [Request] -> ShowS #

data RequestHead #

An HTTP request. The request body is not yet read.

Constructors

RequestHead |
Fields requestPath :: !ByteString requestHeaders :: Headers requestSecure :: Bool

Instances

Show RequestHead # |
Methods showsPrec :: Int -> RequestHead -> ShowS # show :: RequestHead -> String # showList :: [RequestHead] -> ShowS #

getRequestSubprotocols :: RequestHead -> [ByteString] #

List of subprotocols specified by the client, in order of preference. If the client did not specify a list of subprotocols, this will be the empty list.

data Response #

A response including a body

Constructors

Response ResponseHead ByteString |

Instances

Show Response # |
Methods showsPrec :: Int -> Response -> ShowS # show :: Response -> String # showList :: [Response] -> ShowS #

data ResponseHead #

HTTP response, without body.

Constructors

ResponseHead |
Fields responseCode :: !Int responseMessage :: !ByteString responseHeaders :: Headers

Instances

Show ResponseHead # |
Methods showsPrec :: Int -> ResponseHead -> ShowS # show :: ResponseHead -> String # showList :: [ResponseHead] -> ShowS #

# WebSocket message types

data Message #

The kind of message a server application typically deals with

Constructors

ControlMessage ControlMessage |
DataMessage DataMessage |

Instances

Eq Message # |
Methods (==) :: Message -> Message -> Bool # (/=) :: Message -> Message -> Bool #
Show Message # |
Methods showsPrec :: Int -> Message -> ShowS # show :: Message -> String # showList :: [Message] -> ShowS #

data ControlMessage #

Different control messages

Constructors

Close Word16 ByteString |
Ping ByteString |
Pong ByteString |

Instances

Eq ControlMessage # |
Methods (==) :: ControlMessage -> ControlMessage -> Bool # (/=) :: ControlMessage -> ControlMessage -> Bool #
Show ControlMessage # |
Methods showsPrec :: Int -> ControlMessage -> ShowS # show :: ControlMessage -> String # showList :: [ControlMessage] -> ShowS #

data DataMessage #

For an end-user of this library, dealing with `Frame`s would be a bit low-level. This is why define another type on top of it, which represents data for the application layer.

Constructors

Text ByteString |
Binary ByteString |

Instances

Eq DataMessage # |
Methods (==) :: DataMessage -> DataMessage -> Bool # (/=) :: DataMessage -> DataMessage -> Bool #
Show DataMessage # |
Methods showsPrec :: Int -> DataMessage -> ShowS # show :: DataMessage -> String # showList :: [DataMessage] -> ShowS #

class WebSocketsData a where #

In order to have an even more high-level API, we define a typeclass for values the user can receive from and send to the socket. A few warnings apply:

- Natively, everything is represented as a `ByteString`, so this is the fastest instance
- You should only use the `Text` or the `Text` instance when you are sure that the data is UTF-8 encoded (which is the case for `Text` messages).
- Messages can be very large. If this is the case, it might be inefficient to use the strict `ByteString` and `Text` instances.

Minimal complete definition

fromLazyByteString, toLazyByteString

Methods

fromLazyByteString :: ByteString -> a #

toLazyByteString :: a -> ByteString #

Instances

WebSocketsData ByteString # |
Methods fromLazyByteString :: ByteString -> ByteString # toLazyByteString :: ByteString -> ByteString #
WebSocketsData ByteString # |
Methods fromLazyByteString :: ByteString -> ByteString # toLazyByteString :: ByteString -> ByteString #
WebSocketsData Text # |
Methods fromLazyByteString :: ByteString -> Text # toLazyByteString :: Text -> ByteString #
WebSocketsData Text # |
Methods fromLazyByteString :: ByteString -> Text # toLazyByteString :: Text -> ByteString #

# Exceptions

data HandshakeException #

Error in case of failed handshake. Will be thrown as an `Exception`.

TODO: This should probably be in the Handshake module, and is solely here to prevent a cyclic dependency.

Constructors

NotSupported | We don't have a match for the protocol requested by the client. todo: version parameter
MalformedRequest RequestHead String | The request was somehow invalid (missing headers or wrong security token)
MalformedResponse ResponseHead String | The servers response was somehow invalid (missing headers or wrong security token)
RequestRejected Request String | The request was well-formed, but the library user rejected it. (e.g. "unknown path")
OtherHandshakeException String | for example "EOF came too early" (which is actually a parse error) or for your own errors. (like "unknown path"?)

Instances

Show HandshakeException # |
Methods showsPrec :: Int -> HandshakeException -> ShowS # show :: HandshakeException -> String # showList :: [HandshakeException] -> ShowS #
Exception HandshakeException # |
Methods toException :: HandshakeException -> SomeException # fromException :: SomeException -> Maybe HandshakeException # displayException :: HandshakeException -> String #

data ConnectionException #

Various exceptions that can occur while receiving or transmitting messages

Constructors

CloseRequest Word16 ByteString | The peer has requested that the connection be closed, and included a close code and a reason for closing. When receiving this exception, no more messages can be sent. Also, the server is responsible for closing the TCP connection once this exception is received. See http://tools.ietf.org/html/rfc6455#section-7.4 for a list of close codes.
ConnectionClosed | The peer unexpectedly closed the connection while we were trying to receive some data. This is a violation of the websocket RFC since the TCP connection should only be closed after sending and receiving close control messages.
ParseException String | The client sent garbage, i.e. we could not parse the WebSockets stream.

Instances

Show ConnectionException # |
Methods showsPrec :: Int -> ConnectionException -> ShowS # show :: ConnectionException -> String # showList :: [ConnectionException] -> ShowS #
Exception ConnectionException # |
Methods toException :: ConnectionException -> SomeException # fromException :: SomeException -> Maybe ConnectionException # displayException :: ConnectionException -> String #

# Running a standalone server

type ServerApp = PendingConnection -> IO () #

WebSockets application that can be ran by a server. Once this `IO` action finishes, the underlying socket is closed automatically.

runServer #

Arguments

:: String | Address to bind
-> Int | Port to listen on
-> ServerApp | Application
-> IO () | Never returns

Provides a simple server. This function blocks forever. Note that this is merely provided for quick-and-dirty standalone applications, for real applications, you should use a real server.

Glue for using this package with real servers is provided by:

- https://hackage.haskell.org/package/wai-websockets
- https://hackage.haskell.org/package/websockets-snap

runServerWith :: String -> Int -> ConnectionOptions -> ServerApp -> IO () #

A version of `runServer` which allows you to customize some options.

# Utilities for writing your own server

makeListenSocket :: String -> Int -> IO Socket #

Create a standardized socket on which you can listen for incomming connections. Should only be used for a quick and dirty solution! Should be preceded by the call `withSocketsDo`.

makePendingConnection :: Socket -> ConnectionOptions -> IO PendingConnection #

Turns a socket, connected to some client, into a `PendingConnection`. The `PendingConnection` should be closed using `close` later.

makePendingConnectionFromStream :: Stream -> ConnectionOptions -> IO PendingConnection #

More general version of `makePendingConnection` for `Stream` instead of a `Socket`.

# Running a client

type ClientApp a = Connection -> IO a #

A client application interacting with a single server. Once this `IO` action finished, the underlying socket is closed automatically.

runClient #

Arguments

:: String | Host
-> Int | Port
-> String | Path
-> ClientApp a | Client application
-> IO a |

runClientWith #

Arguments

:: String | Host
-> Int | Port
-> String | Path
-> ConnectionOptions | Options
-> Headers | Custom headers to send
-> ClientApp a | Client application
-> IO a |

runClientWithSocket #

Arguments

:: Socket | Socket
-> String | Host
-> String | Path
-> ConnectionOptions | Options
-> Headers | Custom headers to send
-> ClientApp a | Client application
-> IO a |

runClientWithStream #

Arguments

:: Stream | Stream
-> String | Host
-> String | Path
-> ConnectionOptions | Connection options
-> Headers | Custom headers to send
-> ClientApp a | Client application
-> IO a |

# Utilities

forkPingThread :: Connection -> Int -> IO () #

Forks a ping thread, sending a ping message every `n` seconds over the connection. The thread dies silently if the connection crashes or is closed.