forked from loafle/openapi-generator-original
[Rust Sever] Upgrade to openssl 0.10 (#5564)
* [Rust Server] Upgrade to openssl 0.10 Use hyper-openssl/openssl instead of hyper-tls/native-tls/openssl on Linux * Update samples
This commit is contained in:
parent
16646b39c1
commit
4aefc9ba33
@ -23,9 +23,9 @@ client = [
|
||||
"serde_urlencoded",
|
||||
{{/usesUrlEncodedForm}}
|
||||
{{#hasCallbacks}}
|
||||
"tokio-tls", "regex", "percent-encoding", "lazy_static",
|
||||
"regex", "percent-encoding", "lazy_static",
|
||||
{{/hasCallbacks}}
|
||||
"serde_json", "serde_ignored", "hyper", "hyper-tls", "native-tls", "openssl", "tokio", "url"
|
||||
"serde_json", "serde_ignored", "hyper", "hyper-openssl", "native-tls", "openssl", "tokio", "url"
|
||||
]
|
||||
server = [
|
||||
{{#apiUsesMultipart}}
|
||||
@ -37,16 +37,26 @@ server = [
|
||||
{{#apiUsesMultipartRelated}}
|
||||
"hyper_0_10", "mime_multipart",
|
||||
{{/apiUsesMultipartRelated}}
|
||||
{{#hasCallbacks}}
|
||||
"native-tls", "hyper-openssl", "openssl",
|
||||
{{/hasCallbacks}}
|
||||
{{! Anything added to the list below, should probably be added to the callbacks list above }}
|
||||
"serde_json", "serde_ignored", "hyper", "native-tls", "openssl", "tokio", "tokio-tls", "regex", "percent-encoding", "url", "lazy_static"
|
||||
"serde_json", "serde_ignored", "hyper", "tokio", "regex", "percent-encoding", "url", "lazy_static"
|
||||
]
|
||||
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]
|
||||
native-tls = { version = "0.2", optional = true }
|
||||
|
||||
[target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dependencies]
|
||||
hyper-openssl = { version = "0.7.1", optional = true }
|
||||
openssl = {version = "0.10", optional = true }
|
||||
|
||||
[dependencies]
|
||||
# Common
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
futures = "0.1"
|
||||
swagger = "3.0"
|
||||
swagger = "4.0"
|
||||
log = "0.3.0"
|
||||
mime = "0.3"
|
||||
|
||||
@ -71,13 +81,10 @@ uuid = {version = "0.7", features = ["serde", "v4"]}
|
||||
|
||||
# Common between server and client features
|
||||
hyper = {version = "0.12", optional = true}
|
||||
hyper-tls = {version = "0.2.1", optional = true}
|
||||
{{#apiUsesMultipartRelated}}
|
||||
mime_multipart = {version = "0.5", optional = true}
|
||||
hyper_0_10 = {package = "hyper", version = "0.10", default-features = false, optional=true}
|
||||
{{/apiUsesMultipartRelated}}
|
||||
native-tls = {version = "0.1.4", optional = true}
|
||||
openssl = {version = "0.9.14", optional = true}
|
||||
serde_json = {version = "1.0", optional = true}
|
||||
serde_ignored = {version = "0.0.4", optional = true}
|
||||
tokio = {version = "0.1.17", optional = true}
|
||||
@ -92,7 +99,6 @@ serde_urlencoded = {version = "0.5.1", optional = true}
|
||||
lazy_static = { version = "1.4", optional = true }
|
||||
percent-encoding = {version = "1.0.0", optional = true}
|
||||
regex = {version = "0.2", optional = true}
|
||||
tokio-tls = {version = "0.1.3", optional = true}
|
||||
|
||||
# Conversion
|
||||
frunk = { version = "0.3.0", optional = true }
|
||||
@ -109,6 +115,10 @@ env_logger = "0.6"
|
||||
uuid = {version = "0.7", features = ["serde", "v4"]}
|
||||
{{/apiUsesUuid}}
|
||||
|
||||
[target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dev-dependencies]
|
||||
tokio-openssl = "0.3"
|
||||
openssl = "0.10"
|
||||
|
||||
[[example]]
|
||||
name = "client"
|
||||
required-features = ["client"]
|
||||
|
@ -4,7 +4,8 @@ use hyper;
|
||||
use hyper::client::HttpConnector;
|
||||
use hyper::header::{HeaderName, HeaderValue, CONTENT_TYPE};
|
||||
use hyper::{Body, Uri, Response};
|
||||
use hyper_tls::HttpsConnector;
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
use hyper_openssl::HttpsConnector;
|
||||
use serde_json;
|
||||
use std::borrow::Cow;
|
||||
#[allow(unused_imports)]
|
||||
@ -18,9 +19,7 @@ use std::str;
|
||||
use std::str::FromStr;
|
||||
use std::string::ToString;
|
||||
use swagger;
|
||||
use swagger::client::Service;
|
||||
use swagger::connector;
|
||||
use swagger::{ApiError, XSpanIdString, Has, AuthData};
|
||||
use swagger::{ApiError, Connector, client::Service, XSpanIdString, Has, AuthData};
|
||||
use url::form_urlencoded;
|
||||
use url::percent_encoding::{utf8_percent_encode, PATH_SEGMENT_ENCODE_SET, QUERY_ENCODE_SET};
|
||||
{{#apiUsesMultipartFormData}}
|
||||
|
@ -59,8 +59,7 @@ impl Client<hyper::client::ResponseFuture>
|
||||
///
|
||||
/// Intended for use with custom implementations of connect for e.g. protocol logging
|
||||
/// or similar functionality which requires wrapping the transport layer. When wrapping a TCP connection,
|
||||
/// this function should be used in conjunction with
|
||||
/// `swagger::{http_connector, https_connector, https_mutual_connector}`.
|
||||
/// this function should be used in conjunction with `swagger::Connector::builder()`.
|
||||
///
|
||||
/// For ordinary tcp connections, prefer the use of `try_new_http`, `try_new_https`
|
||||
/// and `try_new_https_mutual`, to avoid introducing a dependency on the underlying transport layer.
|
||||
@ -69,18 +68,16 @@ impl Client<hyper::client::ResponseFuture>
|
||||
///
|
||||
/// * `base_path` - base path of the client API, i.e. "www.my-api-implementation.com"
|
||||
/// * `protocol` - Which protocol to use when constructing the request url, e.g. `Some("http")`
|
||||
/// * `connector_fn` - Function which returns an implementation of `hyper::client::Connect`
|
||||
/// * `connector` - Implementation of `hyper::client::Connect` to use for the client
|
||||
pub fn try_new_with_connector<C>(
|
||||
base_path: &str,
|
||||
protocol: Option<&'static str>,
|
||||
connector_fn: Box<dyn Fn() -> C + Send + Sync>,
|
||||
connector: C,
|
||||
) -> Result<Self, ClientInitError> where
|
||||
C: hyper::client::connect::Connect + 'static,
|
||||
C::Transport: 'static,
|
||||
C::Future: 'static,
|
||||
{
|
||||
let connector = connector_fn();
|
||||
|
||||
let client_service = Box::new(hyper::client::Client::builder().build(connector));
|
||||
|
||||
Ok(Client {
|
||||
@ -96,24 +93,42 @@ impl Client<hyper::client::ResponseFuture>
|
||||
pub fn try_new_http(
|
||||
base_path: &str,
|
||||
) -> Result<Self, ClientInitError> {
|
||||
let http_connector = connector::http_connector();
|
||||
let http_connector = Connector::builder().build();
|
||||
|
||||
Self::try_new_with_connector(base_path, Some("http"), http_connector)
|
||||
}
|
||||
|
||||
/// Create a client with a TLS connection to the server.
|
||||
/// Create a client with a TLS connection to the server
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `base_path` - base path of the client API, i.e. "www.my-api-implementation.com"
|
||||
pub fn try_new_https(base_path: &str) -> Result<Self, ClientInitError>
|
||||
{
|
||||
let https_connector = Connector::builder()
|
||||
.https()
|
||||
.build()
|
||||
.map_err(|e| ClientInitError::SslError(e))?;
|
||||
Self::try_new_with_connector(base_path, Some("https"), https_connector)
|
||||
}
|
||||
|
||||
/// Create a client with a TLS connection to the server using a pinned certificate
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `base_path` - base path of the client API, i.e. "www.my-api-implementation.com"
|
||||
/// * `ca_certificate` - Path to CA certificate used to authenticate the server
|
||||
pub fn try_new_https<CA>(
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
pub fn try_new_https_pinned<CA>(
|
||||
base_path: &str,
|
||||
ca_certificate: CA,
|
||||
) -> Result<Self, ClientInitError>
|
||||
where
|
||||
CA: AsRef<Path>,
|
||||
{
|
||||
let https_connector = connector::https_connector(ca_certificate);
|
||||
let https_connector = Connector::builder()
|
||||
.https()
|
||||
.pin_server_certificate(ca_certificate)
|
||||
.build()
|
||||
.map_err(|e| ClientInitError::SslError(e))?;
|
||||
Self::try_new_with_connector(base_path, Some("https"), https_connector)
|
||||
}
|
||||
|
||||
@ -124,6 +139,7 @@ impl Client<hyper::client::ResponseFuture>
|
||||
/// * `ca_certificate` - Path to CA certificate used to authenticate the server
|
||||
/// * `client_key` - Path to the client private key
|
||||
/// * `client_certificate` - Path to the client's public certificate associated with the private key
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
pub fn try_new_https_mutual<CA, K, D>(
|
||||
base_path: &str,
|
||||
ca_certificate: CA,
|
||||
@ -135,8 +151,12 @@ impl Client<hyper::client::ResponseFuture>
|
||||
K: AsRef<Path>,
|
||||
D: AsRef<Path>,
|
||||
{
|
||||
let https_connector =
|
||||
connector::https_mutual_connector(ca_certificate, client_key, client_certificate);
|
||||
let https_connector = Connector::builder()
|
||||
.https()
|
||||
.pin_server_certificate(ca_certificate)
|
||||
.client_authentication(client_key, client_certificate)
|
||||
.build()
|
||||
.map_err(|e| ClientInitError::SslError(e))?;
|
||||
Self::try_new_with_connector(base_path, Some("https"), https_connector)
|
||||
}
|
||||
}
|
||||
@ -170,7 +190,12 @@ pub enum ClientInitError {
|
||||
MissingHost,
|
||||
|
||||
/// SSL Connection Error
|
||||
SslError(openssl::error::ErrorStack)
|
||||
#[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))]
|
||||
SslError(native_tls::Error),
|
||||
|
||||
/// SSL Connection Error
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
SslError(openssl::error::ErrorStack),
|
||||
}
|
||||
|
||||
impl From<hyper::http::uri::InvalidUri> for ClientInitError {
|
||||
@ -179,12 +204,6 @@ impl From<hyper::http::uri::InvalidUri> for ClientInitError {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<openssl::error::ErrorStack> for ClientInitError {
|
||||
fn from(err: openssl::error::ErrorStack) -> ClientInitError {
|
||||
ClientInitError::SslError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ClientInitError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let s: &dyn fmt::Debug = self;
|
||||
|
@ -13,13 +13,15 @@ extern crate chrono;
|
||||
#[macro_use]
|
||||
extern crate error_chain;
|
||||
extern crate hyper;
|
||||
extern crate openssl;
|
||||
extern crate native_tls;
|
||||
extern crate tokio_tls;
|
||||
{{#apiUsesUuid}}
|
||||
extern crate uuid;
|
||||
{{/apiUsesUuid}}
|
||||
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
extern crate openssl;
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
extern crate tokio_openssl;
|
||||
|
||||
mod server;
|
||||
{{/hasCallbacks}}
|
||||
|
||||
@ -79,9 +81,7 @@ fn main() {
|
||||
|
||||
let client = if matches.is_present("https") {
|
||||
// Using Simple HTTPS
|
||||
Client::try_new_https(
|
||||
&base_url,
|
||||
"examples/ca.pem")
|
||||
Client::try_new_https(&base_url)
|
||||
.expect("Failed to create HTTPS client")
|
||||
} else {
|
||||
// Using HTTP
|
||||
@ -99,7 +99,7 @@ fn main() {
|
||||
{{#hasCallbacks}}
|
||||
|
||||
// We could do HTTPS here, but for simplicity we don't
|
||||
rt.spawn(server::create("127.0.0.1:8081", None));
|
||||
rt.spawn(server::create("127.0.0.1:8081", false));
|
||||
{{/hasCallbacks}}
|
||||
|
||||
match matches.value_of("operation") {
|
||||
|
@ -12,7 +12,6 @@ use chrono;
|
||||
use futures::{future, Future, Stream};
|
||||
use hyper::server::conn::Http;
|
||||
use hyper::service::MakeService as _;
|
||||
use native_tls;
|
||||
use openssl::ssl::SslAcceptorBuilder;
|
||||
use std::collections::HashMap;
|
||||
use std::marker::PhantomData;
|
||||
@ -23,12 +22,18 @@ use swagger::{Has, XSpanIdString};
|
||||
use swagger::auth::MakeAllowAllAuthenticator;
|
||||
use swagger::EmptyContext;
|
||||
use tokio::net::TcpListener;
|
||||
use tokio_tls::TlsAcceptorExt;
|
||||
{{#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")))]
|
||||
use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod};
|
||||
|
||||
use {{{externCrateName}}}::models;
|
||||
|
||||
pub fn create(addr: &str, https: Option<SslAcceptorBuilder>) -> Box<Future<Item = (), Error = ()> + Send> {
|
||||
#[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
|
||||
pub fn create(addr: &str, https: bool) -> Box<dyn Future<Item = (), Error = ()> + Send> {
|
||||
let addr = addr.parse().expect("Failed to parse bind address");
|
||||
|
||||
let server = Server::new();
|
||||
@ -42,30 +47,44 @@ pub fn create(addr: &str, https: Option<SslAcceptorBuilder>) -> Box<Future<Item
|
||||
service_fn
|
||||
);
|
||||
|
||||
if let Some(ssl) = https {
|
||||
let builder: native_tls::TlsAcceptorBuilder = native_tls::backend::openssl::TlsAcceptorBuilderExt::from_openssl(ssl);
|
||||
let tls_acceptor = builder.build().expect("Failed to build TLS acceptor");
|
||||
let service_fn = Arc::new(Mutex::new(service_fn));
|
||||
let tls_listener = TcpListener::bind(&addr).unwrap().incoming().for_each(move |tcp| {
|
||||
let addr = tcp.peer_addr().expect("Unable to get remote address");
|
||||
if https {
|
||||
#[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))]
|
||||
{
|
||||
unimplemented!("SSL is not implemented for the examples on MacOS, Windows or iOS");
|
||||
}
|
||||
|
||||
let service_fn = service_fn.clone();
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
{
|
||||
let mut ssl = SslAcceptor::mozilla_intermediate_v5(SslMethod::tls()).expect("Failed to create SSL Acceptor");
|
||||
|
||||
hyper::rt::spawn(tls_acceptor.accept_async(tcp).map_err(|_| ()).and_then(move |tls| {
|
||||
let ms = {
|
||||
let mut service_fn = service_fn.lock().unwrap();
|
||||
service_fn.make_service(&addr)
|
||||
};
|
||||
// Server authentication
|
||||
ssl.set_private_key_file("examples/server-key.pem", SslFiletype::PEM).expect("Failed to set private key");
|
||||
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");
|
||||
|
||||
ms.and_then(move |service| {
|
||||
Http::new().serve_connection(tls, service)
|
||||
}).map_err(|_| ())
|
||||
}));
|
||||
let tls_acceptor = ssl.build();
|
||||
let service_fn = Arc::new(Mutex::new(service_fn));
|
||||
let tls_listener = TcpListener::bind(&addr).unwrap().incoming().for_each(move |tcp| {
|
||||
let addr = tcp.peer_addr().expect("Unable to get remote address");
|
||||
|
||||
Ok(())
|
||||
}).map_err(|_| ());
|
||||
let service_fn = service_fn.clone();
|
||||
|
||||
Box::new(tls_listener)
|
||||
hyper::rt::spawn(tls_acceptor.accept_async(tcp).map_err(|_| ()).and_then(move |tls| {
|
||||
let ms = {
|
||||
let mut service_fn = service_fn.lock().unwrap();
|
||||
service_fn.make_service(&addr)
|
||||
};
|
||||
|
||||
ms.and_then(move |service| {
|
||||
Http::new().serve_connection(tls, service)
|
||||
}).map_err(|_| ())
|
||||
}));
|
||||
|
||||
Ok(())
|
||||
}).map_err(|_| ());
|
||||
|
||||
Box::new(tls_listener)
|
||||
}
|
||||
} else {
|
||||
// Using HTTP
|
||||
Box::new(hyper::server::Server::bind(&addr).serve(service_fn).map_err(|e| panic!("{:?}", e)))
|
||||
|
@ -10,7 +10,6 @@ extern crate env_logger;
|
||||
extern crate hyper;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate openssl;
|
||||
extern crate swagger;
|
||||
|
||||
// Imports required by server library.
|
||||
@ -19,32 +18,21 @@ extern crate chrono;
|
||||
#[macro_use]
|
||||
extern crate error_chain;
|
||||
extern crate futures;
|
||||
extern crate native_tls;
|
||||
// extern crate swagger;
|
||||
extern crate tokio;
|
||||
extern crate tokio_tls;
|
||||
{{#apiUsesUuid}}
|
||||
extern crate uuid;
|
||||
{{/apiUsesUuid}}
|
||||
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
extern crate openssl;
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
extern crate tokio_openssl;
|
||||
|
||||
use clap::{App, Arg};
|
||||
use openssl::x509::X509_FILETYPE_PEM;
|
||||
use openssl::ssl::{SslAcceptorBuilder, SslMethod};
|
||||
use openssl::error::ErrorStack;
|
||||
|
||||
mod server;
|
||||
|
||||
// Builds an SSL implementation for Simple HTTPS from some hard-coded file names
|
||||
fn ssl() -> Result<SslAcceptorBuilder, ErrorStack> {
|
||||
let mut ssl = SslAcceptorBuilder::mozilla_intermediate_raw(SslMethod::tls())?;
|
||||
|
||||
// Server authentication
|
||||
ssl.set_private_key_file("examples/server-key.pem", X509_FILETYPE_PEM)?;
|
||||
ssl.set_certificate_chain_file("examples/server-chain.pem")?;
|
||||
ssl.check_private_key()?;
|
||||
|
||||
Ok(ssl)
|
||||
}
|
||||
|
||||
/// Create custom server, wire it to the autogenerated router,
|
||||
/// and pass it to the web server.
|
||||
@ -59,11 +47,5 @@ fn main() {
|
||||
|
||||
let addr = "127.0.0.1:{{{serverPort}}}";
|
||||
|
||||
let https = if matches.is_present("https") {
|
||||
Some(ssl().expect("Failed to load SSL keys"))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
hyper::rt::run(server::create(addr, https));
|
||||
hyper::rt::run(server::create(addr, matches.is_present("https")));
|
||||
}
|
||||
|
@ -41,10 +41,14 @@ extern crate swagger;
|
||||
|
||||
#[cfg(any(feature = "client", feature = "server"))]
|
||||
extern crate hyper;
|
||||
#[cfg(feature = "client")]
|
||||
extern crate hyper_tls;
|
||||
{{#hasCallbacks}}
|
||||
#[cfg(any(feature = "client", feature = "server"))]
|
||||
extern crate openssl;
|
||||
{{/hasCallbacks}}
|
||||
{{^hasCallbacks}}
|
||||
#[cfg(feature = "server")]
|
||||
{{/hasCallbacks}}
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
extern crate hyper_openssl;
|
||||
{{#apiUsesMultipart}}
|
||||
#[cfg(any(feature = "client", feature = "server"))]
|
||||
extern crate mime_0_2;
|
||||
@ -53,8 +57,6 @@ extern crate mime_0_2;
|
||||
#[cfg(any(feature = "client", feature = "server"))]
|
||||
extern crate mime_multipart;
|
||||
{{/apiUsesMultipartRelated}}
|
||||
#[cfg(any(feature = "client", feature = "server"))]
|
||||
extern crate native_tls;
|
||||
{{#hasCallbacks}}
|
||||
#[cfg(any(feature = "client", feature = "server"))]
|
||||
{{/hasCallbacks}}
|
||||
|
@ -45,24 +45,21 @@ impl Client<hyper::client::ResponseFuture>
|
||||
///
|
||||
/// Intended for use with custom implementations of connect for e.g. protocol logging
|
||||
/// or similar functionality which requires wrapping the transport layer. When wrapping a TCP connection,
|
||||
/// this function should be used in conjunction with
|
||||
/// `swagger::{http_connector, https_connector, https_mutual_connector}`.
|
||||
/// this function should be used in conjunction with `swagger::Connector::builder()`.
|
||||
///
|
||||
/// For ordinary tcp connections, prefer the use of `new_http`, `new_https`
|
||||
/// and `new_https_mutual`, to avoid introducing a dependency on the underlying transport layer.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `connector_fn` - Function which returns an implementation of `hyper::client::Connect`
|
||||
/// * `connector` - Implementation of `hyper::client::Connect` to use for the client
|
||||
pub fn new_with_connector<C>(
|
||||
connector_fn: Box<dyn Fn() -> C + Send + Sync>,
|
||||
connector: C,
|
||||
) -> Self where
|
||||
C: hyper::client::connect::Connect + 'static,
|
||||
C::Transport: 'static,
|
||||
C::Future: 'static,
|
||||
{
|
||||
let connector = connector_fn();
|
||||
|
||||
let client_service = Box::new(hyper::client::Client::builder().build(connector));
|
||||
|
||||
Client {
|
||||
@ -72,21 +69,41 @@ impl Client<hyper::client::ResponseFuture>
|
||||
|
||||
/// Create an HTTP client.
|
||||
pub fn new_http() -> Self {
|
||||
let http_connector = connector::http_connector();
|
||||
let http_connector = Connector::builder().build();
|
||||
Self::new_with_connector(http_connector)
|
||||
}
|
||||
|
||||
/// Create a client with a TLS connection to the server.
|
||||
#[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))]
|
||||
pub fn new_https() -> Result<Self, native_tls::Error>
|
||||
{
|
||||
let https_connector = Connector::builder().https().build()?;
|
||||
Ok(Self::new_with_connector(https_connector))
|
||||
}
|
||||
|
||||
/// Create a client with a TLS connection to the server.
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
pub fn new_https() -> Result<Self, openssl::error::ErrorStack>
|
||||
{
|
||||
let https_connector = Connector::builder().https().build()?;
|
||||
Ok(Self::new_with_connector(https_connector))
|
||||
}
|
||||
|
||||
/// Create a client with a TLS connection to the server, pinning the certificate
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `ca_certificate` - Path to CA certificate used to authenticate the server
|
||||
pub fn new_https<CA>(
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
pub fn new_https_pinned<CA>(
|
||||
ca_certificate: CA,
|
||||
) -> Self where
|
||||
) -> Result<Self, openssl::error::ErrorStack> where
|
||||
CA: AsRef<Path>,
|
||||
{
|
||||
let https_connector = connector::https_connector(ca_certificate);
|
||||
Self::new_with_connector(https_connector)
|
||||
let https_connector = Connector::builder()
|
||||
.https()
|
||||
.pin_server_certificate(ca_certificate)
|
||||
.build()?;
|
||||
Ok(Self::new_with_connector(https_connector))
|
||||
}
|
||||
|
||||
/// Create a client with a mutually authenticated TLS connection to the server.
|
||||
@ -95,19 +112,23 @@ impl Client<hyper::client::ResponseFuture>
|
||||
/// * `ca_certificate` - Path to CA certificate used to authenticate the server
|
||||
/// * `client_key` - Path to the client private key
|
||||
/// * `client_certificate` - Path to the client's public certificate associated with the private key
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
pub fn new_https_mutual<CA, K, D>(
|
||||
ca_certificate: CA,
|
||||
client_key: K,
|
||||
client_certificate: D,
|
||||
) -> Self
|
||||
) -> Result<Self, openssl::error::ErrorStack>
|
||||
where
|
||||
CA: AsRef<Path>,
|
||||
K: AsRef<Path>,
|
||||
D: AsRef<Path>,
|
||||
{
|
||||
let https_connector =
|
||||
connector::https_mutual_connector(ca_certificate, client_key, client_certificate);
|
||||
Self::new_with_connector(https_connector)
|
||||
let https_connector = Connector::builder()
|
||||
.https()
|
||||
.pin_server_certificate(ca_certificate)
|
||||
.client_authentication(client_key, client_certificate)
|
||||
.build()?;
|
||||
Ok(Self::new_with_connector(https_connector))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,21 +11,28 @@ client = [
|
||||
"mime_0_2",
|
||||
"multipart", "multipart/client", "swagger/multipart",
|
||||
"hyper_0_10", "mime_multipart",
|
||||
"serde_json", "serde_ignored", "hyper", "hyper-tls", "native-tls", "openssl", "tokio", "url"
|
||||
"serde_json", "serde_ignored", "hyper", "hyper-openssl", "native-tls", "openssl", "tokio", "url"
|
||||
]
|
||||
server = [
|
||||
"mime_0_2",
|
||||
"multipart", "multipart/server",
|
||||
"hyper_0_10", "mime_multipart",
|
||||
"serde_json", "serde_ignored", "hyper", "native-tls", "openssl", "tokio", "tokio-tls", "regex", "percent-encoding", "url", "lazy_static"
|
||||
"serde_json", "serde_ignored", "hyper", "tokio", "regex", "percent-encoding", "url", "lazy_static"
|
||||
]
|
||||
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]
|
||||
native-tls = { version = "0.2", optional = true }
|
||||
|
||||
[target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dependencies]
|
||||
hyper-openssl = { version = "0.7.1", optional = true }
|
||||
openssl = {version = "0.10", optional = true }
|
||||
|
||||
[dependencies]
|
||||
# Common
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
futures = "0.1"
|
||||
swagger = "3.0"
|
||||
swagger = "4.0"
|
||||
log = "0.3.0"
|
||||
mime = "0.3"
|
||||
|
||||
@ -38,11 +45,8 @@ multipart = { version = "0.16", default-features = false, optional = true }
|
||||
|
||||
# Common between server and client features
|
||||
hyper = {version = "0.12", optional = true}
|
||||
hyper-tls = {version = "0.2.1", optional = true}
|
||||
mime_multipart = {version = "0.5", optional = true}
|
||||
hyper_0_10 = {package = "hyper", version = "0.10", default-features = false, optional=true}
|
||||
native-tls = {version = "0.1.4", optional = true}
|
||||
openssl = {version = "0.9.14", optional = true}
|
||||
serde_json = {version = "1.0", optional = true}
|
||||
serde_ignored = {version = "0.0.4", optional = true}
|
||||
tokio = {version = "0.1.17", optional = true}
|
||||
@ -54,7 +58,6 @@ url = {version = "1.5", optional = true}
|
||||
lazy_static = { version = "1.4", optional = true }
|
||||
percent-encoding = {version = "1.0.0", optional = true}
|
||||
regex = {version = "0.2", optional = true}
|
||||
tokio-tls = {version = "0.1.3", optional = true}
|
||||
|
||||
# Conversion
|
||||
frunk = { version = "0.3.0", optional = true }
|
||||
@ -69,6 +72,10 @@ error-chain = "0.12"
|
||||
env_logger = "0.6"
|
||||
uuid = {version = "0.7", features = ["serde", "v4"]}
|
||||
|
||||
[target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dev-dependencies]
|
||||
tokio-openssl = "0.3"
|
||||
openssl = "0.10"
|
||||
|
||||
[[example]]
|
||||
name = "client"
|
||||
required-features = ["client"]
|
||||
|
@ -55,9 +55,7 @@ fn main() {
|
||||
|
||||
let client = if matches.is_present("https") {
|
||||
// Using Simple HTTPS
|
||||
Client::try_new_https(
|
||||
&base_url,
|
||||
"examples/ca.pem")
|
||||
Client::try_new_https(&base_url)
|
||||
.expect("Failed to create HTTPS client")
|
||||
} else {
|
||||
// Using HTTP
|
||||
|
@ -10,7 +10,6 @@ extern crate env_logger;
|
||||
extern crate hyper;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate openssl;
|
||||
extern crate swagger;
|
||||
|
||||
// Imports required by server library.
|
||||
@ -19,29 +18,18 @@ extern crate chrono;
|
||||
#[macro_use]
|
||||
extern crate error_chain;
|
||||
extern crate futures;
|
||||
extern crate native_tls;
|
||||
// extern crate swagger;
|
||||
extern crate tokio;
|
||||
extern crate tokio_tls;
|
||||
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
extern crate openssl;
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
extern crate tokio_openssl;
|
||||
|
||||
use clap::{App, Arg};
|
||||
use openssl::x509::X509_FILETYPE_PEM;
|
||||
use openssl::ssl::{SslAcceptorBuilder, SslMethod};
|
||||
use openssl::error::ErrorStack;
|
||||
|
||||
mod server;
|
||||
|
||||
// Builds an SSL implementation for Simple HTTPS from some hard-coded file names
|
||||
fn ssl() -> Result<SslAcceptorBuilder, ErrorStack> {
|
||||
let mut ssl = SslAcceptorBuilder::mozilla_intermediate_raw(SslMethod::tls())?;
|
||||
|
||||
// Server authentication
|
||||
ssl.set_private_key_file("examples/server-key.pem", X509_FILETYPE_PEM)?;
|
||||
ssl.set_certificate_chain_file("examples/server-chain.pem")?;
|
||||
ssl.check_private_key()?;
|
||||
|
||||
Ok(ssl)
|
||||
}
|
||||
|
||||
/// Create custom server, wire it to the autogenerated router,
|
||||
/// and pass it to the web server.
|
||||
@ -56,11 +44,5 @@ fn main() {
|
||||
|
||||
let addr = "127.0.0.1:80";
|
||||
|
||||
let https = if matches.is_present("https") {
|
||||
Some(ssl().expect("Failed to load SSL keys"))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
hyper::rt::run(server::create(addr, https));
|
||||
hyper::rt::run(server::create(addr, matches.is_present("https")));
|
||||
}
|
||||
|
@ -12,7 +12,6 @@ use chrono;
|
||||
use futures::{future, Future, Stream};
|
||||
use hyper::server::conn::Http;
|
||||
use hyper::service::MakeService as _;
|
||||
use native_tls;
|
||||
use openssl::ssl::SslAcceptorBuilder;
|
||||
use std::collections::HashMap;
|
||||
use std::marker::PhantomData;
|
||||
@ -23,12 +22,18 @@ use swagger::{Has, XSpanIdString};
|
||||
use swagger::auth::MakeAllowAllAuthenticator;
|
||||
use swagger::EmptyContext;
|
||||
use tokio::net::TcpListener;
|
||||
use tokio_tls::TlsAcceptorExt;
|
||||
|
||||
|
||||
#[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")))]
|
||||
use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod};
|
||||
|
||||
use multipart_v3::models;
|
||||
|
||||
pub fn create(addr: &str, https: Option<SslAcceptorBuilder>) -> Box<Future<Item = (), Error = ()> + Send> {
|
||||
#[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
|
||||
pub fn create(addr: &str, https: bool) -> Box<dyn Future<Item = (), Error = ()> + Send> {
|
||||
let addr = addr.parse().expect("Failed to parse bind address");
|
||||
|
||||
let server = Server::new();
|
||||
@ -42,30 +47,44 @@ pub fn create(addr: &str, https: Option<SslAcceptorBuilder>) -> Box<Future<Item
|
||||
service_fn
|
||||
);
|
||||
|
||||
if let Some(ssl) = https {
|
||||
let builder: native_tls::TlsAcceptorBuilder = native_tls::backend::openssl::TlsAcceptorBuilderExt::from_openssl(ssl);
|
||||
let tls_acceptor = builder.build().expect("Failed to build TLS acceptor");
|
||||
let service_fn = Arc::new(Mutex::new(service_fn));
|
||||
let tls_listener = TcpListener::bind(&addr).unwrap().incoming().for_each(move |tcp| {
|
||||
let addr = tcp.peer_addr().expect("Unable to get remote address");
|
||||
if https {
|
||||
#[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))]
|
||||
{
|
||||
unimplemented!("SSL is not implemented for the examples on MacOS, Windows or iOS");
|
||||
}
|
||||
|
||||
let service_fn = service_fn.clone();
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
{
|
||||
let mut ssl = SslAcceptor::mozilla_intermediate_v5(SslMethod::tls()).expect("Failed to create SSL Acceptor");
|
||||
|
||||
hyper::rt::spawn(tls_acceptor.accept_async(tcp).map_err(|_| ()).and_then(move |tls| {
|
||||
let ms = {
|
||||
let mut service_fn = service_fn.lock().unwrap();
|
||||
service_fn.make_service(&addr)
|
||||
};
|
||||
// Server authentication
|
||||
ssl.set_private_key_file("examples/server-key.pem", SslFiletype::PEM).expect("Failed to set private key");
|
||||
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");
|
||||
|
||||
ms.and_then(move |service| {
|
||||
Http::new().serve_connection(tls, service)
|
||||
}).map_err(|_| ())
|
||||
}));
|
||||
let tls_acceptor = ssl.build();
|
||||
let service_fn = Arc::new(Mutex::new(service_fn));
|
||||
let tls_listener = TcpListener::bind(&addr).unwrap().incoming().for_each(move |tcp| {
|
||||
let addr = tcp.peer_addr().expect("Unable to get remote address");
|
||||
|
||||
Ok(())
|
||||
}).map_err(|_| ());
|
||||
let service_fn = service_fn.clone();
|
||||
|
||||
Box::new(tls_listener)
|
||||
hyper::rt::spawn(tls_acceptor.accept_async(tcp).map_err(|_| ()).and_then(move |tls| {
|
||||
let ms = {
|
||||
let mut service_fn = service_fn.lock().unwrap();
|
||||
service_fn.make_service(&addr)
|
||||
};
|
||||
|
||||
ms.and_then(move |service| {
|
||||
Http::new().serve_connection(tls, service)
|
||||
}).map_err(|_| ())
|
||||
}));
|
||||
|
||||
Ok(())
|
||||
}).map_err(|_| ());
|
||||
|
||||
Box::new(tls_listener)
|
||||
}
|
||||
} else {
|
||||
// Using HTTP
|
||||
Box::new(hyper::server::Server::bind(&addr).serve(service_fn).map_err(|e| panic!("{:?}", e)))
|
||||
|
@ -4,7 +4,8 @@ use hyper;
|
||||
use hyper::client::HttpConnector;
|
||||
use hyper::header::{HeaderName, HeaderValue, CONTENT_TYPE};
|
||||
use hyper::{Body, Uri, Response};
|
||||
use hyper_tls::HttpsConnector;
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
use hyper_openssl::HttpsConnector;
|
||||
use serde_json;
|
||||
use std::borrow::Cow;
|
||||
#[allow(unused_imports)]
|
||||
@ -18,9 +19,7 @@ use std::str;
|
||||
use std::str::FromStr;
|
||||
use std::string::ToString;
|
||||
use swagger;
|
||||
use swagger::client::Service;
|
||||
use swagger::connector;
|
||||
use swagger::{ApiError, XSpanIdString, Has, AuthData};
|
||||
use swagger::{ApiError, Connector, client::Service, XSpanIdString, Has, AuthData};
|
||||
use url::form_urlencoded;
|
||||
use url::percent_encoding::{utf8_percent_encode, PATH_SEGMENT_ENCODE_SET, QUERY_ENCODE_SET};
|
||||
use mime::Mime;
|
||||
@ -99,8 +98,7 @@ impl Client<hyper::client::ResponseFuture>
|
||||
///
|
||||
/// Intended for use with custom implementations of connect for e.g. protocol logging
|
||||
/// or similar functionality which requires wrapping the transport layer. When wrapping a TCP connection,
|
||||
/// this function should be used in conjunction with
|
||||
/// `swagger::{http_connector, https_connector, https_mutual_connector}`.
|
||||
/// this function should be used in conjunction with `swagger::Connector::builder()`.
|
||||
///
|
||||
/// For ordinary tcp connections, prefer the use of `try_new_http`, `try_new_https`
|
||||
/// and `try_new_https_mutual`, to avoid introducing a dependency on the underlying transport layer.
|
||||
@ -109,18 +107,16 @@ impl Client<hyper::client::ResponseFuture>
|
||||
///
|
||||
/// * `base_path` - base path of the client API, i.e. "www.my-api-implementation.com"
|
||||
/// * `protocol` - Which protocol to use when constructing the request url, e.g. `Some("http")`
|
||||
/// * `connector_fn` - Function which returns an implementation of `hyper::client::Connect`
|
||||
/// * `connector` - Implementation of `hyper::client::Connect` to use for the client
|
||||
pub fn try_new_with_connector<C>(
|
||||
base_path: &str,
|
||||
protocol: Option<&'static str>,
|
||||
connector_fn: Box<dyn Fn() -> C + Send + Sync>,
|
||||
connector: C,
|
||||
) -> Result<Self, ClientInitError> where
|
||||
C: hyper::client::connect::Connect + 'static,
|
||||
C::Transport: 'static,
|
||||
C::Future: 'static,
|
||||
{
|
||||
let connector = connector_fn();
|
||||
|
||||
let client_service = Box::new(hyper::client::Client::builder().build(connector));
|
||||
|
||||
Ok(Client {
|
||||
@ -136,24 +132,42 @@ impl Client<hyper::client::ResponseFuture>
|
||||
pub fn try_new_http(
|
||||
base_path: &str,
|
||||
) -> Result<Self, ClientInitError> {
|
||||
let http_connector = connector::http_connector();
|
||||
let http_connector = Connector::builder().build();
|
||||
|
||||
Self::try_new_with_connector(base_path, Some("http"), http_connector)
|
||||
}
|
||||
|
||||
/// Create a client with a TLS connection to the server.
|
||||
/// Create a client with a TLS connection to the server
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `base_path` - base path of the client API, i.e. "www.my-api-implementation.com"
|
||||
pub fn try_new_https(base_path: &str) -> Result<Self, ClientInitError>
|
||||
{
|
||||
let https_connector = Connector::builder()
|
||||
.https()
|
||||
.build()
|
||||
.map_err(|e| ClientInitError::SslError(e))?;
|
||||
Self::try_new_with_connector(base_path, Some("https"), https_connector)
|
||||
}
|
||||
|
||||
/// Create a client with a TLS connection to the server using a pinned certificate
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `base_path` - base path of the client API, i.e. "www.my-api-implementation.com"
|
||||
/// * `ca_certificate` - Path to CA certificate used to authenticate the server
|
||||
pub fn try_new_https<CA>(
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
pub fn try_new_https_pinned<CA>(
|
||||
base_path: &str,
|
||||
ca_certificate: CA,
|
||||
) -> Result<Self, ClientInitError>
|
||||
where
|
||||
CA: AsRef<Path>,
|
||||
{
|
||||
let https_connector = connector::https_connector(ca_certificate);
|
||||
let https_connector = Connector::builder()
|
||||
.https()
|
||||
.pin_server_certificate(ca_certificate)
|
||||
.build()
|
||||
.map_err(|e| ClientInitError::SslError(e))?;
|
||||
Self::try_new_with_connector(base_path, Some("https"), https_connector)
|
||||
}
|
||||
|
||||
@ -164,6 +178,7 @@ impl Client<hyper::client::ResponseFuture>
|
||||
/// * `ca_certificate` - Path to CA certificate used to authenticate the server
|
||||
/// * `client_key` - Path to the client private key
|
||||
/// * `client_certificate` - Path to the client's public certificate associated with the private key
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
pub fn try_new_https_mutual<CA, K, D>(
|
||||
base_path: &str,
|
||||
ca_certificate: CA,
|
||||
@ -175,8 +190,12 @@ impl Client<hyper::client::ResponseFuture>
|
||||
K: AsRef<Path>,
|
||||
D: AsRef<Path>,
|
||||
{
|
||||
let https_connector =
|
||||
connector::https_mutual_connector(ca_certificate, client_key, client_certificate);
|
||||
let https_connector = Connector::builder()
|
||||
.https()
|
||||
.pin_server_certificate(ca_certificate)
|
||||
.client_authentication(client_key, client_certificate)
|
||||
.build()
|
||||
.map_err(|e| ClientInitError::SslError(e))?;
|
||||
Self::try_new_with_connector(base_path, Some("https"), https_connector)
|
||||
}
|
||||
}
|
||||
@ -210,7 +229,12 @@ pub enum ClientInitError {
|
||||
MissingHost,
|
||||
|
||||
/// SSL Connection Error
|
||||
SslError(openssl::error::ErrorStack)
|
||||
#[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))]
|
||||
SslError(native_tls::Error),
|
||||
|
||||
/// SSL Connection Error
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
SslError(openssl::error::ErrorStack),
|
||||
}
|
||||
|
||||
impl From<hyper::http::uri::InvalidUri> for ClientInitError {
|
||||
@ -219,12 +243,6 @@ impl From<hyper::http::uri::InvalidUri> for ClientInitError {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<openssl::error::ErrorStack> for ClientInitError {
|
||||
fn from(err: openssl::error::ErrorStack) -> ClientInitError {
|
||||
ClientInitError::SslError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ClientInitError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let s: &dyn fmt::Debug = self;
|
||||
|
@ -34,16 +34,13 @@ extern crate swagger;
|
||||
|
||||
#[cfg(any(feature = "client", feature = "server"))]
|
||||
extern crate hyper;
|
||||
#[cfg(feature = "client")]
|
||||
extern crate hyper_tls;
|
||||
#[cfg(any(feature = "client", feature = "server"))]
|
||||
extern crate openssl;
|
||||
#[cfg(feature = "server")]
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
extern crate hyper_openssl;
|
||||
#[cfg(any(feature = "client", feature = "server"))]
|
||||
extern crate mime_0_2;
|
||||
#[cfg(any(feature = "client", feature = "server"))]
|
||||
extern crate mime_multipart;
|
||||
#[cfg(any(feature = "client", feature = "server"))]
|
||||
extern crate native_tls;
|
||||
#[cfg(feature = "server")]
|
||||
extern crate percent_encoding;
|
||||
#[cfg(any(feature = "client", feature = "server"))]
|
||||
|
@ -8,19 +8,27 @@ license = "Unlicense"
|
||||
[features]
|
||||
default = ["client", "server"]
|
||||
client = [
|
||||
"tokio-tls", "regex", "percent-encoding", "lazy_static",
|
||||
"serde_json", "serde_ignored", "hyper", "hyper-tls", "native-tls", "openssl", "tokio", "url"
|
||||
"regex", "percent-encoding", "lazy_static",
|
||||
"serde_json", "serde_ignored", "hyper", "hyper-openssl", "native-tls", "openssl", "tokio", "url"
|
||||
]
|
||||
server = [
|
||||
"serde_json", "serde_ignored", "hyper", "native-tls", "openssl", "tokio", "tokio-tls", "regex", "percent-encoding", "url", "lazy_static"
|
||||
"native-tls", "hyper-openssl", "openssl",
|
||||
"serde_json", "serde_ignored", "hyper", "tokio", "regex", "percent-encoding", "url", "lazy_static"
|
||||
]
|
||||
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]
|
||||
native-tls = { version = "0.2", optional = true }
|
||||
|
||||
[target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dependencies]
|
||||
hyper-openssl = { version = "0.7.1", optional = true }
|
||||
openssl = {version = "0.10", optional = true }
|
||||
|
||||
[dependencies]
|
||||
# Common
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
futures = "0.1"
|
||||
swagger = "3.0"
|
||||
swagger = "4.0"
|
||||
log = "0.3.0"
|
||||
mime = "0.3"
|
||||
|
||||
@ -35,9 +43,6 @@ uuid = {version = "0.7", features = ["serde", "v4"]}
|
||||
|
||||
# Common between server and client features
|
||||
hyper = {version = "0.12", optional = true}
|
||||
hyper-tls = {version = "0.2.1", optional = true}
|
||||
native-tls = {version = "0.1.4", optional = true}
|
||||
openssl = {version = "0.9.14", optional = true}
|
||||
serde_json = {version = "1.0", optional = true}
|
||||
serde_ignored = {version = "0.0.4", optional = true}
|
||||
tokio = {version = "0.1.17", optional = true}
|
||||
@ -49,7 +54,6 @@ url = {version = "1.5", optional = true}
|
||||
lazy_static = { version = "1.4", optional = true }
|
||||
percent-encoding = {version = "1.0.0", optional = true}
|
||||
regex = {version = "0.2", optional = true}
|
||||
tokio-tls = {version = "0.1.3", optional = true}
|
||||
|
||||
# Conversion
|
||||
frunk = { version = "0.3.0", optional = true }
|
||||
@ -63,6 +67,10 @@ clap = "2.25"
|
||||
error-chain = "0.12"
|
||||
env_logger = "0.6"
|
||||
|
||||
[target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dev-dependencies]
|
||||
tokio-openssl = "0.3"
|
||||
openssl = "0.10"
|
||||
|
||||
[[example]]
|
||||
name = "client"
|
||||
required-features = ["client"]
|
||||
|
@ -12,11 +12,13 @@ extern crate chrono;
|
||||
#[macro_use]
|
||||
extern crate error_chain;
|
||||
extern crate hyper;
|
||||
extern crate openssl;
|
||||
extern crate native_tls;
|
||||
extern crate tokio_tls;
|
||||
extern crate uuid;
|
||||
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
extern crate openssl;
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
extern crate tokio_openssl;
|
||||
|
||||
mod server;
|
||||
|
||||
#[allow(unused_imports)]
|
||||
@ -98,9 +100,7 @@ fn main() {
|
||||
|
||||
let client = if matches.is_present("https") {
|
||||
// Using Simple HTTPS
|
||||
Client::try_new_https(
|
||||
&base_url,
|
||||
"examples/ca.pem")
|
||||
Client::try_new_https(&base_url)
|
||||
.expect("Failed to create HTTPS client")
|
||||
} else {
|
||||
// Using HTTP
|
||||
@ -117,7 +117,7 @@ fn main() {
|
||||
let mut rt = tokio::runtime::Runtime::new().unwrap();
|
||||
|
||||
// We could do HTTPS here, but for simplicity we don't
|
||||
rt.spawn(server::create("127.0.0.1:8081", None));
|
||||
rt.spawn(server::create("127.0.0.1:8081", false));
|
||||
|
||||
match matches.value_of("operation") {
|
||||
Some("CallbackWithHeaderPost") => {
|
||||
|
@ -12,7 +12,6 @@ use chrono;
|
||||
use futures::{future, Future, Stream};
|
||||
use hyper::server::conn::Http;
|
||||
use hyper::service::MakeService as _;
|
||||
use native_tls;
|
||||
use openssl::ssl::SslAcceptorBuilder;
|
||||
use std::collections::HashMap;
|
||||
use std::marker::PhantomData;
|
||||
@ -23,12 +22,18 @@ use swagger::{Has, XSpanIdString};
|
||||
use swagger::auth::MakeAllowAllAuthenticator;
|
||||
use swagger::EmptyContext;
|
||||
use tokio::net::TcpListener;
|
||||
use tokio_tls::TlsAcceptorExt;
|
||||
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")))]
|
||||
use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod};
|
||||
|
||||
use openapi_v3::models;
|
||||
|
||||
pub fn create(addr: &str, https: Option<SslAcceptorBuilder>) -> Box<Future<Item = (), Error = ()> + Send> {
|
||||
#[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
|
||||
pub fn create(addr: &str, https: bool) -> Box<dyn Future<Item = (), Error = ()> + Send> {
|
||||
let addr = addr.parse().expect("Failed to parse bind address");
|
||||
|
||||
let server = Server::new();
|
||||
@ -42,30 +47,44 @@ pub fn create(addr: &str, https: Option<SslAcceptorBuilder>) -> Box<Future<Item
|
||||
service_fn
|
||||
);
|
||||
|
||||
if let Some(ssl) = https {
|
||||
let builder: native_tls::TlsAcceptorBuilder = native_tls::backend::openssl::TlsAcceptorBuilderExt::from_openssl(ssl);
|
||||
let tls_acceptor = builder.build().expect("Failed to build TLS acceptor");
|
||||
let service_fn = Arc::new(Mutex::new(service_fn));
|
||||
let tls_listener = TcpListener::bind(&addr).unwrap().incoming().for_each(move |tcp| {
|
||||
let addr = tcp.peer_addr().expect("Unable to get remote address");
|
||||
if https {
|
||||
#[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))]
|
||||
{
|
||||
unimplemented!("SSL is not implemented for the examples on MacOS, Windows or iOS");
|
||||
}
|
||||
|
||||
let service_fn = service_fn.clone();
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
{
|
||||
let mut ssl = SslAcceptor::mozilla_intermediate_v5(SslMethod::tls()).expect("Failed to create SSL Acceptor");
|
||||
|
||||
hyper::rt::spawn(tls_acceptor.accept_async(tcp).map_err(|_| ()).and_then(move |tls| {
|
||||
let ms = {
|
||||
let mut service_fn = service_fn.lock().unwrap();
|
||||
service_fn.make_service(&addr)
|
||||
};
|
||||
// Server authentication
|
||||
ssl.set_private_key_file("examples/server-key.pem", SslFiletype::PEM).expect("Failed to set private key");
|
||||
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");
|
||||
|
||||
ms.and_then(move |service| {
|
||||
Http::new().serve_connection(tls, service)
|
||||
}).map_err(|_| ())
|
||||
}));
|
||||
let tls_acceptor = ssl.build();
|
||||
let service_fn = Arc::new(Mutex::new(service_fn));
|
||||
let tls_listener = TcpListener::bind(&addr).unwrap().incoming().for_each(move |tcp| {
|
||||
let addr = tcp.peer_addr().expect("Unable to get remote address");
|
||||
|
||||
Ok(())
|
||||
}).map_err(|_| ());
|
||||
let service_fn = service_fn.clone();
|
||||
|
||||
Box::new(tls_listener)
|
||||
hyper::rt::spawn(tls_acceptor.accept_async(tcp).map_err(|_| ()).and_then(move |tls| {
|
||||
let ms = {
|
||||
let mut service_fn = service_fn.lock().unwrap();
|
||||
service_fn.make_service(&addr)
|
||||
};
|
||||
|
||||
ms.and_then(move |service| {
|
||||
Http::new().serve_connection(tls, service)
|
||||
}).map_err(|_| ())
|
||||
}));
|
||||
|
||||
Ok(())
|
||||
}).map_err(|_| ());
|
||||
|
||||
Box::new(tls_listener)
|
||||
}
|
||||
} else {
|
||||
// Using HTTP
|
||||
Box::new(hyper::server::Server::bind(&addr).serve(service_fn).map_err(|e| panic!("{:?}", e)))
|
||||
|
@ -10,7 +10,6 @@ extern crate env_logger;
|
||||
extern crate hyper;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate openssl;
|
||||
extern crate swagger;
|
||||
|
||||
// Imports required by server library.
|
||||
@ -19,30 +18,19 @@ extern crate chrono;
|
||||
#[macro_use]
|
||||
extern crate error_chain;
|
||||
extern crate futures;
|
||||
extern crate native_tls;
|
||||
// extern crate swagger;
|
||||
extern crate tokio;
|
||||
extern crate tokio_tls;
|
||||
extern crate uuid;
|
||||
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
extern crate openssl;
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
extern crate tokio_openssl;
|
||||
|
||||
use clap::{App, Arg};
|
||||
use openssl::x509::X509_FILETYPE_PEM;
|
||||
use openssl::ssl::{SslAcceptorBuilder, SslMethod};
|
||||
use openssl::error::ErrorStack;
|
||||
|
||||
mod server;
|
||||
|
||||
// Builds an SSL implementation for Simple HTTPS from some hard-coded file names
|
||||
fn ssl() -> Result<SslAcceptorBuilder, ErrorStack> {
|
||||
let mut ssl = SslAcceptorBuilder::mozilla_intermediate_raw(SslMethod::tls())?;
|
||||
|
||||
// Server authentication
|
||||
ssl.set_private_key_file("examples/server-key.pem", X509_FILETYPE_PEM)?;
|
||||
ssl.set_certificate_chain_file("examples/server-chain.pem")?;
|
||||
ssl.check_private_key()?;
|
||||
|
||||
Ok(ssl)
|
||||
}
|
||||
|
||||
/// Create custom server, wire it to the autogenerated router,
|
||||
/// and pass it to the web server.
|
||||
@ -57,11 +45,5 @@ fn main() {
|
||||
|
||||
let addr = "127.0.0.1:80";
|
||||
|
||||
let https = if matches.is_present("https") {
|
||||
Some(ssl().expect("Failed to load SSL keys"))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
hyper::rt::run(server::create(addr, https));
|
||||
hyper::rt::run(server::create(addr, matches.is_present("https")));
|
||||
}
|
||||
|
@ -12,7 +12,6 @@ use chrono;
|
||||
use futures::{future, Future, Stream};
|
||||
use hyper::server::conn::Http;
|
||||
use hyper::service::MakeService as _;
|
||||
use native_tls;
|
||||
use openssl::ssl::SslAcceptorBuilder;
|
||||
use std::collections::HashMap;
|
||||
use std::marker::PhantomData;
|
||||
@ -23,12 +22,18 @@ use swagger::{Has, XSpanIdString};
|
||||
use swagger::auth::MakeAllowAllAuthenticator;
|
||||
use swagger::EmptyContext;
|
||||
use tokio::net::TcpListener;
|
||||
use tokio_tls::TlsAcceptorExt;
|
||||
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")))]
|
||||
use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod};
|
||||
|
||||
use openapi_v3::models;
|
||||
|
||||
pub fn create(addr: &str, https: Option<SslAcceptorBuilder>) -> Box<Future<Item = (), Error = ()> + Send> {
|
||||
#[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
|
||||
pub fn create(addr: &str, https: bool) -> Box<dyn Future<Item = (), Error = ()> + Send> {
|
||||
let addr = addr.parse().expect("Failed to parse bind address");
|
||||
|
||||
let server = Server::new();
|
||||
@ -42,30 +47,44 @@ pub fn create(addr: &str, https: Option<SslAcceptorBuilder>) -> Box<Future<Item
|
||||
service_fn
|
||||
);
|
||||
|
||||
if let Some(ssl) = https {
|
||||
let builder: native_tls::TlsAcceptorBuilder = native_tls::backend::openssl::TlsAcceptorBuilderExt::from_openssl(ssl);
|
||||
let tls_acceptor = builder.build().expect("Failed to build TLS acceptor");
|
||||
let service_fn = Arc::new(Mutex::new(service_fn));
|
||||
let tls_listener = TcpListener::bind(&addr).unwrap().incoming().for_each(move |tcp| {
|
||||
let addr = tcp.peer_addr().expect("Unable to get remote address");
|
||||
if https {
|
||||
#[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))]
|
||||
{
|
||||
unimplemented!("SSL is not implemented for the examples on MacOS, Windows or iOS");
|
||||
}
|
||||
|
||||
let service_fn = service_fn.clone();
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
{
|
||||
let mut ssl = SslAcceptor::mozilla_intermediate_v5(SslMethod::tls()).expect("Failed to create SSL Acceptor");
|
||||
|
||||
hyper::rt::spawn(tls_acceptor.accept_async(tcp).map_err(|_| ()).and_then(move |tls| {
|
||||
let ms = {
|
||||
let mut service_fn = service_fn.lock().unwrap();
|
||||
service_fn.make_service(&addr)
|
||||
};
|
||||
// Server authentication
|
||||
ssl.set_private_key_file("examples/server-key.pem", SslFiletype::PEM).expect("Failed to set private key");
|
||||
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");
|
||||
|
||||
ms.and_then(move |service| {
|
||||
Http::new().serve_connection(tls, service)
|
||||
}).map_err(|_| ())
|
||||
}));
|
||||
let tls_acceptor = ssl.build();
|
||||
let service_fn = Arc::new(Mutex::new(service_fn));
|
||||
let tls_listener = TcpListener::bind(&addr).unwrap().incoming().for_each(move |tcp| {
|
||||
let addr = tcp.peer_addr().expect("Unable to get remote address");
|
||||
|
||||
Ok(())
|
||||
}).map_err(|_| ());
|
||||
let service_fn = service_fn.clone();
|
||||
|
||||
Box::new(tls_listener)
|
||||
hyper::rt::spawn(tls_acceptor.accept_async(tcp).map_err(|_| ()).and_then(move |tls| {
|
||||
let ms = {
|
||||
let mut service_fn = service_fn.lock().unwrap();
|
||||
service_fn.make_service(&addr)
|
||||
};
|
||||
|
||||
ms.and_then(move |service| {
|
||||
Http::new().serve_connection(tls, service)
|
||||
}).map_err(|_| ())
|
||||
}));
|
||||
|
||||
Ok(())
|
||||
}).map_err(|_| ());
|
||||
|
||||
Box::new(tls_listener)
|
||||
}
|
||||
} else {
|
||||
// Using HTTP
|
||||
Box::new(hyper::server::Server::bind(&addr).serve(service_fn).map_err(|e| panic!("{:?}", e)))
|
||||
|
@ -4,7 +4,8 @@ use hyper;
|
||||
use hyper::client::HttpConnector;
|
||||
use hyper::header::{HeaderName, HeaderValue, CONTENT_TYPE};
|
||||
use hyper::{Body, Uri, Response};
|
||||
use hyper_tls::HttpsConnector;
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
use hyper_openssl::HttpsConnector;
|
||||
use serde_json;
|
||||
use std::borrow::Cow;
|
||||
#[allow(unused_imports)]
|
||||
@ -18,9 +19,7 @@ use std::str;
|
||||
use std::str::FromStr;
|
||||
use std::string::ToString;
|
||||
use swagger;
|
||||
use swagger::client::Service;
|
||||
use swagger::connector;
|
||||
use swagger::{ApiError, XSpanIdString, Has, AuthData};
|
||||
use swagger::{ApiError, Connector, client::Service, XSpanIdString, Has, AuthData};
|
||||
use url::form_urlencoded;
|
||||
use url::percent_encoding::{utf8_percent_encode, PATH_SEGMENT_ENCODE_SET, QUERY_ENCODE_SET};
|
||||
use uuid;
|
||||
@ -114,8 +113,7 @@ impl Client<hyper::client::ResponseFuture>
|
||||
///
|
||||
/// Intended for use with custom implementations of connect for e.g. protocol logging
|
||||
/// or similar functionality which requires wrapping the transport layer. When wrapping a TCP connection,
|
||||
/// this function should be used in conjunction with
|
||||
/// `swagger::{http_connector, https_connector, https_mutual_connector}`.
|
||||
/// this function should be used in conjunction with `swagger::Connector::builder()`.
|
||||
///
|
||||
/// For ordinary tcp connections, prefer the use of `try_new_http`, `try_new_https`
|
||||
/// and `try_new_https_mutual`, to avoid introducing a dependency on the underlying transport layer.
|
||||
@ -124,18 +122,16 @@ impl Client<hyper::client::ResponseFuture>
|
||||
///
|
||||
/// * `base_path` - base path of the client API, i.e. "www.my-api-implementation.com"
|
||||
/// * `protocol` - Which protocol to use when constructing the request url, e.g. `Some("http")`
|
||||
/// * `connector_fn` - Function which returns an implementation of `hyper::client::Connect`
|
||||
/// * `connector` - Implementation of `hyper::client::Connect` to use for the client
|
||||
pub fn try_new_with_connector<C>(
|
||||
base_path: &str,
|
||||
protocol: Option<&'static str>,
|
||||
connector_fn: Box<dyn Fn() -> C + Send + Sync>,
|
||||
connector: C,
|
||||
) -> Result<Self, ClientInitError> where
|
||||
C: hyper::client::connect::Connect + 'static,
|
||||
C::Transport: 'static,
|
||||
C::Future: 'static,
|
||||
{
|
||||
let connector = connector_fn();
|
||||
|
||||
let client_service = Box::new(hyper::client::Client::builder().build(connector));
|
||||
|
||||
Ok(Client {
|
||||
@ -151,24 +147,42 @@ impl Client<hyper::client::ResponseFuture>
|
||||
pub fn try_new_http(
|
||||
base_path: &str,
|
||||
) -> Result<Self, ClientInitError> {
|
||||
let http_connector = connector::http_connector();
|
||||
let http_connector = Connector::builder().build();
|
||||
|
||||
Self::try_new_with_connector(base_path, Some("http"), http_connector)
|
||||
}
|
||||
|
||||
/// Create a client with a TLS connection to the server.
|
||||
/// Create a client with a TLS connection to the server
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `base_path` - base path of the client API, i.e. "www.my-api-implementation.com"
|
||||
pub fn try_new_https(base_path: &str) -> Result<Self, ClientInitError>
|
||||
{
|
||||
let https_connector = Connector::builder()
|
||||
.https()
|
||||
.build()
|
||||
.map_err(|e| ClientInitError::SslError(e))?;
|
||||
Self::try_new_with_connector(base_path, Some("https"), https_connector)
|
||||
}
|
||||
|
||||
/// Create a client with a TLS connection to the server using a pinned certificate
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `base_path` - base path of the client API, i.e. "www.my-api-implementation.com"
|
||||
/// * `ca_certificate` - Path to CA certificate used to authenticate the server
|
||||
pub fn try_new_https<CA>(
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
pub fn try_new_https_pinned<CA>(
|
||||
base_path: &str,
|
||||
ca_certificate: CA,
|
||||
) -> Result<Self, ClientInitError>
|
||||
where
|
||||
CA: AsRef<Path>,
|
||||
{
|
||||
let https_connector = connector::https_connector(ca_certificate);
|
||||
let https_connector = Connector::builder()
|
||||
.https()
|
||||
.pin_server_certificate(ca_certificate)
|
||||
.build()
|
||||
.map_err(|e| ClientInitError::SslError(e))?;
|
||||
Self::try_new_with_connector(base_path, Some("https"), https_connector)
|
||||
}
|
||||
|
||||
@ -179,6 +193,7 @@ impl Client<hyper::client::ResponseFuture>
|
||||
/// * `ca_certificate` - Path to CA certificate used to authenticate the server
|
||||
/// * `client_key` - Path to the client private key
|
||||
/// * `client_certificate` - Path to the client's public certificate associated with the private key
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
pub fn try_new_https_mutual<CA, K, D>(
|
||||
base_path: &str,
|
||||
ca_certificate: CA,
|
||||
@ -190,8 +205,12 @@ impl Client<hyper::client::ResponseFuture>
|
||||
K: AsRef<Path>,
|
||||
D: AsRef<Path>,
|
||||
{
|
||||
let https_connector =
|
||||
connector::https_mutual_connector(ca_certificate, client_key, client_certificate);
|
||||
let https_connector = Connector::builder()
|
||||
.https()
|
||||
.pin_server_certificate(ca_certificate)
|
||||
.client_authentication(client_key, client_certificate)
|
||||
.build()
|
||||
.map_err(|e| ClientInitError::SslError(e))?;
|
||||
Self::try_new_with_connector(base_path, Some("https"), https_connector)
|
||||
}
|
||||
}
|
||||
@ -225,7 +244,12 @@ pub enum ClientInitError {
|
||||
MissingHost,
|
||||
|
||||
/// SSL Connection Error
|
||||
SslError(openssl::error::ErrorStack)
|
||||
#[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))]
|
||||
SslError(native_tls::Error),
|
||||
|
||||
/// SSL Connection Error
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
SslError(openssl::error::ErrorStack),
|
||||
}
|
||||
|
||||
impl From<hyper::http::uri::InvalidUri> for ClientInitError {
|
||||
@ -234,12 +258,6 @@ impl From<hyper::http::uri::InvalidUri> for ClientInitError {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<openssl::error::ErrorStack> for ClientInitError {
|
||||
fn from(err: openssl::error::ErrorStack) -> ClientInitError {
|
||||
ClientInitError::SslError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ClientInitError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let s: &dyn fmt::Debug = self;
|
||||
|
@ -31,12 +31,9 @@ extern crate swagger;
|
||||
|
||||
#[cfg(any(feature = "client", feature = "server"))]
|
||||
extern crate hyper;
|
||||
#[cfg(feature = "client")]
|
||||
extern crate hyper_tls;
|
||||
#[cfg(any(feature = "client", feature = "server"))]
|
||||
extern crate openssl;
|
||||
#[cfg(any(feature = "client", feature = "server"))]
|
||||
extern crate native_tls;
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
extern crate hyper_openssl;
|
||||
#[cfg(any(feature = "client", feature = "server"))]
|
||||
extern crate percent_encoding;
|
||||
#[cfg(any(feature = "client", feature = "server"))]
|
||||
|
@ -4,7 +4,8 @@ use hyper;
|
||||
use hyper::client::HttpConnector;
|
||||
use hyper::header::{HeaderName, HeaderValue, CONTENT_TYPE};
|
||||
use hyper::{Body, Uri, Response};
|
||||
use hyper_tls::HttpsConnector;
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
use hyper_openssl::HttpsConnector;
|
||||
use serde_json;
|
||||
use std::borrow::Cow;
|
||||
#[allow(unused_imports)]
|
||||
@ -18,9 +19,7 @@ use std::str;
|
||||
use std::str::FromStr;
|
||||
use std::string::ToString;
|
||||
use swagger;
|
||||
use swagger::client::Service;
|
||||
use swagger::connector;
|
||||
use swagger::{ApiError, XSpanIdString, Has, AuthData};
|
||||
use swagger::{ApiError, Connector, client::Service, XSpanIdString, Has, AuthData};
|
||||
use url::form_urlencoded;
|
||||
use url::percent_encoding::{utf8_percent_encode, PATH_SEGMENT_ENCODE_SET, QUERY_ENCODE_SET};
|
||||
use uuid;
|
||||
@ -71,24 +70,21 @@ impl Client<hyper::client::ResponseFuture>
|
||||
///
|
||||
/// Intended for use with custom implementations of connect for e.g. protocol logging
|
||||
/// or similar functionality which requires wrapping the transport layer. When wrapping a TCP connection,
|
||||
/// this function should be used in conjunction with
|
||||
/// `swagger::{http_connector, https_connector, https_mutual_connector}`.
|
||||
/// this function should be used in conjunction with `swagger::Connector::builder()`.
|
||||
///
|
||||
/// For ordinary tcp connections, prefer the use of `new_http`, `new_https`
|
||||
/// and `new_https_mutual`, to avoid introducing a dependency on the underlying transport layer.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `connector_fn` - Function which returns an implementation of `hyper::client::Connect`
|
||||
/// * `connector` - Implementation of `hyper::client::Connect` to use for the client
|
||||
pub fn new_with_connector<C>(
|
||||
connector_fn: Box<dyn Fn() -> C + Send + Sync>,
|
||||
connector: C,
|
||||
) -> Self where
|
||||
C: hyper::client::connect::Connect + 'static,
|
||||
C::Transport: 'static,
|
||||
C::Future: 'static,
|
||||
{
|
||||
let connector = connector_fn();
|
||||
|
||||
let client_service = Box::new(hyper::client::Client::builder().build(connector));
|
||||
|
||||
Client {
|
||||
@ -98,21 +94,41 @@ impl Client<hyper::client::ResponseFuture>
|
||||
|
||||
/// Create an HTTP client.
|
||||
pub fn new_http() -> Self {
|
||||
let http_connector = connector::http_connector();
|
||||
let http_connector = Connector::builder().build();
|
||||
Self::new_with_connector(http_connector)
|
||||
}
|
||||
|
||||
/// Create a client with a TLS connection to the server.
|
||||
#[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))]
|
||||
pub fn new_https() -> Result<Self, native_tls::Error>
|
||||
{
|
||||
let https_connector = Connector::builder().https().build()?;
|
||||
Ok(Self::new_with_connector(https_connector))
|
||||
}
|
||||
|
||||
/// Create a client with a TLS connection to the server.
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
pub fn new_https() -> Result<Self, openssl::error::ErrorStack>
|
||||
{
|
||||
let https_connector = Connector::builder().https().build()?;
|
||||
Ok(Self::new_with_connector(https_connector))
|
||||
}
|
||||
|
||||
/// Create a client with a TLS connection to the server, pinning the certificate
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `ca_certificate` - Path to CA certificate used to authenticate the server
|
||||
pub fn new_https<CA>(
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
pub fn new_https_pinned<CA>(
|
||||
ca_certificate: CA,
|
||||
) -> Self where
|
||||
) -> Result<Self, openssl::error::ErrorStack> where
|
||||
CA: AsRef<Path>,
|
||||
{
|
||||
let https_connector = connector::https_connector(ca_certificate);
|
||||
Self::new_with_connector(https_connector)
|
||||
let https_connector = Connector::builder()
|
||||
.https()
|
||||
.pin_server_certificate(ca_certificate)
|
||||
.build()?;
|
||||
Ok(Self::new_with_connector(https_connector))
|
||||
}
|
||||
|
||||
/// Create a client with a mutually authenticated TLS connection to the server.
|
||||
@ -121,19 +137,23 @@ impl Client<hyper::client::ResponseFuture>
|
||||
/// * `ca_certificate` - Path to CA certificate used to authenticate the server
|
||||
/// * `client_key` - Path to the client private key
|
||||
/// * `client_certificate` - Path to the client's public certificate associated with the private key
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
pub fn new_https_mutual<CA, K, D>(
|
||||
ca_certificate: CA,
|
||||
client_key: K,
|
||||
client_certificate: D,
|
||||
) -> Self
|
||||
) -> Result<Self, openssl::error::ErrorStack>
|
||||
where
|
||||
CA: AsRef<Path>,
|
||||
K: AsRef<Path>,
|
||||
D: AsRef<Path>,
|
||||
{
|
||||
let https_connector =
|
||||
connector::https_mutual_connector(ca_certificate, client_key, client_certificate);
|
||||
Self::new_with_connector(https_connector)
|
||||
let https_connector = Connector::builder()
|
||||
.https()
|
||||
.pin_server_certificate(ca_certificate)
|
||||
.client_authentication(client_key, client_certificate)
|
||||
.build()?;
|
||||
Ok(Self::new_with_connector(https_connector))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,18 +8,25 @@ license = "Unlicense"
|
||||
[features]
|
||||
default = ["client", "server"]
|
||||
client = [
|
||||
"serde_json", "serde_ignored", "hyper", "hyper-tls", "native-tls", "openssl", "tokio", "url"
|
||||
"serde_json", "serde_ignored", "hyper", "hyper-openssl", "native-tls", "openssl", "tokio", "url"
|
||||
]
|
||||
server = [
|
||||
"serde_json", "serde_ignored", "hyper", "native-tls", "openssl", "tokio", "tokio-tls", "regex", "percent-encoding", "url", "lazy_static"
|
||||
"serde_json", "serde_ignored", "hyper", "tokio", "regex", "percent-encoding", "url", "lazy_static"
|
||||
]
|
||||
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]
|
||||
native-tls = { version = "0.2", optional = true }
|
||||
|
||||
[target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dependencies]
|
||||
hyper-openssl = { version = "0.7.1", optional = true }
|
||||
openssl = {version = "0.10", optional = true }
|
||||
|
||||
[dependencies]
|
||||
# Common
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
futures = "0.1"
|
||||
swagger = "3.0"
|
||||
swagger = "4.0"
|
||||
log = "0.3.0"
|
||||
mime = "0.3"
|
||||
|
||||
@ -30,9 +37,6 @@ serde_derive = "1.0"
|
||||
|
||||
# Common between server and client features
|
||||
hyper = {version = "0.12", optional = true}
|
||||
hyper-tls = {version = "0.2.1", optional = true}
|
||||
native-tls = {version = "0.1.4", optional = true}
|
||||
openssl = {version = "0.9.14", optional = true}
|
||||
serde_json = {version = "1.0", optional = true}
|
||||
serde_ignored = {version = "0.0.4", optional = true}
|
||||
tokio = {version = "0.1.17", optional = true}
|
||||
@ -44,7 +48,6 @@ url = {version = "1.5", optional = true}
|
||||
lazy_static = { version = "1.4", optional = true }
|
||||
percent-encoding = {version = "1.0.0", optional = true}
|
||||
regex = {version = "0.2", optional = true}
|
||||
tokio-tls = {version = "0.1.3", optional = true}
|
||||
|
||||
# Conversion
|
||||
frunk = { version = "0.3.0", optional = true }
|
||||
@ -59,6 +62,10 @@ error-chain = "0.12"
|
||||
env_logger = "0.6"
|
||||
uuid = {version = "0.7", features = ["serde", "v4"]}
|
||||
|
||||
[target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dev-dependencies]
|
||||
tokio-openssl = "0.3"
|
||||
openssl = "0.10"
|
||||
|
||||
[[example]]
|
||||
name = "client"
|
||||
required-features = ["client"]
|
||||
|
@ -125,9 +125,7 @@ fn main() {
|
||||
|
||||
let client = if matches.is_present("https") {
|
||||
// Using Simple HTTPS
|
||||
Client::try_new_https(
|
||||
&base_url,
|
||||
"examples/ca.pem")
|
||||
Client::try_new_https(&base_url)
|
||||
.expect("Failed to create HTTPS client")
|
||||
} else {
|
||||
// Using HTTP
|
||||
|
@ -10,7 +10,6 @@ extern crate env_logger;
|
||||
extern crate hyper;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate openssl;
|
||||
extern crate swagger;
|
||||
|
||||
// Imports required by server library.
|
||||
@ -19,29 +18,18 @@ extern crate chrono;
|
||||
#[macro_use]
|
||||
extern crate error_chain;
|
||||
extern crate futures;
|
||||
extern crate native_tls;
|
||||
// extern crate swagger;
|
||||
extern crate tokio;
|
||||
extern crate tokio_tls;
|
||||
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
extern crate openssl;
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
extern crate tokio_openssl;
|
||||
|
||||
use clap::{App, Arg};
|
||||
use openssl::x509::X509_FILETYPE_PEM;
|
||||
use openssl::ssl::{SslAcceptorBuilder, SslMethod};
|
||||
use openssl::error::ErrorStack;
|
||||
|
||||
mod server;
|
||||
|
||||
// Builds an SSL implementation for Simple HTTPS from some hard-coded file names
|
||||
fn ssl() -> Result<SslAcceptorBuilder, ErrorStack> {
|
||||
let mut ssl = SslAcceptorBuilder::mozilla_intermediate_raw(SslMethod::tls())?;
|
||||
|
||||
// Server authentication
|
||||
ssl.set_private_key_file("examples/server-key.pem", X509_FILETYPE_PEM)?;
|
||||
ssl.set_certificate_chain_file("examples/server-chain.pem")?;
|
||||
ssl.check_private_key()?;
|
||||
|
||||
Ok(ssl)
|
||||
}
|
||||
|
||||
/// Create custom server, wire it to the autogenerated router,
|
||||
/// and pass it to the web server.
|
||||
@ -56,11 +44,5 @@ fn main() {
|
||||
|
||||
let addr = "127.0.0.1:80";
|
||||
|
||||
let https = if matches.is_present("https") {
|
||||
Some(ssl().expect("Failed to load SSL keys"))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
hyper::rt::run(server::create(addr, https));
|
||||
hyper::rt::run(server::create(addr, matches.is_present("https")));
|
||||
}
|
||||
|
@ -12,7 +12,6 @@ use chrono;
|
||||
use futures::{future, Future, Stream};
|
||||
use hyper::server::conn::Http;
|
||||
use hyper::service::MakeService as _;
|
||||
use native_tls;
|
||||
use openssl::ssl::SslAcceptorBuilder;
|
||||
use std::collections::HashMap;
|
||||
use std::marker::PhantomData;
|
||||
@ -23,12 +22,18 @@ use swagger::{Has, XSpanIdString};
|
||||
use swagger::auth::MakeAllowAllAuthenticator;
|
||||
use swagger::EmptyContext;
|
||||
use tokio::net::TcpListener;
|
||||
use tokio_tls::TlsAcceptorExt;
|
||||
|
||||
|
||||
#[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")))]
|
||||
use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod};
|
||||
|
||||
use ops_v3::models;
|
||||
|
||||
pub fn create(addr: &str, https: Option<SslAcceptorBuilder>) -> Box<Future<Item = (), Error = ()> + Send> {
|
||||
#[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
|
||||
pub fn create(addr: &str, https: bool) -> Box<dyn Future<Item = (), Error = ()> + Send> {
|
||||
let addr = addr.parse().expect("Failed to parse bind address");
|
||||
|
||||
let server = Server::new();
|
||||
@ -42,30 +47,44 @@ pub fn create(addr: &str, https: Option<SslAcceptorBuilder>) -> Box<Future<Item
|
||||
service_fn
|
||||
);
|
||||
|
||||
if let Some(ssl) = https {
|
||||
let builder: native_tls::TlsAcceptorBuilder = native_tls::backend::openssl::TlsAcceptorBuilderExt::from_openssl(ssl);
|
||||
let tls_acceptor = builder.build().expect("Failed to build TLS acceptor");
|
||||
let service_fn = Arc::new(Mutex::new(service_fn));
|
||||
let tls_listener = TcpListener::bind(&addr).unwrap().incoming().for_each(move |tcp| {
|
||||
let addr = tcp.peer_addr().expect("Unable to get remote address");
|
||||
if https {
|
||||
#[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))]
|
||||
{
|
||||
unimplemented!("SSL is not implemented for the examples on MacOS, Windows or iOS");
|
||||
}
|
||||
|
||||
let service_fn = service_fn.clone();
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
{
|
||||
let mut ssl = SslAcceptor::mozilla_intermediate_v5(SslMethod::tls()).expect("Failed to create SSL Acceptor");
|
||||
|
||||
hyper::rt::spawn(tls_acceptor.accept_async(tcp).map_err(|_| ()).and_then(move |tls| {
|
||||
let ms = {
|
||||
let mut service_fn = service_fn.lock().unwrap();
|
||||
service_fn.make_service(&addr)
|
||||
};
|
||||
// Server authentication
|
||||
ssl.set_private_key_file("examples/server-key.pem", SslFiletype::PEM).expect("Failed to set private key");
|
||||
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");
|
||||
|
||||
ms.and_then(move |service| {
|
||||
Http::new().serve_connection(tls, service)
|
||||
}).map_err(|_| ())
|
||||
}));
|
||||
let tls_acceptor = ssl.build();
|
||||
let service_fn = Arc::new(Mutex::new(service_fn));
|
||||
let tls_listener = TcpListener::bind(&addr).unwrap().incoming().for_each(move |tcp| {
|
||||
let addr = tcp.peer_addr().expect("Unable to get remote address");
|
||||
|
||||
Ok(())
|
||||
}).map_err(|_| ());
|
||||
let service_fn = service_fn.clone();
|
||||
|
||||
Box::new(tls_listener)
|
||||
hyper::rt::spawn(tls_acceptor.accept_async(tcp).map_err(|_| ()).and_then(move |tls| {
|
||||
let ms = {
|
||||
let mut service_fn = service_fn.lock().unwrap();
|
||||
service_fn.make_service(&addr)
|
||||
};
|
||||
|
||||
ms.and_then(move |service| {
|
||||
Http::new().serve_connection(tls, service)
|
||||
}).map_err(|_| ())
|
||||
}));
|
||||
|
||||
Ok(())
|
||||
}).map_err(|_| ());
|
||||
|
||||
Box::new(tls_listener)
|
||||
}
|
||||
} else {
|
||||
// Using HTTP
|
||||
Box::new(hyper::server::Server::bind(&addr).serve(service_fn).map_err(|e| panic!("{:?}", e)))
|
||||
|
@ -4,7 +4,8 @@ use hyper;
|
||||
use hyper::client::HttpConnector;
|
||||
use hyper::header::{HeaderName, HeaderValue, CONTENT_TYPE};
|
||||
use hyper::{Body, Uri, Response};
|
||||
use hyper_tls::HttpsConnector;
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
use hyper_openssl::HttpsConnector;
|
||||
use serde_json;
|
||||
use std::borrow::Cow;
|
||||
#[allow(unused_imports)]
|
||||
@ -18,9 +19,7 @@ use std::str;
|
||||
use std::str::FromStr;
|
||||
use std::string::ToString;
|
||||
use swagger;
|
||||
use swagger::client::Service;
|
||||
use swagger::connector;
|
||||
use swagger::{ApiError, XSpanIdString, Has, AuthData};
|
||||
use swagger::{ApiError, Connector, client::Service, XSpanIdString, Has, AuthData};
|
||||
use url::form_urlencoded;
|
||||
use url::percent_encoding::{utf8_percent_encode, PATH_SEGMENT_ENCODE_SET, QUERY_ENCODE_SET};
|
||||
|
||||
@ -128,8 +127,7 @@ impl Client<hyper::client::ResponseFuture>
|
||||
///
|
||||
/// Intended for use with custom implementations of connect for e.g. protocol logging
|
||||
/// or similar functionality which requires wrapping the transport layer. When wrapping a TCP connection,
|
||||
/// this function should be used in conjunction with
|
||||
/// `swagger::{http_connector, https_connector, https_mutual_connector}`.
|
||||
/// this function should be used in conjunction with `swagger::Connector::builder()`.
|
||||
///
|
||||
/// For ordinary tcp connections, prefer the use of `try_new_http`, `try_new_https`
|
||||
/// and `try_new_https_mutual`, to avoid introducing a dependency on the underlying transport layer.
|
||||
@ -138,18 +136,16 @@ impl Client<hyper::client::ResponseFuture>
|
||||
///
|
||||
/// * `base_path` - base path of the client API, i.e. "www.my-api-implementation.com"
|
||||
/// * `protocol` - Which protocol to use when constructing the request url, e.g. `Some("http")`
|
||||
/// * `connector_fn` - Function which returns an implementation of `hyper::client::Connect`
|
||||
/// * `connector` - Implementation of `hyper::client::Connect` to use for the client
|
||||
pub fn try_new_with_connector<C>(
|
||||
base_path: &str,
|
||||
protocol: Option<&'static str>,
|
||||
connector_fn: Box<dyn Fn() -> C + Send + Sync>,
|
||||
connector: C,
|
||||
) -> Result<Self, ClientInitError> where
|
||||
C: hyper::client::connect::Connect + 'static,
|
||||
C::Transport: 'static,
|
||||
C::Future: 'static,
|
||||
{
|
||||
let connector = connector_fn();
|
||||
|
||||
let client_service = Box::new(hyper::client::Client::builder().build(connector));
|
||||
|
||||
Ok(Client {
|
||||
@ -165,24 +161,42 @@ impl Client<hyper::client::ResponseFuture>
|
||||
pub fn try_new_http(
|
||||
base_path: &str,
|
||||
) -> Result<Self, ClientInitError> {
|
||||
let http_connector = connector::http_connector();
|
||||
let http_connector = Connector::builder().build();
|
||||
|
||||
Self::try_new_with_connector(base_path, Some("http"), http_connector)
|
||||
}
|
||||
|
||||
/// Create a client with a TLS connection to the server.
|
||||
/// Create a client with a TLS connection to the server
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `base_path` - base path of the client API, i.e. "www.my-api-implementation.com"
|
||||
pub fn try_new_https(base_path: &str) -> Result<Self, ClientInitError>
|
||||
{
|
||||
let https_connector = Connector::builder()
|
||||
.https()
|
||||
.build()
|
||||
.map_err(|e| ClientInitError::SslError(e))?;
|
||||
Self::try_new_with_connector(base_path, Some("https"), https_connector)
|
||||
}
|
||||
|
||||
/// Create a client with a TLS connection to the server using a pinned certificate
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `base_path` - base path of the client API, i.e. "www.my-api-implementation.com"
|
||||
/// * `ca_certificate` - Path to CA certificate used to authenticate the server
|
||||
pub fn try_new_https<CA>(
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
pub fn try_new_https_pinned<CA>(
|
||||
base_path: &str,
|
||||
ca_certificate: CA,
|
||||
) -> Result<Self, ClientInitError>
|
||||
where
|
||||
CA: AsRef<Path>,
|
||||
{
|
||||
let https_connector = connector::https_connector(ca_certificate);
|
||||
let https_connector = Connector::builder()
|
||||
.https()
|
||||
.pin_server_certificate(ca_certificate)
|
||||
.build()
|
||||
.map_err(|e| ClientInitError::SslError(e))?;
|
||||
Self::try_new_with_connector(base_path, Some("https"), https_connector)
|
||||
}
|
||||
|
||||
@ -193,6 +207,7 @@ impl Client<hyper::client::ResponseFuture>
|
||||
/// * `ca_certificate` - Path to CA certificate used to authenticate the server
|
||||
/// * `client_key` - Path to the client private key
|
||||
/// * `client_certificate` - Path to the client's public certificate associated with the private key
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
pub fn try_new_https_mutual<CA, K, D>(
|
||||
base_path: &str,
|
||||
ca_certificate: CA,
|
||||
@ -204,8 +219,12 @@ impl Client<hyper::client::ResponseFuture>
|
||||
K: AsRef<Path>,
|
||||
D: AsRef<Path>,
|
||||
{
|
||||
let https_connector =
|
||||
connector::https_mutual_connector(ca_certificate, client_key, client_certificate);
|
||||
let https_connector = Connector::builder()
|
||||
.https()
|
||||
.pin_server_certificate(ca_certificate)
|
||||
.client_authentication(client_key, client_certificate)
|
||||
.build()
|
||||
.map_err(|e| ClientInitError::SslError(e))?;
|
||||
Self::try_new_with_connector(base_path, Some("https"), https_connector)
|
||||
}
|
||||
}
|
||||
@ -239,7 +258,12 @@ pub enum ClientInitError {
|
||||
MissingHost,
|
||||
|
||||
/// SSL Connection Error
|
||||
SslError(openssl::error::ErrorStack)
|
||||
#[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))]
|
||||
SslError(native_tls::Error),
|
||||
|
||||
/// SSL Connection Error
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
SslError(openssl::error::ErrorStack),
|
||||
}
|
||||
|
||||
impl From<hyper::http::uri::InvalidUri> for ClientInitError {
|
||||
@ -248,12 +272,6 @@ impl From<hyper::http::uri::InvalidUri> for ClientInitError {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<openssl::error::ErrorStack> for ClientInitError {
|
||||
fn from(err: openssl::error::ErrorStack) -> ClientInitError {
|
||||
ClientInitError::SslError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ClientInitError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let s: &dyn fmt::Debug = self;
|
||||
|
@ -31,12 +31,9 @@ extern crate swagger;
|
||||
|
||||
#[cfg(any(feature = "client", feature = "server"))]
|
||||
extern crate hyper;
|
||||
#[cfg(feature = "client")]
|
||||
extern crate hyper_tls;
|
||||
#[cfg(any(feature = "client", feature = "server"))]
|
||||
extern crate openssl;
|
||||
#[cfg(any(feature = "client", feature = "server"))]
|
||||
extern crate native_tls;
|
||||
#[cfg(feature = "server")]
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
extern crate hyper_openssl;
|
||||
#[cfg(feature = "server")]
|
||||
extern crate percent_encoding;
|
||||
#[cfg(any(feature = "client", feature = "server"))]
|
||||
|
@ -11,20 +11,27 @@ client = [
|
||||
"mime_0_2",
|
||||
"multipart", "multipart/client", "swagger/multipart",
|
||||
"serde_urlencoded",
|
||||
"serde_json", "serde_ignored", "hyper", "hyper-tls", "native-tls", "openssl", "tokio", "url"
|
||||
"serde_json", "serde_ignored", "hyper", "hyper-openssl", "native-tls", "openssl", "tokio", "url"
|
||||
]
|
||||
server = [
|
||||
"mime_0_2",
|
||||
"multipart", "multipart/server",
|
||||
"serde_json", "serde_ignored", "hyper", "native-tls", "openssl", "tokio", "tokio-tls", "regex", "percent-encoding", "url", "lazy_static"
|
||||
"serde_json", "serde_ignored", "hyper", "tokio", "regex", "percent-encoding", "url", "lazy_static"
|
||||
]
|
||||
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]
|
||||
native-tls = { version = "0.2", optional = true }
|
||||
|
||||
[target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dependencies]
|
||||
hyper-openssl = { version = "0.7.1", optional = true }
|
||||
openssl = {version = "0.10", optional = true }
|
||||
|
||||
[dependencies]
|
||||
# Common
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
futures = "0.1"
|
||||
swagger = "3.0"
|
||||
swagger = "4.0"
|
||||
log = "0.3.0"
|
||||
mime = "0.3"
|
||||
|
||||
@ -41,9 +48,6 @@ uuid = {version = "0.7", features = ["serde", "v4"]}
|
||||
|
||||
# Common between server and client features
|
||||
hyper = {version = "0.12", optional = true}
|
||||
hyper-tls = {version = "0.2.1", optional = true}
|
||||
native-tls = {version = "0.1.4", optional = true}
|
||||
openssl = {version = "0.9.14", optional = true}
|
||||
serde_json = {version = "1.0", optional = true}
|
||||
serde_ignored = {version = "0.0.4", optional = true}
|
||||
tokio = {version = "0.1.17", optional = true}
|
||||
@ -56,7 +60,6 @@ serde_urlencoded = {version = "0.5.1", optional = true}
|
||||
lazy_static = { version = "1.4", optional = true }
|
||||
percent-encoding = {version = "1.0.0", optional = true}
|
||||
regex = {version = "0.2", optional = true}
|
||||
tokio-tls = {version = "0.1.3", optional = true}
|
||||
|
||||
# Conversion
|
||||
frunk = { version = "0.3.0", optional = true }
|
||||
@ -70,6 +73,10 @@ clap = "2.25"
|
||||
error-chain = "0.12"
|
||||
env_logger = "0.6"
|
||||
|
||||
[target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dev-dependencies]
|
||||
tokio-openssl = "0.3"
|
||||
openssl = "0.10"
|
||||
|
||||
[[example]]
|
||||
name = "client"
|
||||
required-features = ["client"]
|
||||
|
@ -111,9 +111,7 @@ fn main() {
|
||||
|
||||
let client = if matches.is_present("https") {
|
||||
// Using Simple HTTPS
|
||||
Client::try_new_https(
|
||||
&base_url,
|
||||
"examples/ca.pem")
|
||||
Client::try_new_https(&base_url)
|
||||
.expect("Failed to create HTTPS client")
|
||||
} else {
|
||||
// Using HTTP
|
||||
|
@ -10,7 +10,6 @@ extern crate env_logger;
|
||||
extern crate hyper;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate openssl;
|
||||
extern crate swagger;
|
||||
|
||||
// Imports required by server library.
|
||||
@ -19,30 +18,19 @@ extern crate chrono;
|
||||
#[macro_use]
|
||||
extern crate error_chain;
|
||||
extern crate futures;
|
||||
extern crate native_tls;
|
||||
// extern crate swagger;
|
||||
extern crate tokio;
|
||||
extern crate tokio_tls;
|
||||
extern crate uuid;
|
||||
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
extern crate openssl;
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
extern crate tokio_openssl;
|
||||
|
||||
use clap::{App, Arg};
|
||||
use openssl::x509::X509_FILETYPE_PEM;
|
||||
use openssl::ssl::{SslAcceptorBuilder, SslMethod};
|
||||
use openssl::error::ErrorStack;
|
||||
|
||||
mod server;
|
||||
|
||||
// Builds an SSL implementation for Simple HTTPS from some hard-coded file names
|
||||
fn ssl() -> Result<SslAcceptorBuilder, ErrorStack> {
|
||||
let mut ssl = SslAcceptorBuilder::mozilla_intermediate_raw(SslMethod::tls())?;
|
||||
|
||||
// Server authentication
|
||||
ssl.set_private_key_file("examples/server-key.pem", X509_FILETYPE_PEM)?;
|
||||
ssl.set_certificate_chain_file("examples/server-chain.pem")?;
|
||||
ssl.check_private_key()?;
|
||||
|
||||
Ok(ssl)
|
||||
}
|
||||
|
||||
/// Create custom server, wire it to the autogenerated router,
|
||||
/// and pass it to the web server.
|
||||
@ -57,11 +45,5 @@ fn main() {
|
||||
|
||||
let addr = "127.0.0.1:80";
|
||||
|
||||
let https = if matches.is_present("https") {
|
||||
Some(ssl().expect("Failed to load SSL keys"))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
hyper::rt::run(server::create(addr, https));
|
||||
hyper::rt::run(server::create(addr, matches.is_present("https")));
|
||||
}
|
||||
|
@ -12,7 +12,6 @@ use chrono;
|
||||
use futures::{future, Future, Stream};
|
||||
use hyper::server::conn::Http;
|
||||
use hyper::service::MakeService as _;
|
||||
use native_tls;
|
||||
use openssl::ssl::SslAcceptorBuilder;
|
||||
use std::collections::HashMap;
|
||||
use std::marker::PhantomData;
|
||||
@ -23,12 +22,18 @@ use swagger::{Has, XSpanIdString};
|
||||
use swagger::auth::MakeAllowAllAuthenticator;
|
||||
use swagger::EmptyContext;
|
||||
use tokio::net::TcpListener;
|
||||
use tokio_tls::TlsAcceptorExt;
|
||||
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")))]
|
||||
use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod};
|
||||
|
||||
use petstore_with_fake_endpoints_models_for_testing::models;
|
||||
|
||||
pub fn create(addr: &str, https: Option<SslAcceptorBuilder>) -> Box<Future<Item = (), Error = ()> + Send> {
|
||||
#[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
|
||||
pub fn create(addr: &str, https: bool) -> Box<dyn Future<Item = (), Error = ()> + Send> {
|
||||
let addr = addr.parse().expect("Failed to parse bind address");
|
||||
|
||||
let server = Server::new();
|
||||
@ -42,30 +47,44 @@ pub fn create(addr: &str, https: Option<SslAcceptorBuilder>) -> Box<Future<Item
|
||||
service_fn
|
||||
);
|
||||
|
||||
if let Some(ssl) = https {
|
||||
let builder: native_tls::TlsAcceptorBuilder = native_tls::backend::openssl::TlsAcceptorBuilderExt::from_openssl(ssl);
|
||||
let tls_acceptor = builder.build().expect("Failed to build TLS acceptor");
|
||||
let service_fn = Arc::new(Mutex::new(service_fn));
|
||||
let tls_listener = TcpListener::bind(&addr).unwrap().incoming().for_each(move |tcp| {
|
||||
let addr = tcp.peer_addr().expect("Unable to get remote address");
|
||||
if https {
|
||||
#[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))]
|
||||
{
|
||||
unimplemented!("SSL is not implemented for the examples on MacOS, Windows or iOS");
|
||||
}
|
||||
|
||||
let service_fn = service_fn.clone();
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
{
|
||||
let mut ssl = SslAcceptor::mozilla_intermediate_v5(SslMethod::tls()).expect("Failed to create SSL Acceptor");
|
||||
|
||||
hyper::rt::spawn(tls_acceptor.accept_async(tcp).map_err(|_| ()).and_then(move |tls| {
|
||||
let ms = {
|
||||
let mut service_fn = service_fn.lock().unwrap();
|
||||
service_fn.make_service(&addr)
|
||||
};
|
||||
// Server authentication
|
||||
ssl.set_private_key_file("examples/server-key.pem", SslFiletype::PEM).expect("Failed to set private key");
|
||||
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");
|
||||
|
||||
ms.and_then(move |service| {
|
||||
Http::new().serve_connection(tls, service)
|
||||
}).map_err(|_| ())
|
||||
}));
|
||||
let tls_acceptor = ssl.build();
|
||||
let service_fn = Arc::new(Mutex::new(service_fn));
|
||||
let tls_listener = TcpListener::bind(&addr).unwrap().incoming().for_each(move |tcp| {
|
||||
let addr = tcp.peer_addr().expect("Unable to get remote address");
|
||||
|
||||
Ok(())
|
||||
}).map_err(|_| ());
|
||||
let service_fn = service_fn.clone();
|
||||
|
||||
Box::new(tls_listener)
|
||||
hyper::rt::spawn(tls_acceptor.accept_async(tcp).map_err(|_| ()).and_then(move |tls| {
|
||||
let ms = {
|
||||
let mut service_fn = service_fn.lock().unwrap();
|
||||
service_fn.make_service(&addr)
|
||||
};
|
||||
|
||||
ms.and_then(move |service| {
|
||||
Http::new().serve_connection(tls, service)
|
||||
}).map_err(|_| ())
|
||||
}));
|
||||
|
||||
Ok(())
|
||||
}).map_err(|_| ());
|
||||
|
||||
Box::new(tls_listener)
|
||||
}
|
||||
} else {
|
||||
// Using HTTP
|
||||
Box::new(hyper::server::Server::bind(&addr).serve(service_fn).map_err(|e| panic!("{:?}", e)))
|
||||
|
@ -4,7 +4,8 @@ use hyper;
|
||||
use hyper::client::HttpConnector;
|
||||
use hyper::header::{HeaderName, HeaderValue, CONTENT_TYPE};
|
||||
use hyper::{Body, Uri, Response};
|
||||
use hyper_tls::HttpsConnector;
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
use hyper_openssl::HttpsConnector;
|
||||
use serde_json;
|
||||
use std::borrow::Cow;
|
||||
#[allow(unused_imports)]
|
||||
@ -18,9 +19,7 @@ use std::str;
|
||||
use std::str::FromStr;
|
||||
use std::string::ToString;
|
||||
use swagger;
|
||||
use swagger::client::Service;
|
||||
use swagger::connector;
|
||||
use swagger::{ApiError, XSpanIdString, Has, AuthData};
|
||||
use swagger::{ApiError, Connector, client::Service, XSpanIdString, Has, AuthData};
|
||||
use url::form_urlencoded;
|
||||
use url::percent_encoding::{utf8_percent_encode, PATH_SEGMENT_ENCODE_SET, QUERY_ENCODE_SET};
|
||||
use mime::Mime;
|
||||
@ -131,8 +130,7 @@ impl Client<hyper::client::ResponseFuture>
|
||||
///
|
||||
/// Intended for use with custom implementations of connect for e.g. protocol logging
|
||||
/// or similar functionality which requires wrapping the transport layer. When wrapping a TCP connection,
|
||||
/// this function should be used in conjunction with
|
||||
/// `swagger::{http_connector, https_connector, https_mutual_connector}`.
|
||||
/// this function should be used in conjunction with `swagger::Connector::builder()`.
|
||||
///
|
||||
/// For ordinary tcp connections, prefer the use of `try_new_http`, `try_new_https`
|
||||
/// and `try_new_https_mutual`, to avoid introducing a dependency on the underlying transport layer.
|
||||
@ -141,18 +139,16 @@ impl Client<hyper::client::ResponseFuture>
|
||||
///
|
||||
/// * `base_path` - base path of the client API, i.e. "www.my-api-implementation.com"
|
||||
/// * `protocol` - Which protocol to use when constructing the request url, e.g. `Some("http")`
|
||||
/// * `connector_fn` - Function which returns an implementation of `hyper::client::Connect`
|
||||
/// * `connector` - Implementation of `hyper::client::Connect` to use for the client
|
||||
pub fn try_new_with_connector<C>(
|
||||
base_path: &str,
|
||||
protocol: Option<&'static str>,
|
||||
connector_fn: Box<dyn Fn() -> C + Send + Sync>,
|
||||
connector: C,
|
||||
) -> Result<Self, ClientInitError> where
|
||||
C: hyper::client::connect::Connect + 'static,
|
||||
C::Transport: 'static,
|
||||
C::Future: 'static,
|
||||
{
|
||||
let connector = connector_fn();
|
||||
|
||||
let client_service = Box::new(hyper::client::Client::builder().build(connector));
|
||||
|
||||
Ok(Client {
|
||||
@ -168,24 +164,42 @@ impl Client<hyper::client::ResponseFuture>
|
||||
pub fn try_new_http(
|
||||
base_path: &str,
|
||||
) -> Result<Self, ClientInitError> {
|
||||
let http_connector = connector::http_connector();
|
||||
let http_connector = Connector::builder().build();
|
||||
|
||||
Self::try_new_with_connector(base_path, Some("http"), http_connector)
|
||||
}
|
||||
|
||||
/// Create a client with a TLS connection to the server.
|
||||
/// Create a client with a TLS connection to the server
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `base_path` - base path of the client API, i.e. "www.my-api-implementation.com"
|
||||
pub fn try_new_https(base_path: &str) -> Result<Self, ClientInitError>
|
||||
{
|
||||
let https_connector = Connector::builder()
|
||||
.https()
|
||||
.build()
|
||||
.map_err(|e| ClientInitError::SslError(e))?;
|
||||
Self::try_new_with_connector(base_path, Some("https"), https_connector)
|
||||
}
|
||||
|
||||
/// Create a client with a TLS connection to the server using a pinned certificate
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `base_path` - base path of the client API, i.e. "www.my-api-implementation.com"
|
||||
/// * `ca_certificate` - Path to CA certificate used to authenticate the server
|
||||
pub fn try_new_https<CA>(
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
pub fn try_new_https_pinned<CA>(
|
||||
base_path: &str,
|
||||
ca_certificate: CA,
|
||||
) -> Result<Self, ClientInitError>
|
||||
where
|
||||
CA: AsRef<Path>,
|
||||
{
|
||||
let https_connector = connector::https_connector(ca_certificate);
|
||||
let https_connector = Connector::builder()
|
||||
.https()
|
||||
.pin_server_certificate(ca_certificate)
|
||||
.build()
|
||||
.map_err(|e| ClientInitError::SslError(e))?;
|
||||
Self::try_new_with_connector(base_path, Some("https"), https_connector)
|
||||
}
|
||||
|
||||
@ -196,6 +210,7 @@ impl Client<hyper::client::ResponseFuture>
|
||||
/// * `ca_certificate` - Path to CA certificate used to authenticate the server
|
||||
/// * `client_key` - Path to the client private key
|
||||
/// * `client_certificate` - Path to the client's public certificate associated with the private key
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
pub fn try_new_https_mutual<CA, K, D>(
|
||||
base_path: &str,
|
||||
ca_certificate: CA,
|
||||
@ -207,8 +222,12 @@ impl Client<hyper::client::ResponseFuture>
|
||||
K: AsRef<Path>,
|
||||
D: AsRef<Path>,
|
||||
{
|
||||
let https_connector =
|
||||
connector::https_mutual_connector(ca_certificate, client_key, client_certificate);
|
||||
let https_connector = Connector::builder()
|
||||
.https()
|
||||
.pin_server_certificate(ca_certificate)
|
||||
.client_authentication(client_key, client_certificate)
|
||||
.build()
|
||||
.map_err(|e| ClientInitError::SslError(e))?;
|
||||
Self::try_new_with_connector(base_path, Some("https"), https_connector)
|
||||
}
|
||||
}
|
||||
@ -242,7 +261,12 @@ pub enum ClientInitError {
|
||||
MissingHost,
|
||||
|
||||
/// SSL Connection Error
|
||||
SslError(openssl::error::ErrorStack)
|
||||
#[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))]
|
||||
SslError(native_tls::Error),
|
||||
|
||||
/// SSL Connection Error
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
SslError(openssl::error::ErrorStack),
|
||||
}
|
||||
|
||||
impl From<hyper::http::uri::InvalidUri> for ClientInitError {
|
||||
@ -251,12 +275,6 @@ impl From<hyper::http::uri::InvalidUri> for ClientInitError {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<openssl::error::ErrorStack> for ClientInitError {
|
||||
fn from(err: openssl::error::ErrorStack) -> ClientInitError {
|
||||
ClientInitError::SslError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ClientInitError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let s: &dyn fmt::Debug = self;
|
||||
|
@ -31,14 +31,11 @@ extern crate swagger;
|
||||
|
||||
#[cfg(any(feature = "client", feature = "server"))]
|
||||
extern crate hyper;
|
||||
#[cfg(feature = "client")]
|
||||
extern crate hyper_tls;
|
||||
#[cfg(any(feature = "client", feature = "server"))]
|
||||
extern crate openssl;
|
||||
#[cfg(feature = "server")]
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
extern crate hyper_openssl;
|
||||
#[cfg(any(feature = "client", feature = "server"))]
|
||||
extern crate mime_0_2;
|
||||
#[cfg(any(feature = "client", feature = "server"))]
|
||||
extern crate native_tls;
|
||||
#[cfg(feature = "server")]
|
||||
extern crate percent_encoding;
|
||||
#[cfg(any(feature = "client", feature = "server"))]
|
||||
|
@ -8,18 +8,25 @@ license = "Unlicense"
|
||||
[features]
|
||||
default = ["client", "server"]
|
||||
client = [
|
||||
"serde_json", "serde_ignored", "hyper", "hyper-tls", "native-tls", "openssl", "tokio", "url"
|
||||
"serde_json", "serde_ignored", "hyper", "hyper-openssl", "native-tls", "openssl", "tokio", "url"
|
||||
]
|
||||
server = [
|
||||
"serde_json", "serde_ignored", "hyper", "native-tls", "openssl", "tokio", "tokio-tls", "regex", "percent-encoding", "url", "lazy_static"
|
||||
"serde_json", "serde_ignored", "hyper", "tokio", "regex", "percent-encoding", "url", "lazy_static"
|
||||
]
|
||||
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]
|
||||
native-tls = { version = "0.2", optional = true }
|
||||
|
||||
[target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dependencies]
|
||||
hyper-openssl = { version = "0.7.1", optional = true }
|
||||
openssl = {version = "0.10", optional = true }
|
||||
|
||||
[dependencies]
|
||||
# Common
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
futures = "0.1"
|
||||
swagger = "3.0"
|
||||
swagger = "4.0"
|
||||
log = "0.3.0"
|
||||
mime = "0.3"
|
||||
|
||||
@ -30,9 +37,6 @@ serde_derive = "1.0"
|
||||
|
||||
# Common between server and client features
|
||||
hyper = {version = "0.12", optional = true}
|
||||
hyper-tls = {version = "0.2.1", optional = true}
|
||||
native-tls = {version = "0.1.4", optional = true}
|
||||
openssl = {version = "0.9.14", optional = true}
|
||||
serde_json = {version = "1.0", optional = true}
|
||||
serde_ignored = {version = "0.0.4", optional = true}
|
||||
tokio = {version = "0.1.17", optional = true}
|
||||
@ -44,7 +48,6 @@ url = {version = "1.5", optional = true}
|
||||
lazy_static = { version = "1.4", optional = true }
|
||||
percent-encoding = {version = "1.0.0", optional = true}
|
||||
regex = {version = "0.2", optional = true}
|
||||
tokio-tls = {version = "0.1.3", optional = true}
|
||||
|
||||
# Conversion
|
||||
frunk = { version = "0.3.0", optional = true }
|
||||
@ -59,6 +62,10 @@ error-chain = "0.12"
|
||||
env_logger = "0.6"
|
||||
uuid = {version = "0.7", features = ["serde", "v4"]}
|
||||
|
||||
[target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dev-dependencies]
|
||||
tokio-openssl = "0.3"
|
||||
openssl = "0.10"
|
||||
|
||||
[[example]]
|
||||
name = "client"
|
||||
required-features = ["client"]
|
||||
|
@ -67,9 +67,7 @@ fn main() {
|
||||
|
||||
let client = if matches.is_present("https") {
|
||||
// Using Simple HTTPS
|
||||
Client::try_new_https(
|
||||
&base_url,
|
||||
"examples/ca.pem")
|
||||
Client::try_new_https(&base_url)
|
||||
.expect("Failed to create HTTPS client")
|
||||
} else {
|
||||
// Using HTTP
|
||||
|
@ -10,7 +10,6 @@ extern crate env_logger;
|
||||
extern crate hyper;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate openssl;
|
||||
extern crate swagger;
|
||||
|
||||
// Imports required by server library.
|
||||
@ -19,29 +18,18 @@ extern crate chrono;
|
||||
#[macro_use]
|
||||
extern crate error_chain;
|
||||
extern crate futures;
|
||||
extern crate native_tls;
|
||||
// extern crate swagger;
|
||||
extern crate tokio;
|
||||
extern crate tokio_tls;
|
||||
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
extern crate openssl;
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
extern crate tokio_openssl;
|
||||
|
||||
use clap::{App, Arg};
|
||||
use openssl::x509::X509_FILETYPE_PEM;
|
||||
use openssl::ssl::{SslAcceptorBuilder, SslMethod};
|
||||
use openssl::error::ErrorStack;
|
||||
|
||||
mod server;
|
||||
|
||||
// Builds an SSL implementation for Simple HTTPS from some hard-coded file names
|
||||
fn ssl() -> Result<SslAcceptorBuilder, ErrorStack> {
|
||||
let mut ssl = SslAcceptorBuilder::mozilla_intermediate_raw(SslMethod::tls())?;
|
||||
|
||||
// Server authentication
|
||||
ssl.set_private_key_file("examples/server-key.pem", X509_FILETYPE_PEM)?;
|
||||
ssl.set_certificate_chain_file("examples/server-chain.pem")?;
|
||||
ssl.check_private_key()?;
|
||||
|
||||
Ok(ssl)
|
||||
}
|
||||
|
||||
/// Create custom server, wire it to the autogenerated router,
|
||||
/// and pass it to the web server.
|
||||
@ -56,11 +44,5 @@ fn main() {
|
||||
|
||||
let addr = "127.0.0.1:80";
|
||||
|
||||
let https = if matches.is_present("https") {
|
||||
Some(ssl().expect("Failed to load SSL keys"))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
hyper::rt::run(server::create(addr, https));
|
||||
hyper::rt::run(server::create(addr, matches.is_present("https")));
|
||||
}
|
||||
|
@ -12,7 +12,6 @@ use chrono;
|
||||
use futures::{future, Future, Stream};
|
||||
use hyper::server::conn::Http;
|
||||
use hyper::service::MakeService as _;
|
||||
use native_tls;
|
||||
use openssl::ssl::SslAcceptorBuilder;
|
||||
use std::collections::HashMap;
|
||||
use std::marker::PhantomData;
|
||||
@ -23,12 +22,18 @@ use swagger::{Has, XSpanIdString};
|
||||
use swagger::auth::MakeAllowAllAuthenticator;
|
||||
use swagger::EmptyContext;
|
||||
use tokio::net::TcpListener;
|
||||
use tokio_tls::TlsAcceptorExt;
|
||||
|
||||
|
||||
#[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")))]
|
||||
use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod};
|
||||
|
||||
use rust_server_test::models;
|
||||
|
||||
pub fn create(addr: &str, https: Option<SslAcceptorBuilder>) -> Box<Future<Item = (), Error = ()> + Send> {
|
||||
#[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
|
||||
pub fn create(addr: &str, https: bool) -> Box<dyn Future<Item = (), Error = ()> + Send> {
|
||||
let addr = addr.parse().expect("Failed to parse bind address");
|
||||
|
||||
let server = Server::new();
|
||||
@ -42,30 +47,44 @@ pub fn create(addr: &str, https: Option<SslAcceptorBuilder>) -> Box<Future<Item
|
||||
service_fn
|
||||
);
|
||||
|
||||
if let Some(ssl) = https {
|
||||
let builder: native_tls::TlsAcceptorBuilder = native_tls::backend::openssl::TlsAcceptorBuilderExt::from_openssl(ssl);
|
||||
let tls_acceptor = builder.build().expect("Failed to build TLS acceptor");
|
||||
let service_fn = Arc::new(Mutex::new(service_fn));
|
||||
let tls_listener = TcpListener::bind(&addr).unwrap().incoming().for_each(move |tcp| {
|
||||
let addr = tcp.peer_addr().expect("Unable to get remote address");
|
||||
if https {
|
||||
#[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))]
|
||||
{
|
||||
unimplemented!("SSL is not implemented for the examples on MacOS, Windows or iOS");
|
||||
}
|
||||
|
||||
let service_fn = service_fn.clone();
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
{
|
||||
let mut ssl = SslAcceptor::mozilla_intermediate_v5(SslMethod::tls()).expect("Failed to create SSL Acceptor");
|
||||
|
||||
hyper::rt::spawn(tls_acceptor.accept_async(tcp).map_err(|_| ()).and_then(move |tls| {
|
||||
let ms = {
|
||||
let mut service_fn = service_fn.lock().unwrap();
|
||||
service_fn.make_service(&addr)
|
||||
};
|
||||
// Server authentication
|
||||
ssl.set_private_key_file("examples/server-key.pem", SslFiletype::PEM).expect("Failed to set private key");
|
||||
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");
|
||||
|
||||
ms.and_then(move |service| {
|
||||
Http::new().serve_connection(tls, service)
|
||||
}).map_err(|_| ())
|
||||
}));
|
||||
let tls_acceptor = ssl.build();
|
||||
let service_fn = Arc::new(Mutex::new(service_fn));
|
||||
let tls_listener = TcpListener::bind(&addr).unwrap().incoming().for_each(move |tcp| {
|
||||
let addr = tcp.peer_addr().expect("Unable to get remote address");
|
||||
|
||||
Ok(())
|
||||
}).map_err(|_| ());
|
||||
let service_fn = service_fn.clone();
|
||||
|
||||
Box::new(tls_listener)
|
||||
hyper::rt::spawn(tls_acceptor.accept_async(tcp).map_err(|_| ()).and_then(move |tls| {
|
||||
let ms = {
|
||||
let mut service_fn = service_fn.lock().unwrap();
|
||||
service_fn.make_service(&addr)
|
||||
};
|
||||
|
||||
ms.and_then(move |service| {
|
||||
Http::new().serve_connection(tls, service)
|
||||
}).map_err(|_| ())
|
||||
}));
|
||||
|
||||
Ok(())
|
||||
}).map_err(|_| ());
|
||||
|
||||
Box::new(tls_listener)
|
||||
}
|
||||
} else {
|
||||
// Using HTTP
|
||||
Box::new(hyper::server::Server::bind(&addr).serve(service_fn).map_err(|e| panic!("{:?}", e)))
|
||||
|
@ -4,7 +4,8 @@ use hyper;
|
||||
use hyper::client::HttpConnector;
|
||||
use hyper::header::{HeaderName, HeaderValue, CONTENT_TYPE};
|
||||
use hyper::{Body, Uri, Response};
|
||||
use hyper_tls::HttpsConnector;
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
use hyper_openssl::HttpsConnector;
|
||||
use serde_json;
|
||||
use std::borrow::Cow;
|
||||
#[allow(unused_imports)]
|
||||
@ -18,9 +19,7 @@ use std::str;
|
||||
use std::str::FromStr;
|
||||
use std::string::ToString;
|
||||
use swagger;
|
||||
use swagger::client::Service;
|
||||
use swagger::connector;
|
||||
use swagger::{ApiError, XSpanIdString, Has, AuthData};
|
||||
use swagger::{ApiError, Connector, client::Service, XSpanIdString, Has, AuthData};
|
||||
use url::form_urlencoded;
|
||||
use url::percent_encoding::{utf8_percent_encode, PATH_SEGMENT_ENCODE_SET, QUERY_ENCODE_SET};
|
||||
|
||||
@ -100,8 +99,7 @@ impl Client<hyper::client::ResponseFuture>
|
||||
///
|
||||
/// Intended for use with custom implementations of connect for e.g. protocol logging
|
||||
/// or similar functionality which requires wrapping the transport layer. When wrapping a TCP connection,
|
||||
/// this function should be used in conjunction with
|
||||
/// `swagger::{http_connector, https_connector, https_mutual_connector}`.
|
||||
/// this function should be used in conjunction with `swagger::Connector::builder()`.
|
||||
///
|
||||
/// For ordinary tcp connections, prefer the use of `try_new_http`, `try_new_https`
|
||||
/// and `try_new_https_mutual`, to avoid introducing a dependency on the underlying transport layer.
|
||||
@ -110,18 +108,16 @@ impl Client<hyper::client::ResponseFuture>
|
||||
///
|
||||
/// * `base_path` - base path of the client API, i.e. "www.my-api-implementation.com"
|
||||
/// * `protocol` - Which protocol to use when constructing the request url, e.g. `Some("http")`
|
||||
/// * `connector_fn` - Function which returns an implementation of `hyper::client::Connect`
|
||||
/// * `connector` - Implementation of `hyper::client::Connect` to use for the client
|
||||
pub fn try_new_with_connector<C>(
|
||||
base_path: &str,
|
||||
protocol: Option<&'static str>,
|
||||
connector_fn: Box<dyn Fn() -> C + Send + Sync>,
|
||||
connector: C,
|
||||
) -> Result<Self, ClientInitError> where
|
||||
C: hyper::client::connect::Connect + 'static,
|
||||
C::Transport: 'static,
|
||||
C::Future: 'static,
|
||||
{
|
||||
let connector = connector_fn();
|
||||
|
||||
let client_service = Box::new(hyper::client::Client::builder().build(connector));
|
||||
|
||||
Ok(Client {
|
||||
@ -137,24 +133,42 @@ impl Client<hyper::client::ResponseFuture>
|
||||
pub fn try_new_http(
|
||||
base_path: &str,
|
||||
) -> Result<Self, ClientInitError> {
|
||||
let http_connector = connector::http_connector();
|
||||
let http_connector = Connector::builder().build();
|
||||
|
||||
Self::try_new_with_connector(base_path, Some("http"), http_connector)
|
||||
}
|
||||
|
||||
/// Create a client with a TLS connection to the server.
|
||||
/// Create a client with a TLS connection to the server
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `base_path` - base path of the client API, i.e. "www.my-api-implementation.com"
|
||||
pub fn try_new_https(base_path: &str) -> Result<Self, ClientInitError>
|
||||
{
|
||||
let https_connector = Connector::builder()
|
||||
.https()
|
||||
.build()
|
||||
.map_err(|e| ClientInitError::SslError(e))?;
|
||||
Self::try_new_with_connector(base_path, Some("https"), https_connector)
|
||||
}
|
||||
|
||||
/// Create a client with a TLS connection to the server using a pinned certificate
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `base_path` - base path of the client API, i.e. "www.my-api-implementation.com"
|
||||
/// * `ca_certificate` - Path to CA certificate used to authenticate the server
|
||||
pub fn try_new_https<CA>(
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
pub fn try_new_https_pinned<CA>(
|
||||
base_path: &str,
|
||||
ca_certificate: CA,
|
||||
) -> Result<Self, ClientInitError>
|
||||
where
|
||||
CA: AsRef<Path>,
|
||||
{
|
||||
let https_connector = connector::https_connector(ca_certificate);
|
||||
let https_connector = Connector::builder()
|
||||
.https()
|
||||
.pin_server_certificate(ca_certificate)
|
||||
.build()
|
||||
.map_err(|e| ClientInitError::SslError(e))?;
|
||||
Self::try_new_with_connector(base_path, Some("https"), https_connector)
|
||||
}
|
||||
|
||||
@ -165,6 +179,7 @@ impl Client<hyper::client::ResponseFuture>
|
||||
/// * `ca_certificate` - Path to CA certificate used to authenticate the server
|
||||
/// * `client_key` - Path to the client private key
|
||||
/// * `client_certificate` - Path to the client's public certificate associated with the private key
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
pub fn try_new_https_mutual<CA, K, D>(
|
||||
base_path: &str,
|
||||
ca_certificate: CA,
|
||||
@ -176,8 +191,12 @@ impl Client<hyper::client::ResponseFuture>
|
||||
K: AsRef<Path>,
|
||||
D: AsRef<Path>,
|
||||
{
|
||||
let https_connector =
|
||||
connector::https_mutual_connector(ca_certificate, client_key, client_certificate);
|
||||
let https_connector = Connector::builder()
|
||||
.https()
|
||||
.pin_server_certificate(ca_certificate)
|
||||
.client_authentication(client_key, client_certificate)
|
||||
.build()
|
||||
.map_err(|e| ClientInitError::SslError(e))?;
|
||||
Self::try_new_with_connector(base_path, Some("https"), https_connector)
|
||||
}
|
||||
}
|
||||
@ -211,7 +230,12 @@ pub enum ClientInitError {
|
||||
MissingHost,
|
||||
|
||||
/// SSL Connection Error
|
||||
SslError(openssl::error::ErrorStack)
|
||||
#[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))]
|
||||
SslError(native_tls::Error),
|
||||
|
||||
/// SSL Connection Error
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
SslError(openssl::error::ErrorStack),
|
||||
}
|
||||
|
||||
impl From<hyper::http::uri::InvalidUri> for ClientInitError {
|
||||
@ -220,12 +244,6 @@ impl From<hyper::http::uri::InvalidUri> for ClientInitError {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<openssl::error::ErrorStack> for ClientInitError {
|
||||
fn from(err: openssl::error::ErrorStack) -> ClientInitError {
|
||||
ClientInitError::SslError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ClientInitError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let s: &dyn fmt::Debug = self;
|
||||
|
@ -31,12 +31,9 @@ extern crate swagger;
|
||||
|
||||
#[cfg(any(feature = "client", feature = "server"))]
|
||||
extern crate hyper;
|
||||
#[cfg(feature = "client")]
|
||||
extern crate hyper_tls;
|
||||
#[cfg(any(feature = "client", feature = "server"))]
|
||||
extern crate openssl;
|
||||
#[cfg(any(feature = "client", feature = "server"))]
|
||||
extern crate native_tls;
|
||||
#[cfg(feature = "server")]
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
|
||||
extern crate hyper_openssl;
|
||||
#[cfg(feature = "server")]
|
||||
extern crate percent_encoding;
|
||||
#[cfg(any(feature = "client", feature = "server"))]
|
||||
|
Loading…
x
Reference in New Issue
Block a user