feat(ssl): redesign SSL usage
BREAKING CHANGE: Server::https was changed to allow any implementation of Ssl. Server in general was also changed. HttpConnector no longer uses SSL; using HttpsConnector instead.
This commit is contained in:
@@ -6,6 +6,8 @@ matrix:
|
|||||||
env: FEATURES="--features nightly"
|
env: FEATURES="--features nightly"
|
||||||
- rust: beta
|
- rust: beta
|
||||||
- rust: stable
|
- rust: stable
|
||||||
|
- rust: stable
|
||||||
|
env: FEATURES="--no-default-features"
|
||||||
|
|
||||||
sudo: false
|
sudo: false
|
||||||
|
|
||||||
|
|||||||
23
Cargo.toml
23
Cargo.toml
@@ -12,22 +12,33 @@ authors = ["Sean McArthur <sean.monstar@gmail.com>",
|
|||||||
keywords = ["http", "hyper", "hyperium"]
|
keywords = ["http", "hyper", "hyperium"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cookie = "0.1"
|
|
||||||
httparse = "0.1"
|
httparse = "0.1"
|
||||||
log = "0.3"
|
log = "0.3"
|
||||||
mime = "0.0.11"
|
mime = "0.0.12"
|
||||||
num_cpus = "0.2"
|
num_cpus = "0.2"
|
||||||
openssl = "0.6"
|
|
||||||
rustc-serialize = "0.3"
|
rustc-serialize = "0.3"
|
||||||
time = "0.1"
|
time = "0.1"
|
||||||
unicase = "0.1"
|
|
||||||
url = "0.2"
|
|
||||||
traitobject = "0.0.1"
|
traitobject = "0.0.1"
|
||||||
typeable = "0.1"
|
typeable = "0.1"
|
||||||
solicit = "0.2"
|
unicase = "0.1"
|
||||||
|
url = "0.2"
|
||||||
|
|
||||||
|
[dependencies.cookie]
|
||||||
|
version = "0.1"
|
||||||
|
default-features = false
|
||||||
|
|
||||||
|
[dependencies.openssl]
|
||||||
|
version = "0.6"
|
||||||
|
optional = true
|
||||||
|
|
||||||
|
[dependencies.solicit]
|
||||||
|
version = "0.3"
|
||||||
|
default-features = false
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
env_logger = "*"
|
env_logger = "*"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
default = ["ssl"]
|
||||||
|
ssl = ["openssl", "cookie/secure", "solicit/openssl"]
|
||||||
nightly = []
|
nightly = []
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ fn hello(_: Request, res: Response<Fresh>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
Server::http(hello).listen("127.0.0.1:3000").unwrap();
|
Server::http("127.0.0.1:3000").unwrap().handle(hello);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ fn hello(_: Request, res: Response) {
|
|||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
env_logger::init().unwrap();
|
env_logger::init().unwrap();
|
||||||
let _listening = hyper::Server::http(hello)
|
let _listening = hyper::Server::http("127.0.0.1:3000").unwrap()
|
||||||
.listen("127.0.0.1:3000").unwrap();
|
.handle(hello);
|
||||||
println!("Listening on http://127.0.0.1:3000");
|
println!("Listening on http://127.0.0.1:3000");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ fn echo(mut req: Request, mut res: Response) {
|
|||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
env_logger::init().unwrap();
|
env_logger::init().unwrap();
|
||||||
let server = Server::http(echo);
|
let server = Server::http("127.0.0.1:1337").unwrap();
|
||||||
let _guard = server.listen("127.0.0.1:1337").unwrap();
|
let _guard = server.handle(echo);
|
||||||
println!("Listening on http://127.0.0.1:1337");
|
println!("Listening on http://127.0.0.1:1337");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ use url::ParseError as UrlError;
|
|||||||
use header::{Headers, Header, HeaderFormat};
|
use header::{Headers, Header, HeaderFormat};
|
||||||
use header::{ContentLength, Location};
|
use header::{ContentLength, Location};
|
||||||
use method::Method;
|
use method::Method;
|
||||||
use net::{NetworkConnector, NetworkStream, ContextVerifier};
|
use net::{NetworkConnector, NetworkStream};
|
||||||
use {Url};
|
use {Url};
|
||||||
use Error;
|
use Error;
|
||||||
|
|
||||||
@@ -116,11 +116,6 @@ impl Client {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the SSL verifier callback for use with OpenSSL.
|
|
||||||
pub fn set_ssl_verifier(&mut self, verifier: ContextVerifier) {
|
|
||||||
self.protocol.set_ssl_verifier(verifier);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the RedirectPolicy.
|
/// Set the RedirectPolicy.
|
||||||
pub fn set_redirect_policy(&mut self, policy: RedirectPolicy) {
|
pub fn set_redirect_policy(&mut self, policy: RedirectPolicy) {
|
||||||
self.redirect_policy = policy;
|
self.redirect_policy = policy;
|
||||||
@@ -417,8 +412,6 @@ mod tests {
|
|||||||
use header::Server;
|
use header::Server;
|
||||||
use super::{Client, RedirectPolicy};
|
use super::{Client, RedirectPolicy};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
use mock::ChannelMockConnector;
|
|
||||||
use std::sync::mpsc::{self, TryRecvError};
|
|
||||||
|
|
||||||
mock_connector!(MockRedirectPolicy {
|
mock_connector!(MockRedirectPolicy {
|
||||||
"http://127.0.0.1" => "HTTP/1.1 301 Redirect\r\n\
|
"http://127.0.0.1" => "HTTP/1.1 301 Redirect\r\n\
|
||||||
@@ -464,31 +457,4 @@ mod tests {
|
|||||||
let res = client.get("http://127.0.0.1").send().unwrap();
|
let res = client.get("http://127.0.0.1").send().unwrap();
|
||||||
assert_eq!(res.headers.get(), Some(&Server("mock2".to_owned())));
|
assert_eq!(res.headers.get(), Some(&Server("mock2".to_owned())));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tests that the `Client::set_ssl_verifier` method does not drop the
|
|
||||||
/// old connector, but rather delegates the change to the connector itself.
|
|
||||||
#[test]
|
|
||||||
fn test_client_set_ssl_verifer() {
|
|
||||||
let (tx, rx) = mpsc::channel();
|
|
||||||
let mut client = Client::with_connector(ChannelMockConnector::new(tx));
|
|
||||||
|
|
||||||
client.set_ssl_verifier(Box::new(|_| {}));
|
|
||||||
|
|
||||||
// Make sure that the client called the `set_ssl_verifier` method
|
|
||||||
match rx.try_recv() {
|
|
||||||
Ok(meth) => {
|
|
||||||
assert_eq!(meth, "set_ssl_verifier");
|
|
||||||
},
|
|
||||||
_ => panic!("Expected a call to `set_ssl_verifier`"),
|
|
||||||
};
|
|
||||||
// Now make sure that no other method was called, as well as that
|
|
||||||
// the connector is still alive (i.e. wasn't dropped by the client).
|
|
||||||
match rx.try_recv() {
|
|
||||||
Err(TryRecvError::Empty) => {},
|
|
||||||
Err(TryRecvError::Disconnected) => {
|
|
||||||
panic!("Expected the connector to still be alive.");
|
|
||||||
},
|
|
||||||
Ok(_) => panic!("Did not expect any more method calls."),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use std::io::{self, Read, Write};
|
|||||||
use std::net::{SocketAddr, Shutdown};
|
use std::net::{SocketAddr, Shutdown};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use net::{NetworkConnector, NetworkStream, HttpConnector, ContextVerifier};
|
use net::{NetworkConnector, NetworkStream, DefaultConnector};
|
||||||
|
|
||||||
/// The `NetworkConnector` that behaves as a connection pool used by hyper's `Client`.
|
/// The `NetworkConnector` that behaves as a connection pool used by hyper's `Client`.
|
||||||
pub struct Pool<C: NetworkConnector> {
|
pub struct Pool<C: NetworkConnector> {
|
||||||
@@ -58,11 +58,11 @@ impl<'a> From<&'a str> for Scheme {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pool<HttpConnector> {
|
impl Pool<DefaultConnector> {
|
||||||
/// Creates a `Pool` with an `HttpConnector`.
|
/// Creates a `Pool` with a `DefaultConnector`.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(config: Config) -> Pool<HttpConnector> {
|
pub fn new(config: Config) -> Pool<DefaultConnector> {
|
||||||
Pool::with_connector(config, HttpConnector(None))
|
Pool::with_connector(config, DefaultConnector::default())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,10 +119,6 @@ impl<C: NetworkConnector<Stream=S>, S: NetworkStream + Send> NetworkConnector fo
|
|||||||
pool: self.inner.clone()
|
pool: self.inner.clone()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
#[inline]
|
|
||||||
fn set_ssl_verifier(&mut self, verifier: ContextVerifier) {
|
|
||||||
self.connector.set_ssl_verifier(verifier);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A Stream that will try to be returned to the Pool when dropped.
|
/// A Stream that will try to be returned to the Pool when dropped.
|
||||||
@@ -181,9 +177,8 @@ impl<S> Drop for PooledStream<S> {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::net::Shutdown;
|
use std::net::Shutdown;
|
||||||
use mock::{MockConnector, ChannelMockConnector};
|
use mock::{MockConnector};
|
||||||
use net::{NetworkConnector, NetworkStream};
|
use net::{NetworkConnector, NetworkStream};
|
||||||
use std::sync::mpsc;
|
|
||||||
|
|
||||||
use super::{Pool, key};
|
use super::{Pool, key};
|
||||||
|
|
||||||
@@ -220,20 +215,4 @@ mod tests {
|
|||||||
let locked = pool.inner.lock().unwrap();
|
let locked = pool.inner.lock().unwrap();
|
||||||
assert_eq!(locked.conns.len(), 0);
|
assert_eq!(locked.conns.len(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tests that the `Pool::set_ssl_verifier` method sets the SSL verifier of
|
|
||||||
/// the underlying `Connector` instance that it uses.
|
|
||||||
#[test]
|
|
||||||
fn test_set_ssl_verifier_delegates_to_connector() {
|
|
||||||
let (tx, rx) = mpsc::channel();
|
|
||||||
let mut pool = Pool::with_connector(
|
|
||||||
Default::default(), ChannelMockConnector::new(tx));
|
|
||||||
|
|
||||||
pool.set_ssl_verifier(Box::new(|_| { }));
|
|
||||||
|
|
||||||
match rx.try_recv() {
|
|
||||||
Ok(meth) => assert_eq!(meth, "set_ssl_verifier"),
|
|
||||||
_ => panic!("Expected a call to `set_ssl_verifier`"),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use url::Url;
|
|||||||
use method::{self, Method};
|
use method::{self, Method};
|
||||||
use header::Headers;
|
use header::Headers;
|
||||||
use header::Host;
|
use header::Host;
|
||||||
use net::{NetworkStream, NetworkConnector, HttpConnector, Fresh, Streaming};
|
use net::{NetworkStream, NetworkConnector, DefaultConnector, Fresh, Streaming};
|
||||||
use version;
|
use version;
|
||||||
use client::{Response, get_host_and_port};
|
use client::{Response, get_host_and_port};
|
||||||
|
|
||||||
@@ -66,7 +66,7 @@ impl Request<Fresh> {
|
|||||||
|
|
||||||
/// Create a new client request.
|
/// Create a new client request.
|
||||||
pub fn new(method: method::Method, url: Url) -> ::Result<Request<Fresh>> {
|
pub fn new(method: method::Method, url: Url) -> ::Result<Request<Fresh>> {
|
||||||
let mut conn = HttpConnector(None);
|
let mut conn = DefaultConnector::default();
|
||||||
Request::with_connector(method, url, &mut conn)
|
Request::with_connector(method, url, &mut conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
27
src/error.rs
27
src/error.rs
@@ -5,10 +5,12 @@ use std::io::Error as IoError;
|
|||||||
use std::str::Utf8Error;
|
use std::str::Utf8Error;
|
||||||
|
|
||||||
use httparse;
|
use httparse;
|
||||||
use openssl::ssl::error::SslError;
|
|
||||||
use url;
|
use url;
|
||||||
use solicit::http::HttpError as Http2Error;
|
use solicit::http::HttpError as Http2Error;
|
||||||
|
|
||||||
|
#[cfg(feature = "openssl")]
|
||||||
|
use openssl::ssl::error::SslError;
|
||||||
|
|
||||||
use self::Error::{
|
use self::Error::{
|
||||||
Method,
|
Method,
|
||||||
Uri,
|
Uri,
|
||||||
@@ -43,8 +45,8 @@ pub enum Error {
|
|||||||
Status,
|
Status,
|
||||||
/// An `io::Error` that occurred while trying to read or write to a network stream.
|
/// An `io::Error` that occurred while trying to read or write to a network stream.
|
||||||
Io(IoError),
|
Io(IoError),
|
||||||
/// An error from the `openssl` library.
|
/// An error from a SSL library.
|
||||||
Ssl(SslError),
|
Ssl(Box<StdError + Send + Sync>),
|
||||||
/// An HTTP/2-specific error, coming from the `solicit` library.
|
/// An HTTP/2-specific error, coming from the `solicit` library.
|
||||||
Http2(Http2Error),
|
Http2(Http2Error),
|
||||||
/// Parsing a field as string failed
|
/// Parsing a field as string failed
|
||||||
@@ -89,7 +91,7 @@ impl StdError for Error {
|
|||||||
fn cause(&self) -> Option<&StdError> {
|
fn cause(&self) -> Option<&StdError> {
|
||||||
match *self {
|
match *self {
|
||||||
Io(ref error) => Some(error),
|
Io(ref error) => Some(error),
|
||||||
Ssl(ref error) => Some(error),
|
Ssl(ref error) => Some(&**error),
|
||||||
Uri(ref error) => Some(error),
|
Uri(ref error) => Some(error),
|
||||||
Http2(ref error) => Some(error),
|
Http2(ref error) => Some(error),
|
||||||
_ => None,
|
_ => None,
|
||||||
@@ -109,11 +111,12 @@ impl From<url::ParseError> for Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "openssl")]
|
||||||
impl From<SslError> for Error {
|
impl From<SslError> for Error {
|
||||||
fn from(err: SslError) -> Error {
|
fn from(err: SslError) -> Error {
|
||||||
match err {
|
match err {
|
||||||
SslError::StreamError(err) => Io(err),
|
SslError::StreamError(err) => Io(err),
|
||||||
err => Ssl(err),
|
err => Ssl(Box::new(err)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -149,7 +152,6 @@ mod tests {
|
|||||||
use std::error::Error as StdError;
|
use std::error::Error as StdError;
|
||||||
use std::io;
|
use std::io;
|
||||||
use httparse;
|
use httparse;
|
||||||
use openssl::ssl::error::SslError;
|
|
||||||
use solicit::http::HttpError as Http2Error;
|
use solicit::http::HttpError as Http2Error;
|
||||||
use url;
|
use url;
|
||||||
use super::Error;
|
use super::Error;
|
||||||
@@ -192,12 +194,8 @@ mod tests {
|
|||||||
|
|
||||||
from_and_cause!(io::Error::new(io::ErrorKind::Other, "other") => Io(..));
|
from_and_cause!(io::Error::new(io::ErrorKind::Other, "other") => Io(..));
|
||||||
from_and_cause!(url::ParseError::EmptyHost => Uri(..));
|
from_and_cause!(url::ParseError::EmptyHost => Uri(..));
|
||||||
from_and_cause!(SslError::SslSessionClosed => Ssl(..));
|
|
||||||
from_and_cause!(Http2Error::UnknownStreamId => Http2(..));
|
from_and_cause!(Http2Error::UnknownStreamId => Http2(..));
|
||||||
|
|
||||||
from!(SslError::StreamError(io::Error::new(io::ErrorKind::Other, "ssl negotiation")) => Io(..));
|
|
||||||
|
|
||||||
|
|
||||||
from!(httparse::Error::HeaderName => Header);
|
from!(httparse::Error::HeaderName => Header);
|
||||||
from!(httparse::Error::HeaderName => Header);
|
from!(httparse::Error::HeaderName => Header);
|
||||||
from!(httparse::Error::HeaderValue => Header);
|
from!(httparse::Error::HeaderValue => Header);
|
||||||
@@ -207,4 +205,13 @@ mod tests {
|
|||||||
from!(httparse::Error::TooManyHeaders => TooLarge);
|
from!(httparse::Error::TooManyHeaders => TooLarge);
|
||||||
from!(httparse::Error::Version => Version);
|
from!(httparse::Error::Version => Version);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "openssl")]
|
||||||
|
#[test]
|
||||||
|
fn test_from_ssl() {
|
||||||
|
use openssl::ssl::error::SslError;
|
||||||
|
|
||||||
|
from!(SslError::StreamError(io::Error::new(io::ErrorKind::Other, "ssl negotiation")) => Io(..));
|
||||||
|
from_and_cause!(SslError::SslSessionClosed => Ssl(..));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -168,13 +168,13 @@ fn test_fmt() {
|
|||||||
fn cookie_jar() {
|
fn cookie_jar() {
|
||||||
let jar = CookieJar::new(b"secret");
|
let jar = CookieJar::new(b"secret");
|
||||||
let cookie = Cookie::new("foo".to_owned(), "bar".to_owned());
|
let cookie = Cookie::new("foo".to_owned(), "bar".to_owned());
|
||||||
jar.encrypted().add(cookie);
|
jar.add(cookie);
|
||||||
|
|
||||||
let cookies = SetCookie::from_cookie_jar(&jar);
|
let cookies = SetCookie::from_cookie_jar(&jar);
|
||||||
|
|
||||||
let mut new_jar = CookieJar::new(b"secret");
|
let mut new_jar = CookieJar::new(b"secret");
|
||||||
cookies.apply_to_cookie_jar(&mut new_jar);
|
cookies.apply_to_cookie_jar(&mut new_jar);
|
||||||
|
|
||||||
assert_eq!(jar.encrypted().find("foo"), new_jar.encrypted().find("foo"));
|
assert_eq!(jar.find("foo"), new_jar.find("foo"));
|
||||||
assert_eq!(jar.iter().collect::<Vec<Cookie>>(), new_jar.iter().collect::<Vec<Cookie>>());
|
assert_eq!(jar.iter().collect::<Vec<Cookie>>(), new_jar.iter().collect::<Vec<Cookie>>());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ use Error;
|
|||||||
use header::{Headers, ContentLength, TransferEncoding};
|
use header::{Headers, ContentLength, TransferEncoding};
|
||||||
use header::Encoding::Chunked;
|
use header::Encoding::Chunked;
|
||||||
use method::{Method};
|
use method::{Method};
|
||||||
use net::{NetworkConnector, NetworkStream, ContextVerifier};
|
use net::{NetworkConnector, NetworkStream};
|
||||||
use status::StatusCode;
|
use status::StatusCode;
|
||||||
use version::HttpVersion;
|
use version::HttpVersion;
|
||||||
use version::HttpVersion::{Http10, Http11};
|
use version::HttpVersion::{Http10, Http11};
|
||||||
@@ -264,11 +264,6 @@ impl Protocol for Http11Protocol {
|
|||||||
|
|
||||||
Ok(Box::new(Http11Message::with_stream(stream)))
|
Ok(Box::new(Http11Message::with_stream(stream)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn set_ssl_verifier(&mut self, verifier: ContextVerifier) {
|
|
||||||
self.connector.set_ssl_verifier(verifier);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Http11Protocol {
|
impl Http11Protocol {
|
||||||
@@ -292,10 +287,6 @@ impl<C: NetworkConnector<Stream=S> + Send + Sync, S: NetworkStream + Send> Netwo
|
|||||||
-> ::Result<Box<NetworkStream + Send>> {
|
-> ::Result<Box<NetworkStream + Send>> {
|
||||||
Ok(try!(self.0.connect(host, port, scheme)).into())
|
Ok(try!(self.0.connect(host, port, scheme)).into())
|
||||||
}
|
}
|
||||||
#[inline]
|
|
||||||
fn set_ssl_verifier(&mut self, verifier: ContextVerifier) {
|
|
||||||
self.0.set_ssl_verifier(verifier);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Connector(Box<NetworkConnector<Stream=Box<NetworkStream + Send>> + Send + Sync>);
|
struct Connector(Box<NetworkConnector<Stream=Box<NetworkStream + Send>> + Send + Sync>);
|
||||||
@@ -307,10 +298,6 @@ impl NetworkConnector for Connector {
|
|||||||
-> ::Result<Box<NetworkStream + Send>> {
|
-> ::Result<Box<NetworkStream + Send>> {
|
||||||
Ok(try!(self.0.connect(host, port, scheme)).into())
|
Ok(try!(self.0.connect(host, port, scheme)).into())
|
||||||
}
|
}
|
||||||
#[inline]
|
|
||||||
fn set_ssl_verifier(&mut self, verifier: ContextVerifier) {
|
|
||||||
self.0.set_ssl_verifier(verifier);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ use http::{
|
|||||||
ResponseHead,
|
ResponseHead,
|
||||||
RawStatus,
|
RawStatus,
|
||||||
};
|
};
|
||||||
use net::{NetworkStream, NetworkConnector, ContextVerifier};
|
use net::{NetworkStream, NetworkConnector};
|
||||||
use net::{HttpConnector, HttpStream};
|
use net::{HttpConnector, HttpStream};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
use header::Headers;
|
use header::Headers;
|
||||||
@@ -133,11 +133,6 @@ impl<C, S> Protocol for Http2Protocol<C, S> where C: NetworkConnector<Stream=S>
|
|||||||
|
|
||||||
Ok(Box::new(Http2Message::with_client(client)))
|
Ok(Box::new(Http2Message::with_client(client)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn set_ssl_verifier(&mut self, verifier: ContextVerifier) {
|
|
||||||
self.connector.set_ssl_verifier(verifier)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents an HTTP/2 request, described by a `RequestHead` and the body of the request.
|
/// Represents an HTTP/2 request, described by a `RequestHead` and the body of the request.
|
||||||
@@ -387,7 +382,7 @@ impl<S> HttpMessage for Http2Message<S> where S: CloneableStream {
|
|||||||
/// (which produces an `HttpStream` for the underlying transport layer).
|
/// (which produces an `HttpStream` for the underlying transport layer).
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new_protocol() -> Http2Protocol<HttpConnector, HttpStream> {
|
pub fn new_protocol() -> Http2Protocol<HttpConnector, HttpStream> {
|
||||||
Http2Protocol::with_connector(HttpConnector(None))
|
Http2Protocol::with_connector(HttpConnector)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@@ -16,15 +16,12 @@ use url::Url;
|
|||||||
use method;
|
use method;
|
||||||
use version;
|
use version;
|
||||||
use traitobject;
|
use traitobject;
|
||||||
use net::ContextVerifier;
|
|
||||||
|
|
||||||
/// The trait provides an API for creating new `HttpMessage`s depending on the underlying HTTP
|
/// The trait provides an API for creating new `HttpMessage`s depending on the underlying HTTP
|
||||||
/// protocol.
|
/// protocol.
|
||||||
pub trait Protocol {
|
pub trait Protocol {
|
||||||
/// Creates a fresh `HttpMessage` bound to the given host, based on the given protocol scheme.
|
/// Creates a fresh `HttpMessage` bound to the given host, based on the given protocol scheme.
|
||||||
fn new_message(&self, host: &str, port: u16, scheme: &str) -> ::Result<Box<HttpMessage>>;
|
fn new_message(&self, host: &str, port: u16, scheme: &str) -> ::Result<Box<HttpMessage>>;
|
||||||
/// Sets the SSL verifier that should be used when establishing TLS-protected connections.
|
|
||||||
fn set_ssl_verifier(&mut self, verifier: ContextVerifier);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Describes a request.
|
/// Describes a request.
|
||||||
@@ -63,7 +60,9 @@ pub trait HttpMessage: Write + Read + Send + Any + Typeable + Debug {
|
|||||||
/// After this, the `HttpMessage` instance can be used as an `io::Read` in order to read out
|
/// After this, the `HttpMessage` instance can be used as an `io::Read` in order to read out
|
||||||
/// the response body.
|
/// the response body.
|
||||||
fn get_incoming(&mut self) -> ::Result<ResponseHead>;
|
fn get_incoming(&mut self) -> ::Result<ResponseHead>;
|
||||||
|
/// Set the read timeout duration for this message.
|
||||||
|
#[cfg(feature = "timeouts")]
|
||||||
|
fn set_read_timeout(&self, dur: Option<Duration>) -> ::Result<()>;
|
||||||
/// Closes the underlying HTTP connection.
|
/// Closes the underlying HTTP connection.
|
||||||
fn close_connection(&mut self) -> ::Result<()>;
|
fn close_connection(&mut self) -> ::Result<()>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -131,6 +131,7 @@
|
|||||||
extern crate rustc_serialize as serialize;
|
extern crate rustc_serialize as serialize;
|
||||||
extern crate time;
|
extern crate time;
|
||||||
extern crate url;
|
extern crate url;
|
||||||
|
#[cfg(feature = "openssl")]
|
||||||
extern crate openssl;
|
extern crate openssl;
|
||||||
extern crate cookie;
|
extern crate cookie;
|
||||||
extern crate unicase;
|
extern crate unicase;
|
||||||
|
|||||||
45
src/mock.rs
45
src/mock.rs
@@ -3,7 +3,6 @@ use std::ascii::AsciiExt;
|
|||||||
use std::io::{self, Read, Write, Cursor};
|
use std::io::{self, Read, Write, Cursor};
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use std::sync::mpsc::Sender;
|
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use solicit::http::HttpScheme;
|
use solicit::http::HttpScheme;
|
||||||
@@ -12,7 +11,7 @@ use solicit::http::frame::{SettingsFrame, Frame};
|
|||||||
use solicit::http::connection::{HttpConnection, EndStream, DataChunk};
|
use solicit::http::connection::{HttpConnection, EndStream, DataChunk};
|
||||||
|
|
||||||
use header::Headers;
|
use header::Headers;
|
||||||
use net::{NetworkStream, NetworkConnector, ContextVerifier};
|
use net::{NetworkStream, NetworkConnector};
|
||||||
|
|
||||||
pub struct MockStream {
|
pub struct MockStream {
|
||||||
pub read: Cursor<Vec<u8>>,
|
pub read: Cursor<Vec<u8>>,
|
||||||
@@ -133,39 +132,6 @@ impl NetworkConnector for MockConnector {
|
|||||||
fn connect(&self, _host: &str, _port: u16, _scheme: &str) -> ::Result<MockStream> {
|
fn connect(&self, _host: &str, _port: u16, _scheme: &str) -> ::Result<MockStream> {
|
||||||
Ok(MockStream::new())
|
Ok(MockStream::new())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_ssl_verifier(&mut self, _verifier: ContextVerifier) {
|
|
||||||
// pass
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A mock implementation of the `NetworkConnector` trait that keeps track of all calls to its
|
|
||||||
/// methods by sending corresponding messages onto a channel.
|
|
||||||
///
|
|
||||||
/// Otherwise, it behaves the same as `MockConnector`.
|
|
||||||
pub struct ChannelMockConnector {
|
|
||||||
calls: Mutex<Sender<String>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ChannelMockConnector {
|
|
||||||
pub fn new(calls: Sender<String>) -> ChannelMockConnector {
|
|
||||||
ChannelMockConnector { calls: Mutex::new(calls) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NetworkConnector for ChannelMockConnector {
|
|
||||||
type Stream = MockStream;
|
|
||||||
#[inline]
|
|
||||||
fn connect(&self, _host: &str, _port: u16, _scheme: &str)
|
|
||||||
-> ::Result<MockStream> {
|
|
||||||
self.calls.lock().unwrap().send("connect".into()).unwrap();
|
|
||||||
Ok(MockStream::new())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn set_ssl_verifier(&mut self, _verifier: ContextVerifier) {
|
|
||||||
self.calls.lock().unwrap().send("set_ssl_verifier".into()).unwrap();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// new connectors must be created if you wish to intercept requests.
|
/// new connectors must be created if you wish to intercept requests.
|
||||||
@@ -196,10 +162,6 @@ macro_rules! mock_connector (
|
|||||||
None => panic!("{:?} doesn't know url {}", stringify!($name), key)
|
None => panic!("{:?} doesn't know url {}", stringify!($name), key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_ssl_verifier(&mut self, _verifier: ::net::ContextVerifier) {
|
|
||||||
// pass
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
)
|
)
|
||||||
@@ -296,9 +258,4 @@ impl NetworkConnector for MockHttp2Connector {
|
|||||||
-> ::Result<CloneableMockStream> {
|
-> ::Result<CloneableMockStream> {
|
||||||
Ok(self.streams.borrow_mut().remove(0))
|
Ok(self.streams.borrow_mut().remove(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn set_ssl_verifier(&mut self, _verifier: ContextVerifier) {
|
|
||||||
// pass
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
417
src/net.rs
417
src/net.rs
@@ -4,16 +4,12 @@ use std::fmt;
|
|||||||
use std::io::{self, ErrorKind, Read, Write};
|
use std::io::{self, ErrorKind, Read, Write};
|
||||||
use std::net::{SocketAddr, ToSocketAddrs, TcpStream, TcpListener, Shutdown};
|
use std::net::{SocketAddr, ToSocketAddrs, TcpStream, TcpListener, Shutdown};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::path::Path;
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use openssl::ssl::{Ssl, SslStream, SslContext, SSL_VERIFY_NONE};
|
#[cfg(feature = "openssl")]
|
||||||
use openssl::ssl::SslMethod::Sslv23;
|
pub use self::openssl::Openssl;
|
||||||
use openssl::ssl::error::StreamError as SslIoError;
|
|
||||||
use openssl::x509::X509FileType;
|
|
||||||
|
|
||||||
use typeable::Typeable;
|
use typeable::Typeable;
|
||||||
use {traitobject};
|
use traitobject;
|
||||||
|
|
||||||
/// The write-status indicating headers have not been written.
|
/// The write-status indicating headers have not been written.
|
||||||
pub enum Fresh {}
|
pub enum Fresh {}
|
||||||
@@ -70,9 +66,6 @@ pub trait NetworkConnector {
|
|||||||
type Stream: Into<Box<NetworkStream + Send>>;
|
type Stream: Into<Box<NetworkStream + Send>>;
|
||||||
/// Connect to a remote address.
|
/// Connect to a remote address.
|
||||||
fn connect(&self, host: &str, port: u16, scheme: &str) -> ::Result<Self::Stream>;
|
fn connect(&self, host: &str, port: u16, scheme: &str) -> ::Result<Self::Stream>;
|
||||||
/// Sets the given `ContextVerifier` to be used when verifying the SSL context
|
|
||||||
/// on the establishment of a new connection.
|
|
||||||
fn set_ssl_verifier(&mut self, verifier: ContextVerifier);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: NetworkStream + Send> From<T> for Box<NetworkStream + Send> {
|
impl<T: NetworkStream + Send> From<T> for Box<NetworkStream + Send> {
|
||||||
@@ -143,43 +136,22 @@ impl NetworkStream + Send {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A `NetworkListener` for `HttpStream`s.
|
/// A `NetworkListener` for `HttpStream`s.
|
||||||
pub enum HttpListener {
|
pub struct HttpListener(TcpListener);
|
||||||
/// Http variant.
|
|
||||||
Http(TcpListener),
|
|
||||||
/// Https variant. The two paths point to the certificate and key PEM files, in that order.
|
|
||||||
Https(TcpListener, Arc<SslContext>)
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Clone for HttpListener {
|
impl Clone for HttpListener {
|
||||||
|
#[inline]
|
||||||
fn clone(&self) -> HttpListener {
|
fn clone(&self) -> HttpListener {
|
||||||
match *self {
|
HttpListener(self.0.try_clone().unwrap())
|
||||||
HttpListener::Http(ref tcp) => HttpListener::Http(tcp.try_clone().unwrap()),
|
|
||||||
HttpListener::Https(ref tcp, ref ssl) => HttpListener::Https(tcp.try_clone().unwrap(), ssl.clone()),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HttpListener {
|
impl HttpListener {
|
||||||
|
|
||||||
/// Start listening to an address over HTTP.
|
/// Start listening to an address over HTTP.
|
||||||
pub fn http<To: ToSocketAddrs>(addr: To) -> ::Result<HttpListener> {
|
pub fn new<To: ToSocketAddrs>(addr: To) -> ::Result<HttpListener> {
|
||||||
Ok(HttpListener::Http(try!(TcpListener::bind(addr))))
|
Ok(HttpListener(try!(TcpListener::bind(addr))))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Start listening to an address over HTTPS.
|
|
||||||
pub fn https<To: ToSocketAddrs>(addr: To, cert: &Path, key: &Path) -> ::Result<HttpListener> {
|
|
||||||
let mut ssl_context = try!(SslContext::new(Sslv23));
|
|
||||||
try!(ssl_context.set_cipher_list("DEFAULT"));
|
|
||||||
try!(ssl_context.set_certificate_file(cert, X509FileType::PEM));
|
|
||||||
try!(ssl_context.set_private_key_file(key, X509FileType::PEM));
|
|
||||||
ssl_context.set_verify(SSL_VERIFY_NONE, None);
|
|
||||||
HttpListener::https_with_context(addr, ssl_context)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Start listening to an address of HTTPS using the given SslContext
|
|
||||||
pub fn https_with_context<To: ToSocketAddrs>(addr: To, ssl_context: SslContext) -> ::Result<HttpListener> {
|
|
||||||
Ok(HttpListener::Https(try!(TcpListener::bind(addr)), Arc::new(ssl_context)))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NetworkListener for HttpListener {
|
impl NetworkListener for HttpListener {
|
||||||
@@ -187,51 +159,42 @@ impl NetworkListener for HttpListener {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn accept(&mut self) -> ::Result<HttpStream> {
|
fn accept(&mut self) -> ::Result<HttpStream> {
|
||||||
match *self {
|
Ok(HttpStream(try!(self.0.accept()).0))
|
||||||
HttpListener::Http(ref mut tcp) => Ok(HttpStream::Http(CloneTcpStream(try!(tcp.accept()).0))),
|
|
||||||
HttpListener::Https(ref mut tcp, ref ssl_context) => {
|
|
||||||
let stream = CloneTcpStream(try!(tcp.accept()).0);
|
|
||||||
match SslStream::new_server(&**ssl_context, stream) {
|
|
||||||
Ok(ssl_stream) => Ok(HttpStream::Https(ssl_stream)),
|
|
||||||
Err(SslIoError(e)) => {
|
|
||||||
Err(io::Error::new(io::ErrorKind::ConnectionAborted, e).into())
|
|
||||||
},
|
|
||||||
Err(e) => Err(e.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn local_addr(&mut self) -> io::Result<SocketAddr> {
|
fn local_addr(&mut self) -> io::Result<SocketAddr> {
|
||||||
match *self {
|
self.0.local_addr()
|
||||||
HttpListener::Http(ref mut tcp) => tcp.local_addr(),
|
|
||||||
HttpListener::Https(ref mut tcp, _) => tcp.local_addr(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
/// A wrapper around a TcpStream.
|
||||||
pub struct CloneTcpStream(TcpStream);
|
pub struct HttpStream(pub TcpStream);
|
||||||
|
|
||||||
impl Clone for CloneTcpStream{
|
impl Clone for HttpStream {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn clone(&self) -> CloneTcpStream {
|
fn clone(&self) -> HttpStream {
|
||||||
CloneTcpStream(self.0.try_clone().unwrap())
|
HttpStream(self.0.try_clone().unwrap())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Read for CloneTcpStream {
|
impl fmt::Debug for HttpStream {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
f.write_str("HttpStream(_)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Read for HttpStream {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||||
self.0.read(buf)
|
self.0.read(buf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Write for CloneTcpStream {
|
impl Write for HttpStream {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
fn write(&mut self, msg: &[u8]) -> io::Result<usize> {
|
||||||
self.0.write(buf)
|
self.0.write(msg)
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
fn flush(&mut self) -> io::Result<()> {
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
@@ -239,85 +202,26 @@ impl Write for CloneTcpStream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A wrapper around a TcpStream.
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub enum HttpStream {
|
|
||||||
/// A stream over the HTTP protocol.
|
|
||||||
Http(CloneTcpStream),
|
|
||||||
/// A stream over the HTTP protocol, protected by SSL.
|
|
||||||
Https(SslStream<CloneTcpStream>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for HttpStream {
|
|
||||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
match *self {
|
|
||||||
HttpStream::Http(_) => write!(fmt, "Http HttpStream"),
|
|
||||||
HttpStream::Https(_) => write!(fmt, "Https HttpStream"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Read for HttpStream {
|
|
||||||
#[inline]
|
|
||||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
|
||||||
match *self {
|
|
||||||
HttpStream::Http(ref mut inner) => inner.read(buf),
|
|
||||||
HttpStream::Https(ref mut inner) => inner.read(buf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Write for HttpStream {
|
|
||||||
#[inline]
|
|
||||||
fn write(&mut self, msg: &[u8]) -> io::Result<usize> {
|
|
||||||
match *self {
|
|
||||||
HttpStream::Http(ref mut inner) => inner.write(msg),
|
|
||||||
HttpStream::Https(ref mut inner) => inner.write(msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
fn flush(&mut self) -> io::Result<()> {
|
|
||||||
match *self {
|
|
||||||
HttpStream::Http(ref mut inner) => inner.flush(),
|
|
||||||
HttpStream::Https(ref mut inner) => inner.flush(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NetworkStream for HttpStream {
|
impl NetworkStream for HttpStream {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn peer_addr(&mut self) -> io::Result<SocketAddr> {
|
fn peer_addr(&mut self) -> io::Result<SocketAddr> {
|
||||||
match *self {
|
self.0.peer_addr()
|
||||||
HttpStream::Http(ref mut inner) => inner.0.peer_addr(),
|
|
||||||
HttpStream::Https(ref mut inner) => inner.get_mut().0.peer_addr()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn close(&mut self, how: Shutdown) -> io::Result<()> {
|
fn close(&mut self, how: Shutdown) -> io::Result<()> {
|
||||||
#[inline]
|
match self.0.shutdown(how) {
|
||||||
fn shutdown(tcp: &mut TcpStream, how: Shutdown) -> io::Result<()> {
|
Ok(_) => Ok(()),
|
||||||
match tcp.shutdown(how) {
|
// see https://github.com/hyperium/hyper/issues/508
|
||||||
Ok(_) => Ok(()),
|
Err(ref e) if e.kind() == ErrorKind::NotConnected => Ok(()),
|
||||||
// see https://github.com/hyperium/hyper/issues/508
|
err => err
|
||||||
Err(ref e) if e.kind() == ErrorKind::NotConnected => Ok(()),
|
|
||||||
err => err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
match *self {
|
|
||||||
HttpStream::Http(ref mut inner) => shutdown(&mut inner.0, how),
|
|
||||||
HttpStream::Https(ref mut inner) => shutdown(&mut inner.get_mut().0, how)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A connector that will produce HttpStreams.
|
/// A connector that will produce HttpStreams.
|
||||||
pub struct HttpConnector(pub Option<ContextVerifier>);
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub struct HttpConnector;
|
||||||
/// A method that can set verification methods on an SSL context
|
|
||||||
pub type ContextVerifier = Box<Fn(&mut SslContext) -> () + Send + Sync>;
|
|
||||||
|
|
||||||
impl NetworkConnector for HttpConnector {
|
impl NetworkConnector for HttpConnector {
|
||||||
type Stream = HttpStream;
|
type Stream = HttpStream;
|
||||||
@@ -327,19 +231,7 @@ impl NetworkConnector for HttpConnector {
|
|||||||
Ok(try!(match scheme {
|
Ok(try!(match scheme {
|
||||||
"http" => {
|
"http" => {
|
||||||
debug!("http scheme");
|
debug!("http scheme");
|
||||||
Ok(HttpStream::Http(CloneTcpStream(try!(TcpStream::connect(addr)))))
|
Ok(HttpStream(try!(TcpStream::connect(addr))))
|
||||||
},
|
|
||||||
"https" => {
|
|
||||||
debug!("https scheme");
|
|
||||||
let stream = CloneTcpStream(try!(TcpStream::connect(addr)));
|
|
||||||
let mut context = try!(SslContext::new(Sslv23));
|
|
||||||
if let Some(ref verifier) = self.0 {
|
|
||||||
verifier(&mut context);
|
|
||||||
}
|
|
||||||
let ssl = try!(Ssl::new(&context));
|
|
||||||
try!(ssl.set_hostname(host));
|
|
||||||
let stream = try!(SslStream::new(&context, stream));
|
|
||||||
Ok(HttpStream::Https(stream))
|
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
Err(io::Error::new(io::ErrorKind::InvalidInput,
|
Err(io::Error::new(io::ErrorKind::InvalidInput,
|
||||||
@@ -347,15 +239,241 @@ impl NetworkConnector for HttpConnector {
|
|||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
fn set_ssl_verifier(&mut self, verifier: ContextVerifier) {
|
}
|
||||||
self.0 = Some(verifier);
|
|
||||||
|
|
||||||
|
/// An abstraction to allow any SSL implementation to be used with HttpsStreams.
|
||||||
|
pub trait Ssl {
|
||||||
|
/// The protected stream.
|
||||||
|
type Stream: NetworkStream + Send + Clone;
|
||||||
|
/// Wrap a client stream with SSL.
|
||||||
|
fn wrap_client(&self, stream: HttpStream, host: &str) -> ::Result<Self::Stream>;
|
||||||
|
/// Wrap a server stream with SSL.
|
||||||
|
fn wrap_server(&self, stream: HttpStream) -> ::Result<Self::Stream>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A stream over the HTTP protocol, possibly protected by SSL.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum HttpsStream<S: NetworkStream> {
|
||||||
|
/// A plain text stream.
|
||||||
|
Http(HttpStream),
|
||||||
|
/// A stream protected by SSL.
|
||||||
|
Https(S)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S: NetworkStream> Read for HttpsStream<S> {
|
||||||
|
#[inline]
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||||
|
match *self {
|
||||||
|
HttpsStream::Http(ref mut s) => s.read(buf),
|
||||||
|
HttpsStream::Https(ref mut s) => s.read(buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S: NetworkStream> Write for HttpsStream<S> {
|
||||||
|
#[inline]
|
||||||
|
fn write(&mut self, msg: &[u8]) -> io::Result<usize> {
|
||||||
|
match *self {
|
||||||
|
HttpsStream::Http(ref mut s) => s.write(msg),
|
||||||
|
HttpsStream::Https(ref mut s) => s.write(msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
|
match *self {
|
||||||
|
HttpsStream::Http(ref mut s) => s.flush(),
|
||||||
|
HttpsStream::Https(ref mut s) => s.flush()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S: NetworkStream> NetworkStream for HttpsStream<S> {
|
||||||
|
#[inline]
|
||||||
|
fn peer_addr(&mut self) -> io::Result<SocketAddr> {
|
||||||
|
match *self {
|
||||||
|
HttpsStream::Http(ref mut s) => s.peer_addr(),
|
||||||
|
HttpsStream::Https(ref mut s) => s.peer_addr()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn close(&mut self, how: Shutdown) -> io::Result<()> {
|
||||||
|
match *self {
|
||||||
|
HttpsStream::Http(ref mut s) => s.close(how),
|
||||||
|
HttpsStream::Https(ref mut s) => s.close(how)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A Http Listener over SSL.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct HttpsListener<S: Ssl> {
|
||||||
|
listener: HttpListener,
|
||||||
|
ssl: S,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S: Ssl> HttpsListener<S> {
|
||||||
|
|
||||||
|
/// Start listening to an address over HTTPS.
|
||||||
|
pub fn new<To: ToSocketAddrs>(addr: To, ssl: S) -> ::Result<HttpsListener<S>> {
|
||||||
|
HttpListener::new(addr).map(|l| HttpsListener {
|
||||||
|
listener: l,
|
||||||
|
ssl: ssl
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S: Ssl + Clone> NetworkListener for HttpsListener<S> {
|
||||||
|
type Stream = S::Stream;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn accept(&mut self) -> ::Result<S::Stream> {
|
||||||
|
self.listener.accept().and_then(|s| self.ssl.wrap_server(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn local_addr(&mut self) -> io::Result<SocketAddr> {
|
||||||
|
self.listener.local_addr()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A connector that can protect HTTP streams using SSL.
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct HttpsConnector<S: Ssl> {
|
||||||
|
ssl: S
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S: Ssl> HttpsConnector<S> {
|
||||||
|
/// Create a new connector using the provided SSL implementation.
|
||||||
|
pub fn new(s: S) -> HttpsConnector<S> {
|
||||||
|
HttpsConnector { ssl: s }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S: Ssl> NetworkConnector for HttpsConnector<S> {
|
||||||
|
type Stream = HttpsStream<S::Stream>;
|
||||||
|
|
||||||
|
fn connect(&self, host: &str, port: u16, scheme: &str) -> ::Result<Self::Stream> {
|
||||||
|
let addr = &(host, port);
|
||||||
|
if scheme == "https" {
|
||||||
|
debug!("https scheme");
|
||||||
|
let stream = HttpStream(try!(TcpStream::connect(addr)));
|
||||||
|
self.ssl.wrap_client(stream, host).map(HttpsStream::Https)
|
||||||
|
} else {
|
||||||
|
HttpConnector.connect(host, port, scheme).map(HttpsStream::Http)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(not(feature = "openssl"))]
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub type DefaultConnector = HttpConnector;
|
||||||
|
|
||||||
|
#[cfg(feature = "openssl")]
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub type DefaultConnector = HttpsConnector<self::openssl::Openssl>;
|
||||||
|
|
||||||
|
#[cfg(feature = "openssl")]
|
||||||
|
mod openssl {
|
||||||
|
use std::io;
|
||||||
|
use std::net::{SocketAddr, Shutdown};
|
||||||
|
use std::path::Path;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use openssl::ssl::{Ssl, SslContext, SslStream, SslMethod, SSL_VERIFY_NONE};
|
||||||
|
use openssl::ssl::error::StreamError as SslIoError;
|
||||||
|
use openssl::ssl::error::SslError;
|
||||||
|
use openssl::x509::X509FileType;
|
||||||
|
use super::{NetworkStream, HttpStream};
|
||||||
|
|
||||||
|
|
||||||
|
/// An implementation of `Ssl` for OpenSSL.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use hyper::Server;
|
||||||
|
/// use hyper::net::Openssl;
|
||||||
|
///
|
||||||
|
/// let ssl = Openssl::with_cert_and_key("/home/foo/cert", "/home/foo/key").unwrap();
|
||||||
|
/// Server::https("0.0.0.0:443", ssl).unwrap();
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// For complete control, create a `SslContext` with the options you desire
|
||||||
|
/// and then create `Openssl { context: ctx }
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Openssl {
|
||||||
|
/// The `SslContext` from openssl crate.
|
||||||
|
pub context: Arc<SslContext>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Openssl {
|
||||||
|
fn default() -> Openssl {
|
||||||
|
Openssl {
|
||||||
|
context: Arc::new(SslContext::new(SslMethod::Sslv23).unwrap_or_else(|e| {
|
||||||
|
// if we cannot create a SslContext, that's because of a
|
||||||
|
// serious problem. just crash.
|
||||||
|
panic!("{}", e)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Openssl {
|
||||||
|
/// Ease creating an `Openssl` with a certificate and key.
|
||||||
|
pub fn with_cert_and_key<C, K>(cert: C, key: K) -> Result<Openssl, SslError>
|
||||||
|
where C: AsRef<Path>, K: AsRef<Path> {
|
||||||
|
let mut ctx = try!(SslContext::new(SslMethod::Sslv23));
|
||||||
|
try!(ctx.set_cipher_list("DEFAULT"));
|
||||||
|
try!(ctx.set_certificate_file(cert.as_ref(), X509FileType::PEM));
|
||||||
|
try!(ctx.set_private_key_file(key.as_ref(), X509FileType::PEM));
|
||||||
|
ctx.set_verify(SSL_VERIFY_NONE, None);
|
||||||
|
Ok(Openssl { context: Arc::new(ctx) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl super::Ssl for Openssl {
|
||||||
|
type Stream = SslStream<HttpStream>;
|
||||||
|
|
||||||
|
fn wrap_client(&self, stream: HttpStream, host: &str) -> ::Result<Self::Stream> {
|
||||||
|
//if let Some(ref verifier) = self.verifier {
|
||||||
|
// verifier(&mut context);
|
||||||
|
//}
|
||||||
|
let ssl = try!(Ssl::new(&self.context));
|
||||||
|
try!(ssl.set_hostname(host));
|
||||||
|
SslStream::new(&self.context, stream).map_err(From::from)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wrap_server(&self, stream: HttpStream) -> ::Result<Self::Stream> {
|
||||||
|
match SslStream::new_server(&self.context, stream) {
|
||||||
|
Ok(ssl_stream) => Ok(ssl_stream),
|
||||||
|
Err(SslIoError(e)) => {
|
||||||
|
Err(io::Error::new(io::ErrorKind::ConnectionAborted, e).into())
|
||||||
|
},
|
||||||
|
Err(e) => Err(e.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S: NetworkStream> NetworkStream for SslStream<S> {
|
||||||
|
#[inline]
|
||||||
|
fn peer_addr(&mut self) -> io::Result<SocketAddr> {
|
||||||
|
self.get_mut().peer_addr()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn close(&mut self, how: Shutdown) -> io::Result<()> {
|
||||||
|
self.get_mut().close(how)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use mock::MockStream;
|
use mock::MockStream;
|
||||||
use super::{NetworkStream, HttpConnector, NetworkConnector};
|
use super::{NetworkStream};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_downcast_box_stream() {
|
fn test_downcast_box_stream() {
|
||||||
@@ -376,13 +494,4 @@ mod tests {
|
|||||||
assert_eq!(mock, Box::new(MockStream::new()));
|
assert_eq!(mock, Box::new(MockStream::new()));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_http_connector_set_ssl_verifier() {
|
|
||||||
let mut connector = HttpConnector(None);
|
|
||||||
|
|
||||||
connector.set_ssl_verifier(Box::new(|_| {}));
|
|
||||||
|
|
||||||
assert!(connector.0.is_some());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
//! // handle things here
|
//! // handle things here
|
||||||
//! }
|
//! }
|
||||||
//!
|
//!
|
||||||
//! Server::http(hello).listen("0.0.0.0:0").unwrap();
|
//! Server::http("0.0.0.0:0").unwrap().handle(hello).unwrap();
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! As with any trait, you can also define a struct and implement `Handler`
|
//! As with any trait, you can also define a struct and implement `Handler`
|
||||||
@@ -43,9 +43,9 @@
|
|||||||
//!
|
//!
|
||||||
//!
|
//!
|
||||||
//! let (tx, rx) = channel();
|
//! let (tx, rx) = channel();
|
||||||
//! Server::http(SenderHandler {
|
//! Server::http("0.0.0.0:0").unwrap().handle(SenderHandler {
|
||||||
//! sender: Mutex::new(tx)
|
//! sender: Mutex::new(tx)
|
||||||
//! }).listen("0.0.0.0:0").unwrap();
|
//! }).unwrap();
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! Since the `Server` will be listening on multiple threads, the `Handler`
|
//! Since the `Server` will be listening on multiple threads, the `Handler`
|
||||||
@@ -56,9 +56,9 @@
|
|||||||
//! use hyper::server::{Server, Request, Response};
|
//! use hyper::server::{Server, Request, Response};
|
||||||
//!
|
//!
|
||||||
//! let counter = AtomicUsize::new(0);
|
//! let counter = AtomicUsize::new(0);
|
||||||
//! Server::http(move |req: Request, res: Response| {
|
//! Server::http("0.0.0.0:0").unwrap().handle(move |req: Request, res: Response| {
|
||||||
//! counter.fetch_add(1, Ordering::Relaxed);
|
//! counter.fetch_add(1, Ordering::Relaxed);
|
||||||
//! }).listen("0.0.0.0:0").unwrap();
|
//! }).unwrap();
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! # The `Request` and `Response` pair
|
//! # The `Request` and `Response` pair
|
||||||
@@ -76,14 +76,14 @@
|
|||||||
//! use hyper::server::{Server, Request, Response};
|
//! use hyper::server::{Server, Request, Response};
|
||||||
//! use hyper::status::StatusCode;
|
//! use hyper::status::StatusCode;
|
||||||
//!
|
//!
|
||||||
//! Server::http(|mut req: Request, mut res: Response| {
|
//! Server::http("0.0.0.0:0").unwrap().handle(|mut req: Request, mut res: Response| {
|
||||||
//! match req.method {
|
//! match req.method {
|
||||||
//! hyper::Post => {
|
//! hyper::Post => {
|
||||||
//! io::copy(&mut req, &mut res.start().unwrap()).unwrap();
|
//! io::copy(&mut req, &mut res.start().unwrap()).unwrap();
|
||||||
//! },
|
//! },
|
||||||
//! _ => *res.status_mut() = StatusCode::MethodNotAllowed
|
//! _ => *res.status_mut() = StatusCode::MethodNotAllowed
|
||||||
//! }
|
//! }
|
||||||
//! }).listen("0.0.0.0:0").unwrap();
|
//! }).unwrap();
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! ## An aside: Write Status
|
//! ## An aside: Write Status
|
||||||
@@ -107,30 +107,12 @@
|
|||||||
//! out by calling `start` on the `Request<Fresh>`. This will return a new
|
//! out by calling `start` on the `Request<Fresh>`. This will return a new
|
||||||
//! `Request<Streaming>` object, that no longer has `headers_mut()`, but does
|
//! `Request<Streaming>` object, that no longer has `headers_mut()`, but does
|
||||||
//! implement `Write`.
|
//! implement `Write`.
|
||||||
//!
|
|
||||||
//! ```no_run
|
|
||||||
//! use hyper::server::{Server, Request, Response};
|
|
||||||
//! use hyper::status::StatusCode;
|
|
||||||
//! use hyper::uri::RequestUri;
|
|
||||||
//!
|
|
||||||
//! let server = Server::http(|req: Request, mut res: Response| {
|
|
||||||
//! *res.status_mut() = match (req.method, req.uri) {
|
|
||||||
//! (hyper::Get, RequestUri::AbsolutePath(ref path)) if path == "/" => {
|
|
||||||
//! StatusCode::Ok
|
|
||||||
//! },
|
|
||||||
//! (hyper::Get, _) => StatusCode::NotFound,
|
|
||||||
//! _ => StatusCode::MethodNotAllowed
|
|
||||||
//! };
|
|
||||||
//! }).listen("0.0.0.0:8080").unwrap();
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::io::{ErrorKind, BufWriter, Write};
|
use std::io::{ErrorKind, BufWriter, Write};
|
||||||
use std::marker::PhantomData;
|
|
||||||
use std::net::{SocketAddr, ToSocketAddrs};
|
use std::net::{SocketAddr, ToSocketAddrs};
|
||||||
use std::path::Path;
|
|
||||||
use std::thread::{self, JoinHandle};
|
use std::thread::{self, JoinHandle};
|
||||||
|
|
||||||
use num_cpus;
|
use num_cpus;
|
||||||
use openssl::ssl::SslContext;
|
|
||||||
|
|
||||||
pub use self::request::Request;
|
pub use self::request::Request;
|
||||||
pub use self::response::Response;
|
pub use self::response::Response;
|
||||||
@@ -142,7 +124,7 @@ use buffer::BufReader;
|
|||||||
use header::{Headers, Expect, Connection};
|
use header::{Headers, Expect, Connection};
|
||||||
use http;
|
use http;
|
||||||
use method::Method;
|
use method::Method;
|
||||||
use net::{NetworkListener, NetworkStream, HttpListener};
|
use net::{NetworkListener, NetworkStream, HttpListener, HttpsListener, Ssl};
|
||||||
use status::StatusCode;
|
use status::StatusCode;
|
||||||
use uri::RequestUri;
|
use uri::RequestUri;
|
||||||
use version::HttpVersion::Http11;
|
use version::HttpVersion::Http11;
|
||||||
@@ -154,21 +136,13 @@ pub mod response;
|
|||||||
|
|
||||||
mod listener;
|
mod listener;
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
enum SslConfig<'a> {
|
|
||||||
CertAndKey(&'a Path, &'a Path),
|
|
||||||
Context(SslContext),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A server can listen on a TCP socket.
|
/// A server can listen on a TCP socket.
|
||||||
///
|
///
|
||||||
/// Once listening, it will create a `Request`/`Response` pair for each
|
/// Once listening, it will create a `Request`/`Response` pair for each
|
||||||
/// incoming connection, and hand them to the provided handler.
|
/// incoming connection, and hand them to the provided handler.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Server<'a, H: Handler, L = HttpListener> {
|
pub struct Server<L = HttpListener> {
|
||||||
handler: H,
|
listener: L,
|
||||||
ssl: Option<SslConfig<'a>>,
|
|
||||||
_marker: PhantomData<L>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! try_option(
|
macro_rules! try_option(
|
||||||
@@ -180,64 +154,41 @@ macro_rules! try_option(
|
|||||||
}}
|
}}
|
||||||
);
|
);
|
||||||
|
|
||||||
impl<'a, H: Handler, L: NetworkListener> Server<'a, H, L> {
|
impl<L: NetworkListener> Server<L> {
|
||||||
/// Creates a new server with the provided handler.
|
/// Creates a new server with the provided handler.
|
||||||
pub fn new(handler: H) -> Server<'a, H, L> {
|
#[inline]
|
||||||
|
pub fn new(listener: L) -> Server<L> {
|
||||||
Server {
|
Server {
|
||||||
handler: handler,
|
listener: listener
|
||||||
ssl: None,
|
|
||||||
_marker: PhantomData
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, H: Handler + 'static> Server<'a, H, HttpListener> {
|
impl Server<HttpListener> {
|
||||||
/// Creates a new server that will handle `HttpStream`s.
|
/// Creates a new server that will handle `HttpStream`s.
|
||||||
pub fn http(handler: H) -> Server<'a, H, HttpListener> {
|
pub fn http<To: ToSocketAddrs>(addr: To) -> ::Result<Server<HttpListener>> {
|
||||||
Server::new(handler)
|
HttpListener::new(addr).map(Server::new)
|
||||||
}
|
|
||||||
/// Creates a new server that will handler `HttpStreams`s using a TLS connection.
|
|
||||||
pub fn https(handler: H, cert: &'a Path, key: &'a Path) -> Server<'a, H, HttpListener> {
|
|
||||||
Server {
|
|
||||||
handler: handler,
|
|
||||||
ssl: Some(SslConfig::CertAndKey(cert, key)),
|
|
||||||
_marker: PhantomData
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Creates a new server that will handler `HttpStreams`s using a TLS connection defined by an SslContext.
|
|
||||||
pub fn https_with_context(handler: H, ssl_context: SslContext) -> Server<'a, H, HttpListener> {
|
|
||||||
Server {
|
|
||||||
handler: handler,
|
|
||||||
ssl: Some(SslConfig::Context(ssl_context)),
|
|
||||||
_marker: PhantomData
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, H: Handler + 'static> Server<'a, H, HttpListener> {
|
impl<S: Ssl + Clone + Send> Server<HttpsListener<S>> {
|
||||||
/// Binds to a socket, and starts handling connections using a task pool.
|
/// Creates a new server that will handle `HttpStream`s over SSL.
|
||||||
pub fn listen_threads<T: ToSocketAddrs>(self, addr: T, threads: usize) -> ::Result<Listening> {
|
///
|
||||||
let listener = try!(match self.ssl {
|
/// You can use any SSL implementation, as long as implements `hyper::net::Ssl`.
|
||||||
Some(SslConfig::CertAndKey(cert, key)) => HttpListener::https(addr, cert, key),
|
pub fn https<A: ToSocketAddrs>(addr: A, ssl: S) -> ::Result<Server<HttpsListener<S>>> {
|
||||||
Some(SslConfig::Context(ssl_context)) => HttpListener::https_with_context(addr, ssl_context),
|
HttpsListener::new(addr, ssl).map(Server::new)
|
||||||
None => HttpListener::http(addr)
|
|
||||||
});
|
|
||||||
with_listener(self.handler, listener, threads)
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<L: NetworkListener + Send + 'static> Server<L> {
|
||||||
/// Binds to a socket and starts handling connections.
|
/// Binds to a socket and starts handling connections.
|
||||||
pub fn listen<T: ToSocketAddrs>(self, addr: T) -> ::Result<Listening> {
|
pub fn handle<H: Handler + 'static>(self, handler: H) -> ::Result<Listening> {
|
||||||
self.listen_threads(addr, num_cpus::get() * 5 / 4)
|
with_listener(handler, self.listener, num_cpus::get() * 5 / 4)
|
||||||
}
|
}
|
||||||
}
|
/// Binds to a socket and starts handling connections with the provided
|
||||||
impl<
|
/// number of threads.
|
||||||
'a,
|
pub fn handle_threads<H: Handler + 'static>(self, handler: H, threads: usize) -> ::Result<Listening> {
|
||||||
H: Handler + 'static,
|
with_listener(handler, self.listener, threads)
|
||||||
L: NetworkListener<Stream=S> + Send + 'static,
|
|
||||||
S: NetworkStream + Clone + Send> Server<'a, H, L> {
|
|
||||||
/// Creates a new server that will handle `HttpStream`s.
|
|
||||||
pub fn with_listener(self, listener: L, threads: usize) -> ::Result<Listening> {
|
|
||||||
with_listener(self.handler, listener, threads)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -247,7 +198,7 @@ L: NetworkListener + Send + 'static {
|
|||||||
let socket = try!(listener.local_addr());
|
let socket = try!(listener.local_addr());
|
||||||
|
|
||||||
debug!("threads = {:?}", threads);
|
debug!("threads = {:?}", threads);
|
||||||
let pool = ListenerPool::new(listener.clone());
|
let pool = ListenerPool::new(listener);
|
||||||
let work = move |mut stream| Worker(&handler).handle_connection(&mut stream);
|
let work = move |mut stream| Worker(&handler).handle_connection(&mut stream);
|
||||||
|
|
||||||
let guard = thread::spawn(move || pool.accept(work, threads));
|
let guard = thread::spawn(move || pool.accept(work, threads));
|
||||||
|
|||||||
Reference in New Issue
Block a user