1
0
mirror of https://github.com/actix/examples synced 2025-06-26 17:17:42 +02:00

restructure folders

This commit is contained in:
Rob Ede
2022-02-18 02:01:48 +00:00
parent 4d8573c3fe
commit cc3d356209
201 changed files with 52 additions and 49 deletions

View File

@ -0,0 +1,9 @@
[package]
name = "middleware-ext-mut"
version = "1.0.0"
edition = "2021"
[dependencies]
actix-web = "4.0.0-rc.1"
log = "0.4"
env_logger = "0.9"

View File

@ -0,0 +1,18 @@
# middleware examples
This example showcases a middleware that adds and retrieves request-local data. See also the [Middleware guide](https://actix.rs/docs/middleware/).
## Usage
```bash
cd basics/middleware-ext-mut
cargo run
# Started http server: 127.0.0.1:8080
```
Look in `src/add_msg.rs` to see how it works.
## Routes
- [GET /on](http://localhost:8080/on) - `200 OK` with "hello from middleware" body and console log showing the request passed through the middleware
- [GET /off](http://localhost:8080/off) - `500 Internal Server Error` with "no message found" body and console log showing the request passed through the middleware

View File

@ -0,0 +1,77 @@
use std::{
future::{ready, Ready},
task::{Context, Poll},
};
use actix_web::Error;
use actix_web::{
dev::{Service, ServiceRequest, ServiceResponse, Transform},
HttpMessage,
};
#[derive(Debug, Clone)]
pub struct Msg(pub String);
#[doc(hidden)]
pub struct AddMsgService<S> {
service: S,
enabled: bool,
}
impl<S, B> Service<ServiceRequest> for AddMsgService<S>
where
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = actix_web::Error>,
{
type Response = ServiceResponse<B>;
type Error = Error;
type Future = S::Future;
fn poll_ready(&self, ctx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.service.poll_ready(ctx)
}
fn call(&self, req: ServiceRequest) -> Self::Future {
log::info!("request is passing through the AddMsg middleware");
if self.enabled {
// insert data into extensions if enabled
req.extensions_mut()
.insert(Msg("Hello from Middleware!".to_owned()));
}
self.service.call(req)
}
}
#[derive(Clone, Debug)]
pub struct AddMsg {
enabled: bool,
}
impl AddMsg {
pub fn enabled() -> Self {
Self { enabled: true }
}
pub fn disabled() -> Self {
Self { enabled: false }
}
}
impl<S, B> Transform<S, ServiceRequest> for AddMsg
where
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = actix_web::Error>,
{
type Response = ServiceResponse<B>;
type Error = Error;
type Future = Ready<Result<Self::Transform, Self::InitError>>;
type Transform = AddMsgService<S>;
type InitError = ();
fn new_transform(&self, service: S) -> Self::Future {
ready(Ok(AddMsgService {
service,
enabled: self.enabled,
}))
}
}

View File

@ -0,0 +1,36 @@
use std::{env, io};
use actix_web::{
middleware,
web::{self, ReqData},
App, HttpResponse, HttpServer,
};
mod add_msg;
use crate::add_msg::{AddMsg, Msg};
// wrap route in our middleware factory
async fn index(msg: Option<ReqData<Msg>>) -> HttpResponse {
if let Some(msg_data) = msg {
let Msg(message) = msg_data.into_inner();
HttpResponse::Ok().body(message)
} else {
HttpResponse::InternalServerError().body("No message found.")
}
}
#[actix_web::main]
async fn main() -> io::Result<()> {
env::set_var("RUST_LOG", "info");
env_logger::init();
HttpServer::new(|| {
App::new()
.wrap(middleware::Logger::default())
.service(web::resource("/on").wrap(AddMsg::enabled()).to(index))
.service(web::resource("/off").wrap(AddMsg::disabled()).to(index))
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}

View File

@ -0,0 +1,10 @@
[package]
name = "middleware-http-to-https"
version = "1.0.0"
edition = "2021"
[dependencies]
actix-web = { version = "4.0.0-beta.21", features = ["rustls"] }
rustls = "0.20.2"
rustls-pemfile = "0.2.1"
futures = "0.3"

View File

@ -0,0 +1,12 @@
## Middleware eg - redirect any http connection to use https connection
This example is the next step after implementing this example : [Setup TLS via rustls](https://github.com/actix/examples/tree/master/security/rustls).
You might have already implemented TLS(using one of the ways mentioned in the example of security section), and have setup your server to listen to port 443(for https).
Now, the only problem left to solve is, to listen to **http** connections as well and redirect them to use **https**
## Usage
**Note :** You will be required to use sudo while running the binary to access port 80 and 443

View File

@ -0,0 +1 @@
cert.pem

View File

@ -0,0 +1 @@
key.pem

View File

@ -0,0 +1,72 @@
use std::fs::File;
use std::io::BufReader;
use actix_web::dev::Service;
use futures::future::FutureExt;
use actix_web::{get, App, HttpServer};
use actix_web::{http, HttpResponse};
use futures::future;
use futures::future::Either;
use rustls::{Certificate, PrivateKey, ServerConfig};
use rustls_pemfile::{certs, pkcs8_private_keys};
#[get("/")]
async fn index() -> String {
String::from(
"<html><head><title>FOO BAR</title></head><body><h1>FOO BAR</h1></body></html>",
)
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let cert_file = &mut BufReader::new(File::open("cert.pem").unwrap());
let key_file = &mut BufReader::new(File::open("key.pem").unwrap());
let cert_chain: Vec<Certificate> = certs(cert_file)
.unwrap()
.into_iter()
.map(Certificate)
.collect();
let mut keys: Vec<PrivateKey> = pkcs8_private_keys(key_file)
.unwrap()
.into_iter()
.map(PrivateKey)
.collect();
let config = ServerConfig::builder()
.with_safe_defaults()
.with_no_client_auth()
.with_single_cert(cert_chain, keys.remove(0))
.unwrap();
HttpServer::new(|| {
App::new()
.wrap_fn(|sreq, srv| {
let host = sreq.connection_info().host().to_owned();
let uri = sreq.uri().to_owned();
let url = format!("https://{}{}", host, uri);
// If the scheme is "https" then it will let other services below this wrap_fn
// handle the request and if it's "http" then a response with redirect status code
// will be sent whose "location" header will be same as before, with just "http"
// changed to "https"
//
if sreq.connection_info().scheme() == "https" {
Either::Left(srv.call(sreq).map(|res| res))
} else {
println!("An http request has arrived here, i will redirect it to use https");
return Either::Right(future::ready(Ok(sreq.into_response(
HttpResponse::MovedPermanently()
.append_header((http::header::LOCATION, url))
.finish(),
))));
}
})
.service(index)
})
.bind("0.0.0.0:80")? // Port 80 to listen for http request
.bind_rustls("0.0.0.0:443", config)? // Port 443 to listen for https request
.run()
.await
}

View File

@ -0,0 +1,10 @@
[package]
name = "middleware-example"
version = "1.0.0"
edition = "2021"
[dependencies]
actix-web = "4.0.0-rc.1"
env_logger = "0.9"
futures = "0.3.7"
pin-project = "1"

View File

@ -0,0 +1,32 @@
# middleware examples
This example showcases a bunch of different uses of middlewares. See also the [Middleware guide](https://actix.rs/docs/middleware/).
## Usage
```bash
cd basics/middleware
cargo run
# Started http server: 127.0.0.1:8080
```
Look in `src/main.rs` and comment the different middlewares in/out to see how they function.
## Middlewares
### redirect::CheckLogin
A middleware implementing a request guard which sketches a rough approximation of what a login could look like.
### read_request_body::Logging
A middleware demonstrating how to read out the incoming request body.
### read_response_body::Logging
A middleware demonstrating how to read out the outgoing response body.
### simple::SayHi
A minimal middleware demonstrating the sequence of operations in an actix middleware.
There is a second version of the same middleware using `wrap_fn` which shows how easily a middleware can be implemented in actix.

View File

@ -0,0 +1,42 @@
use actix_web::{dev::Service, web, App, HttpServer};
use futures::FutureExt as _;
#[allow(dead_code)]
mod read_request_body;
#[allow(dead_code)]
mod read_response_body;
#[allow(dead_code)]
mod redirect;
#[allow(dead_code)]
mod simple;
#[actix_web::main]
async fn main() -> std::io::Result<()> {
std::env::set_var("RUST_LOG", "actix_web=debug");
env_logger::init();
HttpServer::new(|| {
App::new()
.wrap(redirect::CheckLogin)
.wrap(read_request_body::Logging)
.wrap(read_response_body::Logging)
.wrap(simple::SayHi)
.wrap_fn(|req, srv| {
println!("Hi from start. You requested: {}", req.path());
srv.call(req).map(|res| {
println!("Hi from response");
res
})
})
.service(web::resource("/login").to(|| async {
"You are on /login. Go to src/redirect.rs to change this behavior."
}))
.service(web::resource("/").to(|| async {
"Hello, middleware! Check the console where the server is run."
}))
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}

View File

@ -0,0 +1,67 @@
use std::{
future::{ready, Ready},
rc::Rc,
};
use actix_web::dev::{self, Service, Transform};
use actix_web::web::BytesMut;
use actix_web::{dev::ServiceRequest, dev::ServiceResponse, Error, HttpMessage};
use futures::future::LocalBoxFuture;
use futures::stream::StreamExt;
pub struct Logging;
impl<S: 'static, B> Transform<S, ServiceRequest> for Logging
where
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
S::Future: 'static,
B: 'static,
{
type Response = ServiceResponse<B>;
type Error = Error;
type InitError = ();
type Transform = LoggingMiddleware<S>;
type Future = Ready<Result<Self::Transform, Self::InitError>>;
fn new_transform(&self, service: S) -> Self::Future {
ready(Ok(LoggingMiddleware {
service: Rc::new(service),
}))
}
}
pub struct LoggingMiddleware<S> {
// This is special: We need this to avoid lifetime issues.
service: Rc<S>,
}
impl<S, B> Service<ServiceRequest> for LoggingMiddleware<S>
where
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
S::Future: 'static,
B: 'static,
{
type Response = ServiceResponse<B>;
type Error = Error;
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
dev::forward_ready!(service);
fn call(&self, mut req: ServiceRequest) -> Self::Future {
let svc = self.service.clone();
Box::pin(async move {
let mut body = BytesMut::new();
let mut stream = req.take_payload();
while let Some(chunk) = stream.next().await {
body.extend_from_slice(&chunk?);
}
println!("request body: {:?}", body);
let res = svc.call(req).await?;
println!("response: {:?}", res.headers());
Ok(res)
})
}
}

View File

@ -0,0 +1,120 @@
use std::future::Future;
use std::future::{ready, Ready};
use std::marker::PhantomData;
use std::pin::Pin;
use std::task::{Context, Poll};
use actix_web::body::{BodySize, MessageBody};
use actix_web::dev::{self, Service, Transform};
use actix_web::web::{Bytes, BytesMut};
use actix_web::{dev::ServiceRequest, dev::ServiceResponse, Error};
pub struct Logging;
impl<S: 'static, B> Transform<S, ServiceRequest> for Logging
where
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
B: MessageBody + 'static,
{
type Response = ServiceResponse<BodyLogger<B>>;
type Error = Error;
type InitError = ();
type Transform = LoggingMiddleware<S>;
type Future = Ready<Result<Self::Transform, Self::InitError>>;
fn new_transform(&self, service: S) -> Self::Future {
ready(Ok(LoggingMiddleware { service }))
}
}
pub struct LoggingMiddleware<S> {
service: S,
}
impl<S, B> Service<ServiceRequest> for LoggingMiddleware<S>
where
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
B: MessageBody,
{
type Response = ServiceResponse<BodyLogger<B>>;
type Error = Error;
type Future = WrapperStream<S, B>;
dev::forward_ready!(service);
fn call(&self, req: ServiceRequest) -> Self::Future {
WrapperStream {
fut: self.service.call(req),
_t: PhantomData,
}
}
}
#[pin_project::pin_project]
pub struct WrapperStream<S, B>
where
B: MessageBody,
S: Service<ServiceRequest>,
{
#[pin]
fut: S::Future,
_t: PhantomData<(B,)>,
}
impl<S, B> Future for WrapperStream<S, B>
where
B: MessageBody,
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
{
type Output = Result<ServiceResponse<BodyLogger<B>>, Error>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let res = futures::ready!(self.project().fut.poll(cx));
Poll::Ready(res.map(|res| {
res.map_body(move |_, body| BodyLogger {
body,
body_accum: BytesMut::new(),
})
}))
}
}
#[pin_project::pin_project(PinnedDrop)]
pub struct BodyLogger<B> {
#[pin]
body: B,
body_accum: BytesMut,
}
#[pin_project::pinned_drop]
impl<B> PinnedDrop for BodyLogger<B> {
fn drop(self: Pin<&mut Self>) {
println!("response body: {:?}", self.body_accum);
}
}
impl<B: MessageBody> MessageBody for BodyLogger<B> {
type Error = B::Error;
fn size(&self) -> BodySize {
self.body.size()
}
fn poll_next(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<Result<Bytes, Self::Error>>> {
let this = self.project();
match this.body.poll_next(cx) {
Poll::Ready(Some(Ok(chunk))) => {
this.body_accum.extend_from_slice(&chunk);
Poll::Ready(Some(Ok(chunk)))
}
Poll::Ready(Some(Err(e))) => Poll::Ready(Some(Err(e))),
Poll::Ready(None) => Poll::Ready(None),
Poll::Pending => Poll::Pending,
}
}
}

View File

@ -0,0 +1,63 @@
use std::future::{ready, Ready};
use actix_web::body::EitherBody;
use actix_web::dev::{self, ServiceRequest, ServiceResponse};
use actix_web::dev::{Service, Transform};
use actix_web::{http, Error, HttpResponse};
use futures::future::LocalBoxFuture;
pub struct CheckLogin;
impl<S, B> Transform<S, ServiceRequest> for CheckLogin
where
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
S::Future: 'static,
{
type Response = ServiceResponse<EitherBody<B>>;
type Error = Error;
type InitError = ();
type Transform = CheckLoginMiddleware<S>;
type Future = Ready<Result<Self::Transform, Self::InitError>>;
fn new_transform(&self, service: S) -> Self::Future {
ready(Ok(CheckLoginMiddleware { service }))
}
}
pub struct CheckLoginMiddleware<S> {
service: S,
}
impl<S, B> Service<ServiceRequest> for CheckLoginMiddleware<S>
where
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
S::Future: 'static,
{
type Response = ServiceResponse<EitherBody<B>>;
type Error = Error;
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
dev::forward_ready!(service);
fn call(&self, req: ServiceRequest) -> Self::Future {
// We only need to hook into the `start` for this middleware.
let is_logged_in = false; // Change this to see the change in outcome in the browser
let (request, payload) = req.into_parts();
let svc_response = self
.service
.call(ServiceRequest::from_parts(request.clone(), payload));
Box::pin(async move {
// Don't forward to /login if we are already on /login
if is_logged_in || request.path() == "/login" {
svc_response.await.map(ServiceResponse::map_into_left_body)
} else {
let response = HttpResponse::Found()
.insert_header((http::header::LOCATION, "/login"))
.finish()
.map_into_right_body();
Ok(ServiceResponse::new(request, response))
}
})
}
}

View File

@ -0,0 +1,63 @@
use std::future::{ready, Ready};
use actix_web::{
dev::{self, Service, ServiceRequest, ServiceResponse, Transform},
Error,
};
use futures::future::LocalBoxFuture;
// There are two steps in middleware processing.
// 1. Middleware initialization, middleware factory gets called with
// next service in chain as parameter.
// 2. Middleware's call method gets called with normal request.
pub struct SayHi;
// Middleware factory is `Transform` trait from actix-service crate
// `S` - type of the next service
// `B` - type of response's body
impl<S, B> Transform<S, ServiceRequest> for SayHi
where
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
S::Future: 'static,
B: 'static,
{
type Response = ServiceResponse<B>;
type Error = Error;
type InitError = ();
type Transform = SayHiMiddleware<S>;
type Future = Ready<Result<Self::Transform, Self::InitError>>;
fn new_transform(&self, service: S) -> Self::Future {
ready(Ok(SayHiMiddleware { service }))
}
}
pub struct SayHiMiddleware<S> {
service: S,
}
impl<S, B> Service<ServiceRequest> for SayHiMiddleware<S>
where
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
S::Future: 'static,
B: 'static,
{
type Response = ServiceResponse<B>;
type Error = Error;
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
dev::forward_ready!(service);
fn call(&self, req: ServiceRequest) -> Self::Future {
println!("Hi from start. You requested: {}", req.path());
let fut = self.service.call(req);
Box::pin(async move {
let res = fut.await?;
println!("Hi from response");
Ok(res)
})
}
}