Skip to content

Latest commit

 

History

History
238 lines (165 loc) · 5.87 KB

File metadata and controls

238 lines (165 loc) · 5.87 KB

HTTP/2 Support

FastRMCP web transports (SSE and WebSocket) support HTTP/2 out of the box through Axum 0.7 and Hyper 1.0.

Overview

HTTP/2 provides several benefits over HTTP/1.1:

  • Multiplexing: Multiple requests over a single connection
  • Header Compression: Reduced bandwidth usage
  • Server Push: Proactive resource delivery (when supported)
  • Binary Protocol: More efficient parsing

Default Behavior

FastRMCP's SSE and WebSocket transports automatically support both HTTP/1.1 and HTTP/2:

use fastrmcp::transport::SseTransport;

let (transport, router) = SseTransport::new();

// This server automatically supports both HTTP/1.1 and HTTP/2
let listener = tokio::net::TcpListener::bind("127.0.0.1:3000").await?;
axum::serve(listener, router).await?;

Note: HTTP/2 typically requires TLS in browsers (h2), though cleartext HTTP/2 (h2c) is supported for testing.

Production Deployment with TLS

For production use with HTTP/2, configure TLS:

Using axum-server with rustls

Add to Cargo.toml:

[dependencies]
axum-server = { version = "0.6", features = ["tls-rustls"] }
rustls = "0.22"

Example server with TLS:

use fastrmcp::transport::SseTransport;
use axum_server::tls_rustls::RustlsConfig;
use std::net::SocketAddr;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Load TLS configuration
    let config = RustlsConfig::from_pem_file(
        "cert.pem",
        "key.pem"
    ).await?;

    let (transport, router) = SseTransport::new();

    let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
    println!("Server listening on https://localhost:3000");

    // Serve with TLS - automatically negotiates HTTP/2
    axum_server::bind_rustls(addr, config)
        .serve(router.into_make_service())
        .await?;

    Ok(())
}

Generating Self-Signed Certificates (Development)

For development and testing:

# Generate private key
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes

# Or using mkcert (easier for local development)
mkcert -install
mkcert localhost 127.0.0.1 ::1

Verifying HTTP/2

Using curl

# HTTP/2 over TLS (h2)
curl -v --http2 https://localhost:3000/mcp/sse

# HTTP/2 cleartext (h2c) - not supported by all clients
curl -v --http2-prior-knowledge http://localhost:3000/mcp/sse

Using Browser DevTools

  1. Open browser DevTools (F12)
  2. Go to Network tab
  3. Look for "Protocol" column
  4. Should show "h2" for HTTP/2 connections

Programmatically

use hyper::client::conn::http2;

// Configure HTTP/2 client
let (mut sender, conn) = http2::handshake(io).await?;

Performance Considerations

When HTTP/2 Helps

  • Multiple Resources: Benefit from multiplexing
  • Header-Heavy Requests: Compression reduces bandwidth
  • Concurrent Requests: Single connection for multiple streams

When HTTP/1.1 May Be Better

  • Single Request: No multiplexing benefit
  • Very Simple Clients: HTTP/1.1 is simpler
  • Legacy Systems: Better compatibility

WebSocket with HTTP/2

WebSockets can be established over HTTP/2 connections:

use fastrmcp::transport::WebSocketTransport;

// WebSocket upgrade works over both HTTP/1.1 and HTTP/2
let transport = WebSocketTransport::new("127.0.0.1:3001")?;

Note: WebSocket over HTTP/2 uses the extended CONNECT method (RFC 8441).

Configuration Options

Maximum Concurrent Streams

Configure maximum concurrent HTTP/2 streams:

use hyper::server::conn::http2;

let mut http2 = http2::Builder::new();
http2.max_concurrent_streams(100);

Initial Window Size

Configure flow control window:

let mut http2 = http2::Builder::new();
http2.initial_connection_window_size(1024 * 1024); // 1 MB

Troubleshooting

"Connection refused" with HTTPS

Solution: Ensure TLS certificate is valid and port is correct

"Protocol error" in browsers

Solution: Browsers require TLS for HTTP/2. Use HTTPS or test with curl

Client doesn't support HTTP/2

Solution: Server automatically falls back to HTTP/1.1

Certificate errors in browser

Solution: Use valid certificate or add self-signed cert to trusted store

Best Practices

  1. Production: Always use TLS with valid certificates
  2. Development: Use mkcert for local HTTPS testing
  3. Monitoring: Check protocol version in logs
  4. Fallback: Let server auto-negotiate (supports both HTTP/1.1 and HTTP/2)
  5. Testing: Test with both HTTP/1.1 and HTTP/2 clients

Example: Production-Ready HTTP/2 Server

use fastrmcp::prelude::*;
use fastrmcp::transport::SseTransport;
use fastrmcp_macros::tool;
use axum_server::tls_rustls::RustlsConfig;
use std::net::SocketAddr;

#[tool]
async fn greet(name: String) -> Result<String> {
    Ok(format!("Hello, {}!", name))
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Initialize logging
    tracing_subscriber::fmt()
        .with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
        .init();

    // Load TLS configuration
    let config = RustlsConfig::from_pem_file(
        "cert.pem",
        "key.pem"
    ).await?;

    // Create MCP server
    let (transport, router) = SseTransport::new();
    let server = FastMCP::new("http2-server")
        .version("1.0.0")
        .description("Production HTTP/2 MCP server");

    // Bind with TLS
    let addr = SocketAddr::from(([0, 0, 0, 0], 3000));
    tracing::info!("Server listening on https://0.0.0.0:3000");
    tracing::info!("HTTP/2 enabled");

    axum_server::bind_rustls(addr, config)
        .serve(router.into_make_service())
        .await?;

    Ok(())
}

See Also