pipes-network-0.1.1.0: Use network sockets together with the pipes library.

Safe HaskellNone

Control.Proxy.TCP.Safe

Contents

Description

This module exports functions that allow you to safely use Socket resources within a Proxy pipeline, possibly acquiring and releasing such resources within the pipeline itself, using the facilities provided by ExceptionP from the pipes-safe library.

Instead, if just want to use resources already acquired or released outside the pipeline, then you could use the simpler functions exported by Control.Proxy.TCP.

Synopsis

Server side

The following functions allow you to obtain and use Sockets useful to the server side of a TCP connection.

Here's how you could run a TCP server that handles in different threads each incoming connection to port 8000 at address 127.0.0.1:

 listen id (Host "127.0.0.1") "8000" $ \(listeningSocket, listeningAddr) -> do
   tryIO . putStrLn $ "Listening for incoming connections at " ++ show listeningAddr
   forever . acceptFork id listeningSocket $ \(connectionSocket, remoteAddr) -> do
     putStrLn $ "Connection established from " ++ show remoteAddr
     -- now you may use connectionSocket as you please within this scope,
     -- possibly with any of the socketReadS, nsocketReadS or socketWriteD
     -- proxies explained below.

If you keep reading you'll discover there are different ways to achieve the same, some ways more general than others. The above one was just an example using a pretty general approach, you are encouraged to use simpler approaches such as serve or serveReadS if those suit your needs.

serveSource

Arguments

:: (Proxy p, Monad m) 
=> (forall x. SafeIO x -> m x)

Monad morphism.

-> HostPreference

Preferred host to bind.

-> ServiceName

Service port to bind.

-> ((Socket, SockAddr) -> ExceptionP p a' a b' b m r)

Computation to run once an incoming connection is accepted. Takes the connection socket and remote end address.

-> ExceptionP p a' a b' b m r 

Start a TCP server that sequentially accepts and uses each incoming connection.

Both the listening and connection sockets are closed when done or in case of exceptions.

Note: You don't need to use listen nor accept if you use this function.

serveForkSource

Arguments

:: (Proxy p, Monad m) 
=> (forall x. SafeIO x -> m x)

Monad morphism.

-> HostPreference

Preferred host to bind.

-> ServiceName

Service port to bind.

-> ((Socket, SockAddr) -> IO ())

Computation to run in a different thread once an incoming connection is accepted. Takes the connection socket and remote end address.

-> ExceptionP p a' a b' b m r 

Start a TCP server that accepts incoming connections and uses them concurrently in different threads.

The listening and connection sockets are closed when done or in case of exceptions.

Note: You don't need to use listen nor acceptFork if you use this function.

Listening

listenSource

Arguments

:: (Proxy p, Monad m) 
=> (forall x. SafeIO x -> m x)

Monad morphism.

-> HostPreference

Preferred host to bind.

-> ServiceName

Service port to bind.

-> ((Socket, SockAddr) -> ExceptionP p a' a b' b m r)

Computation taking the listening socket and the address it's bound to.

-> ExceptionP p a' a b' b m r 

Bind a TCP listening socket and use it.

The listening socket is closed when done or in case of exceptions.

If you prefer to acquire and close the socket yourself, then use bindSock and the listen and sClose functions from Network.Socket instead.

Note: maxListenQueue is tipically 128, which is too small for high performance servers. So, we use the maximum between maxListenQueue and 2048 as the default size of the listening queue.

Accepting

acceptSource

Arguments

:: (Proxy p, Monad m) 
=> (forall x. SafeIO x -> m x)

Monad morphism.

-> Socket

Listening and bound socket.

-> ((Socket, SockAddr) -> ExceptionP p a' a b' b m r)

Computation to run once an incoming connection is accepted. Takes the connection socket and remote end address.

-> ExceptionP p a' a b' b m r 

Accept a single incoming connection and use it.

