1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
#[cfg(test)]
#[macro_use]
extern crate serde;

use std::fmt;
use std::ops::{Deref, DerefMut};

use actix_http::{Payload, PayloadStream, Response};
use actix_http::http::StatusCode;
use actix_web::{FromRequest, HttpRequest, Responder};
#[cfg(feature = "compress")]
use actix_web::dev::Decompress;
use futures_util::core_reexport::fmt::Formatter;
use futures_util::future::{err, LocalBoxFuture, ok, Ready};
use futures_util::FutureExt;
use serde::de::DeserializeOwned;
use serde::Serialize;

pub use body::*;
pub use config::*;
pub use error::*;

mod error;
mod config;
mod body;

#[cfg(test)]
mod tests;

pub struct Cbor<T>(pub T);

impl<T> Cbor<T> {
    /// Deconstruct to an inner value
    pub fn into_inner(self) -> T {
        self.0
    }
}

impl<T> Deref for Cbor<T> {
    type Target = T;

    fn deref(&self) -> &T {
        &self.0
    }
}

impl<T> DerefMut for Cbor<T> {
    fn deref_mut(&mut self) -> &mut T {
        &mut self.0
    }
}

impl<T> fmt::Debug for Cbor<T> where T: fmt::Debug {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write!(f, "Cbor: {:?}", self.0)
    }
}

impl<T> Responder for Cbor<T> where T: Serialize {
    type Error = CborError;
    type Future = Ready<Result<Response, Self::Error>>;

    fn respond_to(self, _: &HttpRequest) -> Self::Future {
        let body = match serde_cbor::to_vec(&self.0) {
            Ok(body) => body,
            Err(e) => return err(e.into())
        };

        ok(Response::build(StatusCode::OK)
            .content_type("application/cbor")
            .body(body))
    }
}

impl<T> FromRequest for Cbor<T> where T: DeserializeOwned + 'static {
    type Error = actix_web::Error;
    type Future = LocalBoxFuture<'static, Result<Self, Self::Error>>;
    type Config = CborConfig;

    fn from_request(req: &HttpRequest, payload: &mut Payload<PayloadStream>) -> Self::Future {
        let req2 = req.clone();
        let config = CborConfig::from_req(req);

        let limit = config.limit;
        let ctype = config.content_type.clone();
        let err_handler = config.err_handler.clone();

        CborBody::new(req, payload, ctype)
            .limit(limit)
            .map(move |res| match res {
                Err(e) => {
                    log::debug!(
                        "Failed to deserialize CBOR from payload. \
                         Request path: {}",
                        req2.path()
                    );

                    if let Some(err) = err_handler {
                        Err((*err)(e, &req2))
                    } else {
                        Err(e.into())
                    }
                }
                Ok(data) => Ok(Cbor(data)),
            })
            .boxed_local()
    }
}