How to write a simple web client?

I’m writing a simple client in OCaml to access the Wallabag api, and I’m stuck at the part where I need to send a get request with an arbitrary header. For context, here is what I’m trying to reproduce in OCaml: https://app.wallabag.it/developer/howto/first-app

I can get the token doing this:

let site = "https://app.wallabag.it/"
let token_url = "oauth/v2/token"

let params = [
  ("grant_type", "password");
  ("client_id", !id);
  ("client_secret", !secret);
  ("username", !user);
  ("password", !pass)
]

let () = Nettls_gnutls.init()

let res = Nethttp_client.Convenience.http_post (site ^ token_url) params

let token =
  let json = Yojson.Basic.from_string res in
  let open Yojson.Basic.Util in
  json |> member "access_token" |> to_string

but I don’t know how to do the next call, where I need to set a header in the HTTP request with name “Authorization” and value “Bearer $token” (if I understand https://github.com/jakubroztocil/httpie#request-items correctly). Is there a way to do this with ocamlnet? Or do you have another library I could use for such simple GET and POST HTTP requests?

I usually use cohttp as client, the following should do more or less what you need:

open Lwt

let uri = Uri.of_string (site ^ token_url) in
let headers = Cohttp.Header.init_with "Bearer" token in
let body = params
  |> List.map (fun (k,v) -> Printf.sprintf {|"%s": "%s"|} k v)
  |> String.concat ", "
  |> Printf.sprintf "{%s}"
  |> Cohttp_lwt_body.of_string
in
Cohttp_lwt_unix.Client.post ~headers ~body uri >>= fun (resp, body) ->
...

Thank you very much for this suggestion. It evens has patch, which I need for my planned use. I’ll give it a try.

As a follow-up, I managed to make things work. The server I’m working with does not seem to accept json encoded data for post, so I had to do this:

open Lwt
open Cohttp
open Cohttp_lwt_unix

let id = ref ""
let secret = ref ""
let user = ref ""
let pass = ref ""

let spec = [
  ("-id", Arg.Set_string id, "the client_id");
  ("-secret", Arg.Set_string secret, "the client_secret");
  ("-user", Arg.Set_string user, "the username");
  ("-pass", Arg.Set_string pass, "the password")
]

let () = Arg.parse spec (fun _ -> ()) "wbml -id client_id -secret client_secret -user username -pass password"

let site = "https://app.wallabag.it/"
let token_url = "oauth/v2/token"

let keyvallist_to_form_urlencoded p =
  p |> List.map (fun (k,v) -> Printf.sprintf "%s=%s" k v)
  |> String.concat "&"
  |> Cohttp_lwt.Body.of_string

let params = [
  ("grant_type", "password");
  ("client_id", !id);
  ("client_secret", !secret);
  ("username", !user);
  ("password", !pass)
]

let get_token =
  let body = keyvallist_to_form_urlencoded params in
  let headers =
    Header.init ()
    |> fun h -> Header.add h "content-type" "application/x-www-form-urlencoded"
  in
  Client.post ~headers ~body (Uri.of_string (site ^ token_url)) >>= fun (resp, body) ->
  body |> Cohttp_lwt.Body.to_string

let token =
  let body = Lwt_main.run get_token in
  let open Yojson.Basic.Util in
  body |> Yojson.Basic.from_string |>
  member "access_token" |> to_string
1 Like