Desarrollo#
Angie es un proyecto de código abierto
que da la bienvenida a todos los colaboradores. Puedes clonar el código fuente de Angie desde nuestros repositorios públicos:
Mercurial,
Git. Tus cambios deben ser consistentes con el resto del código de Angie; las convenciones de codificación son un buen punto de partida. Truco En caso de duda, examina el código cercano para seguir su ejemplo,
o simplemente usa grep en la base de código para inspirarte. Históricamente, el registro de commits se mantiene en inglés. Comienza con un resumen de una sola línea de lo que se hizo.
Puede llevar un prefijo que el registro de commits use para la parte de código afectada.
El resumen puede tener hasta 67 caracteres
y puede ir seguido de una línea en blanco y más detalles. Un buen mensaje indica qué causó el cambio, qué se hizo al respecto
y cuál es la situación ahora: Detalles que pueden pasar desapercibidos: El resumen termina con un punto y empieza con una letra mayúscula. Si se usa un prefijo, éste va seguido de una letra minúscula. Dos espacios en blanco separan oraciones dentro de una misma línea. Haz todo lo posible para verificar que los cambios funcionen en todas las plataformas de destino. Para cada plataforma, ejecuta la suite de pruebas para asegurarte de que no haya regresiones: Consulta el archivo Asegúrate de estar cómodo con los términos legales. Para enviar un parche, crea una pull request en nuestro
espejo de GitHub. Para preguntas y sugerencias, ponte en contacto con los desarrolladores a través de
Issues de GitHub. El código fuente sigue la estructura y convenciones siguientes. Las siguientes dos sentencias Además de eso, el código HTTP debe incluir El código de Mail debe incluir El código de Stream debe incluir Para fines generales, el código de Angie utiliza dos tipos enteros:
La mayoría de las funciones en Angie devuelven los siguientes códigos: El macro Los valores de Ejemplo usando Para cadenas en C, Angie utiliza un puntero de tipo carácter sin signo
El tipo de cadena de Angie El campo Las operaciones de cadena en Angie se declaran en
Otras funciones de cadena son específicas de Angie Las siguientes funciones realizan conversión de mayúsculas/minúsculas y comparación: Las siguientes macros simplifican la inicialización de cadenas: Las siguientes funciones de formato admiten tipos específicos de Angie: La lista completa de opciones de formato, admitidas por estas funciones, se encuentra en Puede anteponer Varias funciones para conversión numérica están implementadas en Angie.
Las cuatro primeras convierten una cadena de longitud dada a un entero positivo del tipo indicado.
Devuelven Hay dos funciones adicionales de conversión numérica.
Al igual que las cuatro primeras, devuelven La interfaz de expresiones regulares en Angie es un envoltorio alrededor de la biblioteca
PCRE. El archivo de encabezado correspondiente es Para usar una expresión regular para la coincidencia de cadenas, primero debe compilarse, lo cual generalmente se realiza en la fase de configuración.
Tenga en cuenta que dado que el soporte de PCRE es opcional, todo el código que use la interfaz debe
estar protegido por la macro circundante Después de una compilación exitosa, los campos La expresión regular compilada puede luego usarse para hacer coincidencias con cadenas: Los argumentos de Si hay coincidencias, las capturas pueden accederse de la siguiente manera: La función La estructura La estructura Para obtener la hora actual, por lo general es suficiente acceder a una de las variables globales disponibles, que representan el valor del tiempo almacenado en caché en el formato deseado. Las representaciones en cadena disponibles son: Las macros Para obtener el tiempo explícitamente, use Las siguientes funciones convierten La función El tipo de arreglo Angie Los elementos del arreglo están disponibles en el campo Utilice la llamada Utilice las siguientes funciones para añadir elementos a un arreglo: Si la cantidad de memoria actualmente asignada no es suficiente para alojar
los nuevos elementos, se asigna un nuevo bloque de memoria y los elementos
existentes se copian en él.
El nuevo bloque de memoria suele ser el doble de grande que el existente. En Angie, una lista es una secuencia de arreglos, optimizada para insertar una cantidad potencialmente grande de elementos.
El tipo de lista Los elementos reales se almacenan en partes de la lista, que se definen como sigue: Antes de usarla, una lista debe ser inicializada llamando
Las listas se utilizan principalmente para encabezados HTTP de entrada y salida. Las listas no admiten la eliminación de elementos.
Sin embargo, cuando sea necesario, los elementos pueden marcarse internamente como ausentes sin eliminarse realmente de la lista.
Por ejemplo, para marcar como ausentes los encabezados de salida HTTP (que se almacenan como objetos En Angie, una cola es una lista doblemente enlazada intrusiva, con cada nodo definido como sigue: El nodo cabeza de la cola no está enlazado con ningún dato.
Utilice la llamada Un ejemplo: El archivo de cabecera Para tratar un árbol en su conjunto, necesitas dos nodos: la raíz y la sentinela.
Generalmente, se añaden a una estructura personalizada, lo que te permite
organizar tus datos en un árbol en el que las hojas contienen un enlace a tus datos o incrustan
tus datos. Para inicializar un árbol: Para recorrer un árbol e insertar nuevos valores, usa las funciones
La traversa es bastante directa y puede demostrarse con el siguiente patrón de función de búsqueda: La función Para añadir un nodo al árbol, aloca un nuevo nodo, inicialízalo y llama a
Para eliminar un nodo, llama a la función Las funciones de tablas hash se declaran en Antes de inicializar una hash, necesitas conocer el número de elementos que contendrá para que Angie pueda construirla de forma óptima.
Dos parámetros que deben configurarse son La Las claves del hash se almacenan en Para insertar claves en un arreglo de claves hash, utiliza la
Para construir la tabla hash, llama a la
La función falla si Cuando se construye el hash, usa la
Para crear una hash que funcione con comodines, utiliza el tipo
Es posible añadir claves con comodines usando la bandera
La función reconoce comodines y añade claves a los arreglos correspondientes.
Por favor, consulta la documentación del módulo
Map para la descripción de la sintaxis de comodines y del algoritmo de coincidencia. Dependiendo del contenido de las claves añadidas, podría ser necesario inicializar hasta tres arreglos de claves: uno para la coincidencia exacta (descrita arriba) y dos más para habilitar la coincidencia desde la cabecera o desde la cola de una cadena: The keys array needs to be sorted, and initialization results must be added
to the combined hash.
The initialization of La búsqueda en un hash combinado se maneja mediante
Para asignar memoria desde el heap del sistema, use las siguientes funciones: La mayoría de las asignaciones de Angie se realizan en pools.
La memoria asignada en un pool de Angie se libera automáticamente cuando el pool
se destruye.
Esto proporciona un buen rendimiento de asignación y facilita el control de la memoria. Un pool internamente asigna objetos en bloques continuos de memoria.
Una vez que un bloque se llena, se asigna uno nuevo y se añade a la lista de bloques de memoria del pool.
Cuando la asignación solicitada es demasiado grande para caber en un bloque, la solicitud se envía al asignador del sistema y
el puntero devuelto se almacena en el pool para su liberación posterior. El tipo para los pools de Angie es Las cadenas de enlace (ngx_chain_t) se utilizan activamente en Angie,
por lo que la implementación del pool de Angie proporciona una forma de reutilizarlas.
El campo Se pueden registrar manejadores de limpieza en un pool.
Un manejador de limpieza es una devolución de llamada con un argumento que se llama cuando el pool se destruye.
Un pool suele estar vinculado a un objeto específico de Angie (como una solicitud HTTP) y se destruye cuando el objeto llega al final de su vida.
Registrar una limpieza de pool es una forma conveniente de liberar recursos, cerrar descriptores de archivos o hacer ajustes finales a los datos compartidos asociados con el objeto principal. Para registrar una limpieza de pool, llame
La memoria compartida es utilizada por Angie para compartir datos comunes entre procesos.
La función La estructura de entrada de memoria compartida Las entradas de zonas compartidas se mapean a la memoria real en
Para asignar en memoria compartida, Angie proporciona el tipo de pool en slab
El pool en slab divide toda la zona compartida en páginas.
Cada página se utiliza para asignar objetos del mismo tamaño.
El tamaño especificado debe ser una potencia de 2 y mayor que el tamaño mínimo de
8 bytes.
Los valores no conformes se redondean hacia arriba.
Una máscara de bits para cada página rastrea qué bloques están en uso y cuáles están libres para la
asignación.
Para tamaños mayores que la mitad de una página (que suele ser 2048 bytes), la asignación se realiza una página completa a la vez. Para proteger los datos en la memoria compartida del acceso concurrente, use el mutex
disponible en el campo Para el registro Angie utiliza objetos stderr — Registro a la salida estándar de error (stderr) file — Registro a un archivo syslog — Registro a syslog memory — Registro en el almacenamiento de memoria interna para fines de desarrollo; la memoria
puede ser accedida más tarde con un depurador Una instancia de registrador puede ser una cadena de registradores, vinculados entre sí mediante
el campo Para cada registrador, un nivel de severidad controla qué mensajes se escriben en el
registro (solo se registran los eventos asignados a ese nivel o superior).
Los siguientes niveles de severidad son compatibles: Para el registro de depuración, también se verifica la máscara de depuración.
Las máscaras de depuración son: Normalmente, los registradores se crean por el código existente de Angie a partir de
las directivas Nginx proporciona las siguientes macros de registro: Un mensaje de registro se formatea en un búfer de tamaño
La frase anterior da como resultado entradas de registro como estas: Un objeto ciclo almacena el contexto de ejecución de Angie creado a partir de una configuración específica.
Su tipo es Un ciclo se crea mediante la función Los miembros del ciclo incluyen: cargador de rutas — Se ejecuta solo una vez en 60 segundos después de iniciar o recargar
Angie.
Normalmente, el cargador lee el directorio y almacena datos en la memoria compartida de Angie.
El manejador se llama desde el proceso dedicado de Angie "cache loader". administrador de rutas — Se ejecuta periódicamente.
Normalmente, el administrador elimina archivos antiguos del directorio y actualiza la memoria de Angie para reflejar los cambios.
El manejador se llama desde el proceso dedicado "cache manager". Para operaciones de entrada/salida, Angie proporciona el tipo de búfer La estructura Para operaciones de entrada y salida, los búferes se enlazan en cadenas.
Una cadena es una secuencia de eslabones de cadena del tipo Cada eslabón de la cadena mantiene una referencia a su búfer y una referencia al siguiente eslabón de la cadena. Un ejemplo de uso de búferes y cadenas: Cada enlace de la cadena mantiene una referencia a su búfer y una referencia al siguiente
enlace de la cadena. Un ejemplo de uso de búferes y cadenas: El tipo de conexión Una conexión Angie puede encapsular de manera transparente la capa SSL.
En este caso, el campo La directiva Debido a que el número de conexiones por worker está limitado, Angie proporciona una forma de obtener conexiones que están actualmente en uso.
Para habilitar o deshabilitar la reutilización de una conexión, llama a la función
Las conexiones de cliente HTTP son un ejemplo de conexiones reutilizables en Angie; se marcan como reutilizables hasta que se recibe el primer byte de solicitud del cliente. El objeto de evento Los campos en Cada conexión obtenida al llamar a la función Un evento puede configurarse para enviar una notificación cuando expire un tiempo de espera.
El temporizador utilizado por los eventos cuenta milisegundos desde algún punto anterior no especificado truncados al tipo La función Un evento puede ser publicado, lo que significa que su manejador se llamará en algún momento más tarde dentro de la iteración actual del bucle de eventos.
Publicar eventos es una buena práctica para simplificar el código y evitar desbordamientos de pila.
Los eventos publicados se mantienen en una cola de publicaciones.
La macro Un ejemplo: A excepción del proceso maestro de Angie, todos los procesos de Angie realizan E/S y, por lo tanto, tienen un bucle de eventos.
(El proceso maestro de Angie, en cambio, pasa la mayor parte de su tiempo en la llamada
El bucle de eventos tiene las siguientes etapas: Encontrar el tiempo de espera que esté más cercano a expirar, llamando
Procesar eventos de E/S llamando a un manejador, específico del mecanismo de notificación de eventos, elegido por la configuración de Angie.
Este manejador espera al menos un evento de E/S, pero solo hasta que expire el siguiente
tiempo de espera.
Cuando ocurre un evento de lectura o escritura, se establece la bandera Expirar temporizadores llamando a Procesar eventos publicados llamando a Todos los procesos de Angie manejan señales también.
Los manejadores de señales solo establecen variables globales que se verifican después de la
llamada a Hay varios tipos de procesos en Angie.
El tipo de un proceso se guarda en la variable global Los procesos de Angie manejan las siguientes señales: Aunque todos los procesos trabajadores de Angie pueden recibir y manejar adecuadamente las señales POSIX, el proceso maestro no utiliza la syscall estándar Es posible desagregar a un hilo tareas que, de otro modo, bloquearían al proceso trabajador de Angie.
Por ejemplo, Angie puede configurarse para utilizar hilos para realizar
E/S de archivos.
Otro caso de uso es una biblioteca que no tiene una interfaz asíncrona
y, por lo tanto, no puede usarse normalmente con Angie.
Tenga en cuenta que la interfaz de hilos es una ayuda para el enfoque asíncrono existente para procesar las conexiones de clientes, y de ninguna manera se pretende como un reemplazo. Para gestionar la sincronización, están disponibles los siguientes wrappers sobre las primitivas de
En lugar de crear un nuevo hilo para cada tarea, Angie implementa
una estrategia de thread_pool.
Se pueden configurar múltiples pools de hilos para distintos fines
(por ejemplo, realizar E/S en diferentes conjuntos de discos).
Cada pool de hilos se crea al inicio y contiene un número limitado de hilos
que procesan una cola de tareas.
Cuando se completa una tarea, se llama a un manejador de finalización predefinido. El encabezado En el momento de la configuración, un módulo que desee usar hilos debe obtener una referencia a un grupo de hilos llamando a
Para añadir un Para ejecutar una función en un hilo, pasa los parámetros y configura un manejador de finalización usando la estructura Cada módulo independiente de Angie reside en un directorio separado que contiene al menos dos archivos:
El archivo Los siguientes módulos se suelen usar como referencias.
El Por defecto, los módulos de filtro se colocan antes del
Para compilar un módulo en Angie de forma estática, usa el
argumento Los módulos son los bloques de construcción de Angie, y la mayor parte de su funcionalidad se
implanta como módulos.
El archivo fuente de un módulo debe contener una variable global de tipo
La parte privada omitida incluye la versión del módulo y una firma y se llena usando la macro predefinida Cada módulo mantiene sus datos privados en el campo Los controladores de directivas de configuración se llaman a medida que aparecen
en los archivos de configuración, en el contexto del proceso maestro. Después de que la configuración se analiza con éxito, el manejador El proceso maestro crea uno o más procesos de trabajo y el
manejador Cuando un proceso de trabajo recibe el comando de apagado o terminación del
maestro, invoca el manejador El proceso maestro llama al manejador Debido a que los hilos se utilizan en Angie únicamente como una facilidad de E/S suplementaria con su propia API, los manejadores El tipo de módulo El El conjunto de módulos núcleo incluye los módulos:
donde el Por ejemplo, un módulo simplista llamado El tipo Termine el arreglo con el valor especial Las banderas para los tipos de directiva son: El contexto de una directiva define dónde puede aparecer en la configuración: El analizador de configuración utiliza estas banderas para generar un error en caso de una directiva mal ubicada y llama a los manejadores de directivas proporcionados con un puntero de configuración adecuado, de modo que las mismas directivas en diferentes ubicaciones puedan almacenar sus valores en lugares distintos. El campo El campo El El El argumento Cada conexión de cliente HTTP pasa por las siguientes etapas: Para cada solicitud HTTP de cliente se crea el objeto Cabe señalar que para las conexiones HTTP, el campo Normalmente, una solicitud se pubica mediante la llamada
Cada módulo HTTP puede tener tres tipos de configuración: Configuración principal — Se aplica a todo el bloque Configuración del servidor — Se aplica a un único bloque Configuración de ubicación — Se aplica a un único bloque Las estructuras de configuración se crean en la etapa de configuración de Angie invocando funciones, que asignan las estructuras, las inicializan y las fusionan.
El siguiente ejemplo muestra cómo crear una configuración de ubicación simple para un módulo.
La configuración tiene una única opción, Como se ve en el ejemplo, la función Las siguientes macros están disponibles
para acceder a la configuración de los módulos HTTP en tiempo de configuración.
Todas ellas toman una referencia El siguiente ejemplo obtiene un puntero a una configuración de ubicación del
módulo central estándar de Angie core module
y reemplaza el manejador de contenido de la ubicación guardado
en el campo Las siguientes macros están disponibles para acceder a la configuración de los módulos HTTP
en tiempo de ejecución. Estas macros reciben una referencia a una solicitud HTTP
Cada solicitud HTTP pasa por una secuencia de fases.
En cada fase se realiza un tipo distinto de procesamiento de la solicitud.
Los manejadores específicos de los módulos pueden registrarse en la mayoría de las fases,
y muchos módulos estándar de Angie registran sus manejadores de fase como una forma de
ser llamados en una etapa específica del procesamiento de la solicitud.
Las fases se procesan de forma sucesiva y los manejadores de fase se llaman
una vez que la solicitud llega a la fase.
A continuación se muestra la lista de fases HTTP de Angie. A continuación se muestra un ejemplo de un manejador de la fase preacceso. Phase handlers are expected to return specific codes: Cualquier otro valor devuelto por el manejador de fase se trata como
un código de finalización de la solicitud, en particular, un código de
respuesta HTTP.
La solicitud se finaliza con el código proporcionado. Para algunas fases, los códigos de retorno se tratan de una manera ligeramente
diferente.
En la fase de contenido, cualquier código de retorno distinto de
El
nginx-dev-examples
repositorio proporciona ejemplos de módulos Angie. El ancho máximo de texto es de 80 caracteres La sangría es de 4 espacios No se permiten tabulaciones, ni espacios al final Los elementos de la lista en la misma línea se separan con espacios Los literales hexadecimales están en minúsculas Los nombres de archivos, nombres de funciones y tipos, y las variables globales tienen el
prefijo Un archivo fuente típico puede contener las siguientes secciones separadas por
dos líneas en blanco: declaraciones de derechos de autor inclusiones definiciones de preprocesador definiciones de tipos prototipos de funciones definiciones de variables definiciones de funciones Las declaraciones de derechos de autor se ven así: Si el archivo se modifica significativamente, la lista de autores debe actualizarse,
el nuevo autor se añade en la parte superior. Los archivos Los archivos de cabecera deben incluir la denominada "protección de cabecera": Los comentarios con el texto se escribe en inglés, se prefiere la ortografía estadounidense los comentarios multilínea se formatean así: Los nombres de macros comienzan con Las condiciones están dentro de paréntesis, la negación está fuera: Los nombres de tipos terminan con el sufijo Los tipos de estructuras se definen usando Mantenga la alineación idéntica entre diferentes estructuras en el archivo.
Una estructura que apunta a sí misma tiene el nombre que termina con
" Cada miembro de una estructura se declara en su propia línea: Los punteros de funciones dentro de las estructuras tienen tipos definidos que terminan
con " Las enumeraciones tienen tipos que terminan con " Las variables se declaran ordenadas por la longitud del tipo base, luego alfabéticamente.
Los nombres de tipos y nombres de variables están alineados.
Las "columnas" de tipo y nombre se separan con dos espacios.
Los arreglos grandes se colocan al final de un bloque de declaraciones: Las variables estáticas y globales pueden ser inicializadas en la declaración: Existen numerosas combinaciones comunes de tipos/nombres: Todas las funciones (incluso las estáticas) deben tener prototipos.
Los prototipos incluyen los nombres de los argumentos.
Los prototipos largos se envuelven con una sangría única en las líneas de continuación: El nombre de la función en una definición empieza en una nueva línea.
Las llaves de apertura y cierre del cuerpo de la función están en líneas separadas.
El cuerpo de una función está indentado.
Hay dos líneas en blanco entre funciones: No hay espacio entre el nombre de la función y el paréntesis de apertura.
Las llamadas largas a funciones se envuelven de modo que las líneas de continuación comienzan
desde la posición del primer argumento de la función.
Si esto es imposible, formatee la primera línea de continuación para que termine en la posición 79: La macro Los operadores binarios, excepto Los casteos están separados por un espacio de las expresiones a las que se aplican las conversiones.
Un asterisco dentro del casteo está separado por un espacio del nombre del tipo: Si una expresión no cabe en una sola línea, se envuelve.
El punto preferido para romper una línea es un operador binario.
La línea de continuación se alinea con el inicio de la expresión: Como último recurso, es posible envolver una expresión para que la
línea de continuación termine en la posición 79: Las reglas anteriores también se aplican a subexpresiones,
donde cada subexpresión tiene su propio nivel de sangría: A veces, es conveniente envolver una expresión después de un cast.
En este caso, la línea de continuación está indentada: Los punteros se comparan explícitamente con
La palabra clave Se aplican reglas de formato similares a La palabra clave La mayoría de los bucles Si se omite alguna parte de la instrucción Una construcción con el cuerpo vacío también se indica con el
comentario Un bucle sin fin se ve así: Las etiquetas están rodeadas de líneas en blanco y se indentan al nivel anterior: Para depurar problemas de memoria como desbordamientos de búfer o errores de uso después de liberar, puedes usar AddressSanitizer <https://en.wikipedia.org/wiki/AddressSanitizer>_ (ASan) soportado por algunos compiladores.
Para habilitar ASan con Ya que la mayoría de las asignaciones en Angie se realizan desde el pool interno de Angie, habilitar ASan puede no ser suficiente para depurar problemas de memoria.
El pool interno asigna un gran bloque de memoria del sistema y extrae asignaciones más pequeñas de él.
Sin embargo, este mecanismo puede desactivarse estableciendo la macro
La siguiente línea de configuración resume la información proporcionada anteriormente.
Se recomienda durante el desarrollo de módulos de terceros y al probar Angie en diferentes plataformas. El error más común es intentar escribir un módulo en C completo cuando se puede evitar.
En la mayoría de los casos tu tarea se puede lograr creando una configuración adecuada.
Si escribir un módulo es inevitable, intenta hacerlo
tan pequeño y simple como sea posible.
Por ejemplo, un módulo puede exportar solo algunas
variables. Antes de empezar un módulo, considera las siguientes preguntas: ¿Es posible implementar una característica deseada utilizando ya
módulos disponibles? ¿Es posible resolver un problema utilizando lenguajes de scripting integrados,
como Módulo HTTP Perl o Módulos de Terceros? El tipo de cadena más utilizado en Angie,
ngx_str_t no es una cadena de estilo C terminada en cero.
No puedes pasar los datos a funciones de la biblioteca C estándar
como Evita usar variables globales en tus módulos.
Lo más probable es que sea un error tener una variable global.
Cualquier dato global debe estar ligado a un ciclo de configuración
y ser asignado desde el correspondiente pool de memoria.
Esto permite a Angie realizar recargas de configuración de forma suave.
Un intento de usar variables globales probablemente romperá esta característica,
porque será imposible tener dos configuraciones al
mismo tiempo y deshacerse de ellas.
A veces se requieren variables globales.
En este caso, se necesita una atención especial para gestionar la reconfiguración
correctamente.
Además, verifica si las bibliotecas utilizadas por tu código tienen estado global implícito que podría romperse al recargar. En lugar de lidiar con enfoques malloc/free que son propensos a errores,
aprende a usar los pools de Angie.
Un pool se crea y se vincula a un objeto -
configuración,
ciclo,
conexión,
o solicitud HTTP.
Cuando el objeto se destruye, el pool asociado también se destruye.
Así, al trabajar con un objeto, es posible asignar la cantidad necesaria desde el pool correspondiente y no preocuparse por liberar la memoria incluso en caso de errores. Se recomienda evitar el uso de hilos en Angie porque definitivamente romperá las cosas: la mayoría de las funciones de Angie no son seguras para hilos.
Se espera que un hilo ejecute solo llamadas al sistema y funciones de bibliotecas seguras para hilos.
Si necesitas ejecutar código que no está relacionado con el procesamiento de solicitudes del cliente, la forma adecuada es programar un temporizador en el manejador del módulo Un error común es usar bibliotecas que bloquean internamente.
La mayoría de las bibliotecas disponibles son síncronas y bloqueantes por naturaleza.
En otras palabras, realizan una operación a la vez y pierden tiempo esperando la respuesta de otro par.
Como resultado, cuando se procesa una solicitud con dicha biblioteca, todo el trabajador de Angie queda bloqueado, destruyendo así el rendimiento.
Utiliza solo bibliotecas que proporcionen una interfaz asíncrona y no bloqueen todo el proceso. Frecuentemente los módulos necesitan realizar una llamada HTTP a algún servicio externo.
Un error común es usar alguna biblioteca externa, como libcurl, para realizar la solicitud HTTP.
Es absolutamente innecesario traer una gran cantidad de código externo
(probablemente bloqueante <#using_libraries>!) para la tarea que puede ser realizada por Angie por sí misma. Hay dos escenarios básicos de uso cuando se necesita una solicitud externa: en el contexto del procesamiento de una solicitud de cliente (por ejemplo, en el manejador de contenido) en el contexto de un proceso de trabajo (por ejemplo, el manejador de temporizador) En el primer caso, lo mejor es usar
API de subsolicitudes.
En lugar de acceder directamente al servicio externo, declaras una ubicación
en la configuración de Angie y diriges tu subsolicitud a esa ubicación.
Esta ubicación no se limita a
solicitudes de proxying
sino que puede contener otras directivas de Angie.
Un ejemplo de ese enfoque es la
directiva auth_request implementada en
Auth Request. Para el segundo caso, es posible usar la funcionalidad básica de cliente HTTP
disponible en Angie.
Por ejemplo,
OCSP module
implementa un cliente HTTP simple.Código fuente#
Estilo de codificación#
Mensajes de commits#
API: bad things removed, good things added.
As explained elsewhere[1], the original API was bad because stuff;
this change was introduced to improve that aspect locally.
Levels of goodness have been implemented to mitigate the badness;
this is now the preferred way to work. Also, the badness is gone.
[1] https://example.com
Verificaciones finales#
$ cd tests
$ prove .
tests/README
para obtener detalles.Envío de contribuciones#
Convenciones de codificación#
Disposición del código#
auto
— Scripts de compilaciónsrc
core
— Tipos y funciones básicas — cadena, arreglo, registro, pool, etc.event
— Núcleo de eventosmodules
— Módulos de notificación de eventos:
epoll
, kqueue
, select
etc.http
— Módulo HTTP principal y código comúnmodules
— Otros módulos HTTPv2
— HTTP/2mail
— Módulos de correoos
— Código específico de la plataformaunix
win32
stream
— Módulos de flujoArchivos de inclusión#
#include
deben aparecer al principio de cada archivo Angie:#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
#include <ngx_mail.h>
#include <ngx_stream.h>
Enteros#
ngx_int_t
y ngx_uint_t
, que son
typedefs para intptr_t
y uintptr_t
respectivamente.Códigos de retorno comunes#
NGX_OK
— Operación exitosa.NGX_ERROR
— La operación falló.NGX_AGAIN
— Operación incompleta; vuelva a llamar a la función.NGX_DECLINED
— Operación rechazada, por ejemplo, porque está
deshabilitada en la configuración. Esto nunca es un error.NGX_BUSY
— El recurso no está disponible.NGX_DONE
— Operación completa o continuada en otro lugar.
También se usa como código de éxito alternativo.NGX_ABORT
— La función fue abortada.
También se usa como código de error alternativo.Manejo de errores#
ngx_errno
devuelve el último código de error del sistema.
Se asigna a errno
en plataformas POSIX y a
GetLastError()
en Windows.
El macro ngx_socket_errno
devuelve el último número de error de socket.
Al igual que el macro ngx_errno
, se asigna a
errno
en plataformas POSIX.
Se asigna a la llamada WSAGetLastError()
en Windows.
Acceder a los valores de ngx_errno
o
ngx_socket_errno
más de una vez seguidas puede causar
problemas de rendimiento.
Si el valor de error podría usarse varias veces, cúrralo en una variable local
de tipo ngx_err_t
.
Para establecer errores, use los macros ngx_set_errno(errno)
y
ngx_set_socket_errno(errno)
.ngx_errno
y
ngx_socket_errno
pueden pasarse a las funciones de registro
ngx_log_error()
y ngx_log_debugX()
, en
cuyo caso se añade el texto de error del sistema al mensaje de registro.ngx_errno
:ngx_int_t
ngx_my_kill(ngx_pid_t pid, ngx_log_t *log, int signo)
{
ngx_err_t err;
if (kill(pid, signo) == -1) {
err = ngx_errno;
ngx_log_error(NGX_LOG_ALERT, log, err, "kill(%P, %d) failed", pid, signo);
if (err == NGX_ESRCH) {
return 2;
}
return 1;
}
return 0;
}
Cadenas#
Visión general#
u_char *
.ngx_str_t
se define como sigue:typedef struct {
size_t len;
u_char *data;
} ngx_str_t;
len
almacena la longitud de la cadena y
data
contiene los datos de la cadena.
La cadena, contenida en ngx_str_t
, puede o no estar terminada en nulo tras los bytes de len
.
En la mayoría de los casos no lo está.
Sin embargo, en ciertas partes del código (p. ej., al analizar la configuración),
ngx_str_t
objetos se sabe que están terminados en nulo, lo que
simplifica la comparación de cadenas y facilita pasarlas a
llamadas al sistema.src/core/ngx_string.h
.
Algunas de ellas son envoltorios alrededor de funciones estándar de C:ngx_strcmp()
ngx_strncmp()
ngx_strstr()
ngx_strlen()
ngx_strchr()
ngx_memcmp()
ngx_memset()
ngx_memcpy()
ngx_memmove()
ngx_memzero()
— Llena la memoria de ceros.ngx_explicit_memzero()
— Hace lo mismo que
ngx_memzero()
, pero esta llamada nunca es eliminada por la optimización de eliminación de almacenamiento muerto del compilador.
Esta función puede usarse para borrar datos sensibles como contraseñas y claves.ngx_cpymem()
— Hace lo mismo que
ngx_memcpy()
, pero devuelve la dirección final del destino.
Esto resulta práctico para anexar varias cadenas en una fila.ngx_movemem()
— Hace lo mismo que
ngx_memmove()
, pero devuelve la dirección final del destino.ngx_strlchr()
— Busca un carácter en una cadena,
delimitado por dos punteros.ngx_tolower()
ngx_toupper()
ngx_strlow()
ngx_strcasecmp()
ngx_strncasecmp()
ngx_string(text)
— inicializador estático para el
tipo ngx_str_t
a partir de la cadena literal C
text
ngx_null_string
— inicializador estático de cadena vacía para el
tipo ngx_str_t
ngx_str_set(str, text)
— inicializa la cadena
str
de tipo ngx_str_t *
con la literal de cadena C
text
ngx_str_null(str)
— inicializa la cadena str
de tipo ngx_str_t *
con la cadena vacíaFormato#
ngx_sprintf(buf, fmt, ...)
ngx_snprintf(buf, max, fmt, ...)
ngx_slprintf(buf, last, fmt, ...)
ngx_vslprintf(buf, last, fmt, args)
ngx_vsnprintf(buf, max, fmt, args)
src/core/ngx_string.c
. Algunas de ellas son:%O
— off_t
%T
— time_t
%z
— ssize_t
%i
— ngx_int_t
%p
— void *
%V
— ngx_str_t *
%s
— u_char *
(null-terminated)%*s
— size_t + u_char *
u
en la mayoría de tipos para hacerlos sin signo.
Para convertir la salida a hexadecimal, usa X
o x
.Conversión numérica#
NGX_ERROR
en caso de error.ngx_atoi(line, n)
— ngx_int_t
ngx_atosz(line, n)
— ssize_t
ngx_atoof(line, n)
— off_t
ngx_atotm(line, n)
— time_t
NGX_ERROR
en caso de error.ngx_atofp(line, n, point)
— Convierte un número flotante de punto fijo
de longitud dada a un entero positivo del tipo
ngx_int_t
.
El resultado se desplaza a la izquierda por point
posiciones decimales.
Se espera que la representación en cadena del número tenga no más de points
dígitos fraccionarios.
Por ejemplo, ngx_atofp("10.5", 4, 2)
devuelve
1050
.ngx_hextoi(line, n)
— Convierte una representación hexadecimal
de un entero positivo a ngx_int_t
.Expresiones regulares#
src/core/ngx_regex.h
.NGX_PCRE
:#if (NGX_PCRE)
ngx_regex_t *re;
ngx_regex_compile_t rc;
u_char errstr[NGX_MAX_CONF_ERRSTR];
ngx_str_t value = ngx_string("message (\\d\\d\\d).*Codeword is '(?<cw>\\w+)'");
ngx_memzero(&rc, sizeof(ngx_regex_compile_t));
rc.pattern = value;
rc.pool = cf->pool;
rc.err.len = NGX_MAX_CONF_ERRSTR;
rc.err.data = errstr;
/* rc.options can be set to NGX_REGEX_CASELESS */
if (ngx_regex_compile(&rc) != NGX_OK) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%V", &rc.err);
return NGX_CONF_ERROR;
}
re = rc.regex;
#endif
captures
y
named_captures
en la
ngx_regex_compile_t
estructura contienen la cantidad de capturas y capturas con nombre, respectivamente, encontradas en la expresión regular.ngx_int_t n;
int captures[(1 + rc.captures) * 3];
ngx_str_t input = ngx_string("This is message 123. Codeword is 'foobar'.");
n = ngx_regex_exec(re, &input, captures, (1 + rc.captures) * 3);
if (n >= 0) {
/* string matches expression */
} else if (n == NGX_REGEX_NO_MATCHED) {
/* no match was found */
} else {
/* some error */
ngx_log_error(NGX_LOG_ALERT, log, 0, ngx_regex_exec_n " failed: %i", n);
}
ngx_regex_exec()
son la expresión regular compilada re
, la cadena a comparar input
, un arreglo opcional de enteros para contener las captures
que se encuentren, y el tamaño del arreglo.
El tamaño del arreglo captures
debe ser un múltiplo de tres, tal como lo exige la
PCRE API. En el ejemplo, el tamaño se calcula a partir del total de capturas más uno para la cadena coincidente.u_char *p;
size_t size;
ngx_str_t name, value;
/* todas las capturas */
for (i = 0; i < n * 2; i += 2) {
value.data = input.data + captures[i];
value.len = captures[i + 1] - captures[i];
}
/* accediendo a capturas con nombre */
size = rc.name_size;
p = rc.names;
for (i = 0; i < rc.named_captures; i++, p += size) {
/* nombre de la captura */
name.data = &p[2];
name.len = ngx_strlen(name.data);
n = 2 * ((p[0] << 8) + p[1]);
/* valor capturado */
value.data = &input.data[captures[n]];
value.len = captures[n + 1] - captures[n];
}
ngx_regex_exec_array()
acepta el arreglo de elementos
ngx_regex_elt_t
(que son simplemente expresiones regulares compiladas con nombres asociados),
una cadena a comparar y un registro. La función aplica expresiones del arreglo a la cadena hasta que se encuentre una coincidencia o no queden expresiones por aplicar.
El valor de retorno es NGX_OK
cuando hay coincidencia y
NGX_DECLINED
en caso contrario, o NGX_ERROR
en caso de error.Tiempo#
ngx_time_t
representa el tiempo con tres tipos distintos para segundos, milisegundos y el desplazamiento GMT:typedef struct {
time_t sec;
ngx_uint_t msec;
ngx_int_t gmtoff;
} ngx_time_t;
ngx_tm_t
es un alias de
struct tm
en plataformas UNIX y de SYSTEMTIME
en Windows.ngx_cached_err_log_time
— Utilizada en entradas de registro de errores:
"1970/09/28 12:00:00"
ngx_cached_http_log_time
— Utilizada en entradas de registro de acceso HTTP:
"28/Sep/1970:12:00:00 +0600"
ngx_cached_syslog_time
— Utilizada en entradas de syslog:
"Sep 28 12:00:00"
ngx_cached_http_time
— Utilizada en encabezados HTTP:
"Mon, 28 Sep 1970 06:00:00 GMT"
ngx_cached_http_log_iso8601
— El formato ISO 8601 estándar:
"1970-09-28T12:00:00+06:00"
ngx_time()
y ngx_timeofday()
devuelven el valor de tiempo actual en segundos y son la forma preferida de acceder al valor de tiempo en caché.ngx_gettimeofday()
, que actualiza su argumento (puntero a struct timeval
).
El tiempo se actualiza siempre cuando Angie regresa al bucle de eventos desde llamadas al sistema.
Para actualizar el tiempo de inmediato, llame a ngx_time_update()
,
o ngx_time_sigsafe_update()
si está actualizando el tiempo en el contexto del manejador de señales.time_t
en la representación de tiempo desglosada indicada.
La primera función de cada par convierte time_t
a ngx_tm_t
y la segunda (con el sufijo _libc_
)
a struct tm
:ngx_gmtime(), ngx_libc_gmtime()
— Tiempo expresado como UTCngx_localtime(), ngx_libc_localtime()
— Tiempo expresado
relativo a la zona horaria localngx_http_time(buf, time)
devuelve una representación en cadena adecuada para su uso en encabezados HTTP (por ejemplo, "Mon, 28 Sep 1970 06:00:00 GMT"
).
La ngx_http_cookie_time(buf, time)
devuelve una representación en cadena adecuada para cookies HTTP ("Thu, 31-Dec-37 23:55:55 GMT"
).Contenedores#
Arreglo#
ngx_array_t
se define de la siguiente maneratypedef struct {
void *elts;
ngx_uint_t nelts;
size_t size;
ngx_uint_t nalloc;
ngx_pool_t *pool;
} ngx_array_t;
elts
.
El campo nelts
contiene el número de elementos.
El campo size
contiene el tamaño de un solo elemento y se establece cuando el arreglo se inicializa.ngx_array_create(pool, n, size)
para crear un arreglo en un pool, y la llamada ngx_array_init(array, pool, n, size)
para inicializar un objeto de arreglo que ya ha sido asignado.ngx_array_t *a, b;
/* crear un arreglo de cadenas con memoria preasignada para 10 elementos */
a = ngx_array_create(pool, 10, sizeof(ngx_str_t));
/* inicializar arreglo de cadenas para 10 elementos */
ngx_array_init(&b, pool, 10, sizeof(ngx_str_t));
ngx_array_push(a)
añade un elemento al final y devuelve un puntero a élngx_array_push_n(a, n)
añade n
elementos al final
y devuelve un puntero al primeros = ngx_array_push(a);
ss = ngx_array_push_n(&b, 3);
Lista#
ngx_list_t
se define de la siguiente manera:typedef struct {
ngx_list_part_t *last;
ngx_list_part_t part;
size_t size;
ngx_uint_t nalloc;
ngx_pool_t *pool;
} ngx_list_t;
typedef struct ngx_list_part_s ngx_list_part_t;
struct ngx_list_part_s {
void *elts;
ngx_uint_t nelts;
ngx_list_part_t *next;
};
ngx_list_init(list, pool, n, size)
o creada llamando
ngx_list_create(pool, n, size)
.
Ambas funciones toman como argumentos el tamaño de un solo elemento y
una cantidad de elementos por parte de la lista.
Para añadir un elemento a una lista, use la función
ngx_list_push(list)
.
Para iterar sobre los elementos, acceda directamente a los campos de la lista como se muestra en el
ejemplo:ngx_str_t *v;
ngx_uint_t i;
ngx_list_t *list;
ngx_list_part_t *part;
list = ngx_list_create(pool, 100, sizeof(ngx_str_t));
if (list == NULL) { /* error */ }
/* añadir elementos a la lista */
v = ngx_list_push(list);
if (v == NULL) { /* error */ }
ngx_str_set(v, "foo");
v = ngx_list_push(list);
if (v == NULL) { /* error */ }
ngx_str_set(v, "bar");
/* iterar sobre la lista */
part = &list->part;
v = part->elts;
for (i = 0; /* void */; i++) {
if (i >= part->nelts) {
if (part->next == NULL) {
break;
}
part = part->next;
v = part->elts;
i = 0;
}
ngx_do_smth(&v[i]);
}
ngx_table_elt_t
) establezca el campo hash
en ngx_table_elt_t
a cero.
Los elementos marcados de esta forma se omiten explícitamente cuando se itera sobre los encabezados.Cola#
typedef struct ngx_queue_s ngx_queue_t;
struct ngx_queue_s {
ngx_queue_t *prev;
ngx_queue_t *next;
};
ngx_queue_init(q)
para inicializar la cabeza de la lista antes de su uso.
Las colas soportan las siguientes operaciones:ngx_queue_insert_head(h, x)
,
ngx_queue_insert_tail(h, x)
— Inserta un nuevo nodongx_queue_remove(x)
— Elimina un nodo de la colangx_queue_split(h, q, n)
— Divide una cola en un nodo,
devolviendo la cola tail en una cola separadangx_queue_add(h, n)
— Añade una segunda cola a la primerangx_queue_head(h)
,
ngx_queue_last(h)
— Obtiene el primer o último nodo de la colangx_queue_sentinel(h)
- Obtiene un objeto centinela de cola para terminar
la iteración enngx_queue_data(q, type, link)
— Obtiene una referencia al
inicio de la estructura de datos del nodo de la cola, considerando el desplazamiento
del campo de la cola en ellatypedef struct {
ngx_str_t value;
ngx_queue_t queue;
} ngx_foo_t;
ngx_foo_t *f;
ngx_queue_t values, *q;
ngx_queue_init(&values);
f = ngx_palloc(pool, sizeof(ngx_foo_t));
if (f == NULL) { /* error */ }
ngx_str_set(&f->value, "foo");
ngx_queue_insert_tail(&values, &f->queue);
/* insert more nodes here */
for (q = ngx_queue_head(&values);
q != ngx_queue_sentinel(&values);
q = ngx_queue_next(q))
{
f = ngx_queue_data(q, ngx_foo_t, queue);
ngx_do_smth(&f->value);
}
Árbol Rojo-Negro#
src/core/ngx_rbtree.h
ofrece acceso a la implementación eficiente de árboles rojo-negro.typedef struct {
ngx_rbtree_t rbtree;
ngx_rbtree_node_t sentinel;
/* custom per-tree data here */
} my_tree_t;
typedef struct {
ngx_rbtree_node_t rbnode;
/* custom per-node data */
foo_t val;
} my_node_t;
my_tree_t root;
ngx_rbtree_init(&root.rbtree, &root.sentinel, insert_value_function);
insert_value
.
Por ejemplo, la función ngx_str_rbtree_insert_value
maneja
el tipo ngx_str_t
.
Sus argumentos son punteros al nodo raíz de la inserción, al nodo recién creado
para añadir y a la sentinela del árbol.void ngx_str_rbtree_insert_value(ngx_rbtree_node_t *temp,
ngx_rbtree_node_t *node,
ngx_rbtree_node_t *sentinel)
my_node_t *
my_rbtree_lookup(ngx_rbtree_t *rbtree, foo_t *val, uint32_t hash)
{
ngx_int_t rc;
my_node_t *n;
ngx_rbtree_node_t *node, *sentinel;
node = rbtree->root;
sentinel = rbtree->sentinel;
while (node != sentinel) {
n = (my_node_t *) node;
if (hash != node->key) {
node = (hash < node->key) ? node->left : node->right;
continue;
}
rc = compare(val, node->val);
if (rc < 0) {
node = node->left;
continue;
}
if (rc > 0) {
node = node->right;
continue;
}
return n;
}
return NULL;
}
compare()
es una función de comparación clásica que
devuelve un valor menor que cero, igual a cero o mayor que cero.
Para acelerar las búsquedas y evitar comparar objetos de usuario que pueden ser grandes, se utiliza un campo hash entero.ngx_rbtree_insert()
:my_node_t *my_node;
ngx_rbtree_node_t *node;
my_node = ngx_palloc(...);
init_custom_data(&my_node->val);
node = &my_node->rbnode;
node->key = create_key(my_node->val);
ngx_rbtree_insert(&root->rbtree, node);
ngx_rbtree_delete()
:ngx_rbtree_delete(&root->rbtree, node);
Tabla hash#
src/core/ngx_hash.h
.
Se admite coincidencia exacta y con comodines.
La segunda requiere una configuración adicional y se describe en una sección separada a continuación.max_size
y bucket_size
, como se detalla en un documento separado documento.
Generalmente, son configurables por el usuario.
La configuración de inicialización de hash se almacena con el tipo ngx_hash_init_t
, y el propio hash es ngx_hash_t
:ngx_hash_t foo_hash;
ngx_hash_init_t hash;
hash.hash = &foo_hash;
hash.key = ngx_hash_key;
hash.max_size = 512;
hash.bucket_size = ngx_align(64, ngx_cacheline_size);
hash.name = "foo_hash";
hash.pool = cf->pool;
hash.temp_pool = cf->temp_pool;
clave
es un puntero a una función que crea la clave entera del hash a partir de una cadena.
Existen dos funciones genéricas de creación de claves:
ngx_hash_key(data, len)
y
ngx_hash_key_lc(data, len)
.
La segunda convierte una cadena a todos los caracteres en minúscula, por lo que la cadena pasada debe ser modificable.
Si eso no es así, pasa la bandera NGX_HASH_READONLY_KEY
a la función, inicializando el arreglo de claves (ver más abajo).ngx_hash_keys_arrays_t
y
se inicializan con ngx_hash_keys_array_init(arr, type)
:
El segundo parámetro (type
) controla la cantidad de recursos preasignados para el hash y puede ser NGX_HASH_SMALL
o
NGX_HASH_LARGE
.
Este último es adecuado si esperas que el hash contenga miles de
elementos.ngx_hash_keys_arrays_t foo_keys;
foo_keys.pool = cf->pool;
foo_keys.temp_pool = cf->temp_pool;
ngx_hash_keys_array_init(&foo_keys, NGX_HASH_SMALL);
ngx_hash_add_key(keys_array, key, value, flags)
:ngx_str_t k1 = ngx_string("key1");
ngx_str_t k2 = ngx_string("key2");
ngx_hash_add_key(&foo_keys, &k1, &my_data_ptr_1, NGX_HASH_READONLY_KEY);
ngx_hash_add_key(&foo_keys, &k2, &my_data_ptr_2, NGX_HASH_READONLY_KEY);
ngx_hash_init(hinit, key_names, nelts)
:ngx_hash_init(&hash, foo_keys.keys.elts, foo_keys.keys.nelts);
max_size
o
bucket_size
no son lo suficientemente grandes.ngx_hash_find(hash, key, name, len)
para buscar elementos:my_data_t *data;
ngx_uint_t key;
key = ngx_hash_key(k1.data, k1.len);
data = ngx_hash_find(&foo_hash, key, k1.data, k1.len);
if (data == NULL) {
/* key not found */
}
Coincidencia con comodines#
ngx_hash_combined_t
.
Incluye el tipo hash descrito arriba y tiene dos arreglos de claves adicionales:
dns_wc_head
y dns_wc_tail
.
La inicialización de las propiedades básicas es similar a la de un hash regular:ngx_hash_init_t hash
ngx_hash_combined_t foo_hash;
hash.hash = &foo_hash.hash;
hash.key = ...;
NGX_HASH_WILDCARD_KEY
:/* k1 = ".example.org"; */
/* k2 = "foo.*"; */
ngx_hash_add_key(&foo_keys, &k1, &data1, NGX_HASH_WILDCARD_KEY);
ngx_hash_add_key(&foo_keys, &k2, &data2, NGX_HASH_WILDCARD_KEY);
if (foo_keys.dns_wc_head.nelts) {
ngx_qsort(foo_keys.dns_wc_head.elts,
(size_t) foo_keys.dns_wc_head.nelts,
sizeof(ngx_hash_key_t),
cmp_dns_wildcards);
hash.hash = NULL;
hash.temp_pool = pool;
if (ngx_hash_wildcard_init(&hash, foo_keys.dns_wc_head.elts,
foo_keys.dns_wc_head.nelts)
!= NGX_OK)
{
return NGX_ERROR;
}
foo_hash.wc_head = (ngx_hash_wildcard_t *) hash.hash;
}
dns_wc_tail
array is done similarly.ngx_hash_find_combined(chash, key, name, len)
:/* key = "bar.example.org"; - will match ".example.org" */
/* key = "foo.example.com"; - will match "foo.*" */
hkey = ngx_hash_key(key.data, key.len);
res = ngx_hash_find_combined(&foo_hash, hkey, key.data, key.len);
Gestión de la memoria#
Heap#
ngx_alloc(size, log)
— Asignar memoria desde el heap del sistema.
Este es un envoltorio alrededor de malloc()
con soporte de registro (logging).
Los errores de asignación y la información de depuración se registran en log
.ngx_calloc(size, log)
— Asignar memoria desde el heap del sistema
similar a ngx_alloc()
, pero llena la memoria con ceros después
de la asignación.ngx_memalign(alignment, size, log)
— Asignar memoria alineada
desde el heap del sistema.
Esto es un envoltorio alrededor de posix_memalign()
en aquellas plataformas que proporcionan esa función.
En caso contrario, la implementación recurre a ngx_alloc()
que
proporciona el alineamiento máximo.ngx_free(p)
— Liberar la memoria asignada.
Este es un envoltorio alrededor de free()
Pool#
ngx_pool_t
.
Las siguientes operaciones son compatibles:ngx_create_pool(size, log)
— Crear un pool con el tamaño de bloque especificado.
El objeto pool devuelto también se asigna dentro del pool.
El size
debe ser al menos NGX_MIN_POOL_SIZE
y múltiplo de NGX_POOL_ALIGNMENT
.ngx_destroy_pool(pool)
— Liberar toda la memoria del pool, incluyendo
el propio objeto pool.ngx_palloc(pool, size)
— Asignar memoria alineada desde el pool especificado.ngx_pcalloc(pool, size)
— Asignar memoria alineada
desde el pool especificado y llenarla de ceros.ngx_pnalloc(pool, size)
— Asignar memoria no alineada desde el
pool especificado.
Mayormente utilizada para asignar cadenas.ngx_pfree(pool, p)
— Liberar la memoria que fue previamente
asignada en el pool especificado.
Solo las asignaciones que resulten de solicitudes reenviadas al asignador del sistema
pueden ser liberadas.u_char *p;
ngx_str_t *s;
ngx_pool_t *pool;
pool = ngx_create_pool(1024, log);
if (pool == NULL) { /* error */ }
s = ngx_palloc(pool, sizeof(ngx_str_t));
if (s == NULL) { /* error */ }
ngx_str_set(s, "foo");
p = ngx_pnalloc(pool, 3);
if (p == NULL) { /* error */ }
ngx_memcpy(p, "foo", 3);
chain
de ngx_pool_t
mantiene una
lista de enlaces asignados previamente listos para su reutilización.
Para una asignación eficiente de un enlace de cadena en un pool, use la
función ngx_alloc_chain_link(pool)
.
Esta función busca un enlace de cadena libre en la lista del pool y asigna un nuevo
enlace de cadena si la lista del pool está vacía.
Para liberar un enlace, llame a la función
ngx_free_chain(pool, cl)
.ngx_pool_cleanup_add(pool, size)
, que devuelve un
ngx_pool_cleanup_t
puntero a
ser relleno por el emisor.
Use el argumento size
para asignar contexto para el manejador de limpieza.ngx_pool_cleanup_t *cln;
cln = ngx_pool_cleanup_add(pool, 0);
if (cln == NULL) { /* error */ }
cln->handler = ngx_my_cleanup;
cln->data = "foo";
...
static void
ngx_my_cleanup(void *data)
{
u_char *msg = data;
ngx_do_smth(msg);
}
Memoria compartida#
ngx_shared_memory_add(cf, name, size, tag)
añade
una nueva entrada de memoria compartida ngx_shm_zone_t
a un ciclo.
La función recibe el name
y size
de la zona.
Cada zona compartida debe tener un nombre único.
Si ya existe una entrada de zona compartida con el nombre proporcionado name
y
tag
, la entrada de zona existente se reutiliza.
La función falla con un error si existe una entrada con el mismo nombre pero con una etiqueta diferente.
Por lo general, se pasa la dirección de la estructura del módulo como
tag
, haciendo posible reutilizar zonas compartidas por nombre dentro
de un mismo módulo de Angie.ngx_shm_zone_t
tiene los
siguientes campos:init
— Callback de inicialización, llamado después de que la zona compartida
se mapea a la memoria realdata
— Contexto de datos, usado para pasar datos arbitrarios al
callback init
noreuse
— Bandera que desactiva la reutilización de una zona compartida
desde el ciclo anteriortag
— Etiqueta de la zona compartidashm
— Objeto específico de la plataforma de tipo
ngx_shm_t
, que tiene al menos los siguientes campos:addr
— Dirección de memoria compartida mapeada, inicialmente NULLsize
— Tamaño de la memoria compartidaname
— Nombre de la memoria compartidalog
— Registro de la memoria compartidaexists
— Bandera que indica que la memoria compartida fue heredada
desde el proceso maestro (Windows-específico)ngx_init_cycle()
después de analizar la configuración.
En sistemas POSIX, se utiliza la syscall mmap()
para crear el
mapeo anónimo compartido.
En Windows, se utiliza la pareja CreateFileMapping()
/
MapViewOfFileEx()
.ngx_slab_pool_t
.
Se crea automáticamente un pool en slab para asignar memoria en cada zona compartida de Angie.
El pool se encuentra al inicio de la zona compartida y se puede acceder mediante la expresión (ngx_slab_pool_t *) shm_zone->shm.addr
.
Para asignar memoria en una zona compartida, llame a ngx_slab_alloc(pool, size)
o
ngx_slab_calloc(pool, size)
.
Para liberar memoria, llame a ngx_slab_free(pool, p)
.mutex
de
ngx_slab_pool_t
.
Un mutex se utiliza con mayor frecuencia por el slab pool mientras se asigna y libera
memoria, pero se puede usar para proteger cualquier otra estructura de datos del usuario asignada
en la zona compartida.
Para bloquear o desbloquear un mutex, llame a
ngx_shmtx_lock(&shpool->mutex)
o
ngx_shmtx_unlock(&shpool->mutex)
respectivamente.ngx_str_t name;
ngx_foo_ctx_t *ctx;
ngx_shm_zone_t *shm_zone;
ngx_str_set(&name, "foo");
/* allocate shared zone context */
ctx = ngx_pcalloc(cf->pool, sizeof(ngx_foo_ctx_t));
if (ctx == NULL) {
/* error */
}
/* add an entry for 64k shared zone */
shm_zone = ngx_shared_memory_add(cf, &name, 65536, &ngx_foo_module);
if (shm_zone == NULL) {
/* error */
}
/* register init callback and context */
shm_zone->init = ngx_foo_init_zone;
shm_zone->data = ctx;
...
static ngx_int_t
ngx_foo_init_zone(ngx_shm_zone_t *shm_zone, void *data)
{
ngx_foo_ctx_t *octx = data;
size_t len;
ngx_foo_ctx_t *ctx;
ngx_slab_pool_t *shpool;
value = shm_zone->data;
if (octx) {
/* reusing a shared zone from old cycle */
ctx->value = octx->value;
return NGX_OK;
}
shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
if (shm_zone->shm.exists) {
/* initialize shared zone context in Windows Angie worker */
ctx->value = shpool->data;
return NGX_OK;
}
/* initialize shared zone */
ctx->value = ngx_slab_alloc(shpool, sizeof(ngx_uint_t));
if (ctx->value == NULL) {
return NGX_ERROR;
}
shpool->data = ctx->value;
return NGX_OK;
}
Registro#
ngx_log_t
.
El registrador Angie admite varios tipos de salida:next
.
En este caso, cada mensaje se escribe en todos los registradores de la cadena.NGX_LOG_EMERG
NGX_LOG_ALERT
NGX_LOG_CRIT
NGX_LOG_ERR
NGX_LOG_WARN
NGX_LOG_NOTICE
NGX_LOG_INFO
NGX_LOG_DEBUG
NGX_LOG_DEBUG_CORE
NGX_LOG_DEBUG_ALLOC
NGX_LOG_DEBUG_MUTEX
NGX_LOG_DEBUG_EVENT
NGX_LOG_DEBUG_HTTP
NGX_LOG_DEBUG_MAIL
NGX_LOG_DEBUG_STREAM
error_log
y están disponibles en casi todas las etapas
del procesamiento en el ciclo, la configuración, la conexión del cliente y otros objetos.ngx_log_error(level, log, err, fmt, ...)
— Registro de erroresngx_log_debug0(level, log, err, fmt)
,
ngx_log_debug1(level, log, err, fmt, arg1)
etc — Registro de depuración
con hasta ocho argumentos de formato compatiblesNGX_MAX_ERROR_STR
(actualmente, 2048 bytes) en la pila.
El mensaje se antepone con el nivel de severidad, el ID de proceso (PID), la
ID de conexión (almacenada en log->connection
), y el texto de error del sistema.
Para los mensajes que no son de depuración: log->handler
también se llama para
anteponer información más específica al mensaje de registro.
El módulo HTTP establece la función ngx_http_log_error()
como manejador de registro
para registrar direcciones de cliente y servidor, acción actual (almacenada en
log->action
), línea de la solicitud del cliente, nombre del servidor, etc./* specify what is currently done */
log->action = "sending mp4 to client";
/* error and debug log */
ngx_log_error(NGX_LOG_INFO, c->log, 0, "client prematurely
closed connection");
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"mp4 start:%ui, length:%ui", mp4->start, mp4->length);
2016/09/16 22:08:52 [info] 17445#0: *1 client prematurely closed connection while
sending mp4 to client, client: 127.0.0.1, server: , request: "GET /file.mp4 HTTP/1.1"
2016/09/16 23:28:33 [debug] 22140#0: *1 mp4 start:0, length:10000
Ciclo#
ngx_cycle_t
.
El ciclo actual es referenciado por la variable global ngx_cycle
y es heredado por los trabajadores de Angie al iniciarse.
Cada vez que se recarga la configuración de Angie, se crea un nuevo ciclo a partir de la nueva configuración de Angie; el ciclo antiguo suele eliminarse tras crearse con éxito el nuevo.ngx_init_cycle()
, que toma el ciclo anterior como argumento.
La función localiza el archivo de configuración del ciclo anterior y hereda la mayor cantidad de recursos posible del ciclo anterior.
Se crea un ciclo marcador de posición llamado "init cycle" al inicio de Angie, y luego es reemplazado por un ciclo real construido a partir de la configuración.pool
— Pool del ciclo.
Creado para cada nuevo ciclo.log
— Registro del ciclo.
Inicialmente heredado del ciclo antiguo, se establece para que apunte a
new_log
después de leer la configuración.new_log
— Registro del ciclo, creado por la configuración.
Se ve afectado por la directiva a nivel raíz error_log
.connections
, connection_n
—
Matriz de conexiones del tipo ngx_connection_t
, creada por
el módulo de eventos durante la inicialización de cada trabajador de Angie.
La directiva worker_connections
en la configuración de Angie
establece el número de conexiones connection_n
.free_connections
,
free_connection_n
— Lista y número de conexiones disponibles en la actualidad.
Si no hay conexiones disponibles, un trabajador de Angie se niega a aceptar nuevos clientes
o a conectarse a servidores upstream.files
, files_n
— Matriz para mapear descriptores de archivos
a conexiones de Angie.
Este mapeo es utilizado por los módulos de eventos, que tienen la
bandera NGX_USE_FD_EVENT
(actualmente, es
poll
y devpoll
).conf_ctx
— Matriz de configuraciones de módulos centrales.
Las configuraciones se crean y llenan durante la lectura de los archivos de configuración de Angie.modules
, modules_n
— Matriz de módulos
del tipo ngx_module_t
, tanto estáticos como dinámicos, cargados por
la configuración actual.listening
— Matriz de objetos de escucha del tipo
ngx_listening_t
.
Los objetos de escucha suelen añadirse mediante la directiva listen
de varios módulos que llaman a la función
ngx_create_listening()
.
Los sockets de escucha se crean en función de los objetos de escucha.paths
— Matriz de rutas del tipo ngx_path_t
.
Las rutas se agregan llamando a la función ngx_add_path()
desde
módulos que van a operar en ciertos directorios.
Estos directorios son creados por Angie después de leer la configuración, si faltan.
Además, se pueden añadir dos manejadores para cada ruta:open_files
— Lista de objetos de archivo abiertos del tipo
ngx_open_file_t
, que se crean al llamar a la función
ngx_conf_open_file()
.
Actualmente, Angie usa este tipo de archivos abiertos para el registro.
Después de leer la configuración, Angie abre todos los archivos de la
lista open_files
y almacena cada descriptor en el campo
fd
del objeto.
Los archivos se abren en modo de anexar y se crean si faltan.
Los archivos de la lista son reabiertos por los trabajadores de Angie al recibir la
señal de reapertura (la más frecuente USR1
).
En este caso, el descriptor en el campo fd
se cambia a un
valor nuevo.shared_memory
— Lista de zonas de memoria compartida, cada una añadida por
llamando a la función ngx_shared_memory_add()
.
Las zonas compartidas se asignan al mismo rango de direcciones en todos los procesos de Angie y
se utilizan para compartir datos comunes, por ejemplo el árbol de caché HTTP en memoria.Búfer#
ngx_buf_t
.
Normalmente, se utiliza para contener datos que deben escribirse en un destino o leerse desde una fuente.
Un búfer puede hacer referencia a datos en la memoria o en un archivo y, técnicamente, es posible que un búfer haga referencia a ambos al mismo tiempo.
La memoria para el búfer se asigna por separado y no está relacionada con la estructura del búfer ngx_buf_t
.ngx_buf_t
tiene los siguientes campos:start
, end
— Los límites del bloque de memoria asignado para el búfer.pos
, last
— Los límites del búfer de memoria; normalmente un subrango de start
.. end
.file_pos
, file_last
— Los límites de un búfer de archivo, expresados como desplazamientos desde el inicio del archivo.tag
— Valor único usado para distinguir búferes; creado por diferentes módulos de Angie, usualmente para el fin de reutilización de búferes.file
— Objeto de archivo.temporary
— Indicador de que el búfer hace referencia a memoria escribible.memory
— Indicador de que el búfer hace referencia a memoria de solo lectura.in_file
— Indicador de que el búfer hace referencia a datos en un archivo.flush
— Indicador de que todos los datos anteriores al búfer deben ser vaciados.recycled
— Indicador de que el búfer puede reutilizarse y debe consumirse lo antes posible.sync
— Indicador de que el búfer no transporta datos ni contiene una señal especial como flush
o last_buf
.
Por defecto, Angie considera tales búferes como una condición de error, pero esta bandera indica a Angie que omita la comprobación de errores.last_buf
— Indicador de que el búfer es el último en la salida.last_in_chain
— Indicador de que no quedan más búferes de datos en una solicitud o subsolicitud.shadow
— Referencia a otro búfer ("shadow") relacionado con el búfer actual, usualmente en el sentido de que el búfer utiliza datos del
búfer sombra.
Cuando el búfer se consume, normalmente el búfer sombra también se marca como consumido.last_shadow
— Indicador de que el búfer es el último que referencia a un búfer sombra particular.temp_file
— Indicador de que el búfer se encuentra en un archivo temporal.ngx_chain_t
,
definidos como sigue:typedef struct ngx_chain_s ngx_chain_t;
struct ngx_chain_s {
ngx_buf_t *buf;
ngx_chain_t *next;
};
typedef struct ngx_chain_s ngx_chain_t;
struct ngx_chain_s {
ngx_buf_t *buf;
ngx_chain_t *next;
};
ngx_chain_t *
ngx_get_my_chain(ngx_pool_t *pool)
{
ngx_buf_t *b;
ngx_chain_t *out, *cl, **ll;
/* first buf */
cl = ngx_alloc_chain_link(pool);
if (cl == NULL) { /* error */ }
b = ngx_calloc_buf(pool);
if (b == NULL) { /* error */ }
b->start = (u_char *) "foo";
b->pos = b->start;
b->end = b->start + 3;
b->last = b->end;
b->memory = 1; /* read-only memory */
cl->buf = b;
out = cl;
ll = &cl->next;
/* second buf */
cl = ngx_alloc_chain_link(pool);
if (cl == NULL) { /* error */ }
b = ngx_create_temp_buf(pool, 3);
if (b == NULL) { /* error */ }
b->last = ngx_cpymem(b->last, "foo", 3);
cl->buf = b;
cl->next = NULL;
*ll = cl;
return out;
}
Redes#
Conexión#
ngx_connection_t
es un envoltorio alrededor de un descriptor de socket.
Incluye los siguientes campos:fd
— Descriptor de socketdata
— Contexto arbitrario de la conexión.
Normalmente, es un puntero a un objeto de nivel superior construido sobre la
conexión, como una solicitud HTTP o una sesión de Stream.read
, write
— Eventos de lectura y escritura para
la conexión.recv
, send
,
recv_chain
, send_chain
— Operaciones de E/S
para la conexión.pool
— Pool de conexiones.log
— Registro de la conexión.sockaddr
, socklen
,
addr_text
— Dirección de socket remota en formas binaria y textual.local_sockaddr
, local_socklen
— Dirección de socket local en forma binaria.
Inicialmente, estos campos están vacíos.
Usa la función ngx_connection_local_sockaddr()
para obtener la
dirección del socket local.proxy_protocol_addr
, proxy_protocol_port
- Dirección y puerto del cliente PROXY protocol, si el PROXY protocol está habilitado para
la conexión.ssl
— Contexto SSL para la conexión.reusable
— Indicador de que la conexión está en un estado que la hace elegible para reutilización.close
— Indicador de que la conexión está siendo reutilizada
y debe cerrarse.ssl
de la conexión contiene un puntero a una estructura
ngx_ssl_connection_t
, que mantiene todos los datos relacionados con SSL
para la conexión, incluyendo SSL_CTX
y
SSL
.
Los manejadores recv
, send
,
recv_chain
, y send_chain
también se configuran como funciones habilitadas para SSL.worker_connections
en la configuración de Angie
limita el número de conexiones por worker de Angie.
Todas las estructuras de conexión se crean de antemano cuando un worker inicia y se almacenan en
el campo connections
del objeto ciclo.
Para recuperar una estructura de conexión, usa la función
ngx_get_connection(s, log)
.
Toma como argumento s
un descriptor de socket, que debe envolverse en una estructura de conexión.ngx_reusable_connection(c, reusable)
.
Llamar ngx_reusable_connection(c, 1)
establece la
bandera reuse
en la estructura de conexión e inserta la conexión en la cola
reusable_connections_queue
del ciclo.
Cada vez que ngx_get_connection()
detecta que no hay conexiones disponibles en la lista del ciclo free_connections
,
llama a ngx_drain_connections()
para liberar un número específico de conexiones reutilizables.
Para cada una de esas conexiones, se establece la bandera close
y se llama a su manejador de lectura, que debe liberar la conexión llamando a
ngx_close_connection(c)
y dejarla disponible para su reutilización.
Para salir del estado cuando una conexión puede reutilizarse, se llama a
ngx_reusable_connection(c, 0)
.Eventos#
Evento#
ngx_event_t
en Angie proporciona un mecanismo
para notificar que ha ocurrido un evento específico.ngx_event_t
incluyen:data
— Contexto de evento arbitrario utilizado en los manejadores de eventos,
normalmente como puntero a una conexión relacionada con el evento.handler
— Función de devolución de llamada que se invocará cuando ocurra el evento.write
— Bandera que indica un evento de escritura.
La ausencia de la bandera indica un evento de lectura.active
— Bandera que indica que el evento está registrado para recibir notificaciones de E/S, normalmente desde mecanismos de notificación como
epoll
, kqueue
, poll
.ready
— Bandera que indica que el evento ha recibido una notificación de E/S.delayed
— Bandera que indica que la E/S está retrasada debido a la limitación de velocidad.timer
— Nodo de árbol rojo-negro para insertar el evento en el árbol de temporizadores.timer_set
— Bandera que indica que el temporizador del evento está configurado y aún no ha expirado.timedout
— Bandera que indica que el temporizador del evento ha expirado.eof
— Bandera que indica que se produjo EOF al leer datos.pending_eof
— Bandera que indica que hay un EOF pendiente en el socket, aunque pueda haber algunos datos disponibles antes.
La bandera se entrega a través del evento EPOLLRDHUP
de
epoll
o la bandera EV_EOF
de kqueue
.error
— Bandera que indica que ocurrió un error durante la lectura (para un evento de lectura) o escritura (para un evento de escritura).cancelable
— Bandera de evento de temporizador que indica que el evento debe ignorarse durante el apagado del worker.
El apagado suave del worker se retrasa hasta que no haya eventos de temporizador no cancelables programados.posted
— Bandera que indica que el evento está publicado en una cola.queue
— Nodo de cola para colocar el evento en una cola.Eventos de E/S#
ngx_get_connection()
tiene dos eventos adjuntos, c->read
y
c->write
, que se utilizan para recibir notificación de que el socket está listo para leer o escribir.
Todos estos eventos operan en modo Edge-Triggered, lo que significa que solo disparan notificaciones cuando cambia el estado del socket.
Por ejemplo, realizar una lectura parcial en un socket no hace que Angie entregue una notificación de lectura repetida hasta que lleguen más datos al socket.
Incluso cuando el mecanismo subyacente de notificación de E/S es esencialmente Level-Triggered (poll
, select
etc), Angie
convierte las notificaciones a Edge-Triggered.
Para hacer que las notificaciones de eventos de Angie sean consistentes en todos los sistemas de notificación en diferentes plataformas, las funciones
ngx_handle_read_event(rev, flags)
y
ngx_handle_write_event(wev, lowat)
deben llamarse después de manejar una notificación de socket de E/S o de llamar a cualquier función de E/S en ese socket.
Normalmente, las funciones se llaman una vez al final de cada manejador de evento de lectura o escritura.Eventos de temporización#
ngx_msec_t
.
Su valor actual se puede obtener de la variable ngx_current_msec
.ngx_add_timer(ev, timer)
establece un tiempo de espera para un
evento, ngx_del_timer(ev)
elimina un tiempo de espera previamente configurado.
El árbol rojo-negro global de temporizadores ngx_event_timer_rbtree
almacena todos los tiempos de espera actualmente configurados.
La clave en el árbol es de tipo ngx_msec_t
y es el momento en que ocurre el evento.
La estructura del árbol permite operaciones rápidas de inserción y eliminación, así como acceso a los tiempos de espera más cercanos, que Angie utiliza para determinar cuánto tiempo esperar para eventos de E/S y para expiración de temporizadores.Eventos publicados#
ngx_post_event(ev, q)
publica el evento
ev
en la cola de publicaciones q
.
La macro ngx_delete_posted_event(ev)
elimina el evento
ev
de la cola en la que está publicado actualmente.
Normalmente, los eventos se publican en la cola ngx_posted_events
,
que se procesa tarde en el bucle de eventos — después de que ya se hayan manejado
todos los eventos de E/S y temporizadores.
La función ngx_event_process_posted()
se llama para procesar
una cola de eventos.
Llama a los manejadores de eventos hasta que la cola esté vacía.
Esto significa que un manejador de eventos publicado puede publicar más eventos para ser procesados
dentro de la iteración actual del bucle de eventos.void
ngx_my_connection_read(ngx_connection_t *c)
{
ngx_event_t *rev;
rev = c->read;
ngx_add_timer(rev, 1000);
rev->handler = ngx_my_read_handler;
ngx_my_read(rev);
}
void
ngx_my_read_handler(ngx_event_t *rev)
{
ssize_t n;
ngx_connection_t *c;
u_char buf[256];
if (rev->timedout) { /* timeout expired */ }
c = rev->data;
while (rev->ready) {
n = c->recv(c, buf, sizeof(buf));
if (n == NGX_AGAIN) {
break;
}
if (n == NGX_ERROR) { /* error */ }
/* process buf */
}
if (ngx_handle_read_event(rev, 0) != NGX_OK) { /* error */ }
}
Bucle de eventos#
sigsuspend()
esperando a que lleguen señales.)
El bucle de eventos de Angie se implementa en la función
ngx_process_events_and_timers()
, la cual se llama
repetidamente hasta que el proceso salga.ngx_event_find_timer()
.
Esta función localiza el nodo más a la izquierda del árbol de temporizadores y devuelve
el número de milisegundos hasta que expire el nodo.ready
y se llama al manejador del evento.
Para Linux, normalmente se utiliza el manejador ngx_epoll_process_events()
,
que llama a epoll_wait()
para esperar eventos de E/S.ngx_event_expire_timers()
.
El árbol de temporizadores se recorre desde el elemento más a la izquierda hacia la derecha hasta encontrar un
temporizador no vencido.
Para cada nodo vencido se establece la bandera de evento timedout
,
la bandera timer_set
se restablece y se llama al manejador del evento.ngx_event_process_posted()
.
La función elimina repetidamente el primer elemento de la cola de eventos publicados
y llama al manejador del elemento, hasta que la cola esté vacía.ngx_process_events_and_timers()
.Procesos#
ngx_process
,
y es uno de los siguientes:NGX_PROCESS_MASTER
— El proceso maestro, que lee la
configuración de NGINX, crea ciclos y pone en marcha y controla procesos hijo.
No realiza ninguna E/S y solo responde a señales.
Su función de ciclo es ngx_master_process_cycle()
.NGX_PROCESS_WORKER
— El proceso trabajador, que maneja las conexiones
de los clientes.
Es iniciado por el proceso maestro y también responde a sus señales y comandos de canal.
Su función de ciclo es ngx_worker_process_cycle()
.
Puede haber varios procesos trabajadores, tal como lo configure la directiva
worker_processes
.NGX_PROCESS_SINGLE
— El proceso único, que existe únicamente en
el modo master_process off
, y es el único proceso que se ejecuta en
ese modo.
Crea ciclos (como lo hace el proceso maestro) y maneja las conexiones de clientes
(como lo hace el proceso trabajador).
Su función de ciclo es ngx_single_process_cycle()
.NGX_PROCESS_HELPER
— El proceso auxiliar, del cual actualmente
existen dos tipos: gestor de caché y cargador de caché.
La función de ciclo para ambos es
ngx_cache_manager_process_cycle()
.NGX_SHUTDOWN_SIGNAL
(SIGQUIT
en la mayoría
de sistemas) — Apagado suave.
Al recibir esta señal, el proceso maestro envía una señal de apagado a todos
los procesos hijo.
Cuando ya no quedan procesos hijo, el maestro destruye el grupo de ciclos y sale.
Cuando un proceso trabajador recibe esta señal, cierra todos los sockets de escucha y
espera hasta que no haya eventos programados no cancelables, luego destruye el
grupo de ciclos y sale.
Cuando el proceso de gestor de caché o el cargador de caché recibe esta señal, sale
inmediatamente.
La variable ngx_quit
se establece en 1
cuando un
proceso recibe esta señal, y se restablece inmediatamente después de ser procesada.
La variable ngx_exiting
se establece en 1
mientras
un proceso trabajador está en el estado de apagado.NGX_TERMINATE_SIGNAL
(SIGTERM
en la mayoría
de sistemas) — Terminación.
Al recibir esta señal, el proceso maestro envía una señal de terminación a todos
los procesos hijo.
Si un proceso hijo no sale en 1 segundo, el proceso maestro envía la
señal SIGKILL
para terminarlo.
Cuando ya no quedan procesos hijo, el maestro destruye el grupo de ciclos y
sale.
Cuando un proceso trabajador, el gestor de caché o el cargador de caché
recibe esta señal, destruye el grupo de ciclos y sale.
La variable ngx_terminate
se establece en 1
al recibir esta señal.NGX_NOACCEPT_SIGNAL
(SIGWINCH
en la mayoría
de sistemas) - Detener todos los procesos trabajador y auxiliar.
Al recibir esta señal, el proceso maestro apaga sus procesos hijo.
Si un binario Angie recién iniciado sale, los procesos hijo del antiguo
maestro se inician de nuevo.
Cuando un proceso trabajador recibe esta señal, se apaga en modo de depuración
establecido por la directiva debug_points
.NGX_RECONFIGURE_SIGNAL
(SIGHUP
en la mayoría
de sistemas) - Reconfigurar.
Al recibir esta señal, el proceso maestro vuelve a leer la configuración y
crea un nuevo ciclo basado en ella.
Si el nuevo ciclo se crea con éxito, se elimina el antiguo ciclo y se inician los nuevos
procesos hijo.
Mientras tanto, los procesos hijo antiguos reciben la
señal NGX_SHUTDOWN_SIGNAL
.
En modo de un solo proceso, Angie crea un nuevo ciclo, pero mantiene el antiguo hasta
que ya no haya clientes con conexiones activas vinculadas a él.
Los procesos trabajadores y auxiliares ignoran esta señal.NGX_REOPEN_SIGNAL
(SIGUSR1
en la mayoría
de sistemas) — Reabrir archivos.
El proceso maestro envía esta señal a los trabajadores, que vuelven a abrir todos
open_files
relacionados con el ciclo.NGX_CHANGEBIN_SIGNAL
(SIGUSR2
en la mayoría
de sistemas) — Cambiar el binario de Angie.
El proceso maestro inicia un nuevo binario de Angie y pasa una lista de todos los sockets de escucha.
La lista en formato de texto, pasada en la variable de entorno "NGINX"
,
consta de números de descriptor separados por punto y coma.
El nuevo binario de Angie lee la variable "NGINX"
y añade los
sockets a su ciclo de inicio.
Otros procesos ignoran esta señal.kill()
para pasar señales a trabajadores y ayudantes.
En su lugar, Angie utiliza pares de sockets entre procesos que permiten enviar mensajes
entre todos los procesos de Angie.
Sin embargo, actualmente, los mensajes solo se envían desde el maestro a sus hijos.
Los mensajes transportan las señales estándar.Hilos#
pthreads
:typedef pthread_mutex_t ngx_thread_mutex_t;
ngx_int_t
ngx_thread_mutex_create(ngx_thread_mutex_t *mtx, ngx_log_t *log);
ngx_int_t
ngx_thread_mutex_destroy(ngx_thread_mutex_t *mtx, ngx_log_t *log);
ngx_int_t
ngx_thread_mutex_lock(ngx_thread_mutex_t *mtx, ngx_log_t *log);
ngx_int_t
ngx_thread_mutex_unlock(ngx_thread_mutex_t *mtx, ngx_log_t *log);
typedef pthread_cond_t ngx_thread_cond_t;
ngx_int_t
ngx_thread_cond_create(ngx_thread_cond_t *cond, ngx_log_t *log);
ngx_int_t
ngx_thread_cond_destroy(ngx_thread_cond_t *cond, ngx_log_t *log);
ngx_int_t
ngx_thread_cond_signal(ngx_thread_cond_t *cond, ngx_log_t *log);
ngx_int_t
ngx_thread_cond_wait(ngx_thread_cond_t *cond, ngx_thread_mutex_t *mtx,
ngx_log_t *log);
src/core/ngx_thread_pool.h
contiene
definiciones relevantes:struct ngx_thread_task_s {
ngx_thread_task_t *next;
ngx_uint_t id;
void *ctx;
void (*handler)(void *data, ngx_log_t *log);
ngx_event_t event;
};
typedef struct ngx_thread_pool_s ngx_thread_pool_t;
ngx_thread_pool_t *ngx_thread_pool_add(ngx_conf_t *cf, ngx_str_t *name);
ngx_thread_pool_t *ngx_thread_pool_get(ngx_cycle_t *cycle, ngx_str_t *name);
ngx_thread_task_t *ngx_thread_task_alloc(ngx_pool_t *pool, size_t size);
ngx_int_t ngx_thread_task_post(ngx_thread_pool_t *tp, ngx_thread_task_t *task);
ngx_thread_pool_add(cf, name)
, que o bien crea un nuevo grupo de hilos con el nombre dado name
o devuelve una referencia al grupo con ese nombre si ya existe.task
a una cola de un grupo de hilos especificado tp
en tiempo de ejecución, usa la función
ngx_thread_task_post(tp, task)
.ngx_thread_task_t
:typedef struct {
int foo;
} my_thread_ctx_t;
static void
my_thread_func(void *data, ngx_log_t *log)
{
my_thread_ctx_t *ctx = data;
/* esta función se ejecuta en un hilo separado */
}
static void
my_thread_completion(ngx_event_t *ev)
{
my_thread_ctx_t *ctx = ev->data;
/* ejecutado en el bucle de eventos de Angie */
}
ngx_int_t
my_task_offload(my_conf_t *conf)
{
my_thread_ctx_t *ctx;
ngx_thread_task_t *task;
task = ngx_thread_task_alloc(conf->pool, sizeof(my_thread_ctx_t));
if (task == NULL) {
return NGX_ERROR;
}
ctx = task->ctx;
ctx->foo = 42;
task->handler = my_thread_func;
task->event.handler = my_thread_completion;
task->event.data = ctx;
if (ngx_thread_task_post(conf->thread_pool, task) != NGX_OK) {
return NGX_ERROR;
}
return NGX_OK;
}
Módulos#
Agregar nuevos módulos#
config
y un archivo con el código fuente del módulo.
El archivo config
contiene toda la información necesaria para que Angie integre el módulo, por ejemplo:ngx_module_type=CORE
ngx_module_name=ngx_foo_module
ngx_module_srcs="$ngx_addon_dir/ngx_foo_module.c"
. auto/module
ngx_addon_name=$ngx_module_name
config
es un script de shell POSIX que puede definir y acceder a las siguientes variables:ngx_module_type
— Tipo de módulo a construir.
Los valores posibles son CORE
, HTTP
,
HTTP_FILTER
, HTTP_INIT_FILTER
,
HTTP_AUX_FILTER
, MAIL
,
STREAM
, o MISC
.ngx_module_name
— Nombres de módulo.
Para construir varios módulos a partir de un conjunto de archivos de origen, especifica una
lista de nombres separados por espacios.
El primer nombre indica el nombre del binario de salida para el módulo dinámico.
Los nombres de la lista deben coincidir con los nombres usados en el código fuente.ngx_addon_name
— Nombre del módulo tal como aparece en la salida
en la consola desde el script de configuración.ngx_module_srcs
— Lista de archivos fuente separados por espacios
utilizados para compilar el módulo.
La variable $ngx_addon_dir
se puede usar para representar la ruta
al directorio del módulo.ngx_module_incs
— Rutas de include necesarias para compilar el módulongx_module_deps
— Lista de dependencias del módulo separadas por espacios.
Por lo general, es la lista de archivos de cabecera.ngx_module_libs
— Lista de bibliotecas separadas por espacios con las que enlazar
el módulo.
Por ejemplo, use ngx_module_libs=-lpthread
para enlazar
a la biblioteca libpthread
.
Los siguientes macros se pueden usar para enlazar con las mismas bibliotecas que Angie:
LIBXSLT
, LIBGD
, GEOIP
,
PCRE
, OPENSSL
, MD5
,
SHA1
, ZLIB
y PERL
.ngx_module_link
— Variable establecida por el sistema de construcción para
DYNAMIC
en un módulo dinámico o ADDON
para un módulo estático y se usa para determinar distintas acciones a realizar
dependiendo del tipo de enlace.ngx_module_order
— Orden de carga del módulo;
útil para los tipos de módulos HTTP_FILTER
y
HTTP_AUX_FILTER
.
El formato de esta opción es una lista de módulos separados por espacios.
Todos los módulos de la lista que siguen al nombre del módulo actual terminan después de él en
la lista global de módulos, lo que establece el orden para la inicialización de módulos.
Para los módulos de filtro, una inicialización posterior significa una ejecución anterior.ngx_http_copy_filter_module
lee los datos para otros
módulos de filtro y se ubica cerca de la parte inferior de la lista para que sea uno de
los primeros en ejecutarse.
El ngx_http_write_filter_module
escribe los datos en el
socket del cliente y se ubica cerca de la parte superior de la lista, y es el último en
ejecutarse.ngx_http_copy_filter
en la lista de módulos para que el filtro
controlador se ejecute después del controlador de la copia.
Para otros tipos de módulos, el valor por defecto es la cadena vacía.--add-module=/path/to/module
en el script de configuración.
Para compilar un módulo para su carga dinámica posterior en Angie, usa el argumento
--add-dynamic-module=/path/to/module
.Core Modules#
ngx_module_t
, que se define de la siguiente manera:struct ngx_module_s {
/* private part is omitted */
void *ctx;
ngx_command_t *commands;
ngx_uint_t type;
ngx_int_t (*init_master)(ngx_log_t *log);
ngx_int_t (*init_module)(ngx_cycle_t *cycle);
ngx_int_t (*init_process)(ngx_cycle_t *cycle);
ngx_int_t (*init_thread)(ngx_cycle_t *cycle);
void (*exit_thread)(ngx_cycle_t *cycle);
void (*exit_process)(ngx_cycle_t *cycle);
void (*exit_master)(ngx_cycle_t *cycle);
/* stubs for future extensions are omitted */
};
NGX_MODULE_V1
.ctx
, reconoce las directivas de configuración, especificadas en el arreglo
commands
, y puede ser invocado en ciertas etapas del ciclo de Angie.
El ciclo de vida del módulo consiste en los siguientes eventos:init_module
se llama en el contexto del proceso maestro.
El manejador init_module
se llama en el proceso maestro cada
vez que se carga una configuración.init_process
se llama en cada uno de ellos.exit_process
.exit_master
antes de
salir.init_thread
y exit_thread
no se llaman actualmente.
Tampoco hay un manejador init_master
, porque sería un gasto innecesario.type
define exactamente qué se almacena en el
campo ctx
.
Su valor es uno de los siguientes tipos:NGX_CORE_MODULE
NGX_EVENT_MODULE
NGX_HTTP_MODULE
NGX_MAIL_MODULE
NGX_STREAM_MODULE
NGX_CORE_MODULE
es el más básico y, por tanto, el tipo de módulo más genérico y de nivel más bajo.
Los otros tipos de módulo se implementan sobre él y proporcionan una forma más conveniente de tratar con dominios correspondientes, como manejar eventos o peticiones HTTP.ngx_core_module
,
ngx_errlog_module
, ngx_regex_module
,
ngx_thread_pool_module
y
ngx_openssl_module
módulos.
El módulo HTTP, el módulo de flujo (stream), el módulo de correo y los módulos de eventos son módulos núcleo también.
El contexto de un módulo núcleo se define como:typedef struct {
ngx_str_t name;
void *(*create_conf)(ngx_cycle_t *cycle);
char *(*init_conf)(ngx_cycle_t *cycle, void *conf);
} ngx_core_module_t;
name
es una cadena con el nombre del módulo,
create_conf
y init_conf
son punteros a funciones que crean e inicializan la configuración del módulo, respectivamente.
Para los módulos núcleo, Angie llama a create_conf
antes de analizar
una nueva configuración y init_conf
después de que toda la configuración
se ha analizado con éxito.
La función típica create_conf
reserva memoria para la
configuración y establece valores por defecto.ngx_foo_module
podría
verse así:/*
* Copyright (C) Author.
*/
#include <ngx_config.h>
#include <ngx_core.h>
typedef struct {
ngx_flag_t enable;
} ngx_foo_conf_t;
static void *ngx_foo_create_conf(ngx_cycle_t *cycle);
static char *ngx_foo_init_conf(ngx_cycle_t *cycle, void *conf);
static char *ngx_foo_enable(ngx_conf_t *cf, void *post, void *data);
static ngx_conf_post_t ngx_foo_enable_post = { ngx_foo_enable };
static ngx_command_t ngx_foo_commands[] = {
{ ngx_string("foo_enabled"),
NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
0,
offsetof(ngx_foo_conf_t, enable),
&ngx_foo_enable_post },
ngx_null_command
};
static ngx_core_module_t ngx_foo_module_ctx = {
ngx_string("foo"),
ngx_foo_create_conf,
ngx_foo_init_conf
};
ngx_module_t ngx_foo_module = {
NGX_MODULE_V1,
&ngx_foo_module_ctx, /* module context */
ngx_foo_commands, /* module directives */
NGX_CORE_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static void *
ngx_foo_create_conf(ngx_cycle_t *cycle)
{
ngx_foo_conf_t *fcf;
fcf = ngx_pcalloc(cycle->pool, sizeof(ngx_foo_conf_t));
if (fcf == NULL) {
return NULL;
}
fcf->enable = NGX_CONF_UNSET;
return fcf;
}
static char *
ngx_foo_init_conf(ngx_cycle_t *cycle, void *conf)
{
ngx_foo_conf_t *fcf = conf;
ngx_conf_init_value(fcf->enable, 0);
return NGX_CONF_OK;
}
static char *
ngx_foo_enable(ngx_conf_t *cf, void *post, void *data)
{
ngx_flag_t *fp = data;
if (*fp == 0) {
return NGX_CONF_OK;
}
ngx_log_error(NGX_LOG_NOTICE, cf->log, 0, "Foo Module is enabled");
return NGX_CONF_OK;
}
Directivas de Configuración#
ngx_command_t
define una única directiva de configuración.
Cada módulo que admite configuración proporciona una matriz de tales estructuras
que describen cómo procesar los argumentos y qué manejadores llamar:typedef struct ngx_command_s ngx_command_t;
struct ngx_command_s {
ngx_str_t name;
ngx_uint_t type;
char *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
ngx_uint_t conf;
ngx_uint_t offset;
void *post;
};
ngx_null_command
.
El name
es el nombre de una directiva tal como aparece
en el archivo de configuración, por ejemplo "worker_processes" o "listen".
El type
es un campo de bits de banderas que especifica el número de
argumentos que toma la directiva, su tipo y el contexto en el que aparece.
Las banderas son:NGX_CONF_NOARGS
— La directiva no toma argumentos.NGX_CONF_1MORE
— La directiva toma uno o más argumentos.NGX_CONF_2MORE
— La directiva toma dos o más argumentos.NGX_CONF_TAKE1
..:samp:NGX_CONF_TAKE7 —
La directiva toma exactamente el número indicado de argumentos.NGX_CONF_TAKE12
, NGX_CONF_TAKE13
,
NGX_CONF_TAKE23
, NGX_CONF_TAKE123
,
NGX_CONF_TAKE1234
— La directiva puede tomar un número diferente de
argumentos.
Las opciones están limitadas a los números indicados.
Por ejemplo, NGX_CONF_TAKE12
significa que toma uno o dos
argumentos.NGX_CONF_BLOCK
— La directiva es un bloque, es decir, puede
contener otras directivas dentro de sus llaves de apertura y cierre, o incluso
implementar su propio analizador para manejar contenidos internos.NGX_CONF_FLAG
— La directiva toma un valor booleano, ya sea
on
o off
.NGX_MAIN_CONF
— En el contexto de nivel superior.NGX_HTTP_MAIN_CONF
— En el bloque http
.NGX_HTTP_SRV_CONF
— En un bloque server
dentro del bloque http
.NGX_HTTP_LOC_CONF
— En un bloque location
dentro del bloque http
.NGX_HTTP_UPS_CONF
— En un bloque upstream
dentro del bloque http
.NGX_HTTP_SIF_CONF
— En un bloque if
dentro
de un bloque server
en el bloque http
.NGX_HTTP_LIF_CONF
— En un bloque if
dentro de
un bloque location
en el bloque http
.NGX_HTTP_LMT_CONF
— En un bloque limit_except
dentro del bloque http
.NGX_STREAM_MAIN_CONF
— En el bloque stream
bloque.NGX_STREAM_SRV_CONF
— En un bloque server
dentro del bloque stream
.NGX_STREAM_UPS_CONF
— En un bloque upstream
dentro del bloque stream
.NGX_MAIL_MAIN_CONF
— En el bloque mail
.NGX_MAIL_SRV_CONF
— En un bloque server
dentro del bloque mail
.NGX_EVENT_CONF
— En el bloque event
.NGX_DIRECT_CONF
— Usado por módulos que no crean una jerarquía de contextos y solo tienen una configuración global.
Esta configuración se pasa al manejador como el argumento conf
.set
define un manejador que procesa una directiva y guarda los valores analizados en la configuración correspondiente.
Existen varias funciones que realizan conversiones comunes:ngx_conf_set_flag_slot
— Convierte las cadenas literales
on
y off
en un valor ngx_flag_t
con valores 1 o 0, respectivamente.ngx_conf_set_str_slot
— Almacena una cadena como valor del tipo
ngx_str_t
.ngx_conf_set_str_array_slot
— Añade un valor a un arreglo
ngx_array_t
de cadenas ngx_str_t
.
El arreglo se crea si no existe ya.ngx_conf_set_keyval_slot
— Añade un par clave-valor a un
arreglo ngx_array_t
de pares clave-valor
ngx_keyval_t
.
La primera cadena se convierte en la clave y la segunda en el valor.
El arreglo se crea si no existe ya.ngx_conf_set_num_slot
— Convierte el argumento de una directiva
a un valor ngx_int_t
.ngx_conf_set_size_slot
— Convierte un
size a un valor size_t
expresado en bytes.ngx_conf_set_off_slot
— Convierte un
offset a un valor off_t
expresado en bytes.ngx_conf_set_msec_slot
— Convierte un
time a un valor ngx_msec_t
expresado en milisegundos.ngx_conf_set_sec_slot
— Convierte un
time a un valor time_t
expresado en segundos.ngx_conf_set_bufs_slot
— Convierte los dos argumentos suministrados
en un objeto ngx_bufs_t
que contiene el número y
size de los búferes.ngx_conf_set_enum_slot
— Convierte el argumento suministrado
en un valor ngx_uint_t
.
El arreglo null-terminated de ngx_conf_enum_t
pasado en el
campo post
define las cadenas aceptables y sus valores enteros correspondientes.ngx_conf_set_bitmask_slot
— Convierte los argumentos suministrados
en un valor ngx_uint_t
.
Los valores de máscara para cada argumento se ORean para producir el resultado.
El arreglo null-terminated de ngx_conf_bitmask_t
pasado en el
campo post
define las cadenas aceptables y los valores de máscara correspondientes.set_path_slot
— Convierte los argumentos suministrados a un
valor ngx_path_t
y realiza todas las inicializaciones necesarias.
Para más detalles, consulte la documentación de la directiva
proxy_temp_path.set_access_slot
— Convierte los argumentos suministrados a una máscara de permisos de archivo.
Para más detalles, consulte la documentación de la
directiva proxy_store_access.conf
define qué estructura de configuración se pasa al
manejador de directorios.
Los módulos núcleo solo tienen la configuración global y establecen
la bandera NGX_DIRECT_CONF
para acceder a ella.
Módulos como HTTP, Stream o Mail crean jerarquías de configuraciones.
Por ejemplo, la configuración de un módulo se crea para server
,
location
y if
alcances.NGX_HTTP_MAIN_CONF_OFFSET
— Configuración para el
bloque http
.NGX_HTTP_SRV_CONF_OFFSET
— Configuración para un bloque
server
dentro del bloque http
.NGX_HTTP_LOC_CONF_OFFSET
— Configuración para un
bloque location
dentro del http
.NGX_STREAM_MAIN_CONF_OFFSET
— Configuración para el
bloque stream
.NGX_STREAM_SRV_CONF_OFFSET
— Configuración para un
bloque server
dentro del bloque stream
.NGX_MAIL_MAIN_CONF_OFFSET
— Configuración para el
bloque mail
.NGX_MAIL_SRV_CONF_OFFSET
— Configuración para un
bloque server
dentro del bloque mail
.offset
define el desplazamiento de un campo en una estructura de configuración del módulo que contiene valores para esta directiva en particular.
El uso típico es emplear la macro offsetof()
.post
field tiene dos propósitos: puede usarse para definir
un manejador que se llame después de que el manejador principal haya completado, o para pasar
datos adicionales al manejador principal.
En el primer caso, la estructura ngx_conf_post_t
debe
inicializarse con un puntero al manejador, por ejemplo:static char *ngx_do_foo(ngx_conf_t *cf, void *post, void *data);
static ngx_conf_post_t ngx_foo_post = { ngx_do_foo };
post
es el propio objeto ngx_conf_post_t
y data
es un puntero al valor, convertido a partir de los argumentos por el manejador principal con el tipo adecuado.HTTP#
Conexión#
ngx_event_accept()
acepta una conexión TCP de cliente.
Este manejador se llama en respuesta a una notificación de lectura en un socket de escucha.
En esta etapa se crea un nuevo objeto ngx_connection_t
para envolver el socket del cliente recién aceptado.
Cada listener de Angie proporciona un manejador para pasar el nuevo objeto de conexión.
Para conexiones HTTP es ngx_http_init_connection(c)
.ngx_http_init_connection()
realiza una inicialización temprana de la conexión HTTP.
En esta etapa se crea un objeto ngx_http_connection_t
para la conexión y su referencia se almacena en el campo data
de la conexión.
Más tarde será reemplazado por un objeto de solicitud HTTP.
También se inicia un analizador del protocolo PROXY y el handshake SSL en esta etapa.ngx_http_wait_request_handler()
el manejador de eventos de lectura se llama cuando hay datos disponibles en el socket del cliente.
En esta etapa se crea un objeto de solicitud HTTP ngx_http_request_t
y se asigna al campo data
de la conexión.ngx_http_process_request_line()
el manejador de eventos de lectura lee la línea de solicitud del cliente.
El manejador es establecido por ngx_http_wait_request_handler()
.
Los datos se leen en el buffer
de la conexión.
El tamaño del búfer se establece inicialmente por la directiva client_header_buffer_size.
Se espera que todo el encabezado del cliente quepa en el búfer.
Si el tamaño inicial no es suficiente, se asigna un búfer más grande, con la capacidad definida por la directiva large_client_header_buffers.ngx_http_process_request_headers()
es el manejador de eventos de lectura, se configura después de ngx_http_process_request_line()
para leer el encabezado de la solicitud del cliente.ngx_http_core_run_phases()
se llama cuando el encabezado de la solicitud ha sido completamente leído y analizado.
Esta función ejecuta las fases de la solicitud desde NGX_HTTP_POST_READ_PHASE
hasta NGX_HTTP_CONTENT_PHASE
.
La última fase tiene como objetivo generar una respuesta y pasarla a lo largo de la cadena de filtros.
La respuesta no necesariamente se envía al cliente en esta fase.
Puede permanecer en búfer y enviarse en la etapa de finalización.ngx_http_finalize_request()
suele llamarse cuando la solicitud ha generado toda la salida o ha producido un error.
En este último caso se busca una página de error adecuada y se utiliza como respuesta.
Si la respuesta no se ha enviado por completo al cliente en este punto, se activa un escritor HTTP ngx_http_writer()
para terminar de enviar los datos pendientes.ngx_http_finalize_connection()
se llama cuando la respuesta completa ha sido enviada al cliente y la solicitud puede ser destruida.
Si la función de mantener la conexión viva (keepalive) está habilitada,
ngx_http_set_keepalive()
se llama, lo que destruye la solicitud actual y espera la siguiente solicitud en la conexión.
De lo contrario, ngx_http_close_request()
destruye tanto la solicitud como la conexión.Solicitud#
ngx_http_request_t
. Algunos de los campos de este objeto son:connection
— Puntero a un objeto de conexión de cliente ngx_connection_t
.
Varias solicitudes pueden hacer referencia al mismo objeto de conexión al mismo tiempo: una solicitud principal y sus subsolicitudes.
Después de eliminarse una solicitud, puede crearse una nueva en la misma conexión.data
de ngx_connection_t
apunta de vuelta a la solicitud.
Dichas solicitudes se conocen como activas, a diferencia de las demás solicitudes vinculadas a la conexión.
Una solicitud activa se utiliza para manejar eventos de conexión del cliente y puede enviar su salida al cliente.
Normalmente, cada solicitud llega a estar activa en algún momento para poder enviar su salida.ctx
— Array de contextos de módulos HTTP.
Cada módulo de tipo NGX_HTTP_MODULE
puede almacenar cualquier valor (normalmente, un puntero a una estructura) en la solicitud.
El valor se almacena en el arreglo ctx
en la posición ctx_index
del módulo.
Las siguientes macros proporcionan una forma conveniente de obtener y establecer los contextos de la solicitud:ngx_http_get_module_ctx(r, module)
— Devuelve el contexto del module
ngx_http_set_ctx(r, c, module)
— Establece c
como el contexto del module
main_conf
, srv_conf
, loc_conf
— Arreglos de configuraciones actuales de la solicitud.
Las configuraciones se almacenan en las posiciones ctx_index
del módulo.read_event_handler
, write_event_handler
— Manejadores de eventos de lectura y escritura para la solicitud.
Normalmente, tanto el manejador de lectura como el de escritura para una conexión HTTP se establecen en ngx_http_request_handler()
.
Esta función llama a los manejadores read_event_handler
y write_event_handler
para la solicitud actualmente activa.cache
— Objeto de caché de la solicitud para almacenar en caché la respuesta upstream.upstream
— Objeto upstream de la solicitud para proxying.pool
— Pool de la solicitud.
El objeto de solicitud se asigna en este pool, que se destruye cuando la solicitud se elimina.
Para asignaciones que deben estar disponibles durante toda la vida de la conexión del cliente, use el pool de ngx_connection_t
en su lugar.header_in
— Buffer en el que se lee el encabezado de la solicitud HTTP del cliente.headers_in
, headers_out
— Objetos de encabezados HTTP de entrada y salida.
Ambos objetos contienen el campo headers
de tipo ngx_list_t
para mantener la lista cruda de encabezados.
Además de eso, hay encabezados específicos disponibles para obtenerlos y establecerlos como campos separados, por ejemplo content_length_n
, status
, etc.request_body
— Objeto del cuerpo de la solicitud del cliente.start_sec
, start_msec
— Momento en el que se creó la solicitud, utilizado para rastrear la duración de la solicitud.method
, method_name
— Representación numérica y textual del método de la solicitud HTTP del cliente.
Los valores numéricos de los métodos están definidos en src/http/ngx_http_request.h
con las macros NGX_HTTP_GET
, NGX_HTTP_HEAD
, NGX_HTTP_POST
, etc.http_protocol
— Versión del protocolo HTTP del cliente en su forma de texto original ("HTTP/1.0", "HTTP/1.1" etc).http_version
— Versión del protocolo HTTP del cliente en forma numérica (NGX_HTTP_VERSION_10
, NGX_HTTP_VERSION_11
, etc.).http_major
, http_minor
— Versión numérica del protocolo HTTP del cliente, desglosada en partes mayor y menor.request_line
, unparsed_uri
— Línea de solicitud y URI en la solicitud original del cliente.uri
, args
, exten
— URI, argumentos y extensión de archivo para la solicitud actual.
El valor de URI aquí podría diferir del URI original enviado por el cliente debido a la normalización.
A lo largo del procesamiento de la solicitud, estos valores pueden cambiar a medida que se realizan redirecciones internas.main
— Puntero a un objeto de solicitud principal.
Este objeto se crea para procesar una solicitud HTTP del cliente, a diferencia de las subsolicitudes, que se crean para realizar una tarea específica dentro de la solicitud principal.parent
— Puntero a la solicitud principal de una subsolicitud.postponed
— Lista de búferes de salida y subsolicitudes, en el orden en que se envían y crean.
La lista se utiliza por el filtro de posposición para proporcionar una salida de la solicitud coherente cuando partes de ella son creadas por subsolicitudes.post_subrequest
— Puntero a un manejador con el contexto que debe llamarse cuando una subsolicitud se finaliza.
No se utiliza para las solicitudes principales.posted_requests
— Lista de solicitudes que deben iniciarse o reanudarse, lo que se realiza llamando al manejador de escritura de la solicitud.
Normalmente, este manejador contiene la función principal de la solicitud, que al principio ejecuta las fases de la solicitud y luego genera la salida.ngx_http_post_request(r, NULL)
.
Siempre se publica en la lista principal de solicitudes posted_requests
.
La función ngx_http_run_posted_requests(c)
ejecuta todas las
solicitudes que están publicadas en la solicitud principal de la solicitud activa de la conexión pasada.
Todos los manejadores de eventos llaman a ngx_http_run_posted_requests
,
lo que puede dar lugar a nuevas solicitudes publicadas.
Normalmente, se llama después de invocar el manejador de lectura o escritura de una solicitud.phase_handler
— Índice de la fase actual de la solicitud.ncaptures
, captures
, captures_data
— Capturas de expresiones regulares producidas por la última coincidencia de regex de la solicitud.
Una coincidencia de regex puede ocurrir en varios lugares durante el procesamiento de la solicitud:
búsqueda en mapas, búsqueda del servidor por SNI o HTTP Host, reescritura, proxy_redirect, etc.
Las capturas producidas por una búsqueda se almacenan en los campos mencionados arriba.
El campo ncaptures
contiene el número de capturas, captures
contiene los límites de las capturas y captures_data
contiene la cadena con la que se emparejó la expresión regular y que se utiliza para extraer las capturas.
Después de cada nueva coincidencia de regex, las capturas de la solicitud se restablecen para contener nuevos valores.count
— Contador de referencias de la solicitud.
Este campo tiene sentido solo para la solicitud principal.
Aumentar el contador se hace con r->main->count++
.
Para disminuirlo, llame a ngx_http_finalize_request(r, rc)
.
La creación de una subsolicitud y la ejecución del proceso de lectura del cuerpo de la solicitud también incrementan el contador.subrequests
— Nivel actual de anidamiento de subsolicitudes.
Cada subsolicitud hereda el nivel de anidamiento de su padre, reducido en uno.
Se genera un error si el valor llega a cero.
El valor para la solicitud principal está definido por la
NGX_HTTP_MAX_SUBREQUESTS
constante.uri_changes
— Número de cambios de URI que quedan para la solicitud.
El número total de veces que una solicitud puede cambiar su URI está limitado por la
constante NGX_HTTP_MAX_URI_CHANGES
.
Con cada cambio, el valor se decrementa hasta que llega a cero, momento en el cual
se genera un error.
Las reescrituras y las redirecciones internas a ubicaciones normales o con nombre se consideran cambios de URI.blocked
— Contador de bloqueos mantenidos sobre la solicitud.
Mientras este valor sea distinto de cero, la solicitud no puede terminarse.
Actualmente, este valor aumenta por operaciones AIO pendientes (AIO POSIX y operaciones con hilos) y por un bloqueo de caché activo.buffered
— Máscara de bits que indica qué módulos han bufferizado la salida producida por la solicitud.
Varios filtros pueden bufferizar la salida; por ejemplo, sub_filter puede bufferizar datos debido a una coincidencia parcial de cadena, el filtro copy puede bufferizar datos debido a la falta de búferes de salida disponibles, etc.
Mientras este valor sea distinto de cero, la solicitud no se finaliza hasta que se vacíen los búferes de salida.header_only
— Indicador de que la salida no requiere un cuerpo.
Por ejemplo, este indicador se utiliza en las solicitudes HTTP HEAD.keepalive
— Indicador de si se admite keepalive de la conexión del cliente.
El valor se infiere a partir de la versión HTTP y del valor del encabezado
"Connection".header_sent
— Indicador de que el encabezado de salida ya ha sido enviado por la solicitud.internal
— Indicador de que la solicitud actual es interna.
Para entrar en el estado interno, una solicitud debe pasar por una redirección interna o ser una subsolicitud.
Las solicitudes internas pueden entrar en ubicaciones internas.allow_ranges
— Indicador de que se puede enviar una respuesta parcial al cliente, tal como lo solicita el encabezado HTTP Range.subrequest_ranges
— Indicador de que se puede enviar una respuesta parcial mientras se procesa una subsolicitud.single_range
— Indicador de que solo se puede enviar al cliente un único rango continuo de datos de salida.
Este indicador suele activarse al enviar un flujo de datos, por ejemplo desde un servidor proxy, y toda la respuesta no está disponible en un solo búfer.main_filter_need_in_memory
,
filter_need_in_memory
— Indicadores que solicitan que la salida producida esté en búferes de memoria en lugar de archivos.
Esto es una señal para que el filtro de copia lea datos desde búferes de archivos incluso si sendfile está habilitado.
La diferencia entre las dos señales es la ubicación de los módulos de filtro que las establecen.
Los filtros llamados antes del filtro postpone en la cadena de filtros establecen
filter_need_in_memory
, solicitando que únicamente la salida de la solicitud actual se almacene en búferes de memoria.
Los filtros llamados después en la cadena de filtros establecen
main_filter_need_in_memory
, solicitando que tanto la solicitud principal como todas las subsolicitudes lean archivos en memoria mientras se envía la salida.filter_need_temporary
— Indicador que solicita que la salida de la solicitud se genere en búferes temporales, pero no en búferes de memoria de solo lectura ni en búferes de archivos.
Esto es utilizado por filtros que pueden modificar la salida directamente en los búferes donde se envía.Configuración#
http
.
Funciona como configuración global para un módulo.server
.
Funciona como configuración específica del servidor para un módulo.location
,
if
o limit_except
.
Funciona como configuración específica de ubicación para un módulo.foo
, de tipo entero sin signo.typedef struct {
ngx_uint_t foo;
} ngx_http_foo_loc_conf_t;
static ngx_http_module_t ngx_http_foo_module_ctx = {
NULL, /* preconfiguration */
NULL, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_http_foo_create_loc_conf, /* create location configuration */
ngx_http_foo_merge_loc_conf /* merge location configuration */
};
static void *
ngx_http_foo_create_loc_conf(ngx_conf_t *cf)
{
ngx_http_foo_loc_conf_t *conf;
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_foo_loc_conf_t));
if (conf == NULL) {
return NULL;
}
conf->foo = NGX_CONF_UNSET_UINT;
return conf;
}
static char *
ngx_http_foo_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
{
ngx_http_foo_loc_conf_t *prev = parent;
ngx_http_foo_loc_conf_t *conf = child;
ngx_conf_merge_uint_value(conf->foo, prev->foo, 1);
}
ngx_http_foo_create_loc_conf()
crea una nueva estructura de configuración, y
ngx_http_foo_merge_loc_conf()
fusiona una configuración con
la configuración de un nivel superior.
De hecho, la configuración del servidor y de la ubicación no existe solo a nivel de servidor y de ubicación, sino que también se crea para todos los niveles superiores.
Específicamente, una configuración del servidor también se crea a nivel principal y
las configuraciones de ubicación se crean a los niveles principal, servidor y ubicación.
Estas configuraciones permiten especificar configuraciones específicas del servidor y de la ubicación en cualquier nivel de un archivo de configuración Angie.
Finalmente, las configuraciones se fusionan hacia abajo.
Se proporcionan varios macros como NGX_CONF_UNSET
y
NGX_CONF_UNSET_UINT
para indicar una configuración ausente e ignorarla durante la fusión.
Los macros de fusión estándar de Angie, como ngx_conf_merge_value()
y
ngx_conf_merge_uint_value()
ofrecen una forma conveniente de
fusionar una configuración y establecer el valor por defecto si ninguna de las configuraciones
proporcionó un valor explícito.
Para la lista completa de macros para diferentes tipos, consulta
src/core/ngx_conf_file.h
.ngx_conf_t
como primer argumento.ngx_http_conf_get_module_main_conf(cf, module)
ngx_http_conf_get_module_srv_conf(cf, module)
ngx_http_conf_get_module_loc_conf(cf, module)
handler
de la estructura.static ngx_int_t ngx_http_foo_handler(ngx_http_request_t *r);
static ngx_command_t ngx_http_foo_commands[] = {
{ ngx_string("foo"),
NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
ngx_http_foo,
0,
0,
NULL },
ngx_null_command
};
static char *
ngx_http_foo(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_core_loc_conf_t *clcf;
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
clcf->handler = ngx_http_bar_handler;
return NGX_CONF_OK;
}
ngx_http_get_module_main_conf(r, module)
ngx_http_get_module_srv_conf(r, module)
ngx_http_get_module_loc_conf(r, module)
ngx_http_request_t
.
La configuración principal de una solicitud nunca cambia.
La configuración del servidor puede cambiar con respecto al valor por defecto después de
que se elige el servidor virtual para la solicitud.
La configuración de ubicación seleccionada para procesar una solicitud puede cambiar varias
veces como resultado de una operación de reescritura o de una redirección interna.
El siguiente ejemplo muestra cómo acceder a la configuración HTTP de un módulo en
tiempo de ejecución.static ngx_int_t
ngx_http_foo_handler(ngx_http_request_t *r)
{
ngx_http_foo_loc_conf_t *flcf;
flcf = ngx_http_get_module_loc_conf(r, ngx_http_foo_module);
...
}
Phases#
NGX_HTTP_POST_READ_PHASE
— Primera fase.
El módulo RealIP
registra su manejador en esta fase para permitir
la sustitución de direcciones de cliente antes de que se invoque cualquier otro módulo.NGX_HTTP_SERVER_REWRITE_PHASE
— Fase en la que
se procesan las directivas de reescritura definidas en un bloque server
(pero fuera de un bloque location
).
El
módulo Rewrite instala su manejador en esta fase.NGX_HTTP_FIND_CONFIG_PHASE
— Fase especial
donde se elige una ubicación basada en la URI de la solicitud.
Antes de esta fase, la ubicación por defecto para el servidor virtual relevante
se asigna a la solicitud, y cualquier módulo que solicite una configuración de ubicación
recibe la configuración de la ubicación del servidor por defecto.
Esta fase asigna una nueva ubicación a la solicitud.
No se pueden registrar manejadores adicionales en esta fase.NGX_HTTP_REWRITE_PHASE
— Igual que
NGX_HTTP_SERVER_REWRITE_PHASE
, pero para las reglas de reescritura definidas en la ubicación,
elegida en la fase anterior.NGX_HTTP_POST_REWRITE_PHASE
— Fase especial
donde la solicitud se redirige a una nueva ubicación si su URI cambió
durante una reescritura.
Esto se implementa haciendo que la solicitud pase de nuevo por
NGX_HTTP_FIND_CONFIG_PHASE
nuevamente.
No se pueden registrar manejadores adicionales en esta fase.NGX_HTTP_PREACCESS_PHASE
— Fase común para distintos
tipos de manejadores, no asociada con el control de acceso.
Los módulos estándar de Angie
Limit Conn y
Limit Req registran sus manejadores en esta fase.NGX_HTTP_ACCESS_PHASE
— Fase en la que se verifica
que el cliente está autorizado para realizar la solicitud.
Módulos estándar de Angie como
Acceso y
Auth Basic registran sus manejadores en esta fase.
Por defecto, el cliente debe superar la verificación de autorización de todos los manejadores
registrados en esta fase para que la solicitud continúe a la siguiente fase.
La directiva satisfy puede usarse para permitir que el procesamiento continúe si alguno de los manejadores de la fase autoriza al cliente.NGX_HTTP_POST_ACCESS_PHASE
— Fase especial en la que se procesa la
directiva satisfy any.
Si algunos manejadores de la fase de acceso negaron el acceso y ninguno lo permitió explícitamente, la solicitud se finaliza.
No se pueden registrar manejadores adicionales en esta fase.NGX_HTTP_PRECONTENT_PHASE
— Fase para que se llamen los manejadores antes de generar el contenido.
Módulos estándar como try_files y
Mirror registran sus manejadores en esta fase.NGX_HTTP_CONTENT_PHASE
— Fase en la que normalmente se genera la respuesta.
Varios módulos estándar de Angie registran sus manejadores en esta fase,
incluido Índice.
Se llaman de forma secuencial hasta que alguno de ellos genere la salida.
También es posible establecer manejadores de contenido por ubicación.
Si la configuración de ubicación de Módulo HTTP tiene establecida handler
, se llama como manejador de contenido y se ignoran los manejadores instalados en esta fase.NGX_HTTP_LOG_PHASE
— Fase en la que se realiza el registro de la solicitud.
Actualmente, solo el módulo Log registra su manejador
en esta etapa para el registro de acceso.
Los manejadores de la fase de registro se llaman al final del procesamiento de la solicitud, justo antes de liberar la solicitud.static ngx_http_module_t ngx_http_foo_module_ctx = {
NULL, /* preconfiguration */
ngx_http_foo_init, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
NULL, /* create location configuration */
NULL /* merge location configuration */
};
static ngx_int_t
ngx_http_foo_handler(ngx_http_request_t *r)
{
ngx_table_elt_t *ua;
ua = r->headers_in.user_agent;
if (ua == NULL) {
return NGX_DECLINED;
}
/* reject requests with "User-Agent: foo" */
if (ua->value.len == 3 && ngx_strncmp(ua->value.data, "foo", 3) == 0) {
return NGX_HTTP_FORBIDDEN;
}
return NGX_DECLINED;
}
static ngx_int_t
ngx_http_foo_init(ngx_conf_t *cf)
{
ngx_http_handler_pt *h;
ngx_http_core_main_conf_t *cmcf;
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
h = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers);
if (h == NULL) {
return NGX_ERROR;
}
*h = ngx_http_foo_handler;
return NGX_OK;
}
NGX_OK
— Proceda a la siguiente fase.NGX_DECLINED
— Proceda al siguiente manejador de la fase actual.
Si el manejador actual es el último en la fase actual,
pase a la siguiente fase.NGX_AGAIN
, NGX_DONE
— Suspendir
la gestión de la fase hasta que ocurra algún evento futuro que pueda ser
una operación de E/S asíncrona o simplemente un retardo, por ejemplo.
Se asume que la gestión de la fase se reanudará más tarde llamando
ngx_http_core_run_phases()
.NGX_DECLINED
se considera un código de finalización.
Cualquier código de retorno de los manejadores de contenido de la ubicación se considera un
código de finalización.
En la fase de acceso, en el modo
satisfy any
cualquier código de retorno distinto de NGX_OK
,
NGX_DECLINED
, NGX_AGAIN
,
NGX_DONE
se considera una denegación.
Si no hay manejadores de acceso subsiguientes que permitan o deneguen el acceso con un código diferente,
el código de denegación se convertirá en el código de finalización.Ejemplos#
Code style#
Reglas generales#
ngx_
o un prefijo más específico como
ngx_http_
y ngx_mail_
size_t
ngx_utf8_length(u_char *p, size_t n)
{
u_char c, *last;
size_t len;
last = p + n;
for (len = 0; p < last; len++) {
c = *p;
if (c < 0x80) {
p++;
continue;
}
if (ngx_utf8_decode(&p, last - p) > 0x10ffff) {
/* invalid UTF-8 */
return n;
}
}
return len;
}
Archivos#
/*
* Copyright (C) Author Name
* Copyright (C) Organization, Inc.
*/
ngx_config.h
y ngx_core.h
se
incluyen siempre primero, seguidos de uno de
ngx_http.h
, ngx_stream.h
,
o ngx_mail.h
.
A continuación siguen archivos de cabecera externos opcionales:#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <libxslt/xslt.h>
#if (NGX_HAVE_EXSLT)
#include <libexslt/exslt.h>
#endif
#ifndef _NGX_PROCESS_CYCLE_H_INCLUDED_
#define _NGX_PROCESS_CYCLE_H_INCLUDED_
...
#endif /* _NGX_PROCESS_CYCLE_H_INCLUDED_ */
Comentarios#
//
no se utilizan/*
* The red-black tree code is based on the algorithm described in
* the "Introduction to Algorithms" by Cormen, Leiserson and Rivest.
*/
/* find the server configuration for the address:port */
Preprocesador#
ngx_
o NGX_
(o prefijo más específico).
Los nombres de macros para constantes están en mayúsculas.
Las macros parametrizadas y las macros para inicializadores están en minúsculas.
El nombre de la macro y su valor se separan por al menos dos espacios:#define NGX_CONF_BUFFER 4096
#define ngx_buf_in_memory(b) (b->temporary || b->memory || b->mmap)
#define ngx_buf_size(b) \
(ngx_buf_in_memory(b) ? (off_t) (b->last - b->pos): \
(b->file_last - b->file_pos))
#define ngx_null_string { 0, NULL }
#if (NGX_HAVE_KQUEUE)
...
#elif ((NGX_HAVE_DEVPOLL && !(NGX_TEST_BUILD_DEVPOLL)) \
|| (NGX_HAVE_EVENTPORT && !(NGX_TEST_BUILD_EVENTPORT)))
...
#elif (NGX_HAVE_EPOLL && !(NGX_TEST_BUILD_EPOLL))
...
#elif (NGX_HAVE_POLL)
...
#else /* select */
...
#endif /* NGX_HAVE_KQUEUE */
Tipos#
_t
.
Un nombre de tipo definido está separado por al menos dos espacios:typedef ngx_uint_t ngx_rbtree_key_t;
typedef
.
Dentro de las estructuras, los tipos y nombres de los miembros están alineados:typedef struct {
size_t len;
u_char *data;
} ngx_str_t;
_s
".
Las definiciones de estructuras adyacentes están separadas por dos líneas en blanco:typedef struct ngx_list_part_s ngx_list_part_t;
struct ngx_list_part_s {
void *elts;
ngx_uint_t nelts;
ngx_list_part_t *next;
};
typedef struct {
ngx_list_part_t *last;
ngx_list_part_t part;
size_t size;
ngx_uint_t nalloc;
ngx_pool_t *pool;
} ngx_list_t;
typedef struct {
ngx_uint_t hash;
ngx_str_t key;
ngx_str_t value;
u_char *lowcase_key;
} ngx_table_elt_t;
_pt
":typedef ssize_t (*ngx_recv_pt)(ngx_connection_t *c, u_char *buf, size_t size);
typedef ssize_t (*ngx_recv_chain_pt)(ngx_connection_t *c, ngx_chain_t *in,
off_t limit);
typedef ssize_t (*ngx_send_pt)(ngx_connection_t *c, u_char *buf, size_t size);
typedef ngx_chain_t *(*ngx_send_chain_pt)(ngx_connection_t *c, ngx_chain_t *in,
off_t limit);
typedef struct {
ngx_recv_pt recv;
ngx_recv_chain_pt recv_chain;
ngx_recv_pt udp_recv;
ngx_send_pt send;
ngx_send_pt udp_send;
ngx_send_chain_pt udp_send_chain;
ngx_send_chain_pt send_chain;
ngx_uint_t flags;
} ngx_os_io_t;
_e
":typedef enum {
ngx_http_fastcgi_st_version = 0,
ngx_http_fastcgi_st_type,
...
ngx_http_fastcgi_st_padding
} ngx_http_fastcgi_state_e;
Variables#
u_char *rv, *p;
ngx_conf_t *cf;
ngx_uint_t i, j, k;
unsigned int len;
struct sockaddr *sa;
const unsigned char *data;
ngx_peer_connection_t *pc;
ngx_http_core_srv_conf_t **cscfp;
ngx_http_upstream_srv_conf_t *us, *uscf;
u_char text[NGX_SOCKADDR_STRLEN];
static ngx_str_t ngx_http_memcached_key = ngx_string("memcached_key");
static ngx_uint_t mday[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
static ngx_str_t ngx_http_memcached_key = ngx_string("memcached_key");
static ngx_uint_t mday[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
static uint32_t ngx_crc32_table16[] = {
0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac,
...
0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c
};
u_char *rv;
ngx_int_t rc;
ngx_conf_t *cf;
ngx_connection_t *c;
ngx_http_request_t *r;
ngx_peer_connection_t *pc;
ngx_http_upstream_srv_conf_t *us, *uscf;
Funciones#
static char *ngx_http_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static ngx_int_t ngx_http_init_phases(ngx_conf_t *cf,
ngx_http_core_main_conf_t *cmcf);
static char *ngx_http_merge_servers(ngx_conf_t *cf,
ngx_http_core_main_conf_t *cmcf, ngx_http_module_t *module,
ngx_uint_t ctx_index);
static ngx_int_t
ngx_http_find_virtual_server(ngx_http_request_t *r, u_char *host, size_t len)
{
...
}
static ngx_int_t
ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,
ngx_http_conf_port_t *port, ngx_http_listen_opt_t *lsopt)
{
...
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http header: \"%V: %V\"",
&h->key, &h->value);
hc->busy = ngx_palloc(r->connection->pool,
cscf->large_client_header_buffers.num * sizeof(ngx_buf_t *));
ngx_inline
debe utilizarse en lugar de
inline
:static ngx_inline void ngx_cpuid(uint32_t i, uint32_t *buf);
Expresiones#
.
y ->
deben ir separados de sus operandos por un espacio.
Los operadores unarios y los subíndices no se separan de sus operandos por espacios:width = width * 10 + (*fmt++ - '0');
ch = (u_char) ((decoded << 4) + (ch - '0'));
r->exten.data = &r->uri.data[i + 1];
len = ngx_sock_ntop((struct sockaddr *) sin6, p, len, 1);
if (status == NGX_HTTP_MOVED_PERMANENTLY
|| status == NGX_HTTP_MOVED_TEMPORARILY
|| status == NGX_HTTP_SEE_OTHER
|| status == NGX_HTTP_TEMPORARY_REDIRECT
|| status == NGX_HTTP_PERMANENT_REDIRECT)
{
...
}
p->temp_file->warn = "an upstream response is buffered "
"to a temporary file";
hinit->hash = ngx_pcalloc(hinit->pool, sizeof(ngx_hash_wildcard_t)
+ size * sizeof(ngx_hash_elt_t *));
if (((u->conf->cache_use_stale & NGX_HTTP_UPSTREAM_FT_UPDATING)
|| c->stale_updating) && !r->background
&& u->conf->cache_background_update)
{
...
}
node = (ngx_rbtree_node_t *)
((u_char *) lr - offsetof(ngx_rbtree_node_t, color));
NULL
(no 0
):if (ptr != NULL) {
...
}
Condicionales y Bucles#
if
está separada de la condición por un solo espacio.
La llave de apertura se ubica en la misma línea, o en una línea dedicada si la condición ocupa varias líneas.
La llave de cierre se ubica en una línea dedicada, opcionalmente seguida de
else if
/ else
.
Por lo general, hay una línea en blanco antes de la parte
else if
/ else
:if (node->left == sentinel) {
temp = node->right;
subst = node;
} else if (node->right == sentinel) {
temp = node->left;
subst = node;
} else {
subst = ngx_rbtree_min(node->right, sentinel);
if (subst->left != sentinel) {
temp = subst->left;
} else {
temp = subst->right;
}
}
do
y while
:while (p < last && *p == ' ') {
p++;
}
do {
ctx->node = rn;
ctx = ctx->next;
} while (ctx);
switch
está separada de la condición por
un espacio.
La llave de apertura está ubicada en la misma línea.
La llave de cierre está ubicada en una línea dedicada.
Las palabras clave case
se alinean con
switch
:switch (ch) {
case '!':
looked = 2;
state = ssi_comment0_state;
break;
case '<':
copy_end = p;
break;
default:
copy_end = p;
looked = 0;
state = ssi_start_state;
break;
}
for
se formatean así:for (i = 0; i < ccf->env.nelts; i++) {
...
}
for (q = ngx_queue_head(locations);
q != ngx_queue_sentinel(locations);
q = ngx_queue_next(q))
{
...
}
for
, esto se indica con el comentario
/* void */
:for (i = 0; /* void */ ; i++) {
...
}
/* void */
que puede ir en la misma línea:for (cl = *busy; cl->next; cl = cl->next) { /* void */ }
for ( ;; ) {
...
}
Etiquetas#
if (i == 0) {
u->err = "host not found";
goto failed;
}
u->addrs = ngx_pcalloc(pool, i * sizeof(ngx_addr_t));
if (u->addrs == NULL) {
goto failed;
}
u->naddrs = i;
...
return NGX_OK;
failed:
freeaddrinfo(res);
return NGX_ERROR;
Depuración de problemas de memoria#
gcc
y clang
, utiliza la opción de compilador y enlazador -fsanitize=address
.
Al compilar Angie, esto se puede hacer añadiendo la opción a
--with-cc-opt
y --with-ld-opt
parámetros del script configure
.NGX_DEBUG_PALLOC
a 1
.
En este caso, las asignaciones se pasan directamente al asignador del sistema, dándole control total sobre los límites de los búferes.auto/configure --with-cc-opt='-fsanitize=address -DNGX_DEBUG_PALLOC=1'
--with-ld-opt=-fsanitize=address
Errores comunes#
Escribir un módulo en C#
Cadenas en C#
strlen()
o strstr()
.
En su lugar, se deben usar las contrapartes de Angie
que acepten ya sea ngx_str_t
o un puntero a datos y una longitud.
Sin embargo, hay un caso en el que ngx_str_t
contiene
un puntero a una cadena terminada en cero: las cadenas que provienen del
análisis del archivo de configuración son terminadas en cero.Variables Globales#
Gestión manual de memoria#
Hilos#
init_process
y realizar las acciones requeridas en el manejador del temporizador.
Internamente Angie hace uso de threads para acelerar las operaciones relacionadas con IO, pero esto es un caso especial con muchas limitaciones.Bibliotecas Bloqueantes#
Solicitudes HTTP a Servicios Externos#