camel-component-http 0.2.0

HTTP client component for rust-camel
Documentation

camel-component-http

HTTP client and server component for rust-camel

Overview

The HTTP component provides HTTP client (producer) and HTTP server (consumer) capabilities for rust-camel. Built on reqwest for clients and axum for servers, it enables REST API integration, webhook handling, and HTTP-based messaging.

Features

  • HTTP Server (Consumer): Listen for incoming HTTP requests
  • HTTP Client (Producer): Make outgoing HTTP requests
  • HTTPS Support: Secure connections with https scheme
  • Configurable Timeouts: Connect and response timeouts
  • SSRF Protection: Optional private IP blocking
  • Header Mapping: Automatic header forwarding
  • Status Code Handling: Configurable success ranges

Installation

Add to your Cargo.toml:

[dependencies]
camel-component-http = "0.2"

URI Format

http://host:port/path[?options]
https://host:port/path[?options]

Consumer Options (Server)

Option Default Description
Host/path from URI - e.g., http://0.0.0.0:8080/api

Producer Options (Client)

Option Default Description
httpMethod Auto HTTP method (GET, POST, etc.)
throwExceptionOnFailure true Throw on non-2xx responses
okStatusCodeRange 200-299 Success status code range
followRedirects false Follow HTTP redirects
connectTimeout 30000 Connection timeout (ms)
responseTimeout - Response timeout (ms)
allowPrivateIps false Allow requests to private IPs
blockedHosts - Comma-separated blocked hosts

Usage

HTTP Server (Consumer)

use camel_builder::RouteBuilder;
use camel_component_http::HttpComponent;
use camel_core::CamelContext;

let mut ctx = CamelContext::new();
ctx.register_component("http", Box::new(HttpComponent::new()));

// Simple API endpoint
let route = RouteBuilder::from("http://0.0.0.0:8080/hello")
    .process(|ex| async move {
        let mut ex = ex;
        ex.input.body = camel_api::Body::Text("Hello, World!".to_string());
        Ok(ex)
    })
    .build()?;

HTTP Client (Producer)

// GET request
let route = RouteBuilder::from("timer:tick?period=60000")
    .to("http://api.example.com/data?allowPrivateIps=false")
    .log("Response received", camel_processor::LogLevel::Info)
    .build()?;

// POST request (body becomes request body)
let route = RouteBuilder::from("direct:submit")
    .to("http://api.example.com/submit?httpMethod=POST")
    .build()?;

Dynamic HTTP Method

// Method from header
let route = RouteBuilder::from("direct:api")
    .set_header("CamelHttpMethod", Value::String("DELETE".into()))
    .to("http://api.example.com/resource")
    .build()?;

Dynamic URL

// URL from header
let route = RouteBuilder::from("direct:proxy")
    .set_header("CamelHttpUri", Value::String("http://backend.service/api".into()))
    .to("http://localhost/dummy")  // Base URL, overridden by header
    .build()?;

HTTPS

let route = RouteBuilder::from("timer:secure")
    .to("https://secure.api.com/endpoint")
    .build()?;

Exchange Headers

Request Headers (Consumer)

Header Description
CamelHttpMethod HTTP method (GET, POST, etc.)
CamelHttpPath Request path
CamelHttpQuery Query string
All HTTP headers Forwarded from request

Response Headers (Producer)

Header Description
CamelHttpResponseCode HTTP status code
CamelHttpResponseText Status text
All response headers Forwarded from response

Request Control Headers (Producer)

Header Description
CamelHttpUri Override target URL
CamelHttpPath Append to base URL path
CamelHttpQuery Append query string
CamelHttpMethod Override HTTP method

Example: REST API Server

use camel_builder::RouteBuilder;
use camel_component_http::{HttpComponent, HttpsComponent};
use camel_core::CamelContext;
use camel_api::Body;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut ctx = CamelContext::new();
    ctx.register_component("http", Box::new(HttpComponent::new()));

    // GET /api/users
    ctx.add_route(
        RouteBuilder::from("http://0.0.0.0:8080/api/users")
            .process(|ex| async move {
                let mut ex = ex;
                ex.input.body = Body::Text(r#"[{"id":1,"name":"Alice"}]"#.to_string());
                ex.input.set_header("Content-Type", serde_json::Value::String("application/json".into()));
                Ok(ex)
            })
            .build()?
    ).await?;

    // POST /api/users
    ctx.add_route(
        RouteBuilder::from("http://0.0.0.0:8080/api/users")
            .filter(|ex| ex.input.header("CamelHttpMethod").and_then(|v| v.as_str()) == Some("POST"))
                .process(|ex| async move {
                    // Create user from request body
                    Ok(ex)
                })
                .set_body(Body::Text(r#"{"status":"created"}"#))
            .end_filter()
            .build()?
    ).await?;

    ctx.start().await?;
    tokio::signal::ctrl_c().await?;
    ctx.stop().await?;

    Ok(())
}

Example: HTTP Client with Error Handling

let route = RouteBuilder::from("direct:api-call")
    .to("http://api.service.com/endpoint?throwExceptionOnFailure=true&connectTimeout=5000")
    .build()?;

// With custom error handling
let route = RouteBuilder::from("direct:resilient")
    .error_handler(ErrorHandlerConfig::log_only())
    .to("http://api.service.com/endpoint?throwExceptionOnFailure=false")
    .process(|ex| async move {
        let status = ex.input.header("CamelHttpResponseCode")
            .and_then(|v| v.as_u64())
            .unwrap_or(0);
        if status >= 400 {
            // Handle error response
        }
        Ok(ex)
    })
    .build()?;

SSRF Protection

By default, the HTTP client blocks requests to private IP addresses for security. To allow:

.to("http://internal.service/api?allowPrivateIps=true")

To block specific hosts:

.to("http://api.example.com?blockedHosts=localhost,127.0.0.1,internal.local")

Documentation

License

Apache-2.0

Contributing

Contributions are welcome! Please see the main repository for details.