From 1d1b237d2032baaf295cd3df7ebf50c1f5ffd0bd Mon Sep 17 00:00:00 2001 From: vicanso Date: Sat, 23 Mar 2024 08:49:20 +0800 Subject: [PATCH] refactor: improve http header performance --- docs/introduction_zh.md | 2 +- src/proxy/location.rs | 30 +++++++++--------------------- src/proxy/response_data.rs | 9 ++++----- src/proxy/server.rs | 20 ++++++++------------ src/utils/mod.rs | 32 ++++++++++++++++++++++++++++++++ 5 files changed, 54 insertions(+), 39 deletions(-) diff --git a/docs/introduction_zh.md b/docs/introduction_zh.md index 4f1cf16..33e661f 100644 --- a/docs/introduction_zh.md +++ b/docs/introduction_zh.md @@ -2,7 +2,7 @@ description: Pingap 简述 --- -Pingap提供了以toml的形式配置简单易用的反向代理,在pingora中的以下流程中接入调整,实现支持多location代理转发。特性如下: +Pingap是基于[pingora](https://github.com/cloudflare/pingora)开发的,pingora提供了各类模块便于rust开发者使用,但并不方便非rust开发者使用,因此pingap提供了以toml的形式配置简单易用的反向代理,在以下流程中接入调整,实现支持多location代理转发。特性如下: - 可通过请求的路径与域名筛选对应的location - 支持HTTP1与HTTP2 diff --git a/src/proxy/location.rs b/src/proxy/location.rs index 706a5f8..43394c3 100644 --- a/src/proxy/location.rs +++ b/src/proxy/location.rs @@ -1,7 +1,6 @@ use super::Upstream; use crate::{config::LocationConf, utils}; -use bytes::Bytes; -use http::HeaderValue; +use http::{HeaderName, HeaderValue}; use regex::Regex; use snafu::{ResultExt, Snafu}; use std::sync::Arc; @@ -11,11 +10,6 @@ use substring::Substring; pub enum Error { #[snafu(display("Invalid error {message}"))] Invalid { message: String }, - #[snafu(display("Invalid header value {source}, {value}"))] - InvalidHeaderValue { - value: String, - source: http::header::InvalidHeaderValue, - }, #[snafu(display("Regex {source}, {value}"))] Regex { value: String, source: regex::Error }, } @@ -64,22 +58,16 @@ pub struct Location { path_selector: PathSelector, host: String, reg_rewrite: Option<(Regex, String)>, - // TODO better performance for http header - headers: Option>, - proxy_headers: Option>, + headers: Option>, + proxy_headers: Option>, pub upstream: Arc, } -fn convert_headers(values: &Option>) -> Result>> { +fn convert_headers(values: &Option>) -> Result>> { if let Some(header_values) = values { - let mut arr = vec![]; - for item in header_values { - if let Some([k, v]) = utils::split_to_two_trim(item, ":") { - let _ = - HeaderValue::from_str(&v).context(InvalidHeaderValueSnafu { value: v.clone() }); - arr.push((Bytes::from(k), Bytes::from(v))); - } - } + let arr = utils::convert_headers(header_values).map_err(|err| Error::Invalid { + message: err.to_string(), + })?; Ok(Some(arr)) } else { Ok(None) @@ -146,11 +134,11 @@ impl Location { None } #[inline] - pub fn get_proxy_headers(&self) -> Option> { + pub fn get_proxy_headers(&self) -> Option> { self.proxy_headers.clone() } #[inline] - pub fn get_header(&self) -> Option> { + pub fn get_header(&self) -> Option> { self.headers.clone() } } diff --git a/src/proxy/response_data.rs b/src/proxy/response_data.rs index cf4dac4..321ec7a 100644 --- a/src/proxy/response_data.rs +++ b/src/proxy/response_data.rs @@ -1,5 +1,5 @@ use bytes::Bytes; -use http::StatusCode; +use http::{HeaderName, HeaderValue, StatusCode}; use pingora::{http::ResponseHeader, proxy::Session}; #[derive(Default)] @@ -8,8 +8,7 @@ pub struct ResponseData { pub body: Bytes, pub max_age: Option, pub created_at: Option, - // TODO better performance for http header - pub headers: Option>, + pub headers: Option>, } impl ResponseData { @@ -17,8 +16,8 @@ impl ResponseData { let mut resp = ResponseHeader::build(self.status, Some(4))?; resp.insert_header(http::header::CONTENT_LENGTH, self.body.len().to_string())?; if let Some(headers) = &self.headers { - for (name, value) in headers.iter() { - resp.insert_header(name.to_owned(), value.to_vec())?; + for (name, value) in headers { + resp.insert_header(name.to_owned(), value)?; } } let buf = self.body.clone(); diff --git a/src/proxy/server.rs b/src/proxy/server.rs index 9614fa8..3fab131 100644 --- a/src/proxy/server.rs +++ b/src/proxy/server.rs @@ -279,20 +279,16 @@ impl Server { hostname: HOST_NAME.to_string(), }) .unwrap_or_default(); + let headers = utils::convert_headers(&[ + "Content-Type: application/json; charset=utf-8".to_string(), + "Cache-Control: private, no-store".to_string(), + ]) + .unwrap_or_default(); ResponseData { status: StatusCode::OK, body: Bytes::from(buf), - headers: Some(vec![ - ( - Bytes::from("Content-Type"), - Bytes::from("application/json; charset=utf-8"), - ), - ( - Bytes::from("Cache-Control"), - Bytes::from("private, no-store"), - ), - ]), + headers: Some(headers), ..Default::default() } } @@ -359,7 +355,7 @@ impl ProxyHttp for Server { if let Some(arr) = lo.get_proxy_headers() { for (k, v) in arr { // v validate for HeaderValue, so always no error - let _ = header.insert_header(k, v.to_vec()); + let _ = header.insert_header(k, v); } } let peer = lo @@ -398,7 +394,7 @@ impl ProxyHttp for Server { if let Some(arr) = lo.get_header() { for (k, v) in arr { // v validate for HeaderValue, so always no error - let _ = upstream_response.insert_header(k, v.to_vec()); + let _ = upstream_response.insert_header(k, v); } } } diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 00c3398..17db13c 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1,3 +1,21 @@ +use http::{HeaderName, HeaderValue}; +use snafu::{ResultExt, Snafu}; +use std::str::FromStr; + +#[derive(Debug, Snafu)] +pub enum Error { + InvalidHeaderValue { + value: String, + source: http::header::InvalidHeaderValue, + }, + #[snafu(display("Invalid header name {source}, {value}"))] + InvalidHeaderName { + value: String, + source: http::header::InvalidHeaderName, + }, +} +type Result = std::result::Result; + pub fn split_to_two_trim(value: &str, pat: &str) -> Option<[String; 2]> { let arr: Vec<&str> = value.split(pat).collect(); if arr.len() < 2 { @@ -8,6 +26,20 @@ pub fn split_to_two_trim(value: &str, pat: &str) -> Option<[String; 2]> { Some([arr[0].trim().to_string(), value]) } +pub fn convert_headers(header_values: &[String]) -> Result> { + let mut arr = vec![]; + for item in header_values { + if let Some([k, v]) = split_to_two_trim(item, ":") { + let name = + HeaderName::from_str(&k).context(InvalidHeaderNameSnafu { value: k.clone() })?; + let value = + HeaderValue::from_str(&v).context(InvalidHeaderValueSnafu { value: v.clone() })?; + arr.push((name, value)); + } + } + Ok(arr) +} + const NAME: &str = env!("CARGO_PKG_NAME"); const VERSION: &str = env!("CARGO_PKG_VERSION");