rtorrent-rpc-0.2.0.0: Network/RTorrent/SCGI.hs
{-# LANGUAGE OverloadedStrings #-}
{-# OPTIONS_HADDOCK hide #-}
{-|
Module : SCGI
Copyright : (c) Kai Lindholm, 2014
License : MIT
Maintainer : megantti@gmail.com
Stability : experimental
An internal module for establishing a connection with RTorrent.
-}
module Network.RTorrent.SCGI (Headers, Body(..), query) where
import Control.Applicative
import Data.Either (partitionEithers)
import Data.Monoid
import Blaze.ByteString.Builder
import Blaze.ByteString.Builder.Char8
import Blaze.Text
import Data.Attoparsec.ByteString.Char8 (Parser)
import qualified Data.Attoparsec.ByteString.Char8 as A
import Data.ByteString (ByteString)
import qualified Data.ByteString as BS
import Network
type Headers = [(ByteString, ByteString)]
data Body = Body {
headers :: Headers
, body :: ByteString
} deriving Show
makeRequest :: Body -> ByteString
makeRequest (Body hd bd) = res
where
res = toByteString . mconcat $ [
integral len
, fromChar ':'
, fromByteString hdbs
, fromChar ','
, fromByteString bd
]
fromBS0 bs = fromWrite $ writeByteString bs <> writeWord8 0
hdbs = toByteString . mconcat $
(fromBS0 "CONTENT_LENGTH" <> integral (BS.length bd) <> fromWord8 0):
map (\(a, b) -> fromBS0 a <> fromBS0 b) hd
len = BS.length hdbs
parseResponse :: Parser Body
parseResponse = parseBody
where
lineParser :: Parser ByteString
lineParser =
mappend <$> A.takeTill (== '\r') <*> (("\r\n" *> pure "") <|> lineParser)
headerParser = (,) <$> A.takeTill (== ':') <*> (": " *> lineParser)
contentParser = "\r\n" *> A.takeByteString
parseBody = do
header <- headerParser
(moreHeaders, [content]) <-
partitionEithers <$> A.many' (A.eitherP headerParser contentParser)
return $ Body (header : moreHeaders) content
query :: HostName -> Int -> Body -> IO (Either String Body)
query host port queryBody = do
h <- connectTo host (PortNumber (toEnum port))
BS.hPut h (makeRequest queryBody)
A.parseOnly parseResponse <$> BS.hGetContents h