Conexiones, sesiones, solicitudes, registros#

Mecanismos de procesamiento de conexiones#

Angie admite varios métodos de procesamiento de conexiones. La disponibilidad de un método específico depende de la plataforma que se esté utilizando. En plataformas que admiten múltiples métodos, Angie suele seleccionar automáticamente el método más eficiente. No obstante, si es necesario, se puede elegir explícitamente un método de procesamiento de conexiones usando la directiva use.

Los siguientes métodos de procesamiento de conexiones están disponibles:

Método

Descripción

select

Un método estándar. El módulo de soporte se crea automáticamente en plataformas que no disponen de métodos más eficientes. Las opciones de compilación --with-select_module y --without-select_module pueden usarse para habilitar o deshabilitar la construcción de este módulo de forma forzada.

poll

Un método estándar. El módulo de soporte se crea automáticamente en plataformas que no disponen de métodos más eficientes. Las opciones de compilación --with-poll_module y --without-poll_module pueden usarse para habilitar o deshabilitar la construcción de este módulo de forma forzada.

kqueue

Un método eficiente disponible en FreeBSD 4.1+, OpenBSD 2.9+, NetBSD 2.0 y macOS.

epoll

Un método eficiente disponible en Linux 2.6+.

/dev/poll

Un método eficiente disponible en Solaris 7 11/99+, HP/UX 11.22+ (eventport), IRIX 6.5.15+ y Tru64 UNIX 5.1A+.

eventport

El método event ports está disponible en Solaris 10+. (Debido a problemas conocidos, se recomienda utilizar el método /dev/poll en su lugar.)

Procesamiento de solicitudes HTTP#

Una solicitud HTTP atraviesa una serie de fases, durante las cuales se realiza un tipo específico de procesamiento en cada fase.

Post-read

La fase inicial. En esta fase se invoca el módulo RealIP.

Server-rewrite

La fase en la que se procesan las directivas del módulo Rewrite, definidas dentro de un bloque server (pero fuera de un bloque location).

Find-config

Una fase especial en la que se selecciona una location basada en la URI de la solicitud.

Rewrite

Similar a la fase Server-rewrite, pero se aplica a las reglas rewrite definidas dentro del bloque de location seleccionado en la fase anterior.

Post-rewrite

Una fase especial en la que la solicitud se redirige a una nueva ubicación, como en la fase Find-config, si su URI fue modificada durante la fase Rewrite.

Preaccess

Durante esta fase, módulos estándar de Angie como Limit Req registran sus manejadores.

Access

La fase en la que se verifica la autorización del cliente para realizar la solicitud, normalmente invocando módulos estándar de Angie como Auth Basic.

Post-access

Una fase especial en la que se procesa la directiva satisfy any.

Precontent

Las directivas de módulos estándar, como try_files y mirror, registran sus manejadores durante esta fase.

Content

La fase en la que normalmente se genera la respuesta. Varios módulos estándar de Angie registran sus manejadores en esta etapa, incluyendo Index. Las directivas proxy_pass, fastcgi_pass, uwsgi_pass, scgi_pass y grpc_pass también se gestionan aquí.

Los manejadores se invocan de forma secuencial hasta que uno de ellos genera la salida.

Log

La fase final, en la que se registra la solicitud. Actualmente, solo el módulo Log registra su manejador en esta etapa para el registro de acceso.

Procesamiento de sesiones TCP/UDP#

Una sesión TCP/UDP de un cliente atraviesa una serie de fases, durante las cuales se realiza un tipo específico de procesamiento en cada fase:

Post-accept

La fase inicial tras aceptar una conexión del cliente. En esta fase se invoca el módulo RealIP.

Pre-access

Una fase preliminar para comprobar el acceso. Los módulos Set se invocan durante esta fase.

Access

La fase en la que se limita el acceso del cliente antes del procesamiento real de datos. El módulo Access se invoca en esta etapa.

SSL

