Compare commits

...

2 Commits

Author SHA1 Message Date
neri 6362f3bd0b chore: fix Dockerfile warning 2024-10-23 13:51:17 +02:00
neri 5d02c4beef chore: update dependencies 2024-10-23 13:46:54 +02:00
7 changed files with 316 additions and 472 deletions

727
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -12,7 +12,7 @@ actix-web = { version = "4.3.0", default-features = false, features = [
"compress-gzip",
"compress-zstd",
] }
sqlx = { version = "0.7.1", default-features = false, features = [
sqlx = { version = "0.8.2", default-features = false, features = [
"runtime-tokio-rustls",
"postgres",
"time",
@ -23,7 +23,7 @@ env_logger = { version = "0.11.3", default-features = false, features = [
log = "0.4.17"
actix-files = "0.6.2"
tokio = { version = "1.25.0", features = ["rt-multi-thread", "macros", "sync"] }
actix-multipart = "0.6.0"
actix-multipart = "0.7.0"
futures-util = "0.3.26"
rand = "0.8.5"
time = "0.3.17"
@ -33,10 +33,8 @@ tree_magic_mini = { version = "3.0.3", features = ["with-gpl-data"] }
tree_magic_db = "*"
mime = "0.3.16"
url = "2.3.1"
actix-governor = "0.5.0"
governor = "0.6.3"
actix-governor = "0.6.0"
lazy_static = "1.4.0"
actix-web-lab = "0.20.2"
[profile.release]
strip = "symbols"

View File

@ -1,4 +1,4 @@
FROM rust:alpine as builder
FROM rust:alpine AS builder
WORKDIR /app
RUN apk add musl-dev

View File

@ -22,7 +22,7 @@ use actix_web::{
web::{self, Data},
App, Error, HttpResponse, HttpServer,
};
use actix_web_lab::middleware::from_fn;
use actix_web::middleware::from_fn;
use env_logger::Env;
use sqlx::postgres::PgPool;
use std::env;
@ -67,7 +67,7 @@ async fn main() -> std::io::Result<()> {
let config = Data::new(config);
let governor_conf = GovernorConfigBuilder::default()
.per_second(config.rate_limit_replenish_seconds)
.seconds_per_request(config.rate_limit_replenish_seconds)
.burst_size(config.rate_limit_burst)
.key_extractor(ForwardedPeerIpKeyExtractor {
proxied: config.proxied,

View File

@ -1,6 +1,10 @@
use crate::{config, mime_relations};
use actix_multipart::{Field, Multipart};
use actix_web::{error, http::header::DispositionParam, Error};
use actix_web::{
error,
http::header::{ContentDisposition, DispositionParam},
Error,
};
use futures_util::{StreamExt, TryStreamExt};
use mime::{Mime, APPLICATION_OCTET_STREAM, TEXT_PLAIN};
use std::{cmp::min, io::ErrorKind, path::Path};
@ -52,7 +56,9 @@ pub(crate) async fn parse_multipart_inner(
let mut size = 0;
while let Ok(Some(mut field)) = payload.try_next().await {
let name = get_field_name(&field)?.to_owned();
let name = get_field_name(&field)
.ok_or(error::ParseError::Incomplete)?
.to_owned();
match name.as_str() {
"keep_for" => {
keep_for_seconds = Some(parse_string(&name, &mut field).await?);
@ -145,11 +151,8 @@ fn check_requirements(
Ok(())
}
fn get_field_name(field: &Field) -> Result<&str, error::Error> {
Ok(field
.content_disposition()
.get_name()
.ok_or(error::ParseError::Incomplete)?)
fn get_field_name(field: &Field) -> Option<&str> {
field.content_disposition()?.get_name()
}
async fn parse_string(
@ -217,15 +220,18 @@ fn validate_max_size(written_bytes: u64, max_size: Option<u64>) -> Result<(), Er
fn get_file_metadata(field: &actix_multipart::Field) -> (Option<Mime>, Option<String>) {
let mime = field.content_type().cloned();
let filename = field
.content_disposition()
let filename = field.content_disposition().and_then(get_filename);
(mime, filename)
}
fn get_filename(content_disposition: &ContentDisposition) -> Option<String> {
content_disposition
.parameters
.iter()
.find_map(|param| match param {
DispositionParam::Filename(filename) => Some(filename.clone()),
_ => None,
});
(mime, filename)
})
}
fn get_content_type(bytes: &[u8]) -> Option<Mime> {

View File

@ -1,12 +1,12 @@
use actix_governor::governor::clock::{Clock, DefaultClock, QuantaInstant};
use actix_governor::governor::NotUntil;
use actix_governor::KeyExtractor;
use actix_governor::PeerIpKeyExtractor;
use actix_governor::SimpleKeyExtractionError;
use actix_web::HttpResponse;
use actix_web::HttpResponseBuilder;
use actix_web::{dev::ServiceRequest, http::header::ContentType};
use governor::clock::{Clock, DefaultClock, QuantaInstant};
use governor::NotUntil;
use std::net::IpAddr;
use std::net::{IpAddr, Ipv6Addr};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ForwardedPeerIpKeyExtractor {
@ -19,18 +19,15 @@ impl KeyExtractor for ForwardedPeerIpKeyExtractor {
fn extract(&self, req: &ServiceRequest) -> Result<Self::Key, Self::KeyExtractionError> {
let forwarded_for = req.headers().get("x-forwarded-for");
let mut ip = if self.proxied && forwarded_for.is_some() {
let ip = if self.proxied && forwarded_for.is_some() {
read_forwareded_for(forwarded_for).map_err(SimpleKeyExtractionError::new)?
} else {
PeerIpKeyExtractor.extract(req)?
};
// only keep the first /56 for ipv6 addresses
// mask 0xffff_ffff_ffff_ff00_0000_0000_0000_0000
if let IpAddr::V6(ipv6) = ip {
let mut octets = ipv6.octets();
octets[7..16].fill(0);
ip = IpAddr::V6(octets.into());
if let IpAddr::V6(mut ipv6) = ip {
// only keep the first /56 for IPv6 addresses
ipv6 &= Ipv6Addr::from_bits(u128::MAX << (u128::BITS - 56));
}
Ok(ip)

View File

@ -4,7 +4,7 @@ use actix_web::{
http::header::{HeaderValue, CONTENT_SECURITY_POLICY},
Error, HttpMessage,
};
use actix_web_lab::middleware::Next;
use actix_web::middleware::Next;
use rand::Rng;
use std::fmt::Display;