[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:
Richard Whitehouse 2020-03-19 21:05:16 +00:00 committed by GitHub
parent 16646b39c1
commit 4aefc9ba33
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 773 additions and 574 deletions

View File

@ -23,9 +23,9 @@ client = [
"serde_urlencoded", "serde_urlencoded",
{{/usesUrlEncodedForm}} {{/usesUrlEncodedForm}}
{{#hasCallbacks}} {{#hasCallbacks}}
"tokio-tls", "regex", "percent-encoding", "lazy_static", "regex", "percent-encoding", "lazy_static",
{{/hasCallbacks}} {{/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 = [ server = [
{{#apiUsesMultipart}} {{#apiUsesMultipart}}
@ -37,16 +37,26 @@ server = [
{{#apiUsesMultipartRelated}} {{#apiUsesMultipartRelated}}
"hyper_0_10", "mime_multipart", "hyper_0_10", "mime_multipart",
{{/apiUsesMultipartRelated}} {{/apiUsesMultipartRelated}}
{{#hasCallbacks}}
"native-tls", "hyper-openssl", "openssl",
{{/hasCallbacks}}
{{! Anything added to the list below, should probably be added to the callbacks list above }} {{! Anything added to the list below, should probably be added to the callbacks list above }}
"serde_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"] 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] [dependencies]
# Common # Common
chrono = { version = "0.4", features = ["serde"] } chrono = { version = "0.4", features = ["serde"] }
futures = "0.1" futures = "0.1"
swagger = "3.0" swagger = "4.0"
log = "0.3.0" log = "0.3.0"
mime = "0.3" mime = "0.3"
@ -71,13 +81,10 @@ uuid = {version = "0.7", features = ["serde", "v4"]}
# Common between server and client features # Common between server and client features
hyper = {version = "0.12", optional = true} hyper = {version = "0.12", optional = true}
hyper-tls = {version = "0.2.1", optional = true}
{{#apiUsesMultipartRelated}} {{#apiUsesMultipartRelated}}
mime_multipart = {version = "0.5", optional = true} mime_multipart = {version = "0.5", optional = true}
hyper_0_10 = {package = "hyper", version = "0.10", default-features = false, optional=true} hyper_0_10 = {package = "hyper", version = "0.10", default-features = false, optional=true}
{{/apiUsesMultipartRelated}} {{/apiUsesMultipartRelated}}
native-tls = {version = "0.1.4", optional = true}
openssl = {version = "0.9.14", optional = true}
serde_json = {version = "1.0", optional = true} serde_json = {version = "1.0", optional = true}
serde_ignored = {version = "0.0.4", optional = true} serde_ignored = {version = "0.0.4", optional = true}
tokio = {version = "0.1.17", 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 } lazy_static = { version = "1.4", optional = true }
percent-encoding = {version = "1.0.0", optional = true} percent-encoding = {version = "1.0.0", optional = true}
regex = {version = "0.2", optional = true} regex = {version = "0.2", optional = true}
tokio-tls = {version = "0.1.3", optional = true}
# Conversion # Conversion
frunk = { version = "0.3.0", optional = true } frunk = { version = "0.3.0", optional = true }
@ -109,6 +115,10 @@ env_logger = "0.6"
uuid = {version = "0.7", features = ["serde", "v4"]} uuid = {version = "0.7", features = ["serde", "v4"]}
{{/apiUsesUuid}} {{/apiUsesUuid}}
[target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dev-dependencies]
tokio-openssl = "0.3"
openssl = "0.10"
[[example]] [[example]]
name = "client" name = "client"
required-features = ["client"] required-features = ["client"]

View File

@ -4,7 +4,8 @@ use hyper;
use hyper::client::HttpConnector; use hyper::client::HttpConnector;
use hyper::header::{HeaderName, HeaderValue, CONTENT_TYPE}; use hyper::header::{HeaderName, HeaderValue, CONTENT_TYPE};
use hyper::{Body, Uri, Response}; use hyper::{Body, 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 serde_json;
use std::borrow::Cow; use std::borrow::Cow;
#[allow(unused_imports)] #[allow(unused_imports)]
@ -18,9 +19,7 @@ use std::str;
use std::str::FromStr; use std::str::FromStr;
use std::string::ToString; use std::string::ToString;
use swagger; use swagger;
use swagger::client::Service; use swagger::{ApiError, Connector, client::Service, XSpanIdString, Has, AuthData};
use swagger::connector;
use swagger::{ApiError, XSpanIdString, Has, AuthData};
use url::form_urlencoded; use url::form_urlencoded;
use url::percent_encoding::{utf8_percent_encode, PATH_SEGMENT_ENCODE_SET, QUERY_ENCODE_SET}; use url::percent_encoding::{utf8_percent_encode, PATH_SEGMENT_ENCODE_SET, QUERY_ENCODE_SET};
{{#apiUsesMultipartFormData}} {{#apiUsesMultipartFormData}}

View File

@ -59,8 +59,7 @@ impl Client<hyper::client::ResponseFuture>
/// ///
/// Intended for use with custom implementations of connect for e.g. protocol logging /// 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, /// or similar functionality which requires wrapping the transport layer. When wrapping a TCP connection,
/// this function should be used in conjunction with /// this function should be used in conjunction with `swagger::Connector::builder()`.
/// `swagger::{http_connector, https_connector, https_mutual_connector}`.
/// ///
/// For ordinary tcp connections, prefer the use of `try_new_http`, `try_new_https` /// 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. /// 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" /// * `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")` /// * `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>( pub fn try_new_with_connector<C>(
base_path: &str, base_path: &str,
protocol: Option<&'static str>, protocol: Option<&'static str>,
connector_fn: Box<dyn Fn() -> C + Send + Sync>, connector: C,
) -> Result<Self, ClientInitError> where ) -> Result<Self, ClientInitError> where
C: hyper::client::connect::Connect + 'static, C: hyper::client::connect::Connect + 'static,
C::Transport: 'static, C::Transport: 'static,
C::Future: 'static, C::Future: 'static,
{ {
let connector = connector_fn();
let client_service = Box::new(hyper::client::Client::builder().build(connector)); let client_service = Box::new(hyper::client::Client::builder().build(connector));
Ok(Client { Ok(Client {
@ -96,24 +93,42 @@ impl Client<hyper::client::ResponseFuture>
pub fn try_new_http( pub fn try_new_http(
base_path: &str, base_path: &str,
) -> Result<Self, ClientInitError> { ) -> 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) 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 /// # Arguments
/// * `base_path` - base path of the client API, i.e. "www.my-api-implementation.com" /// * `base_path` - base path of the client API, i.e. "www.my-api-implementation.com"
/// * `ca_certificate` - Path to CA certificate used to authenticate the server /// * `ca_certificate` - Path to CA certificate used to authenticate the server
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, base_path: &str,
ca_certificate: CA, ca_certificate: CA,
) -> Result<Self, ClientInitError> ) -> Result<Self, ClientInitError>
where where
CA: AsRef<Path>, 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) 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 /// * `ca_certificate` - Path to CA certificate used to authenticate the server
/// * `client_key` - Path to the client private key /// * `client_key` - Path to the client private key
/// * `client_certificate` - Path to the client's public certificate associated with the private key /// * `client_certificate` - Path to the client's public certificate associated with the private key
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
pub fn try_new_https_mutual<CA, K, D>( pub fn try_new_https_mutual<CA, K, D>(
base_path: &str, base_path: &str,
ca_certificate: CA, ca_certificate: CA,
@ -135,8 +151,12 @@ impl Client<hyper::client::ResponseFuture>
K: AsRef<Path>, K: AsRef<Path>,
D: AsRef<Path>, D: AsRef<Path>,
{ {
let https_connector = let https_connector = Connector::builder()
connector::https_mutual_connector(ca_certificate, client_key, client_certificate); .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) Self::try_new_with_connector(base_path, Some("https"), https_connector)
} }
} }
@ -170,7 +190,12 @@ pub enum ClientInitError {
MissingHost, MissingHost,
/// SSL Connection Error /// 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 { 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 { impl fmt::Display for ClientInitError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let s: &dyn fmt::Debug = self; let s: &dyn fmt::Debug = self;

View File

@ -13,13 +13,15 @@ extern crate chrono;
#[macro_use] #[macro_use]
extern crate error_chain; extern crate error_chain;
extern crate hyper; extern crate hyper;
extern crate openssl;
extern crate native_tls;
extern crate tokio_tls;
{{#apiUsesUuid}} {{#apiUsesUuid}}
extern crate uuid; extern crate uuid;
{{/apiUsesUuid}} {{/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; mod server;
{{/hasCallbacks}} {{/hasCallbacks}}
@ -79,9 +81,7 @@ fn main() {
let client = if matches.is_present("https") { let client = if matches.is_present("https") {
// Using Simple HTTPS // Using Simple HTTPS
Client::try_new_https( Client::try_new_https(&base_url)
&base_url,
"examples/ca.pem")
.expect("Failed to create HTTPS client") .expect("Failed to create HTTPS client")
} else { } else {
// Using HTTP // Using HTTP
@ -99,7 +99,7 @@ fn main() {
{{#hasCallbacks}} {{#hasCallbacks}}
// We could do HTTPS here, but for simplicity we don't // 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}} {{/hasCallbacks}}
match matches.value_of("operation") { match matches.value_of("operation") {

View File

@ -12,7 +12,6 @@ use chrono;
use futures::{future, Future, Stream}; use futures::{future, Future, Stream};
use hyper::server::conn::Http; use hyper::server::conn::Http;
use hyper::service::MakeService as _; use hyper::service::MakeService as _;
use native_tls;
use openssl::ssl::SslAcceptorBuilder; use openssl::ssl::SslAcceptorBuilder;
use std::collections::HashMap; use std::collections::HashMap;
use std::marker::PhantomData; use std::marker::PhantomData;
@ -23,12 +22,18 @@ use swagger::{Has, XSpanIdString};
use swagger::auth::MakeAllowAllAuthenticator; use swagger::auth::MakeAllowAllAuthenticator;
use swagger::EmptyContext; use swagger::EmptyContext;
use tokio::net::TcpListener; use tokio::net::TcpListener;
use tokio_tls::TlsAcceptorExt;
{{#apiUsesUuid}}use uuid;{{/apiUsesUuid}} {{#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; 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 addr = addr.parse().expect("Failed to parse bind address");
let server = Server::new(); let server = Server::new();
@ -42,9 +47,22 @@ pub fn create(addr: &str, https: Option<SslAcceptorBuilder>) -> Box<Future<Item
service_fn service_fn
); );
if let Some(ssl) = https { if https {
let builder: native_tls::TlsAcceptorBuilder = native_tls::backend::openssl::TlsAcceptorBuilderExt::from_openssl(ssl); #[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))]
let tls_acceptor = builder.build().expect("Failed to build TLS acceptor"); {
unimplemented!("SSL is not implemented for the examples on MacOS, Windows or iOS");
}
#[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");
// 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");
let tls_acceptor = ssl.build();
let service_fn = Arc::new(Mutex::new(service_fn)); let service_fn = Arc::new(Mutex::new(service_fn));
let tls_listener = TcpListener::bind(&addr).unwrap().incoming().for_each(move |tcp| { let tls_listener = TcpListener::bind(&addr).unwrap().incoming().for_each(move |tcp| {
let addr = tcp.peer_addr().expect("Unable to get remote address"); let addr = tcp.peer_addr().expect("Unable to get remote address");
@ -66,6 +84,7 @@ pub fn create(addr: &str, https: Option<SslAcceptorBuilder>) -> Box<Future<Item
}).map_err(|_| ()); }).map_err(|_| ());
Box::new(tls_listener) Box::new(tls_listener)
}
} else { } else {
// Using HTTP // Using HTTP
Box::new(hyper::server::Server::bind(&addr).serve(service_fn).map_err(|e| panic!("{:?}", e))) Box::new(hyper::server::Server::bind(&addr).serve(service_fn).map_err(|e| panic!("{:?}", e)))

View File

@ -10,7 +10,6 @@ extern crate env_logger;
extern crate hyper; extern crate hyper;
#[macro_use] #[macro_use]
extern crate log; extern crate log;
extern crate openssl;
extern crate swagger; extern crate swagger;
// Imports required by server library. // Imports required by server library.
@ -19,32 +18,21 @@ extern crate chrono;
#[macro_use] #[macro_use]
extern crate error_chain; extern crate error_chain;
extern crate futures; extern crate futures;
extern crate native_tls;
// extern crate swagger; // extern crate swagger;
extern crate tokio; extern crate tokio;
extern crate tokio_tls;
{{#apiUsesUuid}} {{#apiUsesUuid}}
extern crate uuid; extern crate uuid;
{{/apiUsesUuid}} {{/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 clap::{App, Arg};
use openssl::x509::X509_FILETYPE_PEM;
use openssl::ssl::{SslAcceptorBuilder, SslMethod};
use openssl::error::ErrorStack;
mod server; 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, /// Create custom server, wire it to the autogenerated router,
/// and pass it to the web server. /// and pass it to the web server.
@ -59,11 +47,5 @@ fn main() {
let addr = "127.0.0.1:{{{serverPort}}}"; let addr = "127.0.0.1:{{{serverPort}}}";
let https = if matches.is_present("https") { hyper::rt::run(server::create(addr, matches.is_present("https")));
Some(ssl().expect("Failed to load SSL keys"))
} else {
None
};
hyper::rt::run(server::create(addr, https));
} }

View File

@ -41,10 +41,14 @@ extern crate swagger;
#[cfg(any(feature = "client", feature = "server"))] #[cfg(any(feature = "client", feature = "server"))]
extern crate hyper; extern crate hyper;
#[cfg(feature = "client")] {{#hasCallbacks}}
extern crate hyper_tls;
#[cfg(any(feature = "client", feature = "server"))] #[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}} {{#apiUsesMultipart}}
#[cfg(any(feature = "client", feature = "server"))] #[cfg(any(feature = "client", feature = "server"))]
extern crate mime_0_2; extern crate mime_0_2;
@ -53,8 +57,6 @@ extern crate mime_0_2;
#[cfg(any(feature = "client", feature = "server"))] #[cfg(any(feature = "client", feature = "server"))]
extern crate mime_multipart; extern crate mime_multipart;
{{/apiUsesMultipartRelated}} {{/apiUsesMultipartRelated}}
#[cfg(any(feature = "client", feature = "server"))]
extern crate native_tls;
{{#hasCallbacks}} {{#hasCallbacks}}
#[cfg(any(feature = "client", feature = "server"))] #[cfg(any(feature = "client", feature = "server"))]
{{/hasCallbacks}} {{/hasCallbacks}}

View File

@ -45,24 +45,21 @@ impl Client<hyper::client::ResponseFuture>
/// ///
/// Intended for use with custom implementations of connect for e.g. protocol logging /// 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, /// or similar functionality which requires wrapping the transport layer. When wrapping a TCP connection,
/// this function should be used in conjunction with /// this function should be used in conjunction with `swagger::Connector::builder()`.
/// `swagger::{http_connector, https_connector, https_mutual_connector}`.
/// ///
/// For ordinary tcp connections, prefer the use of `new_http`, `new_https` /// 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. /// and `new_https_mutual`, to avoid introducing a dependency on the underlying transport layer.
/// ///
/// # Arguments /// # 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>( pub fn new_with_connector<C>(
connector_fn: Box<dyn Fn() -> C + Send + Sync>, connector: C,
) -> Self where ) -> Self where
C: hyper::client::connect::Connect + 'static, C: hyper::client::connect::Connect + 'static,
C::Transport: 'static, C::Transport: 'static,
C::Future: 'static, C::Future: 'static,
{ {
let connector = connector_fn();
let client_service = Box::new(hyper::client::Client::builder().build(connector)); let client_service = Box::new(hyper::client::Client::builder().build(connector));
Client { Client {
@ -72,21 +69,41 @@ impl Client<hyper::client::ResponseFuture>
/// Create an HTTP client. /// Create an HTTP client.
pub fn new_http() -> Self { pub fn new_http() -> Self {
let http_connector = connector::http_connector(); let http_connector = Connector::builder().build();
Self::new_with_connector(http_connector) Self::new_with_connector(http_connector)
} }
/// Create a client with a TLS connection to the server. /// Create a client with a TLS connection to the server.
#[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))]
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 /// # Arguments
/// * `ca_certificate` - Path to CA certificate used to authenticate the server /// * `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, ca_certificate: CA,
) -> Self where ) -> Result<Self, openssl::error::ErrorStack> where
CA: AsRef<Path>, CA: AsRef<Path>,
{ {
let https_connector = connector::https_connector(ca_certificate); let https_connector = Connector::builder()
Self::new_with_connector(https_connector) .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. /// 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 /// * `ca_certificate` - Path to CA certificate used to authenticate the server
/// * `client_key` - Path to the client private key /// * `client_key` - Path to the client private key
/// * `client_certificate` - Path to the client's public certificate associated with the private key /// * `client_certificate` - Path to the client's public certificate associated with the private key
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
pub fn new_https_mutual<CA, K, D>( pub fn new_https_mutual<CA, K, D>(
ca_certificate: CA, ca_certificate: CA,
client_key: K, client_key: K,
client_certificate: D, client_certificate: D,
) -> Self ) -> Result<Self, openssl::error::ErrorStack>
where where
CA: AsRef<Path>, CA: AsRef<Path>,
K: AsRef<Path>, K: AsRef<Path>,
D: AsRef<Path>, D: AsRef<Path>,
{ {
let https_connector = let https_connector = Connector::builder()
connector::https_mutual_connector(ca_certificate, client_key, client_certificate); .https()
Self::new_with_connector(https_connector) .pin_server_certificate(ca_certificate)
.client_authentication(client_key, client_certificate)
.build()?;
Ok(Self::new_with_connector(https_connector))
} }
} }

View File

@ -11,21 +11,28 @@ client = [
"mime_0_2", "mime_0_2",
"multipart", "multipart/client", "swagger/multipart", "multipart", "multipart/client", "swagger/multipart",
"hyper_0_10", "mime_multipart", "hyper_0_10", "mime_multipart",
"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 = [ server = [
"mime_0_2", "mime_0_2",
"multipart", "multipart/server", "multipart", "multipart/server",
"hyper_0_10", "mime_multipart", "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"] 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] [dependencies]
# Common # Common
chrono = { version = "0.4", features = ["serde"] } chrono = { version = "0.4", features = ["serde"] }
futures = "0.1" futures = "0.1"
swagger = "3.0" swagger = "4.0"
log = "0.3.0" log = "0.3.0"
mime = "0.3" mime = "0.3"
@ -38,11 +45,8 @@ multipart = { version = "0.16", default-features = false, optional = true }
# Common between server and client features # Common between server and client features
hyper = {version = "0.12", optional = true} hyper = {version = "0.12", optional = true}
hyper-tls = {version = "0.2.1", optional = true}
mime_multipart = {version = "0.5", optional = true} mime_multipart = {version = "0.5", optional = true}
hyper_0_10 = {package = "hyper", version = "0.10", default-features = false, optional=true} hyper_0_10 = {package = "hyper", version = "0.10", default-features = false, optional=true}
native-tls = {version = "0.1.4", optional = true}
openssl = {version = "0.9.14", optional = true}
serde_json = {version = "1.0", optional = true} serde_json = {version = "1.0", optional = true}
serde_ignored = {version = "0.0.4", optional = true} serde_ignored = {version = "0.0.4", optional = true}
tokio = {version = "0.1.17", 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 } lazy_static = { version = "1.4", optional = true }
percent-encoding = {version = "1.0.0", optional = true} percent-encoding = {version = "1.0.0", optional = true}
regex = {version = "0.2", optional = true} regex = {version = "0.2", optional = true}
tokio-tls = {version = "0.1.3", optional = true}
# Conversion # Conversion
frunk = { version = "0.3.0", optional = true } frunk = { version = "0.3.0", optional = true }
@ -69,6 +72,10 @@ error-chain = "0.12"
env_logger = "0.6" env_logger = "0.6"
uuid = {version = "0.7", features = ["serde", "v4"]} 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]] [[example]]
name = "client" name = "client"
required-features = ["client"] required-features = ["client"]

View File

@ -55,9 +55,7 @@ fn main() {
let client = if matches.is_present("https") { let client = if matches.is_present("https") {
// Using Simple HTTPS // Using Simple HTTPS
Client::try_new_https( Client::try_new_https(&base_url)
&base_url,
"examples/ca.pem")
.expect("Failed to create HTTPS client") .expect("Failed to create HTTPS client")
} else { } else {
// Using HTTP // Using HTTP

View File

@ -10,7 +10,6 @@ extern crate env_logger;
extern crate hyper; extern crate hyper;
#[macro_use] #[macro_use]
extern crate log; extern crate log;
extern crate openssl;
extern crate swagger; extern crate swagger;
// Imports required by server library. // Imports required by server library.
@ -19,29 +18,18 @@ extern crate chrono;
#[macro_use] #[macro_use]
extern crate error_chain; extern crate error_chain;
extern crate futures; extern crate futures;
extern crate native_tls;
// extern crate swagger; // extern crate swagger;
extern crate tokio; 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 clap::{App, Arg};
use openssl::x509::X509_FILETYPE_PEM;
use openssl::ssl::{SslAcceptorBuilder, SslMethod};
use openssl::error::ErrorStack;
mod server; 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, /// Create custom server, wire it to the autogenerated router,
/// and pass it to the web server. /// and pass it to the web server.
@ -56,11 +44,5 @@ fn main() {
let addr = "127.0.0.1:80"; let addr = "127.0.0.1:80";
let https = if matches.is_present("https") { hyper::rt::run(server::create(addr, matches.is_present("https")));
Some(ssl().expect("Failed to load SSL keys"))
} else {
None
};
hyper::rt::run(server::create(addr, https));
} }

View File

@ -12,7 +12,6 @@ use chrono;
use futures::{future, Future, Stream}; use futures::{future, Future, Stream};
use hyper::server::conn::Http; use hyper::server::conn::Http;
use hyper::service::MakeService as _; use hyper::service::MakeService as _;
use native_tls;
use openssl::ssl::SslAcceptorBuilder; use openssl::ssl::SslAcceptorBuilder;
use std::collections::HashMap; use std::collections::HashMap;
use std::marker::PhantomData; use std::marker::PhantomData;
@ -23,12 +22,18 @@ use swagger::{Has, XSpanIdString};
use swagger::auth::MakeAllowAllAuthenticator; use swagger::auth::MakeAllowAllAuthenticator;
use swagger::EmptyContext; use swagger::EmptyContext;
use tokio::net::TcpListener; use tokio::net::TcpListener;
use 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; 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 addr = addr.parse().expect("Failed to parse bind address");
let server = Server::new(); let server = Server::new();
@ -42,9 +47,22 @@ pub fn create(addr: &str, https: Option<SslAcceptorBuilder>) -> Box<Future<Item
service_fn service_fn
); );
if let Some(ssl) = https { if https {
let builder: native_tls::TlsAcceptorBuilder = native_tls::backend::openssl::TlsAcceptorBuilderExt::from_openssl(ssl); #[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))]
let tls_acceptor = builder.build().expect("Failed to build TLS acceptor"); {
unimplemented!("SSL is not implemented for the examples on MacOS, Windows or iOS");
}
#[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");
// 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");
let tls_acceptor = ssl.build();
let service_fn = Arc::new(Mutex::new(service_fn)); let service_fn = Arc::new(Mutex::new(service_fn));
let tls_listener = TcpListener::bind(&addr).unwrap().incoming().for_each(move |tcp| { let tls_listener = TcpListener::bind(&addr).unwrap().incoming().for_each(move |tcp| {
let addr = tcp.peer_addr().expect("Unable to get remote address"); let addr = tcp.peer_addr().expect("Unable to get remote address");
@ -66,6 +84,7 @@ pub fn create(addr: &str, https: Option<SslAcceptorBuilder>) -> Box<Future<Item
}).map_err(|_| ()); }).map_err(|_| ());
Box::new(tls_listener) Box::new(tls_listener)
}
} else { } else {
// Using HTTP // Using HTTP
Box::new(hyper::server::Server::bind(&addr).serve(service_fn).map_err(|e| panic!("{:?}", e))) Box::new(hyper::server::Server::bind(&addr).serve(service_fn).map_err(|e| panic!("{:?}", e)))

View File

@ -4,7 +4,8 @@ use hyper;
use hyper::client::HttpConnector; use hyper::client::HttpConnector;
use hyper::header::{HeaderName, HeaderValue, CONTENT_TYPE}; use hyper::header::{HeaderName, HeaderValue, CONTENT_TYPE};
use hyper::{Body, Uri, Response}; use hyper::{Body, 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 serde_json;
use std::borrow::Cow; use std::borrow::Cow;
#[allow(unused_imports)] #[allow(unused_imports)]
@ -18,9 +19,7 @@ use std::str;
use std::str::FromStr; use std::str::FromStr;
use std::string::ToString; use std::string::ToString;
use swagger; use swagger;
use swagger::client::Service; use swagger::{ApiError, Connector, client::Service, XSpanIdString, Has, AuthData};
use swagger::connector;
use swagger::{ApiError, XSpanIdString, Has, AuthData};
use url::form_urlencoded; use url::form_urlencoded;
use url::percent_encoding::{utf8_percent_encode, PATH_SEGMENT_ENCODE_SET, QUERY_ENCODE_SET}; use url::percent_encoding::{utf8_percent_encode, PATH_SEGMENT_ENCODE_SET, QUERY_ENCODE_SET};
use mime::Mime; 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 /// 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, /// or similar functionality which requires wrapping the transport layer. When wrapping a TCP connection,
/// this function should be used in conjunction with /// this function should be used in conjunction with `swagger::Connector::builder()`.
/// `swagger::{http_connector, https_connector, https_mutual_connector}`.
/// ///
/// For ordinary tcp connections, prefer the use of `try_new_http`, `try_new_https` /// 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. /// 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" /// * `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")` /// * `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>( pub fn try_new_with_connector<C>(
base_path: &str, base_path: &str,
protocol: Option<&'static str>, protocol: Option<&'static str>,
connector_fn: Box<dyn Fn() -> C + Send + Sync>, connector: C,
) -> Result<Self, ClientInitError> where ) -> Result<Self, ClientInitError> where
C: hyper::client::connect::Connect + 'static, C: hyper::client::connect::Connect + 'static,
C::Transport: 'static, C::Transport: 'static,
C::Future: 'static, C::Future: 'static,
{ {
let connector = connector_fn();
let client_service = Box::new(hyper::client::Client::builder().build(connector)); let client_service = Box::new(hyper::client::Client::builder().build(connector));
Ok(Client { Ok(Client {
@ -136,24 +132,42 @@ impl Client<hyper::client::ResponseFuture>
pub fn try_new_http( pub fn try_new_http(
base_path: &str, base_path: &str,
) -> Result<Self, ClientInitError> { ) -> 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) 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 /// # Arguments
/// * `base_path` - base path of the client API, i.e. "www.my-api-implementation.com" /// * `base_path` - base path of the client API, i.e. "www.my-api-implementation.com"
/// * `ca_certificate` - Path to CA certificate used to authenticate the server /// * `ca_certificate` - Path to CA certificate used to authenticate the server
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, base_path: &str,
ca_certificate: CA, ca_certificate: CA,
) -> Result<Self, ClientInitError> ) -> Result<Self, ClientInitError>
where where
CA: AsRef<Path>, 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) 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 /// * `ca_certificate` - Path to CA certificate used to authenticate the server
/// * `client_key` - Path to the client private key /// * `client_key` - Path to the client private key
/// * `client_certificate` - Path to the client's public certificate associated with the private key /// * `client_certificate` - Path to the client's public certificate associated with the private key
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
pub fn try_new_https_mutual<CA, K, D>( pub fn try_new_https_mutual<CA, K, D>(
base_path: &str, base_path: &str,
ca_certificate: CA, ca_certificate: CA,
@ -175,8 +190,12 @@ impl Client<hyper::client::ResponseFuture>
K: AsRef<Path>, K: AsRef<Path>,
D: AsRef<Path>, D: AsRef<Path>,
{ {
let https_connector = let https_connector = Connector::builder()
connector::https_mutual_connector(ca_certificate, client_key, client_certificate); .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) Self::try_new_with_connector(base_path, Some("https"), https_connector)
} }
} }
@ -210,7 +229,12 @@ pub enum ClientInitError {
MissingHost, MissingHost,
/// SSL Connection Error /// 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 { 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 { impl fmt::Display for ClientInitError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let s: &dyn fmt::Debug = self; let s: &dyn fmt::Debug = self;

View File

@ -34,16 +34,13 @@ extern crate swagger;
#[cfg(any(feature = "client", feature = "server"))] #[cfg(any(feature = "client", feature = "server"))]
extern crate hyper; extern crate hyper;
#[cfg(feature = "client")] #[cfg(feature = "server")]
extern crate hyper_tls; #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
#[cfg(any(feature = "client", feature = "server"))] extern crate hyper_openssl;
extern crate openssl;
#[cfg(any(feature = "client", feature = "server"))] #[cfg(any(feature = "client", feature = "server"))]
extern crate mime_0_2; extern crate mime_0_2;
#[cfg(any(feature = "client", feature = "server"))] #[cfg(any(feature = "client", feature = "server"))]
extern crate mime_multipart; extern crate mime_multipart;
#[cfg(any(feature = "client", feature = "server"))]
extern crate native_tls;
#[cfg(feature = "server")] #[cfg(feature = "server")]
extern crate percent_encoding; extern crate percent_encoding;
#[cfg(any(feature = "client", feature = "server"))] #[cfg(any(feature = "client", feature = "server"))]

View File

@ -8,19 +8,27 @@ license = "Unlicense"
[features] [features]
default = ["client", "server"] default = ["client", "server"]
client = [ client = [
"tokio-tls", "regex", "percent-encoding", "lazy_static", "regex", "percent-encoding", "lazy_static",
"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 = [ 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"] 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] [dependencies]
# Common # Common
chrono = { version = "0.4", features = ["serde"] } chrono = { version = "0.4", features = ["serde"] }
futures = "0.1" futures = "0.1"
swagger = "3.0" swagger = "4.0"
log = "0.3.0" log = "0.3.0"
mime = "0.3" mime = "0.3"
@ -35,9 +43,6 @@ uuid = {version = "0.7", features = ["serde", "v4"]}
# Common between server and client features # Common between server and client features
hyper = {version = "0.12", optional = true} 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_json = {version = "1.0", optional = true}
serde_ignored = {version = "0.0.4", optional = true} serde_ignored = {version = "0.0.4", optional = true}
tokio = {version = "0.1.17", 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 } lazy_static = { version = "1.4", optional = true }
percent-encoding = {version = "1.0.0", optional = true} percent-encoding = {version = "1.0.0", optional = true}
regex = {version = "0.2", optional = true} regex = {version = "0.2", optional = true}
tokio-tls = {version = "0.1.3", optional = true}
# Conversion # Conversion
frunk = { version = "0.3.0", optional = true } frunk = { version = "0.3.0", optional = true }
@ -63,6 +67,10 @@ clap = "2.25"
error-chain = "0.12" error-chain = "0.12"
env_logger = "0.6" 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]] [[example]]
name = "client" name = "client"
required-features = ["client"] required-features = ["client"]

View File

@ -12,11 +12,13 @@ extern crate chrono;
#[macro_use] #[macro_use]
extern crate error_chain; extern crate error_chain;
extern crate hyper; extern crate hyper;
extern crate openssl;
extern crate native_tls;
extern crate tokio_tls;
extern crate uuid; 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; mod server;
#[allow(unused_imports)] #[allow(unused_imports)]
@ -98,9 +100,7 @@ fn main() {
let client = if matches.is_present("https") { let client = if matches.is_present("https") {
// Using Simple HTTPS // Using Simple HTTPS
Client::try_new_https( Client::try_new_https(&base_url)
&base_url,
"examples/ca.pem")
.expect("Failed to create HTTPS client") .expect("Failed to create HTTPS client")
} else { } else {
// Using HTTP // Using HTTP
@ -117,7 +117,7 @@ fn main() {
let mut rt = tokio::runtime::Runtime::new().unwrap(); let mut rt = tokio::runtime::Runtime::new().unwrap();
// We could do HTTPS here, but for simplicity we don't // 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") { match matches.value_of("operation") {
Some("CallbackWithHeaderPost") => { Some("CallbackWithHeaderPost") => {

View File

@ -12,7 +12,6 @@ use chrono;
use futures::{future, Future, Stream}; use futures::{future, Future, Stream};
use hyper::server::conn::Http; use hyper::server::conn::Http;
use hyper::service::MakeService as _; use hyper::service::MakeService as _;
use native_tls;
use openssl::ssl::SslAcceptorBuilder; use openssl::ssl::SslAcceptorBuilder;
use std::collections::HashMap; use std::collections::HashMap;
use std::marker::PhantomData; use std::marker::PhantomData;
@ -23,12 +22,18 @@ use swagger::{Has, XSpanIdString};
use swagger::auth::MakeAllowAllAuthenticator; use swagger::auth::MakeAllowAllAuthenticator;
use swagger::EmptyContext; use swagger::EmptyContext;
use tokio::net::TcpListener; use tokio::net::TcpListener;
use tokio_tls::TlsAcceptorExt;
use uuid; 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; 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 addr = addr.parse().expect("Failed to parse bind address");
let server = Server::new(); let server = Server::new();
@ -42,9 +47,22 @@ pub fn create(addr: &str, https: Option<SslAcceptorBuilder>) -> Box<Future<Item
service_fn service_fn
); );
if let Some(ssl) = https { if https {
let builder: native_tls::TlsAcceptorBuilder = native_tls::backend::openssl::TlsAcceptorBuilderExt::from_openssl(ssl); #[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))]
let tls_acceptor = builder.build().expect("Failed to build TLS acceptor"); {
unimplemented!("SSL is not implemented for the examples on MacOS, Windows or iOS");
}
#[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");
// 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");
let tls_acceptor = ssl.build();
let service_fn = Arc::new(Mutex::new(service_fn)); let service_fn = Arc::new(Mutex::new(service_fn));
let tls_listener = TcpListener::bind(&addr).unwrap().incoming().for_each(move |tcp| { let tls_listener = TcpListener::bind(&addr).unwrap().incoming().for_each(move |tcp| {
let addr = tcp.peer_addr().expect("Unable to get remote address"); let addr = tcp.peer_addr().expect("Unable to get remote address");
@ -66,6 +84,7 @@ pub fn create(addr: &str, https: Option<SslAcceptorBuilder>) -> Box<Future<Item
}).map_err(|_| ()); }).map_err(|_| ());
Box::new(tls_listener) Box::new(tls_listener)
}
} else { } else {
// Using HTTP // Using HTTP
Box::new(hyper::server::Server::bind(&addr).serve(service_fn).map_err(|e| panic!("{:?}", e))) Box::new(hyper::server::Server::bind(&addr).serve(service_fn).map_err(|e| panic!("{:?}", e)))

View File

@ -10,7 +10,6 @@ extern crate env_logger;
extern crate hyper; extern crate hyper;
#[macro_use] #[macro_use]
extern crate log; extern crate log;
extern crate openssl;
extern crate swagger; extern crate swagger;
// Imports required by server library. // Imports required by server library.
@ -19,30 +18,19 @@ extern crate chrono;
#[macro_use] #[macro_use]
extern crate error_chain; extern crate error_chain;
extern crate futures; extern crate futures;
extern crate native_tls;
// extern crate swagger; // extern crate swagger;
extern crate tokio; extern crate tokio;
extern crate tokio_tls;
extern crate uuid; 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 clap::{App, Arg};
use openssl::x509::X509_FILETYPE_PEM;
use openssl::ssl::{SslAcceptorBuilder, SslMethod};
use openssl::error::ErrorStack;
mod server; 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, /// Create custom server, wire it to the autogenerated router,
/// and pass it to the web server. /// and pass it to the web server.
@ -57,11 +45,5 @@ fn main() {
let addr = "127.0.0.1:80"; let addr = "127.0.0.1:80";
let https = if matches.is_present("https") { hyper::rt::run(server::create(addr, matches.is_present("https")));
Some(ssl().expect("Failed to load SSL keys"))
} else {
None
};
hyper::rt::run(server::create(addr, https));
} }

View File

@ -12,7 +12,6 @@ use chrono;
use futures::{future, Future, Stream}; use futures::{future, Future, Stream};
use hyper::server::conn::Http; use hyper::server::conn::Http;
use hyper::service::MakeService as _; use hyper::service::MakeService as _;
use native_tls;
use openssl::ssl::SslAcceptorBuilder; use openssl::ssl::SslAcceptorBuilder;
use std::collections::HashMap; use std::collections::HashMap;
use std::marker::PhantomData; use std::marker::PhantomData;
@ -23,12 +22,18 @@ use swagger::{Has, XSpanIdString};
use swagger::auth::MakeAllowAllAuthenticator; use swagger::auth::MakeAllowAllAuthenticator;
use swagger::EmptyContext; use swagger::EmptyContext;
use tokio::net::TcpListener; use tokio::net::TcpListener;
use tokio_tls::TlsAcceptorExt;
use uuid; 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; 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 addr = addr.parse().expect("Failed to parse bind address");
let server = Server::new(); let server = Server::new();
@ -42,9 +47,22 @@ pub fn create(addr: &str, https: Option<SslAcceptorBuilder>) -> Box<Future<Item
service_fn service_fn
); );
if let Some(ssl) = https { if https {
let builder: native_tls::TlsAcceptorBuilder = native_tls::backend::openssl::TlsAcceptorBuilderExt::from_openssl(ssl); #[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))]
let tls_acceptor = builder.build().expect("Failed to build TLS acceptor"); {
unimplemented!("SSL is not implemented for the examples on MacOS, Windows or iOS");
}
#[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");
// 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");
let tls_acceptor = ssl.build();
let service_fn = Arc::new(Mutex::new(service_fn)); let service_fn = Arc::new(Mutex::new(service_fn));
let tls_listener = TcpListener::bind(&addr).unwrap().incoming().for_each(move |tcp| { let tls_listener = TcpListener::bind(&addr).unwrap().incoming().for_each(move |tcp| {
let addr = tcp.peer_addr().expect("Unable to get remote address"); let addr = tcp.peer_addr().expect("Unable to get remote address");
@ -66,6 +84,7 @@ pub fn create(addr: &str, https: Option<SslAcceptorBuilder>) -> Box<Future<Item
}).map_err(|_| ()); }).map_err(|_| ());
Box::new(tls_listener) Box::new(tls_listener)
}
} else { } else {
// Using HTTP // Using HTTP
Box::new(hyper::server::Server::bind(&addr).serve(service_fn).map_err(|e| panic!("{:?}", e))) Box::new(hyper::server::Server::bind(&addr).serve(service_fn).map_err(|e| panic!("{:?}", e)))

View File

@ -4,7 +4,8 @@ use hyper;
use hyper::client::HttpConnector; use hyper::client::HttpConnector;
use hyper::header::{HeaderName, HeaderValue, CONTENT_TYPE}; use hyper::header::{HeaderName, HeaderValue, CONTENT_TYPE};
use hyper::{Body, Uri, Response}; use hyper::{Body, 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 serde_json;
use std::borrow::Cow; use std::borrow::Cow;
#[allow(unused_imports)] #[allow(unused_imports)]
@ -18,9 +19,7 @@ use std::str;
use std::str::FromStr; use std::str::FromStr;
use std::string::ToString; use std::string::ToString;
use swagger; use swagger;
use swagger::client::Service; use swagger::{ApiError, Connector, client::Service, XSpanIdString, Has, AuthData};
use swagger::connector;
use swagger::{ApiError, XSpanIdString, Has, AuthData};
use url::form_urlencoded; use url::form_urlencoded;
use url::percent_encoding::{utf8_percent_encode, PATH_SEGMENT_ENCODE_SET, QUERY_ENCODE_SET}; use url::percent_encoding::{utf8_percent_encode, PATH_SEGMENT_ENCODE_SET, QUERY_ENCODE_SET};
use uuid; use uuid;
@ -114,8 +113,7 @@ impl Client<hyper::client::ResponseFuture>
/// ///
/// Intended for use with custom implementations of connect for e.g. protocol logging /// 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, /// or similar functionality which requires wrapping the transport layer. When wrapping a TCP connection,
/// this function should be used in conjunction with /// this function should be used in conjunction with `swagger::Connector::builder()`.
/// `swagger::{http_connector, https_connector, https_mutual_connector}`.
/// ///
/// For ordinary tcp connections, prefer the use of `try_new_http`, `try_new_https` /// 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. /// 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" /// * `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")` /// * `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>( pub fn try_new_with_connector<C>(
base_path: &str, base_path: &str,
protocol: Option<&'static str>, protocol: Option<&'static str>,
connector_fn: Box<dyn Fn() -> C + Send + Sync>, connector: C,
) -> Result<Self, ClientInitError> where ) -> Result<Self, ClientInitError> where
C: hyper::client::connect::Connect + 'static, C: hyper::client::connect::Connect + 'static,
C::Transport: 'static, C::Transport: 'static,
C::Future: 'static, C::Future: 'static,
{ {
let connector = connector_fn();
let client_service = Box::new(hyper::client::Client::builder().build(connector)); let client_service = Box::new(hyper::client::Client::builder().build(connector));
Ok(Client { Ok(Client {
@ -151,24 +147,42 @@ impl Client<hyper::client::ResponseFuture>
pub fn try_new_http( pub fn try_new_http(
base_path: &str, base_path: &str,
) -> Result<Self, ClientInitError> { ) -> 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) 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 /// # Arguments
/// * `base_path` - base path of the client API, i.e. "www.my-api-implementation.com" /// * `base_path` - base path of the client API, i.e. "www.my-api-implementation.com"
/// * `ca_certificate` - Path to CA certificate used to authenticate the server /// * `ca_certificate` - Path to CA certificate used to authenticate the server
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, base_path: &str,
ca_certificate: CA, ca_certificate: CA,
) -> Result<Self, ClientInitError> ) -> Result<Self, ClientInitError>
where where
CA: AsRef<Path>, 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) 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 /// * `ca_certificate` - Path to CA certificate used to authenticate the server
/// * `client_key` - Path to the client private key /// * `client_key` - Path to the client private key
/// * `client_certificate` - Path to the client's public certificate associated with the private key /// * `client_certificate` - Path to the client's public certificate associated with the private key
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
pub fn try_new_https_mutual<CA, K, D>( pub fn try_new_https_mutual<CA, K, D>(
base_path: &str, base_path: &str,
ca_certificate: CA, ca_certificate: CA,
@ -190,8 +205,12 @@ impl Client<hyper::client::ResponseFuture>
K: AsRef<Path>, K: AsRef<Path>,
D: AsRef<Path>, D: AsRef<Path>,
{ {
let https_connector = let https_connector = Connector::builder()
connector::https_mutual_connector(ca_certificate, client_key, client_certificate); .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) Self::try_new_with_connector(base_path, Some("https"), https_connector)
} }
} }
@ -225,7 +244,12 @@ pub enum ClientInitError {
MissingHost, MissingHost,
/// SSL Connection Error /// 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 { 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 { impl fmt::Display for ClientInitError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let s: &dyn fmt::Debug = self; let s: &dyn fmt::Debug = self;

View File

@ -31,12 +31,9 @@ extern crate swagger;
#[cfg(any(feature = "client", feature = "server"))] #[cfg(any(feature = "client", feature = "server"))]
extern crate hyper; extern crate hyper;
#[cfg(feature = "client")]
extern crate hyper_tls;
#[cfg(any(feature = "client", feature = "server"))] #[cfg(any(feature = "client", feature = "server"))]
extern crate openssl; #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
#[cfg(any(feature = "client", feature = "server"))] extern crate hyper_openssl;
extern crate native_tls;
#[cfg(any(feature = "client", feature = "server"))] #[cfg(any(feature = "client", feature = "server"))]
extern crate percent_encoding; extern crate percent_encoding;
#[cfg(any(feature = "client", feature = "server"))] #[cfg(any(feature = "client", feature = "server"))]

View File

@ -4,7 +4,8 @@ use hyper;
use hyper::client::HttpConnector; use hyper::client::HttpConnector;
use hyper::header::{HeaderName, HeaderValue, CONTENT_TYPE}; use hyper::header::{HeaderName, HeaderValue, CONTENT_TYPE};
use hyper::{Body, Uri, Response}; use hyper::{Body, 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 serde_json;
use std::borrow::Cow; use std::borrow::Cow;
#[allow(unused_imports)] #[allow(unused_imports)]
@ -18,9 +19,7 @@ use std::str;
use std::str::FromStr; use std::str::FromStr;
use std::string::ToString; use std::string::ToString;
use swagger; use swagger;
use swagger::client::Service; use swagger::{ApiError, Connector, client::Service, XSpanIdString, Has, AuthData};
use swagger::connector;
use swagger::{ApiError, XSpanIdString, Has, AuthData};
use url::form_urlencoded; use url::form_urlencoded;
use url::percent_encoding::{utf8_percent_encode, PATH_SEGMENT_ENCODE_SET, QUERY_ENCODE_SET}; use url::percent_encoding::{utf8_percent_encode, PATH_SEGMENT_ENCODE_SET, QUERY_ENCODE_SET};
use uuid; use uuid;
@ -71,24 +70,21 @@ impl Client<hyper::client::ResponseFuture>
/// ///
/// Intended for use with custom implementations of connect for e.g. protocol logging /// 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, /// or similar functionality which requires wrapping the transport layer. When wrapping a TCP connection,
/// this function should be used in conjunction with /// this function should be used in conjunction with `swagger::Connector::builder()`.
/// `swagger::{http_connector, https_connector, https_mutual_connector}`.
/// ///
/// For ordinary tcp connections, prefer the use of `new_http`, `new_https` /// 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. /// and `new_https_mutual`, to avoid introducing a dependency on the underlying transport layer.
/// ///
/// # Arguments /// # 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>( pub fn new_with_connector<C>(
connector_fn: Box<dyn Fn() -> C + Send + Sync>, connector: C,
) -> Self where ) -> Self where
C: hyper::client::connect::Connect + 'static, C: hyper::client::connect::Connect + 'static,
C::Transport: 'static, C::Transport: 'static,
C::Future: 'static, C::Future: 'static,
{ {
let connector = connector_fn();
let client_service = Box::new(hyper::client::Client::builder().build(connector)); let client_service = Box::new(hyper::client::Client::builder().build(connector));
Client { Client {
@ -98,21 +94,41 @@ impl Client<hyper::client::ResponseFuture>
/// Create an HTTP client. /// Create an HTTP client.
pub fn new_http() -> Self { pub fn new_http() -> Self {
let http_connector = connector::http_connector(); let http_connector = Connector::builder().build();
Self::new_with_connector(http_connector) Self::new_with_connector(http_connector)
} }
/// Create a client with a TLS connection to the server. /// Create a client with a TLS connection to the server.
#[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))]
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 /// # Arguments
/// * `ca_certificate` - Path to CA certificate used to authenticate the server /// * `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, ca_certificate: CA,
) -> Self where ) -> Result<Self, openssl::error::ErrorStack> where
CA: AsRef<Path>, CA: AsRef<Path>,
{ {
let https_connector = connector::https_connector(ca_certificate); let https_connector = Connector::builder()
Self::new_with_connector(https_connector) .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. /// 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 /// * `ca_certificate` - Path to CA certificate used to authenticate the server
/// * `client_key` - Path to the client private key /// * `client_key` - Path to the client private key
/// * `client_certificate` - Path to the client's public certificate associated with the private key /// * `client_certificate` - Path to the client's public certificate associated with the private key
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
pub fn new_https_mutual<CA, K, D>( pub fn new_https_mutual<CA, K, D>(
ca_certificate: CA, ca_certificate: CA,
client_key: K, client_key: K,
client_certificate: D, client_certificate: D,
) -> Self ) -> Result<Self, openssl::error::ErrorStack>
where where
CA: AsRef<Path>, CA: AsRef<Path>,
K: AsRef<Path>, K: AsRef<Path>,
D: AsRef<Path>, D: AsRef<Path>,
{ {
let https_connector = let https_connector = Connector::builder()
connector::https_mutual_connector(ca_certificate, client_key, client_certificate); .https()
Self::new_with_connector(https_connector) .pin_server_certificate(ca_certificate)
.client_authentication(client_key, client_certificate)
.build()?;
Ok(Self::new_with_connector(https_connector))
} }
} }

View File

@ -8,18 +8,25 @@ license = "Unlicense"
[features] [features]
default = ["client", "server"] default = ["client", "server"]
client = [ 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 = [ 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"] 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] [dependencies]
# Common # Common
chrono = { version = "0.4", features = ["serde"] } chrono = { version = "0.4", features = ["serde"] }
futures = "0.1" futures = "0.1"
swagger = "3.0" swagger = "4.0"
log = "0.3.0" log = "0.3.0"
mime = "0.3" mime = "0.3"
@ -30,9 +37,6 @@ serde_derive = "1.0"
# Common between server and client features # Common between server and client features
hyper = {version = "0.12", optional = true} 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_json = {version = "1.0", optional = true}
serde_ignored = {version = "0.0.4", optional = true} serde_ignored = {version = "0.0.4", optional = true}
tokio = {version = "0.1.17", 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 } lazy_static = { version = "1.4", optional = true }
percent-encoding = {version = "1.0.0", optional = true} percent-encoding = {version = "1.0.0", optional = true}
regex = {version = "0.2", optional = true} regex = {version = "0.2", optional = true}
tokio-tls = {version = "0.1.3", optional = true}
# Conversion # Conversion
frunk = { version = "0.3.0", optional = true } frunk = { version = "0.3.0", optional = true }
@ -59,6 +62,10 @@ error-chain = "0.12"
env_logger = "0.6" env_logger = "0.6"
uuid = {version = "0.7", features = ["serde", "v4"]} 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]] [[example]]
name = "client" name = "client"
required-features = ["client"] required-features = ["client"]

View File

@ -125,9 +125,7 @@ fn main() {
let client = if matches.is_present("https") { let client = if matches.is_present("https") {
// Using Simple HTTPS // Using Simple HTTPS
Client::try_new_https( Client::try_new_https(&base_url)
&base_url,
"examples/ca.pem")
.expect("Failed to create HTTPS client") .expect("Failed to create HTTPS client")
} else { } else {
// Using HTTP // Using HTTP

View File

@ -10,7 +10,6 @@ extern crate env_logger;
extern crate hyper; extern crate hyper;
#[macro_use] #[macro_use]
extern crate log; extern crate log;
extern crate openssl;
extern crate swagger; extern crate swagger;
// Imports required by server library. // Imports required by server library.
@ -19,29 +18,18 @@ extern crate chrono;
#[macro_use] #[macro_use]
extern crate error_chain; extern crate error_chain;
extern crate futures; extern crate futures;
extern crate native_tls;
// extern crate swagger; // extern crate swagger;
extern crate tokio; 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 clap::{App, Arg};
use openssl::x509::X509_FILETYPE_PEM;
use openssl::ssl::{SslAcceptorBuilder, SslMethod};
use openssl::error::ErrorStack;
mod server; 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, /// Create custom server, wire it to the autogenerated router,
/// and pass it to the web server. /// and pass it to the web server.
@ -56,11 +44,5 @@ fn main() {
let addr = "127.0.0.1:80"; let addr = "127.0.0.1:80";
let https = if matches.is_present("https") { hyper::rt::run(server::create(addr, matches.is_present("https")));
Some(ssl().expect("Failed to load SSL keys"))
} else {
None
};
hyper::rt::run(server::create(addr, https));
} }

View File

@ -12,7 +12,6 @@ use chrono;
use futures::{future, Future, Stream}; use futures::{future, Future, Stream};
use hyper::server::conn::Http; use hyper::server::conn::Http;
use hyper::service::MakeService as _; use hyper::service::MakeService as _;
use native_tls;
use openssl::ssl::SslAcceptorBuilder; use openssl::ssl::SslAcceptorBuilder;
use std::collections::HashMap; use std::collections::HashMap;
use std::marker::PhantomData; use std::marker::PhantomData;
@ -23,12 +22,18 @@ use swagger::{Has, XSpanIdString};
use swagger::auth::MakeAllowAllAuthenticator; use swagger::auth::MakeAllowAllAuthenticator;
use swagger::EmptyContext; use swagger::EmptyContext;
use tokio::net::TcpListener; use tokio::net::TcpListener;
use 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; 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 addr = addr.parse().expect("Failed to parse bind address");
let server = Server::new(); let server = Server::new();
@ -42,9 +47,22 @@ pub fn create(addr: &str, https: Option<SslAcceptorBuilder>) -> Box<Future<Item
service_fn service_fn
); );
if let Some(ssl) = https { if https {
let builder: native_tls::TlsAcceptorBuilder = native_tls::backend::openssl::TlsAcceptorBuilderExt::from_openssl(ssl); #[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))]
let tls_acceptor = builder.build().expect("Failed to build TLS acceptor"); {
unimplemented!("SSL is not implemented for the examples on MacOS, Windows or iOS");
}
#[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");
// 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");
let tls_acceptor = ssl.build();
let service_fn = Arc::new(Mutex::new(service_fn)); let service_fn = Arc::new(Mutex::new(service_fn));
let tls_listener = TcpListener::bind(&addr).unwrap().incoming().for_each(move |tcp| { let tls_listener = TcpListener::bind(&addr).unwrap().incoming().for_each(move |tcp| {
let addr = tcp.peer_addr().expect("Unable to get remote address"); let addr = tcp.peer_addr().expect("Unable to get remote address");
@ -66,6 +84,7 @@ pub fn create(addr: &str, https: Option<SslAcceptorBuilder>) -> Box<Future<Item
}).map_err(|_| ()); }).map_err(|_| ());
Box::new(tls_listener) Box::new(tls_listener)
}
} else { } else {
// Using HTTP // Using HTTP
Box::new(hyper::server::Server::bind(&addr).serve(service_fn).map_err(|e| panic!("{:?}", e))) Box::new(hyper::server::Server::bind(&addr).serve(service_fn).map_err(|e| panic!("{:?}", e)))

View File

@ -4,7 +4,8 @@ use hyper;
use hyper::client::HttpConnector; use hyper::client::HttpConnector;
use hyper::header::{HeaderName, HeaderValue, CONTENT_TYPE}; use hyper::header::{HeaderName, HeaderValue, CONTENT_TYPE};
use hyper::{Body, Uri, Response}; use hyper::{Body, 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 serde_json;
use std::borrow::Cow; use std::borrow::Cow;
#[allow(unused_imports)] #[allow(unused_imports)]
@ -18,9 +19,7 @@ use std::str;
use std::str::FromStr; use std::str::FromStr;
use std::string::ToString; use std::string::ToString;
use swagger; use swagger;
use swagger::client::Service; use swagger::{ApiError, Connector, client::Service, XSpanIdString, Has, AuthData};
use swagger::connector;
use swagger::{ApiError, XSpanIdString, Has, AuthData};
use url::form_urlencoded; use url::form_urlencoded;
use url::percent_encoding::{utf8_percent_encode, PATH_SEGMENT_ENCODE_SET, QUERY_ENCODE_SET}; 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 /// 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, /// or similar functionality which requires wrapping the transport layer. When wrapping a TCP connection,
/// this function should be used in conjunction with /// this function should be used in conjunction with `swagger::Connector::builder()`.
/// `swagger::{http_connector, https_connector, https_mutual_connector}`.
/// ///
/// For ordinary tcp connections, prefer the use of `try_new_http`, `try_new_https` /// 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. /// 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" /// * `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")` /// * `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>( pub fn try_new_with_connector<C>(
base_path: &str, base_path: &str,
protocol: Option<&'static str>, protocol: Option<&'static str>,
connector_fn: Box<dyn Fn() -> C + Send + Sync>, connector: C,
) -> Result<Self, ClientInitError> where ) -> Result<Self, ClientInitError> where
C: hyper::client::connect::Connect + 'static, C: hyper::client::connect::Connect + 'static,
C::Transport: 'static, C::Transport: 'static,
C::Future: 'static, C::Future: 'static,
{ {
let connector = connector_fn();
let client_service = Box::new(hyper::client::Client::builder().build(connector)); let client_service = Box::new(hyper::client::Client::builder().build(connector));
Ok(Client { Ok(Client {
@ -165,24 +161,42 @@ impl Client<hyper::client::ResponseFuture>
pub fn try_new_http( pub fn try_new_http(
base_path: &str, base_path: &str,
) -> Result<Self, ClientInitError> { ) -> 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) 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 /// # Arguments
/// * `base_path` - base path of the client API, i.e. "www.my-api-implementation.com" /// * `base_path` - base path of the client API, i.e. "www.my-api-implementation.com"
/// * `ca_certificate` - Path to CA certificate used to authenticate the server /// * `ca_certificate` - Path to CA certificate used to authenticate the server
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, base_path: &str,
ca_certificate: CA, ca_certificate: CA,
) -> Result<Self, ClientInitError> ) -> Result<Self, ClientInitError>
where where
CA: AsRef<Path>, 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) 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 /// * `ca_certificate` - Path to CA certificate used to authenticate the server
/// * `client_key` - Path to the client private key /// * `client_key` - Path to the client private key
/// * `client_certificate` - Path to the client's public certificate associated with the private key /// * `client_certificate` - Path to the client's public certificate associated with the private key
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
pub fn try_new_https_mutual<CA, K, D>( pub fn try_new_https_mutual<CA, K, D>(
base_path: &str, base_path: &str,
ca_certificate: CA, ca_certificate: CA,
@ -204,8 +219,12 @@ impl Client<hyper::client::ResponseFuture>
K: AsRef<Path>, K: AsRef<Path>,
D: AsRef<Path>, D: AsRef<Path>,
{ {
let https_connector = let https_connector = Connector::builder()
connector::https_mutual_connector(ca_certificate, client_key, client_certificate); .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) Self::try_new_with_connector(base_path, Some("https"), https_connector)
} }
} }
@ -239,7 +258,12 @@ pub enum ClientInitError {
MissingHost, MissingHost,
/// SSL Connection Error /// 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 { 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 { impl fmt::Display for ClientInitError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let s: &dyn fmt::Debug = self; let s: &dyn fmt::Debug = self;

View File

@ -31,12 +31,9 @@ extern crate swagger;
#[cfg(any(feature = "client", feature = "server"))] #[cfg(any(feature = "client", feature = "server"))]
extern crate hyper; extern crate hyper;
#[cfg(feature = "client")] #[cfg(feature = "server")]
extern crate hyper_tls; #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
#[cfg(any(feature = "client", feature = "server"))] extern crate hyper_openssl;
extern crate openssl;
#[cfg(any(feature = "client", feature = "server"))]
extern crate native_tls;
#[cfg(feature = "server")] #[cfg(feature = "server")]
extern crate percent_encoding; extern crate percent_encoding;
#[cfg(any(feature = "client", feature = "server"))] #[cfg(any(feature = "client", feature = "server"))]

View File

@ -11,20 +11,27 @@ client = [
"mime_0_2", "mime_0_2",
"multipart", "multipart/client", "swagger/multipart", "multipart", "multipart/client", "swagger/multipart",
"serde_urlencoded", "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 = [ server = [
"mime_0_2", "mime_0_2",
"multipart", "multipart/server", "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"] 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] [dependencies]
# Common # Common
chrono = { version = "0.4", features = ["serde"] } chrono = { version = "0.4", features = ["serde"] }
futures = "0.1" futures = "0.1"
swagger = "3.0" swagger = "4.0"
log = "0.3.0" log = "0.3.0"
mime = "0.3" mime = "0.3"
@ -41,9 +48,6 @@ uuid = {version = "0.7", features = ["serde", "v4"]}
# Common between server and client features # Common between server and client features
hyper = {version = "0.12", optional = true} 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_json = {version = "1.0", optional = true}
serde_ignored = {version = "0.0.4", optional = true} serde_ignored = {version = "0.0.4", optional = true}
tokio = {version = "0.1.17", 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 } lazy_static = { version = "1.4", optional = true }
percent-encoding = {version = "1.0.0", optional = true} percent-encoding = {version = "1.0.0", optional = true}
regex = {version = "0.2", optional = true} regex = {version = "0.2", optional = true}
tokio-tls = {version = "0.1.3", optional = true}
# Conversion # Conversion
frunk = { version = "0.3.0", optional = true } frunk = { version = "0.3.0", optional = true }
@ -70,6 +73,10 @@ clap = "2.25"
error-chain = "0.12" error-chain = "0.12"
env_logger = "0.6" 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]] [[example]]
name = "client" name = "client"
required-features = ["client"] required-features = ["client"]

View File

@ -111,9 +111,7 @@ fn main() {
let client = if matches.is_present("https") { let client = if matches.is_present("https") {
// Using Simple HTTPS // Using Simple HTTPS
Client::try_new_https( Client::try_new_https(&base_url)
&base_url,
"examples/ca.pem")
.expect("Failed to create HTTPS client") .expect("Failed to create HTTPS client")
} else { } else {
// Using HTTP // Using HTTP

View File

@ -10,7 +10,6 @@ extern crate env_logger;
extern crate hyper; extern crate hyper;
#[macro_use] #[macro_use]
extern crate log; extern crate log;
extern crate openssl;
extern crate swagger; extern crate swagger;
// Imports required by server library. // Imports required by server library.
@ -19,30 +18,19 @@ extern crate chrono;
#[macro_use] #[macro_use]
extern crate error_chain; extern crate error_chain;
extern crate futures; extern crate futures;
extern crate native_tls;
// extern crate swagger; // extern crate swagger;
extern crate tokio; extern crate tokio;
extern crate tokio_tls;
extern crate uuid; 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 clap::{App, Arg};
use openssl::x509::X509_FILETYPE_PEM;
use openssl::ssl::{SslAcceptorBuilder, SslMethod};
use openssl::error::ErrorStack;
mod server; 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, /// Create custom server, wire it to the autogenerated router,
/// and pass it to the web server. /// and pass it to the web server.
@ -57,11 +45,5 @@ fn main() {
let addr = "127.0.0.1:80"; let addr = "127.0.0.1:80";
let https = if matches.is_present("https") { hyper::rt::run(server::create(addr, matches.is_present("https")));
Some(ssl().expect("Failed to load SSL keys"))
} else {
None
};
hyper::rt::run(server::create(addr, https));
} }

View File

@ -12,7 +12,6 @@ use chrono;
use futures::{future, Future, Stream}; use futures::{future, Future, Stream};
use hyper::server::conn::Http; use hyper::server::conn::Http;
use hyper::service::MakeService as _; use hyper::service::MakeService as _;
use native_tls;
use openssl::ssl::SslAcceptorBuilder; use openssl::ssl::SslAcceptorBuilder;
use std::collections::HashMap; use std::collections::HashMap;
use std::marker::PhantomData; use std::marker::PhantomData;
@ -23,12 +22,18 @@ use swagger::{Has, XSpanIdString};
use swagger::auth::MakeAllowAllAuthenticator; use swagger::auth::MakeAllowAllAuthenticator;
use swagger::EmptyContext; use swagger::EmptyContext;
use tokio::net::TcpListener; use tokio::net::TcpListener;
use tokio_tls::TlsAcceptorExt;
use uuid; 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; 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 addr = addr.parse().expect("Failed to parse bind address");
let server = Server::new(); let server = Server::new();
@ -42,9 +47,22 @@ pub fn create(addr: &str, https: Option<SslAcceptorBuilder>) -> Box<Future<Item
service_fn service_fn
); );
if let Some(ssl) = https { if https {
let builder: native_tls::TlsAcceptorBuilder = native_tls::backend::openssl::TlsAcceptorBuilderExt::from_openssl(ssl); #[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))]
let tls_acceptor = builder.build().expect("Failed to build TLS acceptor"); {
unimplemented!("SSL is not implemented for the examples on MacOS, Windows or iOS");
}
#[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");
// 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");
let tls_acceptor = ssl.build();
let service_fn = Arc::new(Mutex::new(service_fn)); let service_fn = Arc::new(Mutex::new(service_fn));
let tls_listener = TcpListener::bind(&addr).unwrap().incoming().for_each(move |tcp| { let tls_listener = TcpListener::bind(&addr).unwrap().incoming().for_each(move |tcp| {
let addr = tcp.peer_addr().expect("Unable to get remote address"); let addr = tcp.peer_addr().expect("Unable to get remote address");
@ -66,6 +84,7 @@ pub fn create(addr: &str, https: Option<SslAcceptorBuilder>) -> Box<Future<Item
}).map_err(|_| ()); }).map_err(|_| ());
Box::new(tls_listener) Box::new(tls_listener)
}
} else { } else {
// Using HTTP // Using HTTP
Box::new(hyper::server::Server::bind(&addr).serve(service_fn).map_err(|e| panic!("{:?}", e))) Box::new(hyper::server::Server::bind(&addr).serve(service_fn).map_err(|e| panic!("{:?}", e)))

View File

@ -4,7 +4,8 @@ use hyper;
use hyper::client::HttpConnector; use hyper::client::HttpConnector;
use hyper::header::{HeaderName, HeaderValue, CONTENT_TYPE}; use hyper::header::{HeaderName, HeaderValue, CONTENT_TYPE};
use hyper::{Body, Uri, Response}; use hyper::{Body, 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 serde_json;
use std::borrow::Cow; use std::borrow::Cow;
#[allow(unused_imports)] #[allow(unused_imports)]
@ -18,9 +19,7 @@ use std::str;
use std::str::FromStr; use std::str::FromStr;
use std::string::ToString; use std::string::ToString;
use swagger; use swagger;
use swagger::client::Service; use swagger::{ApiError, Connector, client::Service, XSpanIdString, Has, AuthData};
use swagger::connector;
use swagger::{ApiError, XSpanIdString, Has, AuthData};
use url::form_urlencoded; use url::form_urlencoded;
use url::percent_encoding::{utf8_percent_encode, PATH_SEGMENT_ENCODE_SET, QUERY_ENCODE_SET}; use url::percent_encoding::{utf8_percent_encode, PATH_SEGMENT_ENCODE_SET, QUERY_ENCODE_SET};
use mime::Mime; 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 /// 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, /// or similar functionality which requires wrapping the transport layer. When wrapping a TCP connection,
/// this function should be used in conjunction with /// this function should be used in conjunction with `swagger::Connector::builder()`.
/// `swagger::{http_connector, https_connector, https_mutual_connector}`.
/// ///
/// For ordinary tcp connections, prefer the use of `try_new_http`, `try_new_https` /// 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. /// 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" /// * `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")` /// * `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>( pub fn try_new_with_connector<C>(
base_path: &str, base_path: &str,
protocol: Option<&'static str>, protocol: Option<&'static str>,
connector_fn: Box<dyn Fn() -> C + Send + Sync>, connector: C,
) -> Result<Self, ClientInitError> where ) -> Result<Self, ClientInitError> where
C: hyper::client::connect::Connect + 'static, C: hyper::client::connect::Connect + 'static,
C::Transport: 'static, C::Transport: 'static,
C::Future: 'static, C::Future: 'static,
{ {
let connector = connector_fn();
let client_service = Box::new(hyper::client::Client::builder().build(connector)); let client_service = Box::new(hyper::client::Client::builder().build(connector));
Ok(Client { Ok(Client {
@ -168,24 +164,42 @@ impl Client<hyper::client::ResponseFuture>
pub fn try_new_http( pub fn try_new_http(
base_path: &str, base_path: &str,
) -> Result<Self, ClientInitError> { ) -> 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) 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 /// # Arguments
/// * `base_path` - base path of the client API, i.e. "www.my-api-implementation.com" /// * `base_path` - base path of the client API, i.e. "www.my-api-implementation.com"
/// * `ca_certificate` - Path to CA certificate used to authenticate the server /// * `ca_certificate` - Path to CA certificate used to authenticate the server
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, base_path: &str,
ca_certificate: CA, ca_certificate: CA,
) -> Result<Self, ClientInitError> ) -> Result<Self, ClientInitError>
where where
CA: AsRef<Path>, 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) 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 /// * `ca_certificate` - Path to CA certificate used to authenticate the server
/// * `client_key` - Path to the client private key /// * `client_key` - Path to the client private key
/// * `client_certificate` - Path to the client's public certificate associated with the private key /// * `client_certificate` - Path to the client's public certificate associated with the private key
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
pub fn try_new_https_mutual<CA, K, D>( pub fn try_new_https_mutual<CA, K, D>(
base_path: &str, base_path: &str,
ca_certificate: CA, ca_certificate: CA,
@ -207,8 +222,12 @@ impl Client<hyper::client::ResponseFuture>
K: AsRef<Path>, K: AsRef<Path>,
D: AsRef<Path>, D: AsRef<Path>,
{ {
let https_connector = let https_connector = Connector::builder()
connector::https_mutual_connector(ca_certificate, client_key, client_certificate); .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) Self::try_new_with_connector(base_path, Some("https"), https_connector)
} }
} }
@ -242,7 +261,12 @@ pub enum ClientInitError {
MissingHost, MissingHost,
/// SSL Connection Error /// 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 { 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 { impl fmt::Display for ClientInitError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let s: &dyn fmt::Debug = self; let s: &dyn fmt::Debug = self;

View File

@ -31,14 +31,11 @@ extern crate swagger;
#[cfg(any(feature = "client", feature = "server"))] #[cfg(any(feature = "client", feature = "server"))]
extern crate hyper; extern crate hyper;
#[cfg(feature = "client")] #[cfg(feature = "server")]
extern crate hyper_tls; #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
#[cfg(any(feature = "client", feature = "server"))] extern crate hyper_openssl;
extern crate openssl;
#[cfg(any(feature = "client", feature = "server"))] #[cfg(any(feature = "client", feature = "server"))]
extern crate mime_0_2; extern crate mime_0_2;
#[cfg(any(feature = "client", feature = "server"))]
extern crate native_tls;
#[cfg(feature = "server")] #[cfg(feature = "server")]
extern crate percent_encoding; extern crate percent_encoding;
#[cfg(any(feature = "client", feature = "server"))] #[cfg(any(feature = "client", feature = "server"))]

View File

@ -8,18 +8,25 @@ license = "Unlicense"
[features] [features]
default = ["client", "server"] default = ["client", "server"]
client = [ 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 = [ 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"] 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] [dependencies]
# Common # Common
chrono = { version = "0.4", features = ["serde"] } chrono = { version = "0.4", features = ["serde"] }
futures = "0.1" futures = "0.1"
swagger = "3.0" swagger = "4.0"
log = "0.3.0" log = "0.3.0"
mime = "0.3" mime = "0.3"
@ -30,9 +37,6 @@ serde_derive = "1.0"
# Common between server and client features # Common between server and client features
hyper = {version = "0.12", optional = true} 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_json = {version = "1.0", optional = true}
serde_ignored = {version = "0.0.4", optional = true} serde_ignored = {version = "0.0.4", optional = true}
tokio = {version = "0.1.17", 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 } lazy_static = { version = "1.4", optional = true }
percent-encoding = {version = "1.0.0", optional = true} percent-encoding = {version = "1.0.0", optional = true}
regex = {version = "0.2", optional = true} regex = {version = "0.2", optional = true}
tokio-tls = {version = "0.1.3", optional = true}
# Conversion # Conversion
frunk = { version = "0.3.0", optional = true } frunk = { version = "0.3.0", optional = true }
@ -59,6 +62,10 @@ error-chain = "0.12"
env_logger = "0.6" env_logger = "0.6"
uuid = {version = "0.7", features = ["serde", "v4"]} 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]] [[example]]
name = "client" name = "client"
required-features = ["client"] required-features = ["client"]

View File

@ -67,9 +67,7 @@ fn main() {
let client = if matches.is_present("https") { let client = if matches.is_present("https") {
// Using Simple HTTPS // Using Simple HTTPS
Client::try_new_https( Client::try_new_https(&base_url)
&base_url,
"examples/ca.pem")
.expect("Failed to create HTTPS client") .expect("Failed to create HTTPS client")
} else { } else {
// Using HTTP // Using HTTP

View File

@ -10,7 +10,6 @@ extern crate env_logger;
extern crate hyper; extern crate hyper;
#[macro_use] #[macro_use]
extern crate log; extern crate log;
extern crate openssl;
extern crate swagger; extern crate swagger;
// Imports required by server library. // Imports required by server library.
@ -19,29 +18,18 @@ extern crate chrono;
#[macro_use] #[macro_use]
extern crate error_chain; extern crate error_chain;
extern crate futures; extern crate futures;
extern crate native_tls;
// extern crate swagger; // extern crate swagger;
extern crate tokio; 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 clap::{App, Arg};
use openssl::x509::X509_FILETYPE_PEM;
use openssl::ssl::{SslAcceptorBuilder, SslMethod};
use openssl::error::ErrorStack;
mod server; 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, /// Create custom server, wire it to the autogenerated router,
/// and pass it to the web server. /// and pass it to the web server.
@ -56,11 +44,5 @@ fn main() {
let addr = "127.0.0.1:80"; let addr = "127.0.0.1:80";
let https = if matches.is_present("https") { hyper::rt::run(server::create(addr, matches.is_present("https")));
Some(ssl().expect("Failed to load SSL keys"))
} else {
None
};
hyper::rt::run(server::create(addr, https));
} }

View File

@ -12,7 +12,6 @@ use chrono;
use futures::{future, Future, Stream}; use futures::{future, Future, Stream};
use hyper::server::conn::Http; use hyper::server::conn::Http;
use hyper::service::MakeService as _; use hyper::service::MakeService as _;
use native_tls;
use openssl::ssl::SslAcceptorBuilder; use openssl::ssl::SslAcceptorBuilder;
use std::collections::HashMap; use std::collections::HashMap;
use std::marker::PhantomData; use std::marker::PhantomData;
@ -23,12 +22,18 @@ use swagger::{Has, XSpanIdString};
use swagger::auth::MakeAllowAllAuthenticator; use swagger::auth::MakeAllowAllAuthenticator;
use swagger::EmptyContext; use swagger::EmptyContext;
use tokio::net::TcpListener; use tokio::net::TcpListener;
use 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; 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 addr = addr.parse().expect("Failed to parse bind address");
let server = Server::new(); let server = Server::new();
@ -42,9 +47,22 @@ pub fn create(addr: &str, https: Option<SslAcceptorBuilder>) -> Box<Future<Item
service_fn service_fn
); );
if let Some(ssl) = https { if https {
let builder: native_tls::TlsAcceptorBuilder = native_tls::backend::openssl::TlsAcceptorBuilderExt::from_openssl(ssl); #[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))]
let tls_acceptor = builder.build().expect("Failed to build TLS acceptor"); {
unimplemented!("SSL is not implemented for the examples on MacOS, Windows or iOS");
}
#[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");
// 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");
let tls_acceptor = ssl.build();
let service_fn = Arc::new(Mutex::new(service_fn)); let service_fn = Arc::new(Mutex::new(service_fn));
let tls_listener = TcpListener::bind(&addr).unwrap().incoming().for_each(move |tcp| { let tls_listener = TcpListener::bind(&addr).unwrap().incoming().for_each(move |tcp| {
let addr = tcp.peer_addr().expect("Unable to get remote address"); let addr = tcp.peer_addr().expect("Unable to get remote address");
@ -66,6 +84,7 @@ pub fn create(addr: &str, https: Option<SslAcceptorBuilder>) -> Box<Future<Item
}).map_err(|_| ()); }).map_err(|_| ());
Box::new(tls_listener) Box::new(tls_listener)
}
} else { } else {
// Using HTTP // Using HTTP
Box::new(hyper::server::Server::bind(&addr).serve(service_fn).map_err(|e| panic!("{:?}", e))) Box::new(hyper::server::Server::bind(&addr).serve(service_fn).map_err(|e| panic!("{:?}", e)))

View File

@ -4,7 +4,8 @@ use hyper;
use hyper::client::HttpConnector; use hyper::client::HttpConnector;
use hyper::header::{HeaderName, HeaderValue, CONTENT_TYPE}; use hyper::header::{HeaderName, HeaderValue, CONTENT_TYPE};
use hyper::{Body, Uri, Response}; use hyper::{Body, 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 serde_json;
use std::borrow::Cow; use std::borrow::Cow;
#[allow(unused_imports)] #[allow(unused_imports)]
@ -18,9 +19,7 @@ use std::str;
use std::str::FromStr; use std::str::FromStr;
use std::string::ToString; use std::string::ToString;
use swagger; use swagger;
use swagger::client::Service; use swagger::{ApiError, Connector, client::Service, XSpanIdString, Has, AuthData};
use swagger::connector;
use swagger::{ApiError, XSpanIdString, Has, AuthData};
use url::form_urlencoded; use url::form_urlencoded;
use url::percent_encoding::{utf8_percent_encode, PATH_SEGMENT_ENCODE_SET, QUERY_ENCODE_SET}; 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 /// 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, /// or similar functionality which requires wrapping the transport layer. When wrapping a TCP connection,
/// this function should be used in conjunction with /// this function should be used in conjunction with `swagger::Connector::builder()`.
/// `swagger::{http_connector, https_connector, https_mutual_connector}`.
/// ///
/// For ordinary tcp connections, prefer the use of `try_new_http`, `try_new_https` /// 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. /// 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" /// * `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")` /// * `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>( pub fn try_new_with_connector<C>(
base_path: &str, base_path: &str,
protocol: Option<&'static str>, protocol: Option<&'static str>,
connector_fn: Box<dyn Fn() -> C + Send + Sync>, connector: C,
) -> Result<Self, ClientInitError> where ) -> Result<Self, ClientInitError> where
C: hyper::client::connect::Connect + 'static, C: hyper::client::connect::Connect + 'static,
C::Transport: 'static, C::Transport: 'static,
C::Future: 'static, C::Future: 'static,
{ {
let connector = connector_fn();
let client_service = Box::new(hyper::client::Client::builder().build(connector)); let client_service = Box::new(hyper::client::Client::builder().build(connector));
Ok(Client { Ok(Client {
@ -137,24 +133,42 @@ impl Client<hyper::client::ResponseFuture>
pub fn try_new_http( pub fn try_new_http(
base_path: &str, base_path: &str,
) -> Result<Self, ClientInitError> { ) -> 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) 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 /// # Arguments
/// * `base_path` - base path of the client API, i.e. "www.my-api-implementation.com" /// * `base_path` - base path of the client API, i.e. "www.my-api-implementation.com"
/// * `ca_certificate` - Path to CA certificate used to authenticate the server /// * `ca_certificate` - Path to CA certificate used to authenticate the server
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, base_path: &str,
ca_certificate: CA, ca_certificate: CA,
) -> Result<Self, ClientInitError> ) -> Result<Self, ClientInitError>
where where
CA: AsRef<Path>, 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) 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 /// * `ca_certificate` - Path to CA certificate used to authenticate the server
/// * `client_key` - Path to the client private key /// * `client_key` - Path to the client private key
/// * `client_certificate` - Path to the client's public certificate associated with the private key /// * `client_certificate` - Path to the client's public certificate associated with the private key
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
pub fn try_new_https_mutual<CA, K, D>( pub fn try_new_https_mutual<CA, K, D>(
base_path: &str, base_path: &str,
ca_certificate: CA, ca_certificate: CA,
@ -176,8 +191,12 @@ impl Client<hyper::client::ResponseFuture>
K: AsRef<Path>, K: AsRef<Path>,
D: AsRef<Path>, D: AsRef<Path>,
{ {
let https_connector = let https_connector = Connector::builder()
connector::https_mutual_connector(ca_certificate, client_key, client_certificate); .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) Self::try_new_with_connector(base_path, Some("https"), https_connector)
} }
} }
@ -211,7 +230,12 @@ pub enum ClientInitError {
MissingHost, MissingHost,
/// SSL Connection Error /// 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 { 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 { impl fmt::Display for ClientInitError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let s: &dyn fmt::Debug = self; let s: &dyn fmt::Debug = self;

View File

@ -31,12 +31,9 @@ extern crate swagger;
#[cfg(any(feature = "client", feature = "server"))] #[cfg(any(feature = "client", feature = "server"))]
extern crate hyper; extern crate hyper;
#[cfg(feature = "client")] #[cfg(feature = "server")]
extern crate hyper_tls; #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
#[cfg(any(feature = "client", feature = "server"))] extern crate hyper_openssl;
extern crate openssl;
#[cfg(any(feature = "client", feature = "server"))]
extern crate native_tls;
#[cfg(feature = "server")] #[cfg(feature = "server")]
extern crate percent_encoding; extern crate percent_encoding;
#[cfg(any(feature = "client", feature = "server"))] #[cfg(any(feature = "client", feature = "server"))]