1
0
mirror of https://github.com/fafhrd91/actix-web synced 2024-11-28 01:52:57 +01:00

refactor RouteHandler trait

This commit is contained in:
Nikolay Kim 2017-11-29 13:26:55 -08:00
parent 6f833798c7
commit 16ceb741b8
8 changed files with 144 additions and 169 deletions

View File

@ -82,8 +82,15 @@ fn main() {
.header("LOCATION", "/index.html") .header("LOCATION", "/index.html")
.body(Body::Empty) .body(Body::Empty)
})) }))
.handler("/test", |req| {
match *req.method() {
Method::GET => httpcodes::HTTPOk,
Method::POST => httpcodes::HTTPMethodNotAllowed,
_ => httpcodes::HTTPNotFound,
}
})
// static files // static files
.route_handler("/static", StaticFiles::new("examples/static/", true))) .route("/static", StaticFiles::new("examples/static/", true)))
.serve::<_, ()>("127.0.0.1:8080").unwrap(); .serve::<_, ()>("127.0.0.1:8080").unwrap();
println!("Started http server: 127.0.0.1:8080"); println!("Started http server: 127.0.0.1:8080");

View File

@ -66,7 +66,8 @@ fn main() {
.middleware(middlewares::Logger::default()) .middleware(middlewares::Logger::default())
// websocket route // websocket route
.resource("/ws/", |r| r.get(ws_index)) .resource("/ws/", |r| r.get(ws_index))
.route_handler("/", StaticFiles::new("examples/static/", true))) // static files
.route("/", StaticFiles::new("examples/static/", true)))
// start http server on 127.0.0.1:8080 // start http server on 127.0.0.1:8080
.serve::<_, ()>("127.0.0.1:8080").unwrap(); .serve::<_, ()>("127.0.0.1:8080").unwrap();

View File

