Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

twirp + tiny_httpd #215

Closed
wants to merge 13 commits into from
17 changes: 17 additions & 0 deletions dune-project
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,20 @@
(pbrt_yojson (= :version)))
(tags (protobuf encode decode services rpc)))

(package
(name twirp_ezcurl)
(synopsis "Twirp client implementation using ezcurl")
(depends
(pbrt (= :version))
(pbrt_services (= :version))
yojson
ezcurl))

(package
(name twirp_tiny_httpd)
(synopsis "Twirp server implementation for tiny_httpd")
(depends
(pbrt (= :version))
(pbrt_services (= :version))
yojson
tiny_httpd))
8 changes: 8 additions & 0 deletions examples/twirp_ezcurl/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@

While running the server on port 8084:

```sh
$ PORT=8084 dune exec examples/twirp_ezcurl/calculator_client.exe
query on http://localhost:8084/
add call: returned 131
```
29 changes: 29 additions & 0 deletions examples/twirp_ezcurl/calculator.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
syntax = "proto3";

message DivByZero {}

message I32 {
int32 value = 0;
}

message AddReq {
int32 a = 1;
int32 b = 2;
}

message AddAllReq {
repeated int32 ints = 1;
}

message Empty {}

service Calculator {
rpc add(AddReq) returns (I32);

rpc add_all(AddAllReq) returns (I32);

rpc ping(Empty) returns (Empty);

rpc get_pings(Empty) returns (I32);
}

20 changes: 20 additions & 0 deletions examples/twirp_ezcurl/calculator_client.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
let spf = Printf.sprintf
let aspf = Format.asprintf

let () =
let port = try int_of_string (Sys.getenv "PORT") with _ -> 8080 in
Printf.printf "query on http://localhost:%d/\n%!" port;

let r =
match
Twirp_ezcurl.call ~use_tls:false ~host:"localhost" ~port
Calculator.Calculator.add
@@ Calculator.default_add_req ~a:31l ~b:100l ()
with
| Ok x -> x.value |> Int32.to_int
| Error err ->
failwith (aspf "call to add failed: %a" Twirp_ezcurl.pp_error err)
in

Printf.printf "add call: returned %d\n%!" r;
()
11 changes: 11 additions & 0 deletions examples/twirp_ezcurl/dune
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

(executable
(name calculator_client)
(flags :standard -w -40)
(libraries ezcurl twirp_ezcurl))

(rule
(targets calculator.ml calculator.mli)
(deps calculator.proto)
(action
(run ocaml-protoc --binary --pp --yojson --services --ml_out ./ %{deps})))
15 changes: 15 additions & 0 deletions examples/twirp_tiny_httpd/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
Basic service using Twirp.

Example use:

```sh
$ PORT=8084 dune exec examples/twirp_tiny_httpd/calculator_server.exe
listen on http://localhost:8084/

$ curl -X POST http://localhost:8084/twirp/Calculator/add --json '{"a": 1, "b": 2}'
{"value":3}⏎

$ curl -X POST http://localhost:8084/twirp/Calculator/add_all --json '{"ints": [1,2,3,4]}'
{"value":10}⏎
```

29 changes: 29 additions & 0 deletions examples/twirp_tiny_httpd/calculator.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
syntax = "proto3";

message DivByZero {}

message I32 {
int32 value = 0;
}

message AddReq {
int32 a = 1;
int32 b = 2;
}

message AddAllReq {
repeated int32 ints = 1;
}

message Empty {}

service Calculator {
rpc add(AddReq) returns (I32);

rpc add_all(AddAllReq) returns (I32);

rpc ping(Empty) returns (Empty);

rpc get_pings(Empty) returns (I32);
}

33 changes: 33 additions & 0 deletions examples/twirp_tiny_httpd/calculator_server.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
module H = Tiny_httpd
open Calculator

module Service_impl = struct
let add (a : add_req) : i32 = default_i32 ~value:Int32.(add a.a a.b) ()

let add_all (a : add_all_req) : i32 =
let l = ref 0l in
List.iter (fun x -> l := Int32.add !l x) a.ints;
default_i32 ~value:!l ()

let n_pings = ref 0
let ping () = incr n_pings
let get_pings () : i32 = default_i32 ~value:(Int32.of_int !n_pings) ()
end

let calc_service : Twirp_tiny_httpd.handler Pbrt_services.Server.t =
Calculator.make_server
~add:(fun rpc -> Twirp_tiny_httpd.mk_handler rpc Service_impl.add)
~add_all:(fun rpc -> Twirp_tiny_httpd.mk_handler rpc Service_impl.add_all)
~ping:(fun rpc -> Twirp_tiny_httpd.mk_handler rpc Service_impl.ping)
~get_pings:(fun rpc ->
Twirp_tiny_httpd.mk_handler rpc Service_impl.get_pings)
()

