-- | JSON-RPC over HTTP. We only support Basic Authentication at the moment. {-# LANGUAGE CPP #-} module Bitcoin.RPC.HTTP where -------------------------------------------------------------------------------- import Data.Maybe -- import qualified Data.ByteString.Char8 as B import qualified Network.URI as HTTP import qualified Network.Stream as HTTP import qualified Network.HTTP.Base as HTTP import qualified Network.HTTP as HTTP import Text.JSON import Bitcoin.Protocol.Base64 import Bitcoin.RPC.JSON -------------------------------------------------------------------------------- -- | An URI plus username/password for basic auth data BitcoinURI = BitcoinURI (String,String) HTTP.URI deriving Show -- | Example: -- -- > myuri = bitcoinURI username password host port -- -- Default host is 127.0.0.1, default port is 8332 -- bitcoinURI :: String -> String -> Maybe String -> Maybe Int -> BitcoinURI bitcoinURI username pw mbhost mbport = BitcoinURI (username,pw) (fromJust (HTTP.parseURI text)) where host = case mbhost of { Nothing -> "127.0.0.1" ; Just h -> h } port = case mbport of Just p -> p #ifdef WITH_TESTNET Nothing -> 18332 #else Nothing -> 8332 #endif text = "http://" ++ host ++ ":" ++ show port ++ "/" -------------------------------------------------------------------------------- rpcCall :: JSON a => BitcoinURI -> Request a -> IO (Either String Response) rpcCall (BitcoinURI (uname,pw) uri) request = do let text = (Text.JSON.encode $ encodeRequest request) let Base64 unpw = base64Encode (uname ++ ":" ++ pw) auth = "Basic " ++ unpw -- print auth -- debugging !!!!!!!!!!!!!! -- putStrLn text let headers = [ HTTP.Header HTTP.HdrContentType "application/json-rpc" , HTTP.Header HTTP.HdrContentLength (show $ length text) -- now, seriously... why do i have to do this manually?? , HTTP.Header HTTP.HdrAccept "application/json-rpc" , HTTP.Header HTTP.HdrAuthorization auth ] let req = HTTP.Request uri HTTP.POST headers text result <- HTTP.simpleHTTP req :: IO (Either HTTP.ConnError (HTTP.Response String)) case result of Left connerror -> return $ Left ("HTTP connection error - " ++ show connerror) Right rsp -> case HTTP.rspCode rsp of (2,0,0) -> return $ case (Text.JSON.decode $ HTTP.rspBody rsp) of Text.JSON.Error msg -> Left ("cannot parse JSON - " ++ show msg) Text.JSON.Ok jsvalue -> case decodeResponse jsvalue of Just x -> Right x Nothing -> Left "cannot decode JSON-RPC response" (a,b,c) -> return $ Left ("HTTP error code " ++ show a ++ show b ++ show c ++ " - " ++ HTTP.rspReason rsp) --------------------------------------------------------------------------------