Skip to content

Commit

Permalink
Merge branch 'tower' into in-memory-backend
Browse files Browse the repository at this point in the history
  • Loading branch information
AndreyErmilov authored Aug 13, 2023
2 parents 9ef63e8 + 0a27a34 commit ebdb03c
Show file tree
Hide file tree
Showing 24 changed files with 543 additions and 49 deletions.
8 changes: 6 additions & 2 deletions examples/examples/tower.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use hitbox_stretto::StrettoBackend;
use hitbox_redis::RedisBackend;
use hitbox_stretto::builder::StrettoBackendBuilder;
use hitbox_tower::Cache;
use hyper::{Body, Server};
use std::{convert::Infallible, net::SocketAddr};
Expand All @@ -18,10 +20,12 @@ async fn main() {
.finish();
tracing::subscriber::set_global_default(subscriber).unwrap();

let inmemory = StrettoBackend::builder(2 ^ 16).finalize().unwrap();
let inmemory = StrettoBackend::builder(10_000_000).finalize().unwrap();
let redis = RedisBackend::builder().build().unwrap();

let service = tower::ServiceBuilder::new()
.layer(tower_http::trace::TraceLayer::new_for_http())
.layer(Cache::builder().backend(inmemory).build())
.layer(Cache::builder().backend(redis).build())
.service_fn(handle);

let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
Expand Down
2 changes: 1 addition & 1 deletion hitbox-http/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ http = "0.2"
http-body = "0.4"
hitbox = { path = "../hitbox", version = "0.1" }
hitbox-backend = { path = "../hitbox-backend", version = "0.1" }
serde = "1.0.144"
bytes = "1"
chrono = "0.4"
hyper = { version = "0.14", features = ["stream"] }
futures = { version = "0.3", default-features = false }
actix-router = "0.5"
serde_qs = "0.12"
serde = { version = "1", features = ["derive"] }

[dev-dependencies]
tokio = { version = "1", features = ["test-util"], default-features = false }
Expand Down
50 changes: 50 additions & 0 deletions hitbox-http/src/extractors/header.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
use async_trait::async_trait;
use hitbox::cache::{Extractor, KeyPart, KeyParts};
use http::HeaderValue;

use crate::CacheableHttpRequest;

pub struct Header<E> {
inner: E,
name: String,
}

pub trait HeaderExtractor: Sized {
fn header(self, name: String) -> Header<Self>;
}

impl<E> HeaderExtractor for E
where
E: Extractor,
{
fn header(self, name: String) -> Header<Self> {
Header { inner: self, name }
}
}

#[async_trait]
impl<ReqBody, E> Extractor for Header<E>
where
ReqBody: Send + 'static,
E: Extractor<Subject = CacheableHttpRequest<ReqBody>> + Send + Sync,
{
type Subject = E::Subject;

async fn get(&self, subject: Self::Subject) -> KeyParts<Self::Subject> {
let value = subject
.parts()
.headers
.get(self.name.as_str())
.map(HeaderValue::to_str)
.transpose()
.ok()
.flatten()
.map(str::to_string);
let mut parts = self.inner.get(subject).await;
parts.push(KeyPart {
key: self.name.clone(),
value,
});
parts
}
}
41 changes: 41 additions & 0 deletions hitbox-http/src/extractors/method.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
use async_trait::async_trait;
use hitbox::cache::{Extractor, KeyPart, KeyParts};
use http::HeaderValue;

use crate::CacheableHttpRequest;

pub struct Method<E> {
inner: E,
}

pub trait MethodExtractor: Sized {
fn method(self) -> Method<Self>;
}

impl<E> MethodExtractor for E
where
E: Extractor,
{
fn method(self) -> Method<Self> {
Method { inner: self }
}
}

#[async_trait]
impl<ReqBody, E> Extractor for Method<E>
where
ReqBody: Send + 'static,
E: Extractor<Subject = CacheableHttpRequest<ReqBody>> + Send + Sync,
{
type Subject = E::Subject;

async fn get(&self, subject: Self::Subject) -> KeyParts<Self::Subject> {
let method = subject.parts().method.to_string();
let mut parts = self.inner.get(subject).await;
parts.push(KeyPart {
key: "method".to_owned(),
value: Some(method),
});
parts
}
}
36 changes: 36 additions & 0 deletions hitbox-http/src/extractors/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
use std::marker::PhantomData;

use async_trait::async_trait;
use hitbox::cache::{Extractor, KeyPart, KeyParts};

use crate::CacheableHttpRequest;

pub mod header;
pub mod method;
pub mod path;
pub mod query;

pub struct NeutralExtractor<ReqBody> {
_res: PhantomData<fn(ReqBody) -> ReqBody>,
}

impl<ResBody> NeutralExtractor<ResBody> {
pub fn new() -> Self {
NeutralExtractor { _res: PhantomData }
}
}

#[async_trait]
impl<ResBody> Extractor for NeutralExtractor<ResBody>
where
ResBody: Send + 'static,
{
type Subject = CacheableHttpRequest<ResBody>;

async fn get(&self, subject: Self::Subject) -> KeyParts<Self::Subject> {
KeyParts {
subject,
parts: Vec::new(),
}
}
}
51 changes: 51 additions & 0 deletions hitbox-http/src/extractors/path.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
use actix_router::{ResourceDef, ResourcePath};
use async_trait::async_trait;
use hitbox::cache::{CacheableRequest, Extractor, KeyPart, KeyParts};
use http::HeaderValue;

use crate::CacheableHttpRequest;

pub struct Path<E> {
inner: E,
resource: ResourceDef,
}

pub trait PathExtractor: Sized {
fn path(self, resource: &str) -> Path<Self>;
}

impl<E> PathExtractor for E
where
E: Extractor,
{
fn path(self, resource: &str) -> Path<Self> {
Path {
inner: self,
resource: ResourceDef::try_from(resource).unwrap(),
}
}
}

#[async_trait]
impl<ReqBody, E> Extractor for Path<E>
where
ReqBody: Send + 'static,
E: Extractor<Subject = CacheableHttpRequest<ReqBody>> + Send + Sync,
{
type Subject = E::Subject;

async fn get(&self, subject: Self::Subject) -> KeyParts<Self::Subject> {
let mut path = actix_router::Path::new(subject.parts().uri.path());
self.resource.capture_match_info(&mut path);
let mut matched_parts = path
.iter()
.map(|(key, value)| KeyPart {
key: key.to_owned(),
value: Some(value.to_owned()),
})
.collect::<Vec<_>>();
let mut parts = self.inner.get(subject).await;
parts.append(&mut matched_parts);
parts
}
}
50 changes: 50 additions & 0 deletions hitbox-http/src/extractors/query.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
use async_trait::async_trait;
use hitbox::cache::{Extractor, KeyPart, KeyParts};

use crate::CacheableHttpRequest;

pub struct Query<E> {
inner: E,
name: String,
}

pub trait QueryExtractor: Sized {
fn query(self, name: String) -> Query<Self>;
}

impl<E> QueryExtractor for E
where
E: Extractor,
{
fn query(self, name: String) -> Query<Self> {
Query { inner: self, name }
}
}

#[async_trait]
impl<ReqBody, E> Extractor for Query<E>
where
ReqBody: Send + 'static,
E: Extractor<Subject = CacheableHttpRequest<ReqBody>> + Send + Sync,
{
type Subject = E::Subject;

async fn get(&self, subject: Self::Subject) -> KeyParts<Self::Subject> {
let values = subject
.parts()
.uri
.query()
.map(crate::query::parse)
.map(|m| m.get(&self.name).map(crate::query::Value::inner))
.flatten()
.unwrap_or_default();
let mut parts = self.inner.get(subject).await;
for value in values {
parts.push(KeyPart {
key: self.name.clone(),
value: Some(value),
});
}
parts
}
}
2 changes: 2 additions & 0 deletions hitbox-http/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
mod body;
pub mod extractors;
pub mod predicates;
mod query;
mod request;
mod response;

Expand Down
2 changes: 1 addition & 1 deletion hitbox-http/src/predicates/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ where
}

