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 siguiente estructura y convenciones. Las siguientes dos sentencias Además de eso, el código HTTP debe incluir El código de correo debe incluir El código de flujo 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: La 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 el centinela.
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
" El recorrido es bastante directo y puede demostrarse con el siguiente patrón de función de búsqueda: La función Para añadir un nodo al árbol, reserva 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 un hash, necesitas conocer el número de elementos que contendrá para que Angie pueda construirlo de forma óptima.
Dos parámetros que deben configurarse son Las claves del hash se almacenan en Para insertar claves en un array de claves hash, utiliza la función
Para construir la tabla hash, llama a la función
La función falla si los parámetros Cuando se construye el hash, usa la función
Para crear un 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 arrays 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 arrays de claves: uno para la coincidencia exacta (descrita arriba) y dos más para habilitar la coincidencia desde el inicio o desde el final de una cadena: El array de claves necesita ser ordenado, y los resultados de inicialización deben añadirse
al hash combinado.
La inicialización del array 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 reenví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 Los enlaces de cadena ( Se pueden registrar manejadores de limpieza en un pool.
Un manejador de limpieza es una función de callback 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 útil.
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 a
Angie utiliza memoria compartida para compartir datos comunes entre procesos.
La función La estructura de entrada de memoria compartida Las entradas de zona compartida se mapean a la memoria real en
Para asignar memoria compartida, Angie proporciona el slab pool
de tipo El slab pool 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
asignación.
Para tamaños mayores que media página (que normalmente es 2048 bytes), la asignación se
realiza de una página completa a la vez. Para proteger los datos en la memoria compartida del acceso concurrente, utilice 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 Angie proporciona las siguientes macros de registro: Un mensaje de registro se formatea en un búfer de tamaño
El ejemplo anterior da como resultado entradas de registro como estas: Un objeto de ciclo almacena el contexto de ejecución de Angie creado a partir de una configuración específica.
Su tipo es Un ciclo es creado por la función Los miembros del ciclo incluyen: path loader — Se ejecuta solo una vez en 60 segundos después de iniciar o recargar Angie.
Normalmente, el loader lee el directorio y almacena datos en la memoria compartida de Angie.
El manejador es llamado desde el proceso dedicado de Angie "cache loader". path manager — Se ejecuta periódicamente.
Normalmente, el manager elimina archivos antiguos del directorio y actualiza la memoria de Angie para reflejar los cambios.
El manejador es llamado desde el proceso dedicado "cache manager". Para operaciones de entrada/salida, Angie proporciona el tipo de buffer La estructura Para operaciones de entrada y salida, los buffers 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 buffer y una referencia al siguiente eslabón de la cadena. Un ejemplo de uso de buffers 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 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 a
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 a que ocurra 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 delegar a un hilo separado tareas que de otro modo
bloquearían el proceso worker de Angie.
Por ejemplo, Angie puede configurarse para usar hilos para realizar
E/S de archivos.
Otro caso de uso es una biblioteca que no tiene interfaz asíncrona
y por tanto no puede usarse normalmente con Angie.
Tenga en cuenta que la interfaz de hilos es una ayuda para el enfoque
asíncrono existente de procesamiento de conexiones de cliente, y de ninguna manera
está destinada como reemplazo. Para tratar con la sincronización, están disponibles los siguientes envoltorios sobre
primitivas En lugar de crear un nuevo hilo para cada tarea, Angie implementa
una estrategia de thread_pool.
Pueden configurarse múltiples pools de hilos para diferentes propósitos
(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 archivo de cabecera En tiempo de configuración, un módulo que desee usar hilos debe obtener una
referencia a un pool de hilos llamando a
Para añadir una Para ejecutar una función en un hilo, pase parámetros y configure 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 usan típicamente como referencias.
El Por defecto, los módulos de filtro se colocan antes del
Para compilar un módulo en Angie estáticamente, use el
argumento Los módulos son los bloques de construcción de Angie, y la mayor parte de su funcionalidad se
implementa como módulos.
El archivo fuente del 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
completa usando la macro predefinida Cada módulo mantiene sus datos privados en el campo Los manejadores de directivas de configuración se llaman tal como aparecen
en los archivos de configuración en el contexto del proceso maestro. Después de que la configuración se analiza correctamente, el manejador El proceso maestro crea uno o más procesos worker y el
manejador Cuando un proceso worker 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 usan en Angie solo como una facilidad de E/S suplementaria con su
propia API, los manejadores El El El conjunto de módulos núcleo incluye los módulos donde Por ejemplo, un módulo simplista llamado El tipo Termine el array con el valor especial Flags para tipos de directiva: El contexto de la directiva define dónde puede aparecer en la configuración: El analizador de configuración utiliza estos flags para generar un error ante una
directiva mal ubicada y llama a los manejadores de directivas suministrados con el
puntero de configuración apropiado, de modo que las mismas directivas en diferentes ubicaciones puedan
almacenar sus valores en ubicaciones distintas. El campo El campo El campo El campo El argumento Cada conexión de cliente HTTP pasa por las siguientes etapas: El manejador de eventos de lectura El manejador de eventos de lectura El manejador de eventos de lectura Para cada solicitud HTTP del cliente se crea el objeto Tenga en cuenta que para las conexiones HTTP el campo Una solicitud normalmente se publica 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 de 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
llamando a 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 un parámetro, Como se ve en el ejemplo, la función Las siguientes macros están disponibles
para acceder a la configuración de módulos HTTP en tiempo de configuración.
Todas toman una referencia El siguiente ejemplo obtiene un puntero a una configuración de ubicación
del módulo HTTP core estándar
y reemplaza el manejador de contenido de ubicación almacenado
en el campo Las siguientes macros están disponibles para acceder a la configuración de módulos HTTP
en tiempo de ejecución. Estas macros reciben una referencia a una solicitud HTTP
Cada petición HTTP pasa por una secuencia de fases.
En cada fase se realiza un tipo distinto de procesamiento sobre la petición.
Los manejadores específicos de 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 invocados en una etapa específica del procesamiento de la petición.
Las fases se procesan sucesivamente y los manejadores de fase se llaman
una vez que la petición alcanza 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 fase de preacceso. Se espera que los manejadores de fase devuelvan códigos específicos: Cualquier otro valor devuelto por el manejador de fase se trata como un código
de finalización de petición, en particular, un código de respuesta HTTP.
La petición se finaliza con el código proporcionado. Para algunas fases, los códigos de retorno se tratan de manera ligeramente diferente.
En la fase de contenido, cualquier código de retorno distinto de
El
repositorio nginx-dev-examples
proporciona ejemplos de módulos nginx adecuados para Angie también. el ancho máximo de texto es de 80 caracteres la sangría es de 4 espacios sin tabulaciones, sin espacios finales los elementos de lista en la misma línea se separan con espacios los literales hexadecimales están en minúsculas los nombres de archivos, 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 copyright inclusiones definiciones de preprocesador definiciones de tipos prototipos de funciones definiciones de variables definiciones de funciones Las declaraciones de copyright se ven así: Si el archivo se modifica significativamente, la lista de autores debe actualizarse,
el nuevo autor se añade al principio. Los archivos Los archivos de cabecera deben incluir la llamada "protección de cabecera": no se usan comentarios el texto está en inglés, se prefiere la ortografía americana 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 misma alineación entre diferentes estructuras en el archivo.
Una estructura que apunta a sí misma tiene un 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 la 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
en 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.
Un asterisco dentro de un 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 casteo.
En este caso, la línea de continuación está indentada: Los punteros se comparan explícitamente con
La palabra clave Reglas de formato similares se aplican a los bucles La palabra clave La mayoría de los bucles Si se omite alguna parte de la instrucción Un bucle con un cuerpo vacío también se indica mediante el
comentario Un bucle infinito tiene este aspecto: Las etiquetas están rodeadas de líneas vacías y se indentan al nivel anterior: Para depurar problemas de memoria como desbordamientos de búfer o errores de uso después de liberar, puede
usar AddressSanitizer
(ASan), compatible con algunos compiladores modernos.
Para habilitar ASan con Dado que la mayoría de las asignaciones en Angie se realizan desde el
pool interno de Angie, habilitar ASan puede no ser siempre suficiente para depurar
problemas de memoria.
El pool interno asigna un gran fragmento de memoria del sistema y corta
asignaciones más pequeñas de él.
Sin embargo, este mecanismo se puede deshabilitar estableciendo la
macro La siguiente línea de configuración resume la información proporcionada anteriormente.
Se recomienda al desarrollar módulos de terceros y 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, su tarea se puede lograr creando una configuración adecuada.
Si escribir un módulo es inevitable, intente hacerlo
lo más pequeño y simple posible.
Por ejemplo, un módulo solo puede exportar algunas
variables. Antes de comenzar un módulo, considere las siguientes preguntas: ¿Es posible implementar una característica deseada usando
módulos ya disponibles? ¿Es posible resolver un problema usando lenguajes de scripting integrados,
como Perl o NJS? El tipo de cadena más utilizado en Angie,
ngx_str_t, no es una cadena terminada en cero
al estilo de C.
No puede pasar los datos a funciones estándar de la biblioteca de C
como Evite usar variables globales en sus módulos.
Lo más probable es que sea un error tener una variable global.
Cualquier dato global debe estar vinculado a un ciclo de configuración
y asignarse desde el pool de memoria correspondiente.
Esto permite a Angie realizar recargas de configuración elegantes.
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 especial atención para gestionar la reconfiguración
adecuadamente.
Además, verifique si las bibliotecas utilizadas por su código tienen un estado
global implícito que pueda romperse al recargar. En lugar de lidiar con el enfoque malloc/free que es propenso a errores,
aprenda a usar los pools de Angie.
Un pool se crea y se vincula a un objeto:
configuración,
ciclo,
conexión <#http_connection>,
o solicitud HTTP.
Cuando el objeto se destruye, el pool asociado también se destruye.
Así que al trabajar con un objeto, es posible asignar la cantidad
necesaria desde el pool correspondiente y no preocuparse por liberar memoria
incluso en caso de errores. Se recomienda evitar el uso de hilos en Angie porque definitivamente romperá cosas: la mayoría de las funciones de Angie no son seguras para hilos.
Se espera que un hilo solo ejecute llamadas al sistema y
funciones de biblioteca seguras para hilos.
Si necesita ejecutar algún código que no esté relacionado con el procesamiento de solicitudes de clientes,
la forma adecuada es programar un temporizador en el manejador de módulo Un error común es usar bibliotecas que son bloqueantes 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 del otro extremo.
Como resultado, cuando se procesa una solicitud con dicha biblioteca, todo el
worker de Angie se bloquea, destruyendo así el rendimiento.
Use solo bibliotecas que proporcionen una interfaz asíncrona y no
bloqueen todo el proceso. A menudo, 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!)
para la tarea que puede ser realizada por el propio Angie. Hay dos escenarios de uso básicos cuando se necesita una solicitud externa: en el contexto del procesamiento de una solicitud de cliente (por ejemplo, en un manejador de contenido) en el contexto de un proceso worker (por ejemplo, manejador de temporizador) En el primer caso, lo mejor es usar
la API de subrequests.
En lugar de acceder directamente al servicio externo, declare una ubicación
en la configuración de Angie y dirija su subrequest a esta ubicación.
Esta ubicación no se limita a
hacer proxy de
solicitudes, sino que puede contener otras directivas de Angie.
Un ejemplo de este 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, el
módulo OCSP
implementa un cliente HTTP simple.Código fuente#
Estilo de codificación#
Mensajes de commit#
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ónsrccore — 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 plataformaunixwin32stream — 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 la llamada
GetLastError() en Windows.
La macro ngx_socket_errno devuelve el último número de error de socket.
Al igual que la 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, guárdelo en una variable local
de tipo ngx_err_t.
Para establecer errores, use las 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 (por ejemplo, al analizar la configuración),
se sabe que los objetos ngx_str_t 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,
delimitada 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
textngx_null_string — inicializador estático de cadena vacía para el
tipo ngx_str_tngx_str_set(str, text) — inicializa la cadena
str de tipo ngx_str_t * con la literal de cadena C
textngx_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 * (terminada en nulo)%*s — size_t + u_char *u en la mayoría de tipos para hacerlos sin signo.
Para convertir la salida a hexadecimal, use X o x.Conversión numérica#
NGX_ERROR en caso de error.ngx_atoi(line, n) — ngx_int_tngx_atosz(line, n) — ssize_tngx_atoof(line, n) — off_tngx_atotm(line, n) — time_tNGX_ERROR en caso de error.ngx_atofp(line, n, point) — Convierte un número 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 point 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 estructura
ngx_regex_compile_t contienen la cantidad de todas las
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 size del arreglo.
El tamaño del arreglo captures debe ser un múltiplo de tres, tal como lo exige la
API de PCRE.
En el ejemplo, el tamaño se calcula a partir del número total de capturas más
uno para la propia 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 un arreglo de elementos
ngx_regex_elt_t (que son simplemente expresiones regulares compiladas con nombres asociados),
una cadena a comparar y un log. La función aplica expresiones del arreglo a la cadena hasta que se encuentre una coincidencia o no queden más expresiones.
El valor de retorno es NGX_OK cuando hay una coincidencia y
NGX_DECLINED en caso contrario, o NGX_ERROR
en caso de error.Tiempo#
ngx_time_t representa el tiempo con tres tipos separados 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 estándar ISO 8601:
"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 infijo _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 función 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) — Insertar un nuevo nodongx_queue_remove(x) — Eliminar un nodo de la colangx_queue_split(h, q, n) — Dividir una cola en un nodo,
devolviendo la cola final en una cola separadangx_queue_add(h, n) — Añadir una segunda cola a la primerangx_queue_head(h),
ngx_queue_last(h) — Obtener el primer o último nodo de la colangx_queue_sentinel(h) — Obtener un objeto centinela de cola para terminar
la iteración en élngx_queue_data(q, type, link) — Obtener 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 proporciona 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 al centinela 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);
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.
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;
key 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 array 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 arrays 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 normal: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 se hace de forma similar.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 memoria#
Heap#
ngx_alloc(size, log) — Asignar memoria desde el heap del sistema.
Este es un envoltorio alrededor de malloc() con soporte de registro.
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
como 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.
Este 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().Pooling#
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 en el 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 con 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);
ngx_chain_t) se utilizan activamente en Angie,
por lo que la implementación del pool de Angie proporciona una forma de reutilizarlos.
El campo 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
puntero ngx_pool_cleanup_t para
ser rellenado por el llamador.
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 el size
de la zona.
Cada zona compartida debe tener un nombre único.
Si ya existe una entrada de zona compartida con el name y
tag proporcionados, se reutiliza la entrada de zona existente.
La función falla con un error si una entrada existente con el mismo nombre tiene una
etiqueta diferente.
Normalmente, la dirección de la estructura del módulo se pasa como
tag, lo que permite reutilizar zonas compartidas por nombre dentro
de un 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, utilizado para pasar datos arbitrarios al
callback initnoreuse — Bandera que deshabilita la reutilización de una zona compartida del
ciclo antiguotag — 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
del proceso maestro (específico de Windows)ngx_init_cycle() después de que se analiza la configuración.
En sistemas POSIX, se utiliza la llamada al sistema mmap() para crear el
mapeo anónimo compartido.
En Windows, se utiliza el par CreateFileMapping()/
MapViewOfFileEx().ngx_slab_pool_t.
Se crea automáticamente un slab pool para asignar memoria en cada zona compartida de Angie.
El pool se encuentra al principio 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 más comúnmente por el slab pool al asignar y liberar
memoria, pero se puede usar para proteger cualquier otra estructura de datos de 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 de Angie admite varios tipos de salida:next.
En este caso, cada mensaje se escribe en todos los registradores de la cadena.NGX_LOG_EMERGNGX_LOG_ALERTNGX_LOG_CRITNGX_LOG_ERRNGX_LOG_WARNNGX_LOG_NOTICENGX_LOG_INFONGX_LOG_DEBUGNGX_LOG_DEBUG_CORENGX_LOG_DEBUG_ALLOCNGX_LOG_DEBUG_MUTEXNGX_LOG_DEBUG_EVENTNGX_LOG_DEBUG_HTTPNGX_LOG_DEBUG_MAILNGX_LOG_DEBUG_STREAMerror_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), el
ID de conexión (almacenado 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
Ciclos#
ngx_cycle_t.
El ciclo actual es referenciado por la variable global ngx_cycle y heredado por los workers de Angie cuando se inician.
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 normalmente se elimina después de que el nuevo se crea exitosamente.ngx_init_cycle(), que toma el ciclo anterior como argumento.
La función localiza el archivo de configuración del ciclo anterior y hereda tantos recursos como sea posible del ciclo anterior.
Un ciclo de marcador de posición llamado "init cycle" se crea 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 configura para apuntar a new_log después de que se lee la configuración.new_log — Registro del ciclo, creado por la configuración.
Se ve afectado por la directiva error_log de ámbito raíz.connections, connection_n —
Array de conexiones de tipo ngx_connection_t, creado por el módulo de eventos al inicializar cada worker 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 actualmente disponibles.
Si no hay conexiones disponibles, un worker de Angie rechaza aceptar nuevos clientes o conectarse a servidores upstream.files, files_n — Array para mapear descriptores de archivo a conexiones de Angie.
Este mapeo es utilizado por los módulos de eventos que tienen la bandera NGX_USE_FD_EVENT (actualmente, son poll y devpoll).conf_ctx — Array de configuraciones de módulos principales.
Las configuraciones se crean y llenan al leer los archivos de configuración de Angie.modules, modules_n — Array de módulos de tipo ngx_module_t, tanto estáticos como dinámicos, cargados por la configuración actual.listening — Array de objetos de escucha de tipo ngx_listening_t.
Los objetos de escucha normalmente se agregan mediante la directiva listen de diferentes módulos que llaman a la función ngx_create_listening().
Los sockets de escucha se crean basándose en los objetos de escucha.paths — Array de rutas de 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 agregar dos manejadores para cada ruta:open_files — Lista de objetos de archivo abierto de tipo ngx_open_file_t, que se crean llamando 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 en la lista open_files y almacena cada descriptor de archivo en el campo fd del objeto.
Los archivos se abren en modo de anexado y se crean si faltan.
Los archivos en la lista son reabiertos por los workers de Angie al recibir la señal de reapertura (más comúnmente USR1).
En este caso, el descriptor en el campo fd cambia a un nuevo valor.shared_memory — Lista de zonas de memoria compartida, cada una agregada llamando a la función ngx_shared_memory_add().
Las zonas compartidas se mapean al mismo rango de direcciones en todos los procesos de Angie y se utilizan para compartir datos comunes, por ejemplo el árbol en memoria de la caché HTTP.Buffer#
ngx_buf_t.
Normalmente, se utiliza para contener datos que deben escribirse en un destino o leerse desde una fuente.
Un buffer puede hacer referencia a datos en la memoria o en un archivo y, técnicamente, es posible que un buffer haga referencia a ambos al mismo tiempo.
La memoria para el buffer se asigna por separado y no está relacionada con la estructura del buffer ngx_buf_t.ngx_buf_t tiene los siguientes campos:start, end — Los límites del bloque de memoria asignado para el buffer.pos, last — Los límites del buffer de memoria; normalmente un subrango de start .. end.file_pos, file_last — Los límites de un buffer de archivo, expresados como desplazamientos desde el inicio del archivo.tag — Valor único usado para distinguir buffers; creado por diferentes módulos de Angie, usualmente con el propósito de reutilización de buffers.file — Objeto de archivo.temporary — Bandera que indica que el buffer hace referencia a memoria escribible.memory — Bandera que indica que el buffer hace referencia a memoria de solo lectura.in_file — Bandera que indica que el buffer hace referencia a datos en un archivo.flush — Bandera que indica que todos los datos anteriores al buffer deben ser vaciados.recycled — Bandera que indica que el buffer puede reutilizarse y debe consumirse lo antes posible.sync — Bandera que indica que el buffer no transporta datos ni señal especial como flush o last_buf.
Por defecto, Angie considera tales buffers como una condición de error, pero esta bandera indica a Angie que omita la comprobación de errores.last_buf — Bandera que indica que el buffer es el último en la salida.last_in_chain — Bandera que indica que no hay más buffers de datos en una solicitud o subsolicitud.shadow — Referencia a otro buffer ("shadow") relacionado con el buffer actual, usualmente en el sentido de que el buffer utiliza datos del shadow.
Cuando el buffer se consume, normalmente el buffer shadow también se marca como consumido.last_shadow — Bandera que indica que el buffer es el último que hace referencia a un buffer shadow particular.temp_file — Bandera que indica que el buffer 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;
};
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 del protocolo PROXY, si el protocolo PROXY está habilitado para la conexión.ssl — Contexto SSL para la conexión.reusable — Bandera que indica que la conexión está en un estado que la hace elegible para reutilización.close — Bandera que indica 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 free_connections del ciclo, 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).
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.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 la expiración de eventos de temporización.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 Angie, crea ciclos e inicia 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 los
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 auxiliares.
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.Threading#
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
las 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 crea un
nuevo pool de hilos con el name dado o devuelve una referencia
al pool con ese nombre si ya existe.task a una cola de un pool de hilos especificado
tp en tiempo de ejecución, use 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;
/* this function is executed in a separate thread */
}
static void
my_thread_completion(ngx_event_t *ev)
{
my_thread_ctx_t *ctx = ev->data;
/* executed in Angie event loop */
}
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#
Añadir 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 establecer
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 múltiples módulos desde un conjunto de archivos fuente, especifique 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 en 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 del script configure.ngx_module_srcs — Lista separada por espacios de archivos fuente
usados para compilar el módulo.
La variable $ngx_addon_dir puede usarse para representar la ruta
al directorio del módulo.ngx_module_incs — Rutas de inclusión requeridas para construir el módulongx_module_deps — Lista separada por espacios de las dependencias del módulo.
Normalmente, es la lista de archivos de cabecera.ngx_module_libs — Lista separada por espacios de bibliotecas a
enlazar con el módulo.
Por ejemplo, use ngx_module_libs=-lpthread para enlazar
la biblioteca libpthread.
Las siguientes macros pueden usarse 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 a
DYNAMIC para un módulo dinámico o ADDON
para un módulo estático y usada para determinar diferentes acciones a realizar
dependiendo del tipo de enlace.ngx_module_order — Orden de carga para el módulo;
útil para los tipos de módulo HTTP_FILTER y
HTTP_AUX_FILTER.
El formato para esta opción es una lista separada por espacios de módulos.
Todos los módulos en 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 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 coloca cerca del final de la lista para que sea uno de
los primeros en ejecutarse.
El ngx_http_write_filter_module escribe los datos al
socket del cliente y se coloca cerca del principio de la lista, y es el último en
ejecutarse.ngx_http_copy_filter en la lista de módulos para que el
manejador de filtro se ejecute después del manejador del filtro de copia.
Para otros tipos de módulo el valor por defecto es la cadena vacía.--add-module=/path/to/module del script configure.
Para compilar un módulo para carga dinámica posterior en Angie, use el
argumento --add-dynamic-module=/path/to/module.Módulos núcleo#
ngx_module_t, que se define de la siguiente manera:struct ngx_module_s {
/* la parte privada se omite */
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);
/* los stubs para extensiones futuras se omiten */
};
NGX_MODULE_V1.ctx,
reconoce las directivas de configuración especificadas en el
array commands, y puede invocarse en ciertas etapas del
ciclo de vida 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 existe un manejador init_master, porque sería
una sobrecarga innecesaria.type del módulo define exactamente qué se almacena en el
campo ctx.
Su valor es uno de los siguientes tipos:NGX_CORE_MODULENGX_EVENT_MODULENGX_HTTP_MODULENGX_MAIL_MODULENGX_STREAM_MODULENGX_CORE_MODULE es el tipo de módulo más básico y, por lo tanto, el más
genérico y de nivel más bajo.
Los otros tipos de módulos se implementan sobre él y proporcionan una
forma más conveniente de tratar con dominios correspondientes, como el manejo de eventos o solicitudes HTTP.ngx_core_module,
ngx_errlog_module, ngx_regex_module,
ngx_thread_pool_module y
ngx_openssl_module.
El módulo HTTP, el módulo stream, el módulo mail y los módulos de eventos también son módulos núcleo.
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 e 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 e init_conf después de que toda la configuración
se analiza correctamente.
La función típica create_conf asigna memoria para la
configuración y establece valores predeterminados.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 un array de dichas estructuras
que describe 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 la directiva tal como aparece
en el archivo de configuración, por ejemplo, "worker_processes" o "listen".
El type es un campo de bits de flags que especifican el número de
argumentos que acepta la directiva, su tipo y el contexto en el que aparece.
Los flags son:NGX_CONF_NOARGS — La directiva no acepta argumentos.NGX_CONF_1MORE — La directiva acepta uno o más argumentos.NGX_CONF_2MORE — La directiva acepta dos o más argumentos.NGX_CONF_TAKE1 .. NGX_CONF_TAKE7 —
La directiva acepta 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 aceptar un número diferente de
argumentos.
Las opciones están limitadas a los números especificados.
Por ejemplo, NGX_CONF_TAKE12 significa que acepta 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 el contenido interno.NGX_CONF_FLAG — La directiva acepta 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.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 events.NGX_DIRECT_CONF — Utilizado por módulos que no
crean una jerarquía de contextos y tienen solo una configuración global única.
Esta configuración se pasa al manejador como el argumento conf.set define un manejador que procesa la directiva
y almacena 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 un valor del
tipo ngx_str_t.ngx_conf_set_str_array_slot — Añade un valor a un array
ngx_array_t de cadenas ngx_str_t.
El array se crea si aún no existe.ngx_conf_set_keyval_slot — Añade un par clave-valor a un
array 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 array se crea si aún no existe.ngx_conf_set_num_slot — Convierte un argumento de la directiva
a un valor ngx_int_t.ngx_conf_set_size_slot — Convierte un
tamaño a un valor size_t
expresado en bytes.ngx_conf_set_off_slot — Convierte un
desplazamiento a un valor off_t
expresado en bytes.ngx_conf_set_msec_slot — Convierte un
tiempo a un valor ngx_msec_t
expresado en milisegundos.ngx_conf_set_sec_slot — Convierte un
tiempo 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
tamaño de búferes.ngx_conf_set_enum_slot — Convierte el argumento suministrado
a un valor ngx_uint_t.
El array terminado en null de ngx_conf_enum_t pasado en el
campo post define las cadenas aceptables y los correspondientes
valores enteros.ngx_conf_set_bitmask_slot — Convierte los argumentos suministrados
a un valor ngx_uint_t.
Los valores de máscara para cada argumento se combinan con OR produciendo el resultado.
El array terminado en null de ngx_conf_bitmask_t pasado en el
campo post define las cadenas aceptables y los correspondientes
valores de máscara.ngx_conf_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.ngx_conf_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 la directiva.
Los módulos principales solo tienen la configuración global y establecen el
flag 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 los ámbitos server,
location e if.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 bloque 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 la estructura de configuración de un módulo
que contiene los valores para esta directiva en particular.
El uso típico es emplear la macro offsetof().post tiene dos propósitos: puede utilizarse para definir
un manejador que se llamará 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 necesita
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 objeto ngx_conf_post_t
en sí mismo, y data es un puntero al valor,
convertido desde los argumentos por el manejador principal con el tipo apropiado.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 de 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 la 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.
Posteriormente será reemplazado por un objeto de solicitud HTTP.
En esta etapa también se inician el analizador del protocolo PROXY y el handshake SSL.ngx_http_wait_request_handler()
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 establece en el campo data de la conexión.ngx_http_process_request_line()
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 mediante la directiva
client_header_buffer_size.
Se supone que todo el encabezado del cliente debe caber en el búfer.
Si el tamaño inicial no es suficiente, se asigna un búfer más grande,
con la capacidad establecida por la directiva large_client_header_buffers.ngx_http_process_request_headers(),
se establece 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
se ha leído y analizado completamente.
Esta función ejecuta las fases de la solicitud desde
NGX_HTTP_POST_READ_PHASE hasta
NGX_HTTP_CONTENT_PHASE.
La última fase está destinada a generar una respuesta y pasarla a lo largo de la cadena de filtros.
La respuesta no se envía necesariamente al cliente en esta fase.
Puede permanecer en búfer y enviarse en la etapa de finalización.ngx_http_finalize_request() normalmente se llama cuando la
solicitud ha generado toda la salida o ha producido un error.
En este último caso se busca una página de error apropiada y se usa como
respuesta.
Si la respuesta no se ha enviado completamente 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 se ha enviado al cliente y la solicitud puede destruirse.
Si la función keepalive de la conexión del cliente está habilitada,
se llama a ngx_http_set_keepalive(), 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 referenciar el mismo objeto de conexión al mismo tiempo:
una solicitud principal y sus subsolicitudes.
Después de que se elimina una solicitud, se puede crear una nueva solicitud en la misma conexión.data de
ngx_connection_t apunta de vuelta a la solicitud.
Tales solicitudes se denominan activas, en contraposición a las otras solicitudes vinculadas a la
conexión.
Una solicitud activa se utiliza para manejar eventos de conexión del cliente y se le permite
enviar su respuesta al cliente.
Normalmente, cada solicitud se vuelve 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 array ctx en la posición
ctx_index del módulo.
Las siguientes macros proporcionan una forma conveniente de obtener y establecer contextos de solicitud:ngx_http_get_module_ctx(r, module) — Devuelve
el contexto del modulengx_http_set_ctx(r, c, module) — Establece c
como el contexto del modulemain_conf, srv_conf,
loc_conf — Arrays de configuraciones de la solicitud
actual.
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 eventos 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 activa
actual.cache — Objeto de caché de solicitud para almacenar en caché la
respuesta upstream.upstream — Objeto upstream de solicitud para proxy.pool — Pool de solicitud.
El objeto de solicitud en sí se asigna en este pool, que se destruye cuando
se elimina la solicitud.
Para asignaciones que necesitan 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 sin procesar de encabezados.
Además de eso, los encabezados específicos están disponibles para obtener y establecer como
campos separados, por ejemplo content_length_n,
status, etc.request_body — Objeto de cuerpo de solicitud del cliente.start_sec, start_msec — Punto temporal en el que
se creó la solicitud, utilizado para rastrear la duración de la solicitud.method, method_name — Representación numérica y de texto
del método de solicitud HTTP del cliente.
Los valores numéricos para los métodos se definen 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 del protocolo HTTP
del cliente en forma numérica dividida 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í puede diferir del URI original enviado por el cliente debido a
la normalización.
Durante el 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, en contraposición a
las subsolicitudes, que se crean para realizar una subtarea específica dentro de la solicitud
principal.parent — Puntero a la solicitud padre de una subsolicitud.postponed — Lista de buffers de salida y subsolicitudes, en el
orden en que se envían y crean.
La lista es utilizada por el filtro postpone para proporcionar una salida de solicitud consistente
cuando partes de ella son creadas por subsolicitudes.post_subrequest — Puntero a un manejador con el contexto
que se llamará cuando una subsolicitud se finalice.
No se utiliza para solicitudes principales.posted_requests — Lista de solicitudes que se iniciarán o
reanudarán, lo cual se hace llamando al
write_event_handler 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 produce la salida.ngx_http_post_request(r, NULL).
Siempre se publica en la lista posted_requests de la solicitud principal.
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 conducir 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 regex 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 de map, búsqueda de servidor por SNI o Host HTTP, rewrite, proxy_redirect, etc.
Las capturas producidas por una búsqueda se almacenan en los campos mencionados anteriormente.
El campo ncaptures contiene el número de capturas,
captures contiene los límites de las capturas y
captures_data contiene la cadena contra la cual se comparó la regex
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.
El campo solo tiene sentido para la solicitud principal.
Aumentar el contador se hace mediante un simple r->main->count++.
Para disminuir el contador, 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
incrementan el contador.subrequests — Nivel de anidamiento de subsolicitud actual.
Cada subsolicitud hereda el nivel de anidamiento de su padre, disminuido en uno.
Se genera un error si el valor llega a cero.
El valor para la solicitud principal se define mediante la
constante NGX_HTTP_MAX_SUBREQUESTS.uri_changes — Número de cambios de URI restantes 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 disminuye 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 en la solicitud.
Mientras este valor sea distinto de cero, la solicitud no puede finalizarse.
Actualmente, este valor se incrementa por operaciones AIO pendientes (POSIX AIO y
operaciones de hilos) y bloqueos de caché activos.buffered — Máscara de bits que muestra qué módulos tienen salida
almacenada en buffer producida por la solicitud.
Varios filtros pueden almacenar en buffer la salida; por ejemplo, sub_filter puede almacenar datos en buffer
debido a una coincidencia parcial de cadena, el filtro copy puede almacenar datos en buffer debido a
la falta de buffers de salida libres, etc.
Mientras este valor sea distinto de cero, la solicitud no se finaliza,
esperando un vaciado.header_only — Bandera que indica que la salida no requiere
un cuerpo.
Por ejemplo, esta bandera se utiliza en las solicitudes HTTP HEAD.keepalive — Bandera que indica si se admite
keepalive de conexión del cliente.
El valor se infiere de la versión HTTP y del valor del
encabezado "Connection".header_sent — Bandera que indica que el encabezado de salida
ya ha sido enviado por la solicitud.internal — Bandera que indica 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 — Bandera que indica que se puede enviar una respuesta parcial
al cliente, según lo solicitado por el encabezado HTTP Range.subrequest_ranges — Bandera que indica que se puede enviar una respuesta parcial
mientras se procesa una subsolicitud.single_range — Bandera que indica que solo se puede enviar un único rango
continuo de datos de salida al cliente.
Esta bandera generalmente se establece cuando se envía un flujo de datos, por ejemplo, desde
un servidor proxy, y la respuesta completa no está disponible en un solo buffer.main_filter_need_in_memory,
filter_need_in_memory — Banderas
que solicitan que la salida se produzca en buffers de memoria pero no en archivos.
Esta es una señal para el filtro copy de leer datos de buffers de archivo incluso si
sendfile está habilitado.
La diferencia entre las dos banderas 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 solo la salida de la
solicitud actual entre en buffers de memoria.
Los filtros llamados más tarde 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
al enviar la salida.filter_need_temporary — Bandera que solicita que la salida de la solicitud
se produzca en buffers temporales, pero no en buffers de memoria de solo lectura o
buffers de archivo.
Esto es utilizado por filtros que pueden cambiar la salida directamente en los buffers donde
se envía.Configuración de módulos HTTP#
http.
Sirve como configuración global para el módulo.server.
Sirve como configuración específica del servidor para el módulo.location,
if o limit_except.
Sirve como configuración específica de ubicación para el 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, las configuraciones de servidor y ubicación no solo existen en los niveles
de servidor y ubicación, sino que también se crean para todos los niveles superiores.
Específicamente, una configuración de servidor también se crea en el nivel principal, y
las configuraciones de ubicación se crean en los niveles principal, de servidor y de ubicación.
Estas configuraciones permiten especificar parámetros específicos de servidor y ubicación
en cualquier nivel de un archivo de configuración de Angie.
Finalmente, las configuraciones se fusionan hacia abajo.
Se proporcionan varias macros, como NGX_CONF_UNSET y
NGX_CONF_UNSET_UINT, para indicar un parámetro faltante
e ignorarlo durante la fusión.
Las macros de fusión estándar de Angie, como ngx_conf_merge_value() y
ngx_conf_merge_uint_value(), proporcionan una forma conveniente de
fusionar un parámetro y establecer el valor predeterminado si ninguna de las configuraciones
proporcionó un valor explícito.
Para obtener una lista completa de macros para diferentes tipos, consulte
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 de servidor puede cambiar de la predeterminada después de
elegir un servidor virtual para una 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 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);
...
}
Fases#
NGX_HTTP_POST_READ_PHASE — Primera fase.
El módulo RealIP
registra su manejador en esta fase para habilitar
la sustitución de direcciones de cliente antes de que se invoque cualquier otro módulo.NGX_HTTP_SERVER_REWRITE_PHASE — Fase donde
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 basándose en el URI de la petición.
Antes de esta fase, la ubicación predeterminada para el servidor virtual relevante se asigna a la
petición, y cualquier módulo que solicite una configuración de ubicación
recibe la configuración para la ubicación del servidor predeterminado.
Esta fase asigna una nueva ubicación a la petición.
No se pueden registrar manejadores adicionales en esta fase.NGX_HTTP_REWRITE_PHASE — Igual que
NGX_HTTP_SERVER_REWRITE_PHASE, pero para
reglas de reescritura definidas en la ubicación elegida en la fase anterior.NGX_HTTP_POST_REWRITE_PHASE — Fase especial donde
la petición se redirige a una nueva ubicación si su URI cambió
durante una reescritura.
Esto se implementa haciendo que la petición pase por la
NGX_HTTP_FIND_CONFIG_PHASE nuevamente.
No se pueden registrar manejadores adicionales en esta fase.NGX_HTTP_PREACCESS_PHASE — Una fase común para diferentes
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 donde se verifica
que el cliente esté autorizado para realizar la petición.
Los módulos estándar de Angie como
Access y
Auth Basic registran sus manejadores en esta fase.
Por defecto, el cliente debe pasar la verificación de autorización de todos los manejadores
registrados en esta fase para que la petición continúe a la siguiente fase.
La directiva satisfy
puede usarse para permitir que el procesamiento continúe si alguno de los manejadores de fase
autoriza al cliente.NGX_HTTP_POST_ACCESS_PHASE — Fase especial donde se
procesa la directiva satisfy.
Si algunos manejadores de fase de acceso denegaron el acceso y ninguno lo permitió explícitamente,
la petición se finaliza.
No se pueden registrar manejadores adicionales en esta fase.NGX_HTTP_PRECONTENT_PHASE — Fase para manejadores que se llaman
antes de generar contenido.
Los módulos estándar como
try_files y
Mirror
registran sus manejadores en esta fase.NGX_HTTP_CONTENT_PHASE — Fase donde normalmente
se genera la respuesta.
Múltiples módulos estándar de Angie registran sus manejadores en esta fase,
incluyendo
Index.
Se llaman secuencialmente hasta que uno de ellos produce
la salida.
También es posible establecer manejadores de contenido por ubicación.
Si la configuración de ubicación del módulo Módulo HTTP tiene
handler establecido, se llama como el manejador de contenido
y los manejadores instalados en esta fase se ignoran.NGX_HTTP_LOG_PHASE — Fase donde se realiza
el registro de la petición.
Actualmente, solo el
módulo Log
registra su manejador
en esta etapa para el registro de acceso.
Los manejadores de fase de registro se llaman al final del procesamiento de la petición, justo
antes de liberar la petición.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 — proceder a la siguiente fase.NGX_DECLINED — proceder al siguiente manejador de la fase
actual.
Si el manejador actual es el último en la fase actual,
pasar a la siguiente fase.NGX_AGAIN, NGX_DONE — suspender
el manejo de fase hasta algún evento futuro, que podría ser
una operación de E/S asíncrona o simplemente un retraso, por ejemplo.
Se asume que el manejo de fase se reanudará más tarde llamando a
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 ubicación se considera
un código de finalización.
En la fase de acceso, en
modo satisfy any,
devolver un código distinto de NGX_OK,
NGX_DECLINED, NGX_AGAIN,
NGX_DONE se considera una denegación.
Si ningún manejador de acceso posterior permite o deniega el acceso con un código
diferente, el código de denegación se convertirá en el código de finalización.Ejemplos#
Estilo de código#
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
siempre se incluyen primero, seguidos de uno de
ngx_http.h, ngx_stream.h,
o ngx_mail.h.
Luego siguen los 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#
///*
* 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 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 se separa de la condición
por un espacio.
La llave de apertura se coloca en la misma línea, o en una
línea dedicada si la condición ocupa varias líneas.
La llave de cierre se coloca en una línea dedicada, opcionalmente seguida de
else if / else.
Normalmente, hay una línea vacía 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++;
}
while (p < last && *p == ' ') {
p++;
}
do {
ctx->node = rn;
ctx = ctx->next;
} while (ctx);
switch se separa de la condición
por un espacio.
La llave de apertura se coloca en la misma línea.
La llave de cierre se coloca 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 de la siguiente manera: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 mediante el comentario /* void */:for (i = 0; /* void */ ; i++) {
...
}
/* void */ que puede colocarse 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,
use la opción de compilador y enlazador -fsanitize=address.
Al compilar Angie, esto se puede hacer añadiendo la opción a
los parámetros --with-cc-opt y --with-ld-opt
del script configure.NGX_DEBUG_PALLOC en 1.
En este caso, las asignaciones se pasan directamente al asignador del sistema, dándole
control total sobre los límites del búfer.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 aceptan 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 como resultado del
análisis del archivo de configuración están 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 hilos para
mejorar las operaciones relacionadas con E/S, pero este es un caso especial con muchas
limitaciones.Bibliotecas bloqueantes#
Solicitudes HTTP a servicios externos#