module Network.Ntrip.Client where
import BasicPrelude
import Control.Concurrent.Async
import Data.Attoparsec.ByteString
import Data.Attoparsec.ByteString.Char8 (decimal, endOfLine, isEndOfLine)
import Data.ByteString.Base64
import Data.CaseInsensitive
import Data.Conduit
import Data.Conduit.Attoparsec
import Data.Conduit.Network
import Network.HTTP.Types
tcpClient :: ByteString -> Int -> Source IO ByteString -> Sink ByteString IO () -> IO ()
tcpClient host port source sink =
runTCPClient (clientSettings port host) $ \ad ->
void $ concurrently (source $$ appSink ad) (appSource ad $$ sink)
sourceNtrip :: ByteString -> ByteString -> ByteString -> Source IO ByteString -> Source IO ByteString
sourceNtrip path user password source = do
yield $ intercalate "\r\n" $
[ methodGet <> " " <> path <> " HTTP/1.0"
, original hUserAgent <> ": NTRIP ntrip-client/0.0"
, original hAuthorization <> ": " <> encode (user <> ":" <> password)
, ""
, ""
]
source
parseNtrip :: Parser (ByteString, Status)
parseNtrip = do
format <- parseFormat
code <- parseCode
message <- parseMessage
return (format, Status code message)
where
takeSpace = takeWhile1 (== 32)
takeInClass = takeWhile1 . inClass
takeEndOfLine1 = takeWhile1 (not . isEndOfLine)
parseFormat = takeInClass "a-zA-Z0-9/." <* takeSpace
parseCode = decimal <* takeSpace
parseMessage = takeEndOfLine1 <* endOfLine
sinkNtrip :: Sink ByteString IO () -> Sink ByteString IO ()
sinkNtrip sink = do
(format, status) <- sinkParser parseNtrip
unless (format == "ICY") $ throwIO $ userError "Unsupported format"
unless (status == status200) $ throwIO $ userError "Bad status"
sink
runNtrip :: ByteString -> Int -> ByteString -> ByteString -> ByteString -> Source IO ByteString -> Sink ByteString IO () -> IO ()
runNtrip host port path user password source sink =
tcpClient host port (sourceNtrip path user password source) (sinkNtrip sink)