Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
rust-server.client-mod.mustache Maven / Gradle / Ivy
{{>client-imports}}
use crate::{Api{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}},
{{{operationId}}}Response{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}}
};
{{#hasCallbacks}}
pub mod callbacks;
{{/hasCallbacks}}
/// Convert input into a base path, e.g. "http://example:123". Also checks the scheme as it goes.
fn into_base_path(input: impl TryInto, correct_scheme: Option<&'static str>) -> Result {
// First convert to Uri, since a base path is a subset of Uri.
let uri = input.try_into()?;
let scheme = uri.scheme_str().ok_or(ClientInitError::InvalidScheme)?;
// Check the scheme if necessary
if let Some(correct_scheme) = correct_scheme {
if scheme != correct_scheme {
return Err(ClientInitError::InvalidScheme);
}
}
let host = uri.host().ok_or(ClientInitError::MissingHost)?;
let port = uri.port_u16().map(|x| format!(":{}", x)).unwrap_or_default();
Ok(format!("{}://{}{}{}", scheme, host, port, uri.path().trim_end_matches('/')))
}
/// A client that implements the API by making HTTP calls out to a server.
pub struct Client where
S: Service<
(Request, C),
Response=Response> + Clone + Sync + Send + 'static,
S::Future: Send + 'static,
S::Error: Into + fmt::Display,
C: Clone + Send + Sync + 'static
{
/// Inner service
client_service: S,
/// Base path of the API
base_path: String,
/// Marker
marker: PhantomData,
}
impl fmt::Debug for Client where
S: Service<
(Request, C),
Response=Response> + Clone + Sync + Send + 'static,
S::Future: Send + 'static,
S::Error: Into + fmt::Display,
C: Clone + Send + Sync + 'static
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Client {{ base_path: {} }}", self.base_path)
}
}
impl Clone for Client where
S: Service<
(Request, C),
Response=Response> + Clone + Sync + Send + 'static,
S::Future: Send + 'static,
S::Error: Into + fmt::Display,
C: Clone + Send + Sync + 'static
{
fn clone(&self) -> Self {
Self {
client_service: self.client_service.clone(),
base_path: self.base_path.clone(),
marker: PhantomData,
}
}
}
impl Client, C>, C> where
Connector: hyper::client::connect::Connect + Clone + Send + Sync + 'static,
C: Clone + Send + Sync + 'static,
{
/// Create a client with a custom implementation of hyper::client::Connect.
///
/// Intended for use with custom implementations of connect for e.g. protocol logging
/// or similar functionality which requires wrapping the transport layer. When wrapping a TCP connection,
/// this function should be used in conjunction with `swagger::Connector::builder()`.
///
/// For ordinary tcp connections, prefer the use of `try_new_http`, `try_new_https`
/// and `try_new_https_mutual`, to avoid introducing a dependency on the underlying transport layer.
///
/// # Arguments
///
/// * `base_path` - base path of the client API, i.e. "http://www.my-api-implementation.com"
/// * `protocol` - Which protocol to use when constructing the request url, e.g. `Some("http")`
/// * `connector` - Implementation of `hyper::client::Connect` to use for the client
pub fn try_new_with_connector(
base_path: &str,
protocol: Option<&'static str>,
connector: Connector,
) -> Result
{
let client_service = hyper::client::Client::builder().build(connector);
let client_service = DropContextService::new(client_service);
Ok(Self {
client_service,
base_path: into_base_path(base_path, protocol)?,
marker: PhantomData,
})
}
}
#[derive(Debug, Clone)]
pub enum HyperClient {
Http(hyper::client::Client),
Https(hyper::client::Client),
}
impl Service> for HyperClient {
type Response = Response;
type Error = hyper::Error;
type Future = hyper::client::ResponseFuture;
fn poll_ready(&mut self, cx: &mut Context) -> Poll> {
match self {
HyperClient::Http(client) => client.poll_ready(cx),
HyperClient::Https(client) => client.poll_ready(cx),
}
}
fn call(&mut self, req: Request) -> Self::Future {
match self {
HyperClient::Http(client) => client.call(req),
HyperClient::Https(client) => client.call(req)
}
}
}
impl Client, C> where
C: Clone + Send + Sync + 'static,
{
/// Create an HTTP client.
///
/// # Arguments
/// * `base_path` - base path of the client API, i.e. "http://www.my-api-implementation.com"
pub fn try_new(
base_path: &str,
) -> Result {
let uri = Uri::from_str(base_path)?;
let scheme = uri.scheme_str().ok_or(ClientInitError::InvalidScheme)?;
let scheme = scheme.to_ascii_lowercase();
let connector = Connector::builder();
let client_service = match scheme.as_str() {
"http" => {
HyperClient::Http(hyper::client::Client::builder().build(connector.build()))
},
"https" => {
let connector = connector.https()
.build()
.map_err(ClientInitError::SslError)?;
HyperClient::Https(hyper::client::Client::builder().build(connector))
},
_ => {
return Err(ClientInitError::InvalidScheme);
}
};
let client_service = DropContextService::new(client_service);
Ok(Self {
client_service,
base_path: into_base_path(base_path, None)?,
marker: PhantomData,
})
}
}
impl Client, C>, C> where
C: Clone + Send + Sync + 'static
{
/// Create an HTTP client.
///
/// # Arguments
/// * `base_path` - base path of the client API, i.e. "http://www.my-api-implementation.com"
pub fn try_new_http(
base_path: &str,
) -> Result {
let http_connector = Connector::builder().build();
Self::try_new_with_connector(base_path, Some("http"), http_connector)
}
}
#[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))]
type HttpsConnector = hyper_tls::HttpsConnector;
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
type HttpsConnector = hyper_openssl::HttpsConnector;
impl Client, C>, C> where
C: Clone + Send + Sync + 'static
{
/// Create a client with a TLS connection to the server
///
/// # Arguments
/// * `base_path` - base path of the client API, i.e. "https://www.my-api-implementation.com"
pub fn try_new_https(base_path: &str) -> Result
{
let https_connector = Connector::builder()
.https()
.build()
.map_err(ClientInitError::SslError)?;
Self::try_new_with_connector(base_path, Some("https"), https_connector)
}
/// Create a client with a TLS connection to the server using a pinned certificate
///
/// # Arguments
/// * `base_path` - base path of the client API, i.e. "https://www.my-api-implementation.com"
/// * `ca_certificate` - Path to CA certificate used to authenticate the server
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
pub fn try_new_https_pinned(
base_path: &str,
ca_certificate: CA,
) -> Result
where
CA: AsRef,
{
let https_connector = Connector::builder()
.https()
.pin_server_certificate(ca_certificate)
.build()
.map_err(ClientInitError::SslError)?;
Self::try_new_with_connector(base_path, Some("https"), https_connector)
}
/// Create a client with a mutually authenticated TLS connection to the server.
///
/// # Arguments
/// * `base_path` - base path of the client API, i.e. "https://www.my-api-implementation.com"
/// * `ca_certificate` - Path to CA certificate used to authenticate the server
/// * `client_key` - Path to the client private key
/// * `client_certificate` - Path to the client's public certificate associated with the private key
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
pub fn try_new_https_mutual(
base_path: &str,
ca_certificate: CA,
client_key: K,
client_certificate: D,
) -> Result
where
CA: AsRef,
K: AsRef,
D: AsRef,
{
let https_connector = Connector::builder()
.https()
.pin_server_certificate(ca_certificate)
.client_authentication(client_key, client_certificate)
.build()
.map_err(ClientInitError::SslError)?;
Self::try_new_with_connector(base_path, Some("https"), https_connector)
}
}
impl Client where
S: Service<
(Request, C),
Response=Response> + Clone + Sync + Send + 'static,
S::Future: Send + 'static,
S::Error: Into + fmt::Display,
C: Clone + Send + Sync + 'static
{
/// Constructor for creating a `Client` by passing in a pre-made `hyper::service::Service` /
/// `tower::Service`
///
/// This allows adding custom wrappers around the underlying transport, for example for logging.
pub fn try_new_with_client_service(
client_service: S,
base_path: &str,
) -> Result
{
Ok(Self {
client_service,
base_path: into_base_path(base_path, None)?,
marker: PhantomData,
})
}
}
/// Error type failing to create a Client
#[derive(Debug)]
pub enum ClientInitError {
/// Invalid URL Scheme
InvalidScheme,
/// Invalid URI
InvalidUri(hyper::http::uri::InvalidUri),
/// Missing Hostname
MissingHost,
/// SSL Connection Error
#[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))]
SslError(native_tls::Error),
/// SSL Connection Error
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
SslError(openssl::error::ErrorStack),
}
impl From for ClientInitError {
fn from(err: hyper::http::uri::InvalidUri) -> ClientInitError {
ClientInitError::InvalidUri(err)
}
}
impl fmt::Display for ClientInitError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s: &dyn fmt::Debug = self;
s.fmt(f)
}
}
impl Error for ClientInitError {
fn description(&self) -> &str {
"Failed to produce a hyper client."
}
}
#[async_trait]
impl Api for Client where
S: Service<
(Request, C),
Response=Response> + Clone + Sync + Send + 'static,
S::Future: Send + 'static,
S::Error: Into + fmt::Display,
C: Has {{#hasAuthMethods}}+ Has>{{/hasAuthMethods}} + Clone + Send + Sync + 'static,
{
fn poll_ready(&self, cx: &mut Context) -> Poll> {
match self.client_service.clone().poll_ready(cx) {
Poll::Ready(Err(e)) => Poll::Ready(Err(e.into())),
Poll::Ready(Ok(o)) => Poll::Ready(Ok(o)),
Poll::Pending => Poll::Pending,
}
}
{{#apiInfo}}
{{#apis}}
{{#operations}}
{{#operation}}
{{>client-operation}}
{{/operation}}
{{/operations}}
{{/apis}}
{{/apiInfo}}
}