Repository navigation
internos nucleo
mobius1qwe edited this page Oct 15, 2026
·
2 revisions
Home > Guia de contribuição > Internos: Núcleo
Para quem mexe no código: servidor, cliente, pedido, resposta, parâmetros, rotas e módulos. 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).
-
Valor novo de enum entra sempre no fim (
amUNKNOWNemTRALMethod,rteCancelledemTRALTransportError,sftBCDemTRALFieldType). Assim nenhum ordinal gravado num.dfmou enviado por outra versão muda de sentido, e oelsedeCanSwitchURLjá recusa reenviar o que não conhece. -
Quem decide o reenvio é
TRALTransportError, nãoStatusCode: sem resposta HTTP não existe status.rteConnectgarante que o pedido não chegou ao servidor, então qualquer método pode ir para o próximoBaseURL.rteTimeoutquer dizer que ele pode ter rodado lá, então só método idempotente (RALIdempotentMethods) vai para outro. -
TRALHTTPVersioné um pedido: o RAL não implementa HTTP/2, quem o faz é a plataforma sob o motor (WinHTTP no Windows, OkHttp no Android). O que o ALPN negociou volta emProtocolVersion. Pedirrhv2a um motor semSupportsHTTP2levanta no primeiro pedido em vez de cair para 1.1 calado.rhv10só existe do lado observado: o servidor recebe 1.0, e nenhum motor consegue pedir. -
TRALParamTypeé dito, não deduzido do valor: o Pascal promove literais (2 cabe em Integer, Int64 e Double, e Currency não se distingue de Double). O texto depende do locale das duas pontas (2,5 lido com.vira 0; 03/04 é março ou abril); o valor binário não.
-
defaultde propriedade e construtor andam juntos: os timeouts do cliente usam a mesma constante na diretivadefaulte no construtor (DEFAULTCONNECTTIMEOUT,DEFAULTREQUESTTIMEOUT). Odefaultdiz ao streaming para não gravar a propriedade no.dfm; se diverge do construtor, digitar exatamente o valor dodefaultno Object Inspector gera um componente que roda com o valor do construtor. Ao criar umdefaultnumérico, confira o construtor. -
Arquivos de idioma com BOM: os
ralconsts_*.incsão UTF-8 com BOM. Sem BOM o Delphi lê o include na página ANSI do sistema e todo acento chega codificado duas vezes; com BOM o FPC só aceita o include sob{$CODEPAGE UTF8}. ORALConsts.pasem si fica ASCII, ou a diretiva muda como os literais dele são lidos. - Tempos que parecem iguais e não são:
| Constante | O que mede | Por que é separada |
|---|---|---|
RALENGINEIDLETIMEOUT (5 min) |
motor parado no pool do cliente | abaixo do que um servidor costuma manter uma conexão keep-alive |
RALTHREADREQUESTTIMEOUT (30 min) |
Request de uma thread que sumiu |
uma thread que pôs um header uma vez e chama a cada dez minutos não pode perder o Request
|
DEFAULTWEBSESSIONTIMEOUT (30 min) |
sessão do WebModule |
TRALServer.SessionTimeout é outra coisa: o tempo que o mORMot2 segura uma conexão ociosa (30 s); com 30 min ali, um cliente ocioso prenderia uma thread do smThreads
|
RALQUICIDLETIMEOUT |
conexão QUIC ociosa | o QUIC negocia o menor dos dois lados, então cliente e servidor partem do mesmo valor |
-
RALMAXIDLEENGINESé também o teto de conexões que um cliente mantém abertas sem nada em voo, onde cada motor guarda a sua. -
DEFAULTSERVERPORTvale também para umBaseURLQUIC sem porta: abaixo do HTTP não há convenção 80/443.
-
Valor em texto ou em stream:
TRALParamguarda texto emFTexte stream emFContent(corpo do motor, arquivo aberto, valor tipado);FIsTextdiz qual vale. Um header como stream custaria um objeto no heap além do param, e é a quantidade de alocações por pedido que limita o servidor: com várias threads, elas fazem fila no gerenciador de memória. -
Strings compartilhadas (
gTextPlain,gRalBody...): o Delphi copia um literal para o heap a cada atribuição a campo ou resultado; entregar uma global só conta referência. O FPC não copia literal. -
Índice por nome: todo param lido do fio é buscado primeiro (query, form, headers,
cookies), e uma busca linear daria N*N/2 comparações antes de qualquer autenticação.
O índice nasce com o primeiro param e dobra ao encher; cada cadeia fica em ordem de
criação, então o primeiro param de um nome continua sendo o achado. O hash é FNV-1a
com
$20em cada byte, para nomes queRALSameNameconsidera iguais caírem juntos. -
Bloco de headers guardado como texto (
FPending): o motor entrega todo header que o cliente mandou, e a maioria nunca é lida. A busca por nome responde das linhas e cria só aquele param, no lugar que teria; qualquer outra coisa analisa o bloco inteiro antes (FParams). Um bloco não é adiado comSet-Cookie, mais de 32 linhas ou um nome já na lista (a análise teria mesclado nele). -
Tipados escrevem o conteúdo e depois o tipo: escrever conteúdo derruba o marcador
tipado (senão um texto de 8 bytes sobre um
rctRALDOUBLEseria lido como double), então quem restaura um param tipado atribui o stream e só depois oContentType. Os escritores tipados são métodos próprios, nãoAsInteger/AsDouble, para o fio não mudar para quem já usa; os leitores olham o tipo primeiro e caem no texto. Com um param só, o tipo viaja noContent-TypeHTTP, ao qual oSetContentTypeacrescenta; charset=utf-8: por issoMediaTypecorta os parâmetros e os brancos. No FPC big-endian os bytes são trocados nas duas pontas. -
Separador dos headers decidido pelo dado:
TStrings.NameValueSeparatoré umCharque vale'='e nunca é vazio, e oTIdHeaderListdo Indy declara': 'numa propriedade própria, invisível pela referênciaTStrings. Indy e mORMot2 mandamNome: Valor, o fpHTTP mandanome=valor(TRequest.CustomHeaders).FindHeaderNameSeparatorusa o que vier primeiro na linha entre': 'e'='. Decidir pelo motor erra dos dois jeitos: o'='corta um header do Indy no=do valor, e o':'não acha nada no fpHTTP. - Linhas cortadas por índice: montar uma linha caractere a caractere realoca a string a cada um, e cada alocação pega o lock do gerenciador de memória - sob carga, isso sozinho domina o pedido. Os headers da resposta são medidos e escritos no tamanho exato, e um bloco ASCII não passa por UTF-16.
-
AppendParamsTextvarre uma vez, por ponteiro: um segmento vazio (?&a=1) tem de avançar, ou o laço gira para sempre, e apagar cada segmento do início do texto seria quadrático. -
Header não é URL-encoded: decodificar trocaria todo
+por espaço (o base64 de umAuthorization: Basic,application/ld+json). -
Pointer(FText)^, nãoFText[1]: indexar uma string compartilhada para um argumento sem tipo faz o Delphi copiar a string inteira antes. -
OpenFileabre comfmShareDenyNone:fmShareDenyWriteseguraria uma nova versão do arquivo até a resposta sair. O arquivo vai do disco comoTRALFileStream. -
Corpos que o servidor analisa sozinho: o Indy e o libmicrohttpd (Sagui) analisam
x-www-form-urlencodede multipart antes de qualquer camada do RAL. Por isso, no pedido, form e multipart vão sem compressão (gzip lido como form perde todos os campos); form cifrado vai como multipart, e multipart cifrado é declaradooctet-stream(oDecodeBodyacha o boundary na primeira linha do texto claro). Parte de corpo sem nome de arquivo leva o próprio nome (o libmicrohttpd descarta partes sem nome), mas parte de campo fica sem, ou PHP, Spring e Go a tratam como upload. Só umrpkBODYsozinho é o corpo inteiro: um campo sozinho vai comonome=valor, para um servidor de terceiros achar o campo. Pouco se perde sem compressão no pedido: o volume está na resposta. -
Um corpo decodificado, lido onde está: o buffer do motor é lido no lugar, a cifra
trabalha nele quando pode escrever, e os params são janelas sobre um só corpo. Corpo
que começa com delimitador de multipart nunca foi comprimido, diga o header o que
disser (dois
TRALParamsno mesmo processo nascem com gzip).
-
ClientInfo.ConnectionIDé a conexão, não o cliente: vários pedidos podem dividir uma (keep-alive no HTTP/1.1, multiplexação no HTTP/2 e no QUIC); contar valores distintos contra o número de pedidos é o que mostra se houve multiplexação. Só é único e comparável dentro de um servidor rodando: cada motor entrega o que tem (http.sys o endereço e a porta do par, porque oConnectionIddele é por stream no HTTP/2 e oRawConnectionIdnão vem certo em todo Windows; os modos socket do mORMot2 um contador próprio; os outros o objeto ou o handle da conexão com a porta do par acima, porque o sistema entrega o handle de uma conexão fechada à seguinte). Nunca grave, mande ao cliente ou compare entre servidores. Zero é "o motor não sabe" (CGI, UniGUI) e quem conta deve pular. - A credencial é decodificada só quando pedida: um Bearer é analisado uma vez, e o autenticador usa o mesmo resultado.
-
AddFinishHandler/Finish: o que um plugin pega para um pedido e precisa devolver (a vaga doTRALConcurrencyPlugin) volta noFinish, que todo motor chama logo depois de pegar o corpo da resposta (TakeWireStream): mandar os bytes fica fora do limite. O destrutor do pedido é a rede de segurança para uma exceção antes doFinish. Os dois primeiros handlers ficam no próprio pedido, sem alocação. -
HttpVersioné o esquema (HTTP/HTTPS), não a versão; a versão éProtocol/ProtocolVersion. -
Cookies da resposta: todo motor monta os seus
Set-CookiedeGetParamsCookies, então é ali que CR e LF saem. Cookie simples sai comPath=/(sem Path o navegador o guarda só para a pasta da URL que o criou) e comExpiresem data HTTP, que não depende do locale. -
ResponseText/ResponseStreamno servidor são o corpo decodificado; motor que precisa do corpo do fio usaTakeWireStream.
-
404 ou 405:
CanAnswerRouteacha a rota pelo caminho; entre rotas do mesmo caminho vence a que aceita o método, e quando nenhuma aceita, a rota do caminho responde e o módulo dela devolve 405. Filtrar por método ali faria todo verbo errado virar 404.AllowedMethodsOfjunta o que as rotas do caminho aceitam, para oAllowdo 405 (RFC 9110 15.5.6), já que várias rotas podem dividir um caminho com um verbo cada. -
Custo por pedido: cada rota guarda o caminho completo e os segmentos
(
UpdateSegments, chamado quando a rota, a coleção ou oDomaindo módulo mudam), e o texto doAllow, que o CORS pede a cada pedido. O caminho do pedido é dividido uma vez só. -
Find[]procura peloNameda rota, não pelo caminho.
-
Os dois laços:
ProcessCommandsacha a rota uma vez (FindRoute, guardada no pedido) antes doOnRequest, roda os pluginsppProcesspor prioridade até um responder e depois os módulos até um ser dono da rota; ninguém: 404. O primeiro módulo é o interno (TRALBaseModule): rotas do próprio servidor e a página de status em/. Ele nunca aparece emSubModule[]e só entra no laço quando o servidor tem rota ou mostra o status. - Corpo que o motor não desmontou (multipart sem parte) é 400 antes de qualquer rota: a rota receberia os params vazios, o upload perdido sem aviso.
-
Exceção no handler: o 500 com a mensagem vem primeiro, para nunca sobrar o 200 com
corpo vazio com que a resposta nasce; depois
OnServerError, ou a exceção sobe comRaiseError. -
HideErrorDetails: a mensagem de um driver cita tabelas, colunas, SQL e às vezes a string de conexão.ErrorTexté a regra única que todo motor, oTRALDBModulee o DAO usam para o texto de um 500. -
Activelido do form: é a primeira propriedade publicada, então aplicá-lo na hora subiria o motor antes dePort,IPConfigeSSLserem lidos (um servidor salvo com SSL ouviria em HTTP puro).WriteActivesó guarda,Loadedaplica. Só um True é guardado, porque os setters que reiniciam o servidor (SetPort,PoolCount,Mode) gravamActiveFalse e True enquanto as próprias propriedades são lidas. -
Ciclo de vida:
SetActiveavisa os plugins primeiro (eles preparam o que o motor abre, o certificado por exemplo) e depois os módulos; parando, módulos e depois plugins. Os motores chamaminherited SetActiveantes de abrir a porta, então "ativando" quer dizer pedido, não escuta. Um servidor liberado ativo nunca passa porSetActive(False):BeforeDestructionpara os plugins antes doinherited, que marca o componente como destruindo (e um host destruindo não avisa ninguém). -
IPv6Enablednum motor sem IPv6 é recusado antes de o servidor ser parado, para a recusa não derrubar um servidor no ar. -
Método desconhecido é 501 (RFC 9110 9.1), em
ValidateRequest, depois dos pluginsppValidate: o flood conta o pedido como qualquer outro. -
SessionTimeouté quanto o mORMot2 mantém uma conexão keep-alive ociosa (0 desliga ali); o Indy o passa à própria lista de sessões, que o RAL deixa desligada, e o fpHTTP ao período em que o laço de accept acorda ocioso. Não é a sessão do WebModule. -
DecodeAuthValueusaRALTrimePosemStringRAL: no Delphi,TrimePos(' ')passam por UTF-16, com alocações a cada pedido que traz credencial. -
Certificado em DER (
TRALTLSCertificate): existe também para que o motor que carrega o certificado por conexão nunca leia um arquivo pela metade numa renovação. A troca a quente e o que cada motor faz estão em Certificado automático.
O que um módulo pode mudar, do mais leve ao mais fundo: rotas no construtor;
BeforeExecute/AfterExecute; RouteClass (a rota guarda dados ou um handler
tipado); RequestClass/ResponseClass com ExecuteContext; HandleException;
ServerActivating/ServerDeactivating. O guia de quem escreve um é
Criar um módulo.
-
CreateRoute/NewRoutepassam pela coleção (Routes.Add), porqueTRALRoute.Createcriaria sempre umaTRALRoutesimples, qualquer que fosse oRouteClass. -
O pedido e a resposta do módulo são invólucros, não subclasses: o motor cria os
objetos do núcleo antes de a rota ser conhecida, e os plugins escrevem neles primeiro.
TRALModuleRequest/TRALModuleResponseguardam o objeto do núcleo (Core) e escrevem nele, então o corpo nunca é copiado por causa deles. Rota com handler do núcleo roda direto; o contexto só é montado para as outras. -
overloadnumAnswerdo descendente soma às sobrecargas herdadas, no Delphi e no FPC; sem a diretiva, as esconde. -
Ciclo de vida:
ServerActivatinglevantando mantém o servidor parado;ServerDeactivatingpode correr com pedidos ainda rodando, e a exceção vai paraOnServerError- parar sempre termina. Nada dispara em design, carga ou destruição (CanNotify); um módulo lido do form com o servidor ativo ouve no próprioLoaded. -
Não publique
Routesnum módulo que cria as rotas no construtor: o form as guardaria, e a carga as recriaria sem handler.
-
Pool de motores (
PoolConnection): um motor tem uma conexão e só atende um pedido por vez. Com o pool, a thread pega um emprestado e devolve, e o pool se acomoda na concorrência real do cliente; o motor emprestado sai do pool, então duas threads nunca dividem uma conexão. Sem o pool (Enabled = False) fica um motor para a primeira thread e um motor novo por pedido para as outras: socket e handshake TLS a cada pedido. Medido com 20 processos, um cliente de três threads cada, contra mORMot2 em TLS: 448 pedidos/s sem o pool, 3816 com ele. - A chave do pool (
TargetKey, esquema + host + porta) prefere um motor já apontado para o destino; sem ele, reaproveita outro (o motor percebe e reconecta) em vez de crescer o pool.IdleTimeouté conferido ao pegar e ao devolver, sem timer. -
Pool não é
ShareConnection: este divide o transporte entre motores (netHTTP, OkHttp, MsQuic); o pool reaproveita o objeto motor, que é do que Indy, mORMot2 e fpHTTP dependem. Os dois ligados convivem.MaxIdlelimita conexões só onde cada motor tem a sua; com transporte compartilhado, limita memória. -
TRALThreadClientpega o motor do pool só com o pool ligado: o construtor roda na thread que chama, e com o pool desligadoAcquireEngineentregaria a ele o motor guardado para ela. Devolve o motor emExecute, não no destrutor: o destrutor roda depois doOnTerminate, que libera o chamador para o próximo pedido, e o pool não teria o motor a tempo. -
Um
Requestpor thread: o uso "preenche e chama" não se protege com lock, porque o chamador segura o objeto entre instruções. Cada thread tem o seu, e a que criou o cliente fica com o original. A primeira leitura de outra thread copia o do criador, para "preenche na principal, chama no worker" mandar o que foi preenchido; o criador não trava enquanto preenche, então quem lê só deve começar depois. Threads são distinguidas porThreadToken, nunca pelo id do sistema, que a próxima thread herda. -
AdvanceIndexUrlsó move o índice de failover quando ele ainda está onde o pedido começou: dois avanços simultâneos viram um (no máximo uma tentativa falha a mais), mas o índice nunca volta para um servidor já morto.
-
Uma tentativa por
BaseURL, sem piso: com um piso, uma URL só receberia o mesmo pedido várias vezes, e um POST com timeout seria gravado mais de uma vez. -
Recusas antes do socket (TLS exigido, pin sem suporte,
rhv2sem suporte,rhv10), nunca dentro do callback TLS, que roda na pilha do OpenSSL. -
Tentativa não é chamada: o failover e a repetição do 401 são tentativas novas,
com
Attemptmaior; quem quer a chamada ignoraAttempt > 1.OnBeforeExecuteroda antes de qualquer rede, inclusive a busca do token (quem recusa por falta de rede não paga o token);AInfoéconstporque reescrever a URL ali passaria por cima do pin já decidido.OnAfterExecutesempre faz par, inclusive na recusa, levantada dentro dotry.ErrorMessagevem doExceptObjectsó quando algo está desenrolando: numa saída normal ele pode ser a exceção de um handler externo. - O lock é do autenticador, não do cliente: um autenticador serve muitos clientes (o DAO faz um por dataset), e enquanto um busca o token os outros esperam e depois o acham pronto.
-
SetTransportErrorpõeErrorCode-1 quando o motor passa 0, porque é oErrorCodeque o resto lê para saber que falhou, eStatusCode0, o único valor que todo motor pode dar quando não houve resposta. -
Quem cria a resposta e levanta, a libera:
ExecuteSinglelibera a resposta quando levanta, eGet/Post/... zeramAResponseantes, porque a atribuição no chamador não roda e umfinallydele liberaria o que a variável tinha.
-
AcceptServerCerté o único lugar que julga: o evento decide, senão o pin, senão o motor. Os motores só traduzem o callback paraTRALCertInfo(mesmos campos em todo motor, compilador e plataforma; o que o motor não sabe vem vazio).Host/Portvêm da URL escolhida pelo RAL, não do certificado: é isso que deixa um handler servir vários servidores. -
SSL.Pins:=separa o lugar do hash porque os dois lados têm:(AB:CD:...,[::1]:8443);RALSplitHostPorté o único parser dos dois. Uma linha que não é SHA-256 levanta ao entrar na lista, não no primeiro pedido, onde pareceria troca de certificado. O formato e as receitas estão em HTTPS no cliente. -
svEngineé o padrão porque ligar a verificação quebraria HTTPS no Windows para Indy e fpHTTP, cujo OpenSSL não tem repositório de certificados lá.CertCheckWantedsó a liga quando um pin ou o evento pedem. -
CertPolicyKey: uma conexão TLS foi julgada uma vez, no handshake, por quem a abriu; outro cliente que a reusa não faz handshake e herdaria um veredito que não deu. Por isso só divide conexão quem julga igual. Fica na base porque todos os motores que dividem precisam da mesma resposta. -
LeavesTLS/TLSRequired: os motores seguem redirect sozinhos, e um 301 parahttp://tiraria a chamada do TLS por trás doSSL.Requirede do pin.
-
Supports*eMinKeepAliveIntervalsão métodos de classe porque a IDE pergunta a um motor só escolhido no Object Inspector, sem instância. -
IsPropertyRelevantescondeShareConnectiononde o motor não divide conexão (um motor de uma conexão por objeto serializaria as chamadas se dividisse) eHTTPVersiononde não há escolha.SetEngineTypefixaHTTPVersionem 1.1 ourhvDefault, porque umrhv2deixado por outro motor faria todo pedido levantaremHTTP2Unsupported. -
KeepAliveInterval: no HTTP/2 a conexão é longa e compartilhada, e um par que some (Wi-Fi caindo, troca de ponto de acesso, servidor reiniciando) não deixa rastro; o TCP não avisa e a chamada só falharia noRequestTimeout. O OkHttp manda PING a cada intervalo e derruba quando o pong não volta no mesmo intervalo, sem mínimo. O netHTTP usaWINHTTP_OPTION_HTTP2_KEEPALIVE, que começa depois do intervalo de inatividade, recusa menos de 5000 ms e só existe no Windows 11 (sem ele o pedido vai em HTTP/2 sem ping). O piso é aplicado na atribuição, para o valor lido ser o valor em vigor. QUIC multiplexa sem pedir HTTP/2, por isso não exigerhv2. -
MaxRedirectsmora no cliente porque cada motor tinha um limite que ninguém escolheu (Indy 3, mORMot2 3, fpHTTP 255, netHTTP o doTHTTPClient). -
AcceptEncodingé o que o cliente lê;CompressTypeé o que ele envia. UmAccept-Encodingposto noRequestvence os dois. -
Jar de cookies (
CookieJar): é doTRALClient, não do motor. Os motores que o cliente empresta veem os cookies uns dos outros, e dois clientes nunca, nem dividindo transporte. O motor usa o tipo de jar da sua biblioteca (regras de domínio, caminho e validade), mas não deixa a biblioteca escrever oCookie.
-
Propriedade que não se aplica some do Object Inspector, e quem decide é o próprio
componente (
TRALComponent.IsPropertyRelevant): a unit de registro nunca distingue um motor do outro, e motor nenhum depende da IDE. É conforto, não regra: o valor escondido fica e é ignorado; escolha impossível nunca se esconde, levanta exceção dizendo por quê. O Delphi filtra por uma segunda interface no selection editor (ISelectionPropertyFilter), o Lazarus por método virtual mais atributo. Com seleção mista, só some o que nenhum dos selecionados acha relevante. -
O mesmo um nível abaixo (
TRALNestedProperty): dentro de uma propriedade objeto (oSSL), o componente é perguntado pelo nome composto ('SSL.CertificateFile'), então um sóIsPropertyRelevantresponde pelos dois níveis. É registrado pelo tipo base, e vale para oSSLde todo motor sem uma linha de design-time em cada um. -
Classe guardada morre com o pacote dela: toda unit que registra uma classe numa
lista do RAL a tira no
finalization(motor:UnregisterEngine; driver de banco:UnregisterDatabase; storage:UnregisterStorageLink). A IDE descarrega e recarrega um pacote quando o recompila, ou quando recompila um de que ele depende, e oPascalRAL.bplfica carregado: a lista apontaria para a classe liberada, e o próximoEngineType :=ouIsPropertyRelevantseria um access violation. Cada unit tira só a própria entrada, e registrar o mesmo nome de novo troca a classe, então um motor de terceiros semfinalizationvolta a funcionar quando o pacote dele recarrega. OGetClassnão tem esse problema: a RTL tira as classes de um módulo quando ele descarrega.
-
Uma pasta por submódulo, um pacote cada.
RALModuleXMLPackage(leitor, escritor, DOM, storage) eRALModuleSOAPPackage(módulo, exportador de WSDL, cliente, ação). O pacote principal não conhece nenhuma unit nova: ganhou só pontos de extensão (TRALModuleRoutes.DefaultContentType,prtXMLemTRALRouteParamType,TRALClientClasspara oClonede um descendente, e os virtuaisAllowNative/NativeContentType/PrepareForDataSetdoTRALStorageLink). O nome do pacote Lazarus não pode ser o de uma unit dele:ralxml.lpkgera a unitralxml.pas, que esconde aRALXMLde quem a usa. - O leitor é pull e trabalha em bytes UTF-8. Toda entrada é convertida para UTF-8 uma vez; os nomes vão para uma tabela de nomes por documento, para que comparar nomes seja comparar ponteiros na maioria das vezes. O DOM libera e serializa sem recursão: um documento muito fundo não estoura a pilha.
-
O módulo SOAP escolhe a operação antes da autenticação. O roteamento acontece
no
CanAnswerRoute, que os plugins chamam para saber de quem é a rota: ali só se lê o começo do envelope (versão, primeiro elemento doBody) com o leitor pull, e o que se leu fica noRouteDatado pedido. O envelope inteiro só é montado quando o tratador pede (Document). As duas rotas do próprio endpoint (o WSDL noGET, o "operação não encontrada" noPOST) ficam numa coleção interna, fora deOperations, para não irem ao.dfm. -
Toda exceção do módulo vira
Fault.HandleExceptionresponde o envelope na versão do pedido, com o status da norma; o texto passa porErrorText, para oHideErrorDetailsvaler também aqui. -
O cliente é um
TRALClient. Por issoClonecria a classe do objeto (TRALClientClass(ClassType)) eCopyPropertiesé virtual: o DAO e as threads clonam o cliente, e um clone que virasseTRALClientperderia as operações. O cliente SOAP nasce comCompressType := ctNone: quase nenhum servidor SOAP lê um pedido comprimido (o WCF responde 400). Uma chamada em thread monta o envelope na thread de quem chamou - osParamssão do formulário - e entrega a resposta peloOnTerminate, na thread principal; o destrutor espera as chamadas pendentes.
Começar
Servidor
- Rotas e parâmetros
- Ler o pedido e responder
- 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
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