use std::{
borrow::Borrow,
io::{Cursor, Read},
sync::Arc,
};
use base64::Engine;
use base64::engine::general_purpose::STANDARD as BASE64;
use http::Request;
use log::info;
use serde::de::DeserializeOwned;
use crate::error::ApiError;
pub struct RequestBody(RequestBodyInner);
impl RequestBody {
pub fn void() -> Self {
RequestBody(RequestBodyInner::Void)
}
pub fn from_bytes(bytes: Vec<u8>) -> Self {
RequestBody(RequestBodyInner::Bytes(Arc::from(bytes.into_boxed_slice())))
}
pub fn from_reader(reader: impl Read + Sync + Send + 'static) -> Self {
RequestBody(RequestBodyInner::Reader(Box::new(reader)))
}
pub fn try_clone(&self) -> Option<Self> {
match &self.0 {
RequestBodyInner::Void => Some(RequestBody(RequestBodyInner::Void)),
RequestBodyInner::Bytes(p) => Some(RequestBody(RequestBodyInner::Bytes(Arc::clone(p)))),
RequestBodyInner::Reader(_) => None, }
}
pub fn into_reader(self) -> impl Read {
self.into_ureq_body().into_reader()
}
pub(crate) fn into_ureq_body(self) -> ureq::SendBody<'static> {
match self.0 {
RequestBodyInner::Void => ureq::SendBody::none(),
RequestBodyInner::Bytes(b) => ureq::SendBody::from_owned_reader(Cursor::new(b)),
RequestBodyInner::Reader(reader) => ureq::SendBody::from_owned_reader(reader),
}
}
}
enum RequestBodyInner {
Void,
Bytes(Arc<[u8]>),
Reader(Box<dyn Read + Sync + Send + 'static>),
}
pub struct ResponseBody(ureq::Body);
impl ResponseBody {
const MAX_JSON_SIZE: u64 = 10 * 1024 * 1024;
pub(crate) fn from_ureq_body(body: ureq::Body) -> Self {
ResponseBody(body)
}
pub fn into_reader(self) -> impl Read + 'static {
self.0.into_reader()
}
pub fn read_json<D: DeserializeOwned>(&mut self) -> Result<D, ApiError> {
let body = self.0.with_config().limit(Self::MAX_JSON_SIZE).read_to_vec()?;
serde_json::from_slice(&body).map_err(|e| e.into())
}
}
pub trait Handler: Send + Sync + 'static {
fn handle(
&self,
req: http::Request<RequestBody>,
) -> Result<http::Response<ResponseBody>, ApiError>;
}
pub trait Layer<Inner: Handler>: Send + Sync + 'static {
type Outer: Handler;
fn layer(self, inner: Inner) -> Self::Outer;
}
pub type ShouldRetryFn = dyn Fn(&http::Request<()>, Result<&http::Response<ResponseBody>, &ApiError>) -> bool
+ Send
+ Sync
+ 'static;
pub struct RetryLayer {
max_attempts: usize,
initial_delay: std::time::Duration,
max_delay: std::time::Duration,
should_retry: Box<ShouldRetryFn>,
}
impl RetryLayer {
const NONRETRYABLE_CODES: &[&str] = &[
"CB_IL02", ];
pub const DEFAULT_MAX_ATTEMPTS: usize = 5;
pub const DEFAULT_INITIAL_DELAY: std::time::Duration = std::time::Duration::from_secs(1);
pub const DEFAULT_MAX_DELAY: std::time::Duration = std::time::Duration::from_secs(8);
pub const DEFAULT_SHOULD_RETRY_FN: &ShouldRetryFn = &|_, resp_or_err| match resp_or_err {
Ok(resp) => !resp.status().is_success(),
Err(err) => {
if let ApiError::Kintone(kintone_err) = err {
!Self::NONRETRYABLE_CODES.contains(&kintone_err.code.borrow())
} else {
true
}
}
};
pub fn new() -> Self {
RetryLayer {
max_attempts: Self::DEFAULT_MAX_ATTEMPTS,
initial_delay: Self::DEFAULT_INITIAL_DELAY,
max_delay: Self::DEFAULT_MAX_DELAY,
should_retry: Box::new(Self::DEFAULT_SHOULD_RETRY_FN),
}
}
pub fn with_max_attempts(mut self, max_attempts: usize) -> Self {
if max_attempts == 0 {
panic!("max_attempts must be >= 1");
}
self.max_attempts = max_attempts;
self
}
pub fn with_initial_delay(mut self, initial_delay: std::time::Duration) -> Self {
self.initial_delay = initial_delay;
self
}
pub fn with_max_delay(mut self, max_delay: std::time::Duration) -> Self {
self.max_delay = max_delay;
self
}
pub fn with_should_retry(mut self, should_retry: Box<ShouldRetryFn>) -> Self {
self.should_retry = should_retry;
self
}
}
impl Default for RetryLayer {
fn default() -> Self {
Self::new()
}
}
impl<Inner: Handler> Layer<Inner> for RetryLayer {
type Outer = RetryHandler<Inner>;
fn layer(self, inner: Inner) -> Self::Outer {
RetryHandler { inner, layer: self }
}
}
pub struct RetryHandler<Inner> {
inner: Inner,
layer: RetryLayer,
}
impl<Inner: Handler> Handler for RetryHandler<Inner> {
fn handle(
&self,
req: http::Request<RequestBody>,
) -> Result<http::Response<ResponseBody>, ApiError> {
if self.layer.max_attempts == 1 {
return self.inner.handle(req);
}
let (parts, body) = req.into_parts();
let mut attempts = 1;
let mut delay = self.layer.initial_delay;
loop {
let Some(body_cloned) = body.try_clone() else {
let req = Request::from_parts(parts, body);
return self.inner.handle(req);
};
let req_cloned = http::Request::from_parts(parts.clone(), body_cloned);
let result = self.inner.handle(req_cloned);
match result {
Ok(resp) => {
if attempts >= self.layer.max_attempts {
return Ok(resp);
}
let req_nobody = http::Request::from_parts(parts.clone(), ());
let retry_ok = (self.layer.should_retry)(&req_nobody, Ok(&resp));
if !retry_ok {
return Ok(resp);
}
}
Err(e) => {
if attempts >= self.layer.max_attempts {
return Err(e);
}
let req_nobody = http::Request::from_parts(parts.clone(), ());
let retry_ok = (self.layer.should_retry)(&req_nobody, Err(&e));
if !retry_ok {
return Err(e);
}
}
}
std::thread::sleep(delay);
delay = std::cmp::min(delay * 2, self.layer.max_delay);
attempts += 1;
}
}
}
pub struct LoggingLayer {
log_target: String,
enabled: bool,
}
impl LoggingLayer {
const DEFAULT_LOG_TARGET: &str = "kintone";
pub fn new() -> Self {
LoggingLayer {
log_target: Self::DEFAULT_LOG_TARGET.to_owned(),
enabled: true,
}
}
pub fn with_enabled(mut self, enabled: bool) -> Self {
self.enabled = enabled;
self
}
pub fn with_log_target(mut self, target: impl Into<String>) -> Self {
self.log_target = target.into();
self
}
}
impl Default for LoggingLayer {
fn default() -> Self {
LoggingLayer::new()
}
}
impl<Inner: Handler> Layer<Inner> for LoggingLayer {
type Outer = LoggingHandler<Inner>;
fn layer(self, inner: Inner) -> Self::Outer {
LoggingHandler {
inner,
log_target: self.log_target,
enabled: self.enabled,
}
}
}
pub struct LoggingHandler<Inner> {
inner: Inner,
log_target: String,
enabled: bool,
}
impl<Inner: Handler> Handler for LoggingHandler<Inner> {
fn handle(
&self,
req: http::Request<RequestBody>,
) -> Result<http::Response<ResponseBody>, ApiError> {
if !self.enabled {
return self.inner.handle(req);
}
info!(target: &self.log_target, "Request: method={}, url={:?}", req.method(), req.uri());
if let Some(body) = req.body().try_clone() {
let mut buf = String::new();
if body.into_reader().read_to_string(&mut buf).is_ok() {
info!(target: &self.log_target, "Request body:\n{buf}");
}
}
let result = self.inner.handle(req);
match &result {
Ok(resp) => {
info!(target: &self.log_target, "Response: status={}", resp.status().as_u16());
}
Err(e) => info!(target: &self.log_target, "Response: error={e}"),
}
result
}
}
pub struct BasicAuthLayer {
credentials: Option<(String, String)>,
}
impl BasicAuthLayer {
pub fn new(credentials: Option<(String, String)>) -> Self {
BasicAuthLayer { credentials }
}
pub fn enabled(username: impl Into<String>, password: impl Into<String>) -> Self {
BasicAuthLayer {
credentials: Some((username.into(), password.into())),
}
}
pub fn disabled() -> Self {
BasicAuthLayer { credentials: None }
}
fn encode_credentials(&self) -> Option<String> {
self.credentials.as_ref().map(|(username, password)| {
let credentials = format!("{username}:{password}");
let encoded = BASE64.encode(credentials.as_bytes());
format!("Basic {encoded}")
})
}
}
impl<Inner: Handler> Layer<Inner> for BasicAuthLayer {
type Outer = BasicAuthHandler<Inner>;
fn layer(self, inner: Inner) -> Self::Outer {
BasicAuthHandler { inner, layer: self }
}
}
pub struct BasicAuthHandler<Inner> {
inner: Inner,
layer: BasicAuthLayer,
}
impl<Inner: Handler> Handler for BasicAuthHandler<Inner> {
fn handle(
&self,
mut req: http::Request<RequestBody>,
) -> Result<http::Response<ResponseBody>, ApiError> {
if let Some(auth_header_value) = self.layer.encode_credentials() {
req.headers_mut()
.insert(http::header::AUTHORIZATION, auth_header_value.parse().unwrap());
}
self.inner.handle(req)
}
}
pub struct NoLayer;
impl<Inner: Handler> Layer<Inner> for NoLayer {
type Outer = Inner;
fn layer(self, inner: Inner) -> Self::Outer {
inner
}
}
pub struct Stack<Head, Tail>(Head, Tail);
impl<Head, Tail> Stack<Head, Tail> {
pub fn new(head: Head, tail: Tail) -> Self {
Stack(head, tail)
}
}
impl<Inner, Head, Tail> Layer<Inner> for Stack<Head, Tail>
where
Inner: Handler,
Head: Layer<Tail::Outer>,
Tail: Layer<Inner>,
{
type Outer = Head::Outer;
fn layer(self, inner: Inner) -> Self::Outer {
self.0.layer(self.1.layer(inner))
}
}