La fase en la que tiene lugar la terminación TLS/SSL. El módulo SSL se invoca durante esta fase.

Preread

La fase para leer los bytes iniciales de datos en el búfer de prelectura para permitir que módulos como SSL Preread analicen los datos antes del procesamiento.

Content

Una fase obligatoria en la que realmente se procesan los datos, normalmente involucrando el módulo Return para enviar una respuesta al cliente. La directiva proxy_pass también se maneja aquí.

Log

La fase final en la que se registra el resultado del procesamiento de la sesión del cliente. El módulo Log se invoca en esta fase.

Selección de un servidor para una sesión#

Angie selecciona el bloque server para una sesión según la dirección y el puerto que la aceptaron, exactamente igual que para las conexiones HTTP; consulte Selección de un servidor por dirección y puerto para conocer las reglas de coincidencia y el servidor predeterminado de un par. Los pares TCP y UDP de un mismo puerto son independientes, cada uno con su propio servidor predeterminado: un servidor con listen 9000 udp; nunca recibe las conexiones TCP del puerto 9000.

Dentro del par coincidente, una sesión toma su nombre de servidor únicamente de la SNI: o bien el módulo SSL termina el TLS y obtiene el nombre a partir del protocolo de enlace, o bien el módulo SSL Preread lo lee sin terminar la conexión. Angie compara entonces ese nombre con las entradas server_name de los servidores que escuchan en el par; una sesión sin nombre SNI, o con un nombre que no coincide con ninguno de ellos, es gestionada por el servidor predeterminado del par.

Procesamiento de solicitudes#

Selección de servidor virtual#

Inicialmente, se crea una conexión dentro del contexto de un servidor predeterminado. El nombre del servidor puede determinarse en las siguientes etapas del procesamiento de la solicitud, cada una de las cuales está involucrada en la selección de la configuración del servidor:

  • Durante el protocolo de enlace SSL, de antemano, según el SNI.

  • Después de procesar la línea de solicitud.

  • Después de procesar el campo de cabecera Host.

Si el nombre del servidor no se determina después de procesar la línea de solicitud o el campo de cabecera Host, Angie utilizará un nombre vacío como nombre de servidor.

En cada una de estas etapas, se pueden aplicar diferentes configuraciones de servidor. Por lo tanto, ciertas directivas deben especificarse con precaución:

  • En el caso de la directiva ssl_protocols, la lista de protocolos es establecida por la biblioteca OpenSSL antes de que se aplique la configuración del servidor según el nombre solicitado a través de SNI. Como resultado, los protocolos solo deben especificarse para el servidor predeterminado.

  • Las directivas client_header_buffer_size y merge_slashes se aplican antes de leer la línea de solicitud. Por lo tanto, estas directivas utilizan la configuración del servidor predeterminado o la configuración del servidor elegida por SNI.

  • En el caso de las directivas ignore_invalid_headers, large_client_header_buffers y underscores_in_headers, que están involucradas en el procesamiento de los campos de cabecera de solicitud, la configuración del servidor depende adicionalmente de si se actualizó según la línea de solicitud o el campo de cabecera Host.

  • Una respuesta de error se gestiona mediante la directiva error_page en el servidor que está procesando actualmente la solicitud.

Servidores virtuales basados en nombres#

Angie determina primero qué servidor debe manejar la solicitud. Considere una configuración simple donde los tres servidores virtuales escuchan en el puerto 80:

server {

    listen 80;
    server_name example.org www.example.org;
    # ...
}

server {

    listen 80;
    server_name example.net www.example.net;
    #  ...
}

server {

    listen 80;
    server_name example.com www.example.com;
    #  ...
}

En esta configuración, Angie determina qué servidor debe manejar la solicitud basándose únicamente en el campo de cabecera Host. Si el valor de esta cabecera no coincide con ningún nombre de servidor, Angie enruta la solicitud al servidor predeterminado del par *:80. En la configuración anterior, el servidor predeterminado es el primero — el comportamiento predeterminado estándar de Angie. También puede especificarse explícitamente qué servidor debe ser el predeterminado mediante el parámetro default_server en la directiva listen:

