actix_web::dev::HttpServiceFactory for #name {
+ fn register(self, config: &mut actix_web::dev::ServiceConfig
) {
+ #ast
+ actix_web::dev::HttpServiceFactory::register(
+ actix_web::Resource::new(#path)
+ .guard(actix_web::guard::Get())
+ .to(#name), config);
+ }
+ }
+ })
+ .into()
+}
+
+/// #[post("path")] attribute
+#[proc_macro_attribute]
+pub fn post(args: TokenStream, input: TokenStream) -> TokenStream {
+ let args = parse_macro_input!(args as syn::AttributeArgs);
+ if args.is_empty() {
+ panic!("invalid server definition, expected: #[get(\"some path\")]");
+ }
+
+ // path
+ let path = match args[0] {
+ syn::NestedMeta::Literal(syn::Lit::Str(ref fname)) => {
+ let fname = quote!(#fname).to_string();
+ fname.as_str()[1..fname.len() - 1].to_owned()
+ }
+ _ => panic!("resource path"),
+ };
+
+ let ast: syn::ItemFn = syn::parse(input).unwrap();
+ let name = ast.ident.clone();
+
+ (quote! {
+ #[allow(non_camel_case_types)]
+ struct #name;
+
+ impl actix_web::dev::HttpServiceFactory for #name {
+ fn register(self, config: &mut actix_web::dev::ServiceConfig
) {
+ #ast
+ actix_web::dev::HttpServiceFactory::register(
+ actix_web::Resource::new(#path)
+ .guard(actix_web::guard::Post())
+ .to(#name), config);
+ }
+ }
+ })
+ .into()
+}
+
+/// #[put("path")] attribute
+#[proc_macro_attribute]
+pub fn put(args: TokenStream, input: TokenStream) -> TokenStream {
+ let args = parse_macro_input!(args as syn::AttributeArgs);
+ if args.is_empty() {
+ panic!("invalid server definition, expected: #[get(\"some path\")]");
+ }
+
+ // path
+ let path = match args[0] {
+ syn::NestedMeta::Literal(syn::Lit::Str(ref fname)) => {
+ let fname = quote!(#fname).to_string();
+ fname.as_str()[1..fname.len() - 1].to_owned()
+ }
+ _ => panic!("resource path"),
+ };
+
+ let ast: syn::ItemFn = syn::parse(input).unwrap();
+ let name = ast.ident.clone();
+
+ (quote! {
+ #[allow(non_camel_case_types)]
+ struct #name;
+
+ impl actix_web::dev::HttpServiceFactory for #name {
+ fn register(self, config: &mut actix_web::dev::ServiceConfig
) {
+ #ast
+ actix_web::dev::HttpServiceFactory::register(
+ actix_web::Resource::new(#path)
+ .guard(actix_web::guard::Put())
+ .to(#name), config);
+ }
+ }
+ })
+ .into()
+}
diff --git a/actix-web-codegen/tests/test_macro.rs b/actix-web-codegen/tests/test_macro.rs
new file mode 100644
index 00000000..62b5d618
--- /dev/null
+++ b/actix-web-codegen/tests/test_macro.rs
@@ -0,0 +1,17 @@
+use actix_http::HttpService;
+use actix_http_test::TestServer;
+use actix_web::{get, App, HttpResponse, Responder};
+
+#[get("/test")]
+fn test() -> impl Responder {
+ HttpResponse::Ok()
+}
+
+#[test]
+fn test_body() {
+ let mut srv = TestServer::new(|| HttpService::new(App::new().service(test)));
+
+ let request = srv.get().uri(srv.url("/test")).finish().unwrap();
+ let response = srv.send_request(request).unwrap();
+ assert!(response.status().is_success());
+}
diff --git a/build.rs b/build.rs
deleted file mode 100644
index c8457944..00000000
--- a/build.rs
+++ /dev/null
@@ -1,16 +0,0 @@
-extern crate version_check;
-
-fn main() {
- match version_check::is_min_version("1.26.0") {
- Some((true, _)) => println!("cargo:rustc-cfg=actix_impl_trait"),
- _ => (),
- };
- match version_check::is_nightly() {
- Some(true) => {
- println!("cargo:rustc-cfg=actix_nightly");
- println!("cargo:rustc-cfg=actix_impl_trait");
- }
- Some(false) => (),
- None => (),
- };
-}
diff --git a/examples/basic.rs b/examples/basic.rs
new file mode 100644
index 00000000..756f1b79
--- /dev/null
+++ b/examples/basic.rs
@@ -0,0 +1,49 @@
+use futures::IntoFuture;
+
+use actix_web::{
+ get, middleware, web, App, Error, HttpRequest, HttpResponse, HttpServer,
+};
+
+#[get("/resource1/{name}/index.html")]
+fn index(req: HttpRequest, name: web::Path) -> String {
+ println!("REQ: {:?}", req);
+ format!("Hello: {}!\r\n", name)
+}
+
+fn index_async(req: HttpRequest) -> impl IntoFuture- {
+ println!("REQ: {:?}", req);
+ Ok("Hello world!\r\n")
+}
+
+#[get("/")]
+fn no_params() -> &'static str {
+ "Hello world!\r\n"
+}
+
+fn main() -> std::io::Result<()> {
+ std::env::set_var("RUST_LOG", "actix_server=info,actix_web=info");
+ env_logger::init();
+
+ HttpServer::new(|| {
+ App::new()
+ .middleware(middleware::DefaultHeaders::new().header("X-Version", "0.2"))
+ .middleware(middleware::Compress::default())
+ .middleware(middleware::Logger::default())
+ .service(index)
+ .service(no_params)
+ .service(
+ web::resource("/resource2/index.html")
+ .middleware(
+ middleware::DefaultHeaders::new().header("X-Version-R2", "0.3"),
+ )
+ .default_resource(|r| {
+ r.route(web::route().to(|| HttpResponse::MethodNotAllowed()))
+ })
+ .route(web::get().to_async(index_async)),
+ )
+ .service(web::resource("/test1.html").to(|| "Test\r\n"))
+ })
+ .bind("127.0.0.1:8080")?
+ .workers(1)
+ .run()
+}
diff --git a/rustfmt.toml b/rustfmt.toml
index 4fff285e..94bd11d5 100644
--- a/rustfmt.toml
+++ b/rustfmt.toml
@@ -1,5 +1,2 @@
max_width = 89
reorder_imports = true
-#wrap_comments = true
-fn_args_density = "Compressed"
-#use_small_heuristics = false
diff --git a/src/app.rs b/src/app.rs
new file mode 100644
index 00000000..c4f2e33b
--- /dev/null
+++ b/src/app.rs
@@ -0,0 +1,513 @@
+use std::cell::RefCell;
+use std::marker::PhantomData;
+use std::rc::Rc;
+
+use actix_http::body::{Body, MessageBody};
+use actix_server_config::ServerConfig;
+use actix_service::boxed::{self, BoxedNewService};
+use actix_service::{
+ ApplyTransform, IntoNewService, IntoTransform, NewService, Transform,
+};
+use futures::IntoFuture;
+
+use crate::app_service::{AppChain, AppEntry, AppInit, AppRouting, AppRoutingFactory};
+use crate::config::{AppConfig, AppConfigInner};
+use crate::data::{Data, DataFactory};
+use crate::dev::{PayloadStream, ResourceDef};
+use crate::error::Error;
+use crate::resource::Resource;
+use crate::route::Route;
+use crate::service::{
+ HttpServiceFactory, ServiceFactory, ServiceFactoryWrapper, ServiceRequest,
+ ServiceResponse,
+};
+
+type HttpNewService
=
+ BoxedNewService<(), ServiceRequest
, ServiceResponse, Error, ()>;
+
+/// Application builder - structure that follows the builder pattern
+/// for building application instances.
+pub struct App
+where
+ T: NewService>,
+{
+ chain: T,
+ data: Vec>,
+ config: AppConfigInner,
+ _t: PhantomData<(P,)>,
+}
+
+impl App {
+ /// Create application builder. Application can be configured with a builder-like pattern.
+ pub fn new() -> Self {
+ App {
+ chain: AppChain,
+ data: Vec::new(),
+ config: AppConfigInner::default(),
+ _t: PhantomData,
+ }
+ }
+}
+
+impl App
+where
+ P: 'static,
+ T: NewService<
+ Request = ServiceRequest,
+ Response = ServiceRequest
,
+ Error = Error,
+ InitError = (),
+ >,
+{
+ /// Set application data. Applicatin data could be accessed
+ /// by using `Data` extractor where `T` is data type.
+ ///
+ /// **Note**: http server accepts an application factory rather than
+ /// an application instance. Http server constructs an application
+ /// instance for each thread, thus application data must be constructed
+ /// multiple times. If you want to share data between different
+ /// threads, a shared object should be used, e.g. `Arc`. Application
+ /// data does not need to be `Send` or `Sync`.
+ ///
+ /// ```rust
+ /// use std::cell::Cell;
+ /// use actix_web::{web, App};
+ ///
+ /// struct MyData {
+ /// counter: Cell,
+ /// }
+ ///
+ /// fn index(data: web::Data) {
+ /// data.counter.set(data.counter.get() + 1);
+ /// }
+ ///
+ /// fn main() {
+ /// let app = App::new()
+ /// .data(MyData{ counter: Cell::new(0) })
+ /// .service(
+ /// web::resource("/index.html").route(
+ /// web::get().to(index)));
+ /// }
+ /// ```
+ pub fn data(mut self, data: S) -> Self {
+ self.data.push(Box::new(Data::new(data)));
+ self
+ }
+
+ /// Set application data factory. This function is
+ /// similar to `.data()` but it accepts data factory. Data object get
+ /// constructed asynchronously during application initialization.
+ pub fn data_factory(mut self, data: F) -> Self
+ where
+ F: Fn() -> Out + 'static,
+ Out: IntoFuture + 'static,
+ Out::Error: std::fmt::Debug,
+ {
+ self.data.push(Box::new(data));
+ self
+ }
+
+ /// Register a middleware.
+ pub fn middleware(
+ self,
+ mw: F,
+ ) -> AppRouter<
+ T,
+ P,
+ B,
+ impl NewService<
+ Request = ServiceRequest,
+ Response = ServiceResponse,
+ Error = Error,
+ InitError = (),
+ >,
+ >
+ where
+ M: Transform<
+ AppRouting
,
+ Request = ServiceRequest
,
+ Response = ServiceResponse,
+ Error = Error,
+ InitError = (),
+ >,
+ F: IntoTransform>,
+ {
+ let fref = Rc::new(RefCell::new(None));
+ let endpoint = ApplyTransform::new(mw, AppEntry::new(fref.clone()));
+ AppRouter {
+ endpoint,
+ chain: self.chain,
+ data: self.data,
+ services: Vec::new(),
+ default: None,
+ factory_ref: fref,
+ config: self.config,
+ external: Vec::new(),
+ _t: PhantomData,
+ }
+ }
+
+ /// Register a request modifier. It can modify any request parameters
+ /// including payload stream type.
+ pub fn chain(
+ self,
+ chain: C,
+ ) -> App<
+ P1,
+ impl NewService<
+ Request = ServiceRequest,
+ Response = ServiceRequest,
+ Error = Error,
+ InitError = (),
+ >,
+ >
+ where
+ C: NewService<
+ Request = ServiceRequest,
+ Response = ServiceRequest,
+ Error = Error,
+ InitError = (),
+ >,
+ F: IntoNewService,
+ {
+ let chain = self.chain.and_then(chain.into_new_service());
+ App {
+ chain,
+ data: self.data,
+ config: self.config,
+ _t: PhantomData,
+ }
+ }
+
+ /// Configure route for a specific path.
+ ///
+ /// This is a simplified version of the `App::service()` method.
+ /// This method can be used multiple times with same path, in that case
+ /// multiple resources with one route would be registered for same resource path.
+ ///
+ /// ```rust
+ /// use actix_web::{web, App, HttpResponse};
+ ///
+ /// fn index(data: web::Path<(String, String)>) -> &'static str {
+ /// "Welcome!"
+ /// }
+ ///
+ /// fn main() {
+ /// let app = App::new()
+ /// .route("/test1", web::get().to(index))
+ /// .route("/test2", web::post().to(|| HttpResponse::MethodNotAllowed()));
+ /// }
+ /// ```
+ pub fn route(
+ self,
+ path: &str,
+ mut route: Route,
+ ) -> AppRouter> {
+ self.service(
+ Resource::new(path)
+ .add_guards(route.take_guards())
+ .route(route),
+ )
+ }
+
+ /// Register http service.
+ ///
+ /// Http service is any type that implements `HttpServiceFactory` trait.
+ ///
+ /// Actix web provides several services implementations:
+ ///
+ /// * *Resource* is an entry in resource table which corresponds to requested URL.
+ /// * *Scope* is a set of resources with common root path.
+ /// * "StaticFiles" is a service for static files support
+ pub fn service