diff --git a/examples/basic.rs b/examples/basic.rs index 164ca7a18..e6fe48227 100644 --- a/examples/basic.rs +++ b/examples/basic.rs @@ -72,15 +72,6 @@ fn main() { .resource("/user/{name}/", |r| r.method(Method::GET).f(with_param)) // async handler .resource("/async/{name}", |r| r.method(Method::GET).a(index_async)) - // redirect - .resource("/", |r| r.method(Method::GET).f(|req| { - println!("{:?}", req); - - httpcodes::HTTPFound - .build() - .header("LOCATION", "/index.html") - .body(Body::Empty) - })) .resource("/test", |r| r.f(|req| { match *req.method() { Method::GET => httpcodes::HTTPOk, @@ -89,7 +80,16 @@ fn main() { } })) // static files - .resource("/static", |r| r.h(fs::StaticFiles::new("examples/static/", true)))) + .resource("/static", |r| r.h(fs::StaticFiles::new("examples/static/", true))) + // redirect + .resource("/", |r| r.method(Method::GET).f(|req| { + println!("{:?}", req); + + httpcodes::HTTPFound + .build() + .header("LOCATION", "/index.html") + .body(Body::Empty) + }))) .serve::<_, ()>("127.0.0.1:8080").unwrap(); println!("Started http server: 127.0.0.1:8080"); diff --git a/src/channel.rs b/src/channel.rs index 3a253862f..e266d4d48 100644 --- a/src/channel.rs +++ b/src/channel.rs @@ -1,4 +1,3 @@ -use std::rc::Rc; use std::net::SocketAddr; use actix::dev::*; @@ -10,6 +9,7 @@ use h1; use h2; use pipeline::Pipeline; use httprequest::HttpRequest; +use server::ServerSettings; /// Low level http request handler pub trait HttpHandler: 'static { @@ -51,21 +51,17 @@ pub struct HttpChannel impl HttpChannel where T: AsyncRead + AsyncWrite + 'static, H: HttpHandler + 'static { - pub fn new(stream: T, - local: SocketAddr, - secure: bool, - peer: Option, - router: Rc>, - http2: bool) -> HttpChannel + pub fn new(settings: ServerSettings, + stream: T, peer: Option, http2: bool) -> HttpChannel { if http2 { HttpChannel { proto: Some(HttpProtocol::H2( - h2::Http2::new(stream, local, secure, peer, router, Bytes::new()))) } + h2::Http2::new(settings, stream, peer, Bytes::new()))) } } else { HttpChannel { proto: Some(HttpProtocol::H1( - h1::Http1::new(stream, local, secure, peer, router))) } + h1::Http1::new(settings, stream, peer))) } } } } @@ -110,9 +106,9 @@ impl Future for HttpChannel let proto = self.proto.take().unwrap(); match proto { HttpProtocol::H1(h1) => { - let (stream, local, secure, addr, router, buf) = h1.into_inner(); - self.proto = Some(HttpProtocol::H2( - h2::Http2::new(stream, local, secure, addr, router, buf))); + let (settings, stream, addr, buf) = h1.into_inner(); + self.proto = Some( + HttpProtocol::H2(h2::Http2::new(settings, stream, addr, buf))); self.poll() } _ => unreachable!() diff --git a/src/h1.rs b/src/h1.rs index 54217610b..cebdfa190 100644 --- a/src/h1.rs +++ b/src/h1.rs @@ -1,5 +1,4 @@ use std::{self, io, ptr}; -use std::rc::Rc; use std::net::SocketAddr; use std::time::Duration; use std::collections::VecDeque; @@ -21,6 +20,7 @@ use httpcodes::HTTPNotFound; use httprequest::HttpRequest; use error::{ParseError, PayloadError, ResponseError}; use payload::{Payload, PayloadWriter, DEFAULT_BUFFER_SIZE}; +use server::ServerSettings; const KEEPALIVE_PERIOD: u64 = 15; // seconds const INIT_BUFFER_SIZE: usize = 8192; @@ -60,8 +60,7 @@ enum Item { pub(crate) struct Http1 { flags: Flags, - router: Rc>, - local: SocketAddr, + settings: ServerSettings, addr: Option, stream: H1Writer, reader: Reader, @@ -79,11 +78,14 @@ impl Http1 where T: AsyncRead + AsyncWrite + 'static, H: HttpHandler + 'static { - pub fn new(stream: T, local: SocketAddr, secure: bool, - addr: Option, router: Rc>) -> Self { - Http1{ router: router, - local: local, - flags: if secure { Flags::SECURE | Flags::KEEPALIVE } else { Flags::KEEPALIVE }, + pub fn new(settings: ServerSettings, stream: T, addr: Option) -> Self { + let flags = if settings.secure() { + Flags::SECURE | Flags::KEEPALIVE + } else { + Flags::KEEPALIVE + }; + Http1{ flags: flags, + settings: settings, addr: addr, stream: H1Writer::new(stream), reader: Reader::new(), @@ -92,11 +94,8 @@ impl Http1 keepalive_timer: None } } - pub fn into_inner(mut self) -> (T, SocketAddr, bool, - Option, Rc>, Bytes) { - (self.stream.unwrap(), self.local, - self.flags.contains(Flags::SECURE), - self.addr, self.router, self.read_buf.freeze()) + pub fn into_inner(mut self) -> (ServerSettings, T, Option, Bytes) { + (self.settings, self.stream.unwrap(), self.addr, self.read_buf.freeze()) } pub fn poll(&mut self) -> Poll { @@ -205,7 +204,7 @@ impl Http1 // start request processing let mut pipe = None; - for h in self.router.iter() { + for h in self.settings.handlers() { req = match h.handle(req) { Ok(t) => { pipe = Some(t); diff --git a/src/h2.rs b/src/h2.rs index 74f033ff8..5a77e1943 100644 --- a/src/h2.rs +++ b/src/h2.rs @@ -1,5 +1,4 @@ use std::{io, cmp, mem}; -use std::rc::Rc; use std::io::{Read, Write}; use std::time::Duration; use std::net::SocketAddr; @@ -22,6 +21,7 @@ use encoding::PayloadType; use httpcodes::HTTPNotFound; use httprequest::HttpRequest; use payload::{Payload, PayloadWriter}; +use server::ServerSettings; const KEEPALIVE_PERIOD: u64 = 15; // seconds @@ -37,8 +37,7 @@ pub(crate) struct Http2 where T: AsyncRead + AsyncWrite + 'static, H: 'static { flags: Flags, - router: Rc>, - local: SocketAddr, + settings: ServerSettings, addr: Option, state: State>, tasks: VecDeque, @@ -55,11 +54,10 @@ impl Http2 where T: AsyncRead + AsyncWrite + 'static, H: HttpHandler + 'static { - pub fn new(stream: T, local: SocketAddr, secure: bool, - addr: Option, router: Rc>, buf: Bytes) -> Self { - Http2{ flags: if secure { Flags::SECURE } else { Flags::empty() }, - router: router, - local: local, + pub fn new(settings: ServerSettings, stream: T, addr: Option, buf: Bytes) -> Self + { + Http2{ flags: if settings.secure() { Flags::SECURE } else { Flags::empty() }, + settings: settings, addr: addr, tasks: VecDeque::new(), state: State::Handshake( @@ -154,7 +152,7 @@ impl Http2 self.keepalive_timer.take(); self.tasks.push_back( - Entry::new(parts, body, resp, self.addr, &self.router)); + Entry::new(parts, body, resp, self.addr, &self.settings)); } Ok(Async::NotReady) => { // start keep-alive timer @@ -233,7 +231,7 @@ impl Entry { recv: RecvStream, resp: Respond, addr: Option, - router: &Rc>) -> Entry + settings: &ServerSettings) -> Entry where H: HttpHandler + 'static { // Payload and Content-Encoding @@ -250,7 +248,7 @@ impl Entry { // start request processing let mut task = None; - for h in router.iter() { + for h in settings.handlers() { req = match h.handle(req) { Ok(t) => { task = Some(t); diff --git a/src/lib.rs b/src/lib.rs index a2d9d6830..78aa9c577 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -131,7 +131,7 @@ pub mod dev { pub use pipeline::Pipeline; pub use channel::{HttpChannel, HttpHandler, IntoHttpHandler}; pub use param::{FromParam, Params}; - + pub use server::ServerSettings; pub use httprequest::UrlEncoded; pub use httpresponse::HttpResponseBuilder; } diff --git a/src/server.rs b/src/server.rs index 635147b51..674aca697 100644 --- a/src/server.rs +++ b/src/server.rs @@ -26,6 +26,53 @@ use tokio_openssl::{SslStream, SslAcceptorExt}; use channel::{HttpChannel, HttpHandler, IntoHttpHandler}; +/// Various server settings +pub struct ServerSettings (Rc>); + +struct InnerServerSettings { + h: Vec, + addr: Option, + secure: bool, + sethost: bool, +} + +impl Clone for ServerSettings { + fn clone(&self) -> Self { + ServerSettings(Rc::clone(&self.0)) + } +} + +impl ServerSettings { + /// Crate server settings instance + fn new(h: Vec, addr: Option, secure: bool, sethost: bool) -> Self { + ServerSettings( + Rc::new(InnerServerSettings { + h: h, + addr: addr, + secure: secure, + sethost: sethost })) + } + + /// Returns list of http handlers + pub fn handlers(&self) -> &Vec { + &self.0.h + } + /// Returns the socket address of the local half of this TCP connection + pub fn local_addr(&self) -> Option { + self.0.addr + } + + /// Returns true if connection is secure(https) + pub fn secure(&self) -> bool { + self.0.secure + } + + /// Should http channel set *HOST* header + pub fn set_host_header(&self) -> bool { + self.0.sethost + } +} + /// An HTTP Server /// /// `T` - async stream, anything that implements `AsyncRead` + `AsyncWrite`. @@ -34,9 +81,10 @@ use channel::{HttpChannel, HttpHandler, IntoHttpHandler}; /// /// `H` - request handler pub struct HttpServer { - h: Rc>, + h: Option>, io: PhantomData, addr: PhantomData, + sethost: bool, } impl Actor for HttpServer { @@ -51,9 +99,16 @@ impl HttpServer where H: HttpHandler { let apps: Vec<_> = handler.into_iter().map(|h| h.into_handler()).collect(); - HttpServer {h: Rc::new(apps), + HttpServer {h: Some(apps), io: PhantomData, - addr: PhantomData} + addr: PhantomData, + sethost: false} + } + + /// Set *HOST* header if not set + pub fn set_host_header(mut self) -> Self { + self.sethost = true; + self } } @@ -63,15 +118,18 @@ impl HttpServer H: HttpHandler, { /// Start listening for incomming connections from stream. - pub fn serve_incoming(self, stream: S, secure: bool) -> io::Result + pub fn serve_incoming(mut self, stream: S, secure: bool) -> io::Result where Self: ActorAddress, S: Stream + 'static { + let addr: SocketAddr = "127.0.0.1:8080".parse().unwrap(); + let settings = ServerSettings::new( + self.h.take().unwrap(), Some(addr), secure, self.sethost); + Ok(HttpServer::create(move |ctx| { - let addr: SocketAddr = "127.0.0.1:8080".parse().unwrap(); ctx.add_stream(stream.map( - move |(t, _)| IoStream{io: t, srv: addr, - peer: None, http2: false, secure: secure})); + move |(t, _)| IoStream{settings: settings.clone(), + io: t, peer: None, http2: false})); self })) } @@ -107,18 +165,21 @@ impl HttpServer { /// /// This methods converts address to list of `SocketAddr` /// then binds to all available addresses. - pub fn serve(self, addr: S) -> io::Result + pub fn serve(mut self, addr: S) -> io::Result where Self: ActorAddress, S: net::ToSocketAddrs, { let addrs = self.bind(addr)?; + let settings = ServerSettings::new( + self.h.take().unwrap(), Some(addrs[0].0), false, self.sethost); Ok(HttpServer::create(move |ctx| { for (addr, tcp) in addrs { info!("Starting http server on {}", addr); + let s = settings.clone(); ctx.add_stream(tcp.incoming().map( - move |(t, a)| IoStream{io: t, srv: addr, - peer: Some(a), http2: false, secure: false})); + move |(t, a)| IoStream{settings: s.clone(), + io: t, peer: Some(a), http2: false})); } self })) @@ -132,11 +193,14 @@ impl HttpServer, net::SocketAddr, H> { /// /// This methods converts address to list of `SocketAddr` /// then binds to all available addresses. - pub fn serve_tls(self, addr: S, pkcs12: ::Pkcs12) -> io::Result + pub fn serve_tls(mut self, addr: S, pkcs12: ::Pkcs12) -> io::Result where Self: ActorAddress, S: net::ToSocketAddrs, { let addrs = self.bind(addr)?; + let settings = ServerSettings::new( + self.h.take().unwrap(), Some(addrs[0].0.clone()), true, self.sethost); + let acceptor = match TlsAcceptor::builder(pkcs12) { Ok(builder) => { match builder.build() { @@ -151,12 +215,14 @@ impl HttpServer, net::SocketAddr, H> { for (srv, tcp) in addrs { info!("Starting tls http server on {}", srv); + let st = settings.clone(); let acc = acceptor.clone(); ctx.add_stream(tcp.incoming().and_then(move |(stream, addr)| { + let st2 = st.clone(); TlsAcceptorExt::accept_async(acc.as_ref(), stream) .map(move |t| - IoStream{io: t, srv: srv.clone(), - peer: Some(addr), http2: false, secure: true}) + IoStream{settings: st2.clone(), + io: t, peer: Some(addr), http2: false}) .map_err(|err| { trace!("Error during handling tls connection: {}", err); io::Error::new(io::ErrorKind::Other, err) @@ -175,15 +241,16 @@ impl HttpServer, net::SocketAddr, H> { /// /// This methods converts address to list of `SocketAddr` /// then binds to all available addresses. - pub fn serve_tls(self, addr: S, identity: ParsedPkcs12) -> io::Result + pub fn serve_tls(mut self, addr: S, identity: ParsedPkcs12) -> io::Result where Self: ActorAddress, S: net::ToSocketAddrs, { let addrs = self.bind(addr)?; - let acceptor = match SslAcceptorBuilder::mozilla_intermediate(SslMethod::tls(), - &identity.pkey, - &identity.cert, - &identity.chain) + let settings = ServerSettings::new( + self.h.take().unwrap(), Some(addrs[0].0.clone()), true, self.sethost); + + let acceptor = match SslAcceptorBuilder::mozilla_intermediate( + SslMethod::tls(), &identity.pkey, &identity.cert, &identity.chain) { Ok(mut builder) => { match builder.builder_mut().set_alpn_protocols(&[b"h2", b"http/1.1"]) { @@ -198,8 +265,10 @@ impl HttpServer, net::SocketAddr, H> { for (srv, tcp) in addrs { info!("Starting tls http server on {}", srv); + let st = settings.clone(); let acc = acceptor.clone(); ctx.add_stream(tcp.incoming().and_then(move |(stream, addr)| { + let st2 = st.clone(); SslAcceptorExt::accept_async(&acc, stream) .map(move |stream| { let http2 = if let Some(p) = @@ -209,8 +278,8 @@ impl HttpServer, net::SocketAddr, H> { } else { false }; - IoStream{io: stream, srv: srv.clone(), - peer: Some(addr), http2: http2, secure: true} + IoStream{settings: st2.clone(), + io: stream, peer: Some(addr), http2: http2} }) .map_err(|err| { trace!("Error during handling tls connection: {}", err); @@ -223,27 +292,26 @@ impl HttpServer, net::SocketAddr, H> { } } -struct IoStream { +struct IoStream { io: T, - srv: SocketAddr, peer: Option, http2: bool, - secure: bool, + settings: ServerSettings, } -impl ResponseType for IoStream +impl ResponseType for IoStream where T: AsyncRead + AsyncWrite + 'static { type Item = (); type Error = (); } -impl StreamHandler, io::Error> for HttpServer +impl StreamHandler, io::Error> for HttpServer where T: AsyncRead + AsyncWrite + 'static, H: HttpHandler + 'static, A: 'static {} -impl Handler, io::Error> for HttpServer +impl Handler, io::Error> for HttpServer where T: AsyncRead + AsyncWrite + 'static, H: HttpHandler + 'static, A: 'static, @@ -252,12 +320,11 @@ impl Handler, io::Error> for HttpServer debug!("Error handling request: {}", err) } - fn handle(&mut self, msg: IoStream, _: &mut Context) - -> Response> + fn handle(&mut self, msg: IoStream, _: &mut Context) + -> Response> { Arbiter::handle().spawn( - HttpChannel::new(msg.io, msg.srv, msg.secure, - msg.peer, Rc::clone(&self.h), msg.http2)); + HttpChannel::new(msg.settings, msg.io, msg.peer, msg.http2)); Self::empty() } }