mirror of
https://github.com/actix/actix-web.git
synced 2024-11-25 19:11:10 +00:00
Application, router, resource builders
This commit is contained in:
parent
5480cb5d49
commit
5901f0f9f5
11 changed files with 406 additions and 146 deletions
36
README.md
36
README.md
|
@ -39,35 +39,21 @@ use std::str::FromStr;
|
||||||
use actix::prelude::*;
|
use actix::prelude::*;
|
||||||
use actix_web::*;
|
use actix_web::*;
|
||||||
|
|
||||||
// Route
|
|
||||||
struct MyRoute;
|
|
||||||
|
|
||||||
impl Actor for MyRoute {
|
|
||||||
type Context = HttpContext<Self>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Route for MyRoute {
|
|
||||||
type State = ();
|
|
||||||
|
|
||||||
fn request(req: HttpRequest, payload: Payload, ctx: &mut HttpContext<Self>) -> Reply<Self>
|
|
||||||
{
|
|
||||||
Reply::reply(httpcodes::HTTPOk)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let system = System::new("test");
|
let system = System::new("test");
|
||||||
|
|
||||||
// create routing map with `MyRoute` route
|
|
||||||
let mut routes = RoutingMap::default();
|
|
||||||
routes
|
|
||||||
.add_resource("/")
|
|
||||||
.post::<MyRoute>();
|
|
||||||
|
|
||||||
// start http server
|
// start http server
|
||||||
let http = HttpServer::new(routes);
|
HttpServer::new(
|
||||||
http.serve::<()>(
|
// create routing map with `MyRoute` route
|
||||||
&net::SocketAddr::from_str("127.0.0.1:8880").unwrap()).unwrap();
|
RoutingMap::default()
|
||||||
|
.resource("/", |r|
|
||||||
|
r.handler(Method::GET, |req, payload, state| {
|
||||||
|
httpcodes::HTTPOk
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.finish())
|
||||||
|
.serve::<()>(
|
||||||
|
&net::SocketAddr::from_str("127.0.0.1:8880").unwrap()).unwrap();
|
||||||
|
|
||||||
// stop system
|
// stop system
|
||||||
Arbiter::handle().spawn_fn(|| {
|
Arbiter::handle().spawn_fn(|| {
|
||||||
|
|
|
@ -5,11 +5,12 @@ use std::collections::HashMap;
|
||||||
use route_recognizer::Router;
|
use route_recognizer::Router;
|
||||||
|
|
||||||
use task::Task;
|
use task::Task;
|
||||||
use route::RouteHandler;
|
use route::{RouteHandler, FnHandler};
|
||||||
use router::Handler;
|
use router::Handler;
|
||||||
use resource::Resource;
|
use resource::Resource;
|
||||||
use payload::Payload;
|
use payload::Payload;
|
||||||
use httprequest::HttpRequest;
|
use httprequest::HttpRequest;
|
||||||
|
use httpresponse::HttpResponse;
|
||||||
|
|
||||||
|
|
||||||
/// Application
|
/// Application
|
||||||
|
@ -47,21 +48,34 @@ impl<S> Application<S> where S: 'static
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Application<()> {
|
|
||||||
|
|
||||||
/// Create default `Application` with no state
|
impl Application<()> {
|
||||||
fn default() -> Self {
|
|
||||||
Application {
|
/// Create default `ApplicationBuilder` with no state
|
||||||
state: (),
|
pub fn default() -> ApplicationBuilder<()> {
|
||||||
default: Resource::default(),
|
ApplicationBuilder {
|
||||||
handlers: HashMap::new(),
|
parts: Some(ApplicationBuilderParts {
|
||||||
resources: HashMap::new(),
|
state: (),
|
||||||
|
default: Resource::default(),
|
||||||
|
handlers: HashMap::new(),
|
||||||
|
resources: HashMap::new()})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S> Application<S> where S: 'static {
|
impl<S> Application<S> where S: 'static {
|
||||||
|
|
||||||
|
/// Create application builder
|
||||||
|
pub fn builder(state: S) -> ApplicationBuilder<S> {
|
||||||
|
ApplicationBuilder {
|
||||||
|
parts: Some(ApplicationBuilderParts {
|
||||||
|
state: state,
|
||||||
|
default: Resource::default(),
|
||||||
|
handlers: HashMap::new(),
|
||||||
|
resources: HashMap::new()})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Create http application with specific state. State is shared with all
|
/// Create http application with specific state. State is shared with all
|
||||||
/// routes within same application and could be
|
/// routes within same application and could be
|
||||||
/// accessed with `HttpContext::state()` method.
|
/// accessed with `HttpContext::state()` method.
|
||||||
|
@ -75,7 +89,7 @@ impl<S> Application<S> where S: 'static {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add resource by path.
|
/// Add resource by path.
|
||||||
pub fn add<P: ToString>(&mut self, path: P) -> &mut Resource<S>
|
pub fn resource<P: ToString>(&mut self, path: P) -> &mut Resource<S>
|
||||||
{
|
{
|
||||||
let path = path.to_string();
|
let path = path.to_string();
|
||||||
|
|
||||||
|
@ -87,8 +101,31 @@ impl<S> Application<S> where S: 'static {
|
||||||
self.resources.get_mut(&path).unwrap()
|
self.resources.get_mut(&path).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This method register handler for specified path.
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// extern crate actix_web;
|
||||||
|
/// use actix_web::*;
|
||||||
|
///
|
||||||
|
/// fn main() {
|
||||||
|
/// let mut app = Application::new(());
|
||||||
|
///
|
||||||
|
/// app.handler("/test", |req, payload, state| {
|
||||||
|
/// httpcodes::HTTPOk
|
||||||
|
/// });
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub fn handler<P, F, R>(&mut self, path: P, handler: F) -> &mut Self
|
||||||
|
where F: Fn(HttpRequest, Payload, &S) -> R + 'static,
|
||||||
|
R: Into<HttpResponse> + 'static,
|
||||||
|
P: ToString,
|
||||||
|
{
|
||||||
|
self.handlers.insert(path.to_string(), Box::new(FnHandler::new(handler)));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Add path handler
|
/// Add path handler
|
||||||
pub fn add_handler<H, P>(&mut self, path: P, h: H)
|
pub fn route_handler<H, P>(&mut self, path: P, h: H)
|
||||||
where H: RouteHandler<S> + 'static, P: ToString
|
where H: RouteHandler<S> + 'static, P: ToString
|
||||||
{
|
{
|
||||||
let path = path.to_string();
|
let path = path.to_string();
|
||||||
|
@ -107,6 +144,141 @@ impl<S> Application<S> where S: 'static {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ApplicationBuilderParts<S> {
|
||||||
|
state: S,
|
||||||
|
default: Resource<S>,
|
||||||
|
handlers: HashMap<String, Box<RouteHandler<S>>>,
|
||||||
|
resources: HashMap<String, Resource<S>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> From<ApplicationBuilderParts<S>> for Application<S> {
|
||||||
|
fn from(b: ApplicationBuilderParts<S>) -> Self {
|
||||||
|
Application {
|
||||||
|
state: b.state,
|
||||||
|
default: b.default,
|
||||||
|
handlers: b.handlers,
|
||||||
|
resources: b.resources,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Application builder
|
||||||
|
pub struct ApplicationBuilder<S=()> {
|
||||||
|
parts: Option<ApplicationBuilderParts<S>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> ApplicationBuilder<S> where S: 'static {
|
||||||
|
|
||||||
|
/// Configure resource for specific path.
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// extern crate actix;
|
||||||
|
/// extern crate actix_web;
|
||||||
|
/// use actix_web::*;
|
||||||
|
/// use actix::prelude::*;
|
||||||
|
///
|
||||||
|
/// struct MyRoute;
|
||||||
|
///
|
||||||
|
/// impl Actor for MyRoute {
|
||||||
|
/// type Context = HttpContext<Self>;
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// impl Route for MyRoute {
|
||||||
|
/// type State = ();
|
||||||
|
///
|
||||||
|
/// fn request(req: HttpRequest,
|
||||||
|
/// payload: Payload,
|
||||||
|
/// ctx: &mut HttpContext<Self>) -> Reply<Self> {
|
||||||
|
/// Reply::reply(httpcodes::HTTPOk)
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// fn main() {
|
||||||
|
/// let app = Application::default()
|
||||||
|
/// .resource("/test", |r| {
|
||||||
|
/// r.get::<MyRoute>();
|
||||||
|
/// r.handler(Method::HEAD, |req, payload, state| {
|
||||||
|
/// httpcodes::HTTPMethodNotAllowed
|
||||||
|
/// });
|
||||||
|
/// })
|
||||||
|
/// .finish();
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub fn resource<F, P: ToString>(&mut self, path: P, f: F) -> &mut Self
|
||||||
|
where F: FnOnce(&mut Resource<S>) + 'static
|
||||||
|
{
|
||||||
|
{
|
||||||
|
let parts = self.parts.as_mut().expect("Use after finish");
|
||||||
|
|
||||||
|
// add resource
|
||||||
|
let path = path.to_string();
|
||||||
|
if !parts.resources.contains_key(&path) {
|
||||||
|
parts.resources.insert(path.clone(), Resource::default());
|
||||||
|
}
|
||||||
|
f(parts.resources.get_mut(&path).unwrap());
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Default resource is used if no matches route could be found.
|
||||||
|
pub fn default_resource<F>(&mut self, f: F) -> &mut Self
|
||||||
|
where F: FnOnce(&mut Resource<S>) + 'static
|
||||||
|
{
|
||||||
|
{
|
||||||
|
let parts = self.parts.as_mut().expect("Use after finish");
|
||||||
|
f(&mut parts.default);
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This method register handler for specified path.
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// extern crate actix_web;
|
||||||
|
/// use actix_web::*;
|
||||||
|
///
|
||||||
|
/// fn main() {
|
||||||
|
/// let app = Application::default()
|
||||||
|
/// .handler("/test", |req, payload, state| {
|
||||||
|
/// match *req.method() {
|
||||||
|
/// Method::GET => httpcodes::HTTPOk,
|
||||||
|
/// Method::POST => httpcodes::HTTPMethodNotAllowed,
|
||||||
|
/// _ => httpcodes::HTTPNotFound,
|
||||||
|
/// }
|
||||||
|
/// })
|
||||||
|
/// .finish();
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub fn handler<P, F, R>(&mut self, path: P, handler: F) -> &mut Self
|
||||||
|
where F: Fn(HttpRequest, Payload, &S) -> R + 'static,
|
||||||
|
R: Into<HttpResponse> + 'static,
|
||||||
|
P: ToString,
|
||||||
|
{
|
||||||
|
self.parts.as_mut().expect("Use after finish")
|
||||||
|
.handlers.insert(path.to_string(), Box::new(FnHandler::new(handler)));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add path handler
|
||||||
|
pub fn route_handler<H, P>(&mut self, path: P, h: H) -> &mut Self
|
||||||
|
where H: RouteHandler<S> + 'static, P: ToString
|
||||||
|
{
|
||||||
|
{
|
||||||
|
// add resource
|
||||||
|
let parts = self.parts.as_mut().expect("Use after finish");
|
||||||
|
let path = path.to_string();
|
||||||
|
if parts.handlers.contains_key(&path) {
|
||||||
|
panic!("Handler already registered: {:?}", path);
|
||||||
|
}
|
||||||
|
parts.handlers.insert(path, Box::new(h));
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct application
|
||||||
|
pub fn finish(&mut self) -> Application<S> {
|
||||||
|
self.parts.take().expect("Use after finish").into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate)
|
pub(crate)
|
||||||
struct InnerApplication<S> {
|
struct InnerApplication<S> {
|
||||||
|
|
|
@ -10,9 +10,9 @@
|
||||||
pub use ws;
|
pub use ws;
|
||||||
pub use httpcodes;
|
pub use httpcodes;
|
||||||
pub use error::ParseError;
|
pub use error::ParseError;
|
||||||
pub use application::Application;
|
pub use application::{Application, ApplicationBuilder};
|
||||||
pub use httprequest::HttpRequest;
|
pub use httprequest::HttpRequest;
|
||||||
pub use httpresponse::{Body, Builder, HttpResponse};
|
pub use httpresponse::{Body, HttpResponse, HttpResponseBuilder};
|
||||||
pub use payload::{Payload, PayloadItem, PayloadError};
|
pub use payload::{Payload, PayloadItem, PayloadError};
|
||||||
pub use router::RoutingMap;
|
pub use router::RoutingMap;
|
||||||
pub use resource::{Reply, Resource};
|
pub use resource::{Reply, Resource};
|
||||||
|
@ -22,6 +22,7 @@ pub use context::HttpContext;
|
||||||
pub use staticfiles::StaticFiles;
|
pub use staticfiles::StaticFiles;
|
||||||
|
|
||||||
// re-exports
|
// re-exports
|
||||||
|
pub use http::{Method, StatusCode};
|
||||||
pub use cookie::{Cookie, CookieBuilder};
|
pub use cookie::{Cookie, CookieBuilder};
|
||||||
pub use cookie::{ParseError as CookieParseError};
|
pub use cookie::{ParseError as CookieParseError};
|
||||||
pub use route_recognizer::Params;
|
pub use route_recognizer::Params;
|
||||||
|
|
|
@ -7,7 +7,7 @@ use task::Task;
|
||||||
use route::RouteHandler;
|
use route::RouteHandler;
|
||||||
use payload::Payload;
|
use payload::Payload;
|
||||||
use httprequest::HttpRequest;
|
use httprequest::HttpRequest;
|
||||||
use httpresponse::{Body, Builder, HttpResponse};
|
use httpresponse::{Body, HttpResponse, HttpResponseBuilder};
|
||||||
|
|
||||||
pub const HTTPOk: StaticResponse = StaticResponse(StatusCode::OK);
|
pub const HTTPOk: StaticResponse = StaticResponse(StatusCode::OK);
|
||||||
pub const HTTPCreated: StaticResponse = StaticResponse(StatusCode::CREATED);
|
pub const HTTPCreated: StaticResponse = StaticResponse(StatusCode::CREATED);
|
||||||
|
@ -23,7 +23,7 @@ pub const HTTPInternalServerError: StaticResponse =
|
||||||
pub struct StaticResponse(StatusCode);
|
pub struct StaticResponse(StatusCode);
|
||||||
|
|
||||||
impl StaticResponse {
|
impl StaticResponse {
|
||||||
pub fn builder(&self) -> Builder {
|
pub fn builder(&self) -> HttpResponseBuilder {
|
||||||
HttpResponse::builder(self.0)
|
HttpResponse::builder(self.0)
|
||||||
}
|
}
|
||||||
pub fn response(&self) -> HttpResponse {
|
pub fn response(&self) -> HttpResponse {
|
||||||
|
|
|
@ -62,8 +62,8 @@ pub struct HttpResponse {
|
||||||
impl HttpResponse {
|
impl HttpResponse {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn builder(status: StatusCode) -> Builder {
|
pub fn builder(status: StatusCode) -> HttpResponseBuilder {
|
||||||
Builder {
|
HttpResponseBuilder {
|
||||||
parts: Some(Parts::new(status)),
|
parts: Some(Parts::new(status)),
|
||||||
err: None,
|
err: None,
|
||||||
}
|
}
|
||||||
|
@ -224,12 +224,12 @@ impl Parts {
|
||||||
/// This type can be used to construct an instance of `HttpResponse` through a
|
/// This type can be used to construct an instance of `HttpResponse` through a
|
||||||
/// builder-like pattern.
|
/// builder-like pattern.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Builder {
|
pub struct HttpResponseBuilder {
|
||||||
parts: Option<Parts>,
|
parts: Option<Parts>,
|
||||||
err: Option<Error>,
|
err: Option<Error>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Builder {
|
impl HttpResponseBuilder {
|
||||||
/// Get the HTTP version of this response.
|
/// Get the HTTP version of this response.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn version(&mut self, version: Version) -> &mut Self {
|
pub fn version(&mut self, version: Version) -> &mut Self {
|
||||||
|
|
|
@ -48,11 +48,11 @@ pub mod ws;
|
||||||
pub mod dev;
|
pub mod dev;
|
||||||
pub mod httpcodes;
|
pub mod httpcodes;
|
||||||
pub use error::ParseError;
|
pub use error::ParseError;
|
||||||
pub use application::Application;
|
pub use application::{Application, ApplicationBuilder};
|
||||||
pub use httprequest::HttpRequest;
|
pub use httprequest::HttpRequest;
|
||||||
pub use httpresponse::{Body, Builder, HttpResponse};
|
pub use httpresponse::{Body, HttpResponse, HttpResponseBuilder};
|
||||||
pub use payload::{Payload, PayloadItem, PayloadError};
|
pub use payload::{Payload, PayloadItem, PayloadError};
|
||||||
pub use router::RoutingMap;
|
pub use router::{Router, RoutingMap};
|
||||||
pub use resource::{Reply, Resource};
|
pub use resource::{Reply, Resource};
|
||||||
pub use route::{Route, RouteFactory, RouteHandler};
|
pub use route::{Route, RouteFactory, RouteHandler};
|
||||||
pub use server::HttpServer;
|
pub use server::HttpServer;
|
||||||
|
@ -60,6 +60,7 @@ pub use context::HttpContext;
|
||||||
pub use staticfiles::StaticFiles;
|
pub use staticfiles::StaticFiles;
|
||||||
|
|
||||||
// re-exports
|
// re-exports
|
||||||
|
pub use http::{Method, StatusCode};
|
||||||
pub use cookie::{Cookie, CookieBuilder};
|
pub use cookie::{Cookie, CookieBuilder};
|
||||||
pub use cookie::{ParseError as CookieParseError};
|
pub use cookie::{ParseError as CookieParseError};
|
||||||
pub use route_recognizer::Params;
|
pub use route_recognizer::Params;
|
||||||
|
|
36
src/main.rs
36
src/main.rs
|
@ -105,24 +105,24 @@ fn main() {
|
||||||
|
|
||||||
let sys = actix::System::new("http-example");
|
let sys = actix::System::new("http-example");
|
||||||
|
|
||||||
let mut routes = RoutingMap::default();
|
HttpServer::new(
|
||||||
|
RoutingMap::default()
|
||||||
let mut app = Application::default();
|
.app(
|
||||||
app.add("/test")
|
"/blah", Application::default()
|
||||||
.get::<MyRoute>()
|
.resource("/test", |r| {
|
||||||
.post::<MyRoute>();
|
r.get::<MyRoute>();
|
||||||
|
r.post::<MyRoute>();
|
||||||
routes.add("/blah", app);
|
})
|
||||||
|
.finish())
|
||||||
routes.add_resource("/test")
|
.resource("/test", |r| r.post::<MyRoute>())
|
||||||
.post::<MyRoute>();
|
.resource("/ws/", |r| r.get::<MyWS>())
|
||||||
|
.resource("/simple/", |r|
|
||||||
routes.add_resource("/ws/")
|
r.handler(Method::GET, |req, payload, state| {
|
||||||
.get::<MyWS>();
|
httpcodes::HTTPOk
|
||||||
|
}))
|
||||||
let http = HttpServer::new(routes);
|
.finish())
|
||||||
http.serve::<()>(
|
.serve::<()>(
|
||||||
&net::SocketAddr::from_str("127.0.0.1:9080").unwrap()).unwrap();
|
&net::SocketAddr::from_str("127.0.0.1:9080").unwrap()).unwrap();
|
||||||
|
|
||||||
println!("starting");
|
println!("starting");
|
||||||
let _ = sys.run();
|
let _ = sys.run();
|
||||||
|
|
|
@ -5,9 +5,10 @@ use std::collections::HashMap;
|
||||||
|
|
||||||
use actix::Actor;
|
use actix::Actor;
|
||||||
use http::Method;
|
use http::Method;
|
||||||
|
use futures::Stream;
|
||||||
|
|
||||||
use task::Task;
|
use task::Task;
|
||||||
use route::{Route, RouteHandler};
|
use route::{Route, RouteHandler, Frame, FnHandler, StreamHandler};
|
||||||
use payload::Payload;
|
use payload::Payload;
|
||||||
use context::HttpContext;
|
use context::HttpContext;
|
||||||
use httprequest::HttpRequest;
|
use httprequest::HttpRequest;
|
||||||
|
@ -26,10 +27,9 @@ use httpcodes::HTTPMethodNotAllowed;
|
||||||
/// struct MyRoute;
|
/// struct MyRoute;
|
||||||
///
|
///
|
||||||
/// fn main() {
|
/// fn main() {
|
||||||
/// let mut routes = RoutingMap::default();
|
/// let router = RoutingMap::default()
|
||||||
///
|
/// .resource("/", |r| r.post::<MyRoute>())
|
||||||
/// routes.add_resource("/")
|
/// .finish();
|
||||||
/// .post::<MyRoute>();
|
|
||||||
/// }
|
/// }
|
||||||
pub struct Resource<S=()> {
|
pub struct Resource<S=()> {
|
||||||
state: PhantomData<S>,
|
state: PhantomData<S>,
|
||||||
|
@ -50,48 +50,62 @@ impl<S> Default for Resource<S> {
|
||||||
impl<S> Resource<S> where S: 'static {
|
impl<S> Resource<S> where S: 'static {
|
||||||
|
|
||||||
/// Register handler for specified method.
|
/// Register handler for specified method.
|
||||||
pub fn handler<H>(&mut self, method: Method, handler: H) -> &mut Self
|
pub fn handler<F, R>(&mut self, method: Method, handler: F)
|
||||||
|
where F: Fn(HttpRequest, Payload, &S) -> R + 'static,
|
||||||
|
R: Into<HttpResponse> + 'static,
|
||||||
|
{
|
||||||
|
self.routes.insert(method, Box::new(FnHandler::new(handler)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Register async handler for specified method.
|
||||||
|
pub fn async<F, R>(&mut self, method: Method, handler: F)
|
||||||
|
where F: Fn(HttpRequest, Payload, &S) -> R + 'static,
|
||||||
|
R: Stream<Item=Frame, Error=()> + 'static,
|
||||||
|
{
|
||||||
|
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>
|
where H: RouteHandler<S>
|
||||||
{
|
{
|
||||||
self.routes.insert(method, Box::new(handler));
|
self.routes.insert(method, Box::new(handler));
|
||||||
self
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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) -> &mut Self
|
pub fn default_handler<H>(&mut self, handler: H)
|
||||||
where H: RouteHandler<S>
|
where H: RouteHandler<S>
|
||||||
{
|
{
|
||||||
self.default = Box::new(handler);
|
self.default = Box::new(handler);
|
||||||
self
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handler for `GET` method.
|
/// Handler for `GET` method.
|
||||||
pub fn get<A>(&mut self) -> &mut Self
|
pub fn get<A>(&mut self)
|
||||||
where A: Actor<Context=HttpContext<A>> + Route<State=S>
|
where A: Actor<Context=HttpContext<A>> + Route<State=S>
|
||||||
{
|
{
|
||||||
self.handler(Method::GET, A::factory())
|
self.route_handler(Method::GET, A::factory());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handler for `POST` method.
|
/// Handler for `POST` method.
|
||||||
pub fn post<A>(&mut self) -> &mut Self
|
pub fn post<A>(&mut self)
|
||||||
where A: Actor<Context=HttpContext<A>> + Route<State=S>
|
where A: Actor<Context=HttpContext<A>> + Route<State=S>
|
||||||
{
|
{
|
||||||
self.handler(Method::POST, A::factory())
|
self.route_handler(Method::POST, A::factory());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handler for `PUR` method.
|
/// Handler for `PUR` method.
|
||||||
pub fn put<A>(&mut self) -> &mut Self
|
pub fn put<A>(&mut self)
|
||||||
where A: Actor<Context=HttpContext<A>> + Route<State=S>
|
where A: Actor<Context=HttpContext<A>> + Route<State=S>
|
||||||
{
|
{
|
||||||
self.handler(Method::PUT, A::factory())
|
self.route_handler(Method::PUT, A::factory());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handler for `METHOD` method.
|
/// Handler for `METHOD` method.
|
||||||
pub fn delete<A>(&mut self) -> &mut Self
|
pub fn delete<A>(&mut self)
|
||||||
where A: Actor<Context=HttpContext<A>> + Route<State=S>
|
where A: Actor<Context=HttpContext<A>> + Route<State=S>
|
||||||
{
|
{
|
||||||
self.handler(Method::DELETE, A::factory())
|
self.route_handler(Method::DELETE, A::factory());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
69
src/route.rs
69
src/route.rs
|
@ -1,8 +1,10 @@
|
||||||
|
use std::io;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use actix::Actor;
|
use actix::Actor;
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
|
use futures::Stream;
|
||||||
|
|
||||||
use task::Task;
|
use task::Task;
|
||||||
use context::HttpContext;
|
use context::HttpContext;
|
||||||
|
@ -59,3 +61,70 @@ impl<A, S> RouteHandler<S> for RouteFactory<A, S>
|
||||||
A::request(req, payload, &mut ctx).into(ctx)
|
A::request(req, payload, &mut ctx).into(ctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Simple route handler
|
||||||
|
pub(crate)
|
||||||
|
struct FnHandler<S, R, F>
|
||||||
|
where F: Fn(HttpRequest, Payload, &S) -> R + 'static,
|
||||||
|
R: Into<HttpResponse>,
|
||||||
|
S: 'static,
|
||||||
|
{
|
||||||
|
f: Box<F>,
|
||||||
|
s: PhantomData<S>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S, R, F> FnHandler<S, R, F>
|
||||||
|
where F: Fn(HttpRequest, Payload, &S) -> R + 'static,
|
||||||
|
R: Into<HttpResponse> + '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>
|
||||||
|
where F: Fn(HttpRequest, Payload, &S) -> R + 'static,
|
||||||
|
R: Into<HttpResponse> + 'static,
|
||||||
|
S: 'static,
|
||||||
|
{
|
||||||
|
fn handle(&self, req: HttpRequest, payload: Payload, state: Rc<S>) -> Task
|
||||||
|
{
|
||||||
|
Task::reply((self.f)(req, payload, &state).into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Async route handler
|
||||||
|
pub(crate)
|
||||||
|
struct StreamHandler<S, R, F>
|
||||||
|
where F: Fn(HttpRequest, Payload, &S) -> R + 'static,
|
||||||
|
R: Stream<Item=Frame, Error=()> + 'static,
|
||||||
|
S: 'static,
|
||||||
|
{
|
||||||
|
f: Box<F>,
|
||||||
|
s: PhantomData<S>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S, R, F> StreamHandler<S, R, F>
|
||||||
|
where F: Fn(HttpRequest, Payload, &S) -> R + 'static,
|
||||||
|
R: Stream<Item=Frame, 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, Payload, &S) -> R + 'static,
|
||||||
|
R: Stream<Item=Frame, Error=()> + 'static,
|
||||||
|
S: 'static,
|
||||||
|
{
|
||||||
|
fn handle(&self, req: HttpRequest, payload: Payload, state: Rc<S>) -> Task
|
||||||
|
{
|
||||||
|
Task::with_stream(
|
||||||
|
(self.f)(req, payload, &state).map_err(
|
||||||
|
|_| io::Error::new(io::ErrorKind::Other, ""))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
139
src/router.rs
139
src/router.rs
|
@ -15,7 +15,30 @@ pub(crate) trait Handler: 'static {
|
||||||
fn handle(&self, req: HttpRequest, payload: Payload) -> Task;
|
fn handle(&self, req: HttpRequest, payload: Payload) -> Task;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Request routing map
|
/// Server routing map
|
||||||
|
pub struct Router {
|
||||||
|
apps: HashMap<String, Box<Handler>>,
|
||||||
|
resources: Recognizer<Resource>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Router {
|
||||||
|
|
||||||
|
pub(crate) fn call(&self, req: HttpRequest, payload: Payload) -> Task
|
||||||
|
{
|
||||||
|
if let Ok(h) = self.resources.recognize(req.path()) {
|
||||||
|
h.handler.handle(req.with_params(h.params), payload, Rc::new(()))
|
||||||
|
} else {
|
||||||
|
for (prefix, app) in &self.apps {
|
||||||
|
if req.path().starts_with(prefix) {
|
||||||
|
return app.handle(req, payload)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Task::reply(HTTPNotFound.response())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Request routing map builder
|
||||||
///
|
///
|
||||||
/// Route supports glob patterns: * for a single wildcard segment and :param
|
/// Route supports glob patterns: * for a single wildcard segment and :param
|
||||||
/// for matching storing that segment of the request url in the Params object,
|
/// for matching storing that segment of the request url in the Params object,
|
||||||
|
@ -25,11 +48,15 @@ pub(crate) trait Handler: 'static {
|
||||||
/// store userid and friend in the exposed Params object:
|
/// store userid and friend in the exposed Params object:
|
||||||
///
|
///
|
||||||
/// ```rust,ignore
|
/// ```rust,ignore
|
||||||
/// let mut router = RoutingMap::default();
|
/// let mut map = RoutingMap::default();
|
||||||
///
|
///
|
||||||
/// router.add_resource("/users/:userid/:friendid").get::<MyRoute>();
|
/// map.resource("/users/:userid/:friendid", |r| r.get::<MyRoute>());
|
||||||
/// ```
|
/// ```
|
||||||
pub struct RoutingMap {
|
pub struct RoutingMap {
|
||||||
|
parts: Option<RoutingMapParts>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct RoutingMapParts {
|
||||||
apps: HashMap<String, Box<Handler>>,
|
apps: HashMap<String, Box<Handler>>,
|
||||||
resources: HashMap<String, Resource>,
|
resources: HashMap<String, Resource>,
|
||||||
}
|
}
|
||||||
|
@ -37,8 +64,9 @@ pub struct RoutingMap {
|
||||||
impl Default for RoutingMap {
|
impl Default for RoutingMap {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
RoutingMap {
|
RoutingMap {
|
||||||
apps: HashMap::new(),
|
parts: Some(RoutingMapParts {
|
||||||
resources: HashMap::new()
|
apps: HashMap::new(),
|
||||||
|
resources: HashMap::new()}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,92 +81,81 @@ impl RoutingMap {
|
||||||
/// struct MyRoute;
|
/// struct MyRoute;
|
||||||
///
|
///
|
||||||
/// fn main() {
|
/// fn main() {
|
||||||
/// let mut app = Application::default();
|
/// let mut router =
|
||||||
/// app.add("/test")
|
/// RoutingMap::default()
|
||||||
/// .get::<MyRoute>()
|
/// .app("/pre", Application::default()
|
||||||
/// .post::<MyRoute>();
|
/// .resource("/test", |r| {
|
||||||
///
|
/// r.get::<MyRoute>();
|
||||||
/// let mut routes = RoutingMap::default();
|
/// r.post::<MyRoute>();
|
||||||
/// routes.add("/pre", app);
|
/// })
|
||||||
|
/// .finish()
|
||||||
|
/// ).finish();
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
/// In this example, `MyRoute` route is available as `http://.../pre/test` url.
|
/// In this example, `MyRoute` route is available as `http://.../pre/test` url.
|
||||||
pub fn add<P, S: 'static>(&mut self, prefix: P, app: Application<S>)
|
pub fn app<P, S: 'static>(&mut self, prefix: P, app: Application<S>) -> &mut Self
|
||||||
where P: ToString
|
where P: ToString
|
||||||
{
|
{
|
||||||
let prefix = prefix.to_string();
|
{
|
||||||
|
let parts = self.parts.as_mut().expect("Use after finish");
|
||||||
|
|
||||||
// we can not override registered resource
|
// we can not override registered resource
|
||||||
if self.apps.contains_key(&prefix) {
|
let prefix = prefix.to_string();
|
||||||
panic!("Resource is registered: {}", prefix);
|
if parts.apps.contains_key(&prefix) {
|
||||||
|
panic!("Resource is registered: {}", prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add application
|
||||||
|
parts.apps.insert(prefix.clone(), app.prepare(prefix));
|
||||||
}
|
}
|
||||||
|
self
|
||||||
// add application
|
|
||||||
self.apps.insert(prefix.clone(), app.prepare(prefix));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This method creates `Resource` for specified path
|
/// Configure resource for specific path.
|
||||||
/// or returns mutable reference to resource object.
|
|
||||||
///
|
///
|
||||||
/// ```rust,ignore
|
/// ```rust,ignore
|
||||||
///
|
///
|
||||||
/// struct MyRoute;
|
/// struct MyRoute;
|
||||||
///
|
///
|
||||||
/// fn main() {
|
/// fn main() {
|
||||||
/// let mut routes = RoutingMap::default();
|
/// RoutingMap::default().resource("/test", |r| {
|
||||||
///
|
/// r.post::<MyRoute>();
|
||||||
/// routes.add_resource("/test")
|
/// }).finish();
|
||||||
/// .post::<MyRoute>();
|
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
/// In this example, `MyRoute` route is available as `http://.../test` url.
|
/// In this example, `MyRoute` route is available as `http://.../test` url.
|
||||||
pub fn add_resource<P>(&mut self, path: P) -> &mut Resource
|
pub fn resource<P, F>(&mut self, path: P, f: F) -> &mut Self
|
||||||
where P: ToString
|
where F: FnOnce(&mut Resource<()>) + 'static,
|
||||||
|
P: ToString,
|
||||||
{
|
{
|
||||||
let path = path.to_string();
|
{
|
||||||
|
let parts = self.parts.as_mut().expect("Use after finish");
|
||||||
|
|
||||||
// add resource
|
// add resource
|
||||||
if !self.resources.contains_key(&path) {
|
let path = path.to_string();
|
||||||
self.resources.insert(path.clone(), Resource::default());
|
if !parts.resources.contains_key(&path) {
|
||||||
|
parts.resources.insert(path.clone(), Resource::default());
|
||||||
|
}
|
||||||
|
// configure resource
|
||||||
|
f(parts.resources.get_mut(&path).unwrap());
|
||||||
}
|
}
|
||||||
|
self
|
||||||
self.resources.get_mut(&path).unwrap()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn into_router(self) -> Router {
|
/// Finish configuration and create `Router` instance
|
||||||
|
pub fn finish(&mut self) -> Router
|
||||||
|
{
|
||||||
|
let parts = self.parts.take().expect("Use after finish");
|
||||||
|
|
||||||
let mut router = Recognizer::new();
|
let mut router = Recognizer::new();
|
||||||
|
|
||||||
for (path, resource) in self.resources {
|
for (path, resource) in parts.resources {
|
||||||
router.add(path.as_str(), resource);
|
router.add(path.as_str(), resource);
|
||||||
}
|
}
|
||||||
|
|
||||||
Router {
|
Router {
|
||||||
apps: self.apps,
|
apps: parts.apps,
|
||||||
resources: router,
|
resources: router,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub(crate)
|
|
||||||
struct Router {
|
|
||||||
apps: HashMap<String, Box<Handler>>,
|
|
||||||
resources: Recognizer<Resource>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Router {
|
|
||||||
|
|
||||||
pub fn call(&self, req: HttpRequest, payload: Payload) -> Task
|
|
||||||
{
|
|
||||||
if let Ok(h) = self.resources.recognize(req.path()) {
|
|
||||||
h.handler.handle(req.with_params(h.params), payload, Rc::new(()))
|
|
||||||
} else {
|
|
||||||
for (prefix, app) in &self.apps {
|
|
||||||
if req.path().starts_with(prefix) {
|
|
||||||
return app.handle(req, payload)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Task::reply(HTTPNotFound.response())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -9,8 +9,8 @@ use tokio_core::reactor::Timeout;
|
||||||
use tokio_core::net::{TcpListener, TcpStream};
|
use tokio_core::net::{TcpListener, TcpStream};
|
||||||
|
|
||||||
use task::{Task, RequestInfo};
|
use task::{Task, RequestInfo};
|
||||||
|
use router::Router;
|
||||||
use reader::{Reader, ReaderError};
|
use reader::{Reader, ReaderError};
|
||||||
use router::{Router, RoutingMap};
|
|
||||||
|
|
||||||
/// An HTTP Server
|
/// An HTTP Server
|
||||||
pub struct HttpServer {
|
pub struct HttpServer {
|
||||||
|
@ -23,8 +23,8 @@ impl Actor for HttpServer {
|
||||||
|
|
||||||
impl HttpServer {
|
impl HttpServer {
|
||||||
/// Create new http server with specified `RoutingMap`
|
/// Create new http server with specified `RoutingMap`
|
||||||
pub fn new(routes: RoutingMap) -> Self {
|
pub fn new(router: Router) -> Self {
|
||||||
HttpServer {router: Rc::new(routes.into_router())}
|
HttpServer {router: Rc::new(router)}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Start listening for incomming connections.
|
/// Start listening for incomming connections.
|
||||||
|
|
Loading…
Reference in a new issue