Lwan é um servidor web de alto desempenho e escalável .
O site do projeto contém mais detalhes.
| OS | Arco | Liberar | Depurar | Análise estática | Testes |
|---|---|---|---|---|---|
| Linux | x86_64 | Histórico de relatórios | |||
| FreeBSD 14 | x86_64 | ||||
| OpenBSD 7.4 | x86_64 |
Você pode criar o LWAN, usar uma imagem de contêiner ou pegar um pacote da sua distribuição favorita.
Antes de instalar o LWAN, verifique se todas as dependências estão instaladas. Todos eles são dependências comuns encontradas em qualquer distribuição GNU/Linux; Os nomes dos pacotes serão diferentes, mas não deve ser difícil pesquisar usando qualquer ferramenta de gerenciamento de pacotes usada por sua distribuição.
O sistema de construção procurará essas bibliotecas e ativará/link, se disponível.
-DENABLE_BROTLI=NO-DENABLE_ZSTD=NO-DENABLE_TLS=ON (padrão) é passado:-DUSE_ALTERNATIVE_MALLOC para procurar com os seguintes valores:pacman -S cmake zlibpkg install cmake pkgconfapt-get update && apt-get install git cmake zlib1g-dev pkg-configbrew install cmake pacman -S cmake zlib sqlite luajit mariadb-libs gperftools valgrind mbedtlspkg install cmake pkgconf sqlite3 lua51apt-get update && apt-get install git cmake zlib1g-dev pkg-config lua5.1-dev libsqlite3-dev libmariadb-dev libmbedtls-devbrew install cmake mariadb-connector-c sqlite [email protected] pkg-config ~$ git clone git://github.com/lpereira/lwan
~$ cd lwan
~/lwan$ mkdir build
~/lwan$ cd build
Selecionando uma versão de liberação (sem símbolos de depuração, mensagens, ativar algumas otimizações, etc.):
~/lwan/build$ cmake .. -DCMAKE_BUILD_TYPE=Release
Se você deseja ativar otimizações, mas ainda use um depurador, use isso: em vez disso:
~/lwan/build$ cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo
Para desativar otimizações e criar uma versão mais amigável de depuração:
~/lwan/build$ cmake .. -DCMAKE_BUILD_TYPE=Debug
~/lwan/build$ make
Isso irá gerar alguns binários:
src/bin/lwan/lwan : o principal executável Lwan. Pode ser executado com --help para orientação.src/bin/testrunner/testrunner : contém código para executar o conjunto de testes ( src/scripts/testsuite.py ).src/samples/freegeoip/freegeoip : implementação de amostra FreeGeoip. Requer sqlite.src/samples/techempower/techempower : Código para a referência da estrutura da Web TechemPower. Requer bibliotecas SQLite e MariaDB.src/samples/clock/clock : amostra de relógio. Gera um arquivo GIF que sempre mostra a hora local.src/bin/tools/mimegen : cria a tabela de tipos de mímio de extensão. Usado durante o processo de construção.src/bin/tools/bin2hex : gera um arquivo c a partir de um arquivo binário, adequado para uso com #include. Usado durante o processo de construção.src/bin/tools/configdump : despeja um arquivo de configuração usando a API do Reader de Configuração. Usado para teste.src/bin/tools/weighttp : reescrever a ferramenta de benchmarking HTTP weighttp .src/bin/tools/statuslookupgen : gera uma tabela de hash perfeita para os códigos de status HTTP e suas descrições. Usado durante o processo de construção. Passando -DCMAKE_BUILD_TYPE=Release permitirá algumas otimizações do compilador (como LTO) e ajustará o código da arquitetura atual.
Importante
Por favor, use a construção de lançamento quando o benchmarking . O padrão é a construção de depuração, que não apenas registra todas as solicitações na saída padrão, mas também faz isso, mantendo um bloqueio, mantendo severamente pressionando o servidor.
A compilação padrão (ou seja, não passa -DCMAKE_BUILD_TYPE=Release ) criará uma versão adequada para fins de depuração. Esta versão pode ser usada no Valgrind (se seus cabeçalhos estiverem presentes) e inclui depuração de mensagens que são despojadas na versão de liberação. As mensagens de depuração são impressas para cada solicitação.
Nessas construções, os desinfetantes podem ser ativados. Para selecionar qual para construir LWAN, especifique uma das seguintes opções para a linha de invocação do CMake:
-DSANITIZER=ubsan seleciona o desinfetante de comportamento indefinido.-DSANITIZER=address seleciona o desinfetante do endereço.-DSANITIZER=thread seleciona o desinfetante do thread. Os alocadores de memória alternativos também podem ser selecionados. Atualmente, a LWAN suporta TCMalloc, Mimalloc e Jemalloc fora da caixa. Para usar qualquer um deles, passe -DALTERNATIVE_MALLOC=name na linha de invocação CMake, usando os nomes fornecidos na seção "Dependências opcionais".
A opção -DUSE_SYSLOG=ON pode ser passada para o cmake para também fazer login no log do sistema, além da saída padrão.
Se você estiver construindo LWAN para uma distribuição, pode ser aconselhável usar a opção -DMTUNE_NATIVE=OFF , caso contrário, o binário gerado poderá não ser executado em alguns computadores.
O suporte ao TLS é ativado automaticamente na presença de uma instalação de MBEDTLS adequada em sistemas Linux com cabeçalhos novos o suficiente para suportar KTLs, mas pode ser desativado pela passagem -DENABLE_TLS=NO para cmake.
~/lwan/build$ make testsuite
Isso compilará o programa testrunner e executará o conjunto de testes de regressão no src/scripts/testsuite.py .
~/lwan/build$ make benchmark
Isso compilará testrunner e executará o script de referência src/scripts/benchmark.py .
O LWAN também pode ser criado com o tipo de construção de cobertura especificando -DCMAKE_BUILD_TYPE=Coverage . Isso permite que a generate-coverage faça o alvo, que executará testrunner para preparar um relatório de cobertura de teste com o LCOV.
Todo compromisso neste repositório desencadeia a geração deste relatório e os resultados estão disponíveis ao público.
Configure o servidor editando o lwan.conf fornecido; O formato é explicado em detalhes abaixo.
Observação
Lwan tentará encontrar um arquivo de configuração com base no nome executável no diretório atual; testrunner.conf será usado para o Binário testrunner , lwan.conf para o binário lwan e assim por diante.
Os arquivos de configuração são carregados no diretório atual. Se nenhuma alteração for feita neste arquivo, a execução do LWAN servirá arquivos estáticos localizados no diretório ./wwwroot . Lwan ouvirá na porta 8080 em todas as interfaces.
A LWAN detectará o número de CPUs, aumentará o número máximo de descritores de arquivos abertos e geralmente tentará o melhor para as configurações razoáveis automaticamente para o ambiente em que está em execução. Muitas dessas configurações podem ser ajustadas no arquivo de configuração, mas geralmente é uma boa ideia não mexer com elas.
Dica
Opcionalmente, o binário lwan pode ser usado para o arquivo estático de uma tiro, sem nenhum arquivo de configuração. Execute -o com --help para obter ajuda nisso.
Lwan usa uma sintaxe de arquivo de configuração de key = value . Os comentários são suportados com o personagem # (semelhante aos scripts de shell, Python e Perl). Seções aninhadas podem ser criadas com colchetes encaracolados. As seções podem estar vazias; Nesse caso, os suportes encaracolados são opcionais.
some_key_name é equivalente a some key name nos arquivos de configuração (como um detalhe de implementação, as opções de configuração de leitura de código receberão apenas a versão com sublinhores).
Dica
Os valores podem conter variáveis de ambiente. Use a sintaxe ${VARIABLE_NAME} . Os valores padrão podem ser especificados com um cólon (por exemplo, ${VARIABLE_NAME:foo} , que avalia para ${VARIABLE_NAME} se for definido ou foo ).
sound volume = 11 # This one is 1 louder
playlist metal {
files = '''
/multi/line/strings/are/supported.mp3
/anything/inside/these/are/stored/verbatim.mp3
'''
}
playlist chiptune {
files = """
/if/it/starts/with/single/quotes/it/ends/with/single/quotes.mod
/but/it/can/use/double/quotes.s3m
"""
}
Alguns exemplos podem ser encontrados em lwan.conf e techempower.conf .
As constantes podem ser definidas e reutilizadas durante todo o arquivo de configuração, especificando -as em uma seção de constants em qualquer lugar do arquivo de configuração. Uma constante estará disponível somente após a seção definir uma constante específica. As constantes podem ser redefinidas. Se uma constante não estiver definida, seu valor será obtido de uma variável de ambiente. Se não estiver definido em nenhuma seção de uma constants ou no ambiente, Lwan abortará com uma mensagem de erro apropriada.
constants {
user_name = ${USER}
home_directory = ${HOME}
buffer_size = 1000000
}
A mesma sintaxe para valores padrão especificados acima é válida aqui (por exemplo, especificando user_name para ser ${USER:nobody} definirá ${user_name} para nobody se ${USER} não estiver definido na variável do ambiente ou não é outra constante.)
| Tipo | Descrição |
|---|---|
str | Qualquer tipo de texto de forma livre, geralmente específica do aplicativo |
int | Número inteiro. Range é específico do aplicativo |
time | Intervalo de tempo. Veja a tabela abaixo para unidades |
bool | Valor booleano. Veja a tabela abaixo para valores válidos |
Os campos de tempo podem ser especificados usando multiplicadores. Vários podem ser especificados, eles acabam de ser adicionados; Por exemplo, "1M 1W" especifica "1 mês e 1 semana" (37 dias). A tabela a seguir lista todos os multiplicadores conhecidos:
| Multiplicador | Descrição |
|---|---|
s | Segundos |
m | Minutos |
h | Horas |
d | Dias |
w | Semanas de 7 dias |
M | Meses de 30 dias |
y | Anos de 365 dias |
Observação
Um número com um multiplicador não nesta tabela é ignorado; Um aviso é emitido ao ler o arquivo de configuração. Não existem espaços entre o número e seu multiplicador.
| Valores verdadeiros | Valores falsos |
|---|---|
| Qualquer número inteiro diferente de 0 | 0 |
on | off |
true | false |
yes | no |
Geralmente, é uma boa ideia deixar Lwan decidir as melhores configurações para o seu ambiente. No entanto, nem todo ambiente é o mesmo, e nem todos os usos podem ser decididos automaticamente; portanto, são fornecidas algumas opções de configuração.
| Opção | Tipo | Padrão | Descrição |
|---|---|---|---|
keep_alive_timeout | time | 15 | Tempo limite para manter uma conexão viva |
quiet | bool | false | Defina como true para não imprimir nenhuma mensagem de depuração. Somente eficaz nas construções de liberação. |
expires | time | 1M 1w | Valor do cabeçalho "expira". O padrão é 1 mês e 1 semana |
threads | int | 0 | Número de threads de E/S. Padrão (0) é o número de CPUs online |
proxy_protocol | bool | false | Ativa o protocolo proxy. As versões 1 e 2 são suportadas. Habilite apenas essa configuração se estiver usando o LWAN por trás de um proxy, e o proxy suporta esse protocolo; Caso contrário, isso permite que qualquer pessoa falsifique endereços IP de origem |
max_post_data_size | int | 40960 | Define o número máximo de tamanho de dados para solicitações de postagem, em bytes |
max_put_data_size | int | 40960 | Define o número máximo de tamanho de dados para solicitações de venda, em bytes |
max_file_descriptors | int | 524288 | Número máximo de descritores de arquivo. Precisa ser pelo menos 10x threads |
request_buffer_size | int | 4096 | Solicite o comprimento do tamanho do buffer. Se maior que o padrão de 4096 , será alocado dinamicamente. |
allow_temp_files | str | "" | Usar arquivos temporários; Defina para post solicitações de postagem, put solicitações de venda ou all (equivalente à configuração para post put ) para ambos. |
error_template | str | Modelo de erro padrão | Modelo para códigos de erro. Veja variáveis abaixo. |
error_template| Variável | Tipo | Descrição |
|---|---|---|
short_message | str | Mensagem de erro curta (por exemplo, Not found ) |
long_message | str | Mensagem de erro longa (por exemplo, The requested resource could not be found on this server ) |
A Lwan pode abandonar seus privilégios para um usuário no sistema e limitar sua visualização do sistema de arquivos com um chroot. Embora não seja à prova de balas, isso fornece uma primeira camada de segurança no caso, há um bug em Lwan.
Para usar esse recurso, declare uma seção straitjacket (ou straightjacket ) e defina algumas opções. Isso exige que Lwan seja executado como root .
Embora esta seção possa ser gravada em qualquer lugar do arquivo (desde que seja uma declaração de nível superior), se houver algum diretório aberto, por exemplo, instanciando o módulo serve_files , a LWAN se recusará a iniciar. (Esta verificação é executada apenas no Linux como uma salvaguarda para Malconfiguration.)
Dica
Declare uma camisa de força logo antes de uma seção site de forma que os arquivos de configuração e dados privados (por exemplo, as teclas TLS) estejam fora do alcance do servidor após a inicialização.
| Opção | Tipo | Padrão | Descrição |
|---|---|---|---|
user | str | NULL | Soltar privilégios para este nome de usuário |
chroot | str | NULL | Caminho para chroot() |
drop_capabilities | bool | true | Solte todos os recursos com o Capset (2) (em Linux) ou penteado (2) (em OpenBSD). |
Se houver necessidade de especificar cabeçalhos personalizados para cada resposta, pode -se declarar uma seção headers no escopo global. A ordem que esta seção aparece não é importante.
Por exemplo, esta declaração:
headers {
Server = Apache/1.0.0 or nginx/1.0.0 (at your option)
Some-Custom-Header = ${WITH_THIS_ENVIRONMENT_VARIABLE}
}
Ambos substituirão o cabeçalho Server ( Server: lwan não será enviado) e definirá Some-Custom-Header com o valor obtido da variável de ambiente $WITH_THIS_ENVIRONMENT_VARIABLE .
Alguns cabeçalhos não podem ser substituídos, pois isso causaria problemas ao enviar seus valores reais enquanto atende solicitações. Isso inclui, mas não se limita a:
DateExpiresWWW-AuthenticateConnectionContent-TypeTransfer-EncodingAccess-Control-Allow- Observação
Os nomes dos cabeçalhos também são insensíveis a minúsculas (e preservação de casos). A substituição SeRVeR substituirá o cabeçalho Server , mas o enviará da maneira que foi escrito no arquivo de configuração.
Apenas dois ouvintes são suportados por processo LWAN: o ouvinte HTTP (seção listener ) e o ouvinte HTTPS (seção tls_listener ). Apenas um ouvinte de cada tipo é permitido.
Aviso
O suporte ao TLS é experimental. Embora seja estável durante os testes iniciais, sua milhagem pode variar. Somente o TLSV1.2 é suportado neste momento, mas o TLSV1.3 está planejado.
Observação
O suporte TLS exige? Linux com o módulo tls.ko embutido ou carregado. O suporte a outros sistemas operacionais pode ser adicionado no futuro. FreeBSD parece possível, outros sistemas operacionais não parecem oferecer um recurso semelhante. Para sistemas operacionais não suportados, o uso de um proxy TLS Terminator, como Hitch, é uma boa opção.
Para o listener e as seções tls_listener , o único parâmetro é o endereço e a porta da interface para ouvir. A sintaxe do ouvinte é ${ADDRESS}:${PORT} , onde ${ADDRESS} pode ser * (ligação a todas as interfaces), um endereço IPv6 (se cercado por colchetes), um endereço IPv4 ou um nome de host. Por exemplo, listener localhost:9876 ouviria apenas na interface lo , porta 9876 .
Enquanto uma seção listener não leva as teclas, uma seção tls_listener requer dois: cert e key (cada um apontando, respectivamente, para o local no disco onde o certificado TLS e os arquivos de chave privados estão localizados) e recebe uma chave de hsts booleana opcional, que controla se os cabeçalhos de Strict-Transport-Security serão enviados sobre as respostas HTTPs.
Dica
Para gerar essas chaves para fins de teste, a ferramenta de linha de comando OpenSSL pode ser usada como a seguinte: openssl req -nodes -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -sha256 -days 7
Observação
Recomenda -se que uma camisa de força com uma opção chroot seja declarada logo após uma seção tls_listener , de modo que os caminhos para o certificado e a chave estejam fora de alcance a partir desse ponto.
Se a ativação do soquete Systemd for usada, systemd poderá ser especificado como um parâmetro. (Se vários ouvintes do Systemd forem especificados, systemd:FileDescriptorName pode ser especificado, onde FileDescriptorName segue as convenções definidas na documentação systemd.socket .)
Exemplos:
listener *:8080 # Listen on all interfaces, port 8080, HTTP
tls_listener *:8081 { # Listen on all interfaces, port 8081, HTTPS
cert = /path/to/cert.pem
key = /path/to/key.pem
}
# Use named systemd socket activation for HTTP listener
listener systemd:my-service-http.socket
# Use named systemd socket activation for HTTPS listener
tls_listener systemd:my-service-https.socket {
...
}
Uma seção site agrupa instâncias de módulos e manipuladores que responderão a solicitações a um determinado prefixo de URL.
Para rotear os URLs, a LWAN corresponde ao maior prefixo comum do URI da solicitação com um conjunto de prefixos especificados na seção do ouvinte. Como uma solicitação para um prefixo específico será manuseada depende de qual manipulador ou módulo foi declarado na seção do ouvinte. Manipuladores e módulos são semelhantes internamente; Os manipuladores são apenas funções e não mantêm estado, e os módulos detêm o estado (instância nomeada). Várias instâncias de um módulo podem aparecer em uma seção do ouvinte.
Não há sintaxe especial para anexar um prefixo a um manipulador ou módulo; Todas as regras do analisador de configuração se aplicam aqui. Use ${NAME} ${PREFIX} para vincular o caminho ${PREFIX} prefixo para um manipulador chamado ${NAME} (se ${NAME} começa com & , como com o endereço "de C de C de C) ou um módulo chamado ${NAME} . Seções vazias podem ser usadas aqui.
Cada módulo terá seu conjunto específico de opções e eles estão listados nas próximas seções. Além das opções de configuração, uma seção authorization especial pode estar presente na declaração de uma instância do módulo. Os manipuladores não tomam nenhuma opção de configuração, mas podem incluir a seção authorization .
Dica
A execução do LWAN com o argumento da linha de comando- --help mostrará uma lista de módulos e manipuladores internos.
A seguir, há alguma documentação básica para os módulos enviados com Lwan.
O módulo serve_files servirá arquivos estáticos e criará automaticamente índices de diretório ou servirá arquivos pré-comprovados. Geralmente, tentará servir os arquivos da maneira mais rápida possível, de acordo com algumas heurísticas.
| Opção | Tipo | Padrão | Descrição |
|---|---|---|---|
path | str | NULL | Caminho para um diretório que contém arquivos a serem servidos |
index_path | str | index.html | Nome do arquivo para servir como um índice para um diretório |
serve_precompressed_path | bool | true | Se $ FILE.GZ existir, for menor e mais recente que $ File, e o cliente aceita a codificação gzip , transfira -o |
auto_index | bool | true | Gere uma lista de diretórios automaticamente se não estiver presente o arquivo index_path . Caso contrário, produz 404 |
auto_index_readme | bool | true | Inclui o conteúdo dos arquivos readme como parte do índice de diretório gerado automaticamente |
directory_list_template | str | NULL | Caminho para um modelo de bigode para a lista de diretórios; Por padrão, use um modelo interno |
read_ahead | int | 131702 | Quantidade máxima de bytes para ler com antecedência ao armazenar em cache de arquivos abertos. Um valor de 0 desabilita o ReadaHead. O ReadaHead é realizado por um encadeamento de baixa prioridade para não bloquear os threads de E/S enquanto as extensões do arquivo estão sendo lidas no sistema de arquivos. |
cache_for | time | 5s | Hora de manter os metadados do arquivo (tamanho, conteúdo compactado, descritor de arquivo aberto, etc.) em cache |
Observação
Os arquivos menores que 16kib serão compactados na RAM pela duração especificada na configuração cache_for . Lwan sempre tentará comprimir com o deflate e opcionalmente comprime com Brotli e Zstd (se Lwan tiver sido construído com suporte adequado).
Nos casos em que a compactação não valeria o esforço (por exemplo, adicionar o cabeçalho Content-Encoding resultaria em uma resposta maior do que o envio do arquivo não compactado, geralmente o caso de arquivos muito pequenos), Lwan não gastará tempo compactando um arquivo.
Para arquivos maiores que 16kib, Lwan não tentará comprimi -los. Nas versões futuras, pode fazer isso e enviar respostas usando o codificação em chunked enquanto o arquivo está sendo compactado (até um determinado limite, é claro), mas por enquanto, apenas arquivos pré-aprimorados (consulte a configuração de serve_precompressed_path na tabela acima) são considerados.
Para todos os casos, a LWAN pode tentar usar a versão gzipped se isso for encontrado no sistema de arquivos e o cliente solicitou essa codificação.
directory_list_template| Variável | Tipo | Descrição |
|---|---|---|
rel_path | str | Caminho em relação ao caminho real do diretório raiz |
readme | str | Conteúdo do First ReadMe File Found ( readme , readme.txt , read.me , README.TXT , README ) |
file_list | iterador | Itera na lista de arquivos |
file_list.zebra_class | str | odd para itens estranhos, ou even itens |
file_list.icon | str | Caminho para o ícone para o tipo de arquivo |
file_list.name | str | Nome do arquivo (escapado) |
file_list.type | str | Tipo de arquivo (diretório ou arquivo regular) |
file_list.size | int | Tamanho do arquivo |
file_list.unit | str | Unidade para file_size |
O módulo lua permitirá que as solicitações sejam atendidas por scripts escritos na linguagem de programação Lua. Embora a funcionalidade fornecida por este módulo seja bastante espartana, ela é capaz de executar estruturas como marinheiro.
Os scripts podem ser servidos a partir de arquivos ou incorporados no arquivo de configuração, e os resultados de carregá -los, os módulos LUA padrão e (opcionalmente, se usar o Luajit) otimizar o código será armazenado em cache por um tempo.
| Opção | Tipo | Padrão | Descrição |
|---|---|---|---|
default_type | str | text/plain | Tipo MIME padrão para respostas |
script_file | str | NULL | Caminho para o script Lua |
cache_period | time | 15s | Hora de manter o estado de lua carregado na memória |
script | str | NULL | Script inline Lua |
Observação
Os scripts da Lua não podem usar variáveis globais, pois podem ser atendidas não apenas por threads diferentes, mas o estado estará disponível apenas para a quantidade de tempo especificado na opção de configuração cache_period . Isso ocorre porque cada thread de E/S em Lwan criará uma instância de uma lua VM (ou seja, uma estrutura lua_State para cada thread de E/S), e cada coroutina LWAN gerará um thread Lua (com lua_newthread() ) por solicitação.
Não há necessidade de ter uma instância do módulo Lua para cada terminal; Um único script, incorporado no arquivo de configuração ou não, pode atender a muitos pontos de extremidade diferentes. Os scripts devem implementar funções com a seguinte assinatura: handle_${METHOD}_${ENDPOINT}(req) , onde ${METHOD} pode ser um método http (ou seja, get , post , head etc.) e ${ENDPOINT} é o fim desejado a ser tratado por essa função. Uma função handle(req) será chamada se a versão específica não existir.
Dica
Use o terminal root para um captura. Por exemplo, o manipulador handle_get_root() será chamado se nenhum outro manipulador puder ser encontrado para essa solicitação. Se não houver captura, o servidor retornará um erro 404 Not Found .
O parâmetro req aponta para um metatável que contém métodos para obter informações da solicitação ou definir a resposta, como visto abaixo:
req:query_param(param) retorna o parâmetro de consulta (da sequência de consulta) com o param -chave, ou nil se não for encontradoreq:post_param(param) retorna o parâmetro post (apenas para ${POST} manipuladores) com o param -chave, ou nil se não for encontradoreq:set_response(str) define a resposta à String strreq:say(str) envia um pedaço de resposta (usando a codificação em chunked no HTTP)req:send_event(event, str) envia um evento (usando eventos enviados pelo servidor)req:cookie(param) retorna o cookie chamado param , ou nil não é encontradoreq:set_headers(tbl) define os cabeçalhos de resposta da tabela tbl ; Um cabeçalho pode ser especificado várias vezes usando uma tabela, em vez de uma string, no valor da tabela ( {'foo'={'bar', 'baz'}} ); deve ser chamado antes de enviar qualquer resposta com say() ou send_event()req:header(name) obtém o cabeçalho da solicitação com o nome dado ou nil se não for encontradoreq:sleep(ms) faz uma pausa no manipulador atual pela quantidade especificada de milissegundosreq:ws_upgrade() retorna 1 se a conexão poderá ser atualizada para um websocket; 0 caso contrárioreq:ws_write_text(str) envia str através da conexão WebSocket-Atualizada como quadro de textoreq:ws_write_binary(str) envia str através da conexão WebSocket-Gradled como quadro binárioreq:ws_write(str) envia str através da conexão WebSocket-Gradled como texto ou quadro binário, dependendo do conteúdo que contém apenas caracteres ASCII ou nãoreq:ws_read() Retorna uma string com o conteúdo do último quadro do WebSocket, ou um número indicando um status (enotconn/107 no Linux, se ele tiver sido desconectado; EAGAIN/11 no Linux se nada estiver disponível; enomsg/42 no Linux, caso contrário). O valor de retorno aqui pode mudar no futuro para algo mais parecido com Lua.req:remote_address() retorna uma string com o endereço IP remoto.req:path() retorna uma string com o caminho de solicitação.req:query_string() Retorna uma string com a sequência de consulta (String vazia se não houver string de consulta presente).req:body() retorna o corpo da solicitação (Publicar/colocar solicitações).req:request_id() retorna uma string que contém o ID da solicitação.req:request_date() retorna a data, pois será gravada no cabeçalho da resposta Date .req:is_https() retorna true se essa solicitação for atendida por HTTPS, false caso contrário.req:host() retorna o valor do cabeçalho Host , se presente, caso contrário, nil .req:http_version() retorna HTTP/1.0 ou HTTP/1.1 dependendo da versão de solicitação.req:http_method() retorna uma string, na mancha, com o método HTTP (por exemplo, "GET" ).req:http_headers() retorna uma tabela com todos os cabeçalhos e seus valores. As funções do manipulador podem retornar nil (nesse caso, uma resposta 200 OK é gerada) ou um número correspondente a um código de status HTTP. Tentar retornar um código de status HTTP inválido ou qualquer outra coisa que não seja um número ou nil resultará em uma resposta 500 Internal Server Error sendo lançada.
Além dos metames do parâmetro req , também é possível registrar mensagens com diferentes níveis de registro, chamando métodos de Lwan.log :
Lwan.log:warning(str)Lwan.log:info(str)Lwan.log:error(str)Lwan.log:critical(str) (também abortará Lwan! Use com cautela)Lwan.log:debug(str) (disponível apenas em depuração construídas; não-op de outra forma) Observação
Se o LWAN for construído com suporte ao syslog, essas mensagens também serão enviadas para o log do sistema, caso contrário elas serão impressas no erro padrão.
O módulo rewrite corresponderá aos padrões nos URLs e dará a opção de redirecionar para outro URL ou reescrever a solicitação de uma maneira que a LWAN lidará com a solicitação como se fosse feita dessa maneira originalmente.
Observação
Gifra-se da Lua 5.3.1, o mecanismo de expressão regular pode não ser tão repleto de recursos quanto os motores de uso geral, mas foi escolhido especificamente porque é um autômato finito determinístico, na tentativa de tornar impossível algum tipo de negação de ataques de serviço.
O novo URL pode ser especificado usando uma sintaxe de substituição de texto simples ou use scripts Lua.
Dica
Os scripts da Lua conterão os mesmos metames disponíveis no Metatável req fornecido pelo módulo Lua, para que possa ser bastante poderoso.
Cada instância do módulo de reescrita exigirá um pattern e a ação a ser executada quando esse padrão for correspondido. Os padrões são avaliados na ordem em que aparecem no arquivo de configuração e são especificados usando seções aninhadas no arquivo de configuração. Por exemplo, considere o exemplo a seguir, onde dois padrões são especificados:
rewrite /some/base/endpoint {
pattern posts/(%d+) {
# Matches /some/base/endpointposts/2600 and /some/base/endpoint/posts/2600
rewrite_as = /cms/view-post?id=%1
}
pattern imgur/(%a+)/(%g+) {
# Matches /some/base/endpointimgur/gif/mpT94Ld and /some/base/endpoint/imgur/gif/mpT94Ld
redirect_to = https://i.imgur.com/%2.%1
}
}
Este exemplo define dois padrões, um fornecendo um URL mais agradável que está escondido do usuário e outro fornecendo uma maneira diferente de obter um link direto para uma imagem hospedada em um serviço de hospedagem de imagens popular (ou seja, solicitação /some/base/endpoint/imgur/mp4/4kOZNYX irá redirecionar diretamente para um recurso no serviço imgur).
O valor de rewrite_as ou redirect_to também pode ser scripts lua; Nesse caso, a opção expand_with_lua deve ser definida como true e, em vez de usar a sintaxe de substituição de texto simples como o exemplo acima, uma função chamada handle_rewrite(req, captures) deve ser definida. O parâmetro req está documentado na seção Módulo Lua; O parâmetro captures é uma tabela que contém todas as capturas, em ordem (o ou seja, captures[2] é equivalente a %2 na sintaxe de substituição de texto simples). Esta função retorna o novo URL para redirecionar.
Este módulo não tem opções por si só. As opções são especificadas em todos os padrões.
| Opção | Tipo | Padrão | Descrição |
|---|---|---|---|
rewrite_as | str | NULL | Reescrever o URL seguindo este padrão |
redirect_to | str | NULL | Redirecionar para um novo URL após este padrão |
expand_with_lua | bool | false | Use os scripts da Lua para redirecionar ou reescrever uma solicitação |
redirect_to e rewrite_as as opções são mutuamente exclusivas e uma delas deve ser especificada pelo menos.
Também é possível especificar condições para desencadear uma reescrita. Para especificar um, abra um bloco condition , especifique o tipo de condição e, em seguida, os parâmetros para que essa condição seja avaliada. Várias condições podem ser definidas por regra de reescrita, desde que haja uma condição por tipo:
| Doença | Pode usar substr. sintaxe | Seção necessária | Parâmetros | Descrição |
|---|---|---|---|---|
cookie | Sim | Sim | Uma única key = value | Verifica se a solicitação tiver key de cookie tem value de valor |
query | Sim | Sim | Uma única key = value | Verifica se a solicitação tiver key variável de consulta tem value de valor |
post | Sim | Sim | Uma única key = value | Verifica se a solicitação tiver key de dados postagem tem value de valor |
header | Sim | Sim | Uma única key = value | Verifica se key do cabeçalho da solicitação tem value de valor |
environment | Sim | Sim | Uma única key = value | Verifica se key da variável de ambiente tem value de valor |
stat | Sim | Sim | path , is_dir , is_file | Verifica se path existe no sistema de arquivos e opcionalmente verifica se is_dir ou is_file |
encoding | Não | Sim | deflate , gzip , brotli , zstd , none | Verifica se o cliente aceita respostas em uma codificação determinada (por exemplo, deflate = yes para deflatar a codificação) |
proxied | Não | Não | Booleano | Verifica se a solicitação foi proxiada através do protocolo proxy |
http_1.0 | Não | Não | Booleano | Verifica se a solicitação for feita com um cliente HTTP/1.0 |
is_https | Não | Não | Booleano | Verifica se a solicitação for feita através de https |
has_query_string | Não | Não | Booleano | Verifica se a solicitação tem uma string de consulta (mesmo que vazia) |
method | Não | Não | Nome do método | Verifica se o método http é o especificado |
lua | Não | Não | Corda | Executa a função lua matches(req) dentro da string e verifica se ela retorna true ou false |
backref | Não | Sim | Um único backref index = value | Verifica se o número do backref corresponde ao valor fornecido |
Pode usar substr. A sintaxe refere -se à capacidade de fazer referência ao padrão correspondente usando a mesma sintaxe de substituição usada para a rewrite as ou redirect to as ações. Por exemplo, condition cookie { some-cookie-name = foo-%1-bar } substituirá %1 pela primeira partida do padrão em que essa condição está relacionada.
Observação
Condições que não exigem uma seção devem ser escritas como chave; Por exemplo, condition has_query_string = yes .
Por exemplo, se alguém quiser enviar site-dark-mode.css se houver um cookie style com o valor dark e enviar site-light-mode.css , caso contrário, pode-se escrever:
pattern site.css {
rewrite as = /site-dark-mode.css
condition cookie { style = dark }
}
pattern site.css {
rewrite as = /site-light-mode.css
}
Outro exemplo: se alguém quiser enviar arquivos pré-comprovados, se existirem no sistema de arquivos e o usuário solicitou:
pattern (%g+) {
condition encoding { brotli = yes }
condition stat { path = %1.brotli }
rewrite as = %1.brotli
}
pattern (%g+) {
condition encoding { gzip = yes }
condition stat { path = %1.gzip }
rewrite as = %1.gzip
}
pattern (%g+) {
condition encoding { zstd = yes }
condition stat { path = %1.zstd }
rewrite as = %1.zstd
}
pattern (%g+) {
condition encoding { deflate = yes }
condition stat { path = %1.deflate }
rewrite as = %1.deflate
}
Observação
Em geral, isso não é necessário, pois o módulo de porção de arquivos fará isso automaticamente e escolherá o menor arquivo disponível para a codificação solicitada, mas isso mostra que é possível ter um recurso semelhante por configuração sozinho.
O módulo redirect , como diz na lata, gerará um 301 Moved permanently (por padrão; o código pode ser alterado, veja abaixo) Resposta, de acordo com as opções especificadas em sua configuração. Geralmente, o módulo rewrite deve ser usado, pois possui mais recursos; No entanto, este módulo serve também como um exemplo de como escrever módulos LWAN (menos de 100 linhas de código).
Se a opção to não for especificada, ele sempre gera uma resposta 500 Internal Server Error . Especificando um código HTTP inválido ou um código que Lwan não conhece (consulte enum lwan_http_status ), produzirá uma resposta 301 Moved Permanently .
| Opção | Tipo | Padrão | Descrição |
|---|---|---|---|
to | str | NULL | O local para redirecionar para |
code | int | 301 | O código HTTP para executar um redirecionamento |
O módulo response gerará uma resposta artificial de qualquer código HTTP. Além de também servir como um exemplo de como escrever um módulo LWAN, ele pode ser usado para criar vazios de outros módulos (por exemplo, gerando uma resposta 405 Not Allowed para arquivos em /.git , se / for servido com o módulo serve_files ).
Se o code fornecido estiver fora dos códigos de resposta conhecidos por Lwan, um erro 404 Not Found será enviado.
| Opção | Tipo | Padrão | Descrição |
|---|---|---|---|
code | int | 999 | Um código de resposta HTTP |
As solicitações de proxies do módulo fastcgi entre o cliente HTTP que se conecta ao LWAN e a um servidor FastCGI acessível pela LWAN. Isso é útil, por exemplo, para servir páginas de uma linguagem de script, como o PHP.
Observação
Esta é uma versão preliminar deste módulo e, como tal, não está bem otimizada, alguns recursos estão ausentes e alguns valores fornecidos ao ambiente são codificados.
| Opção | Tipo | Padrão | Descrição |
|---|---|---|---|
address | str | Endereço para conectar -se. Pode ser um caminho de arquivo (para soquetes de domínio UNIX), endereço IPv4 ( aaa.bbb.ccc.ddd:port ) ou endereço IPv6 ( [...]:port ). | |
script_path | str | Localização onde estão localizados os scripts CGI. | |
default_index | str | index.php | Script padrão para executar se não especificado no URI da solicitação. |
As seções de autorização podem ser declaradas em qualquer instância ou manipulador do módulo e fornece uma maneira de autorizar o cumprimento dessa solicitação através do mecanismo de autorização HTTP padrão. Para exigir a autorização para acessar uma determinada instância ou manipulador do módulo, declare uma seção authorization com um parâmetro basic e defina uma de suas opções.
| Opção | Tipo | Padrão | Descrição |
|---|---|---|---|
realm | str | Lwan | Reino para autorização. Isso geralmente é mostrado na interface do usuário do usuário/senha nos navegadores |
password_file | str | NULL | Caminho para um arquivo que contém nome de usuário e senhas (em texto claro). O formato de arquivo é o mesmo que o formato de arquivo de configuração usado por Lwan |
Aviso
Não apenas as senhas são armazenadas em texto claro em um arquivo que deve ser acessível pelo servidor, mas serão mantidos em memória por alguns segundos. Evite usar esse recurso, se possível.
Leia esta seção (e siga -a) se você planeja contribuir para Lwan. Não há nada inesperado aqui; Isso segue principalmente as regras e expectativas de muitos outros projetos de fins, mas todos esperam que as coisas um pouco diferentes umas das outras.
Lwan tenta seguir um estilo de codificação consistente ao longo do projeto. Se você está pensando em contribuir com um patch para o projeto, respeite esse estilo tentando combinar o estilo do código circundante. Em geral:
global_variables_are_named_like_this , mesmo que tendam a ser raros e sejam marcados como static (com raras exceções)local_var , i , conntypedef for structs raramente são usados em lwan#pragma once em vez do habitual, incluem hackery de guardalwan-private.hlwan_lwan_lwan_/* Old C-style comments are preferred */clang-format can be used to format the source code in an acceptable way; a .clang-format file is provided If modifying well-tested areas of the code (eg the event loop, HTTP parser, etc.), please add a new integration test and make sure that, before you send a pull request, all tests (including the new ones you've sent) are working. Tests can be added by modifying src/scripts/testsuite.py , and executed by either invoking that script directly from the source root, or executing the testsuite build target.
Some tests will only work on Linux, and won't be executed on other platforms.
Lwan is automatically fuzz-tested by OSS-Fuzz. To fuzz-test locally, though, one can follow the instructions to test locally.
Currently, there are fuzzing drivers for the request parsing code, the configuration file parser, the template parser, and the Lua string pattern matching library used in the rewrite module.
Adding new fuzzers is trivial:
src/bin/fuzz .${FUZZER_NAME}_fuzzer.cc . Look at the OSS-Fuzz documentation and other fuzzers on information about how to write these.src/fuzz/corpus . Files have to be named corpus-${FUZZER_NAME}-${UNIQUE_ID} . The shared object version of liblwan on ELF targets (eg Linux) will use a symbol filter script to hide symbols that are considered private to the library. Please edit src/lib/liblwan.sym to add new symbols that should be exported to liblwan.so .
Lwan tries to maintain a source history that's as flat as possible, devoid of merge commits. This means that pull requests should be rebased on top of the current master before they can be merged; sometimes this can be done automatically by the GitHub interface, sometimes they need some manual work to fix conflicts. It is appreciated if the contributor fixes these conflicts when asked.
It is advisable to push your changes to your fork on a branch-per-pull request, rather than pushing to the master branch; the reason is explained below.
Please ensure that Git is configured properly with your name (it doesn't really matter if it is your legal name or a nickname, but it should be enough to credit you) and a valid email address. There's no need to add Signed-off-by lines, even though it's fine to send commits with them.
If a change is requested in a pull request, you have two choices:
It is not enforced, but it is recommended to create smaller commits. How commits are split in Lwan is pretty much arbitrary, so please take a look at the commit history to get an idea on how the division should be made. Git offers a plethora of commands to achieve this result: the already mentioned interactive rebase, the -p option to git add , and git commit --amend are good examples.
Commit messages should have one line of summary (~72 chars), followed by an empty line, followed by paragraphs of 80-char lines explaining the change. The paragraphs explaining the changes are usually not necessary if the summary is good enough. Try to write good commit messages.
Lwan is licensed under the GNU General Public License, version 2, or (at your option), any later version. Portanto:
While Lwan was written originally for Linux, it has been ported to BSD systems as well. The build system will detect the supported features and build support library functions as appropriate.
For instance, epoll has been implemented on top of kqueue, and Linux-only syscalls and GNU extensions have been implemented for the supported systems. This blog post explains the details and how #include_next is used.
It can achieve good performance, yielding about 320000 requests/second on a Core i7 laptop for requests without disk access, and without pipelining.
When disk I/O is required, for files up to 16KiB, it yields about 290000 requests/second ; for larger files, this drops to 185000 requests/second , which isn't too shabby either.
These results, of course, with keep-alive connections, and with weighttp running on the same machine (and thus using resources that could be used for the webserver itself).
Without keep-alive, these numbers drop around 6-fold.
There is an IRC channel ( #lwan ) on Libera. A standard IRC client can be used.
Here's a non-definitive list of third-party stuff that uses Lwan and have been seen in the wild. If you see mentions of Lwan in the media or academia, however small it might be, please contact the author! It'll make her day!
Some other distribution channels were made available as well:
Dockerfile is maintained by @jaxgeller, and is available from the Docker registry.Lwan has been also used as a benchmark:
Mentions in academic journals:
Mentions in magazines:
Mentions in books:
Some talks mentioning Lwan:
Not really third-party, but alas:
Lwan container images are available at ghcr.io/lpereira/lwan. Container runtimes like Docker or Podman may be used to build and run Lwan in a container.
Container images are tagged with release version numbers, so a specific version of Lwan can be pulled.
# latest version
docker pull ghcr.io/lpereira/lwan:latest
# pull a specific version
docker pull ghcr.io/lpereira/lwan:v0.3
Clone the repository and use Containerfile (Dockerfile) to build Lwan with all optional dependencies enabled.
podman build -t lwan .
The image expects to find static content at /wwwroot , so a volume containing your content can be mounted.
docker run --rm -p 8080:8080 -v ./www:/wwwroot lwan
To bring your own lwan.conf , simply mount it at /lwan.conf .
podman run --rm -p 8080:8080 -v ./lwan.conf:/lwan.conf lwan
Podman supports socket activation of containers. This example shows how to run lwan with socket activation and Podman on a Linux host.
Requirements: Podman version 4.5.0 or higher.
sudo useradd test
sudo machinectl shell test@
podman build -t lwan ~/lwan
mkdir -p ~/.config/containers/systemd
mkdir -p ~/.config/systemd/user
listener systemd:my.socket
site {
serve_files / {
path = /web
}
}
[Socket]
ListenStream=8080
[Unit]
After=my.socket
Requires=my.socket
[Container]
Network=none
Image=localhost/lwan
Volume=/home/test/lwan.conf:/lwan.conf:Z
Volume=/home/test/web:/web:Z
:Z is needed on SELinux systems. As lwan only needs to communicate over the socket-activated socket, it's possible to use Network=none . See the article How to limit container privilege with socket activation. mkdir ~/web
echo hello > ~/web/file.txt
systemctl --user daemon-reload
systemctl --user start my.socket
$ curl localhost:8080/file.txt
hello
These are some of the quotes found in the wild about Lwan. They're presented in no particular order. Contributions are appreciated:
"Lwan is like a classic, according to the definition given by Italian -- writer Italo Calvino: you can read it again and again" Antonio Piccolboni
"I read lwan's source code. Especially, the part of using coroutine was very impressive and it was more interesting than a good novel. Thank you for that." -- @patagonia
"For the server side, we're using Lwan, which can handle 100k+ reqs/s. It's supposed to be super robust and it's working well for us." -- @fawadkhaliq
"Insane C thing" -- Michael Sproul
"The best performer is LWAN, a newcomer" -- InfoQ
"I've never had a chance to thank you for Lwan. It inspired me a lot to develop Zewo" -- @paulofariarl
"Let me say that lwan is a thing of beauty. I got sucked into reading the source code for pure entertainment, it's so good. high five " -- @kwilczynski
"mad science" -- jwz
"Nice work with Lwan! I haven't looked that carefully yet but so far I like what I saw. You definitely have the right ideas." -- @thinkingfish
"Lwan is a work of art. Every time I read through it, I am almost always awe-struck." -- @neurodrone
"For Round 10, Lwan has taken the crown" -- TechEmpower
"Jeez this is amazing. Just end to end, rock solid engineering. (...) But that sells this work short." kjeetgill
"I am only a spare time C coder myself and was surprised that I can follow the code. Nice!" cntlzw
"Impressive all and all, even more for being written in (grokkable!) C. Nice work." tpaschalis
"LWAN was a complete failure" dermetfan