1111 [io.pedestal.http.body-params :as body-params]
1212 [example.types :as example]
1313 [protojure.protobuf :as pb]
14- [clojure.data.codec.base64 :as b64]))
14+ [clojure.data.codec.base64 :as b64]
15+ [protojure.pedestal.interceptors.grpc :as grpc]
16+ [clojure.core.async :as async]
17+ [example.hello.Greeter :as greeter]
18+ [clojure.core.async :refer [<!! >!! <! >! go go-loop] :as async]
19+ [protojure.test.utils :as test.utils]
20+ [protojure.grpc.client.api :as grpc-api]
21+ [protojure.grpc.client.providers.http2 :as grpc.http2]
22+ [protojure.grpc.client.utils :as client.utils]
23+ [promesa.core :as p]
24+ [protojure.internal.grpc.client.providers.http2.jetty :as jetty-client]
25+ [protojure.grpc.status :as grpc.status]
26+ [protojure.pedestal.routes :as pedestal.routes]
27+ [example.hello :refer [new-HelloRequest pb->HelloReply]]
28+ [clj-http.client :as client]
29+ [protojure.grpc.codec.lpm :as lpm]))
1530
16- (defn grpc-echo [{:keys [body] :as request}]
17- {:status 200
18- :body (example/pb->Money body)
19- :trailers {" grpc-status" 0 " grpc-message" " Got it!" }})
31+ (defonce test-env (atom {}))
2032
21- (def interceptors [(body-params/body-params )
22- grpc-web/proxy])
33+ ; ;-----------------------------------------------------------------------------
34+ ; ; "Greeter" service endpoint
35+ ; ;-----------------------------------------------------------------------------
36+ (deftype Greeter []
37+ greeter /Service
38+ (SayHello
39+ [this {{:keys [name]} :grpc-params :as request}]
40+ {:status 200
41+ :body {:message (str " Hello, " name)}})
42+ (SayRepeatHello
43+ [this {{:keys [name]} :grpc-params :as request}]
44+ (let [resp-chan (:grpc-out request)]
45+ (go
46+ (dotimes [_ 3 ]
47+ (>! resp-chan {:message (str " Hello, " name)}))
48+ (async/close! resp-chan))
49+ {:status 200
50+ :body resp-chan}))
51+ (SayHelloOnDemand
52+ [this {:keys [grpc-params] :as request}]
53+ (let [out-chan (:grpc-out request)]
54+ (go-loop [name (:name (<! grpc-params))]
55+ (if name
56+ (do
57+ (>! out-chan {:message (str " Hello, " name)})
58+ (recur (:name (<! grpc-params))))
59+ (async/close! out-chan)))
60+ {:status 200
61+ :body out-chan}))
62+ (SayHelloError
63+ [this req]
64+ {:status 200
65+ :body " This isn't a protobuf message" })
66+ (SayNil
67+ [this req]
68+ (grpc.status/error :unauthenticated )))
2369
24- (def routes [[" /" :get (conj interceptors `grpc-echo)]])
70+ (defn- greeter-mock-routes [interceptors]
71+ (pedestal.routes/->tablesyntax {:rpc-metadata greeter/rpc-metadata
72+ :interceptors interceptors
73+ :callback-context (Greeter. )}))
2574
26- (def service (let [service-params {:env :prod
27- ::pedestal/routes (into #{} routes)
28- ::pedestal/type protojure.pedestal/config
29- ::pedestal/chain-provider protojure.pedestal/provider}]
30- (:io.pedestal.http/service-fn (io.pedestal.http/create-servlet service-params))))
75+ (defn- grpc-connect
76+ ([] (grpc-connect (:port @test-env)))
77+ ([port]
78+ @(grpc.http2/connect {:uri (str " http://localhost:" port) :content-coding " gzip" })))
3179
32- (deftest grpc-web-text-check
33- (testing " Check that a round-trip GRPC request works"
34- (let [input-msg (pb/->pb (example/new-Money {:currency_code (apply str (repeat 20 " foobar" )) :units 42 :nanos 750000000 }))]
35- (is
36- (=
37- (with-out-str (pr (example/pb->Money input-msg)))
38- (:body (response-for
39- service
40- :get " /"
41- :headers {" Content-Type" " application/grpc-web-text" }
42- :body (clojure.java.io/input-stream (b64/encode input-msg)))))))))
80+ ; ;-----------------------------------------------------------------------------
81+ ; ; Fixtures
82+ ; ;-----------------------------------------------------------------------------
83+ (defn create-service []
84+ (let [port (test.utils/get-free-port )
85+ interceptors [(body-params/body-params )
86+ pedestal/html-body]
87+ server-params {:env :prod
88+ ::pedestal/routes (into #{} (greeter-mock-routes interceptors))
89+ ::pedestal/port port
4390
44- (deftest grpc-web-check
45- (testing " Check that a round-trip GRPC request works"
46- (let [input-msg (pb/->pb (example/new-Money {:currency_code (apply str (repeat 20 " foobar" )) :units 42 :nanos 750000000 }))]
47- (is
48- (=
49- (with-out-str (pr (example/pb->Money input-msg)))
50- (:body (response-for
51- service
52- :get " /"
53- :headers {" Content-Type" " application/grpc-web" }
54- :body (clojure.java.io/input-stream input-msg))))))))
91+ ::pedestal/type protojure.pedestal/config
92+ ::pedestal/chain-provider protojure.pedestal/provider}
93+ client-params {:port port}]
5594
56- (deftest grpc-web-proto-check
57- (testing " Check that a round-trip GRPC request works"
58- (let [input-msg (pb/->pb (example/new-Money {:currency_code (apply str (repeat 20 " foobar" )) :units 42 :nanos 750000000 }))]
59- (is
60- (=
61- (with-out-str (pr (example/pb->Money input-msg)))
62- (:body (response-for
63- service
64- :get " /"
65- :headers {" Content-Type" " application/grpc-web+proto" }
66- :body (clojure.java.io/input-stream input-msg))))))))
95+ (let [server (test.utils/start-pedestal-server server-params)
96+ client @(jetty-client/connect client-params)
97+ grpc-client (grpc-connect port)]
98+ (swap! test-env assoc :port port :server server :client client :grpc-client grpc-client))))
6799
68- (deftest grpc-web-text-proto-check
69- (testing " Check that a round-trip GRPC request works"
70- (let [input-msg (pb/->pb (example/new-Money {:currency_code (apply str (repeat 20 " foobar" )) :units 42 :nanos 750000000 }))]
71- (is
72- (=
73- (with-out-str (pr (example/pb->Money input-msg)))
74- (:body (response-for
75- service
76- :get " /"
77- :headers {" Content-Type" " application/grpc-web-text+proto" }
78- :body (clojure.java.io/input-stream (b64/encode input-msg)))))))))
100+ (defn destroy-service []
101+ (swap! test-env update :grpc-client grpc-api/disconnect)
102+ (swap! test-env update :client jetty-client/disconnect)
103+ (swap! test-env update :server pedestal/stop))
79104
80- (deftest grpc-web-no-header-match-check
81- (testing " Check that a round-trip GRPC request works"
82- (let [input-msg (pb/->pb (example/new-Money {:currency_code (apply str (repeat 20 " foobar" )) :units 42 :nanos 750000000 }))]
83- (is
84- (=
85- (with-out-str (pr (example/pb->Money input-msg)))
86- (:body (response-for
87- service
88- :get " /"
89- :headers {" Content-Type" " application/grpc" }
90- :body (clojure.java.io/input-stream input-msg))))))))
105+ (defn wrap-service [test-fn]
106+ (create-service )
107+ (test-fn )
108+ (destroy-service ))
109+
110+ (use-fixtures :once wrap-service)
111+
112+ (deftest grpc-web-test-check
113+ (let [in (async/chan 10 )
114+ out (async/chan 10 )
115+ resp-in (async/chan 10 )
116+ resp-out (async/chan 10 )]
117+ (lpm/encode new-HelloRequest in out {:encoding identity})
118+ (lpm/decode pb->HelloReply resp-in resp-out {:encoding identity})
119+ (async/>!! in {:name " World" })
120+ (async/close! in)
121+ (testing " Check that a round-trip unary grpc-web-text request works"
122+ (let [lpm (async/<!! (async/into [] out))
123+ b64-encoded (-> (java.util.Base64/getEncoder )
124+ (.encode (byte-array lpm)))
125+ body (-> (java.util.Base64/getDecoder )
126+ (.decode (-> (client/post
127+ (str " http://localhost:" (:port @test-env) " /example.hello.Greeter/SayHello" )
128+ {:body b64-encoded
129+ :content-type " application/grpc-web-text"
130+ :accept " application/grpc-web-text" })
131+ :body )))]
132+ (doseq [b body]
133+ (async/>!! resp-in b))
134+ (async/close! resp-in)
135+ (is (= " Hello, World" (:message (async/<!! resp-out))))))))
0 commit comments