http3.cr
http3.cr
http3.cr è una shard Crystal per stabilire connessioni QUIC e scambiare richieste/risposte HTTP/3 veloci ed asincrone. La libreria si interfaccia a basso livello alla libreria ad alte prestazioni libquiche di Cloudflare tramite binding FFI a zero overhead.
Prerequisiti (libquiche)
Per compilare ed eseguire progetti con http3.cr, è necessario che la libreria nativa libquiche di Cloudflare sia compilata e disponibile nel percorso di ricerca del linker del tuo sistema operativo.
0. Installazione tramite Package Manager (Opzionale - Senza Rust)
Se non vuoi compilare la libreria dai sorgenti, puoi installare libquiche pre-compilata tramite i package manager ove disponibile:
- macOS (Homebrew):
brew install cloudflare-quiche - Arch Linux (pacman):
sudo pacman -S quiche
Nota per Debian/Ubuntu e Windows: Attualmente Cloudflare non rilascia pacchetti binari ufficiali preconfezionati per queste piattaforme nei repository predefiniti. Su tali sistemi si consiglia di compilare tramite Rust come descritto nei passaggi successivi.
1. Installazione delle dipendenze di compilazione (Se compili da sorgente)
Il processo richiede Rust (cargo), CMake e Git installati sulla tua macchina solo se decidi di compilare libquiche partendo dai sorgenti.
macOS
Usa Homebrew per installare le dipendenze:
brew install rust cmake git
Linux Debian / Ubuntu
Installa i pacchetti necessari tramite apt:
sudo apt update
sudo apt install -y build-essential cmake git
# Installa Rust via rustup (raccomandato):
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
Arch Linux
Installa i pacchetti tramite pacman:
sudo pacman -S --needed base-devel cmake git rust
Windows
- Installa Build Tools per Visual Studio selezionando il carico di lavoro "Sviluppo di applicazioni desktop con C++".
- Installa CMake e Git (ad esempio tramite
winget install git.git cmake.cmake). - Installa Rust tramite rustup.
2. Compilazione di libquiche FFI
Clona il repository ufficiale di quiche e compila il target FFI in modalità release:
git clone --recursive https://github.com/cloudflare/quiche.git
cd quiche
cargo build --package quiche --release --features ffi
I file generati saranno situati in target/release/:
- Linux:
libquiche.soelibquiche.a - macOS:
libquiche.dylibelibquiche.a - Windows:
quiche.dllequiche.lib
3. Configurazione dell'ambiente
Configura le variabili d'ambiente per consentire al compilatore di Crystal e al sistema di trovare la libreria FFI.
macOS
Esporta le variabili d'ambiente nel tuo terminale:
export LIBRARY_PATH="/percorso/a/quiche/target/release:$LIBRARY_PATH"
export DYLD_LIBRARY_PATH="/percorso/a/quiche/target/release:$DYLD_LIBRARY_PATH"
Linux (Debian, Arch, ecc.)
Esporta le variabili d'ambiente nel tuo terminale:
export LIBRARY_PATH="/percorso/a/quiche/target/release:$LIBRARY_PATH"
export LD_LIBRARY_PATH="/percorso/a/quiche/target/release:$LD_LIBRARY_PATH"
(In alternativa, copia libquiche.so in /usr/local/lib ed esegui sudo ldconfig)
Windows (PowerShell)
Aggiungi la cartella contenente quiche.dll al PATH e al percorso del linker:
$env:PATH = "C:\percorso\a\quiche\target\release;" + $env:PATH
$env:LIB = "C:\percorso\a\quiche\target\release;" + $env:LIB
Installazione
-
Aggiungi la dipendenza nel file
shard.ymldel tuo progetto:dependencies: http3: github: eltony81/http3.cr -
Esegui
shards install
Esempio Server HTTP/3
Il server HTTP/3 si mette in ascolto su una porta UDP reale, gestisce le nuove connessioni QUIC ed elabora le richieste HTTP/3 in arrivo tramite il gestore degli eventi (poll):
require "http3"
require "socket"
# 1. Configurazione QUIC e HTTP/3
config = Quic::Config.new
config.set_application_protos(["h3"])
config.load_cert_chain_from_pem_file("spec/fixtures/cert.pem")
config.load_priv_key_from_pem_file("spec/fixtures/key.pem")
# Configurazione dei limiti degli stream H3
config.set_initial_max_streams_bidi(1000_u64)
config.set_initial_max_streams_uni(1000_u64)
config.set_initial_max_stream_data_bidi_local(1048576_u64)
config.set_initial_max_stream_data_bidi_remote(1048576_u64)
config.set_initial_max_stream_data_uni(1048576_u64)
config.set_initial_max_data(1048576_u64)
config.set_max_idle_timeout(30000_u64)
# 2. Bind del socket UDP e avvio SocketHandler
server_socket = UDPSocket.new
server_socket.bind("127.0.0.1", 4433)
server_socket.blocking = false
handler = Quic::SocketHandler.new(server_socket)
handler.start_server_loop(config)
h3_config = Quic::H3::Config.new
h3_connections = {} of Bytes => Quic::H3::Connection
puts "Server HTTP/3 in ascolto su udp://127.0.0.1:4433..."
# 3. Loop principale per elaborare le connessioni e le richieste
spawn do
buf = Bytes.new(4096)
loop do
# Controlla tutte le connessioni QUIC attive nel server
handler.connections.each do |scid, conn|
next unless conn.established?
# Inizializza lo stato HTTP/3 per la connessione
h3_conn = h3_connections[scid] ||= Quic::H3::Connection.new(conn, h3_config)
# Polling degli eventi HTTP/3 sulla connessione
loop do
stream_id, ev = h3_conn.poll(conn)
break if ev.nil?
case ev.event_type
when 0 # HEADERS (Richiesta ricevuta)
puts "[Server] Ricevuti header sulla stream #{stream_id}:"
ev.headers.each { |k, v| puts " #{k}: #{v}" }
# Invia gli header di risposta
resp_headers = [
{":status", "200"},
{"content-type", "text/plain"},
{"server", "CrystalHTTP3Server"}
]
h3_conn.send_response(conn, stream_id.to_u64, resp_headers, false)
# Invia il body di risposta e chiudi lo stream (fin = true)
body = "Hello from Crystal HTTP/3 Server!".to_slice
h3_conn.send_body(conn, stream_id.to_u64, body, true)
# Invia immediatamente i pacchetti generati sul socket
handler.send_pending(conn)
when 1 # DATA
body_len = h3_conn.recv_body(conn, stream_id.to_u64, buf)
puts "[Server] Ricevuto DATA: #{String.new(buf[0, body_len])}"
end
end
end
sleep 1.millisecond
end
end
sleep # Mantiene attivo il programma principale
Esempio Client HTTP/3
Il client si connette al server UDP via QUIC, istanzia il client HTTP/3 ed invia una richiesta GET leggendo la risposta:
require "http3"
require "socket"
# 1. Configurazione client
config = Quic::Config.new
config.set_application_protos(["h3"])
config.verify_peer(false) # Per certificati auto-firmati
config.set_initial_max_streams_bidi(1000_u64)
config.set_initial_max_streams_uni(1000_u64)
config.set_initial_max_data(1048576_u64)
client_socket = UDPSocket.new
client_socket.bind("127.0.0.1", 0)
client_socket.blocking = false
scid = Bytes.new(16)
Random.new.random_bytes(scid)
server_addr = Socket::IPAddress.new("127.0.0.1", 4433)
client_conn = Quic::Connection.connect("localhost", scid, client_socket.local_address, server_addr, config)
handler = Quic::SocketHandler.new(client_socket)
handler.register_connection(scid, client_conn)
# Avvia loop di lettura asincrono per il client
spawn do
buf = Bytes.new(65535)
while !client_socket.closed?
begin
bytes_read, from_addr = client_socket.receive(buf)
if bytes_read > 0
client_conn.recv(buf[0, bytes_read], from_addr, client_socket.local_address)
handler.send_pending(client_conn)
end
rescue IO::Error
break
end
end
end
# Invia pacchetto di handshake iniziale
handler.send_pending(client_conn)
puts "Connessione in corso..."
while !client_conn.established?
sleep 10.milliseconds
end
puts "Connessione QUIC Stabilita!"
# 2. Inizializza HTTP/3
h3_config = Quic::H3::Config.new
h3_client = Quic::H3::Connection.new(client_conn, h3_config)
sleep 50.milliseconds # Attesa negoziazione settings H3 iniziali
# 3. Invia la richiesta HTTP/3
req_headers = [
{":method", "GET"},
{":scheme", "https"},
{":authority", "localhost"},
{":path", "/"}
]
stream_id = h3_client.send_request(client_conn, req_headers, true)
handler.send_pending(client_conn)
puts "Richiesta inviata sulla stream #{stream_id}."
# 4. Leggi la risposta
response_done = false
buf = Bytes.new(4096)
while !response_done
loop do
status, ev = h3_client.poll(client_conn)
break if ev.nil?
case ev.event_type
when 0 # HEADERS
puts "[Client] Ricevuti header di risposta:"
ev.headers.each { |k, v| puts " #{k}: #{v}" }
when 1 # DATA
body_len = h3_client.recv_body(client_conn, status.to_u64, buf)
puts "[Client] Ricevuto Body: #{String.new(buf[0, body_len])}"
response_done = true
end
end
sleep 1.millisecond
end
client_socket.close
Compatibilità con la Libreria Standard HTTP di Crystal
La libreria http3.cr fornisce moduli ad alto livello (HTTP3::Client e HTTP3::Server) progettati specificamente per essere compatibili con le classi standard di Crystal (HTTP::Client, HTTP::Server, HTTP::Request, HTTP::Client::Response, HTTP::Server::Context e HTTP::Handler), consentendo di integrare HTTP/3 con il minimo sforzo e supportando anche il middleware di Crystal.
Client HTTP/3 ad Alto Livello
Il client HTTP3::Client espone lo stesso set di metodi della libreria standard (get, post, put, delete, ed exec) e restituisce una risposta di tipo HTTP::Client::Response standard:
require "http3"
# Istanzia un client HTTP/3 connesso via QUIC
client = HTTP3::Client.new("localhost", 4433)
# Effettua richieste GET o POST usando classi standard
response = client.get("/percorso")
puts response.status_code # => 200
puts response.body # => "..."
# Supporta l'invio di istanze HTTP::Request complesse
request = HTTP::Request.new("POST", "/upload", HTTP::Headers{"Content-Type" => "application/json"}, "{\"data\": 123}")
response = client.exec(request)
client.close
Server HTTP/3 ad Alto Livello
Il server HTTP3::Server accetta i medesimi tipi di handler e middleware della libreria standard di Crystal. È possibile passare un blocco che riceve un HTTP::Server::Context standard (con request e response), oppure definire una catena di HTTP::Handler:
require "http3"
# 1. Configura il certificato TLS per il server HTTP/3
config = Quic::Config.new
config.load_cert_chain_from_pem_file("spec/fixtures/cert.pem")
config.load_priv_key_from_pem_file("spec/fixtures/key.pem")
h3_config = Quic::H3::Config.new
# 2. Definisce un handler o middleware compatibile con la libreria standard
server = HTTP3::Server.new("127.0.0.1", 4433, config, h3_config) do |context|
context.response.content_type = "text/plain"
context.response.print "Hello World from HTTP/3 Server!"
end
# 3. Avvia l'ascolto UDP per le connessioni HTTP/3
puts "Server HTTP/3 avviato..."
server.listen
Sviluppo e Test
Per far girare i test unitari di questa shard:
LIBRARY_PATH=./usr/lib LD_LIBRARY_PATH=./usr/lib crystal spec
Licenza
Il progetto è rilasciato sotto licenza MIT.
http3.cr
- 0
- 0
- 0
- 0
- 1
- about 4 hours ago
- June 14, 2026
MIT License
Sun, 14 Jun 2026 20:08:34 GMT