Conversation
Adds `Interceptor::intercept_head`, which receives a `RequestHead` (path, resolved `Spec`, headers, protocol, request extensions) before the server reads any of the body or starts a body reader. It runs for every registered interceptor in registration order, on unary, streaming, gRPC unary and Connect GET requests, and the first error is returned to the client in the protocol's error format without the body being read. An HTTP/2 stream is reset and an HTTP/1.x connection is usually closed, as for a Tower layer that returns a response without calling the service. Interceptors previously ran after the body was read (unary) or after the body reader had started (streaming), so only Tower middleware could reject early, and middleware cannot see the resolved `Spec`. The new method defaults to accepting every request, so existing interceptors are unchanged. A head check can insert values with `RequestHead::extensions_mut`, which later interceptors and the handler read from the request context, so an authentication check can pass the caller on. `RequestHead::new` builds a head for unit tests, and `peer_addr` / `peer_certs` read the connection's address and client certificates. Requests the service rejects for an unsupported HTTP method or an unrecognized content type still never reach interceptors. Signed-off-by: Iain McGinniss <309153+iainmcgin@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Interceptors run after the request body is read (unary) or after a body reader has started (streaming), so the only way to reject a request before any body byte is read was Tower middleware, which cannot see the resolved
Spec.Interceptor::intercept_head(&self, head: &mut RequestHead<'_>)runs for every registered interceptor, in registration order, before the body is read or a reader starts, on unary, streaming, gRPC unary and Connect GET requests. The firstErris returned to the client in the protocol's error format and the body is never read: an HTTP/2 stream is reset and an HTTP/1.x connection is usually closed, as for a Tower rejection. The default accepts every request, so existing interceptors are unchanged.RequestHead::spec()isNonefor an unknown path. Values inserted withextensions_mut()reach later interceptors and the handler.The head is
&mutso an authentication check can pass the caller on; adding mutability later would break every implementor.All head checks run before any
intercept_unaryorintercept_streaming, so they see the headers as they reached the service, not as an outer interceptor rewrote them, and an outer interceptor never sees a head rejection. The hook runs before the request deadline applies, so it has no time limit. The guide now listsintercept_headnext to Tower middleware as a place for authentication.Requests rejected for an unsupported method or unrecognized content type never reach interceptors, and their body is still drained under
max_request_body_sizeand the request deadline. Each registered interceptor allocates one boxed future per request for this method, even if it does not override it.