json.rs (5174B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2025 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify it under the 6 terms of the GNU Affero General Public License as published by the Free Software 7 Foundation; either version 3, or (at your option) any later version. 8 9 TALER is distributed in the hope that it will be useful, but WITHOUT ANY 10 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. 12 13 You should have received a copy of the GNU Affero General Public License along with 14 TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 */ 16 17 use axum::{ 18 body::Bytes, 19 extract::{FromRequest, Request}, 20 http::{StatusCode, header}, 21 }; 22 use http_body_util::BodyExt as _; 23 use serde::de::DeserializeOwned; 24 use taler_common::error_code::ErrorCode; 25 26 use crate::{ 27 constants::MAX_BODY_LENGTH, 28 error::{ApiError, failure, failure_status}, 29 }; 30 31 #[derive(Debug, Clone, Copy, Default)] 32 #[must_use] 33 pub struct Req<T>(pub T); 34 35 impl<T, S> FromRequest<S> for Req<T> 36 where 37 T: DeserializeOwned, 38 S: Send + Sync, 39 { 40 type Rejection = ApiError; 41 42 async fn from_request(req: Request, _state: &S) -> Result<Self, Self::Rejection> { 43 // Check content type 44 match req.headers().get(header::CONTENT_TYPE) { 45 Some(header) => { 46 if !header.as_bytes().starts_with(b"application/json") { 47 return Err(failure_status( 48 ErrorCode::GENERIC_HTTP_HEADERS_MALFORMED, 49 "Bad Content-Type header", 50 StatusCode::UNSUPPORTED_MEDIA_TYPE, 51 )); 52 } 53 } 54 None => { 55 return Err(failure_status( 56 ErrorCode::GENERIC_HTTP_HEADERS_MALFORMED, 57 "Missing Content-Type header", 58 StatusCode::UNSUPPORTED_MEDIA_TYPE, 59 )); 60 } 61 } 62 63 // Check content length if present and wellformed 64 if let Some(length) = req 65 .headers() 66 .get(header::CONTENT_LENGTH) 67 .and_then(|it| it.to_str().ok()) 68 .and_then(|it| it.parse::<usize>().ok()) 69 && length > MAX_BODY_LENGTH 70 { 71 return Err(failure( 72 ErrorCode::GENERIC_UPLOAD_EXCEEDS_LIMIT, 73 format!("Body is suspiciously big > {MAX_BODY_LENGTH}B"), 74 )); 75 } 76 77 // Check compression 78 let compressed = if let Some(encoding) = req.headers().get(header::CONTENT_ENCODING) { 79 if encoding == "deflate" { 80 true 81 } else { 82 return Err(failure_status( 83 ErrorCode::GENERIC_HTTP_HEADERS_MALFORMED, 84 format!( 85 "Unsupported encoding '{}'", 86 String::from_utf8_lossy(encoding.as_bytes()) 87 ), 88 StatusCode::UNSUPPORTED_MEDIA_TYPE, 89 )); 90 } 91 } else { 92 false 93 }; 94 95 // Buffer body 96 let (_, body) = req.into_parts(); 97 let body = http_body_util::Limited::new(body, MAX_BODY_LENGTH); 98 let bytes = match body.collect().await { 99 Ok(chunks) => chunks.to_bytes(), 100 Err(it) => match it.downcast::<http_body_util::LengthLimitError>() { 101 Ok(_) => { 102 return Err(failure( 103 ErrorCode::GENERIC_UPLOAD_EXCEEDS_LIMIT, 104 format!("Body is suspiciously big > {MAX_BODY_LENGTH}B"), 105 )); 106 } 107 Err(err) => { 108 return Err(failure( 109 ErrorCode::GENERIC_UNEXPECTED_REQUEST_ERROR, 110 format!("Failed to read body: {err}"), 111 )); 112 } 113 }, 114 }; 115 116 let bytes = if compressed { 117 let mut buf = vec![0; MAX_BODY_LENGTH]; 118 match libdeflater::Decompressor::new().zlib_decompress(&bytes, &mut buf) { 119 Ok(it) => Bytes::copy_from_slice(&buf[..it]), 120 Err(it) => match it { 121 libdeflater::DecompressionError::BadData => { 122 return Err(failure( 123 ErrorCode::GENERIC_COMPRESSION_INVALID, 124 "Failed to decompress body: invalid compression", 125 )); 126 } 127 libdeflater::DecompressionError::InsufficientSpace => { 128 return Err(failure( 129 ErrorCode::GENERIC_UPLOAD_EXCEEDS_LIMIT, 130 format!("Decompressed body is suspiciously big > {MAX_BODY_LENGTH}B"), 131 )); 132 } 133 }, 134 } 135 } else { 136 bytes 137 }; 138 let mut de = serde_json::de::Deserializer::from_slice(&bytes); 139 let parsed = serde_path_to_error::deserialize(&mut de)?; 140 Ok(Req(parsed)) 141 } 142 }