pingora_core/protocols/http/
client.rs1use bytes::Bytes;
16use pingora_error::Result;
17use pingora_http::{RequestHeader, ResponseHeader};
18use std::time::Duration;
19
20use super::v2::client::Http2Session;
21use super::{custom::client::Session, v1::client::HttpSession as Http1Session};
22use crate::protocols::{Digest, SocketAddr, Stream};
23
24pub enum HttpSession<S = ()> {
26 H1(Http1Session),
27 H2(Http2Session),
28 Custom(S),
29}
30
31impl<S: Session> HttpSession<S> {
32 pub fn as_http1(&self) -> Option<&Http1Session> {
33 match self {
34 Self::H1(s) => Some(s),
35 Self::H2(_) => None,
36 Self::Custom(_) => None,
37 }
38 }
39
40 pub fn as_http2(&self) -> Option<&Http2Session> {
41 match self {
42 Self::H1(_) => None,
43 Self::H2(s) => Some(s),
44 Self::Custom(_) => None,
45 }
46 }
47
48 pub fn as_custom(&self) -> Option<&S> {
49 match self {
50 Self::H1(_) => None,
51 Self::H2(_) => None,
52 Self::Custom(c) => Some(c),
53 }
54 }
55
56 pub fn as_custom_mut(&mut self) -> Option<&mut S> {
57 match self {
58 Self::H1(_) => None,
59 Self::H2(_) => None,
60 Self::Custom(c) => Some(c),
61 }
62 }
63
64 pub async fn write_request_header(&mut self, req: Box<RequestHeader>) -> Result<()> {
68 match self {
69 HttpSession::H1(h1) => {
70 h1.write_request_header(req).await?;
71 Ok(())
72 }
73 HttpSession::H2(h2) => h2.write_request_header(req, false),
74 HttpSession::Custom(c) => c.write_request_header(req, false).await,
75 }
76 }
77
78 pub async fn write_request_body(&mut self, data: Bytes, end: bool) -> Result<()> {
80 match self {
81 HttpSession::H1(h1) => {
82 h1.write_body(&data).await?;
84 Ok(())
85 }
86 HttpSession::H2(h2) => h2.write_request_body(data, end).await,
87 HttpSession::Custom(c) => c.write_request_body(data, end).await,
88 }
89 }
90
91 pub async fn finish_request_body(&mut self) -> Result<()> {
93 match self {
94 HttpSession::H1(h1) => {
95 h1.finish_body().await?;
96 Ok(())
97 }
98 HttpSession::H2(h2) => h2.finish_request_body(),
99 HttpSession::Custom(c) => c.finish_request_body().await,
100 }
101 }
102
103 pub fn set_read_timeout(&mut self, timeout: Option<Duration>) {
107 match self {
108 HttpSession::H1(h1) => h1.read_timeout = timeout,
109 HttpSession::H2(h2) => h2.read_timeout = timeout,
110 HttpSession::Custom(c) => c.set_read_timeout(timeout),
111 }
112 }
113
114 pub fn set_write_timeout(&mut self, timeout: Option<Duration>) {
118 match self {
119 HttpSession::H1(h1) => h1.write_timeout = timeout,
120 HttpSession::H2(h2) => h2.write_timeout = timeout,
121 HttpSession::Custom(c) => c.set_write_timeout(timeout),
122 }
123 }
124
125 pub async fn read_response_header(&mut self) -> Result<()> {
129 match self {
130 HttpSession::H1(h1) => {
131 h1.read_response().await?;
132 Ok(())
133 }
134 HttpSession::H2(h2) => h2.read_response_header().await,
135 HttpSession::Custom(c) => c.read_response_header().await,
136 }
137 }
138
139 pub async fn read_response_body(&mut self) -> Result<Option<Bytes>> {
143 match self {
144 HttpSession::H1(h1) => h1.read_body_bytes().await,
145 HttpSession::H2(h2) => h2.read_response_body().await,
146 HttpSession::Custom(c) => c.read_response_body().await,
147 }
148 }
149
150 pub fn response_done(&mut self) -> bool {
152 match self {
153 HttpSession::H1(h1) => h1.is_body_done(),
154 HttpSession::H2(h2) => h2.response_finished(),
155 HttpSession::Custom(c) => c.response_finished(),
156 }
157 }
158
159 pub async fn shutdown(&mut self) {
167 match self {
168 Self::H1(s) => s.shutdown().await,
169 Self::H2(s) => s.shutdown(),
170 Self::Custom(c) => c.abandon("shutdown").await,
171 }
172 }
173
174 pub fn response_header(&self) -> Option<&ResponseHeader> {
178 match self {
179 Self::H1(s) => s.resp_header(),
180 Self::H2(s) => s.response_header(),
181 Self::Custom(c) => c.response_header(),
182 }
183 }
184
185 pub fn digest(&self) -> Option<&Digest> {
190 match self {
191 Self::H1(s) => Some(s.digest()),
192 Self::H2(s) => s.digest(),
193 Self::Custom(c) => c.digest(),
194 }
195 }
196
197 pub fn digest_mut(&mut self) -> Option<&mut Digest> {
201 match self {
202 Self::H1(s) => Some(s.digest_mut()),
203 Self::H2(s) => s.digest_mut(),
204 Self::Custom(s) => s.digest_mut(),
205 }
206 }
207
208 pub fn server_addr(&self) -> Option<&SocketAddr> {
210 match self {
211 Self::H1(s) => s.server_addr(),
212 Self::H2(s) => s.server_addr(),
213 Self::Custom(s) => s.server_addr(),
214 }
215 }
216
217 pub fn client_addr(&self) -> Option<&SocketAddr> {
219 match self {
220 Self::H1(s) => s.client_addr(),
221 Self::H2(s) => s.client_addr(),
222 Self::Custom(s) => s.client_addr(),
223 }
224 }
225
226 pub fn stream(&self) -> Option<&Stream> {
229 match self {
230 Self::H1(s) => Some(s.stream()),
231 Self::H2(_) => None,
232 Self::Custom(_) => None,
233 }
234 }
235}
236
237#[cfg(test)]
238mod tests {
239 use super::{HttpSession, Session};
240 use crate::protocols::http::custom::{BodyWrite, CustomMessageWrite};
241 use crate::protocols::{Digest, SocketAddr, UniqueIDType};
242 use async_trait::async_trait;
243 use bytes::Bytes;
244 use futures::Stream;
245 use http::HeaderMap;
246 use pingora_error::Result;
247 use pingora_http::{RequestHeader, ResponseHeader};
248 use std::sync::{Arc, Mutex};
249 use std::time::Duration;
250
251 struct ShutdownRecordingCustom {
255 shutdown_calls: Arc<Mutex<Vec<String>>>,
256 }
257
258 #[async_trait]
259 impl Session for ShutdownRecordingCustom {
260 async fn shutdown(&mut self, code: u32, ctx: &str) {
261 self.shutdown_calls
262 .lock()
263 .unwrap()
264 .push(format!("shutdown({code}, {ctx})"));
265 }
266
267 async fn abandon(&mut self, ctx: &str) {
268 self.shutdown_calls
269 .lock()
270 .unwrap()
271 .push(format!("abandon({ctx})"));
272 }
273
274 async fn write_request_header(
275 &mut self,
276 _req: Box<RequestHeader>,
277 _end: bool,
278 ) -> Result<()> {
279 unreachable!("not used by the shutdown dispatch test")
280 }
281
282 async fn write_request_body(&mut self, _data: Bytes, _end: bool) -> Result<()> {
283 unreachable!("not used by the shutdown dispatch test")
284 }
285
286 async fn finish_request_body(&mut self) -> Result<()> {
287 unreachable!("not used by the shutdown dispatch test")
288 }
289
290 fn set_read_timeout(&mut self, _timeout: Option<Duration>) {
291 unreachable!("not used by the shutdown dispatch test")
292 }
293
294 fn set_write_timeout(&mut self, _timeout: Option<Duration>) {
295 unreachable!("not used by the shutdown dispatch test")
296 }
297
298 async fn read_response_header(&mut self) -> Result<()> {
299 unreachable!("not used by the shutdown dispatch test")
300 }
301
302 async fn read_response_body(&mut self) -> Result<Option<Bytes>> {
303 unreachable!("not used by the shutdown dispatch test")
304 }
305
306 fn response_finished(&self) -> bool {
307 unreachable!("not used by the shutdown dispatch test")
308 }
309
310 fn response_header(&self) -> Option<&ResponseHeader> {
311 unreachable!("not used by the shutdown dispatch test")
312 }
313
314 fn was_upgraded(&self) -> bool {
315 unreachable!("not used by the shutdown dispatch test")
316 }
317
318 fn digest(&self) -> Option<&Digest> {
319 unreachable!("not used by the shutdown dispatch test")
320 }
321
322 fn digest_mut(&mut self) -> Option<&mut Digest> {
323 unreachable!("not used by the shutdown dispatch test")
324 }
325
326 fn server_addr(&self) -> Option<&SocketAddr> {
327 unreachable!("not used by the shutdown dispatch test")
328 }
329
330 fn client_addr(&self) -> Option<&SocketAddr> {
331 unreachable!("not used by the shutdown dispatch test")
332 }
333
334 async fn read_trailers(&mut self) -> Result<Option<HeaderMap>> {
335 unreachable!("not used by the shutdown dispatch test")
336 }
337
338 fn fd(&self) -> UniqueIDType {
339 unreachable!("not used by the shutdown dispatch test")
340 }
341
342 async fn check_response_end_or_error(&mut self, _headers: bool) -> Result<bool> {
343 unreachable!("not used by the shutdown dispatch test")
344 }
345
346 fn take_request_body_writer(&mut self) -> Option<Box<dyn BodyWrite>> {
347 unreachable!("not used by the shutdown dispatch test")
348 }
349
350 async fn finish_custom(&mut self) -> Result<()> {
351 unreachable!("not used by the shutdown dispatch test")
352 }
353
354 fn take_custom_message_reader(
355 &mut self,
356 ) -> Option<Box<dyn Stream<Item = Result<Bytes>> + Unpin + Send + Sync + 'static>> {
357 unreachable!("not used by the shutdown dispatch test")
358 }
359
360 async fn drain_custom_messages(&mut self) -> Result<()> {
361 unreachable!("not used by the shutdown dispatch test")
362 }
363
364 fn take_custom_message_writer(&mut self) -> Option<Box<dyn CustomMessageWrite>> {
365 unreachable!("not used by the shutdown dispatch test")
366 }
367 }
368
369 #[tokio::test]
377 async fn custom_session_shutdown_signals_an_incomplete_message() {
378 let shutdown_calls = Arc::new(Mutex::new(Vec::new()));
379 let mut session = HttpSession::Custom(ShutdownRecordingCustom {
380 shutdown_calls: shutdown_calls.clone(),
381 });
382
383 session.shutdown().await;
384
385 assert_eq!(*shutdown_calls.lock().unwrap(), ["abandon(shutdown)"]);
386 }
387}