From afeecea05fe2ab7a2691a633cebe0f13729e5a43 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Tue, 28 Nov 2017 19:49:17 -0800 Subject: [PATCH] refactor reply handling --- README.md | 4 +- examples/basic.rs | 1 - examples/state.rs | 4 +- examples/websocket.rs | 4 +- guide/src/qs_2.md | 4 +- guide/src/qs_3.md | 2 +- src/application.rs | 14 ++++--- src/context.rs | 9 ++++- src/dev.rs | 6 ++- src/httpcodes.rs | 4 +- src/lib.rs | 6 +-- src/pipeline.rs | 30 ++++++++------ src/prelude.rs | 7 ---- src/resource.rs | 51 ++---------------------- src/route.rs | 84 ++++++++++++++++++++++++++++++--------- src/staticfiles.rs | 24 +++++------ src/task.rs | 52 ++++++++++++++---------- src/ws.rs | 4 +- tests/test_httprequest.rs | 1 + 19 files changed, 167 insertions(+), 144 deletions(-) delete mode 100644 src/prelude.rs diff --git a/README.md b/README.md index b86ef1de..fdc13266 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,7 @@ impl Actor for MyWebSocket { impl Route for MyWebSocket { type State = (); - fn request(mut req: HttpRequest, ctx: &mut HttpContext) -> RouteResult + fn request(mut req: HttpRequest, mut ctx: HttpContext) -> Result { // websocket handshake let resp = ws::handshake(&req)?; @@ -92,7 +92,7 @@ impl Route for MyWebSocket { ctx.start(resp); // convert bytes stream to a stream of `ws::Message` and handle stream ctx.add_stream(ws::WsStream::new(&mut req)); - Reply::async(MyWebSocket) + ctx.reply(MyWebSocket) } } diff --git a/examples/basic.rs b/examples/basic.rs index dd33014b..e01bb637 100644 --- a/examples/basic.rs +++ b/examples/basic.rs @@ -7,7 +7,6 @@ extern crate env_logger; extern crate futures; use actix_web::*; -use actix_web::error::{Error, Result}; use actix_web::middlewares::RequestSession; use futures::stream::{once, Once}; diff --git a/examples/state.rs b/examples/state.rs index 6b33a933..2b7f8a2e 100644 --- a/examples/state.rs +++ b/examples/state.rs @@ -37,12 +37,12 @@ impl Route for MyWebSocket { /// Shared application state type State = AppState; - fn request(mut req: HttpRequest, ctx: &mut HttpContext) -> RouteResult + fn request(mut req: HttpRequest, mut ctx: HttpContext) -> Result { let resp = ws::handshake(&req)?; ctx.start(resp); ctx.add_stream(ws::WsStream::new(&mut req)); - Reply::async(MyWebSocket{counter: 0}) + ctx.reply(MyWebSocket{counter: 0}) } } diff --git a/examples/websocket.rs b/examples/websocket.rs index 59e363b8..15016601 100644 --- a/examples/websocket.rs +++ b/examples/websocket.rs @@ -22,7 +22,7 @@ impl Actor for MyWebSocket { impl Route for MyWebSocket { type State = (); - fn request(mut req: HttpRequest, ctx: &mut HttpContext) -> RouteResult + fn request(mut req: HttpRequest, mut ctx: HttpContext) -> Result { // websocket handshake let resp = ws::handshake(&req)?; @@ -30,7 +30,7 @@ impl Route for MyWebSocket { ctx.start(resp); // convert bytes stream to a stream of `ws::Message` and register it ctx.add_stream(ws::WsStream::new(&mut req)); - Reply::async(MyWebSocket) + ctx.reply(MyWebSocket) } } diff --git a/guide/src/qs_2.md b/guide/src/qs_2.md index b3727b58..1e6b227f 100644 --- a/guide/src/qs_2.md +++ b/guide/src/qs_2.md @@ -31,7 +31,7 @@ and returns a type that can be converted into `HttpResponse`: ```rust,ignore extern crate actix_web; -use actix_web::prelude::*; +use actix_web::*; fn index(req: HttpRequest) -> &'static str { "Hello world!" @@ -62,7 +62,7 @@ Here is full source of main.rs file: ```rust extern crate actix; extern crate actix_web; -use actix_web::prelude::*; +use actix_web::*; fn index(req: HttpRequest) -> &'static str { "Hello world!" diff --git a/guide/src/qs_3.md b/guide/src/qs_3.md index 43f9265e..d7ead5fa 100644 --- a/guide/src/qs_3.md +++ b/guide/src/qs_3.md @@ -40,7 +40,7 @@ extern crate actix; extern crate actix_web; use std::cell::Cell; -use actix_web::prelude::*; +use actix_web::*; // This struct represents state struct AppState { diff --git a/src/application.rs b/src/application.rs index b12019d5..642bdd63 100644 --- a/src/application.rs +++ b/src/application.rs @@ -24,21 +24,22 @@ pub struct Application { impl Application { - fn run(&self, req: HttpRequest) -> Task { + fn run(&self, req: HttpRequest, task: &mut 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) + h.handle(req, task) } else { for (prefix, handler) in &self.handlers { if req.path().starts_with(prefix) { - return handler.handle(req) + handler.handle(req, task); + return } } - self.default.handle(req) + self.default.handle(req, task) } } } @@ -50,7 +51,8 @@ impl HttpHandler for Application { } fn handle(&self, req: HttpRequest) -> Pipeline { - Pipeline::new(req, Rc::clone(&self.middlewares), &|req: HttpRequest| {self.run(req)}) + Pipeline::new(req, Rc::clone(&self.middlewares), + &|req: HttpRequest, task: &mut Task| {self.run(req, task)}) } } @@ -140,7 +142,7 @@ impl ApplicationBuilder where S: 'static { /// impl Route for MyRoute { /// type State = (); /// - /// fn request(req: HttpRequest, ctx: &mut HttpContext) -> RouteResult { + /// fn request(req: HttpRequest, ctx: HttpContext) -> Result { /// Reply::reply(httpcodes::HTTPOk) /// } /// } diff --git a/src/context.rs b/src/context.rs index 6f02dcce..a0bf97fb 100644 --- a/src/context.rs +++ b/src/context.rs @@ -14,8 +14,8 @@ use actix::dev::{AsyncContextApi, ActorAddressCell, ActorItemsCell, ActorWaitCel use task::{IoContext, DrainFut}; use body::Binary; -use error::Error; -use route::{Route, Frame}; +use error::{Error, Result as ActixResult}; +use route::{Route, Frame, Reply}; use httpresponse::HttpResponse; @@ -158,6 +158,11 @@ impl HttpContext where A: Actor + Route { pub fn connected(&self) -> bool { !self.disconnected } + + pub fn reply(mut self, actor: A) -> ActixResult { + self.set_actor(actor); + Reply::async(self) + } } impl HttpContext where A: Actor + Route { diff --git a/src/dev.rs b/src/dev.rs index 296801e6..53cb546e 100644 --- a/src/dev.rs +++ b/src/dev.rs @@ -8,11 +8,13 @@ //! use actix_web::dev::*; //! ``` -pub use super::*; - // dev specific pub use task::Task; pub use pipeline::Pipeline; pub use route::RouteFactory; pub use recognizer::RouteRecognizer; pub use channel::HttpChannel; + +pub use application::ApplicationBuilder; +pub use httpresponse::HttpResponseBuilder; +pub use cookie::CookieBuilder; diff --git a/src/httpcodes.rs b/src/httpcodes.rs index 5ff5cdbe..23547019 100644 --- a/src/httpcodes.rs +++ b/src/httpcodes.rs @@ -69,8 +69,8 @@ impl StaticResponse { } impl RouteHandler for StaticResponse { - fn handle(&self, _: HttpRequest) -> Task { - Task::reply(HttpResponse::new(self.0, Body::Empty)) + fn handle(&self, _: HttpRequest, task: &mut Task) { + task.reply(HttpResponse::new(self.0, Body::Empty)) } } diff --git a/src/lib.rs b/src/lib.rs index 039f4ffd..84fe563a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -71,19 +71,19 @@ mod h2writer; pub mod ws; pub mod dev; -pub mod prelude; pub mod error; pub mod httpcodes; pub mod multipart; pub mod middlewares; +pub use error::{Error, Result}; pub use encoding::ContentEncoding; pub use body::{Body, Binary}; pub use application::Application; pub use httprequest::{HttpRequest, UrlEncoded}; pub use httpresponse::HttpResponse; pub use payload::{Payload, PayloadItem}; -pub use route::{Frame, Route, RouteFactory, RouteHandler, RouteResult}; -pub use resource::{Reply, Resource}; +pub use route::{Frame, Route, RouteFactory, RouteHandler, Reply}; +pub use resource::Resource; pub use recognizer::Params; pub use server::HttpServer; pub use context::HttpContext; diff --git a/src/pipeline.rs b/src/pipeline.rs index 7723cefa..de230aa9 100644 --- a/src/pipeline.rs +++ b/src/pipeline.rs @@ -10,8 +10,8 @@ use h1writer::Writer; use httprequest::HttpRequest; use httpresponse::HttpResponse; -type Handler = Fn(HttpRequest) -> Task; -pub(crate) type PipelineHandler<'a> = &'a Fn(HttpRequest) -> Task; +type Handler = Fn(HttpRequest, &mut Task); +pub(crate) type PipelineHandler<'a> = &'a Fn(HttpRequest, &mut Task); pub struct Pipeline(PipelineState); @@ -29,7 +29,8 @@ impl Pipeline { pub fn new(req: HttpRequest, mw: Rc>>, handler: PipelineHandler) -> Pipeline { if mw.is_empty() { - let task = (handler)(req.clone()); + let mut task = Task::default(); + (handler)(req.clone(), &mut task); Pipeline(PipelineState::Task(Box::new((task, req)))) } else { match Start::init(mw, req, handler) { @@ -39,13 +40,14 @@ impl Pipeline { Pipeline(PipelineState::Starting(res)), Err(err) => Pipeline(PipelineState::Error( - Box::new((Task::reply(err), HttpRequest::default())))) + Box::new((Task::from_error(err), HttpRequest::default())))) } } } pub fn error>(resp: R) -> Self { - Pipeline(PipelineState::Error(Box::new((Task::reply(resp), HttpRequest::default())))) + Pipeline(PipelineState::Error( + Box::new((Task::from_response(resp), HttpRequest::default())))) } pub(crate) fn disconnected(&mut self) { @@ -79,7 +81,7 @@ impl Pipeline { self.0 = PipelineState::Handle(h), Err(err) => self.0 = PipelineState::Error( - Box::new((Task::reply(err), HttpRequest::default()))) + Box::new((Task::from_error(err), HttpRequest::default()))) } } PipelineState::Handle(mut st) => { @@ -193,7 +195,8 @@ impl Start { let len = self.middlewares.len(); loop { if self.idx == len { - let task = (unsafe{&*self.hnd})(self.req.clone()); + let mut task = Task::default(); + (unsafe{&*self.hnd})(self.req.clone(), &mut task); return Ok(StartResult::Ready( Box::new(Handle::new(self.idx-1, self.req.clone(), self.prepare(task), self.middlewares)))) @@ -205,7 +208,7 @@ impl Start { return Ok(StartResult::Ready( Box::new(Handle::new( self.idx, self.req.clone(), - self.prepare(Task::reply(resp)), self.middlewares)))), + self.prepare(Task::from_response(resp)), self.middlewares)))), Started::Future(mut fut) => match fut.poll() { Ok(Async::NotReady) => { @@ -217,7 +220,8 @@ impl Start { return Ok(StartResult::Ready( Box::new(Handle::new( self.idx, self.req.clone(), - self.prepare(Task::reply(resp)), self.middlewares)))) + self.prepare(Task::from_response(resp)), + self.middlewares)))) } self.idx += 1; } @@ -239,10 +243,12 @@ impl Start { if let Some(resp) = resp { return Ok(Async::Ready(Box::new(Handle::new( self.idx-1, self.req.clone(), - self.prepare(Task::reply(resp)), Rc::clone(&self.middlewares))))) + self.prepare(Task::from_response(resp)), + Rc::clone(&self.middlewares))))) } if self.idx == len { - let task = (unsafe{&*self.hnd})(self.req.clone()); + let mut task = Task::default(); + (unsafe{&*self.hnd})(self.req.clone(), &mut task); return Ok(Async::Ready(Box::new(Handle::new( self.idx-1, self.req.clone(), self.prepare(task), Rc::clone(&self.middlewares))))) @@ -255,7 +261,7 @@ impl Start { self.idx += 1; return Ok(Async::Ready(Box::new(Handle::new( self.idx-1, self.req.clone(), - self.prepare(Task::reply(resp)), + self.prepare(Task::from_response(resp)), Rc::clone(&self.middlewares))))) }, Started::Future(fut) => { diff --git a/src/prelude.rs b/src/prelude.rs deleted file mode 100644 index 01c2a90e..00000000 --- a/src/prelude.rs +++ /dev/null @@ -1,7 +0,0 @@ -//! The `actix-web` prelude - -pub use super::*; -pub use error::*; -pub use application::ApplicationBuilder; -pub use httpresponse::HttpResponseBuilder; -pub use cookie::CookieBuilder; diff --git a/src/resource.rs b/src/resource.rs index 5cda2a83..f9549714 100644 --- a/src/resource.rs +++ b/src/resource.rs @@ -7,7 +7,7 @@ use futures::Stream; use task::Task; use error::Error; -use route::{Route, RouteHandler, RouteResult, Frame, FnHandler, StreamHandler}; +use route::{Route, RouteHandler, Frame, FnHandler, StreamHandler}; use context::HttpContext; use httprequest::HttpRequest; use httpresponse::HttpResponse; @@ -124,54 +124,11 @@ impl Resource where S: 'static { impl RouteHandler for Resource { - fn handle(&self, req: HttpRequest) -> Task { + fn handle(&self, req: HttpRequest, task: &mut Task) { if let Some(handler) = self.routes.get(req.method()) { - handler.handle(req) + handler.handle(req, task) } else { - self.default.handle(req) + self.default.handle(req, task) } } } - -#[cfg_attr(feature="cargo-clippy", allow(large_enum_variant))] -enum ReplyItem where A: Actor + Route { - Message(HttpResponse), - Actor(A), -} - -/// Represents response process. -pub struct Reply (ReplyItem); - -impl Reply where A: Actor + Route -{ - /// Create async response - pub fn async(act: A) -> RouteResult { - Ok(Reply(ReplyItem::Actor(act))) - } - - /// Send response - pub fn reply>(response: R) -> RouteResult { - Ok(Reply(ReplyItem::Message(response.into()))) - } - - pub fn into(self, mut ctx: HttpContext) -> Task where A: Actor> - { - match self.0 { - ReplyItem::Message(msg) => { - Task::reply(msg) - }, - ReplyItem::Actor(act) => { - ctx.set_actor(act); - Task::with_context(ctx) - } - } - } -} - -impl From for Reply - where T: Into, A: Actor + Route -{ - fn from(item: T) -> Self { - Reply(ReplyItem::Message(item.into())) - } -} diff --git a/src/route.rs b/src/route.rs index 72318a21..08e64d1a 100644 --- a/src/route.rs +++ b/src/route.rs @@ -6,11 +6,10 @@ use actix::Actor; use http::{header, Version}; use futures::Stream; -use task::{Task, DrainFut}; +use task::{Task, DrainFut, IoContext}; use body::Binary; -use error::{Error, ExpectError}; +use error::{Error, ExpectError, Result}; use context::HttpContext; -use resource::Reply; use httprequest::HttpRequest; use httpresponse::HttpResponse; @@ -32,15 +31,12 @@ impl Frame { #[allow(unused_variables)] pub trait RouteHandler: 'static { /// Handle request - fn handle(&self, req: HttpRequest) -> Task; + fn handle(&self, req: HttpRequest, task: &mut Task); /// Set route prefix fn set_prefix(&mut self, prefix: String) {} } -/// Request handling result. -pub type RouteResult = Result, Error>; - /// Actors with ability to handle http requests. #[allow(unused_variables)] pub trait Route: Actor { @@ -49,7 +45,7 @@ pub trait Route: Actor { 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, ctx: &mut Self::Context) -> Result<()> where Self: Actor> { // handle expect header only for HTTP/1.1 @@ -79,7 +75,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; + fn request(req: HttpRequest, ctx: Self::Context) -> Result; /// This method creates `RouteFactory` for this actor. fn factory() -> RouteFactory { @@ -94,18 +90,18 @@ impl RouteHandler for RouteFactory where A: Actor> + Route, S: 'static { - fn handle(&self, mut req: HttpRequest) -> Task { + fn handle(&self, mut req: HttpRequest, task: &mut Task) { let mut ctx = HttpContext::new(req.clone_state()); // handle EXPECT header if req.headers().contains_key(header::EXPECT) { if let Err(resp) = A::expect(&mut req, &mut ctx) { - return Task::reply(resp) + task.reply(resp) } } - match A::request(req, &mut ctx) { - Ok(reply) => reply.into(ctx), - Err(err) => Task::reply(err), + match A::request(req, ctx) { + Ok(reply) => reply.into(task), + Err(err) => task.reply(err), } } } @@ -136,8 +132,8 @@ impl RouteHandler for FnHandler R: Into + 'static, S: 'static, { - fn handle(&self, req: HttpRequest) -> Task { - Task::reply((self.f)(req).into()) + fn handle(&self, req: HttpRequest, task: &mut Task) { + task.reply((self.f)(req).into()) } } @@ -167,7 +163,59 @@ impl RouteHandler for StreamHandler R: Stream + 'static, S: 'static, { - fn handle(&self, req: HttpRequest) -> Task { - Task::with_stream((self.f)(req)) + fn handle(&self, req: HttpRequest, task: &mut Task) { + task.stream((self.f)(req)) + } +} + +enum ReplyItem { + Message(HttpResponse), + Actor(Box>), + Stream(Box>), +} + +/// Represents response process. +pub struct Reply(ReplyItem); + +impl Reply +{ + /// Create actor response + pub(crate) fn async(ctx: C) -> Result { + Ok(Reply(ReplyItem::Actor(Box::new(ctx)))) + } + + /// Create async response + pub fn stream(stream: S) -> Result + where S: Stream + 'static + { + Ok(Reply(ReplyItem::Stream(Box::new(stream)))) + } + + /// Send response + pub fn reply>(response: R) -> Result { + Ok(Reply(ReplyItem::Message(response.into()))) + } + + pub fn into(self, task: &mut Task) + { + match self.0 { + ReplyItem::Message(msg) => { + task.reply(msg) + }, + ReplyItem::Actor(ctx) => { + task.context(ctx) + } + ReplyItem::Stream(stream) => { + task.stream(stream) + } + } + } +} + +impl From for Reply + where T: Into +{ + fn from(item: T) -> Self { + Reply(ReplyItem::Message(item.into())) } } diff --git a/src/staticfiles.rs b/src/staticfiles.rs index 5d11e759..0e3761f7 100644 --- a/src/staticfiles.rs +++ b/src/staticfiles.rs @@ -136,9 +136,9 @@ impl RouteHandler for StaticFiles { } } - fn handle(&self, req: HttpRequest) -> Task { + fn handle(&self, req: HttpRequest, task: &mut Task) { if !self.accessible { - Task::reply(HTTPNotFound) + task.reply(HTTPNotFound) } else { let mut hidden = false; let filepath = req.path()[self.prefix.len()..] @@ -152,7 +152,7 @@ impl RouteHandler for StaticFiles { // hidden file if hidden { - return Task::reply(HTTPNotFound) + task.reply(HTTPNotFound) } // full filepath @@ -160,19 +160,19 @@ impl RouteHandler for StaticFiles { let filename = match self.directory.join(&filepath[idx..]).canonicalize() { Ok(fname) => fname, Err(err) => return match err.kind() { - io::ErrorKind::NotFound => Task::reply(HTTPNotFound), - io::ErrorKind::PermissionDenied => Task::reply(HTTPForbidden), - _ => Task::error(err), + io::ErrorKind::NotFound => task.reply(HTTPNotFound), + io::ErrorKind::PermissionDenied => task.reply(HTTPForbidden), + _ => task.error(err), } }; if filename.is_dir() { match self.index(&filepath[idx..], &filename) { - Ok(resp) => Task::reply(resp), + Ok(resp) => task.reply(resp), Err(err) => match err.kind() { - io::ErrorKind::NotFound => Task::reply(HTTPNotFound), - io::ErrorKind::PermissionDenied => Task::reply(HTTPForbidden), - _ => Task::error(err), + io::ErrorKind::NotFound => task.reply(HTTPNotFound), + io::ErrorKind::PermissionDenied => task.reply(HTTPForbidden), + _ => task.error(err), } } } else { @@ -185,9 +185,9 @@ impl RouteHandler for StaticFiles { Ok(mut file) => { let mut data = Vec::new(); let _ = file.read_to_end(&mut data); - Task::reply(resp.body(data).unwrap()) + task.reply(resp.body(data).unwrap()) }, - Err(err) => Task::error(err), + Err(err) => task.error(err), } } } diff --git a/src/task.rs b/src/task.rs index f111891a..f3a4b1cc 100644 --- a/src/task.rs +++ b/src/task.rs @@ -114,9 +114,23 @@ pub struct Task { middlewares: Option, } +impl Default for Task { + + fn default() -> Task { + Task { state: TaskRunningState::Running, + iostate: TaskIOState::ReadingMessage, + frames: VecDeque::new(), + drain: Vec::new(), + stream: TaskStream::None, + prepared: None, + disconnected: false, + middlewares: None } + } +} + impl Task { - pub fn reply>(response: R) -> Self { + pub fn from_response>(response: R) -> Task { let mut frames = VecDeque::new(); frames.push_back(Frame::Message(response.into())); frames.push_back(Frame::Payload(None)); @@ -131,32 +145,28 @@ impl Task { middlewares: None } } - pub fn error>(err: E) -> Self { - Task::reply(err.into()) + pub fn from_error>(err: E) -> Task { + Task::from_response(err.into()) } - pub(crate) fn with_context(ctx: C) -> Self { - Task { state: TaskRunningState::Running, - iostate: TaskIOState::ReadingMessage, - frames: VecDeque::new(), - stream: TaskStream::Context(Box::new(ctx)), - drain: Vec::new(), - prepared: None, - disconnected: false, - middlewares: None } + pub fn reply>(&mut self, response: R) { + self.frames.push_back(Frame::Message(response.into())); + self.frames.push_back(Frame::Payload(None)); + self.iostate = TaskIOState::Done; } - pub(crate) fn with_stream(stream: S) -> Self + pub fn error>(&mut self, err: E) { + self.reply(err.into()) + } + + pub(crate) fn context(&mut self, ctx: Box>) { + self.stream = TaskStream::Context(ctx); + } + + pub(crate) fn stream(&mut self, stream: S) where S: Stream + 'static { - Task { state: TaskRunningState::Running, - iostate: TaskIOState::ReadingMessage, - frames: VecDeque::new(), - stream: TaskStream::Stream(Box::new(stream)), - drain: Vec::new(), - prepared: None, - disconnected: false, - middlewares: None } + self.stream = TaskStream::Stream(Box::new(stream)); } pub(crate) fn response(&mut self) -> HttpResponse { diff --git a/src/ws.rs b/src/ws.rs index 6ae76826..201197e4 100644 --- a/src/ws.rs +++ b/src/ws.rs @@ -22,7 +22,7 @@ //! impl Route for WsRoute { //! type State = (); //! -//! fn request(mut req: HttpRequest, ctx: &mut HttpContext) -> RouteResult +//! fn request(mut req: HttpRequest, mut ctx: HttpContext) -> Result //! { //! // WebSocket handshake //! let resp = ws::handshake(&req)?; @@ -31,7 +31,7 @@ //! // Map Payload into WsStream //! ctx.add_stream(ws::WsStream::new(&mut req)); //! // Start ws messages processing -//! Reply::async(WsRoute) +//! ctx.reply(WsRoute) //! } //! } //! diff --git a/tests/test_httprequest.rs b/tests/test_httprequest.rs index 6bd01f74..96318368 100644 --- a/tests/test_httprequest.rs +++ b/tests/test_httprequest.rs @@ -3,6 +3,7 @@ extern crate http; extern crate time; use std::str; +use actix_web::*; use actix_web::dev::*; use http::{header, Method, Version, HeaderMap};