Quiche es una implementación del Protocolo de Transporte de Quic y HTTP/3 según lo especificado por el IETF. Proporciona una API de bajo nivel para procesar los paquetes de Quic y el estado de manejo de la conexión. La aplicación es responsable de proporcionar E/S (por ejemplo, manejo de enchufes), así como un bucle de eventos con soporte para temporizadores.
Para obtener más información sobre cómo surgió Quiche y algunas ideas sobre su diseño, puede leer una publicación en el blog de Cloudflare que entra en más detalles.
Soporte HTTP/3 de Quiche Powers Support HTTP/3 de Cloudflare Network. El sitio web de Cloudflare-quic.com se puede utilizar para probar y experimentar.
El resolución DNS de Android usa Quiche para implementar DNS a través de HTTP/3.
El quiche se puede integrar en curl para proporcionar soporte para HTTP/3.
El quiche se puede integrar en NGINX utilizando un parche no oficial para proporcionar soporte para HTTP/3.
Antes de sumergirse en la API del quiche, aquí hay algunos ejemplos sobre cómo usar las herramientas del quiche proporcionadas como parte de la caja de las aplicaciones de quiche.
Después de clonar el proyecto de acuerdo con el comando mencionado en la sección del edificio, el cliente se puede ejecutar de la siguiente manera:
$ Cargo Run --Bin Quiche-Client-https://cloudflare-quic.com/
Mientras que el servidor se puede ejecutar de la siguiente manera:
$ Cargo Run--Bin Quiche-Server---Cert Apps/Src/bin/cert.crt --key Apps/src/bin/cert.key
(Tenga en cuenta que el certificado proporcionado es autofirmado y no debe usarse en la producción)
Use el indicador de línea de comandos --help para obtener una descripción más detallada de las opciones de cada herramienta.
El primer paso para establecer una conexión Quic usando Quiche es crear un objeto Config :
Let Mut config = quiche :: config :: new (quiche :: protocol_version)?; config.set_application_protas (& [b "ejemplo-propo"]); // Configuración adicional específica para la aplicación y el caso de uso ...
El objeto Config controla aspectos importantes de la conexión Quic, como la versión Quic, los ID de ALPN, el control de flujo, el control de congestión, el tiempo de espera inactivo y otras propiedades o características.
Quic es un protocolo de transporte de uso general y hay varias propiedades de configuración en las que no hay un valor predeterminado razonable. Por ejemplo, el número permitido de secuencias concurrentes de cualquier tipo en particular depende de la aplicación que se ejecuta a través de Quic y otras preocupaciones específicas de casos de uso.
Quiche predeterminado varias propiedades a cero, las aplicaciones probablemente necesiten establecerlas en otra cosa para satisfacer sus necesidades utilizando lo siguiente:
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 también contiene la configuración de TLS. Los mutadores pueden cambiar esto en un objeto existente, o construyendo un contexto TLS manualmente y creando una configuración usando with_boring_ssl_ctx_builder() .
Se puede compartir un objeto de configuración entre múltiples conexiones.
En el lado del cliente, la función de utilidad connect() se puede usar para crear una nueva conexión, mientras que accept() es para servidores:
// Client Connection.let Conn = Quiche :: Connect (Some (& Server_Name), & Scid, Local, Peer y Mut Config) ?; // Server Connection.let Conn = Quiche :: Acepter (& Scid, None, Local, Peer, Peer, & Mut config)?;
Utilizando el método recv() de la conexión, la aplicación puede procesar paquetes entrantes que pertenecen a esa conexión desde la red:
Let to = Socket.local_addr (). Unwrap (); Loop {LET (lea, from) = Socket.recv_from (& Mut buf) .unwrap (); Let Recv_info = Quiche :: Recvinfo {from, to}; LET lEt = Match Conn.Recv (& Mut buf [.. Read], Recv_info) {Ok (V) => V, Err (E) => {// ocurrió un error, manejarlo.break;},};} El paquete saliente se genera utilizando el método send() de la conexión en su lugar:
bucle {let (escribir, send_info) = coincidir conn.send (& mut out) {ok (v) => v, err (quiche :: error :: ded) => {// ded writing.break;}, err (err ( e) => {// ocurrió un error, manejar it.break;},}; socket.send_to (& out [.. write], & send_info.to) .unwrap ();} Cuando se envían paquetes, la aplicación es responsable de mantener un temporizador para reaccionar a los eventos de conexión basados en el tiempo. La expiración del temporizador se puede obtener utilizando el método timeout() de la conexión.
Let timeOut = conn.timeout ();
La aplicación es responsable de proporcionar una implementación de temporizador, que puede ser específica para el sistema operativo o el marco de red utilizado. Cuando expira un temporizador, se debe llamar al método on_timeout() de la conexión, después de lo cual los paquetes adicionales pueden ser necesarios en la red:
// Tiempo de espera expirado, manejable.conn.on_timeout (); // Envía más paquetes según sea necesario después de Timeout.loop {LET (Write, Send_info) = Match Conn.send (& Mut Out) {Ok (V) => V, Err (quiche :: error :: ded) => {// ded writing.break;}, err (e) => {// ocurrió un error, manejar it.break;},}; Socket.send_to (& out [.. Write], & send_info.to) .unwrap ();}Se recomienda que las aplicaciones se encuentren en el envío de paquetes salientes para evitar crear ráfagas de paquetes que puedan causar congestión y pérdidas a corto plazo en la red.
Quiche expone pistas de ritmo para paquetes salientes a través del campo [ at ] de la estructura [ SendInfo ] que devuelve el método send() . Este campo representa el momento en que se debe enviar un paquete específico a la red.
Las aplicaciones pueden usar estos sugerencias retrasando artificialmente el envío de paquetes a través de mecanismos específicos de la plataforma (como la opción SO_TXTIME Socket en Linux) o métodos personalizados (por ejemplo, utilizando temporizadores del espacio de usuario).
Después de un poco de ida y vuelta, la conexión completará su apretón de manos y estará listo para enviar o recibir datos de solicitud.
Los datos se pueden enviar en un flujo utilizando el método stream_send() :
Si conn.is_established () {// handshake completado, envíe algunos datos en el flujo 0.conn.stream_send (0, b "hola", true)?;} La aplicación puede verificar si hay transmisiones legibles utilizando el método readable() de la conexión, que devuelve un iterador sobre todas las secuencias que tienen datos sobresalientes para leer.
El método stream_recv() se puede usar para recuperar los datos de la aplicación de la secuencia legible:
Si conn.is_established () {// iterar sobre secuencias legibles. para stream_id en conn.readable () {// stream es legible, lea hasta que no haya más datos. (stream_id, & mut buf) {println! ("Got {} bytes en la transmisión {}", read, stream_id);}}}El módulo de quiche HTTP/3 proporciona una API de alto nivel para enviar y recibir solicitudes y respuestas HTTP sobre el Protocolo de transporte de Quic.
Eche un vistazo al directorio [Quiche/Ejemplos/] para ejemplos más completos sobre cómo usar la API de Quiche, incluidos ejemplos sobre cómo usar el quiche en aplicaciones C/C ++ (ver más abajo para obtener más información).
Quiche expone una API C delgada en la parte superior de la API de óxido que se puede utilizar para integrar más fácilmente el quiche en aplicaciones C/C ++ (así como en otros idiomas que permiten llamar a C API a través de alguna forma de FFI). La API C sigue el mismo diseño del Rust One, Modulo las limitaciones impuestas por el lenguaje C en sí.
Al ejecutar cargo build , una biblioteca estática llamada libquiche.a se construirá automáticamente junto con la de óxido. Esto es completamente independiente y se puede vincular directamente en aplicaciones C/C ++.
Tenga en cuenta que para habilitar la API FFI, la función ffi debe estar habilitada (está deshabilitada de forma predeterminada), pasando --features ffi a cargo .
Quiche requiere Rust 1.67 o más tarde para construir. El último lanzamiento de óxido estable se puede instalar usando Rustup.
Una vez que se configura el entorno de construcción de óxido, el código fuente del quiche se puede obtener usando GIT:
$ Git Clone ---Cursive https://github.com/cloudflare/quiche
y luego construido con carga:
$ Cargo Build -Examples
La carga también se puede usar para ejecutar el testSuite:
$ prueba de carga
Tenga en cuenta que BoringsSl, que se utiliza para implementar el apretón de manos criptográfico de QUIC basado en TLS, debe ser construido y vinculado al quiche. Esto se hace automáticamente cuando se construye Quiche usando carga, pero requiere que el comando cmake esté disponible durante el proceso de compilación. En Windows también necesitas NASM. La documentación oficial de BoringsSL tiene más detalles.
En alternativa, puede usar su propia compilación personalizada de BoringsSl configurando el directorio BoringsSL con la variable de entorno QUICHE_BSSL_PATH :
$ Quiche_bssl_path = "/rath/to/boringssl" construcción de carga -Examples
Alternativamente, puede usar OpenSSL/Quictls. Para habilitar el quiche para usar este proveedor, la función openssl se puede agregar a la lista --feature . Tenga en cuenta que 0-RTT no es compatible si se usa este proveedor.
La construcción del quiche para Android (NDK versión 19 o superior, 21 recomendado), se puede hacer con carga-NDK (v2.0 o posterior).
Primero, el NDK de Android debe instalarse, ya sea usando Android Studio o directamente, y la variable de entorno ANDROID_NDK_HOME debe establecerse en la ruta de instalación NDK, por ejemplo:
$ Export Android_ndk_home =/usr/local/share/android-ndk
Luego, la cadena de herramientas Rust para las arquitecturas de Android necesarias se puede instalar de la siguiente manera:
$ Rustup Target Agregar AARCH64-Linux-Android ARMV7-Linux-Androideabi I686-Linux-Android x86_64-Linux-Android
Tenga en cuenta que el nivel mínimo de API es 21 para todas las arquitecturas objetivo.
Cargo-NDK (v2.0 o posterior) también debe instalarse:
$ Cargo Instale Cargo-NDK
Finalmente, la biblioteca del quiche se puede construir utilizando el siguiente procedimiento. Tenga en cuenta que las opciones -t <architecture> y -p <NDK version> son obligatorias.
$ CARGO NDK -T ARM64 -V8A -P 21 -Build -Finge FFI
Consulte Build_android_ndk19.sh para obtener más información.
Para construir quiche para iOS, necesitas lo siguiente:
Instale herramientas de línea de comandos Xcode. Puede instalarlos con Xcode o con el siguiente comando:
$ Xcode-Select-Install
Instale la cadena de herramientas de Rust para las arquitecturas iOS:
$ Rustup Target Agregar AARCH64-APPLE-IOS X86_64-APPLE-IIS
Instale cargo-lipo :
$ Cargo Instale Lipo de carga
Para construir libquiche, ejecute el siguiente comando:
$ Cargo Lipo -FFI FFI
o
$ Cargo Lipo -FFI FFI -Lanza
La compilación de iOS se prueba en Xcode 10.1 y Xcode 11.2.
Para construir las imágenes de Docker, simplemente ejecute el siguiente comando:
$ Make Docker-Build
Puede encontrar las imágenes de Docker de Quiche en los siguientes repositorios de Docker Hub:
Cloudflare/Quiche
CloudFlare/Quiche-QNS
La latest etiqueta se actualizará cada vez que las actualizaciones de la rama maestra del quiche.
Cloudflare/Quiche
Proporciona un servidor y un cliente instalado en/usr/local/bin.
CloudFlare/Quiche-QNS
Proporciona el script para probar el quiche dentro de la corredora de interpolta de Quic.
Copyright (c) 2018-2019, Cloudflare, Inc.
Vea la copia de la licencia.