Haskellでは、http-clientライブラリを用いることで、HTTPクライアント機能を簡単に実装できます。http-client以外にも何種類かライブラリがありますが、今回はhttp-client, http-client-tlsの機能と使い方をまとめておきます。
本エントリで紹介するhttp-client, http-client-tlsライブラリの機能:
- 単純なHTTP GETリクエスト
- Managerのカスタマイズ
- Requestのカスタマイズ
- Responseの操作
- エラーハンドリング
単純なHTTP GETリクエスト
{-# LANGUAGE OverloadedStrings #-}
import Network.HTTP.Client
import Network.HTTP.Types.Status (statusCode)
main :: IO ()
main = do
manager <- newManager defaultManagerSettings
request <- parseRequest "http://httpbin.org/get"
response <- httpLbs request manager
putStrLn $ "The status code was: " ++ (show $ statusCode $ responseStatus response)
print $ responseBody response
デフォルトManagerと、"http://httpbin/org/get"へのRequestを生成し、それらを引数にhttpLbsを呼び出して、サーバーからのResponseを得てその内容を表示するサンプルです。
文字列リテラルをRequest, ByteStringに変換する目的でOverloadedStrings言語拡張を利用しています。
主要な型の説明:
- Manager
- Managerはサーバーとの間に生成するコネクションの管理するもの。
- 複数サーバーのコネクションを管理する前提で実装されており、クライアントの中では1つのインスタンスを共有することが推奨されている。
- Managerに対しては以下のような設定ができる。デフォルト設定を併記。
- プロキシ設定:環境変数(http_proxy)の値を利用
- TLS(https):サポートなし
- 1サーバーあたりのkeep-aliveコネクション維持数: 10
- 最大同時オープンコネクション数: 512
- 受信タイムアウト: 30秒
- Request
- 特定サーバーに対して送信する1つのリクエストを表す型。
- parseRequest関数などでHTTP METHOD, URIを指定してインスタンスを生成する。
- Request単位の細かい設定ができる
- ベーシック認証情報(アカウント&パスワード)
- プロキシ設定、プロキシ認証情報
- クエリストリング、bodyなどの送信データ
- HTTPメソッド(GET, POST, DELETEなど)、ヘッダ は生成したRequestインスタンスに対して、record構文で設定する
- メソッドのデフォルトはGET
- Content-Length, Transfer-Encoding, Accept-Encodingが自動で設定される
- Response
- サーバーに送信したRequestに対応するサーバーからのレスポンスを表す型。HTTPステータスコード、レスポンスヘッダ、レスポンスボディなどを取り出すことができます。
Managerのカスタマイズ
defaultManagerSettingsの代わりにtlsManagerSettingsを用いてManagerを生成することでhttps通信が可能になります。
以下のサンプルではhttps対応に加えて、プロキシサーバーとして"127.0.0.1:8080"をManagerに設定しています。
{-# LANGUAGE OverloadedStrings #-}
import Network.HTTP.Client
import Network.HTTP.Types.Status (statusCode)
import Network.HTTP.Client.TLS (tlsManagerSettings) -- 新たにimport文を追加
main :: IO ()
main = do
-- manageSetProxyでデフォルトのプロキシ設定を上書く。
-- tlsManagerSettingsを利用するとこでhttps通信が可能になる。
manager <- newManager $ managerSetProxy (useProxy $ Proxy "127.0.0.1" 8080) tlsManagerSettings
request <- parseRequest "https://httpbin.org/get" -- https通信に変更
response <- httpLbs request manager
putStrLn $ "The status code was: " ++ (show $ statusCode $ responseStatus response)
print $ responseBody response
プロキシの設定:
- デフォルトの挙動。環境変数(http_proxy/https_proxy) を参照する。
- defaultProxy :: ProxyOverride
- プロキシサーバーの設定を無視して通信する。
- コードで明示的にプロキシサーバーを設定する。
- useProxy :: Proxy -> ProxyOverride
レコード構文でManagerSettings値を生成する方法:
非公開APIを利用するため、オススメの方法ではありませんが以下のようにレコード構文を用いることで、Managerの細かい設定をカスタマイズすることができます。
import Network.HTTP.Client.Internal
-- tlsManagerSettingsをベースにmanagerResponseTimeoutを30→5秒に、
-- managerConnCountを10→3に変更
mySettings :: ManagerSettings
mySettings = tlsManagerSettings
{ managerResponseTimeout = responseTimeoutMicro 5000000
, managerConnCount = 3
}
Requestのカスタマイズ
以下のコードはRequestに対して、リクエストヘッダ、クエリーパラメタ、ベーシック認証情報を設定するサンプルです。Requestに対して設定できる項目の詳細は、
Request type and fieldsを参照してください。
{-# LANGUAGE OverloadedStrings #-}
import Network.HTTP.Client
import Network.HTTP.Types.Status (statusCode)
main :: IO ()
main = do
manager <- newManager defaultManagerSettings
initialRequest <- parseRequest "http://httpbin.org/anything"
let request = initialRequest
{ method = "POST" -- "GET", "PUT", "DELETE"などのメソッドを指定
, queryString = "foo=bar&xxx=yyy" -- parseRequestのuriに記述してもよい
, requestHeaders = -- ヘッダはタプル(名前、値)のリストで指定
[ ("User-Agent", "New Agent!")
, ("Content-Type", "text/plain")
, ("Added-Header", "hoge")
]
, requestBody = "body string."
}
let authRequest = applyBasicAuth "user" "pass" request -- ベーシック認証情報付与
response <- httpLbs authRequest manager
putStrLn $ "The status code was: " ++ (show $ statusCode $ responseStatus response)
print $ responseBody response
Responseの操作
httpLbs関数はRequestとManagerを引数にとり、IO (Response ByteString)を返します。Response型クラスの関数で、Responseからステータスコード、レスポンスヘッダなどの情報を取り出すことができます。ResonseのAPIリファレンスは
こちら。
httpLbsは全データを受信しますが、大きなデータをレスポンスとして受信する場合はhttpLbsの代わりに、withResponseとbrReadを利用して逐次受信することが推奨されています。以下にそのサンプルを記載します。
{-# LANGUAGE OverloadedStrings #-}
import Network.HTTP.Client
import Network.HTTP.Types.Status (statusCode)
import qualified Data.ByteString as B
main :: IO ()
main = do
manager <- newManager defaultManagerSettings
request <- parseRequest "http://httpbin.org/get"
withResponse request manager receiveResponse
receiveResponse :: Response BodyReader -> IO ()
receiveResponse response = do
putStrLn $ "response version: " ++ (show $ responseVersion response)
putStrLn $ "status code: " ++ (show $ statusCode $ responseStatus response)
putStrLn $ "response header: " ++ (show $ responseHeaders response)
-- receive body data block by block
let loop = do
bs <- brRead $ responseBody response
if B.null bs
then putStrLn "\nFinished response body"
else do
print bs
loop
loop
エラーハンドリング
以下は、urlのパース(parseRequest)、及び、サーバーとの通信(httpLbs)のエラーハンドリングを行うサンプルです。これらの関数はエラー時にはHttpExceptionをスローします。サンプルではtry関数を用い、Left eでスローされたHttpExceptionの情報を表示しています。
{-# LANGUAGE OverloadedStrings #-}
import Network.HTTP.Client
import Network.HTTP.Types.Status (statusCode)
import Control.Exception (try)
import System.Environment (getArgs)
createRequest :: [String] -> IO Request
createRequest [] = do
putStrLn "NOTE: no argument is given. so use not existing url."
parseRequest "http://unknown-host:80/" -- valid but not exists
createRequest args = do
let url = head args
eRequest <- try $ parseRequest url
case eRequest of
Left e -> do
print (e :: HttpException)
putStrLn $ "given url (1st argument) is invalid: " ++ url
error "error!"
Right request -> return $ request
main :: IO ()
main = do
args <- getArgs
manager <- newManager defaultManagerSettings
request <- createRequest args
eResponse <- try $ httpLbs request manager
case eResponse of
Left e -> do
print (e :: HttpException)
putStrLn $ "cannot reach server with given url: " ++ (head args)
Right response -> do
putStrLn $ "The status code was: " ++ (show $ statusCode $ responseStatus response)
print $ responseBody response
他のライブラリ:
このエントリではhttp-clientの使い方を紹介していますが、これ以外にも以下のようなライブラリがあるようです。これらのライブラリも利用する機会があれば比較などを含めて記事にしたいと思います。
参考:
- http-clientライブラリ
- http-client-tlsライブラリ
- テストサービス