pub struct NeutralResponsePredicate<ResBody> {
_res: PhantomData<fn() -> ResBody>, // FIX: HEHE
_res: PhantomData<fn(ResBody) -> ResBody>, // FIX: HEHE
}

impl<ResBody> NeutralResponsePredicate<ResBody> {
Expand Down
21 changes: 4 additions & 17 deletions hitbox-http/src/predicates/query.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,10 @@
use crate::CacheableHttpRequest;
use async_trait::async_trait;
use hitbox::predicates::{Operation, Predicate, PredicateResult};
use serde::Deserialize;
use std::{collections::HashMap, marker::PhantomData};

#[derive(Deserialize, PartialEq, Eq)]
#[serde(untagged)]
pub enum QsValue {
Scalar(String),
Array(Vec<String>),
}

pub struct Query<P> {
pub name: String,
pub value: QsValue,
pub value: crate::query::Value,
pub operation: Operation,
inner: P,
}
Expand All @@ -29,17 +20,13 @@ where
fn query(self, name: String, value: String) -> Query<P> {
Query {
name,
value: QsValue::Scalar(value),
value: crate::query::Value::Scalar(value),
operation: Operation::Eq,
inner: self,
}
}
}

fn parse_query(value: &str) -> HashMap<String, QsValue> {
serde_qs::from_str(value).unwrap()
}

