Repository navigation
internos motores
Home > Guia de contribuição > Internos: Motores
Para quem mexe no código: Indy, fpHTTP, CGI, Synopse (mORMot2), Sagui, UniGUI, netHTTP, OkHttp, MsQuic e Kwik. Aqui fica o porquê de cada parte ser como é, e o que não pode mudar sem quebrar algo. O código traz só a descrição curta de cada coisa.
Conteúdo da versão 2.0, ainda em desenvolvimento (branch 2.0).
O fcl-web serve uma requisição por conexão e, no 3.2, cria uma thread para cada uma.
O RAL põe um pool de threads (TRALfpHttpWorker) no lugar: o laço de accept enfileira a
conexão e um trabalhador livre a pega; um trabalhador novo só nasce quando nenhum está
livre, então uma rota que trava não segura a próxima conexão.
O gancho muda de versão para versão do fcl-web, e o código testa o que o fcl-web tem, não o número do FPC (o trunk é 3.3.1 há anos, com APIs diferentes):
-
fcl-web 3.2:
CreateConnectionThreadé sobrescrito, enfileira e devolve nil. -
fcl-web 3.3 (
{$IF DECLARED(TFPHTTPServerConnectionHandler)}): esse método não existe mais; cada conexão vai para um connection handler criado porCreateConnectionHandler. O RAL devolve o seu (TRALfpHttpConnectionHandler), que entrega a conexão ao mesmo pool.ConnectionCountpassa a ser a contagem do pool.
O fcl-web 3.3 trouxe limites novos ligados por padrão: no máximo 128 conexões
simultâneas (com um 503 escrito cru no socket, ilegível em TLS), 256 cabeçalhos, linha
de cabeçalho de 32 KB, URL só ASCII e um tempo máximo de 30 s por leitura. O RAL
desliga todos, para que o 3.3 sirva o que o 3.2 servia: o teto de conexões é o
MaxConnections do RAL (vira MaxLiveConnectionCount, porque esse fcl-web não chama
mais o OnAllowConnect) e os limites de tamanho são os dos plugins. O corpo da
requisição, que no 3.3 fica em ContentBytes, é lido sem cópia.
Keep-alive continua desligado - uma requisição por conexão e Connection: close
sempre. O fcl-web 3.3 sabe manter a conexão, mas só de dois jeitos: deixando uma thread
parada em cada conexão ociosa (o que o pool existe para evitar) ou procurando pedidos
pendentes no "tique" do accept ocioso, que exige AcceptIdleTimeout > 0 - a
configuração que corria com a limpeza do próprio fcl-web no Windows e derrubava o
processo. Ligar também faria o mesmo motor se comportar diferente no 3.2 e no 3.3. Se um
dia vier, que venha opcional e com uma verificação de prontidão do próprio RAL.
O núcleo só conhece o canal (TRALResponseStream: trava, enquadramento chunked,
compressor opcional) e um escritor por motor (TRALStreamWriter). Motor sem escritor
fica em memória e manda tudo no fim - correto, só não é ao vivo.
-
Indy e fpHTTP empurram: o handler escreve direto no socket da conexão, na thread
do pedido. No Indy, uma resposta HTTP/1.0 não tem chunked:
ContentLength := -2e a conexão fecha no fim. No fpHTTP o "cliente ainda está aí?" é umselect+recvcomMSG_PEEK, que pergunta sem consumir nada. -
Sagui puxa: o libmicrohttpd pede bytes quando quer (
sg_httpres_sendstream). Os pedaços vão para uma fila contada por referência, que o callback de leitura esvazia e o handler enche; a fila é de quem a soltar por último (o handler ou o libmicrohttpd). Uma rotaStreamingroda numa thread própria e o callback espera só o commit, porque o payload do pedido só vale enquanto o callback não retorna (é copiado). O fim e o erro são-1e-2direto: osg_eordo binding devolvia lixo no Linux. O libmicrohttpd só nota um cliente que saiu quando uma escrita falha, então um fluxo calado e sem SSE (sem ping) não descobre. -
mORMot2 não tinha como mandar um corpo de tamanho desconhecido: a resposta inteira
sai depois que o
Requestretorna. O RAL acrescentou ao mORMot2 oBeginChunkedResponse/SendChunk/EndChunkedResponsenoTHttpServer(modosmThreads), genérico, para virar PR no mORMot2. O motor usa sob{$IF DECLARED(THttpServerChunkedState)}: com um mORMot2 sem a mudança ele compila e fica em memória.smAsyncesmHttpSystambém ficam em memória até o mORMot2 ganhar o equivalente lá. -
Cliente: o motor escreve o corpo num "ralo" (
TRALClientStreamSink) no lugar do stream de sempre; no primeiro pedaço o ralo lê status e tipos (ReadStreamHead) e decide se entrega aos pedaços. O netHTTP não deixa: oTHTTPClientsó devolve a resposta (e seus cabeçalhos) quando oExecutetermina, e sem tipo nem codificação não dá para separar SSE nem descomprimir - por isso entrega no fim. -
Compressão em fluxo usa janela de 4 KB e
memLevel5 (uns 30 KB por fluxo, contra 256 KB do padrão do zlib) e descarrega (Z_SYNC_FLUSH) a cadaSend. O Delphi antes do XE2 não temdeflateInit2/inflateInit2: lá não há compressão nem descompressão aos pedaços.
Começar
Servidor
- Rotas e parâmetros
- Rotas avançadas: tipos, grupos, versões 🚧
- Ler o pedido e responder
- Resposta em fluxo 🚧
- Plugins 🚧
- Autenticação
- Banco de dados (DBWare)
- Servir site e arquivos
- Swagger e Postman
- Servidor SOAP 🚧
- HTTPS
- Criptografia ponto a ponto
- Segurança para produção
- Colocar no ar
- Desempenho
MQTT 🚧
- O que é e quando usar 🚧
- Cliente MQTT 🚧
- Broker MQTT 🚧
- MQTT-SN 🚧
- Ponte REST 🚧
- Segurança 🚧
- Rede ruim 🚧
Cliente
Motores
Avançado
Ajuda
- Problemas comuns
- Atualizar o Indy
- Várias versões do Delphi
- Vídeos e guias
- Módulos de terceiros
- Licenças
- Contribuir
- Roadmap e changelog
🚧 = em desenvolvimento, pode mudar