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"
|
||||
- rust: beta
|
||||
- rust: stable
|
||||
- rust: stable
|
||||
env: FEATURES="--no-default-features"
|
||||
|
||||
sudo: false
|
||||
|
||||
|
||||
23
Cargo.toml
23
Cargo.toml
@@ -12,22 +12,33 @@ authors = ["Sean McArthur <sean.monstar@gmail.com>",
|
||||
keywords = ["http", "hyper", "hyperium"]
|
||||
|
||||
[dependencies]
|
||||
cookie = "0.1"
|
||||
httparse = "0.1"
|
||||
log = "0.3"
|
||||
mime = "0.0.11"
|
||||
mime = "0.0.12"
|
||||
num_cpus = "0.2"
|
||||
openssl = "0.6"
|
||||
rustc-serialize = "0.3"
|
||||
time = "0.1"
|
||||
unicase = "0.1"
|
||||
url = "0.2"
|
||||
traitobject = "0.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]
|
||||
env_logger = "*"
|
||||
|
||||
[features]
|
||||
default = ["ssl"]
|
||||
ssl = ["openssl", "cookie/secure", "solicit/openssl"]
|
||||
nightly = []
|
||||
|
||||
@@ -41,7 +41,7 @@ fn hello(_: Request, res: Response<Fresh>) {
|
||||
}
|
||||
|
||||
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() {
|
||||
env_logger::init().unwrap();
|
||||
let _listening = hyper::Server::http(hello)
|
||||
.listen("127.0.0.1:3000").unwrap();
|
||||
let _listening = hyper::Server::http("127.0.0.1:3000").unwrap()
|
||||
.handle(hello);
|
||||
println!("Listening on http://127.0.0.1:3000");
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ fn echo(mut req: Request, mut res: Response) {
|
||||
|
||||
fn main() {
|
||||
env_logger::init().unwrap();
|
||||
let server = Server::http(echo);
|
||||
let _guard = server.listen("127.0.0.1:1337").unwrap();
|
||||
let server = Server::http("127.0.0.1:1337").unwrap();
|
||||
let _guard = server.handle(echo);
|
||||
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::{ContentLength, Location};
|
||||
use method::Method;
|
||||
use net::{NetworkConnector, NetworkStream, ContextVerifier};
|
||||
use net::{NetworkConnector, NetworkStream};
|
||||
use {Url};
|
||||
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.
|
||||
pub fn set_redirect_policy(&mut self, policy: RedirectPolicy) {
|
||||
self.redirect_policy = policy;
|
||||
@@ -417,8 +412,6 @@ mod tests {
|
||||
use header::Server;
|
||||
use super::{Client, RedirectPolicy};
|
||||
use url::Url;
|
||||
use mock::ChannelMockConnector;
|
||||
use std::sync::mpsc::{self, TryRecvError};
|
||||
|
||||
mock_connector!(MockRedirectPolicy {
|
||||
"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();
|
||||
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::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`.
|
||||
pub struct Pool<C: NetworkConnector> {
|
||||
@@ -58,11 +58,11 @@ impl<'a> From<&'a str> for Scheme {
|
||||
}
|
||||
}
|
||||
|
||||
impl Pool<HttpConnector> {
|
||||
/// Creates a `Pool` with an `HttpConnector`.
|
||||
impl Pool<DefaultConnector> {
|
||||
/// Creates a `Pool` with a `DefaultConnector`.
|
||||
#[inline]
|
||||
pub fn new(config: Config) -> Pool<HttpConnector> {
|
||||
Pool::with_connector(config, HttpConnector(None))
|
||||
pub fn new(config: Config) -> Pool<DefaultConnector> {
|
||||
Pool::with_connector(config, DefaultConnector::default())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,10 +119,6 @@ impl<C: NetworkConnector<Stream=S>, S: NetworkStream + Send> NetworkConnector fo
|
||||
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.
|
||||
@@ -181,9 +177,8 @@ impl<S> Drop for PooledStream<S> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::net::Shutdown;
|
||||
use mock::{MockConnector, ChannelMockConnector};
|
||||
use mock::{MockConnector};
|
||||
use net::{NetworkConnector, NetworkStream};
|
||||
use std::sync::mpsc;
|
||||
|
||||
use super::{Pool, key};
|
||||
|
||||
@@ -220,20 +215,4 @@ mod tests {
|
||||
let locked = pool.inner.lock().unwrap();
|
||||
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 header::Headers;
|
||||
use header::Host;
|
||||
use net::{NetworkStream, NetworkConnector, HttpConnector, Fresh, Streaming};
|
||||
use net::{NetworkStream, NetworkConnector, DefaultConnector, Fresh, Streaming};
|
||||
use version;
|
||||
use client::{Response, get_host_and_port};
|
||||
|
||||
@@ -66,7 +66,7 @@ impl Request<Fresh> {
|
||||
|
||||
/// Create a new client request.
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
27
src/error.rs
27
src/error.rs
@@ -5,10 +5,12 @@ use std::io::Error as IoError;
|
||||
use std::str::Utf8Error;
|
||||
|
||||
use httparse;
|
||||
use openssl::ssl::error::SslError;
|
||||
use url;
|
||||
use solicit::http::HttpError as Http2Error;
|
||||
|
||||
#[cfg(feature = "openssl")]
|
||||
use openssl::ssl::error::SslError;
|
||||
|
||||
use self::Error::{
|
||||
Method,
|
||||
Uri,
|
||||
@@ -43,8 +45,8 @@ pub enum Error {
|
||||
Status,
|
||||
/// An `io::Error` that occurred while trying to read or write to a network stream.
|
||||
Io(IoError),
|
||||
/// An error from the `openssl` library.
|
||||
Ssl(SslError),
|
||||
/// An error from a SSL library.
|
||||
Ssl(Box<StdError + Send + Sync>),
|
||||
/// An HTTP/2-specific error, coming from the `solicit` library.
|
||||
Http2(Http2Error),
|
||||
/// Parsing a field as string failed
|
||||
@@ -89,7 +91,7 @@ impl StdError for Error {
|
||||
fn cause(&self) -> Option<&StdError> {
|
||||
match *self {
|
||||
Io(ref error) => Some(error),
|
||||
Ssl(ref error) => Some(error),
|
||||
Ssl(ref error) => Some(&**error),
|
||||
Uri(ref error) => Some(error),
|
||||
Http2(ref error) => Some(error),
|
||||
_ => None,
|
||||
@@ -109,11 +111,12 @@ impl From<url::ParseError> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "openssl")]
|
||||
impl From<SslError> for Error {
|
||||
fn from(err: SslError) -> Error {
|
||||
match 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::io;
|
||||
use httparse;
|
||||
use openssl::ssl::error::SslError;
|
||||
use solicit::http::HttpError as Http2Error;
|
||||
use url;
|
||||
use super::Error;
|
||||
@@ -192,12 +194,8 @@ mod tests {
|
||||
|
||||
from_and_cause!(io::Error::new(io::ErrorKind::Other, "other") => Io(..));
|
||||
from_and_cause!(url::ParseError::EmptyHost => Uri(..));
|
||||
from_and_cause!(SslError::SslSessionClosed => Ssl(..));
|
||||
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::HeaderValue => Header);
|
||||
@@ -207,4 +205,13 @@ mod tests {
|
||||
from!(httparse::Error::TooManyHeaders => TooLarge);
|
||||
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() {
|
||||
let jar = CookieJar::new(b"secret");
|
||||
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 mut new_jar = CookieJar::new(b"secret");
|
||||
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>>());
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ use Error;
|
||||
use header::{Headers, ContentLength, TransferEncoding};
|
||||
use header::Encoding::Chunked;
|
||||
use method::{Method};
|
||||
use net::{NetworkConnector, NetworkStream, ContextVerifier};
|
||||
use net::{NetworkConnector, NetworkStream};
|
||||
use status::StatusCode;
|
||||
use version::HttpVersion;
|
||||
use version::HttpVersion::{Http10, Http11};
|
||||
@@ -264,11 +264,6 @@ impl Protocol for Http11Protocol {
|
||||
|
||||
Ok(Box::new(Http11Message::with_stream(stream)))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_ssl_verifier(&mut self, verifier: ContextVerifier) {
|
||||
self.connector.set_ssl_verifier(verifier);
|
||||
}
|
||||
}
|
||||
|
||||
impl Http11Protocol {
|
||||
@@ -292,10 +287,6 @@ impl<C: NetworkConnector<Stream=S> + Send + Sync, S: NetworkStream + Send> Netwo
|
||||
-> ::Result<Box<NetworkStream + Send>> {
|
||||
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>);
|
||||
@@ -307,10 +298,6 @@ impl NetworkConnector for Connector {
|
||||
-> ::Result<Box<NetworkStream + Send>> {
|
||||
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,
|
||||
RawStatus,
|
||||
};
|
||||
use net::{NetworkStream, NetworkConnector, ContextVerifier};
|
||||
use net::{NetworkStream, NetworkConnector};
|
||||
use net::{HttpConnector, HttpStream};
|
||||
use url::Url;
|
||||
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)))
|
||||
}
|
||||
|
||||
#[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.
|
||||
@@ -387,7 +382,7 @@ impl<S> HttpMessage for Http2Message<S> where S: CloneableStream {
|
||||
/// (which produces an `HttpStream` for the underlying transport layer).
|
||||
#[inline]
|
||||
pub fn new_protocol() -> Http2Protocol<HttpConnector, HttpStream> {
|
||||
Http2Protocol::with_connector(HttpConnector(None))
|
||||
Http2Protocol::with_connector(HttpConnector)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -16,15 +16,12 @@ use url::Url;
|
||||
use method;
|
||||
use version;
|
||||
use traitobject;
|
||||
use net::ContextVerifier;
|
||||
|
||||
/// The trait provides an API for creating new `HttpMessage`s depending on the underlying HTTP
|
||||
/// protocol.
|
||||
pub trait Protocol {
|
||||
/// 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>>;
|
||||
/// Sets the SSL verifier that should be used when establishing TLS-protected connections.
|
||||
fn set_ssl_verifier(&mut self, verifier: ContextVerifier);
|
||||
}
|
||||
|
||||
/// 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
|
||||
/// the response body.
|
||||
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.
|
||||
fn close_connection(&mut self) -> ::Result<()>;
|
||||
}
|
||||
|
||||
@@ -131,6 +131,7 @@
|
||||
extern crate rustc_serialize as serialize;
|
||||
extern crate time;
|
||||
extern crate url;
|
||||
#[cfg(feature = "openssl")]
|
||||
extern crate openssl;
|
||||
extern crate cookie;
|
||||
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::cell::RefCell;
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::mpsc::Sender;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use solicit::http::HttpScheme;
|
||||
@@ -12,7 +11,7 @@ use solicit::http::frame::{SettingsFrame, Frame};
|
||||
use solicit::http::connection::{HttpConnection, EndStream, DataChunk};
|
||||
|
||||
use header::Headers;
|
||||
use net::{NetworkStream, NetworkConnector, ContextVerifier};
|
||||
use net::{NetworkStream, NetworkConnector};
|
||||
|
||||
pub struct MockStream {
|
||||
pub read: Cursor<Vec<u8>>,
|
||||
@@ -133,39 +132,6 @@ impl NetworkConnector for MockConnector {
|
||||
fn connect(&self, _host: &str, _port: u16, _scheme: &str) -> ::Result<MockStream> {
|
||||
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.
|
||||
@@ -196,10 +162,6 @@ macro_rules! mock_connector (
|
||||
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> {
|
||||
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::net::{SocketAddr, ToSocketAddrs, TcpStream, TcpListener, Shutdown};
|
||||
use std::mem;
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
|
||||
use openssl::ssl::{Ssl, SslStream, SslContext, SSL_VERIFY_NONE};
|
||||
use openssl::ssl::SslMethod::Sslv23;
|
||||
use openssl::ssl::error::StreamError as SslIoError;
|
||||
use openssl::x509::X509FileType;
|
||||
#[cfg(feature = "openssl")]
|
||||
pub use self::openssl::Openssl;
|
||||
|
||||
use typeable::Typeable;
|
||||
use {traitobject};
|
||||
use traitobject;
|
||||
|
||||
/// The write-status indicating headers have not been written.
|
||||
pub enum Fresh {}
|
||||
@@ -70,9 +66,6 @@ pub trait NetworkConnector {
|
||||
type Stream: Into<Box<NetworkStream + Send>>;
|
||||
/// Connect to a remote address.
|
||||
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> {
|
||||
@@ -143,43 +136,22 @@ impl NetworkStream + Send {
|
||||
}
|
||||
|
||||
/// A `NetworkListener` for `HttpStream`s.
|
||||
pub enum HttpListener {
|
||||
/// Http variant.
|
||||
Http(TcpListener),
|
||||
/// Https variant. The two paths point to the certificate and key PEM files, in that order.
|
||||
Https(TcpListener, Arc<SslContext>)
|
||||
}
|
||||
pub struct HttpListener(TcpListener);
|
||||
|
||||
impl Clone for HttpListener {
|
||||
#[inline]
|
||||
fn clone(&self) -> HttpListener {
|
||||
match *self {
|
||||
HttpListener::Http(ref tcp) => HttpListener::Http(tcp.try_clone().unwrap()),
|
||||
HttpListener::Https(ref tcp, ref ssl) => HttpListener::Https(tcp.try_clone().unwrap(), ssl.clone()),
|
||||
}
|
||||
HttpListener(self.0.try_clone().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl HttpListener {
|
||||
|
||||
/// Start listening to an address over HTTP.
|
||||
pub fn http<To: ToSocketAddrs>(addr: To) -> ::Result<HttpListener> {
|
||||
Ok(HttpListener::Http(try!(TcpListener::bind(addr))))
|
||||
pub fn new<To: ToSocketAddrs>(addr: To) -> ::Result<HttpListener> {
|
||||
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 {
|
||||
@@ -187,51 +159,42 @@ impl NetworkListener for HttpListener {
|
||||
|
||||
#[inline]
|
||||
fn accept(&mut self) -> ::Result<HttpStream> {
|
||||
match *self {
|
||||
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())
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(HttpStream(try!(self.0.accept()).0))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn local_addr(&mut self) -> io::Result<SocketAddr> {
|
||||
match *self {
|
||||
HttpListener::Http(ref mut tcp) => tcp.local_addr(),
|
||||
HttpListener::Https(ref mut tcp, _) => tcp.local_addr(),
|
||||
}
|
||||
self.0.local_addr()
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct CloneTcpStream(TcpStream);
|
||||
/// A wrapper around a TcpStream.
|
||||
pub struct HttpStream(pub TcpStream);
|
||||
|
||||
impl Clone for CloneTcpStream{
|
||||
impl Clone for HttpStream {
|
||||
#[inline]
|
||||
fn clone(&self) -> CloneTcpStream {
|
||||
CloneTcpStream(self.0.try_clone().unwrap())
|
||||
fn clone(&self) -> HttpStream {
|
||||
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]
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.0.read(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for CloneTcpStream {
|
||||
impl Write for HttpStream {
|
||||
#[inline]
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.0.write(buf)
|
||||
fn write(&mut self, msg: &[u8]) -> io::Result<usize> {
|
||||
self.0.write(msg)
|
||||
}
|
||||
#[inline]
|
||||
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 {
|
||||
#[inline]
|
||||
fn peer_addr(&mut self) -> io::Result<SocketAddr> {
|
||||
match *self {
|
||||
HttpStream::Http(ref mut inner) => inner.0.peer_addr(),
|
||||
HttpStream::Https(ref mut inner) => inner.get_mut().0.peer_addr()
|
||||
}
|
||||
self.0.peer_addr()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn close(&mut self, how: Shutdown) -> io::Result<()> {
|
||||
#[inline]
|
||||
fn shutdown(tcp: &mut TcpStream, how: Shutdown) -> io::Result<()> {
|
||||
match tcp.shutdown(how) {
|
||||
Ok(_) => Ok(()),
|
||||
// see https://github.com/hyperium/hyper/issues/508
|
||||
Err(ref e) if e.kind() == ErrorKind::NotConnected => Ok(()),
|
||||
err => err
|
||||
}
|
||||
match self.0.shutdown(how) {
|
||||
Ok(_) => Ok(()),
|
||||
// see https://github.com/hyperium/hyper/issues/508
|
||||
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.
|
||||
pub struct HttpConnector(pub Option<ContextVerifier>);
|
||||
|
||||
/// A method that can set verification methods on an SSL context
|
||||
pub type ContextVerifier = Box<Fn(&mut SslContext) -> () + Send + Sync>;
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct HttpConnector;
|
||||
|
||||
impl NetworkConnector for HttpConnector {
|
||||
type Stream = HttpStream;
|
||||
@@ -327,19 +231,7 @@ impl NetworkConnector for HttpConnector {
|
||||
Ok(try!(match scheme {
|
||||
"http" => {
|
||||
debug!("http scheme");
|
||||
Ok(HttpStream::Http(CloneTcpStream(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))
|
||||
Ok(HttpStream(try!(TcpStream::connect(addr))))
|
||||
},
|
||||
_ => {
|
||||
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)]
|
||||
mod tests {
|
||||
use mock::MockStream;
|
||||
use super::{NetworkStream, HttpConnector, NetworkConnector};
|
||||
use super::{NetworkStream};
|
||||
|
||||
#[test]
|
||||
fn test_downcast_box_stream() {
|
||||
@@ -376,13 +494,4 @@ mod tests {
|
||||
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
|
||||
//! }
|
||||
//!
|
||||
//! 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`
|
||||
@@ -43,9 +43,9 @@
|
||||
//!
|
||||
//!
|
||||
//! let (tx, rx) = channel();
|
||||
//! Server::http(SenderHandler {
|
||||
//! Server::http("0.0.0.0:0").unwrap().handle(SenderHandler {
|
||||
//! sender: Mutex::new(tx)
|
||||
//! }).listen("0.0.0.0:0").unwrap();
|
||||
//! }).unwrap();
|
||||
//! ```
|
||||
//!
|
||||
//! Since the `Server` will be listening on multiple threads, the `Handler`
|
||||
@@ -56,9 +56,9 @@
|
||||
//! use hyper::server::{Server, Request, Response};
|
||||
//!
|
||||
//! 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);
|
||||
//! }).listen("0.0.0.0:0").unwrap();
|
||||
//! }).unwrap();
|
||||
//! ```
|
||||
//!
|
||||
//! # The `Request` and `Response` pair
|
||||
@@ -76,14 +76,14 @@
|
||||
//! use hyper::server::{Server, Request, Response};
|
||||
//! 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 {
|
||||
//! hyper::Post => {
|
||||
//! io::copy(&mut req, &mut res.start().unwrap()).unwrap();
|
||||
//! },
|
||||
//! _ => *res.status_mut() = StatusCode::MethodNotAllowed
|
||||
//! }
|
||||
//! }).listen("0.0.0.0:0").unwrap();
|
||||
//! }).unwrap();
|
||||
//! ```
|
||||
//!
|
||||
//! ## An aside: Write Status
|
||||
@@ -107,30 +107,12 @@
|
||||
//! out by calling `start` on the `Request<Fresh>`. This will return a new
|
||||
//! `Request<Streaming>` object, that no longer has `headers_mut()`, but does
|
||||
//! 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::io::{ErrorKind, BufWriter, Write};
|
||||
use std::marker::PhantomData;
|
||||
use std::net::{SocketAddr, ToSocketAddrs};
|
||||
use std::path::Path;
|
||||
use std::thread::{self, JoinHandle};
|
||||
|
||||
use num_cpus;
|
||||
use openssl::ssl::SslContext;
|
||||
|
||||
pub use self::request::Request;
|
||||
pub use self::response::Response;
|
||||
@@ -142,7 +124,7 @@ use buffer::BufReader;
|
||||
use header::{Headers, Expect, Connection};
|
||||
use http;
|
||||
use method::Method;
|
||||
use net::{NetworkListener, NetworkStream, HttpListener};
|
||||
use net::{NetworkListener, NetworkStream, HttpListener, HttpsListener, Ssl};
|
||||
use status::StatusCode;
|
||||
use uri::RequestUri;
|
||||
use version::HttpVersion::Http11;
|
||||
@@ -154,21 +136,13 @@ pub mod response;
|
||||
|
||||
mod listener;
|
||||
|
||||
#[derive(Debug)]
|
||||
enum SslConfig<'a> {
|
||||
CertAndKey(&'a Path, &'a Path),
|
||||
Context(SslContext),
|
||||
}
|
||||
|
||||
/// A server can listen on a TCP socket.
|
||||
///
|
||||
/// Once listening, it will create a `Request`/`Response` pair for each
|
||||
/// incoming connection, and hand them to the provided handler.
|
||||
#[derive(Debug)]
|
||||
pub struct Server<'a, H: Handler, L = HttpListener> {
|
||||
handler: H,
|
||||
ssl: Option<SslConfig<'a>>,
|
||||
_marker: PhantomData<L>
|
||||
pub struct Server<L = HttpListener> {
|
||||
listener: L,
|
||||
}
|
||||
|
||||
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.
|
||||
pub fn new(handler: H) -> Server<'a, H, L> {
|
||||
#[inline]
|
||||
pub fn new(listener: L) -> Server<L> {
|
||||
Server {
|
||||
handler: handler,
|
||||
ssl: None,
|
||||
_marker: PhantomData
|
||||
listener: listener
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, H: Handler + 'static> Server<'a, H, HttpListener> {
|
||||
impl Server<HttpListener> {
|
||||
/// Creates a new server that will handle `HttpStream`s.
|
||||
pub fn http(handler: H) -> Server<'a, H, HttpListener> {
|
||||
Server::new(handler)
|
||||
}
|
||||
/// 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
|
||||
}
|
||||
pub fn http<To: ToSocketAddrs>(addr: To) -> ::Result<Server<HttpListener>> {
|
||||
HttpListener::new(addr).map(Server::new)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, H: Handler + 'static> Server<'a, H, HttpListener> {
|
||||
/// Binds to a socket, and starts handling connections using a task pool.
|
||||
pub fn listen_threads<T: ToSocketAddrs>(self, addr: T, threads: usize) -> ::Result<Listening> {
|
||||
let listener = try!(match self.ssl {
|
||||
Some(SslConfig::CertAndKey(cert, key)) => HttpListener::https(addr, cert, key),
|
||||
Some(SslConfig::Context(ssl_context)) => HttpListener::https_with_context(addr, ssl_context),
|
||||
None => HttpListener::http(addr)
|
||||
});
|
||||
with_listener(self.handler, listener, threads)
|
||||
impl<S: Ssl + Clone + Send> Server<HttpsListener<S>> {
|
||||
/// Creates a new server that will handle `HttpStream`s over SSL.
|
||||
///
|
||||
/// You can use any SSL implementation, as long as implements `hyper::net::Ssl`.
|
||||
pub fn https<A: ToSocketAddrs>(addr: A, ssl: S) -> ::Result<Server<HttpsListener<S>>> {
|
||||
HttpsListener::new(addr, ssl).map(Server::new)
|
||||
}
|
||||
}
|
||||
|
||||
impl<L: NetworkListener + Send + 'static> Server<L> {
|
||||
/// Binds to a socket and starts handling connections.
|
||||
pub fn listen<T: ToSocketAddrs>(self, addr: T) -> ::Result<Listening> {
|
||||
self.listen_threads(addr, num_cpus::get() * 5 / 4)
|
||||
pub fn handle<H: Handler + 'static>(self, handler: H) -> ::Result<Listening> {
|
||||
with_listener(handler, self.listener, num_cpus::get() * 5 / 4)
|
||||
}
|
||||
}
|
||||
impl<
|
||||
'a,
|
||||
H: Handler + 'static,
|
||||
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)
|
||||
/// Binds to a socket and starts handling connections with the provided
|
||||
/// number of threads.
|
||||
pub fn handle_threads<H: Handler + 'static>(self, handler: H, threads: usize) -> ::Result<Listening> {
|
||||
with_listener(handler, self.listener, threads)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,7 +198,7 @@ L: NetworkListener + Send + 'static {
|
||||
let socket = try!(listener.local_addr());
|
||||
|
||||
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 guard = thread::spawn(move || pool.accept(work, threads));
|
||||
|
||||
Reference in New Issue
Block a user