#[async_trait]
impl<ReqBody, P> Predicate for Query<P>
where
Expand All @@ -52,11 +39,11 @@ where
match self.inner.check(request).await {
PredicateResult::Cacheable(request) => {
let op = match self.operation {
Operation::Eq => QsValue::eq,
Operation::Eq => crate::query::Value::eq,
Operation::In => unimplemented!(),
};
match request.parts().uri.query() {
Some(query_string) => match parse_query(query_string).get(&self.name) {
Some(query_string) => match crate::query::parse(query_string).get(&self.name) {
Some(value) if op(value, &self.value) => {
PredicateResult::Cacheable(request)
}
Expand Down
51 changes: 51 additions & 0 deletions hitbox-http/src/query.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
use serde::Deserialize;
use std::collections::HashMap;

#[derive(Debug, Deserialize, PartialEq, Eq)]
#[serde(untagged)]
pub enum Value {
Scalar(String),
Array(Vec<String>),
}

impl Value {
pub fn inner(&self) -> Vec<String> {
match self {
Value::Scalar(value) => vec![value.to_owned()],
Value::Array(values) => values.to_owned(),
}
}
}

pub fn parse(value: &str) -> HashMap<String, Value> {
serde_qs::from_str(value).expect("Unreachable branch reached")
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_parse_valid_one() {
let hash_map = parse("key=value");
let value = hash_map.get("key").unwrap();
assert_eq!(value.inner(), vec!["value"]);
}

#[test]
fn test_parse_valid_multiple() {
let hash_map = parse("key-one=value-one&key-two=value-two&key-three=value-three");
let value = hash_map.get("key-one").unwrap();
assert_eq!(value.inner(), vec!["value-one"]);
let value = hash_map.get("key-two").unwrap();
assert_eq!(value.inner(), vec!["value-two"]);
let value = hash_map.get("key-three").unwrap();
assert_eq!(value.inner(), vec!["value-three"]);
}

#[test]
fn test_parse_not_valid() {
let hash_map = parse(" wrong ");
assert_eq!(hash_map.len(), 1);
}
}
Loading

0 comments on commit ebdb03c

Please sign in to comment.