Quiche est une implémentation du protocole de transport de Quic et HTTP / 3 comme spécifié par l'IETF. Il fournit une API de bas niveau pour le traitement des paquets de quic et la gestion de l'état de connexion. L'application est chargée de fournir des E / S (par exemple, la gestion des sockets) ainsi qu'une boucle d'événements avec le support pour les minuteries.
Pour plus d'informations sur la façon dont la quiche est née et quelques informations sur sa conception, vous pouvez lire un article sur le blog de Cloudflare qui va plus en détail.
Quiche alimente la prise en charge HTTP / 3 du réseau Cloudflare Edge. Le site Web Cloudflare-quic.com peut être utilisé pour les tests et l'expérimentation.
DNS Resolver d'Android utilise la quiche pour implémenter DNS sur HTTP / 3.
La quiche peut être intégrée dans Curl pour fournir une prise en charge de HTTP / 3.
La quiche peut être intégrée dans Nginx en utilisant un patch non officiel pour fournir une prise en charge de HTTP / 3.
Avant de plonger dans l'API de quiche, voici quelques exemples sur la façon d'utiliser les outils de quiches fournis dans la caisse de quiche-apps.
Après le clonage du projet selon la commande mentionnée dans la section du bâtiment, le client peut être exécuté comme suit:
$ Cargo Run - Bin Quiche-Client - https://cloudflare-quic.com/
tandis que le serveur peut être exécuté comme suit:
$ Cargo Run - Bin Quiche-Server - - CERT APPS / SRC / BIN / CERT.CRT --KEKE APPS / SRC / BIN / CERT.KEY
(Notez que le certificat fourni est auto-signé et ne doit pas être utilisé en production)
Utilisez l'indicateur de ligne de commande --help
pour obtenir une description plus détaillée des options de chaque outil.
La première étape pour établir une connexion Quic à l'aide de quiche consiste à créer un objet Config
:
Laissez mut config = Quiche :: Config :: new (Quiche :: Protocol_version)?; config.set_application_protos (& [b "Exemple-proto"]); // Configuration supplémentaire spécifique à l'application et au cas d'utilisation ...
L'objet Config
contrôle les aspects importants de la connexion Quic tels que la version Quic, les ID ALPN, le contrôle de l'écoulement, le contrôle de la congestion, le délai d'inactivité et d'autres propriétés ou fonctionnalités.
Quic est un protocole de transport à usage général et il existe plusieurs propriétés de configuration où il n'y a pas de valeur par défaut raisonnable. Par exemple, le nombre autorisé de flux simultanés de tout type particulier dépend de l'application exécutée sur Quic et d'autres préoccupations spécifiques à des cas d'utilisation.
Quiche fait par défaut plusieurs propriétés à zéro, les applications doivent probablement les définir sur autre chose pour satisfaire leurs besoins en utilisant les éléments suivants:
set_initial_max_streams_bidi()
set_initial_max_streams_uni()
set_initial_max_data()
set_initial_max_stream_data_bidi_local()
set_initial_max_stream_data_bidi_remote()
set_initial_max_stream_data_uni()
Config
contient également la configuration TLS. Cela peut être modifié par des mutateurs sur un objet existant, ou en construisant manuellement un contexte TLS et en créant une configuration à l'aide with_boring_ssl_ctx_builder()
.
Un objet de configuration peut être partagé entre plusieurs connexions.
Sur le côté client, la fonction utilitaire connect()
peut être utilisée pour créer une nouvelle connexion, tandis que accept()
est pour les serveurs:
// Client Connection.let Conn = Quiche :: Connect (Some (& Server_Name), & Scid, Local, Peer, & Mut Config) ?; // Server Connection.let Conn = Quiche :: Accept (& scid, Aucun, local, pair, & mut config)?;
À l'aide de la méthode recv()
de la connexion, l'application peut traiter des paquets entrants qui appartiennent à cette connexion à partir du réseau:
Selt to = socket.local_addr (). Unfrap (); Loop {let (read, from) = socket.recv_from (& mut buf) .unwrap (); let recv_info = Quiche :: recvinfo {from, to}; LET LEAD = correspond à Conn.recv (& mut buf [.. read], recv_info) {ok (v) => v, err (e) => {// Une erreur s'est produite, le gérer.break;},};}
Les paquets sortants sont générés à l'aide de la méthode send()
de la connexion:
LOOP {LET (WRITE, SEND_INFO) = MATCH CONN.SEND (& MUS e) => {// Une erreur s'est produite, le gérer.break;},}; socket.send_to (& out [.. write], & send_info.to) .unwrap ();}
Lorsque des paquets sont envoyés, la demande est responsable du maintien d'une minuterie pour réagir aux événements de connexion basés sur le temps. L'expiration du temporisateur peut être obtenue à l'aide de la méthode timeout()
de la connexion.
LET TimeOut = Conn.Timeout ();
L'application est chargée de fournir une implémentation de temporisation, qui peut être spécifique au système d'exploitation ou au cadre de mise en réseau utilisé. Lorsqu'une minuterie expire, la méthode on_timeout()
de la connexion doit être appelée, après quoi des paquets supplémentaires pourraient avoir besoin d'être envoyés sur le réseau:
// Timeout expiré, gérez-le.conn.on_timeout (); // Envoyez plus de paquets au besoin après timeout.loop {let (écrire, send_info) = correspondre à conn.send (& mut out) {ok (v) => v, Err (Quiche :: Error :: Done) => {// Done Writing.break;}, err (e) => {// Une erreur s'est produite, manche it.break;},}; socket.send_to (& out [.. write], & send_info.to) .unwrap ();}
Il est recommandé que les applications soient un rythme d'envoi de paquets sortants pour éviter de créer des rafales de paquets qui pourraient provoquer une congestion à court terme et des pertes dans le réseau.
Quiche expose des conseils de stimulation pour les paquets sortants via le champ [ at
] de la structure [ SendInfo
] qui est renvoyé par la méthode send()
. Ce champ représente le moment où un paquet spécifique doit être envoyé dans le réseau.
Les applications peuvent utiliser ces indices en retardant artificiellement l'envoi de paquets via des mécanismes spécifiques à la plate-forme (tels que l'option SO_TXTIME
Socket sur Linux), ou des méthodes personnalisées (par exemple en utilisant des minuteries d'espace utilisateur).
Après quelques allers-retours, la connexion terminera sa poignée de main et sera prête à envoyer ou à recevoir des données d'application.
Les données peuvent être envoyées sur un flux en utilisant la méthode stream_send()
:
Si Conn.is_establowed () {// poignée de main terminée, envoyez des données sur le flux 0.Conn.Stream_Send (0, B "Hello", true)?;}
L'application peut vérifier s'il existe des flux lisibles en utilisant la méthode readable()
de la connexion, qui renvoie un itérateur sur tous les flux qui ont des données exceptionnelles à lire.
La méthode stream_recv()
peut ensuite être utilisée pour récupérer les données d'application à partir du flux lisible:
Si Conn.is_established () {// itérater sur des flux lisibles. (stream_id, & mut buf) {println! ("got {} octets on stream {}", read, stream_id);}}}
Le module HTTP / 3 de Quiche fournit une API de haut niveau pour envoyer et recevoir des demandes et réponses HTTP en plus du protocole de transport de Quic.
Jetez un œil au répertoire [Quiche / Exemples /] pour des exemples plus complets sur la façon d'utiliser l'API de Quiche, y compris des exemples sur la façon d'utiliser des quiches dans les applications C / C ++ (voir ci-dessous pour plus d'informations).
Quiche expose une mince API C sur le dessus de l'API Rust qui peut être utilisée pour intégrer plus facilement la quiche dans les applications C / C ++ (ainsi que dans d'autres langues qui permettent d'appeler les API C via une forme de FFI). L'API C suit la même conception de la rouille, modulo les contraintes imposées par la langue C elle-même.
Lors de cargo build
, une bibliothèque statique appelée libquiche.a
sera construite automatiquement aux côtés de la rouille. Ceci est entièrement autonome et peut être lié directement à des applications C / C ++.
Notez que pour activer l'API FFI, la fonction ffi
doit être activée (elle est désactivée par défaut), en passant --features ffi
à cargo
.
La quiche nécessite une rouille 1,67 ou plus tard pour construire. La dernière version de rouille stable peut être installée à l'aide de rustup.
Une fois l'environnement de construction de rouille configuré, le code source de quiche peut être récupéré à l'aide de Git:
$ git clone ---recursive https://github.com/cloudflare/Quiche
puis construit à l'aide de la cargaison:
$ cargo build - Exemples
La cargaison peut également être utilisée pour exécuter la rédaction de test:
$ test de cargaison
Notez que BoringSSL, qui est utilisé pour implémenter la poignée de main cryptographique de Quic basée sur TLS, doit être construite et liée à Quiche. Cela se fait automatiquement lors de la construction de quiche à l'aide de la cargaison, mais nécessite que la commande cmake
soit disponible pendant le processus de construction. Sur les fenêtres, vous avez également besoin de NASM. La documentation officielle de BoringSSL a plus de détails.
En alternative, vous pouvez utiliser votre propre version personnalisée de BoringSSL en configurant le répertoire BoringSSL avec la variable d'environnement QUICHE_BSSL_PATH
:
$ Quiche_bssl_path = "/ path / to / boringssl" cargo build --examples
Vous pouvez également utiliser OpenSSL / QUICTLS. Pour permettre à la quiche d'utiliser ce fournisseur, la fonction openssl
peut être ajoutée à la liste --feature
. Sachez que 0-RTT
n'est pas pris en charge si ce fournisseur est utilisé.
Building Quiche pour Android (version NDK 19 ou supérieure, 21 recommandée), peut être effectué à l'aide de Cargo-NDK (v2.0 ou version ultérieure).
Tout d'abord, le NDK Android doit être installé, en utilisant Android Studio ou directement, et la variable environnementale ANDROID_NDK_HOME
doit être définie sur le chemin d'installation NDK, par exemple:
$ export Android_ndk_home = / usr / local / share / android-ndk
Ensuite, la chaîne d'outils Rust pour les architectures Android nécessaires peut être installée comme suit:
$ rustup Target Ajouter Aarch64-Linux-Android Armv7-Linux-AndroidAbi i686-Linux-Android x86_64-Linux-Android
Notez que le niveau API minimum est de 21 pour toutes les architectures cibles.
Cargo-NDK (v2.0 ou version ultérieure) doit également être installé:
$ cargo installer cargo-ndk
Enfin, la bibliothèque de quiche peut être construite en utilisant la procédure suivante. Notez que les options -t <architecture>
et -p <NDK version>
sont obligatoires.
$ cargo ndk -t arm64-v8a -p 21 - build - FFI-FFI
Voir build_android_ndk19.sh pour plus d'informations.
Pour construire une quiche pour iOS, vous avez besoin de ce qui suit:
Installez les outils de ligne de commande xcode. Vous pouvez les installer avec Xcode ou avec la commande suivante:
$ xcode-select - install
Installez la chaîne d'outils Rust pour les architectures iOS:
$ rustup Target Ajouter AARCH64-APPLE-IOS X86_64-APPLE-IOS
Installez cargo-lipo
:
$ cargo installer cargo-lipo
Pour construire libquiche, exécutez la commande suivante:
$ cargo lipo - Ffi de comptes FFI
ou
$ Cargo Lipo - FFI-FFI - Release
La construction iOS est testée dans Xcode 10.1 et Xcode 11.2.
Afin de construire les images Docker, exécutez simplement la commande suivante:
$ faire docker-build
Vous pouvez trouver les images Docker de Quiche sur les référentiels Docker Hub suivants:
cloudflare / quiche
cloudflare / quiche-QNS
La latest
balise sera mise à jour chaque fois que les mises à jour de la Branche Master.
cloudflare / quiche
Fournit un serveur et un client installés dans / usr / local / bin.
cloudflare / quiche-QNS
Fournit le script pour tester la quiche dans le coureur de quic-interopés.
Copyright (C) 2018-2019, Cloudflare, Inc.
Voir Copie pour la licence.