|
1 | 1 | type response_handler = H2.Client_connection.response_handler |
| 2 | +type error_handler = H2.Client_connection.error_handler |
2 | 3 |
|
3 | 4 | type do_request = |
4 | 5 | ?flush_headers_immediately:bool -> |
5 | 6 | ?trailers_handler:(H2.Headers.t -> unit) -> |
6 | 7 | H2.Request.t -> |
| 8 | + error_handler:error_handler -> |
7 | 9 | response_handler:response_handler -> |
8 | 10 | H2.Body.Writer.t |
9 | 11 |
|
@@ -34,22 +36,61 @@ let make_trailers_handler () = |
34 | 36 | let get_response_and_bodies request = |
35 | 37 | let response, response_notify = Eio.Promise.create () in |
36 | 38 | let read_body, read_body_notify = Eio.Promise.create () in |
| 39 | + let error_promise, error_notify = Eio.Promise.create () in |
| 40 | + |
37 | 41 | let response_handler response body = |
38 | 42 | Eio.Promise.resolve response_notify response; |
39 | 43 | Eio.Promise.resolve read_body_notify body |
40 | 44 | in |
41 | | - let write_body = request ~response_handler in |
42 | | - let response = Eio.Promise.await response in |
43 | | - let read_body = Eio.Promise.await read_body in |
44 | | - (response, read_body, write_body) |
| 45 | + |
| 46 | + let error_handler err = |
| 47 | + (* When H2 error occurs, resolve the error promise *) |
| 48 | + if not (Eio.Promise.is_resolved error_promise) then |
| 49 | + Eio.Promise.resolve error_notify (Some err) |
| 50 | + in |
| 51 | + |
| 52 | + let write_body = request ~error_handler ~response_handler in |
| 53 | + |
| 54 | + (* Race between getting response and error *) |
| 55 | + match Eio.Fiber.first |
| 56 | + (fun () -> |
| 57 | + let resp = Eio.Promise.await response in |
| 58 | + let body = Eio.Promise.await read_body in |
| 59 | + `Response (resp, body)) |
| 60 | + (fun () -> |
| 61 | + let err = Eio.Promise.await error_promise in |
| 62 | + match err with |
| 63 | + | Some e -> `Error e |
| 64 | + | None -> |
| 65 | + (* This shouldn't happen *) |
| 66 | + failwith "Internal error: error promise resolved without value") |
| 67 | + with |
| 68 | + | `Response (response, read_body) -> (response, read_body, write_body) |
| 69 | + | `Error err -> |
| 70 | + (* Convert H2 error to exception *) |
| 71 | + let msg = match err with |
| 72 | + | `Protocol_error (code, msg) -> |
| 73 | + Printf.sprintf "H2 protocol error (%s): %s" |
| 74 | + (H2.Error_code.to_string code) msg |
| 75 | + | `Invalid_response_body_length resp -> |
| 76 | + Printf.sprintf "Invalid response body length for status %s" |
| 77 | + (H2.Status.to_string resp.H2.Response.status) |
| 78 | + | `Malformed_response msg -> |
| 79 | + Printf.sprintf "Malformed response: %s" msg |
| 80 | + | `Exn exn -> |
| 81 | + Printf.sprintf "H2 exception: %s" (Printexc.to_string exn) |
| 82 | + in |
| 83 | + failwith msg |
45 | 84 |
|
46 | 85 | let call ~service ~rpc ?(scheme = "https") ~handler ~(do_request : do_request) |
47 | 86 | ?(headers = default_headers) () = |
48 | 87 | let request = make_request ~service ~rpc ~scheme ~headers in |
49 | 88 | let status, trailers_handler = make_trailers_handler () in |
50 | 89 | let response, read_body, write_body = |
51 | 90 | get_response_and_bodies |
52 | | - (do_request ~flush_headers_immediately:true request ~trailers_handler) |
| 91 | + (fun ~error_handler ~response_handler -> |
| 92 | + do_request ~flush_headers_immediately:true request ~trailers_handler |
| 93 | + ~error_handler ~response_handler) |
53 | 94 | in |
54 | 95 | match response.status with |
55 | 96 | | `OK -> |
|
0 commit comments