module LibP2P.Protocol.Identify
(
identifyProtocolId
, identifyPushProtocolId
, handleIdentify
, requestIdentify
, handleIdentifyPush
, pushIdentify
, mergeIdentify
, identifyPeer
, identifyTimeoutMicros
, buildLocalIdentify
, registerIdentifyHandlers
, encodeFramedIdentify
, readFramedIdentify
) where
import Control.Applicative ((<|>))
import Control.Concurrent.STM (atomically, modifyTVar', readTVar, writeTVar)
import Control.Exception (SomeException, bracket, catch, finally, try)
import Control.Monad (void)
import System.Timeout (timeout)
import qualified Data.ByteString as BS
import qualified Data.Map.Strict as Map
import LibP2P.Core.Varint (decodeUvarint, encodeUvarint)
import LibP2P.Crypto.PeerId (PeerId, fromPublicKey, peerIdBytes)
import LibP2P.Crypto.PeerRecord
( PeerRecord (..)
, sealPeerRecord
, timestampSeq
)
import LibP2P.Switch.CertifiedRecords
( CertifiedRecord (..)
, consumeCertifiedRecord
, verifyPeerRecord
)
import LibP2P.Crypto.Protobuf (decodePublicKey, encodePublicKey)
import LibP2P.Crypto.Key (kpPublic)
import LibP2P.Crypto.SignedEnvelope (encodeSignedEnvelope)
import LibP2P.Multiaddr.Codec (encodeProtocols)
import LibP2P.Multiaddr (Multiaddr (..))
import LibP2P.MultistreamSelect.Negotiation
( ProtocolId
, StreamIO (..)
, closeQuietly
, negotiateInitiator
, NegotiationResult (..)
, readExactBounded
)
import LibP2P.Protocol.Identify.Message
( IdentifyInfo (..)
, decodeIdentify
, encodeIdentify
, maxIdentifySize
)
import LibP2P.Switch.ConnPool (allConns)
import LibP2P.Switch.Types
( ActiveListener (..)
, Connection (..)
, MuxerSession (..)
, Switch (..)
)
identifyProtocolId :: ProtocolId
identifyProtocolId :: Text
identifyProtocolId = Text
"/ipfs/id/1.0.0"
identifyPushProtocolId :: ProtocolId
identifyPushProtocolId :: Text
identifyPushProtocolId = Text
"/ipfs/id/push/1.0.0"
handleIdentify :: Switch -> Connection -> StreamIO -> IO ()
handleIdentify :: Switch -> Connection -> StreamIO -> IO ()
handleIdentify Switch
sw Connection
conn StreamIO
stream =
(do
info <- Switch -> Maybe Connection -> IO IdentifyInfo
buildLocalIdentify Switch
sw (Connection -> Maybe Connection
forall a. a -> Maybe a
Just Connection
conn)
streamWrite stream (encodeFramedIdentify info))
IO () -> IO () -> IO ()
forall a b. IO a -> IO b -> IO a
`finally` StreamIO -> IO ()
closeQuietly StreamIO
stream
identifyTimeoutMicros :: Int
identifyTimeoutMicros :: Int
identifyTimeoutMicros = Int
5000000
requestIdentify :: Connection -> IO (Either String IdentifyInfo)
requestIdentify :: Connection -> IO (Either [Char] IdentifyInfo)
requestIdentify Connection
conn = do
outcome <- IO (Maybe (Either [Char] IdentifyInfo))
-> IO (Either SomeException (Maybe (Either [Char] IdentifyInfo)))
forall e a. Exception e => IO a -> IO (Either e a)
try (IO (Maybe (Either [Char] IdentifyInfo))
-> IO (Either SomeException (Maybe (Either [Char] IdentifyInfo))))
-> IO (Maybe (Either [Char] IdentifyInfo))
-> IO (Either SomeException (Maybe (Either [Char] IdentifyInfo)))
forall a b. (a -> b) -> a -> b
$ IO StreamIO
-> (StreamIO -> IO ())
-> (StreamIO -> IO (Maybe (Either [Char] IdentifyInfo)))
-> IO (Maybe (Either [Char] IdentifyInfo))
forall a b c. IO a -> (a -> IO b) -> (a -> IO c) -> IO c
bracket (MuxerSession -> IO StreamIO
muxOpenStream (Connection -> MuxerSession
connSession Connection
conn)) StreamIO -> IO ()
closeQuietly StreamIO -> IO (Maybe (Either [Char] IdentifyInfo))
exchange
pure $ case outcome of
Left (SomeException
e :: SomeException) -> [Char] -> Either [Char] IdentifyInfo
forall a b. a -> Either a b
Left ([Char]
"identify failed: " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ SomeException -> [Char]
forall a. Show a => a -> [Char]
show SomeException
e)
Right Maybe (Either [Char] IdentifyInfo)
Nothing -> [Char] -> Either [Char] IdentifyInfo
forall a b. a -> Either a b
Left [Char]
"identify timed out"
Right (Just Either [Char] IdentifyInfo
result) -> Either [Char] IdentifyInfo
result
where
exchange :: StreamIO -> IO (Maybe (Either [Char] IdentifyInfo))
exchange StreamIO
stream = Int
-> IO (Either [Char] IdentifyInfo)
-> IO (Maybe (Either [Char] IdentifyInfo))
forall a. Int -> IO a -> IO (Maybe a)
timeout Int
identifyTimeoutMicros (IO (Either [Char] IdentifyInfo)
-> IO (Maybe (Either [Char] IdentifyInfo)))
-> IO (Either [Char] IdentifyInfo)
-> IO (Maybe (Either [Char] IdentifyInfo))
forall a b. (a -> b) -> a -> b
$ do
negotiated <- StreamIO -> [Text] -> IO NegotiationResult
negotiateInitiator StreamIO
stream [Text
identifyProtocolId]
case negotiated of
NegotiationResult
NoProtocol -> Either [Char] IdentifyInfo -> IO (Either [Char] IdentifyInfo)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ([Char] -> Either [Char] IdentifyInfo
forall a b. a -> Either a b
Left [Char]
"remote does not support identify")
Accepted Text
_ ->
(IdentifyInfo -> IdentifyInfo)
-> Either [Char] IdentifyInfo -> Either [Char] IdentifyInfo
forall a b. (a -> b) -> Either [Char] a -> Either [Char] b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (PeerId -> IdentifyInfo -> IdentifyInfo
validateIdentify (Connection -> PeerId
connPeerId Connection
conn))
(Either [Char] IdentifyInfo -> Either [Char] IdentifyInfo)
-> IO (Either [Char] IdentifyInfo)
-> IO (Either [Char] IdentifyInfo)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> StreamIO -> Int -> IO (Either [Char] IdentifyInfo)
readFramedIdentify StreamIO
stream Int
maxIdentifySize
identifyPeer :: Switch -> Connection -> IO (Either String ())
identifyPeer :: Switch -> Connection -> IO (Either [Char] ())
identifyPeer Switch
sw Connection
conn = do
result <- Connection -> IO (Either [Char] IdentifyInfo)
requestIdentify Connection
conn
case result of
Left [Char]
err -> Either [Char] () -> IO (Either [Char] ())
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ([Char] -> Either [Char] ()
forall a b. a -> Either a b
Left [Char]
err)
Right IdentifyInfo
info -> do
Switch -> PeerId -> IdentifyInfo -> IO ()
storeIdentify Switch
sw (Connection -> PeerId
connPeerId Connection
conn) IdentifyInfo
info
Either [Char] () -> IO (Either [Char] ())
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (() -> Either [Char] ()
forall a b. b -> Either a b
Right ())
handleIdentifyPush :: Switch -> Connection -> StreamIO -> IO ()
handleIdentifyPush :: Switch -> Connection -> StreamIO -> IO ()
handleIdentifyPush Switch
sw Connection
conn StreamIO
stream =
(do
infoOrErr <- StreamIO -> Int -> IO (Either [Char] IdentifyInfo)
readFramedIdentify StreamIO
stream Int
maxIdentifySize
case infoOrErr of
Left [Char]
_ -> () -> IO ()
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
Right IdentifyInfo
rawInfo ->
Switch -> PeerId -> IdentifyInfo -> IO ()
storeIdentify Switch
sw (Connection -> PeerId
connPeerId Connection
conn)
(PeerId -> IdentifyInfo -> IdentifyInfo
validateIdentify (Connection -> PeerId
connPeerId Connection
conn) IdentifyInfo
rawInfo))
IO () -> IO () -> IO ()
forall a b. IO a -> IO b -> IO a
`finally` StreamIO -> IO ()
closeQuietly StreamIO
stream
storeIdentify :: Switch -> PeerId -> IdentifyInfo -> IO ()
storeIdentify :: Switch -> PeerId -> IdentifyInfo -> IO ()
storeIdentify Switch
sw PeerId
peerId IdentifyInfo
info = do
let offered :: Maybe CertifiedRecord
offered = do
envBytes <- IdentifyInfo -> Maybe ByteString
idSignedPeerRecord IdentifyInfo
info
either (const Nothing) Just (verifyPeerRecord peerId envBytes)
STM () -> IO ()
forall a. STM a -> IO a
atomically (STM () -> IO ()) -> STM () -> IO ()
forall a b. (a -> b) -> a -> b
$ do
fresh <- case Maybe CertifiedRecord
offered of
Maybe CertifiedRecord
Nothing -> Bool -> STM Bool
forall a. a -> STM a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Bool
True
Just CertifiedRecord
record -> TVar (Map PeerId CertifiedRecord)
-> PeerId -> CertifiedRecord -> STM Bool
consumeCertifiedRecord (Switch -> TVar (Map PeerId CertifiedRecord)
swCertifiedRecords Switch
sw) PeerId
peerId CertifiedRecord
record
let update
| Bool
fresh = IdentifyInfo
info
| Bool
otherwise = IdentifyInfo
info { idSignedPeerRecord = Nothing, idListenAddrs = [] }
store <- readTVar (swPeerStore sw)
let merged = IdentifyInfo
-> (IdentifyInfo -> IdentifyInfo)
-> Maybe IdentifyInfo
-> IdentifyInfo
forall b a. b -> (a -> b) -> Maybe a -> b
maybe IdentifyInfo
update (IdentifyInfo -> IdentifyInfo -> IdentifyInfo
`mergeIdentify` IdentifyInfo
update) (PeerId -> Map PeerId IdentifyInfo -> Maybe IdentifyInfo
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup PeerId
peerId Map PeerId IdentifyInfo
store)
writeTVar (swPeerStore sw) (Map.insert peerId merged store)
validateIdentify :: PeerId -> IdentifyInfo -> IdentifyInfo
validateIdentify :: PeerId -> IdentifyInfo -> IdentifyInfo
validateIdentify PeerId
remotePeer =
PeerId -> IdentifyInfo -> IdentifyInfo
validateSignedPeerRecord PeerId
remotePeer (IdentifyInfo -> IdentifyInfo)
-> (IdentifyInfo -> IdentifyInfo) -> IdentifyInfo -> IdentifyInfo
forall b c a. (b -> c) -> (a -> b) -> a -> c
. PeerId -> IdentifyInfo -> IdentifyInfo
validatePublicKey PeerId
remotePeer
validatePublicKey :: PeerId -> IdentifyInfo -> IdentifyInfo
validatePublicKey :: PeerId -> IdentifyInfo -> IdentifyInfo
validatePublicKey PeerId
remotePeer IdentifyInfo
info = case IdentifyInfo -> Maybe ByteString
idPublicKey IdentifyInfo
info of
Maybe ByteString
Nothing -> IdentifyInfo
info
Just ByteString
keyBytes -> case ByteString -> Either [Char] PublicKey
decodePublicKey ByteString
keyBytes of
Right PublicKey
pk | PublicKey -> PeerId
fromPublicKey PublicKey
pk PeerId -> PeerId -> Bool
forall a. Eq a => a -> a -> Bool
== PeerId
remotePeer -> IdentifyInfo
info
Either [Char] PublicKey
_ -> IdentifyInfo
info { idPublicKey = Nothing }
validateSignedPeerRecord :: PeerId -> IdentifyInfo -> IdentifyInfo
validateSignedPeerRecord :: PeerId -> IdentifyInfo -> IdentifyInfo
validateSignedPeerRecord PeerId
remotePeer IdentifyInfo
info = case IdentifyInfo -> Maybe ByteString
idSignedPeerRecord IdentifyInfo
info of
Maybe ByteString
Nothing -> IdentifyInfo
info
Just ByteString
envBytes -> case PeerId -> ByteString -> Either [Char] CertifiedRecord
verifyPeerRecord PeerId
remotePeer ByteString
envBytes of
Right CertifiedRecord
record -> IdentifyInfo
info { idListenAddrs = crAddresses record }
Left [Char]
_ -> IdentifyInfo
info { idSignedPeerRecord = Nothing }
mergeIdentify :: IdentifyInfo -> IdentifyInfo -> IdentifyInfo
mergeIdentify :: IdentifyInfo -> IdentifyInfo -> IdentifyInfo
mergeIdentify IdentifyInfo
known IdentifyInfo
update = IdentifyInfo
{ idProtocolVersion :: Maybe Text
idProtocolVersion = IdentifyInfo -> Maybe Text
idProtocolVersion IdentifyInfo
update Maybe Text -> Maybe Text -> Maybe Text
forall a. Maybe a -> Maybe a -> Maybe a
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> IdentifyInfo -> Maybe Text
idProtocolVersion IdentifyInfo
known
, idAgentVersion :: Maybe Text
idAgentVersion = IdentifyInfo -> Maybe Text
idAgentVersion IdentifyInfo
update Maybe Text -> Maybe Text -> Maybe Text
forall a. Maybe a -> Maybe a -> Maybe a
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> IdentifyInfo -> Maybe Text
idAgentVersion IdentifyInfo
known
, idPublicKey :: Maybe ByteString
idPublicKey = IdentifyInfo -> Maybe ByteString
idPublicKey IdentifyInfo
update Maybe ByteString -> Maybe ByteString -> Maybe ByteString
forall a. Maybe a -> Maybe a -> Maybe a
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> IdentifyInfo -> Maybe ByteString
idPublicKey IdentifyInfo
known
, idListenAddrs :: [ByteString]
idListenAddrs = [ByteString] -> [ByteString] -> [ByteString]
forall a. [a] -> [a] -> [a]
replaceUnlessEmpty (IdentifyInfo -> [ByteString]
idListenAddrs IdentifyInfo
known) (IdentifyInfo -> [ByteString]
idListenAddrs IdentifyInfo
update)
, idObservedAddr :: Maybe ByteString
idObservedAddr = IdentifyInfo -> Maybe ByteString
idObservedAddr IdentifyInfo
update Maybe ByteString -> Maybe ByteString -> Maybe ByteString
forall a. Maybe a -> Maybe a -> Maybe a
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> IdentifyInfo -> Maybe ByteString
idObservedAddr IdentifyInfo
known
, idProtocols :: [Text]
idProtocols = [Text] -> [Text] -> [Text]
forall a. [a] -> [a] -> [a]
replaceUnlessEmpty (IdentifyInfo -> [Text]
idProtocols IdentifyInfo
known) (IdentifyInfo -> [Text]
idProtocols IdentifyInfo
update)
, idSignedPeerRecord :: Maybe ByteString
idSignedPeerRecord = IdentifyInfo -> Maybe ByteString
idSignedPeerRecord IdentifyInfo
update Maybe ByteString -> Maybe ByteString -> Maybe ByteString
forall a. Maybe a -> Maybe a -> Maybe a
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> IdentifyInfo -> Maybe ByteString
idSignedPeerRecord IdentifyInfo
known
}
where
replaceUnlessEmpty :: [a] -> [a] -> [a]
replaceUnlessEmpty [a]
old [] = [a]
old
replaceUnlessEmpty [a]
_ [a]
new = [a]
new
pushIdentify :: Switch -> IO ()
pushIdentify :: Switch -> IO ()
pushIdentify Switch
sw = do
conns <- STM [Connection] -> IO [Connection]
forall a. STM a -> IO a
atomically (STM [Connection] -> IO [Connection])
-> STM [Connection] -> IO [Connection]
forall a b. (a -> b) -> a -> b
$ TVar (Map PeerId [Connection]) -> STM [Connection]
allConns (Switch -> TVar (Map PeerId [Connection])
swConnPool Switch
sw)
mapM_ (\Connection
conn -> Connection -> IO ()
pushToConn Connection
conn IO () -> (SomeException -> IO ()) -> IO ()
forall e a. Exception e => IO a -> (e -> IO a) -> IO a
`catch` \(SomeException
_ :: SomeException) -> () -> IO ()
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()) conns
where
pushToConn :: Connection -> IO ()
pushToConn Connection
conn = do
stream <- MuxerSession -> IO StreamIO
muxOpenStream (Connection -> MuxerSession
connSession Connection
conn)
result <- negotiateInitiator stream [identifyPushProtocolId]
case result of
Accepted Text
_ -> do
info <- Switch -> Maybe Connection -> IO IdentifyInfo
buildLocalIdentify Switch
sw (Connection -> Maybe Connection
forall a. a -> Maybe a
Just Connection
conn)
streamWrite stream (encodeFramedIdentify info)
streamClose stream
NegotiationResult
NoProtocol -> StreamIO -> IO ()
streamClose StreamIO
stream
buildLocalIdentify :: Switch -> Maybe Connection -> IO IdentifyInfo
buildLocalIdentify :: Switch -> Maybe Connection -> IO IdentifyInfo
buildLocalIdentify Switch
sw Maybe Connection
mConn = do
(protocols, listenAddrs) <- STM ([Text], [Multiaddr]) -> IO ([Text], [Multiaddr])
forall a. STM a -> IO a
atomically (STM ([Text], [Multiaddr]) -> IO ([Text], [Multiaddr]))
-> STM ([Text], [Multiaddr]) -> IO ([Text], [Multiaddr])
forall a b. (a -> b) -> a -> b
$ do
protos <- Map Text (Connection -> StreamIO -> IO ()) -> [Text]
forall k a. Map k a -> [k]
Map.keys (Map Text (Connection -> StreamIO -> IO ()) -> [Text])
-> STM (Map Text (Connection -> StreamIO -> IO ())) -> STM [Text]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> TVar (Map Text (Connection -> StreamIO -> IO ()))
-> STM (Map Text (Connection -> StreamIO -> IO ()))
forall a. TVar a -> STM a
readTVar (Switch -> TVar (Map Text (Connection -> StreamIO -> IO ()))
swProtocols Switch
sw)
listeners <- readTVar (swListeners sw)
pure (protos, map alAddress listeners)
seqNo <- timestampSeq
let addrBytes = (Multiaddr -> ByteString) -> [Multiaddr] -> [ByteString]
forall a b. (a -> b) -> [a] -> [b]
map (\(Multiaddr [Protocol]
ps) -> [Protocol] -> ByteString
encodeProtocols [Protocol]
ps) [Multiaddr]
listenAddrs
record = PeerRecord
{ prPeerId :: ByteString
prPeerId = PeerId -> ByteString
peerIdBytes (Switch -> PeerId
swLocalPeerId Switch
sw)
, prSeq :: Word64
prSeq = Word64
seqNo
, prAddresses :: [ByteString]
prAddresses = [ByteString]
addrBytes
}
signedRecord = ([Char] -> Maybe ByteString)
-> (SignedEnvelope -> Maybe ByteString)
-> Either [Char] SignedEnvelope
-> Maybe ByteString
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either (Maybe ByteString -> [Char] -> Maybe ByteString
forall a b. a -> b -> a
const Maybe ByteString
forall a. Maybe a
Nothing) (ByteString -> Maybe ByteString
forall a. a -> Maybe a
Just (ByteString -> Maybe ByteString)
-> (SignedEnvelope -> ByteString)
-> SignedEnvelope
-> Maybe ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SignedEnvelope -> ByteString
encodeSignedEnvelope)
(KeyPair -> PeerRecord -> Either [Char] SignedEnvelope
sealPeerRecord (Switch -> KeyPair
swIdentityKey Switch
sw) PeerRecord
record)
pure IdentifyInfo
{ idProtocolVersion = Just "ipfs/0.1.0"
, idAgentVersion = Just "libp2p-hs/0.1.0"
, idPublicKey = Just (encodePublicKey (kpPublic (swIdentityKey sw)))
, idListenAddrs = addrBytes
, idObservedAddr = (\(Multiaddr [Protocol]
ps) -> [Protocol] -> ByteString
encodeProtocols [Protocol]
ps) . connRemoteAddr <$> mConn
, idProtocols = protocols
, idSignedPeerRecord = signedRecord
}
registerIdentifyHandlers :: Switch -> IO ()
registerIdentifyHandlers :: Switch -> IO ()
registerIdentifyHandlers Switch
sw = do
STM () -> IO ()
forall a. STM a -> IO a
atomically (STM () -> IO ()) -> STM () -> IO ()
forall a b. (a -> b) -> a -> b
$ do
protos <- TVar (Map Text (Connection -> StreamIO -> IO ()))
-> STM (Map Text (Connection -> StreamIO -> IO ()))
forall a. TVar a -> STM a
readTVar (Switch -> TVar (Map Text (Connection -> StreamIO -> IO ()))
swProtocols Switch
sw)
let protos' = Text
-> (Connection -> StreamIO -> IO ())
-> Map Text (Connection -> StreamIO -> IO ())
-> Map Text (Connection -> StreamIO -> IO ())
forall k a. Ord k => k -> a -> Map k a -> Map k a
Map.insert Text
identifyProtocolId (Switch -> Connection -> StreamIO -> IO ()
handleIdentify Switch
sw) Map Text (Connection -> StreamIO -> IO ())
protos
protos'' = Text
-> (Connection -> StreamIO -> IO ())
-> Map Text (Connection -> StreamIO -> IO ())
-> Map Text (Connection -> StreamIO -> IO ())
forall k a. Ord k => k -> a -> Map k a -> Map k a
Map.insert Text
identifyPushProtocolId (Switch -> Connection -> StreamIO -> IO ()
handleIdentifyPush Switch
sw) Map Text (Connection -> StreamIO -> IO ())
protos'
writeTVar (swProtocols sw) protos''
STM () -> IO ()
forall a. STM a -> IO a
atomically (STM () -> IO ()) -> STM () -> IO ()
forall a b. (a -> b) -> a -> b
$ TVar [Connection -> IO ()]
-> ([Connection -> IO ()] -> [Connection -> IO ()]) -> STM ()
forall a. TVar a -> (a -> a) -> STM ()
modifyTVar' (Switch -> TVar [Connection -> IO ()]
swNotifiers Switch
sw) (IO (Either [Char] ()) -> IO ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (IO (Either [Char] ()) -> IO ())
-> (Connection -> IO (Either [Char] ())) -> Connection -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Switch -> Connection -> IO (Either [Char] ())
identifyPeer Switch
sw (Connection -> IO ())
-> [Connection -> IO ()] -> [Connection -> IO ()]
forall a. a -> [a] -> [a]
:)
encodeFramedIdentify :: IdentifyInfo -> BS.ByteString
encodeFramedIdentify :: IdentifyInfo -> ByteString
encodeFramedIdentify IdentifyInfo
info =
let payload :: ByteString
payload = IdentifyInfo -> ByteString
encodeIdentify IdentifyInfo
info
in Word64 -> ByteString
encodeUvarint (Int -> Word64
forall a b. (Integral a, Num b) => a -> b
fromIntegral (ByteString -> Int
BS.length ByteString
payload)) ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> ByteString
payload
readFramedIdentify :: StreamIO -> Int -> IO (Either String IdentifyInfo)
readFramedIdentify :: StreamIO -> Int -> IO (Either [Char] IdentifyInfo)
readFramedIdentify StreamIO
stream Int
maxSize = IO (Either [Char] IdentifyInfo)
readFramed IO (Either [Char] IdentifyInfo)
-> (SomeException -> IO (Either [Char] IdentifyInfo))
-> IO (Either [Char] IdentifyInfo)
forall e a. Exception e => IO a -> (e -> IO a) -> IO a
`catch` SomeException -> IO (Either [Char] IdentifyInfo)
onError
where
onError :: SomeException -> IO (Either String IdentifyInfo)
onError :: SomeException -> IO (Either [Char] IdentifyInfo)
onError SomeException
e = Either [Char] IdentifyInfo -> IO (Either [Char] IdentifyInfo)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ([Char] -> Either [Char] IdentifyInfo
forall a b. a -> Either a b
Left ([Char]
"identify stream read failed: " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ SomeException -> [Char]
forall a. Show a => a -> [Char]
show SomeException
e))
readFramed :: IO (Either [Char] IdentifyInfo)
readFramed = do
varintBytes <- StreamIO -> IO ByteString
readVarintBytes StreamIO
stream
case decodeUvarint varintBytes of
Left [Char]
err -> Either [Char] IdentifyInfo -> IO (Either [Char] IdentifyInfo)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ([Char] -> Either [Char] IdentifyInfo
forall a b. a -> Either a b
Left ([Char]
"identify length prefix decode error: " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
err))
Right (Word64
len, ByteString
_) -> do
let msgLen :: Int
msgLen = Word64 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word64
len :: Int
if Int
msgLen Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
maxSize
then Either [Char] IdentifyInfo -> IO (Either [Char] IdentifyInfo)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ([Char] -> Either [Char] IdentifyInfo
forall a b. a -> Either a b
Left ([Char]
"identify message too large: "
[Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ Int -> [Char]
forall a. Show a => a -> [Char]
show Int
msgLen [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
" > " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ Int -> [Char]
forall a. Show a => a -> [Char]
show Int
maxSize))
else do
payloadOrErr <- StreamIO -> Int -> Int -> IO (Either [Char] ByteString)
readExactBounded StreamIO
stream Int
maxSize Int
msgLen
case payloadOrErr of
Left [Char]
err -> Either [Char] IdentifyInfo -> IO (Either [Char] IdentifyInfo)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ([Char] -> Either [Char] IdentifyInfo
forall a b. a -> Either a b
Left ([Char]
"identify read error: " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
err))
Right ByteString
payload -> case ByteString -> Either ParseError IdentifyInfo
decodeIdentify ByteString
payload of
Left ParseError
parseErr ->
Either [Char] IdentifyInfo -> IO (Either [Char] IdentifyInfo)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ([Char] -> Either [Char] IdentifyInfo
forall a b. a -> Either a b
Left ([Char]
"identify protobuf decode error: " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ ParseError -> [Char]
forall a. Show a => a -> [Char]
show ParseError
parseErr))
Right IdentifyInfo
info -> Either [Char] IdentifyInfo -> IO (Either [Char] IdentifyInfo)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (IdentifyInfo -> Either [Char] IdentifyInfo
forall a b. b -> Either a b
Right IdentifyInfo
info)
readVarintBytes :: StreamIO -> IO BS.ByteString
readVarintBytes :: StreamIO -> IO ByteString
readVarintBytes StreamIO
stream = [Word8] -> Int -> IO ByteString
forall {t}. (Ord t, Num t) => [Word8] -> t -> IO ByteString
go [] (Int
0 :: Int)
where
go :: [Word8] -> t -> IO ByteString
go [Word8]
acc t
n
| t
n t -> t -> Bool
forall a. Ord a => a -> a -> Bool
>= t
10 = ByteString -> IO ByteString
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ([Word8] -> ByteString
BS.pack ([Word8] -> [Word8]
forall a. [a] -> [a]
reverse [Word8]
acc))
| Bool
otherwise = do
b <- StreamIO -> IO Word8
streamReadByte StreamIO
stream
if b < 0x80
then pure (BS.pack (reverse (b : acc)))
else go (b : acc) (n + 1)