# WarpDrive Plugin Development Guide

WarpDrive supports WASM-based plugins for extending proxy functionality. Plugins can handle HTTP requests, WebSocket connections, or gRPC calls using any language that compiles to WebAssembly.

## Architecture

### Plugin Lifecycle

```
┌─────────────┐
│  WarpDrive  │
│   (Host)    │
└──────┬──────┘
       │
       │ Loads .wasm file
       ▼
┌─────────────┐
│   Plugin    │
│   (Guest)   │
└─────────────┘
```

### Protocol Support

WarpDrive plugins can intercept three protocol types:

1. **HTTP** - Request/response filtering
2. **WebSocket** - Connection lifecycle events (connect, message, disconnect)
3. **gRPC** - Method call interception

## Plugin Contract

### Required Exports

Your plugin **must** export these functions:

#### 1. Plugin Metadata

```rust
// Get plugin name (required)
#[no_mangle]
pub extern "C" fn plugin_name() -> *const u8;

// Get plugin name length (required)
#[no_mangle]
pub extern "C" fn plugin_name_len() -> i32;

// Get plugin version (required)
#[no_mangle]
pub extern "C" fn plugin_version() -> *const u8;

// Get plugin version length (required)
#[no_mangle]
pub extern "C" fn plugin_version_len() -> i32;
```

#### 2. Event Handler (Protocol-Specific)

**For WebSocket plugins:**

```rust
/// Handle WebSocket events
///
/// # Arguments
/// * `event_type` - Event type: 0=Connect, 1=Message, 2=Disconnect
/// * `conn_id_ptr` - Pointer to connection ID string
/// * `conn_id_len` - Length of connection ID string
/// * `payload_ptr` - Pointer to payload bytes
/// * `payload_len` - Length of payload bytes
///
/// # Returns
/// * `0` - Success
/// * Non-zero - Error code
#[no_mangle]
pub extern "C" fn wasm_on_event(
    event_type: i32,
    conn_id_ptr: i32,
    conn_id_len: i32,
    payload_ptr: i32,
    payload_len: i32,
) -> i32;
```

**For HTTP plugins:**

```rust
/// Handle HTTP request
///
/// # Arguments
/// * `method_ptr` - HTTP method (GET, POST, etc.)
/// * `method_len` - Method string length
/// * `path_ptr` - Request path
/// * `path_len` - Path string length
/// * `headers_ptr` - JSON-encoded headers
/// * `headers_len` - Headers length
/// * `body_ptr` - Request body
/// * `body_len` - Body length
///
/// # Returns
/// * `0` - Allow request
/// * `1` - Deny request
/// * `2` - Modified (call get_modified_request)
#[no_mangle]
pub extern "C" fn wasm_on_request(
    method_ptr: i32,
    method_len: i32,
    path_ptr: i32,
    path_len: i32,
    headers_ptr: i32,
    headers_len: i32,
    body_ptr: i32,
    body_len: i32,
) -> i32;
```

**For gRPC plugins:**

```rust
/// Handle gRPC call
///
/// # Arguments
/// * `service_ptr` - gRPC service name
/// * `service_len` - Service name length
/// * `method_ptr` - gRPC method name
/// * `method_len` - Method name length
/// * `metadata_ptr` - JSON-encoded metadata
/// * `metadata_len` - Metadata length
///
/// # Returns
/// * `0` - Allow call
/// * `1` - Deny call
#[no_mangle]
pub extern "C" fn wasm_on_grpc_call(
    service_ptr: i32,
    service_len: i32,
    method_ptr: i32,
    method_len: i32,
    metadata_ptr: i32,
    metadata_len: i32,
) -> i32;
```

### Host Functions (Provided by WarpDrive)

WarpDrive provides these functions for plugins to call:

#### WebSocket Host Functions

```rust
// Import from "warpdrive" module
extern "C" {
    /// Send message to specific connection
    fn host_send(conn_id_ptr: i32, conn_id_len: i32, payload_ptr: i32, payload_len: i32) -> i32;

    /// Broadcast message to stream/channel
    fn host_broadcast(stream_ptr: i32, stream_len: i32, payload_ptr: i32, payload_len: i32) -> i32;

    /// Close connection
    fn host_close(conn_id_ptr: i32, conn_id_len: i32, code: i32, reason_ptr: i32, reason_len: i32) -> i32;
}
```

#### HTTP Host Functions

```rust
extern "C" {
    /// Log message from plugin
    fn host_log(level: i32, msg_ptr: i32, msg_len: i32) -> i32;
}
```

## Language Support

### Rust (Recommended)

**Cargo.toml:**
```toml
[package]
name = "my-plugin"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib"]

[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
```

**Build:**
```bash
cargo build --target wasm32-unknown-unknown --release
```

Output: `target/wasm32-unknown-unknown/release/my_plugin.wasm`

### AssemblyScript

**Install:**
```bash
npm install --save-dev assemblyscript
```