server {

    listen 80 default_server;
    server_name example.net www.example.net;
    #  ...
}

Nota

Tenga en cuenta que el servidor predeterminado es una propiedad del par address:port, no del nombre del servidor.

Nombres internacionalizados#

Los nombres de dominio internacionalizados (IDN) deberían especificarse utilizando una representación ASCII (Punycode) en la directiva server_name (nombre del servidor):

server {

    listen 80;
    server_name xn--e1afmkfd.xn--80akhbyknj4f; # пример.испытание
    #    ...
}

Prevención de solicitudes con nombres de servidor indefinidos#

Si no se deben permitir solicitudes sin el campo de cabecera Host, se puede definir un servidor que simplemente descarte esas solicitudes:

server {

    listen 80;
    server_name "";
    return 444;
}

En esta configuración, el nombre del servidor se establece como una cadena vacía, que coincide con las solicitudes sin el campo de cabecera Host. A continuación se devuelve un código especial no estándar 444, que cierra la conexión.

Selección de un servidor por dirección y puerto#

Angie selecciona el bloque server para una conexión en dos pasos: primero por la dirección y el puerto que aceptaron la conexión, y después por el nombre.

Las directivas listen de la configuración definen los pares address:port en los que Angie acepta conexiones. Los pares en puertos distintos, en familias de direcciones distintas o en transportes distintos nunca compiten por una conexión: un socket de escucha quic es UDP, por lo que tiene su propio par incluso en un puerto que también tiene sockets de escucha TCP. Cuando llega una conexión, Angie compara la dirección que la aceptó con los pares de ese puerto: una dirección explícita tiene prioridad, y *:port es el último recurso. Por eso, un servidor que escucha en *:port tiene su propio par y no sustituye a los servidores de las direcciones explícitas de ese puerto. El par coincidente determina el resto de la selección: qué servidores comprueba Angie por nombre, cuál de ellos es el predeterminado, y cuáles de los parámetros ssl, http2, quic y proxy_protocol se aplican a la conexión. Una ruta de socket de dominio UNIX es un par en el mismo sentido: tiene su propio servidor predeterminado y su propio conjunto de nombres de servidor.

A continuación, Angie compara el campo de cabecera Host con las entradas server_name de los servidores cuyas directivas listen nombran el par coincidente; los servidores que escuchan en otra dirección del mismo puerto, incluido *:port, nunca se consultan. Si ningún nombre coincide, el servidor predeterminado del par gestiona la solicitud: el primer servidor de la configuración que nombra el par, salvo que otro servidor lo nombre con el parámetro default_server. Una solicitud que no lleva nombre de host se compara con el nombre de servidor vacío, como se describe en Prevención de solicitudes con nombres de servidor indefinidos; sobre HTTP/2 y HTTP/3 esa solicitud se rechaza antes de seleccionar un servidor. Angie rechaza una configuración en la que dos servidores reclaman el mismo par como predeterminado, informando a duplicate default server for address:port.

En una conexión TLS, Angie inicia el protocolo de enlace en la configuración del servidor predeterminado del par, por lo que su ssl_certificate es el que se ofrece a menos que el nombre SNI coincida con otro servidor del mismo par; consulte Servidores HTTPS basados en nombres para servir varios nombres en un mismo par.

En la siguiente configuración, cuatro servidores escuchan en el puerto 80 — dos en 198.51.100.1, uno en 198.51.100.2 y uno en *:80:

server {

    listen 198.51.100.1:80;
    server_name example.org www.example.org;
    #  ...
}

server {

    listen 198.51.100.1:80;
    server_name example.net www.example.net;
    #  ...
}

server {

    listen 198.51.100.2:80;
    server_name example.com www.example.com;
    #  ...
}

