json.rs (5146B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2025, 2026 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 use zlib_rs::{InflateConfig, ReturnCode}; 26 27 use crate::{ 28 constants::MAX_BODY_LENGTH, 29 error::{ApiError, failure, failure_status}, 30 }; 31 32 #[derive(Debug, Clone, Copy, Default)] 33 #[must_use] 34 pub struct Req<T>(pub T); 35 36 impl<T, S> FromRequest<S> for Req<T> 37 where 38 T: DeserializeOwned, 39 S: Send + Sync, 40 { 41 type Rejection = ApiError; 42 43 async fn from_request(req: Request, _state: &S) -> Result<Self, Self::Rejection> { 44 // Check content type 45 match req.headers().get(header::CONTENT_TYPE) { 46 Some(header) => { 47 if !header.as_bytes().starts_with(b"application/json") { 48 return Err(failure_status( 49 ErrorCode::GENERIC_HTTP_HEADERS_MALFORMED, 50 "Bad Content-Type header", 51 StatusCode::UNSUPPORTED_MEDIA_TYPE, 52 )); 53 } 54 } 55 None => { 56 return Err(failure_status( 57 ErrorCode::GENERIC_HTTP_HEADERS_MALFORMED, 58 "Missing Content-Type header", 59 StatusCode::UNSUPPORTED_MEDIA_TYPE, 60 )); 61 } 62 } 63 64 // Check content length if present and wellformed 65 if let Some(length) = req 66 .headers() 67 .get(header::CONTENT_LENGTH) 68 .and_then(|it| it.to_str().ok()) 69 .and_then(|it| it.parse::<usize>().ok()) 70 && length > MAX_BODY_LENGTH 71 { 72 return Err(failure( 73 ErrorCode::GENERIC_UPLOAD_EXCEEDS_LIMIT, 74 format!("Body is suspiciously big > {MAX_BODY_LENGTH}B"), 75 )); 76 } 77 78 // Check compression 79 let compressed = if let Some(encoding) = req.headers().get(header::CONTENT_ENCODING) { 80 if encoding == "deflate" { 81 true 82 } else { 83 return Err(failure_status( 84 ErrorCode::GENERIC_HTTP_HEADERS_MALFORMED, 85 format!( 86 "Unsupported encoding '{}'", 87 String::from_utf8_lossy(encoding.as_bytes()) 88 ), 89 StatusCode::UNSUPPORTED_MEDIA_TYPE, 90 )); 91 } 92 } else { 93 false 94 }; 95 96 // Buffer body 97 let (_, body) = req.into_parts(); 98 let body = http_body_util::Limited::new(body, MAX_BODY_LENGTH); 99 let bytes = match body.collect().await { 100 Ok(chunks) => chunks.to_bytes(), 101 Err(it) => match it.downcast::<http_body_util::LengthLimitError>() { 102 Ok(_) => { 103 return Err(failure( 104 ErrorCode::GENERIC_UPLOAD_EXCEEDS_LIMIT, 105 format!("Body is suspiciously big > {MAX_BODY_LENGTH}B"), 106 )); 107 } 108 Err(err) => { 109 return Err(failure( 110 ErrorCode::GENERIC_UNEXPECTED_REQUEST_ERROR, 111 format!("Failed to read body: {err}"), 112 )); 113 } 114 }, 115 }; 116 117 let bytes = if compressed { 118 let mut buf = [0; MAX_BODY_LENGTH]; 119 let (decompressed, code) = 120 zlib_rs::decompress_slice(&mut buf, &bytes, InflateConfig::default()); 121 dbg!(code); 122 match code { 123 ReturnCode::Ok => Bytes::copy_from_slice(decompressed), 124 ReturnCode::BufError => { 125 return Err(failure( 126 ErrorCode::GENERIC_UPLOAD_EXCEEDS_LIMIT, 127 format!("Decompressed body is suspiciously big > {MAX_BODY_LENGTH}B"), 128 )); 129 } 130 _ => { 131 return Err(failure( 132 ErrorCode::GENERIC_COMPRESSION_INVALID, 133 "Failed to decompress body: invalid compression", 134 )); 135 } 136 } 137 } else { 138 bytes 139 }; 140 let mut de = serde_json::de::Deserializer::from_slice(&bytes); 141 let parsed = serde_path_to_error::deserialize(&mut de)?; 142 Ok(Req(parsed)) 143 } 144 }