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

@ -1 +0,0 @@
Dockerfile

View File

@ -1,9 +0,0 @@
[package]
name = "docker_sample"
version = "1.0.0"
edition = "2021"
[dependencies]
actix-web = "4.0.0-beta.21"
env_logger = "0.9"
log = "0.4"

View File

@ -1,38 +0,0 @@
# NB: This is not a production-grade Dockerfile.
#################
## build stage ##
#################
FROM rust:1-slim-bullseye AS builder
WORKDIR /code
# Download crates-io index and fetch dependency code.
# This step avoids needing to spend time on every build downloading the index
# which can take a long time within the docker context. Docker will cache it.
RUN USER=root cargo init
COPY Cargo.toml Cargo.toml
RUN cargo fetch
# copy app files
COPY src src
# compile app
RUN cargo build --release
###############
## run stage ##
###############
FROM debian:bullseye-slim
WORKDIR /app
# copy server binary from build stage
COPY --from=builder /code/target/release/docker_sample docker_sample
# set user to non-root unless root is required for your app
USER 1001
# indicate what port the server is running on
EXPOSE 8080
# run server
CMD [ "/app/docker_sample" ]

View File

@ -1,22 +0,0 @@
# Docker sample
## Build image
```shell
docker build -t docker_sample .
```
## Run built image
```shell
docker run -d -p 8080:8080 docker_sample
# and the server should start instantly
curl http://localhost:8080
```
## Running unit tests
```shell
docker build -t docker_sample:test .
docker run --rm docker_sample:test
```

View File

@ -1,28 +0,0 @@
use actix_web::{get, middleware::Logger, App, HttpResponse, HttpServer, Responder};
#[get("/")]
async fn index() -> impl Responder {
HttpResponse::Ok().body("Hello world!")
}
#[get("/again")]
async fn again() -> impl Responder {
HttpResponse::Ok().body("Hello world again!")
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
log::info!("Starting HTTP server: go to http://localhost:8080");
HttpServer::new(|| {
App::new()
.wrap(Logger::default())
.service(index)
.service(again)
})
.bind(("0.0.0.0", 8080))?
.run()
.await
}

View File

@ -1,9 +0,0 @@
[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

@ -1,18 +0,0 @@
# 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

@ -1,77 +0,0 @@
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

@ -1,36 +0,0 @@
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

@ -1,10 +0,0 @@
[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

@ -1,12 +0,0 @@
## 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

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

View File

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

View File

@ -1,72 +0,0 @@
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

@ -1,10 +0,0 @@
[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

@ -1,32 +0,0 @@
# 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

@ -1,42 +0,0 @@
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

@ -1,67 +0,0 @@
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

@ -1,120 +0,0 @@
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

@ -1,63 +0,0 @@
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

@ -1,63 +0,0 @@
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)
})
}
}

View File

@ -1,11 +0,0 @@
[package]
name = "shutdown-server"
version = "1.0.0"
edition = "2021"
description = "Send a request to the server to shut it down"
[dependencies]
actix-web = "4.0.0-rc.1"
env_logger = "0.9"
futures = "0.3"
tokio = { version = "1.16", features = ["signal"] }

View File

@ -1,27 +0,0 @@
# shutdown-server
Demonstrates how to shutdown the web server in a couple of ways:
1. remotely, via http request
- Created in response to actix/actix-web#1315
1. sending a SIGINT signal to the server (control-c)
- actix-server natively supports SIGINT
## Usage
### Running The Server
```bash
cd basics/shutdown-server
cargo run --bin shutdown-server
# Starting 8 workers
# Starting "actix-web-service-127.0.0.1:8080" service on 127.0.0.1:8080
```
### Available Routes
- [GET /hello](http://localhost:8080/hello)
- Regular hello world route
- [POST /stop](http://localhost:8080/stop)
- Calling this will shutdown the server and exit

View File

@ -1,52 +0,0 @@
use actix_web::{get, middleware, post, web, App, HttpResponse, HttpServer};
use futures::executor;
use std::{sync::mpsc, thread};
#[get("/hello")]
async fn hello() -> &'static str {
"Hello world!"
}
#[post("/stop")]
async fn stop(stopper: web::Data<mpsc::Sender<()>>) -> HttpResponse {
// make request that sends message through the Sender
stopper.send(()).unwrap();
HttpResponse::NoContent().finish()
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
std::env::set_var("RUST_LOG", "actix_server=debug,actix_web=debug");
env_logger::init();
// create a channel
let (tx, rx) = mpsc::channel::<()>();
let bind = ("127.0.0.1", 8080);
// start server as normal but don't .await after .run() yet
let server = HttpServer::new(move || {
// give the server a Sender in .data
App::new()
.app_data(web::Data::new(tx.clone()))
.wrap(middleware::Logger::default())
.service(hello)
.service(stop)
})
.bind(&bind)?
.run();
// clone the Server handle
let srv = server.handle();
thread::spawn(move || {
// wait for shutdown signal
rx.recv().unwrap();
// stop server gracefully
executor::block_on(srv.stop(true))
});
// run server
server.await
}

View File

@ -1,2 +0,0 @@
# Auto detect text files and perform LF normalization
* text=auto

View File

@ -4,6 +4,7 @@ version = "1.0.0"
edition = "2021"
[dependencies]
actix-web = "4.0.0-rc.1"
actix-web = "4.0.0-rc.3"
actix-files = "0.6.0-beta.15"
env_logger = "0.9.0"
log = "0.4"

View File

@ -1,15 +1,14 @@
use actix_files::Files;
use actix_web::{middleware, App, HttpServer};
use actix_web::{middleware::Logger, App, HttpServer};
#[actix_web::main]
async fn main() -> std::io::Result<()> {
std::env::set_var("RUST_LOG", "actix_web=info");
env_logger::init();
env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
log::info!("starting HTTP server at http://localhost:8080");
HttpServer::new(|| {
App::new()
// Enable the logger.
.wrap(middleware::Logger::default())
// We allow the visitor to see an index of the images at `/images`.
.service(Files::new("/images", "static/images/").show_files_listing())
// Serve a tree of static files at the web root and specify the index file.
@ -17,6 +16,8 @@ async fn main() -> std::io::Result<()> {
// resolved in the order they are defined. If this would be placed before the `/images`
// path then the service for the static images would never be reached.
.service(Files::new("/", "./static/root/").index_file("index.html"))
// Enable the logger.
.wrap(Logger::default())
})
.bind(("127.0.0.1", 8080))?
.run()