acid-state-0.16.0.1: Add ACID guarantees to any serializable Haskell data structure.

Copyright | PublicDomain
Maintainer | lemmih@gmail.com
Portability | non-portable (uses GHC extensions)
Safe Haskell | None
Language | Haskell2010

Data.Acid.Remote

Contents

- Server/Client
- Authentication
- Exception type
- Low-Level functions needed to implement additional communication channels

Description

This module provides the ability perform `update` and `query` calls from a remote process.

On the server-side you:

1. open your `AcidState` normally
2. then use `acidServer` to share the state

On the client-side you:

1. use `openRemoteState` to connect to the remote state
2. use the returned `AcidState` like any other `AcidState` handle

`openRemoteState` and `acidServer` communicate over an unencrypted socket. If you need an encrypted connection, see `acid-state-tls`.

On Unix®-like systems you can use `SockAddrUnix` to create a socket file for local communication between the client and server. Access can be controlled by setting the permissions of the parent directory containing the socket file.

It is also possible to perform some simple authentication using `sharedSecretCheck` and `sharedSecretPerform`. Keep in mind that secrets will be sent in plain-text if you do not use `acid-state-tls`. If you are using a `SockAddrUnix` additional authentication may not be required, so you can use `skipAuthenticationCheck` and `skipAuthenticationPerform`.

Working with a remote `AcidState` is nearly identical to working with a local `AcidState` with a few important differences.

The connection to the remote `AcidState` can be lost. The client will automatically attempt to reconnect every second. Because `query` events do not affect the state, an aborted `query` will be retried automatically after the server is reconnected.

If the connection was lost during an `update` event, the event will not be retried. Instead `RemoteConnectionError` will be raised. This is because it is impossible for the client to know if the aborted update completed on the server-side or not.

When using a local `AcidState`, an update event in one thread does not block query events taking place in other threads. With a remote connection, all queries and requests are channeled over a single connection. As a result, updates and queries are performed in the order they are executed and do block each other. In the rare case where this is an issue, you could create one remote connection per thread.

When working with local state, a query or update which returns the whole state is not usually a problem due to memory sharing. The update/query event basically just needs to return a pointer to the data already in memory. But, when working remotely, the entire result will be serialized and sent to the remote client. Hence, it is good practice to create queries and updates that will only return the required data.

This module is designed to be extenible. You can easily add your own authentication methods by creating a suitable pair of functions and passing them to `acidServer` and `openRemoteState`.

It is also possible to create alternative communication layers using `CommChannel`, `process`, and `processRemoteState`.

Synopsis

- acidServer :: (CommChannel -> IO Bool) -> PortNumber -> AcidState st -> IO ()
- acidServerSockAddr :: (CommChannel -> IO Bool) -> SockAddr -> AcidState st -> IO ()
- acidServer' :: (CommChannel -> IO Bool) -> Socket -> AcidState st -> IO ()
- openRemoteState :: IsAcidic st => (CommChannel -> IO ()) -> HostName -> PortNumber -> IO (AcidState st)
- openRemoteStateSockAddr :: IsAcidic st => (CommChannel -> IO ()) -> SockAddr -> IO (AcidState st)
- skipAuthenticationCheck :: CommChannel -> IO Bool
- skipAuthenticationPerform :: CommChannel -> IO ()
- sharedSecretCheck :: Set ByteString -> CommChannel -> IO Bool
- sharedSecretPerform :: ByteString -> CommChannel -> IO ()
- data AcidRemoteException
  - = RemoteConnectionError
  - | AcidStateClosed
  - | SerializeError String
  - | AuthenticationError String
- data CommChannel = CommChannel {
  - ccPut :: ByteString -> IO ()
  - ccGetSome :: Int -> IO ByteString
  - ccClose :: IO ()
  }
- process :: CommChannel -> AcidState st -> IO ()
- processRemoteState :: IsAcidic st => IO CommChannel -> IO (AcidState st)

# Server/Client

acidServer Source #

Arguments

:: (CommChannel -> IO Bool) | check authentication, see `sharedSecretPerform`
-> PortNumber | Port to listen on
-> AcidState st | state to serve
-> IO () |

