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("{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;
42pub mod ja4;
43mod notification;
44mod plugin;
45mod service;
46mod ttl_lru_limit;
47mod util;
48
49pub use ctx::*;
50pub use http_header::*;
51pub use http_response::*;
52pub use ja4::{Ja4Fingerprint, is_grease};
53pub use notification::*;
54pub use pingora_limits::inflight::{Guard, Inflight};
55pub use pingora_limits::rate::Rate;
56pub use plugin::*;
57pub use service::*;
58pub use tinyufo::TinyUfo;
59pub use ttl_lru_limit::*;
60pub use util::*;
61
62/// Builds an HTTP/1 session that has read `GET <url>` with `headers`, for
63/// tests that need a real `Session` without a socket.
64#[cfg(test)]
65pub(crate) async fn new_test_session(
66    headers: &[&str],
67    url: &str,
68) -> pingora::proxy::Session {
69    let headers = headers.join("\r\n");
70    let input_header = format!("GET {url} HTTP/1.1\r\n{headers}\r\n\r\n");
71    let mock_io = tokio_test::io::Builder::new()
72        .read(input_header.as_bytes())
73        .build();
74    let mut session = pingora::proxy::Session::new_h1(Box::new(mock_io));
75    session
76        .read_request()
77        .await
78        .expect("the test request must parse");
79    session
80}
81
82#[cfg(test)]
83mod tests {
84    use super::*;
85
86    #[test]
87    fn test_new_internal_error() {
88        let err = new_internal_error(500, "Internal Server Error");
89        assert_eq!(
90            err.to_string().trim(),
91            "HTTPStatus context: Internal Server Error cause:  InternalError"
92        );
93    }
94
95    #[tokio::test]
96    async fn test_new_test_session() {
97        let session = new_test_session(
98            &["Host: github.com", "user-agent: pingap/0.1.1"],
99            "/",
100        )
101        .await;
102        assert_eq!(
103            b"pingap/0.1.1",
104            session.get_header("user-agent").unwrap().as_bytes()
105        );
106        assert_eq!("/", session.req_header().uri.path());
107    }
108}