You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Browse filesBrowse the repository at this point in the historyBrowse files
tempraturbo
committed
Add OnBeforeExecute and OnAfterExecute to the client
Both live in TRALClientHTTP.BeforeSendUrl, the single funnel every engine
already goes through, so one implementation serves all six engines on Delphi
and FPC without touching an engine unit.
They report an attempt, not a call: the BaseURL failover and the 401 resend
fire the pair again with Attempt one higher, which is the only way a handler
can see the failover and time the right thing.
OnBeforeExecute runs after the URL and its TLS policy are settled and before
any network work, the token fetch included, and may refuse the attempt through
ACancel/ACancelReason - failing it with the new rteCancelled, appended so every
existing TRALTransportError value keeps its ordinal and CanSwitchURL's else
already declines to resend it. OnAfterExecute always pairs with it, the refusal
included, so a handler may count in one and discount in the other.
Copy file name to clipboardExpand all lines: CLAUDE.md
+14Lines changed: 14 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -161,6 +161,20 @@ Two engine traps live under this:
161
161
162
162
Verified with `testes_ral_matriz/timeout` (repro `tmout.dpr`, verifier `tmfix.dpr` + `fpc/tmfixfpc.lpr`), across Indy, mORMot2, netHTTP and fpHTTP.
163
163
164
+
### The application's own say over each attempt (`OnBeforeExecute`/`OnAfterExecute`)
165
+
166
+
`TRALClient.OnBeforeExecute` and `OnAfterExecute` live in `TRALClientHTTP.BeforeSendUrl` — the same single funnel as the resend above — so **one implementation serves every engine on both compilers**: no engine unit knows they exist, and `ExecuteThread`, `ExecuteSingle` and `TRALThreadClient` all pass through them.
167
+
168
+
They report an **attempt, not a call**, and that is deliberate: `BeforeSendUrl` rotates `BaseURL` on a transport failure and repeats once on a 401, so one `Post` can be three attempts. Each reports itself with `TRALExecInfo.Attempt` one higher; collapsing them would hide the failover and time the wrong thing. Whoever wants the call rather than the attempt ignores `Attempt > 1`. Fields the client cannot know yet come back empty, never invented — the same rule as `TRALCertInfo`.
169
+
170
+
`OnBeforeExecute` runs **after** the URL is settled and the TLS policy for it enforced, and **before any network work at all** — the `AutoGetToken` fetch included, since that one is a request of its own: whoever refuses for lack of connectivity should not pay for a token round trip first. `AInfo` is `const` on purpose (rewriting the URL there would slip past the pin decided just above), while `ARequest` is not — adding a header or a param is the point of the hook.
171
+
172
+
Setting `ACancel` fails the attempt with `TransportError = rteCancelled` and raises, instead of the application having to raise from inside the engine's stack. `rteCancelled` is **appended** to `TRALTransportError`, so every existing value keeps its ordinal and `CanSwitchURL`'s `else` already declines to resend it — nothing went out, so there is nothing to resend anywhere. `ACancelReason`, when given, *becomes* the message verbatim; left empty, RAL uses `emRequestCancelled` with the URL.
173
+
174
+
`OnAfterExecute`**always pairs with `OnBeforeExecute`** — including when the attempt raised, and including when the application itself refused it, which is why the refusal is raised from *inside* the `try` whose `finally` calls it. A handler may therefore count in one and discount in the other without ever losing a pair. `TRALExecInfo.ErrorMessage` comes from `ExceptObject`, not from `AResponse`: an exception that never reached `SetTransportError` would otherwise arrive indistinguishable from success. Both hooks run on the **calling** thread with no `Synchronize`, so under the default `ebMultiThread` they run on the `TRALThreadClient` and not on the main thread — reaching the UI from there is the handler's own business.
175
+
176
+
`CopyProperties` carries both, next to `SSL` and `OnValidateServerCert`: the DAO clones its client, and a clone that lost the hooks would stop reporting.
177
+
164
178
### A published `default` that disagrees with the constructor silently wins
165
179
166
180
`TRALClient.ConnectTimeout` declared `default 5000` while the constructor set 30000, and `RequestTimeout` declared `default 30000` while the constructor set 10000 — the two were swapped. The directive is not decoration: streaming skips writing a property whose value equals it, so typing exactly `5000` into the Object Inspector produced a `.dfm` with no `ConnectTimeout` at all and a component that ran with 30000. It never showed up in code-driven tests, where `default` has no effect whatsoever — only in the normal use, dropping the component on a form.
Copy file name to clipboardExpand all lines: src/languages/ralconsts_enus.inc
+1Lines changed: 1 addition & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -13,6 +13,7 @@
13
13
emCertNotInspectable = 'The %s engine could not inspect the server certificate on this connection, so SSL.Pinsand OnValidateServerCert cannot be applied. Use the Indy engine.';
14
14
emCertPinInvalid = 'Every SSL.Pins line must end in the SHA-256 of the certificate: 64 hex digits, with or without separators.';
15
15
emCertPinUnsupported = 'SSL.Pins is not supported by the %s engine on this platform: it cannot read the certificate fingerprint. Use the Indy engine, or decide in OnValidateServerCert.';
16
+
emRequestCancelled = 'The request was refused by OnBeforeExecute: %s.';
16
17
emCertRejected = 'Server certificate refused.';
17
18
emCertRequiresTLS = 'SSL.Required is set, or a line of SSL.Pins applies to this host, so plain http is refused: %s.';
Copy file name to clipboardExpand all lines: src/languages/ralconsts_eses.inc
+1Lines changed: 1 addition & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -13,6 +13,7 @@
13
13
emCertNotInspectable = 'El motor %s no pudo inspeccionar el certificado del servidor en esta conexión, por lo que SSL.Pins y OnValidateServerCert no pueden aplicarse. Use el motor Indy.';
14
14
emCertPinInvalid = 'Cada línea de SSL.Pins debe terminar en el SHA-256 del certificado: 64 dígitos hexadecimales, con o sin separadores.';
15
15
emCertPinUnsupported = 'SSL.Pins no es compatible con el motor %s en esta plataforma: no puede leer la huella digital del certificado. Use el motor Indy o decida en OnValidateServerCert.';
16
+
emRequestCancelled = 'La solicitud fue rechazada por OnBeforeExecute: %s.';
16
17
emCertRejected = 'Certificado del servidor rechazado.';
17
18
emCertRequiresTLS = 'SSL.Required está definido, o una línea de SSL.Pins aplica a este host, por lo que se rechaza http sin cifrar: %s.';
18
19
emCompressInvalidFormat = 'Formato de compressión no és válido.';
Copy file name to clipboardExpand all lines: src/languages/ralconsts_ptbr.inc
+1Lines changed: 1 addition & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -13,6 +13,7 @@
13
13
emCertNotInspectable = 'O engine %s não conseguiu inspecionar o certificado do servidor nesta conexão, então SSL.Pins e OnValidateServerCert não podem ser aplicados. Use o engine Indy.';
14
14
emCertPinInvalid = 'Toda linha de SSL.Pins deve terminar no SHA-256do certificado: 64 dígitos hexadecimais, com ou sem separadores.';
15
15
emCertPinUnsupported = 'SSL.Pins não é suportado pelo engine %s nesta plataforma: ele não consegue ler a impressão digital do certificado. Use o engine Indy ou decida no OnValidateServerCert.';
16
+
emRequestCancelled = 'A requisição foi recusada pelo OnBeforeExecute: %s.';
16
17
emCertRejected = 'Certificado do servidor recusado.';
17
18
emCertRequiresTLS = 'SSL.Required está definido, ou uma linha de SSL.Pins vale para este host, então http puro é recusado: %s.';
18
19
emCompressInvalidFormat = 'Formato de compressão inválido.';
0 commit comments