The connection socket is closed when done or in case of exceptions.

acceptForkSource

Arguments

:: (Proxy p, Monad m) 
=> (forall x. SafeIO x -> m x)

Monad morphism.

-> Socket

Listening and bound socket.

-> ((Socket, SockAddr) -> IO ())

Computation to run in a different thread once an incoming connection is accepted. Takes the connection socket and remote end address.

-> ExceptionP p a' a b' b m ThreadId 

Accept a single incoming connection and use it in a different thread.

The connection socket is closed when done or in case of exceptions.

Streaming

The following proxies allow you to easily run a TCP server and immediately interact with incoming connections using streams, all at once, instead of having to perform the individual steps separately.

serveReadSSource

Arguments

:: Proxy p 
=> Maybe Int

Optional timeout in microseconds (1/10^6 seconds).

-> Int

Maximum number of bytes to receive at once.

-> HostPreference

Preferred host to bind.

-> ServiceName

Service port to bind.

-> () 
-> Producer (ExceptionP p) ByteString SafeIO () 

Binds a listening socket, accepts a single connection and sends downstream any bytes received from the remote end.

If an optional timeout is given and receiveing data from the remote end takes more time that such timeout, then throw a Timeout exception in the ExceptionP proxy transformer.

Less than the specified maximum number of bytes might be received at once.

If the remote peer closes its side of the connection, this proxy returns.

Both the listening and connection sockets are closed when done or in case of exceptions.

Using this proxy you can write straightforward code like the following, which prints whatever is received from a single TCP connection to port 9000:

>>> :set -XOverloadedStrings
>>> runSafeIO . runProxy . runEitherK $ serveReadS Nothing 4096 "127.0.0.1" "9000" >-> tryK printD

serveWriteDSource

Arguments

:: Proxy p 
=> Maybe Int

Optional timeout in microseconds (1/10^6 seconds).

-> HostPreference

Preferred host to bind.

-> ServiceName

Service port to bind.

-> x 
-> ExceptionP p x ByteString x ByteString SafeIO () 

Binds a listening socket, accepts a single connection, sends to the remote end the bytes received from upstream, then forwards such sames bytes downstream.

Requests from downstream are forwarded upstream.

If an optional timeout is given and sending data to the remote end takes more time that such timeout, then throw a Timeout exception in the ExceptionP proxy transformer.

Both the listening and connection sockets are closed when done or in case of exceptions.

Using this proxy you can write straightforward code like the following, which greets a TCP client connecting to port 9000:

>>> :set -XOverloadedStrings
>>> runSafeIO . runProxy . runEitherK $ fromListS ["He","llo\r\n"] >-> serveWriteD Nothing "127.0.0.1" "9000"

Client side

The following functions allow you to obtain and use Sockets useful to the client side of a TCP connection.

Here's how you could run a TCP client:

 connect id "www.example.org" "80" $ \(connectionSocket, remoteAddr) -> do
   tryIO . putStrLn $ "Connection established to " ++ show remoteAddr
   -- now you may use connectionSocket as you please within this scope,
   -- possibly with any of the socketReadS, nsocketReadS or socketWriteD
   -- proxies explained below.

You might instead prefer the simpler but less general solutions offered by connectReadS and connectWriteD, so check those too.

connectSource

Arguments

:: (Proxy p, Monad m) 
=> (forall x. SafeIO x -> m x)

Monad morphism.

-> HostName

Server hostname.

-> ServiceName

Server service port.

-> ((Socket, SockAddr) -> ExceptionP p a' a b' b m r)

Computation taking the communication socket and the server address.

-> ExceptionP p a' a b' b m r 

Connect to a TCP server and use the connection.

The connection socket is closed when done or in case of exceptions.

If you prefer to acquire close the socket yourself, then use connectSock and the sClose from Network.Socket instead.

Streaming

The following proxies allow you to easily connect to a TCP server and immediately interact with it using streams, all at once, instead of having to perform the individual steps separately.

