From 074b362028e5c0aad0f40ba0af1d5edc1710eea7 Mon Sep 17 00:00:00 2001 From: yaohuiming Date: Sat, 20 Apr 2024 04:49:55 +0000 Subject: [PATCH 01/12] Add async_cleanup() method to ServerApp trait --- Refactor cleanup functions to use async/await Includes-commit: 3cd52fee465e955e8ccfb98826cf4976ca284304 Includes-commit: 58946a8d586155875678eef163ad1a60882bc959 Replicated-from: https://github.com/cloudflare/pingora/pull/193 --- .bleep | 2 +- pingora-core/src/apps/mod.rs | 8 ++++---- pingora-core/src/services/listening.rs | 2 +- pingora-proxy/src/lib.rs | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.bleep b/.bleep index ee7b1835..c06c5f81 100644 --- a/.bleep +++ b/.bleep @@ -1 +1 @@ -0d6c0fbd55a4b0d8b5f2ae892a6602457dc61b7e \ No newline at end of file +e21e3b7e97830f026794014cf1406dd544cac032 \ No newline at end of file diff --git a/pingora-core/src/apps/mod.rs b/pingora-core/src/apps/mod.rs index a8fa29c2..6614c9f7 100644 --- a/pingora-core/src/apps/mod.rs +++ b/pingora-core/src/apps/mod.rs @@ -51,7 +51,7 @@ pub trait ServerApp { ) -> Option; /// This callback will be called once after the service stops listening to its endpoints. - fn cleanup(&self) {} + async fn cleanup(&self) {} } /// This trait defines the interface of an HTTP application. @@ -77,7 +77,7 @@ pub trait HttpServerApp { None } - fn http_cleanup(&self) {} + async fn http_cleanup(&self) {} } #[cfg_attr(not(doc_async_trait), async_trait)] @@ -141,7 +141,7 @@ where } } - fn cleanup(&self) { - self.http_cleanup() + async fn cleanup(&self) { + self.http_cleanup().await; } } diff --git a/pingora-core/src/services/listening.rs b/pingora-core/src/services/listening.rs index 58d7d5bb..54373230 100644 --- a/pingora-core/src/services/listening.rs +++ b/pingora-core/src/services/listening.rs @@ -205,7 +205,7 @@ impl ServiceTrait for Service { futures::future::join_all(handlers).await; self.listeners.cleanup(); - self.app_logic.cleanup(); + self.app_logic.cleanup().await; } fn name(&self) -> &str { diff --git a/pingora-proxy/src/lib.rs b/pingora-proxy/src/lib.rs index 453e9ddb..40783a0b 100644 --- a/pingora-proxy/src/lib.rs +++ b/pingora-proxy/src/lib.rs @@ -614,7 +614,7 @@ where self.process_request(session, ctx).await } - fn http_cleanup(&self) { + async fn http_cleanup(&self) { // Notify all keepalived requests blocking on read_request() to abort self.shutdown.notify_waiters(); From e9429e5d651b2d85aaf6dd76b14b281e17e8ed7f Mon Sep 17 00:00:00 2001 From: Alex Severin Date: Sun, 21 Apr 2024 23:01:54 +0000 Subject: [PATCH 02/12] unzip map items Includes-commit: db64afd0a38e55f6684931cd2b00307df877510a Replicated-from: https://github.com/cloudflare/pingora/pull/216 --- .bleep | 2 +- pingora-core/src/server/transfer_fd/mod.rs | 12 +----------- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/.bleep b/.bleep index c06c5f81..f330239f 100644 --- a/.bleep +++ b/.bleep @@ -1 +1 @@ -e21e3b7e97830f026794014cf1406dd544cac032 \ No newline at end of file +f753f291f3b4c478428b8233cc472d153650eecf \ No newline at end of file diff --git a/pingora-core/src/server/transfer_fd/mod.rs b/pingora-core/src/server/transfer_fd/mod.rs index 46807e31..d3e82845 100644 --- a/pingora-core/src/server/transfer_fd/mod.rs +++ b/pingora-core/src/server/transfer_fd/mod.rs @@ -51,17 +51,7 @@ impl Fds { } pub fn serialize(&self) -> (Vec, Vec) { - let serialized: Vec<(String, RawFd)> = self - .map - .iter() - .map(|(key, value)| (key.clone(), *value)) - .collect(); - - ( - serialized.iter().map(|v| v.0.clone()).collect(), - serialized.iter().map(|v| v.1).collect(), - ) - // Surely there is a better way of doing this + self.map.iter().map(|(key, val)| (key.clone(), val)).unzip() } pub fn deserialize(&mut self, binds: Vec, fds: Vec) { From f408d2247ba7789024ce3eed1006c2dd33920122 Mon Sep 17 00:00:00 2001 From: Alex Severin Date: Sun, 21 Apr 2024 21:55:20 +0000 Subject: [PATCH 03/12] finish zip todo Includes-commit: 68a91966421910f47b4a3159619fb9c2cabdf240 Replicated-from: https://github.com/cloudflare/pingora/pull/214 --- .bleep | 2 +- pingora-core/src/server/transfer_fd/mod.rs | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.bleep b/.bleep index f330239f..f8838663 100644 --- a/.bleep +++ b/.bleep @@ -1 +1 @@ -f753f291f3b4c478428b8233cc472d153650eecf \ No newline at end of file +b5cb1b6f679bf1dcd136617b0212eb4f7c4bce84 \ No newline at end of file diff --git a/pingora-core/src/server/transfer_fd/mod.rs b/pingora-core/src/server/transfer_fd/mod.rs index d3e82845..969d9cc3 100644 --- a/pingora-core/src/server/transfer_fd/mod.rs +++ b/pingora-core/src/server/transfer_fd/mod.rs @@ -55,10 +55,9 @@ impl Fds { } pub fn deserialize(&mut self, binds: Vec, fds: Vec) { - assert!(binds.len() == fds.len()); - // TODO: use zip() - for i in 0..binds.len() { - self.map.insert(binds[i].clone(), fds[i]); + assert_eq!(binds.len(), fds.len()); + for (bind, fd) in binds.into_iter().zip(fds) { + self.map.insert(bind, fd); } } From e7f9c9276f167edbc68cddc2231422bb66d2c3b1 Mon Sep 17 00:00:00 2001 From: Yuchen Wu Date: Wed, 1 May 2024 14:49:05 -0700 Subject: [PATCH 04/12] parse and send HTTP/1 reason phrase This change allows customized HTTP1 reason phrase to be used and proxied. --- .bleep | 2 +- pingora-core/src/protocols/http/v1/client.rs | 16 +++++++ pingora-core/src/protocols/http/v1/server.rs | 17 +++++++- pingora-http/src/lib.rs | 45 ++++++++++++++++++++ 4 files changed, 78 insertions(+), 2 deletions(-) diff --git a/.bleep b/.bleep index f8838663..2334b4d4 100644 --- a/.bleep +++ b/.bleep @@ -1 +1 @@ -b5cb1b6f679bf1dcd136617b0212eb4f7c4bce84 \ No newline at end of file +c6dc070f9cc16937943cc3b4d3299dbe3904fab8 \ No newline at end of file diff --git a/pingora-core/src/protocols/http/v1/client.rs b/pingora-core/src/protocols/http/v1/client.rs index 1dfb97ae..aaa7d8cf 100644 --- a/pingora-core/src/protocols/http/v1/client.rs +++ b/pingora-core/src/protocols/http/v1/client.rs @@ -249,6 +249,8 @@ impl HttpSession { _ => Version::HTTP_09, }); + response_header.set_reason_phrase(resp.reason)?; + let buf = buf.freeze(); for header in header_refs { @@ -720,6 +722,20 @@ mod tests_stream { assert_eq!(0, http_stream.resp_header().unwrap().headers.len()); } + #[tokio::test] + async fn read_response_custom_reason() { + init_log(); + let input = b"HTTP/1.1 200 Just Fine\r\n\r\n"; + let mock_io = Builder::new().read(&input[..]).build(); + let mut http_stream = HttpSession::new(Box::new(mock_io)); + let res = http_stream.read_response().await; + assert_eq!(input.len(), res.unwrap()); + assert_eq!( + http_stream.resp_header().unwrap().get_reason_phrase(), + Some("Just Fine") + ); + } + #[tokio::test] async fn read_response_default() { init_log(); diff --git a/pingora-core/src/protocols/http/v1/server.rs b/pingora-core/src/protocols/http/v1/server.rs index 7939ed46..d8085062 100644 --- a/pingora-core/src/protocols/http/v1/server.rs +++ b/pingora-core/src/protocols/http/v1/server.rs @@ -1013,7 +1013,7 @@ fn http_resp_header_to_buf( let status = resp.status; buf.put_slice(status.as_str().as_bytes()); buf.put_u8(b' '); - let reason = status.canonical_reason(); + let reason = resp.get_reason_phrase(); if let Some(reason_buf) = reason { buf.put_slice(reason_buf.as_bytes()); } @@ -1381,6 +1381,21 @@ mod tests_stream { .unwrap(); } + #[tokio::test] + async fn write_custom_reason() { + let wire = b"HTTP/1.1 200 Just Fine\r\nFoo: Bar\r\n\r\n"; + let mock_io = Builder::new().write(wire).build(); + let mut http_stream = HttpSession::new(Box::new(mock_io)); + let mut new_response = ResponseHeader::build(StatusCode::OK, None).unwrap(); + new_response.set_reason_phrase(Some("Just Fine")).unwrap(); + new_response.append_header("Foo", "Bar").unwrap(); + http_stream.update_resp_headers = false; + http_stream + .write_response_header_ref(&new_response) + .await + .unwrap(); + } + #[tokio::test] async fn write_informational() { let wire = b"HTTP/1.1 100 Continue\r\n\r\nHTTP/1.1 200 OK\r\nFoo: Bar\r\n\r\n"; diff --git a/pingora-http/src/lib.rs b/pingora-http/src/lib.rs index b6167439..74dee532 100644 --- a/pingora-http/src/lib.rs +++ b/pingora-http/src/lib.rs @@ -288,6 +288,8 @@ pub struct ResponseHeader { base: RespParts, // an ordered header map to store the original case of each header name header_name_map: Option, + // the reason phrase of the response, if unset, a default one will be used + reason_phrase: Option, } impl AsRef for ResponseHeader { @@ -309,6 +311,7 @@ impl Clone for ResponseHeader { Self { base: self.as_owned_parts(), header_name_map: self.header_name_map.clone(), + reason_phrase: self.reason_phrase.clone(), } } } @@ -319,6 +322,7 @@ impl From for ResponseHeader { Self { base: parts, header_name_map: None, + reason_phrase: None, } } } @@ -350,6 +354,7 @@ impl ResponseHeader { ResponseHeader { base, header_name_map: None, + reason_phrase: None, } } @@ -443,6 +448,27 @@ impl ResponseHeader { self.base.version = version } + /// Set the HTTP reason phase. If `None`, a default reason phase will be used + pub fn set_reason_phrase(&mut self, reason_phrase: Option<&str>) -> Result<()> { + // No need to allocate memory to store the phrase if it is the default one. + if reason_phrase == self.base.status.canonical_reason() { + self.reason_phrase = None; + return Ok(()); + } + + // TODO: validate it "*( HTAB / SP / VCHAR / obs-text )" + self.reason_phrase = reason_phrase.map(str::to_string); + Ok(()) + } + + /// Get the HTTP reason phase. If [Self::set_reason_phrase()] is never called + /// or set to `None`, a default reason phase will be used + pub fn get_reason_phrase(&self) -> Option<&str> { + self.reason_phrase + .as_deref() + .or_else(|| self.base.status.canonical_reason()) + } + /// Clone `self` into [http::response::Parts]. pub fn as_owned_parts(&self) -> RespParts { clone_resp_parts(&self.base) @@ -668,4 +694,23 @@ mod tests { assert_eq!("Hello�World", req.uri.path_and_query().unwrap()); assert_eq!(raw_path, req.raw_path()); } + + #[test] + fn test_reason_phrase() { + let mut resp = ResponseHeader::new(None); + let reason = resp.get_reason_phrase().unwrap(); + assert_eq!(reason, "OK"); + + resp.set_reason_phrase(Some("FooBar")).unwrap(); + let reason = resp.get_reason_phrase().unwrap(); + assert_eq!(reason, "FooBar"); + + resp.set_reason_phrase(Some("OK")).unwrap(); + let reason = resp.get_reason_phrase().unwrap(); + assert_eq!(reason, "OK"); + + resp.set_reason_phrase(None).unwrap(); + let reason = resp.get_reason_phrase().unwrap(); + assert_eq!(reason, "OK"); + } } From 0617b1a3523914c44f73bbd8304f153c824f324f Mon Sep 17 00:00:00 2001 From: Andrew Hauck Date: Fri, 26 Apr 2024 14:58:07 -0700 Subject: [PATCH 05/12] Always return HttpTask::Body on body done instead of HttpTask::done --- .bleep | 2 +- pingora-core/src/protocols/http/v1/client.rs | 26 ++++++++------- pingora-core/src/protocols/http/v1/server.rs | 33 ++++++++++---------- pingora-core/src/protocols/http/v2/server.rs | 9 +++--- 4 files changed, 37 insertions(+), 33 deletions(-) diff --git a/.bleep b/.bleep index 2334b4d4..a3caa84d 100644 --- a/.bleep +++ b/.bleep @@ -1 +1 @@ -c6dc070f9cc16937943cc3b4d3299dbe3904fab8 \ No newline at end of file +547ccfbe180fd88e07d229c771fa66479eaabedb \ No newline at end of file diff --git a/pingora-core/src/protocols/http/v1/client.rs b/pingora-core/src/protocols/http/v1/client.rs index aaa7d8cf..fa7710f4 100644 --- a/pingora-core/src/protocols/http/v1/client.rs +++ b/pingora-core/src/protocols/http/v1/client.rs @@ -598,27 +598,26 @@ impl HttpSession { if self.should_read_resp_header() { let resp_header = self.read_resp_header_parts().await?; let end_of_body = self.is_body_done(); - debug!("Response header: {:?}", resp_header); + debug!("Response header: {resp_header:?}"); trace!( "Raw Response header: {:?}", str::from_utf8(self.get_headers_raw()).unwrap() ); Ok(HttpTask::Header(resp_header, end_of_body)) } else if self.is_body_done() { + // no body debug!("Response is done"); Ok(HttpTask::Done) } else { /* need to read body */ - let data = self.read_body_bytes().await?; + let body = self.read_body_bytes().await?; let end_of_body = self.is_body_done(); - if let Some(body) = data { - debug!("Response body: {} bytes", body.len()); - trace!("Response body: {:?}", body); - Ok(HttpTask::Body(Some(body), end_of_body)) - } else { - debug!("Response is done"); - Ok(HttpTask::Done) - } + debug!( + "Response body: {} bytes, end: {end_of_body}", + body.as_ref().map_or(0, |b| b.len()) + ); + trace!("Response body: {body:?}"); + Ok(HttpTask::Body(body, end_of_body)) } // TODO: support h1 trailer } @@ -983,9 +982,12 @@ mod tests_stream { // read body let task = http_stream.read_response_task().await.unwrap(); match task { - HttpTask::Done => {} + HttpTask::Body(b, eob) => { + assert!(b.is_none()); + assert!(eob); + } _ => { - panic!("task should be Done") + panic!("task should be body with end of stream") } } } diff --git a/pingora-core/src/protocols/http/v1/server.rs b/pingora-core/src/protocols/http/v1/server.rs index d8085062..714e9906 100644 --- a/pingora-core/src/protocols/http/v1/server.rs +++ b/pingora-core/src/protocols/http/v1/server.rs @@ -854,29 +854,31 @@ impl HttpSession { } async fn response_duplex(&mut self, task: HttpTask) -> Result { - match task { + let end_stream = match task { HttpTask::Header(header, end_stream) => { self.write_response_header(header) .await .map_err(|e| e.into_down())?; - Ok(end_stream) + end_stream } HttpTask::Body(data, end_stream) => match data { Some(d) => { if !d.is_empty() { self.write_body(&d).await.map_err(|e| e.into_down())?; } - Ok(end_stream) + end_stream } - None => Ok(end_stream), + None => end_stream, }, - HttpTask::Trailer(_) => Ok(true), // h1 trailer is not supported yet - HttpTask::Done => { - self.finish_body().await.map_err(|e| e.into_down())?; - Ok(true) - } - HttpTask::Failed(e) => Err(e), + HttpTask::Trailer(_) => true, // h1 trailer is not supported yet + HttpTask::Done => true, + HttpTask::Failed(e) => return Err(e), + }; + if end_stream { + // no-op if body wasn't initialized or is finished already + self.finish_body().await.map_err(|e| e.into_down())?; } + Ok(end_stream) } // TODO: use vectored write to avoid copying @@ -905,12 +907,7 @@ impl HttpSession { None => end_stream, }, HttpTask::Trailer(_) => true, // h1 trailer is not supported yet - HttpTask::Done => { - // flush body first - self.write_body_buf().await.map_err(|e| e.into_down())?; - self.finish_body().await.map_err(|e| e.into_down())?; - return Ok(true); - } + HttpTask::Done => true, HttpTask::Failed(e) => { // flush the data we have and quit self.write_body_buf().await.map_err(|e| e.into_down())?; @@ -923,6 +920,10 @@ impl HttpSession { } } self.write_body_buf().await.map_err(|e| e.into_down())?; + if end_stream { + // no-op if body wasn't initialized or is finished already + self.finish_body().await.map_err(|e| e.into_down())?; + } Ok(end_stream) } } diff --git a/pingora-core/src/protocols/http/v2/server.rs b/pingora-core/src/protocols/http/v2/server.rs index 9062f1fa..57238d10 100644 --- a/pingora-core/src/protocols/http/v2/server.rs +++ b/pingora-core/src/protocols/http/v2/server.rs @@ -331,15 +331,16 @@ impl HttpSession { true } HttpTask::Trailer(None) => true, - HttpTask::Done => { - self.finish().map_err(|e| e.into_down())?; - return Ok(true); - } + HttpTask::Done => true, HttpTask::Failed(e) => { return Err(e); } } || end_stream // safe guard in case `end` in tasks flips from true to false } + if end_stream { + // no-op if finished already + self.finish().map_err(|e| e.into_down())?; + } Ok(end_stream) } From d056cc1c2be56d103ce44cac1e2c49680241fde4 Mon Sep 17 00:00:00 2001 From: Benjamin Leggett Date: Wed, 17 Apr 2024 20:57:44 +0000 Subject: [PATCH 06/12] Make `pop_closed` pub, to simplify DIY drains Includes-commit: 069a792c91c19b1ad01b2a151a74242aec901ce6 Replicated-from: https://github.com/cloudflare/pingora/pull/209 Signed-off-by: Benjamin Leggett --- .bleep | 2 +- pingora-pool/src/connection.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.bleep b/.bleep index a3caa84d..85bf8043 100644 --- a/.bleep +++ b/.bleep @@ -1 +1 @@ -547ccfbe180fd88e07d229c771fa66479eaabedb \ No newline at end of file +64c5e3fa4a538348e25a1ac5fe18245fc6d1eefc \ No newline at end of file diff --git a/pingora-pool/src/connection.rs b/pingora-pool/src/connection.rs index 183e70a1..e5a96558 100644 --- a/pingora-pool/src/connection.rs +++ b/pingora-pool/src/connection.rs @@ -215,7 +215,7 @@ impl ConnectionPool { debug!("evict fd: {} from key {}", meta.id, meta.key); } - fn pop_closed(&self, meta: &ConnectionMeta) { + pub fn pop_closed(&self, meta: &ConnectionMeta) { // NOTE: which of these should be done first? self.pop_evicted(meta); self.lru.pop(&meta.id); From bf3723c4fddb34822f273305ceeedad22e8bf2ec Mon Sep 17 00:00:00 2001 From: ewang Date: Wed, 3 Apr 2024 11:16:15 -0700 Subject: [PATCH 07/12] Add purge_response callback Allow generating custom responses to purge requests (requests to invalidate or delete from the HTTP cache). --- .bleep | 2 +- pingora-proxy/src/lib.rs | 1 + pingora-proxy/src/proxy_cache.rs | 6 +- pingora-proxy/src/proxy_purge.rs | 110 +++++++++++++++++++++---------- pingora-proxy/src/proxy_trait.rs | 16 +++++ 5 files changed, 93 insertions(+), 42 deletions(-) diff --git a/.bleep b/.bleep index 85bf8043..fe486e48 100644 --- a/.bleep +++ b/.bleep @@ -1 +1 @@ -64c5e3fa4a538348e25a1ac5fe18245fc6d1eefc \ No newline at end of file +b10bac2a769c0b0307110f10280c9518705512dd \ No newline at end of file diff --git a/pingora-proxy/src/lib.rs b/pingora-proxy/src/lib.rs index 40783a0b..7880476d 100644 --- a/pingora-proxy/src/lib.rs +++ b/pingora-proxy/src/lib.rs @@ -79,6 +79,7 @@ mod subrequest; use subrequest::Ctx as SubReqCtx; +pub use proxy_purge::PurgeStatus; pub use proxy_trait::ProxyHttp; pub mod prelude { diff --git a/pingora-proxy/src/proxy_cache.rs b/pingora-proxy/src/proxy_cache.rs index f7e35b13..00b61daa 100644 --- a/pingora-proxy/src/proxy_cache.rs +++ b/pingora-proxy/src/proxy_cache.rs @@ -62,11 +62,7 @@ impl HttpProxy { // cache purge logic: PURGE short-circuits rest of request if self.inner.is_purge(session, ctx) { - if session.cache.enabled() { - return self.proxy_purge(session, ctx).await; - } else { - return Some(proxy_purge::write_no_purge_response(session).await); - } + return self.proxy_purge(session, ctx).await; } // bypass cache lookup if we predict to be uncacheable diff --git a/pingora-proxy/src/proxy_purge.rs b/pingora-proxy/src/proxy_purge.rs index 73e27a05..1bfce7c5 100644 --- a/pingora-proxy/src/proxy_purge.rs +++ b/pingora-proxy/src/proxy_purge.rs @@ -13,6 +13,33 @@ // limitations under the License. use super::*; +use pingora_core::protocols::http::error_resp; +use std::borrow::Cow; + +#[derive(Debug)] +pub enum PurgeStatus { + /// Cache was not enabled, purge ineffectual. + NoCache, + /// Asset was found in cache (and presumably purged or being purged). + Found, + /// Asset was not found in cache. + NotFound, + /// Cache returned a purge error. + /// Contains causing error in case it should affect the downstream response. + Error(Box), +} + +// Return a canned response to a purge request, based on whether the cache had the asset or not +// (or otherwise returned an error). +fn purge_response(purge_status: &PurgeStatus) -> Cow<'static, ResponseHeader> { + let resp = match purge_status { + PurgeStatus::NoCache => &*NOT_PURGEABLE, + PurgeStatus::Found => &*OK, + PurgeStatus::NotFound => &*NOT_FOUND, + PurgeStatus::Error(ref _e) => &*INTERNAL_ERROR, + }; + Cow::Borrowed(resp) +} fn gen_purge_response(code: u16) -> ResponseHeader { let mut resp = ResponseHeader::build(code, Some(3)).unwrap(); @@ -25,27 +52,12 @@ fn gen_purge_response(code: u16) -> ResponseHeader { resp } -async fn write_purge_response( - session: &mut Session, - resp: &ResponseHeader, -) -> (bool, Option>) { - match session.as_mut().write_response_header_ref(resp).await { - Ok(_) => (true, None), - // dirty, not reusable - Err(e) => (false, Some(e.into_down())), - } -} - -/// Write a response for a rejected cache purge requests -pub async fn write_no_purge_response(session: &mut Session) -> (bool, Option>) { - // TODO: log send error - write_purge_response(session, &NOT_PURGEABLE).await -} - static OK: Lazy = Lazy::new(|| gen_purge_response(200)); static NOT_FOUND: Lazy = Lazy::new(|| gen_purge_response(404)); // for when purge is sent to uncacheable assets static NOT_PURGEABLE: Lazy = Lazy::new(|| gen_purge_response(405)); +// on cache storage or proxy error +static INTERNAL_ERROR: Lazy = Lazy::new(|| error_resp::gen_error_response(500)); impl HttpProxy { pub(crate) async fn proxy_purge( @@ -57,31 +69,57 @@ impl HttpProxy { SV: ProxyHttp + Send + Sync, SV::CTX: Send + Sync, { - match session.cache.purge().await { - Ok(found) => { - // canned PURGE response based on whether we found the asset or not - let resp = if found { &*OK } else { &*NOT_FOUND }; - let (reuse, err) = write_purge_response(session, resp).await; - if let Some(e) = err.as_ref() { - error!( - "Failed to send purge response: {}, {}", - e, + let purge_status = if session.cache.enabled() { + match session.cache.purge().await { + Ok(found) => { + if found { + PurgeStatus::Found + } else { + PurgeStatus::NotFound + } + } + Err(e) => { + session.cache.disable(NoCacheReason::StorageError); + warn!( + "Fail to purge cache: {e}, {}", self.inner.request_summary(session, ctx) - ) + ); + PurgeStatus::Error(e) } - Some((reuse, err)) } + } else { + // cache was not enabled + PurgeStatus::NoCache + }; + + let mut purge_resp = purge_response(&purge_status); + if let Err(e) = + self.inner + .purge_response_filter(session, ctx, purge_status, &mut purge_resp) + { + error!( + "Failed purge response filter: {e}, {}", + self.inner.request_summary(session, ctx) + ); + purge_resp = Cow::Borrowed(&*INTERNAL_ERROR) + } + + let write_result = match purge_resp { + Cow::Borrowed(r) => session.as_mut().write_response_header_ref(r).await, + Cow::Owned(r) => session.as_mut().write_response_header(Box::new(r)).await, + }; + let (reuse, err) = match write_result { + Ok(_) => (true, None), + // dirty, not reusable Err(e) => { - session.cache.disable(NoCacheReason::StorageError); - warn!( - "Fail to purge cache: {}, {}", - e, + let e = e.into_down(); + error!( + "Failed to send purge response: {e}, {}", self.inner.request_summary(session, ctx) ); - session.downstream_session.respond_error(500).await; - // still reusable - Some((true, Some(e))) + (false, Some(e)) } - } + }; + Some((reuse, err)) } } diff --git a/pingora-proxy/src/proxy_trait.rs b/pingora-proxy/src/proxy_trait.rs index 888854c2..01586810 100644 --- a/pingora-proxy/src/proxy_trait.rs +++ b/pingora-proxy/src/proxy_trait.rs @@ -400,4 +400,20 @@ pub trait ProxyHttp { fn is_purge(&self, _session: &Session, _ctx: &Self::CTX) -> bool { false } + + /// This filter is called after the proxy cache generates the downstream response to the purge + /// request (to invalidate or delete from the HTTP cache), based on the purge status, which + /// indicates whether the request succeeded or failed. + /// + /// The filter allows the user to modify or replace the generated downstream response. + /// If the filter returns `Err`, the proxy will instead send a 500 response. + fn purge_response_filter( + &self, + _session: &Session, + _ctx: &mut Self::CTX, + _purge_status: PurgeStatus, + _purge_response: &mut std::borrow::Cow<'static, ResponseHeader>, + ) -> Result<()> { + Ok(()) + } } From 7827cba782cc6bbc71a5327ad5d5731cd6634e69 Mon Sep 17 00:00:00 2001 From: Andrew Hauck Date: Tue, 30 Apr 2024 20:57:52 -0700 Subject: [PATCH 08/12] Add upstream_response_trailer_filter reference to user guide --- .bleep | 2 +- docs/user_guide/phase.md | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.bleep b/.bleep index fe486e48..6fd3f3e1 100644 --- a/.bleep +++ b/.bleep @@ -1 +1 @@ -b10bac2a769c0b0307110f10280c9518705512dd \ No newline at end of file +b15d1f685f4842d5cf638286a37e80cf55c3aa58 \ No newline at end of file diff --git a/docs/user_guide/phase.md b/docs/user_guide/phase.md index 5455ed90..a3264968 100644 --- a/docs/user_guide/phase.md +++ b/docs/user_guide/phase.md @@ -84,10 +84,10 @@ If the error is not retry-able, the request will end. ### `upstream_request_filter()` This phase is to modify requests before sending to upstream. -### `upstream_response_filter()/upstream_response_body_filter()` -This phase is triggered after an upstream response header/body is received. +### `upstream_response_filter()/upstream_response_body_filter()/upstream_response_trailer_filter()` +This phase is triggered after an upstream response header/body/trailer is received. -This phase is to modify or process response headers (or body) before sending to downstream. Note that this phase is called _prior_ to HTTP caching and therefore any changes made here will affect the response stored in the HTTP cache. +This phase is to modify or process response headers, body, or trailers before sending to downstream. Note that this phase is called _prior_ to HTTP caching and therefore any changes made here will affect the response stored in the HTTP cache. ### `response_filter()/response_body_filter()/response_trailer_filter()` This phase is triggered after a response header/body/trailer is ready to send to downstream. From 91615dc6994e831a305d41ca5d23107b4df85af2 Mon Sep 17 00:00:00 2001 From: James Munns Date: Mon, 22 Apr 2024 15:01:37 +0000 Subject: [PATCH 09/12] Add `Service>` constructor for providing name Includes-commit: 8727b2466f9a11aec6c5386c61c51ce54946b962 Replicated-from: https://github.com/cloudflare/pingora/pull/218 --- .bleep | 2 +- pingora-proxy/src/lib.rs | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/.bleep b/.bleep index 6fd3f3e1..7e9c425a 100644 --- a/.bleep +++ b/.bleep @@ -1 +1 @@ -b15d1f685f4842d5cf638286a37e80cf55c3aa58 \ No newline at end of file +6c5858ed364e54961a79110d08f8cece89bb9b04 \ No newline at end of file diff --git a/pingora-proxy/src/lib.rs b/pingora-proxy/src/lib.rs index 7880476d..da21ed35 100644 --- a/pingora-proxy/src/lib.rs +++ b/pingora-proxy/src/lib.rs @@ -636,3 +636,17 @@ pub fn http_proxy_service(conf: &Arc, inner: SV) -> Service( + conf: &Arc, + inner: SV, + name: &str, +) -> Service> { + Service::new( + name.to_string(), + HttpProxy::new(inner, conf.clone()), + ) +} From c99ab6086fe91e97083606d498665af6670a4696 Mon Sep 17 00:00:00 2001 From: Kevin Guthrie Date: Fri, 3 May 2024 15:49:39 -0400 Subject: [PATCH 10/12] cargo fmt --- .bleep | 2 +- pingora-proxy/src/lib.rs | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/.bleep b/.bleep index 7e9c425a..f2904f03 100644 --- a/.bleep +++ b/.bleep @@ -1 +1 @@ -6c5858ed364e54961a79110d08f8cece89bb9b04 \ No newline at end of file +db858a34938d5a4f3eefbc5187cc225dde5c02de \ No newline at end of file diff --git a/pingora-proxy/src/lib.rs b/pingora-proxy/src/lib.rs index da21ed35..a60e6a7f 100644 --- a/pingora-proxy/src/lib.rs +++ b/pingora-proxy/src/lib.rs @@ -645,8 +645,5 @@ pub fn http_proxy_service_with_name( inner: SV, name: &str, ) -> Service> { - Service::new( - name.to_string(), - HttpProxy::new(inner, conf.clone()), - ) + Service::new(name.to_string(), HttpProxy::new(inner, conf.clone())) } From 96d13a35a15151e4b2615ac407d759aa6ded8ca6 Mon Sep 17 00:00:00 2001 From: Yuchen Wu Date: Fri, 10 May 2024 13:29:48 -0700 Subject: [PATCH 11/12] Fix clipper warning on Rust 1.78 --- pingora-proxy/examples/multi_lb.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pingora-proxy/examples/multi_lb.rs b/pingora-proxy/examples/multi_lb.rs index 59125821..1321c207 100644 --- a/pingora-proxy/examples/multi_lb.rs +++ b/pingora-proxy/examples/multi_lb.rs @@ -53,9 +53,7 @@ impl ProxyHttp for Router { } } -fn build_cluster_service( - upstreams: &[&str], -) -> GenBackgroundService> +fn build_cluster_service(upstreams: &[&str]) -> GenBackgroundService> where S: BackendSelection + 'static, S::Iter: BackendIter, From bd2369a76a1cec121a7d92d7f195319ebf4079c9 Mon Sep 17 00:00:00 2001 From: Yuchen Wu Date: Fri, 10 May 2024 10:40:14 -0700 Subject: [PATCH 12/12] Release 0.2.0 --- .bleep | 2 +- CHANGELOG.md | 33 +++++++++++++++++++++++++++++++ pingora-boringssl/Cargo.toml | 2 +- pingora-cache/Cargo.toml | 14 ++++++------- pingora-core/Cargo.toml | 16 +++++++-------- pingora-error/Cargo.toml | 2 +- pingora-header-serde/Cargo.toml | 6 +++--- pingora-http/Cargo.toml | 4 ++-- pingora-ketama/Cargo.toml | 2 +- pingora-limits/Cargo.toml | 2 +- pingora-load-balancing/Cargo.toml | 12 +++++------ pingora-lru/Cargo.toml | 2 +- pingora-memory-cache/Cargo.toml | 8 ++++---- pingora-openssl/Cargo.toml | 2 +- pingora-pool/Cargo.toml | 4 ++-- pingora-proxy/Cargo.toml | 14 ++++++------- pingora-runtime/Cargo.toml | 2 +- pingora-timeout/Cargo.toml | 2 +- pingora/Cargo.toml | 14 ++++++------- tinyufo/Cargo.toml | 2 +- 20 files changed, 89 insertions(+), 56 deletions(-) diff --git a/.bleep b/.bleep index f2904f03..759a4832 100644 --- a/.bleep +++ b/.bleep @@ -1 +1 @@ -db858a34938d5a4f3eefbc5187cc225dde5c02de \ No newline at end of file +28022cfd206a7dfa1670a9884eb5b8bf621e8bfd \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index d0bfceea..281fee49 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,39 @@ All notable changes to this project will be documented in this file. +## [0.2.0](https://github.com/cloudflare/pingora/compare/0.1.1...0.2.0) - 2024-05-10 + +### 🚀 Features +- Add support for downstream h2 trailers and add an upstream h2 response trailer filter +- Add the ability to set TCP recv buf size +- Add a convenience function to retrieve Session digest +- Add `body_bytes_read()` method to Session +- Add `cache_not_modified_filter` +- Add `SSLKEYLOG` support for tls upstream +- Add `Service>` constructor for providing name +- Add `purge_response` callback +- Make `pop_closed` pub, to simplify DIY drains + +### 🐛 Bug Fixes +- Fixed gRPC trailer proxying +- Fixed `response_body_filter` `end_of_stream` always being false +- Fixed compile error in Rust <= 1.73 +- Fixed non linux build +- Fixed the counting problem of used_weight data field in `LruUnit` +- Fixed `cargo run --example server` missing cert +- Fixed error log string interpolation outside of proper context +- Fixed tinylfu test flake + +### ⚙️ Changes and Miscellaneous Tasks +- API change: `Server::run_forever` now takes ownership and ensures exit semantics +- API change: `cleanup()` method of `ServerApp` trait is now async +- Behavior change: Always return `HttpTask::Body` on body done instead of `HttpTask::done` +- Behavior change: HTTP/1 reason phrase is now parsed and proxied +- Updated `h2` dependency for RUSTSEC-2024-0332 +- Updated zstd dependencies +- Code optimization and refactor in a few crates +- More examples and docs + ## [0.1.1](https://github.com/cloudflare/pingora/compare/0.1.0...0.1.1) - 2024-04-05 ### 🚀 Features diff --git a/pingora-boringssl/Cargo.toml b/pingora-boringssl/Cargo.toml index d84ed813..c6746710 100644 --- a/pingora-boringssl/Cargo.toml +++ b/pingora-boringssl/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pingora-boringssl" -version = "0.1.0" +version = "0.2.0" authors = ["Yuchen Wu "] license = "Apache-2.0" edition = "2021" diff --git a/pingora-cache/Cargo.toml b/pingora-cache/Cargo.toml index 06bc8649..eba68c71 100644 --- a/pingora-cache/Cargo.toml +++ b/pingora-cache/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pingora-cache" -version = "0.1.0" +version = "0.2.0" authors = ["Yuchen Wu "] license = "Apache-2.0" edition = "2021" @@ -17,12 +17,12 @@ name = "pingora_cache" path = "src/lib.rs" [dependencies] -pingora-core = { version = "0.1.0", path = "../pingora-core", default-features = false } -pingora-error = { version = "0.1.0", path = "../pingora-error" } -pingora-header-serde = { version = "0.1.0", path = "../pingora-header-serde" } -pingora-http = { version = "0.1.0", path = "../pingora-http" } -pingora-lru = { version = "0.1.0", path = "../pingora-lru" } -pingora-timeout = { version = "0.1.0", path = "../pingora-timeout" } +pingora-core = { version = "0.2.0", path = "../pingora-core", default-features = false } +pingora-error = { version = "0.2.0", path = "../pingora-error" } +pingora-header-serde = { version = "0.2.0", path = "../pingora-header-serde" } +pingora-http = { version = "0.2.0", path = "../pingora-http" } +pingora-lru = { version = "0.2.0", path = "../pingora-lru" } +pingora-timeout = { version = "0.2.0", path = "../pingora-timeout" } http = { workspace = true } indexmap = "1" once_cell = { workspace = true } diff --git a/pingora-core/Cargo.toml b/pingora-core/Cargo.toml index 936a7b99..5ced49db 100644 --- a/pingora-core/Cargo.toml +++ b/pingora-core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pingora-core" -version = "0.1.0" +version = "0.2.0" authors = ["Yuchen Wu "] license = "Apache-2.0" edition = "2021" @@ -19,13 +19,13 @@ name = "pingora_core" path = "src/lib.rs" [dependencies] -pingora-runtime = { version = "0.1.0", path = "../pingora-runtime" } -pingora-openssl = { version = "0.1.0", path = "../pingora-openssl", optional = true } -pingora-boringssl = { version = "0.1.0", path = "../pingora-boringssl", optional = true } -pingora-pool = { version = "0.1.0", path = "../pingora-pool" } -pingora-error = { version = "0.1.0", path = "../pingora-error" } -pingora-timeout = { version = "0.1.0", path = "../pingora-timeout" } -pingora-http = { version = "0.1.0", path = "../pingora-http" } +pingora-runtime = { version = "0.2.0", path = "../pingora-runtime" } +pingora-openssl = { version = "0.2.0", path = "../pingora-openssl", optional = true } +pingora-boringssl = { version = "0.2.0", path = "../pingora-boringssl", optional = true } +pingora-pool = { version = "0.2.0", path = "../pingora-pool" } +pingora-error = { version = "0.2.0", path = "../pingora-error" } +pingora-timeout = { version = "0.2.0", path = "../pingora-timeout" } +pingora-http = { version = "0.2.0", path = "../pingora-http" } tokio = { workspace = true, features = ["rt-multi-thread", "signal"] } futures = "0.3" async-trait = { workspace = true } diff --git a/pingora-error/Cargo.toml b/pingora-error/Cargo.toml index cdbc619d..707a6609 100644 --- a/pingora-error/Cargo.toml +++ b/pingora-error/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pingora-error" -version = "0.1.0" +version = "0.2.0" authors = ["Yuchen Wu "] license = "Apache-2.0" edition = "2021" diff --git a/pingora-header-serde/Cargo.toml b/pingora-header-serde/Cargo.toml index eaaa6433..da079a9a 100644 --- a/pingora-header-serde/Cargo.toml +++ b/pingora-header-serde/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pingora-header-serde" -version = "0.1.0" +version = "0.2.0" authors = ["Yuchen Wu "] license = "Apache-2.0" edition = "2021" @@ -27,6 +27,6 @@ zstd-safe = { version = "7.1.0", features = ["std"] } http = { workspace = true } bytes = { workspace = true } httparse = { workspace = true } -pingora-error = { version = "0.1.0", path = "../pingora-error" } -pingora-http = { version = "0.1.0", path = "../pingora-http" } +pingora-error = { version = "0.2.0", path = "../pingora-error" } +pingora-http = { version = "0.2.0", path = "../pingora-http" } thread_local = "1.0" diff --git a/pingora-http/Cargo.toml b/pingora-http/Cargo.toml index 6cf7f978..ebf90d76 100644 --- a/pingora-http/Cargo.toml +++ b/pingora-http/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pingora-http" -version = "0.1.0" +version = "0.2.0" authors = ["Yuchen Wu "] license = "Apache-2.0" edition = "2021" @@ -19,7 +19,7 @@ path = "src/lib.rs" [dependencies] http = { workspace = true } bytes = { workspace = true } -pingora-error = { version = "0.1.0", path = "../pingora-error" } +pingora-error = { version = "0.2.0", path = "../pingora-error" } [features] default = [] diff --git a/pingora-ketama/Cargo.toml b/pingora-ketama/Cargo.toml index 1e467b59..bf13e74e 100644 --- a/pingora-ketama/Cargo.toml +++ b/pingora-ketama/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pingora-ketama" -version = "0.1.0" +version = "0.2.0" description = "Rust port of the nginx consistent hash function" authors = ["Pingora Team "] license = "Apache-2.0" diff --git a/pingora-limits/Cargo.toml b/pingora-limits/Cargo.toml index 0a0ef991..8018a138 100644 --- a/pingora-limits/Cargo.toml +++ b/pingora-limits/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pingora-limits" -version = "0.1.0" +version = "0.2.0" authors = ["Yuchen Wu "] license = "Apache-2.0" description = "A library for rate limiting and event frequency estimation" diff --git a/pingora-load-balancing/Cargo.toml b/pingora-load-balancing/Cargo.toml index f4057867..73a917be 100644 --- a/pingora-load-balancing/Cargo.toml +++ b/pingora-load-balancing/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pingora-load-balancing" -version = "0.1.0" +version = "0.2.0" authors = ["Yuchen Wu "] license = "Apache-2.0" edition = "2021" @@ -18,11 +18,11 @@ path = "src/lib.rs" [dependencies] async-trait = { workspace = true } -pingora-http = { version = "0.1.0", path = "../pingora-http" } -pingora-error = { version = "0.1.0", path = "../pingora-error" } -pingora-core = { version = "0.1.0", path = "../pingora-core", default-features = false } -pingora-ketama = { version = "0.1.0", path = "../pingora-ketama" } -pingora-runtime = { version = "0.1.0", path = "../pingora-runtime" } +pingora-http = { version = "0.2.0", path = "../pingora-http" } +pingora-error = { version = "0.2.0", path = "../pingora-error" } +pingora-core = { version = "0.2.0", path = "../pingora-core", default-features = false } +pingora-ketama = { version = "0.2.0", path = "../pingora-ketama" } +pingora-runtime = { version = "0.2.0", path = "../pingora-runtime" } arc-swap = "1" fnv = "1" rand = "0" diff --git a/pingora-lru/Cargo.toml b/pingora-lru/Cargo.toml index 69851c3d..f263e468 100644 --- a/pingora-lru/Cargo.toml +++ b/pingora-lru/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pingora-lru" -version = "0.1.0" +version = "0.2.0" authors = ["Yuchen Wu "] license = "Apache-2.0" edition = "2021" diff --git a/pingora-memory-cache/Cargo.toml b/pingora-memory-cache/Cargo.toml index d51268b7..3d8f549d 100644 --- a/pingora-memory-cache/Cargo.toml +++ b/pingora-memory-cache/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pingora-memory-cache" -version = "0.1.0" +version = "0.2.0" authors = ["Yuchen Wu "] license = "Apache-2.0" edition = "2021" @@ -17,11 +17,11 @@ name = "pingora_memory_cache" path = "src/lib.rs" [dependencies] -TinyUFO = { version = "0.1.0", path = "../tinyufo" } +TinyUFO = { version = "0.2.0", path = "../tinyufo" } ahash = { workspace = true } tokio = { workspace = true, features = ["sync"] } async-trait = { workspace = true } -pingora-error = { version = "0.1.0", path = "../pingora-error" } +pingora-error = { version = "0.2.0", path = "../pingora-error" } log = { workspace = true } parking_lot = "0" -pingora-timeout = { version = "0.1.0", path = "../pingora-timeout" } +pingora-timeout = { version = "0.2.0", path = "../pingora-timeout" } diff --git a/pingora-openssl/Cargo.toml b/pingora-openssl/Cargo.toml index 19b33490..cb0574a4 100644 --- a/pingora-openssl/Cargo.toml +++ b/pingora-openssl/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pingora-openssl" -version = "0.1.0" +version = "0.2.0" authors = ["Yuchen Wu "] license = "Apache-2.0" edition = "2021" diff --git a/pingora-pool/Cargo.toml b/pingora-pool/Cargo.toml index 170e4971..5282cf30 100644 --- a/pingora-pool/Cargo.toml +++ b/pingora-pool/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pingora-pool" -version = "0.1.0" +version = "0.2.0" authors = ["Yuchen Wu "] license = "Apache-2.0" edition = "2021" @@ -23,7 +23,7 @@ lru = { workspace = true } log = { workspace = true } parking_lot = "0.12" crossbeam-queue = "0.3" -pingora-timeout = { version = "0.1.0", path = "../pingora-timeout" } +pingora-timeout = { version = "0.2.0", path = "../pingora-timeout" } [dev-dependencies] tokio-test = "0.4" diff --git a/pingora-proxy/Cargo.toml b/pingora-proxy/Cargo.toml index a1a49bd7..c3911961 100644 --- a/pingora-proxy/Cargo.toml +++ b/pingora-proxy/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pingora-proxy" -version = "0.1.0" +version = "0.2.0" authors = ["Yuchen Wu "] license = "Apache-2.0" edition = "2021" @@ -18,12 +18,12 @@ name = "pingora_proxy" path = "src/lib.rs" [dependencies] -pingora-error = { version = "0.1.0", path = "../pingora-error" } -pingora-core = { version = "0.1.0", path = "../pingora-core", default-features = false } -pingora-timeout = { version = "0.1.0", path = "../pingora-timeout" } -pingora-cache = { version = "0.1.0", path = "../pingora-cache", default-features = false } +pingora-error = { version = "0.2.0", path = "../pingora-error" } +pingora-core = { version = "0.2.0", path = "../pingora-core", default-features = false } +pingora-timeout = { version = "0.2.0", path = "../pingora-timeout" } +pingora-cache = { version = "0.2.0", path = "../pingora-cache", default-features = false } tokio = { workspace = true, features = ["macros", "net"] } -pingora-http = { version = "0.1.0", path = "../pingora-http" } +pingora-http = { version = "0.2.0", path = "../pingora-http" } http = { workspace = true } futures = "0.3" bytes = { workspace = true } @@ -44,7 +44,7 @@ env_logger = "0.9" hyperlocal = "0.8" hyper = "0.14" tokio-tungstenite = "0.20.1" -pingora-load-balancing = { version = "0.1.0", path = "../pingora-load-balancing" } +pingora-load-balancing = { version = "0.2.0", path = "../pingora-load-balancing" } prometheus = "0" futures-util = "0.3" serde = { version = "1.0", features = ["derive"] } diff --git a/pingora-runtime/Cargo.toml b/pingora-runtime/Cargo.toml index 73051297..abaac73d 100644 --- a/pingora-runtime/Cargo.toml +++ b/pingora-runtime/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pingora-runtime" -version = "0.1.0" +version = "0.2.0" authors = ["Yuchen Wu "] license = "Apache-2.0" edition = "2021" diff --git a/pingora-timeout/Cargo.toml b/pingora-timeout/Cargo.toml index 1b271e45..8f62476b 100644 --- a/pingora-timeout/Cargo.toml +++ b/pingora-timeout/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pingora-timeout" -version = "0.1.0" +version = "0.2.0" authors = ["Yuchen Wu "] license = "Apache-2.0" edition = "2021" diff --git a/pingora/Cargo.toml b/pingora/Cargo.toml index 2996d573..8ae3aba5 100644 --- a/pingora/Cargo.toml +++ b/pingora/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pingora" -version = "0.1.0" +version = "0.2.0" authors = ["Yuchen Wu "] license = "Apache-2.0" edition = "2021" @@ -22,12 +22,12 @@ all-features = true rustdoc-args = ["--cfg", "docsrs"] [dependencies] -pingora-core = { version = "0.1.0", path = "../pingora-core", default-features = false } -pingora-http = { version = "0.1.0", path = "../pingora-http" } -pingora-timeout = { version = "0.1.0", path = "../pingora-timeout" } -pingora-load-balancing = { version = "0.1.0", path = "../pingora-load-balancing", optional = true, default-features = false } -pingora-proxy = { version = "0.1.0", path = "../pingora-proxy", optional = true, default-features = false } -pingora-cache = { version = "0.1.0", path = "../pingora-cache", optional = true, default-features = false } +pingora-core = { version = "0.2.0", path = "../pingora-core", default-features = false } +pingora-http = { version = "0.2.0", path = "../pingora-http" } +pingora-timeout = { version = "0.2.0", path = "../pingora-timeout" } +pingora-load-balancing = { version = "0.2.0", path = "../pingora-load-balancing", optional = true, default-features = false } +pingora-proxy = { version = "0.2.0", path = "../pingora-proxy", optional = true, default-features = false } +pingora-cache = { version = "0.2.0", path = "../pingora-cache", optional = true, default-features = false } [dev-dependencies] structopt = "0.3" diff --git a/tinyufo/Cargo.toml b/tinyufo/Cargo.toml index 4b3b2c91..0326fe54 100644 --- a/tinyufo/Cargo.toml +++ b/tinyufo/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "TinyUFO" -version = "0.1.0" +version = "0.2.0" authors = ["Yuchen Wu "] edition = "2021" license = "Apache-2.0"