Conexões, sessões, requisições, logs#

Mecanismos de processamento de conexão#

O Angie suporta vários métodos de processamento de conexão. A disponibilidade de um método específico depende da plataforma sendo utilizada. Em plataformas que suportam múltiplos métodos, o Angie normalmente seleciona o método mais eficiente automaticamente. No entanto, se necessário, um método de processamento de conexão pode ser explicitamente escolhido usando a diretiva use.

Os seguintes métodos de processamento de conexão estão disponíveis:

Método

Descrição

select

Um método padrão. O módulo de suporte é construído automaticamente em plataformas que não possuem métodos mais eficientes. As opções de construção --with-select_module e --without-select_module podem ser usadas para forçar a habilitação ou desabilitação da construção deste módulo.

poll

Um método padrão. O módulo de suporte é construído automaticamente em plataformas que não possuem métodos mais eficientes. As opções de construção --with-poll_module e --without-poll_module podem ser usadas para forçar a habilitação ou desabilitação da construção deste módulo.

kqueue

Um método eficiente disponível no FreeBSD 4.1+, OpenBSD 2.9+, NetBSD 2.0, e macOS.

epoll

Um método eficiente disponível no Linux 2.6+.

/dev/poll

Um método eficiente disponível no Solaris 7 11/99+, HP/UX 11.22+ (eventport), IRIX 6.5.15+, e Tru64 UNIX 5.1A+.

eventport

O método event ports está disponível no Solaris 10+. (Devido a problemas conhecidos, é recomendado usar o método /dev/poll em seu lugar.)

Processamento de requisições HTTP#

Uma requisição HTTP passa por uma série de fases, onde um tipo específico de processamento é realizado em cada fase.

Post-read

A fase inicial. O módulo RealIP é invocado durante esta fase.

Server-rewrite

A fase onde as diretivas do módulo Rewrite, definidas em um bloco server (mas fora de um bloco location), são processadas.

Find-config

Uma fase especial onde um location é selecionado baseado na URI da requisição.

Rewrite

Similar à fase Server-rewrite, mas se aplica às regras rewrite definidas dentro do bloco location selecionado na fase anterior.

Post-rewrite

Uma fase especial onde a requisição é redirecionada para um novo location, como na fase Find-config, se sua URI foi modificada durante a fase Rewrite.

Preaccess

Durante esta fase, módulos padrão do Angie como Limit Req registram seus manipuladores.

Access

A fase onde a autorização do cliente para fazer a requisição é verificada, normalmente invocando módulos padrão do Angie como Auth Basic.

Post-access

Uma fase especial onde a diretiva satisfy any é processada.

Precontent

Diretivas de módulos padrão, como try_files e mirror, registram seus manipuladores durante esta fase.

Content

A fase onde a resposta é normalmente gerada. Múltiplos módulos padrão do Angie registram seus manipuladores neste estágio, incluindo Index. As diretivas proxy_pass, fastcgi_pass, uwsgi_pass, scgi_pass e grpc_pass também são tratadas aqui.

Os manipuladores são chamados sequencialmente até que um deles produza a saída.

Log

A fase final, onde o registro da requisição é realizado. Atualmente, apenas o módulo Log registra seu manipulador neste estágio para registro de acesso.

Processamento de sessões TCP/UDP#

Uma sessão TCP/UDP de um cliente passa por uma série de fases, onde um tipo específico de processamento é realizado em cada fase:

Post-accept

A fase inicial após aceitar uma conexão do cliente. O módulo RealIP é invocado nesta fase.

Pre-access

Uma fase preliminar para verificar o acesso. Os módulos Set são invocados durante esta fase.

Access

A fase para limitar o acesso do cliente antes do processamento real dos dados. O módulo Access é invocado neste estágio.

SSL

A fase onde ocorre a terminação TLS/SSL. O módulo SSL é invocado durante esta fase.

Preread

A fase para ler os bytes iniciais de dados no buffer de preread para permitir que módulos como SSL Preread analisem os dados antes do processamento.

Content

Uma fase obrigatória onde os dados são realmente processados, normalmente envolvendo o módulo Return para enviar uma resposta ao cliente. A diretiva proxy_pass também é tratada aqui.

