[Rust Server] Hyper 0.13 + Async/Await support (#6244)

* [Rust Server] Hyper 0.13 + Async/Await support

Upgrade dependencies to Hyper 0.13 and use async/await.

* [Rust Server] Add missing hyper-tls dependency

* [Rust Server] Add missing models import

* Update samples
This commit is contained in:
Richard Whitehouse 2020-05-29 15:18:30 +01:00 committed by GitHub
parent c3ac84cec2
commit 82410ae90b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
69 changed files with 10635 additions and 10802 deletions

View File

@ -27,7 +27,7 @@ client = [
"serde_ignored", "regex", "percent-encoding", "lazy_static", "serde_ignored", "regex", "percent-encoding", "lazy_static",
{{/hasCallbacks}} {{/hasCallbacks}}
{{! Anything added to the list below, should probably be added to the callbacks list below }} {{! Anything added to the list below, should probably be added to the callbacks list below }}
"hyper", "hyper-openssl", "native-tls", "openssl", "url" "hyper", "hyper-openssl", "hyper-tls", "native-tls", "openssl", "url"
] ]
server = [ server = [
{{#apiUsesMultipart}} {{#apiUsesMultipart}}
@ -40,7 +40,7 @@ server = [
"hyper_0_10", "mime_multipart", "hyper_0_10", "mime_multipart",
{{/apiUsesMultipartRelated}} {{/apiUsesMultipartRelated}}
{{#hasCallbacks}} {{#hasCallbacks}}
"native-tls", "hyper-openssl", "openssl", "native-tls", "hyper-openssl", "hyper-tls", "openssl",
{{/hasCallbacks}} {{/hasCallbacks}}
{{! Anything added to the list below, should probably be added to the callbacks list above }} {{! Anything added to the list below, should probably be added to the callbacks list above }}
"serde_ignored", "hyper", "regex", "percent-encoding", "url", "lazy_static" "serde_ignored", "hyper", "regex", "percent-encoding", "url", "lazy_static"
@ -49,20 +49,22 @@ conversion = ["frunk", "frunk_derives", "frunk_core", "frunk-enum-core", "frunk-
[target.'cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))'.dependencies] [target.'cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))'.dependencies]
native-tls = { version = "0.2", optional = true } native-tls = { version = "0.2", optional = true }
hyper-tls = { version = "0.4", optional = true }
[target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dependencies] [target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dependencies]
hyper-openssl = { version = "0.7.1", optional = true } hyper-openssl = { version = "0.8", optional = true }
openssl = {version = "0.10", optional = true } openssl = {version = "0.10", optional = true }
[dependencies] [dependencies]
# Common # Common
async-trait = "0.1.24"
chrono = { version = "0.4", features = ["serde"] } chrono = { version = "0.4", features = ["serde"] }
futures = "0.1" futures = "0.3"
swagger = "4.0" swagger = "5.0.0-alpha-1"
log = "0.4.0" log = "0.4.0"
mime = "0.3" mime = "0.3"
serde = { version = "1.0", features = ["derive"]} serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"
# Crates included if required by the API definition # Crates included if required by the API definition
@ -78,27 +80,27 @@ mime_0_2 = { package = "mime", version = "0.2.6", optional = true }
multipart = { version = "0.16", default-features = false, optional = true } multipart = { version = "0.16", default-features = false, optional = true }
{{/apiUsesMultipartFormData}} {{/apiUsesMultipartFormData}}
{{#apiUsesUuid}} {{#apiUsesUuid}}
uuid = {version = "0.7", features = ["serde", "v4"]} uuid = {version = "0.8", features = ["serde", "v4"]}
{{/apiUsesUuid}} {{/apiUsesUuid}}
# Common between server and client features # Common between server and client features
hyper = {version = "0.12", optional = true} hyper = {version = "0.13", optional = true}
{{#apiUsesMultipartRelated}} {{#apiUsesMultipartRelated}}
mime_multipart = {version = "0.5", optional = true} mime_multipart = {version = "0.5", optional = true}
hyper_0_10 = {package = "hyper", version = "0.10", default-features = false, optional=true} hyper_0_10 = {package = "hyper", version = "0.10", default-features = false, optional=true}
{{/apiUsesMultipartRelated}} {{/apiUsesMultipartRelated}}
serde_ignored = {version = "0.0.4", optional = true} serde_ignored = {version = "0.1.1", optional = true}
url = {version = "1.5", optional = true} url = {version = "2.1", optional = true}
# Client-specific # Client-specific
{{#usesUrlEncodedForm}} {{#usesUrlEncodedForm}}
serde_urlencoded = {version = "0.5.1", optional = true} serde_urlencoded = {version = "0.6.1", optional = true}
{{/usesUrlEncodedForm}} {{/usesUrlEncodedForm}}
# Server, and client callback-specific # Server, and client callback-specific
lazy_static = { version = "1.4", optional = true } lazy_static = { version = "1.4", optional = true }
percent-encoding = {version = "1.0.0", optional = true} percent-encoding = {version = "2.1.0", optional = true}
regex = {version = "0.2", optional = true} regex = {version = "1.3", optional = true}
# Conversion # Conversion
frunk = { version = "0.3.0", optional = true } frunk = { version = "0.3.0", optional = true }
@ -109,15 +111,13 @@ frunk-enum-core = { version = "0.2.0", optional = true }
[dev-dependencies] [dev-dependencies]
clap = "2.25" clap = "2.25"
error-chain = "0.12" env_logger = "0.7"
env_logger = "0.6" tokio = { version = "0.2", features = ["rt-threaded", "macros", "stream"] }
tokio = "0.1.17" native-tls = "0.2"
{{^apiUsesUuid}} tokio-tls = "0.3"
uuid = {version = "0.7", features = ["serde", "v4"]}
{{/apiUsesUuid}}
[target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dev-dependencies] [target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dev-dependencies]
tokio-openssl = "0.3" tokio-openssl = "0.4"
openssl = "0.10" openssl = "0.10"
[[example]] [[example]]

View File

@ -1,26 +1,23 @@
use futures; use async_trait::async_trait;
use futures::{Future, Stream, future, stream}; use futures::{Stream, future, future::BoxFuture, stream, future::TryFutureExt, future::FutureExt, stream::StreamExt};
use hyper;
use hyper::client::HttpConnector;
use hyper::header::{HeaderName, HeaderValue, CONTENT_TYPE}; use hyper::header::{HeaderName, HeaderValue, CONTENT_TYPE};
use hyper::{Body, Uri, Response}; use hyper::{Body, Request, Response, service::Service, Uri};
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] use percent_encoding::{utf8_percent_encode, AsciiSet};
use hyper_openssl::HttpsConnector;
use serde_json;
use std::borrow::Cow; use std::borrow::Cow;
use std::convert::TryInto; use std::convert::TryInto;
use std::io::{Read, Error, ErrorKind}; use std::io::{ErrorKind, Read};
use std::error; use std::error::Error;
use std::future::Future;
use std::fmt; use std::fmt;
use std::path::Path; use std::path::Path;
use std::sync::Arc; use std::sync::{Arc, Mutex};
use std::str; use std::str;
use std::str::FromStr; use std::str::FromStr;
use std::string::ToString; use std::string::ToString;
use swagger; use std::task::{Context, Poll};
use swagger::{ApiError, Connector, client::Service, XSpanIdString, Has, AuthData}; use swagger::{ApiError, AuthData, BodyExt, Connector, Has, XSpanIdString};
use url::form_urlencoded; use url::form_urlencoded;
use url::percent_encoding::{utf8_percent_encode, PATH_SEGMENT_ENCODE_SET, QUERY_ENCODE_SET};
{{#apiUsesMultipartFormData}} {{#apiUsesMultipartFormData}}
use mime::Mime; use mime::Mime;
use std::io::Cursor; use std::io::Cursor;
@ -30,20 +27,18 @@ use multipart::client::lazy::Multipart;
use hyper_0_10::header::{Headers, ContentType}; use hyper_0_10::header::{Headers, ContentType};
use mime_multipart::{Node, Part, generate_boundary, write_multipart}; use mime_multipart::{Node, Part, generate_boundary, write_multipart};
{{/apiUsesMultipartRelated}} {{/apiUsesMultipartRelated}}
{{#apiUsesUuid}}
use uuid;
{{/apiUsesUuid}}
{{#usesXml}}
use serde_xml_rs;
{{/usesXml}}
use crate::models; use crate::models;
use crate::header; use crate::header;
url::define_encode_set! { /// https://url.spec.whatwg.org/#fragment-percent-encode-set
/// This encode set is used for object IDs #[allow(dead_code)]
/// const FRAGMENT_ENCODE_SET: &AsciiSet = &percent_encoding::CONTROLS
/// Aside from the special characters defined in the `PATH_SEGMENT_ENCODE_SET`, .add(b' ').add(b'"').add(b'<').add(b'>').add(b'`');
/// the vertical bar (|) is encoded.
pub ID_ENCODE_SET = [PATH_SEGMENT_ENCODE_SET] | {'|'} /// This encode set is used for object IDs
} ///
/// Aside from the special characters defined in the `PATH_SEGMENT_ENCODE_SET`,
/// the vertical bar (|) is encoded.
#[allow(dead_code)]
const ID_ENCODE_SET: &AsciiSet = &FRAGMENT_ENCODE_SET.add(b'|');

View File

@ -8,11 +8,11 @@ pub mod callbacks;
{{/hasCallbacks}} {{/hasCallbacks}}
/// Convert input into a base path, e.g. "http://example:123". Also checks the scheme as it goes. /// Convert input into a base path, e.g. "http://example:123". Also checks the scheme as it goes.
fn into_base_path(input: &str, correct_scheme: Option<&'static str>) -> Result<String, ClientInitError> { fn into_base_path(input: impl TryInto<Uri, Error=hyper::http::uri::InvalidUri>, correct_scheme: Option<&'static str>) -> Result<String, ClientInitError> {
// First convert to Uri, since a base path is a subset of Uri. // First convert to Uri, since a base path is a subset of Uri.
let uri = Uri::from_str(input)?; let uri = input.try_into()?;
let scheme = uri.scheme_part().ok_or(ClientInitError::InvalidScheme)?; let scheme = uri.scheme_str().ok_or(ClientInitError::InvalidScheme)?;
// Check the scheme if necessary // Check the scheme if necessary
if let Some(correct_scheme) = correct_scheme { if let Some(correct_scheme) = correct_scheme {
@ -22,38 +22,54 @@ fn into_base_path(input: &str, correct_scheme: Option<&'static str>) -> Result<S
} }
let host = uri.host().ok_or_else(|| ClientInitError::MissingHost)?; let host = uri.host().ok_or_else(|| ClientInitError::MissingHost)?;
let port = uri.port_part().map(|x| format!(":{}", x)).unwrap_or_default(); let port = uri.port_u16().map(|x| format!(":{}", x)).unwrap_or_default();
Ok(format!("{}://{}{}{}", scheme, host, port, uri.path().trim_end_matches('/'))) Ok(format!("{}://{}{}{}", scheme, host, port, uri.path().trim_end_matches('/')))
} }
/// A client that implements the API by making HTTP calls out to a server. /// A client that implements the API by making HTTP calls out to a server.
pub struct Client<F> pub struct Client<S> where
S: Service<
Request<Body>,
Response=Response<Body>> + Clone + Sync + Send + 'static,
S::Future: Send + 'static,
S::Error: Into<crate::ServiceError> + fmt::Display,
{ {
/// Inner service /// Inner service
client_service: Arc<Box<dyn Service<ReqBody=Body, Future=F> + Send + Sync>>, client_service: S,
/// Base path of the API /// Base path of the API
base_path: String, base_path: String,
} }
impl<F> fmt::Debug for Client<F> impl<S> fmt::Debug for Client<S> where
S: Service<
Request<Body>,
Response=Response<Body>> + Clone + Sync + Send + 'static,
S::Future: Send + 'static,
S::Error: Into<crate::ServiceError> + fmt::Display,
{ {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Client {{ base_path: {} }}", self.base_path) write!(f, "Client {{ base_path: {} }}", self.base_path)
} }
} }
impl<F> Clone for Client<F> impl<S> Clone for Client<S> where
S: Service<
Request<Body>,
Response=Response<Body>> + Clone + Sync + Send + 'static,
S::Future: Send + 'static,
S::Error: Into<crate::ServiceError> + fmt::Display,
{ {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Client { Self {
client_service: self.client_service.clone(), client_service: self.client_service.clone(),
base_path: self.base_path.clone(), base_path: self.base_path.clone(),
} }
} }
} }
impl Client<hyper::client::ResponseFuture> impl<C> Client<hyper::client::Client<C, Body>> where
C: hyper::client::connect::Connect + Clone + Send + Sync + 'static
{ {
/// Create a client with a custom implementation of hyper::client::Connect. /// Create a client with a custom implementation of hyper::client::Connect.
/// ///
@ -66,30 +82,93 @@ impl Client<hyper::client::ResponseFuture>
/// ///
/// # Arguments /// # Arguments
/// ///
/// * `base_path` - base path of the client API, i.e. "www.my-api-implementation.com" /// * `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")` /// * `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 /// * `connector` - Implementation of `hyper::client::Connect` to use for the client
pub fn try_new_with_connector<C>( pub fn try_new_with_connector(
base_path: &str, base_path: &str,
protocol: Option<&'static str>, protocol: Option<&'static str>,
connector: C, connector: C,
) -> Result<Self, ClientInitError> where ) -> Result<Self, ClientInitError>
C: hyper::client::connect::Connect + 'static,
C::Transport: 'static,
C::Future: 'static,
{ {
let client_service = Box::new(hyper::client::Client::builder().build(connector)); let client_service = hyper::client::Client::builder().build(connector);
Ok(Client { Ok(Self {
client_service: Arc::new(client_service), client_service,
base_path: into_base_path(base_path, protocol)?, base_path: into_base_path(base_path, protocol)?,
}) })
} }
}
#[derive(Debug, Clone)]
pub enum HyperClient {
Http(hyper::client::Client<hyper::client::HttpConnector, Body>),
Https(hyper::client::Client<HttpsConnector, Body>),
}
impl Service<Request<Body>> for HyperClient {
type Response = Response<Body>;
type Error = hyper::Error;
type Future = hyper::client::ResponseFuture;
fn poll_ready(&mut self, cx: &mut Context) -> Poll<Result<(), Self::Error>> {
match self {
HyperClient::Http(client) => client.poll_ready(cx),
HyperClient::Https(client) => client.poll_ready(cx),
}
}
fn call(&mut self, req: Request<Body>) -> Self::Future {
match self {
HyperClient::Http(client) => client.call(req),
HyperClient::Https(client) => client.call(req)
}
}
}
impl Client<HyperClient> {
/// Create an HTTP client. /// Create an HTTP client.
/// ///
/// # Arguments /// # Arguments
/// * `base_path` - base path of the client API, i.e. "www.my-api-implementation.com" /// * `base_path` - base path of the client API, i.e. "http://www.my-api-implementation.com"
pub fn try_new(
base_path: &str,
) -> Result<Self, ClientInitError> {
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(|e| ClientInitError::SslError(e))?;
HyperClient::Https(hyper::client::Client::builder().build(connector))
},
_ => {
return Err(ClientInitError::InvalidScheme);
}
};
Ok(Self {
client_service,
base_path: into_base_path(base_path, None)?,
})
}
}
impl Client<hyper::client::Client<hyper::client::HttpConnector, Body>>
{
/// 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( pub fn try_new_http(
base_path: &str, base_path: &str,
) -> Result<Self, ClientInitError> { ) -> Result<Self, ClientInitError> {
@ -97,11 +176,20 @@ impl Client<hyper::client::ResponseFuture>
Self::try_new_with_connector(base_path, Some("http"), http_connector) 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<hyper::client::HttpConnector>;
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
type HttpsConnector = hyper_openssl::HttpsConnector<hyper::client::HttpConnector>;
impl Client<hyper::client::Client<HttpsConnector, Body>>
{
/// Create a client with a TLS connection to the server /// Create a client with a TLS connection to the server
/// ///
/// # Arguments /// # Arguments
/// * `base_path` - base path of the client API, i.e. "www.my-api-implementation.com" /// * `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<Self, ClientInitError> pub fn try_new_https(base_path: &str) -> Result<Self, ClientInitError>
{ {
let https_connector = Connector::builder() let https_connector = Connector::builder()
@ -114,7 +202,7 @@ impl Client<hyper::client::ResponseFuture>
/// Create a client with a TLS connection to the server using a pinned certificate /// Create a client with a TLS connection to the server using a pinned certificate
/// ///
/// # Arguments /// # Arguments
/// * `base_path` - base path of the client API, i.e. "www.my-api-implementation.com" /// * `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 /// * `ca_certificate` - Path to CA certificate used to authenticate the server
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
pub fn try_new_https_pinned<CA>( pub fn try_new_https_pinned<CA>(
@ -135,7 +223,7 @@ impl Client<hyper::client::ResponseFuture>
/// Create a client with a mutually authenticated TLS connection to the server. /// Create a client with a mutually authenticated TLS connection to the server.
/// ///
/// # Arguments /// # Arguments
/// * `base_path` - base path of the client API, i.e. "www.my-api-implementation.com" /// * `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 /// * `ca_certificate` - Path to CA certificate used to authenticate the server
/// * `client_key` - Path to the client private key /// * `client_key` - Path to the client private key
/// * `client_certificate` - Path to the client's public certificate associated with the private key /// * `client_certificate` - Path to the client's public certificate associated with the private key
@ -161,17 +249,24 @@ impl Client<hyper::client::ResponseFuture>
} }
} }
impl<F> Client<F> impl<S> Client<S> where
S: Service<
Request<Body>,
Response=Response<Body>> + Clone + Sync + Send + 'static,
S::Future: Send + 'static,
S::Error: Into<crate::ServiceError> + fmt::Display,
{ {
/// Constructor for creating a `Client` by passing in a pre-made `swagger::Service` /// 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. /// This allows adding custom wrappers around the underlying transport, for example for logging.
pub fn try_new_with_client_service( pub fn try_new_with_client_service(
client_service: Arc<Box<dyn Service<ReqBody=Body, Future=F> + Send + Sync>>, client_service: S,
base_path: &str, base_path: &str,
) -> Result<Self, ClientInitError> { ) -> Result<Self, ClientInitError>
Ok(Client { {
client_service: client_service, Ok(Self {
client_service,
base_path: into_base_path(base_path, None)?, base_path: into_base_path(base_path, None)?,
}) })
} }
@ -211,16 +306,29 @@ impl fmt::Display for ClientInitError {
} }
} }
impl error::Error for ClientInitError { impl Error for ClientInitError {
fn description(&self) -> &str { fn description(&self) -> &str {
"Failed to produce a hyper client." "Failed to produce a hyper client."
} }
} }
impl<C, F> Api<C> for Client<F> where #[async_trait]
C: Has<XSpanIdString> {{#hasAuthMethods}}+ Has<Option<AuthData>>{{/hasAuthMethods}}, impl<C, S> Api<C> for Client<S> where
F: Future<Item=Response<Body>, Error=hyper::Error> + Send + 'static C: Has<XSpanIdString> {{#hasAuthMethods}}+ Has<Option<AuthData>>{{/hasAuthMethods}} + Clone + Send + Sync + 'static,
S: Service<
Request<Body>,
Response=Response<Body>> + Clone + Sync + Send + 'static,
S::Future: Send + 'static,
S::Error: Into<crate::ServiceError> + fmt::Display,
{ {
fn poll_ready(&self, cx: &mut Context) -> Poll<Result<(), crate::ServiceError>> {
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}} {{#apiInfo}}
{{#apis}} {{#apis}}
{{#operations}} {{#operations}}

View File

@ -1,4 +1,4 @@
fn {{#vendorExtensions}}{{{operation_id}}}{{/vendorExtensions}}( async fn {{#vendorExtensions}}{{{operation_id}}}{{/vendorExtensions}}(
&self, &self,
{{#vendorExtensions}} {{#vendorExtensions}}
{{#callbackParams}} {{#callbackParams}}
@ -8,8 +8,9 @@
{{#allParams}} {{#allParams}}
param_{{{paramName}}}: {{^required}}Option<{{/required}}{{#isListContainer}}&{{/isListContainer}}{{{dataType}}}{{^required}}>{{/required}}, param_{{{paramName}}}: {{^required}}Option<{{/required}}{{#isListContainer}}&{{/isListContainer}}{{{dataType}}}{{^required}}>{{/required}},
{{/allParams}} {{/allParams}}
context: &C) -> Box<dyn Future<Item={{{operationId}}}Response, Error=ApiError> + Send> context: &C) -> Result<{{{operationId}}}Response, ApiError>
{ {
let mut client_service = self.client_service.clone();
let mut uri = format!( let mut uri = format!(
{{#isCallbackRequest}} {{#isCallbackRequest}}
"{{vendorExtensions.x-path-format-string}}" "{{vendorExtensions.x-path-format-string}}"
@ -29,105 +30,113 @@
); );
// Query parameters // Query parameters
let mut query_string = url::form_urlencoded::Serializer::new("".to_owned()); let query_string = {
let mut query_string = form_urlencoded::Serializer::new("".to_owned());
{{#queryParams}} {{#queryParams}}
{{^required}} {{^required}}
if let Some(param_{{{paramName}}}) = param_{{{paramName}}} { if let Some(param_{{{paramName}}}) = param_{{{paramName}}} {
{{/required}} {{/required}}
query_string.append_pair("{{{baseName}}}", &param_{{{paramName}}}{{#isListContainer}}.iter().map(ToString::to_string).collect::<Vec<String>>().join(","){{/isListContainer}}{{^isListContainer}}.to_string(){{/isListContainer}}); query_string.append_pair("{{{baseName}}}", &param_{{{paramName}}}{{#isListContainer}}.iter().map(ToString::to_string).collect::<Vec<String>>().join(","){{/isListContainer}}{{^isListContainer}}.to_string(){{/isListContainer}});
{{^required}} {{^required}}
} }
{{/required}} {{/required}}
{{/queryParams}} {{/queryParams}}
{{#authMethods}} {{#authMethods}}
{{#isApiKey}} {{#isApiKey}}
{{#isKeyInQuery}} {{#isKeyInQuery}}
if let Some(auth_data) = (context as &dyn Has<Option<AuthData>>).get().as_ref() { if let Some(auth_data) = (context as &dyn Has<Option<AuthData>>).get().as_ref() {
if let AuthData::ApiKey(ref api_key) = *auth_data { if let AuthData::ApiKey(ref api_key) = *auth_data {
query_string.append_pair("{{keyParamName}}", api_key); query_string.append_pair("{{keyParamName}}", api_key);
}
} }
}
{{/isKeyInQuery}} {{/isKeyInQuery}}
{{/isApiKey}} {{/isApiKey}}
{{/authMethods}} {{/authMethods}}
let query_string_str = query_string.finish(); query_string.finish()
if !query_string_str.is_empty() { };
if !query_string.is_empty() {
uri += "?"; uri += "?";
uri += &query_string_str; uri += &query_string;
} }
let uri = match Uri::from_str(&uri) { let uri = match Uri::from_str(&uri) {
Ok(uri) => uri, Ok(uri) => uri,
Err(err) => return Box::new(future::err(ApiError(format!("Unable to build URI: {}", err)))), Err(err) => return Err(ApiError(format!("Unable to build URI: {}", err))),
}; };
let mut request = match hyper::Request::builder() let mut request = match Request::builder()
.method("{{{vendorExtensions.HttpMethod}}}") .method("{{{vendorExtensions.HttpMethod}}}")
.uri(uri) .uri(uri)
.body(Body::empty()) { .body(Body::empty()) {
Ok(req) => req, Ok(req) => req,
Err(e) => return Box::new(future::err(ApiError(format!("Unable to create request: {}", e)))) Err(e) => return Err(ApiError(format!("Unable to create request: {}", e)))
}; };
{{#vendorExtensions}} {{#vendorExtensions}}
{{#consumesMultipart}} {{#consumesMultipart}}
let mut multipart = Multipart::new(); let (body_string, multipart_header) = {
let mut multipart = Multipart::new();
{{#vendorExtensions}} {{#vendorExtensions}}
{{#formParams}} {{#formParams}}
{{#-first}} {{#-first}}
// For each parameter, encode as appropriate and add to the multipart body as a stream. // For each parameter, encode as appropriate and add to the multipart body as a stream.
{{/-first}} {{/-first}}
{{^isByteArray}} {{^isByteArray}}
{{#jsonSchema}} {{#jsonSchema}}
let {{{paramName}}}_str = match serde_json::to_string(&param_{{{paramName}}}) { let {{{paramName}}}_str = match serde_json::to_string(&param_{{{paramName}}}) {
Ok(str) => str, Ok(str) => str,
Err(e) => return Box::new(future::err(ApiError(format!("Unable to parse {{{paramName}}} to string: {}", e)))), Err(e) => return Err(ApiError(format!("Unable to parse {{{paramName}}} to string: {}", e))),
}; };
let {{{paramName}}}_vec = {{{paramName}}}_str.as_bytes().to_vec(); let {{{paramName}}}_vec = {{{paramName}}}_str.as_bytes().to_vec();
let {{{paramName}}}_mime = mime_0_2::Mime::from_str("application/json").expect("impossible to fail to parse");
let {{{paramName}}}_cursor = Cursor::new({{{paramName}}}_vec);
let {{{paramName}}}_mime = mime_0_2::Mime::from_str("application/json").expect("impossible to fail to parse"); multipart.add_stream("{{{paramName}}}", {{{paramName}}}_cursor, None as Option<&str>, Some({{{paramName}}}_mime));
let {{{paramName}}}_cursor = Cursor::new({{{paramName}}}_vec);
multipart.add_stream("{{{paramName}}}", {{{paramName}}}_cursor, None as Option<&str>, Some({{{paramName}}}_mime));
{{/jsonSchema}} {{/jsonSchema}}
{{/isByteArray}} {{/isByteArray}}
{{#isByteArray}} {{#isByteArray}}
let {{{paramName}}}_vec = param_{{{paramName}}}.to_vec(); let {{{paramName}}}_vec = param_{{{paramName}}}.to_vec();
let {{{paramName}}}_mime = match mime_0_2::Mime::from_str("application/octet-stream") { let {{{paramName}}}_mime = match mime_0_2::Mime::from_str("application/octet-stream") {
Ok(mime) => mime, Ok(mime) => mime,
Err(err) => return Box::new(future::err(ApiError(format!("Unable to get mime type: {:?}", err)))), Err(err) => return Err(ApiError(format!("Unable to get mime type: {:?}", err))),
}; };
let {{{paramName}}}_cursor = Cursor::new({{{paramName}}}_vec); let {{{paramName}}}_cursor = Cursor::new({{{paramName}}}_vec);
let filename = None as Option<&str> ; let filename = None as Option<&str> ;
multipart.add_stream("{{{paramName}}}", {{{paramName}}}_cursor, filename, Some({{{paramName}}}_mime)); multipart.add_stream("{{{paramName}}}", {{{paramName}}}_cursor, filename, Some({{{paramName}}}_mime));
{{/isByteArray}} {{/isByteArray}}
{{/formParams}} {{/formParams}}
{{/vendorExtensions}} {{/vendorExtensions}}
let mut fields = match multipart.prepare() {
Ok(fields) => fields, let mut fields = match multipart.prepare() {
Err(err) => return Box::new(future::err(ApiError(format!("Unable to build request: {}", err)))), Ok(fields) => fields,
Err(err) => return Err(ApiError(format!("Unable to build request: {}", err))),
};
let mut body_string = String::new();
match fields.read_to_string(&mut body_string) {
Ok(_) => (),
Err(err) => return Err(ApiError(format!("Unable to build body: {}", err))),
}
let boundary = fields.boundary();
let multipart_header = format!("multipart/form-data;boundary={}", boundary);
(body_string, multipart_header)
}; };
let mut body_string = String::new();
match fields.read_to_string(&mut body_string) {
Ok(_) => (),
Err(err) => return Box::new(future::err(ApiError(format!("Unable to build body: {}", err)))),
}
let boundary = fields.boundary();
let multipart_header = format!("multipart/form-data;boundary={}", boundary);
*request.body_mut() = Body::from(body_string); *request.body_mut() = Body::from(body_string);
request.headers_mut().insert(CONTENT_TYPE, match HeaderValue::from_str(&multipart_header) { request.headers_mut().insert(CONTENT_TYPE, match HeaderValue::from_str(&multipart_header) {
Ok(h) => h, Ok(h) => h,
Err(e) => return Box::new(future::err(ApiError(format!("Unable to create header: {} - {}", multipart_header, e)))) Err(e) => return Err(ApiError(format!("Unable to create header: {} - {}", multipart_header, e)))
}); });
{{/consumesMultipart}} {{/consumesMultipart}}
@ -146,7 +155,7 @@
let header = "{{#consumes}}{{#-first}}{{{mediaType}}}{{/-first}}{{/consumes}}{{^consumes}}application/json{{/consumes}}"; let header = "{{#consumes}}{{#-first}}{{{mediaType}}}{{/-first}}{{/consumes}}{{^consumes}}application/json{{/consumes}}";
request.headers_mut().insert(CONTENT_TYPE, match HeaderValue::from_str(header) { request.headers_mut().insert(CONTENT_TYPE, match HeaderValue::from_str(header) {
Ok(h) => h, Ok(h) => h,
Err(e) => return Box::new(future::err(ApiError(format!("Unable to create header: {} - {}", header, e)))) Err(e) => return Err(ApiError(format!("Unable to create header: {} - {}", header, e)))
}); });
*request.body_mut() = Body::from(body.into_bytes()); *request.body_mut() = Body::from(body.into_bytes());
{{/-last}} {{/-last}}
@ -209,7 +218,7 @@
&[header.as_bytes(), "; boundary=".as_bytes(), &boundary, "; type=\"application/json\"".as_bytes()].concat() &[header.as_bytes(), "; boundary=".as_bytes(), &boundary, "; type=\"application/json\"".as_bytes()].concat()
) { ) {
Ok(h) => h, Ok(h) => h,
Err(e) => return Box::new(future::err(ApiError(format!("Unable to create header: {} - {}", header, e)))) Err(e) => return Err(ApiError(format!("Unable to create header: {} - {}", header, e)))
}); });
{{/-last}} {{/-last}}
@ -264,7 +273,7 @@
let header = "{{#consumes}}{{#-first}}{{{mediaType}}}{{/-first}}{{/consumes}}{{^consumes}}application/json{{/consumes}}"; let header = "{{#consumes}}{{#-first}}{{{mediaType}}}{{/-first}}{{/consumes}}{{^consumes}}application/json{{/consumes}}";
request.headers_mut().insert(CONTENT_TYPE, match HeaderValue::from_str(header) { request.headers_mut().insert(CONTENT_TYPE, match HeaderValue::from_str(header) {
Ok(h) => h, Ok(h) => h,
Err(e) => return Box::new(future::err(ApiError(format!("Unable to create header: {} - {}", header, e)))) Err(e) => return Err(ApiError(format!("Unable to create header: {} - {}", header, e)))
}); });
{{#-last}} {{#-last}}
@ -272,14 +281,14 @@
{{/bodyParam}} {{/bodyParam}}
{{/consumesMultipart}} {{/consumesMultipart}}
{{/vendorExtensions}} {{/vendorExtensions}}
let header = HeaderValue::from_str((context as &dyn Has<XSpanIdString>).get().0.clone().to_string().as_str()); let header = HeaderValue::from_str(Has::<XSpanIdString>::get(context).0.clone().to_string().as_str());
request.headers_mut().insert(HeaderName::from_static("x-span-id"), match header { request.headers_mut().insert(HeaderName::from_static("x-span-id"), match header {
Ok(h) => h, Ok(h) => h,
Err(e) => return Box::new(future::err(ApiError(format!("Unable to create X-Span ID header value: {}", e)))) Err(e) => return Err(ApiError(format!("Unable to create X-Span ID header value: {}", e)))
}); });
{{#hasAuthMethods}} {{#hasAuthMethods}}
if let Some(auth_data) = (context as &dyn Has<Option<AuthData>>).get().as_ref() { if let Some(auth_data) = Has::<Option<AuthData>>::get(context).as_ref() {
// Currently only authentication with Basic and Bearer are supported // Currently only authentication with Basic and Bearer are supported
match auth_data { match auth_data {
{{#authMethods}} {{#authMethods}}
@ -288,7 +297,7 @@
let auth = swagger::auth::Header(basic_header.clone()); let auth = swagger::auth::Header(basic_header.clone());
let header = match HeaderValue::from_str(&format!("{}", auth)) { let header = match HeaderValue::from_str(&format!("{}", auth)) {
Ok(h) => h, Ok(h) => h,
Err(e) => return Box::new(future::err(ApiError(format!("Unable to create Authorization header: {}", e)))) Err(e) => return Err(ApiError(format!("Unable to create Authorization header: {}", e)))
}; };
request.headers_mut().insert( request.headers_mut().insert(
hyper::header::AUTHORIZATION, hyper::header::AUTHORIZATION,
@ -300,7 +309,7 @@
let auth = swagger::auth::Header(bearer_header.clone()); let auth = swagger::auth::Header(bearer_header.clone());
let header = match HeaderValue::from_str(&format!("{}", auth)) { let header = match HeaderValue::from_str(&format!("{}", auth)) {
Ok(h) => h, Ok(h) => h,
Err(e) => return Box::new(future::err(ApiError(format!("Unable to create Authorization header: {}", e)))) Err(e) => return Err(ApiError(format!("Unable to create Authorization header: {}", e)))
}; };
request.headers_mut().insert( request.headers_mut().insert(
hyper::header::AUTHORIZATION, hyper::header::AUTHORIZATION,
@ -313,7 +322,7 @@
let auth = swagger::auth::Header(bearer_header.clone()); let auth = swagger::auth::Header(bearer_header.clone());
let header = match HeaderValue::from_str(&format!("{}", auth)) { let header = match HeaderValue::from_str(&format!("{}", auth)) {
Ok(h) => h, Ok(h) => h,
Err(e) => return Box::new(future::err(ApiError(format!("Unable to create Authorization header: {}", e)))) Err(e) => return Err(ApiError(format!("Unable to create Authorization header: {}", e)))
}; };
request.headers_mut().insert( request.headers_mut().insert(
hyper::header::AUTHORIZATION, hyper::header::AUTHORIZATION,
@ -341,8 +350,8 @@
match header::IntoHeaderValue(param_{{{paramName}}}.clone()).try_into() { match header::IntoHeaderValue(param_{{{paramName}}}.clone()).try_into() {
Ok(header) => header, Ok(header) => header,
Err(e) => { Err(e) => {
return Box::new(future::err(ApiError(format!( return Err(ApiError(format!(
"Invalid header {{{paramName}}} - {}", e)))) as Box<dyn Future<Item=_, Error=_> + Send>; "Invalid header {{{paramName}}} - {}", e)));
}, },
}); });
{{^required}} {{^required}}
@ -356,111 +365,102 @@
{{/isMapContainer}} {{/isMapContainer}}
{{/headerParams}} {{/headerParams}}
Box::new(self.client_service.request(request) let mut response = client_service.call(request)
.map_err(|e| ApiError(format!("No response received: {}", e))) .map_err(|e| ApiError(format!("No response received: {}", e))).await?;
.and_then(|mut response| {
match response.status().as_u16() { match response.status().as_u16() {
{{#responses}} {{#responses}}
{{{code}}} => { {{{code}}} => {
{{#headers}} {{#headers}}
let response_{{{name}}} = match response.headers().get(HeaderName::from_static("{{{nameInLowerCase}}}")) { let response_{{{name}}} = match response.headers().get(HeaderName::from_static("{{{nameInLowerCase}}}")) {
Some(response_{{{name}}}) => response_{{{name}}}.clone(), Some(response_{{{name}}}) => response_{{{name}}}.clone(),
None => return Box::new(future::err(ApiError(String::from("Required response header {{{baseName}}} for response {{{code}}} was not found.")))) as Box<dyn Future<Item=_, Error=_> + Send>, None => {
}; return Err(ApiError(String::from("Required response header {{{baseName}}} for response {{{code}}} was not found.")));
let response_{{{name}}} = match TryInto::<header::IntoHeaderValue<{{{dataType}}}>>::try_into(response_{{{name}}}) { }
Ok(value) => value, };
Err(e) => { let response_{{{name}}} = match TryInto::<header::IntoHeaderValue<{{{dataType}}}>>::try_into(response_{{{name}}}) {
return Box::new(future::err(ApiError(format!("Invalid response header {{baseName}} for response {{code}} - {}", e)))) as Box<dyn Future<Item=_, Error=_> + Send>; Ok(value) => value,
}, Err(e) => {
}; return Err(ApiError(format!("Invalid response header {{baseName}} for response {{code}} - {}", e)));
let response_{{{name}}} = response_{{{name}}}.0; },
};
let response_{{{name}}} = response_{{{name}}}.0;
{{/headers}} {{/headers}}
let body = response.into_body(); let body = response.into_body();
Box::new(
{{#dataType}} {{#dataType}}
body let body = body
.concat2() .to_raw()
.map_err(|e| ApiError(format!("Failed to read response: {}", e))) .map_err(|e| ApiError(format!("Failed to read response: {}", e))).await?;
.and_then(|body|
{{#vendorExtensions}} {{#vendorExtensions}}
{{#producesBytes}} {{#producesBytes}}
Ok(swagger::ByteArray(body.to_vec())) let body = swagger::ByteArray(body.to_vec());
{{/producesBytes}} {{/producesBytes}}
{{^producesBytes}} {{^producesBytes}}
str::from_utf8(&body) let body = str::from_utf8(&body)
.map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e))) .map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?;
.and_then(|body|
{{#producesXml}} {{#producesXml}}
// ToDo: this will move to swagger-rs and become a standard From conversion trait // ToDo: this will move to swagger-rs and become a standard From conversion trait
// once https://github.com/RReverser/serde-xml-rs/pull/45 is accepted upstream // once https://github.com/RReverser/serde-xml-rs/pull/45 is accepted upstream
serde_xml_rs::from_str::<{{{dataType}}}>(body) let body = serde_xml_rs::from_str::<{{{dataType}}}>(body)
.map_err(|e| ApiError(format!("Response body did not match the schema: {}", e))) .map_err(|e| ApiError(format!("Response body did not match the schema: {}", e)))?;
{{/producesXml}} {{/producesXml}}
{{#producesJson}} {{#producesJson}}
serde_json::from_str::<{{{dataType}}}>(body) let body = serde_json::from_str::<{{{dataType}}}>(body)?;
.map_err(|e| e.into())
{{/producesJson}} {{/producesJson}}
{{#producesPlainText}} {{#producesPlainText}}
Ok(body.to_string()) let body = body.to_string();
{{/producesPlainText}} {{/producesPlainText}}
)
{{/producesBytes}} {{/producesBytes}}
{{/vendorExtensions}} {{/vendorExtensions}}
) Ok({{{operationId}}}Response::{{#vendorExtensions}}{{x-responseId}}{{/vendorExtensions}}
.map(move |body| {
{{{operationId}}}Response::{{#vendorExtensions}}{{x-responseId}}{{/vendorExtensions}}
{{^headers}} {{^headers}}
(body) (body)
{{/headers}} {{/headers}}
{{#headers}} {{#headers}}
{{#-first}} {{#-first}}
{ {
body: body, body: body,
{{/-first}} {{/-first}}
{{{name}}}: response_{{name}}, {{{name}}}: response_{{name}},
{{#-last}} {{#-last}}
} }
{{/-last}} {{/-last}}
{{/headers}} {{/headers}}
}) )
{{/dataType}} {{/dataType}}
{{^dataType}} {{^dataType}}
future::ok( Ok(
{{{operationId}}}Response::{{#vendorExtensions}}{{x-responseId}}{{/vendorExtensions}} {{{operationId}}}Response::{{#vendorExtensions}}{{x-responseId}}{{/vendorExtensions}}
{{#headers}} {{#headers}}
{{#-first}} {{#-first}}
{ {
{{/-first}} {{/-first}}
{{{name}}}: response_{{name}}, {{{name}}}: response_{{name}},
{{#-last}} {{#-last}}
} }
{{/-last}} {{/-last}}
{{/headers}} {{/headers}}
) )
{{/dataType}} {{/dataType}}
) as Box<dyn Future<Item=_, Error=_> + Send>
},
{{/responses}}
code => {
let headers = response.headers().clone();
Box::new(response.into_body()
.take(100)
.concat2()
.then(move |body|
future::err(ApiError(format!("Unexpected response code {}:\n{:?}\n\n{}",
code,
headers,
match body {
Ok(ref body) => match str::from_utf8(body) {
Ok(body) => Cow::from(body),
Err(e) => Cow::from(format!("<Body was not UTF8: {:?}>", e)),
},
Err(e) => Cow::from(format!("<Failed to read body: {}>", e)),
})))
)
) as Box<dyn Future<Item=_, Error=_> + Send>
}
} }
})) {{/responses}}
code => {
let headers = response.headers().clone();
let body = response.into_body()
.take(100)
.to_raw().await;
Err(ApiError(format!("Unexpected response code {}:\n{:?}\n\n{}",
code,
headers,
match body {
Ok(body) => match String::from_utf8(body) {
Ok(body) => body,
Err(e) => format!("<Body was not UTF8: {:?}>", e),
},
Err(e) => format!("<Failed to read body: {}>", e),
}
)))
}
}
} }

View File

@ -1,13 +1,12 @@
use futures::Future; use futures::future::BoxFuture;
use hyper;
use hyper::header::HeaderName; use hyper::header::HeaderName;
use hyper::{Error, Request, Response, StatusCode, service::Service, body::Payload}; use hyper::{Error, Request, Response, StatusCode, service::Service};
use url::form_urlencoded; use url::form_urlencoded;
use std::default::Default; use std::default::Default;
use std::io; use std::io;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::task::{Poll, Context};
use swagger::auth::{AuthData, Authorization, Bearer, Scopes}; use swagger::auth::{AuthData, Authorization, Bearer, Scopes};
use swagger::context::ContextualPayload;
use swagger::{EmptyContext, Has, Pop, Push, XSpanIdString}; use swagger::{EmptyContext, Has, Pop, Push, XSpanIdString};
use crate::Api; use crate::Api;
@ -31,58 +30,52 @@ where
} }
// Make a service that adds context. // Make a service that adds context.
impl<'a, T, SC, A, B, C, D, E, ME, S, OB, F> hyper::service::MakeService<&'a SC> for impl<Target, T, A, B, C, D> Service<Target> for
MakeAddContext<T, A> MakeAddContext<T, A>
where where
A: Default + Push<XSpanIdString, Result = B>, Target: Send,
A: Default + Push<XSpanIdString, Result = B> + Send,
B: Push<Option<AuthData>, Result = C>, B: Push<Option<AuthData>, Result = C>,
C: Push<Option<Authorization>, Result = D>, C: Push<Option<Authorization>, Result = D>,
D: Send + 'static, D: Send + 'static,
T: hyper::service::MakeService< T: Service<Target> + Send,
&'a SC, T::Future: Send + 'static
Error = E,
MakeError = ME,
Service = S,
ReqBody = ContextualPayload<hyper::Body, D>,
ResBody = OB,
Future = F
>,
S: Service<
Error = E,
ReqBody = ContextualPayload<hyper::Body, D>,
ResBody = OB> + 'static,
ME: swagger::ErrorBound,
E: swagger::ErrorBound,
F: Future<Item=S, Error=ME> + Send + 'static,
S::Future: Send,
OB: Payload,
{ {
type ReqBody = hyper::Body; type Error = T::Error;
type ResBody = OB; type Response = AddContext<T::Response, A, B, C, D>;
type Error = E; type Future = BoxFuture<'static, Result<Self::Response, Self::Error>>;
type MakeError = ME;
type Service = AddContext<S, A>;
type Future = Box<dyn Future<Item = Self::Service, Error = ME> + Send + 'static>;
fn make_service(&mut self, ctx: &'a SC) -> Self::Future { fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Box::new(self.inner.make_service(ctx).map(|s| AddContext::new(s))) self.inner.poll_ready(cx)
}
fn call(&mut self, target: Target) -> Self::Future {
let service = self.inner.call(target);
Box::pin(async move {
Ok(AddContext::new(service.await?))
})
} }
} }
/// Middleware to extract authentication data from request /// Middleware to add context data from the request
pub struct AddContext<T, A> { pub struct AddContext<T, A, B, C, D>
where
A: Default + Push<XSpanIdString, Result = B>,
B: Push<Option<AuthData>, Result = C>,
C: Push<Option<Authorization>, Result = D>
{
inner: T, inner: T,
marker: PhantomData<A>, marker: PhantomData<A>,
} }
impl<T, A, B, C, D> AddContext<T, A> impl<T, A, B, C, D> AddContext<T, A, B, C, D>
where where
A: Default + Push<XSpanIdString, Result = B>, A: Default + Push<XSpanIdString, Result = B>,
B: Push<Option<AuthData>, Result = C>, B: Push<Option<AuthData>, Result = C>,
C: Push<Option<Authorization>, Result = D>, C: Push<Option<Authorization>, Result = D>,
T: Service,
{ {
pub fn new(inner: T) -> AddContext<T, A> { pub fn new(inner: T) -> Self {
AddContext { AddContext {
inner, inner,
marker: PhantomData, marker: PhantomData,
@ -90,24 +83,26 @@ where
} }
} }
impl<T, A, B, C, D> Service for AddContext<T, A> impl<T, A, B, C, D, ReqBody> Service<Request<ReqBody>> for AddContext<T, A, B, C, D>
where where
A: Default + Push<XSpanIdString, Result=B>, A: Default + Push<XSpanIdString, Result=B>,
B: Push<Option<AuthData>, Result=C>, B: Push<Option<AuthData>, Result=C>,
C: Push<Option<Authorization>, Result=D>, C: Push<Option<Authorization>, Result=D>,
D: Send + 'static, D: Send + 'static,
T: Service<ReqBody = ContextualPayload<hyper::Body, D>>, T: Service<(Request<ReqBody>, D)>
T::Future: Future<Item=Response<T::ResBody>, Error=T::Error> + Send + 'static
{ {
type ReqBody = hyper::Body;
type ResBody = T::ResBody;
type Error = T::Error; type Error = T::Error;
type Future = Box<dyn Future<Item=Response<T::ResBody>, Error=T::Error> + Send + 'static>; type Future = T::Future;
type Response = T::Response;
fn call(&mut self, req: Request<Self::ReqBody>) -> Self::Future { fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
let context = A::default().push(XSpanIdString::get_or_generate(&req)); self.inner.poll_ready(cx)
let (head, body) = req.into_parts(); }
let headers = head.headers.clone();
fn call(&mut self, request: Request<ReqBody>) -> Self::Future {
let context = A::default().push(XSpanIdString::get_or_generate(&request));
let headers = request.headers();
{{#authMethods}} {{#authMethods}}
{{#isBasic}} {{#isBasic}}
@ -119,12 +114,7 @@ impl<T, A, B, C, D> Service for AddContext<T, A>
let context = context.push(Some(auth_data)); let context = context.push(Some(auth_data));
let context = context.push(None::<Authorization>); let context = context.push(None::<Authorization>);
let body = ContextualPayload { return self.inner.call((request, context))
inner: body,
context: context,
};
return Box::new(self.inner.call(hyper::Request::from_parts(head, body)));
} }
} }
{{/isBasic}} {{/isBasic}}
@ -137,12 +127,7 @@ impl<T, A, B, C, D> Service for AddContext<T, A>
let context = context.push(Some(auth_data)); let context = context.push(Some(auth_data));
let context = context.push(None::<Authorization>); let context = context.push(None::<Authorization>);
let body = ContextualPayload { return self.inner.call((request, context))
inner: body,
context: context,
};
return Box::new(self.inner.call(hyper::Request::from_parts(head, body)));
} }
} }
{{/isOAuth}} {{/isOAuth}}
@ -156,18 +141,13 @@ impl<T, A, B, C, D> Service for AddContext<T, A>
let context = context.push(Some(auth_data)); let context = context.push(Some(auth_data));
let context = context.push(None::<Authorization>); let context = context.push(None::<Authorization>);
let body = ContextualPayload { return self.inner.call((request, context))
inner: body,
context: context,
};
return Box::new(self.inner.call(hyper::Request::from_parts(head, body)));
} }
} }
{{/isKeyInHeader}} {{/isKeyInHeader}}
{{#isKeyInQuery}} {{#isKeyInQuery}}
{ {
let key = form_urlencoded::parse(head.uri.query().unwrap_or_default().as_bytes()) let key = form_urlencoded::parse(request.uri().query().unwrap_or_default().as_bytes())
.filter(|e| e.0 == "api_key_query") .filter(|e| e.0 == "api_key_query")
.map(|e| e.1.clone().into_owned()) .map(|e| e.1.clone().into_owned())
.nth(0); .nth(0);
@ -176,11 +156,7 @@ impl<T, A, B, C, D> Service for AddContext<T, A>
let context = context.push(Some(auth_data)); let context = context.push(Some(auth_data));
let context = context.push(None::<Authorization>); let context = context.push(None::<Authorization>);
let body = ContextualPayload { return self.inner.call((request, context))
inner: body,
context: context,
};
return Box::new(self.inner.call(hyper::Request::from_parts(head, body)));
} }
} }
{{/isKeyInQuery}} {{/isKeyInQuery}}
@ -189,11 +165,7 @@ impl<T, A, B, C, D> Service for AddContext<T, A>
let context = context.push(None::<AuthData>); let context = context.push(None::<AuthData>);
let context = context.push(None::<Authorization>); let context = context.push(None::<Authorization>);
let body = ContextualPayload {
inner: body,
context: context,
};
Box::new(self.inner.call(hyper::Request::from_parts(head, body))) self.inner.call((request, context))
} }
} }

View File

@ -5,11 +5,18 @@ mod server;
{{/hasCallbacks}} {{/hasCallbacks}}
#[allow(unused_imports)] #[allow(unused_imports)]
use futures::{Future, future, Stream, stream}; use futures::{future, Stream, stream};
#[allow(unused_imports)] #[allow(unused_imports)]
use {{{externCrateName}}}::{Api, ApiNoContext, Client, ContextWrapperExt, models, use {{{externCrateName}}}::{Api, ApiNoContext, Client, ContextWrapperExt, models,
ApiError{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}, {{#apiInfo}}
{{{operationId}}}Response{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}} {{#apis}}
{{#operations}}
{{#operation}}
{{{operationId}}}Response,
{{/operation}}
{{/operations}}
{{/apis}}
{{/apiInfo}}
}; };
use clap::{App, Arg}; use clap::{App, Arg};
@ -18,7 +25,9 @@ use log::info;
// swagger::Has may be unused if there are no examples // swagger::Has may be unused if there are no examples
#[allow(unused_imports)] #[allow(unused_imports)]
use swagger::{ContextBuilder, EmptyContext, XSpanIdString, Has, Push, AuthData}; use swagger::{AuthData, ContextBuilder, EmptyContext, Has, Push, XSpanIdString};
type ClientContext = swagger::make_context_ty!(ContextBuilder, EmptyContext, Option<AuthData>, XSpanIdString);
// rt may be unused if there are no examples // rt may be unused if there are no examples
#[allow(unused_mut)] #[allow(unused_mut)]
@ -66,21 +75,21 @@ fn main() {
matches.value_of("host").unwrap(), matches.value_of("host").unwrap(),
matches.value_of("port").unwrap()); matches.value_of("port").unwrap());
let client = if matches.is_present("https") { let context: ClientContext =
// Using Simple HTTPS
Client::try_new_https(&base_url)
.expect("Failed to create HTTPS client")
} else {
// Using HTTP
Client::try_new_http(
&base_url)
.expect("Failed to create HTTP client")
};
let context: swagger::make_context_ty!(ContextBuilder, EmptyContext, Option<AuthData>, XSpanIdString) =
swagger::make_context!(ContextBuilder, EmptyContext, None as Option<AuthData>, XSpanIdString::default()); swagger::make_context!(ContextBuilder, EmptyContext, None as Option<AuthData>, XSpanIdString::default());
let client = client.with_context(context); let mut client : Box<dyn ApiNoContext<ClientContext>> = if matches.is_present("https") {
// Using Simple HTTPS
let client = Box::new(Client::try_new_https(&base_url)
.expect("Failed to create HTTPS client"));
Box::new(client.with_context(context))
} else {
// Using HTTP
let client = Box::new(Client::try_new_http(
&base_url)
.expect("Failed to create HTTP client"));
Box::new(client.with_context(context))
};
let mut rt = tokio::runtime::Runtime::new().unwrap(); let mut rt = tokio::runtime::Runtime::new().unwrap();
{{#hasCallbacks}} {{#hasCallbacks}}

View File

@ -1,5 +1,5 @@
{{>example-server-common}} {{>example-server-common}}
use {{{externCrateName}}}::{CallbackApi, ApiError}; use {{{externCrateName}}}::CallbackApi;
{{#apiInfo}} {{#apiInfo}}
{{#apis}} {{#apis}}
{{#operations}} {{#operations}}
@ -16,8 +16,12 @@ use {{{externCrateName}}}::{{{operationId}}}Response;
{{/apis}} {{/apis}}
{{/apiInfo}} {{/apiInfo}}
use {{{externCrateName}}}::client::callbacks::MakeService; use {{{externCrateName}}}::client::callbacks::MakeService;
use std::error::Error;
use swagger::ApiError;
impl<C> CallbackApi<C> for Server<C> where C: Has<XSpanIdString>{ #[async_trait]
impl<C> CallbackApi<C> for Server<C> where C: Has<XSpanIdString> + Send + Sync
{
{{#apiInfo}} {{#apiInfo}}
{{#apis}} {{#apis}}
{{#operations}} {{#operations}}

View File

@ -2,30 +2,22 @@
#![allow(unused_imports)] #![allow(unused_imports)]
mod errors { use async_trait::async_trait;
error_chain::error_chain!{} use futures::{future, Stream, StreamExt, TryFutureExt, TryStreamExt};
}
pub use self::errors::*;
use chrono;
use futures::{future, Future, Stream};
use hyper::server::conn::Http; use hyper::server::conn::Http;
use hyper::service::MakeService as _; use hyper::service::Service;
use log::info; use log::info;
use openssl::ssl::SslAcceptorBuilder; use openssl::ssl::SslAcceptorBuilder;
use std::future::Future;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::net::SocketAddr; use std::net::SocketAddr;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use swagger; use std::task::{Context, Poll};
use swagger::{Has, XSpanIdString}; use swagger::{Has, XSpanIdString};
use swagger::auth::MakeAllowAllAuthenticator; use swagger::auth::MakeAllowAllAuthenticator;
use swagger::EmptyContext; use swagger::EmptyContext;
use tokio::net::TcpListener; use tokio::net::TcpListener;
{{#apiUsesUuid}}use uuid;{{/apiUsesUuid}}
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
use tokio_openssl::SslAcceptorExt;
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod}; use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod};
@ -33,18 +25,18 @@ use {{{externCrateName}}}::models;
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
/// Builds an SSL implementation for Simple HTTPS from some hard-coded file names /// Builds an SSL implementation for Simple HTTPS from some hard-coded file names
pub fn create(addr: &str, https: bool) -> Box<dyn Future<Item = (), Error = ()> + Send> { pub async fn create(addr: &str, https: bool) {
let addr = addr.parse().expect("Failed to parse bind address"); let addr = addr.parse().expect("Failed to parse bind address");
let server = Server::new(); let server = Server::new();
let service_fn = MakeService::new(server); let service = MakeService::new(server);
let service_fn = MakeAllowAllAuthenticator::new(service_fn, "cosmo"); let service = MakeAllowAllAuthenticator::new(service, "cosmo");
let service_fn = let mut service =
{{{externCrateName}}}::server::context::MakeAddContext::<_, EmptyContext>::new( {{{externCrateName}}}::server::context::MakeAddContext::<_, EmptyContext>::new(
service_fn service
); );
if https { if https {
@ -62,32 +54,31 @@ pub fn create(addr: &str, https: bool) -> Box<dyn Future<Item = (), Error = ()>
ssl.set_certificate_chain_file("examples/server-chain.pem").expect("Failed to set cerificate chain"); ssl.set_certificate_chain_file("examples/server-chain.pem").expect("Failed to set cerificate chain");
ssl.check_private_key().expect("Failed to check private key"); ssl.check_private_key().expect("Failed to check private key");
let tls_acceptor = ssl.build(); let tls_acceptor = Arc::new(ssl.build());
let service_fn = Arc::new(Mutex::new(service_fn)); let mut tcp_listener = TcpListener::bind(&addr).await.unwrap();
let tls_listener = TcpListener::bind(&addr).unwrap().incoming().for_each(move |tcp| { let mut incoming = tcp_listener.incoming();
let addr = tcp.peer_addr().expect("Unable to get remote address");
let service_fn = service_fn.clone(); while let (Some(tcp), rest) = incoming.into_future().await {
if let Ok(tcp) = tcp {
let addr = tcp.peer_addr().expect("Unable to get remote address");
let service = service.call(addr);
let tls_acceptor = Arc::clone(&tls_acceptor);
hyper::rt::spawn(tls_acceptor.accept_async(tcp).map_err(|_| ()).and_then(move |tls| { tokio::spawn(async move {
let ms = { let tls = tokio_openssl::accept(&*tls_acceptor, tcp).await.map_err(|_| ())?;
let mut service_fn = service_fn.lock().unwrap();
service_fn.make_service(&addr)
};
ms.and_then(move |service| { let service = service.await.map_err(|_| ())?;
Http::new().serve_connection(tls, service)
}).map_err(|_| ())
}));
Ok(()) Http::new().serve_connection(tls, service).await.map_err(|_| ())
}).map_err(|_| ()); });
}
Box::new(tls_listener) incoming = rest;
}
} }
} else { } else {
// Using HTTP // Using HTTP
Box::new(hyper::server::Server::bind(&addr).serve(service_fn).map_err(|e| panic!("{:?}", e))) hyper::server::Server::bind(&addr).serve(service).await.unwrap()
} }
} }

View File

@ -9,7 +9,8 @@ mod server;
/// Create custom server, wire it to the autogenerated router, /// Create custom server, wire it to the autogenerated router,
/// and pass it to the web server. /// and pass it to the web server.
fn main() { #[tokio::main]
async fn main() {
env_logger::init(); env_logger::init();
let matches = App::new("server") let matches = App::new("server")
@ -20,5 +21,5 @@ fn main() {
let addr = "127.0.0.1:{{{serverPort}}}"; let addr = "127.0.0.1:{{{serverPort}}}";
hyper::rt::run(server::create(addr, matches.is_present("https"))); server::create(addr, matches.is_present("https")).await;
} }

View File

@ -1,7 +1,7 @@
{{#summary}} {{#summary}}
/// {{{summary}}} /// {{{summary}}}
{{/summary}} {{/summary}}
fn {{#vendorExtensions}}{{{operation_id}}}{{/vendorExtensions}}( async fn {{#vendorExtensions}}{{{operation_id}}}{{/vendorExtensions}}(
&self, &self,
{{#vendorExtensions}} {{#vendorExtensions}}
{{#callbackParams}} {{#callbackParams}}
@ -11,9 +11,9 @@
{{#allParams}} {{#allParams}}
{{{paramName}}}: {{^required}}Option<{{/required}}{{#isListContainer}}&{{/isListContainer}}{{{dataType}}}{{^required}}>{{/required}}, {{{paramName}}}: {{^required}}Option<{{/required}}{{#isListContainer}}&{{/isListContainer}}{{{dataType}}}{{^required}}>{{/required}},
{{/allParams}} {{/allParams}}
context: &C) -> Box<dyn Future<Item={{{operationId}}}Response, Error=ApiError> + Send> context: &C) -> Result<{{{operationId}}}Response, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("{{#vendorExtensions}}{{{operation_id}}}{{/vendorExtensions}}({{#allParams}}{{#vendorExtensions}}{{{formatString}}}{{/vendorExtensions}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) - X-Span-ID: {:?}"{{#allParams}}, {{{paramName}}}{{/allParams}}, context.get().0.clone()); info!("{{#vendorExtensions}}{{{operation_id}}}{{/vendorExtensions}}({{#allParams}}{{#vendorExtensions}}{{{formatString}}}{{/vendorExtensions}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) - X-Span-ID: {:?}"{{#allParams}}, {{{paramName}}}{{/allParams}}, context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }

View File

@ -2,7 +2,6 @@
use {{{externCrateName}}}::{ use {{{externCrateName}}}::{
Api, Api,
ApiError,
{{#apiInfo}} {{#apiInfo}}
{{#apis}} {{#apis}}
{{#operations}} {{#operations}}
@ -14,8 +13,12 @@ use {{{externCrateName}}}::{
{{/apiInfo}} {{/apiInfo}}
}; };
use {{{externCrateName}}}::server::MakeService; use {{{externCrateName}}}::server::MakeService;
use std::error::Error;
use swagger::ApiError;
impl<C> Api<C> for Server<C> where C: Has<XSpanIdString>{ #[async_trait]
impl<C> Api<C> for Server<C> where C: Has<XSpanIdString> + Send + Sync
{
{{#apiInfo}} {{#apiInfo}}
{{#apis}} {{#apis}}
{{#operations}} {{#operations}}

View File

@ -1,12 +1,12 @@
#![allow(missing_docs, trivial_casts, unused_variables, unused_mut, unused_imports, unused_extern_crates, non_camel_case_types)] #![allow(missing_docs, trivial_casts, unused_variables, unused_mut, unused_imports, unused_extern_crates, non_camel_case_types)]
use async_trait::async_trait;
use futures::Stream; use futures::Stream;
use std::io::Error; use std::error::Error;
use std::task::{Poll, Context};
use swagger::{ApiError, ContextWrapper};
#[deprecated(note = "Import swagger-rs directly")] type ServiceError = Box<dyn Error + Send + Sync + 'static>;
pub use swagger::{ApiError, ContextWrapper};
#[deprecated(note = "Import futures directly")]
pub use futures::Future;
pub const BASE_PATH: &'static str = "{{{basePathWithoutHost}}}"; pub const BASE_PATH: &'static str = "{{{basePathWithoutHost}}}";
{{#appVersion}} {{#appVersion}}
@ -23,7 +23,12 @@ pub const API_VERSION: &'static str = "{{{appVersion}}}";
{{/apis}} {{/apis}}
{{/apiInfo}} {{/apiInfo}}
/// API /// API
pub trait Api<C> { #[async_trait]
pub trait Api<C: Send + Sync> {
fn poll_ready(&self, _cx: &mut Context) -> Poll<Result<(), Box<dyn Error + Send + Sync + 'static>>> {
Poll::Ready(Ok(()))
}
{{#apiInfo}} {{#apiInfo}}
{{#apis}} {{#apis}}
{{#operations}} {{#operations}}
@ -31,12 +36,12 @@ pub trait Api<C> {
{{#summary}} {{#summary}}
/// {{{summary}}} /// {{{summary}}}
{{/summary}} {{/summary}}
fn {{#vendorExtensions}}{{{operation_id}}}{{/vendorExtensions}}( async fn {{#vendorExtensions}}{{{operation_id}}}{{/vendorExtensions}}(
&self, &self,
{{#allParams}} {{#allParams}}
{{{paramName}}}: {{^required}}Option<{{/required}}{{#isListContainer}}&{{/isListContainer}}{{{dataType}}}{{^required}}>{{/required}}, {{{paramName}}}: {{^required}}Option<{{/required}}{{#isListContainer}}&{{/isListContainer}}{{{dataType}}}{{^required}}>{{/required}},
{{/allParams}} {{/allParams}}
context: &C) -> Box<dyn Future<Item={{{operationId}}}Response, Error=ApiError> + Send>; context: &C) -> Result<{{{operationId}}}Response, ApiError>;
{{/operation}} {{/operation}}
{{/operations}} {{/operations}}
@ -44,8 +49,14 @@ pub trait Api<C> {
{{/apiInfo}} {{/apiInfo}}
} }
/// API without a `Context` /// API where `Context` isn't passed on every API call
pub trait ApiNoContext { #[async_trait]
pub trait ApiNoContext<C: Send + Sync> {
fn poll_ready(&self, _cx: &mut Context) -> Poll<Result<(), Box<dyn Error + Send + Sync + 'static>>>;
fn context(&self) -> &C;
{{#apiInfo}} {{#apiInfo}}
{{#apis}} {{#apis}}
{{#operations}} {{#operations}}
@ -53,12 +64,12 @@ pub trait ApiNoContext {
{{#summary}} {{#summary}}
/// {{{summary}}} /// {{{summary}}}
{{/summary}} {{/summary}}
fn {{#vendorExtensions}}{{{operation_id}}}{{/vendorExtensions}}( async fn {{#vendorExtensions}}{{{operation_id}}}{{/vendorExtensions}}(
&self, &self,
{{#allParams}} {{#allParams}}
{{{paramName}}}: {{^required}}Option<{{/required}}{{#isListContainer}}&{{/isListContainer}}{{{dataType}}}{{^required}}>{{/required}}, {{{paramName}}}: {{^required}}Option<{{/required}}{{#isListContainer}}&{{/isListContainer}}{{{dataType}}}{{^required}}>{{/required}},
{{/allParams}} {{/allParams}}
) -> Box<dyn Future<Item={{{operationId}}}Response, Error=ApiError> + Send>; ) -> Result<{{{operationId}}}Response, ApiError>;
{{/operation}} {{/operation}}
{{/operations}} {{/operations}}
@ -67,18 +78,28 @@ pub trait ApiNoContext {
} }
/// Trait to extend an API to make it easy to bind it to a context. /// Trait to extend an API to make it easy to bind it to a context.
pub trait ContextWrapperExt<'a, C> where Self: Sized { pub trait ContextWrapperExt<C: Send + Sync> where Self: Sized
{
/// Binds this API to a context. /// Binds this API to a context.
fn with_context(self: &'a Self, context: C) -> ContextWrapper<'a, Self, C>; fn with_context(self: Self, context: C) -> ContextWrapper<Self, C>;
} }
impl<'a, T: Api<C> + Sized, C> ContextWrapperExt<'a, C> for T { impl<T: Api<C> + Send + Sync, C: Clone + Send + Sync> ContextWrapperExt<C> for T {
fn with_context(self: &'a T, context: C) -> ContextWrapper<'a, T, C> { fn with_context(self: T, context: C) -> ContextWrapper<T, C> {
ContextWrapper::<T, C>::new(self, context) ContextWrapper::<T, C>::new(self, context)
} }
} }
impl<'a, T: Api<C>, C> ApiNoContext for ContextWrapper<'a, T, C> { #[async_trait]
impl<T: Api<C> + Send + Sync, C: Clone + Send + Sync> ApiNoContext<C> for ContextWrapper<T, C> {
fn poll_ready(&self, cx: &mut Context) -> Poll<Result<(), ServiceError>> {
self.api().poll_ready(cx)
}
fn context(&self) -> &C {
ContextWrapper::context(self)
}
{{#apiInfo}} {{#apiInfo}}
{{#apis}} {{#apis}}
{{#operations}} {{#operations}}
@ -86,14 +107,15 @@ impl<'a, T: Api<C>, C> ApiNoContext for ContextWrapper<'a, T, C> {
{{#summary}} {{#summary}}
/// {{{summary}}} /// {{{summary}}}
{{/summary}} {{/summary}}
fn {{#vendorExtensions}}{{{operation_id}}}{{/vendorExtensions}}( async fn {{#vendorExtensions}}{{{operation_id}}}{{/vendorExtensions}}(
&self, &self,
{{#allParams}} {{#allParams}}
{{{paramName}}}: {{^required}}Option<{{/required}}{{#isListContainer}}&{{/isListContainer}}{{{dataType}}}{{^required}}>{{/required}}, {{{paramName}}}: {{^required}}Option<{{/required}}{{#isListContainer}}&{{/isListContainer}}{{{dataType}}}{{^required}}>{{/required}},
{{/allParams}} {{/allParams}}
) -> Box<dyn Future<Item={{{operationId}}}Response, Error=ApiError> + Send> ) -> Result<{{{operationId}}}Response, ApiError>
{ {
self.api().{{#vendorExtensions}}{{{operation_id}}}{{/vendorExtensions}}({{#allParams}}{{{paramName}}}, {{/allParams}}&self.context()) let context = self.context().clone();
self.api().{{#vendorExtensions}}{{{operation_id}}}{{/vendorExtensions}}({{#allParams}}{{{paramName}}}, {{/allParams}}&context).await
} }
{{/operation}} {{/operation}}
@ -101,6 +123,7 @@ impl<'a, T: Api<C>, C> ApiNoContext for ContextWrapper<'a, T, C> {
{{/apis}} {{/apis}}
{{/apiInfo}} {{/apiInfo}}
} }
{{#hasCallbacks}} {{#hasCallbacks}}
{{#apiInfo}} {{#apiInfo}}
@ -120,7 +143,12 @@ impl<'a, T: Api<C>, C> ApiNoContext for ContextWrapper<'a, T, C> {
{{/apiInfo}} {{/apiInfo}}
/// Callback API /// Callback API
pub trait CallbackApi<C> { #[async_trait]
pub trait CallbackApi<C: Send + Sync> {
fn poll_ready(&self, _cx: &mut Context) -> Poll<Result<(), Box<dyn Error + Send + Sync + 'static>>> {
Poll::Ready(Ok(()))
}
{{#apiInfo}} {{#apiInfo}}
{{#apis}} {{#apis}}
{{#operations}} {{#operations}}
@ -131,7 +159,7 @@ pub trait CallbackApi<C> {
{{#summary}} {{#summary}}
/// {{{summary}}} /// {{{summary}}}
{{/summary}} {{/summary}}
fn {{#vendorExtensions}}{{{operation_id}}}{{/vendorExtensions}}( async fn {{#vendorExtensions}}{{{operation_id}}}{{/vendorExtensions}}(
&self, &self,
{{#vendorExtensions}} {{#vendorExtensions}}
{{#callbackParams}} {{#callbackParams}}
@ -141,7 +169,7 @@ pub trait CallbackApi<C> {
{{#allParams}} {{#allParams}}
{{{paramName}}}: {{^required}}Option<{{/required}}{{#isListContainer}}&{{/isListContainer}}{{{dataType}}}{{^required}}>{{/required}}, {{{paramName}}}: {{^required}}Option<{{/required}}{{#isListContainer}}&{{/isListContainer}}{{{dataType}}}{{^required}}>{{/required}},
{{/allParams}} {{/allParams}}
context: &C) -> Box<dyn Future<Item={{{operationId}}}Response, Error=ApiError> + Send>; context: &C) -> Result<{{{operationId}}}Response, ApiError>;
{{/requests}} {{/requests}}
{{/urls}} {{/urls}}
@ -153,7 +181,12 @@ pub trait CallbackApi<C> {
} }
/// Callback API without a `Context` /// Callback API without a `Context`
pub trait CallbackApiNoContext { #[async_trait]
pub trait CallbackApiNoContext<C: Send + Sync> {
fn poll_ready(&self, _cx: &mut Context) -> Poll<Result<(), Box<dyn Error + Send + Sync + 'static>>>;
fn context(&self) -> &C;
{{#apiInfo}} {{#apiInfo}}
{{#apis}} {{#apis}}
{{#operations}} {{#operations}}
@ -164,7 +197,7 @@ pub trait CallbackApiNoContext {
{{#summary}} {{#summary}}
/// {{{summary}}} /// {{{summary}}}
{{/summary}} {{/summary}}
fn {{#vendorExtensions}}{{{operation_id}}}{{/vendorExtensions}}( async fn {{#vendorExtensions}}{{{operation_id}}}{{/vendorExtensions}}(
&self, &self,
{{#vendorExtensions}} {{#vendorExtensions}}
{{#callbackParams}} {{#callbackParams}}
@ -174,7 +207,7 @@ pub trait CallbackApiNoContext {
{{#allParams}} {{#allParams}}
{{{paramName}}}: {{^required}}Option<{{/required}}{{#isListContainer}}&{{/isListContainer}}{{{dataType}}}{{^required}}>{{/required}}, {{{paramName}}}: {{^required}}Option<{{/required}}{{#isListContainer}}&{{/isListContainer}}{{{dataType}}}{{^required}}>{{/required}},
{{/allParams}} {{/allParams}}
) -> Box<dyn Future<Item={{{operationId}}}Response, Error=ApiError> + Send>; ) -> Result<{{{operationId}}}Response, ApiError>;
{{/requests}} {{/requests}}
{{/urls}} {{/urls}}
@ -185,19 +218,28 @@ pub trait CallbackApiNoContext {
{{/apiInfo}} {{/apiInfo}}
} }
/// Trait to extend an API to make it easy to bind it to a context. pub trait CallbackContextWrapperExt<C: Send + Sync> where Self: Sized
pub trait CallbackContextWrapperExt<'a, C> where Self: Sized { {
/// Binds this API to a context. /// Binds this API to a context.
fn with_context(self: &'a Self, context: C) -> ContextWrapper<'a, Self, C>; fn with_context(self: Self, context: C) -> ContextWrapper<Self, C>;
} }
impl<'a, T: CallbackApi<C> + Sized, C> CallbackContextWrapperExt<'a, C> for T { impl<T: CallbackApi<C> + Send + Sync, C: Clone + Send + Sync> CallbackContextWrapperExt<C> for T {
fn with_context(self: &'a T, context: C) -> ContextWrapper<'a, T, C> { fn with_context(self: T, context: C) -> ContextWrapper<T, C> {
ContextWrapper::<T, C>::new(self, context) ContextWrapper::<T, C>::new(self, context)
} }
} }
impl<'a, T: CallbackApi<C>, C> CallbackApiNoContext for ContextWrapper<'a, T, C> { #[async_trait]
impl<T: CallbackApi<C> + Send + Sync, C: Clone + Send + Sync> CallbackApiNoContext<C> for ContextWrapper<T, C> {
fn poll_ready(&self, cx: &mut Context) -> Poll<Result<(), ServiceError>> {
self.api().poll_ready(cx)
}
fn context(&self) -> &C {
ContextWrapper::context(self)
}
{{#apiInfo}} {{#apiInfo}}
{{#apis}} {{#apis}}
{{#operations}} {{#operations}}
@ -208,7 +250,7 @@ impl<'a, T: CallbackApi<C>, C> CallbackApiNoContext for ContextWrapper<'a, T, C>
{{#summary}} {{#summary}}
/// {{{summary}}} /// {{{summary}}}
{{/summary}} {{/summary}}
fn {{#vendorExtensions}}{{{operation_id}}}{{/vendorExtensions}}( async fn {{#vendorExtensions}}{{{operation_id}}}{{/vendorExtensions}}(
&self, &self,
{{#vendorExtensions}} {{#vendorExtensions}}
{{#callbackParams}} {{#callbackParams}}
@ -218,8 +260,9 @@ impl<'a, T: CallbackApi<C>, C> CallbackApiNoContext for ContextWrapper<'a, T, C>
{{#allParams}} {{#allParams}}
{{{paramName}}}: {{^required}}Option<{{/required}}{{#isListContainer}}&{{/isListContainer}}{{{dataType}}}{{^required}}>{{/required}}, {{{paramName}}}: {{^required}}Option<{{/required}}{{#isListContainer}}&{{/isListContainer}}{{{dataType}}}{{^required}}>{{/required}},
{{/allParams}} {{/allParams}}
) -> Box<dyn Future<Item={{{operationId}}}Response, Error=ApiError> + Send> ) -> Result<{{{operationId}}}Response, ApiError>
{ {
let context = self.context().clone();
self.api().{{#vendorExtensions}}{{{operation_id}}}{{/vendorExtensions}}( self.api().{{#vendorExtensions}}{{{operation_id}}}{{/vendorExtensions}}(
{{#vendorExtensions}} {{#vendorExtensions}}
{{#callbackParams}} {{#callbackParams}}
@ -229,7 +272,7 @@ impl<'a, T: CallbackApi<C>, C> CallbackApiNoContext for ContextWrapper<'a, T, C>
{{#allParams}} {{#allParams}}
{{{paramName}}}, {{{paramName}}},
{{/allParams}} {{/allParams}}
&self.context()) &context).await
} }
{{/requests}} {{/requests}}

View File

@ -17,29 +17,45 @@ use crate::{{{operationId}}}Response;
{{/apiInfo}} {{/apiInfo}}
/// A client that implements the API by making HTTP calls out to a server. /// A client that implements the API by making HTTP calls out to a server.
pub struct Client<F> pub struct Client<S> where
S: Service<
Request<Body>,
Response=Response<Body>,
Error=hyper::Error> + Clone + Send + Sync,
S::Future: Send + 'static,
{ {
/// Inner service /// Inner service
client_service: Arc<Box<dyn Service<ReqBody=Body, Future=F> + Send + Sync>>, client_service: S,
} }
impl<F> fmt::Debug for Client<F> impl<S> fmt::Debug for Client<S> where
S: Service<
Request<Body>,
Response=Response<Body>,
Error=hyper::Error> + Clone + Send + Sync,
S::Future: Send + 'static,
{ {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Client") write!(f, "Client")
} }
} }
impl<F> Clone for Client<F> impl<S> Clone for Client<S> where
S: Service<
Request<Body>,
Response=Response<Body>,
Error=hyper::Error> + Clone + Send + Sync,
S::Future: Send + 'static,
{ {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Client { Self {
client_service: self.client_service.clone(), client_service: self.client_service.clone(),
} }
} }
} }
impl Client<hyper::client::ResponseFuture> impl<C> Client<hyper::client::Client<C, Body>> where
C: hyper::client::connect::Connect + Clone + Send + Sync + 'static
{ {
/// Create a client with a custom implementation of hyper::client::Connect. /// Create a client with a custom implementation of hyper::client::Connect.
/// ///
@ -53,26 +69,33 @@ impl Client<hyper::client::ResponseFuture>
/// # Arguments /// # Arguments
/// ///
/// * `connector` - Implementation of `hyper::client::Connect` to use for the client /// * `connector` - Implementation of `hyper::client::Connect` to use for the client
pub fn new_with_connector<C>( pub fn new_with_connector(connector: C) -> Self
connector: C,
) -> Self where
C: hyper::client::connect::Connect + 'static,
C::Transport: 'static,
C::Future: 'static,
{ {
let client_service = Box::new(hyper::client::Client::builder().build(connector)); let client_service = hyper::client::Client::builder().build(connector);
Client { Self {
client_service: Arc::new(client_service), client_service,
} }
} }
}
impl Client<hyper::client::Client<hyper::client::HttpConnector, Body>>
{
/// Create an HTTP client. /// Create an HTTP client.
pub fn new_http() -> Self { pub fn new_http() -> Self {
let http_connector = Connector::builder().build(); let http_connector = Connector::builder().build();
Self::new_with_connector(http_connector) Self::new_with_connector(http_connector)
} }
}
#[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))]
type HttpConnector = hyper_tls::HttpsConnector<hyper::client::HttpConnector>;
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
type HttpsConnector = hyper_openssl::HttpsConnector<hyper::client::HttpConnector>;
impl Client<hyper::client::Client<HttpsConnector, Body>>
{
/// Create a client with a TLS connection to the server. /// Create a client with a TLS connection to the server.
#[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))] #[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))]
pub fn new_https() -> Result<Self, native_tls::Error> pub fn new_https() -> Result<Self, native_tls::Error>
@ -132,24 +155,43 @@ impl Client<hyper::client::ResponseFuture>
} }
} }
impl<F> Client<F> impl<S> Client<S> where
S: Service<
Request<Body>,
Response=Response<Body>,
Error=hyper::Error> + Clone + Send + Sync,
S::Future: Send + 'static,
{ {
/// Constructor for creating a `Client` by passing in a pre-made `swagger::Service` /// Constructor for creating a `Client` by passing in a pre-made `swagger::Service`
/// ///
/// This allows adding custom wrappers around the underlying transport, for example for logging. /// This allows adding custom wrappers around the underlying transport, for example for logging.
pub fn new_with_client_service( pub fn new_with_client_service(
client_service: Arc<Box<dyn Service<ReqBody=Body, Future=F> + Send + Sync>>, client_service: S,
) -> Self { ) -> Self {
Client { Client {
client_service: client_service, client_service,
} }
} }
} }
impl<C, F> CallbackApi<C> for Client<F> where #[async_trait]
C: Has<XSpanIdString> {{#hasAuthMethods}}+ Has<Option<AuthData>>{{/hasAuthMethods}}, impl<C, S> CallbackApi<C> for Client<S> where
F: Future<Item=Response<Body>, Error=hyper::Error> + Send + 'static C: Has<XSpanIdString> {{#hasAuthMethods}}+ Has<Option<AuthData>>{{/hasAuthMethods}} + Send + Sync,
S: Service<
Request<Body>,
Response=Response<Body>,
Error=hyper::Error> + Clone + Send + Sync,
S::Future: Send + 'static,
S::Error: Into<crate::ServiceError> + fmt::Display,
{ {
fn poll_ready(&self, cx: &mut Context) -> Poll<Result<(), crate::ServiceError>> {
match self.client_service.clone().poll_ready(cx) {
Poll::Ready(Err(e)) => Poll::Ready(Err(Box::new(e))),
Poll::Ready(Ok(o)) => Poll::Ready(Ok(o)),
Poll::Pending => Poll::Pending,
}
}
{{#apiInfo}} {{#apiInfo}}
{{#apis}} {{#apis}}
{{#operations}} {{#operations}}

View File

@ -1,38 +1,31 @@
use std::marker::PhantomData; use futures::{future, future::BoxFuture, Stream, stream, future::FutureExt, stream::TryStreamExt};
use futures::{Future, future, Stream, stream}; use hyper::{Request, Response, StatusCode, Body, HeaderMap};
use hyper;
use hyper::{Request, Response, Error, StatusCode, Body, HeaderMap};
use hyper::header::{HeaderName, HeaderValue, CONTENT_TYPE}; use hyper::header::{HeaderName, HeaderValue, CONTENT_TYPE};
use log::warn; use log::warn;
use serde_json;
#[allow(unused_imports)] #[allow(unused_imports)]
use std::convert::{TryFrom, TryInto}; use std::convert::{TryFrom, TryInto};
use std::io; use std::error::Error;
use url::form_urlencoded; use std::future::Future;
#[allow(unused_imports)] use std::marker::PhantomData;
use swagger; use std::task::{Context, Poll};
use swagger::{ApiError, XSpanIdString, Has, RequestParser}; use swagger::{ApiError, BodyExt, Has, RequestParser, XSpanIdString};
pub use swagger::auth::Authorization; pub use swagger::auth::Authorization;
use swagger::auth::Scopes; use swagger::auth::Scopes;
use swagger::context::ContextualPayload; use url::form_urlencoded;
{{#apiUsesMultipartRelated}} {{#apiUsesMultipartRelated}}
use hyper_0_10::header::{Headers, ContentType}; use hyper_0_10::header::{Headers, ContentType};
use mime_0_2::{TopLevel, SubLevel, Mime as Mime2}; use mime_0_2::{TopLevel, SubLevel, Mime as Mime2};
use mime_multipart::{read_multipart_body, Node, Part}; use mime_multipart::{read_multipart_body, Node, Part};
{{/apiUsesMultipartRelated}} {{/apiUsesMultipartRelated}}
{{#apiUsesUuid}}
use uuid;
{{/apiUsesUuid}}
{{#apiUsesMultipartFormData}} {{#apiUsesMultipartFormData}}
use multipart::server::Multipart; use multipart::server::Multipart;
use multipart::server::save::SaveResult; use multipart::server::save::SaveResult;
{{/apiUsesMultipartFormData}} {{/apiUsesMultipartFormData}}
{{#usesXml}}
use serde_xml_rs;
{{/usesXml}}
#[allow(unused_imports)] #[allow(unused_imports)]
use crate::models; use crate::models;
use crate::header; use crate::header;
pub use crate::context; pub use crate::context;
type ServiceFuture = BoxFuture<'static, Result<Response<Body>, crate::ServiceError>>;

View File

@ -1,12 +1,14 @@
pub struct MakeService<T, RC> { pub struct MakeService<T, C> where
T: Api<C> + Clone + Send + 'static,
C: Has<XSpanIdString> {{#hasAuthMethods}}+ Has<Option<Authorization>>{{/hasAuthMethods}} + Send + Sync + 'static
{
api_impl: T, api_impl: T,
marker: PhantomData<RC>, marker: PhantomData<C>,
} }
impl<T, RC> MakeService<T, RC> impl<T, C> MakeService<T, C> where
where T: Api<C> + Clone + Send + 'static,
T: Api<RC> + Clone + Send + 'static, C: Has<XSpanIdString> {{#hasAuthMethods}}+ Has<Option<Authorization>>{{/hasAuthMethods}} + Send + Sync + 'static
RC: Has<XSpanIdString> {{#hasAuthMethods}}+ Has<Option<Authorization>>{{/hasAuthMethods}} + 'static
{ {
pub fn new(api_impl: T) -> Self { pub fn new(api_impl: T) -> Self {
MakeService { MakeService {
@ -16,21 +18,21 @@ where
} }
} }
impl<'a, T, SC, RC> hyper::service::MakeService<&'a SC> for MakeService<T, RC> impl<T, C, Target> hyper::service::Service<Target> for MakeService<T, C> where
where T: Api<C> + Clone + Send + 'static,
T: Api<RC> + Clone + Send + 'static, C: Has<XSpanIdString> {{#hasAuthMethods}}+ Has<Option<Authorization>>{{/hasAuthMethods}} + Send + Sync + 'static
RC: Has<XSpanIdString> {{#hasAuthMethods}}+ Has<Option<Authorization>>{{/hasAuthMethods}} + 'static + Send
{ {
type ReqBody = ContextualPayload<Body, RC>; type Response = Service<T, C>;
type ResBody = Body; type Error = crate::ServiceError;
type Error = Error; type Future = future::Ready<Result<Self::Response, Self::Error>>;
type Service = Service<T, RC>;
type Future = future::FutureResult<Self::Service, Self::MakeError>;
type MakeError = Error;
fn make_service(&mut self, _ctx: &'a SC) -> Self::Future { fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
future::FutureResult::from(Ok(Service::new( Poll::Ready(Ok(()))
}
fn call(&mut self, target: Target) -> Self::Future {
futures::future::ok(Service::new(
self.api_impl.clone(), self.api_impl.clone(),
))) ))
} }
} }

View File

@ -4,10 +4,10 @@
{ {
let authorization = match (&context as &dyn Has<Option<Authorization>>).get() { let authorization = match (&context as &dyn Has<Option<Authorization>>).get() {
&Some(ref authorization) => authorization, &Some(ref authorization) => authorization,
&None => return Box::new(future::ok(Response::builder() &None => return Ok(Response::builder()
.status(StatusCode::FORBIDDEN) .status(StatusCode::FORBIDDEN)
.body(Body::from("Unauthenticated")) .body(Body::from("Unauthenticated"))
.expect("Unable to create Authentication Forbidden response"))), .expect("Unable to create Authentication Forbidden response")),
}; };
{{#authMethods}} {{#authMethods}}
{{#isOAuth}} {{#isOAuth}}
@ -22,14 +22,14 @@
if !required_scopes.is_subset(scopes) { if !required_scopes.is_subset(scopes) {
let missing_scopes = required_scopes.difference(scopes); let missing_scopes = required_scopes.difference(scopes);
return Box::new(future::ok(Response::builder() return Ok(Response::builder()
.status(StatusCode::FORBIDDEN) .status(StatusCode::FORBIDDEN)
.body(Body::from(missing_scopes.fold( .body(Body::from(missing_scopes.fold(
"Insufficient authorization, missing scopes".to_string(), "Insufficient authorization, missing scopes".to_string(),
|s, scope| format!("{} {}", s, scope)) |s, scope| format!("{} {}", s, scope))
)) ))
.expect("Unable to create Authentication Insufficient response") .expect("Unable to create Authentication Insufficient response")
)); );
} }
} }
{{/isOAuth}} {{/isOAuth}}
@ -41,10 +41,10 @@
{{#consumesMultipart}} {{#consumesMultipart}}
let boundary = match swagger::multipart::boundary(&headers) { let boundary = match swagger::multipart::boundary(&headers) {
Some(boundary) => boundary.to_string(), Some(boundary) => boundary.to_string(),
None => return Box::new(future::ok(Response::builder() None => return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST) .status(StatusCode::BAD_REQUEST)
.body(Body::from("Couldn't find valid multipart body".to_string())) .body(Body::from("Couldn't find valid multipart body".to_string()))
.expect("Unable to create Bad Request response for incorrect boundary"))), .expect("Unable to create Bad Request response for incorrect boundary")),
}; };
{{/consumesMultipart}} {{/consumesMultipart}}
@ -64,15 +64,15 @@
let param_{{{paramName}}} = match percent_encoding::percent_decode(path_params["{{{baseName}}}"].as_bytes()).decode_utf8() { let param_{{{paramName}}} = match percent_encoding::percent_decode(path_params["{{{baseName}}}"].as_bytes()).decode_utf8() {
Ok(param_{{{paramName}}}) => match param_{{{paramName}}}.parse::<{{{dataType}}}>() { Ok(param_{{{paramName}}}) => match param_{{{paramName}}}.parse::<{{{dataType}}}>() {
Ok(param_{{{paramName}}}) => param_{{{paramName}}}, Ok(param_{{{paramName}}}) => param_{{{paramName}}},
Err(e) => return Box::new(future::ok(Response::builder() Err(e) => return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST) .status(StatusCode::BAD_REQUEST)
.body(Body::from(format!("Couldn't parse path parameter {{{baseName}}}: {}", e))) .body(Body::from(format!("Couldn't parse path parameter {{{baseName}}}: {}", e)))
.expect("Unable to create Bad Request response for invalid path parameter"))), .expect("Unable to create Bad Request response for invalid path parameter")),
}, },
Err(_) => return Box::new(future::ok(Response::builder() Err(_) => return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST) .status(StatusCode::BAD_REQUEST)
.body(Body::from(format!("Couldn't percent-decode path parameter as UTF-8: {}", &path_params["{{{baseName}}}"]))) .body(Body::from(format!("Couldn't percent-decode path parameter as UTF-8: {}", &path_params["{{{baseName}}}"])))
.expect("Unable to create Bad Request response for invalid percent decode"))) .expect("Unable to create Bad Request response for invalid percent decode"))
}; };
{{/pathParams}} {{/pathParams}}
@ -97,19 +97,19 @@
Some(result.0), Some(result.0),
{{/required}} {{/required}}
Err(err) => { Err(err) => {
return Box::new(future::ok(Response::builder() return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST) .status(StatusCode::BAD_REQUEST)
.body(Body::from(format!("Invalid header {{{baseName}}} - {}", err))) .body(Body::from(format!("Invalid header {{{baseName}}} - {}", err)))
.expect("Unable to create Bad Request response for invalid header {{{baseName}}}"))); .expect("Unable to create Bad Request response for invalid header {{{baseName}}}"));
}, },
}, },
None => { None => {
{{#required}} {{#required}}
return Box::new(future::ok(Response::builder() return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST) .status(StatusCode::BAD_REQUEST)
.body(Body::from("Missing required header {{{baseName}}}")) .body(Body::from("Missing required header {{{baseName}}}"))
.expect("Unable to create Bad Request response for missing required header {{{baseName}}}"))); .expect("Unable to create Bad Request response for missing required header {{{baseName}}}"));
{{/required}} {{/required}}
{{^required}} {{^required}}
None None
@ -143,15 +143,15 @@
let param_{{{paramName}}} = match param_{{{paramName}}} { let param_{{{paramName}}} = match param_{{{paramName}}} {
Some(param_{{{paramName}}}) => match param_{{{paramName}}}.parse::<{{{dataType}}}>() { Some(param_{{{paramName}}}) => match param_{{{paramName}}}.parse::<{{{dataType}}}>() {
Ok(param_{{{paramName}}}) => param_{{{paramName}}}, Ok(param_{{{paramName}}}) => param_{{{paramName}}},
Err(e) => return Box::new(future::ok(Response::builder() Err(e) => return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST) .status(StatusCode::BAD_REQUEST)
.body(Body::from(format!("Couldn't parse query parameter {{{baseName}}} - doesn't match schema: {}", e))) .body(Body::from(format!("Couldn't parse query parameter {{{baseName}}} - doesn't match schema: {}", e)))
.expect("Unable to create Bad Request response for invalid query parameter {{{baseName}}}"))), .expect("Unable to create Bad Request response for invalid query parameter {{{baseName}}}")),
}, },
None => return Box::new(future::ok(Response::builder() None => return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST) .status(StatusCode::BAD_REQUEST)
.body(Body::from("Missing required query parameter {{{baseName}}}")) .body(Body::from("Missing required query parameter {{{baseName}}}"))
.expect("Unable to create Bad Request response for missing qeury parameter {{{baseName}}}"))), .expect("Unable to create Bad Request response for missing qeury parameter {{{baseName}}}")),
}; };
{{/required}} {{/required}}
{{^required}} {{^required}}
@ -169,9 +169,8 @@
// Body parameters (note that non-required body parameters will ignore garbage // Body parameters (note that non-required body parameters will ignore garbage
// values, rather than causing a 400 response). Produce warning header and logs for // values, rather than causing a 400 response). Produce warning header and logs for
// any unused fields. // any unused fields.
Box::new(body.concat2() let result = body.to_raw().await;
.then(move |result| -> Self::Future { match result {
match result {
Ok(body) => { Ok(body) => {
{{#vendorExtensions}} {{#vendorExtensions}}
{{^consumesPlainText}} {{^consumesPlainText}}
@ -191,10 +190,10 @@
}) { }) {
Ok(param_{{{paramName}}}) => param_{{{paramName}}}, Ok(param_{{{paramName}}}) => param_{{{paramName}}},
{{#required}} {{#required}}
Err(e) => return Box::new(future::ok(Response::builder() Err(e) => return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST) .status(StatusCode::BAD_REQUEST)
.body(Body::from(format!("Couldn't parse body parameter {{{baseName}}} - doesn't match schema: {}", e))) .body(Body::from(format!("Couldn't parse body parameter {{{baseName}}} - doesn't match schema: {}", e)))
.expect("Unable to create Bad Request response for invalid body parameter {{{baseName}}} due to schema"))), .expect("Unable to create Bad Request response for invalid body parameter {{{baseName}}} due to schema")),
{{/required}} {{/required}}
{{^required}} {{^required}}
Err(_) => None, Err(_) => None,
@ -208,10 +207,10 @@
{{#isString}} {{#isString}}
match String::from_utf8(body.to_vec()) { match String::from_utf8(body.to_vec()) {
Ok(param_{{{paramName}}}) => Some(param_{{{paramName}}}), Ok(param_{{{paramName}}}) => Some(param_{{{paramName}}}),
Err(e) => return Box::new(future::ok(Response::builder() Err(e) => return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST) .status(StatusCode::BAD_REQUEST)
.body(Body::from(format!("Couldn't parse body parameter {{{baseName}}} - not valid UTF-8: {}", e))) .body(Body::from(format!("Couldn't parse body parameter {{{baseName}}} - not valid UTF-8: {}", e)))
.expect("Unable to create Bad Request response for invalid body parameter {{{baseName}}} due to UTF-8"))), .expect("Unable to create Bad Request response for invalid body parameter {{{baseName}}} due to UTF-8")),
} }
{{/isString}} {{/isString}}
{{/consumesPlainText}} {{/consumesPlainText}}
@ -222,10 +221,10 @@
{{#required}} {{#required}}
let param_{{{paramName}}} = match param_{{{paramName}}} { let param_{{{paramName}}} = match param_{{{paramName}}} {
Some(param_{{{paramName}}}) => param_{{{paramName}}}, Some(param_{{{paramName}}}) => param_{{{paramName}}},
None => return Box::new(future::ok(Response::builder() None => return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST) .status(StatusCode::BAD_REQUEST)
.body(Body::from("Missing required body parameter {{{baseName}}}")) .body(Body::from("Missing required body parameter {{{baseName}}}"))
.expect("Unable to create Bad Request response for missing body parameter {{{baseName}}}"))), .expect("Unable to create Bad Request response for missing body parameter {{{baseName}}}")),
}; };
{{/required}} {{/required}}
{{/-first}} {{/-first}}
@ -240,9 +239,8 @@
// Form Body parameters (note that non-required body parameters will ignore garbage // Form Body parameters (note that non-required body parameters will ignore garbage
// values, rather than causing a 400 response). Produce warning header and logs for // values, rather than causing a 400 response). Produce warning header and logs for
// any unused fields. // any unused fields.
Box::new(body.concat2() let result = body.to_raw();
.then(move |result| -> Self::Future { match result.await {
match result {
Ok(body) => { Ok(body) => {
use std::io::Read; use std::io::Read;
@ -252,10 +250,10 @@
entries entries
}, },
_ => { _ => {
return Box::new(future::ok(Response::builder() return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST) .status(StatusCode::BAD_REQUEST)
.body(Body::from(format!("Unable to process all message parts"))) .body(Body::from(format!("Unable to process all message parts")))
.expect("Unable to create Bad Request response due to failure to process all message"))) .expect("Unable to create Bad Request response due to failure to process all message"))
}, },
}; };
{{#formParams}} {{#formParams}}
@ -278,11 +276,11 @@
let {{{paramName}}}_model: {{{dataType}}} = match serde_json::from_str(&data) { let {{{paramName}}}_model: {{{dataType}}} = match serde_json::from_str(&data) {
Ok(model) => model, Ok(model) => model,
Err(e) => { Err(e) => {
return Box::new(future::ok( return Ok(
Response::builder() Response::builder()
.status(StatusCode::BAD_REQUEST) .status(StatusCode::BAD_REQUEST)
.body(Body::from(format!("{{{paramName}}} data does not match API definition : {}", e))) .body(Body::from(format!("{{{paramName}}} data does not match API definition : {}", e)))
.expect("Unable to create Bad Request due to missing required form parameter {{{paramName}}}"))) .expect("Unable to create Bad Request due to missing required form parameter {{{paramName}}}"))
} }
}; };
{{{paramName}}}_model {{{paramName}}}_model
@ -294,11 +292,11 @@
}, },
None => { None => {
{{#required}} {{#required}}
return Box::new(future::ok( return Ok(
Response::builder() Response::builder()
.status(StatusCode::BAD_REQUEST) .status(StatusCode::BAD_REQUEST)
.body(Body::from(format!("Missing required form parameter {{{paramName}}}"))) .body(Body::from(format!("Missing required form parameter {{{paramName}}}")))
.expect("Unable to create Bad Request due to missing required form parameter {{{paramName}}}"))) .expect("Unable to create Bad Request due to missing required form parameter {{{paramName}}}"))
{{/required}} {{/required}}
{{^required}} {{^required}}
None None
@ -313,8 +311,6 @@
{{^consumesMultipart}} {{^consumesMultipart}}
{{^bodyParams}} {{^bodyParams}}
{{#vendorExtensions}} {{#vendorExtensions}}
Box::new({
{{
{{#formParams}} {{#formParams}}
{{#-first}} {{#-first}}
// Form parameters // Form parameters
@ -332,9 +328,8 @@
// Body parameters (note that non-required body parameters will ignore garbage // Body parameters (note that non-required body parameters will ignore garbage
// values, rather than causing a 400 response). Produce warning header and logs for // values, rather than causing a 400 response). Produce warning header and logs for
// any unused fields. // any unused fields.
Box::new(body.concat2() let result = body.to_raw();
.then(move |result| -> Self::Future { match result.await {
match result {
Ok(body) => { Ok(body) => {
let mut unused_elements: Vec<String> = vec![]; let mut unused_elements: Vec<String> = vec![];
@ -354,10 +349,10 @@
multi_part_headers.set(ContentType(content_type_mime)); multi_part_headers.set(ContentType(content_type_mime));
}, },
Err(e) => { Err(e) => {
return Box::new(future::ok(Response::builder() return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST) .status(StatusCode::BAD_REQUEST)
.body(Body::from(e)) .body(Body::from(e))
.expect("Unable to create Bad Request response due to unable to read content-type header for {{operationId}}"))); .expect("Unable to create Bad Request response due to unable to read content-type header for {{operationId}}"));
} }
} }
@ -366,10 +361,10 @@
let nodes = match read_multipart_body(&mut&*body, &multi_part_headers, false) { let nodes = match read_multipart_body(&mut&*body, &multi_part_headers, false) {
Ok(nodes) => nodes, Ok(nodes) => nodes,
Err(e) => { Err(e) => {
return Box::new(future::ok(Response::builder() return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST) .status(StatusCode::BAD_REQUEST)
.body(Body::from(format!("Could not read multipart body for {{operationId}}: {}", e))) .body(Body::from(format!("Could not read multipart body for {{operationId}}: {}", e)))
.expect("Unable to create Bad Request response due to unable to read multipart body for {{operationId}}"))); .expect("Unable to create Bad Request response due to unable to read multipart body for {{operationId}}"));
} }
}; };
@ -391,10 +386,10 @@
unused_elements.push(path.to_string()); unused_elements.push(path.to_string());
}) { }) {
Ok(json_data) => json_data, Ok(json_data) => json_data,
Err(e) => return Box::new(future::ok(Response::builder() Err(e) => return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST) .status(StatusCode::BAD_REQUEST)
.body(Body::from(format!("Couldn't parse body parameter {{dataType}} - doesn't match schema: {}", e))) .body(Body::from(format!("Couldn't parse body parameter {{dataType}} - doesn't match schema: {}", e)))
.expect("Unable to create Bad Request response for invalid body parameter {{dataType}} due to schema"))) .expect("Unable to create Bad Request response for invalid body parameter {{dataType}} due to schema"))
}; };
// Push JSON part to return object. // Push JSON part to return object.
param_{{{paramName}}}.get_or_insert(json_data); param_{{{paramName}}}.get_or_insert(json_data);
@ -427,10 +422,10 @@
{{#required}} {{#required}}
let param_{{{paramName}}} = match param_required_binary_field { let param_{{{paramName}}} = match param_required_binary_field {
Some(x) => x, Some(x) => x,
None => return Box::new(future::ok(Response::builder() None => return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST) .status(StatusCode::BAD_REQUEST)
.body(Body::from(format!("Missing required multipart/related parameter {{{paramName}}}"))) .body(Body::from(format!("Missing required multipart/related parameter {{{paramName}}}")))
.expect("Unable to create Bad Request response for missing multipart/related parameter {{{paramName}}} due to schema"))) .expect("Unable to create Bad Request response for missing multipart/related parameter {{{paramName}}} due to schema"))
}; };
{{/required}} {{/required}}
{{#-last}} {{#-last}}
@ -439,8 +434,7 @@
{{/formParams}} {{/formParams}}
{{/consumesMultipartRelated}} {{/consumesMultipartRelated}}
{{/vendorExtensions}} {{/vendorExtensions}}
Box::new( let result = api_impl.{{#vendorExtensions}}{{{operation_id}}}{{/vendorExtensions}}(
api_impl.{{#vendorExtensions}}{{{operation_id}}}{{/vendorExtensions}}(
{{#vendorExtensions}} {{#vendorExtensions}}
{{#callbackParams}} {{#callbackParams}}
callback_{{.}}, callback_{{.}},
@ -450,9 +444,9 @@
param_{{{paramName}}}{{#isListContainer}}.as_ref(){{/isListContainer}}, param_{{{paramName}}}{{#isListContainer}}.as_ref(){{/isListContainer}},
{{/allParams}} {{/allParams}}
&context &context
).then(move |result| { ).await;
let mut response = Response::new(Body::empty()); let mut response = Response::new(Body::empty());
response.headers_mut().insert( response.headers_mut().insert(
HeaderName::from_static("x-span-id"), HeaderName::from_static("x-span-id"),
HeaderValue::from_str((&context as &dyn Has<XSpanIdString>).get().0.clone().to_string().as_str()) HeaderValue::from_str((&context as &dyn Has<XSpanIdString>).get().0.clone().to_string().as_str())
.expect("Unable to create X-Span-ID header value")); .expect("Unable to create X-Span-ID header value"));
@ -505,7 +499,7 @@
let {{{name}}} = match header::IntoHeaderValue({{{name}}}).try_into() { let {{{name}}} = match header::IntoHeaderValue({{{name}}}).try_into() {
Ok(val) => val, Ok(val) => val,
Err(e) => { Err(e) => {
return future::ok(Response::builder() return Ok(Response::builder()
.status(StatusCode::INTERNAL_SERVER_ERROR) .status(StatusCode::INTERNAL_SERVER_ERROR)
.body(Body::from(format!("An internal server error occurred handling {{name}} header - {}", e))) .body(Body::from(format!("An internal server error occurred handling {{name}} header - {}", e)))
.expect("Unable to create Internal Server Error for invalid response header")) .expect("Unable to create Internal Server Error for invalid response header"))
@ -569,26 +563,18 @@
}, },
} }
future::ok(response) Ok(response)
}
))
{{#vendorExtensions}} {{#vendorExtensions}}
{{^consumesMultipart}} {{^consumesMultipart}}
{{^bodyParams}} {{^bodyParams}}
{{#vendorExtensions}} {{#vendorExtensions}}
{{#consumesMultipartRelated}} {{#consumesMultipartRelated}}
}, },
Err(e) => Box::new(future::ok(Response::builder() Err(e) => Ok(Response::builder()
.status(StatusCode::BAD_REQUEST) .status(StatusCode::BAD_REQUEST)
.body(Body::from(format!("Couldn't read body parameter {{{baseName}}}: {}", e))) .body(Body::from(format!("Couldn't read body parameter {{{baseName}}}: {}", e)))
.expect("Unable to create Bad Request response due to unable to read body parameter {{{baseName}}}"))), .expect("Unable to create Bad Request response due to unable to read body parameter {{{baseName}}}")),
} }
})
) as Self::Future
{{/consumesMultipartRelated}}
{{^consumesMultipartRelated}}
}}
}) as Self::Future
{{/consumesMultipartRelated}} {{/consumesMultipartRelated}}
{{/vendorExtensions}} {{/vendorExtensions}}
{{/bodyParams}} {{/bodyParams}}
@ -597,28 +583,23 @@
{{#bodyParams}} {{#bodyParams}}
{{#-first}} {{#-first}}
}, },
Err(e) => Box::new(future::ok(Response::builder() Err(e) => Ok(Response::builder()
.status(StatusCode::BAD_REQUEST) .status(StatusCode::BAD_REQUEST)
.body(Body::from(format!("Couldn't read body parameter {{{baseName}}}: {}", e))) .body(Body::from(format!("Couldn't read body parameter {{{baseName}}}: {}", e)))
.expect("Unable to create Bad Request response due to unable to read body parameter {{{baseName}}}"))), .expect("Unable to create Bad Request response due to unable to read body parameter {{{baseName}}}")),
} }
})
) as Self::Future
{{/-first}} {{/-first}}
{{/bodyParams}} {{/bodyParams}}
{{#vendorExtensions}} {{#vendorExtensions}}
{{#consumesMultipart}} {{#consumesMultipart}}
{{^bodyParams}} {{^bodyParams}}
{{#vendorExtensions}} {{#vendorExtensions}}
as Self::Future
}, },
Err(e) => Box::new(future::ok(Response::builder() Err(e) => Ok(Response::builder()
.status(StatusCode::BAD_REQUEST) .status(StatusCode::BAD_REQUEST)
.body(Body::from(format!("Couldn't read multipart body"))) .body(Body::from(format!("Couldn't read multipart body")))
.expect("Unable to create Bad Request response due to unable read multipart body"))), .expect("Unable to create Bad Request response due to unable read multipart body")),
} }
})
)
{{/vendorExtensions}} {{/vendorExtensions}}
{{/bodyParams}} {{/bodyParams}}
{{/consumesMultipart}} {{/consumesMultipart}}

View File

@ -1,18 +1,6 @@
_ => Box::new(future::ok( _ => Ok(Response::builder().status(StatusCode::NOT_FOUND)
Response::builder().status(StatusCode::NOT_FOUND)
.body(Body::empty()) .body(Body::empty())
.expect("Unable to create Not Found response") .expect("Unable to create Not Found response"))
)) as Self::Future
} }
} } Box::pin(run(self.api_impl.clone(), req)) }
}
impl<T, C> Clone for Service<T, C> where T: Clone
{
fn clone(&self) -> Self {
Service {
api_impl: self.api_impl.clone(),
marker: self.marker.clone(),
}
}
} }

View File

@ -1,22 +1,23 @@
type ServiceFuture = Box<dyn Future<Item = Response<Body>, Error = Error> + Send>; fn method_not_allowed() -> Result<Response<Body>, crate::ServiceError> {
Ok(
fn method_not_allowed() -> ServiceFuture {
Box::new(future::ok(
Response::builder().status(StatusCode::METHOD_NOT_ALLOWED) Response::builder().status(StatusCode::METHOD_NOT_ALLOWED)
.body(Body::empty()) .body(Body::empty())
.expect("Unable to create Method Not Allowed response") .expect("Unable to create Method Not Allowed response")
)) )
} }
pub struct Service<T, RC> { pub struct Service<T, C> where
T: Api<C> + Clone + Send + 'static,
C: Has<XSpanIdString> {{#hasAuthMethods}}+ Has<Option<Authorization>>{{/hasAuthMethods}} + Send + Sync + 'static
{
api_impl: T, api_impl: T,
marker: PhantomData<RC>, marker: PhantomData<C>,
} }
impl<T, RC> Service<T, RC> impl<T, C> Service<T, C> where
where T: Api<C> + Clone + Send + 'static,
T: Api<RC> + Clone + Send + 'static, C: Has<XSpanIdString> {{#hasAuthMethods}}+ Has<Option<Authorization>>{{/hasAuthMethods}} + Send + Sync + 'static
RC: Has<XSpanIdString> {{#hasAuthMethods}}+ Has<Option<Authorization>>{{/hasAuthMethods}} + 'static { {
pub fn new(api_impl: T) -> Self { pub fn new(api_impl: T) -> Self {
Service { Service {
api_impl: api_impl, api_impl: api_impl,
@ -25,23 +26,38 @@ where
} }
} }
impl<T, C> hyper::service::Service for Service<T, C> impl<T, C> Clone for Service<T, C> where
where
T: Api<C> + Clone + Send + 'static, T: Api<C> + Clone + Send + 'static,
C: Has<XSpanIdString> {{#hasAuthMethods}}+ Has<Option<Authorization>>{{/hasAuthMethods}} + 'static + Send C: Has<XSpanIdString> {{#hasAuthMethods}}+ Has<Option<Authorization>>{{/hasAuthMethods}} + Send + Sync + 'static
{ {
type ReqBody = ContextualPayload<Body, C>; fn clone(&self) -> Self {
type ResBody = Body; Service {
type Error = Error; api_impl: self.api_impl.clone(),
marker: self.marker.clone(),
}
}
}
impl<T, C> hyper::service::Service<(Request<Body>, C)> for Service<T, C> where
T: Api<C> + Clone + Send + Sync + 'static,
C: Has<XSpanIdString> {{#hasAuthMethods}}+ Has<Option<Authorization>>{{/hasAuthMethods}} + Send + Sync + 'static
{
type Response = Response<Body>;
type Error = crate::ServiceError;
type Future = ServiceFuture; type Future = ServiceFuture;
fn call(&mut self, req: Request<Self::ReqBody>) -> Self::Future { fn poll_ready(&mut self, cx: &mut Context) -> Poll<Result<(), Self::Error>> {
let api_impl = self.api_impl.clone(); self.api_impl.poll_ready(cx)
let (parts, body) = req.into_parts(); }
fn call(&mut self, req: (Request<Body>, C)) -> Self::Future { async fn run<T, C>(mut api_impl: T, req: (Request<Body>, C)) -> Result<Response<Body>, crate::ServiceError> where
T: Api<C> + Clone + Send + 'static,
C: Has<XSpanIdString> {{#hasAuthMethods}}+ Has<Option<Authorization>>{{/hasAuthMethods}} + Send + Sync + 'static
{
let (request, context) = req;
let (parts, body) = request.into_parts();
let (method, uri, headers) = (parts.method, parts.uri, parts.headers); let (method, uri, headers) = (parts.method, parts.uri, parts.headers);
let path = paths::GLOBAL_REGEX_SET.matches(uri.path()); let path = paths::GLOBAL_REGEX_SET.matches(uri.path());
let mut context = body.context;
let body = body.inner;
{{! {{!
This match statement is duplicated below in `parse_operation_id()`. This match statement is duplicated below in `parse_operation_id()`.

View File

@ -12,7 +12,7 @@ client = [
"mime_0_2", "mime_0_2",
"multipart", "multipart/client", "swagger/multipart", "multipart", "multipart/client", "swagger/multipart",
"hyper_0_10", "mime_multipart", "hyper_0_10", "mime_multipart",
"hyper", "hyper-openssl", "native-tls", "openssl", "url" "hyper", "hyper-openssl", "hyper-tls", "native-tls", "openssl", "url"
] ]
server = [ server = [
"mime_0_2", "mime_0_2",
@ -24,20 +24,22 @@ conversion = ["frunk", "frunk_derives", "frunk_core", "frunk-enum-core", "frunk-
[target.'cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))'.dependencies] [target.'cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))'.dependencies]
native-tls = { version = "0.2", optional = true } native-tls = { version = "0.2", optional = true }
hyper-tls = { version = "0.4", optional = true }
[target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dependencies] [target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dependencies]
hyper-openssl = { version = "0.7.1", optional = true } hyper-openssl = { version = "0.8", optional = true }
openssl = {version = "0.10", optional = true } openssl = {version = "0.10", optional = true }
[dependencies] [dependencies]
# Common # Common
async-trait = "0.1.24"
chrono = { version = "0.4", features = ["serde"] } chrono = { version = "0.4", features = ["serde"] }
futures = "0.1" futures = "0.3"
swagger = "4.0" swagger = "5.0.0-alpha-1"
log = "0.4.0" log = "0.4.0"
mime = "0.3" mime = "0.3"
serde = { version = "1.0", features = ["derive"]} serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"
# Crates included if required by the API definition # Crates included if required by the API definition
@ -45,18 +47,18 @@ mime_0_2 = { package = "mime", version = "0.2.6", optional = true }
multipart = { version = "0.16", default-features = false, optional = true } multipart = { version = "0.16", default-features = false, optional = true }
# Common between server and client features # Common between server and client features
hyper = {version = "0.12", optional = true} hyper = {version = "0.13", optional = true}
mime_multipart = {version = "0.5", optional = true} mime_multipart = {version = "0.5", optional = true}
hyper_0_10 = {package = "hyper", version = "0.10", default-features = false, optional=true} hyper_0_10 = {package = "hyper", version = "0.10", default-features = false, optional=true}
serde_ignored = {version = "0.0.4", optional = true} serde_ignored = {version = "0.1.1", optional = true}
url = {version = "1.5", optional = true} url = {version = "2.1", optional = true}
# Client-specific # Client-specific
# Server, and client callback-specific # Server, and client callback-specific
lazy_static = { version = "1.4", optional = true } lazy_static = { version = "1.4", optional = true }
percent-encoding = {version = "1.0.0", optional = true} percent-encoding = {version = "2.1.0", optional = true}
regex = {version = "0.2", optional = true} regex = {version = "1.3", optional = true}
# Conversion # Conversion
frunk = { version = "0.3.0", optional = true } frunk = { version = "0.3.0", optional = true }
@ -67,13 +69,13 @@ frunk-enum-core = { version = "0.2.0", optional = true }
[dev-dependencies] [dev-dependencies]
clap = "2.25" clap = "2.25"
error-chain = "0.12" env_logger = "0.7"
env_logger = "0.6" tokio = { version = "0.2", features = ["rt-threaded", "macros", "stream"] }
tokio = "0.1.17" native-tls = "0.2"
uuid = {version = "0.7", features = ["serde", "v4"]} tokio-tls = "0.3"
[target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dev-dependencies] [target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dev-dependencies]
tokio-openssl = "0.3" tokio-openssl = "0.4"
openssl = "0.10" openssl = "0.10"
[[example]] [[example]]

View File

@ -2,13 +2,12 @@
#[allow(unused_imports)] #[allow(unused_imports)]
use futures::{Future, future, Stream, stream}; use futures::{future, Stream, stream};
#[allow(unused_imports)] #[allow(unused_imports)]
use multipart_v3::{Api, ApiNoContext, Client, ContextWrapperExt, models, use multipart_v3::{Api, ApiNoContext, Client, ContextWrapperExt, models,
ApiError,
MultipartRelatedRequestPostResponse, MultipartRelatedRequestPostResponse,
MultipartRequestPostResponse, MultipartRequestPostResponse,
MultipleIdenticalMimeTypesPostResponse MultipleIdenticalMimeTypesPostResponse,
}; };
use clap::{App, Arg}; use clap::{App, Arg};
@ -17,7 +16,9 @@ use log::info;
// swagger::Has may be unused if there are no examples // swagger::Has may be unused if there are no examples
#[allow(unused_imports)] #[allow(unused_imports)]
use swagger::{ContextBuilder, EmptyContext, XSpanIdString, Has, Push, AuthData}; use swagger::{AuthData, ContextBuilder, EmptyContext, Has, Push, XSpanIdString};
type ClientContext = swagger::make_context_ty!(ContextBuilder, EmptyContext, Option<AuthData>, XSpanIdString);
// rt may be unused if there are no examples // rt may be unused if there are no examples
#[allow(unused_mut)] #[allow(unused_mut)]
@ -55,21 +56,21 @@ fn main() {
matches.value_of("host").unwrap(), matches.value_of("host").unwrap(),
matches.value_of("port").unwrap()); matches.value_of("port").unwrap());
let client = if matches.is_present("https") { let context: ClientContext =
// Using Simple HTTPS
Client::try_new_https(&base_url)
.expect("Failed to create HTTPS client")
} else {
// Using HTTP
Client::try_new_http(
&base_url)
.expect("Failed to create HTTP client")
};
let context: swagger::make_context_ty!(ContextBuilder, EmptyContext, Option<AuthData>, XSpanIdString) =
swagger::make_context!(ContextBuilder, EmptyContext, None as Option<AuthData>, XSpanIdString::default()); swagger::make_context!(ContextBuilder, EmptyContext, None as Option<AuthData>, XSpanIdString::default());
let client = client.with_context(context); let mut client : Box<dyn ApiNoContext<ClientContext>> = if matches.is_present("https") {
// Using Simple HTTPS
let client = Box::new(Client::try_new_https(&base_url)
.expect("Failed to create HTTPS client"));
Box::new(client.with_context(context))
} else {
// Using HTTP
let client = Box::new(Client::try_new_http(
&base_url)
.expect("Failed to create HTTP client"));
Box::new(client.with_context(context))
};
let mut rt = tokio::runtime::Runtime::new().unwrap(); let mut rt = tokio::runtime::Runtime::new().unwrap();

View File

@ -9,7 +9,8 @@ mod server;
/// Create custom server, wire it to the autogenerated router, /// Create custom server, wire it to the autogenerated router,
/// and pass it to the web server. /// and pass it to the web server.
fn main() { #[tokio::main]
async fn main() {
env_logger::init(); env_logger::init();
let matches = App::new("server") let matches = App::new("server")
@ -20,5 +21,5 @@ fn main() {
let addr = "127.0.0.1:8080"; let addr = "127.0.0.1:8080";
hyper::rt::run(server::create(addr, matches.is_present("https"))); server::create(addr, matches.is_present("https")).await;
} }

View File

@ -2,30 +2,22 @@
#![allow(unused_imports)] #![allow(unused_imports)]
mod errors { use async_trait::async_trait;
error_chain::error_chain!{} use futures::{future, Stream, StreamExt, TryFutureExt, TryStreamExt};
}
pub use self::errors::*;
use chrono;
use futures::{future, Future, Stream};
use hyper::server::conn::Http; use hyper::server::conn::Http;
use hyper::service::MakeService as _; use hyper::service::Service;
use log::info; use log::info;
use openssl::ssl::SslAcceptorBuilder; use openssl::ssl::SslAcceptorBuilder;
use std::future::Future;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::net::SocketAddr; use std::net::SocketAddr;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use swagger; use std::task::{Context, Poll};
use swagger::{Has, XSpanIdString}; use swagger::{Has, XSpanIdString};
use swagger::auth::MakeAllowAllAuthenticator; use swagger::auth::MakeAllowAllAuthenticator;
use swagger::EmptyContext; use swagger::EmptyContext;
use tokio::net::TcpListener; use tokio::net::TcpListener;
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
use tokio_openssl::SslAcceptorExt;
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod}; use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod};
@ -33,18 +25,18 @@ use multipart_v3::models;
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
/// Builds an SSL implementation for Simple HTTPS from some hard-coded file names /// Builds an SSL implementation for Simple HTTPS from some hard-coded file names
pub fn create(addr: &str, https: bool) -> Box<dyn Future<Item = (), Error = ()> + Send> { pub async fn create(addr: &str, https: bool) {
let addr = addr.parse().expect("Failed to parse bind address"); let addr = addr.parse().expect("Failed to parse bind address");
let server = Server::new(); let server = Server::new();
let service_fn = MakeService::new(server); let service = MakeService::new(server);
let service_fn = MakeAllowAllAuthenticator::new(service_fn, "cosmo"); let service = MakeAllowAllAuthenticator::new(service, "cosmo");
let service_fn = let mut service =
multipart_v3::server::context::MakeAddContext::<_, EmptyContext>::new( multipart_v3::server::context::MakeAddContext::<_, EmptyContext>::new(
service_fn service
); );
if https { if https {
@ -62,32 +54,31 @@ pub fn create(addr: &str, https: bool) -> Box<dyn Future<Item = (), Error = ()>
ssl.set_certificate_chain_file("examples/server-chain.pem").expect("Failed to set cerificate chain"); ssl.set_certificate_chain_file("examples/server-chain.pem").expect("Failed to set cerificate chain");
ssl.check_private_key().expect("Failed to check private key"); ssl.check_private_key().expect("Failed to check private key");
let tls_acceptor = ssl.build(); let tls_acceptor = Arc::new(ssl.build());
let service_fn = Arc::new(Mutex::new(service_fn)); let mut tcp_listener = TcpListener::bind(&addr).await.unwrap();
let tls_listener = TcpListener::bind(&addr).unwrap().incoming().for_each(move |tcp| { let mut incoming = tcp_listener.incoming();
let addr = tcp.peer_addr().expect("Unable to get remote address");
let service_fn = service_fn.clone(); while let (Some(tcp), rest) = incoming.into_future().await {
if let Ok(tcp) = tcp {
let addr = tcp.peer_addr().expect("Unable to get remote address");
let service = service.call(addr);
let tls_acceptor = Arc::clone(&tls_acceptor);
hyper::rt::spawn(tls_acceptor.accept_async(tcp).map_err(|_| ()).and_then(move |tls| { tokio::spawn(async move {
let ms = { let tls = tokio_openssl::accept(&*tls_acceptor, tcp).await.map_err(|_| ())?;
let mut service_fn = service_fn.lock().unwrap();
service_fn.make_service(&addr)
};
ms.and_then(move |service| { let service = service.await.map_err(|_| ())?;
Http::new().serve_connection(tls, service)
}).map_err(|_| ())
}));
Ok(()) Http::new().serve_connection(tls, service).await.map_err(|_| ())
}).map_err(|_| ()); });
}
Box::new(tls_listener) incoming = rest;
}
} }
} else { } else {
// Using HTTP // Using HTTP
Box::new(hyper::server::Server::bind(&addr).serve(service_fn).map_err(|e| panic!("{:?}", e))) hyper::server::Server::bind(&addr).serve(service).await.unwrap()
} }
} }
@ -105,48 +96,51 @@ impl<C> Server<C> {
use multipart_v3::{ use multipart_v3::{
Api, Api,
ApiError,
MultipartRelatedRequestPostResponse, MultipartRelatedRequestPostResponse,
MultipartRequestPostResponse, MultipartRequestPostResponse,
MultipleIdenticalMimeTypesPostResponse, MultipleIdenticalMimeTypesPostResponse,
}; };
use multipart_v3::server::MakeService; use multipart_v3::server::MakeService;
use std::error::Error;
use swagger::ApiError;
impl<C> Api<C> for Server<C> where C: Has<XSpanIdString>{ #[async_trait]
fn multipart_related_request_post( impl<C> Api<C> for Server<C> where C: Has<XSpanIdString> + Send + Sync
{
async fn multipart_related_request_post(
&self, &self,
required_binary_field: swagger::ByteArray, required_binary_field: swagger::ByteArray,
object_field: Option<models::MultipartRequestObjectField>, object_field: Option<models::MultipartRequestObjectField>,
optional_binary_field: Option<swagger::ByteArray>, optional_binary_field: Option<swagger::ByteArray>,
context: &C) -> Box<dyn Future<Item=MultipartRelatedRequestPostResponse, Error=ApiError> + Send> context: &C) -> Result<MultipartRelatedRequestPostResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("multipart_related_request_post({:?}, {:?}, {:?}) - X-Span-ID: {:?}", required_binary_field, object_field, optional_binary_field, context.get().0.clone()); info!("multipart_related_request_post({:?}, {:?}, {:?}) - X-Span-ID: {:?}", required_binary_field, object_field, optional_binary_field, context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn multipart_request_post( async fn multipart_request_post(
&self, &self,
string_field: String, string_field: String,
binary_field: swagger::ByteArray, binary_field: swagger::ByteArray,
optional_string_field: Option<String>, optional_string_field: Option<String>,
object_field: Option<models::MultipartRequestObjectField>, object_field: Option<models::MultipartRequestObjectField>,
context: &C) -> Box<dyn Future<Item=MultipartRequestPostResponse, Error=ApiError> + Send> context: &C) -> Result<MultipartRequestPostResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("multipart_request_post(\"{}\", {:?}, {:?}, {:?}) - X-Span-ID: {:?}", string_field, binary_field, optional_string_field, object_field, context.get().0.clone()); info!("multipart_request_post(\"{}\", {:?}, {:?}, {:?}) - X-Span-ID: {:?}", string_field, binary_field, optional_string_field, object_field, context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn multiple_identical_mime_types_post( async fn multiple_identical_mime_types_post(
&self, &self,
binary1: Option<swagger::ByteArray>, binary1: Option<swagger::ByteArray>,
binary2: Option<swagger::ByteArray>, binary2: Option<swagger::ByteArray>,
context: &C) -> Box<dyn Future<Item=MultipleIdenticalMimeTypesPostResponse, Error=ApiError> + Send> context: &C) -> Result<MultipleIdenticalMimeTypesPostResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("multiple_identical_mime_types_post({:?}, {:?}) - X-Span-ID: {:?}", binary1, binary2, context.get().0.clone()); info!("multiple_identical_mime_types_post({:?}, {:?}) - X-Span-ID: {:?}", binary1, binary2, context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
} }

View File

@ -1,26 +1,23 @@
use futures; use async_trait::async_trait;
use futures::{Future, Stream, future, stream}; use futures::{Stream, future, future::BoxFuture, stream, future::TryFutureExt, future::FutureExt, stream::StreamExt};
use hyper;
use hyper::client::HttpConnector;
use hyper::header::{HeaderName, HeaderValue, CONTENT_TYPE}; use hyper::header::{HeaderName, HeaderValue, CONTENT_TYPE};
use hyper::{Body, Uri, Response}; use hyper::{Body, Request, Response, service::Service, Uri};
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] use percent_encoding::{utf8_percent_encode, AsciiSet};
use hyper_openssl::HttpsConnector;
use serde_json;
use std::borrow::Cow; use std::borrow::Cow;
use std::convert::TryInto; use std::convert::TryInto;
use std::io::{Read, Error, ErrorKind}; use std::io::{ErrorKind, Read};
use std::error; use std::error::Error;
use std::future::Future;
use std::fmt; use std::fmt;
use std::path::Path; use std::path::Path;
use std::sync::Arc; use std::sync::{Arc, Mutex};
use std::str; use std::str;
use std::str::FromStr; use std::str::FromStr;
use std::string::ToString; use std::string::ToString;
use swagger; use std::task::{Context, Poll};
use swagger::{ApiError, Connector, client::Service, XSpanIdString, Has, AuthData}; use swagger::{ApiError, AuthData, BodyExt, Connector, Has, XSpanIdString};
use url::form_urlencoded; use url::form_urlencoded;
use url::percent_encoding::{utf8_percent_encode, PATH_SEGMENT_ENCODE_SET, QUERY_ENCODE_SET};
use mime::Mime; use mime::Mime;
use std::io::Cursor; use std::io::Cursor;
use multipart::client::lazy::Multipart; use multipart::client::lazy::Multipart;
@ -30,13 +27,17 @@ use mime_multipart::{Node, Part, generate_boundary, write_multipart};
use crate::models; use crate::models;
use crate::header; use crate::header;
url::define_encode_set! { /// https://url.spec.whatwg.org/#fragment-percent-encode-set
/// This encode set is used for object IDs #[allow(dead_code)]
/// const FRAGMENT_ENCODE_SET: &AsciiSet = &percent_encoding::CONTROLS
/// Aside from the special characters defined in the `PATH_SEGMENT_ENCODE_SET`, .add(b' ').add(b'"').add(b'<').add(b'>').add(b'`');
/// the vertical bar (|) is encoded.
pub ID_ENCODE_SET = [PATH_SEGMENT_ENCODE_SET] | {'|'} /// This encode set is used for object IDs
} ///
/// Aside from the special characters defined in the `PATH_SEGMENT_ENCODE_SET`,
/// the vertical bar (|) is encoded.
#[allow(dead_code)]
const ID_ENCODE_SET: &AsciiSet = &FRAGMENT_ENCODE_SET.add(b'|');
use crate::{Api, use crate::{Api,
MultipartRelatedRequestPostResponse, MultipartRelatedRequestPostResponse,
@ -45,11 +46,11 @@ use crate::{Api,
}; };
/// Convert input into a base path, e.g. "http://example:123". Also checks the scheme as it goes. /// Convert input into a base path, e.g. "http://example:123". Also checks the scheme as it goes.
fn into_base_path(input: &str, correct_scheme: Option<&'static str>) -> Result<String, ClientInitError> { fn into_base_path(input: impl TryInto<Uri, Error=hyper::http::uri::InvalidUri>, correct_scheme: Option<&'static str>) -> Result<String, ClientInitError> {
// First convert to Uri, since a base path is a subset of Uri. // First convert to Uri, since a base path is a subset of Uri.
let uri = Uri::from_str(input)?; let uri = input.try_into()?;
let scheme = uri.scheme_part().ok_or(ClientInitError::InvalidScheme)?; let scheme = uri.scheme_str().ok_or(ClientInitError::InvalidScheme)?;
// Check the scheme if necessary // Check the scheme if necessary
if let Some(correct_scheme) = correct_scheme { if let Some(correct_scheme) = correct_scheme {
@ -59,38 +60,54 @@ fn into_base_path(input: &str, correct_scheme: Option<&'static str>) -> Result<S
} }
let host = uri.host().ok_or_else(|| ClientInitError::MissingHost)?; let host = uri.host().ok_or_else(|| ClientInitError::MissingHost)?;
let port = uri.port_part().map(|x| format!(":{}", x)).unwrap_or_default(); let port = uri.port_u16().map(|x| format!(":{}", x)).unwrap_or_default();
Ok(format!("{}://{}{}{}", scheme, host, port, uri.path().trim_end_matches('/'))) Ok(format!("{}://{}{}{}", scheme, host, port, uri.path().trim_end_matches('/')))
} }
/// A client that implements the API by making HTTP calls out to a server. /// A client that implements the API by making HTTP calls out to a server.
pub struct Client<F> pub struct Client<S> where
S: Service<
Request<Body>,
Response=Response<Body>> + Clone + Sync + Send + 'static,
S::Future: Send + 'static,
S::Error: Into<crate::ServiceError> + fmt::Display,
{ {
/// Inner service /// Inner service
client_service: Arc<Box<dyn Service<ReqBody=Body, Future=F> + Send + Sync>>, client_service: S,
/// Base path of the API /// Base path of the API
base_path: String, base_path: String,
} }
impl<F> fmt::Debug for Client<F> impl<S> fmt::Debug for Client<S> where
S: Service<
Request<Body>,
Response=Response<Body>> + Clone + Sync + Send + 'static,
S::Future: Send + 'static,
S::Error: Into<crate::ServiceError> + fmt::Display,
{ {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Client {{ base_path: {} }}", self.base_path) write!(f, "Client {{ base_path: {} }}", self.base_path)
} }
} }
impl<F> Clone for Client<F> impl<S> Clone for Client<S> where
S: Service<
Request<Body>,
Response=Response<Body>> + Clone + Sync + Send + 'static,
S::Future: Send + 'static,
S::Error: Into<crate::ServiceError> + fmt::Display,
{ {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Client { Self {
client_service: self.client_service.clone(), client_service: self.client_service.clone(),
base_path: self.base_path.clone(), base_path: self.base_path.clone(),
} }
} }
} }
impl Client<hyper::client::ResponseFuture> impl<C> Client<hyper::client::Client<C, Body>> where
C: hyper::client::connect::Connect + Clone + Send + Sync + 'static
{ {
/// Create a client with a custom implementation of hyper::client::Connect. /// Create a client with a custom implementation of hyper::client::Connect.
/// ///
@ -103,30 +120,93 @@ impl Client<hyper::client::ResponseFuture>
/// ///
/// # Arguments /// # Arguments
/// ///
/// * `base_path` - base path of the client API, i.e. "www.my-api-implementation.com" /// * `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")` /// * `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 /// * `connector` - Implementation of `hyper::client::Connect` to use for the client
pub fn try_new_with_connector<C>( pub fn try_new_with_connector(
base_path: &str, base_path: &str,
protocol: Option<&'static str>, protocol: Option<&'static str>,
connector: C, connector: C,
) -> Result<Self, ClientInitError> where ) -> Result<Self, ClientInitError>
C: hyper::client::connect::Connect + 'static,
C::Transport: 'static,
C::Future: 'static,
{ {
let client_service = Box::new(hyper::client::Client::builder().build(connector)); let client_service = hyper::client::Client::builder().build(connector);
Ok(Client { Ok(Self {
client_service: Arc::new(client_service), client_service,
base_path: into_base_path(base_path, protocol)?, base_path: into_base_path(base_path, protocol)?,
}) })
} }
}
#[derive(Debug, Clone)]
pub enum HyperClient {
Http(hyper::client::Client<hyper::client::HttpConnector, Body>),
Https(hyper::client::Client<HttpsConnector, Body>),
}
impl Service<Request<Body>> for HyperClient {
type Response = Response<Body>;
type Error = hyper::Error;
type Future = hyper::client::ResponseFuture;
fn poll_ready(&mut self, cx: &mut Context) -> Poll<Result<(), Self::Error>> {
match self {
HyperClient::Http(client) => client.poll_ready(cx),
HyperClient::Https(client) => client.poll_ready(cx),
}
}
fn call(&mut self, req: Request<Body>) -> Self::Future {
match self {
HyperClient::Http(client) => client.call(req),
HyperClient::Https(client) => client.call(req)
}
}
}
impl Client<HyperClient> {
/// Create an HTTP client. /// Create an HTTP client.
/// ///
/// # Arguments /// # Arguments
/// * `base_path` - base path of the client API, i.e. "www.my-api-implementation.com" /// * `base_path` - base path of the client API, i.e. "http://www.my-api-implementation.com"
pub fn try_new(
base_path: &str,
) -> Result<Self, ClientInitError> {
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(|e| ClientInitError::SslError(e))?;
HyperClient::Https(hyper::client::Client::builder().build(connector))
},
_ => {
return Err(ClientInitError::InvalidScheme);
}
};
Ok(Self {
client_service,
base_path: into_base_path(base_path, None)?,
})
}
}
impl Client<hyper::client::Client<hyper::client::HttpConnector, Body>>
{
/// 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( pub fn try_new_http(
base_path: &str, base_path: &str,
) -> Result<Self, ClientInitError> { ) -> Result<Self, ClientInitError> {
@ -134,11 +214,20 @@ impl Client<hyper::client::ResponseFuture>
Self::try_new_with_connector(base_path, Some("http"), http_connector) 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<hyper::client::HttpConnector>;
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
type HttpsConnector = hyper_openssl::HttpsConnector<hyper::client::HttpConnector>;
impl Client<hyper::client::Client<HttpsConnector, Body>>
{
/// Create a client with a TLS connection to the server /// Create a client with a TLS connection to the server
/// ///
/// # Arguments /// # Arguments
/// * `base_path` - base path of the client API, i.e. "www.my-api-implementation.com" /// * `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<Self, ClientInitError> pub fn try_new_https(base_path: &str) -> Result<Self, ClientInitError>
{ {
let https_connector = Connector::builder() let https_connector = Connector::builder()
@ -151,7 +240,7 @@ impl Client<hyper::client::ResponseFuture>
/// Create a client with a TLS connection to the server using a pinned certificate /// Create a client with a TLS connection to the server using a pinned certificate
/// ///
/// # Arguments /// # Arguments
/// * `base_path` - base path of the client API, i.e. "www.my-api-implementation.com" /// * `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 /// * `ca_certificate` - Path to CA certificate used to authenticate the server
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
pub fn try_new_https_pinned<CA>( pub fn try_new_https_pinned<CA>(
@ -172,7 +261,7 @@ impl Client<hyper::client::ResponseFuture>
/// Create a client with a mutually authenticated TLS connection to the server. /// Create a client with a mutually authenticated TLS connection to the server.
/// ///
/// # Arguments /// # Arguments
/// * `base_path` - base path of the client API, i.e. "www.my-api-implementation.com" /// * `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 /// * `ca_certificate` - Path to CA certificate used to authenticate the server
/// * `client_key` - Path to the client private key /// * `client_key` - Path to the client private key
/// * `client_certificate` - Path to the client's public certificate associated with the private key /// * `client_certificate` - Path to the client's public certificate associated with the private key
@ -198,17 +287,24 @@ impl Client<hyper::client::ResponseFuture>
} }
} }
impl<F> Client<F> impl<S> Client<S> where
S: Service<
Request<Body>,
Response=Response<Body>> + Clone + Sync + Send + 'static,
S::Future: Send + 'static,
S::Error: Into<crate::ServiceError> + fmt::Display,
{ {
/// Constructor for creating a `Client` by passing in a pre-made `swagger::Service` /// 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. /// This allows adding custom wrappers around the underlying transport, for example for logging.
pub fn try_new_with_client_service( pub fn try_new_with_client_service(
client_service: Arc<Box<dyn Service<ReqBody=Body, Future=F> + Send + Sync>>, client_service: S,
base_path: &str, base_path: &str,
) -> Result<Self, ClientInitError> { ) -> Result<Self, ClientInitError>
Ok(Client { {
client_service: client_service, Ok(Self {
client_service,
base_path: into_base_path(base_path, None)?, base_path: into_base_path(base_path, None)?,
}) })
} }
@ -248,47 +344,63 @@ impl fmt::Display for ClientInitError {
} }
} }
impl error::Error for ClientInitError { impl Error for ClientInitError {
fn description(&self) -> &str { fn description(&self) -> &str {
"Failed to produce a hyper client." "Failed to produce a hyper client."
} }
} }
impl<C, F> Api<C> for Client<F> where #[async_trait]
C: Has<XSpanIdString> , impl<C, S> Api<C> for Client<S> where
F: Future<Item=Response<Body>, Error=hyper::Error> + Send + 'static C: Has<XSpanIdString> + Clone + Send + Sync + 'static,
S: Service<
Request<Body>,
Response=Response<Body>> + Clone + Sync + Send + 'static,
S::Future: Send + 'static,
S::Error: Into<crate::ServiceError> + fmt::Display,
{ {
fn multipart_related_request_post( fn poll_ready(&self, cx: &mut Context) -> Poll<Result<(), crate::ServiceError>> {
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,
}
}
async fn multipart_related_request_post(
&self, &self,
param_required_binary_field: swagger::ByteArray, param_required_binary_field: swagger::ByteArray,
param_object_field: Option<models::MultipartRequestObjectField>, param_object_field: Option<models::MultipartRequestObjectField>,
param_optional_binary_field: Option<swagger::ByteArray>, param_optional_binary_field: Option<swagger::ByteArray>,
context: &C) -> Box<dyn Future<Item=MultipartRelatedRequestPostResponse, Error=ApiError> + Send> context: &C) -> Result<MultipartRelatedRequestPostResponse, ApiError>
{ {
let mut client_service = self.client_service.clone();
let mut uri = format!( let mut uri = format!(
"{}/multipart_related_request", "{}/multipart_related_request",
self.base_path self.base_path
); );
// Query parameters // Query parameters
let mut query_string = url::form_urlencoded::Serializer::new("".to_owned()); let query_string = {
let query_string_str = query_string.finish(); let mut query_string = form_urlencoded::Serializer::new("".to_owned());
if !query_string_str.is_empty() { query_string.finish()
};
if !query_string.is_empty() {
uri += "?"; uri += "?";
uri += &query_string_str; uri += &query_string;
} }
let uri = match Uri::from_str(&uri) { let uri = match Uri::from_str(&uri) {
Ok(uri) => uri, Ok(uri) => uri,
Err(err) => return Box::new(future::err(ApiError(format!("Unable to build URI: {}", err)))), Err(err) => return Err(ApiError(format!("Unable to build URI: {}", err))),
}; };
let mut request = match hyper::Request::builder() let mut request = match Request::builder()
.method("POST") .method("POST")
.uri(uri) .uri(uri)
.body(Body::empty()) { .body(Body::empty()) {
Ok(req) => req, Ok(req) => req,
Err(e) => return Box::new(future::err(ApiError(format!("Unable to create request: {}", e)))) Err(e) => return Err(ApiError(format!("Unable to create request: {}", e)))
}; };
// Construct the Body for a multipart/related request. The mime 0.2.6 library // Construct the Body for a multipart/related request. The mime 0.2.6 library
@ -359,229 +471,230 @@ impl<C, F> Api<C> for Client<F> where
&[header.as_bytes(), "; boundary=".as_bytes(), &boundary, "; type=\"application/json\"".as_bytes()].concat() &[header.as_bytes(), "; boundary=".as_bytes(), &boundary, "; type=\"application/json\"".as_bytes()].concat()
) { ) {
Ok(h) => h, Ok(h) => h,
Err(e) => return Box::new(future::err(ApiError(format!("Unable to create header: {} - {}", header, e)))) Err(e) => return Err(ApiError(format!("Unable to create header: {} - {}", header, e)))
}); });
let header = HeaderValue::from_str((context as &dyn Has<XSpanIdString>).get().0.clone().to_string().as_str()); let header = HeaderValue::from_str(Has::<XSpanIdString>::get(context).0.clone().to_string().as_str());
request.headers_mut().insert(HeaderName::from_static("x-span-id"), match header { request.headers_mut().insert(HeaderName::from_static("x-span-id"), match header {
Ok(h) => h, Ok(h) => h,
Err(e) => return Box::new(future::err(ApiError(format!("Unable to create X-Span ID header value: {}", e)))) Err(e) => return Err(ApiError(format!("Unable to create X-Span ID header value: {}", e)))
}); });
Box::new(self.client_service.request(request) let mut response = client_service.call(request)
.map_err(|e| ApiError(format!("No response received: {}", e))) .map_err(|e| ApiError(format!("No response received: {}", e))).await?;
.and_then(|mut response| {
match response.status().as_u16() { match response.status().as_u16() {
201 => { 201 => {
let body = response.into_body(); let body = response.into_body();
Box::new( Ok(
future::ok( MultipartRelatedRequestPostResponse::OK
MultipartRelatedRequestPostResponse::OK )
)
) as Box<dyn Future<Item=_, Error=_> + Send>
},
code => {
let headers = response.headers().clone();
Box::new(response.into_body()
.take(100)
.concat2()
.then(move |body|
future::err(ApiError(format!("Unexpected response code {}:\n{:?}\n\n{}",
code,
headers,
match body {
Ok(ref body) => match str::from_utf8(body) {
Ok(body) => Cow::from(body),
Err(e) => Cow::from(format!("<Body was not UTF8: {:?}>", e)),
},
Err(e) => Cow::from(format!("<Failed to read body: {}>", e)),
})))
)
) as Box<dyn Future<Item=_, Error=_> + Send>
}
} }
})) code => {
let headers = response.headers().clone();
let body = response.into_body()
.take(100)
.to_raw().await;
Err(ApiError(format!("Unexpected response code {}:\n{:?}\n\n{}",
code,
headers,
match body {
Ok(body) => match String::from_utf8(body) {
Ok(body) => body,
Err(e) => format!("<Body was not UTF8: {:?}>", e),
},
Err(e) => format!("<Failed to read body: {}>", e),
}
)))
}
}
} }
fn multipart_request_post( async fn multipart_request_post(
&self, &self,
param_string_field: String, param_string_field: String,
param_binary_field: swagger::ByteArray, param_binary_field: swagger::ByteArray,
param_optional_string_field: Option<String>, param_optional_string_field: Option<String>,
param_object_field: Option<models::MultipartRequestObjectField>, param_object_field: Option<models::MultipartRequestObjectField>,
context: &C) -> Box<dyn Future<Item=MultipartRequestPostResponse, Error=ApiError> + Send> context: &C) -> Result<MultipartRequestPostResponse, ApiError>
{ {
let mut client_service = self.client_service.clone();
let mut uri = format!( let mut uri = format!(
"{}/multipart_request", "{}/multipart_request",
self.base_path self.base_path
); );
// Query parameters // Query parameters
let mut query_string = url::form_urlencoded::Serializer::new("".to_owned()); let query_string = {
let query_string_str = query_string.finish(); let mut query_string = form_urlencoded::Serializer::new("".to_owned());
if !query_string_str.is_empty() { query_string.finish()
};
if !query_string.is_empty() {
uri += "?"; uri += "?";
uri += &query_string_str; uri += &query_string;
} }
let uri = match Uri::from_str(&uri) { let uri = match Uri::from_str(&uri) {
Ok(uri) => uri, Ok(uri) => uri,
Err(err) => return Box::new(future::err(ApiError(format!("Unable to build URI: {}", err)))), Err(err) => return Err(ApiError(format!("Unable to build URI: {}", err))),
}; };
let mut request = match hyper::Request::builder() let mut request = match Request::builder()
.method("POST") .method("POST")
.uri(uri) .uri(uri)
.body(Body::empty()) { .body(Body::empty()) {
Ok(req) => req, Ok(req) => req,
Err(e) => return Box::new(future::err(ApiError(format!("Unable to create request: {}", e)))) Err(e) => return Err(ApiError(format!("Unable to create request: {}", e)))
}; };
let mut multipart = Multipart::new(); let (body_string, multipart_header) = {
let mut multipart = Multipart::new();
// For each parameter, encode as appropriate and add to the multipart body as a stream. // For each parameter, encode as appropriate and add to the multipart body as a stream.
let string_field_str = match serde_json::to_string(&param_string_field) { let string_field_str = match serde_json::to_string(&param_string_field) {
Ok(str) => str, Ok(str) => str,
Err(e) => return Box::new(future::err(ApiError(format!("Unable to parse string_field to string: {}", e)))), Err(e) => return Err(ApiError(format!("Unable to parse string_field to string: {}", e))),
};
let string_field_vec = string_field_str.as_bytes().to_vec();
let string_field_mime = mime_0_2::Mime::from_str("application/json").expect("impossible to fail to parse");
let string_field_cursor = Cursor::new(string_field_vec);
multipart.add_stream("string_field", string_field_cursor, None as Option<&str>, Some(string_field_mime));
let optional_string_field_str = match serde_json::to_string(&param_optional_string_field) {
Ok(str) => str,
Err(e) => return Err(ApiError(format!("Unable to parse optional_string_field to string: {}", e))),
};
let optional_string_field_vec = optional_string_field_str.as_bytes().to_vec();
let optional_string_field_mime = mime_0_2::Mime::from_str("application/json").expect("impossible to fail to parse");
let optional_string_field_cursor = Cursor::new(optional_string_field_vec);
multipart.add_stream("optional_string_field", optional_string_field_cursor, None as Option<&str>, Some(optional_string_field_mime));
let object_field_str = match serde_json::to_string(&param_object_field) {
Ok(str) => str,
Err(e) => return Err(ApiError(format!("Unable to parse object_field to string: {}", e))),
};
let object_field_vec = object_field_str.as_bytes().to_vec();
let object_field_mime = mime_0_2::Mime::from_str("application/json").expect("impossible to fail to parse");
let object_field_cursor = Cursor::new(object_field_vec);
multipart.add_stream("object_field", object_field_cursor, None as Option<&str>, Some(object_field_mime));
let binary_field_vec = param_binary_field.to_vec();
let binary_field_mime = match mime_0_2::Mime::from_str("application/octet-stream") {
Ok(mime) => mime,
Err(err) => return Err(ApiError(format!("Unable to get mime type: {:?}", err))),
};
let binary_field_cursor = Cursor::new(binary_field_vec);
let filename = None as Option<&str> ;
multipart.add_stream("binary_field", binary_field_cursor, filename, Some(binary_field_mime));
let mut fields = match multipart.prepare() {
Ok(fields) => fields,
Err(err) => return Err(ApiError(format!("Unable to build request: {}", err))),
};
let mut body_string = String::new();
match fields.read_to_string(&mut body_string) {
Ok(_) => (),
Err(err) => return Err(ApiError(format!("Unable to build body: {}", err))),
}
let boundary = fields.boundary();
let multipart_header = format!("multipart/form-data;boundary={}", boundary);
(body_string, multipart_header)
}; };
let string_field_vec = string_field_str.as_bytes().to_vec();
let string_field_mime = mime_0_2::Mime::from_str("application/json").expect("impossible to fail to parse");
let string_field_cursor = Cursor::new(string_field_vec);
multipart.add_stream("string_field", string_field_cursor, None as Option<&str>, Some(string_field_mime));
let optional_string_field_str = match serde_json::to_string(&param_optional_string_field) {
Ok(str) => str,
Err(e) => return Box::new(future::err(ApiError(format!("Unable to parse optional_string_field to string: {}", e)))),
};
let optional_string_field_vec = optional_string_field_str.as_bytes().to_vec();
let optional_string_field_mime = mime_0_2::Mime::from_str("application/json").expect("impossible to fail to parse");
let optional_string_field_cursor = Cursor::new(optional_string_field_vec);
multipart.add_stream("optional_string_field", optional_string_field_cursor, None as Option<&str>, Some(optional_string_field_mime));
let object_field_str = match serde_json::to_string(&param_object_field) {
Ok(str) => str,
Err(e) => return Box::new(future::err(ApiError(format!("Unable to parse object_field to string: {}", e)))),
};
let object_field_vec = object_field_str.as_bytes().to_vec();
let object_field_mime = mime_0_2::Mime::from_str("application/json").expect("impossible to fail to parse");
let object_field_cursor = Cursor::new(object_field_vec);
multipart.add_stream("object_field", object_field_cursor, None as Option<&str>, Some(object_field_mime));
let binary_field_vec = param_binary_field.to_vec();
let binary_field_mime = match mime_0_2::Mime::from_str("application/octet-stream") {
Ok(mime) => mime,
Err(err) => return Box::new(future::err(ApiError(format!("Unable to get mime type: {:?}", err)))),
};
let binary_field_cursor = Cursor::new(binary_field_vec);
let filename = None as Option<&str> ;
multipart.add_stream("binary_field", binary_field_cursor, filename, Some(binary_field_mime));
let mut fields = match multipart.prepare() {
Ok(fields) => fields,
Err(err) => return Box::new(future::err(ApiError(format!("Unable to build request: {}", err)))),
};
let mut body_string = String::new();
match fields.read_to_string(&mut body_string) {
Ok(_) => (),
Err(err) => return Box::new(future::err(ApiError(format!("Unable to build body: {}", err)))),
}
let boundary = fields.boundary();
let multipart_header = format!("multipart/form-data;boundary={}", boundary);
*request.body_mut() = Body::from(body_string); *request.body_mut() = Body::from(body_string);
request.headers_mut().insert(CONTENT_TYPE, match HeaderValue::from_str(&multipart_header) { request.headers_mut().insert(CONTENT_TYPE, match HeaderValue::from_str(&multipart_header) {
Ok(h) => h, Ok(h) => h,
Err(e) => return Box::new(future::err(ApiError(format!("Unable to create header: {} - {}", multipart_header, e)))) Err(e) => return Err(ApiError(format!("Unable to create header: {} - {}", multipart_header, e)))
}); });
let header = HeaderValue::from_str((context as &dyn Has<XSpanIdString>).get().0.clone().to_string().as_str()); let header = HeaderValue::from_str(Has::<XSpanIdString>::get(context).0.clone().to_string().as_str());
request.headers_mut().insert(HeaderName::from_static("x-span-id"), match header { request.headers_mut().insert(HeaderName::from_static("x-span-id"), match header {
Ok(h) => h, Ok(h) => h,
Err(e) => return Box::new(future::err(ApiError(format!("Unable to create X-Span ID header value: {}", e)))) Err(e) => return Err(ApiError(format!("Unable to create X-Span ID header value: {}", e)))
}); });
Box::new(self.client_service.request(request) let mut response = client_service.call(request)
.map_err(|e| ApiError(format!("No response received: {}", e))) .map_err(|e| ApiError(format!("No response received: {}", e))).await?;
.and_then(|mut response| {
match response.status().as_u16() { match response.status().as_u16() {
201 => { 201 => {
let body = response.into_body(); let body = response.into_body();
Box::new( Ok(
future::ok( MultipartRequestPostResponse::OK
MultipartRequestPostResponse::OK )
)
) as Box<dyn Future<Item=_, Error=_> + Send>
},
code => {
let headers = response.headers().clone();
Box::new(response.into_body()
.take(100)
.concat2()
.then(move |body|
future::err(ApiError(format!("Unexpected response code {}:\n{:?}\n\n{}",
code,
headers,
match body {
Ok(ref body) => match str::from_utf8(body) {
Ok(body) => Cow::from(body),
Err(e) => Cow::from(format!("<Body was not UTF8: {:?}>", e)),
},
Err(e) => Cow::from(format!("<Failed to read body: {}>", e)),
})))
)
) as Box<dyn Future<Item=_, Error=_> + Send>
}
} }
})) code => {
let headers = response.headers().clone();
let body = response.into_body()
.take(100)
.to_raw().await;
Err(ApiError(format!("Unexpected response code {}:\n{:?}\n\n{}",
code,
headers,
match body {
Ok(body) => match String::from_utf8(body) {
Ok(body) => body,
Err(e) => format!("<Body was not UTF8: {:?}>", e),
},
Err(e) => format!("<Failed to read body: {}>", e),
}
)))
}
}
} }
fn multiple_identical_mime_types_post( async fn multiple_identical_mime_types_post(
&self, &self,
param_binary1: Option<swagger::ByteArray>, param_binary1: Option<swagger::ByteArray>,
param_binary2: Option<swagger::ByteArray>, param_binary2: Option<swagger::ByteArray>,
context: &C) -> Box<dyn Future<Item=MultipleIdenticalMimeTypesPostResponse, Error=ApiError> + Send> context: &C) -> Result<MultipleIdenticalMimeTypesPostResponse, ApiError>
{ {
let mut client_service = self.client_service.clone();
let mut uri = format!( let mut uri = format!(
"{}/multiple-identical-mime-types", "{}/multiple-identical-mime-types",
self.base_path self.base_path
); );
// Query parameters // Query parameters
let mut query_string = url::form_urlencoded::Serializer::new("".to_owned()); let query_string = {
let query_string_str = query_string.finish(); let mut query_string = form_urlencoded::Serializer::new("".to_owned());
if !query_string_str.is_empty() { query_string.finish()
};
if !query_string.is_empty() {
uri += "?"; uri += "?";
uri += &query_string_str; uri += &query_string;
} }
let uri = match Uri::from_str(&uri) { let uri = match Uri::from_str(&uri) {
Ok(uri) => uri, Ok(uri) => uri,
Err(err) => return Box::new(future::err(ApiError(format!("Unable to build URI: {}", err)))), Err(err) => return Err(ApiError(format!("Unable to build URI: {}", err))),
}; };
let mut request = match hyper::Request::builder() let mut request = match Request::builder()
.method("POST") .method("POST")
.uri(uri) .uri(uri)
.body(Body::empty()) { .body(Body::empty()) {
Ok(req) => req, Ok(req) => req,
Err(e) => return Box::new(future::err(ApiError(format!("Unable to create request: {}", e)))) Err(e) => return Err(ApiError(format!("Unable to create request: {}", e)))
}; };
// Construct the Body for a multipart/related request. The mime 0.2.6 library // Construct the Body for a multipart/related request. The mime 0.2.6 library
@ -637,48 +750,43 @@ impl<C, F> Api<C> for Client<F> where
&[header.as_bytes(), "; boundary=".as_bytes(), &boundary, "; type=\"application/json\"".as_bytes()].concat() &[header.as_bytes(), "; boundary=".as_bytes(), &boundary, "; type=\"application/json\"".as_bytes()].concat()
) { ) {
Ok(h) => h, Ok(h) => h,
Err(e) => return Box::new(future::err(ApiError(format!("Unable to create header: {} - {}", header, e)))) Err(e) => return Err(ApiError(format!("Unable to create header: {} - {}", header, e)))
}); });
let header = HeaderValue::from_str((context as &dyn Has<XSpanIdString>).get().0.clone().to_string().as_str()); let header = HeaderValue::from_str(Has::<XSpanIdString>::get(context).0.clone().to_string().as_str());
request.headers_mut().insert(HeaderName::from_static("x-span-id"), match header { request.headers_mut().insert(HeaderName::from_static("x-span-id"), match header {
Ok(h) => h, Ok(h) => h,
Err(e) => return Box::new(future::err(ApiError(format!("Unable to create X-Span ID header value: {}", e)))) Err(e) => return Err(ApiError(format!("Unable to create X-Span ID header value: {}", e)))
}); });
Box::new(self.client_service.request(request) let mut response = client_service.call(request)
.map_err(|e| ApiError(format!("No response received: {}", e))) .map_err(|e| ApiError(format!("No response received: {}", e))).await?;
.and_then(|mut response| {
match response.status().as_u16() { match response.status().as_u16() {
200 => { 200 => {
let body = response.into_body(); let body = response.into_body();
Box::new( Ok(
future::ok( MultipleIdenticalMimeTypesPostResponse::OK
MultipleIdenticalMimeTypesPostResponse::OK )
)
) as Box<dyn Future<Item=_, Error=_> + Send>
},
code => {
let headers = response.headers().clone();
Box::new(response.into_body()
.take(100)
.concat2()
.then(move |body|
future::err(ApiError(format!("Unexpected response code {}:\n{:?}\n\n{}",
code,
headers,
match body {
Ok(ref body) => match str::from_utf8(body) {
Ok(body) => Cow::from(body),
Err(e) => Cow::from(format!("<Body was not UTF8: {:?}>", e)),
},
Err(e) => Cow::from(format!("<Failed to read body: {}>", e)),
})))
)
) as Box<dyn Future<Item=_, Error=_> + Send>
}
} }
})) code => {
let headers = response.headers().clone();
let body = response.into_body()
.take(100)
.to_raw().await;
Err(ApiError(format!("Unexpected response code {}:\n{:?}\n\n{}",
code,
headers,
match body {
Ok(body) => match String::from_utf8(body) {
Ok(body) => body,
Err(e) => format!("<Body was not UTF8: {:?}>", e),
},
Err(e) => format!("<Failed to read body: {}>", e),
}
)))
}
}
} }
} }

View File

@ -1,13 +1,12 @@
use futures::Future; use futures::future::BoxFuture;
use hyper;
use hyper::header::HeaderName; use hyper::header::HeaderName;
use hyper::{Error, Request, Response, StatusCode, service::Service, body::Payload}; use hyper::{Error, Request, Response, StatusCode, service::Service};
use url::form_urlencoded; use url::form_urlencoded;
use std::default::Default; use std::default::Default;
use std::io; use std::io;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::task::{Poll, Context};
use swagger::auth::{AuthData, Authorization, Bearer, Scopes}; use swagger::auth::{AuthData, Authorization, Bearer, Scopes};
use swagger::context::ContextualPayload;
use swagger::{EmptyContext, Has, Pop, Push, XSpanIdString}; use swagger::{EmptyContext, Has, Pop, Push, XSpanIdString};
use crate::Api; use crate::Api;
@ -31,58 +30,52 @@ where
} }
// Make a service that adds context. // Make a service that adds context.
impl<'a, T, SC, A, B, C, D, E, ME, S, OB, F> hyper::service::MakeService<&'a SC> for impl<Target, T, A, B, C, D> Service<Target> for
MakeAddContext<T, A> MakeAddContext<T, A>
where where
A: Default + Push<XSpanIdString, Result = B>, Target: Send,
A: Default + Push<XSpanIdString, Result = B> + Send,
B: Push<Option<AuthData>, Result = C>, B: Push<Option<AuthData>, Result = C>,
C: Push<Option<Authorization>, Result = D>, C: Push<Option<Authorization>, Result = D>,
D: Send + 'static, D: Send + 'static,
T: hyper::service::MakeService< T: Service<Target> + Send,
&'a SC, T::Future: Send + 'static
Error = E,
MakeError = ME,
Service = S,
ReqBody = ContextualPayload<hyper::Body, D>,
ResBody = OB,
Future = F
>,
S: Service<
Error = E,
ReqBody = ContextualPayload<hyper::Body, D>,
ResBody = OB> + 'static,
ME: swagger::ErrorBound,
E: swagger::ErrorBound,
F: Future<Item=S, Error=ME> + Send + 'static,
S::Future: Send,
OB: Payload,
{ {
type ReqBody = hyper::Body; type Error = T::Error;
type ResBody = OB; type Response = AddContext<T::Response, A, B, C, D>;
type Error = E; type Future = BoxFuture<'static, Result<Self::Response, Self::Error>>;
type MakeError = ME;
type Service = AddContext<S, A>;
type Future = Box<dyn Future<Item = Self::Service, Error = ME> + Send + 'static>;
fn make_service(&mut self, ctx: &'a SC) -> Self::Future { fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Box::new(self.inner.make_service(ctx).map(|s| AddContext::new(s))) self.inner.poll_ready(cx)
}
fn call(&mut self, target: Target) -> Self::Future {
let service = self.inner.call(target);
Box::pin(async move {
Ok(AddContext::new(service.await?))
})
} }
} }
/// Middleware to extract authentication data from request /// Middleware to add context data from the request
pub struct AddContext<T, A> { pub struct AddContext<T, A, B, C, D>
where
A: Default + Push<XSpanIdString, Result = B>,
B: Push<Option<AuthData>, Result = C>,
C: Push<Option<Authorization>, Result = D>
{
inner: T, inner: T,
marker: PhantomData<A>, marker: PhantomData<A>,
} }
impl<T, A, B, C, D> AddContext<T, A> impl<T, A, B, C, D> AddContext<T, A, B, C, D>
where where
A: Default + Push<XSpanIdString, Result = B>, A: Default + Push<XSpanIdString, Result = B>,
B: Push<Option<AuthData>, Result = C>, B: Push<Option<AuthData>, Result = C>,
C: Push<Option<Authorization>, Result = D>, C: Push<Option<Authorization>, Result = D>,
T: Service,
{ {
pub fn new(inner: T) -> AddContext<T, A> { pub fn new(inner: T) -> Self {
AddContext { AddContext {
inner, inner,
marker: PhantomData, marker: PhantomData,
@ -90,33 +83,31 @@ where
} }
} }
impl<T, A, B, C, D> Service for AddContext<T, A> impl<T, A, B, C, D, ReqBody> Service<Request<ReqBody>> for AddContext<T, A, B, C, D>
where where
A: Default + Push<XSpanIdString, Result=B>, A: Default + Push<XSpanIdString, Result=B>,
B: Push<Option<AuthData>, Result=C>, B: Push<Option<AuthData>, Result=C>,
C: Push<Option<Authorization>, Result=D>, C: Push<Option<Authorization>, Result=D>,
D: Send + 'static, D: Send + 'static,
T: Service<ReqBody = ContextualPayload<hyper::Body, D>>, T: Service<(Request<ReqBody>, D)>
T::Future: Future<Item=Response<T::ResBody>, Error=T::Error> + Send + 'static
{ {
type ReqBody = hyper::Body;
type ResBody = T::ResBody;
type Error = T::Error; type Error = T::Error;
type Future = Box<dyn Future<Item=Response<T::ResBody>, Error=T::Error> + Send + 'static>; type Future = T::Future;
type Response = T::Response;
fn call(&mut self, req: Request<Self::ReqBody>) -> Self::Future { fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
let context = A::default().push(XSpanIdString::get_or_generate(&req)); self.inner.poll_ready(cx)
let (head, body) = req.into_parts(); }
let headers = head.headers.clone();
fn call(&mut self, request: Request<ReqBody>) -> Self::Future {
let context = A::default().push(XSpanIdString::get_or_generate(&request));
let headers = request.headers();
let context = context.push(None::<AuthData>); let context = context.push(None::<AuthData>);
let context = context.push(None::<Authorization>); let context = context.push(None::<Authorization>);
let body = ContextualPayload {
inner: body,
context: context,
};
Box::new(self.inner.call(hyper::Request::from_parts(head, body))) self.inner.call((request, context))
} }
} }

View File

@ -1,12 +1,12 @@
#![allow(missing_docs, trivial_casts, unused_variables, unused_mut, unused_imports, unused_extern_crates, non_camel_case_types)] #![allow(missing_docs, trivial_casts, unused_variables, unused_mut, unused_imports, unused_extern_crates, non_camel_case_types)]
use async_trait::async_trait;
use futures::Stream; use futures::Stream;
use std::io::Error; use std::error::Error;
use std::task::{Poll, Context};
use swagger::{ApiError, ContextWrapper};
#[deprecated(note = "Import swagger-rs directly")] type ServiceError = Box<dyn Error + Send + Sync + 'static>;
pub use swagger::{ApiError, ContextWrapper};
#[deprecated(note = "Import futures directly")]
pub use futures::Future;
pub const BASE_PATH: &'static str = ""; pub const BASE_PATH: &'static str = "";
pub const API_VERSION: &'static str = "1.0.7"; pub const API_VERSION: &'static str = "1.0.7";
@ -30,100 +30,125 @@ pub enum MultipleIdenticalMimeTypesPostResponse {
} }
/// API /// API
pub trait Api<C> { #[async_trait]
fn multipart_related_request_post( pub trait Api<C: Send + Sync> {
fn poll_ready(&self, _cx: &mut Context) -> Poll<Result<(), Box<dyn Error + Send + Sync + 'static>>> {
Poll::Ready(Ok(()))
}
async fn multipart_related_request_post(
&self, &self,
required_binary_field: swagger::ByteArray, required_binary_field: swagger::ByteArray,
object_field: Option<models::MultipartRequestObjectField>, object_field: Option<models::MultipartRequestObjectField>,
optional_binary_field: Option<swagger::ByteArray>, optional_binary_field: Option<swagger::ByteArray>,
context: &C) -> Box<dyn Future<Item=MultipartRelatedRequestPostResponse, Error=ApiError> + Send>; context: &C) -> Result<MultipartRelatedRequestPostResponse, ApiError>;
fn multipart_request_post( async fn multipart_request_post(
&self, &self,
string_field: String, string_field: String,
binary_field: swagger::ByteArray, binary_field: swagger::ByteArray,
optional_string_field: Option<String>, optional_string_field: Option<String>,
object_field: Option<models::MultipartRequestObjectField>, object_field: Option<models::MultipartRequestObjectField>,
context: &C) -> Box<dyn Future<Item=MultipartRequestPostResponse, Error=ApiError> + Send>; context: &C) -> Result<MultipartRequestPostResponse, ApiError>;
fn multiple_identical_mime_types_post( async fn multiple_identical_mime_types_post(
&self, &self,
binary1: Option<swagger::ByteArray>, binary1: Option<swagger::ByteArray>,
binary2: Option<swagger::ByteArray>, binary2: Option<swagger::ByteArray>,
context: &C) -> Box<dyn Future<Item=MultipleIdenticalMimeTypesPostResponse, Error=ApiError> + Send>; context: &C) -> Result<MultipleIdenticalMimeTypesPostResponse, ApiError>;
} }
/// API without a `Context` /// API where `Context` isn't passed on every API call
pub trait ApiNoContext { #[async_trait]
fn multipart_related_request_post( pub trait ApiNoContext<C: Send + Sync> {
fn poll_ready(&self, _cx: &mut Context) -> Poll<Result<(), Box<dyn Error + Send + Sync + 'static>>>;
fn context(&self) -> &C;
async fn multipart_related_request_post(
&self, &self,
required_binary_field: swagger::ByteArray, required_binary_field: swagger::ByteArray,
object_field: Option<models::MultipartRequestObjectField>, object_field: Option<models::MultipartRequestObjectField>,
optional_binary_field: Option<swagger::ByteArray>, optional_binary_field: Option<swagger::ByteArray>,
) -> Box<dyn Future<Item=MultipartRelatedRequestPostResponse, Error=ApiError> + Send>; ) -> Result<MultipartRelatedRequestPostResponse, ApiError>;
fn multipart_request_post( async fn multipart_request_post(
&self, &self,
string_field: String, string_field: String,
binary_field: swagger::ByteArray, binary_field: swagger::ByteArray,
optional_string_field: Option<String>, optional_string_field: Option<String>,
object_field: Option<models::MultipartRequestObjectField>, object_field: Option<models::MultipartRequestObjectField>,
) -> Box<dyn Future<Item=MultipartRequestPostResponse, Error=ApiError> + Send>; ) -> Result<MultipartRequestPostResponse, ApiError>;
fn multiple_identical_mime_types_post( async fn multiple_identical_mime_types_post(
&self, &self,
binary1: Option<swagger::ByteArray>, binary1: Option<swagger::ByteArray>,
binary2: Option<swagger::ByteArray>, binary2: Option<swagger::ByteArray>,
) -> Box<dyn Future<Item=MultipleIdenticalMimeTypesPostResponse, Error=ApiError> + Send>; ) -> Result<MultipleIdenticalMimeTypesPostResponse, ApiError>;
} }
/// Trait to extend an API to make it easy to bind it to a context. /// Trait to extend an API to make it easy to bind it to a context.
pub trait ContextWrapperExt<'a, C> where Self: Sized { pub trait ContextWrapperExt<C: Send + Sync> where Self: Sized
{
/// Binds this API to a context. /// Binds this API to a context.
fn with_context(self: &'a Self, context: C) -> ContextWrapper<'a, Self, C>; fn with_context(self: Self, context: C) -> ContextWrapper<Self, C>;
} }
impl<'a, T: Api<C> + Sized, C> ContextWrapperExt<'a, C> for T { impl<T: Api<C> + Send + Sync, C: Clone + Send + Sync> ContextWrapperExt<C> for T {
fn with_context(self: &'a T, context: C) -> ContextWrapper<'a, T, C> { fn with_context(self: T, context: C) -> ContextWrapper<T, C> {
ContextWrapper::<T, C>::new(self, context) ContextWrapper::<T, C>::new(self, context)
} }
} }
impl<'a, T: Api<C>, C> ApiNoContext for ContextWrapper<'a, T, C> { #[async_trait]
fn multipart_related_request_post( impl<T: Api<C> + Send + Sync, C: Clone + Send + Sync> ApiNoContext<C> for ContextWrapper<T, C> {
fn poll_ready(&self, cx: &mut Context) -> Poll<Result<(), ServiceError>> {
self.api().poll_ready(cx)
}
fn context(&self) -> &C {
ContextWrapper::context(self)
}
async fn multipart_related_request_post(
&self, &self,
required_binary_field: swagger::ByteArray, required_binary_field: swagger::ByteArray,
object_field: Option<models::MultipartRequestObjectField>, object_field: Option<models::MultipartRequestObjectField>,
optional_binary_field: Option<swagger::ByteArray>, optional_binary_field: Option<swagger::ByteArray>,
) -> Box<dyn Future<Item=MultipartRelatedRequestPostResponse, Error=ApiError> + Send> ) -> Result<MultipartRelatedRequestPostResponse, ApiError>
{ {
self.api().multipart_related_request_post(required_binary_field, object_field, optional_binary_field, &self.context()) let context = self.context().clone();
self.api().multipart_related_request_post(required_binary_field, object_field, optional_binary_field, &context).await
} }
fn multipart_request_post( async fn multipart_request_post(
&self, &self,
string_field: String, string_field: String,
binary_field: swagger::ByteArray, binary_field: swagger::ByteArray,
optional_string_field: Option<String>, optional_string_field: Option<String>,
object_field: Option<models::MultipartRequestObjectField>, object_field: Option<models::MultipartRequestObjectField>,
) -> Box<dyn Future<Item=MultipartRequestPostResponse, Error=ApiError> + Send> ) -> Result<MultipartRequestPostResponse, ApiError>
{ {
self.api().multipart_request_post(string_field, binary_field, optional_string_field, object_field, &self.context()) let context = self.context().clone();
self.api().multipart_request_post(string_field, binary_field, optional_string_field, object_field, &context).await
} }
fn multiple_identical_mime_types_post( async fn multiple_identical_mime_types_post(
&self, &self,
binary1: Option<swagger::ByteArray>, binary1: Option<swagger::ByteArray>,
binary2: Option<swagger::ByteArray>, binary2: Option<swagger::ByteArray>,
) -> Box<dyn Future<Item=MultipleIdenticalMimeTypesPostResponse, Error=ApiError> + Send> ) -> Result<MultipleIdenticalMimeTypesPostResponse, ApiError>
{ {
self.api().multiple_identical_mime_types_post(binary1, binary2, &self.context()) let context = self.context().clone();
self.api().multiple_identical_mime_types_post(binary1, binary2, &context).await
} }
} }
#[cfg(feature = "client")] #[cfg(feature = "client")]
pub mod client; pub mod client;

View File

@ -1,20 +1,17 @@
use std::marker::PhantomData; use futures::{future, future::BoxFuture, Stream, stream, future::FutureExt, stream::TryStreamExt};
use futures::{Future, future, Stream, stream}; use hyper::{Request, Response, StatusCode, Body, HeaderMap};
use hyper;
use hyper::{Request, Response, Error, StatusCode, Body, HeaderMap};
use hyper::header::{HeaderName, HeaderValue, CONTENT_TYPE}; use hyper::header::{HeaderName, HeaderValue, CONTENT_TYPE};
use log::warn; use log::warn;
use serde_json;
#[allow(unused_imports)] #[allow(unused_imports)]
use std::convert::{TryFrom, TryInto}; use std::convert::{TryFrom, TryInto};
use std::io; use std::error::Error;
use url::form_urlencoded; use std::future::Future;
#[allow(unused_imports)] use std::marker::PhantomData;
use swagger; use std::task::{Context, Poll};
use swagger::{ApiError, XSpanIdString, Has, RequestParser}; use swagger::{ApiError, BodyExt, Has, RequestParser, XSpanIdString};
pub use swagger::auth::Authorization; pub use swagger::auth::Authorization;
use swagger::auth::Scopes; use swagger::auth::Scopes;
use swagger::context::ContextualPayload; use url::form_urlencoded;
use hyper_0_10::header::{Headers, ContentType}; use hyper_0_10::header::{Headers, ContentType};
use mime_0_2::{TopLevel, SubLevel, Mime as Mime2}; use mime_0_2::{TopLevel, SubLevel, Mime as Mime2};
use mime_multipart::{read_multipart_body, Node, Part}; use mime_multipart::{read_multipart_body, Node, Part};
@ -27,6 +24,8 @@ use crate::header;
pub use crate::context; pub use crate::context;
type ServiceFuture = BoxFuture<'static, Result<Response<Body>, crate::ServiceError>>;
use crate::{Api, use crate::{Api,
MultipartRelatedRequestPostResponse, MultipartRelatedRequestPostResponse,
MultipartRequestPostResponse, MultipartRequestPostResponse,
@ -49,15 +48,17 @@ mod paths {
pub(crate) static ID_MULTIPLE_IDENTICAL_MIME_TYPES: usize = 2; pub(crate) static ID_MULTIPLE_IDENTICAL_MIME_TYPES: usize = 2;
} }
pub struct MakeService<T, RC> { pub struct MakeService<T, C> where
T: Api<C> + Clone + Send + 'static,
C: Has<XSpanIdString> + Send + Sync + 'static
{
api_impl: T, api_impl: T,
marker: PhantomData<RC>, marker: PhantomData<C>,
} }
impl<T, RC> MakeService<T, RC> impl<T, C> MakeService<T, C> where
where T: Api<C> + Clone + Send + 'static,
T: Api<RC> + Clone + Send + 'static, C: Has<XSpanIdString> + Send + Sync + 'static
RC: Has<XSpanIdString> + 'static
{ {
pub fn new(api_impl: T) -> Self { pub fn new(api_impl: T) -> Self {
MakeService { MakeService {
@ -67,44 +68,45 @@ where
} }
} }
impl<'a, T, SC, RC> hyper::service::MakeService<&'a SC> for MakeService<T, RC> impl<T, C, Target> hyper::service::Service<Target> for MakeService<T, C> where
where T: Api<C> + Clone + Send + 'static,
T: Api<RC> + Clone + Send + 'static, C: Has<XSpanIdString> + Send + Sync + 'static
RC: Has<XSpanIdString> + 'static + Send
{ {
type ReqBody = ContextualPayload<Body, RC>; type Response = Service<T, C>;
type ResBody = Body; type Error = crate::ServiceError;
type Error = Error; type Future = future::Ready<Result<Self::Response, Self::Error>>;
type Service = Service<T, RC>;
type Future = future::FutureResult<Self::Service, Self::MakeError>;
type MakeError = Error;
fn make_service(&mut self, _ctx: &'a SC) -> Self::Future { fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
future::FutureResult::from(Ok(Service::new( Poll::Ready(Ok(()))
}
fn call(&mut self, target: Target) -> Self::Future {
futures::future::ok(Service::new(
self.api_impl.clone(), self.api_impl.clone(),
))) ))
} }
} }
type ServiceFuture = Box<dyn Future<Item = Response<Body>, Error = Error> + Send>; fn method_not_allowed() -> Result<Response<Body>, crate::ServiceError> {
Ok(
fn method_not_allowed() -> ServiceFuture {
Box::new(future::ok(
Response::builder().status(StatusCode::METHOD_NOT_ALLOWED) Response::builder().status(StatusCode::METHOD_NOT_ALLOWED)
.body(Body::empty()) .body(Body::empty())
.expect("Unable to create Method Not Allowed response") .expect("Unable to create Method Not Allowed response")
)) )
} }
pub struct Service<T, RC> { pub struct Service<T, C> where
T: Api<C> + Clone + Send + 'static,
C: Has<XSpanIdString> + Send + Sync + 'static
{
api_impl: T, api_impl: T,
marker: PhantomData<RC>, marker: PhantomData<C>,
} }
impl<T, RC> Service<T, RC> impl<T, C> Service<T, C> where
where T: Api<C> + Clone + Send + 'static,
T: Api<RC> + Clone + Send + 'static, C: Has<XSpanIdString> + Send + Sync + 'static
RC: Has<XSpanIdString> + 'static { {
pub fn new(api_impl: T) -> Self { pub fn new(api_impl: T) -> Self {
Service { Service {
api_impl: api_impl, api_impl: api_impl,
@ -113,23 +115,38 @@ where
} }
} }
impl<T, C> hyper::service::Service for Service<T, C> impl<T, C> Clone for Service<T, C> where
where
T: Api<C> + Clone + Send + 'static, T: Api<C> + Clone + Send + 'static,
C: Has<XSpanIdString> + 'static + Send C: Has<XSpanIdString> + Send + Sync + 'static
{ {
type ReqBody = ContextualPayload<Body, C>; fn clone(&self) -> Self {
type ResBody = Body; Service {
type Error = Error; api_impl: self.api_impl.clone(),
marker: self.marker.clone(),
}
}
}
impl<T, C> hyper::service::Service<(Request<Body>, C)> for Service<T, C> where
T: Api<C> + Clone + Send + Sync + 'static,
C: Has<XSpanIdString> + Send + Sync + 'static
{
type Response = Response<Body>;
type Error = crate::ServiceError;
type Future = ServiceFuture; type Future = ServiceFuture;
fn call(&mut self, req: Request<Self::ReqBody>) -> Self::Future { fn poll_ready(&mut self, cx: &mut Context) -> Poll<Result<(), Self::Error>> {
let api_impl = self.api_impl.clone(); self.api_impl.poll_ready(cx)
let (parts, body) = req.into_parts(); }
fn call(&mut self, req: (Request<Body>, C)) -> Self::Future { async fn run<T, C>(mut api_impl: T, req: (Request<Body>, C)) -> Result<Response<Body>, crate::ServiceError> where
T: Api<C> + Clone + Send + 'static,
C: Has<XSpanIdString> + Send + Sync + 'static
{
let (request, context) = req;
let (parts, body) = request.into_parts();
let (method, uri, headers) = (parts.method, parts.uri, parts.headers); let (method, uri, headers) = (parts.method, parts.uri, parts.headers);
let path = paths::GLOBAL_REGEX_SET.matches(uri.path()); let path = paths::GLOBAL_REGEX_SET.matches(uri.path());
let mut context = body.context;
let body = body.inner;
match &method { match &method {
@ -138,9 +155,8 @@ where
// Body parameters (note that non-required body parameters will ignore garbage // Body parameters (note that non-required body parameters will ignore garbage
// values, rather than causing a 400 response). Produce warning header and logs for // values, rather than causing a 400 response). Produce warning header and logs for
// any unused fields. // any unused fields.
Box::new(body.concat2() let result = body.to_raw();
.then(move |result| -> Self::Future { match result.await {
match result {
Ok(body) => { Ok(body) => {
let mut unused_elements: Vec<String> = vec![]; let mut unused_elements: Vec<String> = vec![];
@ -160,10 +176,10 @@ where
multi_part_headers.set(ContentType(content_type_mime)); multi_part_headers.set(ContentType(content_type_mime));
}, },
Err(e) => { Err(e) => {
return Box::new(future::ok(Response::builder() return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST) .status(StatusCode::BAD_REQUEST)
.body(Body::from(e)) .body(Body::from(e))
.expect("Unable to create Bad Request response due to unable to read content-type header for MultipartRelatedRequestPost"))); .expect("Unable to create Bad Request response due to unable to read content-type header for MultipartRelatedRequestPost"));
} }
} }
@ -172,10 +188,10 @@ where
let nodes = match read_multipart_body(&mut&*body, &multi_part_headers, false) { let nodes = match read_multipart_body(&mut&*body, &multi_part_headers, false) {
Ok(nodes) => nodes, Ok(nodes) => nodes,
Err(e) => { Err(e) => {
return Box::new(future::ok(Response::builder() return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST) .status(StatusCode::BAD_REQUEST)
.body(Body::from(format!("Could not read multipart body for MultipartRelatedRequestPost: {}", e))) .body(Body::from(format!("Could not read multipart body for MultipartRelatedRequestPost: {}", e)))
.expect("Unable to create Bad Request response due to unable to read multipart body for MultipartRelatedRequestPost"))); .expect("Unable to create Bad Request response due to unable to read multipart body for MultipartRelatedRequestPost"));
} }
}; };
@ -195,10 +211,10 @@ where
unused_elements.push(path.to_string()); unused_elements.push(path.to_string());
}) { }) {
Ok(json_data) => json_data, Ok(json_data) => json_data,
Err(e) => return Box::new(future::ok(Response::builder() Err(e) => return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST) .status(StatusCode::BAD_REQUEST)
.body(Body::from(format!("Couldn't parse body parameter models::MultipartRequestObjectField - doesn't match schema: {}", e))) .body(Body::from(format!("Couldn't parse body parameter models::MultipartRequestObjectField - doesn't match schema: {}", e)))
.expect("Unable to create Bad Request response for invalid body parameter models::MultipartRequestObjectField due to schema"))) .expect("Unable to create Bad Request response for invalid body parameter models::MultipartRequestObjectField due to schema"))
}; };
// Push JSON part to return object. // Push JSON part to return object.
param_object_field.get_or_insert(json_data); param_object_field.get_or_insert(json_data);
@ -226,21 +242,20 @@ where
// Check that the required multipart chunks are present. // Check that the required multipart chunks are present.
let param_required_binary_field = match param_required_binary_field { let param_required_binary_field = match param_required_binary_field {
Some(x) => x, Some(x) => x,
None => return Box::new(future::ok(Response::builder() None => return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST) .status(StatusCode::BAD_REQUEST)
.body(Body::from(format!("Missing required multipart/related parameter required_binary_field"))) .body(Body::from(format!("Missing required multipart/related parameter required_binary_field")))
.expect("Unable to create Bad Request response for missing multipart/related parameter required_binary_field due to schema"))) .expect("Unable to create Bad Request response for missing multipart/related parameter required_binary_field due to schema"))
}; };
Box::new( let result = api_impl.multipart_related_request_post(
api_impl.multipart_related_request_post(
param_required_binary_field, param_required_binary_field,
param_object_field, param_object_field,
param_optional_binary_field, param_optional_binary_field,
&context &context
).then(move |result| { ).await;
let mut response = Response::new(Body::empty()); let mut response = Response::new(Body::empty());
response.headers_mut().insert( response.headers_mut().insert(
HeaderName::from_static("x-span-id"), HeaderName::from_static("x-span-id"),
HeaderValue::from_str((&context as &dyn Has<XSpanIdString>).get().0.clone().to_string().as_str()) HeaderValue::from_str((&context as &dyn Has<XSpanIdString>).get().0.clone().to_string().as_str())
.expect("Unable to create X-Span-ID header value")); .expect("Unable to create X-Span-ID header value"));
@ -260,35 +275,30 @@ where
}, },
} }
future::ok(response) Ok(response)
}
))
}, },
Err(e) => Box::new(future::ok(Response::builder() Err(e) => Ok(Response::builder()
.status(StatusCode::BAD_REQUEST) .status(StatusCode::BAD_REQUEST)
.body(Body::from(format!("Couldn't read body parameter Default: {}", e))) .body(Body::from(format!("Couldn't read body parameter Default: {}", e)))
.expect("Unable to create Bad Request response due to unable to read body parameter Default"))), .expect("Unable to create Bad Request response due to unable to read body parameter Default")),
} }
})
) as Self::Future
}, },
// MultipartRequestPost - POST /multipart_request // MultipartRequestPost - POST /multipart_request
&hyper::Method::POST if path.matched(paths::ID_MULTIPART_REQUEST) => { &hyper::Method::POST if path.matched(paths::ID_MULTIPART_REQUEST) => {
let boundary = match swagger::multipart::boundary(&headers) { let boundary = match swagger::multipart::boundary(&headers) {
Some(boundary) => boundary.to_string(), Some(boundary) => boundary.to_string(),
None => return Box::new(future::ok(Response::builder() None => return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST) .status(StatusCode::BAD_REQUEST)
.body(Body::from("Couldn't find valid multipart body".to_string())) .body(Body::from("Couldn't find valid multipart body".to_string()))
.expect("Unable to create Bad Request response for incorrect boundary"))), .expect("Unable to create Bad Request response for incorrect boundary")),
}; };
// Form Body parameters (note that non-required body parameters will ignore garbage // Form Body parameters (note that non-required body parameters will ignore garbage
// values, rather than causing a 400 response). Produce warning header and logs for // values, rather than causing a 400 response). Produce warning header and logs for
// any unused fields. // any unused fields.
Box::new(body.concat2() let result = body.to_raw();
.then(move |result| -> Self::Future { match result.await {
match result {
Ok(body) => { Ok(body) => {
use std::io::Read; use std::io::Read;
@ -298,10 +308,10 @@ where
entries entries
}, },
_ => { _ => {
return Box::new(future::ok(Response::builder() return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST) .status(StatusCode::BAD_REQUEST)
.body(Body::from(format!("Unable to process all message parts"))) .body(Body::from(format!("Unable to process all message parts")))
.expect("Unable to create Bad Request response due to failure to process all message"))) .expect("Unable to create Bad Request response due to failure to process all message"))
}, },
}; };
let field_string_field = entries.fields.remove("string_field"); let field_string_field = entries.fields.remove("string_field");
@ -313,21 +323,21 @@ where
let string_field_model: String = match serde_json::from_str(&data) { let string_field_model: String = match serde_json::from_str(&data) {
Ok(model) => model, Ok(model) => model,
Err(e) => { Err(e) => {
return Box::new(future::ok( return Ok(
Response::builder() Response::builder()
.status(StatusCode::BAD_REQUEST) .status(StatusCode::BAD_REQUEST)
.body(Body::from(format!("string_field data does not match API definition : {}", e))) .body(Body::from(format!("string_field data does not match API definition : {}", e)))
.expect("Unable to create Bad Request due to missing required form parameter string_field"))) .expect("Unable to create Bad Request due to missing required form parameter string_field"))
} }
}; };
string_field_model string_field_model
}, },
None => { None => {
return Box::new(future::ok( return Ok(
Response::builder() Response::builder()
.status(StatusCode::BAD_REQUEST) .status(StatusCode::BAD_REQUEST)
.body(Body::from(format!("Missing required form parameter string_field"))) .body(Body::from(format!("Missing required form parameter string_field")))
.expect("Unable to create Bad Request due to missing required form parameter string_field"))) .expect("Unable to create Bad Request due to missing required form parameter string_field"))
} }
}; };
let field_optional_string_field = entries.fields.remove("optional_string_field"); let field_optional_string_field = entries.fields.remove("optional_string_field");
@ -340,11 +350,11 @@ where
let optional_string_field_model: String = match serde_json::from_str(&data) { let optional_string_field_model: String = match serde_json::from_str(&data) {
Ok(model) => model, Ok(model) => model,
Err(e) => { Err(e) => {
return Box::new(future::ok( return Ok(
Response::builder() Response::builder()
.status(StatusCode::BAD_REQUEST) .status(StatusCode::BAD_REQUEST)
.body(Body::from(format!("optional_string_field data does not match API definition : {}", e))) .body(Body::from(format!("optional_string_field data does not match API definition : {}", e)))
.expect("Unable to create Bad Request due to missing required form parameter optional_string_field"))) .expect("Unable to create Bad Request due to missing required form parameter optional_string_field"))
} }
}; };
optional_string_field_model optional_string_field_model
@ -364,11 +374,11 @@ where
let object_field_model: models::MultipartRequestObjectField = match serde_json::from_str(&data) { let object_field_model: models::MultipartRequestObjectField = match serde_json::from_str(&data) {
Ok(model) => model, Ok(model) => model,
Err(e) => { Err(e) => {
return Box::new(future::ok( return Ok(
Response::builder() Response::builder()
.status(StatusCode::BAD_REQUEST) .status(StatusCode::BAD_REQUEST)
.body(Body::from(format!("object_field data does not match API definition : {}", e))) .body(Body::from(format!("object_field data does not match API definition : {}", e)))
.expect("Unable to create Bad Request due to missing required form parameter object_field"))) .expect("Unable to create Bad Request due to missing required form parameter object_field"))
} }
}; };
object_field_model object_field_model
@ -387,23 +397,22 @@ where
swagger::ByteArray(data) swagger::ByteArray(data)
}, },
None => { None => {
return Box::new(future::ok( return Ok(
Response::builder() Response::builder()
.status(StatusCode::BAD_REQUEST) .status(StatusCode::BAD_REQUEST)
.body(Body::from(format!("Missing required form parameter binary_field"))) .body(Body::from(format!("Missing required form parameter binary_field")))
.expect("Unable to create Bad Request due to missing required form parameter binary_field"))) .expect("Unable to create Bad Request due to missing required form parameter binary_field"))
} }
}; };
Box::new( let result = api_impl.multipart_request_post(
api_impl.multipart_request_post(
param_string_field, param_string_field,
param_binary_field, param_binary_field,
param_optional_string_field, param_optional_string_field,
param_object_field, param_object_field,
&context &context
).then(move |result| { ).await;
let mut response = Response::new(Body::empty()); let mut response = Response::new(Body::empty());
response.headers_mut().insert( response.headers_mut().insert(
HeaderName::from_static("x-span-id"), HeaderName::from_static("x-span-id"),
HeaderValue::from_str((&context as &dyn Has<XSpanIdString>).get().0.clone().to_string().as_str()) HeaderValue::from_str((&context as &dyn Has<XSpanIdString>).get().0.clone().to_string().as_str())
.expect("Unable to create X-Span-ID header value")); .expect("Unable to create X-Span-ID header value"));
@ -423,18 +432,13 @@ where
}, },
} }
future::ok(response) Ok(response)
}
))
as Self::Future
}, },
Err(e) => Box::new(future::ok(Response::builder() Err(e) => Ok(Response::builder()
.status(StatusCode::BAD_REQUEST) .status(StatusCode::BAD_REQUEST)
.body(Body::from(format!("Couldn't read multipart body"))) .body(Body::from(format!("Couldn't read multipart body")))
.expect("Unable to create Bad Request response due to unable read multipart body"))), .expect("Unable to create Bad Request response due to unable read multipart body")),
} }
})
)
}, },
// MultipleIdenticalMimeTypesPost - POST /multiple-identical-mime-types // MultipleIdenticalMimeTypesPost - POST /multiple-identical-mime-types
@ -442,9 +446,8 @@ where
// Body parameters (note that non-required body parameters will ignore garbage // Body parameters (note that non-required body parameters will ignore garbage
// values, rather than causing a 400 response). Produce warning header and logs for // values, rather than causing a 400 response). Produce warning header and logs for
// any unused fields. // any unused fields.
Box::new(body.concat2() let result = body.to_raw();
.then(move |result| -> Self::Future { match result.await {
match result {
Ok(body) => { Ok(body) => {
let mut unused_elements: Vec<String> = vec![]; let mut unused_elements: Vec<String> = vec![];
@ -464,10 +467,10 @@ where
multi_part_headers.set(ContentType(content_type_mime)); multi_part_headers.set(ContentType(content_type_mime));
}, },
Err(e) => { Err(e) => {
return Box::new(future::ok(Response::builder() return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST) .status(StatusCode::BAD_REQUEST)
.body(Body::from(e)) .body(Body::from(e))
.expect("Unable to create Bad Request response due to unable to read content-type header for MultipleIdenticalMimeTypesPost"))); .expect("Unable to create Bad Request response due to unable to read content-type header for MultipleIdenticalMimeTypesPost"));
} }
} }
@ -476,10 +479,10 @@ where
let nodes = match read_multipart_body(&mut&*body, &multi_part_headers, false) { let nodes = match read_multipart_body(&mut&*body, &multi_part_headers, false) {
Ok(nodes) => nodes, Ok(nodes) => nodes,
Err(e) => { Err(e) => {
return Box::new(future::ok(Response::builder() return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST) .status(StatusCode::BAD_REQUEST)
.body(Body::from(format!("Could not read multipart body for MultipleIdenticalMimeTypesPost: {}", e))) .body(Body::from(format!("Could not read multipart body for MultipleIdenticalMimeTypesPost: {}", e)))
.expect("Unable to create Bad Request response due to unable to read multipart body for MultipleIdenticalMimeTypesPost"))); .expect("Unable to create Bad Request response due to unable to read multipart body for MultipleIdenticalMimeTypesPost"));
} }
}; };
@ -512,14 +515,13 @@ where
// Check that the required multipart chunks are present. // Check that the required multipart chunks are present.
Box::new( let result = api_impl.multiple_identical_mime_types_post(
api_impl.multiple_identical_mime_types_post(
param_binary1, param_binary1,
param_binary2, param_binary2,
&context &context
).then(move |result| { ).await;
let mut response = Response::new(Body::empty()); let mut response = Response::new(Body::empty());
response.headers_mut().insert( response.headers_mut().insert(
HeaderName::from_static("x-span-id"), HeaderName::from_static("x-span-id"),
HeaderValue::from_str((&context as &dyn Has<XSpanIdString>).get().0.clone().to_string().as_str()) HeaderValue::from_str((&context as &dyn Has<XSpanIdString>).get().0.clone().to_string().as_str())
.expect("Unable to create X-Span-ID header value")); .expect("Unable to create X-Span-ID header value"));
@ -539,39 +541,23 @@ where
}, },
} }
future::ok(response) Ok(response)
}
))
}, },
Err(e) => Box::new(future::ok(Response::builder() Err(e) => Ok(Response::builder()
.status(StatusCode::BAD_REQUEST) .status(StatusCode::BAD_REQUEST)
.body(Body::from(format!("Couldn't read body parameter Default: {}", e))) .body(Body::from(format!("Couldn't read body parameter Default: {}", e)))
.expect("Unable to create Bad Request response due to unable to read body parameter Default"))), .expect("Unable to create Bad Request response due to unable to read body parameter Default")),
} }
})
) as Self::Future
}, },
_ if path.matched(paths::ID_MULTIPART_RELATED_REQUEST) => method_not_allowed(), _ if path.matched(paths::ID_MULTIPART_RELATED_REQUEST) => method_not_allowed(),
_ if path.matched(paths::ID_MULTIPART_REQUEST) => method_not_allowed(), _ if path.matched(paths::ID_MULTIPART_REQUEST) => method_not_allowed(),
_ if path.matched(paths::ID_MULTIPLE_IDENTICAL_MIME_TYPES) => method_not_allowed(), _ if path.matched(paths::ID_MULTIPLE_IDENTICAL_MIME_TYPES) => method_not_allowed(),
_ => Box::new(future::ok( _ => Ok(Response::builder().status(StatusCode::NOT_FOUND)
Response::builder().status(StatusCode::NOT_FOUND)
.body(Body::empty()) .body(Body::empty())
.expect("Unable to create Not Found response") .expect("Unable to create Not Found response"))
)) as Self::Future
} }
} } Box::pin(run(self.api_impl.clone(), req)) }
}
impl<T, C> Clone for Service<T, C> where T: Clone
{
fn clone(&self) -> Self {
Service {
api_impl: self.api_impl.clone(),
marker: self.marker.clone(),
}
}
} }
/// Request parser for `Api`. /// Request parser for `Api`.

View File

@ -9,7 +9,7 @@ edition = "2018"
[features] [features]
default = ["client", "server"] default = ["client", "server"]
client = [ client = [
"hyper", "hyper-openssl", "native-tls", "openssl", "url" "hyper", "hyper-openssl", "hyper-tls", "native-tls", "openssl", "url"
] ]
server = [ server = [
"serde_ignored", "hyper", "regex", "percent-encoding", "url", "lazy_static" "serde_ignored", "hyper", "regex", "percent-encoding", "url", "lazy_static"
@ -18,35 +18,37 @@ conversion = ["frunk", "frunk_derives", "frunk_core", "frunk-enum-core", "frunk-
[target.'cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))'.dependencies] [target.'cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))'.dependencies]
native-tls = { version = "0.2", optional = true } native-tls = { version = "0.2", optional = true }
hyper-tls = { version = "0.4", optional = true }
[target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dependencies] [target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dependencies]
hyper-openssl = { version = "0.7.1", optional = true } hyper-openssl = { version = "0.8", optional = true }
openssl = {version = "0.10", optional = true } openssl = {version = "0.10", optional = true }
[dependencies] [dependencies]
# Common # Common
async-trait = "0.1.24"
chrono = { version = "0.4", features = ["serde"] } chrono = { version = "0.4", features = ["serde"] }
futures = "0.1" futures = "0.3"
swagger = "4.0" swagger = "5.0.0-alpha-1"
log = "0.4.0" log = "0.4.0"
mime = "0.3" mime = "0.3"
serde = { version = "1.0", features = ["derive"]} serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"
# Crates included if required by the API definition # Crates included if required by the API definition
# Common between server and client features # Common between server and client features
hyper = {version = "0.12", optional = true} hyper = {version = "0.13", optional = true}
serde_ignored = {version = "0.0.4", optional = true} serde_ignored = {version = "0.1.1", optional = true}
url = {version = "1.5", optional = true} url = {version = "2.1", optional = true}
# Client-specific # Client-specific
# Server, and client callback-specific # Server, and client callback-specific
lazy_static = { version = "1.4", optional = true } lazy_static = { version = "1.4", optional = true }
percent-encoding = {version = "1.0.0", optional = true} percent-encoding = {version = "2.1.0", optional = true}
regex = {version = "0.2", optional = true} regex = {version = "1.3", optional = true}
# Conversion # Conversion
frunk = { version = "0.3.0", optional = true } frunk = { version = "0.3.0", optional = true }
@ -57,13 +59,13 @@ frunk-enum-core = { version = "0.2.0", optional = true }
[dev-dependencies] [dev-dependencies]
clap = "2.25" clap = "2.25"
error-chain = "0.12" env_logger = "0.7"
env_logger = "0.6" tokio = { version = "0.2", features = ["rt-threaded", "macros", "stream"] }
tokio = "0.1.17" native-tls = "0.2"
uuid = {version = "0.7", features = ["serde", "v4"]} tokio-tls = "0.3"
[target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dev-dependencies] [target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dev-dependencies]
tokio-openssl = "0.3" tokio-openssl = "0.4"
openssl = "0.10" openssl = "0.10"
[[example]] [[example]]

View File

@ -2,11 +2,10 @@
#[allow(unused_imports)] #[allow(unused_imports)]
use futures::{Future, future, Stream, stream}; use futures::{future, Stream, stream};
#[allow(unused_imports)] #[allow(unused_imports)]
use no_example_v3::{Api, ApiNoContext, Client, ContextWrapperExt, models, use no_example_v3::{Api, ApiNoContext, Client, ContextWrapperExt, models,
ApiError, OpGetResponse,
OpGetResponse
}; };
use clap::{App, Arg}; use clap::{App, Arg};
@ -15,7 +14,9 @@ use log::info;
// swagger::Has may be unused if there are no examples // swagger::Has may be unused if there are no examples
#[allow(unused_imports)] #[allow(unused_imports)]
use swagger::{ContextBuilder, EmptyContext, XSpanIdString, Has, Push, AuthData}; use swagger::{AuthData, ContextBuilder, EmptyContext, Has, Push, XSpanIdString};
type ClientContext = swagger::make_context_ty!(ContextBuilder, EmptyContext, Option<AuthData>, XSpanIdString);
// rt may be unused if there are no examples // rt may be unused if there are no examples
#[allow(unused_mut)] #[allow(unused_mut)]
@ -50,21 +51,21 @@ fn main() {
matches.value_of("host").unwrap(), matches.value_of("host").unwrap(),
matches.value_of("port").unwrap()); matches.value_of("port").unwrap());
let client = if matches.is_present("https") { let context: ClientContext =
// Using Simple HTTPS
Client::try_new_https(&base_url)
.expect("Failed to create HTTPS client")
} else {
// Using HTTP
Client::try_new_http(
&base_url)
.expect("Failed to create HTTP client")
};
let context: swagger::make_context_ty!(ContextBuilder, EmptyContext, Option<AuthData>, XSpanIdString) =
swagger::make_context!(ContextBuilder, EmptyContext, None as Option<AuthData>, XSpanIdString::default()); swagger::make_context!(ContextBuilder, EmptyContext, None as Option<AuthData>, XSpanIdString::default());
let client = client.with_context(context); let mut client : Box<dyn ApiNoContext<ClientContext>> = if matches.is_present("https") {
// Using Simple HTTPS
let client = Box::new(Client::try_new_https(&base_url)
.expect("Failed to create HTTPS client"));
Box::new(client.with_context(context))
} else {
// Using HTTP
let client = Box::new(Client::try_new_http(
&base_url)
.expect("Failed to create HTTP client"));
Box::new(client.with_context(context))
};
let mut rt = tokio::runtime::Runtime::new().unwrap(); let mut rt = tokio::runtime::Runtime::new().unwrap();

View File

@ -9,7 +9,8 @@ mod server;
/// Create custom server, wire it to the autogenerated router, /// Create custom server, wire it to the autogenerated router,
/// and pass it to the web server. /// and pass it to the web server.
fn main() { #[tokio::main]
async fn main() {
env_logger::init(); env_logger::init();
let matches = App::new("server") let matches = App::new("server")
@ -20,5 +21,5 @@ fn main() {
let addr = "127.0.0.1:8080"; let addr = "127.0.0.1:8080";
hyper::rt::run(server::create(addr, matches.is_present("https"))); server::create(addr, matches.is_present("https")).await;
} }

View File

@ -2,30 +2,22 @@
#![allow(unused_imports)] #![allow(unused_imports)]
mod errors { use async_trait::async_trait;
error_chain::error_chain!{} use futures::{future, Stream, StreamExt, TryFutureExt, TryStreamExt};
}
pub use self::errors::*;
use chrono;
use futures::{future, Future, Stream};
use hyper::server::conn::Http; use hyper::server::conn::Http;
use hyper::service::MakeService as _; use hyper::service::Service;
use log::info; use log::info;
use openssl::ssl::SslAcceptorBuilder; use openssl::ssl::SslAcceptorBuilder;
use std::future::Future;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::net::SocketAddr; use std::net::SocketAddr;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use swagger; use std::task::{Context, Poll};
use swagger::{Has, XSpanIdString}; use swagger::{Has, XSpanIdString};
use swagger::auth::MakeAllowAllAuthenticator; use swagger::auth::MakeAllowAllAuthenticator;
use swagger::EmptyContext; use swagger::EmptyContext;
use tokio::net::TcpListener; use tokio::net::TcpListener;
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
use tokio_openssl::SslAcceptorExt;
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod}; use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod};
@ -33,18 +25,18 @@ use no_example_v3::models;
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
/// Builds an SSL implementation for Simple HTTPS from some hard-coded file names /// Builds an SSL implementation for Simple HTTPS from some hard-coded file names
pub fn create(addr: &str, https: bool) -> Box<dyn Future<Item = (), Error = ()> + Send> { pub async fn create(addr: &str, https: bool) {
let addr = addr.parse().expect("Failed to parse bind address"); let addr = addr.parse().expect("Failed to parse bind address");
let server = Server::new(); let server = Server::new();
let service_fn = MakeService::new(server); let service = MakeService::new(server);
let service_fn = MakeAllowAllAuthenticator::new(service_fn, "cosmo"); let service = MakeAllowAllAuthenticator::new(service, "cosmo");
let service_fn = let mut service =
no_example_v3::server::context::MakeAddContext::<_, EmptyContext>::new( no_example_v3::server::context::MakeAddContext::<_, EmptyContext>::new(
service_fn service
); );
if https { if https {
@ -62,32 +54,31 @@ pub fn create(addr: &str, https: bool) -> Box<dyn Future<Item = (), Error = ()>
ssl.set_certificate_chain_file("examples/server-chain.pem").expect("Failed to set cerificate chain"); ssl.set_certificate_chain_file("examples/server-chain.pem").expect("Failed to set cerificate chain");
ssl.check_private_key().expect("Failed to check private key"); ssl.check_private_key().expect("Failed to check private key");
let tls_acceptor = ssl.build(); let tls_acceptor = Arc::new(ssl.build());
let service_fn = Arc::new(Mutex::new(service_fn)); let mut tcp_listener = TcpListener::bind(&addr).await.unwrap();
let tls_listener = TcpListener::bind(&addr).unwrap().incoming().for_each(move |tcp| { let mut incoming = tcp_listener.incoming();
let addr = tcp.peer_addr().expect("Unable to get remote address");
let service_fn = service_fn.clone(); while let (Some(tcp), rest) = incoming.into_future().await {
if let Ok(tcp) = tcp {
let addr = tcp.peer_addr().expect("Unable to get remote address");
let service = service.call(addr);
let tls_acceptor = Arc::clone(&tls_acceptor);
hyper::rt::spawn(tls_acceptor.accept_async(tcp).map_err(|_| ()).and_then(move |tls| { tokio::spawn(async move {
let ms = { let tls = tokio_openssl::accept(&*tls_acceptor, tcp).await.map_err(|_| ())?;
let mut service_fn = service_fn.lock().unwrap();
service_fn.make_service(&addr)
};
ms.and_then(move |service| { let service = service.await.map_err(|_| ())?;
Http::new().serve_connection(tls, service)
}).map_err(|_| ())
}));
Ok(()) Http::new().serve_connection(tls, service).await.map_err(|_| ())
}).map_err(|_| ()); });
}
Box::new(tls_listener) incoming = rest;
}
} }
} else { } else {
// Using HTTP // Using HTTP
Box::new(hyper::server::Server::bind(&addr).serve(service_fn).map_err(|e| panic!("{:?}", e))) hyper::server::Server::bind(&addr).serve(service).await.unwrap()
} }
} }
@ -105,20 +96,23 @@ impl<C> Server<C> {
use no_example_v3::{ use no_example_v3::{
Api, Api,
ApiError,
OpGetResponse, OpGetResponse,
}; };
use no_example_v3::server::MakeService; use no_example_v3::server::MakeService;
use std::error::Error;
use swagger::ApiError;
impl<C> Api<C> for Server<C> where C: Has<XSpanIdString>{ #[async_trait]
fn op_get( impl<C> Api<C> for Server<C> where C: Has<XSpanIdString> + Send + Sync
{
async fn op_get(
&self, &self,
inline_object: models::InlineObject, inline_object: models::InlineObject,
context: &C) -> Box<dyn Future<Item=OpGetResponse, Error=ApiError> + Send> context: &C) -> Result<OpGetResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("op_get({:?}) - X-Span-ID: {:?}", inline_object, context.get().0.clone()); info!("op_get({:?}) - X-Span-ID: {:?}", inline_object, context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
} }

View File

@ -1,48 +1,49 @@
use futures; use async_trait::async_trait;
use futures::{Future, Stream, future, stream}; use futures::{Stream, future, future::BoxFuture, stream, future::TryFutureExt, future::FutureExt, stream::StreamExt};
use hyper;
use hyper::client::HttpConnector;
use hyper::header::{HeaderName, HeaderValue, CONTENT_TYPE}; use hyper::header::{HeaderName, HeaderValue, CONTENT_TYPE};
use hyper::{Body, Uri, Response}; use hyper::{Body, Request, Response, service::Service, Uri};
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] use percent_encoding::{utf8_percent_encode, AsciiSet};
use hyper_openssl::HttpsConnector;
use serde_json;
use std::borrow::Cow; use std::borrow::Cow;
use std::convert::TryInto; use std::convert::TryInto;
use std::io::{Read, Error, ErrorKind}; use std::io::{ErrorKind, Read};
use std::error; use std::error::Error;
use std::future::Future;
use std::fmt; use std::fmt;
use std::path::Path; use std::path::Path;
use std::sync::Arc; use std::sync::{Arc, Mutex};
use std::str; use std::str;
use std::str::FromStr; use std::str::FromStr;
use std::string::ToString; use std::string::ToString;
use swagger; use std::task::{Context, Poll};
use swagger::{ApiError, Connector, client::Service, XSpanIdString, Has, AuthData}; use swagger::{ApiError, AuthData, BodyExt, Connector, Has, XSpanIdString};
use url::form_urlencoded; use url::form_urlencoded;
use url::percent_encoding::{utf8_percent_encode, PATH_SEGMENT_ENCODE_SET, QUERY_ENCODE_SET};
use crate::models; use crate::models;
use crate::header; use crate::header;
url::define_encode_set! { /// https://url.spec.whatwg.org/#fragment-percent-encode-set
/// This encode set is used for object IDs #[allow(dead_code)]
/// const FRAGMENT_ENCODE_SET: &AsciiSet = &percent_encoding::CONTROLS
/// Aside from the special characters defined in the `PATH_SEGMENT_ENCODE_SET`, .add(b' ').add(b'"').add(b'<').add(b'>').add(b'`');
/// the vertical bar (|) is encoded.
pub ID_ENCODE_SET = [PATH_SEGMENT_ENCODE_SET] | {'|'} /// This encode set is used for object IDs
} ///
/// Aside from the special characters defined in the `PATH_SEGMENT_ENCODE_SET`,
/// the vertical bar (|) is encoded.
#[allow(dead_code)]
const ID_ENCODE_SET: &AsciiSet = &FRAGMENT_ENCODE_SET.add(b'|');
use crate::{Api, use crate::{Api,
OpGetResponse OpGetResponse
}; };
/// Convert input into a base path, e.g. "http://example:123". Also checks the scheme as it goes. /// Convert input into a base path, e.g. "http://example:123". Also checks the scheme as it goes.
fn into_base_path(input: &str, correct_scheme: Option<&'static str>) -> Result<String, ClientInitError> { fn into_base_path(input: impl TryInto<Uri, Error=hyper::http::uri::InvalidUri>, correct_scheme: Option<&'static str>) -> Result<String, ClientInitError> {
// First convert to Uri, since a base path is a subset of Uri. // First convert to Uri, since a base path is a subset of Uri.
let uri = Uri::from_str(input)?; let uri = input.try_into()?;
let scheme = uri.scheme_part().ok_or(ClientInitError::InvalidScheme)?; let scheme = uri.scheme_str().ok_or(ClientInitError::InvalidScheme)?;
// Check the scheme if necessary // Check the scheme if necessary
if let Some(correct_scheme) = correct_scheme { if let Some(correct_scheme) = correct_scheme {
@ -52,38 +53,54 @@ fn into_base_path(input: &str, correct_scheme: Option<&'static str>) -> Result<S
} }
let host = uri.host().ok_or_else(|| ClientInitError::MissingHost)?; let host = uri.host().ok_or_else(|| ClientInitError::MissingHost)?;
let port = uri.port_part().map(|x| format!(":{}", x)).unwrap_or_default(); let port = uri.port_u16().map(|x| format!(":{}", x)).unwrap_or_default();
Ok(format!("{}://{}{}{}", scheme, host, port, uri.path().trim_end_matches('/'))) Ok(format!("{}://{}{}{}", scheme, host, port, uri.path().trim_end_matches('/')))
} }
/// A client that implements the API by making HTTP calls out to a server. /// A client that implements the API by making HTTP calls out to a server.
pub struct Client<F> pub struct Client<S> where
S: Service<
Request<Body>,
Response=Response<Body>> + Clone + Sync + Send + 'static,
S::Future: Send + 'static,
S::Error: Into<crate::ServiceError> + fmt::Display,
{ {
/// Inner service /// Inner service
client_service: Arc<Box<dyn Service<ReqBody=Body, Future=F> + Send + Sync>>, client_service: S,
/// Base path of the API /// Base path of the API
base_path: String, base_path: String,
} }
impl<F> fmt::Debug for Client<F> impl<S> fmt::Debug for Client<S> where
S: Service<
Request<Body>,
Response=Response<Body>> + Clone + Sync + Send + 'static,
S::Future: Send + 'static,
S::Error: Into<crate::ServiceError> + fmt::Display,
{ {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Client {{ base_path: {} }}", self.base_path) write!(f, "Client {{ base_path: {} }}", self.base_path)
} }
} }
impl<F> Clone for Client<F> impl<S> Clone for Client<S> where
S: Service<
Request<Body>,
Response=Response<Body>> + Clone + Sync + Send + 'static,
S::Future: Send + 'static,
S::Error: Into<crate::ServiceError> + fmt::Display,
{ {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Client { Self {
client_service: self.client_service.clone(), client_service: self.client_service.clone(),
base_path: self.base_path.clone(), base_path: self.base_path.clone(),
} }
} }
} }
impl Client<hyper::client::ResponseFuture> impl<C> Client<hyper::client::Client<C, Body>> where
C: hyper::client::connect::Connect + Clone + Send + Sync + 'static
{ {
/// Create a client with a custom implementation of hyper::client::Connect. /// Create a client with a custom implementation of hyper::client::Connect.
/// ///
@ -96,30 +113,93 @@ impl Client<hyper::client::ResponseFuture>
/// ///
/// # Arguments /// # Arguments
/// ///
/// * `base_path` - base path of the client API, i.e. "www.my-api-implementation.com" /// * `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")` /// * `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 /// * `connector` - Implementation of `hyper::client::Connect` to use for the client
pub fn try_new_with_connector<C>( pub fn try_new_with_connector(
base_path: &str, base_path: &str,
protocol: Option<&'static str>, protocol: Option<&'static str>,
connector: C, connector: C,
) -> Result<Self, ClientInitError> where ) -> Result<Self, ClientInitError>
C: hyper::client::connect::Connect + 'static,
C::Transport: 'static,
C::Future: 'static,
{ {
let client_service = Box::new(hyper::client::Client::builder().build(connector)); let client_service = hyper::client::Client::builder().build(connector);
Ok(Client { Ok(Self {
client_service: Arc::new(client_service), client_service,
base_path: into_base_path(base_path, protocol)?, base_path: into_base_path(base_path, protocol)?,
}) })
} }
}
#[derive(Debug, Clone)]
pub enum HyperClient {
Http(hyper::client::Client<hyper::client::HttpConnector, Body>),
Https(hyper::client::Client<HttpsConnector, Body>),
}
impl Service<Request<Body>> for HyperClient {
type Response = Response<Body>;
type Error = hyper::Error;
type Future = hyper::client::ResponseFuture;
fn poll_ready(&mut self, cx: &mut Context) -> Poll<Result<(), Self::Error>> {
match self {
HyperClient::Http(client) => client.poll_ready(cx),
HyperClient::Https(client) => client.poll_ready(cx),
}
}
fn call(&mut self, req: Request<Body>) -> Self::Future {
match self {
HyperClient::Http(client) => client.call(req),
HyperClient::Https(client) => client.call(req)
}
}
}
impl Client<HyperClient> {
/// Create an HTTP client. /// Create an HTTP client.
/// ///
/// # Arguments /// # Arguments
/// * `base_path` - base path of the client API, i.e. "www.my-api-implementation.com" /// * `base_path` - base path of the client API, i.e. "http://www.my-api-implementation.com"
pub fn try_new(
base_path: &str,
) -> Result<Self, ClientInitError> {
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(|e| ClientInitError::SslError(e))?;
HyperClient::Https(hyper::client::Client::builder().build(connector))
},
_ => {
return Err(ClientInitError::InvalidScheme);
}
};
Ok(Self {
client_service,
base_path: into_base_path(base_path, None)?,
})
}
}
impl Client<hyper::client::Client<hyper::client::HttpConnector, Body>>
{
/// 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( pub fn try_new_http(
base_path: &str, base_path: &str,
) -> Result<Self, ClientInitError> { ) -> Result<Self, ClientInitError> {
@ -127,11 +207,20 @@ impl Client<hyper::client::ResponseFuture>
Self::try_new_with_connector(base_path, Some("http"), http_connector) 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<hyper::client::HttpConnector>;
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
type HttpsConnector = hyper_openssl::HttpsConnector<hyper::client::HttpConnector>;
impl Client<hyper::client::Client<HttpsConnector, Body>>
{
/// Create a client with a TLS connection to the server /// Create a client with a TLS connection to the server
/// ///
/// # Arguments /// # Arguments
/// * `base_path` - base path of the client API, i.e. "www.my-api-implementation.com" /// * `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<Self, ClientInitError> pub fn try_new_https(base_path: &str) -> Result<Self, ClientInitError>
{ {
let https_connector = Connector::builder() let https_connector = Connector::builder()
@ -144,7 +233,7 @@ impl Client<hyper::client::ResponseFuture>
/// Create a client with a TLS connection to the server using a pinned certificate /// Create a client with a TLS connection to the server using a pinned certificate
/// ///
/// # Arguments /// # Arguments
/// * `base_path` - base path of the client API, i.e. "www.my-api-implementation.com" /// * `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 /// * `ca_certificate` - Path to CA certificate used to authenticate the server
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
pub fn try_new_https_pinned<CA>( pub fn try_new_https_pinned<CA>(
@ -165,7 +254,7 @@ impl Client<hyper::client::ResponseFuture>
/// Create a client with a mutually authenticated TLS connection to the server. /// Create a client with a mutually authenticated TLS connection to the server.
/// ///
/// # Arguments /// # Arguments
/// * `base_path` - base path of the client API, i.e. "www.my-api-implementation.com" /// * `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 /// * `ca_certificate` - Path to CA certificate used to authenticate the server
/// * `client_key` - Path to the client private key /// * `client_key` - Path to the client private key
/// * `client_certificate` - Path to the client's public certificate associated with the private key /// * `client_certificate` - Path to the client's public certificate associated with the private key
@ -191,17 +280,24 @@ impl Client<hyper::client::ResponseFuture>
} }
} }
impl<F> Client<F> impl<S> Client<S> where
S: Service<
Request<Body>,
Response=Response<Body>> + Clone + Sync + Send + 'static,
S::Future: Send + 'static,
S::Error: Into<crate::ServiceError> + fmt::Display,
{ {
/// Constructor for creating a `Client` by passing in a pre-made `swagger::Service` /// 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. /// This allows adding custom wrappers around the underlying transport, for example for logging.
pub fn try_new_with_client_service( pub fn try_new_with_client_service(
client_service: Arc<Box<dyn Service<ReqBody=Body, Future=F> + Send + Sync>>, client_service: S,
base_path: &str, base_path: &str,
) -> Result<Self, ClientInitError> { ) -> Result<Self, ClientInitError>
Ok(Client { {
client_service: client_service, Ok(Self {
client_service,
base_path: into_base_path(base_path, None)?, base_path: into_base_path(base_path, None)?,
}) })
} }
@ -241,45 +337,61 @@ impl fmt::Display for ClientInitError {
} }
} }
impl error::Error for ClientInitError { impl Error for ClientInitError {
fn description(&self) -> &str { fn description(&self) -> &str {
"Failed to produce a hyper client." "Failed to produce a hyper client."
} }
} }
impl<C, F> Api<C> for Client<F> where #[async_trait]
C: Has<XSpanIdString> , impl<C, S> Api<C> for Client<S> where
F: Future<Item=Response<Body>, Error=hyper::Error> + Send + 'static C: Has<XSpanIdString> + Clone + Send + Sync + 'static,
S: Service<
Request<Body>,
Response=Response<Body>> + Clone + Sync + Send + 'static,
S::Future: Send + 'static,
S::Error: Into<crate::ServiceError> + fmt::Display,
{ {
fn op_get( fn poll_ready(&self, cx: &mut Context) -> Poll<Result<(), crate::ServiceError>> {
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,
}
}
async fn op_get(
&self, &self,
param_inline_object: models::InlineObject, param_inline_object: models::InlineObject,
context: &C) -> Box<dyn Future<Item=OpGetResponse, Error=ApiError> + Send> context: &C) -> Result<OpGetResponse, ApiError>
{ {
let mut client_service = self.client_service.clone();
let mut uri = format!( let mut uri = format!(
"{}/op", "{}/op",
self.base_path self.base_path
); );
// Query parameters // Query parameters
let mut query_string = url::form_urlencoded::Serializer::new("".to_owned()); let query_string = {
let query_string_str = query_string.finish(); let mut query_string = form_urlencoded::Serializer::new("".to_owned());
if !query_string_str.is_empty() { query_string.finish()
};
if !query_string.is_empty() {
uri += "?"; uri += "?";
uri += &query_string_str; uri += &query_string;
} }
let uri = match Uri::from_str(&uri) { let uri = match Uri::from_str(&uri) {
Ok(uri) => uri, Ok(uri) => uri,
Err(err) => return Box::new(future::err(ApiError(format!("Unable to build URI: {}", err)))), Err(err) => return Err(ApiError(format!("Unable to build URI: {}", err))),
}; };
let mut request = match hyper::Request::builder() let mut request = match Request::builder()
.method("GET") .method("GET")
.uri(uri) .uri(uri)
.body(Body::empty()) { .body(Body::empty()) {
Ok(req) => req, Ok(req) => req,
Err(e) => return Box::new(future::err(ApiError(format!("Unable to create request: {}", e)))) Err(e) => return Err(ApiError(format!("Unable to create request: {}", e)))
}; };
// Body parameter // Body parameter
@ -290,48 +402,43 @@ impl<C, F> Api<C> for Client<F> where
let header = "application/json"; let header = "application/json";
request.headers_mut().insert(CONTENT_TYPE, match HeaderValue::from_str(header) { request.headers_mut().insert(CONTENT_TYPE, match HeaderValue::from_str(header) {
Ok(h) => h, Ok(h) => h,
Err(e) => return Box::new(future::err(ApiError(format!("Unable to create header: {} - {}", header, e)))) Err(e) => return Err(ApiError(format!("Unable to create header: {} - {}", header, e)))
}); });
let header = HeaderValue::from_str((context as &dyn Has<XSpanIdString>).get().0.clone().to_string().as_str()); let header = HeaderValue::from_str(Has::<XSpanIdString>::get(context).0.clone().to_string().as_str());
request.headers_mut().insert(HeaderName::from_static("x-span-id"), match header { request.headers_mut().insert(HeaderName::from_static("x-span-id"), match header {
Ok(h) => h, Ok(h) => h,
Err(e) => return Box::new(future::err(ApiError(format!("Unable to create X-Span ID header value: {}", e)))) Err(e) => return Err(ApiError(format!("Unable to create X-Span ID header value: {}", e)))
}); });
Box::new(self.client_service.request(request) let mut response = client_service.call(request)
.map_err(|e| ApiError(format!("No response received: {}", e))) .map_err(|e| ApiError(format!("No response received: {}", e))).await?;
.and_then(|mut response| {
match response.status().as_u16() { match response.status().as_u16() {
200 => { 200 => {
let body = response.into_body(); let body = response.into_body();
Box::new( Ok(
future::ok( OpGetResponse::OK
OpGetResponse::OK )
)
) as Box<dyn Future<Item=_, Error=_> + Send>
},
code => {
let headers = response.headers().clone();
Box::new(response.into_body()
.take(100)
.concat2()
.then(move |body|
future::err(ApiError(format!("Unexpected response code {}:\n{:?}\n\n{}",
code,
headers,
match body {
Ok(ref body) => match str::from_utf8(body) {
Ok(body) => Cow::from(body),
Err(e) => Cow::from(format!("<Body was not UTF8: {:?}>", e)),
},
Err(e) => Cow::from(format!("<Failed to read body: {}>", e)),
})))
)
) as Box<dyn Future<Item=_, Error=_> + Send>
}
} }
})) code => {
let headers = response.headers().clone();
let body = response.into_body()
.take(100)
.to_raw().await;
Err(ApiError(format!("Unexpected response code {}:\n{:?}\n\n{}",
code,
headers,
match body {
Ok(body) => match String::from_utf8(body) {
Ok(body) => body,
Err(e) => format!("<Body was not UTF8: {:?}>", e),
},
Err(e) => format!("<Failed to read body: {}>", e),
}
)))
}
}
} }
} }

View File

@ -1,13 +1,12 @@
use futures::Future; use futures::future::BoxFuture;
use hyper;
use hyper::header::HeaderName; use hyper::header::HeaderName;
use hyper::{Error, Request, Response, StatusCode, service::Service, body::Payload}; use hyper::{Error, Request, Response, StatusCode, service::Service};
use url::form_urlencoded; use url::form_urlencoded;
use std::default::Default; use std::default::Default;
use std::io; use std::io;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::task::{Poll, Context};
use swagger::auth::{AuthData, Authorization, Bearer, Scopes}; use swagger::auth::{AuthData, Authorization, Bearer, Scopes};
use swagger::context::ContextualPayload;
use swagger::{EmptyContext, Has, Pop, Push, XSpanIdString}; use swagger::{EmptyContext, Has, Pop, Push, XSpanIdString};
use crate::Api; use crate::Api;
@ -31,58 +30,52 @@ where
} }
// Make a service that adds context. // Make a service that adds context.
impl<'a, T, SC, A, B, C, D, E, ME, S, OB, F> hyper::service::MakeService<&'a SC> for impl<Target, T, A, B, C, D> Service<Target> for
MakeAddContext<T, A> MakeAddContext<T, A>
where where
A: Default + Push<XSpanIdString, Result = B>, Target: Send,
A: Default + Push<XSpanIdString, Result = B> + Send,
B: Push<Option<AuthData>, Result = C>, B: Push<Option<AuthData>, Result = C>,
C: Push<Option<Authorization>, Result = D>, C: Push<Option<Authorization>, Result = D>,
D: Send + 'static, D: Send + 'static,
T: hyper::service::MakeService< T: Service<Target> + Send,
&'a SC, T::Future: Send + 'static
Error = E,
MakeError = ME,
Service = S,
ReqBody = ContextualPayload<hyper::Body, D>,
ResBody = OB,
Future = F
>,
S: Service<
Error = E,
ReqBody = ContextualPayload<hyper::Body, D>,
ResBody = OB> + 'static,
ME: swagger::ErrorBound,
E: swagger::ErrorBound,
F: Future<Item=S, Error=ME> + Send + 'static,
S::Future: Send,
OB: Payload,
{ {
type ReqBody = hyper::Body; type Error = T::Error;
type ResBody = OB; type Response = AddContext<T::Response, A, B, C, D>;
type Error = E; type Future = BoxFuture<'static, Result<Self::Response, Self::Error>>;
type MakeError = ME;
type Service = AddContext<S, A>;
type Future = Box<dyn Future<Item = Self::Service, Error = ME> + Send + 'static>;
fn make_service(&mut self, ctx: &'a SC) -> Self::Future { fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Box::new(self.inner.make_service(ctx).map(|s| AddContext::new(s))) self.inner.poll_ready(cx)
}
fn call(&mut self, target: Target) -> Self::Future {
let service = self.inner.call(target);
Box::pin(async move {
Ok(AddContext::new(service.await?))
})
} }
} }
/// Middleware to extract authentication data from request /// Middleware to add context data from the request
pub struct AddContext<T, A> { pub struct AddContext<T, A, B, C, D>
where
A: Default + Push<XSpanIdString, Result = B>,
B: Push<Option<AuthData>, Result = C>,
C: Push<Option<Authorization>, Result = D>
{
inner: T, inner: T,
marker: PhantomData<A>, marker: PhantomData<A>,
} }
impl<T, A, B, C, D> AddContext<T, A> impl<T, A, B, C, D> AddContext<T, A, B, C, D>
where where
A: Default + Push<XSpanIdString, Result = B>, A: Default + Push<XSpanIdString, Result = B>,
B: Push<Option<AuthData>, Result = C>, B: Push<Option<AuthData>, Result = C>,
C: Push<Option<Authorization>, Result = D>, C: Push<Option<Authorization>, Result = D>,
T: Service,
{ {
pub fn new(inner: T) -> AddContext<T, A> { pub fn new(inner: T) -> Self {
AddContext { AddContext {
inner, inner,
marker: PhantomData, marker: PhantomData,
@ -90,33 +83,31 @@ where
} }
} }
impl<T, A, B, C, D> Service for AddContext<T, A> impl<T, A, B, C, D, ReqBody> Service<Request<ReqBody>> for AddContext<T, A, B, C, D>
where where
A: Default + Push<XSpanIdString, Result=B>, A: Default + Push<XSpanIdString, Result=B>,
B: Push<Option<AuthData>, Result=C>, B: Push<Option<AuthData>, Result=C>,
C: Push<Option<Authorization>, Result=D>, C: Push<Option<Authorization>, Result=D>,
D: Send + 'static, D: Send + 'static,
T: Service<ReqBody = ContextualPayload<hyper::Body, D>>, T: Service<(Request<ReqBody>, D)>
T::Future: Future<Item=Response<T::ResBody>, Error=T::Error> + Send + 'static
{ {
type ReqBody = hyper::Body;
type ResBody = T::ResBody;
type Error = T::Error; type Error = T::Error;
type Future = Box<dyn Future<Item=Response<T::ResBody>, Error=T::Error> + Send + 'static>; type Future = T::Future;
type Response = T::Response;
fn call(&mut self, req: Request<Self::ReqBody>) -> Self::Future { fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
let context = A::default().push(XSpanIdString::get_or_generate(&req)); self.inner.poll_ready(cx)
let (head, body) = req.into_parts(); }
let headers = head.headers.clone();
fn call(&mut self, request: Request<ReqBody>) -> Self::Future {
let context = A::default().push(XSpanIdString::get_or_generate(&request));
let headers = request.headers();
let context = context.push(None::<AuthData>); let context = context.push(None::<AuthData>);
let context = context.push(None::<Authorization>); let context = context.push(None::<Authorization>);
let body = ContextualPayload {
inner: body,
context: context,
};
Box::new(self.inner.call(hyper::Request::from_parts(head, body))) self.inner.call((request, context))
} }
} }

View File

@ -1,12 +1,12 @@
#![allow(missing_docs, trivial_casts, unused_variables, unused_mut, unused_imports, unused_extern_crates, non_camel_case_types)] #![allow(missing_docs, trivial_casts, unused_variables, unused_mut, unused_imports, unused_extern_crates, non_camel_case_types)]
use async_trait::async_trait;
use futures::Stream; use futures::Stream;
use std::io::Error; use std::error::Error;
use std::task::{Poll, Context};
use swagger::{ApiError, ContextWrapper};
#[deprecated(note = "Import swagger-rs directly")] type ServiceError = Box<dyn Error + Send + Sync + 'static>;
pub use swagger::{ApiError, ContextWrapper};
#[deprecated(note = "Import futures directly")]
pub use futures::Future;
pub const BASE_PATH: &'static str = ""; pub const BASE_PATH: &'static str = "";
pub const API_VERSION: &'static str = "0.0.1"; pub const API_VERSION: &'static str = "0.0.1";
@ -18,46 +18,69 @@ pub enum OpGetResponse {
} }
/// API /// API
pub trait Api<C> { #[async_trait]
fn op_get( pub trait Api<C: Send + Sync> {
fn poll_ready(&self, _cx: &mut Context) -> Poll<Result<(), Box<dyn Error + Send + Sync + 'static>>> {
Poll::Ready(Ok(()))
}
async fn op_get(
&self, &self,
inline_object: models::InlineObject, inline_object: models::InlineObject,
context: &C) -> Box<dyn Future<Item=OpGetResponse, Error=ApiError> + Send>; context: &C) -> Result<OpGetResponse, ApiError>;
} }
/// API without a `Context` /// API where `Context` isn't passed on every API call
pub trait ApiNoContext { #[async_trait]
fn op_get( pub trait ApiNoContext<C: Send + Sync> {
fn poll_ready(&self, _cx: &mut Context) -> Poll<Result<(), Box<dyn Error + Send + Sync + 'static>>>;
fn context(&self) -> &C;
async fn op_get(
&self, &self,
inline_object: models::InlineObject, inline_object: models::InlineObject,
) -> Box<dyn Future<Item=OpGetResponse, Error=ApiError> + Send>; ) -> Result<OpGetResponse, ApiError>;
} }
/// Trait to extend an API to make it easy to bind it to a context. /// Trait to extend an API to make it easy to bind it to a context.
pub trait ContextWrapperExt<'a, C> where Self: Sized { pub trait ContextWrapperExt<C: Send + Sync> where Self: Sized
{
/// Binds this API to a context. /// Binds this API to a context.
fn with_context(self: &'a Self, context: C) -> ContextWrapper<'a, Self, C>; fn with_context(self: Self, context: C) -> ContextWrapper<Self, C>;
} }
impl<'a, T: Api<C> + Sized, C> ContextWrapperExt<'a, C> for T { impl<T: Api<C> + Send + Sync, C: Clone + Send + Sync> ContextWrapperExt<C> for T {
fn with_context(self: &'a T, context: C) -> ContextWrapper<'a, T, C> { fn with_context(self: T, context: C) -> ContextWrapper<T, C> {
ContextWrapper::<T, C>::new(self, context) ContextWrapper::<T, C>::new(self, context)
} }
} }
impl<'a, T: Api<C>, C> ApiNoContext for ContextWrapper<'a, T, C> { #[async_trait]
fn op_get( impl<T: Api<C> + Send + Sync, C: Clone + Send + Sync> ApiNoContext<C> for ContextWrapper<T, C> {
fn poll_ready(&self, cx: &mut Context) -> Poll<Result<(), ServiceError>> {
self.api().poll_ready(cx)
}
fn context(&self) -> &C {
ContextWrapper::context(self)
}
async fn op_get(
&self, &self,
inline_object: models::InlineObject, inline_object: models::InlineObject,
) -> Box<dyn Future<Item=OpGetResponse, Error=ApiError> + Send> ) -> Result<OpGetResponse, ApiError>
{ {
self.api().op_get(inline_object, &self.context()) let context = self.context().clone();
self.api().op_get(inline_object, &context).await
} }
} }
#[cfg(feature = "client")] #[cfg(feature = "client")]
pub mod client; pub mod client;

View File

@ -1,20 +1,17 @@
use std::marker::PhantomData; use futures::{future, future::BoxFuture, Stream, stream, future::FutureExt, stream::TryStreamExt};
use futures::{Future, future, Stream, stream}; use hyper::{Request, Response, StatusCode, Body, HeaderMap};
use hyper;
use hyper::{Request, Response, Error, StatusCode, Body, HeaderMap};
use hyper::header::{HeaderName, HeaderValue, CONTENT_TYPE}; use hyper::header::{HeaderName, HeaderValue, CONTENT_TYPE};
use log::warn; use log::warn;
use serde_json;
#[allow(unused_imports)] #[allow(unused_imports)]
use std::convert::{TryFrom, TryInto}; use std::convert::{TryFrom, TryInto};
use std::io; use std::error::Error;
use url::form_urlencoded; use std::future::Future;
#[allow(unused_imports)] use std::marker::PhantomData;
use swagger; use std::task::{Context, Poll};
use swagger::{ApiError, XSpanIdString, Has, RequestParser}; use swagger::{ApiError, BodyExt, Has, RequestParser, XSpanIdString};
pub use swagger::auth::Authorization; pub use swagger::auth::Authorization;
use swagger::auth::Scopes; use swagger::auth::Scopes;
use swagger::context::ContextualPayload; use url::form_urlencoded;
#[allow(unused_imports)] #[allow(unused_imports)]
use crate::models; use crate::models;
@ -22,6 +19,8 @@ use crate::header;
pub use crate::context; pub use crate::context;
type ServiceFuture = BoxFuture<'static, Result<Response<Body>, crate::ServiceError>>;
use crate::{Api, use crate::{Api,
OpGetResponse OpGetResponse
}; };
@ -38,15 +37,17 @@ mod paths {
pub(crate) static ID_OP: usize = 0; pub(crate) static ID_OP: usize = 0;
} }
pub struct MakeService<T, RC> { pub struct MakeService<T, C> where
T: Api<C> + Clone + Send + 'static,
C: Has<XSpanIdString> + Send + Sync + 'static
{
api_impl: T, api_impl: T,
marker: PhantomData<RC>, marker: PhantomData<C>,
} }
impl<T, RC> MakeService<T, RC> impl<T, C> MakeService<T, C> where
where T: Api<C> + Clone + Send + 'static,
T: Api<RC> + Clone + Send + 'static, C: Has<XSpanIdString> + Send + Sync + 'static
RC: Has<XSpanIdString> + 'static
{ {
pub fn new(api_impl: T) -> Self { pub fn new(api_impl: T) -> Self {
MakeService { MakeService {
@ -56,44 +57,45 @@ where
} }
} }
impl<'a, T, SC, RC> hyper::service::MakeService<&'a SC> for MakeService<T, RC> impl<T, C, Target> hyper::service::Service<Target> for MakeService<T, C> where
where T: Api<C> + Clone + Send + 'static,
T: Api<RC> + Clone + Send + 'static, C: Has<XSpanIdString> + Send + Sync + 'static
RC: Has<XSpanIdString> + 'static + Send
{ {
type ReqBody = ContextualPayload<Body, RC>; type Response = Service<T, C>;
type ResBody = Body; type Error = crate::ServiceError;
type Error = Error; type Future = future::Ready<Result<Self::Response, Self::Error>>;
type Service = Service<T, RC>;
type Future = future::FutureResult<Self::Service, Self::MakeError>;
type MakeError = Error;
fn make_service(&mut self, _ctx: &'a SC) -> Self::Future { fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
future::FutureResult::from(Ok(Service::new( Poll::Ready(Ok(()))
}
fn call(&mut self, target: Target) -> Self::Future {
futures::future::ok(Service::new(
self.api_impl.clone(), self.api_impl.clone(),
))) ))
} }
} }
type ServiceFuture = Box<dyn Future<Item = Response<Body>, Error = Error> + Send>; fn method_not_allowed() -> Result<Response<Body>, crate::ServiceError> {
Ok(
fn method_not_allowed() -> ServiceFuture {
Box::new(future::ok(
Response::builder().status(StatusCode::METHOD_NOT_ALLOWED) Response::builder().status(StatusCode::METHOD_NOT_ALLOWED)
.body(Body::empty()) .body(Body::empty())
.expect("Unable to create Method Not Allowed response") .expect("Unable to create Method Not Allowed response")
)) )
} }
pub struct Service<T, RC> { pub struct Service<T, C> where
T: Api<C> + Clone + Send + 'static,
C: Has<XSpanIdString> + Send + Sync + 'static
{
api_impl: T, api_impl: T,
marker: PhantomData<RC>, marker: PhantomData<C>,
} }
impl<T, RC> Service<T, RC> impl<T, C> Service<T, C> where
where T: Api<C> + Clone + Send + 'static,
T: Api<RC> + Clone + Send + 'static, C: Has<XSpanIdString> + Send + Sync + 'static
RC: Has<XSpanIdString> + 'static { {
pub fn new(api_impl: T) -> Self { pub fn new(api_impl: T) -> Self {
Service { Service {
api_impl: api_impl, api_impl: api_impl,
@ -102,23 +104,38 @@ where
} }
} }
impl<T, C> hyper::service::Service for Service<T, C> impl<T, C> Clone for Service<T, C> where
where
T: Api<C> + Clone + Send + 'static, T: Api<C> + Clone + Send + 'static,
C: Has<XSpanIdString> + 'static + Send C: Has<XSpanIdString> + Send + Sync + 'static
{ {
type ReqBody = ContextualPayload<Body, C>; fn clone(&self) -> Self {
type ResBody = Body; Service {
type Error = Error; api_impl: self.api_impl.clone(),
marker: self.marker.clone(),
}
}
}
impl<T, C> hyper::service::Service<(Request<Body>, C)> for Service<T, C> where
T: Api<C> + Clone + Send + Sync + 'static,
C: Has<XSpanIdString> + Send + Sync + 'static
{
type Response = Response<Body>;
type Error = crate::ServiceError;
type Future = ServiceFuture; type Future = ServiceFuture;
fn call(&mut self, req: Request<Self::ReqBody>) -> Self::Future { fn poll_ready(&mut self, cx: &mut Context) -> Poll<Result<(), Self::Error>> {
let api_impl = self.api_impl.clone(); self.api_impl.poll_ready(cx)
let (parts, body) = req.into_parts(); }
fn call(&mut self, req: (Request<Body>, C)) -> Self::Future { async fn run<T, C>(mut api_impl: T, req: (Request<Body>, C)) -> Result<Response<Body>, crate::ServiceError> where
T: Api<C> + Clone + Send + 'static,
C: Has<XSpanIdString> + Send + Sync + 'static
{
let (request, context) = req;
let (parts, body) = request.into_parts();
let (method, uri, headers) = (parts.method, parts.uri, parts.headers); let (method, uri, headers) = (parts.method, parts.uri, parts.headers);
let path = paths::GLOBAL_REGEX_SET.matches(uri.path()); let path = paths::GLOBAL_REGEX_SET.matches(uri.path());
let mut context = body.context;
let body = body.inner;
match &method { match &method {
@ -127,9 +144,8 @@ where
// Body parameters (note that non-required body parameters will ignore garbage // Body parameters (note that non-required body parameters will ignore garbage
// values, rather than causing a 400 response). Produce warning header and logs for // values, rather than causing a 400 response). Produce warning header and logs for
// any unused fields. // any unused fields.
Box::new(body.concat2() let result = body.to_raw().await;
.then(move |result| -> Self::Future { match result {
match result {
Ok(body) => { Ok(body) => {
let mut unused_elements = Vec::new(); let mut unused_elements = Vec::new();
let param_inline_object: Option<models::InlineObject> = if !body.is_empty() { let param_inline_object: Option<models::InlineObject> = if !body.is_empty() {
@ -139,29 +155,28 @@ where
unused_elements.push(path.to_string()); unused_elements.push(path.to_string());
}) { }) {
Ok(param_inline_object) => param_inline_object, Ok(param_inline_object) => param_inline_object,
Err(e) => return Box::new(future::ok(Response::builder() Err(e) => return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST) .status(StatusCode::BAD_REQUEST)
.body(Body::from(format!("Couldn't parse body parameter InlineObject - doesn't match schema: {}", e))) .body(Body::from(format!("Couldn't parse body parameter InlineObject - doesn't match schema: {}", e)))
.expect("Unable to create Bad Request response for invalid body parameter InlineObject due to schema"))), .expect("Unable to create Bad Request response for invalid body parameter InlineObject due to schema")),
} }
} else { } else {
None None
}; };
let param_inline_object = match param_inline_object { let param_inline_object = match param_inline_object {
Some(param_inline_object) => param_inline_object, Some(param_inline_object) => param_inline_object,
None => return Box::new(future::ok(Response::builder() None => return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST) .status(StatusCode::BAD_REQUEST)
.body(Body::from("Missing required body parameter InlineObject")) .body(Body::from("Missing required body parameter InlineObject"))
.expect("Unable to create Bad Request response for missing body parameter InlineObject"))), .expect("Unable to create Bad Request response for missing body parameter InlineObject")),
}; };
Box::new( let result = api_impl.op_get(
api_impl.op_get(
param_inline_object, param_inline_object,
&context &context
).then(move |result| { ).await;
let mut response = Response::new(Body::empty()); let mut response = Response::new(Body::empty());
response.headers_mut().insert( response.headers_mut().insert(
HeaderName::from_static("x-span-id"), HeaderName::from_static("x-span-id"),
HeaderValue::from_str((&context as &dyn Has<XSpanIdString>).get().0.clone().to_string().as_str()) HeaderValue::from_str((&context as &dyn Has<XSpanIdString>).get().0.clone().to_string().as_str())
.expect("Unable to create X-Span-ID header value")); .expect("Unable to create X-Span-ID header value"));
@ -188,37 +203,21 @@ where
}, },
} }
future::ok(response) Ok(response)
}
))
}, },
Err(e) => Box::new(future::ok(Response::builder() Err(e) => Ok(Response::builder()
.status(StatusCode::BAD_REQUEST) .status(StatusCode::BAD_REQUEST)
.body(Body::from(format!("Couldn't read body parameter InlineObject: {}", e))) .body(Body::from(format!("Couldn't read body parameter InlineObject: {}", e)))
.expect("Unable to create Bad Request response due to unable to read body parameter InlineObject"))), .expect("Unable to create Bad Request response due to unable to read body parameter InlineObject")),
} }
})
) as Self::Future
}, },
_ if path.matched(paths::ID_OP) => method_not_allowed(), _ if path.matched(paths::ID_OP) => method_not_allowed(),
_ => Box::new(future::ok( _ => Ok(Response::builder().status(StatusCode::NOT_FOUND)
Response::builder().status(StatusCode::NOT_FOUND)
.body(Body::empty()) .body(Body::empty())
.expect("Unable to create Not Found response") .expect("Unable to create Not Found response"))
)) as Self::Future
} }
} } Box::pin(run(self.api_impl.clone(), req)) }
}
impl<T, C> Clone for Service<T, C> where T: Clone
{
fn clone(&self) -> Self {
Service {
api_impl: self.api_impl.clone(),
marker: self.marker.clone(),
}
}
} }
/// Request parser for `Api`. /// Request parser for `Api`.

View File

@ -10,49 +10,51 @@ edition = "2018"
default = ["client", "server"] default = ["client", "server"]
client = [ client = [
"serde_ignored", "regex", "percent-encoding", "lazy_static", "serde_ignored", "regex", "percent-encoding", "lazy_static",
"hyper", "hyper-openssl", "native-tls", "openssl", "url" "hyper", "hyper-openssl", "hyper-tls", "native-tls", "openssl", "url"
] ]
server = [ server = [
"native-tls", "hyper-openssl", "openssl", "native-tls", "hyper-openssl", "hyper-tls", "openssl",
"serde_ignored", "hyper", "regex", "percent-encoding", "url", "lazy_static" "serde_ignored", "hyper", "regex", "percent-encoding", "url", "lazy_static"
] ]
conversion = ["frunk", "frunk_derives", "frunk_core", "frunk-enum-core", "frunk-enum-derive"] conversion = ["frunk", "frunk_derives", "frunk_core", "frunk-enum-core", "frunk-enum-derive"]
[target.'cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))'.dependencies] [target.'cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))'.dependencies]
native-tls = { version = "0.2", optional = true } native-tls = { version = "0.2", optional = true }
hyper-tls = { version = "0.4", optional = true }
[target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dependencies] [target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dependencies]
hyper-openssl = { version = "0.7.1", optional = true } hyper-openssl = { version = "0.8", optional = true }
openssl = {version = "0.10", optional = true } openssl = {version = "0.10", optional = true }
[dependencies] [dependencies]
# Common # Common
async-trait = "0.1.24"
chrono = { version = "0.4", features = ["serde"] } chrono = { version = "0.4", features = ["serde"] }
futures = "0.1" futures = "0.3"
swagger = "4.0" swagger = "5.0.0-alpha-1"
log = "0.4.0" log = "0.4.0"
mime = "0.3" mime = "0.3"
serde = { version = "1.0", features = ["derive"]} serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"
# Crates included if required by the API definition # Crates included if required by the API definition
# TODO: this should be updated to point at the official crate once # TODO: this should be updated to point at the official crate once
# https://github.com/RReverser/serde-xml-rs/pull/45 is accepted upstream # https://github.com/RReverser/serde-xml-rs/pull/45 is accepted upstream
serde-xml-rs = {git = "git://github.com/Metaswitch/serde-xml-rs.git" , branch = "master"} serde-xml-rs = {git = "git://github.com/Metaswitch/serde-xml-rs.git" , branch = "master"}
uuid = {version = "0.7", features = ["serde", "v4"]} uuid = {version = "0.8", features = ["serde", "v4"]}
# Common between server and client features # Common between server and client features
hyper = {version = "0.12", optional = true} hyper = {version = "0.13", optional = true}
serde_ignored = {version = "0.0.4", optional = true} serde_ignored = {version = "0.1.1", optional = true}
url = {version = "1.5", optional = true} url = {version = "2.1", optional = true}
# Client-specific # Client-specific
# Server, and client callback-specific # Server, and client callback-specific
lazy_static = { version = "1.4", optional = true } lazy_static = { version = "1.4", optional = true }
percent-encoding = {version = "1.0.0", optional = true} percent-encoding = {version = "2.1.0", optional = true}
regex = {version = "0.2", optional = true} regex = {version = "1.3", optional = true}
# Conversion # Conversion
frunk = { version = "0.3.0", optional = true } frunk = { version = "0.3.0", optional = true }
@ -63,12 +65,13 @@ frunk-enum-core = { version = "0.2.0", optional = true }
[dev-dependencies] [dev-dependencies]
clap = "2.25" clap = "2.25"
error-chain = "0.12" env_logger = "0.7"
env_logger = "0.6" tokio = { version = "0.2", features = ["rt-threaded", "macros", "stream"] }
tokio = "0.1.17" native-tls = "0.2"
tokio-tls = "0.3"
[target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dev-dependencies] [target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dev-dependencies]
tokio-openssl = "0.3" tokio-openssl = "0.4"
openssl = "0.10" openssl = "0.10"
[[example]] [[example]]

View File

@ -3,10 +3,9 @@
mod server; mod server;
#[allow(unused_imports)] #[allow(unused_imports)]
use futures::{Future, future, Stream, stream}; use futures::{future, Stream, stream};
#[allow(unused_imports)] #[allow(unused_imports)]
use openapi_v3::{Api, ApiNoContext, Client, ContextWrapperExt, models, use openapi_v3::{Api, ApiNoContext, Client, ContextWrapperExt, models,
ApiError,
CallbackWithHeaderPostResponse, CallbackWithHeaderPostResponse,
ComplexQueryParamGetResponse, ComplexQueryParamGetResponse,
EnumInPathPathParamGetResponse, EnumInPathPathParamGetResponse,
@ -29,7 +28,7 @@ use openapi_v3::{Api, ApiNoContext, Client, ContextWrapperExt, models,
XmlPostResponse, XmlPostResponse,
XmlPutResponse, XmlPutResponse,
CreateRepoResponse, CreateRepoResponse,
GetRepoInfoResponse GetRepoInfoResponse,
}; };
use clap::{App, Arg}; use clap::{App, Arg};
@ -38,7 +37,9 @@ use log::info;
// swagger::Has may be unused if there are no examples // swagger::Has may be unused if there are no examples
#[allow(unused_imports)] #[allow(unused_imports)]
use swagger::{ContextBuilder, EmptyContext, XSpanIdString, Has, Push, AuthData}; use swagger::{AuthData, ContextBuilder, EmptyContext, Has, Push, XSpanIdString};
type ClientContext = swagger::make_context_ty!(ContextBuilder, EmptyContext, Option<AuthData>, XSpanIdString);
// rt may be unused if there are no examples // rt may be unused if there are no examples
#[allow(unused_mut)] #[allow(unused_mut)]
@ -95,21 +96,21 @@ fn main() {
matches.value_of("host").unwrap(), matches.value_of("host").unwrap(),
matches.value_of("port").unwrap()); matches.value_of("port").unwrap());
let client = if matches.is_present("https") { let context: ClientContext =
// Using Simple HTTPS
Client::try_new_https(&base_url)
.expect("Failed to create HTTPS client")
} else {
// Using HTTP
Client::try_new_http(
&base_url)
.expect("Failed to create HTTP client")
};
let context: swagger::make_context_ty!(ContextBuilder, EmptyContext, Option<AuthData>, XSpanIdString) =
swagger::make_context!(ContextBuilder, EmptyContext, None as Option<AuthData>, XSpanIdString::default()); swagger::make_context!(ContextBuilder, EmptyContext, None as Option<AuthData>, XSpanIdString::default());
let client = client.with_context(context); let mut client : Box<dyn ApiNoContext<ClientContext>> = if matches.is_present("https") {
// Using Simple HTTPS
let client = Box::new(Client::try_new_https(&base_url)
.expect("Failed to create HTTPS client"));
Box::new(client.with_context(context))
} else {
// Using HTTP
let client = Box::new(Client::try_new_http(
&base_url)
.expect("Failed to create HTTP client"));
Box::new(client.with_context(context))
};
let mut rt = tokio::runtime::Runtime::new().unwrap(); let mut rt = tokio::runtime::Runtime::new().unwrap();

View File

@ -2,30 +2,22 @@
#![allow(unused_imports)] #![allow(unused_imports)]
mod errors { use async_trait::async_trait;
error_chain::error_chain!{} use futures::{future, Stream, StreamExt, TryFutureExt, TryStreamExt};
}
pub use self::errors::*;
use chrono;
use futures::{future, Future, Stream};
use hyper::server::conn::Http; use hyper::server::conn::Http;
use hyper::service::MakeService as _; use hyper::service::Service;
use log::info; use log::info;
use openssl::ssl::SslAcceptorBuilder; use openssl::ssl::SslAcceptorBuilder;
use std::future::Future;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::net::SocketAddr; use std::net::SocketAddr;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use swagger; use std::task::{Context, Poll};
use swagger::{Has, XSpanIdString}; use swagger::{Has, XSpanIdString};
use swagger::auth::MakeAllowAllAuthenticator; use swagger::auth::MakeAllowAllAuthenticator;
use swagger::EmptyContext; use swagger::EmptyContext;
use tokio::net::TcpListener; use tokio::net::TcpListener;
use uuid;
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
use tokio_openssl::SslAcceptorExt;
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod}; use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod};
@ -33,18 +25,18 @@ use openapi_v3::models;
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
/// Builds an SSL implementation for Simple HTTPS from some hard-coded file names /// Builds an SSL implementation for Simple HTTPS from some hard-coded file names
pub fn create(addr: &str, https: bool) -> Box<dyn Future<Item = (), Error = ()> + Send> { pub async fn create(addr: &str, https: bool) {
let addr = addr.parse().expect("Failed to parse bind address"); let addr = addr.parse().expect("Failed to parse bind address");
let server = Server::new(); let server = Server::new();
let service_fn = MakeService::new(server); let service = MakeService::new(server);
let service_fn = MakeAllowAllAuthenticator::new(service_fn, "cosmo"); let service = MakeAllowAllAuthenticator::new(service, "cosmo");
let service_fn = let mut service =
openapi_v3::server::context::MakeAddContext::<_, EmptyContext>::new( openapi_v3::server::context::MakeAddContext::<_, EmptyContext>::new(
service_fn service
); );
if https { if https {
@ -62,32 +54,31 @@ pub fn create(addr: &str, https: bool) -> Box<dyn Future<Item = (), Error = ()>
ssl.set_certificate_chain_file("examples/server-chain.pem").expect("Failed to set cerificate chain"); ssl.set_certificate_chain_file("examples/server-chain.pem").expect("Failed to set cerificate chain");
ssl.check_private_key().expect("Failed to check private key"); ssl.check_private_key().expect("Failed to check private key");
let tls_acceptor = ssl.build(); let tls_acceptor = Arc::new(ssl.build());
let service_fn = Arc::new(Mutex::new(service_fn)); let mut tcp_listener = TcpListener::bind(&addr).await.unwrap();
let tls_listener = TcpListener::bind(&addr).unwrap().incoming().for_each(move |tcp| { let mut incoming = tcp_listener.incoming();
let addr = tcp.peer_addr().expect("Unable to get remote address");
let service_fn = service_fn.clone(); while let (Some(tcp), rest) = incoming.into_future().await {
if let Ok(tcp) = tcp {
let addr = tcp.peer_addr().expect("Unable to get remote address");
let service = service.call(addr);
let tls_acceptor = Arc::clone(&tls_acceptor);
hyper::rt::spawn(tls_acceptor.accept_async(tcp).map_err(|_| ()).and_then(move |tls| { tokio::spawn(async move {
let ms = { let tls = tokio_openssl::accept(&*tls_acceptor, tcp).await.map_err(|_| ())?;
let mut service_fn = service_fn.lock().unwrap();
service_fn.make_service(&addr)
};
ms.and_then(move |service| { let service = service.await.map_err(|_| ())?;
Http::new().serve_connection(tls, service)
}).map_err(|_| ())
}));
Ok(()) Http::new().serve_connection(tls, service).await.map_err(|_| ())
}).map_err(|_| ()); });
}
Box::new(tls_listener) incoming = rest;
}
} }
} else { } else {
// Using HTTP // Using HTTP
Box::new(hyper::server::Server::bind(&addr).serve(service_fn).map_err(|e| panic!("{:?}", e))) hyper::server::Server::bind(&addr).serve(service).await.unwrap()
} }
} }
@ -102,31 +93,35 @@ impl<C> Server<C> {
} }
} }
use openapi_v3::{CallbackApi, ApiError}; use openapi_v3::CallbackApi;
use openapi_v3::CallbackCallbackWithHeaderPostResponse; use openapi_v3::CallbackCallbackWithHeaderPostResponse;
use openapi_v3::CallbackCallbackPostResponse; use openapi_v3::CallbackCallbackPostResponse;
use openapi_v3::client::callbacks::MakeService; use openapi_v3::client::callbacks::MakeService;
use std::error::Error;
use swagger::ApiError;
impl<C> CallbackApi<C> for Server<C> where C: Has<XSpanIdString>{ #[async_trait]
fn callback_callback_with_header_post( impl<C> CallbackApi<C> for Server<C> where C: Has<XSpanIdString> + Send + Sync
{
async fn callback_callback_with_header_post(
&self, &self,
callback_request_query_url: String, callback_request_query_url: String,
information: Option<String>, information: Option<String>,
context: &C) -> Box<dyn Future<Item=CallbackCallbackWithHeaderPostResponse, Error=ApiError> + Send> context: &C) -> Result<CallbackCallbackWithHeaderPostResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("callback_callback_with_header_post({:?}) - X-Span-ID: {:?}", information, context.get().0.clone()); info!("callback_callback_with_header_post({:?}) - X-Span-ID: {:?}", information, context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn callback_callback_post( async fn callback_callback_post(
&self, &self,
callback_request_query_url: String, callback_request_query_url: String,
context: &C) -> Box<dyn Future<Item=CallbackCallbackPostResponse, Error=ApiError> + Send> context: &C) -> Result<CallbackCallbackPostResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("callback_callback_post() - X-Span-ID: {:?}", context.get().0.clone()); info!("callback_callback_post() - X-Span-ID: {:?}", context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
} }

View File

@ -9,7 +9,8 @@ mod server;
/// Create custom server, wire it to the autogenerated router, /// Create custom server, wire it to the autogenerated router,
/// and pass it to the web server. /// and pass it to the web server.
fn main() { #[tokio::main]
async fn main() {
env_logger::init(); env_logger::init();
let matches = App::new("server") let matches = App::new("server")
@ -20,5 +21,5 @@ fn main() {
let addr = "127.0.0.1:8080"; let addr = "127.0.0.1:8080";
hyper::rt::run(server::create(addr, matches.is_present("https"))); server::create(addr, matches.is_present("https")).await;
} }

View File

@ -2,30 +2,22 @@
#![allow(unused_imports)] #![allow(unused_imports)]
mod errors { use async_trait::async_trait;
error_chain::error_chain!{} use futures::{future, Stream, StreamExt, TryFutureExt, TryStreamExt};
}
pub use self::errors::*;
use chrono;
use futures::{future, Future, Stream};
use hyper::server::conn::Http; use hyper::server::conn::Http;
use hyper::service::MakeService as _; use hyper::service::Service;
use log::info; use log::info;
use openssl::ssl::SslAcceptorBuilder; use openssl::ssl::SslAcceptorBuilder;
use std::future::Future;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::net::SocketAddr; use std::net::SocketAddr;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use swagger; use std::task::{Context, Poll};
use swagger::{Has, XSpanIdString}; use swagger::{Has, XSpanIdString};
use swagger::auth::MakeAllowAllAuthenticator; use swagger::auth::MakeAllowAllAuthenticator;
use swagger::EmptyContext; use swagger::EmptyContext;
use tokio::net::TcpListener; use tokio::net::TcpListener;
use uuid;
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
use tokio_openssl::SslAcceptorExt;
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod}; use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod};
@ -33,18 +25,18 @@ use openapi_v3::models;
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
/// Builds an SSL implementation for Simple HTTPS from some hard-coded file names /// Builds an SSL implementation for Simple HTTPS from some hard-coded file names
pub fn create(addr: &str, https: bool) -> Box<dyn Future<Item = (), Error = ()> + Send> { pub async fn create(addr: &str, https: bool) {
let addr = addr.parse().expect("Failed to parse bind address"); let addr = addr.parse().expect("Failed to parse bind address");
let server = Server::new(); let server = Server::new();
let service_fn = MakeService::new(server); let service = MakeService::new(server);
let service_fn = MakeAllowAllAuthenticator::new(service_fn, "cosmo"); let service = MakeAllowAllAuthenticator::new(service, "cosmo");
let service_fn = let mut service =
openapi_v3::server::context::MakeAddContext::<_, EmptyContext>::new( openapi_v3::server::context::MakeAddContext::<_, EmptyContext>::new(
service_fn service
); );
if https { if https {
@ -62,32 +54,31 @@ pub fn create(addr: &str, https: bool) -> Box<dyn Future<Item = (), Error = ()>
ssl.set_certificate_chain_file("examples/server-chain.pem").expect("Failed to set cerificate chain"); ssl.set_certificate_chain_file("examples/server-chain.pem").expect("Failed to set cerificate chain");
ssl.check_private_key().expect("Failed to check private key"); ssl.check_private_key().expect("Failed to check private key");
let tls_acceptor = ssl.build(); let tls_acceptor = Arc::new(ssl.build());
let service_fn = Arc::new(Mutex::new(service_fn)); let mut tcp_listener = TcpListener::bind(&addr).await.unwrap();
let tls_listener = TcpListener::bind(&addr).unwrap().incoming().for_each(move |tcp| { let mut incoming = tcp_listener.incoming();
let addr = tcp.peer_addr().expect("Unable to get remote address");
let service_fn = service_fn.clone(); while let (Some(tcp), rest) = incoming.into_future().await {
if let Ok(tcp) = tcp {
let addr = tcp.peer_addr().expect("Unable to get remote address");
let service = service.call(addr);
let tls_acceptor = Arc::clone(&tls_acceptor);
hyper::rt::spawn(tls_acceptor.accept_async(tcp).map_err(|_| ()).and_then(move |tls| { tokio::spawn(async move {
let ms = { let tls = tokio_openssl::accept(&*tls_acceptor, tcp).await.map_err(|_| ())?;
let mut service_fn = service_fn.lock().unwrap();
service_fn.make_service(&addr)
};
ms.and_then(move |service| { let service = service.await.map_err(|_| ())?;
Http::new().serve_connection(tls, service)
}).map_err(|_| ())
}));
Ok(()) Http::new().serve_connection(tls, service).await.map_err(|_| ())
}).map_err(|_| ()); });
}
Box::new(tls_listener) incoming = rest;
}
} }
} else { } else {
// Using HTTP // Using HTTP
Box::new(hyper::server::Server::bind(&addr).serve(service_fn).map_err(|e| panic!("{:?}", e))) hyper::server::Server::bind(&addr).serve(service).await.unwrap()
} }
} }
@ -105,7 +96,6 @@ impl<C> Server<C> {
use openapi_v3::{ use openapi_v3::{
Api, Api,
ApiError,
CallbackWithHeaderPostResponse, CallbackWithHeaderPostResponse,
ComplexQueryParamGetResponse, ComplexQueryParamGetResponse,
EnumInPathPathParamGetResponse, EnumInPathPathParamGetResponse,
@ -131,233 +121,237 @@ use openapi_v3::{
GetRepoInfoResponse, GetRepoInfoResponse,
}; };
use openapi_v3::server::MakeService; use openapi_v3::server::MakeService;
use std::error::Error;
use swagger::ApiError;
impl<C> Api<C> for Server<C> where C: Has<XSpanIdString>{ #[async_trait]
fn callback_with_header_post( impl<C> Api<C> for Server<C> where C: Has<XSpanIdString> + Send + Sync
{
async fn callback_with_header_post(
&self, &self,
url: String, url: String,
context: &C) -> Box<dyn Future<Item=CallbackWithHeaderPostResponse, Error=ApiError> + Send> context: &C) -> Result<CallbackWithHeaderPostResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("callback_with_header_post(\"{}\") - X-Span-ID: {:?}", url, context.get().0.clone()); info!("callback_with_header_post(\"{}\") - X-Span-ID: {:?}", url, context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn complex_query_param_get( async fn complex_query_param_get(
&self, &self,
list_of_strings: Option<&Vec<models::StringObject>>, list_of_strings: Option<&Vec<models::StringObject>>,
context: &C) -> Box<dyn Future<Item=ComplexQueryParamGetResponse, Error=ApiError> + Send> context: &C) -> Result<ComplexQueryParamGetResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("complex_query_param_get({:?}) - X-Span-ID: {:?}", list_of_strings, context.get().0.clone()); info!("complex_query_param_get({:?}) - X-Span-ID: {:?}", list_of_strings, context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn enum_in_path_path_param_get( async fn enum_in_path_path_param_get(
&self, &self,
path_param: models::StringEnum, path_param: models::StringEnum,
context: &C) -> Box<dyn Future<Item=EnumInPathPathParamGetResponse, Error=ApiError> + Send> context: &C) -> Result<EnumInPathPathParamGetResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("enum_in_path_path_param_get({:?}) - X-Span-ID: {:?}", path_param, context.get().0.clone()); info!("enum_in_path_path_param_get({:?}) - X-Span-ID: {:?}", path_param, context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn mandatory_request_header_get( async fn mandatory_request_header_get(
&self, &self,
x_header: String, x_header: String,
context: &C) -> Box<dyn Future<Item=MandatoryRequestHeaderGetResponse, Error=ApiError> + Send> context: &C) -> Result<MandatoryRequestHeaderGetResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("mandatory_request_header_get(\"{}\") - X-Span-ID: {:?}", x_header, context.get().0.clone()); info!("mandatory_request_header_get(\"{}\") - X-Span-ID: {:?}", x_header, context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn merge_patch_json_get( async fn merge_patch_json_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=MergePatchJsonGetResponse, Error=ApiError> + Send> context: &C) -> Result<MergePatchJsonGetResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("merge_patch_json_get() - X-Span-ID: {:?}", context.get().0.clone()); info!("merge_patch_json_get() - X-Span-ID: {:?}", context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
/// Get some stuff. /// Get some stuff.
fn multiget_get( async fn multiget_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=MultigetGetResponse, Error=ApiError> + Send> context: &C) -> Result<MultigetGetResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("multiget_get() - X-Span-ID: {:?}", context.get().0.clone()); info!("multiget_get() - X-Span-ID: {:?}", context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn multiple_auth_scheme_get( async fn multiple_auth_scheme_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=MultipleAuthSchemeGetResponse, Error=ApiError> + Send> context: &C) -> Result<MultipleAuthSchemeGetResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("multiple_auth_scheme_get() - X-Span-ID: {:?}", context.get().0.clone()); info!("multiple_auth_scheme_get() - X-Span-ID: {:?}", context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn override_server_get( async fn override_server_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=OverrideServerGetResponse, Error=ApiError> + Send> context: &C) -> Result<OverrideServerGetResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("override_server_get() - X-Span-ID: {:?}", context.get().0.clone()); info!("override_server_get() - X-Span-ID: {:?}", context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
/// Get some stuff with parameters. /// Get some stuff with parameters.
fn paramget_get( async fn paramget_get(
&self, &self,
uuid: Option<uuid::Uuid>, uuid: Option<uuid::Uuid>,
some_object: Option<models::ObjectParam>, some_object: Option<models::ObjectParam>,
some_list: Option<models::MyIdList>, some_list: Option<models::MyIdList>,
context: &C) -> Box<dyn Future<Item=ParamgetGetResponse, Error=ApiError> + Send> context: &C) -> Result<ParamgetGetResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("paramget_get({:?}, {:?}, {:?}) - X-Span-ID: {:?}", uuid, some_object, some_list, context.get().0.clone()); info!("paramget_get({:?}, {:?}, {:?}) - X-Span-ID: {:?}", uuid, some_object, some_list, context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn readonly_auth_scheme_get( async fn readonly_auth_scheme_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=ReadonlyAuthSchemeGetResponse, Error=ApiError> + Send> context: &C) -> Result<ReadonlyAuthSchemeGetResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("readonly_auth_scheme_get() - X-Span-ID: {:?}", context.get().0.clone()); info!("readonly_auth_scheme_get() - X-Span-ID: {:?}", context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn register_callback_post( async fn register_callback_post(
&self, &self,
url: String, url: String,
context: &C) -> Box<dyn Future<Item=RegisterCallbackPostResponse, Error=ApiError> + Send> context: &C) -> Result<RegisterCallbackPostResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("register_callback_post(\"{}\") - X-Span-ID: {:?}", url, context.get().0.clone()); info!("register_callback_post(\"{}\") - X-Span-ID: {:?}", url, context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn required_octet_stream_put( async fn required_octet_stream_put(
&self, &self,
body: swagger::ByteArray, body: swagger::ByteArray,
context: &C) -> Box<dyn Future<Item=RequiredOctetStreamPutResponse, Error=ApiError> + Send> context: &C) -> Result<RequiredOctetStreamPutResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("required_octet_stream_put({:?}) - X-Span-ID: {:?}", body, context.get().0.clone()); info!("required_octet_stream_put({:?}) - X-Span-ID: {:?}", body, context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn responses_with_headers_get( async fn responses_with_headers_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=ResponsesWithHeadersGetResponse, Error=ApiError> + Send> context: &C) -> Result<ResponsesWithHeadersGetResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("responses_with_headers_get() - X-Span-ID: {:?}", context.get().0.clone()); info!("responses_with_headers_get() - X-Span-ID: {:?}", context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn rfc7807_get( async fn rfc7807_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Rfc7807GetResponse, Error=ApiError> + Send> context: &C) -> Result<Rfc7807GetResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("rfc7807_get() - X-Span-ID: {:?}", context.get().0.clone()); info!("rfc7807_get() - X-Span-ID: {:?}", context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn untyped_property_get( async fn untyped_property_get(
&self, &self,
object_untyped_props: Option<models::ObjectUntypedProps>, object_untyped_props: Option<models::ObjectUntypedProps>,
context: &C) -> Box<dyn Future<Item=UntypedPropertyGetResponse, Error=ApiError> + Send> context: &C) -> Result<UntypedPropertyGetResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("untyped_property_get({:?}) - X-Span-ID: {:?}", object_untyped_props, context.get().0.clone()); info!("untyped_property_get({:?}) - X-Span-ID: {:?}", object_untyped_props, context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn uuid_get( async fn uuid_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=UuidGetResponse, Error=ApiError> + Send> context: &C) -> Result<UuidGetResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("uuid_get() - X-Span-ID: {:?}", context.get().0.clone()); info!("uuid_get() - X-Span-ID: {:?}", context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn xml_extra_post( async fn xml_extra_post(
&self, &self,
duplicate_xml_object: Option<models::DuplicateXmlObject>, duplicate_xml_object: Option<models::DuplicateXmlObject>,
context: &C) -> Box<dyn Future<Item=XmlExtraPostResponse, Error=ApiError> + Send> context: &C) -> Result<XmlExtraPostResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("xml_extra_post({:?}) - X-Span-ID: {:?}", duplicate_xml_object, context.get().0.clone()); info!("xml_extra_post({:?}) - X-Span-ID: {:?}", duplicate_xml_object, context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn xml_other_post( async fn xml_other_post(
&self, &self,
another_xml_object: Option<models::AnotherXmlObject>, another_xml_object: Option<models::AnotherXmlObject>,
context: &C) -> Box<dyn Future<Item=XmlOtherPostResponse, Error=ApiError> + Send> context: &C) -> Result<XmlOtherPostResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("xml_other_post({:?}) - X-Span-ID: {:?}", another_xml_object, context.get().0.clone()); info!("xml_other_post({:?}) - X-Span-ID: {:?}", another_xml_object, context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn xml_other_put( async fn xml_other_put(
&self, &self,
another_xml_array: Option<models::AnotherXmlArray>, another_xml_array: Option<models::AnotherXmlArray>,
context: &C) -> Box<dyn Future<Item=XmlOtherPutResponse, Error=ApiError> + Send> context: &C) -> Result<XmlOtherPutResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("xml_other_put({:?}) - X-Span-ID: {:?}", another_xml_array, context.get().0.clone()); info!("xml_other_put({:?}) - X-Span-ID: {:?}", another_xml_array, context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
/// Post an array /// Post an array
fn xml_post( async fn xml_post(
&self, &self,
xml_array: Option<models::XmlArray>, xml_array: Option<models::XmlArray>,
context: &C) -> Box<dyn Future<Item=XmlPostResponse, Error=ApiError> + Send> context: &C) -> Result<XmlPostResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("xml_post({:?}) - X-Span-ID: {:?}", xml_array, context.get().0.clone()); info!("xml_post({:?}) - X-Span-ID: {:?}", xml_array, context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn xml_put( async fn xml_put(
&self, &self,
xml_object: Option<models::XmlObject>, xml_object: Option<models::XmlObject>,
context: &C) -> Box<dyn Future<Item=XmlPutResponse, Error=ApiError> + Send> context: &C) -> Result<XmlPutResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("xml_put({:?}) - X-Span-ID: {:?}", xml_object, context.get().0.clone()); info!("xml_put({:?}) - X-Span-ID: {:?}", xml_object, context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn create_repo( async fn create_repo(
&self, &self,
object_param: models::ObjectParam, object_param: models::ObjectParam,
context: &C) -> Box<dyn Future<Item=CreateRepoResponse, Error=ApiError> + Send> context: &C) -> Result<CreateRepoResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("create_repo({:?}) - X-Span-ID: {:?}", object_param, context.get().0.clone()); info!("create_repo({:?}) - X-Span-ID: {:?}", object_param, context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn get_repo_info( async fn get_repo_info(
&self, &self,
repo_id: String, repo_id: String,
context: &C) -> Box<dyn Future<Item=GetRepoInfoResponse, Error=ApiError> + Send> context: &C) -> Result<GetRepoInfoResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("get_repo_info(\"{}\") - X-Span-ID: {:?}", repo_id, context.get().0.clone()); info!("get_repo_info(\"{}\") - X-Span-ID: {:?}", repo_id, context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
} }

View File

@ -1,22 +1,17 @@
use std::marker::PhantomData; use futures::{future, future::BoxFuture, Stream, stream, future::FutureExt, stream::TryStreamExt};
use futures::{Future, future, Stream, stream}; use hyper::{Request, Response, StatusCode, Body, HeaderMap};
use hyper;
use hyper::{Request, Response, Error, StatusCode, Body, HeaderMap};
use hyper::header::{HeaderName, HeaderValue, CONTENT_TYPE}; use hyper::header::{HeaderName, HeaderValue, CONTENT_TYPE};
use log::warn; use log::warn;
use serde_json;
#[allow(unused_imports)] #[allow(unused_imports)]
use std::convert::{TryFrom, TryInto}; use std::convert::{TryFrom, TryInto};
use std::io; use std::error::Error;
use url::form_urlencoded; use std::future::Future;
#[allow(unused_imports)] use std::marker::PhantomData;
use swagger; use std::task::{Context, Poll};
use swagger::{ApiError, XSpanIdString, Has, RequestParser}; use swagger::{ApiError, BodyExt, Has, RequestParser, XSpanIdString};
pub use swagger::auth::Authorization; pub use swagger::auth::Authorization;
use swagger::auth::Scopes; use swagger::auth::Scopes;
use swagger::context::ContextualPayload; use url::form_urlencoded;
use uuid;
use serde_xml_rs;
#[allow(unused_imports)] #[allow(unused_imports)]
use crate::models; use crate::models;
@ -24,6 +19,8 @@ use crate::header;
pub use crate::context; pub use crate::context;
type ServiceFuture = BoxFuture<'static, Result<Response<Body>, crate::ServiceError>>;
use crate::CallbackApi as Api; use crate::CallbackApi as Api;
use crate::CallbackCallbackWithHeaderPostResponse; use crate::CallbackCallbackWithHeaderPostResponse;
use crate::CallbackCallbackPostResponse; use crate::CallbackCallbackPostResponse;
@ -53,15 +50,17 @@ mod paths {
} }
pub struct MakeService<T, RC> { pub struct MakeService<T, C> where
T: Api<C> + Clone + Send + 'static,
C: Has<XSpanIdString> + Has<Option<Authorization>> + Send + Sync + 'static
{
api_impl: T, api_impl: T,
marker: PhantomData<RC>, marker: PhantomData<C>,
} }
impl<T, RC> MakeService<T, RC> impl<T, C> MakeService<T, C> where
where T: Api<C> + Clone + Send + 'static,
T: Api<RC> + Clone + Send + 'static, C: Has<XSpanIdString> + Has<Option<Authorization>> + Send + Sync + 'static
RC: Has<XSpanIdString> + Has<Option<Authorization>> + 'static
{ {
pub fn new(api_impl: T) -> Self { pub fn new(api_impl: T) -> Self {
MakeService { MakeService {
@ -71,45 +70,46 @@ where
} }
} }
impl<'a, T, SC, RC> hyper::service::MakeService<&'a SC> for MakeService<T, RC> impl<T, C, Target> hyper::service::Service<Target> for MakeService<T, C> where
where T: Api<C> + Clone + Send + 'static,
T: Api<RC> + Clone + Send + 'static, C: Has<XSpanIdString> + Has<Option<Authorization>> + Send + Sync + 'static
RC: Has<XSpanIdString> + Has<Option<Authorization>> + 'static + Send
{ {
type ReqBody = ContextualPayload<Body, RC>; type Response = Service<T, C>;
type ResBody = Body; type Error = crate::ServiceError;
type Error = Error; type Future = future::Ready<Result<Self::Response, Self::Error>>;
type Service = Service<T, RC>;
type Future = future::FutureResult<Self::Service, Self::MakeError>;
type MakeError = Error;
fn make_service(&mut self, _ctx: &'a SC) -> Self::Future { fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
future::FutureResult::from(Ok(Service::new( Poll::Ready(Ok(()))
}
fn call(&mut self, target: Target) -> Self::Future {
futures::future::ok(Service::new(
self.api_impl.clone(), self.api_impl.clone(),
))) ))
} }
} }
type ServiceFuture = Box<dyn Future<Item = Response<Body>, Error = Error> + Send>; fn method_not_allowed() -> Result<Response<Body>, crate::ServiceError> {
Ok(
fn method_not_allowed() -> ServiceFuture {
Box::new(future::ok(
Response::builder().status(StatusCode::METHOD_NOT_ALLOWED) Response::builder().status(StatusCode::METHOD_NOT_ALLOWED)
.body(Body::empty()) .body(Body::empty())
.expect("Unable to create Method Not Allowed response") .expect("Unable to create Method Not Allowed response")
)) )
} }
pub struct Service<T, RC> { pub struct Service<T, C> where
T: Api<C> + Clone + Send + 'static,
C: Has<XSpanIdString> + Has<Option<Authorization>> + Send + Sync + 'static
{
api_impl: T, api_impl: T,
marker: PhantomData<RC>, marker: PhantomData<C>,
} }
impl<T, RC> Service<T, RC> impl<T, C> Service<T, C> where
where T: Api<C> + Clone + Send + 'static,
T: Api<RC> + Clone + Send + 'static, C: Has<XSpanIdString> + Has<Option<Authorization>> + Send + Sync + 'static
RC: Has<XSpanIdString> + Has<Option<Authorization>> + 'static { {
pub fn new(api_impl: T) -> Self { pub fn new(api_impl: T) -> Self {
Service { Service {
api_impl: api_impl, api_impl: api_impl,
@ -118,23 +118,38 @@ where
} }
} }
impl<T, C> hyper::service::Service for Service<T, C> impl<T, C> Clone for Service<T, C> where
where
T: Api<C> + Clone + Send + 'static, T: Api<C> + Clone + Send + 'static,
C: Has<XSpanIdString> + Has<Option<Authorization>> + 'static + Send C: Has<XSpanIdString> + Has<Option<Authorization>> + Send + Sync + 'static
{ {
type ReqBody = ContextualPayload<Body, C>; fn clone(&self) -> Self {
type ResBody = Body; Service {
type Error = Error; api_impl: self.api_impl.clone(),
marker: self.marker.clone(),
}
}
}
impl<T, C> hyper::service::Service<(Request<Body>, C)> for Service<T, C> where
T: Api<C> + Clone + Send + Sync + 'static,
C: Has<XSpanIdString> + Has<Option<Authorization>> + Send + Sync + 'static
{
type Response = Response<Body>;
type Error = crate::ServiceError;
type Future = ServiceFuture; type Future = ServiceFuture;
fn call(&mut self, req: Request<Self::ReqBody>) -> Self::Future { fn poll_ready(&mut self, cx: &mut Context) -> Poll<Result<(), Self::Error>> {
let api_impl = self.api_impl.clone(); self.api_impl.poll_ready(cx)
let (parts, body) = req.into_parts(); }
fn call(&mut self, req: (Request<Body>, C)) -> Self::Future { async fn run<T, C>(mut api_impl: T, req: (Request<Body>, C)) -> Result<Response<Body>, crate::ServiceError> where
T: Api<C> + Clone + Send + 'static,
C: Has<XSpanIdString> + Has<Option<Authorization>> + Send + Sync + 'static
{
let (request, context) = req;
let (parts, body) = request.into_parts();
let (method, uri, headers) = (parts.method, parts.uri, parts.headers); let (method, uri, headers) = (parts.method, parts.uri, parts.headers);
let path = paths::GLOBAL_REGEX_SET.matches(uri.path()); let path = paths::GLOBAL_REGEX_SET.matches(uri.path());
let mut context = body.context;
let body = body.inner;
match &method { match &method {
@ -158,10 +173,10 @@ where
Ok(result) => Ok(result) =>
Some(result.0), Some(result.0),
Err(err) => { Err(err) => {
return Box::new(future::ok(Response::builder() return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST) .status(StatusCode::BAD_REQUEST)
.body(Body::from(format!("Invalid header Information - {}", err))) .body(Body::from(format!("Invalid header Information - {}", err)))
.expect("Unable to create Bad Request response for invalid header Information"))); .expect("Unable to create Bad Request response for invalid header Information"));
}, },
}, },
@ -170,16 +185,13 @@ where
} }
}; };
Box::new({ let result = api_impl.callback_callback_with_header_post(
{{
Box::new(
api_impl.callback_callback_with_header_post(
callback_request_query_url, callback_request_query_url,
param_information, param_information,
&context &context
).then(move |result| { ).await;
let mut response = Response::new(Body::empty()); let mut response = Response::new(Body::empty());
response.headers_mut().insert( response.headers_mut().insert(
HeaderName::from_static("x-span-id"), HeaderName::from_static("x-span-id"),
HeaderValue::from_str((&context as &dyn Has<XSpanIdString>).get().0.clone().to_string().as_str()) HeaderValue::from_str((&context as &dyn Has<XSpanIdString>).get().0.clone().to_string().as_str())
.expect("Unable to create X-Span-ID header value")); .expect("Unable to create X-Span-ID header value"));
@ -199,11 +211,7 @@ where
}, },
} }
future::ok(response) Ok(response)
}
))
}}
}) as Self::Future
}, },
// CallbackCallbackPost - POST /{$request.query.url}/callback // CallbackCallbackPost - POST /{$request.query.url}/callback
@ -218,15 +226,12 @@ where
); );
let callback_request_query_url = path_params["request_query_url"].to_string(); let callback_request_query_url = path_params["request_query_url"].to_string();
Box::new({ let result = api_impl.callback_callback_post(
{{
Box::new(
api_impl.callback_callback_post(
callback_request_query_url, callback_request_query_url,
&context &context
).then(move |result| { ).await;
let mut response = Response::new(Body::empty()); let mut response = Response::new(Body::empty());
response.headers_mut().insert( response.headers_mut().insert(
HeaderName::from_static("x-span-id"), HeaderName::from_static("x-span-id"),
HeaderValue::from_str((&context as &dyn Has<XSpanIdString>).get().0.clone().to_string().as_str()) HeaderValue::from_str((&context as &dyn Has<XSpanIdString>).get().0.clone().to_string().as_str())
.expect("Unable to create X-Span-ID header value")); .expect("Unable to create X-Span-ID header value"));
@ -246,32 +251,16 @@ where
}, },
} }
future::ok(response) Ok(response)
}
))
}}
}) as Self::Future
}, },
_ if path.matched(paths::ID_REQUEST_QUERY_URL_CALLBACK) => method_not_allowed(), _ if path.matched(paths::ID_REQUEST_QUERY_URL_CALLBACK) => method_not_allowed(),
_ if path.matched(paths::ID_REQUEST_QUERY_URL_CALLBACK_WITH_HEADER) => method_not_allowed(), _ if path.matched(paths::ID_REQUEST_QUERY_URL_CALLBACK_WITH_HEADER) => method_not_allowed(),
_ => Box::new(future::ok( _ => Ok(Response::builder().status(StatusCode::NOT_FOUND)
Response::builder().status(StatusCode::NOT_FOUND)
.body(Body::empty()) .body(Body::empty())
.expect("Unable to create Not Found response") .expect("Unable to create Not Found response"))
)) as Self::Future
} }
} } Box::pin(run(self.api_impl.clone(), req)) }
}
impl<T, C> Clone for Service<T, C> where T: Clone
{
fn clone(&self) -> Self {
Service {
api_impl: self.api_impl.clone(),
marker: self.marker.clone(),
}
}
} }
/// Request parser for `Api`. /// Request parser for `Api`.

View File

@ -1,13 +1,12 @@
use futures::Future; use futures::future::BoxFuture;
use hyper;
use hyper::header::HeaderName; use hyper::header::HeaderName;
use hyper::{Error, Request, Response, StatusCode, service::Service, body::Payload}; use hyper::{Error, Request, Response, StatusCode, service::Service};
use url::form_urlencoded; use url::form_urlencoded;
use std::default::Default; use std::default::Default;
use std::io; use std::io;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::task::{Poll, Context};
use swagger::auth::{AuthData, Authorization, Bearer, Scopes}; use swagger::auth::{AuthData, Authorization, Bearer, Scopes};
use swagger::context::ContextualPayload;
use swagger::{EmptyContext, Has, Pop, Push, XSpanIdString}; use swagger::{EmptyContext, Has, Pop, Push, XSpanIdString};
use crate::Api; use crate::Api;
@ -31,58 +30,52 @@ where
} }
// Make a service that adds context. // Make a service that adds context.
impl<'a, T, SC, A, B, C, D, E, ME, S, OB, F> hyper::service::MakeService<&'a SC> for impl<Target, T, A, B, C, D> Service<Target> for
MakeAddContext<T, A> MakeAddContext<T, A>
where where
A: Default + Push<XSpanIdString, Result = B>, Target: Send,
A: Default + Push<XSpanIdString, Result = B> + Send,
B: Push<Option<AuthData>, Result = C>, B: Push<Option<AuthData>, Result = C>,
C: Push<Option<Authorization>, Result = D>, C: Push<Option<Authorization>, Result = D>,
D: Send + 'static, D: Send + 'static,
T: hyper::service::MakeService< T: Service<Target> + Send,
&'a SC, T::Future: Send + 'static
Error = E,
MakeError = ME,
Service = S,
ReqBody = ContextualPayload<hyper::Body, D>,
ResBody = OB,
Future = F
>,
S: Service<
Error = E,
ReqBody = ContextualPayload<hyper::Body, D>,
ResBody = OB> + 'static,
ME: swagger::ErrorBound,
E: swagger::ErrorBound,
F: Future<Item=S, Error=ME> + Send + 'static,
S::Future: Send,
OB: Payload,
{ {
type ReqBody = hyper::Body; type Error = T::Error;
type ResBody = OB; type Response = AddContext<T::Response, A, B, C, D>;
type Error = E; type Future = BoxFuture<'static, Result<Self::Response, Self::Error>>;
type MakeError = ME;
type Service = AddContext<S, A>;
type Future = Box<dyn Future<Item = Self::Service, Error = ME> + Send + 'static>;
fn make_service(&mut self, ctx: &'a SC) -> Self::Future { fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Box::new(self.inner.make_service(ctx).map(|s| AddContext::new(s))) self.inner.poll_ready(cx)
}
fn call(&mut self, target: Target) -> Self::Future {
let service = self.inner.call(target);
Box::pin(async move {
Ok(AddContext::new(service.await?))
})
} }
} }
/// Middleware to extract authentication data from request /// Middleware to add context data from the request
pub struct AddContext<T, A> { pub struct AddContext<T, A, B, C, D>
where
A: Default + Push<XSpanIdString, Result = B>,
B: Push<Option<AuthData>, Result = C>,
C: Push<Option<Authorization>, Result = D>
{
inner: T, inner: T,
marker: PhantomData<A>, marker: PhantomData<A>,
} }
impl<T, A, B, C, D> AddContext<T, A> impl<T, A, B, C, D> AddContext<T, A, B, C, D>
where where
A: Default + Push<XSpanIdString, Result = B>, A: Default + Push<XSpanIdString, Result = B>,
B: Push<Option<AuthData>, Result = C>, B: Push<Option<AuthData>, Result = C>,
C: Push<Option<Authorization>, Result = D>, C: Push<Option<Authorization>, Result = D>,
T: Service,
{ {
pub fn new(inner: T) -> AddContext<T, A> { pub fn new(inner: T) -> Self {
AddContext { AddContext {
inner, inner,
marker: PhantomData, marker: PhantomData,
@ -90,24 +83,26 @@ where
} }
} }
impl<T, A, B, C, D> Service for AddContext<T, A> impl<T, A, B, C, D, ReqBody> Service<Request<ReqBody>> for AddContext<T, A, B, C, D>
where where
A: Default + Push<XSpanIdString, Result=B>, A: Default + Push<XSpanIdString, Result=B>,
B: Push<Option<AuthData>, Result=C>, B: Push<Option<AuthData>, Result=C>,
C: Push<Option<Authorization>, Result=D>, C: Push<Option<Authorization>, Result=D>,
D: Send + 'static, D: Send + 'static,
T: Service<ReqBody = ContextualPayload<hyper::Body, D>>, T: Service<(Request<ReqBody>, D)>
T::Future: Future<Item=Response<T::ResBody>, Error=T::Error> + Send + 'static
{ {
type ReqBody = hyper::Body;
type ResBody = T::ResBody;
type Error = T::Error; type Error = T::Error;
type Future = Box<dyn Future<Item=Response<T::ResBody>, Error=T::Error> + Send + 'static>; type Future = T::Future;
type Response = T::Response;
fn call(&mut self, req: Request<Self::ReqBody>) -> Self::Future { fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
let context = A::default().push(XSpanIdString::get_or_generate(&req)); self.inner.poll_ready(cx)
let (head, body) = req.into_parts(); }
let headers = head.headers.clone();
fn call(&mut self, request: Request<ReqBody>) -> Self::Future {
let context = A::default().push(XSpanIdString::get_or_generate(&request));
let headers = request.headers();
{ {
use swagger::auth::Bearer; use swagger::auth::Bearer;
@ -117,22 +112,13 @@ impl<T, A, B, C, D> Service for AddContext<T, A>
let context = context.push(Some(auth_data)); let context = context.push(Some(auth_data));
let context = context.push(None::<Authorization>); let context = context.push(None::<Authorization>);
let body = ContextualPayload { return self.inner.call((request, context))
inner: body,
context: context,
};
return Box::new(self.inner.call(hyper::Request::from_parts(head, body)));
} }
} }
let context = context.push(None::<AuthData>); let context = context.push(None::<AuthData>);
let context = context.push(None::<Authorization>); let context = context.push(None::<Authorization>);
let body = ContextualPayload {
inner: body,
context: context,
};
Box::new(self.inner.call(hyper::Request::from_parts(head, body))) self.inner.call((request, context))
} }
} }

View File

@ -1,12 +1,12 @@
#![allow(missing_docs, trivial_casts, unused_variables, unused_mut, unused_imports, unused_extern_crates, non_camel_case_types)] #![allow(missing_docs, trivial_casts, unused_variables, unused_mut, unused_imports, unused_extern_crates, non_camel_case_types)]
use async_trait::async_trait;
use futures::Stream; use futures::Stream;
use std::io::Error; use std::error::Error;
use std::task::{Poll, Context};
use swagger::{ApiError, ContextWrapper};
#[deprecated(note = "Import swagger-rs directly")] type ServiceError = Box<dyn Error + Send + Sync + 'static>;
pub use swagger::{ApiError, ContextWrapper};
#[deprecated(note = "Import futures directly")]
pub use futures::Future;
pub const BASE_PATH: &'static str = ""; pub const BASE_PATH: &'static str = "";
pub const API_VERSION: &'static str = "1.0.7"; pub const API_VERSION: &'static str = "1.0.7";
@ -225,433 +225,478 @@ pub enum GetRepoInfoResponse {
} }
/// API /// API
pub trait Api<C> { #[async_trait]
fn callback_with_header_post( pub trait Api<C: Send + Sync> {
fn poll_ready(&self, _cx: &mut Context) -> Poll<Result<(), Box<dyn Error + Send + Sync + 'static>>> {
Poll::Ready(Ok(()))
}
async fn callback_with_header_post(
&self, &self,
url: String, url: String,
context: &C) -> Box<dyn Future<Item=CallbackWithHeaderPostResponse, Error=ApiError> + Send>; context: &C) -> Result<CallbackWithHeaderPostResponse, ApiError>;
fn complex_query_param_get( async fn complex_query_param_get(
&self, &self,
list_of_strings: Option<&Vec<models::StringObject>>, list_of_strings: Option<&Vec<models::StringObject>>,
context: &C) -> Box<dyn Future<Item=ComplexQueryParamGetResponse, Error=ApiError> + Send>; context: &C) -> Result<ComplexQueryParamGetResponse, ApiError>;
fn enum_in_path_path_param_get( async fn enum_in_path_path_param_get(
&self, &self,
path_param: models::StringEnum, path_param: models::StringEnum,
context: &C) -> Box<dyn Future<Item=EnumInPathPathParamGetResponse, Error=ApiError> + Send>; context: &C) -> Result<EnumInPathPathParamGetResponse, ApiError>;
fn mandatory_request_header_get( async fn mandatory_request_header_get(
&self, &self,
x_header: String, x_header: String,
context: &C) -> Box<dyn Future<Item=MandatoryRequestHeaderGetResponse, Error=ApiError> + Send>; context: &C) -> Result<MandatoryRequestHeaderGetResponse, ApiError>;
fn merge_patch_json_get( async fn merge_patch_json_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=MergePatchJsonGetResponse, Error=ApiError> + Send>; context: &C) -> Result<MergePatchJsonGetResponse, ApiError>;
/// Get some stuff. /// Get some stuff.
fn multiget_get( async fn multiget_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=MultigetGetResponse, Error=ApiError> + Send>; context: &C) -> Result<MultigetGetResponse, ApiError>;
fn multiple_auth_scheme_get( async fn multiple_auth_scheme_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=MultipleAuthSchemeGetResponse, Error=ApiError> + Send>; context: &C) -> Result<MultipleAuthSchemeGetResponse, ApiError>;
fn override_server_get( async fn override_server_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=OverrideServerGetResponse, Error=ApiError> + Send>; context: &C) -> Result<OverrideServerGetResponse, ApiError>;
/// Get some stuff with parameters. /// Get some stuff with parameters.
fn paramget_get( async fn paramget_get(
&self, &self,
uuid: Option<uuid::Uuid>, uuid: Option<uuid::Uuid>,
some_object: Option<models::ObjectParam>, some_object: Option<models::ObjectParam>,
some_list: Option<models::MyIdList>, some_list: Option<models::MyIdList>,
context: &C) -> Box<dyn Future<Item=ParamgetGetResponse, Error=ApiError> + Send>; context: &C) -> Result<ParamgetGetResponse, ApiError>;
fn readonly_auth_scheme_get( async fn readonly_auth_scheme_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=ReadonlyAuthSchemeGetResponse, Error=ApiError> + Send>; context: &C) -> Result<ReadonlyAuthSchemeGetResponse, ApiError>;
fn register_callback_post( async fn register_callback_post(
&self, &self,
url: String, url: String,
context: &C) -> Box<dyn Future<Item=RegisterCallbackPostResponse, Error=ApiError> + Send>; context: &C) -> Result<RegisterCallbackPostResponse, ApiError>;
fn required_octet_stream_put( async fn required_octet_stream_put(
&self, &self,
body: swagger::ByteArray, body: swagger::ByteArray,
context: &C) -> Box<dyn Future<Item=RequiredOctetStreamPutResponse, Error=ApiError> + Send>; context: &C) -> Result<RequiredOctetStreamPutResponse, ApiError>;
fn responses_with_headers_get( async fn responses_with_headers_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=ResponsesWithHeadersGetResponse, Error=ApiError> + Send>; context: &C) -> Result<ResponsesWithHeadersGetResponse, ApiError>;
fn rfc7807_get( async fn rfc7807_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Rfc7807GetResponse, Error=ApiError> + Send>; context: &C) -> Result<Rfc7807GetResponse, ApiError>;
fn untyped_property_get( async fn untyped_property_get(
&self, &self,
object_untyped_props: Option<models::ObjectUntypedProps>, object_untyped_props: Option<models::ObjectUntypedProps>,
context: &C) -> Box<dyn Future<Item=UntypedPropertyGetResponse, Error=ApiError> + Send>; context: &C) -> Result<UntypedPropertyGetResponse, ApiError>;
fn uuid_get( async fn uuid_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=UuidGetResponse, Error=ApiError> + Send>; context: &C) -> Result<UuidGetResponse, ApiError>;
fn xml_extra_post( async fn xml_extra_post(
&self, &self,
duplicate_xml_object: Option<models::DuplicateXmlObject>, duplicate_xml_object: Option<models::DuplicateXmlObject>,
context: &C) -> Box<dyn Future<Item=XmlExtraPostResponse, Error=ApiError> + Send>; context: &C) -> Result<XmlExtraPostResponse, ApiError>;
fn xml_other_post( async fn xml_other_post(
&self, &self,
another_xml_object: Option<models::AnotherXmlObject>, another_xml_object: Option<models::AnotherXmlObject>,
context: &C) -> Box<dyn Future<Item=XmlOtherPostResponse, Error=ApiError> + Send>; context: &C) -> Result<XmlOtherPostResponse, ApiError>;
fn xml_other_put( async fn xml_other_put(
&self, &self,
another_xml_array: Option<models::AnotherXmlArray>, another_xml_array: Option<models::AnotherXmlArray>,
context: &C) -> Box<dyn Future<Item=XmlOtherPutResponse, Error=ApiError> + Send>; context: &C) -> Result<XmlOtherPutResponse, ApiError>;
/// Post an array /// Post an array
fn xml_post( async fn xml_post(
&self, &self,
xml_array: Option<models::XmlArray>, xml_array: Option<models::XmlArray>,
context: &C) -> Box<dyn Future<Item=XmlPostResponse, Error=ApiError> + Send>; context: &C) -> Result<XmlPostResponse, ApiError>;
fn xml_put( async fn xml_put(
&self, &self,
xml_object: Option<models::XmlObject>, xml_object: Option<models::XmlObject>,
context: &C) -> Box<dyn Future<Item=XmlPutResponse, Error=ApiError> + Send>; context: &C) -> Result<XmlPutResponse, ApiError>;
fn create_repo( async fn create_repo(
&self, &self,
object_param: models::ObjectParam, object_param: models::ObjectParam,
context: &C) -> Box<dyn Future<Item=CreateRepoResponse, Error=ApiError> + Send>; context: &C) -> Result<CreateRepoResponse, ApiError>;
fn get_repo_info( async fn get_repo_info(
&self, &self,
repo_id: String, repo_id: String,
context: &C) -> Box<dyn Future<Item=GetRepoInfoResponse, Error=ApiError> + Send>; context: &C) -> Result<GetRepoInfoResponse, ApiError>;
} }
/// API without a `Context` /// API where `Context` isn't passed on every API call
pub trait ApiNoContext { #[async_trait]
fn callback_with_header_post( pub trait ApiNoContext<C: Send + Sync> {
fn poll_ready(&self, _cx: &mut Context) -> Poll<Result<(), Box<dyn Error + Send + Sync + 'static>>>;
fn context(&self) -> &C;
async fn callback_with_header_post(
&self, &self,
url: String, url: String,
) -> Box<dyn Future<Item=CallbackWithHeaderPostResponse, Error=ApiError> + Send>; ) -> Result<CallbackWithHeaderPostResponse, ApiError>;
fn complex_query_param_get( async fn complex_query_param_get(
&self, &self,
list_of_strings: Option<&Vec<models::StringObject>>, list_of_strings: Option<&Vec<models::StringObject>>,
) -> Box<dyn Future<Item=ComplexQueryParamGetResponse, Error=ApiError> + Send>; ) -> Result<ComplexQueryParamGetResponse, ApiError>;
fn enum_in_path_path_param_get( async fn enum_in_path_path_param_get(
&self, &self,
path_param: models::StringEnum, path_param: models::StringEnum,
) -> Box<dyn Future<Item=EnumInPathPathParamGetResponse, Error=ApiError> + Send>; ) -> Result<EnumInPathPathParamGetResponse, ApiError>;
fn mandatory_request_header_get( async fn mandatory_request_header_get(
&self, &self,
x_header: String, x_header: String,
) -> Box<dyn Future<Item=MandatoryRequestHeaderGetResponse, Error=ApiError> + Send>; ) -> Result<MandatoryRequestHeaderGetResponse, ApiError>;
fn merge_patch_json_get( async fn merge_patch_json_get(
&self, &self,
) -> Box<dyn Future<Item=MergePatchJsonGetResponse, Error=ApiError> + Send>; ) -> Result<MergePatchJsonGetResponse, ApiError>;
/// Get some stuff. /// Get some stuff.
fn multiget_get( async fn multiget_get(
&self, &self,
) -> Box<dyn Future<Item=MultigetGetResponse, Error=ApiError> + Send>; ) -> Result<MultigetGetResponse, ApiError>;
fn multiple_auth_scheme_get( async fn multiple_auth_scheme_get(
&self, &self,
) -> Box<dyn Future<Item=MultipleAuthSchemeGetResponse, Error=ApiError> + Send>; ) -> Result<MultipleAuthSchemeGetResponse, ApiError>;
fn override_server_get( async fn override_server_get(
&self, &self,
) -> Box<dyn Future<Item=OverrideServerGetResponse, Error=ApiError> + Send>; ) -> Result<OverrideServerGetResponse, ApiError>;
/// Get some stuff with parameters. /// Get some stuff with parameters.
fn paramget_get( async fn paramget_get(
&self, &self,
uuid: Option<uuid::Uuid>, uuid: Option<uuid::Uuid>,
some_object: Option<models::ObjectParam>, some_object: Option<models::ObjectParam>,
some_list: Option<models::MyIdList>, some_list: Option<models::MyIdList>,
) -> Box<dyn Future<Item=ParamgetGetResponse, Error=ApiError> + Send>; ) -> Result<ParamgetGetResponse, ApiError>;
fn readonly_auth_scheme_get( async fn readonly_auth_scheme_get(
&self, &self,
) -> Box<dyn Future<Item=ReadonlyAuthSchemeGetResponse, Error=ApiError> + Send>; ) -> Result<ReadonlyAuthSchemeGetResponse, ApiError>;
fn register_callback_post( async fn register_callback_post(
&self, &self,
url: String, url: String,
) -> Box<dyn Future<Item=RegisterCallbackPostResponse, Error=ApiError> + Send>; ) -> Result<RegisterCallbackPostResponse, ApiError>;
fn required_octet_stream_put( async fn required_octet_stream_put(
&self, &self,
body: swagger::ByteArray, body: swagger::ByteArray,
) -> Box<dyn Future<Item=RequiredOctetStreamPutResponse, Error=ApiError> + Send>; ) -> Result<RequiredOctetStreamPutResponse, ApiError>;
fn responses_with_headers_get( async fn responses_with_headers_get(
&self, &self,
) -> Box<dyn Future<Item=ResponsesWithHeadersGetResponse, Error=ApiError> + Send>; ) -> Result<ResponsesWithHeadersGetResponse, ApiError>;
fn rfc7807_get( async fn rfc7807_get(
&self, &self,
) -> Box<dyn Future<Item=Rfc7807GetResponse, Error=ApiError> + Send>; ) -> Result<Rfc7807GetResponse, ApiError>;
fn untyped_property_get( async fn untyped_property_get(
&self, &self,
object_untyped_props: Option<models::ObjectUntypedProps>, object_untyped_props: Option<models::ObjectUntypedProps>,
) -> Box<dyn Future<Item=UntypedPropertyGetResponse, Error=ApiError> + Send>; ) -> Result<UntypedPropertyGetResponse, ApiError>;
fn uuid_get( async fn uuid_get(
&self, &self,
) -> Box<dyn Future<Item=UuidGetResponse, Error=ApiError> + Send>; ) -> Result<UuidGetResponse, ApiError>;
fn xml_extra_post( async fn xml_extra_post(
&self, &self,
duplicate_xml_object: Option<models::DuplicateXmlObject>, duplicate_xml_object: Option<models::DuplicateXmlObject>,
) -> Box<dyn Future<Item=XmlExtraPostResponse, Error=ApiError> + Send>; ) -> Result<XmlExtraPostResponse, ApiError>;
fn xml_other_post( async fn xml_other_post(
&self, &self,
another_xml_object: Option<models::AnotherXmlObject>, another_xml_object: Option<models::AnotherXmlObject>,
) -> Box<dyn Future<Item=XmlOtherPostResponse, Error=ApiError> + Send>; ) -> Result<XmlOtherPostResponse, ApiError>;
fn xml_other_put( async fn xml_other_put(
&self, &self,
another_xml_array: Option<models::AnotherXmlArray>, another_xml_array: Option<models::AnotherXmlArray>,
) -> Box<dyn Future<Item=XmlOtherPutResponse, Error=ApiError> + Send>; ) -> Result<XmlOtherPutResponse, ApiError>;
/// Post an array /// Post an array
fn xml_post( async fn xml_post(
&self, &self,
xml_array: Option<models::XmlArray>, xml_array: Option<models::XmlArray>,
) -> Box<dyn Future<Item=XmlPostResponse, Error=ApiError> + Send>; ) -> Result<XmlPostResponse, ApiError>;
fn xml_put( async fn xml_put(
&self, &self,
xml_object: Option<models::XmlObject>, xml_object: Option<models::XmlObject>,
) -> Box<dyn Future<Item=XmlPutResponse, Error=ApiError> + Send>; ) -> Result<XmlPutResponse, ApiError>;
fn create_repo( async fn create_repo(
&self, &self,
object_param: models::ObjectParam, object_param: models::ObjectParam,
) -> Box<dyn Future<Item=CreateRepoResponse, Error=ApiError> + Send>; ) -> Result<CreateRepoResponse, ApiError>;
fn get_repo_info( async fn get_repo_info(
&self, &self,
repo_id: String, repo_id: String,
) -> Box<dyn Future<Item=GetRepoInfoResponse, Error=ApiError> + Send>; ) -> Result<GetRepoInfoResponse, ApiError>;
} }
/// Trait to extend an API to make it easy to bind it to a context. /// Trait to extend an API to make it easy to bind it to a context.
pub trait ContextWrapperExt<'a, C> where Self: Sized { pub trait ContextWrapperExt<C: Send + Sync> where Self: Sized
{
/// Binds this API to a context. /// Binds this API to a context.
fn with_context(self: &'a Self, context: C) -> ContextWrapper<'a, Self, C>; fn with_context(self: Self, context: C) -> ContextWrapper<Self, C>;
} }
impl<'a, T: Api<C> + Sized, C> ContextWrapperExt<'a, C> for T { impl<T: Api<C> + Send + Sync, C: Clone + Send + Sync> ContextWrapperExt<C> for T {
fn with_context(self: &'a T, context: C) -> ContextWrapper<'a, T, C> { fn with_context(self: T, context: C) -> ContextWrapper<T, C> {
ContextWrapper::<T, C>::new(self, context) ContextWrapper::<T, C>::new(self, context)
} }
} }
impl<'a, T: Api<C>, C> ApiNoContext for ContextWrapper<'a, T, C> { #[async_trait]
fn callback_with_header_post( impl<T: Api<C> + Send + Sync, C: Clone + Send + Sync> ApiNoContext<C> for ContextWrapper<T, C> {
fn poll_ready(&self, cx: &mut Context) -> Poll<Result<(), ServiceError>> {
self.api().poll_ready(cx)
}
fn context(&self) -> &C {
ContextWrapper::context(self)
}
async fn callback_with_header_post(
&self, &self,
url: String, url: String,
) -> Box<dyn Future<Item=CallbackWithHeaderPostResponse, Error=ApiError> + Send> ) -> Result<CallbackWithHeaderPostResponse, ApiError>
{ {
self.api().callback_with_header_post(url, &self.context()) let context = self.context().clone();
self.api().callback_with_header_post(url, &context).await
} }
fn complex_query_param_get( async fn complex_query_param_get(
&self, &self,
list_of_strings: Option<&Vec<models::StringObject>>, list_of_strings: Option<&Vec<models::StringObject>>,
) -> Box<dyn Future<Item=ComplexQueryParamGetResponse, Error=ApiError> + Send> ) -> Result<ComplexQueryParamGetResponse, ApiError>
{ {
self.api().complex_query_param_get(list_of_strings, &self.context()) let context = self.context().clone();
self.api().complex_query_param_get(list_of_strings, &context).await
} }
fn enum_in_path_path_param_get( async fn enum_in_path_path_param_get(
&self, &self,
path_param: models::StringEnum, path_param: models::StringEnum,
) -> Box<dyn Future<Item=EnumInPathPathParamGetResponse, Error=ApiError> + Send> ) -> Result<EnumInPathPathParamGetResponse, ApiError>
{ {
self.api().enum_in_path_path_param_get(path_param, &self.context()) let context = self.context().clone();
self.api().enum_in_path_path_param_get(path_param, &context).await
} }
fn mandatory_request_header_get( async fn mandatory_request_header_get(
&self, &self,
x_header: String, x_header: String,
) -> Box<dyn Future<Item=MandatoryRequestHeaderGetResponse, Error=ApiError> + Send> ) -> Result<MandatoryRequestHeaderGetResponse, ApiError>
{ {
self.api().mandatory_request_header_get(x_header, &self.context()) let context = self.context().clone();
self.api().mandatory_request_header_get(x_header, &context).await
} }
fn merge_patch_json_get( async fn merge_patch_json_get(
&self, &self,
) -> Box<dyn Future<Item=MergePatchJsonGetResponse, Error=ApiError> + Send> ) -> Result<MergePatchJsonGetResponse, ApiError>
{ {
self.api().merge_patch_json_get(&self.context()) let context = self.context().clone();
self.api().merge_patch_json_get(&context).await
} }
/// Get some stuff. /// Get some stuff.
fn multiget_get( async fn multiget_get(
&self, &self,
) -> Box<dyn Future<Item=MultigetGetResponse, Error=ApiError> + Send> ) -> Result<MultigetGetResponse, ApiError>
{ {
self.api().multiget_get(&self.context()) let context = self.context().clone();
self.api().multiget_get(&context).await
} }
fn multiple_auth_scheme_get( async fn multiple_auth_scheme_get(
&self, &self,
) -> Box<dyn Future<Item=MultipleAuthSchemeGetResponse, Error=ApiError> + Send> ) -> Result<MultipleAuthSchemeGetResponse, ApiError>
{ {
self.api().multiple_auth_scheme_get(&self.context()) let context = self.context().clone();
self.api().multiple_auth_scheme_get(&context).await
} }
fn override_server_get( async fn override_server_get(
&self, &self,
) -> Box<dyn Future<Item=OverrideServerGetResponse, Error=ApiError> + Send> ) -> Result<OverrideServerGetResponse, ApiError>
{ {
self.api().override_server_get(&self.context()) let context = self.context().clone();
self.api().override_server_get(&context).await
} }
/// Get some stuff with parameters. /// Get some stuff with parameters.
fn paramget_get( async fn paramget_get(
&self, &self,
uuid: Option<uuid::Uuid>, uuid: Option<uuid::Uuid>,
some_object: Option<models::ObjectParam>, some_object: Option<models::ObjectParam>,
some_list: Option<models::MyIdList>, some_list: Option<models::MyIdList>,
) -> Box<dyn Future<Item=ParamgetGetResponse, Error=ApiError> + Send> ) -> Result<ParamgetGetResponse, ApiError>
{ {
self.api().paramget_get(uuid, some_object, some_list, &self.context()) let context = self.context().clone();
self.api().paramget_get(uuid, some_object, some_list, &context).await
} }
fn readonly_auth_scheme_get( async fn readonly_auth_scheme_get(
&self, &self,
) -> Box<dyn Future<Item=ReadonlyAuthSchemeGetResponse, Error=ApiError> + Send> ) -> Result<ReadonlyAuthSchemeGetResponse, ApiError>
{ {
self.api().readonly_auth_scheme_get(&self.context()) let context = self.context().clone();
self.api().readonly_auth_scheme_get(&context).await
} }
fn register_callback_post( async fn register_callback_post(
&self, &self,
url: String, url: String,
) -> Box<dyn Future<Item=RegisterCallbackPostResponse, Error=ApiError> + Send> ) -> Result<RegisterCallbackPostResponse, ApiError>
{ {
self.api().register_callback_post(url, &self.context()) let context = self.context().clone();
self.api().register_callback_post(url, &context).await
} }
fn required_octet_stream_put( async fn required_octet_stream_put(
&self, &self,
body: swagger::ByteArray, body: swagger::ByteArray,
) -> Box<dyn Future<Item=RequiredOctetStreamPutResponse, Error=ApiError> + Send> ) -> Result<RequiredOctetStreamPutResponse, ApiError>
{ {
self.api().required_octet_stream_put(body, &self.context()) let context = self.context().clone();
self.api().required_octet_stream_put(body, &context).await
} }
fn responses_with_headers_get( async fn responses_with_headers_get(
&self, &self,
) -> Box<dyn Future<Item=ResponsesWithHeadersGetResponse, Error=ApiError> + Send> ) -> Result<ResponsesWithHeadersGetResponse, ApiError>
{ {
self.api().responses_with_headers_get(&self.context()) let context = self.context().clone();
self.api().responses_with_headers_get(&context).await
} }
fn rfc7807_get( async fn rfc7807_get(
&self, &self,
) -> Box<dyn Future<Item=Rfc7807GetResponse, Error=ApiError> + Send> ) -> Result<Rfc7807GetResponse, ApiError>
{ {
self.api().rfc7807_get(&self.context()) let context = self.context().clone();
self.api().rfc7807_get(&context).await
} }
fn untyped_property_get( async fn untyped_property_get(
&self, &self,
object_untyped_props: Option<models::ObjectUntypedProps>, object_untyped_props: Option<models::ObjectUntypedProps>,
) -> Box<dyn Future<Item=UntypedPropertyGetResponse, Error=ApiError> + Send> ) -> Result<UntypedPropertyGetResponse, ApiError>
{ {
self.api().untyped_property_get(object_untyped_props, &self.context()) let context = self.context().clone();
self.api().untyped_property_get(object_untyped_props, &context).await
} }
fn uuid_get( async fn uuid_get(
&self, &self,
) -> Box<dyn Future<Item=UuidGetResponse, Error=ApiError> + Send> ) -> Result<UuidGetResponse, ApiError>
{ {
self.api().uuid_get(&self.context()) let context = self.context().clone();
self.api().uuid_get(&context).await
} }
fn xml_extra_post( async fn xml_extra_post(
&self, &self,
duplicate_xml_object: Option<models::DuplicateXmlObject>, duplicate_xml_object: Option<models::DuplicateXmlObject>,
) -> Box<dyn Future<Item=XmlExtraPostResponse, Error=ApiError> + Send> ) -> Result<XmlExtraPostResponse, ApiError>
{ {
self.api().xml_extra_post(duplicate_xml_object, &self.context()) let context = self.context().clone();
self.api().xml_extra_post(duplicate_xml_object, &context).await
} }
fn xml_other_post( async fn xml_other_post(
&self, &self,
another_xml_object: Option<models::AnotherXmlObject>, another_xml_object: Option<models::AnotherXmlObject>,
) -> Box<dyn Future<Item=XmlOtherPostResponse, Error=ApiError> + Send> ) -> Result<XmlOtherPostResponse, ApiError>
{ {
self.api().xml_other_post(another_xml_object, &self.context()) let context = self.context().clone();
self.api().xml_other_post(another_xml_object, &context).await
} }
fn xml_other_put( async fn xml_other_put(
&self, &self,
another_xml_array: Option<models::AnotherXmlArray>, another_xml_array: Option<models::AnotherXmlArray>,
) -> Box<dyn Future<Item=XmlOtherPutResponse, Error=ApiError> + Send> ) -> Result<XmlOtherPutResponse, ApiError>
{ {
self.api().xml_other_put(another_xml_array, &self.context()) let context = self.context().clone();
self.api().xml_other_put(another_xml_array, &context).await
} }
/// Post an array /// Post an array
fn xml_post( async fn xml_post(
&self, &self,
xml_array: Option<models::XmlArray>, xml_array: Option<models::XmlArray>,
) -> Box<dyn Future<Item=XmlPostResponse, Error=ApiError> + Send> ) -> Result<XmlPostResponse, ApiError>
{ {
self.api().xml_post(xml_array, &self.context()) let context = self.context().clone();
self.api().xml_post(xml_array, &context).await
} }
fn xml_put( async fn xml_put(
&self, &self,
xml_object: Option<models::XmlObject>, xml_object: Option<models::XmlObject>,
) -> Box<dyn Future<Item=XmlPutResponse, Error=ApiError> + Send> ) -> Result<XmlPutResponse, ApiError>
{ {
self.api().xml_put(xml_object, &self.context()) let context = self.context().clone();
self.api().xml_put(xml_object, &context).await
} }
fn create_repo( async fn create_repo(
&self, &self,
object_param: models::ObjectParam, object_param: models::ObjectParam,
) -> Box<dyn Future<Item=CreateRepoResponse, Error=ApiError> + Send> ) -> Result<CreateRepoResponse, ApiError>
{ {
self.api().create_repo(object_param, &self.context()) let context = self.context().clone();
self.api().create_repo(object_param, &context).await
} }
fn get_repo_info( async fn get_repo_info(
&self, &self,
repo_id: String, repo_id: String,
) -> Box<dyn Future<Item=GetRepoInfoResponse, Error=ApiError> + Send> ) -> Result<GetRepoInfoResponse, ApiError>
{ {
self.api().get_repo_info(repo_id, &self.context()) let context = self.context().clone();
self.api().get_repo_info(repo_id, &context).await
} }
} }
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum CallbackCallbackWithHeaderPostResponse { pub enum CallbackCallbackWithHeaderPostResponse {
/// OK /// OK
@ -666,68 +711,89 @@ pub enum CallbackCallbackPostResponse {
/// Callback API /// Callback API
pub trait CallbackApi<C> { #[async_trait]
fn callback_callback_with_header_post( pub trait CallbackApi<C: Send + Sync> {
fn poll_ready(&self, _cx: &mut Context) -> Poll<Result<(), Box<dyn Error + Send + Sync + 'static>>> {
Poll::Ready(Ok(()))
}
async fn callback_callback_with_header_post(
&self, &self,
callback_request_query_url: String, callback_request_query_url: String,
information: Option<String>, information: Option<String>,
context: &C) -> Box<dyn Future<Item=CallbackCallbackWithHeaderPostResponse, Error=ApiError> + Send>; context: &C) -> Result<CallbackCallbackWithHeaderPostResponse, ApiError>;
fn callback_callback_post( async fn callback_callback_post(
&self, &self,
callback_request_query_url: String, callback_request_query_url: String,
context: &C) -> Box<dyn Future<Item=CallbackCallbackPostResponse, Error=ApiError> + Send>; context: &C) -> Result<CallbackCallbackPostResponse, ApiError>;
} }
/// Callback API without a `Context` /// Callback API without a `Context`
pub trait CallbackApiNoContext { #[async_trait]
fn callback_callback_with_header_post( pub trait CallbackApiNoContext<C: Send + Sync> {
fn poll_ready(&self, _cx: &mut Context) -> Poll<Result<(), Box<dyn Error + Send + Sync + 'static>>>;
fn context(&self) -> &C;
async fn callback_callback_with_header_post(
&self, &self,
callback_request_query_url: String, callback_request_query_url: String,
information: Option<String>, information: Option<String>,
) -> Box<dyn Future<Item=CallbackCallbackWithHeaderPostResponse, Error=ApiError> + Send>; ) -> Result<CallbackCallbackWithHeaderPostResponse, ApiError>;
fn callback_callback_post( async fn callback_callback_post(
&self, &self,
callback_request_query_url: String, callback_request_query_url: String,
) -> Box<dyn Future<Item=CallbackCallbackPostResponse, Error=ApiError> + Send>; ) -> Result<CallbackCallbackPostResponse, ApiError>;
} }
/// Trait to extend an API to make it easy to bind it to a context. pub trait CallbackContextWrapperExt<C: Send + Sync> where Self: Sized
pub trait CallbackContextWrapperExt<'a, C> where Self: Sized { {
/// Binds this API to a context. /// Binds this API to a context.
fn with_context(self: &'a Self, context: C) -> ContextWrapper<'a, Self, C>; fn with_context(self: Self, context: C) -> ContextWrapper<Self, C>;
} }
impl<'a, T: CallbackApi<C> + Sized, C> CallbackContextWrapperExt<'a, C> for T { impl<T: CallbackApi<C> + Send + Sync, C: Clone + Send + Sync> CallbackContextWrapperExt<C> for T {
fn with_context(self: &'a T, context: C) -> ContextWrapper<'a, T, C> { fn with_context(self: T, context: C) -> ContextWrapper<T, C> {
ContextWrapper::<T, C>::new(self, context) ContextWrapper::<T, C>::new(self, context)
} }
} }
impl<'a, T: CallbackApi<C>, C> CallbackApiNoContext for ContextWrapper<'a, T, C> { #[async_trait]
fn callback_callback_with_header_post( impl<T: CallbackApi<C> + Send + Sync, C: Clone + Send + Sync> CallbackApiNoContext<C> for ContextWrapper<T, C> {
fn poll_ready(&self, cx: &mut Context) -> Poll<Result<(), ServiceError>> {
self.api().poll_ready(cx)
}
fn context(&self) -> &C {
ContextWrapper::context(self)
}
async fn callback_callback_with_header_post(
&self, &self,
callback_request_query_url: String, callback_request_query_url: String,
information: Option<String>, information: Option<String>,
) -> Box<dyn Future<Item=CallbackCallbackWithHeaderPostResponse, Error=ApiError> + Send> ) -> Result<CallbackCallbackWithHeaderPostResponse, ApiError>
{ {
let context = self.context().clone();
self.api().callback_callback_with_header_post( self.api().callback_callback_with_header_post(
callback_request_query_url, callback_request_query_url,
information, information,
&self.context()) &context).await
} }
fn callback_callback_post( async fn callback_callback_post(
&self, &self,
callback_request_query_url: String, callback_request_query_url: String,
) -> Box<dyn Future<Item=CallbackCallbackPostResponse, Error=ApiError> + Send> ) -> Result<CallbackCallbackPostResponse, ApiError>
{ {
let context = self.context().clone();
self.api().callback_callback_post( self.api().callback_callback_post(
callback_request_query_url, callback_request_query_url,
&self.context()) &context).await
} }
} }

View File

@ -1,68 +1,83 @@
use futures; use async_trait::async_trait;
use futures::{Future, Stream, future, stream}; use futures::{Stream, future, future::BoxFuture, stream, future::TryFutureExt, future::FutureExt, stream::StreamExt};
use hyper;
use hyper::client::HttpConnector;
use hyper::header::{HeaderName, HeaderValue, CONTENT_TYPE}; use hyper::header::{HeaderName, HeaderValue, CONTENT_TYPE};
use hyper::{Body, Uri, Response}; use hyper::{Body, Request, Response, service::Service, Uri};
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] use percent_encoding::{utf8_percent_encode, AsciiSet};
use hyper_openssl::HttpsConnector;
use serde_json;
use std::borrow::Cow; use std::borrow::Cow;
use std::convert::TryInto; use std::convert::TryInto;
use std::io::{Read, Error, ErrorKind}; use std::io::{ErrorKind, Read};
use std::error; use std::error::Error;
use std::future::Future;
use std::fmt; use std::fmt;
use std::path::Path; use std::path::Path;
use std::sync::Arc; use std::sync::{Arc, Mutex};
use std::str; use std::str;
use std::str::FromStr; use std::str::FromStr;
use std::string::ToString; use std::string::ToString;
use swagger; use std::task::{Context, Poll};
use swagger::{ApiError, Connector, client::Service, XSpanIdString, Has, AuthData}; use swagger::{ApiError, AuthData, BodyExt, Connector, Has, XSpanIdString};
use url::form_urlencoded; use url::form_urlencoded;
use url::percent_encoding::{utf8_percent_encode, PATH_SEGMENT_ENCODE_SET, QUERY_ENCODE_SET};
use uuid;
use serde_xml_rs;
use crate::models; use crate::models;
use crate::header; use crate::header;
url::define_encode_set! { /// https://url.spec.whatwg.org/#fragment-percent-encode-set
/// This encode set is used for object IDs #[allow(dead_code)]
/// const FRAGMENT_ENCODE_SET: &AsciiSet = &percent_encoding::CONTROLS
/// Aside from the special characters defined in the `PATH_SEGMENT_ENCODE_SET`, .add(b' ').add(b'"').add(b'<').add(b'>').add(b'`');
/// the vertical bar (|) is encoded.
pub ID_ENCODE_SET = [PATH_SEGMENT_ENCODE_SET] | {'|'} /// This encode set is used for object IDs
} ///
/// Aside from the special characters defined in the `PATH_SEGMENT_ENCODE_SET`,
/// the vertical bar (|) is encoded.
#[allow(dead_code)]
const ID_ENCODE_SET: &AsciiSet = &FRAGMENT_ENCODE_SET.add(b'|');
use crate::CallbackApi; use crate::CallbackApi;
use crate::CallbackCallbackWithHeaderPostResponse; use crate::CallbackCallbackWithHeaderPostResponse;
use crate::CallbackCallbackPostResponse; use crate::CallbackCallbackPostResponse;
/// A client that implements the API by making HTTP calls out to a server. /// A client that implements the API by making HTTP calls out to a server.
pub struct Client<F> pub struct Client<S> where
S: Service<
Request<Body>,
Response=Response<Body>,
Error=hyper::Error> + Clone + Send + Sync,
S::Future: Send + 'static,
{ {
/// Inner service /// Inner service
client_service: Arc<Box<dyn Service<ReqBody=Body, Future=F> + Send + Sync>>, client_service: S,
} }
impl<F> fmt::Debug for Client<F> impl<S> fmt::Debug for Client<S> where
S: Service<
Request<Body>,
Response=Response<Body>,
Error=hyper::Error> + Clone + Send + Sync,
S::Future: Send + 'static,
{ {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Client") write!(f, "Client")
} }
} }
impl<F> Clone for Client<F> impl<S> Clone for Client<S> where
S: Service<
Request<Body>,
Response=Response<Body>,
Error=hyper::Error> + Clone + Send + Sync,
S::Future: Send + 'static,
{ {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Client { Self {
client_service: self.client_service.clone(), client_service: self.client_service.clone(),
} }
} }
} }
impl Client<hyper::client::ResponseFuture> impl<C> Client<hyper::client::Client<C, Body>> where
C: hyper::client::connect::Connect + Clone + Send + Sync + 'static
{ {
/// Create a client with a custom implementation of hyper::client::Connect. /// Create a client with a custom implementation of hyper::client::Connect.
/// ///
@ -76,26 +91,33 @@ impl Client<hyper::client::ResponseFuture>
/// # Arguments /// # Arguments
/// ///
/// * `connector` - Implementation of `hyper::client::Connect` to use for the client /// * `connector` - Implementation of `hyper::client::Connect` to use for the client
pub fn new_with_connector<C>( pub fn new_with_connector(connector: C) -> Self
connector: C,
) -> Self where
C: hyper::client::connect::Connect + 'static,
C::Transport: 'static,
C::Future: 'static,
{ {
let client_service = Box::new(hyper::client::Client::builder().build(connector)); let client_service = hyper::client::Client::builder().build(connector);
Client { Self {
client_service: Arc::new(client_service), client_service,
} }
} }
}
impl Client<hyper::client::Client<hyper::client::HttpConnector, Body>>
{
/// Create an HTTP client. /// Create an HTTP client.
pub fn new_http() -> Self { pub fn new_http() -> Self {
let http_connector = Connector::builder().build(); let http_connector = Connector::builder().build();
Self::new_with_connector(http_connector) Self::new_with_connector(http_connector)
} }
}
#[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))]
type HttpConnector = hyper_tls::HttpsConnector<hyper::client::HttpConnector>;
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
type HttpsConnector = hyper_openssl::HttpsConnector<hyper::client::HttpConnector>;
impl Client<hyper::client::Client<HttpsConnector, Body>>
{
/// Create a client with a TLS connection to the server. /// Create a client with a TLS connection to the server.
#[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))] #[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))]
pub fn new_https() -> Result<Self, native_tls::Error> pub fn new_https() -> Result<Self, native_tls::Error>
@ -155,60 +177,82 @@ impl Client<hyper::client::ResponseFuture>
} }
} }
impl<F> Client<F> impl<S> Client<S> where
S: Service<
Request<Body>,
Response=Response<Body>,
Error=hyper::Error> + Clone + Send + Sync,
S::Future: Send + 'static,
{ {
/// Constructor for creating a `Client` by passing in a pre-made `swagger::Service` /// Constructor for creating a `Client` by passing in a pre-made `swagger::Service`
/// ///
/// This allows adding custom wrappers around the underlying transport, for example for logging. /// This allows adding custom wrappers around the underlying transport, for example for logging.
pub fn new_with_client_service( pub fn new_with_client_service(
client_service: Arc<Box<dyn Service<ReqBody=Body, Future=F> + Send + Sync>>, client_service: S,
) -> Self { ) -> Self {
Client { Client {
client_service: client_service, client_service,
} }
} }
} }
impl<C, F> CallbackApi<C> for Client<F> where #[async_trait]
C: Has<XSpanIdString> + Has<Option<AuthData>>, impl<C, S> CallbackApi<C> for Client<S> where
F: Future<Item=Response<Body>, Error=hyper::Error> + Send + 'static C: Has<XSpanIdString> + Has<Option<AuthData>> + Send + Sync,
S: Service<
Request<Body>,
Response=Response<Body>,
Error=hyper::Error> + Clone + Send + Sync,
S::Future: Send + 'static,
S::Error: Into<crate::ServiceError> + fmt::Display,
{ {
fn callback_callback_with_header_post( fn poll_ready(&self, cx: &mut Context) -> Poll<Result<(), crate::ServiceError>> {
match self.client_service.clone().poll_ready(cx) {
Poll::Ready(Err(e)) => Poll::Ready(Err(Box::new(e))),
Poll::Ready(Ok(o)) => Poll::Ready(Ok(o)),
Poll::Pending => Poll::Pending,
}
}
async fn callback_callback_with_header_post(
&self, &self,
callback_request_query_url: String, callback_request_query_url: String,
param_information: Option<String>, param_information: Option<String>,
context: &C) -> Box<dyn Future<Item=CallbackCallbackWithHeaderPostResponse, Error=ApiError> + Send> context: &C) -> Result<CallbackCallbackWithHeaderPostResponse, ApiError>
{ {
let mut client_service = self.client_service.clone();
let mut uri = format!( let mut uri = format!(
"{request_query_url}/callback-with-header" "{request_query_url}/callback-with-header"
,request_query_url=callback_request_query_url ,request_query_url=callback_request_query_url
); );
// Query parameters // Query parameters
let mut query_string = url::form_urlencoded::Serializer::new("".to_owned()); let query_string = {
let query_string_str = query_string.finish(); let mut query_string = form_urlencoded::Serializer::new("".to_owned());
if !query_string_str.is_empty() { query_string.finish()
};
if !query_string.is_empty() {
uri += "?"; uri += "?";
uri += &query_string_str; uri += &query_string;
} }
let uri = match Uri::from_str(&uri) { let uri = match Uri::from_str(&uri) {
Ok(uri) => uri, Ok(uri) => uri,
Err(err) => return Box::new(future::err(ApiError(format!("Unable to build URI: {}", err)))), Err(err) => return Err(ApiError(format!("Unable to build URI: {}", err))),
}; };
let mut request = match hyper::Request::builder() let mut request = match Request::builder()
.method("POST") .method("POST")
.uri(uri) .uri(uri)
.body(Body::empty()) { .body(Body::empty()) {
Ok(req) => req, Ok(req) => req,
Err(e) => return Box::new(future::err(ApiError(format!("Unable to create request: {}", e)))) Err(e) => return Err(ApiError(format!("Unable to create request: {}", e)))
}; };
let header = HeaderValue::from_str((context as &dyn Has<XSpanIdString>).get().0.clone().to_string().as_str()); let header = HeaderValue::from_str(Has::<XSpanIdString>::get(context).0.clone().to_string().as_str());
request.headers_mut().insert(HeaderName::from_static("x-span-id"), match header { request.headers_mut().insert(HeaderName::from_static("x-span-id"), match header {
Ok(h) => h, Ok(h) => h,
Err(e) => return Box::new(future::err(ApiError(format!("Unable to create X-Span ID header value: {}", e)))) Err(e) => return Err(ApiError(format!("Unable to create X-Span ID header value: {}", e)))
}); });
// Header parameters // Header parameters
@ -219,119 +263,112 @@ impl<C, F> CallbackApi<C> for Client<F> where
match header::IntoHeaderValue(param_information.clone()).try_into() { match header::IntoHeaderValue(param_information.clone()).try_into() {
Ok(header) => header, Ok(header) => header,
Err(e) => { Err(e) => {
return Box::new(future::err(ApiError(format!( return Err(ApiError(format!(
"Invalid header information - {}", e)))) as Box<dyn Future<Item=_, Error=_> + Send>; "Invalid header information - {}", e)));
}, },
}); });
}, },
None => {} None => {}
} }
Box::new(self.client_service.request(request) let mut response = client_service.call(request)
.map_err(|e| ApiError(format!("No response received: {}", e))) .map_err(|e| ApiError(format!("No response received: {}", e))).await?;
.and_then(|mut response| {
match response.status().as_u16() { match response.status().as_u16() {
204 => { 204 => {
let body = response.into_body(); let body = response.into_body();
Box::new( Ok(
future::ok( CallbackCallbackWithHeaderPostResponse::OK
CallbackCallbackWithHeaderPostResponse::OK )
)
) as Box<dyn Future<Item=_, Error=_> + Send>
},
code => {
let headers = response.headers().clone();
Box::new(response.into_body()
.take(100)
.concat2()
.then(move |body|
future::err(ApiError(format!("Unexpected response code {}:\n{:?}\n\n{}",
code,
headers,
match body {
Ok(ref body) => match str::from_utf8(body) {
Ok(body) => Cow::from(body),
Err(e) => Cow::from(format!("<Body was not UTF8: {:?}>", e)),
},
Err(e) => Cow::from(format!("<Failed to read body: {}>", e)),
})))
)
) as Box<dyn Future<Item=_, Error=_> + Send>
}
} }
})) code => {
let headers = response.headers().clone();
let body = response.into_body()
.take(100)
.to_raw().await;
Err(ApiError(format!("Unexpected response code {}:\n{:?}\n\n{}",
code,
headers,
match body {
Ok(body) => match String::from_utf8(body) {
Ok(body) => body,
Err(e) => format!("<Body was not UTF8: {:?}>", e),
},
Err(e) => format!("<Failed to read body: {}>", e),
}
)))
}
}
} }
fn callback_callback_post( async fn callback_callback_post(
&self, &self,
callback_request_query_url: String, callback_request_query_url: String,
context: &C) -> Box<dyn Future<Item=CallbackCallbackPostResponse, Error=ApiError> + Send> context: &C) -> Result<CallbackCallbackPostResponse, ApiError>
{ {
let mut client_service = self.client_service.clone();
let mut uri = format!( let mut uri = format!(
"{request_query_url}/callback" "{request_query_url}/callback"
,request_query_url=callback_request_query_url ,request_query_url=callback_request_query_url
); );
// Query parameters // Query parameters
let mut query_string = url::form_urlencoded::Serializer::new("".to_owned()); let query_string = {
let query_string_str = query_string.finish(); let mut query_string = form_urlencoded::Serializer::new("".to_owned());
if !query_string_str.is_empty() { query_string.finish()
};
if !query_string.is_empty() {
uri += "?"; uri += "?";
uri += &query_string_str; uri += &query_string;
} }
let uri = match Uri::from_str(&uri) { let uri = match Uri::from_str(&uri) {
Ok(uri) => uri, Ok(uri) => uri,
Err(err) => return Box::new(future::err(ApiError(format!("Unable to build URI: {}", err)))), Err(err) => return Err(ApiError(format!("Unable to build URI: {}", err))),
}; };
let mut request = match hyper::Request::builder() let mut request = match Request::builder()
.method("POST") .method("POST")
.uri(uri) .uri(uri)
.body(Body::empty()) { .body(Body::empty()) {
Ok(req) => req, Ok(req) => req,
Err(e) => return Box::new(future::err(ApiError(format!("Unable to create request: {}", e)))) Err(e) => return Err(ApiError(format!("Unable to create request: {}", e)))
}; };
let header = HeaderValue::from_str((context as &dyn Has<XSpanIdString>).get().0.clone().to_string().as_str()); let header = HeaderValue::from_str(Has::<XSpanIdString>::get(context).0.clone().to_string().as_str());
request.headers_mut().insert(HeaderName::from_static("x-span-id"), match header { request.headers_mut().insert(HeaderName::from_static("x-span-id"), match header {
Ok(h) => h, Ok(h) => h,
Err(e) => return Box::new(future::err(ApiError(format!("Unable to create X-Span ID header value: {}", e)))) Err(e) => return Err(ApiError(format!("Unable to create X-Span ID header value: {}", e)))
}); });
Box::new(self.client_service.request(request) let mut response = client_service.call(request)
.map_err(|e| ApiError(format!("No response received: {}", e))) .map_err(|e| ApiError(format!("No response received: {}", e))).await?;
.and_then(|mut response| {
match response.status().as_u16() { match response.status().as_u16() {
204 => { 204 => {
let body = response.into_body(); let body = response.into_body();
Box::new( Ok(
future::ok( CallbackCallbackPostResponse::OK
CallbackCallbackPostResponse::OK )
)
) as Box<dyn Future<Item=_, Error=_> + Send>
},
code => {
let headers = response.headers().clone();
Box::new(response.into_body()
.take(100)
.concat2()
.then(move |body|
future::err(ApiError(format!("Unexpected response code {}:\n{:?}\n\n{}",
code,
headers,
match body {
Ok(ref body) => match str::from_utf8(body) {
Ok(body) => Cow::from(body),
Err(e) => Cow::from(format!("<Body was not UTF8: {:?}>", e)),
},
Err(e) => Cow::from(format!("<Failed to read body: {}>", e)),
})))
)
) as Box<dyn Future<Item=_, Error=_> + Send>
}
} }
})) code => {
let headers = response.headers().clone();
let body = response.into_body()
.take(100)
.to_raw().await;
Err(ApiError(format!("Unexpected response code {}:\n{:?}\n\n{}",
code,
headers,
match body {
Ok(body) => match String::from_utf8(body) {
Ok(body) => body,
Err(e) => format!("<Body was not UTF8: {:?}>", e),
},
Err(e) => format!("<Failed to read body: {}>", e),
}
)))
}
}
} }
} }

View File

@ -9,7 +9,7 @@ edition = "2018"
[features] [features]
default = ["client", "server"] default = ["client", "server"]
client = [ client = [
"hyper", "hyper-openssl", "native-tls", "openssl", "url" "hyper", "hyper-openssl", "hyper-tls", "native-tls", "openssl", "url"
] ]
server = [ server = [
"serde_ignored", "hyper", "regex", "percent-encoding", "url", "lazy_static" "serde_ignored", "hyper", "regex", "percent-encoding", "url", "lazy_static"
@ -18,35 +18,37 @@ conversion = ["frunk", "frunk_derives", "frunk_core", "frunk-enum-core", "frunk-
[target.'cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))'.dependencies] [target.'cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))'.dependencies]
native-tls = { version = "0.2", optional = true } native-tls = { version = "0.2", optional = true }
hyper-tls = { version = "0.4", optional = true }
[target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dependencies] [target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dependencies]
hyper-openssl = { version = "0.7.1", optional = true } hyper-openssl = { version = "0.8", optional = true }
openssl = {version = "0.10", optional = true } openssl = {version = "0.10", optional = true }
[dependencies] [dependencies]
# Common # Common
async-trait = "0.1.24"
chrono = { version = "0.4", features = ["serde"] } chrono = { version = "0.4", features = ["serde"] }
futures = "0.1" futures = "0.3"
swagger = "4.0" swagger = "5.0.0-alpha-1"
log = "0.4.0" log = "0.4.0"
mime = "0.3" mime = "0.3"
serde = { version = "1.0", features = ["derive"]} serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"
# Crates included if required by the API definition # Crates included if required by the API definition
# Common between server and client features # Common between server and client features
hyper = {version = "0.12", optional = true} hyper = {version = "0.13", optional = true}
serde_ignored = {version = "0.0.4", optional = true} serde_ignored = {version = "0.1.1", optional = true}
url = {version = "1.5", optional = true} url = {version = "2.1", optional = true}
# Client-specific # Client-specific
# Server, and client callback-specific # Server, and client callback-specific
lazy_static = { version = "1.4", optional = true } lazy_static = { version = "1.4", optional = true }
percent-encoding = {version = "1.0.0", optional = true} percent-encoding = {version = "2.1.0", optional = true}
regex = {version = "0.2", optional = true} regex = {version = "1.3", optional = true}
# Conversion # Conversion
frunk = { version = "0.3.0", optional = true } frunk = { version = "0.3.0", optional = true }
@ -57,13 +59,13 @@ frunk-enum-core = { version = "0.2.0", optional = true }
[dev-dependencies] [dev-dependencies]
clap = "2.25" clap = "2.25"
error-chain = "0.12" env_logger = "0.7"
env_logger = "0.6" tokio = { version = "0.2", features = ["rt-threaded", "macros", "stream"] }
tokio = "0.1.17" native-tls = "0.2"
uuid = {version = "0.7", features = ["serde", "v4"]} tokio-tls = "0.3"
[target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dev-dependencies] [target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dev-dependencies]
tokio-openssl = "0.3" tokio-openssl = "0.4"
openssl = "0.10" openssl = "0.10"
[[example]] [[example]]

View File

@ -2,10 +2,9 @@
#[allow(unused_imports)] #[allow(unused_imports)]
use futures::{Future, future, Stream, stream}; use futures::{future, Stream, stream};
#[allow(unused_imports)] #[allow(unused_imports)]
use ops_v3::{Api, ApiNoContext, Client, ContextWrapperExt, models, use ops_v3::{Api, ApiNoContext, Client, ContextWrapperExt, models,
ApiError,
Op10GetResponse, Op10GetResponse,
Op11GetResponse, Op11GetResponse,
Op12GetResponse, Op12GetResponse,
@ -42,7 +41,7 @@ use ops_v3::{Api, ApiNoContext, Client, ContextWrapperExt, models,
Op6GetResponse, Op6GetResponse,
Op7GetResponse, Op7GetResponse,
Op8GetResponse, Op8GetResponse,
Op9GetResponse Op9GetResponse,
}; };
use clap::{App, Arg}; use clap::{App, Arg};
@ -51,7 +50,9 @@ use log::info;
// swagger::Has may be unused if there are no examples // swagger::Has may be unused if there are no examples
#[allow(unused_imports)] #[allow(unused_imports)]
use swagger::{ContextBuilder, EmptyContext, XSpanIdString, Has, Push, AuthData}; use swagger::{AuthData, ContextBuilder, EmptyContext, Has, Push, XSpanIdString};
type ClientContext = swagger::make_context_ty!(ContextBuilder, EmptyContext, Option<AuthData>, XSpanIdString);
// rt may be unused if there are no examples // rt may be unused if there are no examples
#[allow(unused_mut)] #[allow(unused_mut)]
@ -123,21 +124,21 @@ fn main() {
matches.value_of("host").unwrap(), matches.value_of("host").unwrap(),
matches.value_of("port").unwrap()); matches.value_of("port").unwrap());
let client = if matches.is_present("https") { let context: ClientContext =
// Using Simple HTTPS
Client::try_new_https(&base_url)
.expect("Failed to create HTTPS client")
} else {
// Using HTTP
Client::try_new_http(
&base_url)
.expect("Failed to create HTTP client")
};
let context: swagger::make_context_ty!(ContextBuilder, EmptyContext, Option<AuthData>, XSpanIdString) =
swagger::make_context!(ContextBuilder, EmptyContext, None as Option<AuthData>, XSpanIdString::default()); swagger::make_context!(ContextBuilder, EmptyContext, None as Option<AuthData>, XSpanIdString::default());
let client = client.with_context(context); let mut client : Box<dyn ApiNoContext<ClientContext>> = if matches.is_present("https") {
// Using Simple HTTPS
let client = Box::new(Client::try_new_https(&base_url)
.expect("Failed to create HTTPS client"));
Box::new(client.with_context(context))
} else {
// Using HTTP
let client = Box::new(Client::try_new_http(
&base_url)
.expect("Failed to create HTTP client"));
Box::new(client.with_context(context))
};
let mut rt = tokio::runtime::Runtime::new().unwrap(); let mut rt = tokio::runtime::Runtime::new().unwrap();

View File

@ -9,7 +9,8 @@ mod server;
/// Create custom server, wire it to the autogenerated router, /// Create custom server, wire it to the autogenerated router,
/// and pass it to the web server. /// and pass it to the web server.
fn main() { #[tokio::main]
async fn main() {
env_logger::init(); env_logger::init();
let matches = App::new("server") let matches = App::new("server")
@ -20,5 +21,5 @@ fn main() {
let addr = "127.0.0.1:8080"; let addr = "127.0.0.1:8080";
hyper::rt::run(server::create(addr, matches.is_present("https"))); server::create(addr, matches.is_present("https")).await;
} }

View File

@ -2,30 +2,22 @@
#![allow(unused_imports)] #![allow(unused_imports)]
mod errors { use async_trait::async_trait;
error_chain::error_chain!{} use futures::{future, Stream, StreamExt, TryFutureExt, TryStreamExt};
}
pub use self::errors::*;
use chrono;
use futures::{future, Future, Stream};
use hyper::server::conn::Http; use hyper::server::conn::Http;
use hyper::service::MakeService as _; use hyper::service::Service;
use log::info; use log::info;
use openssl::ssl::SslAcceptorBuilder; use openssl::ssl::SslAcceptorBuilder;
use std::future::Future;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::net::SocketAddr; use std::net::SocketAddr;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use swagger; use std::task::{Context, Poll};
use swagger::{Has, XSpanIdString}; use swagger::{Has, XSpanIdString};
use swagger::auth::MakeAllowAllAuthenticator; use swagger::auth::MakeAllowAllAuthenticator;
use swagger::EmptyContext; use swagger::EmptyContext;
use tokio::net::TcpListener; use tokio::net::TcpListener;
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
use tokio_openssl::SslAcceptorExt;
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod}; use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod};
@ -33,18 +25,18 @@ use ops_v3::models;
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
/// Builds an SSL implementation for Simple HTTPS from some hard-coded file names /// Builds an SSL implementation for Simple HTTPS from some hard-coded file names
pub fn create(addr: &str, https: bool) -> Box<dyn Future<Item = (), Error = ()> + Send> { pub async fn create(addr: &str, https: bool) {
let addr = addr.parse().expect("Failed to parse bind address"); let addr = addr.parse().expect("Failed to parse bind address");
let server = Server::new(); let server = Server::new();
let service_fn = MakeService::new(server); let service = MakeService::new(server);
let service_fn = MakeAllowAllAuthenticator::new(service_fn, "cosmo"); let service = MakeAllowAllAuthenticator::new(service, "cosmo");
let service_fn = let mut service =
ops_v3::server::context::MakeAddContext::<_, EmptyContext>::new( ops_v3::server::context::MakeAddContext::<_, EmptyContext>::new(
service_fn service
); );
if https { if https {
@ -62,32 +54,31 @@ pub fn create(addr: &str, https: bool) -> Box<dyn Future<Item = (), Error = ()>
ssl.set_certificate_chain_file("examples/server-chain.pem").expect("Failed to set cerificate chain"); ssl.set_certificate_chain_file("examples/server-chain.pem").expect("Failed to set cerificate chain");
ssl.check_private_key().expect("Failed to check private key"); ssl.check_private_key().expect("Failed to check private key");
let tls_acceptor = ssl.build(); let tls_acceptor = Arc::new(ssl.build());
let service_fn = Arc::new(Mutex::new(service_fn)); let mut tcp_listener = TcpListener::bind(&addr).await.unwrap();
let tls_listener = TcpListener::bind(&addr).unwrap().incoming().for_each(move |tcp| { let mut incoming = tcp_listener.incoming();
let addr = tcp.peer_addr().expect("Unable to get remote address");
let service_fn = service_fn.clone(); while let (Some(tcp), rest) = incoming.into_future().await {
if let Ok(tcp) = tcp {
let addr = tcp.peer_addr().expect("Unable to get remote address");
let service = service.call(addr);
let tls_acceptor = Arc::clone(&tls_acceptor);
hyper::rt::spawn(tls_acceptor.accept_async(tcp).map_err(|_| ()).and_then(move |tls| { tokio::spawn(async move {
let ms = { let tls = tokio_openssl::accept(&*tls_acceptor, tcp).await.map_err(|_| ())?;
let mut service_fn = service_fn.lock().unwrap();
service_fn.make_service(&addr)
};
ms.and_then(move |service| { let service = service.await.map_err(|_| ())?;
Http::new().serve_connection(tls, service)
}).map_err(|_| ())
}));
Ok(()) Http::new().serve_connection(tls, service).await.map_err(|_| ())
}).map_err(|_| ()); });
}
Box::new(tls_listener) incoming = rest;
}
} }
} else { } else {
// Using HTTP // Using HTTP
Box::new(hyper::server::Server::bind(&addr).serve(service_fn).map_err(|e| panic!("{:?}", e))) hyper::server::Server::bind(&addr).serve(service).await.unwrap()
} }
} }
@ -105,7 +96,6 @@ impl<C> Server<C> {
use ops_v3::{ use ops_v3::{
Api, Api,
ApiError,
Op10GetResponse, Op10GetResponse,
Op11GetResponse, Op11GetResponse,
Op12GetResponse, Op12GetResponse,
@ -145,339 +135,343 @@ use ops_v3::{
Op9GetResponse, Op9GetResponse,
}; };
use ops_v3::server::MakeService; use ops_v3::server::MakeService;
use std::error::Error;
use swagger::ApiError;
impl<C> Api<C> for Server<C> where C: Has<XSpanIdString>{ #[async_trait]
fn op10_get( impl<C> Api<C> for Server<C> where C: Has<XSpanIdString> + Send + Sync
{
async fn op10_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op10GetResponse, Error=ApiError> + Send> context: &C) -> Result<Op10GetResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("op10_get() - X-Span-ID: {:?}", context.get().0.clone()); info!("op10_get() - X-Span-ID: {:?}", context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn op11_get( async fn op11_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op11GetResponse, Error=ApiError> + Send> context: &C) -> Result<Op11GetResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("op11_get() - X-Span-ID: {:?}", context.get().0.clone()); info!("op11_get() - X-Span-ID: {:?}", context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn op12_get( async fn op12_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op12GetResponse, Error=ApiError> + Send> context: &C) -> Result<Op12GetResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("op12_get() - X-Span-ID: {:?}", context.get().0.clone()); info!("op12_get() - X-Span-ID: {:?}", context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn op13_get( async fn op13_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op13GetResponse, Error=ApiError> + Send> context: &C) -> Result<Op13GetResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("op13_get() - X-Span-ID: {:?}", context.get().0.clone()); info!("op13_get() - X-Span-ID: {:?}", context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn op14_get( async fn op14_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op14GetResponse, Error=ApiError> + Send> context: &C) -> Result<Op14GetResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("op14_get() - X-Span-ID: {:?}", context.get().0.clone()); info!("op14_get() - X-Span-ID: {:?}", context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn op15_get( async fn op15_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op15GetResponse, Error=ApiError> + Send> context: &C) -> Result<Op15GetResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("op15_get() - X-Span-ID: {:?}", context.get().0.clone()); info!("op15_get() - X-Span-ID: {:?}", context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn op16_get( async fn op16_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op16GetResponse, Error=ApiError> + Send> context: &C) -> Result<Op16GetResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("op16_get() - X-Span-ID: {:?}", context.get().0.clone()); info!("op16_get() - X-Span-ID: {:?}", context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn op17_get( async fn op17_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op17GetResponse, Error=ApiError> + Send> context: &C) -> Result<Op17GetResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("op17_get() - X-Span-ID: {:?}", context.get().0.clone()); info!("op17_get() - X-Span-ID: {:?}", context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn op18_get( async fn op18_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op18GetResponse, Error=ApiError> + Send> context: &C) -> Result<Op18GetResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("op18_get() - X-Span-ID: {:?}", context.get().0.clone()); info!("op18_get() - X-Span-ID: {:?}", context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn op19_get( async fn op19_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op19GetResponse, Error=ApiError> + Send> context: &C) -> Result<Op19GetResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("op19_get() - X-Span-ID: {:?}", context.get().0.clone()); info!("op19_get() - X-Span-ID: {:?}", context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn op1_get( async fn op1_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op1GetResponse, Error=ApiError> + Send> context: &C) -> Result<Op1GetResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("op1_get() - X-Span-ID: {:?}", context.get().0.clone()); info!("op1_get() - X-Span-ID: {:?}", context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn op20_get( async fn op20_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op20GetResponse, Error=ApiError> + Send> context: &C) -> Result<Op20GetResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("op20_get() - X-Span-ID: {:?}", context.get().0.clone()); info!("op20_get() - X-Span-ID: {:?}", context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn op21_get( async fn op21_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op21GetResponse, Error=ApiError> + Send> context: &C) -> Result<Op21GetResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("op21_get() - X-Span-ID: {:?}", context.get().0.clone()); info!("op21_get() - X-Span-ID: {:?}", context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn op22_get( async fn op22_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op22GetResponse, Error=ApiError> + Send> context: &C) -> Result<Op22GetResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("op22_get() - X-Span-ID: {:?}", context.get().0.clone()); info!("op22_get() - X-Span-ID: {:?}", context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn op23_get( async fn op23_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op23GetResponse, Error=ApiError> + Send> context: &C) -> Result<Op23GetResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("op23_get() - X-Span-ID: {:?}", context.get().0.clone()); info!("op23_get() - X-Span-ID: {:?}", context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn op24_get( async fn op24_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op24GetResponse, Error=ApiError> + Send> context: &C) -> Result<Op24GetResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("op24_get() - X-Span-ID: {:?}", context.get().0.clone()); info!("op24_get() - X-Span-ID: {:?}", context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn op25_get( async fn op25_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op25GetResponse, Error=ApiError> + Send> context: &C) -> Result<Op25GetResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("op25_get() - X-Span-ID: {:?}", context.get().0.clone()); info!("op25_get() - X-Span-ID: {:?}", context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn op26_get( async fn op26_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op26GetResponse, Error=ApiError> + Send> context: &C) -> Result<Op26GetResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("op26_get() - X-Span-ID: {:?}", context.get().0.clone()); info!("op26_get() - X-Span-ID: {:?}", context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn op27_get( async fn op27_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op27GetResponse, Error=ApiError> + Send> context: &C) -> Result<Op27GetResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("op27_get() - X-Span-ID: {:?}", context.get().0.clone()); info!("op27_get() - X-Span-ID: {:?}", context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn op28_get( async fn op28_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op28GetResponse, Error=ApiError> + Send> context: &C) -> Result<Op28GetResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("op28_get() - X-Span-ID: {:?}", context.get().0.clone()); info!("op28_get() - X-Span-ID: {:?}", context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn op29_get( async fn op29_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op29GetResponse, Error=ApiError> + Send> context: &C) -> Result<Op29GetResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("op29_get() - X-Span-ID: {:?}", context.get().0.clone()); info!("op29_get() - X-Span-ID: {:?}", context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn op2_get( async fn op2_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op2GetResponse, Error=ApiError> + Send> context: &C) -> Result<Op2GetResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("op2_get() - X-Span-ID: {:?}", context.get().0.clone()); info!("op2_get() - X-Span-ID: {:?}", context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn op30_get( async fn op30_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op30GetResponse, Error=ApiError> + Send> context: &C) -> Result<Op30GetResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("op30_get() - X-Span-ID: {:?}", context.get().0.clone()); info!("op30_get() - X-Span-ID: {:?}", context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn op31_get( async fn op31_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op31GetResponse, Error=ApiError> + Send> context: &C) -> Result<Op31GetResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("op31_get() - X-Span-ID: {:?}", context.get().0.clone()); info!("op31_get() - X-Span-ID: {:?}", context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn op32_get( async fn op32_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op32GetResponse, Error=ApiError> + Send> context: &C) -> Result<Op32GetResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("op32_get() - X-Span-ID: {:?}", context.get().0.clone()); info!("op32_get() - X-Span-ID: {:?}", context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn op33_get( async fn op33_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op33GetResponse, Error=ApiError> + Send> context: &C) -> Result<Op33GetResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("op33_get() - X-Span-ID: {:?}", context.get().0.clone()); info!("op33_get() - X-Span-ID: {:?}", context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn op34_get( async fn op34_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op34GetResponse, Error=ApiError> + Send> context: &C) -> Result<Op34GetResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("op34_get() - X-Span-ID: {:?}", context.get().0.clone()); info!("op34_get() - X-Span-ID: {:?}", context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn op35_get( async fn op35_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op35GetResponse, Error=ApiError> + Send> context: &C) -> Result<Op35GetResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("op35_get() - X-Span-ID: {:?}", context.get().0.clone()); info!("op35_get() - X-Span-ID: {:?}", context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn op36_get( async fn op36_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op36GetResponse, Error=ApiError> + Send> context: &C) -> Result<Op36GetResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("op36_get() - X-Span-ID: {:?}", context.get().0.clone()); info!("op36_get() - X-Span-ID: {:?}", context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn op37_get( async fn op37_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op37GetResponse, Error=ApiError> + Send> context: &C) -> Result<Op37GetResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("op37_get() - X-Span-ID: {:?}", context.get().0.clone()); info!("op37_get() - X-Span-ID: {:?}", context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn op3_get( async fn op3_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op3GetResponse, Error=ApiError> + Send> context: &C) -> Result<Op3GetResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("op3_get() - X-Span-ID: {:?}", context.get().0.clone()); info!("op3_get() - X-Span-ID: {:?}", context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn op4_get( async fn op4_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op4GetResponse, Error=ApiError> + Send> context: &C) -> Result<Op4GetResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("op4_get() - X-Span-ID: {:?}", context.get().0.clone()); info!("op4_get() - X-Span-ID: {:?}", context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn op5_get( async fn op5_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op5GetResponse, Error=ApiError> + Send> context: &C) -> Result<Op5GetResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("op5_get() - X-Span-ID: {:?}", context.get().0.clone()); info!("op5_get() - X-Span-ID: {:?}", context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn op6_get( async fn op6_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op6GetResponse, Error=ApiError> + Send> context: &C) -> Result<Op6GetResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("op6_get() - X-Span-ID: {:?}", context.get().0.clone()); info!("op6_get() - X-Span-ID: {:?}", context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn op7_get( async fn op7_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op7GetResponse, Error=ApiError> + Send> context: &C) -> Result<Op7GetResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("op7_get() - X-Span-ID: {:?}", context.get().0.clone()); info!("op7_get() - X-Span-ID: {:?}", context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn op8_get( async fn op8_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op8GetResponse, Error=ApiError> + Send> context: &C) -> Result<Op8GetResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("op8_get() - X-Span-ID: {:?}", context.get().0.clone()); info!("op8_get() - X-Span-ID: {:?}", context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn op9_get( async fn op9_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op9GetResponse, Error=ApiError> + Send> context: &C) -> Result<Op9GetResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("op9_get() - X-Span-ID: {:?}", context.get().0.clone()); info!("op9_get() - X-Span-ID: {:?}", context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
} }

View File

@ -1,13 +1,12 @@
use futures::Future; use futures::future::BoxFuture;
use hyper;
use hyper::header::HeaderName; use hyper::header::HeaderName;
use hyper::{Error, Request, Response, StatusCode, service::Service, body::Payload}; use hyper::{Error, Request, Response, StatusCode, service::Service};
use url::form_urlencoded; use url::form_urlencoded;
use std::default::Default; use std::default::Default;
use std::io; use std::io;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::task::{Poll, Context};
use swagger::auth::{AuthData, Authorization, Bearer, Scopes}; use swagger::auth::{AuthData, Authorization, Bearer, Scopes};
use swagger::context::ContextualPayload;
use swagger::{EmptyContext, Has, Pop, Push, XSpanIdString}; use swagger::{EmptyContext, Has, Pop, Push, XSpanIdString};
use crate::Api; use crate::Api;
@ -31,58 +30,52 @@ where
} }
// Make a service that adds context. // Make a service that adds context.
impl<'a, T, SC, A, B, C, D, E, ME, S, OB, F> hyper::service::MakeService<&'a SC> for impl<Target, T, A, B, C, D> Service<Target> for
MakeAddContext<T, A> MakeAddContext<T, A>
where where
A: Default + Push<XSpanIdString, Result = B>, Target: Send,
A: Default + Push<XSpanIdString, Result = B> + Send,
B: Push<Option<AuthData>, Result = C>, B: Push<Option<AuthData>, Result = C>,
C: Push<Option<Authorization>, Result = D>, C: Push<Option<Authorization>, Result = D>,
D: Send + 'static, D: Send + 'static,
T: hyper::service::MakeService< T: Service<Target> + Send,
&'a SC, T::Future: Send + 'static
Error = E,
MakeError = ME,
Service = S,
ReqBody = ContextualPayload<hyper::Body, D>,
ResBody = OB,
Future = F
>,
S: Service<
Error = E,
ReqBody = ContextualPayload<hyper::Body, D>,
ResBody = OB> + 'static,
ME: swagger::ErrorBound,
E: swagger::ErrorBound,
F: Future<Item=S, Error=ME> + Send + 'static,
S::Future: Send,
OB: Payload,
{ {
type ReqBody = hyper::Body; type Error = T::Error;
type ResBody = OB; type Response = AddContext<T::Response, A, B, C, D>;
type Error = E; type Future = BoxFuture<'static, Result<Self::Response, Self::Error>>;
type MakeError = ME;
type Service = AddContext<S, A>;
type Future = Box<dyn Future<Item = Self::Service, Error = ME> + Send + 'static>;
fn make_service(&mut self, ctx: &'a SC) -> Self::Future { fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Box::new(self.inner.make_service(ctx).map(|s| AddContext::new(s))) self.inner.poll_ready(cx)
}
fn call(&mut self, target: Target) -> Self::Future {
let service = self.inner.call(target);
Box::pin(async move {
Ok(AddContext::new(service.await?))
})
} }
} }
/// Middleware to extract authentication data from request /// Middleware to add context data from the request
pub struct AddContext<T, A> { pub struct AddContext<T, A, B, C, D>
where
A: Default + Push<XSpanIdString, Result = B>,
B: Push<Option<AuthData>, Result = C>,
C: Push<Option<Authorization>, Result = D>
{
inner: T, inner: T,
marker: PhantomData<A>, marker: PhantomData<A>,
} }
impl<T, A, B, C, D> AddContext<T, A> impl<T, A, B, C, D> AddContext<T, A, B, C, D>
where where
A: Default + Push<XSpanIdString, Result = B>, A: Default + Push<XSpanIdString, Result = B>,
B: Push<Option<AuthData>, Result = C>, B: Push<Option<AuthData>, Result = C>,
C: Push<Option<Authorization>, Result = D>, C: Push<Option<Authorization>, Result = D>,
T: Service,
{ {
pub fn new(inner: T) -> AddContext<T, A> { pub fn new(inner: T) -> Self {
AddContext { AddContext {
inner, inner,
marker: PhantomData, marker: PhantomData,
@ -90,33 +83,31 @@ where
} }
} }
impl<T, A, B, C, D> Service for AddContext<T, A> impl<T, A, B, C, D, ReqBody> Service<Request<ReqBody>> for AddContext<T, A, B, C, D>
where where
A: Default + Push<XSpanIdString, Result=B>, A: Default + Push<XSpanIdString, Result=B>,
B: Push<Option<AuthData>, Result=C>, B: Push<Option<AuthData>, Result=C>,
C: Push<Option<Authorization>, Result=D>, C: Push<Option<Authorization>, Result=D>,
D: Send + 'static, D: Send + 'static,
T: Service<ReqBody = ContextualPayload<hyper::Body, D>>, T: Service<(Request<ReqBody>, D)>
T::Future: Future<Item=Response<T::ResBody>, Error=T::Error> + Send + 'static
{ {
type ReqBody = hyper::Body;
type ResBody = T::ResBody;
type Error = T::Error; type Error = T::Error;
type Future = Box<dyn Future<Item=Response<T::ResBody>, Error=T::Error> + Send + 'static>; type Future = T::Future;
type Response = T::Response;
fn call(&mut self, req: Request<Self::ReqBody>) -> Self::Future { fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
let context = A::default().push(XSpanIdString::get_or_generate(&req)); self.inner.poll_ready(cx)
let (head, body) = req.into_parts(); }
let headers = head.headers.clone();
fn call(&mut self, request: Request<ReqBody>) -> Self::Future {
let context = A::default().push(XSpanIdString::get_or_generate(&request));
let headers = request.headers();
let context = context.push(None::<AuthData>); let context = context.push(None::<AuthData>);
let context = context.push(None::<Authorization>); let context = context.push(None::<Authorization>);
let body = ContextualPayload {
inner: body,
context: context,
};
Box::new(self.inner.call(hyper::Request::from_parts(head, body))) self.inner.call((request, context))
} }
} }

View File

@ -1,12 +1,12 @@
#![allow(missing_docs, trivial_casts, unused_variables, unused_mut, unused_imports, unused_extern_crates, non_camel_case_types)] #![allow(missing_docs, trivial_casts, unused_variables, unused_mut, unused_imports, unused_extern_crates, non_camel_case_types)]
use async_trait::async_trait;
use futures::Stream; use futures::Stream;
use std::io::Error; use std::error::Error;
use std::task::{Poll, Context};
use swagger::{ApiError, ContextWrapper};
#[deprecated(note = "Import swagger-rs directly")] type ServiceError = Box<dyn Error + Send + Sync + 'static>;
pub use swagger::{ApiError, ContextWrapper};
#[deprecated(note = "Import futures directly")]
pub use futures::Future;
pub const BASE_PATH: &'static str = ""; pub const BASE_PATH: &'static str = "";
pub const API_VERSION: &'static str = "0.0.1"; pub const API_VERSION: &'static str = "0.0.1";
@ -234,583 +234,642 @@ pub enum Op9GetResponse {
} }
/// API /// API
pub trait Api<C> { #[async_trait]
fn op10_get( pub trait Api<C: Send + Sync> {
&self, fn poll_ready(&self, _cx: &mut Context) -> Poll<Result<(), Box<dyn Error + Send + Sync + 'static>>> {
context: &C) -> Box<dyn Future<Item=Op10GetResponse, Error=ApiError> + Send>; Poll::Ready(Ok(()))
}
fn op11_get( async fn op10_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op11GetResponse, Error=ApiError> + Send>; context: &C) -> Result<Op10GetResponse, ApiError>;
fn op12_get( async fn op11_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op12GetResponse, Error=ApiError> + Send>; context: &C) -> Result<Op11GetResponse, ApiError>;
fn op13_get( async fn op12_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op13GetResponse, Error=ApiError> + Send>; context: &C) -> Result<Op12GetResponse, ApiError>;
fn op14_get( async fn op13_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op14GetResponse, Error=ApiError> + Send>; context: &C) -> Result<Op13GetResponse, ApiError>;
fn op15_get( async fn op14_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op15GetResponse, Error=ApiError> + Send>; context: &C) -> Result<Op14GetResponse, ApiError>;
fn op16_get( async fn op15_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op16GetResponse, Error=ApiError> + Send>; context: &C) -> Result<Op15GetResponse, ApiError>;
fn op17_get( async fn op16_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op17GetResponse, Error=ApiError> + Send>; context: &C) -> Result<Op16GetResponse, ApiError>;
fn op18_get( async fn op17_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op18GetResponse, Error=ApiError> + Send>; context: &C) -> Result<Op17GetResponse, ApiError>;
fn op19_get( async fn op18_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op19GetResponse, Error=ApiError> + Send>; context: &C) -> Result<Op18GetResponse, ApiError>;
fn op1_get( async fn op19_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op1GetResponse, Error=ApiError> + Send>; context: &C) -> Result<Op19GetResponse, ApiError>;
fn op20_get( async fn op1_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op20GetResponse, Error=ApiError> + Send>; context: &C) -> Result<Op1GetResponse, ApiError>;
fn op21_get( async fn op20_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op21GetResponse, Error=ApiError> + Send>; context: &C) -> Result<Op20GetResponse, ApiError>;
fn op22_get( async fn op21_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op22GetResponse, Error=ApiError> + Send>; context: &C) -> Result<Op21GetResponse, ApiError>;
fn op23_get( async fn op22_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op23GetResponse, Error=ApiError> + Send>; context: &C) -> Result<Op22GetResponse, ApiError>;
fn op24_get( async fn op23_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op24GetResponse, Error=ApiError> + Send>; context: &C) -> Result<Op23GetResponse, ApiError>;
fn op25_get( async fn op24_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op25GetResponse, Error=ApiError> + Send>; context: &C) -> Result<Op24GetResponse, ApiError>;
fn op26_get( async fn op25_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op26GetResponse, Error=ApiError> + Send>; context: &C) -> Result<Op25GetResponse, ApiError>;
fn op27_get( async fn op26_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op27GetResponse, Error=ApiError> + Send>; context: &C) -> Result<Op26GetResponse, ApiError>;
fn op28_get( async fn op27_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op28GetResponse, Error=ApiError> + Send>; context: &C) -> Result<Op27GetResponse, ApiError>;
fn op29_get( async fn op28_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op29GetResponse, Error=ApiError> + Send>; context: &C) -> Result<Op28GetResponse, ApiError>;
fn op2_get( async fn op29_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op2GetResponse, Error=ApiError> + Send>; context: &C) -> Result<Op29GetResponse, ApiError>;
fn op30_get( async fn op2_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op30GetResponse, Error=ApiError> + Send>; context: &C) -> Result<Op2GetResponse, ApiError>;
fn op31_get( async fn op30_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op31GetResponse, Error=ApiError> + Send>; context: &C) -> Result<Op30GetResponse, ApiError>;
fn op32_get( async fn op31_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op32GetResponse, Error=ApiError> + Send>; context: &C) -> Result<Op31GetResponse, ApiError>;
fn op33_get( async fn op32_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op33GetResponse, Error=ApiError> + Send>; context: &C) -> Result<Op32GetResponse, ApiError>;
fn op34_get( async fn op33_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op34GetResponse, Error=ApiError> + Send>; context: &C) -> Result<Op33GetResponse, ApiError>;
fn op35_get( async fn op34_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op35GetResponse, Error=ApiError> + Send>; context: &C) -> Result<Op34GetResponse, ApiError>;
fn op36_get( async fn op35_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op36GetResponse, Error=ApiError> + Send>; context: &C) -> Result<Op35GetResponse, ApiError>;
fn op37_get( async fn op36_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op37GetResponse, Error=ApiError> + Send>; context: &C) -> Result<Op36GetResponse, ApiError>;
fn op3_get( async fn op37_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op3GetResponse, Error=ApiError> + Send>; context: &C) -> Result<Op37GetResponse, ApiError>;
fn op4_get( async fn op3_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op4GetResponse, Error=ApiError> + Send>; context: &C) -> Result<Op3GetResponse, ApiError>;
fn op5_get( async fn op4_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op5GetResponse, Error=ApiError> + Send>; context: &C) -> Result<Op4GetResponse, ApiError>;
fn op6_get( async fn op5_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op6GetResponse, Error=ApiError> + Send>; context: &C) -> Result<Op5GetResponse, ApiError>;
fn op7_get( async fn op6_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op7GetResponse, Error=ApiError> + Send>; context: &C) -> Result<Op6GetResponse, ApiError>;
fn op8_get( async fn op7_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op8GetResponse, Error=ApiError> + Send>; context: &C) -> Result<Op7GetResponse, ApiError>;
fn op9_get( async fn op8_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=Op9GetResponse, Error=ApiError> + Send>; context: &C) -> Result<Op8GetResponse, ApiError>;
async fn op9_get(
&self,
context: &C) -> Result<Op9GetResponse, ApiError>;
} }
/// API without a `Context` /// API where `Context` isn't passed on every API call
pub trait ApiNoContext { #[async_trait]
fn op10_get( pub trait ApiNoContext<C: Send + Sync> {
&self,
) -> Box<dyn Future<Item=Op10GetResponse, Error=ApiError> + Send>;
fn op11_get( fn poll_ready(&self, _cx: &mut Context) -> Poll<Result<(), Box<dyn Error + Send + Sync + 'static>>>;
&self,
) -> Box<dyn Future<Item=Op11GetResponse, Error=ApiError> + Send>;
fn op12_get( fn context(&self) -> &C;
&self,
) -> Box<dyn Future<Item=Op12GetResponse, Error=ApiError> + Send>;
fn op13_get( async fn op10_get(
&self, &self,
) -> Box<dyn Future<Item=Op13GetResponse, Error=ApiError> + Send>; ) -> Result<Op10GetResponse, ApiError>;
fn op14_get( async fn op11_get(
&self, &self,
) -> Box<dyn Future<Item=Op14GetResponse, Error=ApiError> + Send>; ) -> Result<Op11GetResponse, ApiError>;
fn op15_get( async fn op12_get(
&self, &self,
) -> Box<dyn Future<Item=Op15GetResponse, Error=ApiError> + Send>; ) -> Result<Op12GetResponse, ApiError>;
fn op16_get( async fn op13_get(
&self, &self,
) -> Box<dyn Future<Item=Op16GetResponse, Error=ApiError> + Send>; ) -> Result<Op13GetResponse, ApiError>;
fn op17_get( async fn op14_get(
&self, &self,
) -> Box<dyn Future<Item=Op17GetResponse, Error=ApiError> + Send>; ) -> Result<Op14GetResponse, ApiError>;
fn op18_get( async fn op15_get(
&self, &self,
) -> Box<dyn Future<Item=Op18GetResponse, Error=ApiError> + Send>; ) -> Result<Op15GetResponse, ApiError>;
fn op19_get( async fn op16_get(
&self, &self,
) -> Box<dyn Future<Item=Op19GetResponse, Error=ApiError> + Send>; ) -> Result<Op16GetResponse, ApiError>;
fn op1_get( async fn op17_get(
&self, &self,
) -> Box<dyn Future<Item=Op1GetResponse, Error=ApiError> + Send>; ) -> Result<Op17GetResponse, ApiError>;
fn op20_get( async fn op18_get(
&self, &self,
) -> Box<dyn Future<Item=Op20GetResponse, Error=ApiError> + Send>; ) -> Result<Op18GetResponse, ApiError>;
fn op21_get( async fn op19_get(
&self, &self,
) -> Box<dyn Future<Item=Op21GetResponse, Error=ApiError> + Send>; ) -> Result<Op19GetResponse, ApiError>;
fn op22_get( async fn op1_get(
&self, &self,
) -> Box<dyn Future<Item=Op22GetResponse, Error=ApiError> + Send>; ) -> Result<Op1GetResponse, ApiError>;
fn op23_get( async fn op20_get(
&self, &self,
) -> Box<dyn Future<Item=Op23GetResponse, Error=ApiError> + Send>; ) -> Result<Op20GetResponse, ApiError>;
fn op24_get( async fn op21_get(
&self, &self,
) -> Box<dyn Future<Item=Op24GetResponse, Error=ApiError> + Send>; ) -> Result<Op21GetResponse, ApiError>;
fn op25_get( async fn op22_get(
&self, &self,
) -> Box<dyn Future<Item=Op25GetResponse, Error=ApiError> + Send>; ) -> Result<Op22GetResponse, ApiError>;
fn op26_get( async fn op23_get(
&self, &self,
) -> Box<dyn Future<Item=Op26GetResponse, Error=ApiError> + Send>; ) -> Result<Op23GetResponse, ApiError>;
fn op27_get( async fn op24_get(
&self, &self,
) -> Box<dyn Future<Item=Op27GetResponse, Error=ApiError> + Send>; ) -> Result<Op24GetResponse, ApiError>;
fn op28_get( async fn op25_get(
&self, &self,
) -> Box<dyn Future<Item=Op28GetResponse, Error=ApiError> + Send>; ) -> Result<Op25GetResponse, ApiError>;
fn op29_get( async fn op26_get(
&self, &self,
) -> Box<dyn Future<Item=Op29GetResponse, Error=ApiError> + Send>; ) -> Result<Op26GetResponse, ApiError>;
fn op2_get( async fn op27_get(
&self, &self,
) -> Box<dyn Future<Item=Op2GetResponse, Error=ApiError> + Send>; ) -> Result<Op27GetResponse, ApiError>;
fn op30_get( async fn op28_get(
&self, &self,
) -> Box<dyn Future<Item=Op30GetResponse, Error=ApiError> + Send>; ) -> Result<Op28GetResponse, ApiError>;
fn op31_get( async fn op29_get(
&self, &self,
) -> Box<dyn Future<Item=Op31GetResponse, Error=ApiError> + Send>; ) -> Result<Op29GetResponse, ApiError>;
fn op32_get( async fn op2_get(
&self, &self,
) -> Box<dyn Future<Item=Op32GetResponse, Error=ApiError> + Send>; ) -> Result<Op2GetResponse, ApiError>;
fn op33_get( async fn op30_get(
&self, &self,
) -> Box<dyn Future<Item=Op33GetResponse, Error=ApiError> + Send>; ) -> Result<Op30GetResponse, ApiError>;
fn op34_get( async fn op31_get(
&self, &self,
) -> Box<dyn Future<Item=Op34GetResponse, Error=ApiError> + Send>; ) -> Result<Op31GetResponse, ApiError>;
fn op35_get( async fn op32_get(
&self, &self,
) -> Box<dyn Future<Item=Op35GetResponse, Error=ApiError> + Send>; ) -> Result<Op32GetResponse, ApiError>;
fn op36_get( async fn op33_get(
&self, &self,
) -> Box<dyn Future<Item=Op36GetResponse, Error=ApiError> + Send>; ) -> Result<Op33GetResponse, ApiError>;
fn op37_get( async fn op34_get(
&self, &self,
) -> Box<dyn Future<Item=Op37GetResponse, Error=ApiError> + Send>; ) -> Result<Op34GetResponse, ApiError>;
fn op3_get( async fn op35_get(
&self, &self,
) -> Box<dyn Future<Item=Op3GetResponse, Error=ApiError> + Send>; ) -> Result<Op35GetResponse, ApiError>;
fn op4_get( async fn op36_get(
&self, &self,
) -> Box<dyn Future<Item=Op4GetResponse, Error=ApiError> + Send>; ) -> Result<Op36GetResponse, ApiError>;
fn op5_get( async fn op37_get(
&self, &self,
) -> Box<dyn Future<Item=Op5GetResponse, Error=ApiError> + Send>; ) -> Result<Op37GetResponse, ApiError>;
fn op6_get( async fn op3_get(
&self, &self,
) -> Box<dyn Future<Item=Op6GetResponse, Error=ApiError> + Send>; ) -> Result<Op3GetResponse, ApiError>;
fn op7_get( async fn op4_get(
&self, &self,
) -> Box<dyn Future<Item=Op7GetResponse, Error=ApiError> + Send>; ) -> Result<Op4GetResponse, ApiError>;
fn op8_get( async fn op5_get(
&self, &self,
) -> Box<dyn Future<Item=Op8GetResponse, Error=ApiError> + Send>; ) -> Result<Op5GetResponse, ApiError>;
fn op9_get( async fn op6_get(
&self, &self,
) -> Box<dyn Future<Item=Op9GetResponse, Error=ApiError> + Send>; ) -> Result<Op6GetResponse, ApiError>;
async fn op7_get(
&self,
) -> Result<Op7GetResponse, ApiError>;
async fn op8_get(
&self,
) -> Result<Op8GetResponse, ApiError>;
async fn op9_get(
&self,
) -> Result<Op9GetResponse, ApiError>;
} }
/// Trait to extend an API to make it easy to bind it to a context. /// Trait to extend an API to make it easy to bind it to a context.
pub trait ContextWrapperExt<'a, C> where Self: Sized { pub trait ContextWrapperExt<C: Send + Sync> where Self: Sized
{
/// Binds this API to a context. /// Binds this API to a context.
fn with_context(self: &'a Self, context: C) -> ContextWrapper<'a, Self, C>; fn with_context(self: Self, context: C) -> ContextWrapper<Self, C>;
} }
impl<'a, T: Api<C> + Sized, C> ContextWrapperExt<'a, C> for T { impl<T: Api<C> + Send + Sync, C: Clone + Send + Sync> ContextWrapperExt<C> for T {
fn with_context(self: &'a T, context: C) -> ContextWrapper<'a, T, C> { fn with_context(self: T, context: C) -> ContextWrapper<T, C> {
ContextWrapper::<T, C>::new(self, context) ContextWrapper::<T, C>::new(self, context)
} }
} }
impl<'a, T: Api<C>, C> ApiNoContext for ContextWrapper<'a, T, C> { #[async_trait]
fn op10_get( impl<T: Api<C> + Send + Sync, C: Clone + Send + Sync> ApiNoContext<C> for ContextWrapper<T, C> {
&self, fn poll_ready(&self, cx: &mut Context) -> Poll<Result<(), ServiceError>> {
) -> Box<dyn Future<Item=Op10GetResponse, Error=ApiError> + Send> self.api().poll_ready(cx)
{
self.api().op10_get(&self.context())
} }
fn op11_get( fn context(&self) -> &C {
&self, ContextWrapper::context(self)
) -> Box<dyn Future<Item=Op11GetResponse, Error=ApiError> + Send>
{
self.api().op11_get(&self.context())
} }
fn op12_get( async fn op10_get(
&self, &self,
) -> Box<dyn Future<Item=Op12GetResponse, Error=ApiError> + Send> ) -> Result<Op10GetResponse, ApiError>
{ {
self.api().op12_get(&self.context()) let context = self.context().clone();
self.api().op10_get(&context).await
} }
fn op13_get( async fn op11_get(
&self, &self,
) -> Box<dyn Future<Item=Op13GetResponse, Error=ApiError> + Send> ) -> Result<Op11GetResponse, ApiError>
{ {
self.api().op13_get(&self.context()) let context = self.context().clone();
self.api().op11_get(&context).await
} }
fn op14_get( async fn op12_get(
&self, &self,
) -> Box<dyn Future<Item=Op14GetResponse, Error=ApiError> + Send> ) -> Result<Op12GetResponse, ApiError>
{ {
self.api().op14_get(&self.context()) let context = self.context().clone();
self.api().op12_get(&context).await
} }
fn op15_get( async fn op13_get(
&self, &self,
) -> Box<dyn Future<Item=Op15GetResponse, Error=ApiError> + Send> ) -> Result<Op13GetResponse, ApiError>
{ {
self.api().op15_get(&self.context()) let context = self.context().clone();
self.api().op13_get(&context).await
} }
fn op16_get( async fn op14_get(
&self, &self,
) -> Box<dyn Future<Item=Op16GetResponse, Error=ApiError> + Send> ) -> Result<Op14GetResponse, ApiError>
{ {
self.api().op16_get(&self.context()) let context = self.context().clone();
self.api().op14_get(&context).await
} }
fn op17_get( async fn op15_get(
&self, &self,
) -> Box<dyn Future<Item=Op17GetResponse, Error=ApiError> + Send> ) -> Result<Op15GetResponse, ApiError>
{ {
self.api().op17_get(&self.context()) let context = self.context().clone();
self.api().op15_get(&context).await
} }
fn op18_get( async fn op16_get(
&self, &self,
) -> Box<dyn Future<Item=Op18GetResponse, Error=ApiError> + Send> ) -> Result<Op16GetResponse, ApiError>
{ {
self.api().op18_get(&self.context()) let context = self.context().clone();
self.api().op16_get(&context).await
} }
fn op19_get( async fn op17_get(
&self, &self,
) -> Box<dyn Future<Item=Op19GetResponse, Error=ApiError> + Send> ) -> Result<Op17GetResponse, ApiError>
{ {
self.api().op19_get(&self.context()) let context = self.context().clone();
self.api().op17_get(&context).await
} }
fn op1_get( async fn op18_get(
&self, &self,
) -> Box<dyn Future<Item=Op1GetResponse, Error=ApiError> + Send> ) -> Result<Op18GetResponse, ApiError>
{ {
self.api().op1_get(&self.context()) let context = self.context().clone();
self.api().op18_get(&context).await
} }
fn op20_get( async fn op19_get(
&self, &self,
) -> Box<dyn Future<Item=Op20GetResponse, Error=ApiError> + Send> ) -> Result<Op19GetResponse, ApiError>
{ {
self.api().op20_get(&self.context()) let context = self.context().clone();
self.api().op19_get(&context).await
} }
fn op21_get( async fn op1_get(
&self, &self,
) -> Box<dyn Future<Item=Op21GetResponse, Error=ApiError> + Send> ) -> Result<Op1GetResponse, ApiError>
{ {
self.api().op21_get(&self.context()) let context = self.context().clone();
self.api().op1_get(&context).await
} }
fn op22_get( async fn op20_get(
&self, &self,
) -> Box<dyn Future<Item=Op22GetResponse, Error=ApiError> + Send> ) -> Result<Op20GetResponse, ApiError>
{ {
self.api().op22_get(&self.context()) let context = self.context().clone();
self.api().op20_get(&context).await
} }
fn op23_get( async fn op21_get(
&self, &self,
) -> Box<dyn Future<Item=Op23GetResponse, Error=ApiError> + Send> ) -> Result<Op21GetResponse, ApiError>
{ {
self.api().op23_get(&self.context()) let context = self.context().clone();
self.api().op21_get(&context).await
} }
fn op24_get( async fn op22_get(
&self, &self,
) -> Box<dyn Future<Item=Op24GetResponse, Error=ApiError> + Send> ) -> Result<Op22GetResponse, ApiError>
{ {
self.api().op24_get(&self.context()) let context = self.context().clone();
self.api().op22_get(&context).await
} }
fn op25_get( async fn op23_get(
&self, &self,
) -> Box<dyn Future<Item=Op25GetResponse, Error=ApiError> + Send> ) -> Result<Op23GetResponse, ApiError>
{ {
self.api().op25_get(&self.context()) let context = self.context().clone();
self.api().op23_get(&context).await
} }
fn op26_get( async fn op24_get(
&self, &self,
) -> Box<dyn Future<Item=Op26GetResponse, Error=ApiError> + Send> ) -> Result<Op24GetResponse, ApiError>
{ {
self.api().op26_get(&self.context()) let context = self.context().clone();
self.api().op24_get(&context).await
} }
fn op27_get( async fn op25_get(
&self, &self,
) -> Box<dyn Future<Item=Op27GetResponse, Error=ApiError> + Send> ) -> Result<Op25GetResponse, ApiError>
{ {
self.api().op27_get(&self.context()) let context = self.context().clone();
self.api().op25_get(&context).await
} }
fn op28_get( async fn op26_get(
&self, &self,
) -> Box<dyn Future<Item=Op28GetResponse, Error=ApiError> + Send> ) -> Result<Op26GetResponse, ApiError>
{ {
self.api().op28_get(&self.context()) let context = self.context().clone();
self.api().op26_get(&context).await
} }
fn op29_get( async fn op27_get(
&self, &self,
) -> Box<dyn Future<Item=Op29GetResponse, Error=ApiError> + Send> ) -> Result<Op27GetResponse, ApiError>
{ {
self.api().op29_get(&self.context()) let context = self.context().clone();
self.api().op27_get(&context).await
} }
fn op2_get( async fn op28_get(
&self, &self,
) -> Box<dyn Future<Item=Op2GetResponse, Error=ApiError> + Send> ) -> Result<Op28GetResponse, ApiError>
{ {
self.api().op2_get(&self.context()) let context = self.context().clone();
self.api().op28_get(&context).await
} }
fn op30_get( async fn op29_get(
&self, &self,
) -> Box<dyn Future<Item=Op30GetResponse, Error=ApiError> + Send> ) -> Result<Op29GetResponse, ApiError>
{ {
self.api().op30_get(&self.context()) let context = self.context().clone();
self.api().op29_get(&context).await
} }
fn op31_get( async fn op2_get(
&self, &self,
) -> Box<dyn Future<Item=Op31GetResponse, Error=ApiError> + Send> ) -> Result<Op2GetResponse, ApiError>
{ {
self.api().op31_get(&self.context()) let context = self.context().clone();
self.api().op2_get(&context).await
} }
fn op32_get( async fn op30_get(
&self, &self,
) -> Box<dyn Future<Item=Op32GetResponse, Error=ApiError> + Send> ) -> Result<Op30GetResponse, ApiError>
{ {
self.api().op32_get(&self.context()) let context = self.context().clone();
self.api().op30_get(&context).await
} }
fn op33_get( async fn op31_get(
&self, &self,
) -> Box<dyn Future<Item=Op33GetResponse, Error=ApiError> + Send> ) -> Result<Op31GetResponse, ApiError>
{ {
self.api().op33_get(&self.context()) let context = self.context().clone();
self.api().op31_get(&context).await
} }
fn op34_get( async fn op32_get(
&self, &self,
) -> Box<dyn Future<Item=Op34GetResponse, Error=ApiError> + Send> ) -> Result<Op32GetResponse, ApiError>
{ {
self.api().op34_get(&self.context()) let context = self.context().clone();
self.api().op32_get(&context).await
} }
fn op35_get( async fn op33_get(
&self, &self,
) -> Box<dyn Future<Item=Op35GetResponse, Error=ApiError> + Send> ) -> Result<Op33GetResponse, ApiError>
{ {
self.api().op35_get(&self.context()) let context = self.context().clone();
self.api().op33_get(&context).await
} }
fn op36_get( async fn op34_get(
&self, &self,
) -> Box<dyn Future<Item=Op36GetResponse, Error=ApiError> + Send> ) -> Result<Op34GetResponse, ApiError>
{ {
self.api().op36_get(&self.context()) let context = self.context().clone();
self.api().op34_get(&context).await
} }
fn op37_get( async fn op35_get(
&self, &self,
) -> Box<dyn Future<Item=Op37GetResponse, Error=ApiError> + Send> ) -> Result<Op35GetResponse, ApiError>
{ {
self.api().op37_get(&self.context()) let context = self.context().clone();
self.api().op35_get(&context).await
} }
fn op3_get( async fn op36_get(
&self, &self,
) -> Box<dyn Future<Item=Op3GetResponse, Error=ApiError> + Send> ) -> Result<Op36GetResponse, ApiError>
{ {
self.api().op3_get(&self.context()) let context = self.context().clone();
self.api().op36_get(&context).await
} }
fn op4_get( async fn op37_get(
&self, &self,
) -> Box<dyn Future<Item=Op4GetResponse, Error=ApiError> + Send> ) -> Result<Op37GetResponse, ApiError>
{ {
self.api().op4_get(&self.context()) let context = self.context().clone();
self.api().op37_get(&context).await
} }
fn op5_get( async fn op3_get(
&self, &self,
) -> Box<dyn Future<Item=Op5GetResponse, Error=ApiError> + Send> ) -> Result<Op3GetResponse, ApiError>
{ {
self.api().op5_get(&self.context()) let context = self.context().clone();
self.api().op3_get(&context).await
} }
fn op6_get( async fn op4_get(
&self, &self,
) -> Box<dyn Future<Item=Op6GetResponse, Error=ApiError> + Send> ) -> Result<Op4GetResponse, ApiError>
{ {
self.api().op6_get(&self.context()) let context = self.context().clone();
self.api().op4_get(&context).await
} }
fn op7_get( async fn op5_get(
&self, &self,
) -> Box<dyn Future<Item=Op7GetResponse, Error=ApiError> + Send> ) -> Result<Op5GetResponse, ApiError>
{ {
self.api().op7_get(&self.context()) let context = self.context().clone();
self.api().op5_get(&context).await
} }
fn op8_get( async fn op6_get(
&self, &self,
) -> Box<dyn Future<Item=Op8GetResponse, Error=ApiError> + Send> ) -> Result<Op6GetResponse, ApiError>
{ {
self.api().op8_get(&self.context()) let context = self.context().clone();
self.api().op6_get(&context).await
} }
fn op9_get( async fn op7_get(
&self, &self,
) -> Box<dyn Future<Item=Op9GetResponse, Error=ApiError> + Send> ) -> Result<Op7GetResponse, ApiError>
{ {
self.api().op9_get(&self.context()) let context = self.context().clone();
self.api().op7_get(&context).await
}
async fn op8_get(
&self,
) -> Result<Op8GetResponse, ApiError>
{
let context = self.context().clone();
self.api().op8_get(&context).await
}
async fn op9_get(
&self,
) -> Result<Op9GetResponse, ApiError>
{
let context = self.context().clone();
self.api().op9_get(&context).await
} }
} }
#[cfg(feature = "client")] #[cfg(feature = "client")]
pub mod client; pub mod client;

View File

@ -12,7 +12,7 @@ client = [
"mime_0_2", "mime_0_2",
"multipart", "multipart/client", "swagger/multipart", "multipart", "multipart/client", "swagger/multipart",
"serde_urlencoded", "serde_urlencoded",
"hyper", "hyper-openssl", "native-tls", "openssl", "url" "hyper", "hyper-openssl", "hyper-tls", "native-tls", "openssl", "url"
] ]
server = [ server = [
"mime_0_2", "mime_0_2",
@ -23,20 +23,22 @@ conversion = ["frunk", "frunk_derives", "frunk_core", "frunk-enum-core", "frunk-
[target.'cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))'.dependencies] [target.'cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))'.dependencies]
native-tls = { version = "0.2", optional = true } native-tls = { version = "0.2", optional = true }
hyper-tls = { version = "0.4", optional = true }
[target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dependencies] [target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dependencies]
hyper-openssl = { version = "0.7.1", optional = true } hyper-openssl = { version = "0.8", optional = true }
openssl = {version = "0.10", optional = true } openssl = {version = "0.10", optional = true }
[dependencies] [dependencies]
# Common # Common
async-trait = "0.1.24"
chrono = { version = "0.4", features = ["serde"] } chrono = { version = "0.4", features = ["serde"] }
futures = "0.1" futures = "0.3"
swagger = "4.0" swagger = "5.0.0-alpha-1"
log = "0.4.0" log = "0.4.0"
mime = "0.3" mime = "0.3"
serde = { version = "1.0", features = ["derive"]} serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"
# Crates included if required by the API definition # Crates included if required by the API definition
@ -45,20 +47,20 @@ serde_json = "1.0"
serde-xml-rs = {git = "git://github.com/Metaswitch/serde-xml-rs.git" , branch = "master"} serde-xml-rs = {git = "git://github.com/Metaswitch/serde-xml-rs.git" , branch = "master"}
mime_0_2 = { package = "mime", version = "0.2.6", optional = true } mime_0_2 = { package = "mime", version = "0.2.6", optional = true }
multipart = { version = "0.16", default-features = false, optional = true } multipart = { version = "0.16", default-features = false, optional = true }
uuid = {version = "0.7", features = ["serde", "v4"]} uuid = {version = "0.8", features = ["serde", "v4"]}
# Common between server and client features # Common between server and client features
hyper = {version = "0.12", optional = true} hyper = {version = "0.13", optional = true}
serde_ignored = {version = "0.0.4", optional = true} serde_ignored = {version = "0.1.1", optional = true}
url = {version = "1.5", optional = true} url = {version = "2.1", optional = true}
# Client-specific # Client-specific
serde_urlencoded = {version = "0.5.1", optional = true} serde_urlencoded = {version = "0.6.1", optional = true}
# Server, and client callback-specific # Server, and client callback-specific
lazy_static = { version = "1.4", optional = true } lazy_static = { version = "1.4", optional = true }
percent-encoding = {version = "1.0.0", optional = true} percent-encoding = {version = "2.1.0", optional = true}
regex = {version = "0.2", optional = true} regex = {version = "1.3", optional = true}
# Conversion # Conversion
frunk = { version = "0.3.0", optional = true } frunk = { version = "0.3.0", optional = true }
@ -69,12 +71,13 @@ frunk-enum-core = { version = "0.2.0", optional = true }
[dev-dependencies] [dev-dependencies]
clap = "2.25" clap = "2.25"
error-chain = "0.12" env_logger = "0.7"
env_logger = "0.6" tokio = { version = "0.2", features = ["rt-threaded", "macros", "stream"] }
tokio = "0.1.17" native-tls = "0.2"
tokio-tls = "0.3"
[target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dev-dependencies] [target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dev-dependencies]
tokio-openssl = "0.3" tokio-openssl = "0.4"
openssl = "0.10" openssl = "0.10"
[[example]] [[example]]

View File

@ -2,10 +2,9 @@
#[allow(unused_imports)] #[allow(unused_imports)]
use futures::{Future, future, Stream, stream}; use futures::{future, Stream, stream};
#[allow(unused_imports)] #[allow(unused_imports)]
use petstore_with_fake_endpoints_models_for_testing::{Api, ApiNoContext, Client, ContextWrapperExt, models, use petstore_with_fake_endpoints_models_for_testing::{Api, ApiNoContext, Client, ContextWrapperExt, models,
ApiError,
TestSpecialTagsResponse, TestSpecialTagsResponse,
Call123exampleResponse, Call123exampleResponse,
FakeOuterBooleanSerializeResponse, FakeOuterBooleanSerializeResponse,
@ -40,7 +39,7 @@ use petstore_with_fake_endpoints_models_for_testing::{Api, ApiNoContext, Client,
GetUserByNameResponse, GetUserByNameResponse,
LoginUserResponse, LoginUserResponse,
LogoutUserResponse, LogoutUserResponse,
UpdateUserResponse UpdateUserResponse,
}; };
use clap::{App, Arg}; use clap::{App, Arg};
@ -49,7 +48,9 @@ use log::info;
// swagger::Has may be unused if there are no examples // swagger::Has may be unused if there are no examples
#[allow(unused_imports)] #[allow(unused_imports)]
use swagger::{ContextBuilder, EmptyContext, XSpanIdString, Has, Push, AuthData}; use swagger::{AuthData, ContextBuilder, EmptyContext, Has, Push, XSpanIdString};
type ClientContext = swagger::make_context_ty!(ContextBuilder, EmptyContext, Option<AuthData>, XSpanIdString);
// rt may be unused if there are no examples // rt may be unused if there are no examples
#[allow(unused_mut)] #[allow(unused_mut)]
@ -109,21 +110,21 @@ fn main() {
matches.value_of("host").unwrap(), matches.value_of("host").unwrap(),
matches.value_of("port").unwrap()); matches.value_of("port").unwrap());
let client = if matches.is_present("https") { let context: ClientContext =
// Using Simple HTTPS
Client::try_new_https(&base_url)
.expect("Failed to create HTTPS client")
} else {
// Using HTTP
Client::try_new_http(
&base_url)
.expect("Failed to create HTTP client")
};
let context: swagger::make_context_ty!(ContextBuilder, EmptyContext, Option<AuthData>, XSpanIdString) =
swagger::make_context!(ContextBuilder, EmptyContext, None as Option<AuthData>, XSpanIdString::default()); swagger::make_context!(ContextBuilder, EmptyContext, None as Option<AuthData>, XSpanIdString::default());
let client = client.with_context(context); let mut client : Box<dyn ApiNoContext<ClientContext>> = if matches.is_present("https") {
// Using Simple HTTPS
let client = Box::new(Client::try_new_https(&base_url)
.expect("Failed to create HTTPS client"));
Box::new(client.with_context(context))
} else {
// Using HTTP
let client = Box::new(Client::try_new_http(
&base_url)
.expect("Failed to create HTTP client"));
Box::new(client.with_context(context))
};
let mut rt = tokio::runtime::Runtime::new().unwrap(); let mut rt = tokio::runtime::Runtime::new().unwrap();

View File

@ -9,7 +9,8 @@ mod server;
/// Create custom server, wire it to the autogenerated router, /// Create custom server, wire it to the autogenerated router,
/// and pass it to the web server. /// and pass it to the web server.
fn main() { #[tokio::main]
async fn main() {
env_logger::init(); env_logger::init();
let matches = App::new("server") let matches = App::new("server")
@ -20,5 +21,5 @@ fn main() {
let addr = "127.0.0.1:80"; let addr = "127.0.0.1:80";
hyper::rt::run(server::create(addr, matches.is_present("https"))); server::create(addr, matches.is_present("https")).await;
} }

View File

@ -2,30 +2,22 @@
#![allow(unused_imports)] #![allow(unused_imports)]
mod errors { use async_trait::async_trait;
error_chain::error_chain!{} use futures::{future, Stream, StreamExt, TryFutureExt, TryStreamExt};
}
pub use self::errors::*;
use chrono;
use futures::{future, Future, Stream};
use hyper::server::conn::Http; use hyper::server::conn::Http;
use hyper::service::MakeService as _; use hyper::service::Service;
use log::info; use log::info;
use openssl::ssl::SslAcceptorBuilder; use openssl::ssl::SslAcceptorBuilder;
use std::future::Future;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::net::SocketAddr; use std::net::SocketAddr;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use swagger; use std::task::{Context, Poll};
use swagger::{Has, XSpanIdString}; use swagger::{Has, XSpanIdString};
use swagger::auth::MakeAllowAllAuthenticator; use swagger::auth::MakeAllowAllAuthenticator;
use swagger::EmptyContext; use swagger::EmptyContext;
use tokio::net::TcpListener; use tokio::net::TcpListener;
use uuid;
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
use tokio_openssl::SslAcceptorExt;
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod}; use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod};
@ -33,18 +25,18 @@ use petstore_with_fake_endpoints_models_for_testing::models;
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
/// Builds an SSL implementation for Simple HTTPS from some hard-coded file names /// Builds an SSL implementation for Simple HTTPS from some hard-coded file names
pub fn create(addr: &str, https: bool) -> Box<dyn Future<Item = (), Error = ()> + Send> { pub async fn create(addr: &str, https: bool) {
let addr = addr.parse().expect("Failed to parse bind address"); let addr = addr.parse().expect("Failed to parse bind address");
let server = Server::new(); let server = Server::new();
let service_fn = MakeService::new(server); let service = MakeService::new(server);
let service_fn = MakeAllowAllAuthenticator::new(service_fn, "cosmo"); let service = MakeAllowAllAuthenticator::new(service, "cosmo");
let service_fn = let mut service =
petstore_with_fake_endpoints_models_for_testing::server::context::MakeAddContext::<_, EmptyContext>::new( petstore_with_fake_endpoints_models_for_testing::server::context::MakeAddContext::<_, EmptyContext>::new(
service_fn service
); );
if https { if https {
@ -62,32 +54,31 @@ pub fn create(addr: &str, https: bool) -> Box<dyn Future<Item = (), Error = ()>
ssl.set_certificate_chain_file("examples/server-chain.pem").expect("Failed to set cerificate chain"); ssl.set_certificate_chain_file("examples/server-chain.pem").expect("Failed to set cerificate chain");
ssl.check_private_key().expect("Failed to check private key"); ssl.check_private_key().expect("Failed to check private key");
let tls_acceptor = ssl.build(); let tls_acceptor = Arc::new(ssl.build());
let service_fn = Arc::new(Mutex::new(service_fn)); let mut tcp_listener = TcpListener::bind(&addr).await.unwrap();
let tls_listener = TcpListener::bind(&addr).unwrap().incoming().for_each(move |tcp| { let mut incoming = tcp_listener.incoming();
let addr = tcp.peer_addr().expect("Unable to get remote address");
let service_fn = service_fn.clone(); while let (Some(tcp), rest) = incoming.into_future().await {
if let Ok(tcp) = tcp {
let addr = tcp.peer_addr().expect("Unable to get remote address");
let service = service.call(addr);
let tls_acceptor = Arc::clone(&tls_acceptor);
hyper::rt::spawn(tls_acceptor.accept_async(tcp).map_err(|_| ()).and_then(move |tls| { tokio::spawn(async move {
let ms = { let tls = tokio_openssl::accept(&*tls_acceptor, tcp).await.map_err(|_| ())?;
let mut service_fn = service_fn.lock().unwrap();
service_fn.make_service(&addr)
};
ms.and_then(move |service| { let service = service.await.map_err(|_| ())?;
Http::new().serve_connection(tls, service)
}).map_err(|_| ())
}));
Ok(()) Http::new().serve_connection(tls, service).await.map_err(|_| ())
}).map_err(|_| ()); });
}
Box::new(tls_listener) incoming = rest;
}
} }
} else { } else {
// Using HTTP // Using HTTP
Box::new(hyper::server::Server::bind(&addr).serve(service_fn).map_err(|e| panic!("{:?}", e))) hyper::server::Server::bind(&addr).serve(service).await.unwrap()
} }
} }
@ -105,7 +96,6 @@ impl<C> Server<C> {
use petstore_with_fake_endpoints_models_for_testing::{ use petstore_with_fake_endpoints_models_for_testing::{
Api, Api,
ApiError,
TestSpecialTagsResponse, TestSpecialTagsResponse,
Call123exampleResponse, Call123exampleResponse,
FakeOuterBooleanSerializeResponse, FakeOuterBooleanSerializeResponse,
@ -143,111 +133,115 @@ use petstore_with_fake_endpoints_models_for_testing::{
UpdateUserResponse, UpdateUserResponse,
}; };
use petstore_with_fake_endpoints_models_for_testing::server::MakeService; use petstore_with_fake_endpoints_models_for_testing::server::MakeService;
use std::error::Error;
use swagger::ApiError;
impl<C> Api<C> for Server<C> where C: Has<XSpanIdString>{ #[async_trait]
impl<C> Api<C> for Server<C> where C: Has<XSpanIdString> + Send + Sync
{
/// To test special tags /// To test special tags
fn test_special_tags( async fn test_special_tags(
&self, &self,
body: models::Client, body: models::Client,
context: &C) -> Box<dyn Future<Item=TestSpecialTagsResponse, Error=ApiError> + Send> context: &C) -> Result<TestSpecialTagsResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("test_special_tags({:?}) - X-Span-ID: {:?}", body, context.get().0.clone()); info!("test_special_tags({:?}) - X-Span-ID: {:?}", body, context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn call123example( async fn call123example(
&self, &self,
context: &C) -> Box<dyn Future<Item=Call123exampleResponse, Error=ApiError> + Send> context: &C) -> Result<Call123exampleResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("call123example() - X-Span-ID: {:?}", context.get().0.clone()); info!("call123example() - X-Span-ID: {:?}", context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn fake_outer_boolean_serialize( async fn fake_outer_boolean_serialize(
&self, &self,
body: Option<models::OuterBoolean>, body: Option<models::OuterBoolean>,
context: &C) -> Box<dyn Future<Item=FakeOuterBooleanSerializeResponse, Error=ApiError> + Send> context: &C) -> Result<FakeOuterBooleanSerializeResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("fake_outer_boolean_serialize({:?}) - X-Span-ID: {:?}", body, context.get().0.clone()); info!("fake_outer_boolean_serialize({:?}) - X-Span-ID: {:?}", body, context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn fake_outer_composite_serialize( async fn fake_outer_composite_serialize(
&self, &self,
body: Option<models::OuterComposite>, body: Option<models::OuterComposite>,
context: &C) -> Box<dyn Future<Item=FakeOuterCompositeSerializeResponse, Error=ApiError> + Send> context: &C) -> Result<FakeOuterCompositeSerializeResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("fake_outer_composite_serialize({:?}) - X-Span-ID: {:?}", body, context.get().0.clone()); info!("fake_outer_composite_serialize({:?}) - X-Span-ID: {:?}", body, context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn fake_outer_number_serialize( async fn fake_outer_number_serialize(
&self, &self,
body: Option<models::OuterNumber>, body: Option<models::OuterNumber>,
context: &C) -> Box<dyn Future<Item=FakeOuterNumberSerializeResponse, Error=ApiError> + Send> context: &C) -> Result<FakeOuterNumberSerializeResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("fake_outer_number_serialize({:?}) - X-Span-ID: {:?}", body, context.get().0.clone()); info!("fake_outer_number_serialize({:?}) - X-Span-ID: {:?}", body, context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn fake_outer_string_serialize( async fn fake_outer_string_serialize(
&self, &self,
body: Option<models::OuterString>, body: Option<models::OuterString>,
context: &C) -> Box<dyn Future<Item=FakeOuterStringSerializeResponse, Error=ApiError> + Send> context: &C) -> Result<FakeOuterStringSerializeResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("fake_outer_string_serialize({:?}) - X-Span-ID: {:?}", body, context.get().0.clone()); info!("fake_outer_string_serialize({:?}) - X-Span-ID: {:?}", body, context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn fake_response_with_numerical_description( async fn fake_response_with_numerical_description(
&self, &self,
context: &C) -> Box<dyn Future<Item=FakeResponseWithNumericalDescriptionResponse, Error=ApiError> + Send> context: &C) -> Result<FakeResponseWithNumericalDescriptionResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("fake_response_with_numerical_description() - X-Span-ID: {:?}", context.get().0.clone()); info!("fake_response_with_numerical_description() - X-Span-ID: {:?}", context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn hyphen_param( async fn hyphen_param(
&self, &self,
hyphen_param: String, hyphen_param: String,
context: &C) -> Box<dyn Future<Item=HyphenParamResponse, Error=ApiError> + Send> context: &C) -> Result<HyphenParamResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("hyphen_param(\"{}\") - X-Span-ID: {:?}", hyphen_param, context.get().0.clone()); info!("hyphen_param(\"{}\") - X-Span-ID: {:?}", hyphen_param, context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn test_body_with_query_params( async fn test_body_with_query_params(
&self, &self,
query: String, query: String,
body: models::User, body: models::User,
context: &C) -> Box<dyn Future<Item=TestBodyWithQueryParamsResponse, Error=ApiError> + Send> context: &C) -> Result<TestBodyWithQueryParamsResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("test_body_with_query_params(\"{}\", {:?}) - X-Span-ID: {:?}", query, body, context.get().0.clone()); info!("test_body_with_query_params(\"{}\", {:?}) - X-Span-ID: {:?}", query, body, context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
/// To test \"client\" model /// To test \"client\" model
fn test_client_model( async fn test_client_model(
&self, &self,
body: models::Client, body: models::Client,
context: &C) -> Box<dyn Future<Item=TestClientModelResponse, Error=ApiError> + Send> context: &C) -> Result<TestClientModelResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("test_client_model({:?}) - X-Span-ID: {:?}", body, context.get().0.clone()); info!("test_client_model({:?}) - X-Span-ID: {:?}", body, context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
/// Fake endpoint for testing various parameters 假端點 偽のエンドポイント 가짜 엔드 포인트 /// Fake endpoint for testing various parameters 假端點 偽のエンドポイント 가짜 엔드 포인트
fn test_endpoint_parameters( async fn test_endpoint_parameters(
&self, &self,
number: f64, number: f64,
double: f64, double: f64,
@ -263,15 +257,15 @@ impl<C> Api<C> for Server<C> where C: Has<XSpanIdString>{
date_time: Option<chrono::DateTime::<chrono::Utc>>, date_time: Option<chrono::DateTime::<chrono::Utc>>,
password: Option<String>, password: Option<String>,
callback: Option<String>, callback: Option<String>,
context: &C) -> Box<dyn Future<Item=TestEndpointParametersResponse, Error=ApiError> + Send> context: &C) -> Result<TestEndpointParametersResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("test_endpoint_parameters({}, {}, \"{}\", {:?}, {:?}, {:?}, {:?}, {:?}, {:?}, {:?}, {:?}, {:?}, {:?}, {:?}) - X-Span-ID: {:?}", number, double, pattern_without_delimiter, byte, integer, int32, int64, float, string, binary, date, date_time, password, callback, context.get().0.clone()); info!("test_endpoint_parameters({}, {}, \"{}\", {:?}, {:?}, {:?}, {:?}, {:?}, {:?}, {:?}, {:?}, {:?}, {:?}, {:?}) - X-Span-ID: {:?}", number, double, pattern_without_delimiter, byte, integer, int32, int64, float, string, binary, date, date_time, password, callback, context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
/// To test enum parameters /// To test enum parameters
fn test_enum_parameters( async fn test_enum_parameters(
&self, &self,
enum_header_string_array: Option<&Vec<String>>, enum_header_string_array: Option<&Vec<String>>,
enum_header_string: Option<String>, enum_header_string: Option<String>,
@ -280,270 +274,270 @@ impl<C> Api<C> for Server<C> where C: Has<XSpanIdString>{
enum_query_integer: Option<i32>, enum_query_integer: Option<i32>,
enum_query_double: Option<f64>, enum_query_double: Option<f64>,
enum_form_string: Option<String>, enum_form_string: Option<String>,
context: &C) -> Box<dyn Future<Item=TestEnumParametersResponse, Error=ApiError> + Send> context: &C) -> Result<TestEnumParametersResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("test_enum_parameters({:?}, {:?}, {:?}, {:?}, {:?}, {:?}, {:?}) - X-Span-ID: {:?}", enum_header_string_array, enum_header_string, enum_query_string_array, enum_query_string, enum_query_integer, enum_query_double, enum_form_string, context.get().0.clone()); info!("test_enum_parameters({:?}, {:?}, {:?}, {:?}, {:?}, {:?}, {:?}) - X-Span-ID: {:?}", enum_header_string_array, enum_header_string, enum_query_string_array, enum_query_string, enum_query_integer, enum_query_double, enum_form_string, context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
/// test inline additionalProperties /// test inline additionalProperties
fn test_inline_additional_properties( async fn test_inline_additional_properties(
&self, &self,
param: std::collections::HashMap<String, String>, param: std::collections::HashMap<String, String>,
context: &C) -> Box<dyn Future<Item=TestInlineAdditionalPropertiesResponse, Error=ApiError> + Send> context: &C) -> Result<TestInlineAdditionalPropertiesResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("test_inline_additional_properties({:?}) - X-Span-ID: {:?}", param, context.get().0.clone()); info!("test_inline_additional_properties({:?}) - X-Span-ID: {:?}", param, context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
/// test json serialization of form data /// test json serialization of form data
fn test_json_form_data( async fn test_json_form_data(
&self, &self,
param: String, param: String,
param2: String, param2: String,
context: &C) -> Box<dyn Future<Item=TestJsonFormDataResponse, Error=ApiError> + Send> context: &C) -> Result<TestJsonFormDataResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("test_json_form_data(\"{}\", \"{}\") - X-Span-ID: {:?}", param, param2, context.get().0.clone()); info!("test_json_form_data(\"{}\", \"{}\") - X-Span-ID: {:?}", param, param2, context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
/// To test class name in snake case /// To test class name in snake case
fn test_classname( async fn test_classname(
&self, &self,
body: models::Client, body: models::Client,
context: &C) -> Box<dyn Future<Item=TestClassnameResponse, Error=ApiError> + Send> context: &C) -> Result<TestClassnameResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("test_classname({:?}) - X-Span-ID: {:?}", body, context.get().0.clone()); info!("test_classname({:?}) - X-Span-ID: {:?}", body, context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
/// Add a new pet to the store /// Add a new pet to the store
fn add_pet( async fn add_pet(
&self, &self,
body: models::Pet, body: models::Pet,
context: &C) -> Box<dyn Future<Item=AddPetResponse, Error=ApiError> + Send> context: &C) -> Result<AddPetResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("add_pet({:?}) - X-Span-ID: {:?}", body, context.get().0.clone()); info!("add_pet({:?}) - X-Span-ID: {:?}", body, context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
/// Deletes a pet /// Deletes a pet
fn delete_pet( async fn delete_pet(
&self, &self,
pet_id: i64, pet_id: i64,
api_key: Option<String>, api_key: Option<String>,
context: &C) -> Box<dyn Future<Item=DeletePetResponse, Error=ApiError> + Send> context: &C) -> Result<DeletePetResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("delete_pet({}, {:?}) - X-Span-ID: {:?}", pet_id, api_key, context.get().0.clone()); info!("delete_pet({}, {:?}) - X-Span-ID: {:?}", pet_id, api_key, context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
/// Finds Pets by status /// Finds Pets by status
fn find_pets_by_status( async fn find_pets_by_status(
&self, &self,
status: &Vec<String>, status: &Vec<String>,
context: &C) -> Box<dyn Future<Item=FindPetsByStatusResponse, Error=ApiError> + Send> context: &C) -> Result<FindPetsByStatusResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("find_pets_by_status({:?}) - X-Span-ID: {:?}", status, context.get().0.clone()); info!("find_pets_by_status({:?}) - X-Span-ID: {:?}", status, context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
/// Finds Pets by tags /// Finds Pets by tags
fn find_pets_by_tags( async fn find_pets_by_tags(
&self, &self,
tags: &Vec<String>, tags: &Vec<String>,
context: &C) -> Box<dyn Future<Item=FindPetsByTagsResponse, Error=ApiError> + Send> context: &C) -> Result<FindPetsByTagsResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("find_pets_by_tags({:?}) - X-Span-ID: {:?}", tags, context.get().0.clone()); info!("find_pets_by_tags({:?}) - X-Span-ID: {:?}", tags, context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
/// Find pet by ID /// Find pet by ID
fn get_pet_by_id( async fn get_pet_by_id(
&self, &self,
pet_id: i64, pet_id: i64,
context: &C) -> Box<dyn Future<Item=GetPetByIdResponse, Error=ApiError> + Send> context: &C) -> Result<GetPetByIdResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("get_pet_by_id({}) - X-Span-ID: {:?}", pet_id, context.get().0.clone()); info!("get_pet_by_id({}) - X-Span-ID: {:?}", pet_id, context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
/// Update an existing pet /// Update an existing pet
fn update_pet( async fn update_pet(
&self, &self,
body: models::Pet, body: models::Pet,
context: &C) -> Box<dyn Future<Item=UpdatePetResponse, Error=ApiError> + Send> context: &C) -> Result<UpdatePetResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("update_pet({:?}) - X-Span-ID: {:?}", body, context.get().0.clone()); info!("update_pet({:?}) - X-Span-ID: {:?}", body, context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
/// Updates a pet in the store with form data /// Updates a pet in the store with form data
fn update_pet_with_form( async fn update_pet_with_form(
&self, &self,
pet_id: i64, pet_id: i64,
name: Option<String>, name: Option<String>,
status: Option<String>, status: Option<String>,
context: &C) -> Box<dyn Future<Item=UpdatePetWithFormResponse, Error=ApiError> + Send> context: &C) -> Result<UpdatePetWithFormResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("update_pet_with_form({}, {:?}, {:?}) - X-Span-ID: {:?}", pet_id, name, status, context.get().0.clone()); info!("update_pet_with_form({}, {:?}, {:?}) - X-Span-ID: {:?}", pet_id, name, status, context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
/// uploads an image /// uploads an image
fn upload_file( async fn upload_file(
&self, &self,
pet_id: i64, pet_id: i64,
additional_metadata: Option<String>, additional_metadata: Option<String>,
file: Option<swagger::ByteArray>, file: Option<swagger::ByteArray>,
context: &C) -> Box<dyn Future<Item=UploadFileResponse, Error=ApiError> + Send> context: &C) -> Result<UploadFileResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("upload_file({}, {:?}, {:?}) - X-Span-ID: {:?}", pet_id, additional_metadata, file, context.get().0.clone()); info!("upload_file({}, {:?}, {:?}) - X-Span-ID: {:?}", pet_id, additional_metadata, file, context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
/// Delete purchase order by ID /// Delete purchase order by ID
fn delete_order( async fn delete_order(
&self, &self,
order_id: String, order_id: String,
context: &C) -> Box<dyn Future<Item=DeleteOrderResponse, Error=ApiError> + Send> context: &C) -> Result<DeleteOrderResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("delete_order(\"{}\") - X-Span-ID: {:?}", order_id, context.get().0.clone()); info!("delete_order(\"{}\") - X-Span-ID: {:?}", order_id, context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
/// Returns pet inventories by status /// Returns pet inventories by status
fn get_inventory( async fn get_inventory(
&self, &self,
context: &C) -> Box<dyn Future<Item=GetInventoryResponse, Error=ApiError> + Send> context: &C) -> Result<GetInventoryResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("get_inventory() - X-Span-ID: {:?}", context.get().0.clone()); info!("get_inventory() - X-Span-ID: {:?}", context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
/// Find purchase order by ID /// Find purchase order by ID
fn get_order_by_id( async fn get_order_by_id(
&self, &self,
order_id: i64, order_id: i64,
context: &C) -> Box<dyn Future<Item=GetOrderByIdResponse, Error=ApiError> + Send> context: &C) -> Result<GetOrderByIdResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("get_order_by_id({}) - X-Span-ID: {:?}", order_id, context.get().0.clone()); info!("get_order_by_id({}) - X-Span-ID: {:?}", order_id, context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
/// Place an order for a pet /// Place an order for a pet
fn place_order( async fn place_order(
&self, &self,
body: models::Order, body: models::Order,
context: &C) -> Box<dyn Future<Item=PlaceOrderResponse, Error=ApiError> + Send> context: &C) -> Result<PlaceOrderResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("place_order({:?}) - X-Span-ID: {:?}", body, context.get().0.clone()); info!("place_order({:?}) - X-Span-ID: {:?}", body, context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
/// Create user /// Create user
fn create_user( async fn create_user(
&self, &self,
body: models::User, body: models::User,
context: &C) -> Box<dyn Future<Item=CreateUserResponse, Error=ApiError> + Send> context: &C) -> Result<CreateUserResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("create_user({:?}) - X-Span-ID: {:?}", body, context.get().0.clone()); info!("create_user({:?}) - X-Span-ID: {:?}", body, context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
/// Creates list of users with given input array /// Creates list of users with given input array
fn create_users_with_array_input( async fn create_users_with_array_input(
&self, &self,
body: &Vec<models::User>, body: &Vec<models::User>,
context: &C) -> Box<dyn Future<Item=CreateUsersWithArrayInputResponse, Error=ApiError> + Send> context: &C) -> Result<CreateUsersWithArrayInputResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("create_users_with_array_input({:?}) - X-Span-ID: {:?}", body, context.get().0.clone()); info!("create_users_with_array_input({:?}) - X-Span-ID: {:?}", body, context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
/// Creates list of users with given input array /// Creates list of users with given input array
fn create_users_with_list_input( async fn create_users_with_list_input(
&self, &self,
body: &Vec<models::User>, body: &Vec<models::User>,
context: &C) -> Box<dyn Future<Item=CreateUsersWithListInputResponse, Error=ApiError> + Send> context: &C) -> Result<CreateUsersWithListInputResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("create_users_with_list_input({:?}) - X-Span-ID: {:?}", body, context.get().0.clone()); info!("create_users_with_list_input({:?}) - X-Span-ID: {:?}", body, context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
/// Delete user /// Delete user
fn delete_user( async fn delete_user(
&self, &self,
username: String, username: String,
context: &C) -> Box<dyn Future<Item=DeleteUserResponse, Error=ApiError> + Send> context: &C) -> Result<DeleteUserResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("delete_user(\"{}\") - X-Span-ID: {:?}", username, context.get().0.clone()); info!("delete_user(\"{}\") - X-Span-ID: {:?}", username, context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
/// Get user by user name /// Get user by user name
fn get_user_by_name( async fn get_user_by_name(
&self, &self,
username: String, username: String,
context: &C) -> Box<dyn Future<Item=GetUserByNameResponse, Error=ApiError> + Send> context: &C) -> Result<GetUserByNameResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("get_user_by_name(\"{}\") - X-Span-ID: {:?}", username, context.get().0.clone()); info!("get_user_by_name(\"{}\") - X-Span-ID: {:?}", username, context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
/// Logs user into the system /// Logs user into the system
fn login_user( async fn login_user(
&self, &self,
username: String, username: String,
password: String, password: String,
context: &C) -> Box<dyn Future<Item=LoginUserResponse, Error=ApiError> + Send> context: &C) -> Result<LoginUserResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("login_user(\"{}\", \"{}\") - X-Span-ID: {:?}", username, password, context.get().0.clone()); info!("login_user(\"{}\", \"{}\") - X-Span-ID: {:?}", username, password, context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
/// Logs out current logged in user session /// Logs out current logged in user session
fn logout_user( async fn logout_user(
&self, &self,
context: &C) -> Box<dyn Future<Item=LogoutUserResponse, Error=ApiError> + Send> context: &C) -> Result<LogoutUserResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("logout_user() - X-Span-ID: {:?}", context.get().0.clone()); info!("logout_user() - X-Span-ID: {:?}", context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
/// Updated user /// Updated user
fn update_user( async fn update_user(
&self, &self,
username: String, username: String,
body: models::User, body: models::User,
context: &C) -> Box<dyn Future<Item=UpdateUserResponse, Error=ApiError> + Send> context: &C) -> Result<UpdateUserResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("update_user(\"{}\", {:?}) - X-Span-ID: {:?}", username, body, context.get().0.clone()); info!("update_user(\"{}\", {:?}) - X-Span-ID: {:?}", username, body, context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
} }

View File

@ -1,13 +1,12 @@
use futures::Future; use futures::future::BoxFuture;
use hyper;
use hyper::header::HeaderName; use hyper::header::HeaderName;
use hyper::{Error, Request, Response, StatusCode, service::Service, body::Payload}; use hyper::{Error, Request, Response, StatusCode, service::Service};
use url::form_urlencoded; use url::form_urlencoded;
use std::default::Default; use std::default::Default;
use std::io; use std::io;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::task::{Poll, Context};
use swagger::auth::{AuthData, Authorization, Bearer, Scopes}; use swagger::auth::{AuthData, Authorization, Bearer, Scopes};
use swagger::context::ContextualPayload;
use swagger::{EmptyContext, Has, Pop, Push, XSpanIdString}; use swagger::{EmptyContext, Has, Pop, Push, XSpanIdString};
use crate::Api; use crate::Api;
@ -31,58 +30,52 @@ where
} }
// Make a service that adds context. // Make a service that adds context.
impl<'a, T, SC, A, B, C, D, E, ME, S, OB, F> hyper::service::MakeService<&'a SC> for impl<Target, T, A, B, C, D> Service<Target> for
MakeAddContext<T, A> MakeAddContext<T, A>
where where
A: Default + Push<XSpanIdString, Result = B>, Target: Send,
A: Default + Push<XSpanIdString, Result = B> + Send,
B: Push<Option<AuthData>, Result = C>, B: Push<Option<AuthData>, Result = C>,
C: Push<Option<Authorization>, Result = D>, C: Push<Option<Authorization>, Result = D>,
D: Send + 'static, D: Send + 'static,
T: hyper::service::MakeService< T: Service<Target> + Send,
&'a SC, T::Future: Send + 'static
Error = E,
MakeError = ME,
Service = S,
ReqBody = ContextualPayload<hyper::Body, D>,
ResBody = OB,
Future = F
>,
S: Service<
Error = E,
ReqBody = ContextualPayload<hyper::Body, D>,
ResBody = OB> + 'static,
ME: swagger::ErrorBound,
E: swagger::ErrorBound,
F: Future<Item=S, Error=ME> + Send + 'static,
S::Future: Send,
OB: Payload,
{ {
type ReqBody = hyper::Body; type Error = T::Error;
type ResBody = OB; type Response = AddContext<T::Response, A, B, C, D>;
type Error = E; type Future = BoxFuture<'static, Result<Self::Response, Self::Error>>;
type MakeError = ME;
type Service = AddContext<S, A>;
type Future = Box<dyn Future<Item = Self::Service, Error = ME> + Send + 'static>;
fn make_service(&mut self, ctx: &'a SC) -> Self::Future { fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Box::new(self.inner.make_service(ctx).map(|s| AddContext::new(s))) self.inner.poll_ready(cx)
}
fn call(&mut self, target: Target) -> Self::Future {
let service = self.inner.call(target);
Box::pin(async move {
Ok(AddContext::new(service.await?))
})
} }
} }
/// Middleware to extract authentication data from request /// Middleware to add context data from the request
pub struct AddContext<T, A> { pub struct AddContext<T, A, B, C, D>
where
A: Default + Push<XSpanIdString, Result = B>,
B: Push<Option<AuthData>, Result = C>,
C: Push<Option<Authorization>, Result = D>
{
inner: T, inner: T,
marker: PhantomData<A>, marker: PhantomData<A>,
} }
impl<T, A, B, C, D> AddContext<T, A> impl<T, A, B, C, D> AddContext<T, A, B, C, D>
where where
A: Default + Push<XSpanIdString, Result = B>, A: Default + Push<XSpanIdString, Result = B>,
B: Push<Option<AuthData>, Result = C>, B: Push<Option<AuthData>, Result = C>,
C: Push<Option<Authorization>, Result = D>, C: Push<Option<Authorization>, Result = D>,
T: Service,
{ {
pub fn new(inner: T) -> AddContext<T, A> { pub fn new(inner: T) -> Self {
AddContext { AddContext {
inner, inner,
marker: PhantomData, marker: PhantomData,
@ -90,24 +83,26 @@ where
} }
} }
impl<T, A, B, C, D> Service for AddContext<T, A> impl<T, A, B, C, D, ReqBody> Service<Request<ReqBody>> for AddContext<T, A, B, C, D>
where where
A: Default + Push<XSpanIdString, Result=B>, A: Default + Push<XSpanIdString, Result=B>,
B: Push<Option<AuthData>, Result=C>, B: Push<Option<AuthData>, Result=C>,
C: Push<Option<Authorization>, Result=D>, C: Push<Option<Authorization>, Result=D>,
D: Send + 'static, D: Send + 'static,
T: Service<ReqBody = ContextualPayload<hyper::Body, D>>, T: Service<(Request<ReqBody>, D)>
T::Future: Future<Item=Response<T::ResBody>, Error=T::Error> + Send + 'static
{ {
type ReqBody = hyper::Body;
type ResBody = T::ResBody;
type Error = T::Error; type Error = T::Error;
type Future = Box<dyn Future<Item=Response<T::ResBody>, Error=T::Error> + Send + 'static>; type Future = T::Future;
type Response = T::Response;
fn call(&mut self, req: Request<Self::ReqBody>) -> Self::Future { fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
let context = A::default().push(XSpanIdString::get_or_generate(&req)); self.inner.poll_ready(cx)
let (head, body) = req.into_parts(); }
let headers = head.headers.clone();
fn call(&mut self, request: Request<ReqBody>) -> Self::Future {
let context = A::default().push(XSpanIdString::get_or_generate(&request));
let headers = request.headers();
{ {
use swagger::auth::api_key_from_header; use swagger::auth::api_key_from_header;
@ -117,16 +112,11 @@ impl<T, A, B, C, D> Service for AddContext<T, A>
let context = context.push(Some(auth_data)); let context = context.push(Some(auth_data));
let context = context.push(None::<Authorization>); let context = context.push(None::<Authorization>);
let body = ContextualPayload { return self.inner.call((request, context))
inner: body,
context: context,
};
return Box::new(self.inner.call(hyper::Request::from_parts(head, body)));
} }
} }
{ {
let key = form_urlencoded::parse(head.uri.query().unwrap_or_default().as_bytes()) let key = form_urlencoded::parse(request.uri().query().unwrap_or_default().as_bytes())
.filter(|e| e.0 == "api_key_query") .filter(|e| e.0 == "api_key_query")
.map(|e| e.1.clone().into_owned()) .map(|e| e.1.clone().into_owned())
.nth(0); .nth(0);
@ -135,11 +125,7 @@ impl<T, A, B, C, D> Service for AddContext<T, A>
let context = context.push(Some(auth_data)); let context = context.push(Some(auth_data));
let context = context.push(None::<Authorization>); let context = context.push(None::<Authorization>);
let body = ContextualPayload { return self.inner.call((request, context))
inner: body,
context: context,
};
return Box::new(self.inner.call(hyper::Request::from_parts(head, body)));
} }
} }
{ {
@ -150,12 +136,7 @@ impl<T, A, B, C, D> Service for AddContext<T, A>
let context = context.push(Some(auth_data)); let context = context.push(Some(auth_data));
let context = context.push(None::<Authorization>); let context = context.push(None::<Authorization>);
let body = ContextualPayload { return self.inner.call((request, context))
inner: body,
context: context,
};
return Box::new(self.inner.call(hyper::Request::from_parts(head, body)));
} }
} }
{ {
@ -166,22 +147,13 @@ impl<T, A, B, C, D> Service for AddContext<T, A>
let context = context.push(Some(auth_data)); let context = context.push(Some(auth_data));
let context = context.push(None::<Authorization>); let context = context.push(None::<Authorization>);
let body = ContextualPayload { return self.inner.call((request, context))
inner: body,
context: context,
};
return Box::new(self.inner.call(hyper::Request::from_parts(head, body)));
} }
} }
let context = context.push(None::<AuthData>); let context = context.push(None::<AuthData>);
let context = context.push(None::<Authorization>); let context = context.push(None::<Authorization>);
let body = ContextualPayload {
inner: body,
context: context,
};
Box::new(self.inner.call(hyper::Request::from_parts(head, body))) self.inner.call((request, context))
} }
} }

View File

@ -9,7 +9,7 @@ edition = "2018"
[features] [features]
default = ["client", "server"] default = ["client", "server"]
client = [ client = [
"hyper", "hyper-openssl", "native-tls", "openssl", "url" "hyper", "hyper-openssl", "hyper-tls", "native-tls", "openssl", "url"
] ]
server = [ server = [
"serde_ignored", "hyper", "regex", "percent-encoding", "url", "lazy_static" "serde_ignored", "hyper", "regex", "percent-encoding", "url", "lazy_static"
@ -18,35 +18,37 @@ conversion = ["frunk", "frunk_derives", "frunk_core", "frunk-enum-core", "frunk-
[target.'cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))'.dependencies] [target.'cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))'.dependencies]
native-tls = { version = "0.2", optional = true } native-tls = { version = "0.2", optional = true }
hyper-tls = { version = "0.4", optional = true }
[target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dependencies] [target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dependencies]
hyper-openssl = { version = "0.7.1", optional = true } hyper-openssl = { version = "0.8", optional = true }
openssl = {version = "0.10", optional = true } openssl = {version = "0.10", optional = true }
[dependencies] [dependencies]
# Common # Common
async-trait = "0.1.24"
chrono = { version = "0.4", features = ["serde"] } chrono = { version = "0.4", features = ["serde"] }
futures = "0.1" futures = "0.3"
swagger = "4.0" swagger = "5.0.0-alpha-1"
log = "0.4.0" log = "0.4.0"
mime = "0.3" mime = "0.3"
serde = { version = "1.0", features = ["derive"]} serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"
# Crates included if required by the API definition # Crates included if required by the API definition
# Common between server and client features # Common between server and client features
hyper = {version = "0.12", optional = true} hyper = {version = "0.13", optional = true}
serde_ignored = {version = "0.0.4", optional = true} serde_ignored = {version = "0.1.1", optional = true}
url = {version = "1.5", optional = true} url = {version = "2.1", optional = true}
# Client-specific # Client-specific
# Server, and client callback-specific # Server, and client callback-specific
lazy_static = { version = "1.4", optional = true } lazy_static = { version = "1.4", optional = true }
percent-encoding = {version = "1.0.0", optional = true} percent-encoding = {version = "2.1.0", optional = true}
regex = {version = "0.2", optional = true} regex = {version = "1.3", optional = true}
# Conversion # Conversion
frunk = { version = "0.3.0", optional = true } frunk = { version = "0.3.0", optional = true }
@ -57,13 +59,13 @@ frunk-enum-core = { version = "0.2.0", optional = true }
[dev-dependencies] [dev-dependencies]
clap = "2.25" clap = "2.25"
error-chain = "0.12" env_logger = "0.7"
env_logger = "0.6" tokio = { version = "0.2", features = ["rt-threaded", "macros", "stream"] }
tokio = "0.1.17" native-tls = "0.2"
uuid = {version = "0.7", features = ["serde", "v4"]} tokio-tls = "0.3"
[target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dev-dependencies] [target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dev-dependencies]
tokio-openssl = "0.3" tokio-openssl = "0.4"
openssl = "0.10" openssl = "0.10"
[[example]] [[example]]

View File

@ -2,10 +2,9 @@
#[allow(unused_imports)] #[allow(unused_imports)]
use futures::{Future, future, Stream, stream}; use futures::{future, Stream, stream};
#[allow(unused_imports)] #[allow(unused_imports)]
use rust_server_test::{Api, ApiNoContext, Client, ContextWrapperExt, models, use rust_server_test::{Api, ApiNoContext, Client, ContextWrapperExt, models,
ApiError,
AllOfGetResponse, AllOfGetResponse,
DummyGetResponse, DummyGetResponse,
DummyPutResponse, DummyPutResponse,
@ -14,7 +13,7 @@ use rust_server_test::{Api, ApiNoContext, Client, ContextWrapperExt, models,
HtmlPostResponse, HtmlPostResponse,
PostYamlResponse, PostYamlResponse,
RawJsonGetResponse, RawJsonGetResponse,
SoloObjectPostResponse SoloObjectPostResponse,
}; };
use clap::{App, Arg}; use clap::{App, Arg};
@ -23,7 +22,9 @@ use log::info;
// swagger::Has may be unused if there are no examples // swagger::Has may be unused if there are no examples
#[allow(unused_imports)] #[allow(unused_imports)]
use swagger::{ContextBuilder, EmptyContext, XSpanIdString, Has, Push, AuthData}; use swagger::{AuthData, ContextBuilder, EmptyContext, Has, Push, XSpanIdString};
type ClientContext = swagger::make_context_ty!(ContextBuilder, EmptyContext, Option<AuthData>, XSpanIdString);
// rt may be unused if there are no examples // rt may be unused if there are no examples
#[allow(unused_mut)] #[allow(unused_mut)]
@ -65,21 +66,21 @@ fn main() {
matches.value_of("host").unwrap(), matches.value_of("host").unwrap(),
matches.value_of("port").unwrap()); matches.value_of("port").unwrap());
let client = if matches.is_present("https") { let context: ClientContext =
// Using Simple HTTPS
Client::try_new_https(&base_url)
.expect("Failed to create HTTPS client")
} else {
// Using HTTP
Client::try_new_http(
&base_url)
.expect("Failed to create HTTP client")
};
let context: swagger::make_context_ty!(ContextBuilder, EmptyContext, Option<AuthData>, XSpanIdString) =
swagger::make_context!(ContextBuilder, EmptyContext, None as Option<AuthData>, XSpanIdString::default()); swagger::make_context!(ContextBuilder, EmptyContext, None as Option<AuthData>, XSpanIdString::default());
let client = client.with_context(context); let mut client : Box<dyn ApiNoContext<ClientContext>> = if matches.is_present("https") {
// Using Simple HTTPS
let client = Box::new(Client::try_new_https(&base_url)
.expect("Failed to create HTTPS client"));
Box::new(client.with_context(context))
} else {
// Using HTTP
let client = Box::new(Client::try_new_http(
&base_url)
.expect("Failed to create HTTP client"));
Box::new(client.with_context(context))
};
let mut rt = tokio::runtime::Runtime::new().unwrap(); let mut rt = tokio::runtime::Runtime::new().unwrap();

View File

@ -9,7 +9,8 @@ mod server;
/// Create custom server, wire it to the autogenerated router, /// Create custom server, wire it to the autogenerated router,
/// and pass it to the web server. /// and pass it to the web server.
fn main() { #[tokio::main]
async fn main() {
env_logger::init(); env_logger::init();
let matches = App::new("server") let matches = App::new("server")
@ -20,5 +21,5 @@ fn main() {
let addr = "127.0.0.1:8080"; let addr = "127.0.0.1:8080";
hyper::rt::run(server::create(addr, matches.is_present("https"))); server::create(addr, matches.is_present("https")).await;
} }

View File

@ -2,30 +2,22 @@
#![allow(unused_imports)] #![allow(unused_imports)]
mod errors { use async_trait::async_trait;
error_chain::error_chain!{} use futures::{future, Stream, StreamExt, TryFutureExt, TryStreamExt};
}
pub use self::errors::*;
use chrono;
use futures::{future, Future, Stream};
use hyper::server::conn::Http; use hyper::server::conn::Http;
use hyper::service::MakeService as _; use hyper::service::Service;
use log::info; use log::info;
use openssl::ssl::SslAcceptorBuilder; use openssl::ssl::SslAcceptorBuilder;
use std::future::Future;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::net::SocketAddr; use std::net::SocketAddr;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use swagger; use std::task::{Context, Poll};
use swagger::{Has, XSpanIdString}; use swagger::{Has, XSpanIdString};
use swagger::auth::MakeAllowAllAuthenticator; use swagger::auth::MakeAllowAllAuthenticator;
use swagger::EmptyContext; use swagger::EmptyContext;
use tokio::net::TcpListener; use tokio::net::TcpListener;
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
use tokio_openssl::SslAcceptorExt;
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod}; use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod};
@ -33,18 +25,18 @@ use rust_server_test::models;
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
/// Builds an SSL implementation for Simple HTTPS from some hard-coded file names /// Builds an SSL implementation for Simple HTTPS from some hard-coded file names
pub fn create(addr: &str, https: bool) -> Box<dyn Future<Item = (), Error = ()> + Send> { pub async fn create(addr: &str, https: bool) {
let addr = addr.parse().expect("Failed to parse bind address"); let addr = addr.parse().expect("Failed to parse bind address");
let server = Server::new(); let server = Server::new();
let service_fn = MakeService::new(server); let service = MakeService::new(server);
let service_fn = MakeAllowAllAuthenticator::new(service_fn, "cosmo"); let service = MakeAllowAllAuthenticator::new(service, "cosmo");
let service_fn = let mut service =
rust_server_test::server::context::MakeAddContext::<_, EmptyContext>::new( rust_server_test::server::context::MakeAddContext::<_, EmptyContext>::new(
service_fn service
); );
if https { if https {
@ -62,32 +54,31 @@ pub fn create(addr: &str, https: bool) -> Box<dyn Future<Item = (), Error = ()>
ssl.set_certificate_chain_file("examples/server-chain.pem").expect("Failed to set cerificate chain"); ssl.set_certificate_chain_file("examples/server-chain.pem").expect("Failed to set cerificate chain");
ssl.check_private_key().expect("Failed to check private key"); ssl.check_private_key().expect("Failed to check private key");
let tls_acceptor = ssl.build(); let tls_acceptor = Arc::new(ssl.build());
let service_fn = Arc::new(Mutex::new(service_fn)); let mut tcp_listener = TcpListener::bind(&addr).await.unwrap();
let tls_listener = TcpListener::bind(&addr).unwrap().incoming().for_each(move |tcp| { let mut incoming = tcp_listener.incoming();
let addr = tcp.peer_addr().expect("Unable to get remote address");
let service_fn = service_fn.clone(); while let (Some(tcp), rest) = incoming.into_future().await {
if let Ok(tcp) = tcp {
let addr = tcp.peer_addr().expect("Unable to get remote address");
let service = service.call(addr);
let tls_acceptor = Arc::clone(&tls_acceptor);
hyper::rt::spawn(tls_acceptor.accept_async(tcp).map_err(|_| ()).and_then(move |tls| { tokio::spawn(async move {
let ms = { let tls = tokio_openssl::accept(&*tls_acceptor, tcp).await.map_err(|_| ())?;
let mut service_fn = service_fn.lock().unwrap();
service_fn.make_service(&addr)
};
ms.and_then(move |service| { let service = service.await.map_err(|_| ())?;
Http::new().serve_connection(tls, service)
}).map_err(|_| ())
}));
Ok(()) Http::new().serve_connection(tls, service).await.map_err(|_| ())
}).map_err(|_| ()); });
}
Box::new(tls_listener) incoming = rest;
}
} }
} else { } else {
// Using HTTP // Using HTTP
Box::new(hyper::server::Server::bind(&addr).serve(service_fn).map_err(|e| panic!("{:?}", e))) hyper::server::Server::bind(&addr).serve(service).await.unwrap()
} }
} }
@ -105,7 +96,6 @@ impl<C> Server<C> {
use rust_server_test::{ use rust_server_test::{
Api, Api,
ApiError,
AllOfGetResponse, AllOfGetResponse,
DummyGetResponse, DummyGetResponse,
DummyPutResponse, DummyPutResponse,
@ -117,96 +107,100 @@ use rust_server_test::{
SoloObjectPostResponse, SoloObjectPostResponse,
}; };
use rust_server_test::server::MakeService; use rust_server_test::server::MakeService;
use std::error::Error;
use swagger::ApiError;
impl<C> Api<C> for Server<C> where C: Has<XSpanIdString>{ #[async_trait]
fn all_of_get( impl<C> Api<C> for Server<C> where C: Has<XSpanIdString> + Send + Sync
{
async fn all_of_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=AllOfGetResponse, Error=ApiError> + Send> context: &C) -> Result<AllOfGetResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("all_of_get() - X-Span-ID: {:?}", context.get().0.clone()); info!("all_of_get() - X-Span-ID: {:?}", context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
/// A dummy endpoint to make the spec valid. /// A dummy endpoint to make the spec valid.
fn dummy_get( async fn dummy_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=DummyGetResponse, Error=ApiError> + Send> context: &C) -> Result<DummyGetResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("dummy_get() - X-Span-ID: {:?}", context.get().0.clone()); info!("dummy_get() - X-Span-ID: {:?}", context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn dummy_put( async fn dummy_put(
&self, &self,
nested_response: models::InlineObject, nested_response: models::InlineObject,
context: &C) -> Box<dyn Future<Item=DummyPutResponse, Error=ApiError> + Send> context: &C) -> Result<DummyPutResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("dummy_put({:?}) - X-Span-ID: {:?}", nested_response, context.get().0.clone()); info!("dummy_put({:?}) - X-Span-ID: {:?}", nested_response, context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
/// Get a file /// Get a file
fn file_response_get( async fn file_response_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=FileResponseGetResponse, Error=ApiError> + Send> context: &C) -> Result<FileResponseGetResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("file_response_get() - X-Span-ID: {:?}", context.get().0.clone()); info!("file_response_get() - X-Span-ID: {:?}", context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn get_structured_yaml( async fn get_structured_yaml(
&self, &self,
context: &C) -> Box<dyn Future<Item=GetStructuredYamlResponse, Error=ApiError> + Send> context: &C) -> Result<GetStructuredYamlResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("get_structured_yaml() - X-Span-ID: {:?}", context.get().0.clone()); info!("get_structured_yaml() - X-Span-ID: {:?}", context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
/// Test HTML handling /// Test HTML handling
fn html_post( async fn html_post(
&self, &self,
body: String, body: String,
context: &C) -> Box<dyn Future<Item=HtmlPostResponse, Error=ApiError> + Send> context: &C) -> Result<HtmlPostResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("html_post(\"{}\") - X-Span-ID: {:?}", body, context.get().0.clone()); info!("html_post(\"{}\") - X-Span-ID: {:?}", body, context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
fn post_yaml( async fn post_yaml(
&self, &self,
value: String, value: String,
context: &C) -> Box<dyn Future<Item=PostYamlResponse, Error=ApiError> + Send> context: &C) -> Result<PostYamlResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("post_yaml(\"{}\") - X-Span-ID: {:?}", value, context.get().0.clone()); info!("post_yaml(\"{}\") - X-Span-ID: {:?}", value, context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
/// Get an arbitrary JSON blob. /// Get an arbitrary JSON blob.
fn raw_json_get( async fn raw_json_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=RawJsonGetResponse, Error=ApiError> + Send> context: &C) -> Result<RawJsonGetResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("raw_json_get() - X-Span-ID: {:?}", context.get().0.clone()); info!("raw_json_get() - X-Span-ID: {:?}", context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
/// Send an arbitrary JSON blob /// Send an arbitrary JSON blob
fn solo_object_post( async fn solo_object_post(
&self, &self,
value: serde_json::Value, value: serde_json::Value,
context: &C) -> Box<dyn Future<Item=SoloObjectPostResponse, Error=ApiError> + Send> context: &C) -> Result<SoloObjectPostResponse, ApiError>
{ {
let context = context.clone(); let context = context.clone();
info!("solo_object_post({:?}) - X-Span-ID: {:?}", value, context.get().0.clone()); info!("solo_object_post({:?}) - X-Span-ID: {:?}", value, context.get().0.clone());
Box::new(future::err("Generic failure".into())) Err("Generic failuare".into())
} }
} }

View File

@ -1,13 +1,12 @@
use futures::Future; use futures::future::BoxFuture;
use hyper;
use hyper::header::HeaderName; use hyper::header::HeaderName;
use hyper::{Error, Request, Response, StatusCode, service::Service, body::Payload}; use hyper::{Error, Request, Response, StatusCode, service::Service};
use url::form_urlencoded; use url::form_urlencoded;
use std::default::Default; use std::default::Default;
use std::io; use std::io;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::task::{Poll, Context};
use swagger::auth::{AuthData, Authorization, Bearer, Scopes}; use swagger::auth::{AuthData, Authorization, Bearer, Scopes};
use swagger::context::ContextualPayload;
use swagger::{EmptyContext, Has, Pop, Push, XSpanIdString}; use swagger::{EmptyContext, Has, Pop, Push, XSpanIdString};
use crate::Api; use crate::Api;
@ -31,58 +30,52 @@ where
} }
// Make a service that adds context. // Make a service that adds context.
impl<'a, T, SC, A, B, C, D, E, ME, S, OB, F> hyper::service::MakeService<&'a SC> for impl<Target, T, A, B, C, D> Service<Target> for
MakeAddContext<T, A> MakeAddContext<T, A>
where where
A: Default + Push<XSpanIdString, Result = B>, Target: Send,
A: Default + Push<XSpanIdString, Result = B> + Send,
B: Push<Option<AuthData>, Result = C>, B: Push<Option<AuthData>, Result = C>,
C: Push<Option<Authorization>, Result = D>, C: Push<Option<Authorization>, Result = D>,
D: Send + 'static, D: Send + 'static,
T: hyper::service::MakeService< T: Service<Target> + Send,
&'a SC, T::Future: Send + 'static
Error = E,
MakeError = ME,
Service = S,
ReqBody = ContextualPayload<hyper::Body, D>,
ResBody = OB,
Future = F
>,
S: Service<
Error = E,
ReqBody = ContextualPayload<hyper::Body, D>,
ResBody = OB> + 'static,
ME: swagger::ErrorBound,
E: swagger::ErrorBound,
F: Future<Item=S, Error=ME> + Send + 'static,
S::Future: Send,
OB: Payload,
{ {
type ReqBody = hyper::Body; type Error = T::Error;
type ResBody = OB; type Response = AddContext<T::Response, A, B, C, D>;
type Error = E; type Future = BoxFuture<'static, Result<Self::Response, Self::Error>>;
type MakeError = ME;
type Service = AddContext<S, A>;
type Future = Box<dyn Future<Item = Self::Service, Error = ME> + Send + 'static>;
fn make_service(&mut self, ctx: &'a SC) -> Self::Future { fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Box::new(self.inner.make_service(ctx).map(|s| AddContext::new(s))) self.inner.poll_ready(cx)
}
fn call(&mut self, target: Target) -> Self::Future {
let service = self.inner.call(target);
Box::pin(async move {
Ok(AddContext::new(service.await?))
})
} }
} }
/// Middleware to extract authentication data from request /// Middleware to add context data from the request
pub struct AddContext<T, A> { pub struct AddContext<T, A, B, C, D>
where
A: Default + Push<XSpanIdString, Result = B>,
B: Push<Option<AuthData>, Result = C>,
C: Push<Option<Authorization>, Result = D>
{
inner: T, inner: T,
marker: PhantomData<A>, marker: PhantomData<A>,
} }
impl<T, A, B, C, D> AddContext<T, A> impl<T, A, B, C, D> AddContext<T, A, B, C, D>
where where
A: Default + Push<XSpanIdString, Result = B>, A: Default + Push<XSpanIdString, Result = B>,
B: Push<Option<AuthData>, Result = C>, B: Push<Option<AuthData>, Result = C>,
C: Push<Option<Authorization>, Result = D>, C: Push<Option<Authorization>, Result = D>,
T: Service,
{ {
pub fn new(inner: T) -> AddContext<T, A> { pub fn new(inner: T) -> Self {
AddContext { AddContext {
inner, inner,
marker: PhantomData, marker: PhantomData,
@ -90,33 +83,31 @@ where
} }
} }
impl<T, A, B, C, D> Service for AddContext<T, A> impl<T, A, B, C, D, ReqBody> Service<Request<ReqBody>> for AddContext<T, A, B, C, D>
where where
A: Default + Push<XSpanIdString, Result=B>, A: Default + Push<XSpanIdString, Result=B>,
B: Push<Option<AuthData>, Result=C>, B: Push<Option<AuthData>, Result=C>,
C: Push<Option<Authorization>, Result=D>, C: Push<Option<Authorization>, Result=D>,
D: Send + 'static, D: Send + 'static,
T: Service<ReqBody = ContextualPayload<hyper::Body, D>>, T: Service<(Request<ReqBody>, D)>
T::Future: Future<Item=Response<T::ResBody>, Error=T::Error> + Send + 'static
{ {
type ReqBody = hyper::Body;
type ResBody = T::ResBody;
type Error = T::Error; type Error = T::Error;
type Future = Box<dyn Future<Item=Response<T::ResBody>, Error=T::Error> + Send + 'static>; type Future = T::Future;
type Response = T::Response;
fn call(&mut self, req: Request<Self::ReqBody>) -> Self::Future { fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
let context = A::default().push(XSpanIdString::get_or_generate(&req)); self.inner.poll_ready(cx)
let (head, body) = req.into_parts(); }
let headers = head.headers.clone();
fn call(&mut self, request: Request<ReqBody>) -> Self::Future {
let context = A::default().push(XSpanIdString::get_or_generate(&request));
let headers = request.headers();
let context = context.push(None::<AuthData>); let context = context.push(None::<AuthData>);
let context = context.push(None::<Authorization>); let context = context.push(None::<Authorization>);
let body = ContextualPayload {
inner: body,
context: context,
};
Box::new(self.inner.call(hyper::Request::from_parts(head, body))) self.inner.call((request, context))
} }
} }

View File

@ -1,12 +1,12 @@
#![allow(missing_docs, trivial_casts, unused_variables, unused_mut, unused_imports, unused_extern_crates, non_camel_case_types)] #![allow(missing_docs, trivial_casts, unused_variables, unused_mut, unused_imports, unused_extern_crates, non_camel_case_types)]
use async_trait::async_trait;
use futures::Stream; use futures::Stream;
use std::io::Error; use std::error::Error;
use std::task::{Poll, Context};
use swagger::{ApiError, ContextWrapper};
#[deprecated(note = "Import swagger-rs directly")] type ServiceError = Box<dyn Error + Send + Sync + 'static>;
pub use swagger::{ApiError, ContextWrapper};
#[deprecated(note = "Import futures directly")]
pub use futures::Future;
pub const BASE_PATH: &'static str = ""; pub const BASE_PATH: &'static str = "";
pub const API_VERSION: &'static str = "2.3.4"; pub const API_VERSION: &'static str = "2.3.4";
@ -71,190 +71,221 @@ pub enum SoloObjectPostResponse {
} }
/// API /// API
pub trait Api<C> { #[async_trait]
fn all_of_get( pub trait Api<C: Send + Sync> {
fn poll_ready(&self, _cx: &mut Context) -> Poll<Result<(), Box<dyn Error + Send + Sync + 'static>>> {
Poll::Ready(Ok(()))
}
async fn all_of_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=AllOfGetResponse, Error=ApiError> + Send>; context: &C) -> Result<AllOfGetResponse, ApiError>;
/// A dummy endpoint to make the spec valid. /// A dummy endpoint to make the spec valid.
fn dummy_get( async fn dummy_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=DummyGetResponse, Error=ApiError> + Send>; context: &C) -> Result<DummyGetResponse, ApiError>;
fn dummy_put( async fn dummy_put(
&self, &self,
nested_response: models::InlineObject, nested_response: models::InlineObject,
context: &C) -> Box<dyn Future<Item=DummyPutResponse, Error=ApiError> + Send>; context: &C) -> Result<DummyPutResponse, ApiError>;
/// Get a file /// Get a file
fn file_response_get( async fn file_response_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=FileResponseGetResponse, Error=ApiError> + Send>; context: &C) -> Result<FileResponseGetResponse, ApiError>;
fn get_structured_yaml( async fn get_structured_yaml(
&self, &self,
context: &C) -> Box<dyn Future<Item=GetStructuredYamlResponse, Error=ApiError> + Send>; context: &C) -> Result<GetStructuredYamlResponse, ApiError>;
/// Test HTML handling /// Test HTML handling
fn html_post( async fn html_post(
&self, &self,
body: String, body: String,
context: &C) -> Box<dyn Future<Item=HtmlPostResponse, Error=ApiError> + Send>; context: &C) -> Result<HtmlPostResponse, ApiError>;
fn post_yaml( async fn post_yaml(
&self, &self,
value: String, value: String,
context: &C) -> Box<dyn Future<Item=PostYamlResponse, Error=ApiError> + Send>; context: &C) -> Result<PostYamlResponse, ApiError>;
/// Get an arbitrary JSON blob. /// Get an arbitrary JSON blob.
fn raw_json_get( async fn raw_json_get(
&self, &self,
context: &C) -> Box<dyn Future<Item=RawJsonGetResponse, Error=ApiError> + Send>; context: &C) -> Result<RawJsonGetResponse, ApiError>;
/// Send an arbitrary JSON blob /// Send an arbitrary JSON blob
fn solo_object_post( async fn solo_object_post(
&self, &self,
value: serde_json::Value, value: serde_json::Value,
context: &C) -> Box<dyn Future<Item=SoloObjectPostResponse, Error=ApiError> + Send>; context: &C) -> Result<SoloObjectPostResponse, ApiError>;
} }
/// API without a `Context` /// API where `Context` isn't passed on every API call
pub trait ApiNoContext { #[async_trait]
fn all_of_get( pub trait ApiNoContext<C: Send + Sync> {
fn poll_ready(&self, _cx: &mut Context) -> Poll<Result<(), Box<dyn Error + Send + Sync + 'static>>>;
fn context(&self) -> &C;
async fn all_of_get(
&self, &self,
) -> Box<dyn Future<Item=AllOfGetResponse, Error=ApiError> + Send>; ) -> Result<AllOfGetResponse, ApiError>;
/// A dummy endpoint to make the spec valid. /// A dummy endpoint to make the spec valid.
fn dummy_get( async fn dummy_get(
&self, &self,
) -> Box<dyn Future<Item=DummyGetResponse, Error=ApiError> + Send>; ) -> Result<DummyGetResponse, ApiError>;
fn dummy_put( async fn dummy_put(
&self, &self,
nested_response: models::InlineObject, nested_response: models::InlineObject,
) -> Box<dyn Future<Item=DummyPutResponse, Error=ApiError> + Send>; ) -> Result<DummyPutResponse, ApiError>;
/// Get a file /// Get a file
fn file_response_get( async fn file_response_get(
&self, &self,
) -> Box<dyn Future<Item=FileResponseGetResponse, Error=ApiError> + Send>; ) -> Result<FileResponseGetResponse, ApiError>;
fn get_structured_yaml( async fn get_structured_yaml(
&self, &self,
) -> Box<dyn Future<Item=GetStructuredYamlResponse, Error=ApiError> + Send>; ) -> Result<GetStructuredYamlResponse, ApiError>;
/// Test HTML handling /// Test HTML handling
fn html_post( async fn html_post(
&self, &self,
body: String, body: String,
) -> Box<dyn Future<Item=HtmlPostResponse, Error=ApiError> + Send>; ) -> Result<HtmlPostResponse, ApiError>;
fn post_yaml( async fn post_yaml(
&self, &self,
value: String, value: String,
) -> Box<dyn Future<Item=PostYamlResponse, Error=ApiError> + Send>; ) -> Result<PostYamlResponse, ApiError>;
/// Get an arbitrary JSON blob. /// Get an arbitrary JSON blob.
fn raw_json_get( async fn raw_json_get(
&self, &self,
) -> Box<dyn Future<Item=RawJsonGetResponse, Error=ApiError> + Send>; ) -> Result<RawJsonGetResponse, ApiError>;
/// Send an arbitrary JSON blob /// Send an arbitrary JSON blob
fn solo_object_post( async fn solo_object_post(
&self, &self,
value: serde_json::Value, value: serde_json::Value,
) -> Box<dyn Future<Item=SoloObjectPostResponse, Error=ApiError> + Send>; ) -> Result<SoloObjectPostResponse, ApiError>;
} }
/// Trait to extend an API to make it easy to bind it to a context. /// Trait to extend an API to make it easy to bind it to a context.
pub trait ContextWrapperExt<'a, C> where Self: Sized { pub trait ContextWrapperExt<C: Send + Sync> where Self: Sized
{
/// Binds this API to a context. /// Binds this API to a context.
fn with_context(self: &'a Self, context: C) -> ContextWrapper<'a, Self, C>; fn with_context(self: Self, context: C) -> ContextWrapper<Self, C>;
} }
impl<'a, T: Api<C> + Sized, C> ContextWrapperExt<'a, C> for T { impl<T: Api<C> + Send + Sync, C: Clone + Send + Sync> ContextWrapperExt<C> for T {
fn with_context(self: &'a T, context: C) -> ContextWrapper<'a, T, C> { fn with_context(self: T, context: C) -> ContextWrapper<T, C> {
ContextWrapper::<T, C>::new(self, context) ContextWrapper::<T, C>::new(self, context)
} }
} }
impl<'a, T: Api<C>, C> ApiNoContext for ContextWrapper<'a, T, C> { #[async_trait]
fn all_of_get( impl<T: Api<C> + Send + Sync, C: Clone + Send + Sync> ApiNoContext<C> for ContextWrapper<T, C> {
fn poll_ready(&self, cx: &mut Context) -> Poll<Result<(), ServiceError>> {
self.api().poll_ready(cx)
}
fn context(&self) -> &C {
ContextWrapper::context(self)
}
async fn all_of_get(
&self, &self,
) -> Box<dyn Future<Item=AllOfGetResponse, Error=ApiError> + Send> ) -> Result<AllOfGetResponse, ApiError>
{ {
self.api().all_of_get(&self.context()) let context = self.context().clone();
self.api().all_of_get(&context).await
} }
/// A dummy endpoint to make the spec valid. /// A dummy endpoint to make the spec valid.
fn dummy_get( async fn dummy_get(
&self, &self,
) -> Box<dyn Future<Item=DummyGetResponse, Error=ApiError> + Send> ) -> Result<DummyGetResponse, ApiError>
{ {
self.api().dummy_get(&self.context()) let context = self.context().clone();
self.api().dummy_get(&context).await
} }
fn dummy_put( async fn dummy_put(
&self, &self,
nested_response: models::InlineObject, nested_response: models::InlineObject,
) -> Box<dyn Future<Item=DummyPutResponse, Error=ApiError> + Send> ) -> Result<DummyPutResponse, ApiError>
{ {
self.api().dummy_put(nested_response, &self.context()) let context = self.context().clone();
self.api().dummy_put(nested_response, &context).await
} }
/// Get a file /// Get a file
fn file_response_get( async fn file_response_get(
&self, &self,
) -> Box<dyn Future<Item=FileResponseGetResponse, Error=ApiError> + Send> ) -> Result<FileResponseGetResponse, ApiError>
{ {
self.api().file_response_get(&self.context()) let context = self.context().clone();
self.api().file_response_get(&context).await
} }
fn get_structured_yaml( async fn get_structured_yaml(
&self, &self,
) -> Box<dyn Future<Item=GetStructuredYamlResponse, Error=ApiError> + Send> ) -> Result<GetStructuredYamlResponse, ApiError>
{ {
self.api().get_structured_yaml(&self.context()) let context = self.context().clone();
self.api().get_structured_yaml(&context).await
} }
/// Test HTML handling /// Test HTML handling
fn html_post( async fn html_post(
&self, &self,
body: String, body: String,
) -> Box<dyn Future<Item=HtmlPostResponse, Error=ApiError> + Send> ) -> Result<HtmlPostResponse, ApiError>
{ {
self.api().html_post(body, &self.context()) let context = self.context().clone();
self.api().html_post(body, &context).await
} }
fn post_yaml( async fn post_yaml(
&self, &self,
value: String, value: String,
) -> Box<dyn Future<Item=PostYamlResponse, Error=ApiError> + Send> ) -> Result<PostYamlResponse, ApiError>
{ {
self.api().post_yaml(value, &self.context()) let context = self.context().clone();
self.api().post_yaml(value, &context).await
} }
/// Get an arbitrary JSON blob. /// Get an arbitrary JSON blob.
fn raw_json_get( async fn raw_json_get(
&self, &self,
) -> Box<dyn Future<Item=RawJsonGetResponse, Error=ApiError> + Send> ) -> Result<RawJsonGetResponse, ApiError>
{ {
self.api().raw_json_get(&self.context()) let context = self.context().clone();
self.api().raw_json_get(&context).await
} }
/// Send an arbitrary JSON blob /// Send an arbitrary JSON blob
fn solo_object_post( async fn solo_object_post(
&self, &self,
value: serde_json::Value, value: serde_json::Value,
) -> Box<dyn Future<Item=SoloObjectPostResponse, Error=ApiError> + Send> ) -> Result<SoloObjectPostResponse, ApiError>
{ {
self.api().solo_object_post(value, &self.context()) let context = self.context().clone();
self.api().solo_object_post(value, &context).await
} }
} }
#[cfg(feature = "client")] #[cfg(feature = "client")]
pub mod client; pub mod client;

View File

@ -1,20 +1,17 @@
use std::marker::PhantomData; use futures::{future, future::BoxFuture, Stream, stream, future::FutureExt, stream::TryStreamExt};
use futures::{Future, future, Stream, stream}; use hyper::{Request, Response, StatusCode, Body, HeaderMap};
use hyper;
use hyper::{Request, Response, Error, StatusCode, Body, HeaderMap};
use hyper::header::{HeaderName, HeaderValue, CONTENT_TYPE}; use hyper::header::{HeaderName, HeaderValue, CONTENT_TYPE};
use log::warn; use log::warn;
use serde_json;
#[allow(unused_imports)] #[allow(unused_imports)]
use std::convert::{TryFrom, TryInto}; use std::convert::{TryFrom, TryInto};
use std::io; use std::error::Error;
use url::form_urlencoded; use std::future::Future;
#[allow(unused_imports)] use std::marker::PhantomData;
use swagger; use std::task::{Context, Poll};
use swagger::{ApiError, XSpanIdString, Has, RequestParser}; use swagger::{ApiError, BodyExt, Has, RequestParser, XSpanIdString};
pub use swagger::auth::Authorization; pub use swagger::auth::Authorization;
use swagger::auth::Scopes; use swagger::auth::Scopes;
use swagger::context::ContextualPayload; use url::form_urlencoded;
#[allow(unused_imports)] #[allow(unused_imports)]
use crate::models; use crate::models;
@ -22,6 +19,8 @@ use crate::header;
pub use crate::context; pub use crate::context;
type ServiceFuture = BoxFuture<'static, Result<Response<Body>, crate::ServiceError>>;
use crate::{Api, use crate::{Api,
AllOfGetResponse, AllOfGetResponse,
DummyGetResponse, DummyGetResponse,
@ -60,15 +59,17 @@ mod paths {
pub(crate) static ID_SOLO_OBJECT: usize = 7; pub(crate) static ID_SOLO_OBJECT: usize = 7;
} }
pub struct MakeService<T, RC> { pub struct MakeService<T, C> where
T: Api<C> + Clone + Send + 'static,
C: Has<XSpanIdString> + Send + Sync + 'static
{
api_impl: T, api_impl: T,
marker: PhantomData<RC>, marker: PhantomData<C>,
} }
impl<T, RC> MakeService<T, RC> impl<T, C> MakeService<T, C> where
where T: Api<C> + Clone + Send + 'static,
T: Api<RC> + Clone + Send + 'static, C: Has<XSpanIdString> + Send + Sync + 'static
RC: Has<XSpanIdString> + 'static
{ {
pub fn new(api_impl: T) -> Self { pub fn new(api_impl: T) -> Self {
MakeService { MakeService {
@ -78,44 +79,45 @@ where
} }
} }
impl<'a, T, SC, RC> hyper::service::MakeService<&'a SC> for MakeService<T, RC> impl<T, C, Target> hyper::service::Service<Target> for MakeService<T, C> where
where T: Api<C> + Clone + Send + 'static,
T: Api<RC> + Clone + Send + 'static, C: Has<XSpanIdString> + Send + Sync + 'static
RC: Has<XSpanIdString> + 'static + Send
{ {
type ReqBody = ContextualPayload<Body, RC>; type Response = Service<T, C>;
type ResBody = Body; type Error = crate::ServiceError;
type Error = Error; type Future = future::Ready<Result<Self::Response, Self::Error>>;
type Service = Service<T, RC>;
type Future = future::FutureResult<Self::Service, Self::MakeError>;
type MakeError = Error;
fn make_service(&mut self, _ctx: &'a SC) -> Self::Future { fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
future::FutureResult::from(Ok(Service::new( Poll::Ready(Ok(()))
}
fn call(&mut self, target: Target) -> Self::Future {
futures::future::ok(Service::new(
self.api_impl.clone(), self.api_impl.clone(),
))) ))
} }
} }
type ServiceFuture = Box<dyn Future<Item = Response<Body>, Error = Error> + Send>; fn method_not_allowed() -> Result<Response<Body>, crate::ServiceError> {
Ok(
fn method_not_allowed() -> ServiceFuture {
Box::new(future::ok(
Response::builder().status(StatusCode::METHOD_NOT_ALLOWED) Response::builder().status(StatusCode::METHOD_NOT_ALLOWED)
.body(Body::empty()) .body(Body::empty())
.expect("Unable to create Method Not Allowed response") .expect("Unable to create Method Not Allowed response")
)) )
} }
pub struct Service<T, RC> { pub struct Service<T, C> where
T: Api<C> + Clone + Send + 'static,
C: Has<XSpanIdString> + Send + Sync + 'static
{
api_impl: T, api_impl: T,
marker: PhantomData<RC>, marker: PhantomData<C>,
} }
impl<T, RC> Service<T, RC> impl<T, C> Service<T, C> where
where T: Api<C> + Clone + Send + 'static,
T: Api<RC> + Clone + Send + 'static, C: Has<XSpanIdString> + Send + Sync + 'static
RC: Has<XSpanIdString> + 'static { {
pub fn new(api_impl: T) -> Self { pub fn new(api_impl: T) -> Self {
Service { Service {
api_impl: api_impl, api_impl: api_impl,
@ -124,36 +126,48 @@ where
} }
} }
impl<T, C> hyper::service::Service for Service<T, C> impl<T, C> Clone for Service<T, C> where
where
T: Api<C> + Clone + Send + 'static, T: Api<C> + Clone + Send + 'static,
C: Has<XSpanIdString> + 'static + Send C: Has<XSpanIdString> + Send + Sync + 'static
{ {
type ReqBody = ContextualPayload<Body, C>; fn clone(&self) -> Self {
type ResBody = Body; Service {
type Error = Error; api_impl: self.api_impl.clone(),
marker: self.marker.clone(),
}
}
}
impl<T, C> hyper::service::Service<(Request<Body>, C)> for Service<T, C> where
T: Api<C> + Clone + Send + Sync + 'static,
C: Has<XSpanIdString> + Send + Sync + 'static
{
type Response = Response<Body>;
type Error = crate::ServiceError;
type Future = ServiceFuture; type Future = ServiceFuture;
fn call(&mut self, req: Request<Self::ReqBody>) -> Self::Future { fn poll_ready(&mut self, cx: &mut Context) -> Poll<Result<(), Self::Error>> {
let api_impl = self.api_impl.clone(); self.api_impl.poll_ready(cx)
let (parts, body) = req.into_parts(); }
fn call(&mut self, req: (Request<Body>, C)) -> Self::Future { async fn run<T, C>(mut api_impl: T, req: (Request<Body>, C)) -> Result<Response<Body>, crate::ServiceError> where
T: Api<C> + Clone + Send + 'static,
C: Has<XSpanIdString> + Send + Sync + 'static
{
let (request, context) = req;
let (parts, body) = request.into_parts();
let (method, uri, headers) = (parts.method, parts.uri, parts.headers); let (method, uri, headers) = (parts.method, parts.uri, parts.headers);
let path = paths::GLOBAL_REGEX_SET.matches(uri.path()); let path = paths::GLOBAL_REGEX_SET.matches(uri.path());
let mut context = body.context;
let body = body.inner;
match &method { match &method {
// AllOfGet - GET /allOf // AllOfGet - GET /allOf
&hyper::Method::GET if path.matched(paths::ID_ALLOF) => { &hyper::Method::GET if path.matched(paths::ID_ALLOF) => {
Box::new({ let result = api_impl.all_of_get(
{{
Box::new(
api_impl.all_of_get(
&context &context
).then(move |result| { ).await;
let mut response = Response::new(Body::empty()); let mut response = Response::new(Body::empty());
response.headers_mut().insert( response.headers_mut().insert(
HeaderName::from_static("x-span-id"), HeaderName::from_static("x-span-id"),
HeaderValue::from_str((&context as &dyn Has<XSpanIdString>).get().0.clone().to_string().as_str()) HeaderValue::from_str((&context as &dyn Has<XSpanIdString>).get().0.clone().to_string().as_str())
.expect("Unable to create X-Span-ID header value")); .expect("Unable to create X-Span-ID header value"));
@ -180,23 +194,16 @@ where
}, },
} }
future::ok(response) Ok(response)
}
))
}}
}) as Self::Future
}, },
// DummyGet - GET /dummy // DummyGet - GET /dummy
&hyper::Method::GET if path.matched(paths::ID_DUMMY) => { &hyper::Method::GET if path.matched(paths::ID_DUMMY) => {
Box::new({ let result = api_impl.dummy_get(
{{
Box::new(
api_impl.dummy_get(
&context &context
).then(move |result| { ).await;
let mut response = Response::new(Body::empty()); let mut response = Response::new(Body::empty());
response.headers_mut().insert( response.headers_mut().insert(
HeaderName::from_static("x-span-id"), HeaderName::from_static("x-span-id"),
HeaderValue::from_str((&context as &dyn Has<XSpanIdString>).get().0.clone().to_string().as_str()) HeaderValue::from_str((&context as &dyn Has<XSpanIdString>).get().0.clone().to_string().as_str())
.expect("Unable to create X-Span-ID header value")); .expect("Unable to create X-Span-ID header value"));
@ -216,11 +223,7 @@ where
}, },
} }
future::ok(response) Ok(response)
}
))
}}
}) as Self::Future
}, },
// DummyPut - PUT /dummy // DummyPut - PUT /dummy
@ -228,9 +231,8 @@ where
// Body parameters (note that non-required body parameters will ignore garbage // Body parameters (note that non-required body parameters will ignore garbage
// values, rather than causing a 400 response). Produce warning header and logs for // values, rather than causing a 400 response). Produce warning header and logs for
// any unused fields. // any unused fields.
Box::new(body.concat2() let result = body.to_raw().await;
.then(move |result| -> Self::Future { match result {
match result {
Ok(body) => { Ok(body) => {
let mut unused_elements = Vec::new(); let mut unused_elements = Vec::new();
let param_nested_response: Option<models::InlineObject> = if !body.is_empty() { let param_nested_response: Option<models::InlineObject> = if !body.is_empty() {
@ -240,29 +242,28 @@ where
unused_elements.push(path.to_string()); unused_elements.push(path.to_string());
}) { }) {
Ok(param_nested_response) => param_nested_response, Ok(param_nested_response) => param_nested_response,
Err(e) => return Box::new(future::ok(Response::builder() Err(e) => return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST) .status(StatusCode::BAD_REQUEST)
.body(Body::from(format!("Couldn't parse body parameter nested_response - doesn't match schema: {}", e))) .body(Body::from(format!("Couldn't parse body parameter nested_response - doesn't match schema: {}", e)))
.expect("Unable to create Bad Request response for invalid body parameter nested_response due to schema"))), .expect("Unable to create Bad Request response for invalid body parameter nested_response due to schema")),
} }
} else { } else {
None None
}; };
let param_nested_response = match param_nested_response { let param_nested_response = match param_nested_response {
Some(param_nested_response) => param_nested_response, Some(param_nested_response) => param_nested_response,
None => return Box::new(future::ok(Response::builder() None => return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST) .status(StatusCode::BAD_REQUEST)
.body(Body::from("Missing required body parameter nested_response")) .body(Body::from("Missing required body parameter nested_response"))
.expect("Unable to create Bad Request response for missing body parameter nested_response"))), .expect("Unable to create Bad Request response for missing body parameter nested_response")),
}; };
Box::new( let result = api_impl.dummy_put(
api_impl.dummy_put(
param_nested_response, param_nested_response,
&context &context
).then(move |result| { ).await;
let mut response = Response::new(Body::empty()); let mut response = Response::new(Body::empty());
response.headers_mut().insert( response.headers_mut().insert(
HeaderName::from_static("x-span-id"), HeaderName::from_static("x-span-id"),
HeaderValue::from_str((&context as &dyn Has<XSpanIdString>).get().0.clone().to_string().as_str()) HeaderValue::from_str((&context as &dyn Has<XSpanIdString>).get().0.clone().to_string().as_str())
.expect("Unable to create X-Span-ID header value")); .expect("Unable to create X-Span-ID header value"));
@ -289,29 +290,22 @@ where
}, },
} }
future::ok(response) Ok(response)
}
))
}, },
Err(e) => Box::new(future::ok(Response::builder() Err(e) => Ok(Response::builder()
.status(StatusCode::BAD_REQUEST) .status(StatusCode::BAD_REQUEST)
.body(Body::from(format!("Couldn't read body parameter nested_response: {}", e))) .body(Body::from(format!("Couldn't read body parameter nested_response: {}", e)))
.expect("Unable to create Bad Request response due to unable to read body parameter nested_response"))), .expect("Unable to create Bad Request response due to unable to read body parameter nested_response")),
} }
})
) as Self::Future
}, },
// FileResponseGet - GET /file_response // FileResponseGet - GET /file_response
&hyper::Method::GET if path.matched(paths::ID_FILE_RESPONSE) => { &hyper::Method::GET if path.matched(paths::ID_FILE_RESPONSE) => {
Box::new({ let result = api_impl.file_response_get(
{{
Box::new(
api_impl.file_response_get(
&context &context
).then(move |result| { ).await;
let mut response = Response::new(Body::empty()); let mut response = Response::new(Body::empty());
response.headers_mut().insert( response.headers_mut().insert(
HeaderName::from_static("x-span-id"), HeaderName::from_static("x-span-id"),
HeaderValue::from_str((&context as &dyn Has<XSpanIdString>).get().0.clone().to_string().as_str()) HeaderValue::from_str((&context as &dyn Has<XSpanIdString>).get().0.clone().to_string().as_str())
.expect("Unable to create X-Span-ID header value")); .expect("Unable to create X-Span-ID header value"));
@ -338,23 +332,16 @@ where
}, },
} }
future::ok(response) Ok(response)
}
))
}}
}) as Self::Future
}, },
// GetStructuredYaml - GET /get-structured-yaml // GetStructuredYaml - GET /get-structured-yaml
&hyper::Method::GET if path.matched(paths::ID_GET_STRUCTURED_YAML) => { &hyper::Method::GET if path.matched(paths::ID_GET_STRUCTURED_YAML) => {
Box::new({ let result = api_impl.get_structured_yaml(
{{
Box::new(
api_impl.get_structured_yaml(
&context &context
).then(move |result| { ).await;
let mut response = Response::new(Body::empty()); let mut response = Response::new(Body::empty());
response.headers_mut().insert( response.headers_mut().insert(
HeaderName::from_static("x-span-id"), HeaderName::from_static("x-span-id"),
HeaderValue::from_str((&context as &dyn Has<XSpanIdString>).get().0.clone().to_string().as_str()) HeaderValue::from_str((&context as &dyn Has<XSpanIdString>).get().0.clone().to_string().as_str())
.expect("Unable to create X-Span-ID header value")); .expect("Unable to create X-Span-ID header value"));
@ -381,11 +368,7 @@ where
}, },
} }
future::ok(response) Ok(response)
}
))
}}
}) as Self::Future
}, },
// HtmlPost - POST /html // HtmlPost - POST /html
@ -393,36 +376,34 @@ where
// Body parameters (note that non-required body parameters will ignore garbage // Body parameters (note that non-required body parameters will ignore garbage
// values, rather than causing a 400 response). Produce warning header and logs for // values, rather than causing a 400 response). Produce warning header and logs for
// any unused fields. // any unused fields.
Box::new(body.concat2() let result = body.to_raw().await;
.then(move |result| -> Self::Future { match result {
match result {
Ok(body) => { Ok(body) => {
let param_body: Option<String> = if !body.is_empty() { let param_body: Option<String> = if !body.is_empty() {
match String::from_utf8(body.to_vec()) { match String::from_utf8(body.to_vec()) {
Ok(param_body) => Some(param_body), Ok(param_body) => Some(param_body),
Err(e) => return Box::new(future::ok(Response::builder() Err(e) => return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST) .status(StatusCode::BAD_REQUEST)
.body(Body::from(format!("Couldn't parse body parameter body - not valid UTF-8: {}", e))) .body(Body::from(format!("Couldn't parse body parameter body - not valid UTF-8: {}", e)))
.expect("Unable to create Bad Request response for invalid body parameter body due to UTF-8"))), .expect("Unable to create Bad Request response for invalid body parameter body due to UTF-8")),
} }
} else { } else {
None None
}; };
let param_body = match param_body { let param_body = match param_body {
Some(param_body) => param_body, Some(param_body) => param_body,
None => return Box::new(future::ok(Response::builder() None => return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST) .status(StatusCode::BAD_REQUEST)
.body(Body::from("Missing required body parameter body")) .body(Body::from("Missing required body parameter body"))
.expect("Unable to create Bad Request response for missing body parameter body"))), .expect("Unable to create Bad Request response for missing body parameter body")),
}; };
Box::new( let result = api_impl.html_post(
api_impl.html_post(
param_body, param_body,
&context &context
).then(move |result| { ).await;
let mut response = Response::new(Body::empty()); let mut response = Response::new(Body::empty());
response.headers_mut().insert( response.headers_mut().insert(
HeaderName::from_static("x-span-id"), HeaderName::from_static("x-span-id"),
HeaderValue::from_str((&context as &dyn Has<XSpanIdString>).get().0.clone().to_string().as_str()) HeaderValue::from_str((&context as &dyn Has<XSpanIdString>).get().0.clone().to_string().as_str())
.expect("Unable to create X-Span-ID header value")); .expect("Unable to create X-Span-ID header value"));
@ -449,17 +430,13 @@ where
}, },
} }
future::ok(response) Ok(response)
}
))
}, },
Err(e) => Box::new(future::ok(Response::builder() Err(e) => Ok(Response::builder()
.status(StatusCode::BAD_REQUEST) .status(StatusCode::BAD_REQUEST)
.body(Body::from(format!("Couldn't read body parameter body: {}", e))) .body(Body::from(format!("Couldn't read body parameter body: {}", e)))
.expect("Unable to create Bad Request response due to unable to read body parameter body"))), .expect("Unable to create Bad Request response due to unable to read body parameter body")),
} }
})
) as Self::Future
}, },
// PostYaml - POST /post-yaml // PostYaml - POST /post-yaml
@ -467,36 +444,34 @@ where
// Body parameters (note that non-required body parameters will ignore garbage // Body parameters (note that non-required body parameters will ignore garbage
// values, rather than causing a 400 response). Produce warning header and logs for // values, rather than causing a 400 response). Produce warning header and logs for
// any unused fields. // any unused fields.
Box::new(body.concat2() let result = body.to_raw().await;
.then(move |result| -> Self::Future { match result {
match result {
Ok(body) => { Ok(body) => {
let param_value: Option<String> = if !body.is_empty() { let param_value: Option<String> = if !body.is_empty() {
match String::from_utf8(body.to_vec()) { match String::from_utf8(body.to_vec()) {
Ok(param_value) => Some(param_value), Ok(param_value) => Some(param_value),
Err(e) => return Box::new(future::ok(Response::builder() Err(e) => return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST) .status(StatusCode::BAD_REQUEST)
.body(Body::from(format!("Couldn't parse body parameter value - not valid UTF-8: {}", e))) .body(Body::from(format!("Couldn't parse body parameter value - not valid UTF-8: {}", e)))
.expect("Unable to create Bad Request response for invalid body parameter value due to UTF-8"))), .expect("Unable to create Bad Request response for invalid body parameter value due to UTF-8")),
} }
} else { } else {
None None
}; };
let param_value = match param_value { let param_value = match param_value {
Some(param_value) => param_value, Some(param_value) => param_value,
None => return Box::new(future::ok(Response::builder() None => return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST) .status(StatusCode::BAD_REQUEST)
.body(Body::from("Missing required body parameter value")) .body(Body::from("Missing required body parameter value"))
.expect("Unable to create Bad Request response for missing body parameter value"))), .expect("Unable to create Bad Request response for missing body parameter value")),
}; };
Box::new( let result = api_impl.post_yaml(
api_impl.post_yaml(
param_value, param_value,
&context &context
).then(move |result| { ).await;
let mut response = Response::new(Body::empty()); let mut response = Response::new(Body::empty());
response.headers_mut().insert( response.headers_mut().insert(
HeaderName::from_static("x-span-id"), HeaderName::from_static("x-span-id"),
HeaderValue::from_str((&context as &dyn Has<XSpanIdString>).get().0.clone().to_string().as_str()) HeaderValue::from_str((&context as &dyn Has<XSpanIdString>).get().0.clone().to_string().as_str())
.expect("Unable to create X-Span-ID header value")); .expect("Unable to create X-Span-ID header value"));
@ -516,29 +491,22 @@ where
}, },
} }
future::ok(response) Ok(response)
}
))
}, },
Err(e) => Box::new(future::ok(Response::builder() Err(e) => Ok(Response::builder()
.status(StatusCode::BAD_REQUEST) .status(StatusCode::BAD_REQUEST)
.body(Body::from(format!("Couldn't read body parameter value: {}", e))) .body(Body::from(format!("Couldn't read body parameter value: {}", e)))
.expect("Unable to create Bad Request response due to unable to read body parameter value"))), .expect("Unable to create Bad Request response due to unable to read body parameter value")),
} }
})
) as Self::Future
}, },
// RawJsonGet - GET /raw_json // RawJsonGet - GET /raw_json
&hyper::Method::GET if path.matched(paths::ID_RAW_JSON) => { &hyper::Method::GET if path.matched(paths::ID_RAW_JSON) => {
Box::new({ let result = api_impl.raw_json_get(
{{
Box::new(
api_impl.raw_json_get(
&context &context
).then(move |result| { ).await;
let mut response = Response::new(Body::empty()); let mut response = Response::new(Body::empty());
response.headers_mut().insert( response.headers_mut().insert(
HeaderName::from_static("x-span-id"), HeaderName::from_static("x-span-id"),
HeaderValue::from_str((&context as &dyn Has<XSpanIdString>).get().0.clone().to_string().as_str()) HeaderValue::from_str((&context as &dyn Has<XSpanIdString>).get().0.clone().to_string().as_str())
.expect("Unable to create X-Span-ID header value")); .expect("Unable to create X-Span-ID header value"));
@ -565,11 +533,7 @@ where
}, },
} }
future::ok(response) Ok(response)
}
))
}}
}) as Self::Future
}, },
// SoloObjectPost - POST /solo-object // SoloObjectPost - POST /solo-object
@ -577,9 +541,8 @@ where
// Body parameters (note that non-required body parameters will ignore garbage // Body parameters (note that non-required body parameters will ignore garbage
// values, rather than causing a 400 response). Produce warning header and logs for // values, rather than causing a 400 response). Produce warning header and logs for
// any unused fields. // any unused fields.
Box::new(body.concat2() let result = body.to_raw().await;
.then(move |result| -> Self::Future { match result {
match result {
Ok(body) => { Ok(body) => {
let mut unused_elements = Vec::new(); let mut unused_elements = Vec::new();
let param_value: Option<serde_json::Value> = if !body.is_empty() { let param_value: Option<serde_json::Value> = if !body.is_empty() {
@ -589,29 +552,28 @@ where
unused_elements.push(path.to_string()); unused_elements.push(path.to_string());
}) { }) {
Ok(param_value) => param_value, Ok(param_value) => param_value,
Err(e) => return Box::new(future::ok(Response::builder() Err(e) => return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST) .status(StatusCode::BAD_REQUEST)
.body(Body::from(format!("Couldn't parse body parameter value - doesn't match schema: {}", e))) .body(Body::from(format!("Couldn't parse body parameter value - doesn't match schema: {}", e)))
.expect("Unable to create Bad Request response for invalid body parameter value due to schema"))), .expect("Unable to create Bad Request response for invalid body parameter value due to schema")),
} }
} else { } else {
None None
}; };
let param_value = match param_value { let param_value = match param_value {
Some(param_value) => param_value, Some(param_value) => param_value,
None => return Box::new(future::ok(Response::builder() None => return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST) .status(StatusCode::BAD_REQUEST)
.body(Body::from("Missing required body parameter value")) .body(Body::from("Missing required body parameter value"))
.expect("Unable to create Bad Request response for missing body parameter value"))), .expect("Unable to create Bad Request response for missing body parameter value")),
}; };
Box::new( let result = api_impl.solo_object_post(
api_impl.solo_object_post(
param_value, param_value,
&context &context
).then(move |result| { ).await;
let mut response = Response::new(Body::empty()); let mut response = Response::new(Body::empty());
response.headers_mut().insert( response.headers_mut().insert(
HeaderName::from_static("x-span-id"), HeaderName::from_static("x-span-id"),
HeaderValue::from_str((&context as &dyn Has<XSpanIdString>).get().0.clone().to_string().as_str()) HeaderValue::from_str((&context as &dyn Has<XSpanIdString>).get().0.clone().to_string().as_str())
.expect("Unable to create X-Span-ID header value")); .expect("Unable to create X-Span-ID header value"));
@ -638,17 +600,13 @@ where
}, },
} }
future::ok(response) Ok(response)
}
))
}, },
Err(e) => Box::new(future::ok(Response::builder() Err(e) => Ok(Response::builder()
.status(StatusCode::BAD_REQUEST) .status(StatusCode::BAD_REQUEST)
.body(Body::from(format!("Couldn't read body parameter value: {}", e))) .body(Body::from(format!("Couldn't read body parameter value: {}", e)))
.expect("Unable to create Bad Request response due to unable to read body parameter value"))), .expect("Unable to create Bad Request response due to unable to read body parameter value")),
} }
})
) as Self::Future
}, },
_ if path.matched(paths::ID_ALLOF) => method_not_allowed(), _ if path.matched(paths::ID_ALLOF) => method_not_allowed(),
@ -659,23 +617,11 @@ where
_ if path.matched(paths::ID_POST_YAML) => method_not_allowed(), _ if path.matched(paths::ID_POST_YAML) => method_not_allowed(),
_ if path.matched(paths::ID_RAW_JSON) => method_not_allowed(), _ if path.matched(paths::ID_RAW_JSON) => method_not_allowed(),
_ if path.matched(paths::ID_SOLO_OBJECT) => method_not_allowed(), _ if path.matched(paths::ID_SOLO_OBJECT) => method_not_allowed(),
_ => Box::new(future::ok( _ => Ok(Response::builder().status(StatusCode::NOT_FOUND)
Response::builder().status(StatusCode::NOT_FOUND)
.body(Body::empty()) .body(Body::empty())
.expect("Unable to create Not Found response") .expect("Unable to create Not Found response"))
)) as Self::Future
} }
} } Box::pin(run(self.api_impl.clone(), req)) }
}
impl<T, C> Clone for Service<T, C> where T: Clone
{
fn clone(&self) -> Self {
Service {
api_impl: self.api_impl.clone(),
marker: self.marker.clone(),
}
}
} }
/// Request parser for `Api`. /// Request parser for `Api`.