1
0
Fork 0
mirror of https://github.com/actix/actix-web.git synced 2024-06-13 02:39:32 +00:00

move state to request object

This commit is contained in:
Nikolay Kim 2017-11-26 21:18:38 -08:00
parent 8e0a7f44d4
commit 5a3b6638a7
12 changed files with 81 additions and 71 deletions

View file

@ -12,7 +12,7 @@ use actix_web::middlewares::RequestSession;
use futures::stream::{once, Once};
/// somple handle
fn index(mut req: HttpRequest, state: &()) -> Result<HttpResponse> {
fn index(mut req: HttpRequest) -> Result<HttpResponse> {
println!("{:?}", req);
if let Ok(ch) = req.payload_mut().readany() {
if let futures::Async::Ready(Some(d)) = ch {
@ -32,7 +32,7 @@ fn index(mut req: HttpRequest, state: &()) -> Result<HttpResponse> {
}
/// somple handle
fn index_async(req: HttpRequest, state: &()) -> Once<actix_web::Frame, Error>
fn index_async(req: HttpRequest) -> Once<actix_web::Frame, Error>
{
println!("{:?}", req);
@ -44,7 +44,7 @@ fn index_async(req: HttpRequest, state: &()) -> Once<actix_web::Frame, Error>
}
/// handle with path parameters like `/user/{name}/`
fn with_param(req: HttpRequest, state: &()) -> Result<HttpResponse>
fn with_param(req: HttpRequest) -> Result<HttpResponse>
{
println!("{:?}", req);
@ -75,7 +75,7 @@ fn main() {
// async handler
.resource("/async/{name}", |r| r.async(Method::GET, index_async))
// redirect
.resource("/", |r| r.handler(Method::GET, |req, _| {
.resource("/", |r| r.handler(Method::GET, |req| {
println!("{:?}", req);
Ok(httpcodes::HTTPFound

View file

@ -1,3 +1,4 @@
#![cfg_attr(feature="cargo-clippy", allow(needless_pass_by_value))]
//! There are two level of statfulness in actix-web. Application has state
//! that is shared across all handlers within same Application.
//! And individual handler can have state.
@ -15,11 +16,11 @@ struct AppState {
}
/// somple handle
fn index(req: HttpRequest, state: &AppState) -> HttpResponse {
fn index(req: HttpRequest<AppState>) -> HttpResponse {
println!("{:?}", req);
state.counter.set(state.counter.get() + 1);
req.state().counter.set(req.state().counter.get() + 1);
httpcodes::HTTPOk.with_body(
format!("Num of requests: {}", state.counter.get()))
format!("Num of requests: {}", req.state().counter.get()))
}
/// `MyWebSocket` counts how many messages it receives from peer,
@ -36,7 +37,7 @@ impl Route for MyWebSocket {
/// Shared application state
type State = AppState;
fn request(mut req: HttpRequest, ctx: &mut HttpContext<Self>) -> RouteResult<Self>
fn request(mut req: HttpRequest<AppState>, ctx: &mut HttpContext<Self>) -> RouteResult<Self>
{
let resp = ws::handshake(&req)?;
ctx.start(resp);
@ -44,6 +45,7 @@ impl Route for MyWebSocket {
Reply::async(MyWebSocket{counter: 0})
}
}
impl StreamHandler<ws::Message> for MyWebSocket {}
impl Handler<ws::Message> for MyWebSocket {
fn handle(&mut self, msg: ws::Message, ctx: &mut HttpContext<Self>)
@ -64,7 +66,6 @@ impl Handler<ws::Message> for MyWebSocket {
}
}
fn main() {
::std::env::set_var("RUST_LOG", "actix_web=info");
let _ = env_logger::init();

View file

@ -24,19 +24,21 @@ pub struct Application<S> {
impl<S: 'static> Application<S> {
fn run(&self, mut req: HttpRequest) -> Task {
fn run(&self, req: HttpRequest) -> Task {
let mut req = req.with_state(Rc::clone(&self.state));
if let Some((params, h)) = self.router.recognize(req.path()) {
if let Some(params) = params {
req.set_match_info(params);
}
h.handle(req, Rc::clone(&self.state))
h.handle(req)
} else {
for (prefix, handler) in &self.handlers {
if req.path().starts_with(prefix) {
return handler.handle(req, Rc::clone(&self.state))
return handler.handle(req)
}
}
self.default.handle(req, Rc::clone(&self.state))
self.default.handle(req)
}
}
}
@ -146,7 +148,7 @@ impl<S> ApplicationBuilder<S> where S: 'static {
/// let app = Application::default("/")
/// .resource("/test", |r| {
/// r.get::<MyRoute>();
/// r.handler(Method::HEAD, |req, state| {
/// r.handler(Method::HEAD, |req| {
/// Ok(httpcodes::HTTPMethodNotAllowed)
/// });
/// })
@ -190,7 +192,7 @@ impl<S> ApplicationBuilder<S> where S: 'static {
///
/// fn main() {
/// let app = Application::default("/")
/// .handler("/test", |req, state| {
/// .handler("/test", |req| {
/// match *req.method() {
/// Method::GET => httpcodes::HTTPOk,
/// Method::POST => httpcodes::HTTPMethodNotAllowed,
@ -201,7 +203,7 @@ impl<S> ApplicationBuilder<S> where S: 'static {
/// }
/// ```
pub fn handler<P, F, R>(&mut self, path: P, handler: F) -> &mut Self
where F: Fn(HttpRequest, &S) -> R + 'static,
where F: Fn(HttpRequest<S>) -> R + 'static,
R: Into<HttpResponse> + 'static,
P: Into<String>,
{

View file

@ -29,7 +29,6 @@ pub struct HttpContext<A> where A: Actor<Context=HttpContext<A>> + Route,
address: ActorAddressCell<A>,
stream: VecDeque<Frame>,
wait: ActorWaitCell<A>,
app_state: Rc<<A as Route>::State>,
disconnected: bool,
}
@ -102,10 +101,9 @@ impl<A> AsyncContextApi<A> for HttpContext<A> where A: Actor<Context=Self> + Rou
}
}
impl<A> HttpContext<A> where A: Actor<Context=Self> + Route {
impl<A> Default for HttpContext<A> where A: Actor<Context=Self> + Route {
pub fn new(state: Rc<<A as Route>::State>) -> HttpContext<A>
{
fn default() -> HttpContext<A> {
HttpContext {
act: None,
state: ActorState::Started,
@ -114,10 +112,12 @@ impl<A> HttpContext<A> where A: Actor<Context=Self> + Route {
address: ActorAddressCell::default(),
wait: ActorWaitCell::default(),
stream: VecDeque::new(),
app_state: state,
disconnected: false,
}
}
}
impl<A> HttpContext<A> where A: Actor<Context=Self> + Route {
pub(crate) fn set_actor(&mut self, act: A) {
self.act = Some(act)
@ -126,11 +126,6 @@ impl<A> HttpContext<A> where A: Actor<Context=Self> + Route {
impl<A> HttpContext<A> where A: Actor<Context=Self> + Route {
/// Shared application state
pub fn state(&self) -> &<A as Route>::State {
&self.app_state
}
/// Start response processing
pub fn start<R: Into<HttpResponse>>(&mut self, response: R) {
self.stream.push_back(Frame::Message(response.into()))

View file

@ -35,9 +35,14 @@ pub(crate) enum Http1Result {
Switch,
}
#[derive(Debug)]
enum Item {
Http1(HttpRequest),
Http2,
}
pub(crate) struct Http1<T: AsyncWrite + 'static, H: 'static> {
router: Rc<Vec<H>>,
#[allow(dead_code)]
addr: Option<SocketAddr>,
stream: H1Writer<T>,
reader: Reader,
@ -268,12 +273,6 @@ impl<T, H> Http1<T, H>
}
}
#[derive(Debug)]
enum Item {
Http1(HttpRequest),
Http2,
}
struct Reader {
h1: bool,
payload: Option<PayloadInfo>,

View file

@ -1,6 +1,5 @@
//! Basic http responses
#![allow(non_upper_case_globals)]
use std::rc::Rc;
use http::StatusCode;
use body::Body;
@ -70,7 +69,7 @@ impl StaticResponse {
}
impl<S> RouteHandler<S> for StaticResponse {
fn handle(&self, _: HttpRequest, _: Rc<S>) -> Task {
fn handle(&self, _: HttpRequest<S>) -> Task {
Task::reply(HttpResponse::new(self.0, Body::Empty))
}
}

View file

@ -48,9 +48,9 @@ impl Default for HttpMessage {
}
/// An HTTP Request
pub struct HttpRequest(Rc<HttpMessage>);
pub struct HttpRequest<S=()>(Rc<HttpMessage>, Rc<S>);
impl HttpRequest {
impl HttpRequest<()> {
/// Construct a new Request.
#[inline]
pub fn new(method: Method, path: String, version: Version,
@ -69,20 +69,36 @@ impl HttpRequest {
addr: None,
payload: payload,
extensions: Extensions::new(),
})
}),
Rc::new(())
)
}
/// Construct request for error response.
pub(crate) fn for_error() -> HttpRequest {
HttpRequest(Rc::new(HttpMessage::default()))
HttpRequest(Rc::new(HttpMessage::default()), Rc::new(()))
}
/// Construct new http request with state.
pub(crate) fn with_state<S>(self, state: Rc<S>) -> HttpRequest<S> {
HttpRequest(self.0, state)
}
}
impl<S> HttpRequest<S> {
/// get mutable reference for inner message
fn as_mut(&mut self) -> &mut HttpMessage {
let r: &HttpMessage = self.0.as_ref();
#[allow(mutable_transmutes)]
unsafe{mem::transmute(r)}
}
/// Shared application state
pub fn state(&self) -> &S {
&self.1
}
/// Protocol extensions.
#[inline]
pub fn extensions(&mut self) -> &mut Extensions {
@ -317,13 +333,13 @@ impl HttpRequest {
}
}
impl Clone for HttpRequest {
fn clone(&self) -> HttpRequest {
HttpRequest(Rc::clone(&self.0))
impl<S> Clone for HttpRequest<S> {
fn clone(&self) -> HttpRequest<S> {
HttpRequest(Rc::clone(&self.0), Rc::clone(&self.1))
}
}
impl fmt::Debug for HttpRequest {
impl<S> fmt::Debug for HttpRequest<S> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let res = write!(f, "\nHttpRequest {:?} {}:{}\n",
self.0.version, self.0.method, self.0.path);

View file

@ -1,4 +1,3 @@
use std::rc::Rc;
use std::marker::PhantomData;
use std::collections::HashMap;
@ -64,7 +63,7 @@ impl<S> Resource<S> where S: 'static {
/// Register handler for specified method.
pub fn handler<F, R>(&mut self, method: Method, handler: F)
where F: Fn(HttpRequest, &S) -> Result<R> + 'static,
where F: Fn(HttpRequest<S>) -> Result<R> + 'static,
R: Into<HttpResponse> + 'static,
{
self.routes.insert(method, Box::new(FnHandler::new(handler)));
@ -72,7 +71,7 @@ impl<S> Resource<S> where S: 'static {
/// Register async handler for specified method.
pub fn async<F, R>(&mut self, method: Method, handler: F)
where F: Fn(HttpRequest, &S) -> R + 'static,
where F: Fn(HttpRequest<S>) -> R + 'static,
R: Stream<Item=Frame, Error=Error> + 'static,
{
self.routes.insert(method, Box::new(StreamHandler::new(handler)));
@ -125,11 +124,11 @@ impl<S> Resource<S> where S: 'static {
impl<S: 'static> RouteHandler<S> for Resource<S> {
fn handle(&self, req: HttpRequest, state: Rc<S>) -> Task {
fn handle(&self, req: HttpRequest<S>) -> Task {
if let Some(handler) = self.routes.get(req.method()) {
handler.handle(req, state)
handler.handle(req)
} else {
self.default.handle(req, state)
self.default.handle(req)
}
}
}

View file

@ -33,7 +33,7 @@ impl Frame {
#[allow(unused_variables)]
pub trait RouteHandler<S>: 'static {
/// Handle request
fn handle(&self, req: HttpRequest, state: Rc<S>) -> Task;
fn handle(&self, req: HttpRequest<S>) -> Task;
/// Set route prefix
fn set_prefix(&mut self, prefix: String) {}
@ -46,11 +46,11 @@ pub type RouteResult<T> = Result<Reply<T>, Error>;
#[allow(unused_variables)]
pub trait Route: Actor {
/// Shared state. State is shared with all routes within same application
/// and could be accessed with `HttpContext::state()` method.
/// 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, ctx: &mut Self::Context) -> Result<(), Error>
fn expect(req: &mut HttpRequest<Self::State>, ctx: &mut Self::Context) -> Result<(), Error>
where Self: Actor<Context=HttpContext<Self>>
{
// handle expect header only for HTTP/1.1
@ -80,7 +80,7 @@ pub trait Route: Actor {
/// request/response or websocket connection.
/// In that case `HttpContext::start` and `HttpContext::write` has to be used
/// for writing response.
fn request(req: HttpRequest, ctx: &mut Self::Context) -> RouteResult<Self>;
fn request(req: HttpRequest<Self::State>, ctx: &mut Self::Context) -> RouteResult<Self>;
/// This method creates `RouteFactory` for this actor.
fn factory() -> RouteFactory<Self, Self::State> {
@ -95,8 +95,8 @@ impl<A, S> RouteHandler<S> for RouteFactory<A, S>
where A: Actor<Context=HttpContext<A>> + Route<State=S>,
S: 'static
{
fn handle(&self, mut req: HttpRequest, state: Rc<A::State>) -> Task {
let mut ctx = HttpContext::new(state);
fn handle(&self, mut req: HttpRequest<A::State>) -> Task {
let mut ctx = HttpContext::default();
// handle EXPECT header
if req.headers().contains_key(header::EXPECT) {
@ -114,7 +114,7 @@ impl<A, S> RouteHandler<S> for RouteFactory<A, S>
/// 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<HttpResponse>,
S: 'static,
{
@ -123,7 +123,7 @@ struct FnHandler<S, R, F>
}
impl<S, R, F> FnHandler<S, R, F>
where F: Fn(HttpRequest, &S) -> R + 'static,
where F: Fn(HttpRequest<S>) -> R + 'static,
R: Into<HttpResponse> + 'static,
S: 'static,
{
@ -133,19 +133,19 @@ impl<S, R, F> FnHandler<S, R, F>
}
impl<S, R, F> RouteHandler<S> for FnHandler<S, R, F>
where F: Fn(HttpRequest, &S) -> R + 'static,
where F: Fn(HttpRequest<S>) -> R + 'static,
R: Into<HttpResponse> + 'static,
S: 'static,
{
fn handle(&self, req: HttpRequest, state: Rc<S>) -> Task {
Task::reply((self.f)(req, &state).into())
fn handle(&self, req: HttpRequest<S>) -> Task {
Task::reply((self.f)(req).into())
}
}
/// Async route handler
pub(crate)
struct StreamHandler<S, R, F>
where F: Fn(HttpRequest, &S) -> R + 'static,
where F: Fn(HttpRequest<S>) -> R + 'static,
R: Stream<Item=Frame, Error=Error> + 'static,
S: 'static,
{
@ -154,7 +154,7 @@ struct StreamHandler<S, R, F>
}
impl<S, R, F> StreamHandler<S, R, F>
where F: Fn(HttpRequest, &S) -> R + 'static,
where F: Fn(HttpRequest<S>) -> R + 'static,
R: Stream<Item=Frame, Error=Error> + 'static,
S: 'static,
{
@ -164,11 +164,11 @@ impl<S, R, F> StreamHandler<S, R, F>
}
impl<S, R, F> RouteHandler<S> for StreamHandler<S, R, F>
where F: Fn(HttpRequest, &S) -> R + 'static,
where F: Fn(HttpRequest<S>) -> R + 'static,
R: Stream<Item=Frame, Error=Error> + 'static,
S: 'static,
{
fn handle(&self, req: HttpRequest, state: Rc<S>) -> Task {
Task::with_stream((self.f)(req, &state))
fn handle(&self, req: HttpRequest<S>) -> Task {
Task::with_stream((self.f)(req))
}
}

View file

@ -3,7 +3,6 @@
//! TODO: needs to re-implement actual files handling, current impl blocks
use std::io;
use std::io::Read;
use std::rc::Rc;
use std::fmt::Write;
use std::fs::{File, DirEntry};
use std::path::PathBuf;
@ -137,7 +136,7 @@ impl<S: 'static> RouteHandler<S> for StaticFiles {
}
}
fn handle(&self, req: HttpRequest, _: Rc<S>) -> Task {
fn handle(&self, req: HttpRequest<S>) -> Task {
if !self.accessible {
Task::reply(HTTPNotFound)
} else {

View file

@ -108,7 +108,7 @@ impl ResponseType for Message {
// /// `protocols` is a sequence of known protocols. On successful handshake,
// /// the returned response headers contain the first protocol in this list
// /// which the server also knows.
pub fn handshake(req: &HttpRequest) -> Result<HttpResponse, WsHandshakeError> {
pub fn handshake<S>(req: &HttpRequest<S>) -> Result<HttpResponse, WsHandshakeError> {
// WebSocket accepts only GET
if *req.method() != Method::GET {
return Err(WsHandshakeError::GetMethodRequired)
@ -176,7 +176,7 @@ pub struct WsStream {
}
impl WsStream {
pub fn new(req: &mut HttpRequest) -> WsStream {
pub fn new<S>(req: &mut HttpRequest<S>) -> WsStream {
WsStream { rx: req.take_payload(), buf: BytesMut::new(), closed: false, error_sent: false }
}
}

View file

@ -16,7 +16,7 @@ fn create_server<T, A>() -> HttpServer<T, A, Application<()>> {
HttpServer::new(
vec![Application::default("/")
.resource("/", |r|
r.handler(Method::GET, |_, _| {
r.handler(Method::GET, |_| {
Ok(httpcodes::HTTPOk)
}))
.finish()])
@ -96,7 +96,7 @@ fn test_middlewares() {
response: act_num2,
finish: act_num3})
.resource("/", |r|
r.handler(Method::GET, |_, _| {
r.handler(Method::GET, |_| {
Ok(httpcodes::HTTPOk)
}))
.finish()])