cohttp-lwtというライブラリを使って、モナディックに非同期なHTTP通信を行うとよいらしいです。

インストール

opam install tls cohttp # tlsを入れないとhttpsが使えない

とりあえず実行

モナディックな記法にさえ慣れていれば、割りとスッキリ書けます。某言語みたいにHTTPリクエスト送りたいだけなのに謎演算子が大量に発生したりしないのも◎。

(* httptest.ml *)
open Lwt
open Cohttp
open Cohttp_lwt_unix

let show_body url =
  Client.get (Uri.of_string url) >>= fun (resp, body) ->
  Cohttp_lwt_body.to_string body >>= fun (strbody) ->
  print_endline strbody;
  return ()

let () = Lwt_main.run (show_body "http://dokodeglobal.com/")

実行結果

$ ocamlfind ocamlopt -o httptest.native -linkpkg -package cohttp,lwt,cohttp.lwt httptest.ml
$ ./httptest.native
<!doctype html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <title>dokode global</title>
    <link rel="stylesheet" href="css/style.css">
  </head>
  <body>
  ...

POST

GETのときにClient.getを使ったように、POSTの場合はClient.postを使えます。その他のメソッドも一緒。

let post =
  let reqbody = Uri.encoded_of_query [("hoge", ["fuga"]); ("foo", ["bar"])]
                |> Cohttp_lwt_body.of_string in
  Client.post ~body:reqbody (Uri.of_string "http://example.com/")

post_formを使うと、もう少し楽に書けます。

let post =
  Client.post_form (Uri.of_string "http://example.com/")
                   ~params:[("hoge", ["fuga"]); ("foo", ["bar"])]

ヘッダをつける

~headers引数にヘッダを設定することで、ヘッダの値も指定できます。

let post =
  Client.post_form (Uri.of_string "http://example.com/")
                   ~params:[("hoge", ["fuga"]); ("foo", ["bar"])]
                   ~headers:(Header.of_list [("User-Agent", "Mozilla/5.0")])

エラー処理

エラー処理はLwtの機能を使って行います。

let alwaysfails =
  Client.get (Uri.of_string "http://this-page-does-not-exist.com/")

let () = Lwt_main.run (catch (fun () -> alwaysfails >>= fun (_, _) ->
                                        print_endline "ok"; return ())
                             (function
                              | Failure msg -> print_endline ("error: " ^ msg);
                                               return ()))