Skip to content

Commit

Permalink
Merge pull request #270 from well-typed/edsko/status-details
Browse files Browse the repository at this point in the history
Support `"grpc-status-details-bin"` header
  • Loading branch information
edsko authored Jan 21, 2025
2 parents 17ae234 + 3bf3a85 commit 8b0ba47
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 80 deletions.
1 change: 1 addition & 0 deletions grpc-spec/grpc-spec.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ library
Network.GRPC.Spec.TraceContext
Network.GRPC.Spec.Util.ByteString
Network.GRPC.Spec.Util.Protobuf

Proto.OrcaLoadReport
build-depends:
, aeson >= 1.5 && < 2.3
Expand Down
111 changes: 50 additions & 61 deletions grpc-spec/proto/Proto/OrcaLoadReport.hs
Original file line number Diff line number Diff line change
Expand Up @@ -1056,127 +1056,116 @@ packedFileDescriptor
\\ENQvalue\CAN\STX \SOH(\SOHR\ENQvalue:\STX8\SOH\SUB?\n\
\\DC1NamedMetricsEntry\DC2\DLE\n\
\\ETXkey\CAN\SOH \SOH(\tR\ETXkey\DC2\DC4\n\
\\ENQvalue\CAN\STX \SOH(\SOHR\ENQvalue:\STX8\SOHJ\239\DC4\n\
\\ACK\DC2\EOT\DLE\NUL>\SOH\n\
\\128\ENQ\n\
\\SOH\f\DC2\ETX\DLE\NUL\DC22\183\EOT Copyright 2020 The gRPC Authors\n\
\\n\
\ Licensed under the Apache License, Version 2.0 (the \"License\");\n\
\ you may not use this file except in compliance with the License.\n\
\ You may obtain a copy of the License at\n\
\\n\
\ http://www.apache.org/licenses/LICENSE-2.0\n\
\\n\
\ Unless required by applicable law or agreed to in writing, software\n\
\ distributed under the License is distributed on an \"AS IS\" BASIS,\n\
\ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\
\ See the License for the specific language governing permissions and\n\
\ limitations under the License.\n\
\2< Local copy of Envoy xDS proto file, used for testing only.\n\
\\n\
\\ENQvalue\CAN\STX \SOH(\SOHR\ENQvalue:\STX8\SOHJ\156\DC1\n\
\\ACK\DC2\EOT\NUL\NUL1\SOH\n\
\\b\n\
\\SOH\f\DC2\ETX\NUL\NUL\DC2\n\
\\b\n\
\\SOH\STX\DC2\ETX\DC2\NUL\EM\n\
\\SOH\STX\DC2\ETX\STX\NUL\EM\n\
\\134\SOH\n\
\\STX\EOT\NUL\DC2\EOT\ETB\NUL>\SOH2z See section `ORCA load report format` of the design document in\n\
\\STX\EOT\NUL\DC2\EOT\a\NUL1\SOH2z See section `ORCA load report format` of the design document in\n\
\ :ref:`https://github.com/envoyproxy/envoy/issues/6614`.\n\
\\n\
\\n\
\\n\
\\ETX\EOT\NUL\SOH\DC2\ETX\ETB\b\SYN\n\
\\ETX\EOT\NUL\SOH\DC2\ETX\a\b\SYN\n\
\\250\SOH\n\
\\EOT\EOT\NUL\STX\NUL\DC2\ETX\FS\STX\GS\SUB\236\SOH CPU utilization expressed as a fraction of available CPU resources. This\n\
\\EOT\EOT\NUL\STX\NUL\DC2\ETX\f\STX\GS\SUB\236\SOH CPU utilization expressed as a fraction of available CPU resources. This\n\
\ should be derived from the latest sample or measurement. The value may be\n\
\ larger than 1.0 when the usage exceeds the reporter dependent notion of\n\
\ soft limits.\n\
\\n\
\\f\n\
\\ENQ\EOT\NUL\STX\NUL\ENQ\DC2\ETX\FS\STX\b\n\
\\ENQ\EOT\NUL\STX\NUL\ENQ\DC2\ETX\f\STX\b\n\
\\f\n\
\\ENQ\EOT\NUL\STX\NUL\SOH\DC2\ETX\FS\t\CAN\n\
\\ENQ\EOT\NUL\STX\NUL\SOH\DC2\ETX\f\t\CAN\n\
\\f\n\
\\ENQ\EOT\NUL\STX\NUL\ETX\DC2\ETX\FS\ESC\FS\n\
\\ENQ\EOT\NUL\STX\NUL\ETX\DC2\ETX\f\ESC\FS\n\
\\152\SOH\n\
\\EOT\EOT\NUL\STX\SOH\DC2\ETX \STX\GS\SUB\138\SOH Memory utilization expressed as a fraction of available memory\n\
\\EOT\EOT\NUL\STX\SOH\DC2\ETX\DLE\STX\GS\SUB\138\SOH Memory utilization expressed as a fraction of available memory\n\
\ resources. This should be derived from the latest sample or measurement.\n\
\\n\
\\f\n\
\\ENQ\EOT\NUL\STX\SOH\ENQ\DC2\ETX \STX\b\n\
\\ENQ\EOT\NUL\STX\SOH\ENQ\DC2\ETX\DLE\STX\b\n\
\\f\n\
\\ENQ\EOT\NUL\STX\SOH\SOH\DC2\ETX \t\CAN\n\
\\ENQ\EOT\NUL\STX\SOH\SOH\DC2\ETX\DLE\t\CAN\n\
\\f\n\
\\ENQ\EOT\NUL\STX\SOH\ETX\DC2\ETX \ESC\FS\n\
\z\n\
\\EOT\EOT\NUL\STX\STX\DC2\ETX$\STX%\SUBm Total RPS being served by an endpoint. This should cover all services that an endpoint is\n\
\\ENQ\EOT\NUL\STX\SOH\ETX\DC2\ETX\DLE\ESC\FS\n\
\\176\SOH\n\
\\EOT\EOT\NUL\STX\STX\DC2\ETX\NAK\STX%\SUB\162\SOH Total RPS being served by an endpoint. This should cover all services that an endpoint is\n\
\ responsible for.\n\
\ Deprecated -- use ``rps_fractional`` field instead.\n\
\\n\
\\f\n\
\\ENQ\EOT\NUL\STX\STX\ENQ\DC2\ETX$\STX\b\n\
\\ENQ\EOT\NUL\STX\STX\ENQ\DC2\ETX\NAK\STX\b\n\
\\f\n\
\\ENQ\EOT\NUL\STX\STX\SOH\DC2\ETX$\t\f\n\
\\ENQ\EOT\NUL\STX\STX\SOH\DC2\ETX\NAK\t\f\n\
\\f\n\
\\ENQ\EOT\NUL\STX\STX\ETX\DC2\ETX$\SI\DLE\n\
\\ENQ\EOT\NUL\STX\STX\ETX\DC2\ETX\NAK\SI\DLE\n\
\\f\n\
\\ENQ\EOT\NUL\STX\STX\b\DC2\ETX$\DC1$\n\
\\ENQ\EOT\NUL\STX\STX\b\DC2\ETX\NAK\DC1$\n\
\\r\n\
\\ACK\EOT\NUL\STX\STX\b\ETX\DC2\ETX$\DC2#\n\
\\ACK\EOT\NUL\STX\STX\b\ETX\DC2\ETX\NAK\DC2#\n\
\\142\SOH\n\
\\EOT\EOT\NUL\STX\ETX\DC2\ETX(\STX'\SUB\128\SOH Application specific requests costs. Each value is an absolute cost (e.g. 3487 bytes of\n\
\\EOT\EOT\NUL\STX\ETX\DC2\ETX\EM\STX'\SUB\128\SOH Application specific requests costs. Each value is an absolute cost (e.g. 3487 bytes of\n\
\ storage) associated with the request.\n\
\\n\
\\f\n\
\\ENQ\EOT\NUL\STX\ETX\ACK\DC2\ETX(\STX\NAK\n\
\\ENQ\EOT\NUL\STX\ETX\ACK\DC2\ETX\EM\STX\NAK\n\
\\f\n\
\\ENQ\EOT\NUL\STX\ETX\SOH\DC2\ETX(\SYN\"\n\
\\ENQ\EOT\NUL\STX\ETX\SOH\DC2\ETX\EM\SYN\"\n\
\\f\n\
\\ENQ\EOT\NUL\STX\ETX\ETX\DC2\ETX(%&\n\
\\ENQ\EOT\NUL\STX\ETX\ETX\DC2\ETX\EM%&\n\
\\160\SOH\n\
\\EOT\EOT\NUL\STX\EOT\DC2\ETX,\STX&\SUB\146\SOH Resource utilization values. Each value is expressed as a fraction of total resources\n\
\\EOT\EOT\NUL\STX\EOT\DC2\ETX\GS\STX&\SUB\146\SOH Resource utilization values. Each value is expressed as a fraction of total resources\n\
\ available, derived from the latest sample or measurement.\n\
\\n\
\\f\n\
\\ENQ\EOT\NUL\STX\EOT\ACK\DC2\ETX,\STX\NAK\n\
\\ENQ\EOT\NUL\STX\EOT\ACK\DC2\ETX\GS\STX\NAK\n\
\\f\n\
\\ENQ\EOT\NUL\STX\EOT\SOH\DC2\ETX,\SYN!\n\
\\ENQ\EOT\NUL\STX\EOT\SOH\DC2\ETX\GS\SYN!\n\
\\f\n\
\\ENQ\EOT\NUL\STX\EOT\ETX\DC2\ETX,$%\n\
\\v\n\
\\EOT\EOT\NUL\STX\ENQ\DC2\ETX.\STX\FS\n\
\\ENQ\EOT\NUL\STX\EOT\ETX\DC2\ETX\GS$%\n\
\z\n\
\\EOT\EOT\NUL\STX\ENQ\DC2\ETX!\STX\FS\SUBm Total RPS being served by an endpoint. This should cover all services that an endpoint is\n\
\ responsible for.\n\
\\n\
\\f\n\
\\ENQ\EOT\NUL\STX\ENQ\ENQ\DC2\ETX.\STX\b\n\
\\ENQ\EOT\NUL\STX\ENQ\ENQ\DC2\ETX!\STX\b\n\
\\f\n\
\\ENQ\EOT\NUL\STX\ENQ\SOH\DC2\ETX.\t\ETB\n\
\\ENQ\EOT\NUL\STX\ENQ\SOH\DC2\ETX!\t\ETB\n\
\\f\n\
\\ENQ\EOT\NUL\STX\ENQ\ETX\DC2\ETX.\SUB\ESC\n\
\\ENQ\EOT\NUL\STX\ENQ\ETX\DC2\ETX!\SUB\ESC\n\
\\138\SOH\n\
\\EOT\EOT\NUL\STX\ACK\DC2\ETX2\STX\DC1\SUB} Total EPS (errors/second) being served by an endpoint. This should cover\n\
\\EOT\EOT\NUL\STX\ACK\DC2\ETX%\STX\DC1\SUB} Total EPS (errors/second) being served by an endpoint. This should cover\n\
\ all services that an endpoint is responsible for.\n\
\\n\
\\f\n\
\\ENQ\EOT\NUL\STX\ACK\ENQ\DC2\ETX2\STX\b\n\
\\ENQ\EOT\NUL\STX\ACK\ENQ\DC2\ETX%\STX\b\n\
\\f\n\
\\ENQ\EOT\NUL\STX\ACK\SOH\DC2\ETX2\t\f\n\
\\ENQ\EOT\NUL\STX\ACK\SOH\DC2\ETX%\t\f\n\
\\f\n\
\\ENQ\EOT\NUL\STX\ACK\ETX\DC2\ETX2\SI\DLE\n\
\\ENQ\EOT\NUL\STX\ACK\ETX\DC2\ETX%\SI\DLE\n\
\3\n\
\\EOT\EOT\NUL\STX\a\DC2\ETX5\STX(\SUB& Application specific opaque metrics.\n\
\\EOT\EOT\NUL\STX\a\DC2\ETX(\STX(\SUB& Application specific opaque metrics.\n\
\\n\
\\f\n\
\\ENQ\EOT\NUL\STX\a\ACK\DC2\ETX5\STX\NAK\n\
\\ENQ\EOT\NUL\STX\a\ACK\DC2\ETX(\STX\NAK\n\
\\f\n\
\\ENQ\EOT\NUL\STX\a\SOH\DC2\ETX5\SYN#\n\
\\ENQ\EOT\NUL\STX\a\SOH\DC2\ETX(\SYN#\n\
\\f\n\
\\ENQ\EOT\NUL\STX\a\ETX\DC2\ETX5&'\n\
\\ENQ\EOT\NUL\STX\a\ETX\DC2\ETX(&'\n\
\\148\ETX\n\
\\EOT\EOT\NUL\STX\b\DC2\ETX=\STX%\SUB\134\ETX Application specific utilization expressed as a fraction of available\n\
\\EOT\EOT\NUL\STX\b\DC2\ETX0\STX%\SUB\134\ETX Application specific utilization expressed as a fraction of available\n\
\ resources. For example, an application may report the max of CPU and memory\n\
\ utilization for better load balancing if it is both CPU and memory bound.\n\
\ This should be derived from the latest sample or measurement.\n\
\ The value may be larger than 1.0 when the usage exceeds the reporter\n\
\ dependent notion of soft limits.\n\
\\n\
\\f\n\
\\ENQ\EOT\NUL\STX\b\ENQ\DC2\ETX=\STX\b\n\
\\ENQ\EOT\NUL\STX\b\ENQ\DC2\ETX0\STX\b\n\
\\f\n\
\\ENQ\EOT\NUL\STX\b\SOH\DC2\ETX=\t \n\
\\ENQ\EOT\NUL\STX\b\SOH\DC2\ETX0\t \n\
\\f\n\
\\ENQ\EOT\NUL\STX\b\ETX\DC2\ETX=#$b\ACKproto3"
\\ENQ\EOT\NUL\STX\b\ETX\DC2\ETX0#$b\ACKproto3"
20 changes: 20 additions & 0 deletions grpc-spec/src/Network/GRPC/Spec/Headers/Response.hs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ module Network.GRPC.Spec.Headers.Response (

import Control.Exception
import Control.Monad.Except (throwError)
import Data.ByteString qualified as Strict (ByteString)
import Data.List.NonEmpty (NonEmpty)
import Data.Proxy
import Data.Text (Text)
Expand Down Expand Up @@ -112,6 +113,23 @@ data ProperTrailers_ f = ProperTrailers {
-- | Additional status message
, properTrailersGrpcMessage :: HKD f (Maybe Text)

-- | Status details
--
-- This can be used to provide additional details about the RPC error;
-- as this is a binary field, it can be used for structured data.
--
-- The spec imposes some additional restrictions on this field:
--
-- * @Status-Details@ is allowed only if @Status@ is not OK.
-- * When using Protobuf this contains a @google.rpc.Status@ message.
-- * If it contains a status code (as in the case of a @google.rpc.Status@
-- message), it MUST NOT contradict the Status header.
--
-- The spec additionally mandates that consumers MUST verify that third
-- requirement; however, it is impossible to verify this unless a specific
-- format for the status details is known.
, properTrailersStatusDetails :: HKD f (Maybe Strict.ByteString)

-- | Server pushback
--
-- This is part of automatic retries.
Expand Down Expand Up @@ -143,6 +161,7 @@ simpleProperTrailers :: forall f.
simpleProperTrailers status msg metadata = ProperTrailers {
properTrailersGrpcStatus = status
, properTrailersGrpcMessage = msg
, properTrailersStatusDetails = HKD.pure (Proxy @f) (Nothing :: Maybe Strict.ByteString)
, properTrailersPushback = HKD.pure (Proxy @f) (Nothing :: Maybe Pushback)
, properTrailersOrcaLoadReport = HKD.pure (Proxy @f) (Nothing :: Maybe OrcaLoadReport)
, properTrailersMetadata = metadata
Expand Down Expand Up @@ -172,6 +191,7 @@ instance HKD.Traversable ProperTrailers_ where
ProperTrailers
<$> (f $ properTrailersGrpcStatus x)
<*> (f $ properTrailersGrpcMessage x)
<*> (f $ properTrailersStatusDetails x)
<*> (f $ properTrailersPushback x)
<*> (f $ properTrailersOrcaLoadReport x)
<*> (pure $ properTrailersMetadata x)
Expand Down
4 changes: 4 additions & 0 deletions grpc-spec/src/Network/GRPC/Spec/PercentEncoding.hs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
--
-- The gRPC spec is similar, but not identical, to URI encoding.
--
-- > Percent-Encoded → 1*(Percent-Byte-Unencoded / Percent-Byte-Encoded)
-- > Percent-Byte-Unencoded → 1*( %x20-%x24 / %x26-%x7E ) ; space and VCHAR, except %
-- > Percent-Byte-Encoded → "%" 2HEXDIGIT ; 0-9 A-F
--
-- We work with strict bytestrings here, since these are ultimately intended
-- as header values.
--
Expand Down
26 changes: 24 additions & 2 deletions grpc-spec/src/Network/GRPC/Spec/Serialization/Headers/Response.hs
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,16 @@ invalidContentType err = invalidHeaderSynthesize GrpcException {
}

{-------------------------------------------------------------------------------
> Trailers → Status [Status-Message] *Custom-Metadata
> Trailers → Status [Status-Message] *Custom-Metadata Status →
> "grpc-status" 1*DIGIT ; 0-9 Status-Message → "grpc-message" Percent-Encoded
> Status-Details → "grpc-status-details-bin" {base64 encoded value}
where
> Status-Details is allowed only if Status is not OK. If it is set, it
> contains additional information about the RPC error. If it contains a status
> code field, it MUST NOT contradict the Status header. The consumer MUST
> verify this requirement.
-------------------------------------------------------------------------------}

-- | Construct the HTTP @Trailer@ header
Expand Down Expand Up @@ -352,6 +361,7 @@ buildProperTrailers :: ProperTrailers -> [HTTP.Header]
buildProperTrailers ProperTrailers{
properTrailersGrpcStatus
, properTrailersGrpcMessage
, properTrailersStatusDetails
, properTrailersMetadata
, properTrailersPushback
, properTrailersOrcaLoadReport
Expand All @@ -365,6 +375,9 @@ buildProperTrailers ProperTrailers{
, [ ("grpc-message", PercentEncoding.encode x)
| Just x <- [properTrailersGrpcMessage]
]
, [ ("grpc-status-details-bin", buildBinaryValue x)
| Just x <- [properTrailersStatusDetails]
]
, [ ( "grpc-retry-pushback-ms"
, buildPushback x
)
Expand Down Expand Up @@ -413,7 +426,7 @@ buildTrailersOnly f TrailersOnly{
--
-- The gRPC spec defines:
--
-- > Trailers → Status [Status-Message] *Custom-Metadata
-- > Trailers → ..
-- > Trailers-Only → HTTP-Status Content-Type Trailers
--
-- This means that Trailers-Only is a superset of the Trailers; we make use of
Expand Down Expand Up @@ -503,6 +516,15 @@ parseTrailersOnly' proxy =
Right msg -> return (Just msg)
}

| name == "grpc-status-details-bin"
= modify $ liftProperTrailers $ \x -> x{
properTrailersStatusDetails = throwInvalidHeader hdr $
case parseBinaryValue value of
Left err -> throwError err
Right val -> return (Just val)

}

| name == "grpc-retry-pushback-ms"
= modify $ liftProperTrailers $ \x -> x{
properTrailersPushback =
Expand Down
2 changes: 2 additions & 0 deletions grpc-spec/test-grpc-spec/Test/Prop/Serialization.hs
Original file line number Diff line number Diff line change
Expand Up @@ -457,12 +457,14 @@ instance Arbitrary (Awkward ProperTrailers) where
arbitrary = Awkward <$> do
properTrailersGrpcStatus <- awkward
properTrailersGrpcMessage <- awkward
properTrailersStatusDetails <- awkward
properTrailersPushback <- awkward
properTrailersOrcaLoadReport <- awkward
properTrailersMetadata <- awkward
return $ ProperTrailers{
properTrailersGrpcStatus
, properTrailersGrpcMessage
, properTrailersStatusDetails
, properTrailersPushback
, properTrailersOrcaLoadReport
, properTrailersMetadata
Expand Down
20 changes: 3 additions & 17 deletions proto/official/grpc-spec/orca_load_report.proto
Original file line number Diff line number Diff line change
@@ -1,19 +1,3 @@
// Copyright 2020 The gRPC Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Local copy of Envoy xDS proto file, used for testing only.

syntax = "proto3";

package xds.data.orca.v3;
Expand All @@ -34,6 +18,7 @@ message OrcaLoadReport {

// Total RPS being served by an endpoint. This should cover all services that an endpoint is
// responsible for.
// Deprecated -- use ``rps_fractional`` field instead.
uint64 rps = 3 [deprecated = true];

// Application specific requests costs. Each value is an absolute cost (e.g. 3487 bytes of
Expand All @@ -44,6 +29,8 @@ message OrcaLoadReport {
// available, derived from the latest sample or measurement.
map<string, double> utilization = 5;

// Total RPS being served by an endpoint. This should cover all services that an endpoint is
// responsible for.
double rps_fractional = 6;

// Total EPS (errors/second) being served by an endpoint. This should cover
Expand All @@ -61,4 +48,3 @@ message OrcaLoadReport {
// dependent notion of soft limits.
double application_utilization = 9;
}

0 comments on commit 8b0ba47

Please sign in to comment.