connectReadSSource

Arguments

:: Proxy p 
=> Maybe Int

Optional timeout in microseconds (1/10^6 seconds).

-> Int

Maximum number of bytes to receive at once.

-> HostName

Server host name.

-> ServiceName

Server service port.

-> () 
-> Producer (ExceptionP p) ByteString SafeIO () 

Connect to a TCP server and send downstream the bytes received from the remote end.

If an optional timeout is given and receiveing data from the remote end takes more time that such timeout, then throw a Timeout exception in the ExceptionP proxy transformer.

The connection socket is closed when done or in case of exceptions.

Using this proxy you can write straightforward code like the following, which prints whatever is received from a single TCP connection to a given server listening locally on port 9000:

>>> runSafeIO . runProxy . runEitherK $ connectReadS Nothing 4096 "127.0.0.1" "9000" >-> tryK printD

connectWriteDSource

Arguments

:: Proxy p 
=> Maybe Int

Optional timeout in microseconds (1/10^6 seconds).

-> HostName

Server host name.

-> ServiceName

Server service port.

-> x 
-> ExceptionP p x ByteString x ByteString SafeIO () 

Connects to a TCP server, sends to the remote end the bytes received from upstream, then forwards such same bytes downstream.

Requests from downstream are forwarded upstream.

If an optional timeout is given and sending data to the remote end takes more time that such timeout, then throw a Timeout exception in the ExceptionP proxy transformer.

The connection socket is closed when done or in case of exceptions.

Using this proxy you can write straightforward code like the following, which greets a TCP client listening locally at port 9000:

>>> :set -XOverloadedStrings
>>> runSafeIO . runProxy . runEitherK $ fromListS ["He","llo\r\n"] >-> connectWriteD Nothing "127.0.0.1" "9000"

Socket streams

Once you have a connected Socket, you can use the following Proxys to interact with the other connection end using streams.

socketReadSSource

Arguments

:: Proxy p 
=> Maybe Int

Optional timeout in microseconds (1/10^6 seconds).

-> Int

Maximum number of bytes to receive at once.

-> Socket

Connected socket.

-> () 
-> Producer (ExceptionP p) ByteString SafeIO () 

Receives bytes from the remote end and sends them downstream.

If an optional timeout is given and receiveing data from the remote end takes more time that such timeout, then throw a Timeout exception in the ExceptionP proxy transformer.

Less than the specified maximum number of bytes might be received at once.

If the remote peer closes its side of the connection, this proxy returns.

nsocketReadSSource

Arguments

:: Proxy p 
=> Maybe Int

Optional timeout in microseconds (1/10^6 seconds).

-> Socket

Connected socket.

-> Int 
-> Server (ExceptionP p) Int ByteString SafeIO () 

Just like socketReadS, except each request from downstream specifies the maximum number of bytes to receive.

socketWriteDSource

Arguments

:: Proxy p 
=> Maybe Int

Optional timeout in microseconds (1/10^6 seconds).

-> Socket

Connected socket.

-> x 
-> ExceptionP p x ByteString x ByteString SafeIO r 

Sends to the remote end the bytes received from upstream, then forwards such same bytes downstream.

If an optional timeout is given and sending data to the remote end takes more time that such timeout, then throw a Timeout exception in the ExceptionP proxy transformer.

Requests from downstream are forwarded upstream.

Exports

data HostPreference

Preferred host to bind.

Constructors

HostAny

Any available host.

HostIPv4

Any available IPv4 host.

HostIPv6

Any available IPv6 host.

Host HostName

An explicit host name.

Instances

Eq HostPreference 
Ord HostPreference 
Read HostPreference 
Show HostPreference 
IsString HostPreference

The following special values are recognized:

data Timeout Source

Exception thrown when a timeout has elapsed.

Constructors

Timeout String

Timeouted with an additional explanatory message.