@ -2,7 +2,7 @@ use std::rc::Rc;
use std::collections::HashMap; use std::collections::HashMap;
use task::Task; use task::Task;
use route::{RouteHandler, FnHandler, Reply}; use route::{RouteHandler, WrapHandler, Reply, Handler};
use resource::Resource; use resource::Resource;
use recognizer::{RouteRecognizer, check_pattern}; use recognizer::{RouteRecognizer, check_pattern};
use httprequest::HttpRequest; use httprequest::HttpRequest;
@ -126,10 +126,7 @@ impl<S> ApplicationBuilder<S> where S: 'static {
/// store userid and friend in the exposed Params object: /// store userid and friend in the exposed Params object:
/// ///
/// ```rust /// ```rust
/// extern crate actix;
/// extern crate actix_web; /// extern crate actix_web;
///
/// use actix::*;
/// use actix_web::*; /// use actix_web::*;
/// ///
/// fn main() { /// fn main() {
@ -158,7 +155,7 @@ impl<S> ApplicationBuilder<S> where S: 'static {
self self
} }
/// Default resource is used if no matches route could be found. /// Default resource is used if no match route could be found.
pub fn default_resource<F>(&mut self, f: F) -> &mut Self pub fn default_resource<F>(&mut self, f: F) -> &mut Self
where F: FnOnce(&mut Resource<S>) + 'static where F: FnOnce(&mut Resource<S>) + 'static
{ {
@ -178,7 +175,7 @@ impl<S> ApplicationBuilder<S> where S: 'static {
/// ///
/// fn main() { /// fn main() {
/// let app = Application::default("/") /// let app = Application::default("/")
/// .handler("/test", |req| { /// .inline("/test", |req| {
/// match *req.method() { /// match *req.method() {
/// Method::GET => httpcodes::HTTPOk, /// Method::GET => httpcodes::HTTPOk,
/// Method::POST => httpcodes::HTTPMethodNotAllowed, /// Method::POST => httpcodes::HTTPMethodNotAllowed,
@ -189,28 +186,22 @@ impl<S> ApplicationBuilder<S> where S: 'static {
/// } /// }
/// ``` /// ```
pub fn handler<P, F, R>(&mut self, path: P, handler: F) -> &mut Self pub fn handler<P, F, R>(&mut self, path: P, handler: F) -> &mut Self
where F: Fn(HttpRequest<S>) -> R + 'static, where P: Into<String>,
R: Into<Reply> + 'static, F: Fn(HttpRequest<S>) -> R + 'static,
P: Into<String>, R: Into<Reply> + 'static
{ {
self.parts.as_mut().expect("Use after finish") self.parts.as_mut().expect("Use after finish")
.handlers.insert(path.into(), Box::new(FnHandler::new(handler))); .handlers.insert(path.into(), Box::new(WrapHandler::new(handler)));
self self
} }
/// Add path handler /// This method register handler for specified path prefix.
pub fn route_handler<H, P>(&mut self, path: P, h: H) -> &mut Self /// Any path that starts with this prefix matches handler.
where H: RouteHandler<S> + 'static, P: Into<String> pub fn route<P, H>(&mut self, path: P, handler: H) -> &mut Self
where P: Into<String>, H: Handler<S>
{ {
{ self.parts.as_mut().expect("Use after finish")
// add resource .handlers.insert(path.into(), Box::new(WrapHandler::new(handler)));
let parts = self.parts.as_mut().expect("Use after finish");
let path = path.into();
if parts.handlers.contains_key(&path) {
panic!("Handler already registered: {:?}", path);
}
parts.handlers.insert(path, Box::new(h));
}
self self
} }

View File

@ -10,6 +10,7 @@
// dev specific // dev specific
pub use task::Task; pub use task::Task;
pub use route::Handler;
pub use pipeline::Pipeline; pub use pipeline::Pipeline;
pub use recognizer::RouteRecognizer; pub use recognizer::RouteRecognizer;
pub use channel::HttpChannel; pub use channel::HttpChannel;

View File

@ -82,7 +82,7 @@ pub use application::Application;
pub use httprequest::{HttpRequest, UrlEncoded}; pub use httprequest::{HttpRequest, UrlEncoded};
pub use httpresponse::HttpResponse; pub use httpresponse::HttpResponse;
pub use payload::{Payload, PayloadItem}; pub use payload::{Payload, PayloadItem};
pub use route::{Frame, RouteHandler, Reply}; pub use route::{Frame, Reply};
pub use resource::Resource; pub use resource::Resource;
pub use recognizer::Params; pub use recognizer::Params;
pub use server::HttpServer; pub use server::HttpServer;

View File

@ -6,7 +6,7 @@ use futures::Stream;
use task::Task; use task::Task;
use error::Error; use error::Error;
use route::{Reply, RouteHandler, Frame, FnHandler, StreamHandler}; use route::{Reply, RouteHandler, Frame, WrapHandler, Handler, StreamHandler};
use httprequest::HttpRequest; use httprequest::HttpRequest;
use httpcodes::{HTTPNotFound, HTTPMethodNotAllowed}; use httpcodes::{HTTPNotFound, HTTPMethodNotAllowed};
@ -17,13 +17,12 @@ use httpcodes::{HTTPNotFound, HTTPMethodNotAllowed};
/// Resource in turn has at least one route. /// Resource in turn has at least one route.
/// Route corresponds to handling HTTP method by calling route handler. /// Route corresponds to handling HTTP method by calling route handler.
/// ///
/// ```rust,ignore /// ```rust
/// /// extern crate actix_web;
/// struct MyRoute;
/// ///
/// fn main() { /// fn main() {
/// let router = RoutingMap::default() /// let app = actix_web::Application::default("/")
/// .resource("/", |r| r.post::<MyRoute>()) /// .resource("/", |r| r.get(|_| actix_web::HttpResponse::Ok()))
/// .finish(); /// .finish();
/// } /// }
pub struct Resource<S=()> { pub struct Resource<S=()> {
@ -63,7 +62,7 @@ impl<S> Resource<S> where S: 'static {
where F: Fn(HttpRequest<S>) -> R + 'static, where F: Fn(HttpRequest<S>) -> R + 'static,
R: Into<Reply> + 'static, R: Into<Reply> + 'static,
{ {
self.routes.insert(method, Box::new(FnHandler::new(handler))); self.routes.insert(method, Box::new(WrapHandler::new(handler)));
} }
/// Register async handler for specified method. /// Register async handler for specified method.
@ -74,51 +73,42 @@ impl<S> Resource<S> where S: 'static {
self.routes.insert(method, Box::new(StreamHandler::new(handler))); self.routes.insert(method, Box::new(StreamHandler::new(handler)));
} }
/// Register handler for specified method.
pub fn route_handler<H>(&mut self, method: Method, handler: H)
where H: RouteHandler<S>
{
self.routes.insert(method, Box::new(handler));
}
/// Default handler is used if no matched route found. /// Default handler is used if no matched route found.
/// By default `HTTPMethodNotAllowed` is used. /// By default `HTTPMethodNotAllowed` is used.
pub fn default_handler<H>(&mut self, handler: H) pub fn default_handler<H>(&mut self, handler: H) where H: Handler<S>
where H: RouteHandler<S>
{ {
self.default = Box::new(handler); self.default = Box::new(WrapHandler::new(handler));
} }
/// Handler for `GET` method. /// Handler for `GET` method.
pub fn get<F, R>(&mut self, handler: F) pub fn get<F, R>(&mut self, handler: F)
where F: Fn(HttpRequest<S>) -> R + 'static, where F: Fn(HttpRequest<S>) -> R + 'static,
R: Into<Reply> + 'static, { R: Into<Reply> + 'static, {
self.routes.insert(Method::GET, Box::new(FnHandler::new(handler))); self.routes.insert(Method::GET, Box::new(WrapHandler::new(handler)));
} }
/// Handler for `POST` method. /// Handler for `POST` method.
pub fn post<F, R>(&mut self, handler: F) pub fn post<F, R>(&mut self, handler: F)
where F: Fn(HttpRequest<S>) -> R + 'static, where F: Fn(HttpRequest<S>) -> R + 'static,
R: Into<Reply> + 'static, { R: Into<Reply> + 'static, {
self.routes.insert(Method::POST, Box::new(FnHandler::new(handler))); self.routes.insert(Method::POST, Box::new(WrapHandler::new(handler)));
} }
/// Handler for `PUT` method. /// Handler for `PUT` method.
pub fn put<F, R>(&mut self, handler: F) pub fn put<F, R>(&mut self, handler: F)
where F: Fn(HttpRequest<S>) -> R + 'static, where F: Fn(HttpRequest<S>) -> R + 'static,
R: Into<Reply> + 'static, { R: Into<Reply> + 'static, {
self.routes.insert(Method::PUT, Box::new(FnHandler::new(handler))); self.routes.insert(Method::PUT, Box::new(WrapHandler::new(handler)));
} }
/// Handler for `DELETE` method. /// Handler for `DELETE` method.
pub fn delete<F, R>(&mut self, handler: F) pub fn delete<F, R>(&mut self, handler: F)
where F: Fn(HttpRequest<S>) -> R + 'static, where F: Fn(HttpRequest<S>) -> R + 'static,
R: Into<Reply> + 'static, { R: Into<Reply> + 'static, {
self.routes.insert(Method::DELETE, Box::new(FnHandler::new(handler))); self.routes.insert(Method::DELETE, Box::new(WrapHandler::new(handler)));
} }
} }
impl<S: 'static> RouteHandler<S> for Resource<S> { impl<S: 'static> RouteHandler<S> for Resource<S> {
fn handle(&self, req: HttpRequest<S>, task: &mut Task) { fn handle(&self, req: HttpRequest<S>, task: &mut Task) {

View File

@ -4,15 +4,14 @@ use std::marker::PhantomData;
use std::result::Result as StdResult; use std::result::Result as StdResult;
use actix::Actor; use actix::Actor;
// use http::{header, Version};
use futures::Stream; use futures::Stream;
use task::{Task, DrainFut, IoContext};
use body::Binary; use body::Binary;
use error::{Error}; //, ExpectError, Result}; use error::Error;
use context::HttpContext; use context::HttpContext;
use httprequest::HttpRequest; use httprequest::HttpRequest;
use httpresponse::HttpResponse; use httpresponse::HttpResponse;
use task::{Task, DrainFut, IoContext};
#[doc(hidden)] #[doc(hidden)]
#[derive(Debug)] #[derive(Debug)]
@ -28,118 +27,32 @@ impl Frame {
} }
} }
/// Trait defines object that could be regestered as resource route /// Trait defines object that could be regestered as route handler
#[allow(unused_variables)] #[allow(unused_variables)]
pub trait RouteHandler<S>: 'static { pub trait Handler<S>: 'static {
type Result: Into<Reply>;
/// Handle request /// Handle request
fn handle(&self, req: HttpRequest<S>, task: &mut Task); fn handle(&self, req: HttpRequest<S>) -> Self::Result;
/// Set route prefix /// Set route prefix
fn set_prefix(&mut self, prefix: String) {} fn set_prefix(&mut self, prefix: String) {}
} }
/* /// Handler<S> for Fn()
/// Actors with ability to handle http requests. impl<F, R, S> Handler<S> for F
#[allow(unused_variables)]
pub trait RouteState {
/// Shared state. State is shared with all routes within same application
/// and could be accessed with `HttpRequest::state()` method.
type State;
/// Handle `EXPECT` header. By default respones with `HTTP/1.1 100 Continue`
fn expect(req: &mut HttpRequest<Self::State>, ctx: &mut Self::Context) -> Result<()>
where Self: Actor<Context=HttpContext<Self>>
{
// handle expect header only for HTTP/1.1
if req.version() == Version::HTTP_11 {
if let Some(expect) = req.headers().get(header::EXPECT) {
if let Ok(expect) = expect.to_str() {
if expect.to_lowercase() == "100-continue" {
ctx.write("HTTP/1.1 100 Continue\r\n\r\n");
Ok(())
} else {
Err(ExpectError::UnknownExpect.into())
}
} else {
Err(ExpectError::Encoding.into())
}
} else {
Ok(())
}
} else {
Ok(())
}
}
/// Handle incoming request with http actor.
fn handle(req: HttpRequest<Self::State>) -> Result<Reply>
where Self: Default, Self: Actor<Context=HttpContext<Self>>
{
Ok(HttpContext::new(req, Self::default()).into())
}
}*/
/// Fn() route handler
pub(crate)
struct FnHandler<S, R, F>
where F: Fn(HttpRequest<S>) -> R + 'static, where F: Fn(HttpRequest<S>) -> R + 'static,
R: Into<Reply>, R: Into<Reply> + 'static
S: 'static,
{ {
f: Box<F>, type Result = R;
s: PhantomData<S>,
}
impl<S, R, F> FnHandler<S, R, F> fn handle(&self, req: HttpRequest<S>) -> R {
where F: Fn(HttpRequest<S>) -> R + 'static, (self)(req)
R: Into<Reply> + 'static,
S: 'static,
{
pub fn new(f: F) -> Self {
FnHandler{f: Box::new(f), s: PhantomData}
} }
} }
impl<S, R, F> RouteHandler<S> for FnHandler<S, R, F> /// Represents response process.
where F: Fn(HttpRequest<S>) -> R + 'static, pub struct Reply(ReplyItem);
R: Into<Reply> + 'static,
S: 'static,
{
fn handle(&self, req: HttpRequest<S>, task: &mut Task) {
(self.f)(req).into().into(task)
}
}
/// Async route handler
pub(crate)
struct StreamHandler<S, R, F>
where F: Fn(HttpRequest<S>) -> R + 'static,
R: Stream<Item=Frame, Error=Error> + 'static,
S: 'static,
{
f: Box<F>,
s: PhantomData<S>,
}
impl<S, R, F> StreamHandler<S, R, F>
where F: Fn(HttpRequest<S>) -> R + 'static,
R: Stream<Item=Frame, Error=Error> + 'static,
S: 'static,
{
pub fn new(f: F) -> Self {
StreamHandler{f: Box::new(f), s: PhantomData}
}
}
impl<S, R, F> RouteHandler<S> for StreamHandler<S, R, F>
where F: Fn(HttpRequest<S>) -> R + 'static,
R: Stream<Item=Frame, Error=Error> + 'static,
S: 'static,
{
fn handle(&self, req: HttpRequest<S>, task: &mut Task) {
task.stream((self.f)(req))
}
}
enum ReplyItem { enum ReplyItem {
Message(HttpResponse), Message(HttpResponse),
@ -147,11 +60,8 @@ enum ReplyItem {
Stream(Box<Stream<Item=Frame, Error=Error>>), Stream(Box<Stream<Item=Frame, Error=Error>>),
} }
/// Represents response process. impl Reply {
pub struct Reply(ReplyItem);
impl Reply
{
/// Create actor response /// Create actor response
pub fn actor<A, S>(ctx: HttpContext<A, S>) -> Reply pub fn actor<A, S>(ctx: HttpContext<A, S>) -> Reply
where A: Actor<Context=HttpContext<A, S>>, S: 'static where A: Actor<Context=HttpContext<A, S>>, S: 'static
@ -209,3 +119,78 @@ impl<A: Actor<Context=HttpContext<A, S>>, S: 'static> From<HttpContext<A, S>> fo
Reply(ReplyItem::Actor(Box::new(item))) Reply(ReplyItem::Actor(Box::new(item)))
} }
} }
/// Trait defines object that could be regestered as resource route
pub(crate) trait RouteHandler<S>: 'static {
/// Handle request
fn handle(&self, req: HttpRequest<S>, task: &mut Task);
/// Set route prefix
fn set_prefix(&mut self, _prefix: String) {}
}
/// Route handler wrapper for Handler
pub(crate)
struct WrapHandler<S, H, R>
where H: Handler<S, Result=R>,
R: Into<Reply>,
S: 'static,
{
h: H,
s: PhantomData<S>,
}
impl<S, H, R> WrapHandler<S, H, R>
where H: Handler<S, Result=R>,
R: Into<Reply>,
S: 'static,
{
pub fn new(h: H) -> Self {
WrapHandler{h: h, s: PhantomData}
}
}
impl<S, H, R> RouteHandler<S> for WrapHandler<S, H, R>
where H: Handler<S, Result=R>,
R: Into<Reply> + 'static,
S: 'static,
{
fn handle(&self, req: HttpRequest<S>, task: &mut Task) {
self.h.handle(req).into().into(task)
}
fn set_prefix(&mut self, prefix: String) {
self.h.set_prefix(prefix)
}
}
/// Async route handler
pub(crate)
struct StreamHandler<S, R, F>
where F: Fn(HttpRequest<S>) -> R + 'static,
R: Stream<Item=Frame, Error=Error> + 'static,
S: 'static,
{
f: Box<F>,
s: PhantomData<S>,
}
impl<S, R, F> StreamHandler<S, R, F>
where F: Fn(HttpRequest<S>) -> R + 'static,
R: Stream<Item=Frame, Error=Error> + 'static,
S: 'static,
{
pub fn new(f: F) -> Self {
StreamHandler{f: Box::new(f), s: PhantomData}
}
}
impl<S, R, F> RouteHandler<S> for StreamHandler<S, R, F>
where F: Fn(HttpRequest<S>) -> R + 'static,
R: Stream<Item=Frame, Error=Error> + 'static,
S: 'static,
{
fn handle(&self, req: HttpRequest<S>, task: &mut Task) {
task.stream((self.f)(req))
}
}

