Compare commits
2 Commits
bb35dd97a2
...
6362f3bd0b
Author | SHA1 | Date |
---|---|---|
neri | 6362f3bd0b | |
neri | 5d02c4beef |
File diff suppressed because it is too large
Load Diff
|
@ -12,7 +12,7 @@ actix-web = { version = "4.3.0", default-features = false, features = [
|
||||||
"compress-gzip",
|
"compress-gzip",
|
||||||
"compress-zstd",
|
"compress-zstd",
|
||||||
] }
|
] }
|
||||||
sqlx = { version = "0.7.1", default-features = false, features = [
|
sqlx = { version = "0.8.2", default-features = false, features = [
|
||||||
"runtime-tokio-rustls",
|
"runtime-tokio-rustls",
|
||||||
"postgres",
|
"postgres",
|
||||||
"time",
|
"time",
|
||||||
|
@ -23,7 +23,7 @@ env_logger = { version = "0.11.3", default-features = false, features = [
|
||||||
log = "0.4.17"
|
log = "0.4.17"
|
||||||
actix-files = "0.6.2"
|
actix-files = "0.6.2"
|
||||||
tokio = { version = "1.25.0", features = ["rt-multi-thread", "macros", "sync"] }
|
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"
|
futures-util = "0.3.26"
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
time = "0.3.17"
|
time = "0.3.17"
|
||||||
|
@ -33,10 +33,8 @@ tree_magic_mini = { version = "3.0.3", features = ["with-gpl-data"] }
|
||||||
tree_magic_db = "*"
|
tree_magic_db = "*"
|
||||||
mime = "0.3.16"
|
mime = "0.3.16"
|
||||||
url = "2.3.1"
|
url = "2.3.1"
|
||||||
actix-governor = "0.5.0"
|
actix-governor = "0.6.0"
|
||||||
governor = "0.6.3"
|
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
actix-web-lab = "0.20.2"
|
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
strip = "symbols"
|
strip = "symbols"
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
FROM rust:alpine as builder
|
FROM rust:alpine AS builder
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
RUN apk add musl-dev
|
RUN apk add musl-dev
|
||||||
|
|
|
@ -22,7 +22,7 @@ use actix_web::{
|
||||||
web::{self, Data},
|
web::{self, Data},
|
||||||
App, Error, HttpResponse, HttpServer,
|
App, Error, HttpResponse, HttpServer,
|
||||||
};
|
};
|
||||||
use actix_web_lab::middleware::from_fn;
|
use actix_web::middleware::from_fn;
|
||||||
use env_logger::Env;
|
use env_logger::Env;
|
||||||
use sqlx::postgres::PgPool;
|
use sqlx::postgres::PgPool;
|
||||||
use std::env;
|
use std::env;
|
||||||
|
@ -67,7 +67,7 @@ async fn main() -> std::io::Result<()> {
|
||||||
let config = Data::new(config);
|
let config = Data::new(config);
|
||||||
|
|
||||||
let governor_conf = GovernorConfigBuilder::default()
|
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)
|
.burst_size(config.rate_limit_burst)
|
||||||
.key_extractor(ForwardedPeerIpKeyExtractor {
|
.key_extractor(ForwardedPeerIpKeyExtractor {
|
||||||
proxied: config.proxied,
|
proxied: config.proxied,
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
use crate::{config, mime_relations};
|
use crate::{config, mime_relations};
|
||||||
use actix_multipart::{Field, Multipart};
|
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 futures_util::{StreamExt, TryStreamExt};
|
||||||
use mime::{Mime, APPLICATION_OCTET_STREAM, TEXT_PLAIN};
|
use mime::{Mime, APPLICATION_OCTET_STREAM, TEXT_PLAIN};
|
||||||
use std::{cmp::min, io::ErrorKind, path::Path};
|
use std::{cmp::min, io::ErrorKind, path::Path};
|
||||||
|
@ -52,7 +56,9 @@ pub(crate) async fn parse_multipart_inner(
|
||||||
let mut size = 0;
|
let mut size = 0;
|
||||||
|
|
||||||
while let Ok(Some(mut field)) = payload.try_next().await {
|
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() {
|
match name.as_str() {
|
||||||
"keep_for" => {
|
"keep_for" => {
|
||||||
keep_for_seconds = Some(parse_string(&name, &mut field).await?);
|
keep_for_seconds = Some(parse_string(&name, &mut field).await?);
|
||||||
|
@ -145,11 +151,8 @@ fn check_requirements(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_field_name(field: &Field) -> Result<&str, error::Error> {
|
fn get_field_name(field: &Field) -> Option<&str> {
|
||||||
Ok(field
|
field.content_disposition()?.get_name()
|
||||||
.content_disposition()
|
|
||||||
.get_name()
|
|
||||||
.ok_or(error::ParseError::Incomplete)?)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn parse_string(
|
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>) {
|
fn get_file_metadata(field: &actix_multipart::Field) -> (Option<Mime>, Option<String>) {
|
||||||
let mime = field.content_type().cloned();
|
let mime = field.content_type().cloned();
|
||||||
let filename = field
|
let filename = field.content_disposition().and_then(get_filename);
|
||||||
.content_disposition()
|
(mime, filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_filename(content_disposition: &ContentDisposition) -> Option<String> {
|
||||||
|
content_disposition
|
||||||
.parameters
|
.parameters
|
||||||
.iter()
|
.iter()
|
||||||
.find_map(|param| match param {
|
.find_map(|param| match param {
|
||||||
DispositionParam::Filename(filename) => Some(filename.clone()),
|
DispositionParam::Filename(filename) => Some(filename.clone()),
|
||||||
_ => None,
|
_ => None,
|
||||||
});
|
})
|
||||||
(mime, filename)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_content_type(bytes: &[u8]) -> Option<Mime> {
|
fn get_content_type(bytes: &[u8]) -> Option<Mime> {
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
|
use actix_governor::governor::clock::{Clock, DefaultClock, QuantaInstant};
|
||||||
|
use actix_governor::governor::NotUntil;
|
||||||
use actix_governor::KeyExtractor;
|
use actix_governor::KeyExtractor;
|
||||||
use actix_governor::PeerIpKeyExtractor;
|
use actix_governor::PeerIpKeyExtractor;
|
||||||
use actix_governor::SimpleKeyExtractionError;
|
use actix_governor::SimpleKeyExtractionError;
|
||||||
use actix_web::HttpResponse;
|
use actix_web::HttpResponse;
|
||||||
use actix_web::HttpResponseBuilder;
|
use actix_web::HttpResponseBuilder;
|
||||||
use actix_web::{dev::ServiceRequest, http::header::ContentType};
|
use actix_web::{dev::ServiceRequest, http::header::ContentType};
|
||||||
use governor::clock::{Clock, DefaultClock, QuantaInstant};
|
use std::net::{IpAddr, Ipv6Addr};
|
||||||
use governor::NotUntil;
|
|
||||||
use std::net::IpAddr;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub struct ForwardedPeerIpKeyExtractor {
|
pub struct ForwardedPeerIpKeyExtractor {
|
||||||
|
@ -19,18 +19,15 @@ impl KeyExtractor for ForwardedPeerIpKeyExtractor {
|
||||||
|
|
||||||
fn extract(&self, req: &ServiceRequest) -> Result<Self::Key, Self::KeyExtractionError> {
|
fn extract(&self, req: &ServiceRequest) -> Result<Self::Key, Self::KeyExtractionError> {
|
||||||
let forwarded_for = req.headers().get("x-forwarded-for");
|
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)?
|
read_forwareded_for(forwarded_for).map_err(SimpleKeyExtractionError::new)?
|
||||||
} else {
|
} else {
|
||||||
PeerIpKeyExtractor.extract(req)?
|
PeerIpKeyExtractor.extract(req)?
|
||||||
};
|
};
|
||||||
|
|
||||||
// only keep the first /56 for ipv6 addresses
|
if let IpAddr::V6(mut ipv6) = ip {
|
||||||
// mask 0xffff_ffff_ffff_ff00_0000_0000_0000_0000
|
// only keep the first /56 for IPv6 addresses
|
||||||
if let IpAddr::V6(ipv6) = ip {
|
ipv6 &= Ipv6Addr::from_bits(u128::MAX << (u128::BITS - 56));
|
||||||
let mut octets = ipv6.octets();
|
|
||||||
octets[7..16].fill(0);
|
|
||||||
ip = IpAddr::V6(octets.into());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(ip)
|
Ok(ip)
|
||||||
|
|
|
@ -4,7 +4,7 @@ use actix_web::{
|
||||||
http::header::{HeaderValue, CONTENT_SECURITY_POLICY},
|
http::header::{HeaderValue, CONTENT_SECURITY_POLICY},
|
||||||
Error, HttpMessage,
|
Error, HttpMessage,
|
||||||
};
|
};
|
||||||
use actix_web_lab::middleware::Next;
|
use actix_web::middleware::Next;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue