Skip to content

Commit

Permalink
feat: support content-disposition for static serve
Browse files Browse the repository at this point in the history
  • Loading branch information
vicanso committed May 14, 2024
1 parent f83ae26 commit d71257c
Show file tree
Hide file tree
Showing 24 changed files with 416 additions and 129 deletions.
39 changes: 30 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,28 +45,49 @@ All toml configurations are as follows [pingap.toml](./conf/pingap.toml).

```mermaid
graph TD;
start("New Request")-->server("HTTP Server");
server("HTTP Server");
locationA("Location A");
locationB("Location B");
locationPluginListA("Proxy Plugin List A");
locationPluginListB("Proxy Plugin List B");
upstreamA1("Upstream A1");
upstreamA2("Upstream A2");
upstreamB1("Upstream B1");
upstreamB2("Upstream B2");
locationResponsePluginListA("Response Plugin List A");
locationResponsePluginListB("Response Plugin List B");
server -- "host:HostA, Path:/api/*" --> locationA("Location A")
start("New Request") --> server
server -- "Path:/rest/*"--> locationB("Location B")
server -- "host:HostA, Path:/api/*" --> locationA
locationA -- "Exec Plugins" --> locationPluginListA("Plugin List A")
server -- "Path:/rest/*"--> locationB
locationB -- "Exec Plugins" --> locationPluginListB("Plugin List B")
locationA -- "Exec Proxy Plugins" --> locationPluginListA
locationPluginListA -- "proxy pass: 10.0.0.1:8001" --> upstreamA1("Upstream A1") --> response
locationB -- "Exec Proxy Plugins" --> locationPluginListB
locationPluginListA -- "proxy pass: 10.0.0.2:8001" --> upstreamA2("Upstream A2") --> response
locationPluginListA -- "proxy pass: 10.0.0.1:8001" --> upstreamA1
locationPluginListA -- "proxy pass: 10.0.0.2:8001" --> upstreamA2
locationPluginListA -- "done" --> response
locationPluginListB -- "proxy pass: 10.0.0.1:8002" --> upstreamB1("Upstream B1") --> response
locationPluginListB -- "proxy pass: 10.0.0.1:8002" --> upstreamB1
locationPluginListB -- "proxy pass: 10.0.0.2:8002" --> upstreamB2("Upstream B2") --> response
locationPluginListB -- "proxy pass: 10.0.0.2:8002" --> upstreamB2
locationPluginListB -- "done" --> response
upstreamA1 -- "Exec Response Plugins" --> locationResponsePluginListA
upstreamA2 -- "Exec Response Plugins" --> locationResponsePluginListA
upstreamB1 -- "Exec Response Plugins" --> locationResponsePluginListB
upstreamB2 -- "Exec Response Plugins" --> locationResponsePluginListB
locationResponsePluginListA --> response
locationResponsePluginListB --> response
response("HTTP Response") --> stop("Logging");
```

Expand Down
3 changes: 2 additions & 1 deletion TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
- [ ] fix not_before not_after of cert
- [ ] http headers plugin
- [ ] client body size limit plugin
- [ ] support more limit plugin
- [x] support `Content-Disposition` for directory static serve plugin
- [x] support more limit plugin
- [x] how to use proxy plugin
- [x] server listen multi address
- [x] show name of web view editor
Expand Down
39 changes: 30 additions & 9 deletions docs/phase_chart_zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,49 @@ description: Pingap 处理流程

```mermaid
graph TD;
start("新的请求")-->server("HTTP服务");
server("HTTP服务");
locationA("Location A");
locationB("Location B");
locationPluginListA("转发插件列表A");
locationPluginListB("转发插件列表B");
upstreamA1("上游服务A1");
upstreamA2("上游服务A2");
upstreamB1("上游服务B1");
upstreamB2("上游服务B2");
locationResponsePluginListA("响应插件列表A");
locationResponsePluginListB("响应插件列表B");
server -- "host:HostA, Path:/api/*" --> locationA("Location A")
start("新的请求") --> server
server -- "Path:/rest/*"--> locationB("Location B")
server -- "host:HostA, Path:/api/*" --> locationA
locationA -- "顺序执行各插件" --> locationPluginListA("插件列表A")
server -- "Path:/rest/*"--> locationB
locationB -- "顺序执行各插件" --> locationPluginListB("插件列表B")
locationA -- "顺序执行各转发插件" --> locationPluginListA
locationPluginListA -- "转发至: 10.0.0.1:8001" --> upstreamA1("上游服务A1") --> response
locationB -- "顺序执行各转发插件" --> locationPluginListB
locationPluginListA -- "转发至: 10.0.0.2:8001" --> upstreamA2("上游服务A2") --> response
locationPluginListA -- "转发至: 10.0.0.1:8001" --> upstreamA1
locationPluginListA -- "转发至: 10.0.0.2:8001" --> upstreamA2
locationPluginListA -- "处理完成" --> response
locationPluginListB -- "转发至: 10.0.0.1:8002" --> upstreamB1("上游服务B1") --> response
locationPluginListB -- "转发至: 10.0.0.1:8002" --> upstreamB1
locationPluginListB -- "转发至: 10.0.0.2:8002" --> upstreamB2("上游服务B2") --> response
locationPluginListB -- "转发至: 10.0.0.2:8002" --> upstreamB2
locationPluginListB -- "处理完成" --> response
upstreamA1 -- "顺序执行响应插件" --> locationResponsePluginListA
upstreamA2 -- "顺序执行响应插件" --> locationResponsePluginListA
upstreamB1 -- "顺序执行响应插件" --> locationResponsePluginListB
upstreamB2 -- "顺序执行响应插件" --> locationResponsePluginListB
locationResponsePluginListA --> response
locationResponsePluginListB --> response
response("HTTP响应") --> stop("日志记录");
```

Expand Down
15 changes: 12 additions & 3 deletions docs/plugin_zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ description: Pingap 插件体系

Pingap中通过Locaton添加各种插件支持更多的应用场景,如鉴权、流控、设置响应头等场景。

# 插件执行时点

现支持将插件添加到以下各阶段时点中执行:

- `Request`: 请求的最开始阶段,适用于针对一些权限类的拦截等处理
- `ProxyUpstream`: 请求转发至上流节点之前,因为此流程是在读取缓存之后,因此若不希望针对缓存前限制,但转发至上游前限制的可配置为此阶段。如限制IP访问频繁,但允许高并发读取缓存数据。
- `UpstreamResponse`: 上游数据响应之后,用于针对上游响应数据做调整时使用。

# 转发插件

转发插件是在请求转发至upstream之前执行,支持在`request_filter``proxy_upstream_filter`阶段执行,均为转发到上游节点前的处理。下面介绍一下`proxy plugin`的具体逻辑,trait如下:
Expand Down Expand Up @@ -110,11 +118,12 @@ category = "compression"
静态文件目录服务,为指定目录提供静态文件服务,需要注意query部分的参数均为可选值,说明如下:

- `chunk_size`: Http chunk的大小,默认为`8192`
- `max_age`: 设置http响应的的缓存时间,默认无。此值对于`text/html`无效,html均设置为不可缓存
- `private`: 缓存是否设置为`private`,默认为`public`
- `max_age`: 设置http响应的的缓存时间,默认无。此值对于`text/html`无效,html均设置为不可缓存。如设置为`1h`表示缓存有效期1小时
- `private`: 缓存是否设置为`private`,默认为`public`,,query中只要有`private`即可
- `index`: 设置默认的index文件,默认为`index.html`
- `charset`: 指定charset类型,默认无
- `autoindex`: 是否允许目录以浏览形式展示
- `autoindex`: 是否允许目录以浏览形式展示,query中只要有`autoindex`即可
- `download`: 是否支持下载,指定该参数后响应时会设置响应头`Content-Disposition`,query中只要有`download`即可

```toml
[plugins.downloadsServe]
Expand Down
39 changes: 22 additions & 17 deletions src/plugin/admin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use super::{ProxyPlugin, Result};
use super::{Error, ProxyPlugin, Result};
use crate::config::{
self, save_config, BasicConf, LocationConf, PluginCategory, PluginConf, PluginStep, ServerConf,
UpstreamConf,
Expand Down Expand Up @@ -93,22 +93,6 @@ pub struct AdminServe {
pub proxy_step: PluginStep,
ip_fail_limit: TtlLruLimit,
}
impl AdminServe {
pub fn new(value: &str, proxy_step: PluginStep) -> Result<Self> {
debug!("new admin server proxy plugin, {value}, {proxy_step:?}");
let arr: Vec<&str> = value.split(' ').collect();
let mut authorization = "".to_string();
if arr.len() >= 2 {
authorization = arr[1].trim().to_string();
}
Ok(Self {
path: arr[0].trim().to_string(),
proxy_step,
authorization,
ip_fail_limit: TtlLruLimit::new(512, Duration::from_secs(5 * 60), 5),
})
}
}

#[derive(Serialize, Deserialize)]
struct ErrorResponse {
Expand All @@ -125,6 +109,27 @@ struct BasicInfo {
}

impl AdminServe {
pub fn new(value: &str, proxy_step: PluginStep) -> Result<Self> {
debug!("new admin server proxy plugin, {value}, {proxy_step:?}");
if ![PluginStep::Request, PluginStep::ProxyUpstream].contains(&proxy_step) {
return Err(Error::Invalid {
category: PluginCategory::Admin.to_string(),
message: "Admin serve plugin should be executed at request or proxy upstream step"
.to_string(),
});
}
let arr: Vec<&str> = value.split(' ').collect();
let mut authorization = "".to_string();
if arr.len() >= 2 {
authorization = arr[1].trim().to_string();
}
Ok(Self {
path: arr[0].trim().to_string(),
proxy_step,
authorization,
ip_fail_limit: TtlLruLimit::new(512, Duration::from_secs(5 * 60), 5),
})
}
fn auth_validate(&self, req_header: &RequestHeader) -> bool {
if self.authorization.is_empty() {
return true;
Expand Down
14 changes: 11 additions & 3 deletions src/plugin/basic_auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,19 @@ pub struct BasicAuth {
impl BasicAuth {
pub fn new(value: &str, proxy_step: PluginStep) -> Result<Self> {
debug!("new basic auth proxy plugin, {value}, {proxy_step:?}");
if ![PluginStep::Request, PluginStep::ProxyUpstream].contains(&proxy_step) {
return Err(Error::Invalid {
category: PluginCategory::BasicAuth.to_string(),
message: "Basic auth plugin should be executed at request or proxy upstream step"
.to_string(),
});
}
let mut authorizations = vec![];
for item in value.split(' ') {
let _ = STANDARD
.decode(item)
.map_err(|e| Error::Base64Decode { source: e })?;
let _ = STANDARD.decode(item).map_err(|e| Error::Base64Decode {
category: PluginCategory::BasicAuth.to_string(),
source: e,
})?;

authorizations.push(format!("Basic {item}").as_bytes().to_owned());
}
Expand Down
7 changes: 7 additions & 0 deletions src/plugin/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,14 @@ pub struct Cache {
impl Cache {
pub fn new(value: &str, proxy_step: PluginStep) -> Result<Self> {
debug!("new cache storage proxy plugin, {value}, {proxy_step:?}");
if proxy_step != PluginStep::Request {
return Err(Error::Invalid {
category: PluginCategory::Cache.to_string(),
message: "Cache plugin should be executed at request step".to_string(),
});
}
let url_info = Url::parse(value).map_err(|e| Error::Invalid {
category: PluginCategory::Cache.to_string(),
message: e.to_string(),
})?;
let mut lock = 0;
Expand Down
14 changes: 11 additions & 3 deletions src/plugin/compression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,24 @@ pub struct Compression {
impl Compression {
pub fn new(value: &str, proxy_step: PluginStep) -> Result<Self> {
debug!("new compresson proxy plugin, {value}, {proxy_step:?}");
if ![PluginStep::Request, PluginStep::ProxyUpstream].contains(&proxy_step) {
return Err(Error::Invalid {
category: PluginCategory::Compression.to_string(),
message: "Compression plugin should be executed at request or proxy upstream step"
.to_string(),
});
}

let mut levels: [u32; 3] = [0, 0, 0];
let mut support_compression = false;
for (index, item) in value.split(' ').enumerate() {
if index >= levels.len() {
break;
}
let level = item
.parse::<u32>()
.map_err(|e| Error::ParseInt { source: e })?;
let level = item.parse::<u32>().map_err(|e| Error::ParseInt {
category: PluginCategory::Compression.to_string(),
source: e,
})?;
if level > 0 {
support_compression = true;
levels[index] = level;
Expand Down
Loading

0 comments on commit d71257c

Please sign in to comment.