View File

@ -7,9 +7,8 @@ use std::fmt::Write;
use std::fs::{File, DirEntry}; use std::fs::{File, DirEntry};
use std::path::PathBuf; use std::path::PathBuf;
use task::Task;
use route::RouteHandler;
use mime_guess::get_mime_type; use mime_guess::get_mime_type;
use route::Handler;
use httprequest::HttpRequest; use httprequest::HttpRequest;
use httpresponse::HttpResponse; use httpresponse::HttpResponse;
use httpcodes::{HTTPOk, HTTPNotFound, HTTPForbidden}; use httpcodes::{HTTPOk, HTTPNotFound, HTTPForbidden};
@ -24,7 +23,7 @@ use httpcodes::{HTTPOk, HTTPNotFound, HTTPForbidden};
/// ///
/// fn main() { /// fn main() {
/// let app = Application::default("/") /// let app = Application::default("/")
/// .route_handler("/static", StaticFiles::new(".", true)) /// .handler("/static", StaticFiles::new(".", true))
/// .finish(); /// .finish();
/// } /// }
/// ``` /// ```
@ -128,7 +127,8 @@ impl StaticFiles {
} }
} }
impl<S: 'static> RouteHandler<S> for StaticFiles { impl<S> Handler<S> for StaticFiles {
type Result = Result<HttpResponse, io::Error>;
fn set_prefix(&mut self, prefix: String) { fn set_prefix(&mut self, prefix: String) {
if prefix != "/" { if prefix != "/" {
@ -136,9 +136,9 @@ impl<S: 'static> RouteHandler<S> for StaticFiles {
} }
} }
fn handle(&self, req: HttpRequest<S>, task: &mut Task) { fn handle(&self, req: HttpRequest<S>) -> Self::Result {
if !self.accessible { if !self.accessible {
task.reply(HTTPNotFound) Ok(HTTPNotFound.into())
} else { } else {
let mut hidden = false; let mut hidden = false;
let filepath = req.path()[self.prefix.len()..] let filepath = req.path()[self.prefix.len()..]
@ -152,7 +152,7 @@ impl<S: 'static> RouteHandler<S> for StaticFiles {
// hidden file // hidden file
if hidden { if hidden {
task.reply(HTTPNotFound) return Ok(HTTPNotFound.into())
} }
// full filepath // full filepath
@ -160,19 +160,19 @@ impl<S: 'static> RouteHandler<S> for StaticFiles {
let filename = match self.directory.join(&filepath[idx..]).canonicalize() { let filename = match self.directory.join(&filepath[idx..]).canonicalize() {
Ok(fname) => fname, Ok(fname) => fname,
Err(err) => return match err.kind() { Err(err) => return match err.kind() {
io::ErrorKind::NotFound => task.reply(HTTPNotFound), io::ErrorKind::NotFound => Ok(HTTPNotFound.into()),
io::ErrorKind::PermissionDenied => task.reply(HTTPForbidden), io::ErrorKind::PermissionDenied => Ok(HTTPForbidden.into()),
_ => task.error(err), _ => Err(err),
} }
}; };
if filename.is_dir() { if filename.is_dir() {
match self.index(&filepath[idx..], &filename) { match self.index(&filepath[idx..], &filename) {
Ok(resp) => task.reply(resp), Ok(resp) => Ok(resp),
Err(err) => match err.kind() { Err(err) => match err.kind() {
io::ErrorKind::NotFound => task.reply(HTTPNotFound), io::ErrorKind::NotFound => Ok(HTTPNotFound.into()),
io::ErrorKind::PermissionDenied => task.reply(HTTPForbidden), io::ErrorKind::PermissionDenied => Ok(HTTPForbidden.into()),
_ => task.error(err), _ => Err(err),
} }
} }
} else { } else {
@ -185,9 +185,9 @@ impl<S: 'static> RouteHandler<S> for StaticFiles {
Ok(mut file) => { Ok(mut file) => {
let mut data = Vec::new(); let mut data = Vec::new();
let _ = file.read_to_end(&mut data); let _ = file.read_to_end(&mut data);
task.reply(resp.body(data).unwrap()) Ok(resp.body(data).unwrap())
}, },
Err(err) => task.error(err), Err(err) => Err(err),
} }
} }
} }