Liking cljdoc? Tell your friends :D

clj-grpc.interceptor

Interceptors as functions of the call, on both sides of the wire.

;; server: reject, enrich, or pass through
(fn [call next]
  (if-let [user (verify (metadata/header (:headers call) "authorization"))]
    (next (assoc call :user user))
    (reject :unauthenticated "missing or bad token"
            {"www-authenticate" "Bearer"})))

;; client: declare headers, watch the response
(fn [call next]
  (next (-> call
            (update :headers assoc "authorization" (str "Bearer " token))
            (assoc :on-trailers (fn [status trailers] ...)))))

Either goes in an :interceptors vector — server's or channel's, or per call in invoke's opts — mixed freely with raw io.grpc interceptors. The vector runs in order on every path: [a b c] is a outermost, first in and last out, whether a is a fn or a ServerInterceptor. (grpc's builders run the last-registered interceptor outermost; registration reverses the vector so the declared order is the running order.)

The server call map:

:method kebab keyword, :say-hello — the handlers-map key :service "acme.greeter.Greeter" :full-method-name "acme.greeter.Greeter/SayHello" :headers the request io.grpc.Metadata, raw and mutable :authority what the client sent as :authority, or nil :peer the client's SocketAddress, or nil :deadline io.grpc.Deadline, or nil :method-descriptor :attributes :server-call — the grpc objects

An interceptor returns (next call'), possibly with the map enriched — what it adds is what inner interceptors and, through clj-grpc.context, the handler see — or a rejection. Two keys given to next reach the wire instead of the map: :response-headers and :response-trailers, each a map or a Metadata, merged into what the handler's response carries. Health and reflection calls pass through the chain too; :service tells them apart.

Rejection is a value, not an exception: (reject code description trailers) closes the call with that status before any handler runs, and the normal auth-failure path pays for no stack trace. Throwing works as it does in handlers — a StatusRuntimeException keeps its status and trailers, anything else becomes INTERNAL with the message.

The client call map:

:method :service :full-method-name :method-descriptor as above :call-options the io.grpc.CallOptions, replaceable :headers a MAP of the outgoing headers declared so far :authority the channel's :deadline from the call options, or nil

Headers are declared in the map and written when the call starts — grpc creates the outgoing Metadata after interceptors run — so an inner interceptor sees and may override what an outer one declared. The map given to next may add :on-headers (fn [metadata]) and :on-trailers (fn [status metadata]) to observe the response. Headers a raw interceptor adds at start are invisible here.

Deliberately not here: hooks per message (that is the handler's shape), registration per method (io.grpc.ServerInterceptors/intercept on a service definition is one call away), and trailers set from inside a handler — the error path already carries them: throw (.asRuntimeException (status :not-found "...") trailers).

Interceptors as functions of the call, on both sides of the wire.

    ;; server: reject, enrich, or pass through
    (fn [call next]
      (if-let [user (verify (metadata/header (:headers call) "authorization"))]
        (next (assoc call :user user))
        (reject :unauthenticated "missing or bad token"
                {"www-authenticate" "Bearer"})))

    ;; client: declare headers, watch the response
    (fn [call next]
      (next (-> call
                (update :headers assoc "authorization" (str "Bearer " token))
                (assoc :on-trailers (fn [status trailers] ...)))))

Either goes in an :interceptors vector — `server`'s or `channel`'s, or per
call in `invoke`'s opts — mixed freely with raw io.grpc interceptors. The
vector runs in order on every path: [a b c] is a outermost, first in and
last out, whether a is a fn or a ServerInterceptor. (grpc's builders run
the last-registered interceptor outermost; registration reverses the
vector so the declared order is the running order.)

The server call map:

  :method             kebab keyword, :say-hello — the handlers-map key
  :service            "acme.greeter.Greeter"
  :full-method-name   "acme.greeter.Greeter/SayHello"
  :headers            the request io.grpc.Metadata, raw and mutable
  :authority          what the client sent as :authority, or nil
  :peer               the client's SocketAddress, or nil
  :deadline           io.grpc.Deadline, or nil
  :method-descriptor  :attributes  :server-call   — the grpc objects

An interceptor returns (next call'), possibly with the map enriched — what
it adds is what inner interceptors and, through clj-grpc.context, the
handler see — or a rejection. Two keys given to `next` reach the wire
instead of the map: :response-headers and :response-trailers, each a map
or a Metadata, merged into what the handler's response carries. Health and
reflection calls pass through the chain too; :service tells them apart.

Rejection is a value, not an exception: (reject code description trailers)
closes the call with that status before any handler runs, and the normal
auth-failure path pays for no stack trace. Throwing works as it does in
handlers — a StatusRuntimeException keeps its status and trailers,
anything else becomes INTERNAL with the message.

The client call map:

  :method :service :full-method-name :method-descriptor   as above
  :call-options       the io.grpc.CallOptions, replaceable
  :headers            a MAP of the outgoing headers declared so far
  :authority          the channel's
  :deadline           from the call options, or nil

Headers are declared in the map and written when the call starts — grpc
creates the outgoing Metadata after interceptors run — so an inner
interceptor sees and may override what an outer one declared. The map
given to `next` may add :on-headers (fn [metadata]) and
:on-trailers (fn [status metadata]) to observe the response. Headers a raw
interceptor adds at start are invisible here.

Deliberately not here: hooks per message (that is the handler's shape),
registration per method (io.grpc.ServerInterceptors/intercept on a
service definition is one call away), and trailers set from inside a
handler — the error path already carries them: throw
(.asRuntimeException (status :not-found "...") trailers).
raw docstring

client-interceptorclj

(client-interceptor f)

An io.grpc.ClientInterceptor from a (fn [call next]) — the identity on a ClientInterceptor.

An io.grpc.ClientInterceptor from a (fn [call next]) — the identity on a
ClientInterceptor.
sourceraw docstring

rejectclj

(reject code)
(reject code description)
(reject code description trailers)

The value a server interceptor returns to close the call before any handler runs: a status (keyword, Status$Code or Status), a description, and trailers as a map or Metadata.

The value a server interceptor returns to close the call before any
handler runs: a status (keyword, Status$Code or Status), a description, and
trailers as a map or Metadata.
sourceraw docstring

server-interceptorclj

(server-interceptor f)

An io.grpc.ServerInterceptor from a (fn [call next]) — the identity on a ServerInterceptor, so a mixed :interceptors vector converts uniformly.

An io.grpc.ServerInterceptor from a (fn [call next]) — the identity on a
ServerInterceptor, so a mixed :interceptors vector converts uniformly.
sourceraw docstring

statusclj

(status code)
(status code description)

An io.grpc.Status from a keyword (:not-found), a Status$Code, or a Status, with an optional description.

An io.grpc.Status from a keyword (:not-found), a Status$Code, or a Status,
with an optional description.
sourceraw docstring

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
Move to previous article
Move to next article
Ctrl+/Jump to the search field
× close