Log

A fase final onde o resultado do processamento da sessão do cliente é registrado. O módulo Log é invocado nesta fase.

Seleção de servidor para uma sessão#

O Angie seleciona o bloco server para uma sessão pelo endereço e porta que a aceitaram, exatamente como faz para conexões HTTP; veja Seleção de servidor por endereço e porta para as regras de correspondência e para o servidor padrão de um par. Os pares TCP e UDP de uma mesma porta são distintos, cada um com seu próprio servidor padrão: um servidor com listen 9000 udp; nunca recebe as conexões TCP da porta 9000.

Dentro do par correspondente, uma sessão obtém o nome do servidor apenas a partir do SNI: ou o módulo SSL termina o TLS e lê o nome durante o handshake, ou o módulo SSL Preread o lê sem terminar a conexão. Em seguida, o Angie testa esse nome em relação às entradas server_name dos servidores que escutam no par; uma sessão sem nome SNI, ou com um nome que não corresponde a nenhuma delas, é tratada pelo servidor padrão do par.

Processamento de requisições#

Seleção de servidor virtual#

Inicialmente, uma conexão é criada dentro do contexto de um servidor padrão. O nome do servidor pode então ser determinado nas seguintes etapas do processamento da requisição, cada uma das quais está envolvida na seleção da configuração do servidor:

  • Durante o handshake SSL, antecipadamente, de acordo com o SNI.

  • Após processar a linha de requisição.

  • Após processar o campo de cabeçalho Host.

Se o nome do servidor não for determinado após processar a linha de requisição ou o campo de cabeçalho Host, o Angie usará um nome vazio como nome do servidor.

Em cada uma dessas etapas, diferentes configurações de servidor podem ser aplicadas. Portanto, certas diretivas devem ser especificadas com cuidado:

  • No caso da diretiva ssl_protocols, a lista de protocolos é definida pela biblioteca OpenSSL antes que a configuração do servidor seja aplicada de acordo com o nome solicitado através do SNI. Como resultado, os protocolos devem ser especificados apenas para o servidor padrão.

  • As diretivas client_header_buffer_size e merge_slashes são aplicadas antes de ler a linha de requisição. Portanto, essas diretivas usam a configuração do servidor padrão ou a configuração do servidor escolhida por SNI.

  • No caso das diretivas ignore_invalid_headers, large_client_header_buffers e underscores_in_headers, que estão envolvidas no processamento de campos de cabeçalho de requisição, a configuração do servidor depende adicionalmente de ter sido atualizada de acordo com a linha de requisição ou o campo de cabeçalho Host.

  • Uma resposta de erro é tratada usando a diretiva error_page no servidor que está processando a requisição no momento.

Servidores virtuais baseados em nome#

O Angie primeiro determina qual servidor deve tratar a requisição. Considere uma configuração simples onde todos os três servidores virtuais escutam na porta 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;
    #  ...
}

Nesta configuração, o Angie determina qual servidor deve tratar a requisição baseando-se apenas no campo de cabeçalho Host. Se o valor desse cabeçalho não corresponder a nenhum nome de servidor, o Angie encaminha a requisição para o servidor padrão do par *:80. Na configuração acima, o servidor padrão é o primeiro — que é o comportamento padrão do Angie. Também pode ser explicitamente especificado qual servidor deve ser o padrão usando o parâmetro default_server na diretiva listen:

