Merge pull request #59 from svc-design/codex/implement-nginx-wasm_module-with-rust-proxy-wasm
Add Askai limiter Proxy-Wasm module
This commit is contained in:
commit
21b284fcad
24
deploy/nginx/askai-limiter.conf
Normal file
24
deploy/nginx/askai-limiter.conf
Normal file
@ -0,0 +1,24 @@
|
||||
load_module modules/ngx_http_wasm_module.so;
|
||||
|
||||
http {
|
||||
wasm {
|
||||
module limiter /etc/nginx/wasm/askai_limiter.wasm;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name cn-homepage.svc.plus;
|
||||
|
||||
ssl_certificate /etc/ssl/svc.plus.pem;
|
||||
ssl_certificate_key /etc/ssl/svc.plus.rsa.key;
|
||||
|
||||
location /api/askai {
|
||||
wasm_call limiter;
|
||||
proxy_pass http://127.0.0.1:8080/api/askai;
|
||||
}
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
}
|
||||
}
|
||||
10
wasm/askai_limiter/Cargo.toml
Normal file
10
wasm/askai_limiter/Cargo.toml
Normal file
@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "askai_limiter"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
proxy-wasm = "0.2"
|
||||
50
wasm/askai_limiter/README.md
Normal file
50
wasm/askai_limiter/README.md
Normal file
@ -0,0 +1,50 @@
|
||||
# Askai Limiter Proxy-Wasm Module
|
||||
|
||||
This module provides a simple API rate limiter for Nginx using the experimental
|
||||
`ngx_http_wasm_module` and the [proxy-wasm-rust-sdk](https://github.com/proxy-wasm/proxy-wasm-rust-sdk).
|
||||
It enforces a global daily limit of **200** requests per API endpoint.
|
||||
|
||||
## Build
|
||||
|
||||
```bash
|
||||
rustup target add wasm32-wasip1
|
||||
cargo build --release --target wasm32-wasip1
|
||||
```
|
||||
|
||||
The compiled module will be located at
|
||||
`target/wasm32-wasip1/release/askai_limiter.wasm`.
|
||||
|
||||
## Nginx Configuration
|
||||
|
||||
Example snippet that loads the compiled module and applies it to the
|
||||
`/api/askai` route:
|
||||
|
||||
```nginx
|
||||
load_module modules/ngx_http_wasm_module.so;
|
||||
|
||||
http {
|
||||
wasm {
|
||||
module limiter /etc/nginx/wasm/askai_limiter.wasm;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name cn-homepage.svc.plus;
|
||||
|
||||
ssl_certificate /etc/ssl/svc.plus.pem;
|
||||
ssl_certificate_key /etc/ssl/svc.plus.rsa.key;
|
||||
|
||||
location /api/askai {
|
||||
wasm_call limiter;
|
||||
proxy_pass http://127.0.0.1:8080/api/askai;
|
||||
}
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Requests beyond the first 200 in a single day will return HTTP 429 with the
|
||||
body `{"error":"API daily limit reached"}`.
|
||||
54
wasm/askai_limiter/src/lib.rs
Normal file
54
wasm/askai_limiter/src/lib.rs
Normal file
@ -0,0 +1,54 @@
|
||||
use proxy_wasm::traits::*;
|
||||
use proxy_wasm::types::*;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
struct AskaiLimiter;
|
||||
|
||||
impl Context for AskaiLimiter {}
|
||||
|
||||
impl HttpContext for AskaiLimiter {
|
||||
fn on_http_request_headers(&mut self, _num_headers: usize, _end_of_stream: bool) -> Action {
|
||||
// Use the day since UNIX epoch as the key
|
||||
let today = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_secs()
|
||||
/ 86_400;
|
||||
let key = format!("askai:{}", today);
|
||||
|
||||
// Read the current count from shared data
|
||||
let (data, _cas) = self.get_shared_data(&key);
|
||||
let mut count = data
|
||||
.and_then(|d| String::from_utf8(d).ok())
|
||||
.and_then(|s| s.parse::<u32>().ok())
|
||||
.unwrap_or(0);
|
||||
|
||||
if count >= 200 {
|
||||
self.send_http_response(
|
||||
429,
|
||||
vec![("Content-Type", "application/json")],
|
||||
Some(b"{\"error\":\"API daily limit reached\"}"),
|
||||
);
|
||||
return Action::Pause;
|
||||
}
|
||||
|
||||
// Increment and store the updated count
|
||||
count += 1;
|
||||
let _ = self.set_shared_data(&key, Some(count.to_string().as_bytes()), None);
|
||||
Action::Continue
|
||||
}
|
||||
}
|
||||
|
||||
impl RootContext for AskaiLimiter {
|
||||
fn on_configure(&mut self, _configuration_size: usize) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn create_http_context(&self, _context_id: u32) -> Option<Box<dyn HttpContext>> {
|
||||
Some(Box::new(AskaiLimiter))
|
||||
}
|
||||
}
|
||||
|
||||
proxy_wasm::main! {{
|
||||
proxy_wasm::set_http_context(|_, _| Box::new(AskaiLimiter));
|
||||
}}
|
||||
Loading…
Reference in New Issue
Block a user