diff --git a/bin/main.ml b/bin/main.ml
index d5930e5..6fc65b5 100644
--- a/bin/main.ml
+++ b/bin/main.ml
@@ -1,43 +1,46 @@
open Waq
open Util [@@warning "-33"]
-let server () =
- let _host, port = Config.(listen_host (), listen_port ()) in
+let server env =
+ let host, port = Config.(listen_host (), listen_port ()) in
let error_handler ~req ~status ~headers ~body =
let open Controller.Helper in
- let default () = Httpq.Server.respond ~status ~headers body in
+ let default () = Yume.Server.respond ~status ~headers body in
let main () =
- Httpq.Server.respond ~status ~headers
- ("
" ^ Httpq.Status.to_string status ^ "
")
+ Yume.Server.respond ~status ~headers
+ ("
" ^ Yume.Status.to_string status ^ "
")
in
req |> render ~default [ (text_html, main) ]
in
- Eio_main.run @@ fun env ->
- Lwt_eio.with_event_loop ~clock:env#clock @@ fun _ ->
- Lwt_eio.run_lwt @@ fun () ->
- Httpq.Server.start_server ~port ~error_handler Router.handler @@ fun () ->
- Migration.verify_migration_status ();%lwt
- Logq.info (fun m -> m "Listening on 127.0.0.1:%d" port);
- Lwt.return_unit
-
-let db_migrate () =
- Eio_main.run @@ fun env ->
- Lwt_eio.with_event_loop ~clock:env#clock @@ fun _ ->
- Lwt_eio.run_lwt @@ fun () -> Migration.migrate ()
+ Eio.Switch.run @@ fun sw ->
+ Job.Runner.start_global_runner ~sw;
+ let listen =
+ match
+ Eio.Net.getaddrinfo_stream ~service:(string_of_int port)
+ (Eio.Stdenv.net env) host
+ with
+ | [] -> `Tcp (Eio.Net.Ipaddr.V4.loopback, 8080)
+ | x :: _ -> x
+ in
+ Yume.Server.start_server env ~sw ~listen ~error_handler Router.handler
+ @@ fun _ ->
+ Migration.verify_migration_status ();
+ (match listen with
+ | `Tcp (ipaddr, port) ->
+ Logq.info (fun m ->
+ m "Listening on %s:%d" (Fmt.str "%a" Eio.Net.Ipaddr.pp ipaddr) port)
+ | _ -> Logq.info (fun m -> m "Listening on "));
+ ()
-let db_rollback () =
- Eio_main.run @@ fun env ->
- Lwt_eio.with_event_loop ~clock:env#clock @@ fun _ ->
- Lwt_eio.run_lwt @@ fun () -> Migration.rollback ()
+let db_migrate () = Migration.migrate ()
+let db_rollback () = Migration.rollback ()
let db_reset () =
- Eio_main.run @@ fun env ->
- Lwt_eio.with_event_loop ~clock:env#clock @@ fun _ ->
Lwt_eio.run_lwt @@ fun () ->
Db.debug_drop_all_tables_in_db ();%lwt
- Migration.migrate ();%lwt
+ Migration.migrate ();
if Config.debug_generate_test_users () then
(* Generate some users for tests *)
@@ -101,19 +104,17 @@ add_column ~table_name:"preview_cards" ~name:"blurhash" ~spec:"TEXT"
()
let oauth_generate_access_token username =
- Eio_main.run @@ fun env ->
- Lwt_eio.with_event_loop ~clock:env#clock @@ fun _ ->
Lwt_eio.run_lwt @@ fun () ->
(* Generate a new OAuth application named "Web" *)
- let%lwt web =
+ let web =
Oauth_helper.generate_application ~name:"Web"
~redirect_uri:"urn:ietf:wg:oauth:2.0:oob" ~scopes:"read write follow push"
in
- let%lwt user =
- let%lwt a = Db.(e @@ Account.get_one ~username) in
+ let user =
+ let a = Db.(e @@ Account.get_one ~username) in
Db.(e @@ User.get_one ~account_id:a#id)
in
- let%lwt access_token =
+ let access_token =
Oauth_helper.generate_access_token ~scopes:"read write follow push"
~resource_owner_id:user#id ~app:web ()
in
@@ -131,8 +132,6 @@ let hidden_input f =
res
let user_register ?username ?display_name ?email ?password () =
- Eio_main.run @@ fun env ->
- Lwt_eio.with_event_loop ~clock:env#clock @@ fun _ ->
Lwt_eio.run_lwt @@ fun () ->
let username =
match username with
@@ -174,27 +173,22 @@ let user_register ?username ?display_name ?email ?password () =
(u#id |> Model.User.ID.to_int);
Lwt.return_unit
-let account_fetch () =
- Eio_main.run @@ fun env ->
- Lwt_eio.with_event_loop ~clock:env#clock @@ fun _ ->
- Lwt_eio.run_lwt @@ fun () ->
- let open Lwt.Infix in
- let%lwt accts = Db.(e Account.all) in
+let account_fetch env =
+ let accts = Db.(e Account.all) in
accts
- |> Lwt_list.iteri_s @@ fun i acct ->
+ |> List.iteri @@ fun i acct ->
Logq.info (fun m -> m "[%d/%d] %s" (i + 1) (List.length accts) acct#uri);
match acct#domain with
- | None -> Lwt.return_unit
+ | None -> ()
| Some domain -> (
- try%lwt
- Activity.fetch_person (`DomainUser (domain, acct#username))
- >|= Activity.model_account_of_person ~original:acct
- >>= Db.(e *< Account.save_one)
- |> ignore_lwt
+ try
+ Activity.fetch_person env (`DomainUser (domain, acct#username))
+ |> Activity.model_account_of_person ~original:acct
+ |> (fun a -> Db.(e (Account.save_one a)))
+ |> ignore
with e ->
Logq.err (fun m ->
- m "Couldn't fetch person: %s" (Printexc.to_string e));
- Lwt.return_unit)
+ m "Couldn't fetch person: %s" (Printexc.to_string e)))
let webpush_generate_vapid_key () =
let priv_key, pub_key = Webpush.Vapid.generate_keys () in
@@ -202,22 +196,19 @@ let webpush_generate_vapid_key () =
Printf.printf "vapid_public_key: \"%s\"\n" pub_key;
()
-let webpush_deliver username message =
- Eio_main.run @@ fun env ->
- Lwt_eio.with_event_loop ~clock:env#clock @@ fun _ ->
- Lwt_eio.run_lwt @@ fun () ->
- match%lwt
- Db.(
- (e @@ Account.(get_one ~preload:[ `user [] ] ~username ~domain:None))
- |> maybe_no_row)
+let webpush_deliver env username message =
+ match
+ Db.(e @@ Account.(get_one ~preload:[ `user [] ] ~username ~domain:None))
with
- | None -> failwith "username is not found"
- | Some a -> (
+ | exception Sqlx.Error.NoRowFound -> failwith "username is not found"
+ | a -> (
match a#user with
| None -> failwith "username is not local"
- | Some u -> Webpush_helper.deliver ~user_id:u#id message)
+ | Some u -> Webpush_helper.deliver env ~user_id:u#id message)
let () =
+ Eio_main.run @@ fun env ->
+ Lwt_eio.with_event_loop ~clock:env#clock @@ fun _ ->
(let file_name = Config.log_file_path () in
if file_name = "" then Logq.(add_reporter (make_stderr_reporter ~l:Debug))
else Logq.(add_reporter (make_file_reporter ~l:Debug ~file_name ())));
@@ -226,14 +217,14 @@ let () =
Config.to_list ()
|> List.iter (fun (k, v) -> Logq.debug (fun m -> m "Config %s = %s" k v));
- Crypto.initialize ();
+ Crypto.initialize env @@ fun () ->
Db.initialize ();
(* Parse command-line arguments and call a proper handler *)
let open Cmdliner in
Cmd.(
group
- ~default:Term.(const server $ const ())
+ ~default:Term.(const server $ const env)
(info "waq")
[
v (info "db:reset") Term.(const db_reset $ const ());
@@ -268,17 +259,17 @@ let () =
value
& opt (some string) None
& info ~docv:"PASSWORD" [ "password" ]));
- v (info "account:fetch") Term.(const account_fetch $ const ());
+ v (info "account:fetch") Term.(const account_fetch $ const env);
v
(info "webpush:generate_vapid_key")
Term.(const webpush_generate_vapid_key $ const ());
v (info "webpush:deliver")
Term.(
- const webpush_deliver
+ const webpush_deliver $ const env
$ Arg.(
required & pos 0 (some string) None & info ~docv:"USERNAME" [])
$ Arg.(
required & pos 1 (some string) None & info ~docv:"MESSAGE" []));
- v (info "server") Term.(const server $ const ());
+ v (info "server") Term.(const server $ const env);
])
|> Cmd.eval |> exit
diff --git a/e2e/src/common.ml b/e2e/src/common.ml
index 363e9f3..002c8aa 100644
--- a/e2e/src/common.ml
+++ b/e2e/src/common.ml
@@ -1,6 +1,69 @@
-include Lwt.Infix
-module Uri = Httpq.Uri
-module Ptime = Waq.Util.Ptime
+module Ptime = struct
+ include Ptime
+
+ let now () = Unix.gettimeofday () |> of_float_s |> Option.get
+ let to_int x = x |> to_span |> Span.to_int_s |> Option.get
+ let to_rfc3339 = to_rfc3339 ~tz_offset_s:0
+
+ let to_http_date (v : t) : string =
+ let string_of_week = function
+ | `Sun -> "Sun"
+ | `Mon -> "Mon"
+ | `Tue -> "Tue"
+ | `Wed -> "Wed"
+ | `Thu -> "Thu"
+ | `Fri -> "Fri"
+ | `Sat -> "Sat"
+ in
+ let string_of_month = function
+ | 1 -> "Jan"
+ | 2 -> "Feb"
+ | 3 -> "Mar"
+ | 4 -> "Apr"
+ | 5 -> "May"
+ | 6 -> "Jun"
+ | 7 -> "Jul"
+ | 8 -> "Aug"
+ | 9 -> "Sep"
+ | 10 -> "Oct"
+ | 11 -> "Nov"
+ | 12 -> "Dec"
+ | _ -> assert false
+ in
+ let (year, month, day_of_month), ((hour, minute, second), _) =
+ to_date_time v
+ in
+ let month = string_of_month month in
+ let day_name = weekday v |> string_of_week in
+ Printf.sprintf "%s, %02d %s %d %02d:%02d:%02d GMT" day_name day_of_month
+ month year hour minute second
+end
+
+module Uri = struct
+ include Uri
+
+ let getaddrinfo_port (u : t) =
+ let scheme = Uri.scheme u |> Option.get in
+ u |> Uri.port |> Option.fold ~none:scheme ~some:string_of_int
+
+ let http_host (u : t) =
+ let host = Uri.host u |> Option.get in
+ match Uri.port u with
+ | None -> host
+ | Some port -> host ^ ":" ^ string_of_int port
+
+ let path_query_fragment (u : t) =
+ let res = Uri.path u in
+ let res =
+ match Uri.verbatim_query u with None -> res | Some q -> res ^ "?" ^ q
+ in
+ let res =
+ match Uri.fragment u with None -> res | Some f -> res ^ "#" ^ f
+ in
+ res
+
+ let domain (u : t) = http_host u
+end
module Internal = struct
let kubectl_path = Sys.getenv "KUBECTL"
@@ -196,12 +259,77 @@ module Internal = struct
()
end
-let ( |.> ) f g a = a |> f |> g
-let ignore_lwt = Waq.Util.ignore_lwt
-let fetch = Httpq.Client.fetch
-let fetch_exn = Httpq.Client.fetch_exn
let ( ^/ ) a b = a ^ "/" ^ b
+let fetch env ?(headers = []) ?(meth = `GET) ?(body = "") ?(sign = None) url =
+ let open Yume in
+ let uri = Uri.of_string url in
+
+ (* NOTE: Ad-hoc scheme rewriting (https -> http) for localhost
+ for better dev experience *)
+ let uri =
+ match Uri.scheme uri with
+ | Some "https"
+ when [ Some "localhost"; Some "127.0.0.1" ] |> List.mem (Uri.host uri) ->
+ Uri.with_scheme uri (Some "http")
+ | _ -> uri
+ in
+
+ let meth_s = Method.to_string meth in
+ let headers =
+ let headers =
+ let add (k, v) headers =
+ if List.mem_assoc k headers then headers else (k, v) :: headers
+ in
+ headers
+ |> add (`Content_length, body |> String.length |> string_of_int)
+ |> add (`Connection, "close")
+ |> add (`Host, Uri.http_host uri)
+ |> add (`Date, Ptime.(now () |> to_http_date))
+ in
+ let headers =
+ match sign with
+ | None -> headers
+ | Some (priv_key, key_id, signed_headers) ->
+ Signature.sign ~priv_key ~key_id ~signed_headers ~headers ~meth
+ ~path:(Uri.path_query_fragment uri)
+ ~body:(Some body)
+ in
+ Headers.to_list headers
+ in
+ try
+ Eio.Switch.run @@ fun sw ->
+ let resp =
+ match meth with
+ | `GET | `DELETE ->
+ Client.request env ~sw ~headers ~meth (Uri.to_string uri)
+ | `POST | `PATCH ->
+ Client.request env ~sw ~headers ~body:(`Fixed body) ~meth
+ (Uri.to_string uri)
+ | _ -> failwith "Not implemented method"
+ in
+ let status = Client.Response.status resp in
+ Logq.debug (fun m ->
+ m "[fetch] %s %s --> %s" meth_s url
+ (Cohttp.Code.string_of_status status));
+ let headers = Client.Response.headers resp in
+ let body = Client.Response.drain resp in
+ Ok (status, headers, body)
+ with e ->
+ let backtrace = Printexc.get_backtrace () in
+ Logq.err (fun m ->
+ m "[fetch] %s %s: %s\n%s" meth_s url (Printexc.to_string e) backtrace);
+ Error ()
+
+exception FetchFailure of (Yume.Status.t * Yume.Headers.t * string) option
+
+let fetch_exn ?(headers = []) ?(meth = `GET) ?(body = "") ?(sign = None) env
+ (url : string) : string =
+ match fetch env ~headers ~meth ~body ~sign url with
+ | Ok (`OK, _, body) -> body
+ | Ok r -> raise (FetchFailure (Some r))
+ | _ -> raise (FetchFailure None)
+
let expect_string = function
| `String s -> s
| _ -> failwith "Expected string, got something different"
@@ -223,7 +351,9 @@ let expect_int = function
| _ -> failwith "Expected int, got something different"
let with_lock mtx f =
- match mtx with None -> f () | Some mtx -> Lwt_mutex.with_lock mtx f
+ match mtx with
+ | None -> f ()
+ | Some mtx -> Eio.Mutex.use_rw ~protect:true mtx f
let new_session f =
Internal.launch_waq @@ fun tokens ->
@@ -241,23 +371,18 @@ let make_waq_and_mstdn_scenario ?(timeout = 30.0) handler () : unit =
new_mastodon_session @@ fun mstdn_token ->
Logq.debug (fun m -> m "Access token for Mastodon: %s" mstdn_token);
Unix.sleep 10;
- Lwt_main.run
- @@ Lwt.pick
- [
- handler waq_token mstdn_token;
- (Lwt_unix.sleep timeout >>= fun () -> failwith "Timeout");
- ]
+ Eio_main.run @@ fun env ->
+ Mirage_crypto_rng_eio.run (module Mirage_crypto_rng.Fortuna) env @@ fun () ->
+ Eio.Time.with_timeout_exn env#clock timeout (fun () ->
+ handler env waq_token mstdn_token)
let make_waq_scenario ?(timeout = 30.0) handler () : unit =
new_session @@ fun waq_token ->
Logq.debug (fun m -> m "Access token for Waq: %s" waq_token);
Unix.sleep 1;
- Lwt_main.run
- @@ Lwt.pick
- [
- handler waq_token;
- (Lwt_unix.sleep timeout >>= fun () -> failwith "Timeout");
- ]
+ Eio_main.run @@ fun env ->
+ Mirage_crypto_rng_eio.run (module Mirage_crypto_rng.Fortuna) env @@ fun () ->
+ Eio.Time.with_timeout_exn env#clock timeout (fun () -> handler env waq_token)
let waq_server_name = Sys.getenv "E2E_TEST_WAQ_SERVER_NAME"
let waq_server_domain = Uri.(of_string waq_server_name |> domain)
@@ -271,7 +396,7 @@ let pp_json (s : string) =
Logq.debug (fun m -> m "%s" Yojson.Safe.(from_string s |> pretty_to_string))
[@@warning "-32"]
-let do_fetch ?token ?(meth = `GET) ?(body = "") kind target =
+let do_fetch env ?token ?(meth = `GET) ?(body = "") kind target =
let headers = [ (`Accept, "application/json") ] in
let headers =
match meth with
@@ -283,7 +408,7 @@ let do_fetch ?token ?(meth = `GET) ?(body = "") kind target =
| Some token -> (`Authorization, "Bearer " ^ token) :: headers
| None -> headers
in
- fetch_exn ~headers ~meth ~body (url kind target)
+ fetch_exn env ~headers ~meth ~body (url kind target)
type account = {
id : string;
@@ -351,7 +476,8 @@ type notification = {
type marker = { last_read_id : string; version : int; updated_at : string }
[@@deriving make, yojson]
-let update_credentials ~token kind ?display_name ?note ?avatar ?header ?bot () =
+let update_credentials env ~token kind ?display_name ?note ?avatar ?header ?bot
+ () =
let target = "/api/v1/accounts/update_credentials" in
let headers =
[
@@ -429,55 +555,54 @@ let update_credentials ~token kind ?display_name ?note ?avatar ?header ?bot () =
in
assert (List.length body <> 2);
let body = String.concat "\r\n" body in
- fetch_exn ~headers ~meth:`PATCH ~body (url kind target)
- >|= Yojson.Safe.from_string >|= account_of_yojson
+ fetch_exn env ~headers ~meth:`PATCH ~body (url kind target)
+ |> Yojson.Safe.from_string |> account_of_yojson
-let lookup_via_v1_accounts_lookup ~token kind ?domain ~username () =
+let lookup_via_v1_accounts_lookup env ~token kind ?domain ~username () =
let target =
let src = "/api/v1/accounts/lookup?acct=" in
match domain with
| None -> src ^ username
| Some domain -> src ^ username ^ "@" ^ domain
in
- let%lwt r = do_fetch ~token kind target in
+ let r = do_fetch env ~token kind target in
let a = r |> Yojson.Safe.from_string |> account_of_yojson in
- Lwt.return (a.id, a.username, a.acct)
+ (a.id, a.username, a.acct)
-let lookup_via_v1_accounts_search ?token kind ?domain ~username () =
+let lookup_via_v1_accounts_search env ?token kind ?domain ~username () =
let target =
let src = "/api/v1/accounts/search?resolve=true&q=@" in
match domain with
| None -> src ^ username
| Some domain -> src ^ username ^ "@" ^ domain
in
- let%lwt r = do_fetch ?token kind target in
+ let r = do_fetch env ?token kind target in
let l =
match Yojson.Safe.from_string r with
| `List [ `Assoc l ] -> l
| _ -> assert false
in
- Lwt.return
- ( l |> List.assoc "id" |> expect_string,
- l |> List.assoc "username" |> expect_string,
- l |> List.assoc "acct" |> expect_string )
+ ( l |> List.assoc "id" |> expect_string,
+ l |> List.assoc "username" |> expect_string,
+ l |> List.assoc "acct" |> expect_string )
-let search ?token kind q =
+let search env ?token kind q =
let queries = [ ("resolve", [ "true" ]); ("q", [ q ]) ] in
let u = Uri.of_string "/api/v2/search" in
let u = Uri.add_query_params u queries in
- let%lwt r = do_fetch ?token kind (Uri.to_string u) in
+ let r = do_fetch env ?token kind (Uri.to_string u) in
let l = Yojson.Safe.from_string r |> expect_assoc in
- Lwt.return
- ( List.assoc "accounts" l |> expect_list |> List.map account_of_yojson,
- List.assoc "statuses" l |> expect_list |> List.map status_of_yojson,
- List.assoc "hashtags" l |> expect_list )
-
-let lookup ~token kind ?domain ~username () =
- search ~token kind
- (domain
- |> Option.fold ~none:("@" ^ username) ~some:(fun domain ->
- "@" ^ username ^ "@" ^ domain))
- >|= function
+ ( List.assoc "accounts" l |> expect_list |> List.map account_of_yojson,
+ List.assoc "statuses" l |> expect_list |> List.map status_of_yojson,
+ List.assoc "hashtags" l |> expect_list )
+
+let lookup env ~token kind ?domain ~username () =
+ match
+ search env ~token kind
+ (domain
+ |> Option.fold ~none:("@" ^ username) ~some:(fun domain ->
+ "@" ^ username ^ "@" ^ domain))
+ with
| [ acct ], _, _ -> (acct.id, acct.username, acct.acct)
| accts, _, _ ->
Logq.err (fun m ->
@@ -487,92 +612,92 @@ let lookup ~token kind ?domain ~username () =
|> String.concat ", "));
assert false
-let get_account kind id =
- do_fetch kind ("/api/v1/accounts/" ^ id)
- >|= Yojson.Safe.from_string >|= account_of_yojson
+let get_account env kind id =
+ do_fetch env kind ("/api/v1/accounts/" ^ id)
+ |> Yojson.Safe.from_string |> account_of_yojson
-let get_relationships ~token kind account_ids =
+let get_relationships env ~token kind account_ids =
let target =
"/api/v1/accounts/relationships?"
^ (account_ids |> List.map (fun id -> "id[]=" ^ id) |> String.concat "&")
in
- do_fetch ~token kind target
- >|= Yojson.Safe.from_string >|= expect_list
- >|= List.map relationship_of_yojson
+ do_fetch env ~token kind target
+ |> Yojson.Safe.from_string |> expect_list
+ |> List.map relationship_of_yojson
-let get_followers ?token kind account_id =
- do_fetch ?token kind ("/api/v1/accounts/" ^ account_id ^ "/followers")
- >|= Yojson.Safe.from_string >|= expect_list >|= List.map account_of_yojson
+let get_followers env ?token kind account_id =
+ do_fetch env ?token kind ("/api/v1/accounts/" ^ account_id ^ "/followers")
+ |> Yojson.Safe.from_string |> expect_list |> List.map account_of_yojson
-let get_following ?token kind account_id =
- do_fetch ?token kind ("/api/v1/accounts/" ^ account_id ^ "/following")
- >|= Yojson.Safe.from_string >|= expect_list >|= List.map account_of_yojson
+let get_following env ?token kind account_id =
+ do_fetch env ?token kind ("/api/v1/accounts/" ^ account_id ^ "/following")
+ |> Yojson.Safe.from_string |> expect_list |> List.map account_of_yojson
-let get_notifications ?token kind =
- do_fetch ?token kind "/api/v1/notifications"
- >|= Yojson.Safe.from_string >|= expect_list
- >|= List.map notification_of_yojson
+let get_notifications env ?token kind =
+ do_fetch env ?token kind "/api/v1/notifications"
+ |> Yojson.Safe.from_string |> expect_list
+ |> List.map notification_of_yojson
let markers_of_yojson j =
let l = expect_assoc j in
( List.assoc_opt "home" l |> Option.map marker_of_yojson,
List.assoc_opt "notifications" l |> Option.map marker_of_yojson )
-let get_markers ?token kind timelines =
- do_fetch ?token kind
+let get_markers env ?token kind timelines =
+ do_fetch env ?token kind
("/api/v1/markers?"
^ (timelines |> List.map (fun s -> "timeline[]=" ^ s) |> String.concat "&")
)
- >|= Yojson.Safe.from_string >|= markers_of_yojson
+ |> Yojson.Safe.from_string |> markers_of_yojson
-let post_markers ?token kind values =
+let post_markers env ?token kind values =
let body =
values
|> List.map (fun (timeline, last_read_id) ->
(timeline, `Assoc [ ("last_read_id", `String last_read_id) ]))
|> fun l -> `Assoc l |> Yojson.Safe.to_string
in
- do_fetch ~meth:`POST ?token kind ~body "/api/v1/markers"
- >|= Yojson.Safe.from_string >|= markers_of_yojson
+ do_fetch env ~meth:`POST ?token kind ~body "/api/v1/markers"
+ |> Yojson.Safe.from_string |> markers_of_yojson
-let follow ~token kind account_id =
- let%lwt r =
- do_fetch ~meth:`POST ~token kind
+let follow env ~token kind account_id =
+ let r =
+ do_fetch env ~meth:`POST ~token kind
("/api/v1/accounts/" ^ account_id ^ "/follow")
in
assert (
Yojson.Safe.from_string r |> expect_assoc |> List.assoc "following"
|> expect_bool);
- Lwt.return_unit
+ ()
-let unfollow ~token kind account_id =
- do_fetch ~meth:`POST ~token kind
+let unfollow env ~token kind account_id =
+ do_fetch env ~meth:`POST ~token kind
("/api/v1/accounts/" ^ account_id ^ "/unfollow")
- |> ignore_lwt
+ |> ignore
-let get_status kind ?token status_id =
- do_fetch ?token kind ("/api/v1/statuses/" ^ status_id)
- >|= Yojson.Safe.from_string >|= status_of_yojson
+let get_status env kind ?token status_id =
+ do_fetch env ?token kind ("/api/v1/statuses/" ^ status_id)
+ |> Yojson.Safe.from_string |> status_of_yojson
-let get_account_statuses kind ?token ?(exclude_replies = false) account_id =
- do_fetch ?token kind
+let get_account_statuses env kind ?token ?(exclude_replies = false) account_id =
+ do_fetch env ?token kind
("/api/v1/accounts/" ^ account_id ^ "/statuses?exclude_replies="
^ string_of_bool exclude_replies)
- >|= Yojson.Safe.from_string >|= expect_list >|= List.map status_of_yojson
+ |> Yojson.Safe.from_string |> expect_list |> List.map status_of_yojson
-let get_status_context kind status_id =
- let%lwt r = do_fetch kind ("/api/v1/statuses/" ^ status_id ^ "/context") in
+let get_status_context env kind status_id =
+ let r = do_fetch env kind ("/api/v1/statuses/" ^ status_id ^ "/context") in
let l = Yojson.Safe.from_string r |> expect_assoc in
match l with
| [ ("ancestors", `List ancestors); ("descendants", `List descendants) ]
| [ ("descendants", `List descendants); ("ancestors", `List ancestors) ] ->
let ancestors = ancestors |> List.map status_of_yojson in
let descendants = descendants |> List.map status_of_yojson in
- Lwt.return (ancestors, descendants)
+ (ancestors, descendants)
| _ -> assert false
-let post ~token kind ?spoiler_text ?content ?in_reply_to_id ?(media_ids = []) ()
- =
+let post env ~token kind ?spoiler_text ?content ?in_reply_to_id
+ ?(media_ids = []) () =
let content = content |> Option.value ~default:"こんにちは、世界!" in
let body =
let l =
@@ -592,40 +717,42 @@ let post ~token kind ?spoiler_text ?content ?in_reply_to_id ?(media_ids = []) ()
in
`Assoc l |> Yojson.Safe.to_string
in
- do_fetch ~token ~meth:`POST ~body kind "/api/v1/statuses"
- >|= Yojson.Safe.from_string >|= status_of_yojson
+ do_fetch env ~token ~meth:`POST ~body kind "/api/v1/statuses"
+ |> Yojson.Safe.from_string |> status_of_yojson
-let delete_status ~token kind status_id =
- do_fetch ~token ~meth:`DELETE kind ("/api/v1/statuses/" ^ status_id)
- >|= Yojson.Safe.from_string >|= status_of_yojson
+let delete_status env ~token kind status_id =
+ do_fetch env ~token ~meth:`DELETE kind ("/api/v1/statuses/" ^ status_id)
+ |> Yojson.Safe.from_string |> status_of_yojson
-let reblog ~token kind ~id =
- do_fetch ~token ~meth:`POST kind ("/api/v1/statuses/" ^ id ^ "/reblog")
- >|= Yojson.Safe.from_string >|= status_of_yojson
+let reblog env ~token kind ~id =
+ do_fetch env ~token ~meth:`POST kind ("/api/v1/statuses/" ^ id ^ "/reblog")
+ |> Yojson.Safe.from_string |> status_of_yojson
-let unreblog ~token kind ~id =
- do_fetch ~token ~meth:`POST kind ("/api/v1/statuses/" ^ id ^ "/unreblog")
- >|= Yojson.Safe.from_string >|= status_of_yojson
+let unreblog env ~token kind ~id =
+ do_fetch env ~token ~meth:`POST kind ("/api/v1/statuses/" ^ id ^ "/unreblog")
+ |> Yojson.Safe.from_string |> status_of_yojson
-let fav ~token kind ~id =
- do_fetch ~token ~meth:`POST kind ("/api/v1/statuses/" ^ id ^ "/favourite")
- >|= Yojson.Safe.from_string >|= status_of_yojson
+let fav env ~token kind ~id =
+ do_fetch env ~token ~meth:`POST kind ("/api/v1/statuses/" ^ id ^ "/favourite")
+ |> Yojson.Safe.from_string |> status_of_yojson
-let unfav ~token kind ~id =
- do_fetch ~token ~meth:`POST kind ("/api/v1/statuses/" ^ id ^ "/unfavourite")
- >|= Yojson.Safe.from_string >|= status_of_yojson
+let unfav env ~token kind ~id =
+ do_fetch env ~token ~meth:`POST kind
+ ("/api/v1/statuses/" ^ id ^ "/unfavourite")
+ |> Yojson.Safe.from_string |> status_of_yojson
-let get_favourited_by ~token kind ~id =
- do_fetch ~token ~meth:`GET kind ("/api/v1/statuses/" ^ id ^ "/favourited_by")
- >|= Yojson.Safe.from_string >|= expect_list >|= List.map account_of_yojson
+let get_favourited_by env ~token kind ~id =
+ do_fetch env ~token ~meth:`GET kind
+ ("/api/v1/statuses/" ^ id ^ "/favourited_by")
+ |> Yojson.Safe.from_string |> expect_list |> List.map account_of_yojson
-let home_timeline ~token kind =
- do_fetch ~token kind "/api/v1/timelines/home" >|= fun r ->
+let home_timeline env ~token kind =
+ do_fetch env ~token kind "/api/v1/timelines/home" |> fun r ->
match Yojson.Safe.from_string r with `List l -> l | _ -> assert false
-let fetch_access_token ~username =
- let%lwt r =
- fetch_exn ~meth:`POST
+let fetch_access_token env ~username =
+ let r =
+ fetch_exn env ~meth:`POST
~headers:[ (`Content_type, "application/json") ]
~body:
{|{"client_name":"foo","redirect_uris":"http://example.com?origin=http://example.com"}|}
@@ -639,7 +766,7 @@ let fetch_access_token ~username =
| _ -> assert false
in
- let%lwt r =
+ let r =
let body =
Uri.encoded_of_query
[
@@ -653,7 +780,7 @@ let fetch_access_token ~username =
(* NOTE: The header "content-type: application/x-www-form-urlencoded"
should be specified explicitly like the following to send the POST
request body correctly via Tunnelmole. *)
- fetch
+ fetch env
~headers:[ (`Content_type, "application/x-www-form-urlencoded") ]
~meth:`POST ~body (waq "/oauth/authorize")
in
@@ -662,13 +789,13 @@ let fetch_access_token ~username =
| Ok (`Found, headers, _body) ->
(* 0123456789012345678901234
http://example.com?code=... *)
- headers |> List.assoc "location" |> Uri.of_string |> Uri.query
+ headers |> List.assoc `Location |> Uri.of_string |> Uri.query
|> List.assoc "code" |> List.hd
| _ -> assert false
in
- let%lwt r =
- fetch_exn ~meth:`POST
+ let r =
+ fetch_exn env ~meth:`POST
~headers:[ (`Content_type, "application/json") ]
~body:
(`Assoc
@@ -684,87 +811,84 @@ let fetch_access_token ~username =
(waq "/oauth/token")
in
match Yojson.Safe.from_string r with
- | `Assoc l -> l |> List.assoc "access_token" |> expect_string |> Lwt.return
+ | `Assoc l -> l |> List.assoc "access_token" |> expect_string
| _ -> assert false
-let websocket ?mtx ~token kind ?target handler f =
- let open Websocket_lwt_unix in
+let websocket env ?mtx ~token kind ?target handler f =
let target =
match target with
| Some target -> target
| None -> "/api/v1/streaming?stream=user"
in
- let uri = Uri.of_string (url kind target) in
- let%lwt endp = Resolver_lwt.resolve_uri ~uri Resolver_lwt_unix.system in
- let ctx = Lazy.force Conduit_lwt_unix.default_ctx in
- let%lwt client = Conduit_lwt_unix.endp_to_client ~ctx endp in
-
let extra_headers =
Cohttp.Header.of_list [ ("Sec-WebSocket-Protocol", token) ]
in
- let%lwt conn = connect ~extra_headers ~ctx client uri in
+ Eio.Switch.run @@ fun sw ->
+ let conn = Yume.Ws.Client.connect env ~sw ~extra_headers (url kind target) in
let close_sent = ref false in
let pushf msg =
match msg with
- | Some content -> write conn (Websocket.Frame.create ~content ())
- | None when !close_sent -> Lwt.return_unit
+ | Some content ->
+ Yume.Ws.Client.write conn (Websocket.Frame.create ~content ())
+ | None when !close_sent -> ()
| None ->
- write conn (Websocket.Frame.create ~opcode:Close ());%lwt
- Lwt.return (close_sent := true)
+ Yume.Ws.Client.write conn (Websocket.Frame.create ~opcode:Close ());
+ close_sent := true
in
let rec react () =
- match%lwt read conn with
+ match Yume.Ws.Client.read conn with
| { Websocket.Frame.opcode = Ping; _ } ->
- write conn (Websocket.Frame.create ~opcode:Pong ());%lwt
+ Yume.Ws.Client.write conn (Websocket.Frame.create ~opcode:Pong ());
react ()
| { opcode = Pong; _ } -> react ()
| { opcode = Text; content; _ } | { opcode = Binary; content; _ } ->
- with_lock mtx (fun () -> handler content pushf);%lwt
+ with_lock mtx (fun () -> handler content pushf);
react ()
| { opcode = Close; content; _ } ->
- if !close_sent then Lwt.return_unit
+ if !close_sent then ()
else if String.length content >= 2 then
- write conn
+ Yume.Ws.Client.write conn
(Websocket.Frame.create ~opcode:Close
~content:(String.sub content 0 2) ())
- else write conn (Websocket.Frame.close 1000);%lwt
- close_transport conn
- | _ -> close_transport conn
+ else Yume.Ws.Client.write conn (Websocket.Frame.close 1000);
+ Yume.Ws.Client.close_transport conn
+ | _ -> Yume.Ws.Client.close_transport conn
in
- Lwt.join [ with_lock mtx (fun () -> f pushf); react () ]
+ Eio.Fiber.both
+ (fun () -> react ())
+ (fun () -> with_lock mtx (fun () -> f pushf))
let websocket_handler_state_machine ~states ~init () =
let current = ref init in
let set_current v = current := v in
let handler content pushf =
let real_handler = states |> List.assoc !current in
- let%lwt next_state =
+ let next_state =
real_handler (content |> Yojson.Safe.from_string |> expect_assoc) pushf
in
set_current next_state;
- Lwt.return_unit
+ ()
in
(set_current, handler)
-let websocket_stack kind ~token ?num_msgs f =
+let websocket_stack env kind ~token ?num_msgs f =
let recv_msgs = ref [] in
let handler content pushf =
recv_msgs := content :: !recv_msgs;
match num_msgs with
| Some num_msgs when List.length !recv_msgs = num_msgs -> pushf None
- | _ -> Lwt.return_unit
+ | _ -> ()
in
- websocket kind ~token handler (fun pushf ->
- f pushf;%lwt
- match num_msgs with None -> pushf None | Some _ -> Lwt.return_unit)
- >|= fun () -> !recv_msgs
-
-let expect_exc_lwt f =
- (try%lwt
- let%lwt _ = f () in
- Lwt.return_false
- with _ -> Lwt.return_true)
- >|= fun b -> assert b
+ websocket env kind ~token handler (fun pushf ->
+ f pushf;
+ match num_msgs with None -> pushf None | Some _ -> ());
+ !recv_msgs
+
+let expect_exc f =
+ try
+ let _ = f () in
+ assert false
+ with _ -> ()
let test_image =
{|
diff --git a/e2e/src/common2.ml b/e2e/src/common2.ml
index 363c50c..84a4c58 100644
--- a/e2e/src/common2.ml
+++ b/e2e/src/common2.ml
@@ -11,25 +11,27 @@ type agent = {
let acct_of_agent ~(from : agent) (a : agent) =
if a.domain = from.domain then a.username else a.username ^ "@" ^ a.domain
-let lookup src = lookup src.kind ~token:src.token
-let follow src = follow src.kind ~token:src.token
-let post src = post src.kind ~token:src.token
-let search src = search src.kind ~token:src.token
-let reblog src = reblog src.kind ~token:src.token
-let unreblog src = unreblog src.kind ~token:src.token
+let lookup env src = lookup env src.kind ~token:src.token
+let follow env src = follow env src.kind ~token:src.token
+let post env src = post env src.kind ~token:src.token
+let search env src = search env src.kind ~token:src.token
+let reblog env src = reblog env src.kind ~token:src.token
+let unreblog env src = unreblog env src.kind ~token:src.token
-let home_timeline src =
- home_timeline src.kind ~token:src.token >|= List.map status_of_yojson
+let home_timeline env src =
+ home_timeline env src.kind ~token:src.token |> List.map status_of_yojson
-let delete_status src = delete_status src.kind ~token:src.token
-let get_status src = get_status src.kind ~token:src.token
-let get_notifications src = get_notifications src.kind ~token:src.token
-let update_credentials src = update_credentials src.kind ~token:src.token
+let delete_status env src = delete_status env src.kind ~token:src.token
+let get_status env src = get_status env src.kind ~token:src.token
+let get_notifications env src = get_notifications env src.kind ~token:src.token
-let websocket src ?target handler f =
- websocket src.kind ~token:src.token ?target handler f
+let update_credentials env src =
+ update_credentials env src.kind ~token:src.token
-let upload_media src ~filename ~data ~content_type =
+let websocket env src ?target handler f =
+ websocket env src.kind ~token:src.token ?target handler f
+
+let upload_media env src ~filename ~data ~content_type =
let target = "/api/v2/media" in
let headers =
[
@@ -56,22 +58,22 @@ let upload_media src ~filename ~data ~content_type =
in
assert (List.length body <> 2);
let body = String.concat "\r\n" body in
- fetch_exn ~headers ~meth:`POST ~body (url src.kind target)
- >|= Yojson.Safe.from_string >|= media_attachment_of_yojson
+ fetch_exn env ~headers ~meth:`POST ~body (url src.kind target)
+ |> Yojson.Safe.from_string |> media_attachment_of_yojson
-let lookup_agent src dst =
+let lookup_agent env src dst =
let domain = if src.domain = dst.domain then None else Some dst.domain in
- lookup src ~username:dst.username ?domain ()
+ lookup env src ~username:dst.username ?domain ()
-let follow_agent src dst =
- let%lwt id, _, _ = lookup_agent src dst in
- follow src id
+let follow_agent env src dst =
+ let id, _, _ = lookup_agent env src dst in
+ follow env src id
-let expect_no_status src id =
- try%lwt
- get_status src id |> ignore_lwt;%lwt
+let expect_no_status env src id =
+ try
+ get_status env src id |> ignore;
assert false
- with Httpq.Client.FetchFailure (Some (`Not_found, _, _)) -> Lwt.return_unit
+ with FetchFailure (Some (`Not_found, _, _)) -> ()
type runtime_context = {
waq_tokens : string array;
@@ -95,7 +97,8 @@ let generate_mstdn_agent ctxt =
let username = "mstdn" ^ string_of_int (i + 1) in
make_agent ~kind:`Mstdn ~token ~username ~domain:mstdn_server_domain
-let launch_waq ?(timeout = 30.0) (f : runtime_context -> unit Lwt.t) : unit =
+let launch_waq ?(timeout = 30.0)
+ (f : Eio_unix.Stdenv.base -> runtime_context -> unit) : unit =
Internal.launch_waq @@ fun waq_tokens ->
Logq.debug (fun m ->
m "Access token for Waq: [%s]"
@@ -105,11 +108,12 @@ let launch_waq ?(timeout = 30.0) (f : runtime_context -> unit Lwt.t) : unit =
~mstdn_num_used_tokens:0
in
Unix.sleep 10;
- Lwt.pick [ f ctxt; (Lwt_unix.sleep timeout >>= fun () -> failwith "Timeout") ]
- |> Lwt_main.run
+ Eio_main.run @@ fun env ->
+ Mirage_crypto_rng_eio.run (module Mirage_crypto_rng.Fortuna) env @@ fun () ->
+ Eio.Time.with_timeout_exn env#clock timeout (fun () -> f env ctxt)
-let launch_waq_and_mstdn ?(timeout = 30.0) (f : runtime_context -> unit Lwt.t) :
- unit =
+let launch_waq_and_mstdn ?(timeout = 30.0)
+ (f : Eio_unix.Stdenv.base -> runtime_context -> unit) : unit =
Internal.launch_waq @@ fun waq_tokens ->
Logq.debug (fun m ->
m "Access token for Waq: [%s]"
@@ -123,5 +127,6 @@ let launch_waq_and_mstdn ?(timeout = 30.0) (f : runtime_context -> unit Lwt.t) :
~mstdn_num_used_tokens:0
in
Unix.sleep 10;
- Lwt.pick [ f ctxt; (Lwt_unix.sleep timeout >>= fun () -> failwith "Timeout") ]
- |> Lwt_main.run
+ Eio_main.run @@ fun env ->
+ Mirage_crypto_rng_eio.run (module Mirage_crypto_rng.Fortuna) env @@ fun () ->
+ Eio.Time.with_timeout_exn env#clock timeout (fun () -> f env ctxt)
diff --git a/e2e/src/dune b/e2e/src/dune
index 613a94b..3bea1d8 100644
--- a/e2e/src/dune
+++ b/e2e/src/dune
@@ -1,16 +1,13 @@
(executable
(name main)
(preprocess
- (pps lwt_ppx ppx_deriving.make ppx_yojson_conv))
+ (pps ppx_deriving.make ppx_yojson_conv))
(libraries
- cohttp
- cohttp-lwt-unix
- httpq
+ eio
+ eio_main
+ lambdasoup
logq
- lwt
- lwt.unix
- waq
- websocket
- websocket-lwt-unix
- websocket-lwt-unix.cohttp
- yojson))
+ mirage-crypto-rng
+ mirage-crypto-rng-eio
+ yojson
+ yume))
diff --git a/e2e/src/main.ml b/e2e/src/main.ml
index 5faf7c3..9cfc7af 100644
--- a/e2e/src/main.ml
+++ b/e2e/src/main.ml
@@ -1,6 +1,3 @@
-module Uri = Httpq.Uri
-module Ptime = Waq.Util.Ptime
-
let all_tests =
[
("waq-mstdn-1", Waq_mstdn_1.f);
diff --git a/e2e/src/waq_1.ml b/e2e/src/waq_1.ml
index 140692e..728df4c 100644
--- a/e2e/src/waq_1.ml
+++ b/e2e/src/waq_1.ml
@@ -1,11 +1,11 @@
open Common
let f =
- make_waq_scenario @@ fun _token ->
- let%lwt access_token = fetch_access_token ~username:"user1" in
+ make_waq_scenario @@ fun env _token ->
+ let access_token = fetch_access_token env ~username:"user1" in
- let%lwt r =
- fetch_exn
+ let r =
+ fetch_exn env
~headers:[ (`Authorization, "Bearer " ^ access_token) ]
(waq "/api/v1/apps/verify_credentials")
in
@@ -14,8 +14,8 @@ let f =
| `Assoc l -> l |> List.assoc "name" |> expect_string = "foo"
| _ -> false);
- let%lwt r =
- fetch_exn
+ let r =
+ fetch_exn env
~headers:[ (`Authorization, "Bearer " ^ access_token) ]
(waq "/api/v1/accounts/verify_credentials")
in
@@ -29,11 +29,11 @@ let f =
|> expect_string = "public");
let account_id = l |> List.assoc "id" |> expect_string in
- let%lwt r = fetch_exn (waq "/api/v1/instance") in
+ let r = fetch_exn env (waq "/api/v1/instance") in
let l = Yojson.Safe.from_string r |> expect_assoc in
assert (l |> List.mem_assoc "uri");
- let%lwt r = get_account `Waq account_id in
+ let r = get_account env `Waq account_id in
assert (r.id = account_id);
assert (r.username = "user1");
assert (r.acct = "user1");
@@ -42,11 +42,11 @@ let f =
assert (r.followers_count = 0);
assert (r.following_count = 0);
- let%lwt a =
- update_credentials `Waq ~token:access_token ~display_name:"mod user1" ()
+ let a =
+ update_credentials env `Waq ~token:access_token ~display_name:"mod user1" ()
in
assert (a.display_name = "mod user1");
- let%lwt a = get_account `Waq account_id in
+ let a = get_account env `Waq account_id in
assert (a.display_name = "mod user1");
- Lwt.return_unit
+ ()
diff --git a/e2e/src/waq_10_mention.ml b/e2e/src/waq_10_mention.ml
index d08edd8..30126fe 100644
--- a/e2e/src/waq_10_mention.ml
+++ b/e2e/src/waq_10_mention.ml
@@ -1,12 +1,12 @@
open Common
let f =
- make_waq_scenario @@ fun token ->
- let%lwt user1_id, _, _ = lookup `Waq ~token ~username:"user1" () in
- let%lwt token2 = fetch_access_token ~username:"user2" in
- let%lwt { id; _ } = post `Waq ~token ~content:"@user2 てすと" () in
+ make_waq_scenario @@ fun env token ->
+ let user1_id, _, _ = lookup env `Waq ~token ~username:"user1" () in
+ let token2 = fetch_access_token env ~username:"user2" in
+ let ({ id; _ } : status) = post env `Waq ~token ~content:"@user2 てすと" () in
- let%lwt ntfs = get_notifications `Waq ~token:token2 in
+ let ntfs = get_notifications env `Waq ~token:token2 in
(match ntfs with
| [
{
@@ -18,11 +18,11 @@ let f =
] ->
assert (account_id = user1_id);
assert (status_id = id);
- Lwt.return_unit
- | _ -> assert false);%lwt
+ ()
+ | _ -> assert false);
(* Handle invalid mentions correctly *)
- let%lwt _ = post `Waq ~token ~content:"@not_found_user test" () in
- home_timeline `Waq ~token |> ignore_lwt;%lwt
+ let _ = post env `Waq ~token ~content:"@not_found_user test" () in
+ home_timeline env `Waq ~token |> ignore;
- Lwt.return_unit
+ ()
diff --git a/e2e/src/waq_11_marker.ml b/e2e/src/waq_11_marker.ml
index 745f281..62dd08d 100644
--- a/e2e/src/waq_11_marker.ml
+++ b/e2e/src/waq_11_marker.ml
@@ -1,18 +1,18 @@
open Common
let f =
- make_waq_scenario @@ fun token ->
- let%lwt Some { last_read_id = "0"; _ }, Some { last_read_id = "0"; _ } =
- get_markers ~token `Waq [ "home"; "notifications" ]
+ make_waq_scenario @@ fun env token ->
+ let Some { last_read_id = "0"; _ }, Some { last_read_id = "0"; _ } =
+ get_markers env ~token `Waq [ "home"; "notifications" ]
in
- let%lwt Some { last_read_id = "1"; _ }, None =
- post_markers ~token `Waq [ ("home", "1") ]
+ let Some { last_read_id = "1"; _ }, None =
+ post_markers env ~token `Waq [ ("home", "1") ]
in
- let%lwt None, Some { last_read_id = "2"; _ } =
- post_markers ~token `Waq [ ("notifications", "2") ]
+ let None, Some { last_read_id = "2"; _ } =
+ post_markers env ~token `Waq [ ("notifications", "2") ]
in
- let%lwt Some { last_read_id = "1"; _ }, Some { last_read_id = "2"; _ } =
- get_markers ~token `Waq [ "home"; "notifications" ]
+ let Some { last_read_id = "1"; _ }, Some { last_read_id = "2"; _ } =
+ get_markers env ~token `Waq [ "home"; "notifications" ]
in
- Lwt.return_unit
+ ()
[@@warning "-8"]
diff --git a/e2e/src/waq_2_ws.ml b/e2e/src/waq_2_ws.ml
index d47f877..24d7296 100644
--- a/e2e/src/waq_2_ws.ml
+++ b/e2e/src/waq_2_ws.ml
@@ -2,7 +2,7 @@ open Common
open struct
let f ~use_query_param =
- make_waq_scenario @@ fun waq_token ->
+ make_waq_scenario @@ fun env waq_token ->
let got_uri = ref None in
let set_current_state, handler =
websocket_handler_state_machine ~init:`Init
@@ -21,13 +21,13 @@ open struct
got_uri := Some uri;
(* Check that no event should be received once an unsubscribe message is sent. *)
- if%lwt Lwt.return (not use_query_param) then (
- pushf (Some {|{"type":"unsubscribe","stream":"user"}|});%lwt
- let%lwt _ = post `Waq ~token:waq_token () in
- Lwt_unix.sleep 5.0);%lwt
+ if not use_query_param then (
+ pushf (Some {|{"type":"unsubscribe","stream":"user"}|});
+ let _ = post env `Waq ~token:waq_token () in
+ Eio.Time.sleep (Eio.Stdenv.clock env) 5.0);
- pushf None;%lwt
- Lwt.return `End );
+ pushf None;
+ `End );
(`End, fun _ -> assert false);
]
()
@@ -39,17 +39,16 @@ open struct
in
let expected_uri = ref None in
- let mtx = Lwt_mutex.create () in
- websocket ~mtx `Waq ~target ~token:waq_token handler (fun pushf ->
- if%lwt Lwt.return (not use_query_param) then
- pushf (Some {|{"type":"subscribe","stream":"user"}|});%lwt
- let%lwt { uri; _ } = post `Waq ~token:waq_token () in
+ let mtx = Eio.Mutex.create () in
+ websocket env ~mtx `Waq ~target ~token:waq_token handler (fun pushf ->
+ if not use_query_param then
+ pushf (Some {|{"type":"subscribe","stream":"user"}|});
+ let { uri; _ } = post env `Waq ~token:waq_token () in
expected_uri := Some uri;
- set_current_state `Recv;
- Lwt.return_unit);%lwt
+ set_current_state `Recv);
assert (Option.get !got_uri = Option.get !expected_uri);
- Lwt.return_unit
+ ()
end
let f1 = f ~use_query_param:true
diff --git a/e2e/src/waq_3.ml b/e2e/src/waq_3.ml
index bb88f31..dc06645 100644
--- a/e2e/src/waq_3.ml
+++ b/e2e/src/waq_3.ml
@@ -1,78 +1,78 @@
open Common
let f =
- make_waq_scenario @@ fun waq_token ->
- let%lwt waq_token' = fetch_access_token ~username:"user2" in
- let%lwt user1_id, _, _ = lookup `Waq ~token:waq_token ~username:"user1" () in
- let%lwt user2_id, _, _ = lookup `Waq ~token:waq_token ~username:"user2" () in
+ make_waq_scenario @@ fun env waq_token ->
+ let waq_token' = fetch_access_token env ~username:"user2" in
+ let user1_id, _, _ = lookup env `Waq ~token:waq_token ~username:"user1" () in
+ let user2_id, _, _ = lookup env `Waq ~token:waq_token ~username:"user2" () in
(* Follow @user2 *)
- follow `Waq ~token:waq_token user2_id;%lwt
- Lwt_unix.sleep 1.0;%lwt
+ follow env `Waq ~token:waq_token user2_id;
+ Eio.Time.sleep env#clock 1.0;
(* Post by @user2 *)
- let%lwt { uri; id; _ } = post `Waq ~token:waq_token' () in
+ let { uri; id; _ } = post env `Waq ~token:waq_token' () in
(* check accounts *)
- let%lwt a = get_account `Waq user2_id in
+ let a = get_account env `Waq user2_id in
assert (a.statuses_count = 1);
(* Reply by me *)
- let%lwt { uri = uri2; id = id2; _ } =
- post `Waq ~token:waq_token ~in_reply_to_id:id ()
+ let { uri = uri2; id = id2; _ } =
+ post env `Waq ~token:waq_token ~in_reply_to_id:id ()
in
(* check accounts *)
- let%lwt a = get_account `Waq user1_id in
+ let a = get_account env `Waq user1_id in
assert (a.statuses_count = 1);
(* Reply again *)
- let%lwt { uri = uri3; _ } =
- post `Waq ~token:waq_token ~in_reply_to_id:id2 ()
+ let { uri = uri3; _ } =
+ post env `Waq ~token:waq_token ~in_reply_to_id:id2 ()
in
(* Get my home timeline and check *)
- (home_timeline `Waq ~token:waq_token >|= function
- | [ `Assoc l3; `Assoc l2; `Assoc l ] ->
- (* Check if the timeline is correct *)
- assert (uri = (l |> List.assoc "uri" |> expect_string));
- assert (id = (l2 |> List.assoc "in_reply_to_id" |> expect_string));
- assert (uri2 = (l2 |> List.assoc "uri" |> expect_string));
- assert (id2 = (l3 |> List.assoc "in_reply_to_id" |> expect_string));
- assert (uri3 = (l3 |> List.assoc "uri" |> expect_string));
- ()
- | _ -> assert false);%lwt
+ (match home_timeline env `Waq ~token:waq_token with
+ | [ `Assoc l3; `Assoc l2; `Assoc l ] ->
+ (* Check if the timeline is correct *)
+ assert (uri = (l |> List.assoc "uri" |> expect_string));
+ assert (id = (l2 |> List.assoc "in_reply_to_id" |> expect_string));
+ assert (uri2 = (l2 |> List.assoc "uri" |> expect_string));
+ assert (id2 = (l3 |> List.assoc "in_reply_to_id" |> expect_string));
+ assert (uri3 = (l3 |> List.assoc "uri" |> expect_string));
+ ()
+ | _ -> assert false);
(* Unfollow @user2 *)
- unfollow `Waq ~token:waq_token user2_id;%lwt
- Lwt_unix.sleep 1.0;%lwt
+ unfollow env `Waq ~token:waq_token user2_id;
+ Eio.Time.sleep env#clock 1.0;
(* Get my home timeline and check again *)
- (home_timeline `Waq ~token:waq_token >|= function
- | [ `Assoc l3; `Assoc l2 ] ->
- (* Check if the timeline is correct *)
- assert (uri2 = (l2 |> List.assoc "uri" |> expect_string));
- assert (uri3 = (l3 |> List.assoc "uri" |> expect_string));
- ()
- | _ -> assert false);%lwt
+ (match home_timeline env `Waq ~token:waq_token with
+ | [ `Assoc l3; `Assoc l2 ] ->
+ (* Check if the timeline is correct *)
+ assert (uri2 = (l2 |> List.assoc "uri" |> expect_string));
+ assert (uri3 = (l3 |> List.assoc "uri" |> expect_string));
+ ()
+ | _ -> assert false);
(* Check status itself *)
- let%lwt s = get_status `Waq id in
+ let s = get_status env `Waq id in
assert (s.uri = uri);
(* Check the status's context *)
- let%lwt ancestors, descendants = get_status_context `Waq id2 in
+ let ancestors, descendants = get_status_context env `Waq id2 in
assert (ancestors |> List.map (fun r -> r.uri) = [ uri ]);
assert (descendants |> List.map (fun r -> r.uri) = [ uri3 ]);
(* Check account's statuses *)
- let%lwt statuses = get_account_statuses `Waq user1_id in
+ let statuses = get_account_statuses env `Waq user1_id in
assert ([ uri3; uri2 ] = (statuses |> List.map (fun s -> s.uri)));
- let%lwt statuses = get_account_statuses `Waq ~exclude_replies:true user1_id in
+ let statuses = get_account_statuses env `Waq ~exclude_replies:true user1_id in
assert (statuses = []);
- let%lwt statuses = get_account_statuses `Waq user2_id in
+ let statuses = get_account_statuses env `Waq user2_id in
assert ([ uri ] = (statuses |> List.map (fun s -> s.uri)));
- let%lwt statuses = get_account_statuses `Waq ~exclude_replies:true user2_id in
+ let statuses = get_account_statuses env `Waq ~exclude_replies:true user2_id in
assert ([ uri ] = (statuses |> List.map (fun s -> s.uri)));
- Lwt.return_unit
+ ()
diff --git a/e2e/src/waq_4_reblog.ml b/e2e/src/waq_4_reblog.ml
index 8f44127..daed2a6 100644
--- a/e2e/src/waq_4_reblog.ml
+++ b/e2e/src/waq_4_reblog.ml
@@ -1,40 +1,41 @@
open Common
let f =
- make_waq_scenario @@ fun token ->
+ make_waq_scenario @@ fun env token ->
let expected_ids = ref [] in
- let%lwt ws_recv_msgs =
- websocket_stack `Waq ~token @@ fun _pushf ->
- let%lwt { id = id1; reblog = None; reblogged = false; reblogs_count = 0; _ }
- =
- post `Waq ~token ~content:"Hello world" ()
+ let ws_recv_msgs =
+ websocket_stack env `Waq ~token @@ fun _pushf ->
+ let { id = id1; reblog = None; reblogged = false; reblogs_count = 0; _ } =
+ post env `Waq ~token ~content:"Hello world" ()
in
- let%lwt {
- id = id2;
- reblogged = true;
- reblog = Some { id = id1'; reblogged = true; reblog = None; _ };
- _;
- } =
- reblog `Waq ~token ~id:id1
+ let {
+ id = id2;
+ reblogged = true;
+ reblog = Some { id = id1'; reblogged = true; reblog = None; _ };
+ _;
+ } =
+ reblog env `Waq ~token ~id:id1
in
- let%lwt { id = id2'; reblog = Some { id = id1''; _ }; _ } =
- reblog `Waq ~token ~id:id1
+ let { id = id2'; reblog = Some { id = id1''; _ }; _ } =
+ reblog env `Waq ~token ~id:id1
in
- let%lwt { id = id2''; reblog = Some { id = id1'''; _ }; _ } =
- reblog `Waq ~token ~id:id2
+ let { id = id2''; reblog = Some { id = id1'''; _ }; _ } =
+ reblog env `Waq ~token ~id:id2
in
assert (id1 = id1' && id1 = id1'' && id1 = id1''');
assert (id2 = id2' && id2 = id2'');
expected_ids := [ id1; id2 ];
- let%lwt { reblogs_count; _ } = get_status `Waq ~token id1 in
+ let { reblogs_count; _ } = get_status env `Waq ~token id1 in
assert (reblogs_count = 1);
- Lwt.return_unit
+ ()
+ [@@warning "-8"]
in
let ws_recv_msgs =
- ws_recv_msgs |> List.map (Yojson.Safe.from_string |.> expect_assoc)
+ ws_recv_msgs
+ |> List.map (fun x -> x |> Yojson.Safe.from_string |> expect_assoc)
in
let ws_recv_ids, ws_recv_notfs =
ws_recv_msgs
@@ -57,5 +58,4 @@ let f =
assert (List.sort compare !expected_ids = List.sort compare ws_recv_ids);
assert (ws_recv_notfs = []);
- Lwt.return_unit
- [@@warning "-8"]
+ ()
diff --git a/e2e/src/waq_5_fav.ml b/e2e/src/waq_5_fav.ml
index fb1ed3d..2ad7496 100644
--- a/e2e/src/waq_5_fav.ml
+++ b/e2e/src/waq_5_fav.ml
@@ -1,23 +1,23 @@
open Common
let f =
- make_waq_scenario @@ fun token ->
- let%lwt user1_id, _, _ = lookup `Waq ~token ~username:"user1" () in
- let%lwt user2_id, _, _ = lookup `Waq ~token ~username:"user2" () in
- let%lwt user3_id, _, _ = lookup `Waq ~token ~username:"user3" () in
- let%lwt token2 = fetch_access_token ~username:"user2" in
- let%lwt token3 = fetch_access_token ~username:"user3" in
+ make_waq_scenario @@ fun env token ->
+ let user1_id, _, _ = lookup env `Waq ~token ~username:"user1" () in
+ let user2_id, _, _ = lookup env `Waq ~token ~username:"user2" () in
+ let user3_id, _, _ = lookup env `Waq ~token ~username:"user3" () in
+ let token2 = fetch_access_token env ~username:"user2" in
+ let token3 = fetch_access_token env ~username:"user3" in
- let%lwt { id; _ } = post `Waq ~token () in
- let%lwt { favourited; _ } = fav `Waq ~token ~id in
+ let ({ id; _ } : status) = post env `Waq ~token () in
+ let { favourited; _ } = fav env `Waq ~token ~id in
assert favourited;
- let%lwt { favourited; _ } = fav `Waq ~token:token2 ~id in
+ let { favourited; _ } = fav env `Waq ~token:token2 ~id in
assert favourited;
- let%lwt { favourited; favourites_count; _ } = fav `Waq ~token:token3 ~id in
+ let { favourited; favourites_count; _ } = fav env `Waq ~token:token3 ~id in
assert favourited;
assert (favourites_count = 3);
- let%lwt l = get_favourited_by `Waq ~token ~id in
+ let l = get_favourited_by env `Waq ~token ~id in
assert (List.length l = 3);
assert (
l |> List.find_opt (fun (a : account) -> a.id = user1_id) |> Option.is_some);
@@ -26,7 +26,7 @@ let f =
assert (
l |> List.find_opt (fun (a : account) -> a.id = user3_id) |> Option.is_some);
- (match%lwt get_notifications `Waq ~token with
+ (match get_notifications env `Waq ~token with
| [
{
typ = "favourite";
@@ -44,24 +44,21 @@ let f =
assert (account_id3 = user3_id);
assert (account_id2 = user2_id);
assert (status_id3 = id);
- assert (status_id2 = id);
- Lwt.return_unit
- | _ -> assert false);%lwt
+ assert (status_id2 = id)
+ | _ -> assert false);
- let%lwt { favourited; _ } = unfav `Waq ~token ~id in
+ let { favourited; _ } = unfav env `Waq ~token ~id in
assert (not favourited);
- let%lwt { favourited; _ } = unfav `Waq ~token:token2 ~id in
+ let { favourited; _ } = unfav env `Waq ~token:token2 ~id in
assert (not favourited);
- let%lwt { favourited; favourites_count; _ } = unfav `Waq ~token:token3 ~id in
+ let { favourited; favourites_count; _ } = unfav env `Waq ~token:token3 ~id in
assert (not favourited);
assert (favourites_count = 0);
- (match%lwt get_favourited_by `Waq ~token ~id with
- | [] -> Lwt.return_unit
- | _ -> assert false);%lwt
+ (match get_favourited_by env `Waq ~token ~id with
+ | [] -> ()
+ | _ -> assert false);
- (match%lwt get_notifications `Waq ~token with
- | [] -> Lwt.return_unit
- | _ -> assert false);%lwt
+ (match get_notifications env `Waq ~token with [] -> () | _ -> assert false);
- Lwt.return_unit
+ ()
diff --git a/e2e/src/waq_6_rel.ml b/e2e/src/waq_6_rel.ml
index 03ef7f5..5db31ee 100644
--- a/e2e/src/waq_6_rel.ml
+++ b/e2e/src/waq_6_rel.ml
@@ -1,143 +1,142 @@
open Common
-let expect_followers account_id expected_follower_ids =
- let%lwt l = get_followers `Waq account_id in
+let expect_followers env account_id expected_follower_ids =
+ let l = get_followers env `Waq account_id in
let got = l |> List.map (fun (a : account) -> a.id) in
assert (got = expected_follower_ids);
- Lwt.return_unit
+ ()
-let expect_following account_id expected_follower_ids =
- let%lwt l = get_following `Waq account_id in
+let expect_following env account_id expected_follower_ids =
+ let l = get_following env `Waq account_id in
let got = l |> List.map (fun (a : account) -> a.id) in
assert (got = expected_follower_ids);
- Lwt.return_unit
+ ()
let f =
- make_waq_scenario @@ fun token ->
+ make_waq_scenario @@ fun env token ->
(* Connect WebSocket *)
- let%lwt ws_recv_msgs =
- websocket_stack `Waq ~token @@ fun _pushf ->
- let%lwt token' = fetch_access_token ~username:"user2" in
- let%lwt user1_id, _, _ = lookup `Waq ~token:token' ~username:"user1" () in
- let%lwt user2_id, _, _ = lookup `Waq ~token ~username:"user2" () in
- let%lwt user3_id, _, _ = lookup `Waq ~token ~username:"user3" () in
+ let ws_recv_msgs =
+ websocket_stack env `Waq ~token @@ fun _pushf ->
+ let token' = fetch_access_token env ~username:"user2" in
+ let user1_id, _, _ = lookup env `Waq ~token:token' ~username:"user1" () in
+ let user2_id, _, _ = lookup env `Waq ~token ~username:"user2" () in
+ let user3_id, _, _ = lookup env `Waq ~token ~username:"user3" () in
(* user1: Try to follow myself, which should be forbidden *)
- (try%lwt
- follow `Waq ~token user1_id;%lwt
+ (try
+ follow env `Waq ~token user1_id;
assert false
- with Httpq.Client.FetchFailure (Some (`Forbidden, _, _)) ->
- Lwt.return_unit);%lwt
+ with FetchFailure (Some (`Forbidden, _, _)) -> ());
(* user1: Follow @user2 *)
- follow `Waq ~token user2_id;%lwt
- expect_followers user2_id [ user1_id ];%lwt
- expect_following user1_id [ user2_id ];%lwt
+ follow env `Waq ~token user2_id;
+ expect_followers env user2_id [ user1_id ];
+ expect_following env user1_id [ user2_id ];
(* user1: check relationship *)
- (match%lwt get_relationships `Waq ~token [ user2_id; user3_id ] with
+ (match get_relationships env `Waq ~token [ user2_id; user3_id ] with
| [ rel2; rel3 ] ->
assert (rel2.id = user2_id);
assert (rel3.id = user3_id);
assert rel2.following;
assert (not rel2.followed_by);
- Lwt.return_unit
- | _ -> assert false);%lwt
+ ()
+ | _ -> assert false);
(* check accounts *)
- let%lwt a = get_account `Waq user1_id in
+ let a = get_account env `Waq user1_id in
assert (a.followers_count = 0);
assert (a.following_count = 1);
- let%lwt a = get_account `Waq user2_id in
+ let a = get_account env `Waq user2_id in
assert (a.followers_count = 1);
assert (a.following_count = 0);
(* check notifications *)
- (match%lwt get_notifications `Waq ~token:token' with
+ (match get_notifications env `Waq ~token:token' with
| [ { typ = "follow"; account = a; _ } ] ->
assert (a.id = user1_id);
- Lwt.return_unit
- | _ -> assert false);%lwt
+ ()
+ | _ -> assert false);
(* user2: follow @user1 *)
- follow `Waq ~token:token' user1_id;%lwt
- expect_followers user1_id [ user2_id ];%lwt
- expect_following user2_id [ user1_id ];%lwt
+ follow env `Waq ~token:token' user1_id;
+ expect_followers env user1_id [ user2_id ];
+ expect_following env user2_id [ user1_id ];
(* user1: check relationship *)
- (match%lwt get_relationships `Waq ~token [ user2_id ] with
+ (match get_relationships env `Waq ~token [ user2_id ] with
| [ rel ] ->
assert (rel.id = user2_id);
assert rel.following;
assert rel.followed_by;
- Lwt.return_unit
- | _ -> assert false);%lwt
+ ()
+ | _ -> assert false);
(* check accounts *)
- let%lwt a = get_account `Waq user1_id in
+ let a = get_account env `Waq user1_id in
assert (a.followers_count = 1);
assert (a.following_count = 1);
- let%lwt a = get_account `Waq user2_id in
+ let a = get_account env `Waq user2_id in
assert (a.followers_count = 1);
assert (a.following_count = 1);
(* check notifications *)
- (match%lwt get_notifications `Waq ~token with
+ (match get_notifications env `Waq ~token with
| [ { typ = "follow"; account = a; _ } ] ->
assert (a.id = user2_id);
- Lwt.return_unit
- | _ -> assert false);%lwt
+ ()
+ | _ -> assert false);
(* user1: Unfollow @user2 *)
- unfollow `Waq ~token user2_id;%lwt
- expect_followers user2_id [];%lwt
- expect_following user1_id [];%lwt
+ unfollow env `Waq ~token user2_id;
+ expect_followers env user2_id [];
+ expect_following env user1_id [];
(* user1: check relationship *)
- (match%lwt get_relationships `Waq ~token [ user2_id ] with
+ (match get_relationships env `Waq ~token [ user2_id ] with
| [ rel ] ->
assert (rel.id = user2_id);
assert (not rel.following);
assert rel.followed_by;
- Lwt.return_unit
- | _ -> assert false);%lwt
+ ()
+ | _ -> assert false);
(* check accounts *)
- let%lwt a = get_account `Waq user1_id in
+ let a = get_account env `Waq user1_id in
assert (a.followers_count = 1);
assert (a.following_count = 0);
- let%lwt a = get_account `Waq user2_id in
+ let a = get_account env `Waq user2_id in
assert (a.followers_count = 0);
assert (a.following_count = 1);
(* user2: Unfollow @user1 *)
- unfollow `Waq ~token:token' user1_id;%lwt
- expect_followers user1_id [];%lwt
- expect_following user2_id [];%lwt
+ unfollow env `Waq ~token:token' user1_id;
+ expect_followers env user1_id [];
+ expect_following env user2_id [];
(* user1: check relationship *)
- (match%lwt get_relationships `Waq ~token [ user2_id ] with
+ (match get_relationships env `Waq ~token [ user2_id ] with
| [ rel ] ->
assert (rel.id = user2_id);
assert (not rel.following);
assert (not rel.followed_by);
- Lwt.return_unit
- | _ -> assert false);%lwt
+ ()
+ | _ -> assert false);
(* check accounts *)
- let%lwt a = get_account `Waq user1_id in
+ let a = get_account env `Waq user1_id in
assert (a.followers_count = 0);
assert (a.following_count = 0);
- let%lwt a = get_account `Waq user2_id in
+ let a = get_account env `Waq user2_id in
assert (a.followers_count = 0);
assert (a.following_count = 0);
- Lwt.return_unit
+ ()
in
let ws_notifications =
ws_recv_msgs
- |> List.map (Yojson.Safe.from_string |.> expect_assoc)
+ |> List.map (fun x -> x |> Yojson.Safe.from_string |> expect_assoc)
|> List.filter_map (fun (l : (string * Yojson.Safe.t) list) ->
if
List.assoc "stream" l = `List [ `String "user" ]
@@ -148,11 +147,11 @@ let f =
|> Yojson.Safe.from_string |> notification_of_yojson)
else None)
in
- let%lwt got_notifications = get_notifications `Waq ~token in
+ let got_notifications = get_notifications env `Waq ~token in
assert (
got_notifications
|> List.map (fun r -> r.id)
|> List.sort compare
= (ws_notifications |> List.map (fun r -> r.id) |> List.sort compare));
- Lwt.return_unit
+ ()
diff --git a/e2e/src/waq_7_reblog.ml b/e2e/src/waq_7_reblog.ml
index 22325c2..3cbd34f 100644
--- a/e2e/src/waq_7_reblog.ml
+++ b/e2e/src/waq_7_reblog.ml
@@ -1,15 +1,15 @@
open Common
let f =
- make_waq_scenario @@ fun token ->
- let%lwt user2_id, _, _ = lookup `Waq ~token ~username:"user2" () in
- let%lwt token2 = fetch_access_token ~username:"user2" in
- let%lwt token3 = fetch_access_token ~username:"user3" in
- let%lwt { id; _ } = post `Waq ~token () in
- let%lwt _ = reblog `Waq ~token ~id in
- let%lwt _ = reblog `Waq ~token:token2 ~id in
+ make_waq_scenario @@ fun env token ->
+ let user2_id, _, _ = lookup env `Waq ~token ~username:"user2" () in
+ let token2 = fetch_access_token env ~username:"user2" in
+ let token3 = fetch_access_token env ~username:"user3" in
+ let ({ id; _ } : status) = post env `Waq ~token () in
+ let _ = reblog env `Waq ~token ~id in
+ let _ = reblog env `Waq ~token:token2 ~id in
- let%lwt ntfs = get_notifications `Waq ~token in
+ let ntfs = get_notifications env `Waq ~token in
(match ntfs with
| [
{
@@ -22,26 +22,26 @@ let f =
assert (account_id2 = user2_id);
assert (status_id2 = id);
assert (reblogs_count = 2);
- Lwt.return_unit
- | _ -> assert false);%lwt
+ ()
+ | _ -> assert false);
(* Wrong unreblog *)
- expect_exc_lwt (fun () -> unreblog `Waq ~token:token3 ~id);%lwt
- let%lwt { reblogs_count; _ } = get_status `Waq ~token id in
+ expect_exc (fun () -> unreblog env `Waq ~token:token3 ~id);
+ let { reblogs_count; _ } = get_status env `Waq ~token id in
assert (reblogs_count = 2);
(* Actual unreblogs *)
- let%lwt { id = unreblog_id; reblogs_count; reblogged; _ } =
- unreblog `Waq ~token:token2 ~id
+ let { id = unreblog_id; reblogs_count; reblogged; _ } =
+ unreblog env `Waq ~token:token2 ~id
in
assert (unreblog_id = id);
assert (reblogs_count = 1);
assert (not reblogged);
- let%lwt { id = unreblog_id; reblogs_count; reblogged; _ } =
- unreblog `Waq ~token ~id
+ let { id = unreblog_id; reblogs_count; reblogged; _ } =
+ unreblog env `Waq ~token ~id
in
assert (unreblog_id = id);
assert (reblogs_count = 0);
assert (not reblogged);
- Lwt.return_unit
+ ()
diff --git a/e2e/src/waq_8_delete.ml b/e2e/src/waq_8_delete.ml
index 1134114..5bf0351 100644
--- a/e2e/src/waq_8_delete.ml
+++ b/e2e/src/waq_8_delete.ml
@@ -1,41 +1,41 @@
open Common
-let expect_no_status kind id =
- try%lwt
- get_status kind id |> ignore_lwt;%lwt
+let expect_no_status env kind id =
+ try
+ get_status env kind id |> ignore;
assert false
- with Httpq.Client.FetchFailure (Some (`Not_found, _, _)) -> Lwt.return_unit
+ with FetchFailure (Some (`Not_found, _, _)) -> ()
let f =
- make_waq_scenario @@ fun token ->
- let%lwt token' = fetch_access_token ~username:"user2" in
- let%lwt ws_recv_msgs =
- websocket_stack `Waq ~token @@ fun _pushf ->
- let%lwt { id; _ } = post `Waq ~token () in
- let%lwt s = get_status `Waq id in
+ make_waq_scenario @@ fun env token ->
+ let token' = fetch_access_token env ~username:"user2" in
+ let ws_recv_msgs =
+ websocket_stack env `Waq ~token @@ fun _pushf ->
+ let ({ id; _ } : status) = post env `Waq ~token () in
+ let s = get_status env `Waq id in
assert (s.id = id);
(* Wrong delete *)
- expect_exc_lwt (fun () -> delete_status `Waq ~token:token' id);%lwt
+ expect_exc (fun () -> delete_status env `Waq ~token:token' id);
(* Should remain *)
- get_status `Waq id |> ignore_lwt;%lwt
+ get_status env `Waq id |> ignore;
(* Actual delete *)
- let%lwt s = delete_status `Waq ~token id in
+ let s = delete_status env `Waq ~token id in
assert (s.id = id);
- expect_no_status `Waq id;%lwt
+ expect_no_status env `Waq id;
- let%lwt { id; _ } = post `Waq ~token () in
- let%lwt s = reblog `Waq ~token ~id in
- delete_status `Waq ~token id |> ignore_lwt;%lwt
- expect_no_status `Waq id;%lwt
- expect_no_status `Waq s.id;%lwt
+ let ({ id; _ } : status) = post env `Waq ~token () in
+ let s = reblog env `Waq ~token ~id in
+ delete_status env `Waq ~token id |> ignore;
+ expect_no_status env `Waq id;
+ expect_no_status env `Waq s.id;
- Lwt.return_unit
+ ()
in
let ws_delete_events =
ws_recv_msgs
- |> List.map (Yojson.Safe.from_string |.> expect_assoc)
+ |> List.map (fun x -> x |> Yojson.Safe.from_string |> expect_assoc)
|> List.filter_map (fun (l : (string * Yojson.Safe.t) list) ->
if
List.assoc "stream" l = `List [ `String "user" ]
@@ -46,4 +46,4 @@ let f =
assert (
List.sort compare ws_delete_events = List.sort compare [ "1"; "2"; "3" ]);
- Lwt.return_unit
+ ()
diff --git a/e2e/src/waq_9_ap.ml b/e2e/src/waq_9_ap.ml
index 0ac8d30..ba9003a 100644
--- a/e2e/src/waq_9_ap.ml
+++ b/e2e/src/waq_9_ap.ml
@@ -1,8 +1,8 @@
open Common
let f =
- make_waq_scenario @@ fun _token ->
- let%lwt r = fetch_exn (waq "/users/user1/outbox") in
+ make_waq_scenario @@ fun env _token ->
+ let r = fetch_exn env (waq "/users/user1/outbox") in
let l = Yojson.Safe.from_string r |> expect_assoc in
assert (List.assoc "type" l |> expect_string = "OrderedCollection");
assert (List.assoc "totalItems" l |> expect_int = 0);
@@ -12,18 +12,18 @@ let f =
List.assoc "last" l |> expect_string
= waq "/users/user1/outbox?min_id=0&page=true");
- let%lwt r = fetch_exn (waq "/users/user1/following") in
+ let r = fetch_exn env (waq "/users/user1/following") in
let l = Yojson.Safe.from_string r |> expect_assoc in
assert (List.assoc "type" l |> expect_string = "OrderedCollection");
assert (List.assoc "totalItems" l |> expect_int = 0);
assert (
List.assoc "first" l |> expect_string = waq "/users/user1/following?page=1");
- let%lwt r = fetch_exn (waq "/users/user1/followers") in
+ let r = fetch_exn env (waq "/users/user1/followers") in
let l = Yojson.Safe.from_string r |> expect_assoc in
assert (List.assoc "type" l |> expect_string = "OrderedCollection");
assert (List.assoc "totalItems" l |> expect_int = 0);
assert (
List.assoc "first" l |> expect_string = waq "/users/user1/followers?page=1");
- Lwt.return_unit
+ ()
diff --git a/e2e/src/waq_mstdn_1.ml b/e2e/src/waq_mstdn_1.ml
index 2017c5c..7765097 100644
--- a/e2e/src/waq_mstdn_1.ml
+++ b/e2e/src/waq_mstdn_1.ml
@@ -1,7 +1,7 @@
open Common
let f =
- make_waq_and_mstdn_scenario @@ fun waq_token mstdn_token ->
+ make_waq_and_mstdn_scenario @@ fun env waq_token mstdn_token ->
(* Connect WebSocket *)
let ws_statuses = ref [] in
let _set_current_state, handler =
@@ -16,36 +16,36 @@ let f =
ws_statuses :=
(Yojson.Safe.from_string payload |> expect_assoc)
:: !ws_statuses;
- Lwt.return `Recv );
+ `Recv );
]
()
in
let uris = ref [] in
- websocket `Waq ~token:waq_token handler (fun pushf ->
+ websocket env `Waq ~token:waq_token handler (fun pushf ->
(* Lookup @mstdn1@mstdn_server_domain *)
- let%lwt mstdn1_id, username, acct =
- lookup `Waq ~token:waq_token ~username:"mstdn1"
+ let mstdn1_id, username, acct =
+ lookup env `Waq ~token:waq_token ~username:"mstdn1"
~domain:mstdn_server_domain ()
in
assert (username = "mstdn1");
assert (acct = "mstdn1@" ^ mstdn_server_domain);
(* Follow @mstdn1@mstdn_server_domain *)
- follow `Waq ~token:waq_token mstdn1_id;%lwt
- Lwt_unix.sleep 1.0;%lwt
+ follow env `Waq ~token:waq_token mstdn1_id;
+ Eio.Time.sleep (Eio.Stdenv.clock env) 1.0;
(* Post by @mstdn1@mstdn_server_domain *)
- let%lwt { uri; _ } = post `Mstdn ~token:mstdn_token () in
+ let ({ uri; _ } : status) = post env `Mstdn ~token:mstdn_token () in
uris := uri :: !uris;
- Lwt_unix.sleep 1.0;%lwt
+ Eio.Time.sleep (Eio.Stdenv.clock env) 1.0;
(* Post by me *)
- let%lwt { uri = uri2; _ } = post `Waq ~token:waq_token () in
+ let ({ uri = uri2; _ } : status) = post env `Waq ~token:waq_token () in
uris := uri2 :: !uris;
- Lwt_unix.sleep 1.0;%lwt
+ Eio.Time.sleep (Eio.Stdenv.clock env) 1.0;
(* Get my home timeline and check *)
- (home_timeline `Waq ~token:waq_token >|= function
+ (home_timeline env `Waq ~token:waq_token |> function
| [ `Assoc l2; `Assoc l ] ->
(* Check if the timeline is correct *)
assert (uri = (l |> List.assoc "uri" |> expect_string));
@@ -55,21 +55,21 @@ let f =
Logq.err (fun m ->
m "unexpected home timeline %s"
(Yojson.Safe.to_string (`List res)));
- assert false);%lwt
+ assert false);
(* Unfollow @mstdn1@mstdn_server_domain *)
- unfollow `Waq ~token:waq_token mstdn1_id;%lwt
- Lwt_unix.sleep 1.0;%lwt
+ unfollow env `Waq ~token:waq_token mstdn1_id;
+ Eio.Time.sleep (Eio.Stdenv.clock env) 1.0;
(* Get my home timeline and check again *)
- (home_timeline `Waq ~token:waq_token >|= function
+ (home_timeline env `Waq ~token:waq_token |> function
| [ `Assoc l2 ] ->
(* Check if the timeline is correct *)
assert (uri2 = (l2 |> List.assoc "uri" |> expect_string));
()
- | _ -> assert false);%lwt
+ | _ -> assert false);
- pushf None);%lwt
+ pushf None);
let expected_uris = List.sort compare !uris in
let got_uris =
@@ -79,4 +79,4 @@ let f =
in
assert (expected_uris = got_uris);
- Lwt.return_unit
+ ()
diff --git a/e2e/src/waq_mstdn_10_attachment.ml b/e2e/src/waq_mstdn_10_attachment.ml
index 478b2ac..a8f79f7 100644
--- a/e2e/src/waq_mstdn_10_attachment.ml
+++ b/e2e/src/waq_mstdn_10_attachment.ml
@@ -1,33 +1,31 @@
open Common2
-let f (a0 : agent) (a1 : agent) =
+let f env (a0 : agent) (a1 : agent) =
(* a0: Follow a1 *)
- follow_agent a0 a1;%lwt
- Lwt_unix.sleep 1.0;%lwt
+ follow_agent env a0 a1;
+ Eio.Time.sleep (Eio.Stdenv.clock env) 1.0;
(* a1: Post with attachments *)
- let%lwt { id = media_id; _ } =
- upload_media a1 ~filename:"test0.png" ~data:test_image
+ let ({ id = media_id; _ } : media_attachment) =
+ upload_media env a1 ~filename:"test0.png" ~data:test_image
~content_type:"image/png"
in
- let%lwt { id = media_id2; _ } =
- upload_media a1 ~filename:"test1.png" ~data:test_image
+ let ({ id = media_id2; _ } : media_attachment) =
+ upload_media env a1 ~filename:"test1.png" ~data:test_image
~content_type:"image/png"
in
- let%lwt { uri; media_attachments; _ } =
- post a1 ~media_ids:[ media_id; media_id2 ] ()
+ let { uri; media_attachments; _ } =
+ post env a1 ~media_ids:[ media_id; media_id2 ] ()
in
assert (
media_attachments
|> List.map (fun (a : media_attachment) -> a.id)
= [ media_id; media_id2 ]);
- Lwt_unix.sleep 1.0;%lwt
+ Eio.Time.sleep (Eio.Stdenv.clock env) 1.0;
(* a0: Get the post *)
- let%lwt a0_post =
- match%lwt search a0 uri with
- | _, [ s ], _ -> Lwt.return s
- | _ -> assert false
+ let a0_post =
+ match search env a0 uri with _, [ s ], _ -> s | _ -> assert false
in
(* a0: Check the post *)
@@ -35,10 +33,10 @@ let f (a0 : agent) (a1 : agent) =
assert (List.length ats = 2);
assert (ats |> List.for_all (fun (a : media_attachment) -> a.type_ = "image"));
- Lwt.return_unit
+ ()
let f_waq_mstdn =
- make_waq_and_mstdn_scenario @@ fun waq_token mstdn_token ->
+ make_waq_and_mstdn_scenario @@ fun env waq_token mstdn_token ->
let a0 =
make_agent ~kind:`Waq ~token:waq_token ~username:"user1"
~domain:waq_server_domain
@@ -47,11 +45,11 @@ let f_waq_mstdn =
make_agent ~kind:`Mstdn ~token:mstdn_token ~username:"mstdn1"
~domain:mstdn_server_domain
in
- f a0 a1;%lwt
- Lwt.return_unit
+ f env a0 a1;
+ ()
let f_mstdn_waq =
- make_waq_and_mstdn_scenario @@ fun waq_token mstdn_token ->
+ make_waq_and_mstdn_scenario @@ fun env waq_token mstdn_token ->
let a0 =
make_agent ~kind:`Mstdn ~token:mstdn_token ~username:"mstdn1"
~domain:mstdn_server_domain
@@ -60,12 +58,12 @@ let f_mstdn_waq =
make_agent ~kind:`Waq ~token:waq_token ~username:"user1"
~domain:waq_server_domain
in
- f a0 a1;%lwt
- Lwt.return_unit
+ f env a0 a1;
+ ()
let f_waq_waq =
- make_waq_scenario @@ fun token ->
- let%lwt token2 = fetch_access_token ~username:"user2" in
+ make_waq_scenario @@ fun env token ->
+ let token2 = fetch_access_token env ~username:"user2" in
let a0 =
make_agent ~kind:`Waq ~token ~username:"user1" ~domain:waq_server_domain
in
@@ -73,4 +71,4 @@ let f_waq_waq =
make_agent ~kind:`Waq ~token:token2 ~username:"user2"
~domain:waq_server_domain
in
- f a0 a1
+ f env a0 a1
diff --git a/e2e/src/waq_mstdn_11_mention.ml b/e2e/src/waq_mstdn_11_mention.ml
index a6584ad..52c6ffb 100644
--- a/e2e/src/waq_mstdn_11_mention.ml
+++ b/e2e/src/waq_mstdn_11_mention.ml
@@ -1,65 +1,65 @@
open Common2
-let f (a0 : agent) (a1 : agent) (a2 : agent) =
+let f env (a0 : agent) (a1 : agent) (a2 : agent) =
(* a0: Post with mentions *)
- let%lwt { uri; _ } =
- post a0
+ let { uri; _ } =
+ post env a0
~content:
(Printf.sprintf "@%s @%s てすと"
(acct_of_agent ~from:a0 a1)
(acct_of_agent ~from:a0 a2))
()
in
- Lwt_unix.sleep 10.0;%lwt
+ Eio.Time.sleep (Eio.Stdenv.clock env) 10.0;
(* a1: Check home timeline, which should be empty *)
- let%lwt [] = home_timeline a1 in
+ let [] = home_timeline env a1 in
(* a2: Check home timeline, which should be empty *)
- let%lwt [] = home_timeline a2 in
+ let [] = home_timeline env a2 in
(* a1: Check its notification *)
- let%lwt [ n ] =
- get_notifications a1
- >|= List.filter (fun (n : notification) -> n.typ = "mention")
+ let [ n ] =
+ get_notifications env a1
+ |> List.filter (fun (n : notification) -> n.typ = "mention")
in
assert ((Option.get n.status).uri = uri);
assert (n.account.acct = acct_of_agent ~from:a1 a0);
assert (List.length (Option.get n.status).mentions = 2);
(* a2: Check its notification *)
- let%lwt [ n ] = get_notifications a2 in
+ let [ n ] = get_notifications env a2 in
assert ((Option.get n.status).uri = uri);
assert (n.account.acct = acct_of_agent ~from:a2 a0);
assert (List.length (Option.get n.status).mentions = 2);
- Lwt.return_unit
+ ()
[@@warning "-8"]
let f_waq_mstdn_waq () =
- launch_waq_and_mstdn @@ fun ctxt ->
+ launch_waq_and_mstdn @@ fun env ctxt ->
let a0 = generate_waq_agent ctxt in
let a1 = generate_mstdn_agent ctxt in
let a2 = generate_waq_agent ctxt in
- f a0 a1 a2
+ f env a0 a1 a2
let f_mstdn_waq_waq () =
- launch_waq_and_mstdn @@ fun ctxt ->
+ launch_waq_and_mstdn @@ fun env ctxt ->
let a0 = generate_mstdn_agent ctxt in
let a1 = generate_waq_agent ctxt in
let a2 = generate_waq_agent ctxt in
- f a0 a1 a2
+ f env a0 a1 a2
let f_waq_waq_waq () =
- launch_waq @@ fun ctxt ->
+ launch_waq @@ fun env ctxt ->
let a0 = generate_waq_agent ctxt in
let a1 = generate_waq_agent ctxt in
let a2 = generate_waq_agent ctxt in
- f a0 a1 a2
+ f env a0 a1 a2
let f_mstdn_waq_mstdn () =
- launch_waq_and_mstdn @@ fun ctxt ->
+ launch_waq_and_mstdn @@ fun env ctxt ->
let a0 = generate_mstdn_agent ctxt in
let a1 = generate_waq_agent ctxt in
let a2 = generate_mstdn_agent ctxt in
- f a0 a1 a2
+ f env a0 a1 a2
diff --git a/e2e/src/waq_mstdn_12_summary.ml b/e2e/src/waq_mstdn_12_summary.ml
index f4c9673..179faa1 100644
--- a/e2e/src/waq_mstdn_12_summary.ml
+++ b/e2e/src/waq_mstdn_12_summary.ml
@@ -1,20 +1,20 @@
open Common2
-let f (a0 : agent) (a1 : agent) =
+let f env (a0 : agent) (a1 : agent) =
let spoiler_text = "すぽいらーてきすと" in
(* a0: Post with summary (spoiler_text) *)
- let%lwt { uri; _ } = post a0 ~spoiler_text () in
+ let { uri; _ } = post env a0 ~spoiler_text () in
(* a1: Check the post by lookup *)
- let%lwt _, [ s ], _ = search a1 uri in
+ let _, [ s ], _ = search env a1 uri in
assert (s.spoiler_text = spoiler_text);
- Lwt.return_unit
+ ()
[@@warning "-8"]
let f_mstdn_waq =
- make_waq_and_mstdn_scenario @@ fun waq_token mstdn_token ->
+ make_waq_and_mstdn_scenario @@ fun env waq_token mstdn_token ->
let a0 =
make_agent ~kind:`Mstdn ~token:mstdn_token ~username:"mstdn1"
~domain:mstdn_server_domain
@@ -23,11 +23,11 @@ let f_mstdn_waq =
make_agent ~kind:`Waq ~token:waq_token ~username:"user1"
~domain:waq_server_domain
in
- f a0 a1;%lwt
- Lwt.return_unit
+ f env a0 a1;
+ ()
let f_waq_mstdn =
- make_waq_and_mstdn_scenario @@ fun waq_token mstdn_token ->
+ make_waq_and_mstdn_scenario @@ fun env waq_token mstdn_token ->
let a0 =
make_agent ~kind:`Waq ~token:waq_token ~username:"user1"
~domain:waq_server_domain
@@ -36,12 +36,12 @@ let f_waq_mstdn =
make_agent ~kind:`Mstdn ~token:mstdn_token ~username:"mstdn1"
~domain:mstdn_server_domain
in
- f a0 a1;%lwt
- Lwt.return_unit
+ f env a0 a1;
+ ()
let f_waq_waq =
- make_waq_scenario @@ fun token ->
- let%lwt token2 = fetch_access_token ~username:"user2" in
+ make_waq_scenario @@ fun env token ->
+ let token2 = fetch_access_token env ~username:"user2" in
let a0 =
make_agent ~kind:`Waq ~token ~username:"user1" ~domain:waq_server_domain
in
@@ -49,5 +49,5 @@ let f_waq_waq =
make_agent ~kind:`Waq ~token:token2 ~username:"user2"
~domain:waq_server_domain
in
- f a0 a1;%lwt
- Lwt.return_unit
+ f env a0 a1;
+ ()
diff --git a/e2e/src/waq_mstdn_13_v2_search.ml b/e2e/src/waq_mstdn_13_v2_search.ml
index ecb950a..461c74b 100644
--- a/e2e/src/waq_mstdn_13_v2_search.ml
+++ b/e2e/src/waq_mstdn_13_v2_search.ml
@@ -1,24 +1,24 @@
open Common
let f =
- make_waq_and_mstdn_scenario @@ fun waq_token _mstdn_token ->
+ make_waq_and_mstdn_scenario @@ fun env waq_token _mstdn_token ->
(* Lookup @user1 *)
- (match%lwt search `Waq "@user1" with
+ (match search env `Waq "@user1" with
| [ a ], _, _ ->
assert (a.acct = "user1");
- Lwt.return_unit
- | _ -> assert false);%lwt
+ ()
+ | _ -> assert false);
(* Lookup @mstdn1@mstdn_server_domain without token, which should fail *)
- (match%lwt search `Waq ("@mstdn1@" ^ mstdn_server_domain) with
- | [], _, _ -> Lwt.return_unit
- | _ -> assert false);%lwt
+ (match search env `Waq ("@mstdn1@" ^ mstdn_server_domain) with
+ | [], _, _ -> ()
+ | _ -> assert false);
(* With token, it will succeed *)
- (match%lwt
- search `Waq ~token:waq_token ("@mstdn1@" ^ mstdn_server_domain)
+ (match
+ search env `Waq ~token:waq_token ("@mstdn1@" ^ mstdn_server_domain)
with
- | [ _ ], _, _ -> Lwt.return_unit
- | _ -> assert false);%lwt
+ | [ _ ], _, _ -> ()
+ | _ -> assert false);
- Lwt.return_unit
+ ()
diff --git a/e2e/src/waq_mstdn_14_preview_card.ml b/e2e/src/waq_mstdn_14_preview_card.ml
index 6e2e97c..a0dbc2c 100644
--- a/e2e/src/waq_mstdn_14_preview_card.ml
+++ b/e2e/src/waq_mstdn_14_preview_card.ml
@@ -1,35 +1,35 @@
open Common2
-let get_preview_card_from_a1 (a0 : agent) (a1 : agent) content =
+let get_preview_card_from_a1 env (a0 : agent) (a1 : agent) content =
(* a1: Follow a0 *)
- follow_agent a1 a0;%lwt
+ follow_agent env a1 a0;
(* a0: Post link *)
- let%lwt { uri; _ } = post a0 ~content () in
- Lwt_unix.sleep 2.0;%lwt
+ let { uri; _ } = post env a0 ~content () in
+ Eio.Time.sleep (Eio.Stdenv.clock env) 2.0;
(* a1: Check the post. The post should have been fetched in advance because a1 follows a0. *)
- let%lwt _, [ s ], _ = search a1 uri in
- Lwt.return s
+ let _, [ s ], _ = search env a1 uri in
+ s
[@@warning "-8"]
-let f_case1 (a0 : agent) (a1 : agent) =
+let f_case1 env (a0 : agent) (a1 : agent) =
let url = "https://www.youtube.com/watch?v=OMv_EPMED8Y" in
- let%lwt s = get_preview_card_from_a1 a0 a1 url in
+ let s = get_preview_card_from_a1 env a0 a1 url in
assert (Option.is_some s.card);
let c = Option.get s.card in
assert (c.url = url);
- Lwt.return_unit
+ ()
-let f_case2 (a0 : agent) (a1 : agent) =
+let f_case2 env (a0 : agent) (a1 : agent) =
let content = "@" ^ acct_of_agent ~from:a0 a1 in
- let%lwt s = get_preview_card_from_a1 a0 a1 content in
+ let s = get_preview_card_from_a1 env a0 a1 content in
assert (List.length s.mentions = 1);
assert (Option.is_none s.card);
- Lwt.return_unit
+ ()
let f_mstdn_waq =
- make_waq_and_mstdn_scenario @@ fun waq_token mstdn_token ->
+ make_waq_and_mstdn_scenario @@ fun env waq_token mstdn_token ->
let a0 =
make_agent ~kind:`Mstdn ~token:mstdn_token ~username:"mstdn1"
~domain:mstdn_server_domain
@@ -38,13 +38,13 @@ let f_mstdn_waq =
make_agent ~kind:`Waq ~token:waq_token ~username:"user1"
~domain:waq_server_domain
in
- f_case1 a0 a1;%lwt
- f_case2 a0 a1;%lwt
- Lwt.return_unit
+ f_case1 env a0 a1;
+ f_case2 env a0 a1;
+ ()
let f_waq_waq =
- make_waq_scenario @@ fun token ->
- let%lwt token2 = fetch_access_token ~username:"user2" in
+ make_waq_scenario @@ fun env token ->
+ let token2 = fetch_access_token env ~username:"user2" in
let a0 =
make_agent ~kind:`Waq ~token ~username:"user1" ~domain:waq_server_domain
in
@@ -52,6 +52,6 @@ let f_waq_waq =
make_agent ~kind:`Waq ~token:token2 ~username:"user2"
~domain:waq_server_domain
in
- f_case1 a0 a1;%lwt
- f_case2 a0 a1;%lwt
- Lwt.return_unit
+ f_case1 env a0 a1;
+ f_case2 env a0 a1;
+ ()
diff --git a/e2e/src/waq_mstdn_15_text.ml b/e2e/src/waq_mstdn_15_text.ml
index 97f748c..f20b7f9 100644
--- a/e2e/src/waq_mstdn_15_text.ml
+++ b/e2e/src/waq_mstdn_15_text.ml
@@ -1,24 +1,24 @@
open Common2
-let f (a0 : agent) (a1 : agent) =
+let f env (a0 : agent) (a1 : agent) =
let url = "https://www.youtube.com/watch?v=OMv_EPMED8Y" in
(* a1: Follow a0 *)
- follow_agent a1 a0;%lwt
+ follow_agent env a1 a0;
(* a0: Post link *)
- let%lwt { uri; _ } = post a0 ~content:url () in
- Lwt_unix.sleep 1.0;%lwt
+ let { uri; _ } = post env a0 ~content:url () in
+ Eio.Time.sleep (Eio.Stdenv.clock env) 1.0;
(* a1: Check the post. The post should have been fetched in advance because a1 follows a0. *)
- let%lwt _, [ s ], _ = search a1 uri in
+ let _, [ s ], _ = search env a1 uri in
assert (List.length Soup.(parse (Option.get s.content) $$ "a" |> to_list) = 1);
- Lwt.return_unit
+ ()
[@@warning "-8"]
let f_mstdn_waq =
- make_waq_and_mstdn_scenario @@ fun waq_token mstdn_token ->
+ make_waq_and_mstdn_scenario @@ fun env waq_token mstdn_token ->
let a0 =
make_agent ~kind:`Mstdn ~token:mstdn_token ~username:"mstdn1"
~domain:mstdn_server_domain
@@ -27,11 +27,11 @@ let f_mstdn_waq =
make_agent ~kind:`Waq ~token:waq_token ~username:"user1"
~domain:waq_server_domain
in
- f a0 a1;%lwt
- Lwt.return_unit
+ f env a0 a1;
+ ()
let f_waq_mstdn =
- make_waq_and_mstdn_scenario @@ fun waq_token mstdn_token ->
+ make_waq_and_mstdn_scenario @@ fun env waq_token mstdn_token ->
let a0 =
make_agent ~kind:`Waq ~token:waq_token ~username:"user1"
~domain:waq_server_domain
@@ -40,12 +40,12 @@ let f_waq_mstdn =
make_agent ~kind:`Mstdn ~token:mstdn_token ~username:"mstdn1"
~domain:mstdn_server_domain
in
- f a0 a1;%lwt
- Lwt.return_unit
+ f env a0 a1;
+ ()
let f_waq_waq =
- make_waq_scenario @@ fun token ->
- let%lwt token2 = fetch_access_token ~username:"user2" in
+ make_waq_scenario @@ fun env token ->
+ let token2 = fetch_access_token env ~username:"user2" in
let a0 =
make_agent ~kind:`Waq ~token ~username:"user1" ~domain:waq_server_domain
in
@@ -53,5 +53,5 @@ let f_waq_waq =
make_agent ~kind:`Waq ~token:token2 ~username:"user2"
~domain:waq_server_domain
in
- f a0 a1;%lwt
- Lwt.return_unit
+ f env a0 a1;
+ ()
diff --git a/e2e/src/waq_mstdn_16_cred.ml b/e2e/src/waq_mstdn_16_cred.ml
index f21fe15..97d9c37 100644
--- a/e2e/src/waq_mstdn_16_cred.ml
+++ b/e2e/src/waq_mstdn_16_cred.ml
@@ -2,48 +2,48 @@ open Common2
let strip_html_tags s = Soup.(s |> parse |> texts |> String.concat "")
-let f (a0 : agent) (a1 : agent) =
+let f env (a0 : agent) (a1 : agent) =
(* a0: Follow a1 *)
- follow_agent a0 a1;%lwt
- Lwt_unix.sleep 1.0;%lwt
+ follow_agent env a0 a1;
+ Eio.Time.sleep (Eio.Stdenv.clock env) 1.0;
(* a1: Update display name *)
let modified_display_name = "modified display name" in
- let%lwt a = update_credentials a1 ~display_name:modified_display_name () in
+ let a = update_credentials env a1 ~display_name:modified_display_name () in
assert (a.display_name = modified_display_name);
(* a1: Update credentials *)
assert (not a.bot);
- let%lwt old_avatar_url, old_header_url =
- search a0 (acct_of_agent ~from:a0 a1) >|= fun ([ a ], _, _) ->
+ let old_avatar_url, old_header_url =
+ search env a0 (acct_of_agent ~from:a0 a1) |> fun ([ a ], _, _) ->
(a.avatar, a.header)
in
let modified_note = "modified note" in
let modified_avatar = test_image in
let modified_header = test_image in
let modified_bot = true in
- let%lwt a =
- update_credentials a1 ~note:modified_note ~avatar:modified_avatar
+ let a =
+ update_credentials env a1 ~note:modified_note ~avatar:modified_avatar
~header:modified_header ~bot:modified_bot ()
in
assert (a.display_name = modified_display_name);
assert (strip_html_tags a.note = modified_note);
assert a.bot;
- Lwt_unix.sleep 1.0;%lwt
+ Eio.Time.sleep (Eio.Stdenv.clock env) 1.0;
(* a0: Check a1's info *)
- let%lwt [ a ], _, _ = search a0 (acct_of_agent ~from:a0 a1) in
+ let [ a ], _, _ = search env a0 (acct_of_agent ~from:a0 a1) in
assert (a.display_name = modified_display_name);
assert (strip_html_tags a.note = modified_note);
assert (a.avatar <> old_avatar_url);
assert (a.header <> old_header_url);
assert a.bot;
- Lwt.return_unit
+ ()
[@@warning "-8"]
let f_mstdn_waq =
- make_waq_and_mstdn_scenario @@ fun waq_token mstdn_token ->
+ make_waq_and_mstdn_scenario @@ fun env waq_token mstdn_token ->
let a0 =
make_agent ~kind:`Mstdn ~token:mstdn_token ~username:"mstdn1"
~domain:mstdn_server_domain
@@ -52,11 +52,11 @@ let f_mstdn_waq =
make_agent ~kind:`Waq ~token:waq_token ~username:"user1"
~domain:waq_server_domain
in
- f a0 a1;%lwt
- Lwt.return_unit
+ f env a0 a1;
+ ()
let f_waq_mstdn =
- make_waq_and_mstdn_scenario @@ fun waq_token mstdn_token ->
+ make_waq_and_mstdn_scenario @@ fun env waq_token mstdn_token ->
let a0 =
make_agent ~kind:`Waq ~token:waq_token ~username:"user1"
~domain:waq_server_domain
@@ -65,12 +65,12 @@ let f_waq_mstdn =
make_agent ~kind:`Mstdn ~token:mstdn_token ~username:"mstdn1"
~domain:mstdn_server_domain
in
- f a0 a1;%lwt
- Lwt.return_unit
+ f env a0 a1;
+ ()
let f_waq_waq =
- make_waq_scenario @@ fun token ->
- let%lwt token2 = fetch_access_token ~username:"user2" in
+ make_waq_scenario @@ fun env token ->
+ let token2 = fetch_access_token env ~username:"user2" in
let a0 =
make_agent ~kind:`Waq ~token ~username:"user1" ~domain:waq_server_domain
in
@@ -78,5 +78,5 @@ let f_waq_waq =
make_agent ~kind:`Waq ~token:token2 ~username:"user2"
~domain:waq_server_domain
in
- f a0 a1;%lwt
- Lwt.return_unit
+ f env a0 a1;
+ ()
diff --git a/e2e/src/waq_mstdn_2.ml b/e2e/src/waq_mstdn_2.ml
index 3da378f..f6e7ee7 100644
--- a/e2e/src/waq_mstdn_2.ml
+++ b/e2e/src/waq_mstdn_2.ml
@@ -1,55 +1,55 @@
open Common
let f =
- make_waq_and_mstdn_scenario @@ fun waq_token mstdn_token ->
+ make_waq_and_mstdn_scenario @@ fun env waq_token mstdn_token ->
(* Lookup me from mstdn_server_domain *)
- let%lwt aid, _, _ =
- lookup `Mstdn ~token:mstdn_token ~username:"user1" ~domain:waq_server_domain
- ()
+ let aid, _, _ =
+ lookup env `Mstdn ~token:mstdn_token ~username:"user1"
+ ~domain:waq_server_domain ()
in
(* Follow me from @mstdn1@mstdn_server_domain *)
- follow `Mstdn ~token:mstdn_token aid;%lwt
- Lwt_unix.sleep 1.0;%lwt
+ follow env `Mstdn ~token:mstdn_token aid;
+ Eio.Time.sleep (Eio.Stdenv.clock env) 1.0;
(* Check notifications *)
- (match%lwt get_notifications `Waq ~token:waq_token with
+ (match get_notifications env `Waq ~token:waq_token with
| [ { typ = "follow"; account = a; _ } ] ->
- let%lwt id, _, _ =
- lookup `Waq ~token:waq_token ~username:"mstdn1"
+ let id, _, _ =
+ lookup env `Waq ~token:waq_token ~username:"mstdn1"
~domain:mstdn_server_domain ()
in
assert (a.id = id);
- Lwt.return_unit
- | _ -> assert false);%lwt
+ ()
+ | _ -> assert false);
(* Post by @mstdn1@mstdn_server_domain *)
- let%lwt { uri; _ } = post `Mstdn ~token:mstdn_token () in
- Lwt_unix.sleep 1.0;%lwt
+ let ({ uri; _ } : status) = post env `Mstdn ~token:mstdn_token () in
+ Eio.Time.sleep (Eio.Stdenv.clock env) 1.0;
(* Post by me *)
- let%lwt { uri = uri2; _ } = post `Waq ~token:waq_token () in
- Lwt_unix.sleep 1.0;%lwt
+ let ({ uri = uri2; _ } : status) = post env `Waq ~token:waq_token () in
+ Eio.Time.sleep (Eio.Stdenv.clock env) 1.0;
(* Get home timeline of @mstdn1@mstdn_server_domain and check *)
- (home_timeline `Mstdn ~token:mstdn_token >|= function
+ (home_timeline env `Mstdn ~token:mstdn_token |> function
| [ `Assoc l2; `Assoc l ] ->
(* Check if the timeline is correct *)
assert (uri = (List.assoc "uri" l |> expect_string));
assert (uri2 = (List.assoc "uri" l2 |> expect_string));
()
- | _ -> assert false);%lwt
+ | _ -> assert false);
(* Unfollow me from @mstdn1@mstdn_server_domain *)
- unfollow `Mstdn ~token:mstdn_token aid;%lwt
- Lwt_unix.sleep 1.0;%lwt
+ unfollow env `Mstdn ~token:mstdn_token aid;
+ Eio.Time.sleep (Eio.Stdenv.clock env) 1.0;
(* Get home timeline of @mstdn1@mstdn_server_domain and check again *)
- (home_timeline `Mstdn ~token:mstdn_token >|= function
+ (home_timeline env `Mstdn ~token:mstdn_token |> function
| [ `Assoc l ] ->
(* Check if the timeline is correct *)
assert (uri = (List.assoc "uri" l |> expect_string));
()
- | _ -> assert false);%lwt
+ | _ -> assert false);
- Lwt.return_unit
+ ()
diff --git a/e2e/src/waq_mstdn_3_reply.ml b/e2e/src/waq_mstdn_3_reply.ml
index 734b197..b75be0d 100644
--- a/e2e/src/waq_mstdn_3_reply.ml
+++ b/e2e/src/waq_mstdn_3_reply.ml
@@ -1,34 +1,34 @@
open Common
let f =
- make_waq_and_mstdn_scenario @@ fun waq_token mstdn_token ->
+ make_waq_and_mstdn_scenario @@ fun env waq_token mstdn_token ->
(* Lookup me from mstdn_server_domain *)
- let%lwt aid, _, _ =
- lookup `Mstdn ~token:mstdn_token ~username:"user1" ~domain:waq_server_domain
- ()
+ let aid, _, _ =
+ lookup env `Mstdn ~token:mstdn_token ~username:"user1"
+ ~domain:waq_server_domain ()
in
(* Lookup @mstdn1@mstdn_server_domain *)
- let%lwt mstdn1_id, _username, _acct =
- lookup `Waq ~token:waq_token ~username:"mstdn1" ~domain:mstdn_server_domain
- ()
+ let mstdn1_id, _username, _acct =
+ lookup env `Waq ~token:waq_token ~username:"mstdn1"
+ ~domain:mstdn_server_domain ()
in
(* Follow @mstdn1@mstdn_server_domain *)
- follow `Waq ~token:waq_token mstdn1_id;%lwt
- Lwt_unix.sleep 1.0;%lwt
+ follow env `Waq ~token:waq_token mstdn1_id;
+ Eio.Time.sleep (Eio.Stdenv.clock env) 1.0;
(* Follow me from @mstdn1@mstdn_server_domain *)
- follow `Mstdn ~token:mstdn_token aid;%lwt
- Lwt_unix.sleep 1.0;%lwt
+ follow env `Mstdn ~token:mstdn_token aid;
+ Eio.Time.sleep (Eio.Stdenv.clock env) 1.0;
(* Post by me *)
- let%lwt { uri; _ } = post `Waq ~token:waq_token () in
- Lwt_unix.sleep 1.0;%lwt
+ let ({ uri; _ } : status) = post env `Waq ~token:waq_token () in
+ Eio.Time.sleep (Eio.Stdenv.clock env) 1.0;
(* Get home timeline of @mstdn1@mstdn_server_domain and obtain the status's id *)
- let%lwt id =
- home_timeline `Mstdn ~token:mstdn_token >|= function
+ let id =
+ home_timeline env `Mstdn ~token:mstdn_token |> function
| [ `Assoc l ] ->
(* Check if the timeline is correct *)
assert (uri = (List.assoc "uri" l |> expect_string));
@@ -37,12 +37,14 @@ let f =
in
(* Reply by @mstdn1@mstdn_server_domain *)
- let%lwt { uri; _ } = post `Mstdn ~token:mstdn_token ~in_reply_to_id:id () in
- Lwt_unix.sleep 1.0;%lwt
+ let ({ uri; _ } : status) =
+ post env `Mstdn ~token:mstdn_token ~in_reply_to_id:id ()
+ in
+ Eio.Time.sleep (Eio.Stdenv.clock env) 1.0;
(* Get home timeline of me and obtain the reply's id *)
- let%lwt id =
- home_timeline `Waq ~token:waq_token >|= function
+ let id =
+ home_timeline env `Waq ~token:waq_token |> function
| [ `Assoc l; `Assoc l2 ] ->
assert (uri = (List.assoc "uri" l |> expect_string));
assert (List.assoc "id" l2 = List.assoc "in_reply_to_id" l);
@@ -54,12 +56,14 @@ let f =
in
(* Reply by me *)
- let%lwt { uri; _ } = post `Waq ~token:waq_token ~in_reply_to_id:id () in
- Lwt_unix.sleep 1.0;%lwt
+ let ({ uri; _ } : status) =
+ post env `Waq ~token:waq_token ~in_reply_to_id:id ()
+ in
+ Eio.Time.sleep (Eio.Stdenv.clock env) 1.0;
(* Get home timeline of @mstdn1@mstdn_server_domain and check *)
- let%lwt _ =
- home_timeline `Mstdn ~token:mstdn_token >|= function
+ let _ =
+ home_timeline env `Mstdn ~token:mstdn_token |> function
| [ `Assoc l; `Assoc l2; `Assoc _ ] ->
(* Check if the timeline is correct *)
assert (uri = (List.assoc "uri" l |> expect_string));
@@ -71,4 +75,4 @@ let f =
| _ -> assert false
in
- Lwt.return_unit
+ ()
diff --git a/e2e/src/waq_mstdn_4_reblog.ml b/e2e/src/waq_mstdn_4_reblog.ml
index 648e3fe..03545ea 100644
--- a/e2e/src/waq_mstdn_4_reblog.ml
+++ b/e2e/src/waq_mstdn_4_reblog.ml
@@ -1,28 +1,28 @@
open Common
let f =
- make_waq_and_mstdn_scenario @@ fun waq_token mstdn_token ->
+ make_waq_and_mstdn_scenario @@ fun env waq_token mstdn_token ->
(* Lookup me from mstdn_server_domain *)
- let%lwt aid, _, _ =
- lookup `Mstdn ~token:mstdn_token ~username:"user1" ~domain:waq_server_domain
- ()
+ let aid, _, _ =
+ lookup env `Mstdn ~token:mstdn_token ~username:"user1"
+ ~domain:waq_server_domain ()
in
(* Follow me from @mstdn1@mstdn_server_domain *)
- follow `Mstdn ~token:mstdn_token aid;%lwt
- Lwt_unix.sleep 1.0;%lwt
+ follow env `Mstdn ~token:mstdn_token aid;
+ Eio.Time.sleep (Eio.Stdenv.clock env) 1.0;
(* Post by user2 *)
- let%lwt waq_token' = fetch_access_token ~username:"user2" in
- let%lwt { id; uri; _ } = post `Waq ~token:waq_token' () in
+ let waq_token' = fetch_access_token env ~username:"user2" in
+ let ({ id; uri; _ } : status) = post env `Waq ~token:waq_token' () in
(* Reblog by me (user1) *)
- let%lwt _ = reblog `Waq ~token:waq_token ~id in
- Lwt_unix.sleep 2.0;%lwt
+ let _ = reblog env `Waq ~token:waq_token ~id in
+ Eio.Time.sleep (Eio.Stdenv.clock env) 2.0;
(* Get home timeline of @mstdn1@mstdn_server_domain *)
- let%lwt _ =
- home_timeline `Mstdn ~token:mstdn_token >|= function
+ let _ =
+ home_timeline env `Mstdn ~token:mstdn_token |> function
| [ `Assoc l ] ->
(* Check if the timeline is correct *)
let reblog_uri =
@@ -33,4 +33,4 @@ let f =
| _ -> assert false
in
- Lwt.return_unit
+ ()
diff --git a/e2e/src/waq_mstdn_5_reblog.ml b/e2e/src/waq_mstdn_5_reblog.ml
index 035c6f6..eb4100e 100644
--- a/e2e/src/waq_mstdn_5_reblog.ml
+++ b/e2e/src/waq_mstdn_5_reblog.ml
@@ -1,30 +1,30 @@
open Common
let f =
- make_waq_and_mstdn_scenario @@ fun waq_token mstdn_token ->
+ make_waq_and_mstdn_scenario @@ fun env waq_token mstdn_token ->
(* Lookup @mstdn1@mstdn_server_domain *)
- let%lwt mstdn1_id, _username, _acct =
- lookup `Waq ~token:waq_token ~username:"mstdn1" ~domain:mstdn_server_domain
- ()
+ let mstdn1_id, _username, _acct =
+ lookup env `Waq ~token:waq_token ~username:"mstdn1"
+ ~domain:mstdn_server_domain ()
in
(* Follow @mstdn1@mstdn_server_domain *)
- follow `Waq ~token:waq_token mstdn1_id;%lwt
- Lwt_unix.sleep 1.0;%lwt
+ follow env `Waq ~token:waq_token mstdn1_id;
+ Eio.Time.sleep (Eio.Stdenv.clock env) 1.0;
(* Post by user1 *)
- let%lwt { uri; id = post_id; _ } = post `Waq ~token:waq_token () in
- Lwt_unix.sleep 1.0;%lwt
+ let ({ uri; id = post_id; _ } : status) = post env `Waq ~token:waq_token () in
+ Eio.Time.sleep (Eio.Stdenv.clock env) 1.0;
(* Reblog the post by @mstdn1@locahost:3000 *)
- (match%lwt search `Mstdn ~token:mstdn_token uri with
+ (match search env `Mstdn ~token:mstdn_token uri with
| _, [ status ], _ ->
- reblog `Mstdn ~token:mstdn_token ~id:status.id |> ignore_lwt
- | _ -> assert false);%lwt
- Lwt_unix.sleep 1.0;%lwt
+ reblog env `Mstdn ~token:mstdn_token ~id:status.id |> ignore
+ | _ -> assert false);
+ Eio.Time.sleep (Eio.Stdenv.clock env) 1.0;
(* Check home timeline *)
- (home_timeline `Waq ~token:waq_token >|= function
+ (home_timeline env `Waq ~token:waq_token |> function
| [ `Assoc l1; `Assoc l2 ] ->
(* Check if the timeline is correct *)
assert (uri = (l2 |> List.assoc "uri" |> expect_string));
@@ -33,10 +33,10 @@ let f =
= (l1 |> List.assoc "reblog" |> expect_assoc |> List.assoc "uri"
|> expect_string));
()
- | _ -> assert false);%lwt
+ | _ -> assert false);
(* Check notifications *)
- (get_notifications `Waq ~token:waq_token >|= function
+ (get_notifications env `Waq ~token:waq_token |> function
| [
{
typ = "reblog";
@@ -47,6 +47,6 @@ let f =
] ->
assert (account_id = mstdn1_id);
assert (status_id = post_id)
- | _ -> assert false);%lwt
+ | _ -> assert false);
- Lwt.return_unit
+ ()
diff --git a/e2e/src/waq_mstdn_6_fav.ml b/e2e/src/waq_mstdn_6_fav.ml
index 192f0b8..473dfc1 100644
--- a/e2e/src/waq_mstdn_6_fav.ml
+++ b/e2e/src/waq_mstdn_6_fav.ml
@@ -1,56 +1,58 @@
open Common
let f =
- make_waq_and_mstdn_scenario @@ fun waq_token mstdn_token ->
+ make_waq_and_mstdn_scenario @@ fun env waq_token mstdn_token ->
(* Lookup @mstdn1@mstdn_server_domain *)
- let%lwt mstdn1_id, _username, _acct =
- lookup `Waq ~token:waq_token ~username:"mstdn1" ~domain:mstdn_server_domain
- ()
+ let mstdn1_id, _username, _acct =
+ lookup env `Waq ~token:waq_token ~username:"mstdn1"
+ ~domain:mstdn_server_domain ()
in
(* Follow @mstdn1@mstdn_server_domain *)
- follow `Waq ~token:waq_token mstdn1_id;%lwt
- Lwt_unix.sleep 1.0;%lwt
+ follow env `Waq ~token:waq_token mstdn1_id;
+ Eio.Time.sleep (Eio.Stdenv.clock env) 1.0;
(* Get user1's id on mstdn_server_domain *)
- let%lwt mstdn_user1_id, _, _ =
- lookup `Mstdn ~token:mstdn_token ~username:"user1" ()
+ let mstdn_user1_id, _, _ =
+ lookup env `Mstdn ~token:mstdn_token ~username:"user1" ()
in
(* Post by @mstdn1@mstdn_server_domain *)
- let%lwt { id = mstdn_post_id; _ } = post `Mstdn ~token:mstdn_token () in
- Lwt_unix.sleep 1.0;%lwt
+ let ({ id = mstdn_post_id; _ } : status) =
+ post env `Mstdn ~token:mstdn_token ()
+ in
+ Eio.Time.sleep (Eio.Stdenv.clock env) 1.0;
(* Get id of the post *)
- let%lwt id =
- home_timeline `Waq ~token:waq_token >|= function
+ let id =
+ home_timeline env `Waq ~token:waq_token |> function
| [ `Assoc l ] -> List.assoc "id" l |> expect_string
| _ -> assert false
in
(* Favourite the post by me *)
- let%lwt s = fav `Waq ~token:waq_token ~id in
- Lwt_unix.sleep 1.0;%lwt
+ let s = fav env `Waq ~token:waq_token ~id in
+ Eio.Time.sleep (Eio.Stdenv.clock env) 1.0;
assert s.favourited;
- let%lwt s = get_status `Waq ~token:waq_token id in
+ let s = get_status env `Waq ~token:waq_token id in
assert s.favourited;
(* Check if the post is favourited in mstdn_server_domain *)
- (match%lwt get_favourited_by `Mstdn ~token:mstdn_token ~id:mstdn_post_id with
+ (match get_favourited_by env `Mstdn ~token:mstdn_token ~id:mstdn_post_id with
| [ a ] ->
assert (a.id = mstdn_user1_id);
- Lwt.return_unit
- | _ -> assert false);%lwt
+ ()
+ | _ -> assert false);
(* Unfavourite the post *)
- let%lwt s = unfav `Waq ~token:waq_token ~id in
- Lwt_unix.sleep 1.0;%lwt
+ let s = unfav env `Waq ~token:waq_token ~id in
+ Eio.Time.sleep (Eio.Stdenv.clock env) 1.0;
assert (not s.favourited);
- let%lwt s = get_status `Waq ~token:waq_token id in
+ let s = get_status env `Waq ~token:waq_token id in
assert (not s.favourited);
(* Check if the post is unfavourited *)
- (match%lwt get_favourited_by `Mstdn ~token:mstdn_token ~id:mstdn_post_id with
- | [] -> Lwt.return_unit
- | _ -> assert false);%lwt
+ (match get_favourited_by env `Mstdn ~token:mstdn_token ~id:mstdn_post_id with
+ | [] -> ()
+ | _ -> assert false);
- Lwt.return_unit
+ ()
diff --git a/e2e/src/waq_mstdn_7_fav.ml b/e2e/src/waq_mstdn_7_fav.ml
index 155dcd4..5116455 100644
--- a/e2e/src/waq_mstdn_7_fav.ml
+++ b/e2e/src/waq_mstdn_7_fav.ml
@@ -1,45 +1,47 @@
open Common
let f =
- make_waq_and_mstdn_scenario @@ fun waq_token mstdn_token ->
+ make_waq_and_mstdn_scenario @@ fun env waq_token mstdn_token ->
(* Lookup me from mstdn_server_domain *)
- let%lwt aid, _, _ =
- lookup `Mstdn ~token:mstdn_token ~username:"user1" ~domain:waq_server_domain
- ()
+ let aid, _, _ =
+ lookup env `Mstdn ~token:mstdn_token ~username:"user1"
+ ~domain:waq_server_domain ()
in
(* Lookup @mstdn1@mstdn_server_domain *)
- let%lwt mstdn1_id, _username, _acct =
- lookup `Waq ~token:waq_token ~username:"mstdn1" ~domain:mstdn_server_domain
- ()
+ let mstdn1_id, _username, _acct =
+ lookup env `Waq ~token:waq_token ~username:"mstdn1"
+ ~domain:mstdn_server_domain ()
in
(* Follow me from @mstdn1@mstdn_server_domain *)
- follow `Mstdn ~token:mstdn_token aid;%lwt
- Lwt_unix.sleep 1.0;%lwt
+ follow env `Mstdn ~token:mstdn_token aid;
+ Eio.Time.sleep (Eio.Stdenv.clock env) 1.0;
(* Post by me *)
- let%lwt { id = waq_status_id; _ } = post `Waq ~token:waq_token () in
- Lwt_unix.sleep 1.0;%lwt
+ let ({ id = waq_status_id; _ } : status) =
+ post env `Waq ~token:waq_token ()
+ in
+ Eio.Time.sleep (Eio.Stdenv.clock env) 1.0;
(* Get id of the post *)
- let%lwt mstdn_status_id =
- home_timeline `Mstdn ~token:mstdn_token >|= function
+ let mstdn_status_id =
+ home_timeline env `Mstdn ~token:mstdn_token |> function
| [ `Assoc l ] -> List.assoc "id" l |> expect_string
| _ -> assert false
in
(* Favourite the post by @mstdn1@mstdn_server_domain *)
- let%lwt _ = fav `Mstdn ~token:mstdn_token ~id:mstdn_status_id in
- Lwt_unix.sleep 1.0;%lwt
+ let _ = fav env `Mstdn ~token:mstdn_token ~id:mstdn_status_id in
+ Eio.Time.sleep (Eio.Stdenv.clock env) 1.0;
(* Check if the post is favourited *)
- (match%lwt get_favourited_by `Waq ~token:waq_token ~id:waq_status_id with
+ (match get_favourited_by env `Waq ~token:waq_token ~id:waq_status_id with
| [ a ] ->
assert (a.id = mstdn1_id);
- Lwt.return_unit
- | _ -> assert false);%lwt
+ ()
+ | _ -> assert false);
(* Check notification *)
- (match%lwt get_notifications `Waq ~token:waq_token with
+ (match get_notifications env `Waq ~token:waq_token with
| [
{
typ = "favourite";
@@ -52,16 +54,16 @@ let f =
assert (account_id = mstdn1_id);
assert (status_id = waq_status_id);
assert (account_id' = mstdn1_id);
- Lwt.return_unit
- | _ -> assert false);%lwt
+ ()
+ | _ -> assert false);
(* Unfavourite the post *)
- let%lwt _ = unfav `Mstdn ~token:mstdn_token ~id:mstdn_status_id in
- Lwt_unix.sleep 1.0;%lwt
+ let _ = unfav env `Mstdn ~token:mstdn_token ~id:mstdn_status_id in
+ Eio.Time.sleep (Eio.Stdenv.clock env) 1.0;
(* Check if the post is unfavourited *)
- (match%lwt get_favourited_by `Waq ~token:waq_token ~id:waq_status_id with
- | [] -> Lwt.return_unit
- | _ -> assert false);%lwt
+ (match get_favourited_by env `Waq ~token:waq_token ~id:waq_status_id with
+ | [] -> ()
+ | _ -> assert false);
- Lwt.return_unit
+ ()
diff --git a/e2e/src/waq_mstdn_8_lookup_search.ml b/e2e/src/waq_mstdn_8_lookup_search.ml
index 7b5278a..fd91543 100644
--- a/e2e/src/waq_mstdn_8_lookup_search.ml
+++ b/e2e/src/waq_mstdn_8_lookup_search.ml
@@ -1,21 +1,21 @@
open Common
let f =
- make_waq_and_mstdn_scenario @@ fun waq_token mstdn_token ->
+ make_waq_and_mstdn_scenario @@ fun env waq_token mstdn_token ->
(* Lookup @mstdn1@mstdn_server_domain *)
- let%lwt res1 =
- lookup `Waq ~token:waq_token ~username:"mstdn1" ~domain:mstdn_server_domain
- ()
+ let res1 =
+ lookup env `Waq ~token:waq_token ~username:"mstdn1"
+ ~domain:mstdn_server_domain ()
in
- let%lwt res2 =
- lookup_via_v1_accounts_search `Waq ~token:waq_token ~username:"mstdn1"
+ let res2 =
+ lookup_via_v1_accounts_search env `Waq ~token:waq_token ~username:"mstdn1"
~domain:mstdn_server_domain ()
in
- let%lwt res3 =
- lookup_via_v1_accounts_lookup `Waq ~token:waq_token ~username:"mstdn1"
+ let res3 =
+ lookup_via_v1_accounts_lookup env `Waq ~token:waq_token ~username:"mstdn1"
~domain:mstdn_server_domain ()
in
- let%lwt res4 = search `Waq ~token:waq_token (mstdn "/users/mstdn1") in
+ let res4 = search env `Waq ~token:waq_token (mstdn "/users/mstdn1") in
assert (res1 = res2);
assert (res1 = res3);
assert (
@@ -24,20 +24,20 @@ let f =
| _ -> false);
(* No token should cause an error *)
- expect_exc_lwt (fun () ->
- lookup_via_v1_accounts_search `Waq ~username:"mstdn1"
- ~domain:mstdn_server_domain ());%lwt
+ expect_exc (fun () ->
+ lookup_via_v1_accounts_search env `Waq ~username:"mstdn1"
+ ~domain:mstdn_server_domain ());
(* Lookup me *)
- let%lwt res1 = lookup `Waq ~token:waq_token ~username:"user1" () in
- let%lwt res2 =
- lookup_via_v1_accounts_search `Waq ~token:waq_token ~username:"user1" ()
+ let res1 = lookup env `Waq ~token:waq_token ~username:"user1" () in
+ let res2 =
+ lookup_via_v1_accounts_search env `Waq ~token:waq_token ~username:"user1" ()
in
- let%lwt res3 =
- lookup_via_v1_accounts_lookup `Waq ~token:waq_token ~username:"user1" ()
+ let res3 =
+ lookup_via_v1_accounts_lookup env `Waq ~token:waq_token ~username:"user1" ()
in
- let%lwt res4 =
- search `Waq ~token:waq_token (waq_server_name ^/ "users/user1")
+ let res4 =
+ search env `Waq ~token:waq_token (waq_server_name ^/ "users/user1")
in
assert (res1 = res2);
assert (res1 = res3);
@@ -47,9 +47,9 @@ let f =
| _ -> false);
(* Lookup post of @mstdn1@mstdn_server_domain *)
- let%lwt { uri; _ } = post `Mstdn ~token:mstdn_token () in
- let%lwt res = search `Waq ~token:waq_token uri in
+ let { uri; _ } = post env `Mstdn ~token:mstdn_token () in
+ let res = search env `Waq ~token:waq_token uri in
assert (
match res with _, [ status ], _ when status.uri = uri -> true | _ -> false);
- Lwt.return_unit
+ ()
diff --git a/e2e/src/waq_mstdn_9_delete.ml b/e2e/src/waq_mstdn_9_delete.ml
index 19a2661..721d825 100644
--- a/e2e/src/waq_mstdn_9_delete.ml
+++ b/e2e/src/waq_mstdn_9_delete.ml
@@ -1,82 +1,78 @@
open Common2
-let f (a0 : agent) (a1 : agent) =
+let f env (a0 : agent) (a1 : agent) =
(* a0: Follow a1 *)
- follow_agent a0 a1;%lwt
- Lwt_unix.sleep 1.0;%lwt
+ follow_agent env a0 a1;
+ Eio.Time.sleep (Eio.Stdenv.clock env) 1.0;
(* a1: Post *)
- let%lwt { uri; id = a1_post_id; _ } = post a1 () in
- Lwt_unix.sleep 1.0;%lwt
+ let { uri; id = a1_post_id; _ } = post env a1 () in
+ Eio.Time.sleep (Eio.Stdenv.clock env) 1.0;
(* a0: Get the post id *)
- let%lwt a0_post_id =
- match%lwt search a0 uri with
- | _, [ s ], _ -> Lwt.return s.id
- | _ -> assert false
+ let a0_post_id =
+ match search env a0 uri with _, [ s ], _ -> s.id | _ -> assert false
in
(* a0: Reblog the post *)
- let%lwt a0_reblog_id = reblog a0 ~id:a0_post_id >|= fun r -> r.id in
- Lwt_unix.sleep 1.0;%lwt
+ let a0_reblog_id = reblog env a0 ~id:a0_post_id |> fun r -> r.id in
+ Eio.Time.sleep (Eio.Stdenv.clock env) 1.0;
(* a0: Check the posts *)
- let%lwt _ = get_status a0 a0_post_id in
- let%lwt _ = get_status a0 a0_reblog_id in
+ let _ = get_status env a0 a0_post_id in
+ let _ = get_status env a0 a0_reblog_id in
(* a1: Delete the post *)
- delete_status a1 a1_post_id |> ignore_lwt;%lwt
- Lwt_unix.sleep 2.0;%lwt
+ delete_status env a1 a1_post_id |> ignore;
+ Eio.Time.sleep (Eio.Stdenv.clock env) 2.0;
(* a0: Check the posts *)
- expect_no_status a0 a0_post_id;%lwt
- expect_no_status a0 a0_reblog_id;%lwt
+ expect_no_status env a0 a0_post_id;
+ expect_no_status env a0 a0_reblog_id;
(***************************)
(* a0: Follow a1 *)
- follow_agent a1 a0;%lwt
+ follow_agent env a1 a0;
(* a1: Post *)
- let%lwt { uri; id = post_id; _ } = post a1 () in
- Lwt_unix.sleep 1.0;%lwt
+ let { uri; id = post_id; _ } = post env a1 () in
+ Eio.Time.sleep (Eio.Stdenv.clock env) 1.0;
(* a0: Get the post id *)
- let%lwt a0_post_id =
- match%lwt search a0 uri with
- | _, [ s ], _ -> Lwt.return s.id
- | _ -> assert false
+ let a0_post_id =
+ match search env a0 uri with _, [ s ], _ -> s.id | _ -> assert false
in
(* a0: Reblog the post *)
- let%lwt a0_reblog_id = reblog a0 ~id:a0_post_id >|= fun r -> r.id in
- Lwt_unix.sleep 1.0;%lwt
+ let a0_reblog_id = reblog env a0 ~id:a0_post_id |> fun r -> r.id in
+ Eio.Time.sleep (Eio.Stdenv.clock env) 1.0;
(* a0: Check the posts *)
- let%lwt _ = get_status a0 a0_post_id in
- let%lwt _ = get_status a0 a0_reblog_id in
+ let _ = get_status env a0 a0_post_id in
+ let _ = get_status env a0 a0_reblog_id in
(* a1: Check if a0 reblogged a0_post_id *)
- let%lwt { reblogs_count; _ } = get_status a1 post_id in
+ let { reblogs_count; _ } = get_status env a1 post_id in
assert (reblogs_count = 1);
(* a0: Unreblog the post *)
- unreblog a0 ~id:a0_post_id |> ignore_lwt;%lwt
- Lwt_unix.sleep 1.0;%lwt
+ unreblog env a0 ~id:a0_post_id |> ignore;
+ Eio.Time.sleep (Eio.Stdenv.clock env) 1.0;
(* a0: Check the posts *)
- let%lwt _ = get_status a0 a0_post_id in
- expect_no_status a0 a0_reblog_id;%lwt
+ let _ = get_status env a0 a0_post_id in
+ expect_no_status env a0 a0_reblog_id;
(*
(* a1: Check if a0 unreblogged a0_post_id *)
- let%lwt { reblogs_count; _ } = get_status a1 post_id in
+ let { reblogs_count; _ } = get_status a1 post_id in
assert (reblogs_count = 0);
*)
- Lwt.return_unit
+ ()
let f_waq_mstdn =
- make_waq_and_mstdn_scenario @@ fun waq_token mstdn_token ->
+ make_waq_and_mstdn_scenario @@ fun env waq_token mstdn_token ->
let a0 =
make_agent ~kind:`Waq ~token:waq_token ~username:"user1"
~domain:waq_server_domain
@@ -85,11 +81,11 @@ let f_waq_mstdn =
make_agent ~kind:`Mstdn ~token:mstdn_token ~username:"mstdn1"
~domain:mstdn_server_domain
in
- f a0 a1;%lwt
- Lwt.return_unit
+ f env a0 a1;
+ ()
let f_mstdn_waq =
- make_waq_and_mstdn_scenario @@ fun waq_token mstdn_token ->
+ make_waq_and_mstdn_scenario @@ fun env waq_token mstdn_token ->
let a0 =
make_agent ~kind:`Mstdn ~token:mstdn_token ~username:"mstdn1"
~domain:mstdn_server_domain
@@ -98,5 +94,5 @@ let f_mstdn_waq =
make_agent ~kind:`Waq ~token:waq_token ~username:"user1"
~domain:waq_server_domain
in
- f a0 a1;%lwt
- Lwt.return_unit
+ f env a0 a1;
+ ()
diff --git a/lib/activity.ml b/lib/activity.ml
index fdf9295..b890869 100644
--- a/lib/activity.ml
+++ b/lib/activity.ml
@@ -1,6 +1,16 @@
-open Lwt.Infix
open Util
-module Uri = Httpq.Uri
+
+module Uri = struct
+ include Uri
+
+ let http_host (u : t) =
+ let host = Uri.host u |> Option.get in
+ match Uri.port u with
+ | None -> host
+ | Some port -> host ^ ":" ^ string_of_int port
+
+ let domain (u : t) = http_host u
+end
let is_my_domain (u : string) =
u |> Uri.of_string |> Uri.domain |> Config.is_my_domain
@@ -608,7 +618,7 @@ let rec to_yojson ?(context = Some "https://www.w3.org/ns/activitystreams") v =
let sign_spec_of_account (src : Db.Account.t) =
let priv_key =
- src#private_key |> Option.get |> Httpq.Signature.decode_private_key
+ src#private_key |> Option.get |> Yume.Signature.decode_private_key
in
let key_id = src#uri ^ "#main-key" in
let signed_headers =
@@ -616,12 +626,10 @@ let sign_spec_of_account (src : Db.Account.t) =
in
Some (priv_key, key_id, signed_headers)
-let post_activity_json ~body ~sign ~url =
+let post_activity_json env ~body ~sign ~url =
let meth = `POST in
let headers = [ (`Content_type, "application/activity+json") ] in
- let%lwt res = Throttle_fetch.f ~meth ~headers ~body ~sign url in
- Lwt.return
- &
+ let res = Throttle_fetch.f env ~meth ~headers ~body ~sign url in
match res with
| Ok (status, _, _body)
when Cohttp.Code.(status |> code_of_status |> is_success) ->
@@ -629,27 +637,27 @@ let post_activity_json ~body ~sign ~url =
| _ -> failwith "Failed to post activity json"
(* Get activity+json from the Internet *)
-let fetch_activity ~uri =
+let fetch_activity env ~uri =
match uri with
| "https://www.w3.org/ns/activitystreams" -> failwith "Not valid activity URI"
| _ ->
- Throttle_fetch.f_exn
+ Throttle_fetch.f_exn env
~headers:
[
(`Accept, "application/activity+json");
(`Content_type, "text/html" (* dummy *));
]
uri
- >|= Yojson.Safe.from_string
+ |> Yojson.Safe.from_string
(* Send GET /.well-known/webfinger *)
-let get_webfinger ~scheme ~domain ~username =
+let get_webfinger env ~scheme ~domain ~username =
(* FIXME: Check /.well-known/host-meta if necessary *)
- let%lwt body =
- Throttle_fetch.f_exn @@ scheme ^ ":/" ^/ domain
+ let body =
+ Throttle_fetch.f_exn env @@ scheme ^ ":/" ^/ domain
^/ ".well-known/webfinger?resource=acct:" ^ username ^ "@" ^ domain
in
- body |> Yojson.Safe.from_string |> webfinger_of_yojson |> Lwt.return
+ body |> Yojson.Safe.from_string |> webfinger_of_yojson
let model_account_of_person ?original (r : ap_person) : Model.Account.t =
let domain = Uri.(r.id |> of_string |> domain) |> Option.some in
@@ -682,28 +690,27 @@ let model_account_of_person ?original (r : ap_person) : Model.Account.t =
a#set_actor_type (Some (if r.is_service then `Service else `Person));
a
-let fetch_person by =
+let fetch_person env by =
let get_uri = function
| `DomainUser (domain, username) | `UserDomain (username, domain) ->
- get_webfinger ~scheme:"https" ~domain ~username >|= fun webfinger ->
+ let webfinger = get_webfinger env ~scheme:"https" ~domain ~username in
webfinger.links
|> List.find_map @@ fun l ->
let l = webfinger_link_of_yojson l in
if l.rel = "self" then Some l.href else None
| `Uri uri ->
- Uri.(with_fragment (of_string uri) None |> to_string)
- |> Option.some |> Lwt.return
+ Uri.(with_fragment (of_string uri) None |> to_string) |> Option.some
in
- match%lwt get_uri by with
+ match get_uri by with
| None -> failwith "Couldn't find person's uri"
| Some uri -> (
- fetch_activity ~uri >|= of_yojson >|= get_person >|= function
+ fetch_activity env ~uri |> of_yojson |> get_person |> function
| None -> failwith "Couldn't parse the activity Person"
| Some person -> person)
-let search_account ?(resolve = true) by : Model.Account.t Lwt.t =
+let search_account env ?(resolve = true) by : Model.Account.t =
let make_new_account by =
- let%lwt a = fetch_person by >|= model_account_of_person in
+ let a = fetch_person env by |> model_account_of_person in
Db.(e @@ Account.save_one a)
in
match by with
@@ -713,37 +720,33 @@ let search_account ?(resolve = true) by : Model.Account.t Lwt.t =
| Some s when s <> Config.server_name () -> Some s
| _ -> None
in
- match%lwt Db.(e @@ Account.get_one ~domain ~username) with
- | acc -> Lwt.return acc
- | exception Sqlx.Error.NoRowFound
- when domain = None (* Local *) || not resolve ->
+ try Db.(e @@ Account.get_one ~domain ~username) with
+ | Sqlx.Error.NoRowFound when domain = None (* Local *) || not resolve ->
failwith "Couldn't find the account"
- | exception Sqlx.Error.NoRowFound ->
+ | Sqlx.Error.NoRowFound ->
make_new_account (`DomainUser (Option.get domain, username)))
| `Uri uri -> (
let uri = Uri.(with_fragment (of_string uri) None |> to_string) in
- match%lwt Db.(e @@ Account.get_one ~uri) with
- | acct -> Lwt.return acct
- | exception Sqlx.Error.NoRowFound when not resolve ->
+ try Db.(e @@ Account.get_one ~uri) with
+ | Sqlx.Error.NoRowFound when not resolve ->
failwith "Couldn't find the account"
- | exception Sqlx.Error.NoRowFound -> make_new_account (`Uri uri))
+ | Sqlx.Error.NoRowFound -> make_new_account (`Uri uri))
-let search_account_opt ?resolve by =
- try%lwt search_account ?resolve by >>= Lwt.return_some
- with _ -> Lwt.return_none
+let search_account_opt env ?resolve by =
+ try Some (search_account env ?resolve by) with _ -> None
-let verify_activity_json req =
- let%lwt body = Httpq.Server.body req in
- let signature = Httpq.Server.header `Signature req in
- let headers = Httpq.Server.headers req in
- let path = Httpq.Server.path req in
- let meth = Httpq.Server.meth req in
+let verify_activity_json env req =
+ let body = Yume.Server.body req in
+ let signature = Yume.Server.header `Signature req in
+ let headers = Yume.Server.headers req in
+ let path = Yume.Server.path req in
+ let meth = Yume.Server.meth req in
- let open Httpq.Signature in
+ let open Yume.Signature in
let { key_id; algorithm; headers = signed_headers; signature } =
parse_signature_header signature
in
- search_account_opt (`Uri key_id) >|= function
+ match search_account_opt env (`Uri key_id) with
| None -> (body, Error `AccountNotFound)
| Some acct when Model.Account.is_local acct -> (body, Error `AccountIsLocal)
| Some acct -> (
@@ -755,7 +758,7 @@ let verify_activity_json req =
| Error e ->
Logq.err (fun m ->
m "verify_activity_json failed: [%s]"
- (headers |> Httpq.Headers.to_list
+ (headers |> Yume.Headers.to_list
|> List.map (fun (k, v) -> k ^ "=" ^ v)
|> String.concat ", "));
(body, Error (`VerifFailure e))
@@ -810,9 +813,9 @@ let serialize_status (s : Model.Status.t) (self : Model.Account.t) : ap_note =
url = s#url;
}
-let note_of_status (s : Db.Status.t) : ap_note Lwt.t =
- let%lwt self = Db.(e Account.(get_one ~id:s#account_id)) in
- let%lwt s =
+let note_of_status (s : Db.Status.t) : ap_note =
+ let self = Db.(e Account.(get_one ~id:s#account_id)) in
+ let s =
Db.(
e
Status.(
@@ -825,23 +828,26 @@ let note_of_status (s : Db.Status.t) : ap_note Lwt.t =
`account [];
]))
in
- serialize_status s self |> Lwt.return
+ serialize_status s self
-let rec status_of_note' (note : ap_note) : Db.Status.t Lwt.t =
+let rec status_of_note' env (note : ap_note) : Db.Status.t =
let published, _, _ = Ptime.of_rfc3339 note.published |> Result.get_ok in
- let%lwt in_reply_to_id =
- let uri_to_status_id uri = fetch_status ~uri >|= fun s -> Some s#id in
+ let in_reply_to_id =
+ let uri_to_status_id uri =
+ let s = fetch_status env ~uri in
+ Some s#id
+ in
match note.in_reply_to with
| `String uri -> uri_to_status_id uri
| `Assoc l -> (
match l |> List.assoc_opt "id" with
| Some (`String uri) -> uri_to_status_id uri
- | _ -> Lwt.return_none)
- | _ -> Lwt.return_none
+ | _ -> None)
+ | _ -> None
in
- let%lwt attributedTo = search_account (`Uri note.attributed_to) in
+ let attributedTo = search_account env (`Uri note.attributed_to) in
- let%lwt status =
+ let status =
Db.(
e
Status.(
@@ -854,14 +860,15 @@ let rec status_of_note' (note : ap_note) : Db.Status.t Lwt.t =
(* Handle attachments *)
note.attachment
|> List.filter_map get_document
- |> Lwt_list.iter_s (fun (d : ap_document) ->
- let%lwt blurhash =
+ |> List.iter (fun (d : ap_document) ->
+ let blurhash =
match (d.blurhash, d.url) with
- | Some h, _ -> Lwt.return h
- | None, "" -> Lwt.return Image.dummy_blurhash
+ | Some h, _ -> h
+ | None, "" -> Image.dummy_blurhash
| None, url ->
- Throttle_fetch.http_get url >>= Image.inspect
- >|= fun (_, _, h) -> h
+ let body = Throttle_fetch.http_get env url in
+ let _, _, h = Lwt_eio.run_lwt (fun () -> Image.inspect body) in
+ h
in
Db.(
e
@@ -869,28 +876,28 @@ let rec status_of_note' (note : ap_note) : Db.Status.t Lwt.t =
make ~type_:0 ~remote_url:d.url ~account_id:status#account_id
~status_id:status#id ~blurhash ()
|> save_one))
- |> ignore_lwt);%lwt
+ |> ignore);
(* Handle mentions *)
- (note.cc @ note.to_
- |> Lwt_list.filter_map_p (fun uri -> search_account_opt (`Uri uri))
- >>= Lwt_list.iter_p @@ fun acct ->
- let m =
- Model.Mention.(make ~account_id:acct#id ~status_id:status#id ())
- in
- Db.(e @@ Mention.(save_one m)) |> ignore_lwt);%lwt
+ (* FIXME: can be processed in parallel *)
+ note.cc @ note.to_
+ |> List.filter_map (fun uri -> search_account_opt env (`Uri uri))
+ |> List.iter (fun acct ->
+ let m =
+ Model.Mention.(make ~account_id:acct#id ~status_id:status#id ())
+ in
+ Db.(e @@ Mention.(save_one m)) |> ignore);
- Lwt.return status
+ status
-and status_of_note (note : ap_note) : Db.Status.t Lwt.t =
- match%lwt Db.(e Status.(get_one ~uri:note.id)) with
- | s -> Lwt.return s
- | exception Sqlx.Error.NoRowFound -> status_of_note' note
+and status_of_note env (note : ap_note) : Db.Status.t =
+ try Db.(e Status.(get_one ~uri:note.id))
+ with Sqlx.Error.NoRowFound -> status_of_note' env note
-and status_of_announce' (ann : ap_announce) : Db.Status.t Lwt.t =
+and status_of_announce' env (ann : ap_announce) : Db.Status.t =
let published, _, _ = Ptime.of_rfc3339 ann.published |> Result.get_ok in
- let%lwt reblogee = fetch_status ~uri:ann.obj in
- let%lwt account = search_account (`Uri ann.actor) in
+ let reblogee = fetch_status env ~uri:ann.obj in
+ let account = search_account env (`Uri ann.actor) in
Db.(
e
Status.(
@@ -898,23 +905,21 @@ and status_of_announce' (ann : ap_announce) : Db.Status.t Lwt.t =
~account_id:account#id ~reblog_of_id:reblogee#id ~spoiler_text:"" ()
|> save_one))
-and status_of_announce (ann : ap_announce) : Db.Status.t Lwt.t =
- match%lwt Db.(e Status.(get_one ~uri:ann.id)) with
- | s -> Lwt.return s
- | exception Sqlx.Error.NoRowFound -> status_of_announce' ann
-
-and fetch_status ~uri =
- match%lwt Db.(e Status.(get_one ~uri)) with
- | s -> Lwt.return s
- | exception Sqlx.Error.NoRowFound -> (
- match%lwt fetch_activity ~uri >|= of_yojson with
- | Note note -> status_of_note note
- | Announce ann -> status_of_announce ann
- | _ -> failwith "fetch_status failed: fetched activity is invalid")
-
-let create_note_of_status (s : Db.Status.t) : ap_create Lwt.t =
- let%lwt self = Db.(e Account.(get_one ~id:s#account_id)) in
- note_of_status s >|= fun note ->
+and status_of_announce env (ann : ap_announce) : Db.Status.t =
+ try Db.(e Status.(get_one ~uri:ann.id))
+ with Sqlx.Error.NoRowFound -> status_of_announce' env ann
+
+and fetch_status env ~uri =
+ try Db.(e Status.(get_one ~uri))
+ with Sqlx.Error.NoRowFound -> (
+ match fetch_activity env ~uri |> of_yojson with
+ | Note note -> status_of_note env note
+ | Announce ann -> status_of_announce env ann
+ | _ -> failwith "fetch_status failed: fetched activity is invalid")
+
+let create_note_of_status (s : Db.Status.t) : ap_create =
+ let self = Db.(e Account.(get_one ~id:s#account_id)) in
+ let note = note_of_status s in
{
id = note.id ^/ "activity";
actor = `String self#uri;
@@ -924,14 +929,13 @@ let create_note_of_status (s : Db.Status.t) : ap_create Lwt.t =
obj = Note note;
}
-let announce_of_status ?(deleted = false) (s : Db.Status.t) : ap_announce Lwt.t
- =
- let%lwt reblog =
+let announce_of_status ?(deleted = false) (s : Db.Status.t) : ap_announce =
+ let reblog =
let id = Option.get s#reblog_of_id in
Db.(if deleted then e Status.(get_one' ~id) else e Status.(get_one ~id))
in
- let%lwt reblog_acct = Db.(e Account.(get_one ~id:reblog#account_id)) in
- let%lwt self = Db.(e Account.(get_one ~id:s#account_id)) in
+ let reblog_acct = Db.(e Account.(get_one ~id:reblog#account_id)) in
+ let self = Db.(e Account.(get_one ~id:s#account_id)) in
let id = s#uri ^/ "activity" in
let actor = self#uri in
@@ -940,11 +944,11 @@ let announce_of_status ?(deleted = false) (s : Db.Status.t) : ap_announce Lwt.t
let cc = [ reblog_acct#uri; self#followers_url ] in
let obj = reblog#uri in
- make_announce ~id ~actor ~published ~to_ ~cc ~obj |> Lwt.return
+ make_announce ~id ~actor ~published ~to_ ~cc ~obj
-let like_of_favourite (f : Db.Favourite.t) : ap_like Lwt.t =
- let%lwt acct = Db.(e Account.(get_one ~id:f#account_id)) in
- let%lwt status = Db.(e Status.(get_one ~id:f#status_id)) in
+let like_of_favourite (f : Db.Favourite.t) : ap_like =
+ let acct = Db.(e Account.(get_one ~id:f#account_id)) in
+ let status = Db.(e Status.(get_one ~id:f#status_id)) in
let id =
acct#uri ^ "#likes/" ^ (f#id |> Model.Favourite.ID.to_int |> string_of_int)
@@ -952,27 +956,24 @@ let like_of_favourite (f : Db.Favourite.t) : ap_like Lwt.t =
let actor = acct#uri in
let obj = status#uri in
- make_like ~id ~actor ~obj |> Lwt.return
+ make_like ~id ~actor ~obj
let favourite_of_like ?(must_already_exist = false) (l : ap_like) :
- Db.Favourite.t Lwt.t =
- let%lwt acct = Db.(e Account.(get_one ~uri:l.actor)) in
- let%lwt status = Db.(e Status.(get_one ~uri:l.obj)) in
+ Db.Favourite.t =
+ let acct = Db.(e Account.(get_one ~uri:l.actor)) in
+ let status = Db.(e Status.(get_one ~uri:l.obj)) in
let now = Ptime.now () in
- match%lwt
- Db.(e Favourite.(get_one ~account_id:acct#id ~status_id:status#id))
- with
- | fav -> Lwt.return fav
- | exception Sqlx.Error.NoRowFound ->
- if must_already_exist then
- failwith "favourite_of_like: must_already_exist failed"
- else
- Db.(
- e
- Favourite.(
- make ~created_at:now ~updated_at:now ~account_id:acct#id
- ~status_id:status#id ()
- |> save_one))
+ try Db.(e Favourite.(get_one ~account_id:acct#id ~status_id:status#id))
+ with Sqlx.Error.NoRowFound ->
+ if must_already_exist then
+ failwith "favourite_of_like: must_already_exist failed"
+ else
+ Db.(
+ e
+ Favourite.(
+ make ~created_at:now ~updated_at:now ~account_id:acct#id
+ ~status_id:status#id ()
+ |> save_one))
let to_undo ~actor =
let actor = `String actor in
diff --git a/lib/controller/api_v1/accounts/follow.ml b/lib/controller/api_v1/accounts/follow.ml
index 5ce787c..c0f7848 100644
--- a/lib/controller/api_v1/accounts/follow.ml
+++ b/lib/controller/api_v1/accounts/follow.ml
@@ -2,7 +2,7 @@ open Entity
open Helper
open Util
-let request_follow ~uri (self : Db.Account.t) (acc : Db.Account.t) =
+let request_follow env ~uri (self : Db.Account.t) (acc : Db.Account.t) =
(* FIXME: Assume acc is a remote account *)
assert (acc#domain <> None);
@@ -11,21 +11,19 @@ let request_follow ~uri (self : Db.Account.t) (acc : Db.Account.t) =
e
@@ FollowRequest.(
make ~account_id:self#id ~target_account_id:acc#id ~uri () |> save_one))
- |> ignore_lwt;%lwt
+ |> ignore;
(* Post activity *)
let open Activity in
let activity = make_follow ~id:uri ~actor:self#uri ~obj:acc#uri |> follow in
- Worker.Delivery.kick ~activity ~src:self ~url:acc#inbox_url;%lwt
+ Worker.Delivery.kick env ~activity ~src:self ~url:acc#inbox_url
- Lwt.return_unit
-
-let direct_follow ~uri (self : Db.Account.t) (acc : Db.Account.t) =
+let direct_follow env ~uri (self : Db.Account.t) (acc : Db.Account.t) =
(* Assume acc is a local account *)
assert (acc#domain = None);
(* Insert follow *)
- let%lwt f =
+ let f =
Db.(
e
@@ Follow.(
@@ -34,54 +32,49 @@ let direct_follow ~uri (self : Db.Account.t) (acc : Db.Account.t) =
in
(* Notify *)
- Worker.Local_notify.kick
+ Worker.Local_notify.kick env
~activity_id:(Model.Follow.ID.to_int f#id)
- ~activity_type:`Follow ~dst:acc ~src:self ~typ:`follow;%lwt
-
- Lwt.return_unit
+ ~activity_type:`Follow ~dst:acc ~src:self ~typ:`follow
-let follow_not_possible ~(src : Db.Account.t) ~(dst : Db.Account.t) : bool Lwt.t
- =
- Lwt.return (src#id = dst#id)
+let follow_not_possible ~(src : Db.Account.t) ~(dst : Db.Account.t) : bool =
+ src#id = dst#id
-let already_followed ~(src : Db.Account.t) ~(dst : Db.Account.t) : bool Lwt.t =
- match%lwt
+let already_followed ~(src : Db.Account.t) ~(dst : Db.Account.t) : bool =
+ match
Db.(e @@ Follow.get_one ~account_id:src#id ~target_account_id:dst#id)
with
- | _ -> Lwt.return_true
- | exception Sqlx.Error.NoRowFound -> Lwt.return_false
+ | _ -> true
+ | exception Sqlx.Error.NoRowFound -> false
-let already_follow_requested ~(src : Db.Account.t) ~(dst : Db.Account.t) :
- bool Lwt.t =
- match%lwt
+let already_follow_requested ~(src : Db.Account.t) ~(dst : Db.Account.t) : bool
+ =
+ match
Db.(e @@ FollowRequest.get_one ~account_id:src#id ~target_account_id:dst#id)
with
- | _ -> Lwt.return_true
- | exception Sqlx.Error.NoRowFound -> Lwt.return_false
+ | _ -> true
+ | exception Sqlx.Error.NoRowFound -> false
-let service ~(src : Db.Account.t) ~(dst : Db.Account.t) : unit Lwt.t =
- if%lwt follow_not_possible ~src ~dst then
- Httpq.Server.raise_error_response `Forbidden
+let service env ~(src : Db.Account.t) ~(dst : Db.Account.t) : unit =
+ if follow_not_possible ~src ~dst then
+ Yume.Server.raise_error_response `Forbidden
+ else if already_followed ~src ~dst then ()
+ else if already_follow_requested ~src ~dst then ()
else
- if%lwt already_followed ~src ~dst then Lwt.return_unit
- else
- if%lwt already_follow_requested ~src ~dst then Lwt.return_unit
- else
- let uri = src#uri ^/ Uuidm.(v `V4 |> to_string) in
- match dst#domain with
- | None (* local *) -> direct_follow ~uri src dst
- | Some _ (* remote *) -> request_follow ~uri src dst
+ let uri = src#uri ^/ Uuidm.(v `V4 |> to_string) in
+ match dst#domain with
+ | None (* local *) -> direct_follow env ~uri src dst
+ | Some _ (* remote *) -> request_follow env ~uri src dst
(* Recv POST /api/v1/accounts/:id/follow *)
-let post req =
- let%lwt self = authenticate_account req in
- let acct_id = req |> Httpq.Server.param ":id" |> string_to_account_id in
+let post env req =
+ let self = authenticate_account req in
+ let acct_id = req |> Yume.Server.param ":id" |> string_to_account_id in
- let%lwt acct = Db.e (Model.Account.get_one ~id:acct_id) in
- service ~src:self ~dst:acct;%lwt
+ let acct = Db.e (Model.Account.get_one ~id:acct_id) in
+ service env ~src:self ~dst:acct;
(* Return the result to the client *)
- let%lwt rel = make_relationship_from_model self acct in
+ let rel = make_relationship_from_model self acct in
(* Pretend the follow succeeded *)
let rel = { rel with following = true } in
rel |> yojson_of_relationship |> respond_yojson
diff --git a/lib/controller/api_v1/accounts/following.ml b/lib/controller/api_v1/accounts/following.ml
index d8af1aa..013b317 100644
--- a/lib/controller/api_v1/accounts/following.ml
+++ b/lib/controller/api_v1/accounts/following.ml
@@ -1,4 +1,3 @@
-open Lwt.Infix
open Helper
type params = {
@@ -12,37 +11,35 @@ type params = {
let string_to_follow_id s = s |> int_of_string |> Model.Follow.ID.of_int
let parse_req req =
- let open Httpq.Server in
- let%lwt self_id =
- may_authenticate_account req >|= fun a -> a |> Option.map (fun a -> a#id)
+ let open Yume.Server in
+ let self_id =
+ may_authenticate_account req |> fun a -> a |> Option.map (fun a -> a#id)
in
let id = req |> param ":id" |> int_of_string |> Model.Account.ID.of_int in
- let%lwt limit = req |> query ~default:"40" "limit" >|= int_of_string in
+ let limit = req |> query ~default:"40" "limit" |> int_of_string in
let limit = min limit 80 in
- let%lwt max_id =
- req |> query_opt "max_id" >|= Option.map string_to_follow_id
+ let max_id = req |> query_opt "max_id" |> Option.map string_to_follow_id in
+ let since_id =
+ req |> query_opt "since_id" |> Option.map string_to_follow_id
in
- let%lwt since_id =
- req |> query_opt "since_id" >|= Option.map string_to_follow_id
- in
- Lwt.return { id; self_id; max_id; since_id; limit }
+ { id; self_id; max_id; since_id; limit }
let respond_account_list accts =
accts
|> List.map (fun (a : Db.Account.t) -> a#id)
|> Entity.load_accounts_from_db
- >|= List.map Entity.yojson_of_account
- >|= (fun l -> `List l)
- >>= respond_yojson
+ |> List.map Entity.yojson_of_account
+ |> (fun l -> `List l)
+ |> respond_yojson
-let get_following req =
+let get_following _ req =
(* FIXME: Return Link header for pagination *)
- let%lwt { id; self_id; max_id; since_id; limit } = parse_req req in
+ let { id; self_id; max_id; since_id; limit } = parse_req req in
Db.(e @@ get_following ~id ~self_id ~max_id ~since_id ~limit)
- >>= respond_account_list
+ |> respond_account_list
-let get_followers req =
+let get_followers _ req =
(* FIXME: Return Link header for pagination *)
- let%lwt { id; self_id; max_id; since_id; limit } = parse_req req in
+ let { id; self_id; max_id; since_id; limit } = parse_req req in
Db.(e @@ get_followers ~id ~self_id ~max_id ~since_id ~limit)
- >>= respond_account_list
+ |> respond_account_list
diff --git a/lib/controller/api_v1/accounts/lookup.ml b/lib/controller/api_v1/accounts/lookup.ml
index ffad2d2..3e9f416 100644
--- a/lib/controller/api_v1/accounts/lookup.ml
+++ b/lib/controller/api_v1/accounts/lookup.ml
@@ -1,15 +1,12 @@
open Entity
open Helper
-open Lwt.Infix
-let parse_req req = req |> Httpq.Server.query "acct" >|= parse_webfinger_address
+let parse_req req = req |> Yume.Server.query "acct" |> parse_webfinger_address
-let get req =
- let%lwt username, domain = parse_req req in
- let%lwt a =
- match%lwt Db.e (Model.Account.get_one ~domain ~username) with
- | a -> Lwt.return a
- | exception Sqlx.Error.NoRowFound ->
- Httpq.Server.raise_error_response `Not_found
+let get _ req =
+ let username, domain = parse_req req in
+ let a =
+ try Db.e (Model.Account.get_one ~domain ~username)
+ with Sqlx.Error.NoRowFound -> Yume.Server.raise_error_response `Not_found
in
- make_account_from_model a >|= yojson_of_account >>= respond_yojson
+ make_account_from_model a |> yojson_of_account |> respond_yojson
diff --git a/lib/controller/api_v1/accounts/relationships.ml b/lib/controller/api_v1/accounts/relationships.ml
index ab0d0ae..ff20199 100644
--- a/lib/controller/api_v1/accounts/relationships.ml
+++ b/lib/controller/api_v1/accounts/relationships.ml
@@ -1,18 +1,17 @@
open Entity
-open Lwt.Infix
open Helper
-let get req =
- let%lwt self = authenticate_account req in
- let%lwt account_ids =
+let get _ req =
+ let self = authenticate_account req in
+ let account_ids =
req
- |> Httpq.Server.query_many "id"
- >|= List.map (fun s -> s |> int_of_string |> Model.Account.ID.of_int)
+ |> Yume.Server.query_many "id"
+ |> List.map (fun s -> s |> int_of_string |> Model.Account.ID.of_int)
in
account_ids
- |> Lwt_list.map_p (fun account_id ->
+ |> List.map (fun account_id ->
Db.e (Model.Account.get_one ~id:account_id)
- >>= make_relationship_from_model self)
- >|= List.map yojson_of_relationship
- >|= (fun l -> `List l)
- >>= respond_yojson
+ |> make_relationship_from_model self)
+ |> List.map yojson_of_relationship
+ |> (fun l -> `List l)
+ |> respond_yojson
diff --git a/lib/controller/api_v1/accounts/root.ml b/lib/controller/api_v1/accounts/root.ml
index 8932ac8..0bb99ea 100644
--- a/lib/controller/api_v1/accounts/root.ml
+++ b/lib/controller/api_v1/accounts/root.ml
@@ -1,12 +1,11 @@
open Entity
open Helper
-open Lwt.Infix
-let get req =
+let get _ req =
let id =
- req |> Httpq.Server.param ":id" |> int_of_string |> Model.Account.ID.of_int
+ req |> Yume.Server.param ":id" |> int_of_string |> Model.Account.ID.of_int
in
- match%lwt Db.e (Model.Account.get_one ~id) with
+ match Db.e (Model.Account.get_one ~id) with
| exception Sqlx.Error.NoRowFound ->
- Httpq.Server.raise_error_response `Not_found
- | a -> make_account_from_model a >|= yojson_of_account >>= respond_yojson
+ Yume.Server.raise_error_response `Not_found
+ | a -> make_account_from_model a |> yojson_of_account |> respond_yojson
diff --git a/lib/controller/api_v1/accounts/search.ml b/lib/controller/api_v1/accounts/search.ml
index 9ee8f38..5de1b83 100644
--- a/lib/controller/api_v1/accounts/search.ml
+++ b/lib/controller/api_v1/accounts/search.ml
@@ -1,4 +1,3 @@
-open Lwt.Infix
open Activity
open Helper
@@ -14,18 +13,16 @@ type entry = {
type t = entry list [@@deriving yojson]
let parse_req req =
- let open Httpq.Server in
- let%lwt resolve =
- req |> query ~default:"false" "resolve" >|= bool_of_string
- in
- req |> query "q" >|= parse_webfinger_address >|= fun (username, domain) ->
+ let open Yume.Server in
+ let resolve = req |> query ~default:"false" "resolve" |> bool_of_string in
+ let username, domain = req |> query "q" |> parse_webfinger_address in
(resolve, username, domain)
-let get req =
- let%lwt _ = authenticate_bearer req in
- let%lwt _resolve, username, domain = parse_req req in
- try%lwt
- let%lwt acc = search_account (`Webfinger (domain, username)) in
+let get env req =
+ let _ = authenticate_bearer req in
+ let _resolve, username, domain = parse_req req in
+ try
+ let acc = search_account env (`Webfinger (domain, username)) in
let acct =
match acc#domain with
| None -> username
@@ -37,5 +34,5 @@ let get req =
~username ~acct ~display_name:acc#display_name
in
[ ent ] |> yojson_of_t |> Yojson.Safe.to_string
- |> Httpq.Server.respond ~headers:[ Helper.content_type_app_json ]
- with _ -> Httpq.Server.raise_error_response `Not_found
+ |> Yume.Server.respond ~headers:[ Helper.content_type_app_json ]
+ with _ -> Yume.Server.raise_error_response `Not_found
diff --git a/lib/controller/api_v1/accounts/statuses.ml b/lib/controller/api_v1/accounts/statuses.ml
index 5cb6782..f4a2d08 100644
--- a/lib/controller/api_v1/accounts/statuses.ml
+++ b/lib/controller/api_v1/accounts/statuses.ml
@@ -1,4 +1,3 @@
-open Lwt.Infix
open Helper
type params = {
@@ -11,37 +10,37 @@ type params = {
}
let parse_req req =
- let open Httpq.Server in
- let%lwt self_id =
- may_authenticate_account req >|= fun a -> a |> Option.map (fun a -> a#id)
+ let open Yume.Server in
+ let self_id =
+ may_authenticate_account req |> fun a -> a |> Option.map (fun a -> a#id)
in
let id = req |> param ":id" |> int_of_string |> Model.Account.ID.of_int in
- let%lwt limit = req |> query ~default:"20" "limit" >|= int_of_string in
+ let limit = req |> query ~default:"20" "limit" |> int_of_string in
let limit = min limit 40 in
- let%lwt max_id =
+ let max_id =
req |> query_opt "max_id"
- >|= Option.map (fun s -> s |> int_of_string |> Model.Status.ID.of_int)
+ |> Option.map (fun s -> s |> int_of_string |> Model.Status.ID.of_int)
in
- let%lwt since_id =
+ let since_id =
req |> query_opt "since_id"
- >|= Option.map (fun s -> s |> int_of_string |> Model.Status.ID.of_int)
+ |> Option.map (fun s -> s |> int_of_string |> Model.Status.ID.of_int)
in
- let%lwt exclude_replies =
+ let exclude_replies =
req
|> query_opt "exclude_replies"
- >|= Option.map bool_of_string
- >|= Option.value ~default:false
+ |> Option.map bool_of_string
+ |> Option.value ~default:false
in
- Lwt.return { id; self_id; max_id; since_id; limit; exclude_replies }
+ { id; self_id; max_id; since_id; limit; exclude_replies }
-let get req =
- let%lwt { self_id; id; limit; max_id; since_id; exclude_replies } =
+let get _ req =
+ let { self_id; id; limit; max_id; since_id; exclude_replies } =
parse_req req
in
- let%lwt result =
+ let result =
Db.(e @@ account_statuses ~id ~limit ~max_id ~since_id ~exclude_replies)
- >|= List.map (fun (s : Db.Status.t) -> s#id)
- >>= Entity.load_statuses_from_db ?self_id
+ |> List.map (fun (s : Db.Status.t) -> s#id)
+ |> Entity.load_statuses_from_db ?self_id
in
let headers =
match result with
diff --git a/lib/controller/api_v1/accounts/unfollow.ml b/lib/controller/api_v1/accounts/unfollow.ml
index 3598d0e..878059a 100644
--- a/lib/controller/api_v1/accounts/unfollow.ml
+++ b/lib/controller/api_v1/accounts/unfollow.ml
@@ -1,12 +1,11 @@
open Entity
-open Lwt.Infix
open Helper
open Util
-let service (self : Db.Account.t) (acc : Db.Account.t) (f : Db.Follow.t) =
- Db.(e @@ Follow.delete [ f ]);%lwt
+let service env (self : Db.Account.t) (acc : Db.Account.t) (f : Db.Follow.t) =
+ Db.(e @@ Follow.delete [ f ]);
match acc#domain with
- | None -> Lwt.return_unit
+ | None -> ()
| Some _ ->
(* Remote account *)
let open Activity in
@@ -20,25 +19,24 @@ let service (self : Db.Account.t) (acc : Db.Account.t) (f : Db.Follow.t) =
~actor:(`String self#uri) ~obj ()
|> undo
in
- Worker.Delivery.kick ~activity ~src:self ~url:acc#inbox_url
+ Worker.Delivery.kick env ~activity ~src:self ~url:acc#inbox_url
(* Recv POST /api/v1/accounts/:id/unfollow *)
-let post req =
- let%lwt self = authenticate_account req in
- let id = req |> Httpq.Server.param ":id" |> string_to_account_id in
+let post env req =
+ let self = authenticate_account req in
+ let id = req |> Yume.Server.param ":id" |> string_to_account_id in
(* Check if accounts are valid *)
- let%lwt acc = Db.e (Model.Account.get_one ~id) in
+ let acc = Db.e (Model.Account.get_one ~id) in
(* Check if followed *)
- let%lwt f =
- Db.(
- e @@ Follow.get_one ~account_id:self#id ~target_account_id:id
- |> maybe_no_row)
+ let f =
+ try Some Db.(e @@ Follow.get_one ~account_id:self#id ~target_account_id:id)
+ with Sqlx.Error.NoRowFound -> None
in
(* If valid, send Undo of Follow to the server *)
- if%lwt Lwt.return (f <> None) then service self acc (Option.get f);%lwt
+ if f <> None then service env self acc (Option.get f);
(* Return the result to the client *)
make_relationship_from_model self acc
- >|= yojson_of_relationship >>= respond_yojson
+ |> yojson_of_relationship |> respond_yojson
diff --git a/lib/controller/api_v1/accounts/update_credentials.ml b/lib/controller/api_v1/accounts/update_credentials.ml
index 18cc931..7a5a1be 100644
--- a/lib/controller/api_v1/accounts/update_credentials.ml
+++ b/lib/controller/api_v1/accounts/update_credentials.ml
@@ -1,50 +1,51 @@
open Entity
open Helper
-open Lwt.Infix
open Util
-let patch req =
- let%lwt self = authenticate_account req in
+let patch env req =
+ let self = authenticate_account req in
(* FIXME: support more *)
req
- |> Httpq.Server.query_opt "display_name"
- >|= Option.value ~default:self#display_name
- >|= self#set_display_name;%lwt
+ |> Yume.Server.query_opt "display_name"
+ |> Option.value ~default:self#display_name
+ |> self#set_display_name;
req
- |> Httpq.Server.query_opt "note"
- >|= Option.value ~default:self#note
- >|= self#set_note;%lwt
+ |> Yume.Server.query_opt "note"
+ |> Option.value ~default:self#note
+ |> self#set_note;
(req
- |> Httpq.Server.query_opt "bot"
- >|= Option.value ~default:(Model.Account.is_bot self |> string_of_bool)
- >|= function
+ |> Yume.Server.query_opt "bot"
+ |> Option.value ~default:(Model.Account.is_bot self |> string_of_bool)
+ |> function
| "true" -> self#set_actor_type (Some `Service)
| "false" -> self#set_actor_type (Some `Person)
- | _ -> raise_error_response `Bad_request);%lwt
+ | _ -> raise_error_response `Bad_request);
(* Avatar *)
- Lwt_unix.mkpath (Config.account_avatar_dir ()) 0o755;%lwt
- Httpq.Server.formdata "avatar" req
- |> Lwt_result.iter (fun formdata ->
- let%lwt _, file_name, _ =
+ Eio.Path.mkdirs ~exists_ok:true ~perm:0o755
+ Eio.Path.(env#fs / Config.account_avatar_dir ());
+ Yume.Server.formdata "avatar" req
+ |> Result.iter (fun formdata ->
+ let _, file_name, _ =
+ Lwt_eio.run_lwt @@ fun () ->
Image.save_formdata ~outdir:(Config.account_avatar_dir ()) formdata
in
let file_url = Config.account_avatar_url file_name in
- self#set_avatar_remote_url (Some file_url);
- Lwt.return_unit);%lwt
+ self#set_avatar_remote_url (Some file_url));
(* Header *)
- Lwt_unix.mkpath (Config.account_header_dir ()) 0o755;%lwt
- Httpq.Server.formdata "header" req
- |> Lwt_result.iter (fun formdata ->
- let%lwt _, file_name, _ =
+ Eio.Path.mkdirs ~exists_ok:true ~perm:0o755
+ Eio.Path.(env#fs / Config.account_header_dir ());
+ Yume.Server.formdata "header" req
+ |> Result.iter (fun formdata ->
+ let _, file_name, _ =
+ Lwt_eio.run_lwt @@ fun () ->
Image.save_formdata ~outdir:(Config.account_header_dir ()) formdata
in
let file_url = Config.account_header_url file_name in
- self#set_header_remote_url file_url;
- Lwt.return_unit);%lwt
+ self#set_header_remote_url file_url);
- let%lwt a = Db.(e @@ Account.update [ self ]) >|= List.hd in
- Worker.Account_update.kick ~account_id:a#id ~updated_at:a#updated_at;%lwt
- make_credential_account_from_model a >|= yojson_of_account >>= respond_yojson
+ let a = Db.(e @@ Account.update [ self ]) |> List.hd in
+ Worker.Account_update.kick env ~account_id:a#id ~updated_at:a#updated_at;
+ make_credential_account_from_model a |> yojson_of_account |> respond_yojson
diff --git a/lib/controller/api_v1/accounts/verify_credentials.ml b/lib/controller/api_v1/accounts/verify_credentials.ml
index 467a94f..d834771 100644
--- a/lib/controller/api_v1/accounts/verify_credentials.ml
+++ b/lib/controller/api_v1/accounts/verify_credentials.ml
@@ -1,8 +1,6 @@
open Entity
-open Lwt.Infix
open Helper
-let get req =
- let%lwt self = authenticate_account req in
- make_credential_account_from_model self
- >|= yojson_of_account >>= respond_yojson
+let get _ req =
+ let self = authenticate_account req in
+ make_credential_account_from_model self |> yojson_of_account |> respond_yojson
diff --git a/lib/controller/api_v1/apps/root.ml b/lib/controller/api_v1/apps/root.ml
index 0c56663..fb4fca0 100644
--- a/lib/controller/api_v1/apps/root.ml
+++ b/lib/controller/api_v1/apps/root.ml
@@ -9,12 +9,12 @@ type res = {
}
[@@deriving make, yojson]
-let post req =
- let%lwt client_name = req |> Httpq.Server.query "client_name" in
- let%lwt redirect_uris = req |> Httpq.Server.query "redirect_uris" in
- let%lwt scopes = req |> Httpq.Server.query ~default:"read" "scopes" in
+let post _ req =
+ let client_name = req |> Yume.Server.query "client_name" in
+ let redirect_uris = req |> Yume.Server.query "redirect_uris" in
+ let scopes = req |> Yume.Server.query ~default:"read" "scopes" in
- let%lwt app =
+ let app =
Oauth_helper.generate_application ~name:client_name
~redirect_uri:redirect_uris ~scopes
in
@@ -25,4 +25,4 @@ let post req =
~vapid_key:(Config.vapid_public_key ())
()
|> yojson_of_res |> Yojson.Safe.to_string
- |> Httpq.Server.respond ~headers:[ Helper.content_type_app_json ]
+ |> Yume.Server.respond ~headers:[ Helper.content_type_app_json ]
diff --git a/lib/controller/api_v1/apps/verify_credentials.ml b/lib/controller/api_v1/apps/verify_credentials.ml
index 36335fd..926f9c5 100644
--- a/lib/controller/api_v1/apps/verify_credentials.ml
+++ b/lib/controller/api_v1/apps/verify_credentials.ml
@@ -1,9 +1,9 @@
type res = { name : string } [@@deriving make, yojson]
-let get req =
- let%lwt token = Helper.authenticate_bearer req in
- let%lwt app =
+let get _ req =
+ let token = Helper.authenticate_bearer req in
+ let app =
Db.(e OAuthApplication.(get_one ~id:(Option.get token#application_id)))
in
make_res ~name:app#name |> yojson_of_res |> Yojson.Safe.to_string
- |> Httpq.Server.respond
+ |> Yume.Server.respond
diff --git a/lib/controller/api_v1/instance.ml b/lib/controller/api_v1/instance.ml
index e523e1d..efc3234 100644
--- a/lib/controller/api_v1/instance.ml
+++ b/lib/controller/api_v1/instance.ml
@@ -23,7 +23,7 @@ type v1_instance = {
}
[@@deriving make, yojson]
-let get _req =
+let get _ _req =
let uri = Config.server_name () in
let streaming_api = "wss://" ^ uri in
make_v1_instance ~uri ~title:"Waq"
@@ -32,4 +32,4 @@ let get _req =
~version:"0.0.1"
~urls:(make_v1_instance_urls ~streaming_api)
|> yojson_of_v1_instance |> Yojson.Safe.to_string
- |> Httpq.Server.respond ~headers:[ Helper.content_type_app_json ]
+ |> Yume.Server.respond ~headers:[ Helper.content_type_app_json ]
diff --git a/lib/controller/api_v1/markers.ml b/lib/controller/api_v1/markers.ml
index 4c08b26..276b057 100644
--- a/lib/controller/api_v1/markers.ml
+++ b/lib/controller/api_v1/markers.ml
@@ -9,39 +9,41 @@ let serialize_markers xs =
|> fun j -> (timeline, j))
|> fun j -> `Assoc j
-let get req =
- let%lwt user = authenticate_user req in
- Httpq.Server.query_many "timeline" req
- >|= List.filter (function "home" | "notifications" -> true | _ -> false)
- >|= List.sort_uniq compare
- >>= Lwt_list.map_p (fun timeline ->
- Db.(
- e Marker.(get_one ~timeline ~user_id:(Some user#id)) |> maybe_no_row)
- >|= fun x ->
- let default =
- Model.Marker.make ~updated_at:(Ptime.now ()) ~last_read_id:0
- ~user_id:user#id ~timeline ()
- in
- (timeline, x |> Option.value ~default))
- >|= serialize_markers >>= respond_yojson
+let get _ req =
+ let user = authenticate_user req in
+ Yume.Server.query_many "timeline" req
+ |> List.filter (function "home" | "notifications" -> true | _ -> false)
+ |> List.sort_uniq compare
+ |> List.map (fun timeline ->
+ let x =
+ try Some Db.(e Marker.(get_one ~timeline ~user_id:(Some user#id)))
+ with Sqlx.Error.NoRowFound -> None
+ in
+ let default =
+ Model.Marker.make ~updated_at:(Ptime.now ()) ~last_read_id:0
+ ~user_id:user#id ~timeline ()
+ in
+ (timeline, x |> Option.value ~default))
+ |> serialize_markers |> respond_yojson
-let post req =
- let%lwt user = authenticate_user req in
- let%lwt home_last_read_id, noti_last_read_id =
- Httpq.Server.body req >|= Yojson.Safe.from_string >|= expect_assoc
- >|= List.fold_left
- (fun (home, noti) -> function
- | "home", `Assoc [ ("last_read_id", `String id) ] ->
- (Some (int_of_string id), noti)
- | "notifications", `Assoc [ ("last_read_id", `String id) ] ->
- (home, Some (int_of_string id))
- | _ -> raise_error_response `Bad_request)
- (None, None)
+let post _ req =
+ let user = authenticate_user req in
+ let home_last_read_id, noti_last_read_id =
+ Yume.Server.body req |> Yojson.Safe.from_string |> expect_assoc
+ |> List.fold_left
+ (fun (home, noti) -> function
+ | "home", `Assoc [ ("last_read_id", `String id) ] ->
+ (Some (int_of_string id), noti)
+ | "notifications", `Assoc [ ("last_read_id", `String id) ] ->
+ (home, Some (int_of_string id))
+ | _ -> raise_error_response `Bad_request)
+ (None, None)
in
let home_marker = ref None in
let noti_marker = ref None in
- let%lwt result =
+ let result =
+ Lwt_eio.run_lwt @@ fun () ->
Db.(
transaction @@ fun c ->
let aux last_read_id_opt timeline out_ref =
diff --git a/lib/controller/api_v1/notifications/root.ml b/lib/controller/api_v1/notifications/root.ml
index 3eb0363..cbb16fd 100644
--- a/lib/controller/api_v1/notifications/root.ml
+++ b/lib/controller/api_v1/notifications/root.ml
@@ -1,5 +1,4 @@
open Helper
-open Lwt.Infix
type params = {
self_id : Model.Account.ID.t;
@@ -12,24 +11,24 @@ let string_to_notification_id s =
s |> int_of_string |> Model.Notification.ID.of_int
let parse_req req =
- let open Httpq.Server in
- let%lwt self_id = authenticate_account req >|= fun a -> a#id in
- let%lwt limit = req |> query ~default:"15" "limit" >|= int_of_string in
+ let open Yume.Server in
+ let self_id = authenticate_account req |> fun a -> a#id in
+ let limit = req |> query ~default:"15" "limit" |> int_of_string in
let limit = min limit 30 in
- let%lwt max_id =
- req |> query_opt "max_id" >|= Option.map string_to_notification_id
+ let max_id =
+ req |> query_opt "max_id" |> Option.map string_to_notification_id
in
- let%lwt since_id =
- req |> query_opt "since_id" >|= Option.map string_to_notification_id
+ let since_id =
+ req |> query_opt "since_id" |> Option.map string_to_notification_id
in
- Lwt.return { self_id; max_id; since_id; limit }
+ { self_id; max_id; since_id; limit }
-let get req =
- let%lwt { self_id; max_id; since_id; limit } = parse_req req in
+let get _ req =
+ let { self_id; max_id; since_id; limit } = parse_req req in
Db.(e @@ get_notifications ~account_id:self_id ~max_id ~since_id ~limit)
- >|= List.map (fun (n : Db.Notification.t) -> n#id)
- >>= Entity.load_notifications_from_db ~self_id
- >|= List.map Entity.yojson_of_notification
- >|= (fun l -> `List l)
- >>= respond_yojson
+ |> List.map (fun (n : Db.Notification.t) -> n#id)
+ |> Entity.load_notifications_from_db ~self_id
+ |> List.map Entity.yojson_of_notification
+ |> (fun l -> `List l)
+ |> respond_yojson
diff --git a/lib/controller/api_v1/push/subscription.ml b/lib/controller/api_v1/push/subscription.ml
index c6b682d..f5b33e2 100644
--- a/lib/controller/api_v1/push/subscription.ml
+++ b/lib/controller/api_v1/push/subscription.ml
@@ -1,28 +1,26 @@
open Helper
-open Lwt.Infix
open Util
let expect_assoc = function `Assoc l -> l | _ -> failwith "expect assoc"
let expect_string = function `String s -> s | _ -> failwith "expect string"
-let get req =
- let%lwt oauth_access_token = authenticate_bearer req in
- match%lwt
- Db.(
- e
- WebPushSubscription.(
- get_one ~access_token_id:(Some oauth_access_token#id))
- |> maybe_no_row)
- with
- | None -> raise_error_response `Not_found
- | Some s ->
- s |> Entity.serialize_web_push_subscription
- |> Entity.yojson_of_web_push_subscription |> respond_yojson
+let get _ req =
+ let oauth_access_token = authenticate_bearer req in
+ let s =
+ try
+ Db.(
+ e
+ WebPushSubscription.(
+ get_one ~access_token_id:(Some oauth_access_token#id)))
+ with Sqlx.Error.NoRowFound -> raise_error_response `Not_found
+ in
+ s |> Entity.serialize_web_push_subscription
+ |> Entity.yojson_of_web_push_subscription |> respond_yojson
-let post req =
- let%lwt oauth_access_token = authenticate_bearer req in
+let post _ req =
+ let oauth_access_token = authenticate_bearer req in
- let%lwt body = Httpq.Server.body req in
+ let body = Yume.Server.body req in
let endpoint, key_p256dh, key_auth =
try
let subscription =
@@ -43,17 +41,14 @@ let post req =
~access_token_id:oauth_access_token#id
?user_id:oauth_access_token#resource_owner_id ()
|> save_one))
- >|= Entity.(
- serialize_web_push_subscription *> yojson_of_web_push_subscription)
- >>= respond_yojson
+ |> Entity.(serialize_web_push_subscription *> yojson_of_web_push_subscription)
+ |> respond_yojson
-let delete req =
+let delete _ req =
let open Model.WebPushSubscription in
- let%lwt oauth_access_token = authenticate_bearer req in
- (try%lwt
- let%lwt s =
- Db.(e @@ get_one ~access_token_id:(Some oauth_access_token#id))
- in
+ let oauth_access_token = authenticate_bearer req in
+ (try
+ let s = Db.(e @@ get_one ~access_token_id:(Some oauth_access_token#id)) in
Db.(e @@ delete [ s ])
- with Sqlx.Error.NoRowFound -> Lwt.return_unit);%lwt
+ with Sqlx.Error.NoRowFound -> ());
respond_yojson (`Assoc [])
diff --git a/lib/controller/api_v1/statuses/context.ml b/lib/controller/api_v1/statuses/context.ml
index 31653d6..7e00e90 100644
--- a/lib/controller/api_v1/statuses/context.ml
+++ b/lib/controller/api_v1/statuses/context.ml
@@ -1,33 +1,32 @@
open Helper
-open Lwt.Infix
type t = { ancestors : Entity.status list; descendants : Entity.status list }
[@@deriving make, yojson_of]
-let get req =
- let%lwt self_id =
- may_authenticate_account req >|= fun a -> a |> Option.map (fun x -> x#id)
+let get _ req =
+ let self_id =
+ may_authenticate_account req |> fun a -> a |> Option.map (fun x -> x#id)
in
let status_id =
- req |> Httpq.Server.param ":id" |> int_of_string |> Model.Status.ID.of_int
+ req |> Yume.Server.param ":id" |> int_of_string |> Model.Status.ID.of_int
in
- let%lwt ancestors = Db.(e (Status.get_ancestors status_id) |> maybe_no_row) in
- let%lwt descendants =
- Db.(e (Status.get_descendants status_id) |> maybe_no_row)
- in
- match (ancestors, descendants) with
- | None, _ | _, None -> Httpq.Server.raise_error_response `Not_found
- | Some ancestors, Some descendants ->
- let%lwt ancestors =
+ match
+ ( Db.(e (Status.get_ancestors status_id)),
+ Db.(e (Status.get_descendants status_id)) )
+ with
+ | exception Sqlx.Error.NoRowFound ->
+ Yume.Server.raise_error_response `Not_found
+ | ancestors, descendants ->
+ let ancestors =
ancestors
|> List.map (fun (s : Db.Status.t) -> s#id)
|> Entity.load_statuses_from_db ?self_id
in
- let%lwt descendants =
+ let descendants =
descendants
|> List.map (fun (s : Db.Status.t) -> s#id)
|> Entity.load_statuses_from_db ?self_id
in
make ~ancestors ~descendants ()
|> yojson_of_t |> Yojson.Safe.to_string
- |> Httpq.Server.respond ~headers:[ content_type_app_json ]
+ |> Yume.Server.respond ~headers:[ content_type_app_json ]
diff --git a/lib/controller/api_v1/statuses/favourite.ml b/lib/controller/api_v1/statuses/favourite.ml
index dc50d81..4a55dca 100644
--- a/lib/controller/api_v1/statuses/favourite.ml
+++ b/lib/controller/api_v1/statuses/favourite.ml
@@ -1,46 +1,40 @@
open Util
open Helper
open Entity
-open Lwt.Infix
-let post req =
- let%lwt self = authenticate_account req in
+let post env req =
+ let self = authenticate_account req in
let status_id =
- req |> Httpq.Server.param ":id" |> int_of_string |> Model.Status.ID.of_int
+ req |> Yume.Server.param ":id" |> int_of_string |> Model.Status.ID.of_int
in
- let%lwt status =
- match%lwt
- Db.e (Model.Status.get_one ~id:status_id ~preload:[ `account [] ])
- with
- | exception Sqlx.Error.NoRowFound ->
- Httpq.Server.raise_error_response `Not_found
- | s -> Lwt.return s
+ let status =
+ try Db.e (Model.Status.get_one ~id:status_id ~preload:[ `account [] ])
+ with Sqlx.Error.NoRowFound -> Yume.Server.raise_error_response `Not_found
in
- (match%lwt Db.(e @@ Favourite.get_one ~status_id ~account_id:self#id) with
- | _ -> (* Already favourited *) Lwt.return_unit
- | exception Sqlx.Error.NoRowFound ->
- let now = Ptime.now () in
- let%lwt fav =
- Db.(
- e
- @@ Favourite.(
- make ~created_at:now ~updated_at:now ~account_id:self#id
- ~status_id ()
- |> save_one))
- in
- if%lwt Lwt.return (self#id = status#account_id) then Lwt.return_unit
- else
- let src = self in
- let%lwt dst = Db.e (Model.Account.get_one ~id:status#account_id) in
- if Db.Account.is_remote status#account then
- let%lwt activity = Activity.(like_of_favourite fav >|= like) in
- Worker.Delivery.kick ~activity ~src ~url:dst#inbox_url
- else
- Worker.Local_notify.kick
- ~activity_id:(Model.Favourite.ID.to_int fav#id)
- ~activity_type:`Favourite ~typ:`favourite ~src ~dst);%lwt
+ (try Db.(e @@ Favourite.get_one ~status_id ~account_id:self#id) |> ignore
+ with Sqlx.Error.NoRowFound ->
+ let now = Ptime.now () in
+ let fav =
+ Db.(
+ e
+ @@ Favourite.(
+ make ~created_at:now ~updated_at:now ~account_id:self#id
+ ~status_id ()
+ |> save_one))
+ in
+ if self#id = status#account_id then ()
+ else
+ let src = self in
+ let dst = Db.e (Model.Account.get_one ~id:status#account_id) in
+ if Db.Account.is_remote status#account then
+ let activity = Activity.(like_of_favourite fav |> like) in
+ Worker.Delivery.kick env ~activity ~src ~url:dst#inbox_url
+ else
+ Worker.Local_notify.kick env
+ ~activity_id:(Model.Favourite.ID.to_int fav#id)
+ ~activity_type:`Favourite ~typ:`favourite ~src ~dst);
make_status_from_model ~self_id:self#id status
- >|= yojson_of_status >>= respond_yojson
+ |> yojson_of_status |> respond_yojson
diff --git a/lib/controller/api_v1/statuses/favourited_by.ml b/lib/controller/api_v1/statuses/favourited_by.ml
index f628440..6e6a529 100644
--- a/lib/controller/api_v1/statuses/favourited_by.ml
+++ b/lib/controller/api_v1/statuses/favourited_by.ml
@@ -1,16 +1,15 @@
open Util
open Helper
-open Lwt.Infix
-let get req =
+let get _ req =
let status_id =
- req |> Httpq.Server.param ":id" |> int_of_string |> Model.Status.ID.of_int
+ req |> Yume.Server.param ":id" |> int_of_string |> Model.Status.ID.of_int
in
- let%lwt accts = Db.(e @@ get_favourited_by ~status_id) in
- let%lwt accts =
+ let accts = Db.(e @@ get_favourited_by ~status_id) in
+ let accts =
accts
|> List.map (fun (a : Db.Account.t) -> a#id)
|> Entity.load_accounts_from_db
- >|= List.map Entity.yojson_of_account
+ |> List.map Entity.yojson_of_account
in
`List accts |> respond_yojson
diff --git a/lib/controller/api_v1/statuses/reblog.ml b/lib/controller/api_v1/statuses/reblog.ml
index e521c0d..c032851 100644
--- a/lib/controller/api_v1/statuses/reblog.ml
+++ b/lib/controller/api_v1/statuses/reblog.ml
@@ -1,54 +1,45 @@
open Util
open Helper
open Entity
-open Lwt.Infix
-let post req =
- let%lwt self = authenticate_account req in
+let post env req =
+ let self = authenticate_account req in
let status_id =
- req |> Httpq.Server.param ":id" |> int_of_string |> Model.Status.ID.of_int
+ req |> Yume.Server.param ":id" |> int_of_string |> Model.Status.ID.of_int
in
- let%lwt status =
- Db.(
- e @@ Status.get_one ~id:status_id ~preload:[ `account [] ] |> maybe_no_row)
- >|= function
- | Some s -> s
- | None -> Httpq.Server.raise_error_response `Not_found
+ let status =
+ try Db.(e @@ Status.get_one ~id:status_id ~preload:[ `account [] ])
+ with Sqlx.Error.NoRowFound -> Yume.Server.raise_error_response `Not_found
in
- let%lwt status =
+ let status =
match status#reblog_of_id with
- | None -> Lwt.return status
+ | None -> status
| Some id -> Db.e (Model.Status.get_one ~id)
in
- let%lwt s =
- match%lwt
+ let s =
+ try
Db.e
(Model.Status.get_one ~account_id:self#id ~reblog_of_id:(Some status#id))
- with
- | s ->
- (* Already reblogged *)
- Lwt.return s
- | exception Sqlx.Error.NoRowFound ->
- let now = Ptime.now () in
- let%lwt s =
- Db.(
- e
- @@ Status.(
- make ~text:"" ~created_at:now ~updated_at:now ~uri:""
- ~account_id:self#id ~reblog_of_id:status#id ~spoiler_text:""
- ()
- |> save_one_with_uri_and_url))
- in
- Worker.Distribute.kick s;%lwt
- (if s#account_id = status#account_id then Lwt.return_unit
- else if Model.Account.is_remote status#account then Lwt.return_unit
- else
- let%lwt src = Db.e (Model.Account.get_one ~id:s#account_id) in
- let%lwt dst = Db.e (Model.Account.get_one ~id:status#account_id) in
- Worker.Local_notify.kick
- ~activity_id:(Model.Status.ID.to_int s#id)
- ~activity_type:`Status ~typ:`reblog ~src ~dst);%lwt
- Lwt.return s
+ with Sqlx.Error.NoRowFound ->
+ let now = Ptime.now () in
+ let s =
+ Db.(
+ e
+ @@ Status.(
+ make ~text:"" ~created_at:now ~updated_at:now ~uri:""
+ ~account_id:self#id ~reblog_of_id:status#id ~spoiler_text:"" ()
+ |> save_one_with_uri_and_url))
+ in
+ Worker.Distribute.kick env s;
+ (if s#account_id = status#account_id then ()
+ else if Model.Account.is_remote status#account then ()
+ else
+ let src = Db.e (Model.Account.get_one ~id:s#account_id) in
+ let dst = Db.e (Model.Account.get_one ~id:status#account_id) in
+ Worker.Local_notify.kick env
+ ~activity_id:(Model.Status.ID.to_int s#id)
+ ~activity_type:`Status ~typ:`reblog ~src ~dst);
+ s
in
make_status_from_model ~self_id:self#id s
- >|= yojson_of_status >>= Helper.respond_yojson
+ |> yojson_of_status |> Helper.respond_yojson
diff --git a/lib/controller/api_v1/statuses/root.ml b/lib/controller/api_v1/statuses/root.ml
index 711da7f..292f0f1 100644
--- a/lib/controller/api_v1/statuses/root.ml
+++ b/lib/controller/api_v1/statuses/root.ml
@@ -1,38 +1,34 @@
open Entity
open Util
-open Lwt.Infix [@@warning "-33"]
open Helper
(* GET /api/v1/statuses/:id *)
-let get req =
+let get _ req =
let status_id =
- req |> Httpq.Server.param ":id" |> int_of_string |> Model.Status.ID.of_int
+ req |> Yume.Server.param ":id" |> int_of_string |> Model.Status.ID.of_int
in
- let%lwt self_id =
- may_authenticate_account req >|= fun a -> a |> Option.map (fun x -> x#id)
+ let self_id = may_authenticate_account req |> Option.map (fun x -> x#id) in
+ let s =
+ try Db.(e @@ Status.get_one ~id:status_id)
+ with Sqlx.Error.NoRowFound -> Yume.Server.raise_error_response `Not_found
in
- match%lwt Db.(e @@ Status.get_one ~id:status_id |> maybe_no_row) with
- | None -> Httpq.Server.raise_error_response `Not_found
- | Some s ->
- let%lwt s = make_status_from_model ?self_id s in
- s |> yojson_of_status |> Yojson.Safe.to_string
- |> Httpq.Server.respond ~headers:[ content_type_app_json ]
+ let s = make_status_from_model ?self_id s in
+ s |> yojson_of_status |> Yojson.Safe.to_string
+ |> Yume.Server.respond ~headers:[ content_type_app_json ]
(* Recv POST /api/v1/statuses *)
-let post req =
- let%lwt self = authenticate_account req in
- let%lwt status =
- req |> Httpq.Server.query ~default:"" "status" >|= String.trim
- in
- let%lwt spoiler_text =
- req |> Httpq.Server.query ~default:"" "spoiler_text" >|= String.trim
+let post env req =
+ let self = authenticate_account req in
+ let status = req |> Yume.Server.query ~default:"" "status" |> String.trim in
+ let spoiler_text =
+ req |> Yume.Server.query ~default:"" "spoiler_text" |> String.trim
in
- let%lwt in_reply_to_id =
+ let in_reply_to_id =
req
- |> Httpq.Server.query_opt "in_reply_to_id"
- >|= Option.map (fun s -> s |> int_of_string |> Model.Status.ID.of_int)
+ |> Yume.Server.query_opt "in_reply_to_id"
+ |> Option.map (fun s -> s |> int_of_string |> Model.Status.ID.of_int)
in
- let%lwt media_ids = req |> Httpq.Server.query_many "media_ids" in
+ let media_ids = req |> Yume.Server.query_many "media_ids" in
(* Sanity check *)
(match (status = "", media_ids) with
@@ -40,7 +36,7 @@ let post req =
| _ -> raise_error_response `Unprocessable_entity);
(* Handle media attachments *)
- let%lwt attachments =
+ let attachments =
let ids =
media_ids |> List.map (int_of_string *> Model.MediaAttachment.ID.of_int)
in
@@ -50,22 +46,21 @@ let post req =
raise_error_response `Bad_request;
(* Handle mentions *)
- let%lwt mentioned_accts =
+ let mentioned_accts =
Text_helper.match_mention status
- |> Lwt_list.filter_map_p (fun (_off, _len, username, domain) ->
- try%lwt
- Activity.search_account (`Webfinger (domain, username))
- >|= Option.some
+ |> List.filter_map (fun (_off, _len, username, domain) ->
+ try
+ Some (Activity.search_account env (`Webfinger (domain, username)))
with _ ->
Logq.debug (fun m ->
m "Couldn't find the mentioned account: %s"
(username
^ match domain with None -> "" | Some s -> "@" ^ s));
- Lwt.return_none)
+ None)
in
(* Insert status and mentions *)
- let%lwt s =
+ let s =
Db.(
e
Status.(
@@ -74,39 +69,37 @@ let post req =
~spoiler_text ())))
in
(mentioned_accts
- |> Lwt_list.iter_p @@ fun acct ->
+ |> List.iter @@ fun acct ->
Db.(e Mention.(make ~account_id:acct#id ~status_id:s#id () |> save_one))
- |> ignore_lwt);%lwt
+ |> ignore);
(* Update attachments *)
- ( attachments |> List.map (fun m -> m#with_status_id (Some s#id)) |> fun xs ->
- Db.(e MediaAttachment.(update xs)) |> ignore_lwt );%lwt
+ (let xs = attachments |> List.map (fun m -> m#with_status_id (Some s#id)) in
+ Db.(e MediaAttachment.(update xs)) |> ignore);
(* Deliver the status to others *)
- Worker.Distribute.kick s;%lwt
+ Worker.Distribute.kick env s;
(* Attach preview cards if any *)
- Worker.Link_crawl.kick s#id;%lwt
+ Worker.Link_crawl.kick env s#id;
(* Return the result to the client *)
- let%lwt s = make_status_from_model ~self_id:self#id s in
+ let s = make_status_from_model ~self_id:self#id s in
s |> yojson_of_status |> Yojson.Safe.to_string
- |> Httpq.Server.respond ~headers:[ content_type_app_json ]
+ |> Yume.Server.respond ~headers:[ content_type_app_json ]
-let delete req =
- let%lwt self = authenticate_account req in
+let delete env req =
+ let self = authenticate_account req in
let status_id =
- req |> Httpq.Server.param ":id" |> int_of_string |> Model.Status.ID.of_int
+ req |> Yume.Server.param ":id" |> int_of_string |> Model.Status.ID.of_int
in
- let%lwt status =
+ let status =
try Db.e (Model.Status.get_one ~id:status_id)
- with Sqlx.Error.NoRowFound -> Httpq.Server.raise_error_response `Not_found
+ with Sqlx.Error.NoRowFound -> Yume.Server.raise_error_response `Not_found
in
if status#account_id <> self#id then raise_error_response `Not_found;
(* We should construct the result BEFORE the removal *)
- let%lwt status_to_be_returned =
- make_status_from_model ~self_id:self#id status
- in
- Worker.Removal.kick ~account_id:self#id ~status_id;%lwt
+ let status_to_be_returned = make_status_from_model ~self_id:self#id status in
+ Worker.Removal.kick env ~account_id:self#id ~status_id;
yojson_of_status status_to_be_returned |> respond_yojson
diff --git a/lib/controller/api_v1/statuses/unfavourite.ml b/lib/controller/api_v1/statuses/unfavourite.ml
index 82efa32..bd8d0d1 100644
--- a/lib/controller/api_v1/statuses/unfavourite.ml
+++ b/lib/controller/api_v1/statuses/unfavourite.ml
@@ -1,35 +1,29 @@
open Helper
open Entity
-open Lwt.Infix
-let post req =
- let%lwt self = authenticate_account req in
+let post env req =
+ let self = authenticate_account req in
let status_id =
- req |> Httpq.Server.param ":id" |> int_of_string |> Model.Status.ID.of_int
+ req |> Yume.Server.param ":id" |> int_of_string |> Model.Status.ID.of_int
in
- let%lwt status =
- match%lwt
- Db.e (Model.Status.get_one ~id:status_id ~preload:[ `account [] ])
- with
- | exception Sqlx.Error.NoRowFound ->
- Httpq.Server.raise_error_response `Not_found
- | s -> Lwt.return s
+ let status =
+ try Db.e (Model.Status.get_one ~id:status_id ~preload:[ `account [] ])
+ with Sqlx.Error.NoRowFound -> Yume.Server.raise_error_response `Not_found
in
- (match%lwt Db.(e @@ Favourite.get_one ~status_id ~account_id:self#id) with
- | exception Sqlx.Error.NoRowFound ->
- (* Already unfavourited *) Lwt.return_unit
+ (match Db.(e @@ Favourite.get_one ~status_id ~account_id:self#id) with
+ | exception Sqlx.Error.NoRowFound -> (* Already unfavourited *) ()
| fav ->
- Db.(e @@ Favourite.delete [ fav ]);%lwt
- if%lwt Model.Account.is_remote status#account |> Lwt.return then
+ Db.(e @@ Favourite.delete [ fav ]);
+ if Model.Account.is_remote status#account then
let src = self in
- let%lwt dst = Db.e (Model.Account.get_one ~id:status#account_id) in
- let%lwt activity =
+ let dst = Db.e (Model.Account.get_one ~id:status#account_id) in
+ let activity =
Activity.(
- like_of_favourite fav >|= like >|= to_undo ~actor:src#uri >|= undo)
+ like_of_favourite fav |> like |> to_undo ~actor:src#uri |> undo)
in
- Worker.Delivery.kick ~activity ~src ~url:dst#inbox_url);%lwt
+ Worker.Delivery.kick env ~activity ~src ~url:dst#inbox_url);
make_status_from_model ~self_id:self#id status
- >|= yojson_of_status >>= respond_yojson
+ |> yojson_of_status |> respond_yojson
diff --git a/lib/controller/api_v1/statuses/unreblog.ml b/lib/controller/api_v1/statuses/unreblog.ml
index 9732455..bf80f5a 100644
--- a/lib/controller/api_v1/statuses/unreblog.ml
+++ b/lib/controller/api_v1/statuses/unreblog.ml
@@ -1,26 +1,21 @@
open Helper
open Entity
-open Lwt.Infix
-let post req =
- let%lwt self = authenticate_account req in
+let post env req =
+ let self = authenticate_account req in
let status_id =
- req |> Httpq.Server.param ":id" |> int_of_string |> Model.Status.ID.of_int
+ req |> Yume.Server.param ":id" |> int_of_string |> Model.Status.ID.of_int
in
- try%lwt
- let%lwt status = Db.e (Model.Status.get_one ~id:status_id) in
- let%lwt reblog =
+ try
+ let status = Db.e (Model.Status.get_one ~id:status_id) in
+ let reblog =
Db.(
e
Model.Status.(
- get_one ~reblog_of_id:(Some status_id) ~account_id:self#id)
- |> maybe_no_row)
- >|= function
- | None -> raise_error_response `Not_found
- | Some s -> s
+ get_one ~reblog_of_id:(Some status_id) ~account_id:self#id))
in
- let%lwt entity = make_status_from_model ~self_id:self#id status in
- Worker.Removal.kick ~account_id:self#id ~status_id:reblog#id;%lwt
+ let entity = make_status_from_model ~self_id:self#id status in
+ Worker.Removal.kick env ~account_id:self#id ~status_id:reblog#id;
let entity =
{
entity with
@@ -29,4 +24,4 @@ let post req =
}
in
yojson_of_status entity |> respond_yojson
- with Sqlx.Error.NoRowFound -> Httpq.Server.raise_error_response `Not_found
+ with Sqlx.Error.NoRowFound -> Yume.Server.raise_error_response `Not_found
diff --git a/lib/controller/api_v1/streaming.ml b/lib/controller/api_v1/streaming.ml
index c635204..dbe408a 100644
--- a/lib/controller/api_v1/streaming.ml
+++ b/lib/controller/api_v1/streaming.ml
@@ -1,5 +1,3 @@
-open Lwt.Infix
-
let may_subscribe ~user_id ~stream ~ws_conn subscription_ref =
match !subscription_ref with
| Some _ -> ()
@@ -13,23 +11,23 @@ let may_unsubscribe subscription_ref =
|> Option.iter (fun (key, conn_id) -> Streaming.remove key conn_id);
subscription_ref := None
-let get req =
- let%lwt access_token =
- let open Httpq.Server in
- req |> query_opt "access_token" >|= function
+let get _env req =
+ let access_token =
+ let open Yume.Server in
+ req |> query_opt "access_token" |> function
| Some v -> v
| None -> req |> header `Sec_websocket_protocol
in
- let%lwt stream = req |> Httpq.Server.query_opt "stream" in
+ let stream = req |> Yume.Server.query_opt "stream" in
- let%lwt user_id =
- try%lwt
- Oauth_helper.authenticate_access_token access_token >|= fun token ->
+ let user_id =
+ try
+ Oauth_helper.authenticate_access_token access_token |> fun token ->
Option.get token#resource_owner_id
- with _ -> Httpq.Server.raise_error_response `Unauthorized
+ with _ -> Yume.Server.raise_error_response `Unauthorized
in
- Httpq.Server.websocket req @@ fun ws_conn ->
+ Yume.Server.websocket req @@ fun ws_conn ->
let user_subscription_ref = ref None in
let may_subscribe_user () =
may_subscribe ~user_id ~stream:`User ~ws_conn user_subscription_ref
@@ -39,11 +37,11 @@ let get req =
(match stream with
| None -> ()
| Some "user" -> may_subscribe_user ()
- | _ -> Httpq.Server.raise_error_response `Bad_request);
+ | _ -> Yume.Server.raise_error_response `Bad_request);
let rec loop () =
- match%lwt Httpq.Server.ws_recv ws_conn with
- | None -> Lwt.return_unit (* Closed *)
+ match Yume.Server.ws_recv ws_conn with
+ | None -> () (* Closed *)
| Some json ->
(match
let ev = Yojson.Safe.from_string json |> Yojson.Safe.Util.to_assoc in
@@ -65,4 +63,4 @@ let get req =
());
loop ()
in
- Lwt.finalize loop (fun () -> Lwt.return (may_unsubscribe_user ()))
+ Fun.protect ~finally:may_unsubscribe_user loop
diff --git a/lib/controller/api_v1/timelines/home.ml b/lib/controller/api_v1/timelines/home.ml
index 21caef9..45f2e1e 100644
--- a/lib/controller/api_v1/timelines/home.ml
+++ b/lib/controller/api_v1/timelines/home.ml
@@ -1,28 +1,25 @@
open Helper
-open Lwt.Infix
let parse_req req =
- let open Httpq.Server in
- let%lwt self = authenticate_account req in
- let%lwt limit = req |> query ~default:"20" "limit" >|= int_of_string in
+ let open Yume.Server in
+ let self = authenticate_account req in
+ let limit = req |> query ~default:"20" "limit" |> int_of_string in
let limit = min limit 40 in
- let%lwt max_id =
- req |> query_opt "max_id" >|= Option.map string_to_status_id
+ let max_id = req |> query_opt "max_id" |> Option.map string_to_status_id in
+ let since_id =
+ req |> query_opt "since_id" |> Option.map string_to_status_id
in
- let%lwt since_id =
- req |> query_opt "since_id" >|= Option.map string_to_status_id
- in
- Lwt.return (self#id, max_id, since_id, limit)
+ (self#id, max_id, since_id, limit)
-let get req =
+let get _env req =
(* Parse the request *)
- let%lwt self_id, max_id, since_id, limit = parse_req req in
+ let self_id, max_id, since_id, limit = parse_req req in
(* Retrieve the result from DB *)
- let%lwt result =
+ let result =
Db.(e @@ home_timeline ~id:self_id ~limit ~max_id ~since_id)
- >|= List.map (fun (s : Db.Status.t) -> s#id)
- >>= Entity.load_statuses_from_db ~self_id
+ |> List.map (fun (s : Db.Status.t) -> s#id)
+ |> Entity.load_statuses_from_db ~self_id
in
(* Construct HTTP link header *)
diff --git a/lib/controller/api_v2/media.ml b/lib/controller/api_v2/media.ml
index 2b6ae63..0446fcb 100644
--- a/lib/controller/api_v2/media.ml
+++ b/lib/controller/api_v2/media.ml
@@ -2,12 +2,13 @@ open Helper
open Lwt.Infix
open Util
-let post req =
- let%lwt self = authenticate_account req in
- let%lwt formdata = Httpq.Server.formdata_exn "file" req in
+let post _ req =
+ let self = authenticate_account req in
+ let formdata = Yume.Server.formdata_exn "file" req in
let result_attachment = ref None in
- let%lwt success =
+ let success =
+ Lwt_eio.run_lwt @@ fun () ->
Db.transaction @@ fun c ->
let%lwt attachment =
Model.MediaAttachment.(
diff --git a/lib/controller/api_v2/search.ml b/lib/controller/api_v2/search.ml
index dcc6f4d..f31421a 100644
--- a/lib/controller/api_v2/search.ml
+++ b/lib/controller/api_v2/search.ml
@@ -1,5 +1,4 @@
open Helper
-open Lwt.Infix
open Util
type t = {
@@ -9,71 +8,69 @@ type t = {
}
[@@deriving make, yojson_of]
-let handle_query_accounts resolve q =
+let handle_query_accounts env resolve q =
let re = Regex.e {|^@?([^@]+)(?:@([^@]+))?$|} in
- try%lwt
+ try
match Regex.match_ re q with
| [ [| _; Some { substr = username; _ }; None |] ] -> (
- match%lwt Db.(e Account.(select_by_username_prefix username)) with
+ match Db.(e Account.(select_by_username_prefix username)) with
| [] ->
- Activity.search_account ~resolve (`Webfinger (None, username))
- >|= fun a -> [ a ]
- | xs -> Lwt.return xs)
+ Activity.search_account env ~resolve (`Webfinger (None, username))
+ |> fun a -> [ a ]
+ | xs -> xs)
| [ [| _; Some username; Some domain |] ] ->
- Activity.search_account ~resolve
+ Activity.search_account env ~resolve
(`Webfinger (Some domain.substr, username.substr))
- >|= fun a -> [ a ]
- | _ -> Lwt.return []
- with _ -> Lwt.return []
+ |> fun a -> [ a ]
+ | _ -> []
+ with _ -> []
-let handle_query_uri resolve q =
+let handle_query_uri env resolve q =
let try_search_account uri =
- match%lwt Activity.search_account ~resolve (`Uri uri) with
- | exception e ->
- Logq.debug (fun m ->
- m "try_search_account failed: %s\n%s" (Printexc.to_string e)
- (Printexc.get_backtrace ()));
- Lwt.return_none
- | a -> Lwt.return_some a
+ try Some (Activity.search_account env ~resolve (`Uri uri))
+ with e ->
+ Logq.debug (fun m ->
+ m "try_search_account failed: %s\n%s" (Printexc.to_string e)
+ (Printexc.get_backtrace ()));
+ None
in
let try_fetch_status uri =
- match%lwt Activity.fetch_status ~uri with
- | exception e ->
- Logq.debug (fun m ->
- m "try_fetch_status failed: %s\n%s" (Printexc.to_string e)
- (Printexc.get_backtrace ()));
- Lwt.return_none
- | s -> Lwt.return_some s
+ try Some (Activity.fetch_status env ~uri)
+ with e ->
+ Logq.debug (fun m ->
+ m "try_fetch_status failed: %s\n%s" (Printexc.to_string e)
+ (Printexc.get_backtrace ()));
+ None
in
let list_of_option x = x |> Option.fold ~none:[] ~some:List.singleton in
if
String.(starts_with ~prefix:"http://" q || starts_with ~prefix:"https://" q)
then
- let%lwt a_opt = try_search_account q in
- let%lwt s_opt = if resolve then try_fetch_status q else Lwt.return_none in
- Lwt.return (list_of_option a_opt, list_of_option s_opt)
- else Lwt.return ([], [])
+ let a_opt = try_search_account q in
+ let s_opt = if resolve then try_fetch_status q else None in
+ (list_of_option a_opt, list_of_option s_opt)
+ else ([], [])
-let handle_query resolve q =
+let handle_query env resolve q =
(* FIXME: Support more kinds of queries *)
let q = String.trim q in
- let%lwt a1, s1 = handle_query_uri resolve q in
- let%lwt a2 = handle_query_accounts resolve q in
- let%lwt accounts =
+ let a1, s1 = handle_query_uri env resolve q in
+ let a2 = handle_query_accounts env resolve q in
+ let accounts =
a1 @ a2
|> List.map (fun (a : Db.Account.t) -> a#id)
|> Entity.load_accounts_from_db
in
- let%lwt statuses =
+ let statuses =
s1
|> List.map (fun (s : Db.Status.t) -> s#id)
|> Entity.load_statuses_from_db
in
let hashtags = [] in
- make ~accounts ~statuses ~hashtags () |> Lwt.return
+ make ~accounts ~statuses ~hashtags ()
-let get req =
- let%lwt self = may_authenticate_account req in
- let%lwt q = req |> Httpq.Server.query "q" in
+let get env req =
+ let self = may_authenticate_account req in
+ let q = req |> Yume.Server.query "q" in
Logq.debug (fun m -> m "[/api/v2/search] %b %s" (Option.is_some self) q);
- handle_query (Option.is_some self) q >|= yojson_of_t >>= respond_yojson
+ handle_query env (Option.is_some self) q |> yojson_of_t |> respond_yojson
diff --git a/lib/controller/helper.ml b/lib/controller/helper.ml
index 007fc60..0afa23d 100644
--- a/lib/controller/helper.ml
+++ b/lib/controller/helper.ml
@@ -1,5 +1,4 @@
open Util
-open Lwt.Infix
let app_activity_json = "application/activity+json"
let text_html = "text/html"
@@ -13,10 +12,10 @@ let content_type_app_jrd_json =
let content_type_app_json = (`Content_type, "application/json; charset=utf-8")
let content_type_app_activity_json = (`Content_type, "application/activity+json")
let content_type_text_html = (`Content_type, "text/html; charset=utf-8")
-let raise_error_response = Httpq.Server.raise_error_response
+let raise_error_response = Yume.Server.raise_error_response
let authenticate_bearer = function
- | Httpq.Server.Request r -> (
+ | Yume.Server.Request r -> (
try
let header = r.headers |> List.assoc `Authorization in
assert (String.starts_with ~prefix:"Bearer " header);
@@ -24,23 +23,22 @@ let authenticate_bearer = function
Oauth_helper.authenticate_access_token bearer_token
with _ -> raise_error_response `Unauthorized)
-let authenticate_user (r : Httpq.Server.request) : Model.User.t Lwt.t =
+let authenticate_user (r : Yume.Server.request) : Model.User.t =
try
- let%lwt token = authenticate_bearer r in
+ let token = authenticate_bearer r in
Db.(e User.(get_one ~id:(Option.get token#resource_owner_id)))
with _ -> raise_error_response `Unauthorized
-let authenticate_account (r : Httpq.Server.request) : Model.Account.t Lwt.t =
+let authenticate_account (r : Yume.Server.request) : Model.Account.t =
try
- let%lwt user = authenticate_user r in
+ let user = authenticate_user r in
Db.(e Account.(get_one ~id:user#account_id))
with _ -> raise_error_response `Unauthorized
-let may_authenticate_user r =
- try%lwt authenticate_user r >|= Option.some with _ -> Lwt.return_none
+let may_authenticate_user r = try Some (authenticate_user r) with _ -> None
let may_authenticate_account r =
- try%lwt authenticate_account r >|= Option.some with _ -> Lwt.return_none
+ try Some (authenticate_account r) with _ -> None
let int_of_string s =
match int_of_string_opt s with
@@ -53,19 +51,19 @@ let bool_of_string s =
| Some b -> b
let respond_html ?(headers = []) s =
- Httpq.Server.respond ~headers:(content_type_text_html :: headers) s
+ Yume.Server.respond ~headers:(content_type_text_html :: headers) s
let respond_yojson ?(headers = []) y =
Yojson.Safe.to_string y
- |> Httpq.Server.respond ~headers:(content_type_app_json :: headers)
+ |> Yume.Server.respond ~headers:(content_type_app_json :: headers)
let respond_activity_yojson y =
Yojson.Safe.to_string y
- |> Httpq.Server.respond ~headers:[ content_type_app_activity_json ]
+ |> Yume.Server.respond ~headers:[ content_type_app_activity_json ]
let render ~default routes req =
let accept =
- Httpq.Server.header_opt `Accept req
+ Yume.Server.header_opt `Accept req
|> Option.map (String.split_on_char ',' |.> List.map String.trim)
in
let default = ("default", default) in
@@ -90,7 +88,7 @@ let parse_webfinger_address q =
| _ -> raise_error_response `Bad_request
let raise_if_no_row_found ?(status = `Bad_request) f =
- try%lwt f with Sqlx.Error.NoRowFound -> raise_error_response status
+ try f with Sqlx.Error.NoRowFound -> raise_error_response status
let string_to_status_id s = s |> int_of_string |> Model.Status.ID.of_int
let string_to_account_id s = s |> int_of_string |> Model.Account.ID.of_int
diff --git a/lib/controller/inbox.ml b/lib/controller/inbox.ml
index ab525ab..7b22ec0 100644
--- a/lib/controller/inbox.ml
+++ b/lib/controller/inbox.ml
@@ -2,148 +2,144 @@ open Util
open Activity
(* Recv Follow in inbox *)
-let kick_inbox_follow (req : ap_follow) =
+let kick_inbox_follow env (req : ap_follow) =
let src, dst =
match (req.actor, req.obj) with
| s, d when is_my_domain d -> (s, d)
- | _ -> Httpq.Server.raise_error_response `Bad_request
+ | _ -> Yume.Server.raise_error_response `Bad_request
in
- let%lwt src = search_account (`Uri src) in
- match%lwt Db.e (Model.Account.get_one ~uri:dst) with
- | exception Sqlx.Error.NoRowFound ->
- Httpq.Server.raise_error_response `Bad_request
- | dst ->
- Job.kick ~name:__FUNCTION__ @@ fun () ->
- let%lwt f =
- match%lwt
- Db.(e @@ Follow.get_one ~account_id:src#id ~target_account_id:dst#id)
- with
- | f -> Lwt.return f
- | exception Sqlx.Error.NoRowFound ->
- (* Insert to table 'follows' *)
- let now = Ptime.now () in
- let%lwt f =
- Db.(
- e
- @@ Follow.(
- make ~created_at:now ~updated_at:now ~account_id:src#id
- ~target_account_id:dst#id ~uri:req.id ()
- |> save_one))
- in
- Worker.Local_notify.kick
- ~activity_id:(Model.Follow.ID.to_int f#id)
- ~activity_type:`Follow ~src ~dst ~typ:`follow;%lwt
- Lwt.return f
+ let src = search_account env (`Uri src) in
+ let dst =
+ try Db.e (Model.Account.get_one ~uri:dst)
+ with Sqlx.Error.NoRowFound ->
+ Yume.Server.raise_error_response `Bad_request
+ in
+ Job.kick env ~name:__FUNCTION__ @@ fun () ->
+ let f =
+ try Db.(e @@ Follow.get_one ~account_id:src#id ~target_account_id:dst#id)
+ with Sqlx.Error.NoRowFound ->
+ (* Insert to table 'follows' *)
+ let now = Ptime.now () in
+ let f =
+ Db.(
+ e
+ @@ Follow.(
+ make ~created_at:now ~updated_at:now ~account_id:src#id
+ ~target_account_id:dst#id ~uri:req.id ()
+ |> save_one))
in
+ Worker.Local_notify.kick env
+ ~activity_id:(Model.Follow.ID.to_int f#id)
+ ~activity_type:`Follow ~src ~dst ~typ:`follow;
+ f
+ in
- (* Send 'Accept' *)
- Worker.Accept.kick ~f ~followee:dst ~follower:src
+ (* Send 'Accept' *)
+ Worker.Accept.kick env ~f ~followee:dst ~follower:src
(* Recv Accept in inbox *)
-let kick_inbox_accept (req : ap_accept) =
+let kick_inbox_accept env (req : ap_accept) =
let uri =
match req.obj with
| Follow { id; _ } -> id
- | _ -> Httpq.Server.raise_error_response `Bad_request
+ | _ -> Yume.Server.raise_error_response `Bad_request
in
- match%lwt Db.(e @@ FollowRequest.get_one ~uri) with
- | exception Sqlx.Error.NoRowFound ->
- Httpq.Server.raise_error_response `Bad_request
- | r ->
- Job.kick ~name:__FUNCTION__ @@ fun () ->
- let now = Ptime.now () in
- Db.(e @@ FollowRequest.delete [ r ]) |> ignore_lwt;%lwt
- Db.(
- e
- @@ Follow.(
- make ~account_id:r#account_id
- ~target_account_id:r#target_account_id ~uri ~created_at:now
- ~updated_at:now ()
- |> save_one))
- |> ignore_lwt
+ let r =
+ try Db.(e @@ FollowRequest.get_one ~uri)
+ with Sqlx.Error.NoRowFound ->
+ Yume.Server.raise_error_response `Bad_request
+ in
+ Job.kick env ~name:__FUNCTION__ @@ fun () ->
+ let now = Ptime.now () in
+ Db.(e @@ FollowRequest.delete [ r ]) |> ignore;
+ Db.(
+ e
+ @@ Follow.(
+ make ~account_id:r#account_id ~target_account_id:r#target_account_id
+ ~uri ~created_at:now ~updated_at:now ()
+ |> save_one))
+ |> ignore
-let kick_inbox_undo_like (l : ap_like) =
- Job.kick ~name:__FUNCTION__ @@ fun () ->
- let%lwt fav = favourite_of_like ~must_already_exist:true l in
+let kick_inbox_undo_like env (l : ap_like) =
+ Job.kick env ~name:__FUNCTION__ @@ fun () ->
+ let fav = favourite_of_like ~must_already_exist:true l in
Db.(e @@ Favourite.delete [ fav ])
-let kick_inbox_undo_follow ({ id; _ } : ap_follow) =
- Job.kick ~name:__FUNCTION__ @@ fun () ->
- let%lwt follow = Db.(e @@ Follow.get_one ~uri:id) in
+let kick_inbox_undo_follow env ({ id; _ } : ap_follow) =
+ Job.kick env ~name:__FUNCTION__ @@ fun () ->
+ let follow = Db.(e @@ Follow.get_one ~uri:id) in
Db.(e @@ Follow.delete [ follow ])
-let kick_inbox_undo_announce (a : ap_announce) =
- Job.kick ~name:__FUNCTION__ @@ fun () ->
- let%lwt account = Db.e (Model.Account.get_one ~uri:a.actor) in
- let%lwt status = Db.(e Status.(get_one ~uri:a.id)) in
- Worker.Removal.kick ~account_id:account#id ~status_id:status#id
+let kick_inbox_undo_announce env (a : ap_announce) =
+ Job.kick env ~name:__FUNCTION__ @@ fun () ->
+ let account = Db.e (Model.Account.get_one ~uri:a.actor) in
+ let status = Db.(e Status.(get_one ~uri:a.id)) in
+ Worker.Removal.kick env ~account_id:account#id ~status_id:status#id
(* Recv Create in inbox *)
-let kick_inbox_create (req : ap_create) =
+let kick_inbox_create env (req : ap_create) =
let note =
match req with
| { obj = Note note; _ } -> note
- | _ -> Httpq.Server.raise_error_response `Bad_request
+ | _ -> Yume.Server.raise_error_response `Bad_request
in
- Job.kick ~name:__FUNCTION__ @@ fun () ->
- let%lwt s = status_of_note note in
- Worker.Link_crawl.kick s#id;%lwt
- Worker.Distribute.kick s
+ Job.kick env ~name:__FUNCTION__ @@ fun () ->
+ let s = status_of_note env note in
+ Worker.Link_crawl.kick env s#id;
+ Worker.Distribute.kick env s
(* Recv Announce in inbox *)
-let kick_inbox_announce (req : ap_announce) =
- Job.kick ~name:__FUNCTION__ @@ fun () ->
- let%lwt s = status_of_announce req in
- Worker.Distribute.kick s;%lwt
- let%lwt s' = Db.e (Model.Status.get_one ~id:(Option.get s#reblog_of_id)) in
- let%lwt dst = Db.e (Model.Account.get_one ~id:s'#account_id) in
+let kick_inbox_announce env (req : ap_announce) =
+ Job.kick env ~name:__FUNCTION__ @@ fun () ->
+ let s = status_of_announce env req in
+ Worker.Distribute.kick env s;
+ let s' = Db.e (Model.Status.get_one ~id:(Option.get s#reblog_of_id)) in
+ let dst = Db.e (Model.Account.get_one ~id:s'#account_id) in
if Model.Account.is_local dst then
- let%lwt src = Db.e (Model.Account.get_one ~id:s#account_id) in
- Worker.Local_notify.kick
+ let src = Db.e (Model.Account.get_one ~id:s#account_id) in
+ Worker.Local_notify.kick env
~activity_id:(Model.Status.ID.to_int s#id)
~activity_type:`Status ~typ:`reblog ~src ~dst
- else Lwt.return_unit
(* Recv Like in inbox *)
-let kick_inbox_like (req : ap_like) =
- Job.kick ~name:__FUNCTION__ @@ fun () ->
- let%lwt f = favourite_of_like req in
- let%lwt src = Db.e (Model.Account.get_one ~id:f#account_id) in
- let%lwt s = Db.e (Model.Status.get_one ~id:f#status_id) in
- let%lwt dst = Db.e (Model.Account.get_one ~id:s#account_id) in
- Worker.Local_notify.kick
+let kick_inbox_like env (req : ap_like) =
+ Job.kick env ~name:__FUNCTION__ @@ fun () ->
+ let f = favourite_of_like req in
+ let src = Db.e (Model.Account.get_one ~id:f#account_id) in
+ let s = Db.e (Model.Status.get_one ~id:f#status_id) in
+ let dst = Db.e (Model.Account.get_one ~id:s#account_id) in
+ Worker.Local_notify.kick env
~activity_id:(Model.Favourite.ID.to_int f#id)
- ~activity_type:`Favourite ~typ:`favourite ~src ~dst;%lwt
- Lwt.return_unit
+ ~activity_type:`Favourite ~typ:`favourite ~src ~dst
-let kick_inbox_delete (req : ap_delete) =
- match%lwt Db.e (Model.Account.get_one ~uri:req.actor) with
+let kick_inbox_delete env (req : ap_delete) =
+ match Db.e (Model.Account.get_one ~uri:req.actor) with
| exception Sqlx.Error.NoRowFound ->
(* Unknown account; just ignore *)
- Lwt.return_unit
+ ()
| account -> (
(* FIXME: Support deletion of accounts *)
match req.obj |> of_yojson |> get_tombstone with
- | (exception _) | None ->
- (* Unknown object; just ignore *) Lwt.return_unit
+ | (exception _) | None -> (* Unknown object; just ignore *) ()
| Some tomb -> (
- match%lwt Db.(e Status.(get_many ~uri:tomb.id)) with
- | [] -> Lwt.return_unit
+ match Db.(e Status.(get_many ~uri:tomb.id)) with
+ | [] -> ()
| [ status ] ->
- Worker.Removal.kick ~account_id:account#id ~status_id:status#id
+ Worker.Removal.kick env ~account_id:account#id
+ ~status_id:status#id
| _ :: _ -> failwith "Internal error: not unique status uri"))
let kick_inbox_update_person (r : ap_person) =
- let%lwt a = Db.e (Model.Account.get_one ~uri:r.id) in
+ let a = Db.e (Model.Account.get_one ~uri:r.id) in
let a = Activity.model_account_of_person ~original:a r in
- Db.(e @@ Account.save_one a) |> ignore_lwt
+ Db.(e @@ Account.save_one a) |> ignore
(* Recv POST /users/:name/inbox *)
-let post req =
- match%lwt Activity.verify_activity_json req with
+let post env req =
+ match Activity.verify_activity_json env req with
| body, Error (`AccountNotFound | `AccountIsLocal) ->
Logq.err (fun m -> m "account not found or account is local: %s" body);
- Httpq.Server.respond ~status:`Accepted ""
+ Yume.Server.respond ~status:`Accepted ""
| body, Error (`VerifFailure e) ->
Logq.err (fun m ->
m "verification failure of activity json: %s: %s"
@@ -151,23 +147,23 @@ let post req =
| `AlgorithmNotImplemented -> "algorithm not implemented"
| `Msg msg -> msg)
body);
- Httpq.Server.respond ~status:`Unauthorized ""
+ Yume.Server.respond ~status:`Unauthorized ""
| body, Ok () -> (
- try%lwt
+ try
(*Logq.debug (fun m -> m ">>>>>>>>\n%s" body);*)
(match Yojson.Safe.from_string body |> of_yojson with
- | Accept r -> kick_inbox_accept r
- | Announce r -> kick_inbox_announce r
- | Create r -> kick_inbox_create r
- | Delete r -> kick_inbox_delete r
- | Follow r -> kick_inbox_follow r
- | Like r -> kick_inbox_like r
- | Undo { obj = Follow v; _ } -> kick_inbox_undo_follow v
- | Undo { obj = Like v; _ } -> kick_inbox_undo_like v
- | Undo { obj = Announce v; _ } -> kick_inbox_undo_announce v
+ | Accept r -> kick_inbox_accept env r
+ | Announce r -> kick_inbox_announce env r
+ | Create r -> kick_inbox_create env r
+ | Delete r -> kick_inbox_delete env r
+ | Follow r -> kick_inbox_follow env r
+ | Like r -> kick_inbox_like env r
+ | Undo { obj = Follow v; _ } -> kick_inbox_undo_follow env v
+ | Undo { obj = Like v; _ } -> kick_inbox_undo_like env v
+ | Undo { obj = Announce v; _ } -> kick_inbox_undo_announce env v
| Update { obj = Person r; _ } -> kick_inbox_update_person r
- | _ -> failwith "activity not implemented");%lwt
- Httpq.Server.respond ~status:`Accepted ""
+ | _ -> failwith "activity not implemented");
+ Yume.Server.respond ~status:`Accepted ""
with e ->
Logq.err (fun m ->
m
@@ -177,4 +173,4 @@ let post req =
%s"
(Printexc.to_string e)
(Printexc.get_backtrace ()));
- Httpq.Server.respond ~status:`Accepted ~tags:[ "log" ] "")
+ Yume.Server.respond ~status:`Accepted ~tags:[ "log" ] "")
diff --git a/lib/controller/nodeinfo.ml b/lib/controller/nodeinfo.ml
index 9831eb4..99852c4 100644
--- a/lib/controller/nodeinfo.ml
+++ b/lib/controller/nodeinfo.ml
@@ -1,6 +1,6 @@
open Helper
-let get_2_0 _req =
+let get_2_0 _ _req =
(* FIXME: Fill in correct values *)
`Assoc
[
diff --git a/lib/controller/oauth/authorize.ml b/lib/controller/oauth/authorize.ml
index d716ebc..a4c10eb 100644
--- a/lib/controller/oauth/authorize.ml
+++ b/lib/controller/oauth/authorize.ml
@@ -1,36 +1,38 @@
open Helper
-let post req =
- let%lwt response_type = req |> Httpq.Server.query "response_type" in
- let%lwt client_id = req |> Httpq.Server.query "client_id" in
- let%lwt redirect_uri = req |> Httpq.Server.query "redirect_uri" in
- let%lwt scope = req |> Httpq.Server.query ~default:"read" "scope" in
- let%lwt state = req |> Httpq.Server.query_opt "state" in
- let%lwt username = req |> Httpq.Server.query "username" in
- let%lwt password = req |> Httpq.Server.query "password" in
+let post _env req =
+ let response_type = req |> Yume.Server.query "response_type" in
+ let client_id = req |> Yume.Server.query "client_id" in
+ let redirect_uri = req |> Yume.Server.query "redirect_uri" in
+ let scope = req |> Yume.Server.query ~default:"read" "scope" in
+ let state = req |> Yume.Server.query_opt "state" in
+ let username = req |> Yume.Server.query "username" in
+ let password = req |> Yume.Server.query "password" in
if response_type <> "code" then raise_error_response `Bad_request;
- let%lwt app = Oauth_helper.authenticate_application client_id in
+ let app = Oauth_helper.authenticate_application client_id in
(* FIXME: Check if scope is correct *)
if redirect_uri <> app#redirect_uri then raise_error_response `Bad_request;
- let%lwt account =
- Db.(e Account.(get_one ~domain:None ~username)) |> raise_if_no_row_found
+ let account =
+ try Db.(e Account.(get_one ~domain:None ~username))
+ with Sqlx.Error.NoRowFound -> raise_error_response `Bad_request
in
- let%lwt user =
- Db.(e User.(get_one ~account_id:account#id)) |> raise_if_no_row_found
+ let user =
+ try Db.(e User.(get_one ~account_id:account#id))
+ with Sqlx.Error.NoRowFound -> raise_error_response `Bad_request
in
if not Bcrypt.(verify password (hash_of_string user#encrypted_password)) then
raise_error_response `Unauthorized;
let resource_owner_id = user#id in
- let%lwt grant =
+ let grant =
Oauth_helper.generate_access_grant ~expires_in:600 ~redirect_uri
~scopes:scope ~app ~resource_owner_id
in
if grant#redirect_uri = "urn:ietf:wg:oauth:2.0:oob" then
- Httpq.Server.respond grant#token
+ Yume.Server.respond grant#token
else
let u = Uri.of_string grant#redirect_uri in
let u = Uri.add_query_param u ("code", [ grant#token ]) in
@@ -39,16 +41,16 @@ let post req =
|> Option.fold ~none:u ~some:(fun s ->
Uri.add_query_param u ("state", [ s ]))
in
- Httpq.Server.respond ~status:`Found
+ Yume.Server.respond ~status:`Found
~headers:[ (`Location, Uri.to_string u) ]
""
-let get req =
- let%lwt response_type = req |> Httpq.Server.query "response_type" in
- let%lwt client_id = req |> Httpq.Server.query "client_id" in
- let%lwt redirect_uri = req |> Httpq.Server.query "redirect_uri" in
- let%lwt scope = req |> Httpq.Server.query ~default:"read" "scope" in
- let%lwt state = req |> Httpq.Server.query_opt "state" in
+let get _ req =
+ let response_type = req |> Yume.Server.query "response_type" in
+ let client_id = req |> Yume.Server.query "client_id" in
+ let redirect_uri = req |> Yume.Server.query "redirect_uri" in
+ let scope = req |> Yume.Server.query ~default:"read" "scope" in
+ let state = req |> Yume.Server.query_opt "state" in
let models =
let open Jingoo.Jg_types in
diff --git a/lib/controller/oauth/token.ml b/lib/controller/oauth/token.ml
index 6ca6665..961de0e 100644
--- a/lib/controller/oauth/token.ml
+++ b/lib/controller/oauth/token.ml
@@ -9,38 +9,38 @@ type res = {
}
[@@deriving make, yojson]
-let post req =
- let%lwt grant_type = req |> Httpq.Server.query "grant_type" in
- let%lwt code = req |> Httpq.Server.query_opt "code" in
- let%lwt client_id = req |> Httpq.Server.query "client_id" in
- let%lwt client_secret = req |> Httpq.Server.query "client_secret" in
- let%lwt redirect_uri = req |> Httpq.Server.query "redirect_uri" in
- let%lwt scope = req |> Httpq.Server.query ~default:"read" "scope" in
+let post _ req =
+ let grant_type = req |> Yume.Server.query "grant_type" in
+ let code = req |> Yume.Server.query_opt "code" in
+ let client_id = req |> Yume.Server.query "client_id" in
+ let client_secret = req |> Yume.Server.query "client_secret" in
+ let redirect_uri = req |> Yume.Server.query "redirect_uri" in
+ let scope = req |> Yume.Server.query ~default:"read" "scope" in
- let%lwt token =
+ let token =
match (grant_type, code) with
| "authorization_code", Some code ->
- let%lwt grant = Oauth_helper.authenticate_access_grant code in
+ let grant = Oauth_helper.authenticate_access_grant code in
(* FIXME: Check if scope is correct *)
- let%lwt app =
+ let app =
Db.(
e OAuthApplication.(get_one ~id:(Option.get grant#application_id)))
in
if app#redirect_uri <> redirect_uri then
- Httpq.Server.raise_error_response `Bad_request;
+ Yume.Server.raise_error_response `Bad_request;
if app#uid <> client_id || app#secret <> client_secret then
- Httpq.Server.raise_error_response `Unauthorized;
+ Yume.Server.raise_error_response `Unauthorized;
if
let open Ptime in
grant#expires_in
< (diff (now ()) grant#created_at |> Span.to_int_s |> Option.get)
- then Httpq.Server.raise_error_response `Bad_request;
+ then Yume.Server.raise_error_response `Bad_request;
Oauth_helper.generate_access_token ~scopes:scope
~resource_owner_id:(Option.get grant#resource_owner_id)
~app ()
| "client_credentials", None ->
- let%lwt app = Db.(e OAuthApplication.(get_one ~uid:client_id)) in
+ let app = Db.(e OAuthApplication.(get_one ~uid:client_id)) in
if app#secret <> client_secret then raise_error_response `Unauthorized;
Oauth_helper.generate_access_token ~scopes:scope ~app ()
| _ -> raise_error_response `Bad_request
@@ -50,4 +50,4 @@ let post req =
~created_at:
(token#created_at |> Ptime.to_span |> Ptime.Span.to_int_s |> Option.get)
|> yojson_of_res |> Yojson.Safe.to_string
- |> Httpq.Server.respond ~headers:[ Helper.content_type_app_json ]
+ |> Yume.Server.respond ~headers:[ Helper.content_type_app_json ]
diff --git a/lib/controller/root.ml b/lib/controller/root.ml
index d2b294d..17fcddd 100644
--- a/lib/controller/root.ml
+++ b/lib/controller/root.ml
@@ -1,6 +1,6 @@
open Helper
-let get _req =
+let get _ _req =
let open Jingoo.Jg_types in
let models = [ ("server_name", Tstr (Config.server_name ())) ] in
{|
diff --git a/lib/controller/static.ml b/lib/controller/static.ml
index 992d676..6dc1a5d 100644
--- a/lib/controller/static.ml
+++ b/lib/controller/static.ml
@@ -15,16 +15,16 @@ let get_body path =
s
with _ -> raise_error_response `Not_found
-let get req =
+let get _ req =
let root = Config.static_root () |> Unix.realpath in
let path =
- try Filename.concat root (Httpq.Server.path req) |> Unix.realpath
+ try Filename.concat root (Yume.Server.path req) |> Unix.realpath
with Unix.Unix_error (Unix.ENOENT, "realpath", _) ->
raise_error_response `Not_found
in
if not (String.starts_with ~prefix:root path) then
raise_error_response `Not_found
else
- Httpq.Server.respond
+ Yume.Server.respond
~headers:[ (`Content_type, Filename.extension path |> get_content_type) ]
(get_body path)
diff --git a/lib/controller/users/following.ml b/lib/controller/users/following.ml
index 17313a7..7439e66 100644
--- a/lib/controller/users/following.ml
+++ b/lib/controller/users/following.ml
@@ -1,24 +1,24 @@
open Util
open Helper
-let get_followers req =
+let get_followers _env req =
(* FIXME: Support ?page and ?min_id *)
- let username = Httpq.Server.param ":name" req in
- let%lwt acct = Db.e (Model.Account.get_one ~username ~domain:None) in
+ let username = Yume.Server.param ":name" req in
+ let acct = Db.e (Model.Account.get_one ~username ~domain:None) in
let id = acct#followers_url in
- let%lwt totalItems = Db.(e @@ count_followers ~account_id:acct#id) in
+ let totalItems = Db.(e @@ count_followers ~account_id:acct#id) in
let first = acct#followers_url ^ "?page=1" in
Activity.(
make_ordered_collection ~id ~totalItems ~first ()
|> ordered_collection |> to_yojson)
|> respond_yojson
-let get_following req =
+let get_following _env req =
(* FIXME: Support ?page and ?min_id *)
- let username = Httpq.Server.param ":name" req in
- let%lwt acct = Db.e (Model.Account.get_one ~username ~domain:None) in
+ let username = Yume.Server.param ":name" req in
+ let acct = Db.e (Model.Account.get_one ~username ~domain:None) in
let id = acct#uri ^/ "following" in
- let%lwt totalItems = Db.(e @@ count_following ~account_id:acct#id) in
+ let totalItems = Db.(e @@ count_following ~account_id:acct#id) in
let first = id ^ "?page=1" in
Activity.(
make_ordered_collection ~id ~totalItems ~first ()
diff --git a/lib/controller/users/outbox.ml b/lib/controller/users/outbox.ml
index 3be40e5..668a06a 100644
--- a/lib/controller/users/outbox.ml
+++ b/lib/controller/users/outbox.ml
@@ -1,11 +1,11 @@
open Helper
-let get req =
+let get _ req =
(* FIXME: Support ?page and ?min_id *)
- let username = Httpq.Server.param ":name" req in
- let%lwt acct = Db.e (Model.Account.get_one ~username ~domain:None) in
+ let username = Yume.Server.param ":name" req in
+ let acct = Db.e (Model.Account.get_one ~username ~domain:None) in
let id = acct#outbox_url in
- let%lwt totalItems = Db.(e @@ Status.count ~account_id:(`Eq acct#id)) in
+ let totalItems = Db.(e @@ Status.count ~account_id:(`Eq acct#id)) in
let first = acct#outbox_url ^ "?page=true" in
let last = acct#outbox_url ^ "?min_id=0&page=true" in
Activity.(
diff --git a/lib/controller/users/root.ml b/lib/controller/users/root.ml
index 30fcd19..6888aa4 100644
--- a/lib/controller/users/root.ml
+++ b/lib/controller/users/root.ml
@@ -1,5 +1,4 @@
open Helper
-open Lwt.Infix
open Util
let respond_activity a () =
@@ -8,13 +7,13 @@ let respond_activity a () =
let respond_html (a : Db.Account.t) () =
let open Jingoo.Jg_types in
- let%lwt statuses =
+ let statuses =
Db.(
e
@@ account_statuses ~id:a#id ~limit:30 ~max_id:None ~since_id:None
~exclude_replies:false)
- >|= List.map (fun (s : Db.Status.t) -> s#id)
- >>= Entity.load_statuses_from_db
+ |> List.map (fun (s : Db.Status.t) -> s#id)
+ |> Entity.load_statuses_from_db
in
let status_to_Tobj (s : Entity.status) =
let rec aux (s : Entity.status) =
@@ -78,13 +77,13 @@ let respond_html (a : Db.Account.t) () =
|> String.trim |> respond_html
(* Recv GET /users/:name *)
-let get req =
- let username = req |> Httpq.Server.param ":name" in
- match%lwt Db.e (Model.Account.get_one ~domain:None ~username) with
- | exception Sqlx.Error.NoRowFound ->
- Httpq.Server.raise_error_response `Not_found
- | a ->
- let%lwt _ = Db.(e @@ User.get_one ~account_id:a#id) in
- req
- |> render ~default:(respond_html a)
- [ (app_activity_json, respond_activity a) ]
+let get _env req =
+ let username = req |> Yume.Server.param ":name" in
+ let a =
+ try Db.e (Model.Account.get_one ~domain:None ~username)
+ with Sqlx.Error.NoRowFound -> Yume.Server.raise_error_response `Not_found
+ in
+ let _ = Db.(e @@ User.get_one ~account_id:a#id) in
+ req
+ |> render ~default:(respond_html a)
+ [ (app_activity_json, respond_activity a) ]
diff --git a/lib/controller/users/statuses.ml b/lib/controller/users/statuses.ml
index d5475b8..27c688e 100644
--- a/lib/controller/users/statuses.ml
+++ b/lib/controller/users/statuses.ml
@@ -1,13 +1,12 @@
open Activity
-open Lwt.Infix
open Helper
-let get req =
- let _username = req |> Httpq.Server.param ":name" in
+let get _ req =
+ let _username = req |> Yume.Server.param ":name" in
let status_id =
- req |> Httpq.Server.param ":id" |> int_of_string |> Model.Status.ID.of_int
+ req |> Yume.Server.param ":id" |> int_of_string |> Model.Status.ID.of_int
in
- match%lwt Db.e (Model.Status.get_one ~id:status_id) with
+ match Db.e (Model.Status.get_one ~id:status_id) with
| exception Sqlx.Error.NoRowFound ->
- Httpq.Server.raise_error_response `Not_found
- | s -> note_of_status s >|= of_note >|= to_yojson >>= respond_activity_yojson
+ Yume.Server.raise_error_response `Not_found
+ | s -> note_of_status s |> of_note |> to_yojson |> respond_activity_yojson
diff --git a/lib/controller/well_known/host_meta.ml b/lib/controller/well_known/host_meta.ml
index ad63308..7d0ebb4 100644
--- a/lib/controller/well_known/host_meta.ml
+++ b/lib/controller/well_known/host_meta.ml
@@ -1,5 +1,5 @@
(* Recv GET /.well-known/host-meta *)
-let get _req =
+let get _env _req =
let url = Config.url [ ".well-known"; "webfinger" ] in
Jingoo.Jg_template.from_string
~models:[ ("url", Tstr url) ]
@@ -7,4 +7,4 @@ let get _req =
|}
- |> Httpq.Server.respond ~headers:[ Helper.content_type_app_xrd_xml ]
+ |> Yume.Server.respond ~headers:[ Helper.content_type_app_xrd_xml ]
diff --git a/lib/controller/well_known/nodeinfo.ml b/lib/controller/well_known/nodeinfo.ml
index bcb94fd..eadf1da 100644
--- a/lib/controller/well_known/nodeinfo.ml
+++ b/lib/controller/well_known/nodeinfo.ml
@@ -1,6 +1,6 @@
open Helper
-let get _req =
+let get _ _req =
`Assoc
[
( "links",
diff --git a/lib/controller/well_known/webfinger.ml b/lib/controller/well_known/webfinger.ml
index 737b73e..535e150 100644
--- a/lib/controller/well_known/webfinger.ml
+++ b/lib/controller/well_known/webfinger.ml
@@ -4,7 +4,7 @@ open Util
let respond_jrd w () =
w |> yojson_of_webfinger |> Yojson.Safe.to_string
- |> Httpq.Server.respond ~headers:[ Helper.content_type_app_jrd_json ]
+ |> Yume.Server.respond ~headers:[ Helper.content_type_app_jrd_json ]
let respond_xrd w () =
({||}
@@ -27,12 +27,12 @@ let respond_xrd w () =
],
[] ))) )
|> to_string))
- |> Httpq.Server.respond ~headers:[ Helper.content_type_app_xrd_xml ]
+ |> Yume.Server.respond ~headers:[ Helper.content_type_app_xrd_xml ]
(* Recv GET /.well-known/webfinger *)
-let get req =
- try%lwt
- let%lwt s = req |> Httpq.Server.query "resource" in
+let get _ req =
+ try
+ let s = req |> Yume.Server.query "resource" in
let s =
(* Remove 'acct:' prefix if exists *)
if String.starts_with ~prefix:"acct:" s then
@@ -45,8 +45,8 @@ let get req =
failwith "Invalid request";
(* Return the body *)
let name, dom = (List.hd s, List.nth s 1) in
- let%lwt a = Db.(e & Account.get_one ~domain:None ~username:name) in
- let%lwt _ = Db.(e & User.get_one ~account_id:a#id) in
+ let a = Db.(e & Account.get_one ~domain:None ~username:name) in
+ let _ = Db.(e & User.get_one ~account_id:a#id) in
let res =
make_webfinger
~subject:("acct:" ^ name ^ "@" ^ dom)
@@ -67,4 +67,4 @@ let get req =
m "[well_known_webfinger] Can't find user: %s\n%s"
(Printexc.to_string e)
(Printexc.get_backtrace ()));
- Httpq.Server.raise_error_response `Not_found
+ Yume.Server.raise_error_response `Not_found
diff --git a/lib/crypto.ml b/lib/crypto.ml
index 7cb6b76..496b8d7 100644
--- a/lib/crypto.ml
+++ b/lib/crypto.ml
@@ -1,4 +1,5 @@
-let initialize () = Mirage_crypto_rng_lwt.initialize ()
+let initialize env f =
+ Mirage_crypto_rng_eio.run (module Mirage_crypto_rng.Fortuna) env f
module SecureRandom = struct
let generate len = Mirage_crypto_rng.generate len |> Cstruct.to_string
diff --git a/lib/db.ml b/lib/db.ml
index 8cdeb3c..788ee66 100644
--- a/lib/db.ml
+++ b/lib/db.ml
@@ -27,9 +27,9 @@ open Util
let register_user ~username ~display_name ~email ~password =
let now = Ptime.now () in
let created_at, updated_at = (now, now) in
- let private_key, public_key = Httpq.Signature.generate_keypair () in
- let public_key = Httpq.Signature.encode_public_key public_key in
- let private_key = Httpq.Signature.encode_private_key private_key in
+ let private_key, public_key = Yume.Signature.generate_keypair () in
+ let public_key = Yume.Signature.encode_public_key public_key in
+ let private_key = Yume.Signature.encode_private_key private_key in
let uri = Config.url [ "users"; username ] in
let inbox_url = uri ^/ "inbox" in
let outbox_url = uri ^/ "outbox" in
@@ -58,3 +58,4 @@ let register_user ~username ~display_name ~email ~password =
let initialize () = initialize (Config.db_url ())
let transaction f = e (fun c -> c#transaction f)
+let e x = Lwt_eio.run_lwt @@ fun () -> e x
diff --git a/lib/dune b/lib/dune
index a6e4303..fc9fa12 100644
--- a/lib/dune
+++ b/lib/dune
@@ -32,7 +32,6 @@
eio
eio_main
fpath
- httpq
jingoo
lambdasoup
logq
@@ -40,7 +39,7 @@
lwt_eio
lwt.unix
mirage-crypto-rng
- mirage-crypto-rng.lwt
+ mirage-crypto-rng-eio
pcre
postgresql
ptime
@@ -53,7 +52,8 @@
xml-light
yaml
yaml.unix
- yojson)
+ yojson
+ yume)
(flags :standard -warn-error -33))
(include_subdirs qualified)
diff --git a/lib/entity.ml b/lib/entity.ml
index 4d199d9..af8b2ed 100644
--- a/lib/entity.ml
+++ b/lib/entity.ml
@@ -1,5 +1,4 @@
open Util
-open Lwt.Infix
(* Entity account *)
type emoji = {
@@ -99,18 +98,19 @@ let serialize_account ?(credential = false) (a : Model.Account.t) : account =
~following_count:stat#following_count ?source ()
let load_accounts_from_db ?(credential = false)
- (account_ids : Model.Account.ID.t list) : account list Lwt.t =
- Db.(e Account.(select ~id:(`In account_ids) ~preload:[ `stat [] ]))
- >|= index_by (fun x -> x#id)
- >|= fun accts ->
+ (account_ids : Model.Account.ID.t list) : account list =
+ let accts =
+ Db.(e Account.(select ~id:(`In account_ids) ~preload:[ `stat [] ]))
+ |> index_by (fun x -> x#id)
+ in
account_ids
|> List.map (fun id -> Hashtbl.find accts id |> serialize_account ~credential)
-let make_account_from_model (a : Db.Account.t) : account Lwt.t =
- load_accounts_from_db [ a#id ] >|= List.hd
+let make_account_from_model (a : Db.Account.t) : account =
+ load_accounts_from_db [ a#id ] |> List.hd
-let make_credential_account_from_model (a : Db.Account.t) : account Lwt.t =
- load_accounts_from_db ~credential:true [ a#id ] >|= List.hd
+let make_credential_account_from_model (a : Db.Account.t) : account =
+ load_accounts_from_db ~credential:true [ a#id ] |> List.hd
(* Entity MediaAttachment *)
type media_attachment_meta = MAImage
@@ -348,8 +348,8 @@ let status_preload_spec self_id : Model.Status.preload_spec =
]
let load_statuses_from_db ?visibility ?(self_id : Model.Account.ID.t option)
- (status_ids : Model.Status.ID.t list) : status list Lwt.t =
- let%lwt statuses =
+ (status_ids : Model.Status.ID.t list) : status list =
+ let statuses =
Db.(
e
Status.(
@@ -359,11 +359,10 @@ let load_statuses_from_db ?visibility ?(self_id : Model.Account.ID.t option)
status_ids
|> List.map (fun id ->
Hashtbl.find statuses id |> serialize_status ?visibility)
- |> Lwt.return
let make_status_from_model ?(visibility = "public") ?self_id (s : Db.Status.t) :
- status Lwt.t =
- load_statuses_from_db ~visibility ?self_id [ s#id ] >|= List.hd
+ status =
+ load_statuses_from_db ~visibility ?self_id [ s#id ] |> List.hd
(* Entity relationship *)
type relationship = {
@@ -385,17 +384,16 @@ type relationship = {
let make_relationship_from_model (self : Db.Account.t) (acct : Db.Account.t) =
let id = acct#id |> Model.Account.ID.to_int |> string_of_int in
- let%lwt following =
+ let following =
Db.(e Follow.(does_follow ~account_id:self#id ~target_account_id:acct#id))
in
- let%lwt followed_by =
+ let followed_by =
Db.(e Follow.(does_follow ~target_account_id:self#id ~account_id:acct#id))
in
make_relationship ~id ~following ~followed_by ~showing_reblogs:true
~notifying:false ~blocking:false ~blocked_by:false ~muting:false
~muting_notifications:false ~requested:false ~domain_blocking:false
~endorsed:false ~note:""
- |> Lwt.return
(* Entity push_notification *)
type push_notification = {
@@ -477,18 +475,19 @@ let serialize_notification (n : Model.Notification.t) : notification =
()
let load_notifications_from_db ?self_id
- (noti_ids : Model.Notification.ID.t list) : notification list Lwt.t =
- Db.(
- e
- Notification.(
- select ~id:(`In noti_ids)
- ~preload:
- [
- `from_account [ `stat [] ];
- `target_status (status_preload_spec self_id);
- ]))
- >|= index_by (fun x -> x#id)
- >|= fun notis ->
+ (noti_ids : Model.Notification.ID.t list) : notification list =
+ let notis =
+ Db.(
+ e
+ Notification.(
+ select ~id:(`In noti_ids)
+ ~preload:
+ [
+ `from_account [ `stat [] ];
+ `target_status (status_preload_spec self_id);
+ ]))
+ |> index_by (fun x -> x#id)
+ in
noti_ids
|> List.filter_map (fun id ->
let n = Hashtbl.find notis id in
diff --git a/lib/image.ml b/lib/image.ml
index 3d92872..b43a82a 100644
--- a/lib/image.ml
+++ b/lib/image.ml
@@ -46,7 +46,7 @@ let generate_unique_filename img_type =
Uuidm.(v `V4 |> to_string)
^ match img_type with `PNG -> ".png" | `JPEG -> ".jpeg"
-let save_formdata ~outdir (formdata : Httpq.Server.formdata_t) =
+let save_formdata ~outdir (formdata : Yume.Server.formdata_t) =
let fdata = formdata in
let input_type =
match
@@ -54,7 +54,7 @@ let save_formdata ~outdir (formdata : Httpq.Server.formdata_t) =
|> parse_content_type
with
| Ok t -> t
- | Error _ -> Httpq.Server.raise_error_response `Bad_request
+ | Error _ -> Yume.Server.raise_error_response `Bad_request
in
let file_name = generate_unique_filename input_type in
let file_path = Filename.concat outdir file_name in
diff --git a/lib/job.ml b/lib/job.ml
index b958469..87c634e 100644
--- a/lib/job.ml
+++ b/lib/job.ml
@@ -1,23 +1,32 @@
-open Lwt.Infix
+module Runner = struct
+ type t = { chan : (unit -> unit) Eio.Stream.t }
-let kick ~name (f : unit -> unit Lwt.t) : unit Lwt.t =
+ let global_runner = { chan = Eio.Stream.create 0 }
+
+ let start_global_runner ~sw =
+ Eio.Fiber.fork ~sw (fun () ->
+ let rec loop () =
+ let task = Eio.Stream.take global_runner.chan in
+ Eio.Fiber.fork ~sw task;
+ loop ()
+ in
+ loop ())
+
+ let queue task = Eio.Stream.add global_runner.chan task
+end
+
+let kick env ~name (f : unit -> unit) : unit =
let timeout_seconds = 25.0 in
- let timeout_f () =
- let timeout = Lwt_unix.sleep timeout_seconds in
- let task_done = ref false in
- Lwt.pick [ timeout; (f () >|= fun () -> task_done := true) ] >|= fun () ->
- if not !task_done then failwith "Timeout"
- in
if Config.debug_job_kick_block () then (
Logq.debug (fun m -> m "[DEBUG ONLY] Blocked kick: %s" name);
- try%lwt timeout_f ()
+ try Eio.Time.with_timeout_exn env#clock timeout_seconds f
with e ->
Logq.warn (fun m ->
m "[DEBUG ONLY] Fail immediately due to job failure:\n%s: %s: %s" name
(match e with _ -> Printexc.to_string e)
(Printexc.get_backtrace ()));
- Lwt.fail e)
+ raise e)
else
let task () =
let num_repeats = 3 in
@@ -26,21 +35,20 @@ let kick ~name (f : unit -> unit Lwt.t) : unit Lwt.t =
(i * i * i * i) + 15 + (Random.int 10 * (i + 1)) |> float_of_int
in
let rec loop i =
- try%lwt timeout_f ()
+ try Eio.Time.with_timeout_exn env#clock timeout_seconds f
with e ->
Logq.warn (fun m ->
m "Job failed: %s: %s: %s" name (Printexc.to_string e)
(Printexc.get_backtrace ()));
- if i + 1 = num_repeats then (
- Logq.err (fun m -> m "Job killed: %s: Limit reached" name);
- Lwt.return_unit)
+ if i + 1 = num_repeats then
+ Logq.err (fun m -> m "Job killed: %s: Limit reached" name)
else
let dur = sleep_duration i in
Logq.debug (fun m -> m "Job: %s will sleep %.1f seconds" name dur);
- Lwt_unix.sleep dur;%lwt
+ Eio.Time.sleep env#clock dur;
loop (i + 1)
in
loop 0
in
- Lwt.async task;
- Lwt.return_unit
+ Runner.queue task;
+ ()
diff --git a/lib/oauth_helper.ml b/lib/oauth_helper.ml
index 6c0e42d..32f9655 100644
--- a/lib/oauth_helper.ml
+++ b/lib/oauth_helper.ml
@@ -30,13 +30,9 @@ let generate_access_token ~scopes ?resource_owner_id
let authenticate_access_token token = Db.(e (OAuthAccessToken.get_one ~token))
let authenticate_application uid =
- match%lwt Db.(e (OAuthApplication.get_one ~uid) |> maybe_no_row) with
- | Some app -> Lwt.return app
- | None -> Httpq.Server.raise_error_response `Bad_request
+ try Db.(e (OAuthApplication.get_one ~uid))
+ with Sqlx.Error.NoRowFound -> Yume.Server.raise_error_response `Bad_request
let authenticate_access_grant auth_code =
- match%lwt
- Db.(e (OAuthAccessGrant.get_one ~token:auth_code) |> maybe_no_row)
- with
- | Some grant -> Lwt.return grant
- | None -> Httpq.Server.raise_error_response `Bad_request
+ try Db.(e (OAuthAccessGrant.get_one ~token:auth_code))
+ with Sqlx.Error.NoRowFound -> Yume.Server.raise_error_response `Bad_request
diff --git a/lib/ogp.ml b/lib/ogp.ml
index 1b4bdd5..e8a14a2 100644
--- a/lib/ogp.ml
+++ b/lib/ogp.ml
@@ -1,4 +1,3 @@
-open Lwt.Infix
open Util
type oembed = {
@@ -77,13 +76,13 @@ let parse_json_oembed ~url src =
make ~image ()
| _ -> failwith "Invalid type"
-let fetch_oembed url =
- Throttle_fetch.http_get url
- >|= find_json_oembed_href >>= Throttle_fetch.http_get
- >|= parse_json_oembed ~url
+let fetch_oembed env url =
+ Throttle_fetch.http_get env url
+ |> find_json_oembed_href
+ |> Throttle_fetch.http_get env
+ |> parse_json_oembed ~url
-let fetch_oembed_opt url =
- try%lwt fetch_oembed url >|= Option.some with _ -> Lwt.return_none
+let fetch_oembed_opt env url = try Some (fetch_oembed env url) with _ -> None
let parse_opengraph ~url src =
let open Soup in
@@ -121,23 +120,25 @@ let parse_opengraph ~url src =
make_oembed ~url ~typ:"link" ~title ~description ?image ?provider_name ()
-let fetch_image_info url = Throttle_fetch.http_get url >>= Image.inspect
+let fetch_image_info env url =
+ let body = Throttle_fetch.http_get env url in
+ Lwt_eio.run_lwt @@ fun () -> Image.inspect body
-let fetch_opengraph url =
- let%lwt src = Throttle_fetch.http_get url >|= parse_opengraph ~url in
+let fetch_opengraph env url =
+ let src = Throttle_fetch.http_get env url |> parse_opengraph ~url in
match src.image with
- | None -> Lwt.return src
+ | None -> src
| Some image_url -> (
- try%lwt
- let%lwt width, height, blurhash = fetch_image_info image_url in
+ try
+ let width, height, blurhash = fetch_image_info env image_url in
let blurhash = Some blurhash in
- Lwt.return { src with width; height; blurhash }
- with _ -> Lwt.return { src with image = None })
+ { src with width; height; blurhash }
+ with _ -> { src with image = None })
-let fetch_opengraph_opt url =
- try%lwt fetch_opengraph url >|= Option.some
+let fetch_opengraph_opt env url =
+ try Some (fetch_opengraph env url)
with e ->
Logq.err (fun m ->
m "Couldn't fetch opengraph: %s\n%s" (Printexc.to_string e)
(Printexc.get_backtrace ()));
- Lwt.return_none
+ None
diff --git a/lib/router.ml b/lib/router.ml
index c79184c..2a78135 100644
--- a/lib/router.ml
+++ b/lib/router.ml
@@ -1,5 +1,5 @@
open Util [@@warning "-33"]
-open Httpq.Server
+open Yume.Server
let cors =
Cors.
diff --git a/lib/streaming.ml b/lib/streaming.ml
index 104fdc0..4fea2ed 100644
--- a/lib/streaming.ml
+++ b/lib/streaming.ml
@@ -1,6 +1,6 @@
type stream = [ `User ]
type key = int * stream
-type connection = [ `WebSocket of Httpq.Server.ws_conn ]
+type connection = [ `WebSocket of Yume.Server.ws_conn ]
type connection_id = int
let connections : (key, (connection_id, connection) Hashtbl.t) Hashtbl.t =
@@ -52,4 +52,4 @@ let push ~(key : key) ~(event : string) ?payload () =
|> Option.fold ~none:l ~some:(fun payload ->
("payload", `String payload) :: l)
in
- `Assoc l |> Yojson.Safe.to_string |> Httpq.Server.ws_send conn)
+ `Assoc l |> Yojson.Safe.to_string |> Yume.Server.ws_send conn)
diff --git a/lib/throttle_fetch.ml b/lib/throttle_fetch.ml
index 66fd22e..d23b125 100644
--- a/lib/throttle_fetch.ml
+++ b/lib/throttle_fetch.ml
@@ -1,32 +1,208 @@
open Util
+(*
+module Throttle = struct
+ type t = {
+ rate : int;
+ mtx : Eio.Mutex.t;
+ count : (string, int) Hashtbl.t;
+ mutable cleaning : bool;
+ }
+
+ let create ~rate ~n =
+ {
+ rate;
+ mtx = Eio.Mutex.create ();
+ count = Hashtbl.create n;
+ cleaning = false;
+ }
+
+ let clean clock t =
+ Eio.Time.sleep clock 1.0;
+ Eio.Mutex.use_rw ~protect:true t.mtx (fun () ->
+ Hashtbl.clear t.count;
+ t.cleaning <- false)
+
+ let wait ~sw clock t k =
+ Eio.Mutex.use_rw ~protect:true t.mtx (fun () ->
+ if not t.cleaning then (
+ t.cleaning <- true;
+ Eio.Fiber.fork ~sw (fun () -> clean clock t));
+
+ match Hashtbl.find_opt t.count k with
+ | None ->
+ Hashtbl.add t.count k 1;
+ true
+ | Some c when c > t.rate -> false
+ | Some c ->
+ Hashtbl.replace t.count k (c + 1);
+ true)
+end
+
module StringHash = struct
type t = string
let equal = ( = )
let hash = Hashtbl.hash
end
+*)
+
+module Uri = struct
+ include Uri
-module StringLwtThrottle = Lwt_throttle.Make (StringHash)
+ let getaddrinfo_port (u : t) =
+ let scheme = Uri.scheme u |> Option.get in
+ u |> Uri.port |> Option.fold ~none:scheme ~some:string_of_int
-let limitter = StringLwtThrottle.create ~rate:1 ~max:100 ~n:1
+ let http_host (u : t) =
+ let host = Uri.host u |> Option.get in
+ match Uri.port u with
+ | None -> host
+ | Some port -> host ^ ":" ^ string_of_int port
+
+ let path_query_fragment (u : t) =
+ let res = Uri.path u in
+ let res =
+ match Uri.verbatim_query u with None -> res | Some q -> res ^ "?" ^ q
+ in
+ let res =
+ match Uri.fragment u with None -> res | Some f -> res ^ "#" ^ f
+ in
+ res
+
+ let domain (u : t) = http_host u
+end
+
+module Ptime = struct
+ include Ptime
+
+ let now () = Unix.gettimeofday () |> of_float_s |> Option.get
+
+ let to_http_date (v : t) : string =
+ let string_of_week = function
+ | `Sun -> "Sun"
+ | `Mon -> "Mon"
+ | `Tue -> "Tue"
+ | `Wed -> "Wed"
+ | `Thu -> "Thu"
+ | `Fri -> "Fri"
+ | `Sat -> "Sat"
+ in
+ let string_of_month = function
+ | 1 -> "Jan"
+ | 2 -> "Feb"
+ | 3 -> "Mar"
+ | 4 -> "Apr"
+ | 5 -> "May"
+ | 6 -> "Jun"
+ | 7 -> "Jul"
+ | 8 -> "Aug"
+ | 9 -> "Sep"
+ | 10 -> "Oct"
+ | 11 -> "Nov"
+ | 12 -> "Dec"
+ | _ -> assert false
+ in
+ let (year, month, day_of_month), ((hour, minute, second), _) =
+ to_date_time v
+ in
+ let month = string_of_month month in
+ let day_name = weekday v |> string_of_week in
+ Printf.sprintf "%s, %02d %s %d %02d:%02d:%02d GMT" day_name day_of_month
+ month year hour minute second
+end
+
+let fetch env ?(headers = []) ?(meth = `GET) ?(body = "") ?(sign = None) url =
+ let open Yume in
+ let uri = Uri.of_string url in
+
+ (* NOTE: Ad-hoc scheme rewriting (https -> http) for localhost
+ for better dev experience *)
+ let uri =
+ match Uri.scheme uri with
+ | Some "https"
+ when [ Some "localhost"; Some "127.0.0.1" ] |> List.mem (Uri.host uri) ->
+ Uri.with_scheme uri (Some "http")
+ | _ -> uri
+ in
+
+ let meth_s = Method.to_string meth in
+ let headers =
+ let headers =
+ let add (k, v) headers =
+ if List.mem_assoc k headers then headers else (k, v) :: headers
+ in
+ headers
+ |> add (`Content_length, body |> String.length |> string_of_int)
+ |> add (`Connection, "close")
+ |> add (`Host, Uri.http_host uri)
+ |> add (`Date, Ptime.(now () |> to_http_date))
+ in
+ let headers =
+ match sign with
+ | None -> headers
+ | Some (priv_key, key_id, signed_headers) ->
+ Signature.sign ~priv_key ~key_id ~signed_headers ~headers ~meth
+ ~path:(Uri.path_query_fragment uri)
+ ~body:(Some body)
+ in
+ Headers.to_list headers
+ in
+ try
+ Eio.Switch.run @@ fun sw ->
+ let resp =
+ match meth with
+ | `GET | `DELETE ->
+ Client.request env ~sw ~headers ~meth (Uri.to_string uri)
+ | `POST | `PATCH ->
+ Client.request env ~sw ~headers ~body:(`Fixed body) ~meth
+ (Uri.to_string uri)
+ | _ -> failwith "Not implemented method"
+ in
+ let status = Client.Response.status resp in
+ Logq.debug (fun m ->
+ m "[fetch] %s %s --> %s" meth_s url
+ (Cohttp.Code.string_of_status status));
+ let headers = Client.Response.headers resp in
+ let body = Client.Response.drain resp in
+ Ok (status, headers, body)
+ with e ->
+ let backtrace = Printexc.get_backtrace () in
+ Logq.err (fun m ->
+ m "[fetch] %s %s: %s\n%s" meth_s url (Printexc.to_string e) backtrace);
+ Error ()
+
+exception FetchFailure of (Yume.Status.t * Yume.Headers.t * string) option
+
+let fetch_exn ?(headers = []) ?(meth = `GET) ?(body = "") ?(sign = None) env
+ (url : string) : string =
+ match fetch env ~headers ~meth ~body ~sign url with
+ | Ok (`OK, _, body) -> body
+ | Ok r -> raise (FetchFailure (Some r))
+ | _ -> raise (FetchFailure None)
+
+module Throttle = struct
+ type t = { semaphore : Eio.Semaphore.t }
+
+ let create ~rate = { semaphore = Eio.Semaphore.make rate }
+
+ let wait { semaphore } _k f =
+ Eio.Semaphore.acquire semaphore;
+ Fun.protect ~finally:(fun () -> Eio.Semaphore.release semaphore) f
+end
+
+let limitter = Throttle.create ~rate:10
let call f url =
if Config.debug_no_throttle_fetch () then f url
else
let host = Uri.(of_string url |> host) |> Option.value ~default:"" in
- let rec aux () =
- if%lwt StringLwtThrottle.wait limitter host then f url
- else (
- Lwt_unix.sleep 1.0;%lwt
- aux ())
- in
- aux ()
+ Throttle.wait limitter host (fun () -> f url)
-let f ?(headers = []) ?(meth = `GET) ?(body = "") ?(sign = None) =
- call (Httpq.Client.fetch ~headers ~meth ~body ~sign)
+let f env ?(headers = []) ?(meth = `GET) ?(body = "") ?(sign = None) =
+ call (fetch env ~headers ~meth ~body ~sign)
-let f_exn ?(headers = []) ?(meth = `GET) ?(body = "") ?(sign = None) =
- call (Httpq.Client.fetch_exn ~headers ~meth ~body ~sign)
+let f_exn env ?(headers = []) ?(meth = `GET) ?(body = "") ?(sign = None) =
+ call (fetch_exn env ~headers ~meth ~body ~sign)
let http_get url = f_exn url
diff --git a/lib/webpush_helper.ml b/lib/webpush_helper.ml
index ae29e4a..0d45852 100644
--- a/lib/webpush_helper.ml
+++ b/lib/webpush_helper.ml
@@ -1,27 +1,25 @@
-open Lwt.Infix
-
-let deliver ?user_id message =
+let deliver env ?user_id message =
Db.(e @@ WebPushSubscription.(get_many ~user_id))
- >>= Lwt_list.iter_p @@ fun s ->
- match
- Webpush.construct_request ~message ~auth_key:s#key_auth
- ~p256dh_key:s#key_p256dh
- ~subscriber:(Config.webpush_subscriber ())
- ~endpoint:s#endpoint
- ~vapid_priv_key:(Config.vapid_private_key ())
- with
- | Error _ ->
- Logq.err (fun m ->
- m "Couldn't construct request of webpush: %s" s#endpoint);
- Lwt.return_unit
- | Ok (headers, body) -> (
- let body = Cstruct.to_string body in
- match%lwt Throttle_fetch.f ~headers ~meth:`POST ~body s#endpoint with
- | Ok (status, _, _) when Httpq.Status.is_success status ->
- Lwt.return_unit
- | Ok (`Gone, _, _) ->
- Logq.debug (fun m -> m "Subscription gone: %s" s#endpoint);
- Db.(e WebPushSubscription.(delete [ s ]))
- | _ ->
- Logq.err (fun m -> m "Couldn't post webpush: %s" s#endpoint);
- Lwt.return_unit)
+ |> List.map (fun s () ->
+ match
+ Webpush.construct_request ~message ~auth_key:s#key_auth
+ ~p256dh_key:s#key_p256dh
+ ~subscriber:(Config.webpush_subscriber ())
+ ~endpoint:s#endpoint
+ ~vapid_priv_key:(Config.vapid_private_key ())
+ with
+ | Error _ ->
+ Logq.err (fun m ->
+ m "Couldn't construct request of webpush: %s" s#endpoint)
+ | Ok (headers, body) -> (
+ let body = Cstruct.to_string body in
+ match
+ Throttle_fetch.f env ~headers ~meth:`POST ~body s#endpoint
+ with
+ | Ok (status, _, _) when Yume.Status.is_success status -> ()
+ | Ok (`Gone, _, _) ->
+ Logq.debug (fun m -> m "Subscription gone: %s" s#endpoint);
+ Db.(e WebPushSubscription.(delete [ s ]))
+ | _ -> Logq.err (fun m -> m "Couldn't post webpush: %s" s#endpoint)
+ ))
+ |> Eio.Fiber.all
diff --git a/lib/worker/accept.ml b/lib/worker/accept.ml
index 7cc3403..79725a3 100644
--- a/lib/worker/accept.ml
+++ b/lib/worker/accept.ml
@@ -1,7 +1,7 @@
open Activity
(* Send Accept to POST inbox *)
-let kick ~(f : Db.Follow.t) ~(followee : Db.Account.t)
+let kick env ~(f : Db.Follow.t) ~(followee : Db.Account.t)
~(follower : Db.Account.t) =
let id =
followee#uri ^ "#accepts/follows/"
@@ -11,4 +11,4 @@ let kick ~(f : Db.Follow.t) ~(followee : Db.Account.t)
make_follow ~id:f#uri ~actor:follower#uri ~obj:followee#uri |> follow
in
let activity = make_accept ~id ~actor:(`String followee#uri) ~obj |> accept in
- Delivery.kick ~activity ~src:followee ~url:follower#inbox_url
+ Delivery.kick env ~activity ~src:followee ~url:follower#inbox_url
diff --git a/lib/worker/account_update.ml b/lib/worker/account_update.ml
index 6007810..7495b07 100644
--- a/lib/worker/account_update.ml
+++ b/lib/worker/account_update.ml
@@ -1,14 +1,13 @@
open Util
-open Lwt.Infix
-let kick ~account_id ~updated_at =
- Job.kick ~name:__FUNCTION__ @@ fun () ->
- let%lwt src = Db.e (Model.Account.get_one ~id:account_id) in
+let kick env ~account_id ~updated_at =
+ Job.kick env ~name:__FUNCTION__ @@ fun () ->
+ let src = Db.e (Model.Account.get_one ~id:account_id) in
let id = src#uri ^ "#updates" ^/ (Ptime.to_int updated_at |> string_of_int) in
let actor = src#uri in
let to_ = [ "https://www.w3.org/ns/activitystreams#Public" ] in
let obj = Activity.(person_of_account src |> person) in
let activity = Activity.(make_update ~id ~actor ~to_ ~obj |> update) in
Db.(e @@ get_remote_followers ~account_id)
- >|= Db.Account.preferred_inbox_urls
- >>= Lwt_list.iter_p (fun url -> Delivery.kick ~src ~url ~activity)
+ |> Db.Account.preferred_inbox_urls
+ |> List.iter (fun url -> Delivery.kick env ~src ~url ~activity)
diff --git a/lib/worker/announce.ml b/lib/worker/announce.ml
index 57b8cd0..bdc9559 100644
--- a/lib/worker/announce.ml
+++ b/lib/worker/announce.ml
@@ -1,8 +1,7 @@
open Activity
-open Lwt.Infix
(* Send Announce to POST /users/:name/inbox *)
-let kick ~(url : string) ~(status : Db.Status.t) =
- let%lwt activity = announce_of_status status >|= announce in
- let%lwt src = Db.e (Model.Account.get_one ~id:status#account_id) in
- Delivery.kick ~activity ~src ~url
+let kick env ~(url : string) ~(status : Db.Status.t) =
+ let activity = announce_of_status status |> announce in
+ let src = Db.e (Model.Account.get_one ~id:status#account_id) in
+ Delivery.kick env ~activity ~src ~url
diff --git a/lib/worker/create_note.ml b/lib/worker/create_note.ml
index 3030754..9c94cde 100644
--- a/lib/worker/create_note.ml
+++ b/lib/worker/create_note.ml
@@ -1,8 +1,7 @@
open Activity
-open Lwt.Infix
(* Send Create/Note to POST /users/:name/inbox *)
-let kick ~(url : string) ~(status : Db.Status.t) =
- let%lwt activity = create_note_of_status status >|= create in
- let%lwt src = Db.e (Model.Account.get_one ~id:status#account_id) in
- Delivery.kick ~activity ~src ~url
+let kick env ~(url : string) ~(status : Db.Status.t) =
+ let activity = create_note_of_status status |> create in
+ let src = Db.e (Model.Account.get_one ~id:status#account_id) in
+ Delivery.kick env ~activity ~src ~url
diff --git a/lib/worker/delivery.ml b/lib/worker/delivery.ml
index 4958cbb..b3ed1e8 100644
--- a/lib/worker/delivery.ml
+++ b/lib/worker/delivery.ml
@@ -1,6 +1,6 @@
open Activity
-let kick ~(activity : t) ~(url : string) ~(src : Db.Account.t) =
- Job.kick ~name:__FUNCTION__ @@ fun () ->
+let kick env ~(activity : t) ~(url : string) ~(src : Db.Account.t) =
+ Job.kick env ~name:__FUNCTION__ @@ fun () ->
let body = activity |> to_yojson |> Yojson.Safe.to_string in
- post_activity_json ~body ~sign:(sign_spec_of_account src) ~url
+ post_activity_json env ~body ~sign:(sign_spec_of_account src) ~url
diff --git a/lib/worker/distribute.ml b/lib/worker/distribute.ml
index 81531a4..64b03f0 100644
--- a/lib/worker/distribute.ml
+++ b/lib/worker/distribute.ml
@@ -1,80 +1,78 @@
open Util
-open Lwt.Infix
-let deliver_to_local ~(targets : Db.User.t list) ~(status : Db.Status.t) :
- unit Lwt.t =
+let deliver_to_local env ~(targets : Db.User.t list) ~(status : Db.Status.t) :
+ unit =
targets
- |> Lwt_list.iter_p (fun (u : Db.User.t) ->
- Insert_to_feed.kick ~account_id:u#account_id ~status_id:status#id
+ |> List.iter (fun (u : Db.User.t) ->
+ Insert_to_feed.kick env ~account_id:u#account_id ~status_id:status#id
~user_id:u#id ~stream:`User)
-let deliver_to_remote ~(targets : Db.Account.t list) ~(status : Db.Status.t) :
- unit Lwt.t =
+let deliver_to_remote env ~(targets : Db.Account.t list) ~(status : Db.Status.t)
+ : unit =
targets |> Db.Account.preferred_inbox_urls
- |> Lwt_list.iter_p (fun url ->
+ |> List.iter (fun url ->
match status#reblog_of_id with
- | None -> Create_note.kick ~status ~url
- | Some _ -> Announce.kick ~status ~url)
+ | None -> Create_note.kick env ~status ~url
+ | Some _ -> Announce.kick env ~status ~url)
-let kick (s : Db.Status.t) =
- Job.kick ~name:__FUNCTION__ @@ fun () ->
- let%lwt a = Db.e (Model.Account.get_one ~id:s#account_id) in
+let kick env (s : Db.Status.t) =
+ Job.kick env ~name:__FUNCTION__ @@ fun () ->
+ let a = Db.e (Model.Account.get_one ~id:s#account_id) in
let is_status_from_remote = a#domain <> None in
- let%lwt local_followers = Db.(e @@ get_local_followers ~account_id:a#id) in
- let%lwt remote_followers = Db.(e @@ get_remote_followers ~account_id:a#id) in
+ let local_followers = Db.(e @@ get_local_followers ~account_id:a#id) in
+ let remote_followers = Db.(e @@ get_remote_followers ~account_id:a#id) in
- let%lwt local_mentions, remote_mentions =
+ let local_mentions, remote_mentions =
Db.(
e
@@ Mention.select ~status_id:(`Eq s#id) ~account_id:`NeqNone
~preload:[ `account [ `user [] ]; `status [ `account [] ] ])
- >|= List.partition_map (fun x ->
- let acct = Option.get x#account in
- match acct#user with None -> Right x | Some _ -> Left x)
+ |> List.partition_map (fun x ->
+ let acct = Option.get x#account in
+ match acct#user with None -> Right x | Some _ -> Left x)
in
(* Deliver to self *)
- (if is_status_from_remote then Lwt.return_unit (* Just ignore *)
+ (if is_status_from_remote then () (* Just ignore *)
else
- let%lwt u = Db.(e @@ User.get_one ~account_id:a#id) in
+ let u = Db.(e @@ User.get_one ~account_id:a#id) in
(* Local: Send the status to self *)
- Insert_to_feed.kick ~account_id:a#id ~status_id:s#id ~user_id:u#id
- ~stream:`User);%lwt
+ Insert_to_feed.kick env ~account_id:a#id ~status_id:s#id ~user_id:u#id
+ ~stream:`User);
(* Deliver to remote followers *)
- if not is_status_from_remote then
- let targets = remote_followers in
- let targets =
- targets @ (remote_mentions |> List.map (fun x -> Option.get x#account))
- in
- let%lwt targets =
- match s#reblog_of_id with
- | None -> Lwt.return targets
- | Some id ->
- (* Deliver Announce activity to the original author *)
- Db.(e Status.(get_one ~id ~preload:[ `account [] ])) >|= fun s ->
- if Model.Account.is_remote s#account then s#account :: targets
- else targets
- in
- deliver_to_remote ~targets ~status:s
- else Lwt.return_unit;%lwt
+ (if not is_status_from_remote then
+ let targets = remote_followers in
+ let targets =
+ targets @ (remote_mentions |> List.map (fun x -> Option.get x#account))
+ in
+ let targets =
+ match s#reblog_of_id with
+ | None -> targets
+ | Some id ->
+ (* Deliver Announce activity to the original author *)
+ let s = Db.(e Status.(get_one ~id ~preload:[ `account [] ])) in
+ if Model.Account.is_remote s#account then s#account :: targets
+ else targets
+ in
+ deliver_to_remote env ~targets ~status:s);
(* Deliver to local followers *)
- deliver_to_local
+ deliver_to_local env
~targets:
(local_followers
@ (local_mentions
|> List.map (fun x -> Option.get (Option.get x#account)#user)))
- ~status:s;%lwt
+ ~status:s;
(* Send notification to mentioned local users *)
(local_mentions
- |> Lwt_list.iter_p @@ fun m ->
+ |> List.iter @@ fun m ->
let src = (Option.get m#status)#account in
let dst = Option.get m#account in
- Local_notify.kick
+ Local_notify.kick env
~activity_id:(Model.Mention.ID.to_int m#id)
- ~activity_type:`Mention ~typ:`mention ~src ~dst);%lwt
+ ~activity_type:`Mention ~typ:`mention ~src ~dst);
- Lwt.return_unit
+ ()
diff --git a/lib/worker/insert_to_feed.ml b/lib/worker/insert_to_feed.ml
index d556068..8f023cd 100644
--- a/lib/worker/insert_to_feed.ml
+++ b/lib/worker/insert_to_feed.ml
@@ -1,14 +1,13 @@
open Entity
-let kick ~status_id ~account_id ~user_id ~stream =
- let open Lwt.Infix in
- Job.kick ~name:__FUNCTION__ @@ fun () ->
+let kick env ~status_id ~account_id ~user_id ~stream =
+ Job.kick env ~name:__FUNCTION__ @@ fun () ->
assert (stream = `User);
let key = Streaming.make_key ~user_id ~stream in
- let%lwt payload =
+ let payload =
Db.e (Model.Status.get_one ~id:status_id)
- >>= make_status_from_model ~self_id:account_id
- >|= yojson_of_status >|= Yojson.Safe.to_string
+ |> make_status_from_model ~self_id:account_id
+ |> yojson_of_status |> Yojson.Safe.to_string
in
Streaming.push ~key ~event:"update" ~payload ();
- Lwt.return_unit
+ ()
diff --git a/lib/worker/link_crawl.ml b/lib/worker/link_crawl.ml
index a92a5b5..bf75427 100644
--- a/lib/worker/link_crawl.ml
+++ b/lib/worker/link_crawl.ml
@@ -1,5 +1,4 @@
open Util
-open Lwt.Infix
let extract_urls (status : Model.Status.t) =
let urls =
@@ -38,9 +37,9 @@ let extract_urls (status : Model.Status.t) =
in
urls |> List.sort_uniq compare
-let kick (status_id : Model.Status.ID.t) =
- Job.kick ~name:__FUNCTION__ @@ fun () ->
- let%lwt status =
+let kick env (status_id : Model.Status.ID.t) =
+ Job.kick env ~name:__FUNCTION__ @@ fun () ->
+ let status =
Db.(
e
Status.(
@@ -50,40 +49,38 @@ let kick (status_id : Model.Status.ID.t) =
in
(* Find (or insert if necessary) PreviewCard *)
- let%lwt cards_already_inserted, cards_not_inserted =
+ let cards_already_inserted, cards_not_inserted =
extract_urls status
- |> Lwt_list.filter_map_p (fun url ->
- match%lwt Db.(e PreviewCard.(get_one ~url) |> maybe_no_row) with
- | Some x -> Lwt.return_some x
- | None -> (
- let%lwt oembed_opt =
- match%lwt Ogp.fetch_oembed_opt url with
- | Some y -> Lwt.return_some y
- | None -> Ogp.fetch_opengraph_opt url
- in
- match oembed_opt with
- | None -> Lwt.return_none
- | Some x ->
- (* Convert x into PreviewCard *)
- let type_ =
- match x.typ with
- | "link" -> 0
- | "photo" -> 1
- | "video" -> 2
- | "rich" -> 3
- | _ -> failwith "Invalid oembed type"
- in
- Model.PreviewCard.make ~url:x.url ~title:x.title
- ~description:x.description ?image_url:x.image ~type_
- ~html:x.html ~author_name:x.author_name
- ~author_url:x.author_url ~provider_name:x.provider_name
- ~provider_url:x.provider_url ~width:x.width
- ~height:x.height ~embed_url:x.embed_url
- ?blurhash:x.blurhash ()
- |> Lwt.return_some))
- >|= List.partition (fun c -> Option.is_some c#id_opt)
+ |> List.filter_map (fun url ->
+ try Some Db.(e PreviewCard.(get_one ~url))
+ with Sqlx.Error.NoRowFound -> (
+ let oembed_opt =
+ match Ogp.fetch_oembed_opt env url with
+ | Some y -> Some y
+ | None -> Ogp.fetch_opengraph_opt env url
+ in
+ match oembed_opt with
+ | None -> None
+ | Some x ->
+ (* Convert x into PreviewCard *)
+ let type_ =
+ match x.typ with
+ | "link" -> 0
+ | "photo" -> 1
+ | "video" -> 2
+ | "rich" -> 3
+ | _ -> failwith "Invalid oembed type"
+ in
+ Model.PreviewCard.make ~url:x.url ~title:x.title
+ ~description:x.description ?image_url:x.image ~type_
+ ~html:x.html ~author_name:x.author_name
+ ~author_url:x.author_url ~provider_name:x.provider_name
+ ~provider_url:x.provider_url ~width:x.width ~height:x.height
+ ~embed_url:x.embed_url ?blurhash:x.blurhash ()
+ |> Option.some))
+ |> List.partition (fun c -> Option.is_some c#id_opt)
in
- let%lwt cards_not_inserted = Db.(e PreviewCard.(insert cards_not_inserted)) in
+ let cards_not_inserted = Db.(e PreviewCard.(insert cards_not_inserted)) in
let cards = cards_already_inserted @ cards_not_inserted in
(* Insert PreviewCardStatus (associative entity) *)
@@ -93,12 +90,13 @@ let kick (status_id : Model.Status.ID.t) =
Model.PreviewCardStatus.make ~preview_card_id:card#id
~status_id:status#id ())
in
- if%lwt
+ if
+ Lwt_eio.run_lwt @@ fun () ->
Db.(
transaction (fun c ->
let open PreviewCardStatus in
let%lwt cards = get_many ~status_id c in
if cards <> [] then delete cards c else Lwt.return_unit;%lwt
PreviewCardStatus.insert rels c |> ignore_lwt))
- then Lwt.return_unit
+ then ()
else failwith "Transaction failed"
diff --git a/lib/worker/local_notify.ml b/lib/worker/local_notify.ml
index 7d76008..fbea6d3 100644
--- a/lib/worker/local_notify.ml
+++ b/lib/worker/local_notify.ml
@@ -1,20 +1,19 @@
open Util
-open Lwt.Infix
-let service activity_id activity_type dst src typ () =
+let service env activity_id activity_type dst src typ () =
let account_id = dst#id in
let from_account_id = src#id in
let typ = Some typ in
let now = Ptime.now () in
- match%lwt
+ match
Db.e
@@ Model.Notification.get_many ~activity_id ~activity_type ~account_id
~from_account_id ~typ
with
- | _ :: _ -> Lwt.return_unit
+ | _ :: _ -> ()
| [] ->
- let%lwt u = Db.e @@ Model.User.get_one ~account_id in
- let%lwt n =
+ let u = Db.e @@ Model.User.get_one ~account_id in
+ let n =
Db.e
@@ Model.Notification.(
make ~activity_id ~activity_type ~created_at:now ~updated_at:now
@@ -24,24 +23,25 @@ let service activity_id activity_type dst src typ () =
(* Notification via WebSocket *)
( Entity.load_notifications_from_db ~self_id:account_id [ n#id ]
- >|= List.hd >|= Entity.yojson_of_notification >|= Yojson.Safe.to_string
- >|= fun payload ->
+ |> List.hd |> Entity.yojson_of_notification |> Yojson.Safe.to_string
+ |> fun payload ->
Streaming.(
push
~key:(make_key ~user_id:u#id ~stream:`User)
- ~event:"notification" ~payload ()) );%lwt
+ ~event:"notification" ~payload ()) );
(* Push notification *)
- Webpush_helper.deliver ~user_id:u#id
+ Webpush_helper.deliver env ~user_id:u#id
(Entity.serialize_push_notification n
|> Entity.yojson_of_push_notification |> Yojson.Safe.to_string)
-let kick ~(activity_id : int) ~(activity_type : Db.Notification.activity_type_t)
- ~(dst : Db.Account.t) ~(src : Db.Account.t) ~(typ : Db.Notification.typ_t) =
- if not (Model.Account.is_local dst) then (
+let kick env ~(activity_id : int)
+ ~(activity_type : Db.Notification.activity_type_t) ~(dst : Db.Account.t)
+ ~(src : Db.Account.t) ~(typ : Db.Notification.typ_t) =
+ if not (Model.Account.is_local dst) then
Logq.err (fun m ->
m "Local_notify.kick: dst is not a local account: %s"
- (Model.Notification.activity_type_t_to_string activity_type));
- Lwt.return_unit)
+ (Model.Notification.activity_type_t_to_string activity_type))
else
- Job.kick ~name:__FUNCTION__ (service activity_id activity_type dst src typ)
+ Job.kick env ~name:__FUNCTION__
+ (service env activity_id activity_type dst src typ)
diff --git a/lib/worker/removal.ml b/lib/worker/removal.ml
index fecebda..0fe3a10 100644
--- a/lib/worker/removal.ml
+++ b/lib/worker/removal.ml
@@ -1,9 +1,7 @@
-open Lwt.Infix
-
-let aux ~account_id ~status_id ~status =
- let%lwt reblogs = Db.(e @@ Status.get_many' ~reblog_of_id:(Some status_id)) in
- let%lwt src = Db.e (Model.Account.get_one ~id:account_id) in
- let is_account_local = Model.Account.is_local src |> Lwt.return in
+let aux env ~account_id ~status_id ~status =
+ let reblogs = Db.(e @@ Status.get_many' ~reblog_of_id:(Some status_id)) in
+ let src = Db.e (Model.Account.get_one ~id:account_id) in
+ let is_account_local = Model.Account.is_local src in
let deliver_to_local ~user_id =
let open Streaming in
@@ -15,42 +13,41 @@ let aux ~account_id ~status_id ~status =
in
(* Deliver to self if necessary *)
- (if%lwt is_account_local then
- Db.(e @@ User.get_one ~account_id) >|= fun u ->
- deliver_to_local ~user_id:u#id);%lwt
+ (if is_account_local then
+ let u = Db.(e @@ User.get_one ~account_id) in
+ deliver_to_local ~user_id:u#id);
(* Deliver to local followers *)
Db.(e @@ get_local_followers ~account_id)
- >|= List.iter (fun (user : Db.User.t) -> deliver_to_local ~user_id:user#id);%lwt
+ |> List.iter (fun (user : Db.User.t) -> deliver_to_local ~user_id:user#id);
(* Deliver to remote followers *)
- if%lwt is_account_local then (
- let%lwt activity =
+ if is_account_local then (
+ let activity =
let open Activity in
match status#reblog_of_id with
| Some _ ->
(* Undo Announce *)
announce_of_status ~deleted:true status
- >|= announce >|= to_undo ~actor:src#uri >|= undo
+ |> announce |> to_undo ~actor:src#uri |> undo
| None ->
(* Delete *)
let id = status#uri ^ "#delete" in
let actor = src#uri in
let to_ = [ "https://www.w3.org/ns/activitystreams#Public" ] in
let obj = make_tombstone ~id:status#uri |> tombstone |> to_yojson in
- make_delete ~id ~actor ~to_ ~obj |> delete |> Lwt.return
+ make_delete ~id ~actor ~to_ ~obj |> delete
in
Db.(e @@ get_remote_followers ~account_id)
- >|= Db.Account.preferred_inbox_urls
- >>= Lwt_list.iter_p (fun url -> Delivery.kick ~src ~url ~activity);%lwt
+ |> Db.Account.preferred_inbox_urls
+ |> List.iter (fun url -> Delivery.kick env ~src ~url ~activity);
- Lwt.return_unit)
+ ())
-let kick ~account_id ~status_id =
- Job.kick ~name:__FUNCTION__ @@ fun () ->
- let%lwt status = Db.(e @@ Status.discard_with_reblogs status_id) in
- if account_id <> status#account_id then (
+let kick env ~account_id ~status_id =
+ Job.kick env ~name:__FUNCTION__ @@ fun () ->
+ let status = Db.(e @@ Status.discard_with_reblogs status_id) in
+ if account_id <> status#account_id then
Logq.err (fun m ->
- m "Worker.Removal: ignoring invalid pair of account_id and status_id");
- Lwt.return_unit)
- else aux ~account_id ~status_id ~status
+ m "Worker.Removal: ignoring invalid pair of account_id and status_id")
+ else aux env ~account_id ~status_id ~status
diff --git a/lib_httpq/bare_server.ml b/lib_httpq/bare_server.ml
deleted file mode 100644
index e4eff98..0000000
--- a/lib_httpq/bare_server.ml
+++ /dev/null
@@ -1,121 +0,0 @@
-open Util
-open Lwt.Infix
-
-module Request = struct
- include Cohttp_lwt.Request
-
- let headers = headers |.> Cohttp.Header.to_list
-end
-
-module Response = struct
- type t = Cohttp_lwt_unix.Server.response_action
-end
-
-module Body = Cohttp_lwt.Body
-
-let respond ~(status : Status.t) ~(headers : Headers.t) ~(body : string) =
- let headers = headers |> Headers.to_list |> Cohttp.Header.of_list in
- Cohttp_lwt_unix.Server.respond_string ~status ~headers ~body () >|= fun x ->
- `Response x
-
-let start_server port k callback =
- let callback _conn (req : Request.t) (body : Body.t) =
- (* Invoke the handler *)
- let%lwt resp =
- try%lwt callback req body
- with e ->
- let uri = Request.uri req in
- let meth = Request.meth req in
- Logq.err (fun m ->
- m "Unexpected exception: %s %s: %s\n%s" (Method.to_string meth)
- (Uri.to_string uri) (Printexc.to_string e)
- (Printexc.get_backtrace ()));
- respond ~status:`Internal_server_error ~body:"" ~headers:[]
- in
- match resp with
- | `Response (resp, body) ->
- (* NOTE: Add a connection header if one doesn't exist. It is necessary to
- receive HTTP requests correctly via Tunnelmole. FYI cohttp-async already
- has the same functionality, but cohttp-lwt does not.
- cf.: https://github.com/mirage/ocaml-cohttp/blob/cf2ae3344ed9211230a5680f251613326eacb296/cohttp-async/src/server.ml#L90-L95
- *)
- let resp =
- let keep_alive =
- Request.is_keep_alive req (*&& Http.Response.is_keep_alive resp*)
- in
- let headers =
- Cohttp.Header.add_unless_exists
- (Cohttp.Response.headers resp)
- "connection"
- (if keep_alive then "keep-alive" else "close")
- in
- { resp with headers }
- in
- Lwt.return (`Response (resp, body))
- | _ -> Lwt.return resp
- in
- let server =
- Cohttp_lwt_unix.Server.make_response_action ~callback ()
- |> Cohttp_lwt_unix.Server.create ~mode:(`TCP (`Port port))
- in
- Lwt.pick [ server; (k >>= fun () -> Lwt.task () |> fst) ]
-
-type ws_conn = {
- mutable frames_out_fn : (Websocket.Frame.t option -> unit) option;
- mutable closed : bool; [@default false]
- recv_stream : string Lwt_stream.t;
-}
-[@@deriving make]
-
-let ws_send ?(opcode = Websocket.Frame.Opcode.Text) (c : ws_conn) content =
- if not c.closed then
- Websocket.Frame.create ~opcode ~content ()
- |> Option.some |> Option.get c.frames_out_fn
-
-let ws_recv (c : ws_conn) = Lwt_stream.get c.recv_stream
-
-let websocket (req : Request.t) f =
- let recv_stream, recv_stream_push = Lwt_stream.create () in
- let conn = make_ws_conn ~recv_stream () in
- let%lwt resp, frames_out_fn =
- Websocket_cohttp_lwt.upgrade_connection req (fun { opcode; content; _ } ->
- match opcode with
- | Close ->
- Logq.debug (fun m -> m "Websocket: recv Close");
- ws_send ~opcode:Close conn "";
- conn.closed <- true;
- recv_stream_push None
- | Text | Binary ->
- Logq.debug (fun m -> m "Websocket: recv: %s" content);
- recv_stream_push (Some content)
- | Ping -> ws_send ~opcode:Pong conn content
- | Pong -> () (* Just ignore *)
- | _ ->
- Websocket.Frame.close 1002
- |> Option.(some |.> get conn.frames_out_fn))
- in
- conn.frames_out_fn <- Some frames_out_fn;
- Lwt.async (fun () ->
- try
- Logq.debug (fun m -> m "Websocket: start thread");
-
- (* Send Ping continuously *)
- let timeout = ref None in
- let timeout_seconds = 10 in
- timeout :=
- Some
- ( Lwt_timeout.create timeout_seconds @@ fun () ->
- ws_send ~opcode:Ping conn "";
- Lwt_timeout.start (Option.get !timeout) );
-
- (* Start the process *)
- Lwt_timeout.start (Option.get !timeout);
- f conn;%lwt
- Lwt_timeout.stop (Option.get !timeout);
- Lwt.return_unit
- with e ->
- Logq.err (fun m ->
- m "Websocket: thread error: %s\n%s" (Printexc.to_string e)
- (Printexc.get_backtrace ()));
- Lwt.return_unit);
- Lwt.return resp
diff --git a/lib_httpq/client.ml b/lib_httpq/client.ml
deleted file mode 100644
index 45dd357..0000000
--- a/lib_httpq/client.ml
+++ /dev/null
@@ -1,71 +0,0 @@
-open Util
-
-let fetch ?(headers = []) ?(meth = `GET) ?(body = "") ?(sign = None) url =
- let open Cohttp in
- let open Cohttp_lwt_unix in
- let uri = Uri.of_string url in
-
- (* NOTE: Ad-hoc scheme rewriting (https -> http) for localhost
- for better dev experience *)
- let uri =
- match Uri.scheme uri with
- | Some "https"
- when [ Some "localhost"; Some "127.0.0.1" ] |> List.mem (Uri.host uri) ->
- Uri.with_scheme uri (Some "http")
- | _ -> uri
- in
-
- let meth_s = Method.to_string meth in
- let headers =
- let headers =
- let add (k, v) headers =
- if List.mem_assoc k headers then headers else (k, v) :: headers
- in
- headers
- |> add (`Content_length, body |> String.length |> string_of_int)
- |> add (`Connection, "close")
- |> add (`Host, Uri.http_host uri)
- |> add (`Date, Ptime.(now () |> to_http_date))
- in
- let headers =
- match sign with
- | None -> headers
- | Some (priv_key, key_id, signed_headers) ->
- Signature.sign ~priv_key ~key_id ~signed_headers ~headers ~meth
- ~path:(Uri.path_query_fragment uri)
- ~body:(Some body)
- in
- headers |> Headers.to_list |> Header.of_list
- in
- try%lwt
- let%lwt resp, body =
- match meth with
- | `GET | `DELETE -> Client.call ~headers meth uri
- | `POST | `PATCH ->
- let body = Cohttp_lwt.Body.of_string body in
- Client.call ~headers ~body meth uri
- | _ -> failwith "Not implemented method"
- in
- let status = Response.status resp in
- Logq.debug (fun m ->
- m "[fetch] %s %s --> %s" meth_s url (Code.string_of_status status));
- let headers =
- Response.headers resp |> Header.to_list
- |> List.map (fun (k, v) -> (String.lowercase_ascii k, v))
- in
- let%lwt body = Cohttp_lwt.Body.to_string body in
- Lwt.return_ok (status, headers, body)
- with e ->
- let backtrace = Printexc.get_backtrace () in
- Logq.err (fun m ->
- m "[fetch] %s %s: %s\n%s" meth_s url (Printexc.to_string e) backtrace);
- Lwt.return_error ()
-
-exception FetchFailure of (Status.t * (string * string) list * string) option
-
-let fetch_exn ?(headers = []) ?(meth = `GET) ?(body = "") ?(sign = None)
- (url : string) : string Lwt.t =
- match%lwt fetch ~headers ~meth ~body ~sign url with
- | Ok (`OK, _, body) -> Lwt.return body
- | Ok r -> raise (FetchFailure (Some r))
- | _ -> raise (FetchFailure None)
diff --git a/lib_httpq/dune b/lib_httpq/dune
deleted file mode 100644
index 9d26a93..0000000
--- a/lib_httpq/dune
+++ /dev/null
@@ -1,28 +0,0 @@
-(library
- (name httpq)
- (preprocess
- (pps
- lwt_ppx
- ppx_deriving.enum
- ppx_deriving.make
- ppx_deriving.show
- ppx_deriving_yaml
- ppx_yojson_conv))
- (libraries
- base64
- cohttp-lwt-unix
- cstruct
- logq
- lwt
- lwt.unix
- multipart_form
- multipart_form-lwt
- ptime
- result
- sha
- unix
- uri
- websocket
- websocket-lwt-unix.cohttp
- x509
- yojson))
diff --git a/lib_httpq/util.ml b/lib_httpq/util.ml
deleted file mode 100644
index 290dc66..0000000
--- a/lib_httpq/util.ml
+++ /dev/null
@@ -1,74 +0,0 @@
-let ( |.> ) f g a = f a |> g
-let ( *> ) f g a = f a |> g
-
-module Uri = struct
- include Uri
-
- let getaddrinfo_port (u : t) =
- let scheme = Uri.scheme u |> Option.get in
- u |> Uri.port |> Option.fold ~none:scheme ~some:string_of_int
-
- let http_host (u : t) =
- let host = Uri.host u |> Option.get in
- match Uri.port u with
- | None -> host
- | Some port -> host ^ ":" ^ string_of_int port
-
- let path_query_fragment (u : t) =
- let res = Uri.path u in
- let res =
- match Uri.verbatim_query u with None -> res | Some q -> res ^ "?" ^ q
- in
- let res =
- match Uri.fragment u with None -> res | Some f -> res ^ "#" ^ f
- in
- res
-
- let domain (u : t) = http_host u
-end
-
-module Ptime = struct
- include Ptime
-
- let now () = Unix.gettimeofday () |> of_float_s |> Option.get
-
- let to_http_date (v : t) : string =
- let string_of_week = function
- | `Sun -> "Sun"
- | `Mon -> "Mon"
- | `Tue -> "Tue"
- | `Wed -> "Wed"
- | `Thu -> "Thu"
- | `Fri -> "Fri"
- | `Sat -> "Sat"
- in
- let string_of_month = function
- | 1 -> "Jan"
- | 2 -> "Feb"
- | 3 -> "Mar"
- | 4 -> "Apr"
- | 5 -> "May"
- | 6 -> "Jun"
- | 7 -> "Jul"
- | 8 -> "Aug"
- | 9 -> "Sep"
- | 10 -> "Oct"
- | 11 -> "Nov"
- | 12 -> "Dec"
- | _ -> assert false
- in
- let (year, month, day_of_month), ((hour, minute, second), _) =
- to_date_time v
- in
- let month = string_of_month month in
- let day_name = weekday v |> string_of_week in
- Printf.sprintf "%s, %02d %s %d %02d:%02d:%02d GMT" day_name day_of_month
- month year hour minute second
-end
-
-module List = struct
- include List
-
- let assoc_many k1 l =
- l |> List.filter_map (fun (k, v) -> if k = k1 then Some v else None)
-end
diff --git a/lib_webpush/test/test_webpush.ml b/lib_webpush/test/test_webpush.ml
index 35026a7..bed2384 100644
--- a/lib_webpush/test/test_webpush.ml
+++ b/lib_webpush/test/test_webpush.ml
@@ -18,5 +18,5 @@ let test_main_case1 () =
let () =
let open Alcotest in
- Mirage_crypto_rng_unix.initialize ();
+ Mirage_crypto_rng_unix.initialize (module Mirage_crypto_rng.Fortuna);
run "webpush" [ ("main", [ test_case "case1" `Quick test_main_case1 ]) ]
diff --git a/lib_yume/bare_server.ml b/lib_yume/bare_server.ml
new file mode 100644
index 0000000..2a7c21d
--- /dev/null
+++ b/lib_yume/bare_server.ml
@@ -0,0 +1,105 @@
+module Request = struct
+ include Cohttp.Request
+
+ let headers x = x |> headers |> Cohttp.Header.to_list
+end
+
+module Response = struct
+ type t = Cohttp_eio.Server.response_action
+end
+
+module Body = struct
+ include Cohttp_eio.Body
+
+ let to_string body = Eio.Buf_read.(parse_exn take_all) body ~max_size:max_int
+end
+
+let respond ~(status : Status.t) ~(headers : Headers.t) ~(body : string) =
+ let headers = headers |> Headers.to_list |> Cohttp.Header.of_list in
+ `Response
+ (Http.Response.make ~status ~headers (), Cohttp_eio.Body.of_string body)
+
+let start_server ~listen ~sw env k callback =
+ let callback _conn (req : Request.t) (body : Body.t) =
+ (* Invoke the handler *)
+ try callback req body
+ with e ->
+ let uri = Request.uri req in
+ let meth = Request.meth req in
+ Logq.err (fun m ->
+ m "Unexpected exception: %s %s: %s\n%s" (Method.to_string meth)
+ (Uri.to_string uri) (Printexc.to_string e)
+ (Printexc.get_backtrace ()));
+ respond ~status:`Internal_server_error ~body:"" ~headers:[]
+ in
+ let server = Cohttp_eio.Server.make_response_action ~callback () in
+ let socket =
+ Eio.Net.listen (Eio.Stdenv.net env) ~sw ~backlog:128 ~reuse_addr:true listen
+ in
+ Eio.Fiber.both
+ (fun () -> k socket)
+ (fun () -> Cohttp_eio.Server.run ~on_error:raise socket server)
+
+type ws_conn = {
+ mutable frames_out_fn : (Websocket.Frame.t option -> unit) option;
+ mutable closed : bool; [@default false]
+ recv_stream : string option Eio.Stream.t;
+}
+[@@deriving make]
+
+let websocket_handler conn frames_out_fn frame =
+ match frame with
+ | None ->
+ if not conn.closed then (
+ conn.closed <- true;
+ Eio.Stream.add conn.recv_stream None)
+ | Some Websocket.Frame.{ opcode; content; _ } -> (
+ match opcode with
+ | Close ->
+ Logq.debug (fun m -> m "Websocket: recv Close");
+ frames_out_fn
+ (Some (Websocket.Frame.create ~opcode:Close ~content:"" ()));
+ conn.closed <- true;
+ Eio.Stream.add conn.recv_stream None
+ | Text | Binary ->
+ Logq.debug (fun m -> m "Websocket: recv: %s" content);
+ Eio.Stream.add conn.recv_stream (Some content)
+ | Ping ->
+ frames_out_fn (Some (Websocket.Frame.create ~opcode:Pong ~content ()))
+ | Pong -> () (* Just ignore *)
+ | _ -> frames_out_fn (Some (Websocket.Frame.close 1002)))
+
+let ws_send ?(opcode = Websocket.Frame.Opcode.Text) (c : ws_conn) content =
+ if not c.closed then
+ Websocket.Frame.create ~opcode ~content ()
+ |> Option.some |> Option.get c.frames_out_fn
+
+let ws_recv (c : ws_conn) = Eio.Stream.take c.recv_stream
+
+let websocket env ~sw (req : Request.t) f =
+ let conn = make_ws_conn ~recv_stream:(Eio.Stream.create 10) () in
+ let resp, frames_out_fn =
+ Ws.Server.upgrade_connection req (websocket_handler conn)
+ in
+ conn.frames_out_fn <- Some frames_out_fn;
+ Eio.Fiber.fork ~sw (fun () ->
+ try
+ Logq.debug (fun m -> m "Websocket: start thread");
+
+ Eio.Fiber.both
+ (fun () ->
+ (* Send Ping continuously *)
+ let rec loop () =
+ Eio.Time.sleep env#clock 10.0;
+ ws_send ~opcode:Ping conn "";
+ loop ()
+ in
+ loop ())
+ (fun () ->
+ (* Start the process *)
+ f conn)
+ with e ->
+ Logq.err (fun m ->
+ m "Websocket: thread error: %s\n%s" (Printexc.to_string e)
+ (Printexc.get_backtrace ())));
+ resp
diff --git a/lib_yume/client.ml b/lib_yume/client.ml
new file mode 100644
index 0000000..851713b
--- /dev/null
+++ b/lib_yume/client.ml
@@ -0,0 +1,43 @@
+let null_auth ?ip:_ ~host:_ _ =
+ Ok None (* Warning: use a real authenticator in your code! *)
+
+let https ~authenticator =
+ let tls_config = Tls.Config.client ~authenticator () in
+ fun uri raw ->
+ let host =
+ Uri.host uri
+ |> Option.map (fun x -> Domain_name.(host_exn (of_string_exn x)))
+ in
+ Tls_eio.client_of_flow ?host tls_config raw
+
+module Response = struct
+ type t = { resp : Http.Response.t; body : Cohttp_eio.Body.t }
+
+ let status { resp; _ } = Http.Response.status resp
+
+ let headers { resp; _ } =
+ resp |> Http.Response.headers |> Http.Header.to_list |> Headers.of_list
+
+ let drain { body; _ } =
+ Eio.Buf_read.(parse_exn take_all) body ~max_size:max_int
+end
+
+let request ?headers ?body ~meth env ~sw (url : string) =
+ let headers = headers |> Option.map Cohttp.Header.of_list in
+ let body =
+ body |> Option.map (function `Fixed src -> Cohttp_eio.Body.of_string src)
+ in
+ let client =
+ Cohttp_eio.Client.make
+ ~https:(Some (https ~authenticator:null_auth))
+ (Eio.Stdenv.net env)
+ in
+ let resp, body =
+ Cohttp_eio.Client.call ~sw ?headers ?body client meth (Uri.of_string url)
+ in
+ Response.{ resp; body }
+
+let get = request ~meth:`GET
+let post = request ~meth:`POST
+let put = request ~meth:`PUT
+let delete = request ~meth:`DELETE
diff --git a/lib_yume/dune b/lib_yume/dune
new file mode 100644
index 0000000..df9b406
--- /dev/null
+++ b/lib_yume/dune
@@ -0,0 +1,13 @@
+(library
+ (name yume)
+ (preprocess
+ (pps ppx_deriving.show ppx_deriving.make))
+ (libraries
+ cohttp-eio
+ eio.unix
+ logq
+ multipart_form
+ multipart_form-eio
+ tls-eio
+ yojson
+ websocket))
diff --git a/lib_httpq/header.ml b/lib_yume/header.ml
similarity index 100%
rename from lib_httpq/header.ml
rename to lib_yume/header.ml
diff --git a/lib_httpq/headers.ml b/lib_yume/headers.ml
similarity index 100%
rename from lib_httpq/headers.ml
rename to lib_yume/headers.ml
diff --git a/lib_httpq/method.ml b/lib_yume/method.ml
similarity index 100%
rename from lib_httpq/method.ml
rename to lib_yume/method.ml
diff --git a/lib_httpq/path_pattern.ml b/lib_yume/path_pattern.ml
similarity index 85%
rename from lib_httpq/path_pattern.ml
rename to lib_yume/path_pattern.ml
index efdc5ae..68b6b07 100644
--- a/lib_httpq/path_pattern.ml
+++ b/lib_yume/path_pattern.ml
@@ -1,10 +1,8 @@
-open Util
-
type single_pattern = L of string | P of string | S
type t = single_pattern list
-let split_on_slash =
- String.split_on_char '/' |.> List.tl |.> List.filter (( <> ) "")
+let split_on_slash s =
+ s |> String.split_on_char '/' |> List.tl |> List.filter (( <> ) "")
let of_string (src : string) : t =
src |> split_on_slash
diff --git a/lib_httpq/server.ml b/lib_yume/server.ml
similarity index 52%
rename from lib_httpq/server.ml
rename to lib_yume/server.ml
index 9ed0cd5..571f6e6 100644
--- a/lib_httpq/server.ml
+++ b/lib_yume/server.ml
@@ -1,6 +1,3 @@
-open Util
-open Lwt.Infix
-
type formdata_t = {
filename : string option;
content_type : Multipart_form.Content_type.t;
@@ -22,7 +19,7 @@ type request =
path : string;
query : (string * string) list;
param : (string * string) list;
- body : (string option * request_body option) Lwt.t Lazy.t;
+ body : (string option * request_body option) Lazy.t;
headers : Headers.t;
}
@@ -35,7 +32,7 @@ type response =
tags : string list;
}
-type handler = request -> response Lwt.t
+type handler = Eio_unix.Stdenv.base -> request -> response
type middleware = handler -> handler
exception ErrorResponse of { status : Status.t; body : string }
@@ -44,11 +41,11 @@ let raise_error_response ?(body = "") status =
raise (ErrorResponse { status; body })
let respond ?(status = `OK) ?(headers = []) ?(tags = []) (body : string) =
- Response { status; headers; body; tags } |> Lwt.return
+ Response { status; headers; body; tags }
let body = function
| Request { body; _ } -> (
- Lazy.force body >|= function
+ match Lazy.force body with
| Some raw_body, _ -> raw_body
| _ -> failwith "body: none")
@@ -60,12 +57,15 @@ let string_of_yojson_atom = function
| `String s -> s
| _ -> failwith "string_of_yojson_atom"
-let query_many name : request -> string list Lwt.t = function
+let list_assoc_many k1 l =
+ l |> List.filter_map (fun (k, v) -> if k = k1 then Some v else None)
+
+let query_many name : request -> string list = function
| Request { body; query; _ } -> (
- match query |> List.assoc_many (name ^ "[]") with
- | _ :: _ as res -> Lwt.return res
+ match query |> list_assoc_many (name ^ "[]") with
+ | _ :: _ as res -> res
| [] -> (
- Lazy.force body >|= function
+ match Lazy.force body with
| _, Some (JSON (`Assoc l)) -> (
match List.assoc_opt name l with
| Some (`List l) -> l |> List.map string_of_yojson_atom
@@ -74,42 +74,41 @@ let query_many name : request -> string list Lwt.t = function
let formdata name = function
| Request { body; _ } -> (
- match%lwt Lazy.force body with
+ match Lazy.force body with
| _, Some (MultipartFormdata { loaded; _ }) -> (
match List.assoc_opt name loaded with
- | Some s -> Lwt.return_ok s
- | None -> Lwt.return_error ("The name " ^ name ^ " was not found"))
- | _ -> Lwt.return_error "formdata: not MultipartFormdata"
- | exception _ -> Lwt.return_error "Couldn't read body")
+ | Some s -> Ok s
+ | None -> Error ("The name " ^ name ^ " was not found"))
+ | _ -> Error "formdata: not MultipartFormdata"
+ | exception _ -> Error "Couldn't read body")
let formdata_exn name r =
- match%lwt formdata name r with
- | Ok s -> Lwt.return s
+ match formdata name r with
+ | Ok s -> s
| Error _ -> raise_error_response `Bad_request
let query ?default name req =
match req with
| Request { body; query; _ } -> (
- try%lwt
- Lazy.force body >|= snd >>= function
+ try
+ match Lazy.force body |> snd with
| None -> failwith "query: body none"
| Some x -> (
match x with
- | JSON (`Assoc l) ->
- List.assoc name l |> string_of_yojson_atom |> Lwt.return
+ | JSON (`Assoc l) -> List.assoc name l |> string_of_yojson_atom
| JSON _ -> failwith "json"
| Form body -> (
match query |> List.assoc_opt name with
- | Some x -> Lwt.return x
- | None -> body |> List.assoc name |> List.hd |> Lwt.return)
+ | Some x -> x
+ | None -> body |> List.assoc name |> List.hd)
| MultipartFormdata _ ->
- formdata_exn name req >|= fun f -> f.content)
+ let f = formdata_exn name req in
+ f.content)
with
- | _ when default <> None -> Option.get default |> Lwt.return
+ | _ when default <> None -> Option.get default
| _ -> raise_error_response `Bad_request)
-let query_opt name r =
- try%lwt query name r >|= Option.some with _ -> Lwt.return_none
+let query_opt name r = try Some (query name r) with _ -> None
let header_opt name : request -> string option = function
| Request { headers; _ } -> headers |> List.assoc_opt name
@@ -121,13 +120,46 @@ let headers = function Request { headers; _ } -> headers
let path = function Request { path; _ } -> path
let meth = function Request { meth; _ } -> meth
+let load_formdata_from_stream out_stream =
+ let open Multipart_form in
+ let rec save_part loaded raw =
+ match Eio.Stream.take_nonblocking out_stream with
+ | None -> loaded
+ | Some (_, hdr, contents) ->
+ let loaded =
+ let ( let* ) v f = v |> Option.fold ~none:loaded ~some:f in
+ let* cd = Header.content_disposition hdr in
+ let* name = Content_disposition.name cd in
+ let filename = Content_disposition.filename cd in
+ let buf = Buffer.create 0 in
+ let rec loop () =
+ match Eio.Stream.take_nonblocking contents with
+ | None -> ()
+ | Some s ->
+ Buffer.add_string buf s;
+ loop ()
+ in
+ loop ();
+ ( name,
+ make_formdata_t ?filename ~content_type:(Header.content_type hdr)
+ ~content:(Buffer.contents buf) () )
+ :: loaded
+ in
+ save_part loaded raw
+ in
+ save_part [] []
+
let parse_body ~body ~headers =
let content_type = List.assoc_opt `Content_type headers in
match
content_type
- |> Option.map (String.split_on_char ';' *> List.hd *> String.trim)
+ |> Option.map (fun s ->
+ s |> String.split_on_char ';' |> List.hd |> String.trim)
with
| Some "multipart/form-data" ->
+ (* FIXME: Currenty all contents are stored on memory.
+ This is not the best choice from perspective of space efficiency.
+ We should utilize files on disk *)
let load_body () =
let open Multipart_form in
let content_type =
@@ -135,60 +167,74 @@ let parse_body ~body ~headers =
| Ok s -> s
| Error (`Msg _msg) -> raise_error_response `Bad_request
in
- let `Parse th, stream =
- Multipart_form_lwt.stream ~identify:Fun.id
- (Bare_server.Body.to_stream body)
- content_type
- in
- let rec save_part loaded raw =
- match%lwt Lwt_stream.get stream with
- | None -> Lwt.return loaded
- | Some (_, hdr, contents) ->
- (* FIXME: Currenty all contents are stored on memory.
- This is not the best choice from perspective of space efficiency.
- We should utilize files on disk *)
- let%lwt loaded =
- let ( let* ) v f =
- v |> Option.fold ~none:(Lwt.return loaded) ~some:f
- in
- let* cd = Header.content_disposition hdr in
- let* name = Content_disposition.name cd in
- let filename = Content_disposition.filename cd in
- let buf = Buffer.create 0 in
- Lwt_stream.iter (Buffer.add_string buf) contents;%lwt
- Lwt.return
- (( name,
- make_formdata_t ?filename
- ~content_type:(Header.content_type hdr)
- ~content:(Buffer.contents buf) () )
- :: loaded)
- in
- save_part loaded raw
+
+ let in_stream = Eio.Stream.create 1 in
+ Eio.Stream.add in_stream (Bare_server.Body.to_string body);
+ Eio.Switch.run @@ fun sw ->
+ let th, out_stream =
+ Multipart_form_eio.stream ~sw ~identify:Fun.id in_stream content_type
in
- match%lwt Lwt.both th (save_part [] []) with
- | Error _, _ -> raise_error_response `Bad_request
- | Ok _, data -> Lwt.return data
+ match Eio.Promise.await_exn th with
+ | _ -> load_formdata_from_stream out_stream
+ | exception _ -> raise_error_response `Bad_request
in
- load_body () >|= fun loaded -> (None, MultipartFormdata { loaded })
+ (None, MultipartFormdata { loaded = load_body () })
| Some "application/json" -> (
- Bare_server.Body.to_string body >|= fun raw_body ->
+ let raw_body = Bare_server.Body.to_string body in
( Some raw_body,
try JSON (Yojson.Safe.from_string raw_body) with _ -> Form [] ))
| Some "application/x-www-form-urlencoded" | _ ->
- Bare_server.Body.to_string body >|= fun raw_body ->
+ let raw_body = Bare_server.Body.to_string body in
(Some raw_body, Form (Uri.query_of_encoded raw_body))
-let default_handler : handler = function
+let default_handler : handler =
+ fun _env -> function
| Request req ->
let status =
match req.meth with `GET -> `Not_found | _ -> `Method_not_allowed
in
respond ~status ""
-let start_server ?(port = 8080) ?error_handler (handler : handler) k : unit Lwt.t =
- Bare_server.start_server port (k ())
+type ws_conn = Bare_server.ws_conn
+
+module Ws_conn_man = struct
+ type t = {
+ chan : (request * (ws_conn -> unit) * response Eio.Stream.t) Eio.Stream.t;
+ }
+
+ let global_runner = { chan = Eio.Stream.create 0 }
+
+ let start_global_runner env ~sw =
+ Eio.Fiber.fork ~sw (fun () ->
+ let rec loop () =
+ let req, callback, recv_stream = Eio.Stream.take global_runner.chan in
+ let resp =
+ match req with
+ | Request { bare_req; _ } ->
+ let r = Bare_server.websocket env ~sw bare_req callback in
+ BareResponse r
+ in
+ Eio.Stream.add recv_stream resp;
+ loop ()
+ in
+ loop ())
+
+ let start_ws_conn req callback =
+ let recv_chan = Eio.Stream.create 0 in
+ Eio.Stream.add global_runner.chan (req, callback, recv_chan);
+ Eio.Stream.take recv_chan
+end
+
+let websocket (r : request) f = Ws_conn_man.start_ws_conn r f
+let ws_send = Bare_server.ws_send
+let ws_recv = Bare_server.ws_recv
+
+let start_server env ~sw ?(listen = `Tcp (Eio.Net.Ipaddr.V4.loopback, 8080))
+ ?error_handler (handler : handler) k : unit =
+ Ws_conn_man.start_global_runner env ~sw;
+ Bare_server.start_server ~listen env ~sw k
@@ fun (req : Bare_server.Request.t) (body : Bare_server.Body.t) :
- Bare_server.Response.t Lwt.t ->
+ Bare_server.Response.t ->
(* Parse req *)
let uri = Bare_server.Request.uri req in
let meth = Bare_server.Request.meth req in
@@ -199,9 +245,9 @@ let start_server ?(port = 8080) ?error_handler (handler : handler) k : unit Lwt.
in
let lazy_parsed_body =
lazy
- (match%lwt parse_body ~body ~headers with
- | exception _ -> Lwt.return (None, None)
- | raw_body, parsed_body -> Lwt.return (raw_body, Some parsed_body))
+ (match parse_body ~body ~headers with
+ | exception _ -> (None, None)
+ | raw_body, parsed_body -> (raw_body, Some parsed_body))
in
let req =
Request
@@ -219,11 +265,11 @@ let start_server ?(port = 8080) ?error_handler (handler : handler) k : unit Lwt.
in
(* Invoke the handler *)
- let%lwt res = handler req in
+ let res = handler env req in
(* Respond (after call error_handler if necessary *)
let rec aux first = function
- | BareResponse resp -> Lwt.return resp
+ | BareResponse resp -> resp
| Response { status; headers; body; _ }
when (not first)
|| Option.is_none error_handler
@@ -234,30 +280,19 @@ let start_server ?(port = 8080) ?error_handler (handler : handler) k : unit Lwt.
Bare_server.respond ~status ~headers ~body
| Response { status; headers; body; _ } ->
let error_handler = Option.get error_handler in
- error_handler ~req ~status ~headers ~body >>= aux false
+ error_handler ~req ~status ~headers ~body |> aux false
in
aux true res
-(* WebSocket *)
-type ws_conn = Bare_server.ws_conn
-
-let ws_send = Bare_server.ws_send
-let ws_recv = Bare_server.ws_recv
-
-let websocket (r : request) f =
- match r with
- | Request { bare_req; _ } ->
- Bare_server.websocket bare_req f >|= fun r -> BareResponse r
-
(* Middleware Router *)
module Router = struct
- type route = Method.t * string * (request -> response Lwt.t)
+ type route = Method.t * string * handler
type spec_entry = Route of route | Scope of (string * spec)
and spec = spec_entry list
- let use (spec : spec) (inner_handler : handler) (req : request) :
- response Lwt.t =
+ let use (spec : spec) (inner_handler : handler) env (req : request) : response
+ =
let routes =
let rec aux (spec : spec) : route list =
spec
@@ -274,29 +309,31 @@ module Router = struct
match req with
| Request req -> (
- ((* Choose correct handler from routes *)
- let param, handler =
- routes
- |> List.find_map (fun (meth', pat, handler) ->
- if req.meth <> meth' then None
- else
- Path_pattern.perform ~pat req.path
- |> Option.map (fun param -> (param, handler)))
- |> Option.value ~default:([], inner_handler)
- in
- let req = Request { req with param } in
- try%lwt handler req with
- | ErrorResponse { status; body } ->
- Logq.debug (fun m ->
- m "Error response raised: %s\n%s" (Status.to_string status)
- (Printexc.get_backtrace ()));
- respond ~status ~tags:[ "log" ] body
- | e ->
- Logq.debug (fun m ->
- m "Exception raised: %s\n%s" (Printexc.to_string e)
- (Printexc.get_backtrace ()));
- respond ~status:`Internal_server_error "")
- >|= function
+ (* Choose correct handler from routes *)
+ let param, handler =
+ routes
+ |> List.find_map (fun (meth', pat, handler) ->
+ if req.meth <> meth' then None
+ else
+ Path_pattern.perform ~pat req.path
+ |> Option.map (fun param -> (param, handler)))
+ |> Option.value ~default:([], inner_handler)
+ in
+ let req = Request { req with param } in
+ let resp =
+ try handler env req with
+ | ErrorResponse { status; body } ->
+ Logq.debug (fun m ->
+ m "Error response raised: %s\n%s" (Status.to_string status)
+ (Printexc.get_backtrace ()));
+ respond ~status ~tags:[ "log" ] body
+ | e ->
+ Logq.debug (fun m ->
+ m "Exception raised: %s\n%s" (Printexc.to_string e)
+ (Printexc.get_backtrace ()));
+ respond ~status:`Internal_server_error ""
+ in
+ match resp with
| Response ({ status; tags; _ } as r) when Status.is_error status ->
Response { r with tags = "log" :: tags }
| r -> r)
@@ -323,10 +360,10 @@ module Cors = struct
let target_pat = Path_pattern.of_string target in
{ target; target_pat; methods; origin; expose }
- let use (src : t list) (inner_handler : handler) (req : request) :
- response Lwt.t =
+ let use (src : t list) (inner_handler : handler) env (req : request) :
+ response =
(* Handler for preflight OPTIONS requests *)
- let handler (r : t) (req : request) : response Lwt.t =
+ let handler (r : t) _ (req : request) : response =
let headers =
[
( `Access_control_allow_methods,
@@ -351,31 +388,32 @@ module Cors = struct
in
(* Construct router *)
- req
- |> Router.use routes @@ function
- (* Fallback handler: apply inner_handler, and
- if path matches, append CORS headers *)
- | Request { path; _ } as req -> (
- let path_match =
- src
- |> List.find_opt (fun { target_pat; _ } ->
- Path_pattern.perform ~pat:target_pat path |> Option.is_some)
- in
- inner_handler req >|= fun resp ->
- match (resp, path_match) with
- | _, None | BareResponse _, _ -> resp
- | Response res, Some { origin; expose; _ } ->
- Response
- {
- res with
- headers =
- (`Access_control_allow_origin, origin)
- :: ( `Access_control_expose_headers,
- expose
- |> List.map Header.string_of_name
- |> String.concat ", " )
- :: res.headers;
- })
+ Router.use routes
+ (fun env -> function
+ (* Fallback handler: apply inner_handler, and
+ if path matches, append CORS headers *)
+ | Request { path; _ } as req -> (
+ let path_match =
+ src
+ |> List.find_opt (fun { target_pat; _ } ->
+ Path_pattern.perform ~pat:target_pat path |> Option.is_some)
+ in
+ let resp = inner_handler env req in
+ match (resp, path_match) with
+ | _, None | BareResponse _, _ -> resp
+ | Response res, Some { origin; expose; _ } ->
+ Response
+ {
+ res with
+ headers =
+ (`Access_control_allow_origin, origin)
+ :: ( `Access_control_expose_headers,
+ expose
+ |> List.map Header.string_of_name
+ |> String.concat ", " )
+ :: res.headers;
+ }))
+ env req
end
(* Middlware Logger *)
@@ -389,25 +427,31 @@ module Logger = struct
Bare_server.Request.pp_hum fmt bare_req;
Format.pp_print_flush fmt ();
add_string buf "\n";
- ( body |> Lazy.force >|= fst >|= fun raw_body ->
- raw_body
- |> Option.iter (fun s ->
- add_string buf "\n";
- add_string buf s;
- add_string buf "\n") );%lwt
+ let raw_body =
+ if Http.Request.has_body bare_req = `Yes then
+ body |> Lazy.force |> fst
+ else None
+ in
+ raw_body
+ |> Option.iter (fun s ->
+ add_string buf "\n";
+ add_string buf s;
+ add_string buf "\n");
add_string buf "\n==============================\n";
add_string buf ("Status: " ^ Status.to_string status);
- Buffer.contents buf |> Lwt.return
+ Buffer.contents buf
| _ -> assert false
- let use ?dump_req_dir (inner_handler : handler) (req : request) :
- response Lwt.t =
+ let now () = Unix.gettimeofday () |> Ptime.of_float_s |> Option.get
+
+ let use ?dump_req_dir (inner_handler : handler) env (req : request) : response
+ =
let (Request { uri; meth; _ }) = req in
let meth = Method.to_string meth in
let uri = Uri.to_string uri in
Logq.debug (fun m -> m "%s %s" meth uri);
- let%lwt resp = inner_handler req in
+ let resp = inner_handler env req in
(match resp with
| Response { status; _ } ->
@@ -418,16 +462,10 @@ module Logger = struct
| Response { tags; _ } when List.mem "log" tags -> (
match dump_req_dir with
| None ->
- string_of_request_response req resp >|= fun s ->
+ let s = string_of_request_response req resp in
Logq.info (fun m -> m "Detail of request and response:\n%s" s)
- | Some dir ->
- (* NOTE: We use open_temp_file to make sure that each request is written to each file. So, this file should NOT be removed after we write the content to it. *)
- let prefix =
- Ptime.(now () |> to_float_s |> Printf.sprintf "%.2f") ^ "."
- in
- let%lwt _, oc = Lwt_io.open_temp_file ~temp_dir:dir ~prefix () in
- string_of_request_response req resp >>= Lwt_io.write oc)
- | _ -> Lwt.return_unit);%lwt
+ | Some _dir -> (* FIXME: implement here *) ())
+ | _ -> ());
- Lwt.return resp
+ resp
end
diff --git a/lib_httpq/signature.ml b/lib_yume/signature.ml
similarity index 96%
rename from lib_httpq/signature.ml
rename to lib_yume/signature.ml
index adb5a9a..f793965 100644
--- a/lib_httpq/signature.ml
+++ b/lib_yume/signature.ml
@@ -65,7 +65,10 @@ let may_cons_digest_header ?(prefix = "SHA-256") (headers : Headers.t)
(body : string option) : Headers.t =
body
|> Option.fold ~none:headers ~some:(fun body ->
- let digest = Sha256.(string body |> to_bin |> Base64.encode_exn) in
+ let digest =
+ body |> Cstruct.of_string |> Mirage_crypto.Hash.SHA256.digest
+ |> Cstruct.to_string |> Base64.encode_exn
+ in
let digest = prefix ^ "=" ^ digest in
match List.assoc_opt `Digest headers with
| Some v when v <> digest -> failwith "Digest not match"
diff --git a/lib_httpq/status.ml b/lib_yume/status.ml
similarity index 100%
rename from lib_httpq/status.ml
rename to lib_yume/status.ml
diff --git a/lib_yume/test/dune b/lib_yume/test/dune
new file mode 100644
index 0000000..35d9743
--- /dev/null
+++ b/lib_yume/test/dune
@@ -0,0 +1,10 @@
+(tests
+ (names test_signature test_path_pattern test_ws)
+ (libraries
+ alcotest
+ eio_main
+ yume
+ logq
+ mirage-crypto-rng
+ mirage-crypto-rng.unix
+ mirage-crypto-rng-eio))
diff --git a/lib_yume/test/test_path_pattern.ml b/lib_yume/test/test_path_pattern.ml
new file mode 100644
index 0000000..9ffb5d7
--- /dev/null
+++ b/lib_yume/test/test_path_pattern.ml
@@ -0,0 +1,19 @@
+open Yume
+
+let test_parse_path () =
+ let open Path_pattern in
+ assert (of_string "" = []);
+ assert (of_string "/foo/bar/2000" = [ L "foo"; L "bar"; L "2000" ]);
+ assert (of_string "/foo/:bar/2000" = [ L "foo"; P ":bar"; L "2000" ]);
+ assert (of_string "/foo/*" = [ L "foo"; S ]);
+ assert (of_string "/foo/:bar" = [ L "foo"; P ":bar" ]);
+ assert (perform ~pat:(of_string "/foo/:bar") "/foo/1" = Some [ (":bar", "1") ]);
+ assert (perform ~pat:(of_string "/foo/*") "/foo/1/2" |> Option.is_some);
+ assert (perform ~pat:(of_string "/foo/bar") "/foo/bar/" |> Option.is_some);
+ assert (perform ~pat:(of_string "/foo/bar") "/foo/bar//" |> Option.is_some);
+ ()
+
+let () =
+ let open Alcotest in
+ run "path_pattern"
+ [ ("parse_path", [ test_case "case1" `Quick test_parse_path ]) ]
diff --git a/test/test_http.ml b/lib_yume/test/test_signature.ml
similarity index 82%
rename from test/test_http.ml
rename to lib_yume/test/test_signature.ml
index 771fdcb..7fc82cd 100644
--- a/test/test_http.ml
+++ b/lib_yume/test/test_signature.ml
@@ -1,18 +1,4 @@
-open Waq
-open Httpq
-
-let test_parse_path () =
- let open Path_pattern in
- assert (of_string "" = []);
- assert (of_string "/foo/bar/2000" = [ L "foo"; L "bar"; L "2000" ]);
- assert (of_string "/foo/:bar/2000" = [ L "foo"; P ":bar"; L "2000" ]);
- assert (of_string "/foo/*" = [ L "foo"; S ]);
- assert (of_string "/foo/:bar" = [ L "foo"; P ":bar" ]);
- assert (perform ~pat:(of_string "/foo/:bar") "/foo/1" = Some [ (":bar", "1") ]);
- assert (perform ~pat:(of_string "/foo/*") "/foo/1/2" |> Option.is_some);
- assert (perform ~pat:(of_string "/foo/bar") "/foo/bar/" |> Option.is_some);
- assert (perform ~pat:(of_string "/foo/bar") "/foo/bar//" |> Option.is_some);
- ()
+open Yume
let test_build_signing_string () =
let signed_headers =
@@ -183,35 +169,13 @@ KgbztieZwDBihVKbPtiaiGxeNXrxGWfL37BB0Jcy/RRYomLBjwTj2Ks=
@@ Signature.verify ~pub_key ~algorithm ~signed_headers ~signature ~headers
~meth ~path ~body)
-let test_url () =
- (* Thanks to: https://ja.wikipedia.org/wiki/Uniform_Resource_Identifier *)
- let url =
- Uri.of_string
- "https://user:password@www.example.com:123/forum/questions/?tag=networking&order=newest#top"
- in
- assert (Uri.getaddrinfo_port url = "123");
- assert (Uri.http_host url = "www.example.com:123");
- assert (Uri.domain url = "www.example.com:123");
- assert (
- Uri.path_query_fragment url
- = "/forum/questions/?tag=networking&order=newest#top");
-
- let url = Uri.of_string "http://example.com" in
- assert (Uri.getaddrinfo_port url = "http");
- assert (Uri.http_host url = "example.com");
- assert (Uri.domain url = "example.com");
- assert (Uri.path_query_fragment url = "");
- ()
-
let () =
let open Alcotest in
- Crypto.initialize ();
- run "http"
+ Mirage_crypto_rng_unix.initialize (module Mirage_crypto_rng.Fortuna);
+ run "signature"
[
- ("parse_path", [ test_case "case1" `Quick test_parse_path ]);
( "build_signing_string",
[ test_case "case1" `Quick test_build_signing_string ] );
("sign", [ test_case "case1" `Quick test_sign ]);
("verify", [ test_case "case1" `Quick test_verify ]);
- ("url", [ test_case "case1" `Quick test_url ]);
]
diff --git a/lib_yume/test/test_ws.ml b/lib_yume/test/test_ws.ml
new file mode 100644
index 0000000..eb4e9e9
--- /dev/null
+++ b/lib_yume/test/test_ws.ml
@@ -0,0 +1,56 @@
+open Yume
+
+let test_basic () =
+ let recv_text = ref "" in
+ let expected_string = "TEST TEXT" in
+ Eio_main.run (fun env ->
+ Mirage_crypto_rng_eio.run (module Mirage_crypto_rng.Fortuna) env
+ @@ fun () ->
+ try
+ Eio.Time.with_timeout_exn env#clock 3.0 @@ fun () ->
+ let handler =
+ let open Server in
+ default_handler
+ |> Router.(
+ use
+ [
+ get "/ws" (fun _ req ->
+ websocket req (fun ws_conn ->
+ recv_text :=
+ ws_recv ws_conn |> Option.value ~default:!recv_text));
+ ])
+ in
+ let listen =
+ Eio.Net.getaddrinfo_stream ~service:"0" env#net "localhost" |> List.hd
+ in
+ Eio.Switch.run @@ fun sw ->
+ Server.start_server env ~listen ~sw handler (fun socket ->
+ let listening_port =
+ match Eio.Net.listening_addr socket with
+ | `Tcp (_, port) -> port
+ | _ -> assert false
+ in
+ let ws_conn =
+ let rec loop () =
+ Eio.Time.sleep env#clock 1.0;
+ try
+ Ws.Client.connect ~sw env
+ (Printf.sprintf "http://localhost:%d/ws" listening_port)
+ with e ->
+ Logq.err (fun m ->
+ m "Ws.Client.connect failed: %s" (Printexc.to_string e));
+ loop ()
+ in
+ loop ()
+ in
+ Ws.Client.write ws_conn
+ (Websocket.Frame.create ~opcode:Text ~content:expected_string ());
+ ())
+ with Eio.Time.Timeout -> ());
+ assert (!recv_text = expected_string);
+ ()
+
+let () =
+ let open Alcotest in
+ Logq.(add_reporter (make_stderr_reporter ~l:Debug));
+ run "ws" [ ("basic", [ test_case "case1" `Quick test_basic ]) ]
diff --git a/lib_yume/ws.ml b/lib_yume/ws.ml
new file mode 100644
index 0000000..0b40a53
--- /dev/null
+++ b/lib_yume/ws.ml
@@ -0,0 +1,211 @@
+open struct
+ let random_string len = Mirage_crypto_rng.generate len |> Cstruct.to_string
+
+ let b64_encoded_sha1sum s =
+ s |> Cstruct.of_string |> Mirage_crypto.Hash.SHA1.digest
+ |> Cstruct.to_string |> Base64.encode_exn
+end
+
+open Websocket.Make (Cohttp_eio.Private.IO)
+
+module Client = struct
+ type 'a conn = {
+ id : string;
+ read_frame : unit -> Websocket.Frame.t;
+ write_frame : Websocket.Frame.t option -> unit;
+ socket : 'a Eio.Net.stream_socket_ty Eio.Resource.t;
+ }
+
+ let drain_handshake req ic oc nonce =
+ Request.write (fun _ -> ()) req oc;
+ let resp =
+ match Response.read ic with
+ | `Ok r -> r
+ | `Eof -> raise End_of_file
+ | `Invalid s -> failwith s
+ in
+ let status = Cohttp.Response.status resp in
+ let headers = Cohttp.Response.headers resp in
+ if Cohttp.Code.(is_error (code_of_status status)) then
+ failwith ("error status: " ^ Cohttp.Code.(string_of_status status));
+ if Cohttp.Response.version resp <> `HTTP_1_1 then
+ failwith "invalid HTTP version";
+ if status <> `Switching_protocols then failwith "wrong status";
+ (match Cohttp.Header.get headers "upgrade" with
+ | Some a when String.lowercase_ascii a = "websocket" -> ()
+ | _ -> failwith "wrong upgrade");
+ if not (Websocket.upgrade_present headers) then
+ failwith "upgrade header not present";
+ (match Cohttp.Header.get headers "sec-websocket-accept" with
+ | Some accept
+ when accept = b64_encoded_sha1sum (nonce ^ Websocket.websocket_uuid) ->
+ ()
+ | _ -> failwith "wrong accept");
+ ()
+
+ let connect' env sw url nonce extra_headers =
+ (* Make request *)
+ let headers =
+ Cohttp.Header.add_list extra_headers
+ [
+ ("Upgrade", "websocket");
+ ("Connection", "Upgrade");
+ ("Sec-WebSocket-Key", nonce);
+ ("Sec-WebSocket-Version", "13");
+ ]
+ in
+ let req = Cohttp.Request.make ~headers url in
+
+ (* Make socket *)
+ let host = Uri.host url |> Option.get in
+ let service =
+ match Uri.port url with
+ | Some port -> string_of_int port
+ | None -> (
+ match Uri.scheme url with
+ | Some "ws" -> "http"
+ | Some "wss" -> "https"
+ | Some scheme -> scheme
+ | None -> "http")
+ in
+ let addr =
+ match Eio.Net.getaddrinfo_stream (Eio.Stdenv.net env) host ~service with
+ | [] -> failwith "getaddrinfo failed"
+ | addr :: _ -> addr
+ in
+ let socket = Eio.Net.connect ~sw (Eio.Stdenv.net env) addr in
+ let flow =
+ let tls_enabled =
+ match service with "443" | "https" -> true | _ -> false
+ in
+ if not tls_enabled then (socket :> Eio.Flow.two_way_ty Eio.Resource.t)
+ else
+ let authenticator =
+ let null_auth ?ip:_ ~host:_ _ = Ok None in
+ null_auth
+ in
+ let host =
+ Result.to_option
+ (Result.bind (Domain_name.of_string host) Domain_name.host)
+ in
+ let client =
+ Tls_eio.client_of_flow
+ Tls.Config.(
+ client ~version:(`TLS_1_0, `TLS_1_3) ~authenticator
+ ~ciphers:Ciphers.supported ())
+ ?host socket
+ in
+ (client :> Eio.Flow.two_way_ty Eio.Resource.t)
+ in
+
+ (* Drain handshake *)
+ let ic = Eio.Buf_read.of_flow ~max_size:max_int flow in
+ Eio.Buf_write.with_flow flow (fun oc -> drain_handshake req ic oc nonce);
+
+ (socket, flow, ic)
+
+ let connect ?(extra_headers = Cohttp.Header.init ()) ~sw env url =
+ let url = Uri.of_string url in
+
+ let nonce = Base64.encode_exn (random_string 16) in
+ let socket, flow, ic = connect' env sw url nonce extra_headers in
+
+ (* Start writer fiber. All writes must be done in this fiber,
+ because Eio.Flow.write is not thread-safe.
+ c.f.: https://github.com/ocaml-multicore/eio/blob/v0.11/lib_eio/flow.mli#L73-L74
+ *)
+ let write_queue = Eio.Stream.create 10 in
+ (let rec writer () =
+ try
+ let frame = Eio.Stream.take write_queue in
+ match frame with
+ | None -> ()
+ | Some frame ->
+ let buf = Buffer.create 128 in
+ write_frame_to_buf ~mode:(Client random_string) buf frame;
+ Eio.Buf_write.with_flow flow (fun oc ->
+ Eio.Buf_write.string oc (Buffer.contents buf));
+ writer ()
+ with Eio.Io _ -> ()
+ in
+ Eio.Fiber.fork ~sw writer);
+
+ let write_frame frame = Eio.Stream.add write_queue frame in
+ let read_frame () =
+ Eio.Buf_write.with_flow flow (fun oc ->
+ make_read_frame ~mode:(Client random_string) ic oc ())
+ in
+
+ { socket; id = random_string 10; read_frame; write_frame }
+
+ let id { id; _ } = id
+ let read { read_frame; _ } = read_frame ()
+ let write { write_frame; _ } frame = write_frame (Some frame)
+
+ let close_transport { socket; write_frame; _ } =
+ write_frame None;
+ Eio.Net.close socket
+end
+
+module Server = struct
+ let read_frames ic oc handler_fn =
+ let read_frame = make_read_frame ~mode:Server ic oc in
+ let rec inner () = read_frame () |> handler_fn |> inner in
+ inner ()
+
+ let send_frames stream oc =
+ let buf = Buffer.create 128 in
+ let send_frame fr =
+ Buffer.clear buf;
+ write_frame_to_buf ~mode:Server buf fr;
+ Eio.Buf_write.string oc (Buffer.contents buf)
+ in
+ let rec inner () =
+ match Eio.Stream.take stream with
+ | None -> () (* end of stream *)
+ | Some fr ->
+ send_frame fr;
+ inner ()
+ in
+ inner ()
+
+ let upgrade_connection request incoming_handler =
+ let headers = Cohttp.Request.headers request in
+ let key =
+ match Cohttp.Header.get headers "sec-websocket-key" with
+ | Some key -> key
+ | None ->
+ failwith "upgrade_connection: missing header `sec-websocket-key`"
+ in
+ let hash = b64_encoded_sha1sum (key ^ Websocket.websocket_uuid) in
+ let response_headers =
+ Cohttp.Header.of_list
+ [
+ ("Upgrade", "websocket");
+ ("Connection", "Upgrade");
+ ("Sec-WebSocket-Accept", hash);
+ ]
+ in
+ let resp =
+ Cohttp.Response.make ~status:`Switching_protocols
+ ~encoding:Cohttp.Transfer.Unknown ~headers:response_headers ~flush:true
+ ()
+ in
+ let frames_out_stream = Eio.Stream.create 10 in
+ let frames_out_fn = Eio.Stream.add frames_out_stream in
+ let handle_conn ic oc =
+ let handler = incoming_handler frames_out_fn in
+ (try
+ Eio.Fiber.both
+ (fun () -> read_frames ic oc (fun fr -> handler (Some fr)))
+ (fun () -> send_frames frames_out_stream oc)
+ with
+ | End_of_file -> ()
+ | e ->
+ Logq.err (fun m ->
+ m "ws connection broken by exc: %s: %s" (Printexc.to_string e)
+ (Printexc.get_backtrace ())));
+ handler None
+ in
+ (`Expert (resp, handle_conn), frames_out_fn)
+end
diff --git a/lib_httpq/httpq.ml b/lib_yume/yume.ml
similarity index 80%
rename from lib_httpq/httpq.ml
rename to lib_yume/yume.ml
index 53f95a0..7b1b855 100644
--- a/lib_httpq/httpq.ml
+++ b/lib_yume/yume.ml
@@ -1,9 +1,10 @@
-module Client = Client
+module Path_pattern = Path_pattern
+module Status = Status
module Header = Header
module Headers = Headers
module Method = Method
-module Path_pattern = Path_pattern
+module Ws = Ws
+module Bare_server = Bare_server
module Server = Server
+module Client = Client
module Signature = Signature
-module Status = Status
-module Uri = Util.Uri
diff --git a/test/dune b/test/dune
index 36bfaa4..95f80dd 100644
--- a/test/dune
+++ b/test/dune
@@ -1,5 +1,5 @@
(tests
- (names test_http test_util test_text_helper test_regex test_ogp)
+ (names test_util test_text_helper test_regex test_ogp)
(preprocess
(pps lwt_ppx))
- (libraries alcotest alcotest-lwt httpq logq waq))
+ (libraries alcotest alcotest-lwt logq waq))
diff --git a/waq.opam b/waq.opam
index 5977a90..eee3973 100644
--- a/waq.opam
+++ b/waq.opam
@@ -19,7 +19,7 @@ depends: [
"camlimages"
"cmdliner"
"cohttp"
- "cohttp-lwt-unix"
+ "cohttp-eio"
"cppo"
"cstruct"
"eio"
@@ -32,9 +32,10 @@ depends: [
"lwt_eio"
"lwt_ppx"
"lwt_ssl"
- "mirage-crypto-rng" {<= "0.10.7"}
+ "mirage-crypto-rng"
+ "mirage-crypto-rng-eio"
"multipart_form"
- "multipart_form-lwt"
+ "multipart_form-eio"
"ocaml" {>= "5.1.0"}
"pcre"
"postgresql"
@@ -45,10 +46,10 @@ depends: [
"result"
"safepass"
"sha"
+ "tls-eio"
"uri"
"uuidm"
"websocket"
- "websocket-lwt-unix"
"x509"
"xml-light"
"yaml"
@@ -71,5 +72,10 @@ build: [
]
dev-repo: "git+https://github.com/ushitora-anqou/waq.git"
pin-depends:[
+ [ "cohttp.6.0.0~alpha2" "git+https://github.com/mirage/ocaml-cohttp.git#v6.0.0_beta2" ]
+ [ "cohttp-eio.6.0.0~alpha2" "git+https://github.com/mirage/ocaml-cohttp.git#v6.0.0_beta2" ]
[ "camlimages.5.0.5" "git+https://github.com/ushitora-anqou/camlimages.git#90ceb604527e5e1daddc969d920ff37d118b8a60" ]
+ [ "websocket.2.16" "git+https://github.com/ushitora-anqou/ocaml-websocket#74988ec5ec7d7c620e7d58c5509acd003107c513" ]
+ [ "multipart_form.0.5.0" "git+https://github.com/dinosaure/multipart_form#a794239b8fc9601540ffea489b2c470227216c5e" ]
+ [ "multipart_form-eio.0.5.0" "git+https://github.com/dinosaure/multipart_form#a794239b8fc9601540ffea489b2c470227216c5e" ]
]