@@ -114,9 +114,9 @@ pub trait DynamicVersionPolicy: std::fmt::Debug + Send + Sync {
114114 ) -> Result < Version , HttpError > ;
115115}
116116
117- /// Implementation of `DynamicVersionPolicy` where the client must specify a
117+ /// Implementation of `DynamicVersionPolicy` where the client specifies a
118118/// specific semver in a specific header and we always use whatever they
119- /// requested
119+ /// requested.
120120///
121121/// An incoming request will be rejected with a 400-level error if:
122122///
@@ -125,17 +125,22 @@ pub trait DynamicVersionPolicy: std::fmt::Debug + Send + Sync {
125125/// [`ClientSpecifiesVersionInHeader::new()`], which implies that the client
126126/// is trying to use a newer version of the API than this server supports.
127127///
128+ /// By default, incoming requests will also be rejected with a 400-level error
129+ /// if the header is missing. To override this behavior, supply a default
130+ /// version via [`Self::on_missing`].
131+ ///
128132/// If you need anything more flexible (e.g., validating the provided version
129133/// against a fixed set of supported versions), you'll want to impl
130134/// `DynamicVersionPolicy` yourself.
131135#[ derive( Debug ) ]
132136pub struct ClientSpecifiesVersionInHeader {
133137 name : HeaderName ,
134138 max_version : Version ,
139+ on_missing : Option < Version > ,
135140}
136141
137142impl ClientSpecifiesVersionInHeader {
138- /// Make a new `ClientSpecifiesVersionInHeader` policy
143+ /// Make a new `ClientSpecifiesVersionInHeader` policy.
139144 ///
140145 /// Arguments:
141146 ///
@@ -148,7 +153,25 @@ impl ClientSpecifiesVersionInHeader {
148153 name : HeaderName ,
149154 max_version : Version ,
150155 ) -> ClientSpecifiesVersionInHeader {
151- ClientSpecifiesVersionInHeader { name, max_version }
156+ ClientSpecifiesVersionInHeader { name, max_version, on_missing : None }
157+ }
158+
159+ /// If the header is missing, use the provided version instead.
160+ ///
161+ /// By default, the policy will reject requests with a missing header. Call
162+ /// this function to use the provided version instead.
163+ ///
164+ /// Typically, the provided version should either be a fixed supported
165+ /// version (for backwards compatibility with older clients), or the newest
166+ /// supported version (in case clients are generally kept up-to-date but not
167+ /// all clients send the header).
168+ ///
169+ /// Using this function is not recommended if you control all clients—in
170+ /// that case, arrange for clients to send the header instead. In
171+ /// particular, **at Oxide, do not use this function for internal APIs.**
172+ pub fn on_missing ( mut self , version : Version ) -> Self {
173+ self . on_missing = Some ( version) ;
174+ self
152175 }
153176}
154177
@@ -159,13 +182,25 @@ impl DynamicVersionPolicy for ClientSpecifiesVersionInHeader {
159182 _log : & Logger ,
160183 ) -> Result < Version , HttpError > {
161184 let v = parse_header ( request. headers ( ) , & self . name ) ?;
162- if v <= self . max_version {
163- Ok ( v)
164- } else {
165- Err ( HttpError :: for_bad_request (
185+ match ( v, & self . on_missing ) {
186+ ( Some ( v) , _) => {
187+ if v <= self . max_version {
188+ Ok ( v)
189+ } else {
190+ Err ( HttpError :: for_bad_request (
191+ None ,
192+ format ! (
193+ "server does not support this API version: {}" ,
194+ v
195+ ) ,
196+ ) )
197+ }
198+ }
199+ ( None , Some ( on_missing) ) => Ok ( on_missing. clone ( ) ) ,
200+ ( None , None ) => Err ( HttpError :: for_bad_request (
166201 None ,
167- format ! ( "server does not support this API version: { }" , v ) ,
168- ) )
202+ format ! ( "missing expected header {:? }" , self . name ) ,
203+ ) ) ,
169204 }
170205 }
171206}
@@ -175,17 +210,12 @@ impl DynamicVersionPolicy for ClientSpecifiesVersionInHeader {
175210fn parse_header < T > (
176211 headers : & http:: HeaderMap ,
177212 header_name : & HeaderName ,
178- ) -> Result < T , HttpError >
213+ ) -> Result < Option < T > , HttpError >
179214where
180215 T : FromStr ,
181216 <T as FromStr >:: Err : std:: fmt:: Display ,
182217{
183- let v_value = headers. get ( header_name) . ok_or_else ( || {
184- HttpError :: for_bad_request (
185- None ,
186- format ! ( "missing expected header {:?}" , header_name) ,
187- )
188- } ) ?;
218+ let Some ( v_value) = headers. get ( header_name) else { return Ok ( None ) } ;
189219
190220 let v_str = v_value. to_str ( ) . map_err ( |_| {
191221 HttpError :: for_bad_request (
@@ -197,10 +227,12 @@ where
197227 )
198228 } ) ?;
199229
200- v_str. parse :: < T > ( ) . map_err ( |e| {
230+ let v = v_str. parse :: < T > ( ) . map_err ( |e| {
201231 HttpError :: for_bad_request (
202232 None ,
203233 format ! ( "bad value for header {:?}: {}: {}" , header_name, e, v_str) ,
204234 )
205- } )
235+ } ) ?;
236+
237+ Ok ( Some ( v) )
206238}
0 commit comments