taler-rust

GNU Taler code in Rust. Largely core banking integrations.
Log | Files | Refs | Submodules | README | LICENSE

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 }