packages feed

bolt-0.3.2.0: lib/Database/Bolt/Transport/Socket.hs

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}

module Database.Bolt.Transport.Socket (
  ConnInfo,
  hostserv,
  uri,
  Connection,
  connect,
  close,
  send,
  sendMany,
  recv,
  put,
  get,
) where

import Control.Exception
import Control.Monad
import Data.Monoid
import Data.Text qualified as T
import Network.Socket qualified as Net
import Network.Socket.ByteString qualified as NetBS
import Network.URI

import Database.Bolt.Exception
import Database.Bolt.Transport

-------- Socket --------

newtype Connection = Connection {connSocket :: Net.Socket}

instance Transport Connection where
  send = NetBS.sendAll . connSocket
  sendMany = NetBS.sendMany . connSocket
  recv = NetBS.recv . connSocket
  close = Net.close . connSocket

data ConnInfo = ConnInfo
  { connHost :: Net.HostName
  , connService :: Net.ServiceName
  }
  deriving (Show, Read, Eq, Ord)

-------- Connections --------

-- Create ConnInfo from a host and service name
hostserv :: String -> String -> ConnInfo
hostserv = ConnInfo

-- Create ConnInfo from a bolt URI
uri :: String -> Maybe ConnInfo
uri uristr = do
  uri' <- parseAbsoluteURI uristr
  guard $ uriScheme uri' == "bolt"
  auth <- uriAuthority uri'
  guard $ uriUserInfo auth == ""
  guard $ uriPath uri' == ""
  guard $ uriQuery uri' == ""
  guard $ uriFragment uri' == ""
  return
    ConnInfo
      { connHost = uriRegName auth
      , connService = uriPort auth
      }

-- Connect using ConnInfo and return a handle
connect :: ConnInfo -> IO Connection
connect ConnInfo{..} = do
  let hints = Net.defaultHints{Net.addrSocketType = Net.Stream}
  addrs <- Net.getAddrInfo (Just hints) (Just connHost) (Just connService)
  connectAny addrs
 where
  connectAny [] = transportErr $ "Unable to connect to " <> T.pack connHost <> ":" <> T.pack connService
  connectAny (addr : addrs) = doConnect addr `catch` (\(_ :: IOException) -> connectAny addrs)

  doConnect addr = do
    sock <- Net.socket (Net.addrFamily addr) (Net.addrSocketType addr) (Net.addrProtocol addr)
    Net.connect sock (Net.addrAddress addr) `onException` Net.close sock
    return $ Connection sock