server {

    listen 80;
    server_name www.example.test;
    #  ...
}

Una conexión aceptada en 198.51.100.1:80 coincide con ese par, por lo que Angie compara el campo de cabecera Host solo con los dos primeros servidores. Por eso, una solicitud de www.example.test es gestionada por el primero de ellos, el servidor predeterminado de 198.51.100.1:80, aunque el cuarto servidor tenga ese nombre; lo mismo ocurre con www.example.com, que pertenece al par 198.51.100.2:80. El cuarto servidor gestiona únicamente las conexiones aceptadas en direcciones del puerto 80 que ninguna otra directiva listen nombra.

Cada par tiene su propio servidor predeterminado, y default_server lo designa para el par nombrado por esa misma directiva listen:

server {

    listen 198.51.100.1:80;
    server_name example.org www.example.org;
    #  ...
}

server {

    listen 198.51.100.1:80 default_server;
    server_name example.net www.example.net;
    #  ...
}

server {

    listen 198.51.100.2:80 default_server;
    server_name example.com www.example.com;
    #  ...
}

Añadir listen 80 default_server; a otro servidor establecería el predeterminado solo para *:80; el predeterminado de 198.51.100.1:80 seguiría siendo el servidor que lleva default_server ahí.

Nota

Los parámetros de socket de la directiva listen, entre ellos bind, cambian cómo Angie se enlaza a las direcciones, no qué servidor selecciona: la conexión se compara con los mismos pares en cualquier caso. Consulte el parámetro bind de listen para saber cómo funciona el enlace en sí.

Elección de ubicaciones#

Consideremos una configuración simple de un sitio web PHP:

