pingora_proxy/
proxy_purge.rs1use super::*;
16use pingora_cache::PurgeAction;
17use pingora_core::protocols::http::error_resp;
18use std::borrow::Cow;
19
20#[derive(Debug)]
21pub enum PurgeStatus {
22 NoCache,
24 Found,
26 NotFound,
28 Error(Box<Error>),
31}
32
33fn purge_response(purge_status: &PurgeStatus) -> Cow<'static, ResponseHeader> {
36 let resp = match purge_status {
37 PurgeStatus::NoCache => &*NOT_PURGEABLE,
38 PurgeStatus::Found => &*OK,
39 PurgeStatus::NotFound => &*NOT_FOUND,
40 PurgeStatus::Error(ref _e) => &*INTERNAL_ERROR,
41 };
42 Cow::Borrowed(resp)
43}
44
45fn gen_purge_response(code: u16) -> ResponseHeader {
46 let mut resp = ResponseHeader::build(code, Some(3)).unwrap();
47 resp.insert_header(header::SERVER, &SERVER_NAME[..])
48 .unwrap();
49 resp.insert_header(header::CONTENT_LENGTH, 0).unwrap();
50 resp.insert_header(header::CACHE_CONTROL, "private, no-store")
51 .unwrap();
52 resp
54}
55
56static OK: Lazy<ResponseHeader> = Lazy::new(|| gen_purge_response(200));
57static NOT_FOUND: Lazy<ResponseHeader> = Lazy::new(|| gen_purge_response(404));
58static NOT_PURGEABLE: Lazy<ResponseHeader> = Lazy::new(|| gen_purge_response(405));
60static INTERNAL_ERROR: Lazy<ResponseHeader> = Lazy::new(|| error_resp::gen_error_response(500));
62
63impl<SV, C> HttpProxy<SV, C>
64where
65 C: custom::Connector,
66{
67 pub(crate) async fn proxy_purge(
68 &self,
69 session: &mut Session,
70 ctx: &mut SV::CTX,
71 ) -> Option<(bool, Option<Box<Error>>)>
72 where
73 SV: ProxyHttp + Send + Sync,
74 SV::CTX: Send + Sync,
75 {
76 let purge_status = if session.cache.enabled() {
77 let purged = match self.inner.purge_action(session, ctx) {
78 PurgeAction::Delete => session.cache.purge().await,
79 PurgeAction::Expire => session.cache.expire().await,
80 };
81 match purged {
82 Ok(found) => {
83 if found {
84 PurgeStatus::Found
85 } else {
86 PurgeStatus::NotFound
87 }
88 }
89 Err(e) => {
90 session.cache.disable(NoCacheReason::StorageError);
91 warn!(
92 "Fail to purge cache: {e}, {}",
93 self.inner.request_summary(session, ctx)
94 );
95 PurgeStatus::Error(e)
96 }
97 }
98 } else {
99 PurgeStatus::NoCache
101 };
102
103 let mut purge_resp = purge_response(&purge_status);
104 if let Err(e) =
105 self.inner
106 .purge_response_filter(session, ctx, purge_status, &mut purge_resp)
107 {
108 error!(
109 "Failed purge response filter: {e}, {}",
110 self.inner.request_summary(session, ctx)
111 );
112 purge_resp = Cow::Borrowed(&*INTERNAL_ERROR)
113 }
114
115 let write_result = match purge_resp {
116 Cow::Borrowed(r) => session.as_mut().write_response_header_ref(r).await,
117 Cow::Owned(r) => session.as_mut().write_response_header(Box::new(r)).await,
118 };
119 let (reuse, err) = match write_result {
120 Ok(_) => (true, None),
121 Err(e) => {
123 let e = e.into_down();
124 error!(
125 "Failed to send purge response: {e}, {}",
126 self.inner.request_summary(session, ctx)
127 );
128 (false, Some(e))
129 }
130 };
131 Some((reuse, err))
132 }
133}