Skip to main content

relay_core_api/
rule.rs

1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3
4/// A self-contained unit of logic with explicit stage, priority, and flow control.
5#[derive(Debug, Clone, Serialize, Deserialize)]
6pub struct Rule {
7    /// Unique identifier
8    pub id: String,
9    /// Human-readable name
10    pub name: String,
11    /// Whether the rule is enabled
12    pub active: bool,
13
14    /// [P0] Execution Stage: Determines *when* this rule is evaluated.
15    pub stage: RuleStage,
16
17    /// Execution priority (Higher value = Earlier execution)
18    /// Default: 0
19    #[serde(default)]
20    pub priority: i32,
21
22    /// [P0] Flow Control: Replaces `stop_on_match`.
23    /// Determines if subsequent rules in the same stage should be skipped.
24    pub termination: RuleTermination,
25
26    /// The condition to match traffic
27    pub filter: Filter,
28
29    /// The actions to execute when matched
30    pub actions: Vec<Action>,
31
32    /// [P1] Performance Constraints
33    pub constraints: Option<RuleConstraints>,
34}
35
36#[derive(Debug, Clone, Serialize, Deserialize)]
37pub struct RuleGroup {
38    pub id: String,
39    pub name: String,
40    pub active: bool,
41    pub rules: Vec<Rule>,
42}
43
44#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
45pub enum RuleStage {
46    /// L3/L4: Connection establishment (IP/Port filtering)
47    Connect,
48    /// L7: HTTP Request Headers available
49    RequestHeaders,
50    /// L7: HTTP Request Body available (Streaming or Buffered)
51    RequestBody,
52    /// L7: HTTP Response Headers available
53    ResponseHeaders,
54    /// L7: HTTP Response Body available (Streaming or Buffered)
55    ResponseBody,
56    /// WebSocket Message frame
57    WebSocketMessage,
58}
59
60#[derive(Debug, Clone, Serialize, Deserialize)]
61pub enum RuleTermination {
62    /// Continue to the next rule in this stage (Default)
63    Continue,
64    /// Stop processing subsequent rules in this stage
65    Stop,
66}
67
68#[derive(Debug, Clone, Serialize, Deserialize)]
69pub struct RuleConstraints {
70    /// Max execution time in ms (soft limit)
71    pub timeout_ms: Option<u64>,
72}
73
74// --- Filters ---
75
76#[derive(Debug, Clone, Serialize, Deserialize)]
77#[serde(tag = "type", content = "config")]
78pub enum Filter {
79    /// Matches everything (useful for global rules)
80    All,
81
82    // --- L3/L4 Network Filters (Valid in Connect stage) ---
83    /// Matches by Source IP (CIDR support)
84    SrcIp(String), // e.g., "192.168.1.0/24"
85    /// Matches by Destination Port
86    DstPort(u16),
87    /// Matches by Protocol (TCP/UDP)
88    Protocol(String),
89    /// Matches if connection is in transparent proxy mode
90    TransparentMode(bool),
91
92    // --- L7 HTTP Filters (Valid in Request/Response stages) ---
93    /// Matches by Full URL
94    Url(StringMatcher),
95    /// Matches by Host/Domain
96    Host(StringMatcher),
97    /// Matches by URL Path
98    Path(StringMatcher),
99    /// Matches by HTTP Method (GET, POST, etc.)
100    Method(StringMatcher),
101    /// Matches by Request Header presence or value
102    RequestHeader {
103        name: String,
104        value: Option<StringMatcher>,
105    },
106    /// Matches by Response Header presence or value
107    ResponseHeader {
108        name: String,
109        value: Option<StringMatcher>,
110    },
111    /// Matches by Response Status Code
112    StatusCode(u16),
113    /// Matches by Response Body Content (Valid only in ResponseBody stage)
114    ResponseBody(StringMatcher),
115    /// Matches by WebSocket Message Content (Valid only in WebSocketMessage stage)
116    WebSocketMessage(StringMatcher),
117
118    // --- Logical Compositors ---
119    And(Vec<Filter>),
120    Or(Vec<Filter>),
121    Not(Box<Filter>),
122}
123
124#[derive(Debug, Clone, Serialize, Deserialize)]
125#[serde(tag = "mode", content = "value")]
126pub enum StringMatcher {
127    Exact(String),
128    Contains(String),
129    Prefix(String),
130    Suffix(String),
131    Regex(String),
132    Glob(String),
133}
134
135// --- Actions ---
136
137#[derive(Debug, Clone, Serialize, Deserialize)]
138#[serde(tag = "type", content = "config")]
139pub enum Action {
140    // === Tier 1: Universal Control Actions ===
141    /// Drop the connection immediately
142    Drop,
143    /// Abort the connection (RST)
144    Abort,
145    /// Delay execution (Latency Simulation)
146    Delay { ms: u64 },
147    /// Throttle bandwidth
148    Throttle { kbps: u64 },
149    /// Tag for subsequent processing/stats
150    Tag { key: String, value: String },
151    /// Pause the flow for manual inspection/intervention (e.g., in GUI)
152    Inspect,
153    /// Set a variable for use in subsequent actions (Fluxzy-style)
154    SetVariable { name: String, value: String },
155    /// Rate limit traffic based on a key
156    RateLimit {
157        /// The key to limit by (e.g., "ip", "host", "path", or a template like "{{ip}}:{{path}}")
158        key: String,
159        /// Maximum number of requests allowed in the window
160        limit: u32,
161        /// Time window in milliseconds
162        window_ms: u64,
163    },
164
165    // === Tier 2: L3/L4 Network Actions (Future) ===
166    RedirectIp {
167        target: String,
168    },
169    SetTtl {
170        ttl: u8,
171    },
172    ForwardPort {
173        target_host: String,
174        target_port: u16,
175    },
176
177    // === Tier 3: L7 HTTP Actions ===
178    // --- Terminal Actions ---
179    MockResponse {
180        status: u16,
181        headers: HashMap<String, String>,
182        body: Option<BodySource>,
183    },
184    MapLocal {
185        path: String,
186        content_type: Option<String>,
187    },
188    MapRemote {
189        url: String,
190        preserve_host: bool,
191    },
192    Redirect {
193        location: String,
194        status: u16,
195    },
196
197    // --- Modification Actions (Headers) ---
198    /// Add a header (Appends if exists, useful for multi-value headers like Set-Cookie)
199    AddRequestHeader {
200        name: String,
201        value: String,
202    },
203    /// Update an existing header (Supports {{previous}} variable)
204    /// If add_if_missing is true, creates it if not found.
205    UpdateRequestHeader {
206        name: String,
207        value: String,
208        add_if_missing: bool,
209    },
210    /// Delete a header
211    DeleteRequestHeader {
212        name: String,
213    },
214
215    AddResponseHeader {
216        name: String,
217        value: String,
218    },
219    UpdateResponseHeader {
220        name: String,
221        value: String,
222        add_if_missing: bool,
223    },
224    DeleteResponseHeader {
225        name: String,
226    },
227
228    // --- Modification Actions (Request/Response) ---
229    SetRequestMethod {
230        method: String,
231    },
232    SetRequestUrl {
233        url: String,
234    },
235    SetRequestBody {
236        body: BodySource,
237    },
238    SetResponseStatus {
239        status: u16,
240    },
241    SetResponseBody {
242        body: BodySource,
243    },
244
245    // --- Transformation Actions ---
246    TransformRequestBody {
247        transform: BodyTransform,
248    },
249    TransformResponseBody {
250        transform: BodyTransform,
251    },
252
253    // === Tier 3: WebSocket Actions ===
254    MockWebSocketMessage {
255        direction: WebSocketDirection,
256        message: String,
257    },
258    DropWebSocketMessage,
259}
260
261#[derive(Debug, Clone, Serialize, Deserialize)]
262#[serde(tag = "type", content = "value")]
263pub enum BodySource {
264    Text(String),
265    File(String),
266    Base64(String),
267}
268
269#[derive(Debug, Clone, Serialize, Deserialize)]
270#[serde(tag = "type", content = "config")]
271pub enum BodyTransform {
272    RegexReplace { pattern: String, replacement: String },
273    JsonPathSet { path: String, value: String },
274    JsonPathDelete { path: String },
275}
276
277#[derive(Debug, Clone, Serialize, Deserialize)]
278pub enum WebSocketDirection {
279    Incoming,
280    Outgoing,
281}
282
283// --- Tracing ---
284
285#[derive(Debug, Clone, Serialize, Deserialize)]
286pub struct RuleTrace {
287    pub flow_id: String,
288    /// Summary for list view icons
289    pub summary: RuleTraceSummary,
290    /// Detailed execution log (Lazy loaded)
291    pub events: Vec<RuleExecutionEvent>,
292}
293
294#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
295pub enum RuleTraceSummary {
296    NoMatch,
297    /// Modified by one or more rules
298    Modified { rule_ids: Vec<String> },
299    /// Terminated by a rule (Drop/Mock)
300    Terminated {
301        rule_id: String,
302        reason: TerminalReason,
303    },
304}
305
306#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
307pub enum TerminalReason {
308    Drop,
309    Abort,
310    Mock,
311    Redirect,
312    Inspect,
313    RateLimited,
314}
315
316#[derive(Debug, Clone, Serialize, Deserialize)]
317pub struct RuleExecutionEvent {
318    pub rule_id: String,
319    pub stage: RuleStage,
320    pub matched: bool,
321    pub duration_us: u64,
322    pub outcome: RuleOutcome,
323}
324
325#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
326pub enum RuleOutcome {
327    Skipped,
328    MatchedAndExecuted,
329    MatchedAndTerminated,
330    Failed(String),
331}