Skip to content

Commit 6ef568c

Browse files
xDS: ext_proc: add GRPC body send mode (#38753)
Adds a new body send mode for gRPC traffic. Also adds a safe way for the ext_proc server to return OK status without losing data in FULL_DUPLEX_STREAMED and GRPC modes. See grpc/proposal#484 for context. Risk Level: Low Testing: N/A Docs Changes: Included in PR Release Notes: N/A Platform Specific Features: N/A --------- Signed-off-by: Mark D. Roth <roth@google.com> Co-authored-by: Adi (Suissa) Peleg <adip@google.com> Mirrored from https://github.com/envoyproxy/envoy @ 7b3a632333b587c784aff65e72ff618ff034f331
1 parent faef741 commit 6ef568c

File tree

3 files changed

+84
-13
lines changed

3 files changed

+84
-13
lines changed

envoy/extensions/filters/http/ext_proc/v3/ext_proc.proto

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,10 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE;
6060
// * Request body: If the body is present, the behavior depends on the
6161
// body send mode. In ``BUFFERED`` or ``BUFFERED_PARTIAL`` mode, the body is sent to the external
6262
// processor in a single message. In ``STREAMED`` or ``FULL_DUPLEX_STREAMED`` mode, the body will
63-
// be split across multiple messages sent to the external processor. In ``NONE`` mode, the body
64-
// will not be sent to the external processor.
63+
// be split across multiple messages sent to the external processor. In ``GRPC`` mode, as each
64+
// gRPC message arrives, it will be sent to the external processor (there will be exactly one
65+
// gRPC message in each message sent to the external processor). In ``NONE`` mode, the body will
66+
// not be sent to the external processor.
6567
// * Request trailers: Delivered if they are present and if the trailer mode is set
6668
// to ``SEND``.
6769
// * Response headers: Contains the headers from the HTTP response. Keep in mind
@@ -209,9 +211,9 @@ message ExternalProcessor {
209211
// an error (subject to the processing mode) if the timer expires before a
210212
// matching response is received. There is no timeout when the filter is
211213
// running in observability mode or when the body send mode is
212-
// ``FULL_DUPLEX_STREAMED``. Zero is a valid config which means the timer
213-
// will be triggered immediately. If not configured, default is 200
214-
// milliseconds.
214+
// ``FULL_DUPLEX_STREAMED`` or ``GRPC``. Zero is a valid config which means
215+
// the timer will be triggered immediately. If not configured, default is
216+
// 200 milliseconds.
215217
google.protobuf.Duration message_timeout = 7 [(validate.rules).duration = {
216218
lte {seconds: 3600}
217219
gte {}
@@ -273,8 +275,8 @@ message ExternalProcessor {
273275
// without pausing on filter chain iteration. It is "Send and Go" mode that can be used
274276
// by external processor to observe the request's data and status. In this mode:
275277
//
276-
// 1. Only ``STREAMED`` and ``NONE`` body processing modes are supported; for any other body
277-
// processing mode, the body will not be sent.
278+
// 1. Only ``STREAMED``, ``GRPC``, and ``NONE`` body processing modes are supported; for any
279+
// other body processing mode, the body will not be sent.
278280
//
279281
// 2. External processor should not send back processing response, as any responses will be ignored.
280282
// This also means that

envoy/extensions/filters/http/ext_proc/v3/processing_mode.proto

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,25 @@ message ProcessingMode {
108108
// * The client will stream the body chunks in the responses from the server to the upstream/downstream as they arrive.
109109

110110
FULL_DUPLEX_STREAMED = 4;
111+
112+
// [#not-implemented-hide:]
113+
// A mode for gRPC traffic. This is similar to ``FULL_DUPLEX_STREAMED``,
114+
// except that instead of sending raw chunks of the HTTP/2 DATA frames,
115+
// the ext_proc client will de-frame the individual gRPC messages inside
116+
// the HTTP/2 DATA frames, and as each message is de-framed, it will be
117+
// sent to the ext_proc server as a :ref:`request_body
118+
// <envoy_v3_api_field_service.ext_proc.v3.ProcessingRequest.request_body>`
119+
// or :ref:`response_body
120+
// <envoy_v3_api_field_service.ext_proc.v3.ProcessingRequest.response_body>`.
121+
// The ext_proc server will stream back individual gRPC messages in the
122+
// :ref:`StreamedBodyResponse <envoy_v3_api_msg_service.ext_proc.v3.StreamedBodyResponse>`
123+
// field, but the number of messages sent by the ext_proc server
124+
// does not need to equal the number of messages sent by the data
125+
// plane. This allows the ext_proc server to change the number of
126+
// messages sent on the stream.
127+
// In this mode, the client will send body and trailers to the server as
128+
// they arrive.
129+
GRPC = 5;
111130
}
112131

113132
// How to handle the request header. Default is "SEND".

envoy/service/ext_proc/v3/external_processor.proto

Lines changed: 56 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ message ProcessingRequest {
164164
// the server must send back exactly one ProcessingResponse message.
165165
// * If it is set to ``FULL_DUPLEX_STREAMED``, the server must follow the API defined
166166
// for this mode to send the ProcessingResponse messages.
167-
// [#next-free-field: 11]
167+
// [#next-free-field: 12]
168168
message ProcessingResponse {
169169
// The response type that is sent by the server.
170170
oneof response {
@@ -224,6 +224,19 @@ message ProcessingResponse {
224224
// is set to true.
225225
envoy.extensions.filters.http.ext_proc.v3.ProcessingMode mode_override = 9;
226226

227+
// [#not-implemented-hide:]
228+
// Used only in ``FULL_DUPLEX_STREAMED`` and ``GRPC`` body send modes.
229+
// Instructs the data plane to stop sending body data and to send a
230+
// half-close on the ext_proc stream. The ext_proc server should then echo
231+
// back all subsequent body contents as-is until it sees the client's
232+
// half-close, at which point the ext_proc server can terminate the stream
233+
// with an OK status. This provides a safe way for the ext_proc server
234+
// to indicate that it does not need to see the rest of the stream;
235+
// without this, the ext_proc server could not terminate the stream
236+
// early, because it would wind up dropping any body contents that the
237+
// client had already sent before it saw the ext_proc stream termination.
238+
bool request_drain = 11;
239+
227240
// When ext_proc server receives a request message, in case it needs more
228241
// time to process the message, it sends back a ProcessingResponse message
229242
// with a new timeout value. When the data plane receives this response
@@ -268,11 +281,27 @@ message HttpHeaders {
268281
message HttpBody {
269282
// The contents of the body in the HTTP request/response. Note that in
270283
// streaming mode multiple ``HttpBody`` messages may be sent.
284+
//
285+
// In ``GRPC`` body send mode, a separate ``HttpBody`` message will be
286+
// sent for each message in the gRPC stream.
271287
bytes body = 1;
272288

273289
// If ``true``, this will be the last ``HttpBody`` message that will be sent and no
274290
// trailers will be sent for the current request/response.
275291
bool end_of_stream = 2;
292+
293+
// This field is used in ``GRPC`` body send mode when ``end_of_stream`` is
294+
// true and ``body`` is empty. Those values would normally indicate an
295+
// empty message on the stream with the end-of-stream bit set.
296+
// However, if the half-close happens after the last message on the
297+
// stream was already sent, then this field will be true to indicate an
298+
// end-of-stream with *no* message (as opposed to an empty message).
299+
bool end_of_stream_without_message = 3;
300+
301+
// This field is used in ``GRPC`` body send mode to indicate whether
302+
// the message is compressed. This will never be set to true by gRPC
303+
// but may be set to true by a proxy like Envoy.
304+
bool grpc_message_compressed = 4;
276305
}
277306

278307
// This message is sent to the external server when the HTTP request and
@@ -331,6 +360,8 @@ message CommonResponse {
331360
//
332361
// In other words, this response makes it possible to turn an HTTP GET
333362
// into a POST, PUT, or PATCH.
363+
//
364+
// Not supported if the body send mode is ``GRPC``.
334365
CONTINUE_AND_REPLACE = 1;
335366
}
336367

@@ -415,15 +446,34 @@ message HeaderMutation {
415446
repeated string remove_headers = 2;
416447
}
417448

418-
// The body response message corresponding to FULL_DUPLEX_STREAMED body mode.
449+
// The body response message corresponding to ``FULL_DUPLEX_STREAMED`` or ``GRPC`` body modes.
419450
message StreamedBodyResponse {
420-
// The body response chunk that will be passed to the upstream/downstream by the data plane.
451+
// In ``FULL_DUPLEX_STREAMED`` body send mode, contains the body response chunk that will be
452+
// passed to the upstream/downstream by the data plane. In ``GRPC`` body send mode, contains
453+
// a serialized gRPC message to be passed to the upstream/downstream by the data plane.
421454
bytes body = 1;
422455

423456
// The server sets this flag to true if it has received a body request with
424457
// :ref:`end_of_stream <envoy_v3_api_field_service.ext_proc.v3.HttpBody.end_of_stream>` set to true,
425458
// and this is the last chunk of body responses.
459+
// Note that in ``GRPC`` body send mode, this allows the ext_proc
460+
// server to tell the data plane to send a half close after a client
461+
// message, which will result in discarding any other messages sent by
462+
// the client application.
426463
bool end_of_stream = 2;
464+
465+
// This field is used in ``GRPC`` body send mode when ``end_of_stream`` is
466+
// true and ``body`` is empty. Those values would normally indicate an
467+
// empty message on the stream with the end-of-stream bit set.
468+
// However, if the half-close happens after the last message on the
469+
// stream was already sent, then this field will be true to indicate an
470+
// end-of-stream with *no* message (as opposed to an empty message).
471+
bool end_of_stream_without_message = 3;
472+
473+
// This field is used in ``GRPC`` body send mode to indicate whether
474+
// the message is compressed. This will never be set to true by gRPC
475+
// but may be set to true by a proxy like Envoy.
476+
bool grpc_message_compressed = 4;
427477
}
428478

429479
// This message specifies the body mutation the server sends to the data plane.
@@ -433,19 +483,19 @@ message BodyMutation {
433483
// The entire body to replace.
434484
// Should only be used when the corresponding ``BodySendMode`` in the
435485
// :ref:`processing_mode <envoy_v3_api_field_extensions.filters.http.ext_proc.v3.ExternalProcessor.processing_mode>`
436-
// is not set to ``FULL_DUPLEX_STREAMED``.
486+
// is not set to ``FULL_DUPLEX_STREAMED`` or ``GRPC``.
437487
bytes body = 1;
438488

439489
// Clear the corresponding body chunk.
440490
// Should only be used when the corresponding ``BodySendMode`` in the
441491
// :ref:`processing_mode <envoy_v3_api_field_extensions.filters.http.ext_proc.v3.ExternalProcessor.processing_mode>`
442-
// is not set to ``FULL_DUPLEX_STREAMED``.
492+
// is not set to ``FULL_DUPLEX_STREAMED`` or ``GRPC``.
443493
// Clear the corresponding body chunk.
444494
bool clear_body = 2;
445495

446496
// Must be used when the corresponding ``BodySendMode`` in the
447497
// :ref:`processing_mode <envoy_v3_api_field_extensions.filters.http.ext_proc.v3.ExternalProcessor.processing_mode>`
448-
// is set to ``FULL_DUPLEX_STREAMED``.
498+
// is set to ``FULL_DUPLEX_STREAMED`` or ``GRPC``.
449499
StreamedBodyResponse streamed_response = 3
450500
[(xds.annotations.v3.field_status).work_in_progress = true];
451501
}

0 commit comments

Comments
 (0)