let () =
let port = try int_of_string (Sys.getenv "PORT") with _ -> 8080 in
Printf.printf "listen on http://localhost:%d/\n%!" port;

let server = H.create ~port () in
Twirp_tiny_httpd.add_service ~prefix:(Some "twirp") server calc_service;

H.run_exn server
11 changes: 11 additions & 0 deletions examples/twirp_tiny_httpd/dune
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

(executable
(name calculator_server)
(flags :standard -w -44)
(libraries pbrt_services twirp_tiny_httpd tiny_httpd))

(rule
(targets calculator.ml calculator.mli)
(deps calculator.proto)
(action
(run ocaml-protoc --binary --pp --yojson --services --ml_out ./ %{deps})))
4 changes: 0 additions & 4 deletions src/runtime-services/pbrt_services.ml
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,6 @@ module Server = struct
(** A single RPC method, alongside encoders and decoders for
input and output types. . *)

(** A RPC endpoint. *)
type any_rpc = RPC : ('req, 'req_mode, 'res, 'res_mode) rpc -> any_rpc
[@@unboxed]

(** Helper to build a RPC *)
let mk_rpc :
name:string ->
Expand Down
14 changes: 14 additions & 0 deletions src/twirp_ezcurl/dune
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@

(library
(name twirp_ezcurl)
(public_name twirp_ezcurl)
(synopsis "Twirp client")
(wrapped true)
(libraries pbrt pbrt_yojson yojson pbrt_services ezcurl))

(rule
(targets twirp_error.ml twirp_error.mli)
(deps (:file twirp_error.proto))
(mode promote)
(action (run ../ocaml-protoc/ocaml_protoc.exe --pp --yojson --ml_out=. %{file})))

68 changes: 68 additions & 0 deletions src/twirp_ezcurl/twirp_error.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
[@@@ocaml.warning "-27-30-39"]

type error = {
code : string;
msg : string;
}

let rec default_error
?code:((code:string) = "")
?msg:((msg:string) = "")
() : error = {
code;
msg;
}

type error_mutable = {
mutable code : string;
mutable msg : string;
}

let default_error_mutable () : error_mutable = {
code = "";
msg = "";
}

[@@@ocaml.warning "-27-30-39"]

(** {2 Formatters} *)

let rec pp_error fmt (v:error) =
let pp_i fmt () =
Pbrt.Pp.pp_record_field ~first:true "code" Pbrt.Pp.pp_string fmt v.code;
Pbrt.Pp.pp_record_field ~first:false "msg" Pbrt.Pp.pp_string fmt v.msg;
in
Pbrt.Pp.pp_brk pp_i fmt ()

[@@@ocaml.warning "-27-30-39"]

(** {2 Protobuf YoJson Encoding} *)

let rec encode_json_error (v:error) =
let assoc = [] in
let assoc = ("code", Pbrt_yojson.make_string v.code) :: assoc in
let assoc = ("msg", Pbrt_yojson.make_string v.msg) :: assoc in
`Assoc assoc

[@@@ocaml.warning "-27-30-39"]

(** {2 JSON Decoding} *)

let rec decode_json_error d =
let v = default_error_mutable () in
let assoc = match d with
| `Assoc assoc -> assoc
| _ -> assert(false)
in
List.iter (function
| ("code", json_value) ->
v.code <- Pbrt_yojson.string json_value "error" "code"
| ("msg", json_value) ->
v.msg <- Pbrt_yojson.string json_value "error" "msg"

| (_, _) -> () (*Unknown fields are ignored*)
) assoc;
({
code = v.code;
msg = v.msg;
} : error)
44 changes: 44 additions & 0 deletions src/twirp_ezcurl/twirp_error.mli
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@

(** Code for twirp_error.proto *)

(* generated from "twirp_error.proto", do not edit *)



(** {2 Types} *)

type error = {
code : string;
msg : string;
}


(** {2 Basic values} *)

val default_error :
?code:string ->
?msg:string ->
unit ->
error
(** [default_error ()] is the default value for type [error] *)


(** {2 Formatters} *)

val pp_error : Format.formatter -> error -> unit
(** [pp_error v] formats v *)


(** {2 Protobuf YoJson Encoding} *)

val encode_json_error : error -> Yojson.Basic.t
(** [encode_json_error v encoder] encodes [v] to to json *)


(** {2 JSON Decoding} *)

val decode_json_error : Yojson.Basic.t -> error
(** [decode_json_error decoder] decodes a [error] value from [decoder] *)


(** {2 Services} *)
11 changes: 11 additions & 0 deletions src/twirp_ezcurl/twirp_error.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
syntax = "proto3";

message error {
// Error code
string code = 1;

// Human readable message
string msg = 2;
}

// TODO: meta?
Loading