server {

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

Nota

Observe que o servidor padrão é uma propriedade do par address:port, não do nome do servidor.

Nomes internacionalizados#

Nomes de domínio internacionalizados (IDNs) devem ser especificados usando uma representação ASCII (Punycode) na diretiva server_name:

server {

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

Prevenindo requisições com nomes de servidor indefinidos#

Se requisições sem o campo de cabeçalho Host não devem ser permitidas, um servidor que simplesmente descarta tais requisições pode ser definido:

server {

    listen 80;
    server_name "";
    return 444;
}

Nesta configuração, o nome do servidor é definido como uma string vazia, que corresponde a requisições sem o campo de cabeçalho Host. Um código especial não-padrão 444 é então retornado, que fecha a conexão.

Seleção de servidor por endereço e porta#

O Angie seleciona o bloco server para uma conexão em duas etapas: primeiro pelo endereço e porta que aceitaram a conexão, depois pelo nome.

As diretivas listen da configuração definem os pares address:port nos quais o Angie aceita conexões. Pares em portas diferentes, em famílias de endereço diferentes, ou em transportes diferentes nunca competem por uma conexão: um listener quic é UDP, portanto tem seu próprio par mesmo em uma porta que também carrega listeners TCP. Quando uma conexão chega, o Angie compara o endereço que a aceitou com os pares dessa porta: um endereço explícito prevalece, e *:port é o último recurso. Um servidor que escuta em *:port tem, portanto, um par próprio e não substitui os servidores nos endereços explícitos dessa porta. O par correspondente decide o restante da seleção: quais servidores o Angie testa por nome, qual deles é o padrão, e quais dos parâmetros ssl, http2, quic e proxy_protocol se aplicam à conexão. Um caminho de socket UNIX é um par no mesmo sentido: tem um servidor padrão e um conjunto próprio de nomes de servidor.

Em seguida, o Angie testa o campo de cabeçalho Host em relação às entradas server_name dos servidores cujas diretivas listen nomeiam o par correspondente; servidores que escutam em outro endereço da mesma porta, incluindo *:port, nunca são consultados. Se nenhum nome corresponder, o servidor padrão do par trata a requisição: o primeiro servidor na configuração que nomeia o par, a menos que outro servidor o nomeie com o parâmetro default_server. Uma requisição sem nome de host é comparada com o nome de servidor vazio, conforme descrito em Prevenindo requisições com nomes de servidor indefinidos; em HTTP/2 e HTTP/3, essa requisição é rejeitada antes que um servidor seja selecionado. O Angie rejeita uma configuração em que dois servidores reivindicam o mesmo par como padrão, relatando a duplicate default server for address:port.

Em uma conexão TLS, o Angie inicia o handshake na configuração do servidor padrão do par, de modo que seu ssl_certificate é o que é oferecido, a menos que o nome SNI corresponda a outro servidor no mesmo par; veja Servidores HTTPS baseados em nome para servir vários nomes em um único par.

Na configuração abaixo, quatro servidores escutam na porta 80 — dois em 198.51.100.1, um em 198.51.100.2, e um em *: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;
    #  ...
}

Uma conexão aceita em 198.51.100.1:80 corresponde a esse par, portanto o Angie testa o campo de cabeçalho Host apenas em relação aos dois primeiros servidores. Uma requisição para www.example.test é, portanto, tratada pelo primeiro deles, o servidor padrão de 198.51.100.1:80, embora o quarto servidor carregue esse nome; o mesmo vale para www.example.com, que pertence ao par 198.51.100.2:80. O quarto servidor trata apenas as conexões aceitas em endereços da porta 80 que nenhuma outra diretiva listen nomeia.

Todo par tem seu próprio servidor padrão, e default_server o designa para o par nomeado pela mesma diretiva 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;
    #  ...
}

Adicionar listen 80 default_server; a mais um servidor definiria o padrão apenas para *:80; o padrão de 198.51.100.1:80 permaneceria o servidor que já carrega default_server ali.

Nota

Os parâmetros de socket da diretiva listen, incluindo bind, alteram a forma como o Angie se vincula aos endereços, não qual servidor ele seleciona: a conexão é comparada com os mesmos pares de qualquer forma. Veja o parâmetro bind de listen para saber como a vinculação em si funciona.

Escolhendo localizações#

Considere uma configuração simples de site 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;
    }
}

O Angie primeiro busca pela location de prefixo mais específica dada por strings literais, independentemente da ordem listada. Na configuração acima, a única localização de prefixo é location /, que corresponde a qualquer requisição e será usada como último recurso. O Angie então verifica localizações definidas por expressões regulares na ordem em que aparecem no arquivo de configuração. A primeira expressão correspondente para a busca, e o Angie usará essa location. Se nenhuma expressão regular corresponder a uma requisição, o Angie usará a location de prefixo mais específica encontrada anteriormente.

