Skip to main content

pingap_core/
lib.rs

1// Copyright 2024-2025 Tree xie.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use snafu::Snafu;
16
17static LOG_TARGET: &str = "pingap::core";
18
19#[derive(Debug, Snafu)]
20pub enum Error {
21    #[snafu(display("invalid error, {message}"))]
22    Invalid { message: String },
23    #[snafu(display("plugin {category} not found"))]
24    NotFound { category: String },
25}
26
27/// Creates a new internal error
28pub fn new_internal_error(
29    status: u16,
30    message: impl ToString,
31) -> pingora::BError {
32    pingora::Error::because(
33        pingora::ErrorType::HTTPStatus(status),
34        message.to_string(),
35        pingora::Error::new(pingora::ErrorType::InternalError),
36    )
37}
38
39mod ctx;
40mod http_header;
41mod http_response;
42mod notification;
43mod plugin;
44mod service;
45mod ttl_lru_limit;
46mod util;
47
48pub use ctx::*;
49pub use http_header::*;
50pub use http_response::*;
51pub use notification::*;
52pub use pingora_limits::inflight::*;
53pub use pingora_limits::rate::*;
54pub use plugin::*;
55pub use service::*;
56pub use tinyufo::TinyUfo;
57pub use ttl_lru_limit::*;
58pub use util::*;
59
60#[allow(dead_code)]
61#[cfg(test)]
62fn new_get_session(
63    headers: Vec<String>,
64    url: String,
65) -> std::sync::mpsc::Receiver<Option<pingora::proxy::Session>> {
66    let (tx, rx) = std::sync::mpsc::sync_channel(0);
67    std::thread::spawn(move || {
68        match tokio::runtime::Runtime::new() {
69            Ok(rt) => {
70                let send = async move {
71                    let headers = headers.join("\r\n");
72                    let input_header =
73                        format!("GET {url} HTTP/1.1\r\n{headers}\r\n\r\n");
74                    let mock_io = tokio_test::io::Builder::new()
75                        .read(input_header.as_bytes())
76                        .build();
77
78                    let mut session =
79                        pingora::proxy::Session::new_h1(Box::new(mock_io));
80                    session.read_request().await.unwrap();
81                    let _ = tx.send(Some(session));
82                };
83                rt.block_on(send);
84            },
85            Err(_e) => {
86                let _ = tx.send(None);
87            },
88        };
89    });
90    rx
91}
92
93#[cfg(test)]
94mod tests {
95    use super::*;
96
97    #[test]
98    fn test_new_internal_error() {
99        let err = new_internal_error(500, "Internal Server Error");
100        assert_eq!(
101            err.to_string().trim(),
102            "HTTPStatus context: Internal Server Error cause:  InternalError"
103        );
104    }
105
106    #[test]
107    fn test_new_get_session() {
108        let session = new_get_session(
109            vec!["user-agent: pingap/0.1.1".to_string()],
110            "https://github.com".to_string(),
111        )
112        .recv()
113        .unwrap()
114        .unwrap();
115        assert_eq!(
116            b"pingap/0.1.1",
117            session.get_header("user-agent").unwrap().as_bytes()
118        );
119    }
120}