hs-multiaddr (empty) → 0.1.0.0
raw patch · 14 files changed
+923/−0 lines, 14 filesdep +basedep +bytesdep +bytestringsetup-changed
Dependencies added: base, bytes, bytestring, cereal, either-unwrap, filepath, hs-multiaddr, hspec, io-streams, iproute, multihash, optparse-applicative, sandi, text
Files
- LICENSE +30/−0
- README.md +17/−0
- Setup.hs +2/−0
- hs-multiaddr.cabal +59/−0
- src/Data/Multiaddr.hs +96/−0
- src/Data/Multiaddr/IPFS.hs +40/−0
- src/Data/Multiaddr/IPv4.hs +29/−0
- src/Data/Multiaddr/IPv6.hs +29/−0
- src/Data/Multiaddr/Onion.hs +58/−0
- src/Data/Multiaddr/Port.hs +59/−0
- src/Data/Multiaddr/UnixPath.hs +37/−0
- src/Data/Multiaddr/VarInt.hs +33/−0
- src/Data/MultiaddrPart.hs +175/−0
- test/Spec.hs +259/−0
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright CMCDragonkai (c) 2017++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of CMCDragonkai nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,17 @@+Matrix Multi Address+=========================++Here is an experiment to combine the haskell implementation of Multiaddress with the Haskell implementation of Multihash.++Along the way, we must learn how stack works.++-- Multiaddr is a record wrapping a list of MultiaddrParts+-- a Multiaddr here is a typed Multiaddr within Haskell+-- in the outside world, there are 2 kinds of Multiaddr+-- a textual human readable version+-- a binary encoded version+-- this library exposes the typed Multiaddr+-- while allowing Multiaddr to be created from+-- human readable text or binary encoded string++There is any number of slashes.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ hs-multiaddr.cabal view
@@ -0,0 +1,59 @@+name: hs-multiaddr+version: 0.1.0.0+synopsis: Multiaddr Library for LibP2P+description: Multiaddr is self-describing way to represent addresses that support any standard network protocol.+ Multiaddr has a binary packed format, and nice string representation.+ It is possible to encapsulate addresses within each other.+homepage: https://github.com/MatrixAI/haskell-multiaddr#readme+license: BSD3+license-file: LICENSE+author: CMCDragonkai+maintainer: roger.qiu@matrix.ai+copyright: Copyright: (c) 2017 Roger Qiu+category: Data+build-type: Simple+extra-source-files: README.md+cabal-version: >=1.10++library+ hs-source-dirs: src+ exposed-modules: Data.Multiaddr,+ Data.MultiaddrPart,+ Data.Multiaddr.IPFS,+ Data.Multiaddr.VarInt+ Data.Multiaddr.IPv4,+ Data.Multiaddr.IPv6+ Data.Multiaddr.Onion,+ Data.Multiaddr.Port,+ Data.Multiaddr.UnixPath+ build-depends: base >= 4.7 && < 4.9+ , bytestring+ , text+ , bytes+ , sandi+ , cereal+ , iproute+ , filepath+ , multihash+ , optparse-applicative >= 0.11 && < 0.12+ , io-streams >= 1.2 && < 1.3+ default-language: Haskell2010++test-suite haskell-multiaddr-test+ type: exitcode-stdio-1.0+ hs-source-dirs: test+ main-is: Spec.hs+ build-depends: base+ , hs-multiaddr+ , hspec+ , iproute+ , sandi+ , bytestring+ , multihash+ , either-unwrap+ ghc-options: -threaded -rtsopts -with-rtsopts=-N+ default-language: Haskell2010++source-repository head+ type: git+ location: https://github.com/MatrixAI/haskell-multiaddr
+ src/Data/Multiaddr.hs view
@@ -0,0 +1,96 @@+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE DeriveDataTypeable #-}++module Data.Multiaddr+ (+ -- * As Multiaddr types+ Multiaddr (..),+ P.MultiaddrPart (..),+ -- * As a string+ toString,+ toMultiaddr,+ -- * As binary packed format+ encode,+ decode,+ -- * Utilities+ P.partHdrS,+ P.partHdrB,+ encapsulate,+ decapsulate,+ findFirstPart,+ findLastPart,+ findAllParts+ ) where++import qualified Text.ParserCombinators.ReadP as Parser+import qualified Data.ByteString as BSStrict+import qualified Data.MultiaddrPart as P++import GHC.Generics (Generic)+import Data.Typeable (Typeable)+import Data.String (IsString, fromString)+import Control.Applicative (many, some)+import Data.Bytes.Get (runGetS)+import Data.Bytes.Put (runPutS)+import Data.Serialize.Get (Get)+import Data.List (isPrefixOf, find, filter)++newtype Multiaddr = Multiaddr { parts :: [P.MultiaddrPart] }+ deriving (Show, Eq, Monoid, Generic, Typeable)++instance IsString Multiaddr where+ fromString s = either error id $ toMultiaddr s++toString :: Multiaddr -> String+toString (Multiaddr p) = concatMap P.toString p++toMultiaddr :: String -> Either String Multiaddr+toMultiaddr s =+ case Parser.readP_to_S (parse <* Parser.eof) s of+ (m, ""):_ -> Right m+ otherwise -> Left $ "Data.Multiaddr.toMultiaddr: invalid multiaddr " ++ show s++parse :: Parser.ReadP Multiaddr+parse = do+ multiParts <- some P.parse+ many $ Parser.char '/'+ return $ Multiaddr multiParts++encode :: Multiaddr -> BSStrict.ByteString+encode (Multiaddr p) = BSStrict.concat . map P.encode $ p++decode :: BSStrict.ByteString -> Either String Multiaddr+decode = runGetS $ parseB++parseB :: Get Multiaddr+parseB = Multiaddr <$> some P.parseB++-- first address encapsulates the second address+encapsulate :: Multiaddr -> Multiaddr -> Multiaddr+encapsulate m1 m2 = Multiaddr $ parts m1 ++ parts m2++-- first address is prefix removed from the second address+decapsulate :: Multiaddr -> Multiaddr -> Maybe Multiaddr+decapsulate (Multiaddr p1) (Multiaddr p2)+ | isPrefixOf p1 p2 = Just . Multiaddr . drop (length p1) $ p2+ | otherwise = Nothing++findFirstPart :: P.MultiaddrPart -> Multiaddr -> Maybe P.MultiaddrPart+findFirstPart p a = find (\p' -> P.partHdrB p' == pI) (parts a)+ where+ pI = P.partHdrB p++findLastPart :: P.MultiaddrPart -> Multiaddr -> Maybe P.MultiaddrPart+findLastPart p a = go (\p' -> P.partHdrB p' == pI) (parts a) Nothing+ where+ pI = P.partHdrB p+ go _ [] lastPart = lastPart+ go pred (p:ps) lastPart+ | pred p = go pred ps $ Just p+ | otherwise = go pred ps lastPart++findAllParts :: P.MultiaddrPart -> Multiaddr -> [P.MultiaddrPart]+findAllParts p a = filter (\p' -> P.partHdrB p' == pI) (parts a)+ where+ pI = P.partHdrB p
+ src/Data/Multiaddr/IPFS.hs view
@@ -0,0 +1,40 @@+module Data.Multiaddr.IPFS+ (+ MHD.MultihashDigest (..),+ toString,+ parse,+ encode,+ parseB+ ) where++import qualified Text.ParserCombinators.ReadP as Parser+import qualified Data.ByteString as BSStrict+import qualified Data.ByteString.Lazy as BSLazy+import qualified Data.ByteString.Lazy.Char8 as BSLazyChar+import qualified Data.Multiaddr.VarInt as VarInt+import qualified Data.Multihash.Digest as MHD+import qualified Data.Multihash.Base as MHB++import Data.Serialize.Get (Get)++toString :: MHD.MultihashDigest -> String+toString h =+ BSLazyChar.unpack $+ MHB.encode MHB.Base58+ (BSLazy.fromStrict $ MHD.digest h)++parse :: Parser.ReadP MHD.MultihashDigest+parse = do+ multihashText <- Parser.munch1 (/= '/')+ case ((MHB.decode MHB.Base58) (BSLazyChar.pack multihashText)) of+ Right bs ->+ case MHD.decode (BSLazyChar.toStrict bs) of+ Right digest -> return digest+ otherwise -> Parser.pfail+ otherwise -> Parser.pfail++encode :: MHD.MultihashDigest -> BSStrict.ByteString+encode h = VarInt.encodeWith $ MHD.digest h++parseB :: Get MHD.MultihashDigest+parseB = VarInt.decodeSizeVar >>= (either fail return . MHD.decode)
+ src/Data/Multiaddr/IPv4.hs view
@@ -0,0 +1,29 @@+module Data.Multiaddr.IPv4+ (+ IPv4 (..),+ toString,+ parse,+ encode,+ parseB+ ) where++import qualified Text.ParserCombinators.ReadP as Parser+import qualified Data.ByteString as BSStrict+import qualified Data.Multiaddr.VarInt as VarInt++import Data.IP (IPv4 (..), fromIPv4, toIPv4)+import Data.Serialize.Get (Get)++toString :: IPv4 -> String+toString = show++parse :: Parser.ReadP IPv4+parse = Parser.readS_to_P reads++encode :: IPv4 -> BSStrict.ByteString+encode = BSStrict.pack . map fromIntegral . fromIPv4++parseB :: Get IPv4+parseB =+ fmap (toIPv4 . map fromIntegral . BSStrict.unpack) $+ VarInt.decodeSize 32
+ src/Data/Multiaddr/IPv6.hs view
@@ -0,0 +1,29 @@+module Data.Multiaddr.IPv6+ (+ IPv6 (..),+ toString,+ parse,+ encode,+ parseB+ ) where++import qualified Text.ParserCombinators.ReadP as Parser+import qualified Data.ByteString as BSStrict+import qualified Data.Multiaddr.VarInt as VarInt++import Data.IP (IPv6 (..), fromIPv6b, toIPv6b)+import Data.Serialize.Get (Get)++toString :: IPv6 -> String+toString = show++parse :: Parser.ReadP IPv6+parse = Parser.readS_to_P reads++encode :: IPv6 -> BSStrict.ByteString+encode = BSStrict.pack . map fromIntegral . fromIPv6b++parseB :: Get IPv6+parseB =+ fmap (toIPv6b . map fromIntegral . BSStrict.unpack) $+ VarInt.decodeSize 128
+ src/Data/Multiaddr/Onion.hs view
@@ -0,0 +1,58 @@+{-# LANGUAGE DeriveGeneric #-}++module Data.Multiaddr.Onion+ (+ Onion (..),+ toString,+ parse,+ encode,+ parseB+ ) where++import qualified Text.ParserCombinators.ReadP as Parser+import qualified Data.ByteString as BSStrict+import qualified Data.ByteString.Char8 as BSStrictChar+import qualified Codec.Binary.Base32 as Base32+import qualified Data.Multiaddr.VarInt as VarInt+import qualified Data.Multiaddr.Port as Port++import GHC.Generics (Generic)+import Data.Serialize.Get (Get)+import Data.Char (isAlphaNum, toUpper, toLower)++data Onion = Onion+ {+ onionHash :: {-# UNPACK #-} !BSStrict.ByteString,+ onionPort :: {-# UNPACK #-} !Port.Port+ }+ deriving (Show, Eq, Generic)++toString :: Onion -> String+toString (Onion h p) =+ hashToStr ++ ":" ++ portToStr+ where+ hashToStr = map toLower . BSStrictChar.unpack $ Base32.encode h+ portToStr = Port.toString p++parse :: Parser.ReadP Onion+parse = do+ onionHash <- fmap+ (BSStrictChar.pack . map toUpper)+ (Parser.count 16 $ Parser.satisfy isAlphaNum)+ case Base32.decode onionHash of+ Right onionHashDecoded -> do+ Parser.char ':'+ onionPort <- Port.parse+ if Port.port onionPort > 0+ then return $ Onion onionHashDecoded onionPort+ else Parser.pfail+ otherwise -> Parser.pfail++encode :: Onion -> BSStrict.ByteString+encode (Onion h p) = BSStrict.append h $ Port.encode p++parseB :: Get Onion+parseB = do+ h <- VarInt.decodeSize 80+ p <- Port.parseB+ return $ Onion h p
+ src/Data/Multiaddr/Port.hs view
@@ -0,0 +1,59 @@+{-# LANGUAGE DeriveGeneric #-}++module Data.Multiaddr.Port+ (+ Port (..),+ toString,+ parse,+ encode,+ parseB,+ toPort,+ fromPort+ ) where++import qualified Text.ParserCombinators.ReadP as Parser+import qualified Data.ByteString as BSStrict+import qualified Data.Multiaddr.VarInt as VarInt++import GHC.Generics (Generic)+import Data.Word (Word16)+import Data.Char (isDigit)+import Data.Bytes.Get (runGetS)+import Data.Bytes.Put (runPutS)+import Data.Bytes.Serial (serialize, deserialize)+import Data.Serialize.Get (Get)++newtype Port = Port+ {+ port :: Word16+ }+ deriving (Show, Eq, Ord, Generic)++toPort :: (Integral a, Eq a) => a -> Maybe Port+toPort n | n < 0 || n > 65535 = Nothing+ | otherwise = Just $ Port (fromIntegral n)++fromPort :: Integral a => Port -> a+fromPort p = fromIntegral (port p)++toString :: Port -> String+toString (Port p) = show p++parse :: Parser.ReadP Port+parse = do+ port <- fmap read $ rangeParse (Parser.satisfy isDigit) 1 5 :: Parser.ReadP Int+ case toPort port of+ Just p -> return p+ Nothing -> Parser.pfail++rangeParse :: Read a => Parser.ReadP a -> Int -> Int -> Parser.ReadP [a]+rangeParse parse min max = foldr1 (Parser.<++) $+ fmap (\n -> Parser.count n $ parse) [max, max-1..min]++encode :: Port -> BSStrict.ByteString+encode (Port p) = (runPutS . serialize) $ p++parseB :: Get Port+parseB =+ VarInt.decodeSize 16 >>=+ (either fail (return . Port) . runGetS deserialize)
+ src/Data/Multiaddr/UnixPath.hs view
@@ -0,0 +1,37 @@+{-# LANGUAGE DeriveGeneric #-}++module Data.Multiaddr.UnixPath+ (+ UnixPath (..),+ toString,+ parse,+ encode,+ parseB+ ) where++import qualified Text.ParserCombinators.ReadP as Parser+import qualified Data.ByteString as BSStrict+import qualified Data.ByteString.Char8 as BSStrictChar+import qualified Data.Multiaddr.VarInt as VarInt++import GHC.Generics (Generic)+import System.FilePath (FilePath)+import Data.Serialize.Get (Get)++newtype UnixPath = UnixPath { path :: FilePath }+ deriving (Show, Eq, Ord, Generic)++toString :: UnixPath -> String+toString (UnixPath p) = show p++parse :: Parser.ReadP UnixPath+parse = do+ path <- Parser.many1 Parser.get+ return $ UnixPath $ "/" ++ path++encode :: UnixPath -> BSStrict.ByteString+encode (UnixPath p) = VarInt.encodeWith $ BSStrictChar.pack p++parseB :: Get UnixPath+parseB = fmap (UnixPath . BSStrictChar.unpack) $ VarInt.decodeSizeVar+
+ src/Data/Multiaddr/VarInt.hs view
@@ -0,0 +1,33 @@+module Data.Multiaddr.VarInt+ (+ VarInt (..),+ encode,+ encodeWith,+ decode,+ decodeSize,+ decodeSizeVar+ ) where++import qualified Data.ByteString as BSStrict++import Data.Bytes.VarInt (VarInt (..))+import Data.Bytes.Get (getByteString)+import Data.Bytes.Put (runPutS)+import Data.Bytes.Serial (serialize, deserialize)+import Data.Serialize.Get (Get)++encode :: VarInt Int -> BSStrict.ByteString+encode = runPutS . serialize++encodeWith :: BSStrict.ByteString -> BSStrict.ByteString+encodeWith b =+ BSStrict.append ((encode . fromIntegral . BSStrict.length) b) b++decode :: Get (VarInt Int)+decode = deserialize++decodeSize :: Integral n => n -> Get BSStrict.ByteString+decodeSize n = getByteString $ fromIntegral $ div n 8++decodeSizeVar :: Get BSStrict.ByteString+decodeSizeVar = getByteString . fromIntegral =<< decode
+ src/Data/MultiaddrPart.hs view
@@ -0,0 +1,175 @@+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DeriveDataTypeable #-}++module Data.MultiaddrPart+ (+ MultiaddrPart (..),+ toString,+ parse,+ encode,+ parseB,+ partHdrS,+ partHdrB+ ) where++import qualified Text.ParserCombinators.ReadP as Parser+import qualified Data.ByteString as BSStrict+import qualified Data.Multiaddr.IPFS as IPFS+import qualified Data.Multiaddr.VarInt as VarInt+import qualified Data.Multiaddr.IPv4 as IPv4+import qualified Data.Multiaddr.IPv6 as IPv6+import qualified Data.Multiaddr.Port as Port+import qualified Data.Multiaddr.Onion as Onion+import qualified Data.Multiaddr.UnixPath as UnixPath++import GHC.Generics (Generic)+import Data.Typeable (Typeable)+import Control.Applicative (some, (<|>))+import Control.Monad (unless)+import Data.Serialize.Get (Get)++data MultiaddrPart = IP4m { ip4m :: IPv4.IPv4 }+ | IP6m { ip6m :: IPv6.IPv6 }+ | TCPm { tcpm :: Port.Port }+ | UDPm { udpm :: Port.Port }+ | DCCPm { dccpm :: Port.Port }+ | SCTPm { sctpm :: Port.Port }+ | ONIONm { onionm :: Onion.Onion }+ | IPFSm { ipfsm :: IPFS.MultihashDigest }+ | P2Pm { p2pm :: IPFS.MultihashDigest }+ | UNIXm { unixm :: UnixPath.UnixPath }+ | UTPm+ | UDTm+ | QUICm+ | HTTPm+ | HTTPSm+ | WSm+ | WSSm+ deriving (Show, Eq, Generic, Typeable)++partHdrB :: MultiaddrPart -> VarInt.VarInt Int+partHdrB (IP4m _) = 4+partHdrB (IP6m _) = 41+partHdrB (TCPm _) = 6+partHdrB (UDPm _) = 17+partHdrB (DCCPm _) = 33+partHdrB (SCTPm _) = 132+partHdrB (ONIONm _) = 444+partHdrB (IPFSm _) = 421+partHdrB (P2Pm _) = 420+partHdrB (UNIXm _) = 400+partHdrB UTPm = 302+partHdrB UDTm = 301+partHdrB QUICm = 81+partHdrB HTTPm = 480+partHdrB HTTPSm = 443+partHdrB WSm = 477+partHdrB WSSm = 478++partHdrS :: MultiaddrPart -> String+partHdrS (IP4m _) = "ip4"+partHdrS (IP6m _) = "ip6"+partHdrS (TCPm _) = "tcp"+partHdrS (UDPm _) = "udp"+partHdrS (DCCPm _) = "dccp"+partHdrS (SCTPm _) = "sctp"+partHdrS (ONIONm _) = "onion"+partHdrS (IPFSm _) = "ipfs"+partHdrS (P2Pm _) = "p2p"+partHdrS (UNIXm _) = "unix"+partHdrS UTPm = "utp"+partHdrS UDTm = "udt"+partHdrS QUICm = "quic"+partHdrS HTTPm = "http"+partHdrS HTTPSm = "https"+partHdrS WSm = "ws"+partHdrS WSSm = "wss"++toString :: MultiaddrPart -> String+toString p = concat ["/", partHdrS p, go p]+ where+ go (IP4m i) = "/" ++ IPv4.toString i+ go (IP6m i) = "/" ++ IPv6.toString i+ go (TCPm p) = "/" ++ Port.toString p+ go (UDPm p) = "/" ++ Port.toString p+ go (DCCPm p) = "/" ++ Port.toString p+ go (SCTPm p) = "/" ++ Port.toString p+ go (ONIONm o) = "/" ++ Onion.toString o+ go (IPFSm h) = "/" ++ IPFS.toString h+ go (P2Pm h) = "/" ++ IPFS.toString h+ go (UNIXm p) = "/" ++ UnixPath.toString p+ go UTPm = ""+ go UDTm = ""+ go QUICm = ""+ go HTTPm = ""+ go HTTPSm = ""+ go WSm = ""+ go WSSm = ""++parse :: Parser.ReadP MultiaddrPart+parse =+ (IP4m <$> (parseHdr (IP4m undefined) *> parseSep *> IPv4.parse)) <|>+ (IP6m <$> (parseHdr (IP6m undefined) *> parseSep *> IPv6.parse)) <|>+ (TCPm <$> (parseHdr (TCPm undefined) *> parseSep *> Port.parse)) <|>+ (UDPm <$> (parseHdr (UDPm undefined) *> parseSep *> Port.parse)) <|>+ (DCCPm <$> (parseHdr (DCCPm undefined) *> parseSep *> Port.parse)) <|>+ (SCTPm <$> (parseHdr (SCTPm undefined) *> parseSep *> Port.parse)) <|>+ (ONIONm <$> (parseHdr (ONIONm undefined) *> parseSep *> Onion.parse)) <|>+ (IPFSm <$> (parseHdr (IPFSm undefined) *> parseSep *> IPFS.parse)) <|>+ (P2Pm <$> (parseHdr (P2Pm undefined) *> parseSep *> IPFS.parse)) <|>+ (UNIXm <$> (parseHdr (UNIXm undefined) *> parseSep *> UnixPath.parse)) <|>+ (parseHdr UTPm) <|>+ (parseHdr UDTm) <|>+ (parseHdr QUICm) <|>+ (parseHdr HTTPm) <|>+ (parseHdr HTTPSm) <|>+ (parseHdr WSm) <|>+ (parseHdr WSSm)+ where+ parseHdr c = c <$ (parseSep *> Parser.string (partHdrS c))+ parseSep = some $ Parser.char '/'++encode :: MultiaddrPart -> BSStrict.ByteString+encode p = BSStrict.append (VarInt.encode $ partHdrB p) (go p)+ where+ go (IP4m i) = IPv4.encode i+ go (IP6m i) = IPv6.encode i+ go (TCPm p) = Port.encode p+ go (UDPm p) = Port.encode p+ go (DCCPm p) = Port.encode p+ go (SCTPm p) = Port.encode p+ go (ONIONm o) = Onion.encode o+ go (IPFSm h) = IPFS.encode h+ go (P2Pm h) = IPFS.encode h+ go (UNIXm p) = UnixPath.encode p+ go UTPm = BSStrict.empty+ go UDTm = BSStrict.empty+ go QUICm = BSStrict.empty+ go HTTPm = BSStrict.empty+ go HTTPSm = BSStrict.empty+ go WSm = BSStrict.empty+ go WSSm = BSStrict.empty++parseB :: Get MultiaddrPart+parseB =+ (IP4m <$> (parseHdrB (IP4m undefined) *> IPv4.parseB)) <|>+ (IP6m <$> (parseHdrB (IP6m undefined) *> IPv6.parseB)) <|>+ (TCPm <$> (parseHdrB (TCPm undefined) *> Port.parseB)) <|>+ (UDPm <$> (parseHdrB (UDPm undefined) *> Port.parseB)) <|>+ (DCCPm <$> (parseHdrB (DCCPm undefined) *> Port.parseB)) <|>+ (SCTPm <$> (parseHdrB (SCTPm undefined) *> Port.parseB)) <|>+ (ONIONm <$> (parseHdrB (ONIONm undefined) *> Onion.parseB)) <|>+ (IPFSm <$> (parseHdrB (IPFSm undefined) *> IPFS.parseB)) <|>+ (P2Pm <$> (parseHdrB (P2Pm undefined) *> IPFS.parseB)) <|>+ (UNIXm <$> (parseHdrB (UNIXm undefined) *> UnixPath.parseB)) <|>+ (parseHdrB UTPm) <|>+ (parseHdrB UDTm) <|>+ (parseHdrB QUICm) <|>+ (parseHdrB HTTPm) <|>+ (parseHdrB HTTPSm) <|>+ (parseHdrB WSm) <|>+ (parseHdrB WSSm)+ where+ parseHdrB c = c <$ do+ i <- VarInt.decode+ unless (i == partHdrB c) $ fail "Wrong protocol index"
+ test/Spec.hs view
@@ -0,0 +1,259 @@+{-# LANGUAGE OverloadedStrings #-}++import qualified Data.ByteString as BSStrict+import qualified Data.ByteString.Char8 as BSStrictChar+import qualified Data.ByteString.Lazy.Char8 as BSLazyChar+import qualified Codec.Binary.Base32 as Base32+import qualified Data.Multihash.Digest as MHD+import qualified Data.Multihash.Base as MHB++import Data.IP (IPv4, IPv6, fromIPv4, fromIPv6, toIPv4, toIPv6)+import Data.Maybe (fromJust)+import Data.Char (toUpper)+import Data.Either.Unwrap (fromRight)+import Control.Exception (evaluate)+import Data.Either (isLeft, isRight)++import Test.Hspec+import Data.Multiaddr+import Data.Multiaddr.Onion (Onion (..))+import Data.Multiaddr.UnixPath (UnixPath (..))+import Data.Multiaddr.Port (toPort)++parseFail str = it ("should not parse `" ++ str ++ "`") $ do+ (toMultiaddr str) `shouldSatisfy` isLeft++parseSucceed str addr = it ("should parse `" ++ str ++ "`") $ do+ (toMultiaddr str) `shouldBe` Right addr++parseEqual str1 str2 = it ("should be equal `" ++ str1 ++ "` == `" ++ str2) $ do+ (toMultiaddr str1) `shouldBe` (toMultiaddr str2)++parseNotEqual str1 str2 = it ("should not be equal `" ++ str1 ++ "` /= `" ++ str2) $ do+ (toMultiaddr str1) `shouldNotBe` (toMultiaddr str2)++encodeSucceed str bs = it ("should encode `" ++ str ++ "` to `" ++ show bs ++ "`") $ do+ (fmap encode $ toMultiaddr str) `shouldBe` (Right $ BSStrict.pack bs)++decodeSucceed bs str = it ("should decode `" ++ show bs ++ "` to `" ++ str ++ "`") $ do+ (decode $ BSStrict.pack bs) `shouldBe` (toMultiaddr str)++encapsulateSucceed addr1 addr2 addr3 = it ("should encapsulate `" ++ toString addr2 ++ "` onto `" ++ toString addr1 ++ "`") $ do+ (encapsulate addr1 addr2) `shouldBe` addr3++decapsulateSucceed addr1 addr2 addr3 = it ("should decapsulate `" ++ toString addr1 ++ "` from `" ++ toString addr2 ++ "`") $ do+ (decapsulate addr1 addr2) `shouldBe` Just addr3++findFirstSucceed part1 part2 addr = it ("should find first `" ++ show part2 ++ "` in `" ++ toString addr) $ do+ (findFirstPart part1 addr) `shouldBe` Just part2++toIPFSm hash = IPFSm+ $ fromRight+ $ MHD.decode+ $ BSLazyChar.toStrict+ $ fromRight+ $ MHB.decode MHB.Base58+ $ BSLazyChar.pack hash++toP2Pm hash = P2Pm+ $ fromRight+ $ MHD.decode+ $ BSLazyChar.toStrict+ $ fromRight+ $ MHB.decode MHB.Base58+ $ BSLazyChar.pack hash++toONIONm hash port = ONIONm $+ Onion+ (fromRight $+ Base32.decode $+ BSStrictChar.pack $ map toUpper hash)+ (fromJust $ toPort port)++main :: IO ()+main = hspec $ do+ describe "Multiaddr read" $ do+ parseFail "/ip4"+ parseFail "/ip4::1"+ parseFail "/ip4/fdpsofodsajfdoisa"+ parseFail "/ip6"+ parseFail "/udp"+ parseFail "/tcp"+ parseFail "/sctp"+ parseFail "/udp/65536"+ parseFail "/tcp/65536"+ parseFail "/quic/65536"+ parseFail "/onion/9imaq4ygg2iegci7:80"+ parseFail "/onion/aaimaq4ygg2iegci7:80"+ parseFail "/onion/timaq4ygg2iegci7:0"+ parseFail "/onion/timaq4ygg2iegci7:-1"+ parseFail "/onion/timaq4ygg2iegci7"+ parseFail "/onion/timaq4ygg2iegci@:666"+ parseFail "/udp/1234/sctp"+ parseFail "/udp/1234/udt/1234"+ parseFail "/udp/1234/utp/1234"+ parseFail "/ip4/127.0.0.1/udp/jfodsajfidosajfoidsa"+ parseFail "/ip4/127.0.0.1/udp"+ parseFail "/ip4/127.0.0.1/tcp/jfodsajfidosajfoidsa"+ parseFail "/ip4/127.0.0.1/tcp"+ parseFail "/ip4/127.0.0.1/quic/1234"+ parseFail "/ip4/127.0.0.1/ipfs"+ parseFail "/ip4/127.0.0.1/ipfs/tcp"+ parseFail "/unix"+ parseFail "/ip4/1.2.3.4/tcp/80/unix"+ parseSucceed "/ip4/1.2.3.4" $ Multiaddr [(IP4m $ toIPv4 [1,2,3,4])]+ parseSucceed "/ip4/0.0.0.0" $ Multiaddr [(IP4m $ toIPv4 [0,0,0,0])]+ parseSucceed "/ip4/0.0.0.0//////" $ Multiaddr [(IP4m $ toIPv4 [0,0,0,0])]+ parseSucceed "/ip6/::1" $ Multiaddr [(IP6m $ toIPv6 [0,0,0,0,0,0,0,1])]+ parseSucceed "/ip6/2601:9:4f81:9700:803e:ca65:66e8:c21" $+ Multiaddr+ [+ (IP6m $ toIPv6+ [0x2601, 0x9, 0x4f81, 0x9700, 0x803e, 0xca65, 0x66e8, 0xc21])+ ]+ parseSucceed "/ip6/2601:9:4f81:9700:803e:ca65:66e8:c21/udp/1234/quic" $+ Multiaddr+ [+ (IP6m $ toIPv6+ [0x2601, 0x9, 0x4f81, 0x9700, 0x803e, 0xca65, 0x66e8, 0xc21]),+ (UDPm $ fromJust $ toPort 1234),+ (QUICm)+ ]+ parseSucceed "/onion/timaq4ygg2iegci7:1234" $+ Multiaddr [(toONIONm "timaq4ygg2iegci7" 1234)]+ parseSucceed "/onion/timaq4ygg2iegci7:80/http" $+ Multiaddr+ [+ (toONIONm "timaq4ygg2iegci7" 80),+ (HTTPm)+ ]+ parseSucceed "/udp/0" $+ Multiaddr [(UDPm $ fromJust $ toPort 0)]+ parseSucceed "/tcp/0" $+ Multiaddr [(TCPm $ fromJust $ toPort 0)]+ parseSucceed "/sctp/0" $+ Multiaddr [(SCTPm $ fromJust $ toPort 0)]+ parseSucceed "/udp/1234" $+ Multiaddr [(UDPm $ fromJust $ toPort 1234)]+ parseSucceed "/tcp/1234" $+ Multiaddr [(TCPm $ fromJust $ toPort 1234)]+ parseSucceed "/tcp/1234////" $+ Multiaddr [(TCPm $ fromJust $ toPort 1234)]+ parseSucceed "/udp/65535" $+ Multiaddr [(UDPm $ fromJust $ toPort 65535)]+ parseSucceed "/tcp/65535" $+ Multiaddr [(TCPm $ fromJust $ toPort 65535)]+ parseSucceed "/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC" $+ Multiaddr [(toIPFSm "QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC")]+ parseSucceed "/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC////" $+ Multiaddr [(toIPFSm "QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC")]+ parseSucceed "/udp/1234/sctp/1234" $+ Multiaddr [(UDPm $ fromJust $ toPort 1234), (SCTPm $ fromJust $ toPort 1234)]+ parseSucceed "/udp/1234/udt" $+ Multiaddr [(UDPm $ fromJust $ toPort 1234), UDTm]+ parseSucceed "/udp/1234/utp" $+ Multiaddr [(UDPm $ fromJust $ toPort 1234), UTPm]+ parseSucceed "/tcp/1234/http" $+ Multiaddr [(TCPm $ fromJust $ toPort 1234), HTTPm]+ parseSucceed "/tcp/1234/https" $+ Multiaddr [(TCPm $ fromJust $ toPort 1234), HTTPSm]+ parseSucceed "/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC/tcp/1234" $+ Multiaddr+ [+ (toIPFSm "QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC"),+ (TCPm $ fromJust $ toPort 1234)+ ]+ parseSucceed "/ip4/127.0.0.1/udp/1234" $+ Multiaddr+ [+ (IP4m $ toIPv4 [127,0,0,1]),+ (UDPm $ fromJust $ toPort 1234)+ ]+ parseSucceed "/ip4/127.0.0.1/udp/0" $+ Multiaddr+ [+ (IP4m $ toIPv4 [127,0,0,1]),+ (UDPm $ fromJust $ toPort 0)+ ]+ parseSucceed "/ip4/127.0.0.1/tcp/1234" $+ Multiaddr+ [+ (IP4m $ toIPv4 [127,0,0,1]),+ (TCPm $ fromJust $ toPort 1234)+ ]+ parseSucceed "/ip4/127.0.0.1/tcp/1234/" $+ Multiaddr+ [+ (IP4m $ toIPv4 [127,0,0,1]),+ (TCPm $ fromJust $ toPort 1234)+ ]+ parseSucceed "/ip4/127.0.0.1/udp/1234/quic" $+ Multiaddr+ [+ (IP4m $ toIPv4 [127,0,0,1]),+ (UDPm $ fromJust $ toPort 1234),+ (QUICm)+ ]+ parseSucceed "/ip4/127.0.0.1/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC" $+ Multiaddr+ [+ (IP4m $ toIPv4 [127,0,0,1]),+ (toIPFSm "QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC")+ ]+ parseSucceed "/ip4/127.0.0.1/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC/tcp/1234" $+ Multiaddr+ [+ (IP4m $ toIPv4 [127,0,0,1]),+ (toIPFSm "QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC"),+ (TCPm $ fromJust $ toPort 1234)+ ]+ parseSucceed "/unix/a/b/c/d/e" $+ Multiaddr [(UNIXm $ UnixPath "/a/b/c/d/e")]+ parseSucceed "/unix/a/b/c/d/e/" $+ Multiaddr [(UNIXm $ UnixPath "/a/b/c/d/e")]+ parseSucceed "/unix/stdio///////" $+ Multiaddr [(UNIXm $ UnixPath "/stdio")]+ parseSucceed "/unix/stdio////a/b/c/d/e/f////" $+ Multiaddr [(UNIXm $ UnixPath "/stdio////a/b/c/d/e/f")]+ parseSucceed "/ip4/1.2.3.4/tcp/80/unix/a/b/c/d/e/f" $+ Multiaddr+ [+ (IP4m $ toIPv4 [1,2,3,4]),+ (TCPm $ fromJust $ toPort 80),+ (UNIXm $ UnixPath "/a/b/c/d/e/f")+ ]+ parseSucceed "/ip4/127.0.0.1/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC/tcp/1234/unix/stdio" $+ Multiaddr+ [+ (IP4m $ toIPv4 [127,0,0,1]),+ (toIPFSm "QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC"),+ (TCPm $ fromJust $ toPort 1234),+ (UNIXm $ UnixPath "/stdio")+ ]+ describe "Multiaddr equal" $ do+ parseEqual "/ip4/127.0.0.1/tcp/1234" "/ip4/127.0.0.1/tcp/1234/"+ parseNotEqual "/ip4/127.0.0.1/udp/1234" "/ip4/127.0.0.1/tcp/1234"+ describe "Multiaddr encode" $ do+ encodeSucceed "/ip4/127.0.0.1/udp/1234" [4, 127, 0, 0, 1, 17, 4, 210]+ encodeSucceed "/ip4/127.0.0.1/tcp/4321" [4, 127, 0, 0, 1, 6, 16, 225]+ encodeSucceed "/ip4/127.0.0.1/udp/1234/ip4/127.0.0.1/tcp/4321" [4, 127, 0, 0, 1, 17, 4, 210, 4, 127, 0, 0, 1, 6, 16, 225]+ describe "Multiaddr decode" $ do+ decodeSucceed [4, 127, 0, 0, 1, 17, 4, 210] "/ip4/127.0.0.1/udp/1234"+ decodeSucceed [4, 127, 0, 0, 1, 6, 16, 225] "/ip4/127.0.0.1/tcp/4321"+ decodeSucceed [4, 127, 0, 0, 1, 17, 4, 210, 4, 127, 0, 0, 1, 6, 16, 225] "/ip4/127.0.0.1/udp/1234/ip4/127.0.0.1/tcp/4321"+ decodeSucceed [188, 3, 0, 16, 192, 67, 152, 49, 180, 130, 24, 72, 0, 80] "/onion/aaimaq4ygg2iegci:80"+ describe "Multiaddr encapsulate" $ do+ encapsulateSucceed+ ("/ip4/127.0.0.1" :: Multiaddr)+ ("/udp/1234" :: Multiaddr)+ ("/ip4/127.0.0.1/udp/1234" :: Multiaddr)+ describe "Multiaddr decapsulate" $ do+ decapsulateSucceed+ ("/udp/5678" :: Multiaddr)+ ("/udp/5678/ip4/127.0.0.1/udp/1234" :: Multiaddr)+ ("/ip4/127.0.0.1/udp/1234" :: Multiaddr)+ describe "Multiaddr find" $ do+ findFirstSucceed+ (IP4m undefined)+ (IP4m $ toIPv4 [127, 0, 0, 1])+ ("/ip4/127.0.0.1/utp/tcp/5555/udp/1234/utp/ipfs/QmbHVEEepCi7rn7VL7Exxpd2Ci9NNB6ifvqwhsrbRMgQFP" :: Multiaddr)