mirror of
https://github.com/actix/actix-web.git
synced 2024-11-22 09:31: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_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() {
|
||||
let system = System::new("test");
|
||||
|
||||
// create routing map with `MyRoute` route
|
||||
let mut routes = RoutingMap::default();
|
||||
routes
|
||||
.add_resource("/")
|
||||
.post::<MyRoute>();
|
||||
|
||||
// start http server
|
||||
let http = HttpServer::new(routes);
|
||||
http.serve::<()>(
|
||||
&net::SocketAddr::from_str("127.0.0.1:8880").unwrap()).unwrap();
|
||||
HttpServer::new(
|
||||
// create routing map with `MyRoute` route
|
||||
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
|
||||
Arbiter::handle().spawn_fn(|| {
|
||||
|
|
|
@ -5,11 +5,12 @@ use std::collections::HashMap;
|
|||
use route_recognizer::Router;
|
||||
|
||||
use task::Task;
|
||||
use route::RouteHandler;
|
||||
use route::{RouteHandler, FnHandler};
|
||||
use router::Handler;
|
||||
use resource::Resource;
|
||||
use payload::Payload;
|
||||
use httprequest::HttpRequest;
|
||||
use httpresponse::HttpResponse;
|
||||
|
||||
|
||||
/// Application
|
||||
|
@ -47,21 +48,34 @@ impl<S> Application<S> where S: 'static
|
|||
}
|
||||
}
|
||||
|
||||
impl Default for Application<()> {
|
||||
|
||||
/// Create default `Application` with no state
|
||||
fn default() -> Self {
|
||||
Application {
|
||||
state: (),
|
||||
default: Resource::default(),
|
||||
handlers: HashMap::new(),
|
||||
resources: HashMap::new(),
|
||||
impl Application<()> {
|
||||
|
||||
/// Create default `ApplicationBuilder` with no state
|
||||
pub fn default() -> ApplicationBuilder<()> {
|
||||
ApplicationBuilder {
|
||||
parts: Some(ApplicationBuilderParts {
|
||||
state: (),
|
||||
default: Resource::default(),
|
||||
handlers: HashMap::new(),
|
||||
resources: HashMap::new()})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
/// routes within same application and could be
|
||||
/// accessed with `HttpContext::state()` method.
|
||||
|
@ -75,7 +89,7 @@ impl<S> Application<S> where S: 'static {
|
|||
}
|
||||
|
||||
/// 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();
|
||||
|
||||
|
@ -87,8 +101,31 @@ impl<S> Application<S> where S: 'static {
|
|||
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
|
||||
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
|
||||
{
|
||||
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)
|
||||
struct InnerApplication<S> {
|
||||
|
|
|
@ -10,9 +10,9 @@
|
|||
pub use ws;
|
||||
pub use httpcodes;
|
||||
pub use error::ParseError;
|
||||
pub use application::Application;
|
||||
pub use application::{Application, ApplicationBuilder};
|
||||
pub use httprequest::HttpRequest;
|
||||
pub use httpresponse::{Body, Builder, HttpResponse};
|
||||
pub use httpresponse::{Body, HttpResponse, HttpResponseBuilder};
|
||||
pub use payload::{Payload, PayloadItem, PayloadError};
|
||||
pub use router::RoutingMap;
|
||||
pub use resource::{Reply, Resource};
|
||||
|
@ -22,6 +22,7 @@ pub use context::HttpContext;
|
|||
pub use staticfiles::StaticFiles;
|
||||
|
||||
// re-exports
|
||||
pub use http::{Method, StatusCode};
|
||||
pub use cookie::{Cookie, CookieBuilder};
|
||||
pub use cookie::{ParseError as CookieParseError};
|
||||
pub use route_recognizer::Params;
|
||||
|
|
|
@ -7,7 +7,7 @@ use task::Task;
|
|||
use route::RouteHandler;
|
||||
use payload::Payload;
|
||||
use httprequest::HttpRequest;
|
||||
use httpresponse::{Body, Builder, HttpResponse};
|
||||
use httpresponse::{Body, HttpResponse, HttpResponseBuilder};
|
||||
|
||||
pub const HTTPOk: StaticResponse = StaticResponse(StatusCode::OK);
|
||||
pub const HTTPCreated: StaticResponse = StaticResponse(StatusCode::CREATED);
|
||||
|
@ -23,7 +23,7 @@ pub const HTTPInternalServerError: StaticResponse =
|
|||
pub struct StaticResponse(StatusCode);
|
||||
|
||||
impl StaticResponse {
|
||||
pub fn builder(&self) -> Builder {
|
||||
pub fn builder(&self) -> HttpResponseBuilder {
|
||||
HttpResponse::builder(self.0)
|
||||
}
|
||||
pub fn response(&self) -> HttpResponse {
|
||||
|
|
|
@ -62,8 +62,8 @@ pub struct HttpResponse {
|
|||
impl HttpResponse {
|
||||
|
||||
#[inline]
|
||||
pub fn builder(status: StatusCode) -> Builder {
|
||||
Builder {
|
||||
pub fn builder(status: StatusCode) -> HttpResponseBuilder {
|
||||
HttpResponseBuilder {
|
||||
parts: Some(Parts::new(status)),
|
||||
err: None,
|
||||
}
|
||||
|
@ -224,12 +224,12 @@ impl Parts {
|
|||
/// This type can be used to construct an instance of `HttpResponse` through a
|
||||
/// builder-like pattern.
|
||||
#[derive(Debug)]
|
||||
pub struct Builder {
|
||||
pub struct HttpResponseBuilder {
|
||||
parts: Option<Parts>,
|
||||
err: Option<Error>,
|
||||
}
|
||||
|
||||
impl Builder {
|
||||
impl HttpResponseBuilder {
|
||||
/// Get the HTTP version of this response.
|
||||
#[inline]
|
||||
pub fn version(&mut self, version: Version) -> &mut Self {
|
||||
|
|
|
@ -48,11 +48,11 @@ pub mod ws;
|
|||
pub mod dev;
|
||||
pub mod httpcodes;
|
||||
pub use error::ParseError;
|
||||
pub use application::Application;
|
||||
pub use application::{Application, ApplicationBuilder};
|
||||
pub use httprequest::HttpRequest;
|
||||
pub use httpresponse::{Body, Builder, HttpResponse};
|
||||
pub use httpresponse::{Body, HttpResponse, HttpResponseBuilder};
|
||||
pub use payload::{Payload, PayloadItem, PayloadError};
|
||||
pub use router::RoutingMap;
|
||||
pub use router::{Router, RoutingMap};
|
||||
pub use resource::{Reply, Resource};
|
||||
pub use route::{Route, RouteFactory, RouteHandler};
|
||||
pub use server::HttpServer;
|
||||
|
@ -60,6 +60,7 @@ pub use context::HttpContext;
|
|||
pub use staticfiles::StaticFiles;
|
||||
|
||||
// re-exports
|
||||
pub use http::{Method, StatusCode};
|
||||
pub use cookie::{Cookie, CookieBuilder};
|
||||
pub use cookie::{ParseError as CookieParseError};
|
||||
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 mut routes = RoutingMap::default();
|
||||
|
||||
let mut app = Application::default();
|
||||
app.add("/test")
|
||||
.get::<MyRoute>()
|
||||
.post::<MyRoute>();
|
||||
|
||||
routes.add("/blah", app);
|
||||
|
||||
routes.add_resource("/test")
|
||||
.post::<MyRoute>();
|
||||
|
||||
routes.add_resource("/ws/")
|
||||
.get::<MyWS>();
|
||||
|
||||
let http = HttpServer::new(routes);
|
||||
http.serve::<()>(
|
||||
&net::SocketAddr::from_str("127.0.0.1:9080").unwrap()).unwrap();
|
||||
HttpServer::new(
|
||||
RoutingMap::default()
|
||||
.app(
|
||||
"/blah", Application::default()
|
||||
.resource("/test", |r| {
|
||||
r.get::<MyRoute>();
|
||||
r.post::<MyRoute>();
|
||||
})
|
||||
.finish())
|
||||
.resource("/test", |r| r.post::<MyRoute>())
|
||||
.resource("/ws/", |r| r.get::<MyWS>())
|
||||
.resource("/simple/", |r|
|
||||
r.handler(Method::GET, |req, payload, state| {
|
||||
httpcodes::HTTPOk
|
||||
}))
|
||||
.finish())
|
||||
.serve::<()>(
|
||||
&net::SocketAddr::from_str("127.0.0.1:9080").unwrap()).unwrap();
|
||||
|
||||
println!("starting");
|
||||
let _ = sys.run();
|
||||
|
|
|
@ -5,9 +5,10 @@ use std::collections::HashMap;
|
|||
|
||||
use actix::Actor;
|
||||
use http::Method;
|
||||
use futures::Stream;
|
||||
|
||||
use task::Task;
|
||||
use route::{Route, RouteHandler};
|
||||
use route::{Route, RouteHandler, Frame, FnHandler, StreamHandler};
|
||||
use payload::Payload;
|
||||
use context::HttpContext;
|
||||
use httprequest::HttpRequest;
|
||||
|
@ -26,10 +27,9 @@ use httpcodes::HTTPMethodNotAllowed;
|
|||
/// struct MyRoute;
|
||||
///
|
||||
/// fn main() {
|
||||
/// let mut routes = RoutingMap::default();
|
||||
///
|
||||
/// routes.add_resource("/")
|
||||
/// .post::<MyRoute>();
|
||||
/// let router = RoutingMap::default()
|
||||
/// .resource("/", |r| r.post::<MyRoute>())
|
||||
/// .finish();
|
||||
/// }
|
||||
pub struct Resource<S=()> {
|
||||
state: PhantomData<S>,
|
||||
|
@ -50,48 +50,62 @@ impl<S> Default for Resource<S> {
|
|||
impl<S> Resource<S> where S: 'static {
|
||||
|
||||
/// 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>
|
||||
{
|
||||
self.routes.insert(method, Box::new(handler));
|
||||
self
|
||||
}
|
||||
|
||||
/// Default handler is used if no matched route found.
|
||||
/// 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>
|
||||
{
|
||||
self.default = Box::new(handler);
|
||||
self
|
||||
}
|
||||
|
||||
/// 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>
|
||||
{
|
||||
self.handler(Method::GET, A::factory())
|
||||
self.route_handler(Method::GET, A::factory());
|
||||
}
|
||||
|
||||
/// 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>
|
||||
{
|
||||
self.handler(Method::POST, A::factory())
|
||||
self.route_handler(Method::POST, A::factory());
|
||||
}
|
||||
|
||||
/// 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>
|
||||
{
|
||||
self.handler(Method::PUT, A::factory())
|
||||
self.route_handler(Method::PUT, A::factory());
|
||||
}
|
||||
|
||||
/// 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>
|
||||
{
|
||||
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::marker::PhantomData;
|
||||
|
||||
use actix::Actor;
|
||||
use bytes::Bytes;
|
||||
use futures::Stream;
|
||||
|
||||
use task::Task;
|
||||
use context::HttpContext;
|
||||
|
@ -59,3 +61,70 @@ impl<A, S> RouteHandler<S> for RouteFactory<A, S>
|
|||
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;
|
||||
}
|
||||
|
||||
/// 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
|
||||
/// 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:
|
||||
///
|
||||
/// ```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 {
|
||||
parts: Option<RoutingMapParts>,
|
||||
}
|
||||
|
||||
struct RoutingMapParts {
|
||||
apps: HashMap<String, Box<Handler>>,
|
||||
resources: HashMap<String, Resource>,
|
||||
}
|
||||
|
@ -37,8 +64,9 @@ pub struct RoutingMap {
|
|||
impl Default for RoutingMap {
|
||||
fn default() -> Self {
|
||||
RoutingMap {
|
||||
apps: HashMap::new(),
|
||||
resources: HashMap::new()
|
||||
parts: Some(RoutingMapParts {
|
||||
apps: HashMap::new(),
|
||||
resources: HashMap::new()}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -53,92 +81,81 @@ impl RoutingMap {
|
|||
/// struct MyRoute;
|
||||
///
|
||||
/// fn main() {
|
||||
/// let mut app = Application::default();
|
||||
/// app.add("/test")
|
||||
/// .get::<MyRoute>()
|
||||
/// .post::<MyRoute>();
|
||||
///
|
||||
/// let mut routes = RoutingMap::default();
|
||||
/// routes.add("/pre", app);
|
||||
/// let mut router =
|
||||
/// RoutingMap::default()
|
||||
/// .app("/pre", Application::default()
|
||||
/// .resource("/test", |r| {
|
||||
/// r.get::<MyRoute>();
|
||||
/// r.post::<MyRoute>();
|
||||
/// })
|
||||
/// .finish()
|
||||
/// ).finish();
|
||||
/// }
|
||||
/// ```
|
||||
/// 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
|
||||
{
|
||||
let prefix = prefix.to_string();
|
||||
{
|
||||
let parts = self.parts.as_mut().expect("Use after finish");
|
||||
|
||||
// we can not override registered resource
|
||||
if self.apps.contains_key(&prefix) {
|
||||
panic!("Resource is registered: {}", prefix);
|
||||
// we can not override registered resource
|
||||
let prefix = prefix.to_string();
|
||||
if parts.apps.contains_key(&prefix) {
|
||||
panic!("Resource is registered: {}", prefix);
|
||||
}
|
||||
|
||||
// add application
|
||||
parts.apps.insert(prefix.clone(), app.prepare(prefix));
|
||||
}
|
||||
|
||||
// add application
|
||||
self.apps.insert(prefix.clone(), app.prepare(prefix));
|
||||
self
|
||||
}
|
||||
|
||||
/// This method creates `Resource` for specified path
|
||||
/// or returns mutable reference to resource object.
|
||||
/// Configure resource for specific path.
|
||||
///
|
||||
/// ```rust,ignore
|
||||
///
|
||||
/// struct MyRoute;
|
||||
///
|
||||
/// fn main() {
|
||||
/// let mut routes = RoutingMap::default();
|
||||
///
|
||||
/// routes.add_resource("/test")
|
||||
/// .post::<MyRoute>();
|
||||
/// RoutingMap::default().resource("/test", |r| {
|
||||
/// r.post::<MyRoute>();
|
||||
/// }).finish();
|
||||
/// }
|
||||
/// ```
|
||||
/// In this example, `MyRoute` route is available as `http://.../test` url.
|
||||
pub fn add_resource<P>(&mut self, path: P) -> &mut Resource
|
||||
where P: ToString
|
||||
pub fn resource<P, F>(&mut self, path: P, f: F) -> &mut Self
|
||||
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
|
||||
if !self.resources.contains_key(&path) {
|
||||
self.resources.insert(path.clone(), Resource::default());
|
||||
// add resource
|
||||
let path = path.to_string();
|
||||
if !parts.resources.contains_key(&path) {
|
||||
parts.resources.insert(path.clone(), Resource::default());
|
||||
}
|
||||
// configure resource
|
||||
f(parts.resources.get_mut(&path).unwrap());
|
||||
}
|
||||
|
||||
self.resources.get_mut(&path).unwrap()
|
||||
self
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
for (path, resource) in self.resources {
|
||||
for (path, resource) in parts.resources {
|
||||
router.add(path.as_str(), resource);
|
||||
}
|
||||
|
||||
Router {
|
||||
apps: self.apps,
|
||||
apps: parts.apps,
|
||||
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 task::{Task, RequestInfo};
|
||||
use router::Router;
|
||||
use reader::{Reader, ReaderError};
|
||||
use router::{Router, RoutingMap};
|
||||
|
||||
/// An HTTP Server
|
||||
pub struct HttpServer {
|
||||
|
@ -23,8 +23,8 @@ impl Actor for HttpServer {
|
|||
|
||||
impl HttpServer {
|
||||
/// Create new http server with specified `RoutingMap`
|
||||
pub fn new(routes: RoutingMap) -> Self {
|
||||
HttpServer {router: Rc::new(routes.into_router())}
|
||||
pub fn new(router: Router) -> Self {
|
||||
HttpServer {router: Rc::new(router)}
|
||||
}
|
||||
|
||||
/// Start listening for incomming connections.
|
||||
|
|
Loading…
Reference in a new issue