Accept connections on `port` and handle requests using the given `AcidState`. This call doesn't return.

see also: `acidServerSockAddr`, `openRemoteState` and `sharedSecretCheck`.

acidServerSockAddr Source #

Arguments

:: (CommChannel -> IO Bool) | check authentication, see `sharedSecretPerform`
-> SockAddr | SockAddr to listen on
-> AcidState st | state to serve
-> IO () |

Accept connections on `sockAddr` and handle requests using the given `AcidState`. This call doesn't return.

see also: `acidServer`, `openRemoteState` and `sharedSecretCheck`.

acidServer' Source #

Arguments

:: (CommChannel -> IO Bool) | check authentication, see `sharedSecretPerform`
-> Socket | binded socket to accept connections from
-> AcidState st | state to serve
-> IO () |

Works the same way as `acidServer`, but uses pre-binded socket `listenSocket`.

Can be useful when fine-tuning of socket binding parameters is needed (for example, listening on a particular network interface, IPv4/IPv6 options).

openRemoteState Source #

Arguments

:: IsAcidic st |
=> (CommChannel -> IO ()) | authentication function, see `sharedSecretPerform`
-> HostName | remote host to connect to
-> PortNumber | remote port to connect to
-> IO (AcidState st) |

Connect to an acid-state server which is sharing an `AcidState`.

openRemoteStateSockAddr Source #

Arguments

:: IsAcidic st |
=> (CommChannel -> IO ()) | authentication function, see `sharedSecretPerform`
-> SockAddr | remote SockAddr to connect to
-> IO (AcidState st) |

Connect to an acid-state server which is sharing an `AcidState`.

# Authentication

skipAuthenticationCheck :: CommChannel -> IO Bool Source #

skip server-side authentication checking entirely.

skipAuthenticationPerform :: CommChannel -> IO () Source #

skip client-side authentication entirely.

sharedSecretCheck Source #

Arguments

:: Set ByteString | set of shared secrets
-> CommChannel -> IO Bool |

check that the client knows a shared secret.

The function takes a `Set` of shared secrets. If a client knows any of them, it is considered to be trusted.

The shared secret is any `ByteString` of your choice.

If you give each client a different shared secret then you can revoke access individually.

see also: `sharedSecretPerform`

sharedSecretPerform Source #

Arguments

:: ByteString | shared secret
-> CommChannel -> IO () |

attempt to authenticate with the server using a shared secret.

# Exception type

data AcidRemoteException Source #

Constructors

RemoteConnectionError |
AcidStateClosed |
SerializeError String |
AuthenticationError String |

#### Instances

Instances details

Eq AcidRemoteException Source # |
Instance details Defined in Data.Acid.Remote Methods (==) :: AcidRemoteException -> AcidRemoteException -> Bool (/=) :: AcidRemoteException -> AcidRemoteException -> Bool
Show AcidRemoteException Source # |
Instance details Defined in Data.Acid.Remote Methods showsPrec :: Int -> AcidRemoteException -> ShowS show :: AcidRemoteException -> String showList :: [AcidRemoteException] -> ShowS
Exception AcidRemoteException Source # |
Instance details Defined in Data.Acid.Remote Methods toException :: AcidRemoteException -> SomeException fromException :: SomeException -> Maybe AcidRemoteException displayException :: AcidRemoteException -> String

# Low-Level functions needed to implement additional communication channels

data CommChannel Source #

`CommChannel` is a record containing the IO functions we need for communication between the server and client.

We abstract this out of the core processing function so that we can easily add support for SSL/TLS and Unit testing.

Constructors

CommChannel |

Fields

- ccPut :: ByteString -> IO ()
- ccGetSome :: Int -> IO ByteString
- ccClose :: IO ()

process Source #

Arguments

:: CommChannel | a connected, authenticated communication channel
-> AcidState st | state to share
-> IO () |

Server inner-loop

This function is generally only needed if you are adding a new communication channel.

processRemoteState Source #

Arguments

:: IsAcidic st |
=> IO CommChannel | (re-)connect function
-> IO (AcidState st) |

Client inner-loop

This function is generally only needed if you are adding a new communication channel.