mirror of
https://github.com/c-cube/ezcurl.git
synced 2025-12-06 03:05:31 -05:00
Merge c89c54218d into 0faaf35969
This commit is contained in:
commit
729b4b5913
8 changed files with 268 additions and 1 deletions
3
.github/workflows/main.yml
vendored
3
.github/workflows/main.yml
vendored
|
|
@ -16,8 +16,9 @@ jobs:
|
|||
# - macos-latest # build issues with `ar` (!!!)
|
||||
#- windows-latest # certificate problem
|
||||
ocaml-compiler:
|
||||
- 4.03.x
|
||||
- 4.08.x
|
||||
- 4.12.x
|
||||
- 5.2.x
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
|
|
|||
17
dune-project
17
dune-project
|
|
@ -39,3 +39,20 @@
|
|||
(odoc :with-doc)
|
||||
(ocaml
|
||||
(>= 4.03))))
|
||||
|
||||
(package
|
||||
(name ezcurl-picos)
|
||||
(synopsis "Friendly wrapper around OCurl, using picos")
|
||||
(tags
|
||||
("curl" "web" "http" "client" "lwt"))
|
||||
(depends
|
||||
(ezcurl
|
||||
(= :version))
|
||||
(picos (and (>= 0.5) (< 0.6)))
|
||||
(picos_std (and (>= 0.5) (< 0.6)))
|
||||
(picos_io (and (>= 0.5) (< 0.6)))
|
||||
(picos_mux (and (>= 0.5) (< 0.6) :with-test))
|
||||
(mdx :with-test)
|
||||
(odoc :with-doc)
|
||||
(ocaml
|
||||
(>= 4.03))))
|
||||
|
|
|
|||
37
ezcurl-picos.opam
Normal file
37
ezcurl-picos.opam
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
# This file is generated by dune, edit dune-project instead
|
||||
opam-version: "2.0"
|
||||
version: "0.2.4"
|
||||
synopsis: "Friendly wrapper around OCurl, using picos"
|
||||
maintainer: ["simon.cruanes.2007@m4x.org"]
|
||||
authors: ["Simon Cruanes"]
|
||||
license: "MIT"
|
||||
tags: ["curl" "web" "http" "client" "lwt"]
|
||||
homepage: "https://github.com/c-cube/ezcurl"
|
||||
doc: "https://c-cube.github.io/ezcurl/"
|
||||
bug-reports: "https://github.com/c-cube/ezcurl/issues"
|
||||
depends: [
|
||||
"dune" {>= "3.0"}
|
||||
"ezcurl" {= version}
|
||||
"picos" {>= "0.5" & < "0.6"}
|
||||
"picos_std" {>= "0.5" & < "0.6"}
|
||||
"picos_io" {>= "0.5" & < "0.6"}
|
||||
"picos_mux" {>= "0.5" & < "0.6" & with-test}
|
||||
"mdx" {with-test}
|
||||
"odoc" {with-doc}
|
||||
"ocaml" {>= "4.03"}
|
||||
]
|
||||
build: [
|
||||
["dune" "subst"] {dev}
|
||||
[
|
||||
"dune"
|
||||
"build"
|
||||
"-p"
|
||||
name
|
||||
"-j"
|
||||
jobs
|
||||
"@install"
|
||||
"@runtest" {with-test}
|
||||
"@doc" {with-doc}
|
||||
]
|
||||
]
|
||||
dev-repo: "git+https://github.com/c-cube/ezcurl.git"
|
||||
12
src/picos/dune
Normal file
12
src/picos/dune
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
(library
|
||||
(name ezcurl_picos)
|
||||
(public_name ezcurl-picos)
|
||||
(flags :standard -warn-error -32)
|
||||
(libraries
|
||||
curl
|
||||
ezcurl.core
|
||||
picos
|
||||
picos_std.sync
|
||||
picos_std.event
|
||||
picos_io.select
|
||||
picos_io))
|
||||
185
src/picos/ezcurl_picos.ml
Normal file
185
src/picos/ezcurl_picos.ml
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
(* inspired from curl_lwt *)
|
||||
|
||||
include Ezcurl_core
|
||||
module Mutex = Picos_std_sync.Mutex
|
||||
module Stream = Picos_std_sync.Stream
|
||||
module C = Picos.Computation
|
||||
module M = Curl.Multi
|
||||
module Fd = Picos_io_fd
|
||||
|
||||
type bg_state = {
|
||||
bt: Printexc.raw_backtrace;
|
||||
mt: Curl.Multi.mt;
|
||||
fds: (Unix.file_descr, Fd.t) Hashtbl.t;
|
||||
comps: (Curl.t, Curl.curlCode C.t) Hashtbl.t;
|
||||
}
|
||||
|
||||
type task =
|
||||
| T_add of Curl.t * Curl.curlCode C.t
|
||||
| T_timeout of { ms: int }
|
||||
| T_socket of Unix.file_descr * M.poll
|
||||
|
||||
type st = {
|
||||
lock: Mutex.t;
|
||||
mutable bg: (unit C.t * Picos.Fiber.t * unit Picos_std_sync.Ivar.t) option;
|
||||
(** Runs tasks from [tasks] *)
|
||||
tasks: task Stream.t;
|
||||
}
|
||||
|
||||
let on_cancel_ _tr (self : bg_state) h = Curl.Multi.remove self.mt h
|
||||
|
||||
let perform_ (self : st) (h : Curl.t) : Curl.curlCode =
|
||||
let comp = C.create () in
|
||||
Stream.push self.tasks (T_add (h, comp));
|
||||
C.await comp
|
||||
|
||||
exception Timeout
|
||||
|
||||
let spawn_ (f : Picos.Fiber.t -> 'a) : 'a C.t * Picos.Fiber.t =
|
||||
let comp = C.create () in
|
||||
let fiber = Picos.Fiber.create ~forbid:false comp in
|
||||
Picos.Fiber.spawn fiber f;
|
||||
comp, fiber
|
||||
|
||||
let[@inline] spawn_ignore_ f : unit = ignore (spawn_ f : _ * _)
|
||||
|
||||
let process_task_ (self : bg_state) (t : task) =
|
||||
match t with
|
||||
| T_timeout { ms } ->
|
||||
Printf.eprintf "TIMEOUT %d\n%!" ms;
|
||||
let comp = C.create () in
|
||||
Picos_io_select.cancel_after comp
|
||||
~seconds:(float ms *. 1000.)
|
||||
Timeout self.bt;
|
||||
spawn_ignore_ @@ fun _ ->
|
||||
(match C.await comp with
|
||||
| () -> ()
|
||||
| exception Timeout -> M.action_timeout self.mt)
|
||||
| T_add (h, comp) ->
|
||||
Printf.eprintf "ADD\n%!";
|
||||
let trigger =
|
||||
(* no IO inside handler *)
|
||||
(Picos.Trigger.from_action self h on_cancel_ [@alert "-handler"])
|
||||
in
|
||||
|
||||
let attached_ok = C.try_attach comp trigger in
|
||||
assert attached_ok;
|
||||
|
||||
(* register to curl *)
|
||||
Hashtbl.add self.comps h comp;
|
||||
Curl.Multi.add self.mt h
|
||||
| T_socket (u_fd, what) ->
|
||||
Printf.eprintf "POLL fd=%d\n%!" (Obj.magic u_fd : int);
|
||||
let get_fd self =
|
||||
try Hashtbl.find self.fds u_fd
|
||||
with Not_found ->
|
||||
Unix.set_nonblock u_fd;
|
||||
let fd = Fd.create u_fd in
|
||||
Hashtbl.add self.fds u_fd fd;
|
||||
fd
|
||||
in
|
||||
|
||||
(match what with
|
||||
| M.POLL_REMOVE ->
|
||||
let fd = Hashtbl.find_opt self.fds u_fd in
|
||||
Hashtbl.remove self.fds u_fd;
|
||||
Option.iter Fd.decr fd
|
||||
| M.POLL_NONE -> ()
|
||||
| M.POLL_IN ->
|
||||
let fd = get_fd self in
|
||||
spawn_ignore_ (fun _ ->
|
||||
ignore (Picos_io_select.await_on fd `R : Fd.t);
|
||||
ignore (M.action self.mt u_fd M.EV_IN : int))
|
||||
| M.POLL_OUT ->
|
||||
let fd = get_fd self in
|
||||
spawn_ignore_ (fun _ ->
|
||||
ignore (Picos_io_select.await_on fd `W : Fd.t);
|
||||
ignore (M.action self.mt u_fd M.EV_OUT : int))
|
||||
| M.POLL_INOUT ->
|
||||
let fd = get_fd self in
|
||||
spawn_ignore_ (fun _ ->
|
||||
let ev =
|
||||
Picos_std_event.Event.(
|
||||
select
|
||||
[
|
||||
wrap (Picos_io_select.on fd `R) (fun _ -> M.EV_IN);
|
||||
wrap (Picos_io_select.on fd `W) (fun _ -> M.EV_OUT);
|
||||
])
|
||||
in
|
||||
ignore (M.action self.mt u_fd ev : int)))
|
||||
|
||||
(** Process handles that are finished *)
|
||||
let process_finished_ (self : bg_state) =
|
||||
let continue = ref true in
|
||||
while !continue do
|
||||
match M.remove_finished self.mt with
|
||||
| None -> continue := false
|
||||
| Some (h, code) ->
|
||||
Printf.eprintf "HANDLE DONE\n%!";
|
||||
(match Hashtbl.find_opt self.comps h with
|
||||
| None -> Printf.eprintf "curl_picos: orphan handle, how come?\n%!"
|
||||
| Some comp ->
|
||||
(* resolve computation *)
|
||||
Hashtbl.remove self.comps h;
|
||||
C.return comp code)
|
||||
done
|
||||
|
||||
let create () : st =
|
||||
let bt = Printexc.get_callstack 10 in
|
||||
let self = { lock = Mutex.create (); tasks = Stream.create (); bg = None } in
|
||||
|
||||
(* background fiber that performs tasks submitted by curl *)
|
||||
let bg_ready = Picos_std_sync.Ivar.create () in
|
||||
(let comp, fiber =
|
||||
spawn_ @@ fun _ ->
|
||||
let mt = M.create () in
|
||||
let bg_state =
|
||||
{ bt; mt; fds = Hashtbl.create 16; comps = Hashtbl.create 32 }
|
||||
in
|
||||
|
||||
M.set_timer_function mt (fun ms ->
|
||||
Stream.push self.tasks (T_timeout { ms }));
|
||||
|
||||
M.set_socket_function mt (fun fd what ->
|
||||
Stream.push self.tasks (T_socket (fd, what)));
|
||||
|
||||
let cursor = ref (Stream.tap self.tasks) in
|
||||
Picos_std_sync.Ivar.fill bg_ready ();
|
||||
try
|
||||
while true do
|
||||
let task, new_cursor = Stream.read !cursor in
|
||||
cursor := new_cursor;
|
||||
process_task_ bg_state task;
|
||||
process_finished_ bg_state
|
||||
done
|
||||
with
|
||||
| Exit -> ()
|
||||
| exn ->
|
||||
let bt = Printexc.get_raw_backtrace () in
|
||||
Printf.eprintf "background fiber failed: %s\n%s" (Printexc.to_string exn)
|
||||
(Printexc.raw_backtrace_to_string bt)
|
||||
in
|
||||
self.bg <- Some (comp, fiber, bg_ready));
|
||||
self
|
||||
|
||||
(** Global state for the multi handle *)
|
||||
let global = Picos_std_sync.Lazy.from_fun create
|
||||
|
||||
let perform h =
|
||||
Printf.eprintf "PERFORM\n%!";
|
||||
let (self : st) = Picos_std_sync.Lazy.force global in
|
||||
(match self.bg with
|
||||
| None -> assert false
|
||||
| Some (_, _, ready) -> Picos_std_sync.Ivar.read ready);
|
||||
Printf.eprintf "PERFORM_\n%!";
|
||||
perform_ self h
|
||||
|
||||
include Ezcurl_core.Make (struct
|
||||
type 'a t = 'a
|
||||
|
||||
let return x = x
|
||||
let ( >>= ) = ( |> )
|
||||
let ( >|= ) = ( |> )
|
||||
let fail = raise
|
||||
let perform = perform
|
||||
end)
|
||||
1
src/picos/ezcurl_picos.mli
Normal file
1
src/picos/ezcurl_picos.mli
Normal file
|
|
@ -0,0 +1 @@
|
|||
include Ezcurl_core.S with type 'a io := 'a
|
||||
11
test/picos/basic_test.ml
Normal file
11
test/picos/basic_test.ml
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
let () =
|
||||
Picos_mux_fifo.run @@ fun () ->
|
||||
match
|
||||
Ezcurl_picos.get
|
||||
~url:
|
||||
"https://archive.softwareheritage.org/api/1/content/sha1_git:7bdf38d4468c114206c9b6ebd9cf1176e085d346/"
|
||||
()
|
||||
with
|
||||
| Error (code, msg) ->
|
||||
Format.eprintf "curl error: code `%s` (%s)@." (Curl.strerror code) msg
|
||||
| Ok _response -> Format.printf "OK@."
|
||||
3
test/picos/dune
Normal file
3
test/picos/dune
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
(test
|
||||
(name basic_test)
|
||||
(libraries ezcurl_picos picos_mux.fifo))
|
||||
Loading…
Add table
Reference in a new issue