taler-rust

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

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 }