conjure_http/
client.rs

1// Copyright 2019 Palantir Technologies, Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! The Conjure HTTP client API.
16
17use crate::private::{self, APPLICATION_JSON};
18use bytes::Bytes;
19use conjure_error::Error;
20use conjure_serde::json;
21use futures_core::Stream;
22use http::header::CONTENT_TYPE;
23use http::{HeaderValue, Request, Response};
24use serde::de::DeserializeOwned;
25use serde::Serialize;
26use std::convert::TryFrom;
27use std::fmt::Display;
28use std::future::Future;
29use std::io::Write;
30use std::pin::Pin;
31
32#[allow(missing_docs)]
33#[deprecated(note = "renamed to RequestBody", since = "3.5.0")]
34pub type Body<'a, T> = RequestBody<'a, T>;
35
36#[allow(missing_docs)]
37#[deprecated(note = "renamed to AsyncRequestBody", since = "3.5.0")]
38pub type AsyncBody<'a, T> = AsyncRequestBody<'a, T>;
39
40/// A trait implemented by generated blocking client interfaces for a Conjure service.
41pub trait Service<C> {
42    /// Creates a new service wrapping an HTTP client.
43    fn new(client: C) -> Self;
44}
45
46/// A trait implemented by generated async client interfaces for a Conjure service.
47pub trait AsyncService<C> {
48    /// Creates a new service wrapping an async HTTP client.
49    fn new(client: C) -> Self;
50}
51
52/// Conjure-specific metadata about an endpoint.
53///
54/// This is included as an extension in all `Request`s passed to blocking and async Conjure clients.
55#[derive(Debug, Clone, PartialEq, Eq)]
56pub struct Endpoint {
57    service: &'static str,
58    version: Option<&'static str>,
59    name: &'static str,
60    path: &'static str,
61}
62
63impl Endpoint {
64    /// Creates a new `Endpoint`.
65    #[inline]
66    pub fn new(
67        service: &'static str,
68        version: Option<&'static str>,
69        name: &'static str,
70        path: &'static str,
71    ) -> Self {
72        Endpoint {
73            service,
74            version,
75            name,
76            path,
77        }
78    }
79
80    /// Returns the name of the service the endpoint is part of.
81    #[inline]
82    pub fn service(&self) -> &'static str {
83        self.service
84    }
85
86    /// Returns the version of the Conjure definition defining the service, if known.
87    #[inline]
88    pub fn version(&self) -> Option<&'static str> {
89        self.version
90    }
91
92    /// Returns the name of the endpoint.
93    #[inline]
94    pub fn name(&self) -> &'static str {
95        self.name
96    }
97
98    /// Returns the templated URI path of the endpoint.
99    #[inline]
100    pub fn path(&self) -> &'static str {
101        self.path
102    }
103}
104
105/// The body of a blocking Conjure request.
106pub enum RequestBody<'a, W> {
107    /// No body.
108    Empty,
109    /// A body already buffered in memory.
110    Fixed(Bytes),
111    /// A streaming body.
112    Streaming(Box<dyn WriteBody<W> + 'a>),
113}
114
115/// The body of an async Conjure request.
116pub enum AsyncRequestBody<'a, W> {
117    /// No body.
118    Empty,
119    /// A body already buffered in memory.
120    Fixed(Bytes),
121    /// A streaming body.
122    Streaming(BoxAsyncWriteBody<'a, W>),
123}
124
125/// A trait implemented by HTTP client implementations.
126pub trait Client {
127    /// The client's binary request write type.
128    type BodyWriter;
129    /// The client's binary response body type.
130    type ResponseBody: Iterator<Item = Result<Bytes, Error>>;
131
132    /// Makes an HTTP request.
133    ///
134    /// The request's URI will be in absolute-form and it will always contain an `Endpoint` object in its extensions.
135    ///
136    /// A response must only be returned if it has a 2xx status code. The client is responsible for handling all other
137    /// status codes (for example, converting a 5xx response into a service error). The client is also responsible for
138    /// decoding the response body if necessary.
139    fn send(
140        &self,
141        req: Request<RequestBody<'_, Self::BodyWriter>>,
142    ) -> Result<Response<Self::ResponseBody>, Error>;
143}
144
145/// A trait implemented by async HTTP client implementations.
146pub trait AsyncClient {
147    /// The client's binary request body write type.
148    type BodyWriter;
149    /// The client's binary response body type.
150    type ResponseBody: Stream<Item = Result<Bytes, Error>>;
151
152    /// Makes an HTTP request.
153    ///
154    /// The client is responsible for assembling the request URI. It is provided with the path template, unencoded path
155    /// parameters, unencoded query parameters, header parameters, and request body.
156    ///
157    /// A response must only be returned if it has a 2xx status code. The client is responsible for handling all other
158    /// status codes (for example, converting a 5xx response into a service error). The client is also responsible for
159    /// decoding the response body if necessary.
160    fn send(
161        &self,
162        req: Request<AsyncRequestBody<'_, Self::BodyWriter>>,
163    ) -> impl Future<Output = Result<Response<Self::ResponseBody>, Error>> + Send;
164}
165
166/// A trait implemented by streaming bodies.
167pub trait WriteBody<W> {
168    /// Writes the body out, in its entirety.
169    ///
170    /// Behavior is unspecified if this method is called twice without a successful call to `reset` in between.
171    fn write_body(&mut self, w: &mut W) -> Result<(), Error>;
172
173    /// Attempts to reset the body so that it can be written out again.
174    ///
175    /// Returns `true` if successful. Behavior is unspecified if this is not called after a call to `write_body`.
176    fn reset(&mut self) -> bool;
177}
178
179impl<W> WriteBody<W> for &[u8]
180where
181    W: Write,
182{
183    fn write_body(&mut self, w: &mut W) -> Result<(), Error> {
184        w.write_all(self).map_err(Error::internal_safe)
185    }
186
187    fn reset(&mut self) -> bool {
188        true
189    }
190}
191
192/// A trait implemented by async streaming bodies.
193///
194/// # Examples
195///
196/// ```ignore
197/// use conjure_error::Error;
198/// use conjure_http::client::AsyncWriteBody;
199/// use std::pin::Pin;
200/// use tokio_io::{AsyncWrite, AsyncWriteExt};
201///
202/// pub struct SimpleBodyWriter;
203///
204/// impl<W> AsyncWriteBody<W> for SimpleBodyWriter
205/// where
206///     W: AsyncWrite + Send,
207/// {
208///     async fn write_body(self: Pin<&mut Self>, mut w: Pin<&mut W>) -> Result<(), Error> {
209///         w.write_all(b"hello world").await.map_err(Error::internal_safe)
210///     }
211///
212///     async fn reset(self: Pin<&mut Self>) -> bool {
213///         true
214///     }
215/// }
216/// ```
217pub trait AsyncWriteBody<W> {
218    /// Writes the body out, in its entirety.
219    ///
220    /// Behavior is unspecified if this method is called twice without a successful call to `reset` in between.
221    fn write_body(
222        self: Pin<&mut Self>,
223        w: Pin<&mut W>,
224    ) -> impl Future<Output = Result<(), Error>> + Send;
225
226    /// Attempts to reset the body so that it can be written out again.
227    ///
228    /// Returns `true` if successful. Behavior is unspecified if this is not called after a call to `write_body`.
229    fn reset(self: Pin<&mut Self>) -> impl Future<Output = bool> + Send;
230}
231
232// An internal object-safe version of AsyncWriteBody used to implement BoxAsyncWriteBody.
233trait AsyncWriteBodyEraser<W> {
234    fn write_body<'a>(
235        self: Pin<&'a mut Self>,
236        w: Pin<&'a mut W>,
237    ) -> Pin<Box<dyn Future<Output = Result<(), Error>> + Send + 'a>>;
238
239    fn reset<'a>(self: Pin<&'a mut Self>) -> Pin<Box<dyn Future<Output = bool> + Send + 'a>>
240    where
241        W: 'a;
242}
243
244impl<T, W> AsyncWriteBodyEraser<W> for T
245where
246    T: AsyncWriteBody<W> + ?Sized,
247{
248    fn write_body<'a>(
249        self: Pin<&'a mut Self>,
250        w: Pin<&'a mut W>,
251    ) -> Pin<Box<dyn Future<Output = Result<(), Error>> + Send + 'a>> {
252        Box::pin(self.write_body(w))
253    }
254
255    fn reset<'a>(self: Pin<&'a mut Self>) -> Pin<Box<dyn Future<Output = bool> + Send + 'a>>
256    where
257        W: 'a,
258    {
259        Box::pin(self.reset())
260    }
261}
262
263/// A boxed [`AsyncWriteBody`] trait object.
264pub struct BoxAsyncWriteBody<'a, W> {
265    inner: Pin<Box<dyn AsyncWriteBodyEraser<W> + Send + 'a>>,
266}
267
268impl<'a, W> BoxAsyncWriteBody<'a, W> {
269    /// Creates a new `BoxAsyncWriteBody`.
270    pub fn new<T>(v: T) -> Self
271    where
272        T: AsyncWriteBody<W> + Send + 'a,
273    {
274        BoxAsyncWriteBody { inner: Box::pin(v) }
275    }
276}
277
278impl<W> AsyncWriteBody<W> for BoxAsyncWriteBody<'_, W>
279where
280    W: Send,
281{
282    async fn write_body(mut self: Pin<&mut Self>, w: Pin<&mut W>) -> Result<(), Error> {
283        self.inner.as_mut().write_body(w).await
284    }
285
286    async fn reset(mut self: Pin<&mut Self>) -> bool {
287        self.inner.as_mut().reset().await
288    }
289}
290
291/// A trait implemented by request body serializers used by custom Conjure client trait
292/// implementations.
293pub trait SerializeRequest<'a, T, W> {
294    /// Returns the body's content type.
295    fn content_type(value: &T) -> HeaderValue;
296
297    /// Returns the body's length, if known.
298    ///
299    /// Empty and fixed size bodies will have their content length filled in automatically.
300    ///
301    /// The default implementation returns `None`.
302    fn content_length(value: &T) -> Option<u64> {
303        let _value = value;
304        None
305    }
306
307    /// Serializes the body.
308    fn serialize(value: T) -> Result<RequestBody<'a, W>, Error>;
309}
310
311/// A trait implemented by request body serializers used by custom async Conjure client trait
312/// implementations.
313pub trait AsyncSerializeRequest<'a, T, W> {
314    /// Returns the body's content type.
315    fn content_type(value: &T) -> HeaderValue;
316
317    /// Returns the body's length, if known.
318    ///
319    /// Empty and fixed size bodies will have their content length filled in automatically.
320    ///
321    /// The default implementation returns `None`.
322    fn content_length(value: &T) -> Option<u64> {
323        let _value = value;
324        None
325    }
326
327    /// Serializes the body.
328    fn serialize(value: T) -> Result<AsyncRequestBody<'a, W>, Error>;
329}
330
331/// A body serializer which acts like a Conjure-generated client would.
332pub enum ConjureRequestSerializer {}
333
334impl<'a, T, W> SerializeRequest<'a, T, W> for ConjureRequestSerializer
335where
336    T: Serialize,
337{
338    fn content_type(_: &T) -> HeaderValue {
339        APPLICATION_JSON
340    }
341
342    fn serialize(value: T) -> Result<RequestBody<'a, W>, Error> {
343        let body = json::to_vec(&value).map_err(Error::internal)?;
344        Ok(RequestBody::Fixed(body.into()))
345    }
346}
347
348impl<'a, T, W> AsyncSerializeRequest<'a, T, W> for ConjureRequestSerializer
349where
350    T: Serialize,
351{
352    fn content_type(_: &T) -> HeaderValue {
353        APPLICATION_JSON
354    }
355
356    fn serialize(value: T) -> Result<AsyncRequestBody<'a, W>, Error> {
357        let buf = json::to_vec(&value).map_err(Error::internal)?;
358        Ok(AsyncRequestBody::Fixed(Bytes::from(buf)))
359    }
360}
361
362/// A trait implemented by response deserializers used by custom Conjure client trait
363/// implementations.
364pub trait DeserializeResponse<T, R> {
365    /// Returns the value of the `Accept` header to be included in the request.
366    fn accept() -> Option<HeaderValue>;
367
368    /// Deserializes the response.
369    fn deserialize(response: Response<R>) -> Result<T, Error>;
370}
371
372/// A trait implemented by response deserializers used by custom async Conjure client trait
373/// implementations.
374pub trait AsyncDeserializeResponse<T, R> {
375    /// Returns the value of the `Accept` header to be included in the request.
376    fn accept() -> Option<HeaderValue>;
377
378    /// Deserializes the response.
379    fn deserialize(response: Response<R>) -> impl Future<Output = Result<T, Error>> + Send;
380}
381
382/// A response deserializer which ignores the response and returns `()`.
383pub enum UnitResponseDeserializer {}
384
385impl<R> DeserializeResponse<(), R> for UnitResponseDeserializer {
386    fn accept() -> Option<HeaderValue> {
387        None
388    }
389
390    fn deserialize(_: Response<R>) -> Result<(), Error> {
391        Ok(())
392    }
393}
394
395impl<R> AsyncDeserializeResponse<(), R> for UnitResponseDeserializer
396where
397    R: Send,
398{
399    fn accept() -> Option<HeaderValue> {
400        None
401    }
402
403    async fn deserialize(_: Response<R>) -> Result<(), Error> {
404        Ok(())
405    }
406}
407
408/// A response deserializer which acts like a Conjure-generated client would.
409pub enum ConjureResponseDeserializer {}
410
411impl<T, R> DeserializeResponse<T, R> for ConjureResponseDeserializer
412where
413    T: DeserializeOwned,
414    R: Iterator<Item = Result<Bytes, Error>>,
415{
416    fn accept() -> Option<HeaderValue> {
417        Some(APPLICATION_JSON)
418    }
419
420    fn deserialize(response: Response<R>) -> Result<T, Error> {
421        if response.headers().get(CONTENT_TYPE) != Some(&APPLICATION_JSON) {
422            return Err(Error::internal_safe("invalid response Content-Type"));
423        }
424        let buf = private::read_body(response.into_body(), None)?;
425        json::client_from_slice(&buf).map_err(Error::internal)
426    }
427}
428
429impl<T, R> AsyncDeserializeResponse<T, R> for ConjureResponseDeserializer
430where
431    T: DeserializeOwned,
432    R: Stream<Item = Result<Bytes, Error>> + Send,
433{
434    fn accept() -> Option<HeaderValue> {
435        Some(APPLICATION_JSON)
436    }
437
438    async fn deserialize(response: Response<R>) -> Result<T, Error> {
439        if response.headers().get(CONTENT_TYPE) != Some(&APPLICATION_JSON) {
440            return Err(Error::internal_safe("invalid response Content-Type"));
441        }
442        let buf = private::async_read_body(response.into_body(), None).await?;
443        json::client_from_slice(&buf).map_err(Error::internal)
444    }
445}
446
447/// A trait implemented by header encoders used by custom Conjure client trait implementations.
448pub trait EncodeHeader<T> {
449    /// Encodes the value into headers.
450    ///
451    /// In almost all cases a single `HeaderValue` should be returned.
452    fn encode(value: T) -> Result<Vec<HeaderValue>, Error>;
453}
454
455/// A trait implemented by URL parameter encoders used by custom Conjure client trait
456/// implementations.
457pub trait EncodeParam<T> {
458    /// Encodes the value into a sequence of parameters.
459    ///
460    /// When used with a path parameter, each returned string will be a separate path component.
461    /// When used with a query parameter, each returned string will be the value of a separate query
462    /// entry.
463    fn encode(value: T) -> Result<Vec<String>, Error>;
464}
465
466/// An encoder which converts values via their `Display` implementation.
467pub enum DisplayEncoder {}
468
469impl<T> EncodeHeader<T> for DisplayEncoder
470where
471    T: Display,
472{
473    fn encode(value: T) -> Result<Vec<HeaderValue>, Error> {
474        HeaderValue::try_from(value.to_string())
475            .map_err(Error::internal_safe)
476            .map(|v| vec![v])
477    }
478}
479
480impl<T> EncodeParam<T> for DisplayEncoder
481where
482    T: Display,
483{
484    fn encode(value: T) -> Result<Vec<String>, Error> {
485        Ok(vec![value.to_string()])
486    }
487}
488
489/// An encoder which converts a sequence of values via their individual `Display`
490/// implementations.
491pub enum DisplaySeqEncoder {}
492
493impl<T, U> EncodeHeader<T> for DisplaySeqEncoder
494where
495    T: IntoIterator<Item = U>,
496    U: Display,
497{
498    fn encode(value: T) -> Result<Vec<HeaderValue>, Error> {
499        value
500            .into_iter()
501            .map(|v| HeaderValue::try_from(v.to_string()).map_err(Error::internal_safe))
502            .collect()
503    }
504}
505
506impl<T, U> EncodeParam<T> for DisplaySeqEncoder
507where
508    T: IntoIterator<Item = U>,
509    U: Display,
510{
511    fn encode(value: T) -> Result<Vec<String>, Error> {
512        Ok(value.into_iter().map(|v| v.to_string()).collect())
513    }
514}