oxify-server 0.1.0

HTTP server implementation for OxiFY - Axum, graceful shutdown, middleware (ported from OxiRS)
docs.rs failed to build oxify-server-0.1.0
Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.

oxify-server

Production-ready HTTP server foundation for OxiFY, built on Axum and Tower.

Overview

oxify-server provides a robust, batteries-included HTTP server runtime for building LLM workflow APIs. It handles the operational concerns (logging, tracing, graceful shutdown, middleware) so you can focus on building workflow endpoints.

Ported from: OxiRS - Battle-tested in production semantic web applications.

Features

  • Axum Framework: Modern, ergonomic async web framework
  • Tower Middleware: Composable middleware stack
  • Graceful Shutdown: Signal handling (SIGINT, SIGTERM)
  • Request Tracing: Automatic request ID generation and propagation
  • Structured Logging: Integration with tracing ecosystem
  • CORS Support: Configurable cross-origin resource sharing
  • Compression: Gzip, Brotli, Deflate (optional feature)
  • Authentication: Built-in JWT middleware integration
  • Production-Ready: Comprehensive configuration and error handling

Installation

[dependencies]
oxify-server = { path = "../crates/api/oxify-server" }

# Or with features
oxify-server = { path = "../crates/api/oxify-server", features = ["compression", "cors"] }

Feature Flags

  • compression (default): HTTP response compression (gzip, brotli, deflate)
  • cors (default): CORS middleware support

Quick Start

Basic Server

use oxify_server::{ServerConfig, ServerRuntime};
use axum::{routing::get, Router, Json};
use serde_json::json;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // Create router
    let app = Router::new()
        .route("/", get(|| async {
            Json(json!({"status": "ok"}))
        }));

    // Configure and run server
    let config = ServerConfig::development();
    let server = ServerRuntime::new(config).with_router(app);

    server.run().await?;
    Ok(())
}

With Authentication

use oxify_server::{ServerConfig, ServerRuntime, auth_middleware};
use oxify_authn::{JwtManager, JwtConfig};
use axum::{
    routing::{get, post},
    Router,
    middleware,
};
use std::sync::Arc;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // Initialize JWT manager
    let jwt_config = JwtConfig::development();
    let jwt_manager = Arc::new(JwtManager::new(&jwt_config)?);

    // Build router with protected routes
    let app = Router::new()
        .route("/", get(public_handler))
        .route("/protected", get(protected_handler))
            .layer(middleware::from_fn_with_state(
                jwt_manager.clone(),
                auth_middleware,
            ))
        .route("/login", post(login_handler))
        .with_state(jwt_manager);

    // Run server
    let config = ServerConfig::development();
    let server = ServerRuntime::new(config).with_router(app);

    server.run().await?;
    Ok(())
}

async fn public_handler() -> &'static str {
    "Public endpoint"
}

async fn protected_handler() -> &'static str {
    "Protected endpoint - requires valid JWT"
}

async fn login_handler() -> &'static str {
    "Login endpoint"
}

Core Components

ServerRuntime

Main server runtime that manages the Axum server lifecycle.

pub struct ServerRuntime {
    config: ServerConfig,
    router: Option<Router>,
}

impl ServerRuntime {
    pub fn new(config: ServerConfig) -> Self;
    pub fn with_router(self, router: Router) -> Self;
    pub async fn run(self) -> Result<()>;
}

ServerConfig

Server configuration with sensible defaults.

pub struct ServerConfig {
    pub host: String,
    pub port: u16,
    pub request_logging: bool,
    pub request_id_header: String,
    pub cors: bool,
    pub compression: bool,
    pub graceful_shutdown_timeout_secs: u64,
}

impl ServerConfig {
    pub fn development() -> Self;  // localhost:3000, all features enabled
    pub fn production() -> Self;   // 0.0.0.0:8080, production settings
}

Development Config:

  • Host: 127.0.0.1
  • Port: 3000
  • Request logging: enabled
  • CORS: enabled (all origins)
  • Compression: enabled

Production Config:

  • Host: 0.0.0.0
  • Port: 8080
  • Request logging: enabled
  • CORS: disabled (configure manually)
  • Compression: enabled
  • Graceful shutdown: 30s timeout

Middleware

Request ID Middleware

Automatically adds a unique request ID to each request.

use oxify_server::request_id_middleware;

let app = Router::new()
    .route("/", get(handler))
    .layer(axum::middleware::from_fn(request_id_middleware));

Request ID is available in:

  • Response header: x-request-id
  • Request extensions: request.extensions().get::<RequestId>()

Logging Middleware

Logs request/response details with structured tracing.

use oxify_server::logging_middleware;

let app = Router::new()
    .route("/", get(handler))
    .layer(axum::middleware::from_fn(logging_middleware));

Logs include:

  • Method and path
  • Status code
  • Duration
  • Request ID

Authentication Middleware

JWT-based authentication middleware.

use oxify_server::auth_middleware;
use oxify_authn::JwtManager;
use std::sync::Arc;

let jwt_manager = Arc::new(JwtManager::new(&config)?);

let app = Router::new()
    .route("/protected", get(handler))
    .layer(middleware::from_fn_with_state(
        jwt_manager.clone(),
        auth_middleware,
    ));

Public routes (skip authentication):

  • /health
  • /metrics
  • /login
  • /register

CORS Middleware

Cross-origin resource sharing configuration.

use oxify_server::cors_layer;

let app = Router::new()
    .route("/", get(handler))
    .layer(cors_layer());