Nota

Localizações de todos os tipos testam apenas a parte URI da linha de requisição, excluindo argumentos. Isso ocorre porque argumentos na string de consulta podem ser especificados de várias maneiras, por exemplo:

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

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

Além disso, strings de consulta podem conter qualquer número de parâmetros:

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

Agora vamos ver como as requisições seriam processadas na configuração acima:

  • A requisição /logo.gif é primeiro correspondida pelo prefixo location / e depois pela expressão regular .(gif|jpg|png)$. Portanto, é tratada pela última localização. Usando a diretiva root /data/www, a requisição é mapeada para o arquivo /data/www/logo.gif, e o arquivo é enviado para o cliente.

  • A requisição /index.php também é inicialmente correspondida pelo prefixo location / e depois pela expressão regular .(php)$. Consequentemente, é tratada pela última localização, e a requisição é passada para um servidor FastCGI escutando em localhost:9000. A diretiva fastcgi_param define o parâmetro FastCGI SCRIPT_FILENAME como /data/www/index.php, e o servidor FastCGI executa o arquivo. A variável $document_root é definida com o valor da diretiva root, e a variável $fastcgi_script_name é definida com o URI da requisição, ou seja, /index.php.

  • A requisição /about.html é correspondida apenas pelo prefixo location /, então é tratada nesta localização. Usando a diretiva root /data/www, a requisição é mapeada para o arquivo /data/www/about.html, e o arquivo é enviado para o cliente.

Tratar a requisição / é mais complexo. Ela é correspondida apenas pelo prefixo location /, então é tratada por esta localização. A diretiva index então testa a existência de arquivos de índice de acordo com seus parâmetros e a diretiva root /data/www. Se o arquivo /data/www/index.html não existir mas o arquivo /data/www/index.php existir, a diretiva executa um redirecionamento interno para /index.php, e o Angie busca as localizações novamente como se a requisição tivesse sido enviada por um cliente. Como mencionado anteriormente, a requisição redirecionada será eventualmente tratada pelo servidor FastCGI.

Os exemplos acima mapeiam uma requisição para um arquivo por meio da diretiva root, mas o mapeamento completo envolve várias diretivas atuando em conjunto. Uma vez selecionada a location correspondente, o Angie mapeia o $uri normalizado da requisição para um caminho no sistema de arquivos: a diretiva root acrescenta o URI inteiro ao seu valor, enquanto a diretiva alias substitui o prefixo de location correspondido. A diretiva try_files então verifica arquivos candidatos relativos a esse caminho; se nenhum existir, seu último parâmetro aciona um redirecionamento interno, e o Angie busca as localizações novamente contra o novo $uri, assim como no redirecionamento de índice acima.

Para a mesma requisição /images/top.gif, as duas locations abaixo a resolvem para o mesmo arquivo, /data/w3/images/top.gif:

location /images/ {
  root /data/w3;           # acrescentar:  /data/w3 + /images/top.gif
}

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

Com alias, o prefixo correspondido é removido pelo seu comprimento, portanto as barras finais da location e do valor de alias devem ser consistentes. Remover a barra final do caminho de alias acima resultaria em /data/w3/imagestop.gif.

Proxy e balanceamento de carga#

Um uso comum do Angie é configurá-lo como um servidor proxy. Nesta função, o Angie recebe requisições, encaminha-as para os servidores proxy, recupera respostas desses servidores e envia as respostas de volta aos clientes.

Um servidor proxy simples:

