ZIO Quartz H2 Architecture
This document outlines the architecture of the Quartz H2 server, a high-performance HTTP/2 implementation built with Scala and Cats Effect. The architecture focuses on efficient resource management, functional stream processing, and robust connection handling for both HTTP/1.1 and HTTP/2 protocols.
System Overview
Quartz H2 is a modern HTTP/2 server implementation with the following key features:
- Protocol Support: Full HTTP/2 implementation with fallback to HTTP/1.1
- TLS Support: ALPN-based protocol negotiation for secure connections
- IO-Uring Integration: Linux io_uring support for high-performance I/O operations
- Functional Core: Built with Cats Effect for pure functional programming
- Stream Processing: Efficient data handling with fs2 streams
- Resource Safety: Proper resource acquisition and release
- Flow Control: Comprehensive HTTP/2 flow control implementation
System Architecture
┌───────────────────────────────────────────────────────────────────────────┐
│ QuartzH2Server │
└─────────────────────────────────────┬─────────────────────────────────────┘
│ creates
▼
┌───────────────────────────────────────────────────────────────────────────┐
│ Connection Management Layer │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ TLSChannel │ │ TCPChannel │ │ IOURingChannel │ │
│ │ (ALPN, SNI) │ │ (Plain HTTP) │ │ (Linux only) │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
└───────────────────────────────┬───────────────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────────────────────┐
│ Protocol Handler Layer │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Http2Connection│ │ Http11Connection│ │
│ │ │ │ │ │
│ └────────┬────────┘ └────────┬────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Http2Stream(s) │ │ ChunkedTransfer │ │
│ │ │ │ │ │
│ └─────────────────┘ └─────────────────┘ │
└───────────────────────────────┬───────────────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────────────────────┐
│ Application Layer │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ HttpRoute │ │ WebFilter │ │ HttpRouteIO │ │
│ │ │ │ │ │ │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
└───────────────────────────────────────────────────────────────────────────┘
The architecture is organized into three main layers:
- Connection Management Layer: Handles raw socket connections, TLS negotiation, and I/O operations
- Protocol Handler Layer: Implements HTTP/1.1 and HTTP/2 protocol logic, including streams and flow control
- Application Layer: Processes HTTP requests and generates responses through routes and filters
HTTP/2 Connection Implementation
The Http2Connection
class is the core component that manages HTTP/2 connections. It extends Http2ConnectionCommon
which provides shared functionality for both client and server HTTP/2 connections.
┌─────────────────────────────────────────────────────────────────────────┐
│ Http2Connection │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Outbound Queue │ │ Flow Control │ │ Stream Table │ │
│ │ (outq) │◄────►│ Management │◄────►│ (streamTbl) │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
│ ▲ ▲ ▲ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Outbound Worker │ │ Packet Handler │ │ Http2Stream(s) │ │
│ │ Process │◄────►│ │◄────►│ │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
│ ▲ ▲ │
└────────────────────────────────────┼────────────────────────┼───────────┘
│ │
▼ ▼
┌──────────────────────────────────┐ ┌──────────────────────────┐
│ IOChannel │ │ Http Route │
└──────────────────────────────────┘ └──────────────────────────┘
Key Components
- Http2Connection: The main class that manages an HTTP/2 connection, handling frame processing, flow control, and stream management
- Http2Stream: Represents an individual HTTP/2 stream within a connection, with its own flow control windows and data queues
- Outbound Queue (outq): A bounded queue (capacity 1024) for managing outgoing data packets
- Stream Table (streamTbl): A concurrent map that tracks all active streams in the connection
- Flow Control Management: Implements HTTP/2 flow control at both connection and stream levels
Connection Establishment
The QuartzH2Server
handles connection establishment through several methods:
- Direct HTTP/2 Connection: When a client sends the HTTP/2 preface string, a new
Http2Connection
is created
- HTTP/1.1 Upgrade: When a client requests an upgrade to HTTP/2 via the HTTP/1.1 Upgrade header, the server performs the protocol switch
- TLS with ALPN: For secure connections, ALPN negotiation determines whether to use HTTP/2 or HTTP/1.1
Functional Stream Processing
Quartz H2 leverages functional streams (fs2.Stream) as a core abstraction for processing HTTP/2 data. Unlike traditional imperative streams, these streams represent a description of data transformations that are executed only when the stream is run. This functional approach provides several benefits:
- Composability: Stream transformations can be composed together without side effects
- Resource Safety: Resources are properly acquired and released
- Backpressure: Automatic handling of backpressure throughout the pipeline
- Error Handling: Structured error handling within the stream processing pipeline
Key Stream Transformations
makePacketStream
: Creates a Stream that reads from the IOChannel and transforms raw bytes into HTTP/2 packets
packetStreamPipe
: Transforms a stream of bytes into a stream of HTTP/2 frames
dataEvalEffectProducer
: Produces data from queues as a Stream transformation
Stream Processing Implementation
The implementation uses several advanced stream processing techniques:
- Chunked Processing: Data is processed in chunks for efficiency, with careful handling of partial frames
- Pull-based Consumption: The
Pull
API is used to implement custom stream processing logic
- Lazy Evaluation: Stream transformations are applied lazily when the stream is consumed
- Resource Management: Proper acquisition and release of resources using bracket patterns
1. Outbound Queue (outq
)
- Purpose: Manages outgoing data packets (ByteBuffers) to be sent to the client
- Type:
Queue[IO, ByteBuffer]
- Capacity: Bounded queue with capacity of 1024
- Creation: Created during the initialization of an HTTP/2 connection
- Usage: All outgoing frames are offered to this queue and processed by the outbound worker
2. Stream Flow Control Queues
Each HTTP/2 stream has several queues to manage its data flow:
a. Data Input Queue (inDataQ
)
- Purpose: Accumulates incoming data packets for a specific stream
- Type:
Queue[IO, ByteBuffer]
- Capacity: Unbounded queue
- Creation: Created when a new stream is opened
- Usage: Stores incoming DATA frames for processing by the application
b. Flow Control Sync Queue (outXFlowSync
)
- Purpose: Synchronizes flow control for outgoing data frames
- Type:
Queue[IO, Boolean]
- Capacity: Unbounded queue
- Creation: Created when a new stream is opened
- Usage: Signals when the stream can send more data based on flow control window updates
c. Window Update Sync Queue (syncUpdateWindowQ
)
- Purpose: Manages window update synchronization
- Type:
Queue[IO, Unit]
- Capacity: Dropping queue with capacity of 1
- Creation: Created when a new stream is opened
- Usage: Coordinates window updates for flow control
Concurrency Control
The implementation uses several mechanisms to ensure thread-safe concurrent operations:
- Semaphores: Used to control access to shared resources, particularly for operations that need exclusive access
- Refs: Thread-safe reference cells that manage state updates atomically
- Deferred: Used for one-shot communication between fibers, particularly for signaling completion or shutdown
- Fibers: Lightweight concurrency primitives that allow for parallel execution with proper resource management
Data Flow Process
Incoming Data Flow
-
Data Reception as Stream:
- The
processIncoming
method sets up a Stream pipeline starting with leftover data
makePacketStream
creates a Stream that reads from the IOChannel and transforms raw bytes into HTTP/2 packets
- This is a description of the transformation, not the actual execution
-
Stream Transformation:
- The byte stream is transformed via the
packetStreamPipe
pipeline
- This pipeline chunks the bytes into proper HTTP/2 frames
- The transformation is applied lazily when the stream is consumed
-
Stream Consumption:
- The transformed stream is consumed with
foreach
, which applies packet_handler
to each packet
- Only at this point is the actual I/O performed and frames processed
-
Packet Handling:
- Each packet is processed by the
packet_handler
method
- Frames are parsed and handled according to their type (HEADERS, DATA, SETTINGS, etc.)
-
Stream Data Processing:
- For DATA frames, the data is placed in the appropriate stream's
inDataQ
- Flow control is managed by updating window sizes and sending WINDOW_UPDATE frames when necessary
Outgoing Data Flow
-
Frame Generation:
- Outgoing frames are created using methods like
headerFrame
and dataFrame
- These frames are offered to the
outq
queue
-
Outbound Processing:
- The
outBoundWorkerProc
continuously takes frames from the outq
queue
- It writes the frames to the IOChannel for transmission to the client
-
Flow Control Management:
- Before sending DATA frames, the system checks both global and stream-specific flow control windows
- The
txWindow_Transmit
method handles the logic for splitting frames if necessary based on available window size
- The
outXFlowSync
queue is used to signal when more data can be sent after window updates
-
Response Streaming:
- When sending response bodies, data is often provided as a Stream
- This allows for efficient streaming of large responses without loading everything into memory
- The Stream is consumed and transformed into HTTP/2 DATA frames as needed
HTTP/1.1 Implementation
Quartz H2 includes a robust HTTP/1.1 implementation that serves as a fallback when HTTP/2 is not supported or negotiated. The HTTP/1.1 implementation has been enhanced with comprehensive documentation, better error handling, and improved code structure.
HTTP/1.1 Connection Handler
The HTTP/1.1 connection handler includes the following improvements:
- Detailed Documentation: Comprehensive class and method documentation following Scaladoc conventions
- Improved Error Handling: Better error messages with specific details about what went wrong
- Enhanced Content Length Handling: Fixed content length error handling with better diagnostics
- Type Annotations: Added proper type annotations for better code clarity
- Enhanced Logging: Improved logging with more context and better categorization (debug vs error)
- Header Translation: Clarified HTTP header translation between HTTP/1.1 and HTTP/2 formats
- Exception Handling: Improved exception handling for malformed requests
Chunked Transfer Encoding
The HTTP/1.1 chunked transfer encoding implementation has been enhanced with:
- RFC 7230 Compliance: Proper handling of RFC 7230 compliant chunked transfer encoding
- Improved Validation: Enhanced validation of chunk terminators and headers
- Bounds Checking: Added proper bounds checking to prevent IndexOutOfBoundsExceptions
- Structured Code: Better comments explaining the logic and improved code organization
- Error Propagation: Improved exception handling to propagate errors properly through the stream
HTTP Range Requests
The HttpRangeRequest class has been improved with:
- ETag Generation: Proper ETag generation based on file metadata (path, size, and modification time)
- Range Handling: Better range handling with named variables for readability
- Byte Range Calculations: Fixed byte range calculations for partial content requests
- Native Scala Approach: Used a native Scala approach for byte-to-hex conversion without external dependencies
Key Components
QuartzH2Server
The main server class that manages HTTP/2 connections, handles SSL context setup, and manages incoming connections. It provides the following key functionality:
- Connection Management: Creates and manages HTTP/2 and HTTP/1.1 connections
- TLS Configuration: Sets up SSL context and handles ALPN negotiation
- Server Socket Binding: Binds to the specified host and port
- Connection Lifecycle Hooks: Provides callbacks for connection events (connect, disconnect)
Http2Connection
The main class that manages an HTTP/2 connection. It extends Http2ConnectionCommon
and implements the core HTTP/2 protocol logic, including:
- Frame Processing: Handles different types of HTTP/2 frames
- Stream Management: Creates and manages HTTP/2 streams
- Flow Control: Implements HTTP/2 flow control mechanisms
- Settings Management: Handles HTTP/2 settings frames and parameters
Http2Stream
Represents an individual HTTP/2 stream within a connection. Each stream has its own set of queues for managing data flow and handles:
- Stream-Level Flow Control: Manages stream-specific flow control windows
- Header Processing: Handles HTTP headers for requests and responses
- Data Queueing: Manages incoming and outgoing data
- Stream State: Tracks the state of the stream (idle, open, closed, etc.)
TLSChannel
Handles secure connections using SSL/TLS protocols. Key features include:
- ALPN Support: Application-Layer Protocol Negotiation for HTTP/2
- SNI Support: Server Name Indication for virtual hosting
- Secure Handshake: Manages the SSL/TLS handshake process
- Encrypted Communication: Handles encryption and decryption of data
Flow Control
HTTP/2 implements flow control at two levels:
- Connection-Level: Managed by
globalTransmitWindow
and globalInboundWindow
- Stream-Level: Each stream has its own
transmitWindow
and inboundWindow
Cats Effect Queues play a crucial role in coordinating these flow control mechanisms, ensuring efficient data transmission while preventing buffer bloat and resource exhaustion.
Client Implementation
Quartz H2 includes a client implementation for making HTTP/2 requests to servers. The QuartzH2Client
provides functionality for establishing connections and sending requests.
Connection Establishment
The client supports several methods for establishing connections:
- TLS with ALPN: Secure connections with protocol negotiation via
connectTLS_alpn_h2
- Direct HTTP/2: Plain HTTP/2 connections without TLS
- Connection Pooling: Reusing connections for multiple requests to improve performance
Request Handling
The client provides several features for handling requests:
- Streaming Requests: Support for streaming request bodies
- Header Management: Proper handling of HTTP/2 headers
- Response Processing: Processing and streaming of response data
- Error Handling: Robust error handling for network issues and protocol errors
Conclusion
Quartz H2 is a high-performance HTTP/2 server and client implementation built with Scala and Cats Effect. Its architecture emphasizes:
- Functional Programming: Pure functional approach using Cats Effect for composable, referentially transparent operations
- Stream Processing: Efficient data handling with fs2 streams for backpressure and resource management
- Protocol Compliance: Full HTTP/2 implementation with fallback to enhanced HTTP/1.1
- Performance: Optimized for high throughput and low latency with features like connection pooling and io_uring support
- Robustness: Comprehensive error handling and recovery mechanisms
The architecture provides a solid foundation for building high-performance web applications and services with modern HTTP capabilities while maintaining code clarity and maintainability through functional programming principles.