Default CORS settings:

  • Allow all origins (development)
  • Allow methods: GET, POST, PUT, DELETE, OPTIONS
  • Allow headers: Content-Type, Authorization
  • Max age: 3600 seconds

Complete Example

use oxify_server::{ServerConfig, ServerRuntime};
use oxify_authn::{JwtManager, JwtConfig, User, Permission};
use axum::{
    extract::State,
    http::StatusCode,
    routing::{get, post},
    Router,
    Json,
};
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use tracing::Level;

#[derive(Clone)]
struct AppState {
    jwt_manager: Arc<JwtManager>,
}

#[derive(Deserialize)]
struct LoginRequest {
    username: String,
    password: String,
}

#[derive(Serialize)]
struct LoginResponse {
    token: String,
    user: User,
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // Initialize tracing
    tracing_subscriber::fmt()
        .with_max_level(Level::INFO)
        .init();

    // Initialize state
    let jwt_config = JwtConfig::development();
    let jwt_manager = Arc::new(JwtManager::new(&jwt_config)?);
    let app_state = AppState { jwt_manager };

    // Build application
    let app = Router::new()
        .route("/", get(root))
        .route("/login", post(login))
        .with_state(app_state);

    // Run server
    let config = ServerConfig::development();
    let server = ServerRuntime::new(config).with_router(app);

    tracing::info!("Server starting at http://127.0.0.1:3000");
    server.run().await?;

    Ok(())
}

async fn root() -> Json<serde_json::Value> {
    Json(serde_json::json!({
        "name": "OxiFY API",
        "version": "0.1.0",
        "status": "running"
    }))
}

async fn login(
    State(state): State<AppState>,
    Json(req): Json<LoginRequest>,
) -> Result<Json<LoginResponse>, StatusCode> {
    // In production, verify password against database
    let user = User {
        username: req.username.clone(),
        roles: vec!["user".to_string()],
        email: Some(format!("{}@example.com", req.username)),
        full_name: Some(req.username.clone()),
        last_login: Some(chrono::Utc::now()),
        permissions: vec![Permission::Read, Permission::Write],
    };

    let token = state.jwt_manager
        .generate_token(&user)
        .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;

    Ok(Json(LoginResponse { token, user }))
}

Graceful Shutdown

The server automatically handles shutdown signals:

# Send SIGINT (Ctrl+C)
kill -INT <pid>

# Send SIGTERM
kill -TERM <pid>

Shutdown sequence:

  1. Stop accepting new connections
  2. Wait for active requests to complete (up to timeout)
  3. Force shutdown after timeout

Configure timeout:

let config = ServerConfig {
    graceful_shutdown_timeout_secs: 60,  // 60 second timeout
    ..ServerConfig::default()
};

Production Deployment

Docker

FROM rust:1.75 as builder
WORKDIR /app
COPY . .
RUN cargo build --release

FROM debian:bookworm-slim
COPY --from=builder /app/target/release/oxify-api /usr/local/bin/
EXPOSE 8080
CMD ["oxify-api"]

Environment Variables

# Server configuration
export OXIFY_HOST="0.0.0.0"
export OXIFY_PORT="8080"
export OXIFY_LOG_LEVEL="info"

# Authentication
export JWT_SECRET="your-secret-key-min-32-bytes"
export JWT_EXPIRATION_SECS="3600"

# Database
export DATABASE_URL="postgresql://user:pass@localhost/oxify"

Kubernetes

apiVersion: apps/v1
kind: Deployment
metadata:
  name: oxify-api
spec:
  replicas: 3
  selector:
    matchLabels:
      app: oxify-api
  template:
    metadata:
      labels:
        app: oxify-api
    spec:
      containers:
      - name: oxify-api
        image: oxify/api:latest
        ports:
        - containerPort: 8080
        env:
        - name: OXIFY_HOST
          value: "0.0.0.0"
        - name: OXIFY_PORT
          value: "8080"
        - name: JWT_SECRET
          valueFrom:
            secretKeyRef:
              name: oxify-secrets
              key: jwt-secret
        livenessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 10
          periodSeconds: 30
        readinessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 10

Monitoring

Health Check Endpoint

use axum::{routing::get, Router, Json};

let app = Router::new()
    .route("/health", get(health_check));

async fn health_check() -> Json<serde_json::Value> {
    Json(serde_json::json!({
        "status": "healthy",
        "timestamp": chrono::Utc::now().to_rfc3339()
    }))
}

Metrics (Future)

Integration with Prometheus metrics is planned:

use oxify_server::metrics_middleware;

let app = Router::new()
    .route("/", get(handler))
    .layer(metrics_middleware());

// Exposes /metrics endpoint with:
// - http_requests_total
// - http_request_duration_seconds
// - http_requests_in_flight

Testing

Run the test suite:

cd crates/api/oxify-server
cargo test

# Run with all features
cargo test --all-features

Performance

  • Startup time: <100ms
  • Request overhead: <1ms (middleware processing)
  • Graceful shutdown: <30s (configurable)
  • Concurrent connections: Limited by Tokio runtime (default: CPU cores * 2)

Dependencies

Core dependencies:

  • axum - Web framework
  • tower / tower-http - Middleware
  • tokio - Async runtime
  • tracing - Structured logging

Integration:

  • oxify-authn - Authentication middleware
  • oxify-authz - Authorization (future)

License

Apache-2.0

Attribution

Ported from OxiRS with permission. Original implementation by the OxiLabs team.