packages feed

cql-io 0.14.5 → 0.15.2

raw patch · 9 files changed

+186/−420 lines, 9 filesdep +HsOpenSSLdep +retrydep ~asyncPVP ok

version bump matches the API change (PVP)

Dependencies added: HsOpenSSL, retry

Dependency ranges changed: async

API changes (from Hackage documentation)

- Database.CQL.IO: acceptable :: Policy -> Host -> IO Bool
- Database.CQL.IO: current :: Policy -> IO [Host]
- Database.CQL.IO: display :: Policy -> IO String
- Database.CQL.IO: hasMore :: Page a -> !Bool
- Database.CQL.IO: hostCount :: Policy -> IO Word
- Database.CQL.IO: hostInfo :: DebugInfo -> [Host]
- Database.CQL.IO: instance Functor Page
- Database.CQL.IO: instance RunQ PrepQuery
- Database.CQL.IO: instance RunQ QueryString
- Database.CQL.IO: jobInfo :: DebugInfo -> [InetAddr]
- Database.CQL.IO: nextPage :: Page a -> Client (Page a)
- Database.CQL.IO: onEvent :: Policy -> HostEvent -> IO ()
- Database.CQL.IO: policyInfo :: DebugInfo -> String
- Database.CQL.IO: result :: Page a -> [a]
- Database.CQL.IO: select :: Policy -> IO (Maybe Host)
- Database.CQL.IO: setup :: Policy -> [Host] -> [Host] -> IO ()
- Database.CQL.IO: sockAddr :: InetAddr -> SockAddr
+ Database.CQL.IO: [acceptable] :: Policy -> Host -> IO Bool
+ Database.CQL.IO: [current] :: Policy -> IO [Host]
+ Database.CQL.IO: [display] :: Policy -> IO String
+ Database.CQL.IO: [hasMore] :: Page a -> !Bool
+ Database.CQL.IO: [hostCount] :: Policy -> IO Word
+ Database.CQL.IO: [hostInfo] :: DebugInfo -> [Host]
+ Database.CQL.IO: [jobInfo] :: DebugInfo -> [InetAddr]
+ Database.CQL.IO: [nextPage] :: Page a -> Client (Page a)
+ Database.CQL.IO: [onEvent] :: Policy -> HostEvent -> IO ()
+ Database.CQL.IO: [policyInfo] :: DebugInfo -> String
+ Database.CQL.IO: [result] :: Page a -> [a]
+ Database.CQL.IO: [select] :: Policy -> IO (Maybe Host)
+ Database.CQL.IO: [setup] :: Policy -> [Host] -> [Host] -> IO ()
+ Database.CQL.IO: [sockAddr] :: InetAddr -> SockAddr
+ Database.CQL.IO: instance Database.CQL.IO.RunQ Database.CQL.IO.PrepQuery.PrepQuery
+ Database.CQL.IO: instance Database.CQL.IO.RunQ Database.CQL.Protocol.Types.QueryString
+ Database.CQL.IO: instance GHC.Base.Functor Database.CQL.IO.Page
+ Database.CQL.IO: setSSLContext :: SSLContext -> Settings -> Settings

Files