server {

    location / {

        proxy_pass http://backend:8080;
    }

A diretiva proxy_pass instrui o Angie a passar requisições de clientes para o backend backend:8080 (o servidor proxy). Existem muitas diretivas adicionais disponíveis para configurar ainda mais uma conexão proxy.

Proxy FastCGI#

O Angie pode ser usado para rotear requisições para servidores FastCGI que executam aplicações construídas com vários frameworks e linguagens de programação, como PHP.

A configuração mais básica do Angie para trabalhar com um servidor FastCGI envolve usar a diretiva fastcgi_pass em vez da diretiva proxy_pass, juntamente com diretivas fastcgi_param para definir parâmetros passados para o servidor FastCGI. Suponha que o servidor FastCGI esteja acessível em localhost:9000. Em PHP, o parâmetro SCRIPT_FILENAME é usado para determinar o nome do script, e o parâmetro QUERY_STRING é usado para passar parâmetros de requisição. A configuração resultante seria:

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 configuração define um servidor que roteia todas as requisições, exceto aquelas para imagens estáticas, para o servidor proxy operando em localhost:9000 via o protocolo FastCGI.

Proxy WebSocket#

Para atualizar uma conexão de HTTP/1.1 para WebSocket, é usado o mecanismo de troca de protocolo disponível no HTTP/1.1.

No entanto, há uma sutileza: como o cabeçalho Upgrade é um cabeçalho hop-by-hop, ele não é passado do cliente para o servidor com proxy. Com proxy direto, os clientes podem usar o método CONNECT para contornar esse problema. Essa abordagem não funciona com proxy reverso, pois os clientes não têm conhecimento de nenhum servidor proxy, e é necessário processamento especial no servidor proxy.

O Angie implementa um modo especial de operação que permite configurar um túnel entre um cliente e um servidor com proxy se o servidor com proxy retornar uma resposta com código 101 (Switching Protocols), e o cliente solicitar uma troca de protocolo através do cabeçalho Upgrade na requisição.

Como mencionado, cabeçalhos hop-by-hop, incluindo Upgrade e Connection, não são passados do cliente para o servidor com proxy. Portanto, para que o servidor com proxy tenha conhecimento da intenção do cliente de trocar para o protocolo WebSocket, esses cabeçalhos devem ser passados explicitamente:

location /chat/ {

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

Um exemplo mais sofisticado demonstra como o valor do campo de cabeçalho Connection em uma requisição para o servidor com proxy depende da presença do campo Upgrade no cabeçalho da requisição do 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 padrão, a conexão será fechada se o servidor com proxy não transmitir nenhum dado dentro de 60 segundos. Esse timeout pode ser aumentado usando a diretiva proxy_read_timeout. Alternativamente, o servidor com proxy pode ser configurado para enviar periodicamente frames de ping WebSocket para redefinir o timeout e verificar se a conexão ainda está ativa.

Balanceamento de carga#

O Angie pode atuar como balanceador de carga, distribuindo as requisições entre um grupo de servidores upstream e escolhendo o servidor por um de vários métodos de balanceamento. Consulte Balanceamento de carga para saber como escolher um método, o que o peso de um servidor significa para cada um deles e como as falhas de servidor são tratadas.

Logging#

Nota

Além das opções listadas aqui, você também pode habilitar o log de depuração.

Syslog#

As diretivas error_log e access_log suportam logging para syslog. Os seguintes parâmetros são usados para configurar logging para syslog:

server=address

Especifica o endereço de um servidor syslog. O endereço pode ser um nome de domínio ou um endereço IP, com uma porta opcional, ou um caminho de socket de domínio UNIX especificado após o prefixo "unix:". Se a porta não for especificada, a porta UDP 514 é usada. Se um nome de domínio resolver para múltiplos endereços IP, o primeiro endereço resolvido é usado.

facility=string

Define a facility para mensagens syslog, conforme definido na RFC 3164. As facilities possíveis incluem: "kern", "user", "mail", "daemon", "auth", "intern", "lpr", "news", "uucp", "clock", "authpriv", "ftp", "ntp", "audit", "alert", "cron", "local0".."local7". O padrão é "local7".

severity=string

Define o nível de severidade das mensagens syslog para access_log, conforme especificado na RFC 3164. Os valores possíveis são os mesmos do segundo parâmetro (level) da diretiva error_log. O padrão é "info". A severidade das mensagens de erro é determinada pelo Angie, então este parâmetro é ignorado na diretiva error_log.

tag=string

Define a tag para mensagens syslog. A tag padrão é "angie".

nohostname

Desabilita a adição do campo hostname no cabeçalho da mensagem syslog.

Exemplo de configuração 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;