Skip to content

Commit

Permalink
Merge pull request #46 from myyrakle/feat/#37
Browse files Browse the repository at this point in the history
[#37] 미들웨어 구현
  • Loading branch information
myyrakle authored Dec 16, 2023
2 parents 5b0af5d + aee9108 commit be90828
Show file tree
Hide file tree
Showing 11 changed files with 210 additions and 31 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# rupring

![](https://img.shields.io/badge/language-Rust-red) ![](https://img.shields.io/badge/version-0.3.0-brightgreen) [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/myyrakle/rupring/blob/master/LICENSE)
![](https://img.shields.io/badge/language-Rust-red) ![](https://img.shields.io/badge/version-0.4.0-brightgreen) [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/myyrakle/rupring/blob/master/LICENSE)

spring on rust

Expand Down
4 changes: 2 additions & 2 deletions rupring/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "rupring"
version = "0.3.0"
version = "0.4.0"
edition = "2021"
license = "MIT"
authors = ["myyrakle <[email protected]>"]
Expand All @@ -14,7 +14,7 @@ homepage = "https://github.com/myyrakle/rupring/blob/master/README.md"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
rupring_macro={ version="0.3.0", path="../rupring_macro" }
rupring_macro={ version="0.4.0", path="../rupring_macro" }
hyper = { version = "1", features = ["full"] }
tokio = { version = "1", features = ["full"] }
http-body-util = "0.1.0"
Expand Down
3 changes: 3 additions & 0 deletions rupring/src/boot/di.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::any::Any;
use std::panic::{RefUnwindSafe, UnwindSafe};
use std::{any::TypeId, collections::HashMap};

pub struct DIContext {
Expand All @@ -8,6 +9,8 @@ pub struct DIContext {

unsafe impl Send for DIContext {}
unsafe impl Sync for DIContext {}
impl UnwindSafe for DIContext {}
impl RefUnwindSafe for DIContext {}

impl std::fmt::Debug for DIContext {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Expand Down
59 changes: 42 additions & 17 deletions rupring/src/boot/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ pub async fn run_server(
);

match route {
Some((route, route_path)) => {
Some((route, route_path, middlewares)) => {
let handler = route.handler();

let raw_querystring = uri.query().unwrap_or("");
Expand Down Expand Up @@ -106,29 +106,54 @@ pub async fn run_server(
}
};

let request = crate::Request {
method: request_method,
path: request_path,
body: request_body,
query_parameters,
headers,
path_parameters,
di_context: Arc::clone(&di_context),
};
let response = std::panic::catch_unwind(move || {
let mut request = crate::Request {
method: request_method,
path: request_path,
body: request_body,
query_parameters,
headers,
path_parameters,
di_context: Arc::clone(&di_context),
};

let mut response = crate::Response::new();

for middleware in middlewares {
let middleware_result = middleware(
request,
response.clone(),
move |request, response| {
let next = Some(Box::new((request, response)));

let mut response = crate::Response::new();
response.next = next;

response
},
);

let response = crate::Response::new();
match middleware_result.next {
Some(next) => {
let (next_request, next_response) = *next;

request = next_request;
response = next_response;
}
None => {
return middleware_result;
}
}
}

let response = std::panic::catch_unwind(move || {
handler.handle(request, response)
});

let response = match response {
Ok(response) => response,
Err(_err) => crate::Response {
status: 500,
headers: HashMap::new(),
body: "Internal Server Error".to_string(),
},
Err(_err) => crate::Response::new()
.status(500)
.text("Internal Server Error".to_string()),
};

let headers = response.headers.clone();
Expand Down
25 changes: 21 additions & 4 deletions rupring/src/boot/route.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,11 @@ pub(crate) fn find_route(
root_module: Box<dyn crate::IModule>,
request_path: String,
request_method: Method,
) -> Option<(Box<dyn crate::IRoute + Send + 'static>, String)> {
) -> Option<(
Box<dyn crate::IRoute + Send + 'static>,
String,
Vec<crate::MiddlewareFunction>,
)> {
for controller in root_module.controllers() {
let prefix = controller.prefix();

Expand All @@ -69,15 +73,28 @@ pub(crate) fn find_route(
continue;
}

return Some((route, route_path));
let middlewares = root_module
.middlewares()
.into_iter()
.chain(controller.middlewares().into_iter())
.collect();
return Some((route, route_path, middlewares));
}
}

for child_module in root_module.child_modules() {
let result = find_route(child_module, request_path.clone(), request_method.clone());

if result.is_some() {
return result;
match result {
Some((route, route_path, middlewares)) => {
let middlewares = root_module
.middlewares()
.into_iter()
.chain(middlewares.into_iter())
.collect();
return Some((route, route_path, middlewares));
}
None => {}
}
}

Expand Down
58 changes: 58 additions & 0 deletions rupring/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,56 @@ pub fn hello(_request: rupring::Request, response: rupring::Response) -> rupring
```
This is especially useful when you need to inherit and use Response through middleware.
# Middleware
rupring provides middleware features for common logic processing.
If you want to log requests for all APIs that exist in a module, you can apply middleware in the form below.
First, define a middleware function.
```
pub fn logger_middleware(
request: rupring::Request,
response: rupring::Response,
next: NextFunction,
) -> rupring::Response {
println!(
"Request: {} {}",
request.method.to_string(),
request.path.to_string()
);
next(request, response)
}
```
The above function only records logs and forwards them to the next middleware or route function.
If you want to return a response immediately without forwarding, just return the response without calling the next function.
And you can register the middleware function just defined in the module or controller unit.
```
#[derive(Debug, Clone, Copy)]
#[rupring::Module(
controllers=[HomeController{}],
modules=[UserModule{}],
providers=[],
middlewares=[logger_middleware]
)]
pub struct RootModule {}
// or Controller
#[derive(Debug, Clone)]
#[rupring::Controller(prefix=/, routes=[get_user], middlewares=[logger_middleware])]
pub struct UserController {}
```
Middleware registered in a module is recursively applied to the routes of controllers registered in that module and to child modules.
On the other hand, middleware registered in a controller applies only to the routes of that controller.
The priorities in which middleware is applied are as follows:
1. Middleware of the same unit is executed in the order defined in the array.
2. If module middleware and controller middleware exist at the same time, module middleware is executed first.
3. If the parent module's middleware and the child module's middleware exist at the same time, the parent module middleware is executed first.
# Dependency Injection
Expand Down Expand Up @@ -296,12 +346,17 @@ pub trait IModule {
fn child_modules(&self) -> Vec<Box<dyn IModule>>;
fn controllers(&self) -> Vec<Box<dyn IController>>;
fn providers(&self) -> Vec<Box<dyn IProvider>>;
fn middlewares(&self) -> Vec<MiddlewareFunction>;
}

pub type MiddlewareFunction =
Box<dyn Fn(Request, Response, NextFunction) -> Response + Send + Sync + UnwindSafe + 'static>;

/// Controller interface
pub trait IController {
fn prefix(&self) -> String;
fn routes(&self) -> Vec<Box<dyn IRoute + Send + 'static>>;
fn middlewares(&self) -> Vec<MiddlewareFunction>;
}

/// Route interface
Expand All @@ -316,6 +371,9 @@ pub trait IHandler: UnwindSafe {
fn handle(&self, request: Request, response: Response) -> Response;
}

/// Next function type for middleware
pub type NextFunction = fn(Request, Response) -> Response;

/// Rupring Factory for creating server
#[derive(Debug, Clone)]
pub struct RupringFactory<T: IModule> {
Expand Down
43 changes: 39 additions & 4 deletions rupring/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ use std::{
sync::{Arc, Mutex},
};

use rupring::NextFunction;

#[derive(Debug, Clone, Default)]
pub struct CounterService {
counter: Arc<Mutex<i32>>,
Expand Down Expand Up @@ -102,14 +104,32 @@ impl rupring::IProvider for HomeRepository {
}
}

pub fn logger_middleware(
request: rupring::Request,
response: rupring::Response,
next: NextFunction,
) -> rupring::Response {
println!(
"Request: {} {}",
request.method.to_string(),
request.path.to_string()
);

next(request, response)
}

#[derive(Debug, Clone, Copy)]
#[rupring::Module(controllers=[HomeController{}], modules=[], providers=[
HomeService::default(), HomeRepository::default(), UserService::default(), CounterService::default()
])]
#[rupring::Module(
controllers=[HomeController{}],
modules=[UserModule{}],
providers=[],
middlewares=[logger_middleware]
)]
pub struct RootModule {}


#[derive(Debug, Clone)]
#[rupring::Controller(prefix=/, routes=[hello, get_user, echo, count])]
#[rupring::Controller(prefix=/, routes=[hello, echo, count])]
pub struct HomeController {}

#[rupring::Get(path = /)]
Expand Down Expand Up @@ -140,6 +160,21 @@ pub fn count(request: rupring::Request, _: rupring::Response) -> rupring::Respon
rupring::Response::new().text(format!("{}", count))
}

#[derive(Debug, Clone, Copy)]
#[rupring::Module(
controllers=[UserController{}],
modules=[],
providers=[
UserService::default()
],
middlewares=[]
)]
pub struct UserModule {}

#[derive(Debug, Clone)]
#[rupring::Controller(prefix=/, routes=[get_user], middlewares=[logger_middleware])]
pub struct UserController {}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let app = rupring::RupringFactory::create(RootModule {});
Expand Down
1 change: 1 addition & 0 deletions rupring/src/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::{collections::HashMap, panic::UnwindSafe, sync::Arc};

use crate::Method;

#[derive(Debug, Clone)]
pub struct Request {
pub method: Method,
pub path: String,
Expand Down
8 changes: 6 additions & 2 deletions rupring/src/response.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::collections::HashMap;
use std::{collections::HashMap, panic::UnwindSafe};

use crate::HeaderName;
use crate::{HeaderName, Request};
use http_body_util::Full;
use hyper::body::Bytes;

Expand All @@ -9,8 +9,11 @@ pub struct Response {
pub status: u16,
pub body: String,
pub headers: HashMap<HeaderName, String>,
pub(crate) next: Option<Box<(Request, Response)>>,
}

impl UnwindSafe for Response {}

impl Response {
/// Create a new response with status code 200, empty body and empty headers.
/// ```
Expand All @@ -22,6 +25,7 @@ impl Response {
status: 200,
body: "".to_string(),
headers: Default::default(),
next: None,
}
}

Expand Down
2 changes: 1 addition & 1 deletion rupring_macro/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "rupring_macro"
version = "0.3.0"
version = "0.4.0"
edition = "2021"
description = "rupring macro"
license = "MIT"
Expand Down
Loading

0 comments on commit be90828

Please sign in to comment.