CHANGELOG.txt view
@@ -1,6 +1,18 @@+0.15.2+------+- Update `async` dependency++0.15.1+------+- Use `retry >= 0.7` instead of internal module++0.15.0+------+- Add experimental TLS support+ 0.14.5 -------- Add `trans` to execute "lightweight transactions".+- Add `trans` to execute "lightweight transactions"  0.14.4 ------
README.asciidoc view
@@ -29,3 +29,8 @@ Prepared queries are an optimisation which parse and prepare a query only once on Cassandra nodes but execute it many times with different concrete values.++.TLS support++Client to node communication can optionally use transport layer security+(using HsOpenSSL).
cql-io.cabal view
@@ -1,12 +1,12 @@ name:                 cql-io-version:              0.14.5+version:              0.15.2 synopsis:             Cassandra CQL client. stability:            experimental license:              MPL-2.0 license-file:         LICENSE.txt author:               Toralf Wittner maintainer:           Toralf Wittner <tw@dtex.org>-copyright:            (C) 2014-2015 Toralf Wittner+copyright:            (C) 2014-2016 Toralf Wittner homepage:             https://github.com/twittner/cql-io/ bug-reports:          https://github.com/twittner/cql-io/issues category:             Database@@ -39,6 +39,9 @@     * /Prepared queries/. Prepared queries are an optimisation which parse     and prepare a query only once on Cassandra nodes but execute it many     times with different concrete values.+    .+    * /TLS support/. Client to node communication can optionally use transport+    layer security (using HsOpenSSL).  source-repository head     type:             git@@ -53,13 +56,13 @@         Database.CQL.IO      other-modules:-        Control.Retry         Database.CQL.IO.Batch         Database.CQL.IO.Client         Database.CQL.IO.Cluster.Discovery         Database.CQL.IO.Cluster.Host         Database.CQL.IO.Cluster.Policies         Database.CQL.IO.Connection+        Database.CQL.IO.Connection.Socket         Database.CQL.IO.Hexdump         Database.CQL.IO.Jobs         Database.CQL.IO.Pool@@ -73,7 +76,7 @@         Database.CQL.IO.Types      build-depends:-          async              == 2.0.*+          async              >= 2.0         , auto-update        == 0.1.*         , base               >= 4.7     && < 5.0         , bytestring         >= 0.10    && < 1.0@@ -84,10 +87,12 @@         , exceptions         >= 0.4     && < 1.0         , hashable           >= 1.2     && < 2.0         , iproute            >= 1.3     && < 2.0+        , HsOpenSSL          >= 0.11    && < 1.0         , lens               >= 4.4     && < 5.0         , monad-control      >= 0.3     && < 2.0         , mtl                >= 2.1     && < 2.5         , mwc-random         >= 0.13    && < 0.14+        , retry              >= 0.7     && < 1.0         , network            >= 2.4     && < 3.0         , semigroups         >= 0.15    && < 0.20         , stm                >= 2.4     && < 3.0
− src/Control/Retry.hs
@@ -1,341 +0,0 @@--- This is a copy of Ozgun Ataman's retry module version 0.6--- available from http://hackage.haskell.org/package/retry------ It includes changes introduced by pull request 17 in--- https://github.com/Soostone/retry/pull/17------ If PR 17 is merged into upstream it can be removed again.--{-# LANGUAGE BangPatterns          #-}-{-# LANGUAGE CPP                   #-}-{-# LANGUAGE FlexibleContexts      #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE RankNTypes            #-}-{-# LANGUAGE RecordWildCards       #-}-{-# LANGUAGE ScopedTypeVariables   #-}-{-# LANGUAGE ViewPatterns          #-}---------------------------------------------------------------------------------- |--- Module      :  Control.Retry--- Copyright   :  Ozgun Ataman <ozgun.ataman@soostone.com>--- License     :  BSD3------ Maintainer  :  Ozgun Ataman--- Stability   :  provisional------ This module exposes combinators that can wrap arbitrary monadic--- actions. They run the action and potentially retry running it with--- some configurable delay for a configurable number of times.------ The express purpose of this library is to make it easier to work--- with IO and especially network IO actions that often experience--- temporary failure that warrant retrying of the original action. For--- example, a database query may time out for a while, in which case--- we should delay a bit and retry the query.--------------------------------------------------------------------------------module Control.Retry-    (-      -- * High Level Operation-      RetryPolicy (..)--    , retrying-    , recovering-    , recoverAll-    , logRetries--    -- * Retry Policies-    , constantDelay-    , exponentialBackoff-    , fibonacciBackoff-    , limitRetries-    , limitRetriesByDelay-    , capDelay--    -- * Re-export from Data.Monoid--    , (<>)--    ) where----------------------------------------------------------------------------------import           Control.Concurrent-import           Control.Monad.Catch-import           Control.Monad.IO.Class-import           Data.Default.Class-import           Data.Monoid--------------------------------------------------------------------------------------------------------------------------------------------------------------------- | A 'RetryPolicy' is a function that takes an iteration number and--- possibly returns a delay in microseconds. *Nothing* implies we have--- reached the retry limit.------ Please note that 'RetryPolicy' is a 'Monoid'. You can collapse--- multiple strategies into one using 'mappend' or '<>'. The semantics--- of this combination are as follows:------ 1. If either policy returns 'Nothing', the combined policy returns--- 'Nothing'. This can be used to @inhibit@ after a number of retries,--- for example.------ 2. If both policies return a delay, the larger delay will be used.--- This is quite natural when combining multiple policies to achieve a--- certain effect.------ Example:------ One can easily define an exponential backoff policy with a limited--- number of retries:------ >> limitedBackoff = exponentialBackoff 50 <> limitRetries 5------ Naturally, 'mempty' will retry immediately (delay 0) for an--- unlimited number of retries, forming the identity for the 'Monoid'.------ The default under 'def' implements a constant 50ms delay, up to 5 times:------ >> def = constantDelay 50000 <> limitRetries 5------ For anything more complex, just define your own 'RetryPolicy':------ >> myPolicy = RetryPolicy $ \ n -> if n > 10 then Just 1000 else Just 10000-newtype RetryPolicy = RetryPolicy { getRetryPolicy :: Int -> Maybe Int }---instance Default RetryPolicy where-    def = constantDelay 50000 <> limitRetries 5--instance Monoid RetryPolicy where-    mempty = RetryPolicy $ (const (Just 0))-    (RetryPolicy a) `mappend` (RetryPolicy b) = RetryPolicy $ \ n -> do-      a' <- a n-      b' <- b n-      return $! max a' b'------------------------------------------------------------------------------------- | Retry immediately, but only up to @n@ times.-limitRetries-    :: Int-    -- ^ Maximum number of retries.-    -> RetryPolicy-limitRetries i = RetryPolicy $ \ n -> if n >= i then Nothing else (Just 0)------------------------------------------------------------------------------------- | Add an upperbound to a policy such that once the given time-delay--- amount has been reached or exceeded, the policy will stop retrying--- and fail.-limitRetriesByDelay-    :: Int-    -- ^ Time-delay limit in microseconds.-    -> RetryPolicy-    -> RetryPolicy-limitRetriesByDelay i p = RetryPolicy $ \ n -> getRetryPolicy p n >>= limit-  where-    limit delay = if delay >= i then Nothing else Just delay------------------------------------------------------------------------------------- | Implement a constant delay with unlimited retries.-constantDelay-    :: Int-    -- ^ Base delay in microseconds-    -> RetryPolicy-constantDelay delay = RetryPolicy (const (Just delay))------------------------------------------------------------------------------------- | Grow delay exponentially each iteration.-exponentialBackoff-    :: Int-    -- ^ Base delay in microseconds-    -> RetryPolicy-exponentialBackoff base = RetryPolicy $ \ n -> Just (2^n * base)------------------------------------------------------------------------------------- | Implement Fibonacci backoff.-fibonacciBackoff-    :: Int-    -- ^ Base delay in microseconds-    -> RetryPolicy-fibonacciBackoff base = RetryPolicy $ \ n -> Just $ fib (n + 1) (0, base)-    where-      fib 0 (a, _) = a-      fib !m (!a, !b) = fib (m-1) (b, a + b)------------------------------------------------------------------------------------- | Set a time-upperbound for any delays that may be directed by the--- given policy.  This function does not terminate the retrying.  The policy--- `capDelay maxDelay (exponentialBackoff n)` will never stop retrying.  It--- will reach a state where it retries forever with a delay of `maxDelay`--- between each one.  To get termination you need to use one of the--- 'limitRetries' function variants.-capDelay-    :: Int-    -- ^ A maximum delay in microseconds-    -> RetryPolicy-    -> RetryPolicy-capDelay limit p = RetryPolicy $ \ n -> min limit `fmap` (getRetryPolicy p) n------------------------------------------------------------------------------------- | Retry combinator for actions that don't raise exceptions, but--- signal in their type the outcome has failed. Examples are the--- 'Maybe', 'Either' and 'EitherT' monads.------ Let's write a function that always fails and watch this combinator--- retry it 5 additional times following the initial run:------ >>> import Data.Maybe--- >>> let f = putStrLn "Running action" >> return Nothing--- >>> retrying def (const $ return . isNothing) f--- Running action--- Running action--- Running action--- Running action--- Running action--- Running action--- Nothing------ Note how the latest failing result is returned after all retries--- have been exhausted.-retrying :: MonadIO m-         => RetryPolicy-         -> (Int -> b -> m Bool)-         -- ^ An action to check whether the result should be retried.-         -- If True, we delay and retry the operation.-         -> (Int -> m b)-         -- ^ Action to run-         -> m b-retrying (RetryPolicy policy) chk f = go 0-    where-      go n = do-          res <- f n-          chk' <- chk n res-          case chk' of-            True ->-              case (policy n) of-                Just delay -> do-                  liftIO (threadDelay delay)-                  go $! n+1-                Nothing -> return res-            False -> return res-------------------------------------------------------------------------------------- | Retry ALL exceptions that may be raised. To be used with caution;--- this matches the exception on 'SomeException'.------ See how the action below is run once and retried 5 more times--- before finally failing for good:------ >>> let f = putStrLn "Running action" >> error "this is an error"--- >>> recoverAll def f--- Running action--- Running action--- Running action--- Running action--- Running action--- Running action--- *** Exception: this is an error-recoverAll-#if MIN_VERSION_exceptions(0, 6, 0)-         :: (MonadIO m, MonadMask m)-#else-         :: (MonadIO m, MonadCatch m)-#endif-         => RetryPolicy-         -> (Int -> m a)-         -> m a-recoverAll set f = recovering set [h] f-    where-      h _ = Handler $ \ (_ :: SomeException) -> return True------------------------------------------------------------------------------------- | Run an action and recover from a raised exception by potentially--- retrying the action a number of times.-recovering-#if MIN_VERSION_exceptions(0, 6, 0)-           :: (MonadIO m, MonadMask m)-#else-           :: (MonadIO m, MonadCatch m)-#endif-           => RetryPolicy-           -- ^ Just use 'def' for default settings-           -> [(Int -> Handler m Bool)]-           -- ^ Should a given exception be retried? Action will be-           -- retried if this returns True.-           -> (Int -> m a)-           -- ^ Action to perform-           -> m a-recovering (RetryPolicy policy) hs f = mask $ \restore -> go restore 0-    where-      go restore = loop-        where-          loop n = do-            r <- try $ restore (f n)-            case r of-              Right x -> return x-              Left e -> recover (e :: SomeException) hs-            where-              recover e [] = throwM e-              recover e ((($ n) -> Handler h) : hs')-                | Just e' <- fromException e = do-                    chk <- h e'-                    if chk-                      then case policy n of-                        Just delay -> do-                          liftIO $ threadDelay delay-                          loop $! n+1-                        Nothing -> throwM e'-                      else throwM e'-                | otherwise = recover e hs'------------------------------------------------------------------------------------- | Helper function for constructing handler functions of the form required--- by 'recovering'.-logRetries-    :: (Monad m, Show e, Exception e)-    => (e -> m Bool)-    -- ^ Test for whether action is to be retried-    -> (String -> m ())-    -- ^ How to report the generated warning message.-    -> Int-    -- ^ Retry number-    -> Handler m Bool-logRetries f report n = Handler $ \ e -> do-    res <- f e-    let msg = "[retry:" <> show n <> "] Encountered " <> show e <> ". " <>-              if res then "Retrying." else "Crashing."-    report msg-    return res---                              -------------------                              -- Simple Tests ---                              ------------------------ data TestException = TestException deriving (Show, Typeable)--- data AnotherException = AnotherException deriving (Show, Typeable)---- instance Exception TestException--- instance Exception AnotherException----- test = retrying def [h1,h2] f---     where---       f = putStrLn "Running action" >> throwM AnotherException---       h1 = Handler $ \ (e :: TestException) -> return False---       h2 = Handler $ \ (e :: AnotherException) -> return True
src/Database/CQL/IO.hs view
@@ -75,6 +75,7 @@     , setSendTimeout     , setRetrySettings     , setMaxRecvBuffer+    , setSSLContext        -- ** Retry Settings     , RetrySettings
src/Database/CQL/IO/Client.hs view
@@ -51,7 +51,7 @@ #if MIN_VERSION_transformers(0,4,0) import Control.Monad.Trans.Except #endif-import Control.Retry+import Control.Retry (recovering, capDelay, exponentialBackoff, rsIterNumber) import Data.Foldable (for_, foldrM) import Data.List (find) import Data.List.NonEmpty (NonEmpty (..))@@ -73,6 +73,7 @@ import Database.CQL.IO.Types import Database.CQL.Protocol hiding (Map) import Network.Socket (SockAddr (..), PortNumber)+import OpenSSL.Session (SomeSSLException) import System.Logger.Class hiding (Settings, new, settings, create) import Prelude @@ -226,7 +227,9 @@ mkRequest fn a = do     s <- ask     recovering (s^.context.settings.retrySettings.retryPolicy) recoverFrom $ \i -> do-        r <- if i == 0 then fn a s else fn (newRequest s) (adjust s)+        r <- if rsIterNumber i == 0+                 then fn a s+                 else fn (newRequest s) (adjust s)         maybe (throwM HostsBusy) return r   where     adjust s =@@ -254,9 +257,10 @@             Unavailable  {} -> return True             ServerError  {} -> return True             _               -> return False-        , const $ Handler $ \(_ :: ConnectionError) -> return True-        , const $ Handler $ \(_ :: IOException)     -> return True-        , const $ Handler $ \(_ :: HostError)       -> return True+        , const $ Handler $ \(_ :: ConnectionError)  -> return True+        , const $ Handler $ \(_ :: IOException)      -> return True+        , const $ Handler $ \(_ :: HostError)        -> return True+        , const $ Handler $ \(_ :: SomeSSLException) -> return True         ]  -- | Invoke 'request1' up to @n@ times with different hosts if no@@ -292,8 +296,9 @@         r `seq` return (h, r)      handlers =-        [ Handler $ \(e :: ConnectionError) -> onConnectionError h e >> throwM e-        , Handler $ \(e :: IOException)     -> onConnectionError h e >> throwM e+        [ Handler $ \(e :: ConnectionError)  -> onConnectionError h e >> throwM e+        , Handler $ \(e :: IOException)      -> onConnectionError h e >> throwM e+        , Handler $ \(e :: SomeSSLException) -> onConnectionError h e >> throwM e         ]  -- | Execute the given request. If an 'Unprepared' error is returned, this@@ -555,9 +560,10 @@     reconnectPolicy = capDelay 5000000 (exponentialBackoff 5000)      reconnectHandlers =-        [ const (Handler $ \(_ :: IOException)     -> return True)-        , const (Handler $ \(_ :: ConnectionError) -> return True)-        , const (Handler $ \(_ :: HostError)       -> return True)+        [ const (Handler $ \(_ :: IOException)      -> return True)+        , const (Handler $ \(_ :: ConnectionError)  -> return True)+        , const (Handler $ \(_ :: HostError)        -> return True)+        , const (Handler $ \(_ :: SomeSSLException) -> return True)         ]  replaceControl :: InetAddr -> Client ()
src/Database/CQL/IO/Connection.hs view
@@ -2,8 +2,6 @@ -- License, v. 2.0. If a copy of the MPL was not distributed with this -- file, You can obtain one at http://mozilla.org/MPL/2.0/. -{-# LANGUAGE BangPatterns        #-}-{-# LANGUAGE CPP                 #-} {-# LANGUAGE OverloadedStrings   #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TemplateHaskell     #-}@@ -32,6 +30,7 @@     , compression     , defKeyspace     , maxRecvBuffer+    , tlsContext     ) where  import Control.Applicative@@ -44,15 +43,15 @@ import Control.Monad import Control.Monad.Catch import Control.Monad.IO.Class-import Data.ByteString.Builder import Data.ByteString.Lazy (ByteString) import Data.Int-import Data.Maybe (isJust, fromMaybe)+import Data.Maybe (fromMaybe) import Data.Monoid import Data.Text.Lazy (fromStrict) import Data.Unique import Data.Vector (Vector, (!)) import Database.CQL.Protocol+import Database.CQL.IO.Connection.Socket (Socket) import Database.CQL.IO.Hexdump import Database.CQL.IO.Protocol import Database.CQL.IO.Signal hiding (connect)@@ -60,23 +59,20 @@ import Database.CQL.IO.Types import Database.CQL.IO.Tickets (Pool, toInt, markAvailable) import Database.CQL.IO.Timeouts (TimeoutManager, withTimeout)-import Foreign.C.Types (CInt (..))-import Network-import Network.Socket hiding (connect, close, recv, send)-import Network.Socket.ByteString.Lazy (sendAll)+import Network.Socket hiding (Socket, close, connect, send)+import OpenSSL.Session (SSLContext) import System.IO (nativeNewline, Newline (..)) import System.Logger hiding (Settings, close, defSettings, settings) import System.Timeout import Prelude -import qualified Data.ByteString            as B-import qualified Data.ByteString.Lazy       as L-import qualified Data.ByteString.Lazy.Char8 as Char8-import qualified Data.Vector                as Vector-import qualified Database.CQL.IO.Sync       as Sync-import qualified Database.CQL.IO.Tickets    as Tickets-import qualified Network.Socket             as S-import qualified Network.Socket.ByteString  as NB+import qualified Data.ByteString.Lazy              as L+import qualified Data.ByteString.Lazy.Char8        as Char8+import qualified Data.Vector                       as Vector+import qualified Database.CQL.IO.Connection.Socket as Socket+import qualified Database.CQL.IO.Sync              as Sync+import qualified Database.CQL.IO.Tickets           as Tickets+import qualified Network.Socket                    as S  data ConnectionSettings = ConnectionSettings     { _connectTimeout  :: !Milliseconds@@ -86,6 +82,7 @@     , _compression     :: !Compression     , _defKeyspace     :: !(Maybe Keyspace)     , _maxRecvBuffer   :: !Int+    , _tlsContext      :: !(Maybe SSLContext)     }  type Streams = Vector (Sync (Header, ByteString))@@ -116,7 +113,7 @@     show = Char8.unpack . eval . bytes  instance ToBytes Connection where-    bytes c = bytes (c^.address) +++ val "#" +++ fd (c^.sock)+    bytes c = bytes (c^.address) +++ val "#" +++ c^.sock  defSettings :: ConnectionSettings defSettings =@@ -127,6 +124,7 @@                        noCompression -- compression                        Nothing       -- keyspace                        16384         -- receive buffer size+                       Nothing       -- no tls by default  resolve :: String -> PortNumber -> IO [InetAddr] resolve host port =@@ -136,15 +134,7 @@  connect :: MonadIO m => ConnectionSettings -> TimeoutManager -> Version -> Logger -> InetAddr -> m Connection connect t m v g a = liftIO $ do-    c <- bracketOnError (mkSock a) S.close $ \s -> do-        ok <- timeout (ms (t^.connectTimeout) * 1000) (S.connect s (sockAddr a))-        unless (isJust ok) $-            throwM (ConnectTimeout a)-        open s-    validateSettings c `onException` close c-    return c-  where-    open s = do+    c <- bracketOnError (Socket.open (t^.connectTimeout) a (t^.tlsContext)) Socket.close $ \s -> do         tck <- Tickets.pool (t^.maxStreams)         syn <- Vector.replicateM (t^.maxStreams) Sync.create         lck <- newMVar ()@@ -152,19 +142,11 @@         sig <- signal         rdr <- async (readLoop v g t tck a s syn sig sta lck)         Connection t a m v s sta syn lck rdr tck g sig <$> newUnique--mkSock :: InetAddr -> IO Socket-mkSock (InetAddr a) = socket (familyOf a) Stream defaultProtocol-  where-    familyOf (SockAddrInet  _ _)     = AF_INET-    familyOf (SockAddrInet6 _ _ _ _) = AF_INET6-    familyOf (SockAddrUnix  _)       = AF_UNIX-#if MIN_VERSION_network(2,6,1)-    familyOf (SockAddrCan   _      ) = AF_CAN-#endif+    validateSettings c `onException` close c+    return c  ping :: MonadIO m => InetAddr -> m Bool-ping a = liftIO $ bracket (mkSock a) S.close $ \s ->+ping a = liftIO $ bracket (Socket.mkSock a) S.close $ \s ->     fromMaybe False <$> timeout 5000000         ((S.connect s (sockAddr a) >> return True) `catchAll` const (return False)) @@ -201,8 +183,8 @@             Tickets.close (ConnectionClosed i) tck             Vector.mapM_ (Sync.close (ConnectionClosed i)) syn             void $ forkIOWithUnmask $ \unmask -> unmask $ do-                shutdown sck ShutdownReceive-                withMVar wlck (const $ S.close sck)+                Socket.shutdown sck ShutdownReceive+                withMVar wlck (const $ Socket.close sck)      logException :: SomeException -> IO ()     logException e = case fromException e of@@ -225,7 +207,7 @@         withMVar (c^.wLock) $ const $ do             isOpen <- readTVarIO (c^.status)             if isOpen then-                sendAll (c^.sock) req+                Socket.send (c^.sock) req             else                 throwM $ ConnectionClosed (c^.address)         return i@@ -240,7 +222,7 @@  readSocket :: Version -> Logger -> InetAddr -> Socket -> Int -> IO (Header, ByteString) readSocket v g i s n = do-    b <- recv n i s (if v == V3 then 9 else 8)+    b <- Socket.recv n i s (if v == V3 then 9 else 8)     h <- case header v b of             Left  e -> throwM $ InternalError ("response header reading: " ++ e)             Right h -> return h@@ -248,25 +230,13 @@         RqHeader -> throwM $ InternalError "unexpected request header"         RsHeader -> do             let len = lengthRepr (bodyLength h)-            x <- recv n i s (fromIntegral len)-            trace g $ msg (i +++ val "#" +++ fd s)+            x <- Socket.recv n i s (fromIntegral len)+            trace g $ msg (i +++ val "#" +++ s)                 ~~ "stream" .= fromStreamId (streamId h)                 ~~ "type"   .= val "response"                 ~~ msg' (hexdump $ L.take 160 (b <> x))             return (h, x) -recv :: Int -> InetAddr -> Socket -> Int -> IO ByteString-recv _ _ _ 0 = return L.empty-recv x i s n = toLazyByteString <$> go n mempty-  where-    go !k !bb = do-        a <- NB.recv s (k `min` x)-        when (B.null a) $-            throwM (ConnectionClosed i)-        let b = bb <> byteString a-        let m = k - B.length a-        if m > 0 then go m b else return b- ----------------------------------------------------------------------------- -- Operations @@ -330,9 +300,6 @@     params = QueryParams cons False p Nothing Nothing Nothing  -- logging helpers:--fd :: Socket -> Int32-fd !s = let CInt !n = fdSocket s in n  msg' :: ByteString -> Msg -> Msg msg' x = msg $ case nativeNewline of
+ src/Database/CQL/IO/Connection/Socket.hs view
@@ -0,0 +1,99 @@+-- This Source Code Form is subject to the terms of the Mozilla Public+-- License, v. 2.0. If a copy of the MPL was not distributed with this+-- file, You can obtain one at http://mozilla.org/MPL/2.0/.++{-# LANGUAGE BangPatterns    #-}+{-# LANGUAGE CPP             #-}+{-# LANGUAGE TemplateHaskell #-}++module Database.CQL.IO.Connection.Socket+    ( Socket+    , mkSock+    , open+    , send+    , recv+    , close+    , shutdown+    ) where++import Control.Applicative+import Control.Monad+import Control.Monad.Catch+import Data.ByteString (ByteString)+import Data.ByteString.Builder+import Data.Maybe (isJust)+import Data.Monoid+import Database.CQL.IO.Types+import Foreign.C.Types (CInt (..))+import Network.Socket hiding (Stream, Socket, connect, close, recv, send, shutdown)+import Network.Socket.ByteString.Lazy (sendAll)+import OpenSSL.Session (SSL, SSLContext)+import System.Logger (ToBytes (..))+import System.Timeout+import Prelude++import qualified Data.ByteString            as Bytes+import qualified Data.ByteString.Lazy       as Lazy+import qualified Network.Socket             as S+import qualified Network.Socket.ByteString  as NB+import qualified OpenSSL.Session            as SSL++data Socket = Stream !S.Socket | Tls !S.Socket !SSL++instance ToBytes Socket where+    bytes s = bytes $ case s of+        Stream x -> fd x+        Tls  x _ -> fd x+      where+        fd x = let CInt n = S.fdSocket x in n++open :: Milliseconds -> InetAddr -> Maybe SSLContext -> IO Socket+open to a ctx = do+    bracketOnError (mkSock a) S.close $ \s -> do+        ok <- timeout (ms to * 1000) (S.connect s (sockAddr a))+        unless (isJust ok) $+            throwM (ConnectTimeout a)+        case ctx of+            Nothing  -> return (Stream s)+            Just set -> do+                c <- SSL.connection set s+                SSL.connect c+                return (Tls s c)++mkSock :: InetAddr -> IO S.Socket+mkSock (InetAddr a) = S.socket (familyOf a) S.Stream defaultProtocol+  where+    familyOf (SockAddrInet  _ _)     = AF_INET+    familyOf (SockAddrInet6 _ _ _ _) = AF_INET6+    familyOf (SockAddrUnix  _)       = AF_UNIX+#if MIN_VERSION_network(2,6,1)+    familyOf (SockAddrCan   _      ) = AF_CAN+#endif++close :: Socket -> IO ()+close (Stream s) = S.close s+close (Tls s  c) = SSL.shutdown c SSL.Bidirectional >> S.close s++shutdown :: Socket -> ShutdownCmd -> IO ()+shutdown (Stream s) cmd = S.shutdown s cmd+shutdown _          _   = return ()++recv :: Int -> InetAddr -> Socket -> Int -> IO Lazy.ByteString+recv x a (Stream s) n = receive x a (NB.recv s) n+recv x a (Tls _  c) n = receive x a (SSL.read c) n++receive :: Int -> InetAddr -> (Int -> IO ByteString) -> Int -> IO Lazy.ByteString+receive _ _ _ 0 = return Lazy.empty+receive x i f n = toLazyByteString <$> go n mempty+  where+    go !k !bb = do+        a <- f (k `min` x)+        when (Bytes.null a) $+            throwM (ConnectionClosed i)+        let b = bb <> byteString a+        let m = k - Bytes.length a+        if m > 0 then go m b else return b++send :: Socket -> Lazy.ByteString -> IO ()+send (Stream s) b = sendAll s b+send (Tls _  c) b = mapM_ (SSL.write c) (Lazy.toChunks b)
src/Database/CQL/IO/Settings.hs view
@@ -3,12 +3,13 @@ -- file, You can obtain one at http://mozilla.org/MPL/2.0/.  {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RankNTypes        #-} {-# LANGUAGE TemplateHaskell   #-}  module Database.CQL.IO.Settings where  import Control.Lens hiding ((<|))-import Control.Retry+import Control.Retry hiding (retryPolicy) import Data.List.NonEmpty (NonEmpty (..), (<|)) import Data.Monoid import Data.Time@@ -20,6 +21,7 @@ import Database.CQL.IO.Pool as P import Database.CQL.IO.Types (Milliseconds (..)) import Network.Socket (PortNumber (..))+import OpenSSL.Session (SSLContext) import Prelude  data PrepareStrategy@@ -28,7 +30,7 @@     deriving (Eq, Ord, Show)  data RetrySettings = RetrySettings-    { _retryPolicy        :: !RetryPolicy+    { _retryPolicy        :: !(forall m. Monad m => RetryPolicyM m)     , _reducedConsistency :: !(Maybe Consistency)     , _sendTimeoutChange  :: !Milliseconds     , _recvTimeoutChange  :: !Milliseconds@@ -187,12 +189,18 @@ setMaxRecvBuffer :: Int -> Settings -> Settings setMaxRecvBuffer v = set (connSettings.maxRecvBuffer) v +-- | Set a fully configured SSL context.+--+-- This will make client server queries use TLS.+setSSLContext :: SSLContext -> Settings -> Settings+setSSLContext v = set (connSettings.tlsContext) (Just v)+ ----------------------------------------------------------------------------- -- Retry Settings  -- | Never retry. noRetry :: RetrySettings-noRetry = RetrySettings (RetryPolicy $ const Nothing) Nothing 0 0+noRetry = RetrySettings (RetryPolicyM $ const (return Nothing)) Nothing 0 0  -- | Forever retry immediately. retryForever :: RetrySettings@@ -200,7 +208,8 @@  -- | Limit number of retries. maxRetries :: Word -> RetrySettings -> RetrySettings-maxRetries v = over retryPolicy (mappend (limitRetries $ fromIntegral v))+maxRetries v s =+    s { _retryPolicy = limitRetries (fromIntegral v) <> _retryPolicy s }  -- | When retrying a (batch-) query, change consistency to the given value. adjustConsistency :: Consistency -> RetrySettings -> RetrySettings@@ -241,5 +250,8 @@            -> NominalDiffTime            -> RetrySettings            -> RetrySettings-setDelayFn d v w = over retryPolicy-    (mappend $ capDelay (round (1000000 * w)) $ d (round (1000000 * v)))+setDelayFn f v w s =+    let a = round (1000000 * w)+        b = round (1000000 * v)+    in+        s { _retryPolicy = capDelay a (f b) <> _retryPolicy s }