**Build:**
```bash
npx asc plugin.ts --target release --outFile plugin.wasm
```

### TinyGo

**Build:**
```bash
tinygo build -o plugin.wasm -target wasi plugin.go
```

### C/C++

**Emscripten:**
```bash
emcc plugin.c -o plugin.wasm -s STANDALONE_WASM=1
```

**WASI SDK:**
```bash
clang --target=wasm32-wasi plugin.c -o plugin.wasm
```

## Memory Management

### Reading Strings from WASM Memory

```rust
use std::slice;
use std::str;

unsafe fn read_string(ptr: i32, len: i32) -> Result<String, &'static str> {
    if ptr == 0 || len == 0 {
        return Ok(String::new());
    }

    let slice = slice::from_raw_parts(ptr as *const u8, len as usize);
    str::from_utf8(slice)
        .map(|s| s.to_string())
        .map_err(|_| "Invalid UTF-8")
}
```

### Passing Strings to Host

```rust
fn send_message(conn_id: &str, message: &str) -> i32 {
    let conn_bytes = conn_id.as_bytes();
    let msg_bytes = message.as_bytes();

    unsafe {
        host_send(
            conn_bytes.as_ptr() as i32,
            conn_bytes.len() as i32,
            msg_bytes.as_ptr() as i32,
            msg_bytes.len() as i32,
        )
    }
}
```

## Plugin Configuration

### TOML Configuration

```toml
[[routes]]
path_prefix = "/cable"
upstream = "cable"
protocol = "ws"
plugin = "plugins/my_plugin.wasm"
```

### Route-Plugin Mapping

- One plugin per route
- Plugin only receives events for its route
- Multiple routes can use the same plugin file

## Error Handling

### Return Codes

Plugins should return error codes to indicate status:

- `0` - Success
- `1-99` - Application errors
- `100+` - Fatal errors

### Logging

Use `host_log` to send logs to WarpDrive:

```rust
fn log(level: i32, message: &str) {
    let bytes = message.as_bytes();
    unsafe {
        host_log(level, bytes.as_ptr() as i32, bytes.len() as i32);
    }
}

// Log levels:
// 0 = ERROR
// 1 = WARN
// 2 = INFO
// 3 = DEBUG
```

## Security & Performance

### Sandboxing

- Plugins run in isolated WASM sandbox
- No file system access
- No network access (use host functions)
- CPU/memory limits enforced

### Fuel Limits

WarpDrive sets per-event fuel limits to prevent infinite loops:

- Default: 100,000 fuel units per event
- Configurable in route config

### Best Practices

1. **Keep it fast** - Plugins add latency to requests
2. **Minimize allocations** - Reuse buffers when possible
3. **Handle errors** - Always return proper error codes
4. **Test thoroughly** - Use integration tests
5. **Version your plugin** - Include version in metadata

## Testing

### Unit Tests

Test your plugin logic without WASM:

```rust
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_event_handling() {
        let result = handle_message("conn123", r#"{"action":"test"}"#);
        assert_eq!(result, 0);
    }
}
```

### Integration Tests

Use `wasmtime` to test the actual WASM build:

```rust
use wasmtime::*;

#[test]
fn test_wasm_plugin() {
    let engine = Engine::default();
    let module = Module::from_file(&engine, "plugin.wasm").unwrap();
    // ... test plugin behavior
}
```

## Examples

See the example plugins in this directory:

- `http-echo/` - Simple HTTP plugin that logs requests
- `ws-echo/` - WebSocket echo server plugin
- `grpc-auth/` - gRPC authentication plugin

Each example includes:
- Full source code
- Build instructions
- Test TOML configuration
- Integration tests

## Troubleshooting

### Plugin Won't Load

- Check file path in TOML config
- Verify `.wasm` extension
- Ensure all required exports present
- Check WarpDrive logs for errors

### Plugin Crashes

- Check return codes (0 = success)
- Validate pointer/length arguments
- Add bounds checking
- Use `host_log` for debugging

### Poor Performance

- Reduce per-event allocations
- Cache parsed data structures
- Profile with `wasmtime`
- Check fuel consumption metrics

## Advanced Topics

### State Management

Plugins are stateless by design. For state:

1. Use connection IDs as keys
2. Store state in plugin-local HashMap
3. Clean up on disconnect events

### Custom Protocols

For custom WebSocket protocols (like ActionCable):

1. Parse protocol messages in plugin
2. Maintain subscription state
3. Use `host_broadcast` for pub/sub

### Performance Tuning

```toml
[[routes]]
path_prefix = "/api"
plugin = "plugins/auth.wasm"
plugin_fuel = 200000  # Increase fuel limit
plugin_memory_max_mb = 16  # Increase memory limit
```

## Contributing

Submit example plugins via pull request:

1. Follow examples structure
2. Include README.md
3. Add integration test
4. Document configuration

## License

Example plugins are MIT licensed.
