Desenvolvimento#
Angie é um projeto de código aberto
que recebe bem todos os contribuidores. Você pode clonar o código-fonte do Angie de nossos repositórios públicos:
Mercurial,
Git. Suas alterações devem ser consistentes com o resto do código do Angie;
as convenções de codificação são um bom ponto de partida. Dica Em caso de dúvida, examine o código próximo para seguir seu exemplo,
ou simplesmente use grep na base de código para inspiração. Historicamente, o log de commits é mantido em inglês. Comece com um resumo de uma linha do que foi feito.
Pode ter um prefixo que o log de commits usa para a porção de código afetada.
O resumo pode ter até 67 caracteres
e pode ser seguido por uma linha em branco e mais detalhes. Uma boa mensagem conta o que causou a mudança, o que foi feito sobre isso,
e qual é a situação agora: Detalhes que podem passar despercebidos: O resumo termina com um ponto e começa com letra maiúscula. Se um prefixo é usado, é seguido por uma letra minúscula. Espaço duplo separa frases dentro de uma única linha. Faça o seu melhor para verificar que as alterações funcionam em todas as plataformas alvo. Para cada plataforma, execute a suíte de testes para garantir que não há regressão: Veja o arquivo Certifique-se de que está confortável com os termos legais. Para enviar um patch, crie um pull request em nosso
espelho do GitHub. Para perguntas e sugestões, entre em contato com os desenvolvedores via
GitHub Issues. O código-fonte segue a seguinte estrutura e convenções. As duas declarações Além disso, código HTTP deve incluir Código de mail deve incluir Código de stream deve incluir Para propósitos gerais, o código Angie usa dois tipos de inteiros:
A maioria das funções no Angie retorna os seguintes códigos: A macro Os valores de Exemplo usando Para strings C, o Angie usa o ponteiro de tipo de caractere não assinado
O tipo de string do Angie O campo As operações de string no Angie são declaradas em
Outras funções de string são específicas do Angie As seguintes funções realizam conversão de caso e comparação: As seguintes macros simplificam a inicialização de strings: As seguintes funções de formatação suportam tipos específicos do Angie: A lista completa de opções de formatação, suportadas por essas funções está
em Você pode antepor Várias funções para conversão numérica são implementadas no Angie.
As primeiras quatro convertem cada uma uma string de comprimento dado para um inteiro positivo do
tipo indicado.
Elas retornam Há duas funções adicionais de conversão numérica.
Como as primeiras quatro, elas retornam A interface de expressões regulares no Angie é um wrapper em torno
da biblioteca PCRE.
O arquivo de cabeçalho correspondente é Para usar uma expressão regular para correspondência de strings, ela primeiro precisa ser
compilada, o que geralmente é feito na fase de configuração.
Note que como o suporte PCRE é opcional, todo código usando a interface deve
ser protegido pela macro Após compilação bem-sucedida, os campos A expressão regular compilada pode então ser usada para correspondência contra strings: Os argumentos para Se houver correspondências, as capturas podem ser acessadas da seguinte forma: A função A estrutura A estrutura Para obter o tempo atual, geralmente é suficiente acessar uma das
variáveis globais disponíveis, representando o valor de tempo em cache no formato
desejado. As representações de string disponíveis são: As macros Para obter o tempo explicitamente, use As seguintes funções convertem A função O tipo de array do Angie Os elementos do array estão disponíveis no campo Use a chamada Use as seguintes funções para adicionar elementos a um array: Se a quantidade de memória atualmente alocada não for grande o suficiente para acomodar
os novos elementos, um novo bloco de memória é alocado e os elementos existentes
são copiados para ele.
O novo bloco de memória normalmente é duas vezes maior que o existente. No Angie uma lista é uma sequência de arrays, otimizada para inserir um número
potencialmente grande de itens.
O tipo de lista Os itens reais são armazenados em partes da lista, que são definidas da seguinte forma: Antes do uso, uma lista deve ser inicializada chamando
As listas são usadas principalmente para cabeçalhos de entrada e saída HTTP. As listas não suportam remoção de itens.
No entanto, quando necessário, os itens podem ser marcados internamente como ausentes sem
serem realmente removidos da lista.
Por exemplo, para marcar cabeçalhos de saída HTTP (que são armazenados como
objetos No Angie uma fila é uma lista duplamente ligada intrusiva, com cada nó definido da
seguinte forma: O nó cabeça da fila não está ligado a nenhum dado.
Use a chamada Um exemplo: O arquivo de cabeçalho Para lidar com uma árvore como um todo, você precisa de dois nós: raiz e sentinela.
Tipicamente, eles são adicionados a uma estrutura personalizada, permitindo que você
organize seus dados em uma árvore na qual as folhas contêm um link para ou incorporam
seus dados. Para inicializar uma árvore: Para percorrer uma árvore e inserir novos valores, use as
funções " O percurso é bastante direto e pode ser demonstrado com o
seguinte padrão de função de busca: A função Para adicionar um nó a uma árvore, aloque um novo nó, inicialize-o e chame
Para remover um nó, chame a função As funções de tabela hash são declaradas em Antes de inicializar um hash, você precisa saber o número de elementos que ele irá
conter para que o Angie possa construí-lo de forma otimizada.
Dois parâmetros que precisam ser configurados são O As chaves do hash são armazenadas em Para inserir chaves em um array de chaves hash, use a
função Para construir a tabela hash, chame a
função A função falha se os parâmetros Quando o hash é construído, use a
função Para criar um hash que funciona com curingas, use o
tipo É possível adicionar chaves curinga usando a
flag A função reconhece curingas e adiciona chaves nos arrays correspondentes.
Por favor, consulte a
documentação do módulo Map
para a descrição da sintaxe de curingas e do
algoritmo de correspondência. Dependendo do conteúdo das chaves adicionadas, você pode precisar inicializar até três
arrays de chaves: um para correspondência exata (descrito acima), e dois mais para habilitar
correspondência começando do início ou fim de uma string: O array de chaves precisa ser ordenado, e os resultados da inicialização devem ser adicionados
ao hash combinado.
A inicialização do array A busca em um hash combinado é tratada pela
Para alocar memória do heap do sistema, use as seguintes funções: A maioria das alocações do Angie são feitas em pools.
A memória alocada em um pool do Angie é liberada automaticamente quando o pool é
destruído.
Isso fornece boa performance de alocação e torna o controle de memória fácil. Um pool aloca internamente objetos em blocos contínuos de memória.
Uma vez que um bloco está cheio, um novo é alocado e adicionado à lista de
blocos de memória do pool.
Quando a alocação solicitada é muito grande para caber em um bloco, a solicitação
é encaminhada para o alocador do sistema e o
ponteiro retornado é armazenado no pool para posterior desalocação. O tipo para pools do Angie é Links de cadeia ( Manipuladores de limpeza podem ser registrados em um pool.
Um manipulador de limpeza é um callback com um argumento que é chamado quando o pool é
destruído.
Um pool geralmente está vinculado a um objeto específico do Angie (como uma requisição HTTP) e é
destruído quando o objeto atinge o fim de seu tempo de vida.
Registrar uma limpeza de pool é uma maneira conveniente de liberar recursos, fechar
descritores de arquivo ou fazer ajustes finais nos dados compartilhados associados ao
objeto principal. Para registrar uma limpeza de pool, chame
A memória compartilhada é usada pelo Angie para compartilhar dados comuns entre processos.
A função A estrutura de entrada de memória compartilhada As entradas de zona compartilhada são mapeadas para memória real em
Para alocar em memória compartilhada, o Angie fornece o slab pool
tipo O slab pool divide toda a zona compartilhada em páginas.
Cada página é usada para alocar objetos do mesmo tamanho.
O tamanho especificado deve ser uma potência de 2, e maior que o tamanho mínimo de
8 bytes.
Valores não conformes são arredondados para cima.
Uma máscara de bits para cada página rastreia quais blocos estão em uso e quais estão livres para
alocação.
Para tamanhos maiores que meia página (que geralmente é 2048 bytes), a alocação é
feita uma página inteira por vez Para proteger dados na memória compartilhada de acesso concorrente, use o mutex
disponível no campo Para logging, o Angie usa objetos stderr — Logging para erro padrão (stderr) file — Logging para um arquivo syslog — Logging para syslog memory — Logging para armazenamento de memória interna para fins de desenvolvimento; a memória
pode ser acessada posteriormente com um debugger Uma instância de logger pode ser uma cadeia de loggers, vinculados uns aos outros com
o campo Para cada logger, um nível de severidade controla quais mensagens são escritas no
log (apenas eventos atribuídos a esse nível ou superior são registrados).
Os seguintes níveis de severidade são suportados: Para logging de debug, a máscara de debug também é verificada.
As máscaras de debug são: Normalmente, os loggers são criados pelo código existente do Angie a partir de
diretivas O Nginx fornece as seguintes macros de logging: Uma mensagem de log é formatada em um buffer de tamanho
O exemplo acima resulta em entradas de log como estas: Um objeto de ciclo armazena o contexto de execução do Angie criado a partir de uma
configuração específica.
Seu tipo é Um ciclo é criado pela função Os membros do ciclo incluem: carregador de caminho — Executa apenas uma vez em 60 segundos após iniciar ou recarregar
o Angie.
Normalmente, o carregador lê o diretório e armazena dados na memória compartilhada
do Angie.
O manipulador é chamado do processo dedicado do Angie "carregador de cache". gerenciador de caminho — Executa periodicamente.
Normalmente, o gerenciador remove arquivos antigos do diretório e atualiza a
memória do Angie para refletir as mudanças.
O manipulador é chamado do processo dedicado "gerenciador de cache". Para operações de entrada/saída, o Angie fornece o tipo de buffer
A estrutura Para operações de entrada e saída os buffers são ligados em cadeias.
Uma cadeia é uma sequência de links de cadeia do tipo Cada link de cadeia mantém uma referência ao seu buffer e uma referência ao próximo
link de cadeia. Um exemplo de uso de buffers e cadeias: O tipo de conexão Uma conexão Angie pode encapsular transparentemente a camada SSL.
Neste caso, o campo A diretiva Como o número de conexões por worker é limitado, o Angie fornece uma
maneira de capturar conexões que estão atualmente em uso.
Para habilitar ou desabilitar a reutilização de uma conexão, chame a
função O objeto de evento Os campos em Cada conexão obtida chamando a função Um evento pode ser definido para enviar uma notificação quando um timeout expira.
O timer usado pelos eventos conta milissegundos desde algum ponto não especificado
no passado truncado para o tipo A função Um evento pode ser postado, o que significa que seu manipulador será chamado em algum
ponto posterior dentro da iteração atual do loop de eventos.
Postar eventos é uma boa prática para simplificar código e escapar de estouros de pilha.
Eventos postados são mantidos em uma fila de postagem.
A macro Um exemplo: Exceto pelo processo master do Angie, todos os processos do Angie fazem I/O e, portanto, têm um
loop de eventos.
(O processo master do Angie, em vez disso, passa a maior parte do tempo na
chamada O loop de eventos tem os seguintes estágios: Encontrar o timeout que está mais próximo de expirar, chamando
Processar eventos de I/O chamando um manipulador, específico para o mecanismo de notificação
de eventos, escolhido pela configuração do Angie.
Este manipulador aguarda que pelo menos um evento de I/O aconteça, mas apenas até que o próximo
timeout expire.
Quando um evento de leitura ou escrita ocorre, a flag Expirar timers chamando Processar eventos postados chamando Todos os processos do Angie também manipulam sinais.
Os manipuladores de sinais apenas definem variáveis globais que são verificadas após a
chamada Existem vários tipos de processos no Angie.
O tipo de um processo é mantido na variável global Os processos do Angie manipulam os seguintes sinais: Embora todos os processos worker do Angie sejam capazes de receber e manipular adequadamente sinais
POSIX, o processo master não usa a chamada de sistema É possível descarregar para uma thread separada tarefas que de outra forma
bloqueariam o processo worker do Angie.
Por exemplo, o Angie pode ser configurado para usar threads para executar
I/O de arquivo.
Outro caso de uso é uma biblioteca que não tem interface assíncrona
e, portanto, não pode ser normalmente usada com o Angie.
Tenha em mente que a interface de threads é um auxiliar para a abordagem
assíncrona existente para processar conexões de clientes, e de forma alguma
pretende ser um substituto. Para lidar com sincronização, os seguintes wrappers sobre
primitivos Em vez de criar uma nova thread para cada tarefa, o Angie implementa
uma estratégia de thread_pool.
Múltiplos pools de threads podem ser configurados para diferentes propósitos
(por exemplo, executar I/O em diferentes conjuntos de discos).
Cada pool de threads é criado na inicialização e contém um número limitado de threads
que processam uma fila de tarefas.
Quando uma tarefa é concluída, um manipulador de conclusão predefinido é chamado. O arquivo de cabeçalho No momento da configuração, um módulo que deseja usar threads deve obter uma
referência para um pool de threads chamando
Para adicionar uma Para executar uma função em uma thread, passe parâmetros e configure um
manipulador de conclusão usando a estrutura Cada módulo Angie independente reside em um diretório separado que contém
pelo menos dois arquivos:
O arquivo Os seguintes módulos são tipicamente usados como referências.
O Por padrão, módulos de filtro são colocados antes do
Para compilar um módulo no Angie estaticamente, use o
argumento Módulos são os blocos de construção do Angie, e a maior parte de sua funcionalidade é
implementada como módulos.
O arquivo-fonte do módulo deve conter uma variável global do tipo
A parte privada omitida inclui a versão do módulo e uma assinatura e é
preenchida usando a macro predefinida Cada módulo mantém seus dados privados no campo Manipuladores de diretivas de configuração são chamados conforme aparecem
nos arquivos de configuração no contexto do processo mestre. Após a configuração ser analisada com sucesso, o manipulador O processo mestre cria um ou mais processos worker e o
manipulador Quando um processo worker recebe o comando de desligamento ou término do
mestre, ele invoca o manipulador O processo mestre chama o manipulador Como threads são usadas no Angie apenas como uma facilidade de E/S suplementar com sua
própria API, os manipuladores O O O conjunto de módulos principais inclui os módulos onde Por exemplo, um módulo simplista chamado O tipo Termine o array com o valor especial As flags para tipos de diretiva são: O contexto de uma diretiva define onde ela pode aparecer na configuração: O parser de configuração usa essas flags para gerar um erro em caso de
uma diretiva mal posicionada e chama manipuladores de diretiva fornecidos com um
ponteiro de configuração apropriado, para que as mesmas diretivas em locais diferentes possam
armazenar seus valores em lugares distintos. O campo O campo O O campo O argumento Cada conexão de cliente HTTP passa pelas seguintes etapas: O manipulador de evento de leitura O manipulador de evento de leitura O manipulador de evento de leitura Para cada requisição HTTP do cliente o objeto Note que para conexões HTTP o campo Uma requisição é geralmente postada pela chamada
Cada módulo HTTP pode ter três tipos de configuração: Configuração principal — Aplica-se a todo o bloco Configuração de servidor — Aplica-se a um único bloco Configuração de localização — Aplica-se a um único bloco As estruturas de configuração são criadas na etapa de configuração do Angie chamando
funções, que alocam as estruturas, as inicializam
e as mesclam.
O exemplo a seguir mostra como criar uma configuração de localização
simples para um módulo.
A configuração tem uma configuração, Como visto no exemplo, a função As seguintes macros estão disponíveis
para acessar configuração para módulos HTTP no momento da configuração.
Todas elas recebem uma referência O exemplo a seguir obtém um ponteiro para uma configuração de localização do
módulo core padrão do Angie
e substitui o manipulador de conteúdo de localização mantido
no campo As seguintes macros estão disponíveis para acessar configuração para módulos
HTTP em tempo de execução. Essas macros recebem uma referência para uma requisição HTTP
Cada requisição HTTP passa por uma sequência de fases.
Em cada fase um tipo distinto de processamento é executado na requisição.
Manipuladores específicos de módulo podem ser registrados na maioria das fases,
e muitos módulos padrão do Angie registram seus manipuladores de fase como uma forma
de serem chamados em um estágio específico do processamento da requisição.
As fases são processadas sucessivamente e os manipuladores de fase são chamados
uma vez que a requisição atinge a fase.
A seguir está a lista das fases HTTP do Angie. A seguir está o exemplo de um manipulador de fase de pré-acesso. Espera-se que os manipuladores de fase retornem códigos específicos: Qualquer outro valor retornado pelo manipulador de fase é tratado como um código
de finalização da requisição, em particular, um código de resposta HTTP.
A requisição é finalizada com o código fornecido. Para algumas fases, os códigos de retorno são tratados de forma ligeiramente diferente.
Na fase de conteúdo, qualquer código de retorno diferente de
O repositório
nginx-dev-examples
fornece exemplos de módulos do Angie. largura máxima do texto é 80 caracteres indentação é 4 espaços sem tabs, sem espaços no final elementos de lista na mesma linha são separados com espaços literais hexadecimais são minúsculos nomes de arquivos, funções e tipos, e variáveis globais têm o
prefixo Um arquivo fonte típico pode conter as seguintes seções separadas por
duas linhas vazias: declarações de copyright includes definições do pré-processador definições de tipos protótipos de funções definições de variáveis definições de funções As declarações de copyright ficam assim: Se o arquivo for modificado significativamente, a lista de autores deve ser atualizada,
o novo autor é adicionado ao topo. Os arquivos Arquivos de cabeçalho devem incluir a chamada "proteção de cabeçalho": comentários " texto é escrito em inglês, ortografia americana é preferida comentários multi-linha são formatados assim: Nomes de macro começam com prefixo Condições ficam dentro de parênteses, negação fica fora: Nomes de tipos terminam com o sufixo " Tipos de estrutura são definidos usando Mantenha alinhamento idêntico entre diferentes estruturas no arquivo.
Uma estrutura que aponta para si mesma tem o nome terminando com
" Cada membro da estrutura é declarado em sua própria linha: Ponteiros de função dentro de estruturas têm tipos definidos terminando
com " Enumerações têm tipos terminando com " Variáveis são declaradas ordenadas por comprimento do tipo base, depois alfabeticamente.
Nomes de tipos e nomes de variáveis são alinhados.
As "colunas" de tipo e nome são separadas com dois espaços.
Arrays grandes são colocados no final de um bloco de declaração: Variáveis estáticas e globais podem ser inicializadas na declaração: Existe um conjunto de combinações tipo/nome comumente usadas: Todas as funções (mesmo as estáticas) devem ter protótipos.
Os protótipos incluem nomes de argumentos.
Protótipos longos são quebrados com uma única indentação nas linhas de continuação: O nome da função em uma definição começa com uma nova linha.
As chaves de abertura e fechamento do corpo da função ficam em linhas separadas.
O corpo de uma função é indentado.
Há duas linhas vazias entre as funções: Não há espaço após o nome da função e o parêntese de abertura.
Chamadas de função longas são quebradas de forma que as linhas de continuação comecem
na posição do primeiro argumento da função.
Se isso for impossível, formate a primeira linha de continuação de forma que ela
termine na posição 79: A macro Operadores binários exceto " Conversões de tipo são separadas por um espaço das expressões convertidas.
Um asterisco dentro da conversão de tipo é separado com espaço do nome do tipo: Se uma expressão não cabe em uma única linha, ela é quebrada.
O ponto preferido para quebrar uma linha é um operador binário.
A linha de continuação é alinhada com o início da expressão: Como último recurso, é possível quebrar uma expressão de forma que a
linha de continuação termine na posição 79: As regras acima também se aplicam a sub-expressões,
onde cada sub-expressão tem seu próprio nível de indentação: Às vezes, é conveniente quebrar uma expressão após uma conversão.
Neste caso, a linha de continuação é indentada: Ponteiros são explicitamente comparados com
A palavra-chave " Regras de formatação similares são aplicadas aos loops " A palavra-chave " A maioria dos loops " Se alguma parte da instrução " Um loop com corpo vazio também é indicado pelo
comentário " Um loop infinito fica assim: Rótulos são cercados por linhas vazias e são indentados no nível anterior: Para depurar problemas de memória como estouro de buffer ou erros de uso após liberação, você
pode usar o AddressSanitizer
(ASan) suportado por alguns compiladores modernos.
Para habilitar o ASan com Como a maioria das alocações no Angie são feitas a partir do
pool interno do Angie, habilitar o ASan pode nem sempre ser suficiente para depurar
problemas de memória.
O pool interno aloca um grande bloco de memória do sistema e corta
alocações menores dele.
No entanto, esse mecanismo pode ser desabilitado definindo a
macro A seguinte linha de configuração resume as informações fornecidas acima.
É recomendada durante o desenvolvimento de módulos de terceiros e teste do Angie em
diferentes plataformas. A armadilha mais comum é uma tentativa de escrever um módulo C completo
quando isso pode ser evitado.
Na maioria dos casos sua tarefa pode ser realizada criando uma configuração adequada.
Se escrever um módulo for inevitável, tente torná-lo
o mais pequeno e simples possível.
Por exemplo, um módulo pode apenas exportar algumas
variáveis. Antes de começar um módulo, considere as seguintes questões: É possível implementar uma funcionalidade desejada usando
módulos já disponíveis? É possível resolver um problema usando linguagens de script integradas,
como Perl ou Módulos de Terceiros? O tipo de string mais usado no Angie,
ngx_str_t não é uma string
terminada em zero no estilo C.
Você não pode passar os dados para funções padrão da biblioteca C
como Evite usar variáveis globais em seus módulos.
Muito provavelmente é um erro ter uma variável global.
Qualquer dado global deve estar vinculado a um ciclo de configuração
e ser alocado do pool de memória correspondente.
Isso permite ao Angie realizar recarregamentos de configuração suaves.
Uma tentativa de usar variáveis globais provavelmente quebrará essa funcionalidade,
porque será impossível ter duas configurações ao
mesmo tempo e se livrar delas.
Às vezes variáveis globais são necessárias.
Neste caso, atenção especial é necessária para gerenciar a reconfiguração
adequadamente.
Além disso, verifique se as bibliotecas usadas pelo seu código têm estado
global implícito que pode ser quebrado no recarregamento. Em vez de lidar com a abordagem malloc/free que é propensa a erros,
aprenda como usar os pools do Angie.
Um pool é criado e vinculado a um objeto -
configuração,
ciclo,
conexão,
ou requisição HTTP.
Quando o objeto é destruído, o pool associado também é destruído.
Então ao trabalhar com um objeto, é possível alocar a quantidade
necessária do pool correspondente e não se preocupar em liberar memória
mesmo em caso de erros. É recomendado evitar usar threads no Angie porque isso definitivamente
quebrará as coisas: a maioria das funções do Angie não são thread-safe.
É esperado que uma thread execute apenas chamadas de sistema e
funções de biblioteca thread-safe.
Se você precisar executar algum código que não está relacionado ao processamento de requisições de cliente,
a maneira adequada é agendar um timer no manipulador de módulo Um erro comum é usar bibliotecas que são bloqueantes internamente.
A maioria das bibliotecas por aí são síncronas e bloqueantes por natureza.
Em outras palavras, elas executam uma operação por vez e desperdiçam
tempo esperando resposta de outro peer.
Como resultado, quando uma requisição é processada com tal biblioteca, todo o
worker do Angie é bloqueado, destruindo assim a performance.
Use apenas bibliotecas que fornecem interface assíncrona e não
bloqueiam todo o processo. Frequentemente módulos precisam executar uma chamada HTTP para algum serviço externo.
Um erro comum é usar alguma biblioteca externa, como libcurl,
para executar a requisição HTTP.
É absolutamente desnecessário trazer uma quantidade enorme de código
externo (provavelmente bloqueante!)
para a tarefa que pode ser realizada pelo próprio Angie. Há dois cenários básicos de uso quando uma requisição externa é necessária: no contexto de processamento de uma requisição de cliente (por exemplo, em manipulador de conteúdo) no contexto de um processo worker (por exemplo, manipulador de timer) No primeiro caso, o melhor é usar a
API de subrequests.
Em vez de acessar diretamente o serviço externo, você declara uma localização
na configuração do Angie e direciona sua subrequest para esta localização.
Esta localização não está limitada a
fazer proxy
de requisições, mas pode conter outras diretivas do Angie.
Um exemplo de tal abordagem é a
diretiva auth_request implementada em
Auth Request. Para o segundo caso, é possível usar a funcionalidade básica de cliente HTTP
disponível no Angie.
Por exemplo, o
módulo OCSP
implementa um cliente HTTP simples.Código-Fonte#
Estilo de Codificação#
Mensagens 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
Verificações Finais#
$ cd tests
$ prove .
tests/README
para detalhes.Enviando Contribuições#
Convenções de codificação#
Layout do código#
auto
— Scripts de buildsrc
core
— Tipos e funções básicas — string, array, log,
pool, etc.event
— Núcleo de eventosmodules
— Módulos de notificação de eventos:
epoll
, kqueue
, select
etc.http
— Módulo HTTP principal e código comummodules
— Outros módulos HTTPv2
— HTTP/2mail
— Módulos de mailos
— Código específico da plataformaunix
win32
stream
— Módulos de streamArquivos de inclusão#
#include
a seguir devem aparecer no
início de todo arquivo Angie:#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
#include <ngx_mail.h>
#include <ngx_stream.h>
Inteiros#
ngx_int_t
e ngx_uint_t
, que são
typedefs para intptr_t
e uintptr_t
respectivamente.Códigos de retorno comuns#
NGX_OK
— Operação bem-sucedida.NGX_ERROR
— Operação falhou.NGX_AGAIN
— Operação incompleta; chame a função novamente.NGX_DECLINED
— Operação rejeitada, por exemplo, porque está
desabilitada na configuração. Isso nunca é um erro.NGX_BUSY
— Recurso não está disponível.NGX_DONE
— Operação completa ou continuada em outro lugar.
Também usado como um código de sucesso alternativo.NGX_ABORT
— Função foi abortada.
Também usado como um código de erro alternativo.Tratamento de erros#
ngx_errno
retorna o último código de erro do sistema.
Ela é mapeada para errno
em plataformas POSIX e para a
chamada GetLastError()
no Windows.
A macro ngx_socket_errno
retorna o último número de erro
de socket.
Como a macro ngx_errno
, ela é mapeada para
errno
em plataformas POSIX.
Ela é mapeada para a chamada WSAGetLastError()
no Windows.
Acessar os valores de ngx_errno
ou
ngx_socket_errno
mais de uma vez seguida pode causar
problemas de desempenho.
Se o valor do erro pode ser usado múltiplas vezes, armazene-o em uma variável local
do tipo ngx_err_t
.
Para definir erros, use as macros ngx_set_errno(errno)
e
ngx_set_socket_errno(errno)
.ngx_errno
e
ngx_socket_errno
podem ser passados para as funções de log
ngx_log_error()
e ngx_log_debugX()
, em
que caso o texto de erro do sistema é adicionado à mensagem de log.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;
}
Strings#
Visão geral#
u_char *
.ngx_str_t
é definido como segue:typedef struct {
size_t len;
u_char *data;
} ngx_str_t;
len
contém o comprimento da string e
data
contém os dados da string.
A string, mantida em ngx_str_t
, pode ou não ser
terminada em null após os len
bytes.
Na maioria dos casos não é.
No entanto, em certas partes do código (por exemplo, ao analisar configuração),
objetos ngx_str_t
são conhecidos por serem terminados em null, o que
simplifica a comparação de strings e torna mais fácil passar as strings para
syscalls.src/core/ngx_string.h
.
Algumas delas são wrappers em torno de funções C padrão:ngx_strcmp()
ngx_strncmp()
ngx_strstr()
ngx_strlen()
ngx_strchr()
ngx_memcmp()
ngx_memset()
ngx_memcpy()
ngx_memmove()
ngx_memzero()
— Preenche memória com zeros.ngx_explicit_memzero()
— Faz o mesmo que
ngx_memzero()
, mas esta chamada nunca é removida pela
otimização de eliminação de armazenamento morto do compilador.
Esta função pode ser usada para limpar dados sensíveis como senhas e chaves.ngx_cpymem()
— Faz o mesmo que
ngx_memcpy()
, mas retorna o endereço de destino final
Esta é útil para anexar múltiplas strings em sequência.ngx_movemem()
— Faz o mesmo que
ngx_memmove()
, mas retorna o endereço de destino final.ngx_strlchr()
— Procura por um caractere em uma string,
delimitada por dois ponteiros.ngx_tolower()
ngx_toupper()
ngx_strlow()
ngx_strcasecmp()
ngx_strncasecmp()
ngx_string(text)
— inicializador estático para o
tipo ngx_str_t
a partir do literal de string C
text
ngx_null_string
— inicializador de string vazia estático para o
tipo ngx_str_t
ngx_str_set(str, text)
— inicializa string
str
do tipo ngx_str_t *
com o literal de string C
text
ngx_str_null(str)
— inicializa string str
do tipo ngx_str_t *
com a string vaziaFormatação#
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
. Algumas delas são:%O
— off_t
%T
— time_t
%z
— ssize_t
%i
— ngx_int_t
%p
— void *
%V
— ngx_str_t *
%s
— u_char *
(terminado em null)%*s
— size_t + u_char *
u
na maioria dos tipos para torná-los não assinados.
Para converter saída para hex, use X
ou x
.Conversão numérica#
NGX_ERROR
em caso de erro.ngx_atoi(line, n)
— ngx_int_t
ngx_atosz(line, n)
— ssize_t
ngx_atoof(line, n)
— off_t
ngx_atotm(line, n)
— time_t
NGX_ERROR
em caso de erro.ngx_atofp(line, n, point)
— Converte um número de ponto fixo
de comprimento dado para um inteiro positivo do tipo
ngx_int_t
.
O resultado é deslocado para a esquerda por point
posições
decimais.
A representação em string do número deve ter no máximo
points
dígitos fracionários.
Por exemplo, ngx_atofp("10.5", 4, 2)
retorna
1050
.ngx_hextoi(line, n)
— Converte uma representação hexadecimal
de um inteiro positivo para ngx_int_t
.Expressões regulares#
src/core/ngx_regex.h
.NGX_PCRE
circundante:#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
e
named_captures
na estrutura
ngx_regex_compile_t
contêm a contagem de todas as
capturas e capturas nomeadas, respectivamente, encontradas na expressão 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()
são a expressão regular
compilada re
, a string a ser correspondida input
,
um array opcional de inteiros para armazenar quaisquer captures
que
sejam encontradas, e o size
do array.
O tamanho do array captures
deve ser um múltiplo de três,
conforme exigido pela
API PCRE.
No exemplo, o tamanho é calculado a partir do número total de capturas mais
um para a própria string correspondida.u_char *p;
size_t size;
ngx_str_t name, value;
/* all captures */
for (i = 0; i < n * 2; i += 2) {
value.data = input.data + captures[i];
value.len = captures[i + 1] - captures[i];
}
/* accessing named captures */
size = rc.name_size;
p = rc.names;
for (i = 0; i < rc.named_captures; i++, p += size) {
/* capture name */
name.data = &p[2];
name.len = ngx_strlen(name.data);
n = 2 * ((p[0] << 8) + p[1]);
/* captured value */
value.data = &input.data[captures[n]];
value.len = captures[n + 1] - captures[n];
}
ngx_regex_exec_array()
aceita o array de
elementos ngx_regex_elt_t
(que são apenas expressões regulares
compiladas com nomes associados), uma string para correspondência e um log.
A função aplica expressões do array à string até que
uma correspondência seja encontrada ou não restem mais expressões.
O valor de retorno é NGX_OK
quando há uma correspondência e
NGX_DECLINED
caso contrário, ou NGX_ERROR
em caso de erro.Tempo#
ngx_time_t
representa tempo com três tipos
separados para segundos, milissegundos e o deslocamento GMT:typedef struct {
time_t sec;
ngx_uint_t msec;
ngx_int_t gmtoff;
} ngx_time_t;
ngx_tm_t
é um alias para
struct tm
em plataformas UNIX e SYSTEMTIME
no Windows.ngx_cached_err_log_time
— Usado em entradas de log de erro:
"1970/09/28 12:00:00"
ngx_cached_http_log_time
— Usado em entradas de log de acesso HTTP:
"28/Sep/1970:12:00:00 +0600"
ngx_cached_syslog_time
— Usado em entradas de syslog:
"Sep 28 12:00:00"
ngx_cached_http_time
— Usado em cabeçalhos HTTP:
"Mon, 28 Sep 1970 06:00:00 GMT"
ngx_cached_http_log_iso8601
— O formato padrão
ISO 8601:
"1970-09-28T12:00:00+06:00"
ngx_time()
e ngx_timeofday()
retornam o valor de tempo atual em segundos e são a forma preferida de acessar
o valor de tempo em cache.ngx_gettimeofday()
,
que atualiza seu argumento (ponteiro para
struct timeval
).
O tempo é sempre atualizado quando o Angie retorna ao loop de eventos das chamadas
do sistema.
Para atualizar o tempo imediatamente, chame ngx_time_update()
,
ou ngx_time_sigsafe_update()
se estiver atualizando o tempo no
contexto do manipulador de sinal.time_t
na representação
de tempo decomposto indicada.
A primeira função em cada par converte time_t
para
ngx_tm_t
e a segunda (com o infixo _libc_
)
para struct tm
:ngx_gmtime(), ngx_libc_gmtime()
— Tempo expresso como UTCngx_localtime(), ngx_libc_localtime()
— Tempo expresso
relativo ao fuso horário localngx_http_time(buf, time)
retorna uma representação
de string adequada para uso em cabeçalhos HTTP (por exemplo,
"Mon, 28 Sep 1970 06:00:00 GMT"
).
A função ngx_http_cookie_time(buf, time)
retorna uma representação
de string adequada para cookies HTTP ("Thu, 31-Dec-37 23:55:55 GMT"
).Contêineres#
Array#
ngx_array_t
é definido da seguinte formatypedef struct {
void *elts;
ngx_uint_t nelts;
size_t size;
ngx_uint_t nalloc;
ngx_pool_t *pool;
} ngx_array_t;
elts
.
O campo nelts
contém o número de elementos.
O campo size
contém o tamanho de um único elemento e é definido
quando o array é inicializado.ngx_array_create(pool, n, size)
para criar um
array em um pool, e a chamada ngx_array_init(array, pool, n, size)
para inicializar um objeto array que já foi alocado.ngx_array_t *a, b;
/* create an array of strings with preallocated memory for 10 elements */
a = ngx_array_create(pool, 10, sizeof(ngx_str_t));
/* initialize string array for 10 elements */
ngx_array_init(&b, pool, 10, sizeof(ngx_str_t));
ngx_array_push(a)
adiciona um elemento no final e retorna ponteiro
para elengx_array_push_n(a, n)
adiciona n
elementos no final
e retorna ponteiro para o primeiros = ngx_array_push(a);
ss = ngx_array_push_n(&b, 3);
Lista#
ngx_list_t
é definido da seguinte forma: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)
ou criada chamando
ngx_list_create(pool, n, size)
.
Ambas as funções recebem como argumentos o tamanho de um único item e um número de
itens por parte da lista.
Para adicionar um item a uma lista, use a função ngx_list_push(list)
.
Para iterar sobre os itens, acesse diretamente os campos da lista conforme mostrado no
exemplo: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 */ }
/* add items to the list */
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");
/* iterate over the list */
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
) como ausentes, defina o campo
hash
em ngx_table_elt_t
como
zero.
Itens marcados desta forma são explicitamente ignorados quando os cabeçalhos são iterados.Fila#
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 o cabeça da lista
antes do uso.
As filas suportam as seguintes operações:ngx_queue_insert_head(h, x)
,
ngx_queue_insert_tail(h, x)
— Inserir um novo nóngx_queue_remove(x)
— Remover um nó da filangx_queue_split(h, q, n)
— Dividir uma fila em um nó,
retornando a cauda da fila em uma fila separadangx_queue_add(h, n)
— Adicionar uma segunda fila à primeira filangx_queue_head(h)
,
ngx_queue_last(h)
— Obter o primeiro ou último nó da filangx_queue_sentinel(h)
- Obter um objeto sentinela da fila para terminar
a iteraçãongx_queue_data(q, type, link)
— Obter uma referência ao
início de uma estrutura de dados do nó da fila, considerando o deslocamento do campo da fila
nelatypedef 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);
}
Árvore Rubro-Negra#
src/core/ngx_rbtree.h
fornece acesso à
implementação eficaz de árvores rubro-negras.typedef struct {
ngx_rbtree_t rbtree;
ngx_rbtree_node_t sentinel;
/* dados personalizados por árvore aqui */
} my_tree_t;
typedef struct {
ngx_rbtree_node_t rbnode;
/* dados personalizados por nó */
foo_t val;
} my_node_t;
my_tree_t root;
ngx_rbtree_init(&root.rbtree, &root.sentinel, insert_value_function);
insert_value
".
Por exemplo, a função ngx_str_rbtree_insert_value
lida
com o tipo ngx_str_t
.
Seus argumentos são ponteiros para um nó raiz de uma inserção, o nó recém-criado
a ser adicionado, e uma sentinela da árvore.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()
é uma função comparadora clássica que
retorna um valor menor que, igual a, ou maior que zero.
Para acelerar as buscas e evitar comparar objetos de usuário que podem ser grandes, um campo
hash inteiro é usado.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
.
Tanto correspondência exata quanto com curingas são suportadas.
A última requer configuração extra e é descrita em uma seção separada abaixo.max_size
e bucket_size
, conforme detalhado em um
documento separado.
Eles são geralmente configuráveis pelo usuário.
As configurações de inicialização do hash são armazenadas com o
tipo ngx_hash_init_t
, e o hash em si é
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
é um ponteiro para uma função que cria a chave
inteira do hash a partir de uma string.
Existem duas funções genéricas de criação de chave:
ngx_hash_key(data, len)
e
ngx_hash_key_lc(data, len)
.
A última converte uma string para todos os caracteres minúsculos, então a string passada
deve ser gravável.
Se isso não for verdade, passe a flag NGX_HASH_READONLY_KEY
para a função, inicializando o array de chaves (veja abaixo).ngx_hash_keys_arrays_t
e
são inicializadas com ngx_hash_keys_array_init(arr, type)
:
O segundo parâmetro (type
) controla a quantidade de recursos
pré-alocados para o hash e pode ser NGX_HASH_SMALL
ou
NGX_HASH_LARGE
.
O último é apropriado se você espera que o hash contenha milhares 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
ou
bucket_size
não forem grandes o suficiente.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) {
/* chave não encontrada */
}
Correspondência com curingas#
ngx_hash_combined_t
.
Ele inclui o tipo hash descrito acima e tem dois arrays de chaves adicionais:
dns_wc_head
e dns_wc_tail
.
A inicialização das propriedades básicas é similar a um hash regular:ngx_hash_init_t hash
ngx_hash_combined_t foo_hash;
hash.hash = &foo_hash.hash;
hash.key = ...;
NGX_HASH_WILDCARD_KEY
:/* k1 = ".example.org"; */
/* k2 = "foo.*"; */
ngx_hash_add_key(&foo_keys, &k1, &data1, NGX_HASH_WILDCARD_KEY);
ngx_hash_add_key(&foo_keys, &k2, &data2, NGX_HASH_WILDCARD_KEY);
if (foo_keys.dns_wc_head.nelts) {
ngx_qsort(foo_keys.dns_wc_head.elts,
(size_t) foo_keys.dns_wc_head.nelts,
sizeof(ngx_hash_key_t),
cmp_dns_wildcards);
hash.hash = NULL;
hash.temp_pool = pool;
if (ngx_hash_wildcard_init(&hash, foo_keys.dns_wc_head.elts,
foo_keys.dns_wc_head.nelts)
!= NGX_OK)
{
return NGX_ERROR;
}
foo_hash.wc_head = (ngx_hash_wildcard_t *) hash.hash;
}
dns_wc_tail
é feita de forma similar.ngx_hash_find_combined(chash, key, name, len)
:/* key = "bar.example.org"; - irá corresponder a ".example.org" */
/* key = "foo.example.com"; - irá corresponder a "foo.*" */
hkey = ngx_hash_key(key.data, key.len);
res = ngx_hash_find_combined(&foo_hash, hkey, key.data, key.len);
Gerenciamento de memória#
Heap#
ngx_alloc(size, log)
— Aloca memória do heap do sistema.
Este é um wrapper em torno de malloc()
com suporte a logging.
Erros de alocação e informações de depuração são registrados em log
.ngx_calloc(size, log)
— Aloca memória do heap do sistema
como ngx_alloc()
, mas preenche a memória com zeros após
a alocação.ngx_memalign(alignment, size, log)
— Aloca memória alinhada
do heap do sistema.
Este é um wrapper em torno de posix_memalign()
nas plataformas que fornecem essa função.
Caso contrário, a implementação recorre a ngx_alloc()
que
fornece alinhamento máximo.ngx_free(p)
— Libera memória alocada.
Este é um wrapper em torno de free()
Pool#
ngx_pool_t
.
As seguintes operações são suportadas:ngx_create_pool(size, log)
— Cria um pool com tamanho de
bloco especificado.
O objeto pool retornado também é alocado no pool.
O size
deve ser pelo menos NGX_MIN_POOL_SIZE
e um múltiplo de NGX_POOL_ALIGNMENT
.ngx_destroy_pool(pool)
— Libera toda a memória do pool, incluindo
o próprio objeto pool.ngx_palloc(pool, size)
— Aloca memória alinhada do
pool especificado.ngx_pcalloc(pool, size)
— Aloca memória alinhada
do pool especificado e a preenche com zeros.ngx_pnalloc(pool, size)
— Aloca memória não alinhada do
pool especificado.
Usado principalmente para alocar strings.ngx_pfree(pool, p)
— Libera memória que foi previamente
alocada no pool especificado.
Apenas alocações que resultam de solicitações encaminhadas para o alocador do sistema
podem 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
) são ativamente usados no Angie,
então a implementação de pool do Angie fornece uma maneira de reutilizá-los.
O campo chain
de ngx_pool_t
mantém uma
lista de links previamente alocados prontos para reutilização.
Para alocação eficiente de um link de cadeia em um pool, use a
função ngx_alloc_chain_link(pool)
.
Esta função procura por um link de cadeia livre na lista do pool e aloca um novo
link de cadeia se a lista do pool estiver vazia.
Para liberar um link, chame a função ngx_free_chain(pool, cl)
.ngx_pool_cleanup_add(pool, size)
, que retorna um
ponteiro ngx_pool_cleanup_t
para
ser preenchido pelo chamador.
Use o argumento size
para alocar contexto para o manipulador
de limpeza.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);
}
Memória compartilhada#
ngx_shared_memory_add(cf, name, size, tag)
adiciona
uma nova entrada de memória compartilhada ngx_shm_zone_t
a um ciclo.
A função recebe o name
e size
da zona.
Cada zona compartilhada deve ter um nome único.
Se uma entrada de zona compartilhada com o name
e
tag
fornecidos já existe, a entrada de zona existente é reutilizada.
A função falha com um erro se uma entrada existente com o mesmo nome tem uma
tag diferente.
Geralmente, o endereço da estrutura do módulo é passado como
tag
, tornando possível reutilizar zonas compartilhadas por nome dentro
de um módulo do Angie.ngx_shm_zone_t
tem os
seguintes campos:init
— Callback de inicialização, chamado após a zona compartilhada
ser mapeada para memória realdata
— Contexto de dados, usado para passar dados arbitrários para o
callback init
noreuse
— Flag que desabilita a reutilização de uma zona compartilhada do
ciclo antigotag
— Tag da zona compartilhadashm
— Objeto específico da plataforma do tipo
ngx_shm_t
, tendo pelo menos os seguintes campos:addr
— Endereço de memória compartilhada mapeada, inicialmente NULLsize
— Tamanho da memória compartilhadaname
— Nome da memória compartilhadalog
— Log da memória compartilhadaexists
— Flag que indica que a memória compartilhada foi herdada
do processo mestre (específico do Windows)ngx_init_cycle()
após a configuração ser analisada.
Em sistemas POSIX, a syscall mmap()
é usada para criar o
mapeamento anônimo compartilhado.
No Windows, o par CreateFileMapping()
/
MapViewOfFileEx()
é usado.ngx_slab_pool_t
.
Um slab pool para alocar memória é automaticamente criado em cada zona compartilhada
do Angie.
O pool está localizado no início da zona compartilhada e pode ser acessado pela
expressão (ngx_slab_pool_t *) shm_zone->shm.addr
.
Para alocar memória em uma zona compartilhada, chame
ngx_slab_alloc(pool, size)
ou
ngx_slab_calloc(pool, size)
.
Para liberar memória, chame ngx_slab_free(pool, p)
.mutex
de
ngx_slab_pool_t
.
Um mutex é mais comumente usado pelo slab pool ao alocar e liberar
memória, mas pode ser usado para proteger quaisquer outras estruturas de dados do usuário alocadas
na zona compartilhada.
Para bloquear ou desbloquear um mutex, chame
ngx_shmtx_lock(&shpool->mutex)
ou
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;
}
Logging#
ngx_log_t
.
O logger do Angie suporta vários tipos de saída:next
.
Neste caso, cada mensagem é escrita para todos os loggers na cadeia.NGX_LOG_EMERG
NGX_LOG_ALERT
NGX_LOG_CRIT
NGX_LOG_ERR
NGX_LOG_WARN
NGX_LOG_NOTICE
NGX_LOG_INFO
NGX_LOG_DEBUG
NGX_LOG_DEBUG_CORE
NGX_LOG_DEBUG_ALLOC
NGX_LOG_DEBUG_MUTEX
NGX_LOG_DEBUG_EVENT
NGX_LOG_DEBUG_HTTP
NGX_LOG_DEBUG_MAIL
NGX_LOG_DEBUG_STREAM
error_log
e estão disponíveis em quase todas as etapas
de processamento em ciclo, configuração, conexão do cliente e outros objetos.ngx_log_error(level, log, err, fmt, ...)
— Logging de errongx_log_debug0(level, log, err, fmt)
,
ngx_log_debug1(level, log, err, fmt, arg1)
etc — Logging de
debug com até oito argumentos de formatação suportadosNGX_MAX_ERROR_STR
(atualmente, 2048 bytes) na pilha.
A mensagem é precedida pelo nível de severidade, ID do processo (PID), ID da conexão
(armazenado em log->connection
), e o texto de erro do sistema.
Para mensagens não-debug, log->handler
também é chamado para
preceder informações mais específicas à mensagem de log.
O módulo HTTP define a função ngx_http_log_error()
como manipulador de log
para registrar endereços do cliente e servidor, ação atual (armazenada em
log->action
), linha de requisição do cliente, nome do servidor etc./* specify what is currently done */
log->action = "sending mp4 to client";
/* error and debug log */
ngx_log_error(NGX_LOG_INFO, c->log, 0, "client prematurely
closed connection");
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
"mp4 start:%ui, length:%ui", mp4->start, mp4->length);
2016/09/16 22:08:52 [info] 17445#0: *1 client prematurely closed connection while
sending mp4 to client, client: 127.0.0.1, server: , request: "GET /file.mp4 HTTP/1.1"
2016/09/16 23:28:33 [debug] 22140#0: *1 mp4 start:0, length:10000
Ciclo#
ngx_cycle_t
.
O ciclo atual é referenciado pela variável global ngx_cycle
e herdado pelos workers do Angie quando eles iniciam.
Cada vez que a configuração do Angie é recarregada, um novo ciclo é criado a partir da
nova configuração do Angie; o ciclo antigo é normalmente excluído após o novo ser
criado com sucesso.ngx_init_cycle()
, que
recebe o ciclo anterior como argumento.
A função localiza o arquivo de configuração do ciclo anterior e herda o máximo
de recursos possível do ciclo anterior.
Um ciclo temporário chamado "ciclo de inicialização" é criado quando o Angie inicia, e então é
substituído por um ciclo real construído a partir da configuração.pool
— Pool do ciclo.
Criado para cada novo ciclo.log
— Log do ciclo.
Inicialmente herdado do ciclo antigo, é definido para apontar para
new_log
após a configuração ser lida.new_log
— Log do ciclo, criado pela configuração.
É afetado pela diretiva error_log
de escopo raiz.connections
, connection_n
—
Array de conexões do tipo ngx_connection_t
, criado pelo
módulo de eventos durante a inicialização de cada worker do Angie.
A diretiva worker_connections
na configuração do Angie
define o número de conexões connection_n
.free_connections
,
free_connection_n
— Lista e número de conexões atualmente disponíveis.
Se não há conexões disponíveis, um worker do Angie recusa aceitar novos clientes
ou conectar a servidores upstream.files
, files_n
— Array para mapear descritores
de arquivo para conexões do Angie.
Este mapeamento é usado pelos módulos de eventos, tendo a
flag NGX_USE_FD_EVENT
(atualmente, são
poll
e devpoll
).conf_ctx
— Array de configurações de módulos principais.
As configurações são criadas e preenchidas durante a leitura dos arquivos de configuração
do Angie.modules
, modules_n
— Array de módulos
do tipo ngx_module_t
, tanto estáticos quanto dinâmicos, carregados pela
configuração atual.listening
— Array de objetos de escuta do tipo
ngx_listening_t
.
Objetos de escuta são normalmente adicionados pela diretiva listen
de diferentes módulos que chamam a função
ngx_create_listening()
.
Sockets de escuta são criados com base nos objetos de escuta.paths
— Array de caminhos do tipo ngx_path_t
.
Caminhos são adicionados chamando a função ngx_add_path()
de
módulos que vão operar em determinados diretórios.
Estes diretórios são criados pelo Angie após ler a configuração, se estiverem ausentes.
Além disso, dois manipuladores podem ser adicionados para cada caminho:open_files
— Lista de objetos de arquivo aberto do tipo
ngx_open_file_t
, que são criados chamando a função
ngx_conf_open_file()
.
Atualmente, o Angie usa este tipo de arquivos abertos para logging.
Após ler a configuração, o Angie abre todos os arquivos na
lista open_files
e armazena cada descritor de arquivo no
campo fd
do objeto.
Os arquivos são abertos em modo de anexação e são criados se estiverem ausentes.
Os arquivos na lista são reabertos pelos workers do Angie ao receber o
sinal de reabertura (mais frequentemente USR1
).
Neste caso o descritor no campo fd
é alterado para um
novo valor.shared_memory
— Lista de zonas de memória compartilhada, cada uma adicionada
chamando a função ngx_shared_memory_add()
.
Zonas compartilhadas são mapeadas para a mesma faixa de endereços em todos os processos do Angie e
são usadas para compartilhar dados comuns, por exemplo a árvore em memória do cache HTTP.Buffer#
ngx_buf_t
.
Normalmente, é usado para armazenar dados a serem escritos em um destino ou lidos de uma
fonte.
Um buffer pode referenciar dados na memória ou em um arquivo e é tecnicamente
possível para um buffer referenciar ambos ao mesmo tempo.
A memória para o buffer é alocada separadamente e não está relacionada à estrutura
do buffer ngx_buf_t
.ngx_buf_t
tem os seguintes campos:start
, end
— Os limites do bloco de memória
alocado para o buffer.pos
, last
— Os limites do buffer de memória;
normalmente um subintervalo de start
..
end
.file_pos
, file_last
— Os limites de um
buffer de arquivo, expressos como deslocamentos do início do arquivo.tag
— Valor único usado para distinguir buffers; criado por
diferentes módulos do Angie, normalmente para fins de reutilização de buffer.file
— Objeto de arquivo.temporary
— Flag indicando que o buffer referencia
memória gravável.memory
— Flag indicando que o buffer referencia memória
somente leitura.in_file
— Flag indicando que o buffer referencia dados
em um arquivo.flush
— Flag indicando que todos os dados anteriores ao buffer
precisam ser liberados.recycled
— Flag indicando que o buffer pode ser reutilizado e
precisa ser consumido o mais rápido possível.sync
— Flag indicando que o buffer não carrega dados ou
sinal especial como flush
ou last_buf
.
Por padrão o Angie considera tais buffers uma condição de erro, mas esta flag diz
ao Angie para pular a verificação de erro.last_buf
— Flag indicando que o buffer é o último na
saída.last_in_chain
— Flag indicando que não há mais buffers de dados
em uma requisição ou sub-requisição.shadow
— Referência a outro buffer ("sombra") relacionado ao
buffer atual, normalmente no sentido de que o buffer usa dados da
sombra.
Quando o buffer é consumido, o buffer sombra é normalmente também marcado como
consumido.last_shadow
— Flag indicando que o buffer é o último
que referencia um buffer sombra específico.temp_file
— Flag indicando que o buffer está em um arquivo
temporário.ngx_chain_t
,
definida como segue: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;
}
Rede#
Conexão#
ngx_connection_t
é um wrapper em torno de um
descritor de socket.
Ele inclui os seguintes campos:fd
— Descritor de socketdata
— Contexto de conexão arbitrário.
Normalmente, é um ponteiro para um objeto de nível superior construído sobre a
conexão, como uma requisição HTTP ou uma sessão Stream.read
, write
— Eventos de leitura e escrita para
a conexão.recv
, send
,
recv_chain
, send_chain
— Operações de I/O
para a conexão.pool
— Pool da conexão.log
— Log da conexão.sockaddr
, socklen
,
addr_text
— Endereço do socket remoto em formas binária e texto.local_sockaddr
, local_socklen
— Endereço
do socket local em forma binária.
Inicialmente, esses campos estão vazios.
Use a função ngx_connection_local_sockaddr()
para obter o
endereço do socket local.proxy_protocol_addr
, proxy_protocol_port
- Endereço e porta do cliente do protocolo PROXY, se o protocolo PROXY estiver habilitado para
a conexão.ssl
— Contexto SSL para a conexão.reusable
— Flag indicando que a conexão está em um estado que
a torna elegível para reutilização.close
— Flag indicando que a conexão está sendo reutilizada
e precisa ser fechada.ssl
da conexão mantém um ponteiro para uma
estrutura ngx_ssl_connection_t
, mantendo todos os dados
relacionados ao SSL para a conexão, incluindo SSL_CTX
e
SSL
.
Os manipuladores recv
, send
,
recv_chain
e send_chain
também são
definidos para funções habilitadas para SSL.worker_connections
na configuração do Angie
limita o número de conexões por worker do Angie.
Todas as estruturas de conexão são pré-criadas quando um worker inicia e armazenadas no
campo connections
do objeto cycle.
Para recuperar uma estrutura de conexão, use a
função ngx_get_connection(s, log)
.
Ela recebe como argumento s
um descritor de socket, que precisa
ser envolvido em uma estrutura de conexão.ngx_reusable_connection(c, reusable)
.
Chamar ngx_reusable_connection(c, 1)
define a
flag reuse
na estrutura de conexão e insere a
conexão na reusable_connections_queue
do cycle.
Sempre que ngx_get_connection()
descobre que não há
conexões disponíveis na lista free_connections
do cycle,
ela chama ngx_drain_connections()
para liberar um
número específico de conexões reutilizáveis.
Para cada conexão desse tipo, a flag close
é definida e seu manipulador de leitura
é chamado, que deve liberar a conexão chamando
ngx_close_connection(c)
e torná-la disponível para reutilização.
Para sair do estado quando uma conexão pode ser reutilizada,
ngx_reusable_connection(c, 0)
é chamada.
Conexões de cliente HTTP são um exemplo de conexões reutilizáveis no Angie; elas
são marcadas como reutilizáveis até que o primeiro byte de requisição seja recebido do cliente.Eventos#
Evento#
ngx_event_t
no Angie fornece um mecanismo
para notificação de que um evento específico ocorreu.ngx_event_t
incluem:data
— Contexto de evento arbitrário usado em manipuladores de eventos,
geralmente como ponteiro para uma conexão relacionada ao evento.handler
— Função de callback a ser invocada quando o evento
acontece.write
— Flag indicando um evento de escrita.
A ausência da flag indica um evento de leitura.active
— Flag indicando que o evento está registrado para
receber notificações de I/O, normalmente de mecanismos de notificação como
epoll
, kqueue
, poll
.ready
— Flag indicando que o evento recebeu uma
notificação de I/O.delayed
— Flag indicando que o I/O está atrasado devido à limitação de taxa.timer
— Nó da árvore rubro-negra para inserir o evento na
árvore de timer.timer_set
— Flag indicando que o timer do evento está definido e
ainda não expirou.timedout
— Flag indicando que o timer do evento expirou.eof
— Flag indicando que EOF ocorreu durante a leitura de dados.pending_eof
— Flag indicando que EOF está pendente no
socket, mesmo que possa haver alguns dados disponíveis antes dele.
A flag é entregue via o evento EPOLLRDHUP
epoll
ou
flag EV_EOF
kqueue
.error
— Flag indicando que um erro ocorreu durante
a leitura (para um evento de leitura) ou escrita (para um evento de escrita).cancelable
— Flag de evento de timer indicando que o evento
deve ser ignorado durante o desligamento do worker.
O desligamento gracioso do worker é atrasado até que não haja eventos de timer não canceláveis
agendados.posted
— Flag indicando que o evento está postado em uma fila.queue
— Nó da fila para postar o evento em uma fila.Eventos de I/O#
ngx_get_connection()
tem dois eventos anexados, c->read
e
c->write
, que são usados para receber notificação de que o
socket está pronto para leitura ou escrita.
Todos esses eventos operam no modo Edge-Triggered, significando que eles apenas disparam
notificações quando o estado do socket muda.
Por exemplo, fazer uma leitura parcial em um socket não faz o Angie entregar uma
notificação de leitura repetida até que mais dados cheguem no socket.
Mesmo quando o mecanismo de notificação de I/O subjacente é essencialmente
Level-Triggered (poll
, select
etc), o Angie
converte as notificações para Edge-Triggered.
Para tornar as notificações de eventos do Angie consistentes em todos os sistemas de notificação
em diferentes plataformas, as funções
ngx_handle_read_event(rev, flags)
e
ngx_handle_write_event(wev, lowat)
devem ser chamadas após
manipular uma notificação de socket de I/O ou chamar qualquer função de I/O nesse socket.
Normalmente, as funções são chamadas uma vez no final de cada manipulador de evento de leitura ou escrita.Eventos de timer#
ngx_msec_t
.
Seu valor atual pode ser obtido da variável ngx_current_msec
.ngx_add_timer(ev, timer)
define um timeout para um
evento, ngx_del_timer(ev)
exclui um timeout previamente definido.
A árvore rubro-negra global de timeout ngx_event_timer_rbtree
armazena todos os timeouts atualmente definidos.
A chave na árvore é do tipo ngx_msec_t
e é o tempo
quando o evento ocorre.
A estrutura da árvore permite operações rápidas de inserção e exclusão, bem como
acesso aos timeouts mais próximos, que o Angie usa para descobrir quanto tempo esperar
por eventos de I/O e para expirar eventos de timeout.Eventos postados#
ngx_post_event(ev, q)
posta o evento
ev
na fila de postagem q
.
A macro ngx_delete_posted_event(ev)
exclui o evento
ev
da fila em que está atualmente postado.
Normalmente, eventos são postados na fila ngx_posted_events
,
que é processada tarde no loop de eventos — após todos os eventos de I/O e timer
já terem sido manipulados.
A função ngx_event_process_posted()
é chamada para processar
uma fila de eventos.
Ela chama manipuladores de eventos até que a fila esteja vazia.
Isso significa que um manipulador de evento postado pode postar mais eventos para serem processados
dentro da iteração atual do loop 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 */ }
}
Loop de eventos#
sigsuspend()
aguardando a chegada de sinais.)
O loop de eventos do Angie é implementado na
função ngx_process_events_and_timers()
, que é chamada
repetidamente até que o processo termine.ngx_event_find_timer()
.
Esta função encontra o nó mais à esquerda na árvore de timers e retorna o
número de milissegundos até que o nó expire.ready
é definida e o manipulador do evento é chamado.
Para Linux, o manipulador ngx_epoll_process_events()
é normalmente usado, que chama epoll_wait()
para aguardar eventos de I/O.ngx_event_expire_timers()
.
A árvore de timers é iterada do elemento mais à esquerda para a direita até que um
timeout não expirado seja encontrado.
Para cada nó expirado, a flag de evento timedout
é definida,
a flag timer_set
é resetada, e o manipulador de evento é chamadongx_event_process_posted()
.
A função remove repetidamente o primeiro elemento da fila de eventos
postados e chama o manipulador do elemento, até que a fila esteja vazia.ngx_process_events_and_timers()
.Processos#
ngx_process
e é um dos seguintes:NGX_PROCESS_MASTER
— O processo master, que lê a
configuração do NGINX, cria ciclos, e inicia e controla processos filhos.
Ele não executa nenhum I/O e responde apenas a sinais.
Sua função de ciclo é ngx_master_process_cycle()
.NGX_PROCESS_WORKER
— O processo worker, que manipula conexões
de clientes.
Ele é iniciado pelo processo master e responde aos seus sinais e comandos
de canal também.
Sua função de ciclo é ngx_worker_process_cycle()
.
Pode haver múltiplos processos worker, conforme configurado pela
diretiva worker_processes
.NGX_PROCESS_SINGLE
— O processo único, que existe apenas no
modo master_process off
, e é o único processo executando
nesse modo.
Ele cria ciclos (como o processo master faz) e manipula conexões de clientes
(como o processo worker faz).
Sua função de ciclo é ngx_single_process_cycle()
.NGX_PROCESS_HELPER
— O processo auxiliar, do qual atualmente
existem dois tipos: gerenciador de cache e carregador de cache.
A função de ciclo para ambos é
ngx_cache_manager_process_cycle()
.NGX_SHUTDOWN_SIGNAL
(SIGQUIT
na maioria dos
sistemas) — Desligamento gracioso.
Ao receber este sinal, o processo master envia um sinal de desligamento para todos
os processos filhos.
Quando não restam processos filhos, o master destrói o pool de ciclo e sai.
Quando um processo worker recebe este sinal, ele fecha todos os sockets de escuta e
aguarda até que não haja eventos não canceláveis agendados, então destrói o
pool de ciclo e sai.
Quando o gerenciador de cache ou o processo carregador de cache recebe este sinal, ele
sai imediatamente.
A variável ngx_quit
é definida como 1
quando um
processo recebe este sinal, e é imediatamente resetada após ser processada.
A variável ngx_exiting
é definida como 1
enquanto
um processo worker está no estado de desligamento.NGX_TERMINATE_SIGNAL
(SIGTERM
na maioria dos
sistemas) — Terminar.
Ao receber este sinal, o processo master envia um sinal de terminação para todos
os processos filhos.
Se um processo filho não sair dentro de 1 segundo, o processo master envia o
sinal SIGKILL
para matá-lo.
Quando não restam processos filhos, o processo master destrói o pool de ciclo e
sai.
Quando um processo worker, o processo gerenciador de cache ou o processo carregador de cache
recebe este sinal, ele destrói o pool de ciclo e sai.
A variável ngx_terminate
é definida como 1
quando este sinal é recebido.NGX_NOACCEPT_SIGNAL
(SIGWINCH
na maioria dos
sistemas) - Desligar todos os processos worker e auxiliares.
Ao receber este sinal, o processo master desliga seus processos filhos.
Se um binário do Angie novo iniciado anteriormente sair, os processos filhos do
master antigo são iniciados novamente.
Quando um processo worker recebe este sinal, ele desliga no modo de debug
definido pela diretiva debug_points
.NGX_RECONFIGURE_SIGNAL
(SIGHUP
na maioria dos
sistemas) - Reconfigurar.
Ao receber este sinal, o processo master relê a configuração e
cria um novo ciclo baseado nela.
Se o novo ciclo for criado com sucesso, o ciclo antigo é deletado e novos
processos filhos são iniciados.
Enquanto isso, os processos filhos antigos recebem o
sinal NGX_SHUTDOWN_SIGNAL
.
No modo de processo único, o Angie cria um novo ciclo, mas mantém o antigo até
que não haja mais clientes com conexões ativas vinculadas a ele.
Os processos worker e auxiliares ignoram este sinal.NGX_REOPEN_SIGNAL
(SIGUSR1
na maioria dos
sistemas) — Reabrir arquivos.
O processo master envia este sinal para os workers, que reabrem todos os
open_files
relacionados ao ciclo.NGX_CHANGEBIN_SIGNAL
(SIGUSR2
na maioria dos
sistemas) — Alterar o binário do Angie.
O processo master inicia um novo binário do Angie e passa uma lista de todos os sockets
de escuta.
A lista em formato texto, passada na variável de ambiente "NGINX"
,
consiste em números de descritores separados por ponto e vírgula.
O novo binário do Angie lê a variável "NGINX"
e adiciona os
sockets ao seu ciclo de inicialização.
Outros processos ignoram este sinal.kill()
padrão para passar sinais para workers e auxiliares.
Em vez disso, o Angie usa pares de sockets entre processos que permitem enviar mensagens
entre todos os processos do Angie.
Atualmente, no entanto, as mensagens são enviadas apenas do master para seus filhos.
As mensagens carregam os sinais padrão.Threads#
pthreads
estão disponíveis: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
contém
definições 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 ou cria um
novo pool de threads com o name
fornecido ou retorna uma referência
para o pool com esse nome se ele já existir.task
em uma fila de um pool de threads especificado
tp
em tempo de execução, use a
função ngx_thread_task_post(tp, task)
.ngx_thread_task_t
:typedef struct {
int foo;
} my_thread_ctx_t;
static void
my_thread_func(void *data, ngx_log_t *log)
{
my_thread_ctx_t *ctx = data;
/* esta função é executada em uma thread separada */
}
static void
my_thread_completion(ngx_event_t *ev)
{
my_thread_ctx_t *ctx = ev->data;
/* executada no loop de eventos do Angie */
}
ngx_int_t
my_task_offload(my_conf_t *conf)
{
my_thread_ctx_t *ctx;
ngx_thread_task_t *task;
task = ngx_thread_task_alloc(conf->pool, sizeof(my_thread_ctx_t));
if (task == NULL) {
return NGX_ERROR;
}
ctx = task->ctx;
ctx->foo = 42;
task->handler = my_thread_func;
task->event.handler = my_thread_completion;
task->event.data = ctx;
if (ngx_thread_task_post(conf->thread_pool, task) != NGX_OK) {
return NGX_ERROR;
}
return NGX_OK;
}
Módulos#
Adicionando novos módulos#
config
e um arquivo com o código-fonte do módulo.
O arquivo config
contém todas as informações necessárias para o Angie
integrar o módulo, por exemplo: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
é um script shell POSIX que pode definir
e acessar as seguintes variáveis:ngx_module_type
— Tipo de módulo a ser construído.
Os valores possíveis são CORE
, HTTP
,
HTTP_FILTER
, HTTP_INIT_FILTER
,
HTTP_AUX_FILTER
, MAIL
,
STREAM
, ou MISC
.ngx_module_name
— Nomes dos módulos.
Para construir múltiplos módulos a partir de um conjunto de arquivos-fonte, especifique uma
lista de nomes separados por espaços em branco.
O primeiro nome indica o nome do binário de saída para o módulo dinâmico.
Os nomes na lista devem corresponder aos nomes usados no código-fonte.ngx_addon_name
— Nome do módulo como aparece na saída
no console do script de configuração.ngx_module_srcs
— Lista separada por espaços em branco de arquivos-fonte
usados para compilar o módulo.
A variável $ngx_addon_dir
pode ser usada para representar o caminho
para o diretório do módulo.ngx_module_incs
— Caminhos de inclusão necessários para construir o módulongx_module_deps
— Lista separada por espaços em branco das
dependências do módulo.
Geralmente, é a lista de arquivos de cabeçalho.ngx_module_libs
— Lista separada por espaços em branco de bibliotecas para
vincular com o módulo.
Por exemplo, use ngx_module_libs=-lpthread
para vincular
a biblioteca libpthread
.
As seguintes macros podem ser usadas para vincular às mesmas bibliotecas que o
Angie:
LIBXSLT
, LIBGD
, GEOIP
,
PCRE
, OPENSSL
, MD5
,
SHA1
, ZLIB
, e PERL
.ngx_module_link
— Variável definida pelo sistema de construção para
DYNAMIC
para um módulo dinâmico ou ADDON
para um módulo estático e usada para determinar diferentes ações a serem executadas
dependendo do tipo de vinculação.ngx_module_order
— Ordem de carregamento para o módulo;
útil para os tipos de módulo HTTP_FILTER
e
HTTP_AUX_FILTER
.
O formato para esta opção é uma lista separada por espaços em branco de módulos.
Todos os módulos na lista seguindo o nome do módulo atual ficam depois dele na
lista global de módulos, que define a ordem para inicialização dos módulos.
Para módulos de filtro, inicialização posterior significa execução anterior.ngx_http_copy_filter_module
lê os dados para outros
módulos de filtro e é colocado próximo ao final da lista para que seja um dos
primeiros a ser executado.
O ngx_http_write_filter_module
escreve os dados para o
socket do cliente e é colocado próximo ao topo da lista, sendo o último a ser
executado.ngx_http_copy_filter
na lista de módulos para que o
manipulador de filtro seja executado após o manipulador do filtro de cópia.
Para outros tipos de módulo, o padrão é a string vazia.--add-module=/path/to/module
para o script
de configuração.
Para compilar um módulo para carregamento dinâmico posterior no Angie, use o
argumento --add-dynamic-module=/path/to/module
.Módulos Principais#
ngx_module_t
, que é definida da seguinte forma:struct ngx_module_s {
/* parte privada é omitida */
void *ctx;
ngx_command_t *commands;
ngx_uint_t type;
ngx_int_t (*init_master)(ngx_log_t *log);
ngx_int_t (*init_module)(ngx_cycle_t *cycle);
ngx_int_t (*init_process)(ngx_cycle_t *cycle);
ngx_int_t (*init_thread)(ngx_cycle_t *cycle);
void (*exit_thread)(ngx_cycle_t *cycle);
void (*exit_process)(ngx_cycle_t *cycle);
void (*exit_master)(ngx_cycle_t *cycle);
/* stubs para extensões futuras são omitidos */
};
NGX_MODULE_V1
.ctx
,
reconhece as diretivas de configuração, especificadas no
array commands
, e pode ser invocado em certas etapas do
ciclo de vida do Angie.
O ciclo de vida do módulo consiste nos seguintes eventos:init_module
é chamado no contexto do processo mestre.
O manipulador init_module
é chamado no processo mestre cada
vez que uma configuração é carregada.init_process
é chamado em cada um deles.exit_process
.exit_master
antes
de sair.init_thread
e exit_thread
não são chamados atualmente.
Também não há manipulador init_master
, porque seria
sobrecarga desnecessária.type
do módulo define exatamente o que é armazenado no
campo ctx
.
Seu valor é um dos seguintes tipos:NGX_CORE_MODULE
NGX_EVENT_MODULE
NGX_HTTP_MODULE
NGX_MAIL_MODULE
NGX_STREAM_MODULE
NGX_CORE_MODULE
é o tipo de módulo mais básico e, portanto, o mais
genérico e de mais baixo nível.
Os outros tipos de módulo são implementados sobre ele e fornecem uma
maneira mais conveniente de lidar com domínios correspondentes, como manipular eventos ou
requisições HTTP.ngx_core_module
,
ngx_errlog_module
, ngx_regex_module
,
ngx_thread_pool_module
e
ngx_openssl_module
.
O módulo HTTP, o módulo stream, o módulo mail e módulos de evento também são módulos
principais.
O contexto de um módulo principal é definido 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
é uma string com o nome do módulo,
create_conf
e init_conf
são ponteiros para funções que criam e inicializam a configuração do módulo
respectivamente.
Para módulos principais, o Angie chama create_conf
antes de analisar
uma nova configuração e init_conf
após toda a configuração
ser analisada com sucesso.
A função create_conf
típica aloca memória para a
configuração e define valores padrão.ngx_foo_module
poderia
parecer assim:/*
* 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;
}
Diretivas de Configuração#
ngx_command_t
define uma única diretiva de configuração.
Cada módulo que suporta configuração fornece um array de tais estruturas
que descrevem como processar argumentos e quais manipuladores chamar: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
.
O name
é o nome de uma diretiva como ela aparece
no arquivo de configuração, por exemplo "worker_processes" ou "listen".
O type
é um campo de bits de flags que especifica o número de
argumentos que a diretiva aceita, seu tipo, e o contexto no qual ela aparece.
As flags são:NGX_CONF_NOARGS
— A diretiva não aceita argumentos.NGX_CONF_1MORE
— A diretiva aceita um ou mais argumentos.NGX_CONF_2MORE
— A diretiva aceita dois ou mais argumentos.NGX_CONF_TAKE1
..:samp:NGX_CONF_TAKE7 —
A diretiva aceita exatamente o número indicado de argumentos.NGX_CONF_TAKE12
, NGX_CONF_TAKE13
,
NGX_CONF_TAKE23
, NGX_CONF_TAKE123
,
NGX_CONF_TAKE1234
— A diretiva pode aceitar diferentes números de
argumentos.
As opções são limitadas aos números dados.
Por exemplo, NGX_CONF_TAKE12
significa que aceita um ou dois
argumentos.NGX_CONF_BLOCK
— A diretiva é um bloco, ou seja, pode
conter outras diretivas dentro de suas chaves de abertura e fechamento, ou até mesmo
implementar seu próprio parser para lidar com conteúdo interno.NGX_CONF_FLAG
— A diretiva aceita um valor booleano, seja
on
ou off
.NGX_MAIN_CONF
— No contexto de nível superior.NGX_HTTP_MAIN_CONF
— No bloco http
.NGX_HTTP_SRV_CONF
— Em um bloco server
dentro do bloco http
.NGX_HTTP_LOC_CONF
— Em um bloco location
dentro do bloco http
.NGX_HTTP_UPS_CONF
— Em um bloco upstream
dentro do bloco http
.NGX_HTTP_SIF_CONF
— Em um bloco if
dentro
de um bloco server
no bloco http
.NGX_HTTP_LIF_CONF
— Em um bloco if
dentro
de um bloco location
no bloco http
.NGX_HTTP_LMT_CONF
— Em um bloco limit_except
dentro do bloco http
.NGX_STREAM_MAIN_CONF
— No bloco stream
.NGX_STREAM_SRV_CONF
— Em um bloco server
dentro do bloco stream
.NGX_STREAM_UPS_CONF
— Em um bloco upstream
dentro do bloco stream
.NGX_MAIL_MAIN_CONF
— No bloco mail
.NGX_MAIL_SRV_CONF
— Em um bloco server
dentro do bloco mail
.NGX_EVENT_CONF
— No bloco event
.NGX_DIRECT_CONF
— Usado por módulos que não
criam uma hierarquia de contextos e têm apenas uma configuração global.
Esta configuração é passada para o manipulador como o argumento conf
.set
define um manipulador que processa uma diretiva
e armazena valores analisados na configuração correspondente.
Há várias funções que realizam conversões comuns:ngx_conf_set_flag_slot
— Converte as strings literais
on
e off
em um
valor ngx_flag_t
com valores 1 ou 0, respectivamente.ngx_conf_set_str_slot
— Armazena uma string como um valor do
tipo ngx_str_t
.ngx_conf_set_str_array_slot
— Adiciona um valor a um array
ngx_array_t
de strings ngx_str_t
.
O array é criado se ainda não existir.ngx_conf_set_keyval_slot
— Adiciona um par chave-valor a um
array ngx_array_t
de pares chave-valor
ngx_keyval_t
.
A primeira string torna-se a chave e a segunda o valor.
O array é criado se ainda não existir.ngx_conf_set_num_slot
— Converte um argumento da diretiva
para um valor ngx_int_t
.ngx_conf_set_size_slot
— Converte um
tamanho para um valor size_t
expresso em bytes.ngx_conf_set_off_slot
— Converte um
offset para um valor off_t
expresso em bytes.ngx_conf_set_msec_slot
— Converte um
tempo para um valor ngx_msec_t
expresso em milissegundos.ngx_conf_set_sec_slot
— Converte um
tempo para um valor time_t
expresso em segundos.ngx_conf_set_bufs_slot
— Converte os dois argumentos fornecidos
em um objeto ngx_bufs_t
que contém o número e
tamanho dos buffers.ngx_conf_set_enum_slot
— Converte o argumento fornecido
em um valor ngx_uint_t
.
O array terminado em null de ngx_conf_enum_t
passado no
campo post
define as strings aceitáveis e os valores
inteiros correspondentes.ngx_conf_set_bitmask_slot
— Converte os argumentos fornecidos
em um valor ngx_uint_t
.
Os valores de máscara para cada argumento são combinados com OR produzindo o resultado.
O array terminado em null de ngx_conf_bitmask_t
passado no
campo post
define as strings aceitáveis e os valores
de máscara correspondentes.set_path_slot
— Converte os argumentos fornecidos para um
valor ngx_path_t
e realiza todas as inicializações necessárias.
Para detalhes, veja a documentação da diretiva
proxy_temp_path.set_access_slot
— Converte os argumentos fornecidos para uma máscara
de permissões de arquivo.
Para detalhes, veja a documentação da diretiva
proxy_store_access.conf
define qual estrutura de configuração é
passada para o manipulador de diretório.
Módulos principais têm apenas a configuração global e definem
a flag NGX_DIRECT_CONF
para acessá-la.
Módulos como HTTP, Stream ou Mail criam hierarquias de configurações.
Por exemplo, a configuração de um módulo é criada para escopos server
,
location
e if
.NGX_HTTP_MAIN_CONF_OFFSET
— Configuração para o
bloco http
.NGX_HTTP_SRV_CONF_OFFSET
— Configuração para um
bloco server
dentro do bloco http
.NGX_HTTP_LOC_CONF_OFFSET
— Configuração para um
bloco location
dentro do http
.NGX_STREAM_MAIN_CONF_OFFSET
— Configuração para o
bloco stream
.NGX_STREAM_SRV_CONF_OFFSET
— Configuração para um
bloco server
dentro do bloco stream
.NGX_MAIL_MAIN_CONF_OFFSET
— Configuração para o
bloco mail
.NGX_MAIL_SRV_CONF_OFFSET
— Configuração para um
bloco server
dentro do bloco mail
.offset
define o deslocamento de um campo em uma estrutura
de configuração de módulo que contém valores para esta diretiva específica.
O uso típico é empregar a macro offsetof()
.post
tem dois propósitos: pode ser usado para definir
um manipulador a ser chamado após o manipulador principal ter completado, ou para passar
dados adicionais para o manipulador principal.
No primeiro caso, a estrutura ngx_conf_post_t
precisa ser
inicializada com um ponteiro para o manipulador, por exemplo: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
é o próprio objeto ngx_conf_post_t
,
e o data
é um ponteiro para o valor,
convertido dos argumentos pelo manipulador principal com o tipo apropriado.HTTP#
Conexão#
ngx_event_accept()
aceita uma conexão TCP do cliente.
Este manipulador é chamado em resposta a uma notificação de leitura em um socket de escuta.
Um novo objeto ngx_connection_t
é criado nesta etapa
para encapsular o socket do cliente recém-aceito.
Cada ouvinte do Angie fornece um manipulador para passar o novo objeto de conexão.
Para conexões HTTP é ngx_http_init_connection(c)
.ngx_http_init_connection()
executa a inicialização inicial da
conexão HTTP.
Nesta etapa um objeto ngx_http_connection_t
é criado para
a conexão e sua referência é armazenada no campo
data
da conexão.
Posteriormente será substituído por um objeto de requisição HTTP.
Um analisador de protocolo PROXY e o handshake SSL são iniciados
nesta etapa também.ngx_http_wait_request_handler()
é chamado quando dados estão disponíveis no socket do cliente.
Nesta etapa um objeto de requisição HTTP ngx_http_request_t
é
criado e definido no campo data
da conexão.ngx_http_process_request_line()
lê a linha de requisição do cliente.
O manipulador é definido por ngx_http_wait_request_handler()
.
Os dados são lidos no buffer
da conexão.
O tamanho do buffer é inicialmente definido pela diretiva
client_header_buffer_size.
Todo o cabeçalho do cliente deve caber no buffer.
Se o tamanho inicial não for suficiente, um buffer maior é alocado,
com a capacidade definida pela diretiva large_client_header_buffers.ngx_http_process_request_headers()
,
é definido após ngx_http_process_request_line()
para ler
o cabeçalho da requisição do cliente.ngx_http_core_run_phases()
é chamado quando o cabeçalho da requisição
é completamente lido e analisado.
Esta função executa as fases da requisição de
NGX_HTTP_POST_READ_PHASE
até
NGX_HTTP_CONTENT_PHASE
.
A última fase destina-se a gerar uma resposta e passá-la pela cadeia de filtros.
A resposta não é necessariamente enviada ao cliente nesta fase.
Pode permanecer em buffer e ser enviada na etapa de finalização.ngx_http_finalize_request()
é geralmente chamado quando a
requisição gerou toda a saída ou produziu um erro.
No último caso, uma página de erro apropriada é procurada e usada como
resposta.
Se a resposta não foi completamente enviada ao cliente neste ponto, um
escritor HTTP ngx_http_writer()
é ativado para terminar
de enviar os dados pendentes.ngx_http_finalize_connection()
é chamado quando a resposta
completa foi enviada ao cliente e a requisição pode ser destruída.
Se o recurso keepalive da conexão do cliente estiver habilitado,
ngx_http_set_keepalive()
é chamado, que destrói a
requisição atual e aguarda a próxima requisição na conexão.
Caso contrário, ngx_http_close_request()
destrói tanto a
requisição quanto a conexão.Requisição#
ngx_http_request_t
é
criado. Alguns dos campos deste objeto são:connection
— Ponteiro para um objeto de conexão do cliente
ngx_connection_t
.
Várias requisições podem referenciar o mesmo objeto de conexão ao mesmo tempo -
uma requisição principal e suas sub-requisições.
Após uma requisição ser excluída, uma nova requisição pode ser criada na mesma conexão.data
de
ngx_connection_t
aponta de volta para a requisição.
Tais requisições são chamadas ativas, em oposição às outras requisições vinculadas à
conexão.
Uma requisição ativa é usada para manipular eventos de conexão do cliente e tem permissão para
enviar sua resposta ao cliente.
Normalmente, cada requisição torna-se ativa em algum ponto para que possa enviar sua
saída.ctx
— Array de contextos de módulos HTTP.
Cada módulo do tipo NGX_HTTP_MODULE
pode armazenar qualquer valor
(normalmente, um ponteiro para uma estrutura) na requisição.
O valor é armazenado no array ctx
na posição
ctx_index
do módulo.
As seguintes macros fornecem uma maneira conveniente de obter e definir contextos de requisição:ngx_http_get_module_ctx(r, module)
— Retorna
o contexto do module
ngx_http_set_ctx(r, c, module)
— Define c
como o contexto do module
main_conf
, srv_conf
,
loc_conf
— Arrays das configurações da requisição
atual.
As configurações são armazenadas nas posições ctx_index
do módulo.read_event_handler
, write_event_handler
-
Manipuladores de eventos de leitura e escrita para a requisição.
Normalmente, tanto os manipuladores de eventos de leitura quanto de escrita para uma conexão HTTP
são definidos como ngx_http_request_handler()
.
Esta função chama os manipuladores read_event_handler
e
write_event_handler
para a requisição
atualmente ativa.cache
— Objeto de cache da requisição para armazenar em cache a
resposta upstream.upstream
— Objeto upstream da requisição para proxy.pool
— Pool da requisição.
O próprio objeto de requisição é alocado neste pool, que é destruído quando
a requisição é excluída.
Para alocações que precisam estar disponíveis durante toda a vida útil da conexão do cliente,
use o pool de ngx_connection_t
em vez disso.header_in
— Buffer no qual o cabeçalho da requisição HTTP
do cliente é lido.headers_in
, headers_out
— Objetos de cabeçalhos
HTTP de entrada e saída.
Ambos os objetos contêm o campo headers
do tipo
ngx_list_t
para manter a lista bruta de cabeçalhos.
Além disso, cabeçalhos específicos estão disponíveis para obtenção e definição como
campos separados, por exemplo content_length_n
,
status
etc.request_body
— Objeto do corpo da requisição do cliente.start_sec
, start_msec
— Ponto no tempo quando
a requisição foi criada, usado para rastrear a duração da requisição.method
, method_name
— Representação numérica e textual
do método de requisição HTTP do cliente.
Valores numéricos para métodos são definidos em
src/http/ngx_http_request.h
com as macros
NGX_HTTP_GET
, NGX_HTTP_HEAD
,
NGX_HTTP_POST
, etc.http_protocol
— Versão do protocolo HTTP do cliente em sua
forma textual original ("HTTP/1.0", "HTTP/1.1" etc).http_version
— Versão do protocolo HTTP do cliente em
forma numérica (NGX_HTTP_VERSION_10
,
NGX_HTTP_VERSION_11
, etc.).http_major
, http_minor
— Versão do protocolo HTTP
do cliente em forma numérica dividida em partes maior e menor.request_line
, unparsed_uri
— Linha de requisição
e URI na requisição original do cliente.uri
, args
, exten
—
URI, argumentos e extensão de arquivo para a requisição atual.
O valor do URI aqui pode diferir do URI original enviado pelo cliente devido à
normalização.
Durante o processamento da requisição, esses valores podem mudar conforme redirecionamentos internos
são executados.main
— Ponteiro para um objeto de requisição principal.
Este objeto é criado para processar uma requisição HTTP do cliente, em oposição a
sub-requisições, que são criadas para executar uma subtarefa específica dentro da requisição principal.parent
— Ponteiro para a requisição pai de uma sub-requisição.postponed
— Lista de buffers de saída e sub-requisições, na
ordem em que são enviados e criados.
A lista é usada pelo filtro postpone para fornecer saída consistente da requisição
quando partes dela são criadas por sub-requisições.post_subrequest
— Ponteiro para um manipulador com o contexto
a ser chamado quando uma sub-requisição é finalizada.
Não usado para requisições principais.posted_requests
— Lista de requisições a serem iniciadas ou
retomadas, o que é feito chamando o
write_event_handler
da requisição.
Normalmente, este manipulador contém a função principal da requisição, que primeiro executa
as fases da requisição e depois produz a saída.ngx_http_post_request(r, NULL)
.
É sempre postada na lista posted_requests
da requisição principal.
A função ngx_http_run_posted_requests(c)
executa todas
as requisições que são postadas na requisição principal da
requisição ativa da conexão passada.
Todos os manipuladores de eventos chamam ngx_http_run_posted_requests
,
que pode levar a novas requisições postadas.
Normalmente, é chamado após invocar um manipulador de leitura ou escrita de uma requisição.phase_handler
— Índice da fase atual da requisição.ncaptures
, captures
,
captures_data
— Capturas de regex produzidas
pela última correspondência de regex da requisição.
Uma correspondência de regex pode ocorrer em vários lugares durante o processamento da requisição:
busca em mapa, busca de servidor por SNI ou Host HTTP, rewrite, proxy_redirect, etc.
Capturas produzidas por uma busca são armazenadas nos campos mencionados acima.
O campo ncaptures
contém o número de capturas,
captures
contém os limites das capturas e
captures_data
contém a string contra a qual o regex foi
correspondido e que é usada para extrair capturas.
Após cada nova correspondência de regex, as capturas da requisição são redefinidas para conter novos valores.count
— Contador de referência da requisição.
O campo só faz sentido para a requisição principal.
Aumentar o contador é feito por simples r->main->count++
.
Para diminuir o contador, chame
ngx_http_finalize_request(r, rc)
.
A criação de uma sub-requisição e a execução do processo de leitura do corpo da requisição ambos
incrementam o contador.subrequests
— Nível atual de aninhamento de subrequisições.
Cada subrequisição herda o nível de aninhamento de seu pai, diminuído em um.
Um erro é gerado se o valor chegar a zero.
O valor para a requisição principal é definido pela
constante NGX_HTTP_MAX_SUBREQUESTS
.uri_changes
— Número de mudanças de URI restantes para
a requisição.
O número total de vezes que uma requisição pode alterar seu URI é limitado pela
constante NGX_HTTP_MAX_URI_CHANGES
.
A cada mudança o valor é decrementado até chegar a zero, momento em que
um erro é gerado.
Reescritas e redirecionamentos internos para localizações normais ou nomeadas são considerados mudanças
de URI.blocked
— Contador de bloqueios mantidos na requisição.
Enquanto este valor for diferente de zero, a requisição não pode ser terminada.
Atualmente, este valor é aumentado por operações AIO pendentes (POSIX AIO e
operações de thread) e bloqueio de cache ativo.buffered
— Máscara de bits mostrando quais módulos fizeram buffer da
saída produzida pela requisição.
Vários filtros podem fazer buffer da saída; por exemplo, sub_filter pode fazer buffer de dados
devido a uma correspondência parcial de string, copy filter pode fazer buffer de dados devido à
falta de buffers de saída livres etc.
Enquanto este valor for diferente de zero, a requisição não é finalizada
aguardando o flush.header_only
— Flag indicando que a saída não
requer um corpo.
Por exemplo, esta flag é usada por requisições HTTP HEAD.keepalive
— Flag indicando se o keepalive da conexão
do cliente é suportado.
O valor é inferido da versão HTTP e do valor do
cabeçalho "Connection".header_sent
— Flag indicando que o cabeçalho de saída
já foi enviado pela requisição.internal
— Flag indicando que a requisição atual
é interna.
Para entrar no estado interno, uma requisição deve passar por um
redirecionamento interno ou ser uma subrequisição.
Requisições internas têm permissão para entrar em localizações internas.allow_ranges
— Flag indicando que uma resposta parcial
pode ser enviada ao cliente, conforme solicitado pelo cabeçalho HTTP Range.subrequest_ranges
— Flag indicando que uma resposta parcial
pode ser enviada enquanto uma subrequisição está sendo processada.single_range
— Flag indicando que apenas um único intervalo contínuo
de dados de saída pode ser enviado ao cliente.
Esta flag é geralmente definida ao enviar um fluxo de dados, por exemplo de um
servidor proxy, e a resposta inteira não está disponível em um buffer.main_filter_need_in_memory
,
filter_need_in_memory
— Flags
solicitando que a saída seja produzida em buffers de memória em vez de arquivos.
Este é um sinal para o copy filter ler dados de buffers de arquivo mesmo se
sendfile estiver habilitado.
A diferença entre as duas flags é a localização dos módulos de filtro que
as definem.
Filtros chamados antes do postpone filter na cadeia de filtros definem
filter_need_in_memory
, solicitando que apenas a saída da
requisição atual venha em buffers de memória.
Filtros chamados posteriormente na cadeia de filtros definem
main_filter_need_in_memory
, solicitando que
tanto a requisição principal quanto todas as subrequisições leiam arquivos em memória
ao enviar a saída.filter_need_temporary
— Flag solicitando que a saída da requisição
seja produzida em buffers temporários, mas não em buffers de memória somente leitura ou
buffers de arquivo.
Isto é usado por filtros que podem alterar a saída diretamente nos buffers onde
ela é enviada.Configuração#
http
.
Funciona como configurações globais para um módulo.server
.
Funciona como configurações específicas do servidor para um módulo.location
,
if
ou limit_except
.
Funciona como configurações específicas da localização para um módulo.foo
, do tipo
unsigned integer.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()
cria uma nova estrutura de configuração, e
ngx_http_foo_merge_loc_conf()
mescla uma configuração com
configuração de um nível superior.
Na verdade, as configurações de servidor e localização não existem apenas nos níveis de servidor e
localização, mas também são criadas para todos os níveis acima deles.
Especificamente, uma configuração de servidor também é criada no nível principal e
configurações de localização são criadas nos níveis principal, servidor e localização.
Essas configurações tornam possível especificar configurações específicas de servidor e localização
em qualquer nível de um arquivo de configuração do Angie.
Eventualmente as configurações são mescladas para baixo.
Várias macros como NGX_CONF_UNSET
e
NGX_CONF_UNSET_UINT
são fornecidas
para indicar uma configuração ausente e ignorá-la durante a mesclagem.
Macros de mesclagem padrão do Angie como ngx_conf_merge_value()
e
ngx_conf_merge_uint_value()
fornecem uma maneira conveniente de
mesclar uma configuração e definir o valor padrão se nenhuma das configurações
forneceu um valor explícito.
Para uma lista completa de macros para diferentes tipos, veja
src/core/ngx_conf_file.h
.ngx_conf_t
como primeiro 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
da estrutura.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
.
A configuração principal de uma requisição nunca muda.
A configuração do servidor pode mudar do padrão após
o servidor virtual para a requisição ser escolhido.
A configuração de localização selecionada para processar uma requisição pode mudar múltiplas
vezes como resultado de uma operação de reescrita ou redirecionamento interno.
O exemplo a seguir mostra como acessar a configuração HTTP de um módulo em
tempo de execução.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
— Primeira fase.
O módulo RealIP
registra seu manipulador nesta fase para habilitar
substituição de endereços de cliente antes que qualquer outro módulo seja invocado.NGX_HTTP_SERVER_REWRITE_PHASE
— Fase onde
diretivas de reescrita definidas em um bloco server
(mas fora de um bloco location
) são processadas.
O módulo
Rewrite instala seu manipulador nesta fase.NGX_HTTP_FIND_CONFIG_PHASE
— Fase especial
onde uma localização é escolhida baseada na URI da requisição.
Antes desta fase, a localização padrão para o servidor virtual relevante
é atribuída à requisição, e qualquer módulo solicitando uma configuração de localização
recebe a configuração para a localização padrão do servidor.
Esta fase atribui uma nova localização à requisição.
Nenhum manipulador adicional pode ser registrado nesta fase.NGX_HTTP_REWRITE_PHASE
— Igual a
NGX_HTTP_SERVER_REWRITE_PHASE
, mas para
regras de reescrita definidas na localização, escolhida na fase anterior.NGX_HTTP_POST_REWRITE_PHASE
— Fase especial
onde a requisição é redirecionada para uma nova localização se sua URI mudou
durante uma reescrita.
Isso é implementado pela requisição passando pela
NGX_HTTP_FIND_CONFIG_PHASE
novamente.
Nenhum manipulador adicional pode ser registrado nesta fase.NGX_HTTP_PREACCESS_PHASE
— Uma fase comum para diferentes
tipos de manipuladores, não associados com controle de acesso.
Os módulos padrão do Angie
Limit Conn e
Limit Req registram seus manipuladores nesta fase.NGX_HTTP_ACCESS_PHASE
— Fase onde é verificado
se o cliente está autorizado a fazer a requisição.
Módulos padrão do Angie como
Access e
Auth Basic registram seus manipuladores nesta fase.
Por padrão o cliente deve passar na verificação de autorização de todos os manipuladores
registrados nesta fase para que a requisição continue para a próxima fase.
A diretiva satisfy pode
ser usada para permitir que o processamento continue se qualquer um dos manipuladores de fase
autorizar o cliente.NGX_HTTP_POST_ACCESS_PHASE
— Fase especial onde a
diretiva satisfy any é processada.
Se alguns manipuladores de fase de acesso negaram acesso e nenhum explicitamente permitiu,
a requisição é finalizada.
Nenhum manipulador adicional pode ser registrado nesta fase.NGX_HTTP_PRECONTENT_PHASE
— Fase para manipuladores serem chamados
antes de gerar conteúdo.
Módulos padrão como
try_files e
Mirror registram seus manipuladores nesta fase.NGX_HTTP_CONTENT_PHASE
— Fase onde a resposta
é normalmente gerada.
Múltiplos módulos padrão do Angie registram seus manipuladores nesta fase,
incluindo
Index.
Eles são chamados sequencialmente até que um deles produza
a saída.
Também é possível definir manipuladores de conteúdo por localização.
Se a configuração de localização do
Módulo HTTP tem handler
definido, ele é
chamado como o manipulador de conteúdo e os manipuladores instalados nesta fase
são ignorados.NGX_HTTP_LOG_PHASE
— Fase onde o registro de requisições
é executado.
Atualmente, apenas o módulo
Log registra seu manipulador
neste estágio para registro de acesso.
Manipuladores de fase de log são chamados no final do processamento da requisição, logo
antes de liberar a requisição.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
— Prosseguir para a próxima fase.NGX_DECLINED
— Prosseguir para o próximo manipulador da fase
atual.
Se o manipulador atual for o último na fase atual,
mover para a próxima fase.NGX_AGAIN
, NGX_DONE
— Suspender
o processamento da fase até algum evento futuro que pode ser
uma operação de E/S assíncrona ou apenas um atraso, por exemplo.
Assume-se que o processamento da fase será retomado posteriormente chamando
ngx_http_core_run_phases()
.NGX_DECLINED
é considerado um código de finalização.
Qualquer código de retorno dos manipuladores de conteúdo de localização é considerado um
código de finalização.
Na fase de acesso, no
modo satisfy any,
qualquer código de retorno diferente de NGX_OK
,
NGX_DECLINED
, NGX_AGAIN
,
NGX_DONE
é considerado uma negação.
Se nenhum manipulador de acesso subsequente permitir ou negar acesso com um
código diferente, o código de negação se tornará o código de finalização.Exemplos#
Estilo de código#
Regras gerais#
ngx_
ou mais específico como
ngx_http_
e 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;
}
Arquivos#
/*
* Copyright (C) Author Name
* Copyright (C) Organization, Inc.
*/
ngx_config.h
e ngx_core.h
são sempre incluídos primeiro, seguidos por um de
ngx_http.h
, ngx_stream.h
,
ou ngx_mail.h
.
Então seguem arquivos de cabeçalho externos opcionais:#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_ */
Comentários#
//
" não são usados/*
* 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 */
Pré-processador#
ngx_
ou NGX_
(ou mais específico).
Nomes de macro para constantes são maiúsculos.
Macros parametrizadas e macros para inicializadores são minúsculas.
O nome da macro e o valor são separados por pelo menos dois espaços:#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
".
Um nome de tipo definido é separado por pelo menos dois espaços:typedef ngx_uint_t ngx_rbtree_key_t;
typedef
.
Dentro de estruturas, tipos de membros e nomes são alinhados:typedef struct {
size_t len;
u_char *data;
} ngx_str_t;
_s
".
Definições de estruturas adjacentes são separadas com duas linhas vazias: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;
Variáveis#
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;
Funções#
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
deve ser usada em vez de
inline
:static ngx_inline void ngx_cpuid(uint32_t i, uint32_t *buf);
Expressões#
.
" e "->
"
devem ser separados de seus operandos por um espaço.
Operadores unários e subscritos não são separados de seus operandos por espaços: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
(não 0
):if (ptr != NULL) {
...
}
Condicionais e Loops#
if
" é separada da condição por
um espaço.
A chave de abertura fica na mesma linha, ou em uma
linha dedicada se a condição ocupar várias linhas.
A chave de fechamento fica em uma linha dedicada, opcionalmente seguida
por "else if
/ else
".
Geralmente, há uma linha vazia antes da
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
"
e "while
":while (p < last && *p == ' ') {
p++;
}
do {
ctx->node = rn;
ctx = ctx->next;
} while (ctx);
switch
" é separada da condição por
um espaço.
A chave de abertura fica na mesma linha.
A chave de fechamento fica em uma linha dedicada.
As palavras-chave "case
" são alinhadas com
"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
" são formatados assim: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
" for omitida,
isso é indicado pelo comentário "/* void */
":for (i = 0; /* void */ ; i++) {
...
}
/* void */
" que pode ser colocado na mesma linha:for (cl = *busy; cl->next; cl = cl->next) { /* void */ }
for ( ;; ) {
...
}
Rótulos#
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;
Depurando problemas de memória#
gcc
e clang
,
use a opção de compilador e linker -fsanitize=address
.
Ao compilar o Angie, isso pode ser feito adicionando a opção aos
parâmetros --with-cc-opt
e --with-ld-opt
do script configure
.NGX_DEBUG_PALLOC
como 1
.
Neste caso, as alocações são passadas diretamente para o alocador do sistema dando a ele
controle total sobre os limites dos buffers.auto/configure --with-cc-opt='-fsanitize=address -DNGX_DEBUG_PALLOC=1'
--with-ld-opt=-fsanitize=address
Armadilhas Comuns#
Escrevendo um módulo C#
Strings C#
strlen()
ou strstr()
.
Em vez disso, devem ser usadas as contrapartes
do Angie que aceitam ngx_str_t
ou
ponteiro para dados e um comprimento.
No entanto, há um caso em que ngx_str_t
contém
um ponteiro para uma string terminada em zero: strings que vêm como resultado da
análise do arquivo de configuração são terminadas em zero.Variáveis Globais#
Gerenciamento Manual de Memória#
Threads#
init_process
e executar as ações necessárias no manipulador do timer.
Internamente o Angie faz uso de threads para
acelerar operações relacionadas a IO, mas este é um caso especial com muitas
limitações.Bibliotecas Bloqueantes#
Requisições HTTP para Serviços Externos#