defmodule Unleash.Propagation.GRPC.ContextServerInterceptor do @moduledoc """ A GRPC Server Interceptor to extract unleash context and overrides from incoming GRPC request headers and store them for feature flag checks and further propagation downstream (e.g. via `Unleash.Propagation.GRPC.ContextClientInterceptor`). See `Unleash.Propagation` for more details on the propagation mechanism. Note that, contrarily to the ContextClientInterceptor, this interceptor does not raise in case of invalid context or overrides in the gRPC request headers. The difference is due to the nature of the data: in here, it's dynamic input data, beyond our control. Invalid data is expected and we should handle it gracefully. """ @behaviour GRPC.ServerInterceptor alias Unleash.Propagation alias Unleash.Propagation.Serialization require Logger @context_header_name "x-unleash-context" @overrides_header_name "x-unleash-overrides" @impl true def init(_), do: nil @impl true def call(req, stream, next, _opts) do headers = GRPC.Stream.get_headers(stream) parse_and_store_context(Map.get(headers, @context_header_name)) parse_and_store_overrides(Map.get(headers, @overrides_header_name)) next.(req, stream) end @spec parse_and_store_context(binary() | nil) :: :ok defp parse_and_store_context(header_value) defp parse_and_store_context(nil), do: :ok defp parse_and_store_context(header_value) when is_binary(header_value) do header_value |> Serialization.deserialize_context() |> case do {:ok, context} -> Propagation.put_context(context) {:error, err} -> Logger.error( "Unleash gRPC server interceptor: failed to decode" <> " incoming context: #{Exception.format(:error, err)}" ) end end @spec parse_and_store_overrides(binary() | nil) :: :ok defp parse_and_store_overrides(header_value) defp parse_and_store_overrides(nil), do: :ok defp parse_and_store_overrides(header_value) when is_binary(header_value) do header_value |> Serialization.deserialize_overrides() |> case do {:ok, overrides} -> Propagation.put_overrides(overrides) {:error, err} -> Logger.error( "Unleash gRPC server interceptor: failed to decode" <> " incoming overrides: #{Exception.format(:error, err)}" ) end end end