server {

    listen 80;
    server_name example.org www.example.org;
    root /data/www;

    location / {

        index index.html index.php;
    }

    location ~* \.(gif|jpg|png)$ {

        expires 30d;
    }

    location ~ \.php$ {

        fastcgi_pass localhost:9000;
        fastcgi_param SCRIPT_FILENAME
        $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}

Angie primero busca la location de prefijo más específica dada por cadenas literales, independientemente del orden en que estén listadas. En la configuración anterior, la única ubicación de prefijo es location /, que coincide con cualquier solicitud y se utilizará como último recurso. Luego, Angie comprueba las ubicaciones definidas por expresiones regulares en el orden en que aparecen en el archivo de configuración. La primera expresión coincidente detiene la búsqueda, y Angie utilizará esa location. Si ninguna expresión regular coincide con una solicitud, Angie utilizará la location de prefijo más específica encontrada anteriormente.

Nota

Las ubicaciones de todos los tipos solo prueban la parte URI de la línea de solicitud, excluyendo los argumentos. Esto se debe a que los argumentos en la cadena de consulta pueden especificarse de varias maneras, por ejemplo:

  • /index.php?user=john&page=1

  • /index.php?page=1&user=john

Además, las cadenas de consulta pueden contener cualquier número de parámetros:

  • /index.php?page=1&something+else&user=john

Ahora veamos cómo se procesarían las solicitudes en la configuración anterior:

  • La solicitud /logo.gif primero coincide con el prefijo location / y luego con la expresión regular .(gif|jpg|png)$. Por lo tanto, es manejada por la última ubicación. Usando la directiva root /data/www, la solicitud se asigna al archivo /data/www/logo.gif, y el archivo se envía al cliente.

  • La solicitud /index.php también coincide inicialmente con el prefijo location / y luego con la expresión regular .(php)$. En consecuencia, es manejada por esta última ubicación, y la solicitud se pasa a un servidor FastCGI que escucha en localhost:9000. La directiva fastcgi_param establece el parámetro FastCGI SCRIPT_FILENAME a /data/www/index.php, y el servidor FastCGI ejecuta el archivo. La variable $document_root se establece con el valor de la directiva root, y la variable $fastcgi_script_name se establece con el URI de la solicitud, es decir, /index.php.

  • La solicitud /about.html solo coincide con el prefijo location /, por lo que se maneja en esta ubicación. Usando la directiva root /data/www, la solicitud se asigna al archivo /data/www/about.html, y el archivo se envía al cliente.

El manejo de la solicitud / es más complejo. Solo coincide con el prefijo location /, por lo que se maneja en esta ubicación. La directiva index entonces comprueba la existencia de archivos de índice según sus parámetros y la directiva root /data/www. Si el archivo /data/www/index.html no existe pero el archivo /data/www/index.php sí, la directiva realiza una redirección interna a /index.php, y Angie busca las ubicaciones nuevamente como si la solicitud hubiera sido enviada por un cliente. Como se mencionó anteriormente, la solicitud redirigida finalmente será manejada por el servidor FastCGI.

Los ejemplos anteriores asignan una solicitud a un archivo a través de la directiva root, pero la correspondencia completa implica varias directivas que actúan conjuntamente. Una vez seleccionado el location coincidente, Angie asigna el $uri normalizado de la solicitud a una ruta del sistema de archivos: la directiva root añade el URI completo a su valor, mientras que la directiva alias reemplaza el prefijo de la ubicación coincidente. La directiva try_files comprueba entonces la existencia de archivos candidatos en esa ruta; si no existe ninguno, su último parámetro desencadena una redirección interna, y Angie vuelve a buscar en las ubicaciones con el nuevo $uri, igual que en la redirección al archivo índice mencionada anteriormente.

Para la misma solicitud /images/top.gif, las dos ubicaciones siguientes la resuelven al mismo archivo, /data/w3/images/top.gif:

location /images/ {
  root /data/w3;           # añade:     /data/w3 + /images/top.gif
}

location /images/ {
  alias /data/w3/images/;  # reemplaza: /images/ -> /data/w3/images/, luego top.gif
}

Con alias, el prefijo coincidente se elimina por su longitud, por lo que las barras finales de la ubicación y del valor de alias deben ser consistentes. Si se eliminara la barra final de la ruta del alias anterior, el resultado sería /data/w3/imagestop.gif.

Proxy y balanceo de carga#

Uno de los usos más comunes de Angie es configurarlo como servidor proxy. En este rol, Angie recibe solicitudes, las reenvía a los servidores proxied, obtiene las respuestas de esos servidores y las envía de vuelta a los clientes.

Un servidor proxy simple:

server {

    location / {

        proxy_pass http://backend:8080;
    }

La directiva proxy_pass indica a Angie que pase las solicitudes del cliente al backend backend:8080 (el servidor proxied). Hay muchas directivas adicionales disponibles para configurar aún más una conexión proxy.

Proxy FastCGI#

Angie puede usarse para enrutar solicitudes a servidores FastCGI que ejecutan aplicaciones desarrolladas con varios marcos y lenguajes de programación, como PHP.

La configuración más básica de Angie para trabajar con un servidor FastCGI involucra usar la directiva fastcgi_pass en lugar de la directiva proxy_pass, junto con directivas fastcgi_param para establecer parámetros pasados al servidor FastCGI. Supongamos que el servidor FastCGI es accesible en localhost:9000. En PHP, el parámetro SCRIPT_FILENAME se utiliza para determinar el nombre del script, y el parámetro QUERY_STRING se utiliza para pasar los parámetros de la solicitud. La configuración resultante sería:

server {

    location / {

        fastcgi_pass localhost:9000;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param QUERY_STRING $query_string;
    }

    location ~ \.(gif|jpg|png)$ {

        root /data/images;
    }
}

Esta configuración establece un servidor que enruta todas las solicitudes, excepto aquellas para imágenes estáticas, al servidor proxied que opera en localhost:9000 a través del protocolo FastCGI.

Proxy de WebSocket#

Para actualizar una conexión de HTTP/1.1 a WebSocket, se utiliza el mecanismo de cambio de protocolo disponible en HTTP/1.1.

Sin embargo, existe una sutileza: dado que la cabecera Upgrade es una cabecera salto a salto, no se pasa del cliente al servidor proxy. Con el proxy directo, los clientes pueden usar el método CONNECT para evitar este problema. Este enfoque no funciona con el proxy inverso, ya que los clientes no son conscientes de ningún servidor proxy, y se requiere un procesamiento especial en el servidor proxy.

Angie implementa un modo especial de operación que permite establecer un túnel entre un cliente y un servidor proxy si el servidor proxy devuelve una respuesta con código 101 (Switching Protocols), y el cliente solicita un cambio de protocolo mediante la cabecera Upgrade en la solicitud.

Como se mencionó, las cabeceras salto a salto, incluyendo Upgrade y Connection, no se pasan del cliente al servidor proxy. Por lo tanto, para que el servidor proxy sea consciente de la intención del cliente de cambiar al protocolo WebSocket, estas cabeceras deben pasarse explícitamente:

location /chat/ {

    proxy_pass http://backend;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
}

Un ejemplo más sofisticado demuestra cómo el valor del campo de cabecera Connection en una solicitud al servidor proxy depende de la presencia del campo Upgrade en la cabecera de solicitud del cliente:

http {

    map $http_upgrade $connection_upgrade {

        default upgrade;
        '' close;
    }

    server {

        ...

        location /chat/ {

            proxy_pass http://backend;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection $connection_upgrade;
        }
    }
}

Por defecto, la conexión se cerrará si el servidor proxy no transmite ningún dato en 60 segundos. Este tiempo de espera puede aumentarse usando la directiva proxy_read_timeout. Alternativamente, el servidor proxy puede configurarse para enviar periódicamente tramas de ping de WebSocket para restablecer el tiempo de espera y comprobar si la conexión sigue activa.

Balanceo de carga#

Angie puede actuar como balanceador de carga, distribuyendo las solicitudes entre un grupo de servidores upstream y eligiendo el servidor mediante uno de varios métodos de balanceo. Consulte Balanceo de carga para saber cómo elegir un método, qué significa el peso de un servidor para cada uno de ellos y cómo se gestionan los fallos de servidor.

Registro#

Nota

Además de las opciones enumeradas aquí, también puede habilitar el registro de depuración.

Syslog#

Las directivas error_log y access_log admiten el registro en syslog. Los siguientes parámetros se utilizan para configurar el registro en syslog:

server=address

Especifica la dirección de un servidor syslog. La dirección puede ser un nombre de dominio o una dirección IP, con un puerto opcional, o una ruta de socket de dominio UNIX especificada después del prefijo "unix:". Si no se especifica el puerto, se utiliza el puerto UDP 514. Si un nombre de dominio se resuelve a múltiples direcciones IP, se utiliza la primera dirección resuelta.

facility=string

Establece la facilidad para los mensajes de syslog, como se define en RFC 3164. Las posibles facilidades incluyen: "kern", "user", "mail", "daemon", "auth", "intern", "lpr", "news", "uucp", "clock", "authpriv", "ftp", "ntp", "audit", "alert", "cron", "local0".."local7". El valor predeterminado es "local7".

severity=string

Define el nivel de severidad de los mensajes de syslog para access_log, como se especifica en RFC 3164. Los valores posibles son los mismos que los del segundo parámetro (nivel) de la directiva error_log. El valor predeterminado es "info". La severidad de los mensajes de error es determinada por Angie, por lo que este parámetro se ignora en la directiva error_log.

tag=string

Establece la etiqueta para los mensajes de syslog. La etiqueta predeterminada es "angie".

nohostname

Desactiva la adición del campo hostname en la cabecera del mensaje syslog.

Ejemplo de configuración syslog:

error_log syslog:server=192.168.1.1 debug;

access_log syslog:server=unix:/var/log/angie.sock,nohostname;
access_log syslog:server=[2001:db8::1]:12345,facility=local7,tag=angie,severity=info combined;