#1 Re: mORMot 2 » SChannel server: ESChannel WSAENOTSOCK on stop with keep-alive client » 2026-11-10 06:09:45

Thanks for the detailed report - your analysis is right, and the root cause is a bit more serious than a logged exception.

On Windows, TCrtSocket.Abort (called by THttpServerResp.Shutdown from THttpServer.Destroy) calls closesocket() to release the blocking recv() of the connection thread - shutdown() is not enough on Windows, I checked. But fSock keeps its value, so when the connection thread then closes its socket, TCrtSocket.Close:

releases the TLS layer, and TSChannelNetTls.Destroy sends its close_notify over the already closed handle -> WSAENOTSOCK -> the ESChannel you see (absorbed, but logged);
then calls ShutdownAndClose = a second closesocket() on the same value.
Windows reuses SOCKET values immediately, so this handle may already belong to another connection of the process: the close_notify could be sent to it, and the second closesocket() closes it. The regression test reproduces exactly this: without the fix, a socket opened just after Abort() is closed by the aborted one.

Proposed fix, as a single commit:
https://github.com/synopse/mORMot2/comp … bort-close

TCrtSocket.Abort sets a new fAbortClosed flag before its closesocket() (Windows only) - before, since the woken up thread calls Close at once;
TCrtSocket.Close then skips the second closesocket() and calls a new INetTls.SocketClosed method before releasing the TLS layer, so SChannel won't send close_notify any more, and OpenSSL won't call SSL_shutdown();
new TNetworkProtocols.CrtSocketAbort test, with a fake INetTls (no certificate needed): sequential Abort + Close, and Abort releasing a recv() blocked in another thread.
Tested with FPC 3.3.1 on Win64 aarch64 and Linux aarch64 (TNetworkProtocols), compiled with Delphi 13 Win64.

@vs: could you try this branch with your keep-alive browser scenario, and see if the release-mode leak / heaptrc AV also disappears? I could not reproduce that one, so it may be unrelated.

#2 Re: mORMot 2 » Server-side large file upload (issue #292) — proposal » 2026-10-23 13:35:11

Not out of the box, as far as I can see. OnBodyDownload/OnBeforeBody live on the HTTP server and run before the body arrives, but the session check is done later in TRestServer. TRestHttpServer doesn't expose a stream to a TRestServer method either. For now you'd have to validate the session yourself in OnBeforeBody (return 401/403) and handle the upload there, or put a small authenticated endpoint in front. Wiring this into TRestServer would be a separate change.

#3 Re: mORMot 2 » There is a problem with the variant in the latest version of Mormot2 » 2026-10-23 13:32:30

Thanks for bisecting. I can't reproduce it with your test on FPC 3.3.1 aarch64-win64 (-O1/-O2/-O3) or FPC 3.2.2 aarch64-linux (-O1/-O3), all print 200ok. So it looks like an i386 optimizer problem in FPC trunk rather than a logic bug. Does it go away with -O2 for mormot.core.variants.pas only? And which FPC revision is it (fpc -iW)?

#4 Re: mORMot 2 » Proposing a `mormot.ai.*` extension — Model Context Protocol (MCP) » 2026-10-20 03:35:32

Impressive. If you bump the submodule and something is still missing for the TTS side, please tell me. Here is a summary of the changes since the last pin in your project.

- TDecoderModel.ForwardEmbeds - a forward pass with embedding rows instead of token ids, on the CPU, Vulkan and CUDA. Your talker and code predictor can feed their rows directly. This should make the copied pass in mormot.ai.tts.qwen.lm.vulkan.pas unnecessary. Your qwen3-tts-lm reference runs as a test (talker states within 1.8e-4 of PyTorch, the predictor codes are identical).
- TDecoderModel.CreateStack - loads one decoder stack out of a larger checkpoint by its prefix (talker and code predictor of Qwen3-TTS).
- ForgetWeights on the backend and on the model - this fixes the stale GPU weights you reported. New rule: free a model before its backend.
- TBpeTokenizer.CreateFromVocabMerges for vocab.json + merges.txt. It accepts only what transformers 5.19 loads with the same ids; for Qwen3-TTS it gives the reference ids.
- BF16 and F16 rows in InferRowToF32.
- TInferThreadPool.RunWorkers passes the worker index, so a job can keep its own scratch buffer instead of allocating. For you, allocating inside a job only cost time under Delphi (FPC's heap works per thread).
- The KvF16 docs now say when to turn it off. A talker loop that feeds floats back into the model is one such case: that is where your 2.3e-2 came from.

I also added gpt-oss on the CPU (attention sinks, YaRN, biased and clamped experts). MXFP4 weights and the GPU paths are next.

#5 Re: mORMot 2 » Proposing a `mormot.ai.*` extension — Model Context Protocol (MCP) » 2026-10-19 06:08:26

@flydev - thank you, that was quick. The qwen3-tts-lm dataset is now in mormot2-ai
(tests/data/infer-ref/qwen3-tts-lm, unchanged from your commit 8f5a846, with your two
generator scripts under tools/infer-ref/qwen3-tts and the provenance in UPSTREAM_BASE and
NOTICE). It will be the regression test for the embeddings-input forward: both stacks
(talker and code predictor), layer states and logits, on CPU, Vulkan and CUDA.

You are right about the weight cache: both GPU backends look weights up by host address,
type and size, so a buffer freed and reallocated at the same address is served stale. The
unit header documents it as a contract, but it is a trap as soon as several stacks or
changing weights share one backend - exactly your case. It is now in the backlog next to
the embeddings input: a way to forget a weight or an address range when its owner frees
it, instead of ClearWeights for everything, on Vulkan and CUDA alike, with a test that
reuses the same address.

And thanks for publishing mormot2-qwen-tts. I will report back here when it is pushed.

#6 Re: mORMot 2 » Proposing a `mormot.ai.*` extension — Model Context Protocol (MCP) » 2026-10-18 15:31:13

@flydev - thanks a lot. A native TTS
pipeline at x5.9 real time with ~95 ms to first sound is impressive, and it is good to see the
loader/tokenizer/backends hold up outside the Qwen3 path they were written for.

I went through your points against the code; all of them are now in the backlog:

1. BF16 in InferRowToF32 - correct, it only knows F32/Q8_0/Q4_K/Q6_K today (the decoder widens
   BF16 on its own path). BF16 and F16 will be added there.

2. TBpeTokenizer and vocab.json + merges.txt - correct, only tokenizer.json and GGUF are
   supported. A third constructor for the GPT-2 style file pair will feed the same Build, so
   no more subclassing; the pre-tokenizer then has to be chosen explicitly (GPT-2 by default),
   since those two files do not carry it.

3. Embeddings as input instead of token ids - the most important one, and it will come first.
   The plan is a forward that takes an embedding matrix (optionally mixed with ids inside one
   chunk), with the same positions/KV handling, on CPU, Vulkan and CUDA alike. TTS, vision and
   audio models all need it, and you should not have to copy the push-constant records.

4. F16 KV cache - yes, that is the expected trade-off (it is on by default for long contexts
   and can be switched off). I will document the cost more clearly and consider defaulting to
   F32 for short contexts, where autoregressive loops like your talker/predictor accumulate
   the error.

5. Heap allocation in pool work items - the library's own jobs do not allocate inside the
   items, but you are right that the API makes the safe pattern hard: a TOnInferItem does not
   know its worker, so it cannot use a per-thread scratch buffer. I plan a variant that passes
   the worker index (or a per-worker scratch area kept by the pool), plus a note in the unit
   that items must not allocate.

I will post here when the first of these is pushed. If your TTS code ever becomes public,
I would be glad to add a pointer to it - and if you have a small reference (input embeddings
-> logits) from your model, it would make a nice regression test for point 3.

#7 Re: mORMot 2 » Proposing a `mormot.ai.*` extension — Model Context Protocol (MCP) » 2026-10-15 20:27:39

Thanks a lot, Arnaud!

SQLite3: both commits replace exactly the two workarounds we carried, and we already switched to them.

  • Our RollbackIfOpen existed because a failed Commit (e.g. SQLITE_BUSY) cleared the flag, so the RollBack afterwards did nothing. It is now a plain non-raising RollBack, and a new regression test checks it: a COMMIT fails against another connection's read lock, then RollBack closes the transaction.

  • Our own db_config/load_extension helpers are gone. Your LoadExtension is better, since it restores the previous state and never enables the SQL function.

One small observation: LoadExtension takes a TFileName. On non-Unicode compilers (Delphi 7, FPC on Windows), a path with characters outside the ANSI code page is lost before it reaches UTF-8, so a RawUtf8 overload would avoid that. It is not urgent for us, as those extensions will go once our native engine fully replaces them.

GPU: Vulkan first, CUDA second. Vulkan runs on NVIDIA, AMD, Intel (your B580), Adreno and, through MoltenVK, Apple.

  • On speed, llama.cpp's community benchmarks (indicative only) show token generation on NVIDIA about equal to CUDA, and prompt processing at about 70-95 % of CUDA. For example, an RTX 5090 does 11.8k vs 15.0k tokens/s pp512 and 274 vs 300 tokens/s tg128. So we keep CUDA as a second backend for maximum prompt throughput on NVIDIA.

  • For the binding we would use only PasVulkan's stand-alone Vulkan.pas header (MIT-style licence, Delphi 7 and FPC). It loads the system Vulkan loader dynamically, so we ship no DLL, and without a GPU the executable still runs on the CPU.

  • The kernels would be compute shaders compiled offline to SPIR-V and embedded in the executable; the MIT-licensed ggml-vulkan shaders are a good source.

#8 Re: mORMot 2 » Proposing a `mormot.ai.*` extension — Model Context Protocol (MCP) » 2026-10-14 17:51:11

mormot2-ai - progress update (October 2026)

A short update on where the mormot.ai.* extension stands since the summer. Repository and README: github.com/landrix/mormot2-ai (same three-license as mORMot 2).

Where it lives
Arnaud, thanks for discussing a possible inclusion of mormot.ai.* in the framework. We decided to maintain it as a separate extension built on mORMot 2, with its own roadmap (own inference engine, later CUDA). mORMot 2 remains the foundation: HTTP server, JSON, RTTI, SQLite and crypto are all yours.

What it does today

  • MCP server, revision 2026-07-28 (the new stateless one). Tools are typed records with the input schema generated from RTTI. Transports: stdio, HTTP, Streamable HTTP, in-process. Optional OAuth 2.1 resource server. Our own application (Landrix) already uses it as its MCP endpoint.

  • LLM clients: the OpenAI wire (OpenAI, LiteLLM, Ollama-compatible) plus a native Anthropic driver. Streaming, tool calls, structured output, vision, and an agent/tool-calling loop that can use MCP tools.

  • Embeddings, vector search, RAG: one IEmbedder seam. A native vector store inside mORMot's SQLite (float32 rows, 1-bit RAM index with exact rescoring) replaced sqlite-vec; measured at 2 million x 1024 vectors.

  • Our own inference engine in plain Object Pascal, new since the last post. It runs BGE-M3 embeddings from the same GGUF Q8_0 file that sqlite-lembed (llama.cpp) uses, with the same quality: nDCG@10 0.9004 vs 0.8963, cosine >= 0.999 against lembed's vectors. It needs no Python, no llama.cpp and no foreign .so/.dll. The pieces: GGUF and safetensors loaders, a SentencePiece/Unigram tokenizer, the XLM-RoBERTa encoder, NEON and AVX2 kernels, and a dedicated spinning thread pool for the few hundred short parallel steps per text (TSynParallelProcess has a 1 ms sleep in its acquisition retry path, which adds up at that rate).

Speed, same corpus (320 texts), same model file, one text at a time:

  • Core i9-14900K, AVX2: native 1039 tokens/s vs lembed 524. Peak memory 377 vs 514 MB.

  • Snapdragon X, NEON: on par to 15% faster than lembed, with less memory.

Side finding on Windows 11: started over SSH, both engines ran at about half speed on that machine; switching off execution-speed power throttling for the process with SetProcessInformation(ProcessPowerThrottling) restored the numbers above.

Where it runs
Same compiler range as mORMot 2: FPC 3.2.2 / 3.2.4 and Delphi 7 to 13. I said in September that I would drop Delphi. I changed my mind, and it is now first class: the core is non-generic, generics are only an optional shell, and there is no exit(value) and no with. The test suites pass on:

  • Linux aarch64 (FPC): MCP, LLM and inference

  • Windows x64 (FPC and Delphi 13): MCP, LLM and inference

  • Windows x86 (Delphi 7): MCP, LLM and inference

  • macOS aarch64 (FPC): MCP and LLM

  • Windows ARM64 (FPC 3.3.1, with an external sqlite3-64.dll): MCP and LLM

Next: the inference suite on Linux x86_64 (our production target, Docker), macOS and Windows ARM64.

Credits

  • flydev: the MCP server units were adopted from your mormot2-extensions, original headers kept, and your commit is pinned for later diffs. Thank you.

  • JP, following Arnaud's pointer: we evaluated neural-api for the embeddings; the version we looked at already brought a lot of encoder and tokenizer support. Our specific needs - BGE-M3 from GGUF with its charsmap normalisation, NEON, concurrent callers, and every mORMot 2 compiler including Delphi 7 - led us to a dedicated inference engine instead.

What is planned

  1. Native embeddings as our default (after the Linux x86_64 run and a long indexing run).

  2. Decoder LLMs: Qwen3-0.6B first (BPE, KV cache, GQA) behind the same ILlmClient interface as the cloud providers, with streaming and tool calls.

  3. Q4 / K-quants and sampling.

  4. CUDA through the driver API with embedded PTX.

Feedback, issues and questions are welcome, in particular from anyone who wants to run it on a platform we have not listed.

#9 Re: mORMot 2 » mORMot PDF crossplatform » 2026-10-01 05:39:15

Following your mormot.lib.*.pas remark, I could write mormot.lib.freetype and mormot.lib.harfbuzz as thin TSynLibrary bindings for what the backends load today. That doesn't touch Martin's code.

For the reader: in XRechnung for Delphi I have a small native parser that extracts the embedded invoice XML from ZUGFeRD/Factur-X files. It follows the xref chain including incremental updates, handles the usual stream filters and RC4 with an empty user password. Not a full reader, but maybe a starting point. It is my code, so I can contribute it under the mORMot licence.

How to split the units is Martin's call, of course. Martin, just tell me where I can help.

#10 Re: mORMot 2 » mORMot PDF crossplatform » 2026-09-30 12:59:39

Hi Martin, hi Arnaud,

following Arnaud's idea of bringing this into the trunk under src/pdf, I had a look at what it would take, and I would like to help with it.

A first step is done in my fork, branch mormot2-merge: landrix/mORMot2-PDF-CrossPlatform
It ports the trunk commits to mormot.ui.pdf.pas since the fork was started (among them the EMF PT_BEZIERTO fix and EMR_ALPHABLEND), and replaces the copies of mormot.ui.core, mormot.ui.gdiplus and mormot.lib.uniscribe by the trunk units. mormot2.lpk now contains mormot.lib.uniscribe itself, which shadowed the fork's copy, so GetGlyphIndicesW is now imported in mormot.ui.pdf directly. Tests are green with FPC on Windows aarch64 and Delphi 13 Win32/Win64. On Linux aarch64 the only failures are 5 checks that need CJK/Arabic fonts the machine does not have. Martin, I can send it as a PR whenever you like.

The rest looks mostly mechanical to me: licence headers, OSWINDOWS/OSDARWIN instead of MSWINDOWS/DARWIN, {$I ..\mormot.defines.inc}, the usual mORMot formatting, the roadmap references out of the source comments, ASCII-only sources, FreeType/HarfBuzz loaded via TSynLibrary, and the tests as test.pdf.*.pas in mormot2tests. I am happy to do this in small PRs. Martin, just tell me which parts you would rather do yourself, so we don't get in each other's way while you are still working on it.

Before touching the structure, three questions for Arnaud:

  1. Platform backends: keep the interfaces that register themselves in initialization, or rather per-OS .inc files included by the unit?

  2. The new TGDIPages is a TComponent, the trunk TGdiPages a TScrollBox with a much bigger API. Should the new one replace mormot.ui.report, or live next to it in src/pdf?

  3. HAS_UI_PDF currently means Windows + VCL/LCL. Extend it to POSIX, with a separate flag for TPdfDocumentGdi and the metafile part?

One more thing: the invoice XML in zugferd_demo is Apache-2.0, which does not go well with the GPL option of the mORMot licence. We would need our own sample data there.

#11 Re: mORMot 2 » mORMot PDF crossplatform » 2026-09-25 03:54:50

mdoyle wrote:

The engine now writes PDF/A-3U and PDF/UA-1 in one file.

The new example zugferd_demo builds a ZUGFeRD / Factur-X invoice (profile EN 16931): a tagged, accessible PDF with the invoice XML embedded. The invoice data is public KoSIT test data. The result passes veraPDF, the Mustang validator and PAC 2024 on Windows, Linux and macOS.

Feedback welcome, especially if you try it with your own validator or accounting software.

A dream is coming true. I'm going to take a look at it right away today.

#12 Re: mORMot 2 » mORMot PDF crossplatform » 2026-09-23 22:19:33

Oh, exciting. I'll keep an eye on it.

#13 Re: mORMot 2 » Proposing a `mormot.ai.*` extension — Model Context Protocol (MCP) » 2026-09-09 08:26:41

Okay, I’ll try to remain Delphi-compatible for as long as possible and remove any FPC-only references for now. The reason is that I’ll be using https://github.com/joaopauloschuler/neural-api in places, and that is FPC-only—but we’ll see.

#15 Re: mORMot 2 » Proposing a `mormot.ai.*` extension — Model Context Protocol (MCP) » 2026-09-04 10:51:10

It seems to be working for the time being. However, I will no longer make allowances for Delphi or implement any special handling.

#17 Re: mORMot 2 » mORMot2 Daily - the commit log » 2026-09-03 08:01:07

This will be my new browser start page. :-)

#18 Re: mORMot 2 » Server-side large file upload (issue #292) — proposal » 2026-09-03 07:58:37

Streaming a response body without buffering it is now part of mORMo2. In principle, this topic is now closed.

https://github.com/synopse/mORMot2/pull/554

#19 Re: mORMot 2 » Server-side large file upload (issue #292) — proposal » 2026-08-20 08:25:41

The issue https://github.com/synopse/mORMot2/issues/292 is effectively resolved with the latest PR.

Is there any interest in me also dealing with the other side?

https://github.com/synopse/mORMot2/issues/509

#21 Re: mORMot 2 » Server-side large file upload (issue #292) — proposal » 2026-08-17 09:03:59

The hash was only in the test (test.net.proto.pas) - changed to
crc32cHash(body, crc32cHash(body, 0)), tests still pass.

About the lifetime: TFileStreamEventuallyDelete alone can't work with the
current flow, because we free the stream as soon as the body is complete,
i.e. before OnRequest runs - the spooled file has to survive until the
request is done. It fits perfectly once the stream stays alive until the
end of the request (the InContentStream idea). Then we can have both:
the class for the "delete it for me" case, and rfContentFileNameNeedDelete
for any other stream kind. I can implement it that way if you agree.

TPipeStream: it can work, but not out of the box.
- Write() blocks until the whole buffer is consumed (default timeouts are
  INFINITE), so in THttpAsyncServer a slow consumer stalls an event loop
  thread. With WriteTimeout or psoWritePartial a partial Write() raises
  EWriteError in WriteBuffer, which our state machine already turns into 507.
- Read() only returns a clean 0/EOF if ExpectedSize was set and reached -
  otherwise the consumer sees -1 (abort) instead of a normal end of body.
  We know the size for Content-Length, but not for chunked.
- Freeing the stream releases the buffer, so the consumer must be gone by
  then: in practice TBackgroundPipeStream, and that free blocks the server
  thread until the consumer thread finished.
I'd add a /pipe test case to pin all three down.

#22 Re: mORMot 2 » Server-side large file upload (issue #292) — proposal » 2026-08-17 07:00:47

This comment was not found. Did you delete it?

ab wrote:

Proposal: Use faster crc32cHash(mptext, crc32cHash(mptext, 0)) instead

ab wrote:

I guess it is ready for a pull request?

Still open, from your first-pass feedback: the stream lifetime. My
suggestion was a TFileStreamEventuallyDelete = class(TFileStreamEx) with a
DeleteFileOnDestroy property, which would drop ContentInputName entirely
(the deletion policy lives in the class: nginx's client_body_in_file_only
clean vs. on), plus a THttpServerRequestAbstract.InContentStream property
handing the already-open instance to the request instead of reopening it by
name.

https://github.com/synopse/mORMot2/pull/541

Note: the branch is rebased on 89e95097 rather than current master,
because 17da952a ("crypt: some ICryptCert refactoring") doesn't compile
here - CV_IGNORE_DEFAULT references cvCrlFailed/cvCrlDownloadFailed,
which aren't in TCryptCertValidity (yet?).

#23 Re: mORMot 2 » Server-side large file upload (issue #292) — proposal » 2026-08-17 06:09:26

You were right about the exception frames. 78f283fe drops the
try..except around the OnRead loop; TStream write failures are trapped at
the two ContentStream.WriteBuffer() calls in ProcessRead (only reached
when a stream is assigned) and reported as a new hrsErrorContentStreamWrite
state, which OnRead maps to 507, next to hrsErrorPayloadTooLarge -> 413.

One more pass over the whole branch found two things worth mentioning
(a2f23504): the async family started counting a chunked body from
ContentLength = -1 - GetBody normalises it to 0, ProcessRead never did -
so the limit was one byte off and a bogus Content-Length header could
skew it further. And Prepare now clears an InContentType the client sent
as '!STATICFILE' by itself ('!' is a valid token char), so a callback can
trust that marker to mean a spooled body.

Branch @ a2f23504: FPC aarch64-linux and Delphi Win32/Win64, 179
assertions in TNetworkProtocols.BodyDownload. Still open before the PR is
the stream lifetime - happy to rework it along the
TFileStreamEventuallyDelete idea whenever you've settled on the shape.

#24 Re: mORMot 2 » Server-side large file upload (issue #292) — proposal » 2026-08-13 03:28:11

Both findings confirmed and fixed in e005b09e - test-first: the new suite
cases reproduced exactly your matrix (400 on async, no status at all on
sync, and the 0 vs -1 event length) before the fix.

- A chunked body over MaximumAllowedContentLength now gets a clean 413 on
  both families before the connection closes. As a side effect this also
  fixes a pre-existing quirk where the async server reported its 1GB
  in-memory overflow as 400.
- OnBodyDownload now receives aContentLength = -1 for chunked bodies on
  both families, and the doc says so.

The ~12s deferred cleanup you measured is the async connection GC, as
documented. Thanks for the Int64/4.5GB datapoint - good to have that
confirmed on real hardware rather than just by arithmetic.

#26 Re: mORMot 2 » Server-side large file upload (issue #292) — proposal » 2026-08-01 16:04:37

Great report, thanks.

Your ENOSPC observation is implemented in fcd6afed: when writing to the
spool stream fails, both server families now answer 507 Insufficient
Storage before closing, instead of the raw reset (new
HTTP_INSUFFICIENTSTORAGE constant + StatusCodeToText entry). One caveat:
on THttpServer the 507 is deterministic (the body is fully read before
the failing write), on THttpAsyncServer it is best effort - the failure
happens mid-transfer, so if the client is still blasting the body, the
reset may still win the race. A full lingering-close would be the next
step if that turns out to matter in practice - your curl test would tell.
Covered in the regression tests with a stream that fails after 64KB.

The 7-22s cleanup delay after a hard disconnect is the async connection
instance GC - expected, and now documented in the event comment.

https://github.com/landrix/mORMot2/tree … put-stream

#27 Re: mORMot 2 » Server-side large file upload (issue #292) — proposal » 2026-08-01 08:12:43

* Done in 92470c90 https://github.com/landrix/mORMot2/comm … 54d9abce28 :
  both hooks now live in virtual sub-methods
  (THttpAsyncServerConnection.DoBodyDownload and
  THttpServerSocket.DoBodyDownload), and the managed locals are out of the
  OnRead / GetRequest hot paths.

* Regarding THttpSocket.Destroy calling Http.ProcessDone: I checked every
  place that sets rfContentStreamNeedFree.

  In the THttpSocket hierarchy, the flag is only set for streams created by
  the server:

  * ContentFromFile responses
  * the progressive static-file path
  * the new OnBodyDownload spool

  The first two are released through ProcessDone in the
  THttpServer.Process finally block. The body-download spool is released in
  DownloadBody.

  THttpClientSocket never assigns Http.ContentStream; client downloads use
  the GetBody(DestStream) parameter, and ownership stays with the caller.

  So the new call should be a no-op everywhere except for the case it is meant
  to cover: a connection being closed after the event in GetRequest, but
  before deferred body retrieval by the thread pool or THttpServerResp.

  I would still appreciate a second check in case I missed an ownership path.

* On stream lifetime, nginx as a reverse proxy spools request bodies to a
  temporary file under client_body_temp_path. proxy_request_buffering is
  enabled by default.

  client_body_in_file_only has two relevant modes:

  * clean: nginx deletes the file when the request is complete
  * on: nginx leaves the file in place, and the application becomes
    responsible for it

  In the latter case, the application receives the path through
  $request_body_file.

  The current branch effectively implements clean while exposing the file
  name through InContent, similar to $request_body_file.

  For customizable lifetime handling, I prefer the
  TFileStreamEventuallyDelete approach because it would remove the need for
  ContentInputName entirely.

  TFileStreamEventuallyDelete = class(TFileStreamEx)

  It would delete the file on destruction unless DeleteFileOnDestroy is set
  to false.

  The event would return this stream, or any other TStream as it does today.
  The server would free it through the existing rfContentStreamNeedFree paths,
  while the deletion policy would stay with the stream class.

  The default behavior would match nginx's clean mode. A handler that wants
  to keep the file could set DeleteFileOnDestroy to false or rename the file
  first, which would correspond to nginx's on mode.

  It would also be useful to let the handler reuse the already-open stream
  instead of reopening the file by name. Once the body is complete, the
  server could rewind the stream and expose it on the request, for example
  through a new property on THttpServerRequestAbstract:

  InContentStream: TStream

  The connection would remain responsible for freeing it after the request.
  InContent could still contain the file name with the STATICFILE marker,
  serving as the $request_body_file equivalent.

  This would also remove the current free-before-Prepare sequence in
  DoRequest, because the input stream would move from Http.ContentStream to
  the request instance before response processing begins.

If this design looks right, I will implement it this way. You can choose the
class name and location; mormot.core.os, next to TFileStreamEx, seems like the
natural place.

#28 Re: mORMot 2 » Server-side large file upload (issue #292) — proposal » 2026-08-01 04:10:49

The server side is ready for testing:

https://github.com/landrix/mORMot2/tree … put-stream

I added a new THttpServerGeneric.OnBodyDownload event for both THttpServer and THttpAsyncServer. The event returns a TStream, for example a TFileStreamEx pointing to a temporary file, and the request body is written there without keeping it in memory.

The request then reaches the callbacks with InContentType = STATICFILE_CONTENT_TYPE and InContent containing the file name, similar to how file-based responses are handled. The temporary file is deleted after the request unless the handler renames or moves it.

Compressed request bodies currently return 415 with Accept-Encoding: identity. Chunked bodies are limited by MaximumAllowedContentLength, since their final size is not known in advance. The previous 1 GB in-memory limit does not apply to streamed bodies.

While testing this, I found two existing bugs in the asynchronous request parser and fixed them in the same branch:

* THttpRequestContext.ProcessRead did not consume the actual chunk data from the input buffer. As a result, chunked uploads to THttpAsyncServer could stall or be rejected.
* DoProcessParseLine could read one byte beyond the buffer when a network packet ended exactly on the #13 of a CRLF sequence.

There is also a new TNetworkProtocols.BodyDownload test with 98 assertions. It covers an 8 MB spooled upload, chunked uploads through a raw socket, multipart decoding directly from the spooled file using THttpMultiPartDecoder, rejected compressed bodies, cleanup after aborted uploads, and both server implementations.

The tests are green with FPC on aarch64-linux and Delphi Win32/Win64.

@zen010101: this is the branch to try on your machines. Disk-full situations, slow connections and interrupted uploads are the cases I would especially like to see tested.

I will open the PR after getting some real-world feedback here.

#29 Re: mORMot 2 » Server-side large file upload (issue #292) — proposal » 2026-07-30 06:10:47

Thanks for the quick review and merge.

I can take the server-side implementation next, based on the approach outlined earlier in this thread:

* Add a `THttpServerGeneric` callback that is invoked after the request headers have been parsed, but before the body is read. The callback would return the destination `TStream` for the request body; returning `nil` would preserve the existing in-memory behavior. Most of the required plumbing is already in place, since `THttpRequestContext.ProcessRead` writes to `ContentStream` when one is assigned.
* Use a spooled temporary file as the default stream implementation. This should also work safely with `THttpAsyncServer` and its relatively small worker pool. `TPipeStream` could remain available for blocking server implementations.
* Expose the uploaded body to the handler using `InContentType = STATICFILE_CONTENT_TYPE` and `InContent` set to the temporary file path, mirroring the existing response-side mechanism.
* Wrap this at the REST layer through a `TRestUriContext.InStream` property.
* Extend SOA method handling so that an interface method with a single `TStream` parameter can consume the request body directly.
* In streaming mode, reject compressed request bodies with HTTP `415 Unsupported Media Type` and return `Accept-Encoding: identity`.
* Feed the resulting stream into `THttpMultiPartDecoder`, allowing multipart/form-data uploads of arbitrary size with constant memory usage. This should also address issue #292.

Does this match the architecture you had in mind?

I would follow the same workflow as before: agree on the public interface here first, then implement it on a branch together with the relevant tests.

#32 Re: mORMot 2 » Proposing a `mormot.ai.*` extension — Model Context Protocol (MCP) » 2026-07-29 05:32:51

Thanks @flydev for posting this; I’ll consolidate everything, and then we’ll see what the next steps are.

#33 Re: mORMot 2 » Server-side large file upload (issue #292) — proposal » 2026-07-27 19:50:07

Done as suggested: I added a new TTestCoreBase.MultiPartDecoder method and moved the multipart tests there from MimeTypes. MimeTypes now only covers MIME recognition, while the MIM test data array is shared by both.

The existing encoder round-trips are now also decoded incrementally by THttpMultiPartDecoder. This includes Rfc2388NestedFiles=true, where a second decoder reads the nested multipart/mixed directly from the outer Current.Content stream.

I also added decoder-specific tests for a 300 KB section containing many CRLF--boundary near-matches, several work-buffer sizes, a source returning only 1–7 bytes per Read(), delimiters split at every buffer position, token and quoted-pair parameters, padding, preamble and epilogue, the header-line limit, truncated input raising EHttpMultiPart, and edge cases for MultiPartFormDataBoundary().

1,310 assertions, green with FPC aarch64-linux and Delphi Win32/Win64.
the whole TTestCoreBase suite still passes on both.

One issue I ran into: Current.Content.Size reports the number of bytes read so far because the section length is not known in advance. Therefore Delphi's CopyFrom(src, 0) silently copies nothing. The documentation recommends StreamCopyUntilEnd() or a normal Read() loop instead, as already done in mormot.crypt.core and mormot.core.zip.

Two questions before I open the PR:

* Current is a record property, so each access copies its four RawUtf8 fields. The documentation recommends caching Current.Content in a local variable. Would a pointer-returning getter, similar to PHttpMultiPartStreamSection, be preferable?

* MultiPartFormDataBoundary() currently sits in mormot.net.client next to the decoder. Would it fit better in mormot.core.buffers next to the other MultiPartFormData* functions, so it could also be reused by MultiPartFormDataDecode?

https://github.com/landrix/mORMot2/tree … rt-decoder

#34 Re: mORMot 2 » Server-side large file upload (issue #292) — proposal » 2026-07-27 13:02:21

Quick update: the first `THttpMultiPartDecoder` version is working and passes the round-trip tests.

Before opening the PR: where should the decoder tests go?

Extend the existing multipart tests in test.core.base.pas or add them to test.net.proto.pas, next to the implementation?

#35 Re: mORMot 2 » Server-side large file upload (issue #292) — proposal » 2026-07-17 11:10:52

Thanks for the detailed analysis.

A few thoughts:

  • Dedicated event:  If it receives the parsed headers and Content-Length and returns the destination TStream (or nil for the current in-memory behavior), the application can choose between a temporary TFileStreamEx and a TPipeStream per request. The main DoS consideration could then be documented: pipes should be fine with THttpServer, where each connection has its own thread, or with non-blocking/Pending-style consumption; a temporary file would be the safer default for THttpAsyncServer.

  • Using STATICFILE_CONTENT_TYPE for the input side makes sense and mirrors the existing output logic. Exposing it through TRestUriContext.InStream, together with detecting a single TStream parameter at the SOA level, should also avoid exposing two competing content types to users.

  • 415 with "Accept-Encoding: identity" for compressed input sounds good. Explicit gzip support through TSynZipDecompressor could always be added later if there is actual demand.

The server-side foundation still depends on the event and stream design, but THttpMultiPartDecoder seems fairly self-contained. I could prepare a PR for an incremental boundary scanner operating on any TStream, processing one part at a time, roughly like Go's mime/multipart.Reader. This would let handlers consume a spooled or piped request body without loading the whole multipart payload into memory.

I would include round-trip tests against THttpMultiPartStream, covering preamble and epilogue handling, CRLF edge cases, nested content-type parameters, and boundaries split across read buffers.

Would you prefer it in mormot.net.http, or in mormot.core.buffers next to MultiPartFormDataDecode?

For the server part, I can then adapt to whichever event/stream design you choose.

#36 mORMot 2 » Server-side large file upload (issue #292) — proposal » 2026-07-17 07:41:42

sh17
Replies: 34

Hi ab,

I'd like to pick up GitHub issue #292 and ask whether a PR in this direction would be welcome before I start coding.

The goal is to support server-side uploads larger than 2 GB without buffering the complete request body in RAM.

Current situation

As far as I can see:

  • THttpRequestContext.ProcessRead already supports writing the request body to ContentStream, for both Content-Length and chunked transfer encoding.

  • However, neither THttpServer nor THttpAsyncServer assigns ContentStream for incoming requests. Request bodies therefore end up in the Content buffer and are limited by MaxHttpInMemSize.

  • MultiPartFormDataDecode also expects the complete multipart body in memory.

We currently work around this with application-level chunking: create an upload, send multiple smaller PUT requests, then finalize it. This works well, but it is a custom protocol and cannot be used by normal multipart clients such as HTML forms, curl -F, or third-party applications.

Possible implementation

Because THttpAsyncServer calls the request handler only after the body has been received, a Go-style pull parser cannot be used directly. My suggestion would be to split the work into two steps.

1. Allow incoming bodies to be written to a stream

Add a hook or option to THttpServerSocketGeneric that assigns Ctxt.ContentStream after the headers have been parsed and before the body is read.

Possible variants:

  • An event similar to OnBeforeBody, where the application may assign a stream such as TFileStreamEx, together with rfContentStreamNeedFree.

  • Automatic spooling based on a configurable memory threshold and temporary directory.

I would personally prefer the event first, since it is small and leaves storage policy to the application. A threshold-based default could be added later.

Since both server implementations use ProcessRead, the same mechanism should work for THttpServer and THttpAsyncServer. The request handler would receive either Content or ContentStream, depending on how the body was stored.

2. Add a streaming multipart reader

As a follow-up, a TMultiPartReader could parse multipart data incrementally from any TStream. The handler could then process one part at a time and copy file parts directly to their final destination.

Open questions
  • How should this interact with Content-Encoding, since decompression currently operates on the in-memory buffer?

  • Is freeing ContentStream through ProcessDone sufficient for aborted connections?

  • Would you prefer an application-provided stream, automatic threshold-based spooling, or both?

Would a PR starting with step 1 be welcome?

#37 Re: mORMot 2 » Proposing a `mormot.ai.*` extension — Model Context Protocol (MCP) » 2026-07-05 16:07:20

@flydev Have you been able to publish the code on GitHub yet? I’d share mine with you after that.

#38 Re: mORMot 2 » Proposing a `mormot.ai.*` extension — Model Context Protocol (MCP) » 2026-06-18 19:23:51

OK, then maybe we should try to merge that somehow after your vacation smile

#39 Re: mORMot 2 » Proposing a `mormot.ai.*` extension — Model Context Protocol (MCP) » 2026-06-18 18:49:18

By the way, I also developed a sqlite-vec extension using lembed, available at https://github.com/landrix/sqlite-vec-for-Delphi. It builds upon two extensions.
I'm exclusively using FreePascal now—no more Delphi. It's just because of the repo names.

#40 Re: mORMot 2 » Proposing a `mormot.ai.*` extension — Model Context Protocol (MCP) » 2026-06-18 18:42:52

If it’s okay with flydev, I’d like to propose some code that builds upon his—specifically, a `mormot.ai` namespace. I’m also willing to maintain it. I’ve implemented the current MCP specification, and right now, I’m working on LLM clients and agents.

Flydev's mormot-os is definitely required. The TLimitedStreamWriter you added today is already being put to use as well.

Sven

#41 Re: mORMot 2 » Proposing a `mormot.ai.*` extension — Model Context Protocol (MCP) » 2026-06-18 11:00:30

Hi flydev,

thanks a lot — I didn't know about mormot2-extensions, and your
mormot-mcp-server is genuinely the closest thing to "MCP done the mORMot way"
I've seen. I went through it in detail;

- It reads like a mORMot core unit, not a Delphi port: IInvokable tools,
  RTTI-based schema generation, TDocVariant envelopes, the tri-license header
  and * section style.
- The transport layer is excellent — stdio, HTTP, SSE and a real Streamable
  HTTP with token-by-token streaming (incl. the Claude CLI demo). That directly
  answers the streaming question from my post.
- The TSynTestCase coverage for the transports (origin validation, sessions,
  batching, notifications, bad-JSON, 405 paths) is exactly the rigor I'd want.

Where my use case still diverges — and why I think there's room for both:

1. Spec revision. Our agents target the current MCP revision
   (2025-11-25); your server is on 2024-11-05 + Streamable HTTP 2025-03-26.
   For us the newer revision isn't optional.
2. Auth is our product core. We're a SaaS backend with JWT, HttpOnly refresh
   cookies, __Host-csrf double-submit, a role/permission model and owner-capped
   scopes. MCP tool execution has to run *inside* that context on our existing
   mORMot host (TUriRouter + guards), not as a separate server. Your
   TMcpAuthContext is a nice foundation, but the wiring is fairly specific to us.

So I'll keep building our mormot.ai.* clean-room layer (current spec + auth bound
to our context), but I'd love to align the Streamable HTTP transport with your
approach rather than reinvent it.

Which loops back to my question for Arnaud: with community MCP servers now existing,
is there appetite to converge any of this under an official namespace, or do you
prefer it stays as companion packages? Happy either way.

Thanks again for pointing me to it.

Best,
Sven

#42 mORMot 2 » Proposing a `mormot.ai.*` extension — Model Context Protocol (MCP) » 2026-06-17 21:03:11

sh17
Replies: 37

Hi Arnaud, hi all,

We run mORMot 2 in production (FPC on Linux, native, no Indy/Wine) as the backend
of a SaaS for the trades. We now need an **MCP server** so our backend can expose
tools/resources to AI agents (e.g. Claude Desktop and our own agent), and ideally
LLM client drivers later on.

Before writing much code I'd like to check interest and get your guidance, because
I think this belongs **in the mORMot world**, not as yet another standalone lib.

## The gap

As far as I can see, mORMot has no AI/LLM/MCP module today. Meanwhile mORMot
already ships everything such a module needs and would do it better than the
Delphi-only options out there (which lean on Indy and `System.JSON`/`System.Rtti`):

- `mormot.net.server` / `mormot.net.async` — HTTP server for the *Streamable HTTP*
  MCP transport
- `mormot.net.ws.*` — WebSockets for streaming
- `mormot.core.json` / `mormot.core.variants` — JSON-RPC 2.0 envelopes with
  `TDocVariant`
- `mormot.core.rtti` — generating JSON-Schema for tool input from typed records
- cross-compiler (FPC + Delphi), cross-platform, no extra dependencies

## What I'm proposing

A small, **clean-room** extension under a new `mormot.ai.*` namespace, built
against the official **MCP specification (revision 2025-11-25)** and JSON-RPC 2.0
— not derived from any third-party MCP code. Initial, transport-neutral layout:

```
mormot.ai.mcp.types            JSON-RPC/MCP types + envelope build/parse
mormot.ai.mcp.server           engine: tool/resource registry + JSON-RPC dispatch
                               (initialize, tools/list, tools/call) — JSON in/out
mormot.ai.mcp.transport.stdio  stdin/stdout (local subprocess transport)
mormot.ai.mcp.transport.http   Streamable HTTP on mormot.net.server
```

The engine takes JSON in and returns JSON out, so it is fully unit-testable
without a socket; transports are thin adapters. Tools implement a small
`IMcpTool` interface (`GetName`/`GetDescription`/`GetInputSchema`/`Execute`).

I already have the first unit (`mormot.ai.mcp.types`) and FPCUnit tests for the
JSON-RPC envelopes; the rest is staged behind it.

## Questions

1. **Interest & home** — would you welcome such an extension under the
   `mormot.ai.*` namespace (eventual upstream into mORMot), or would you prefer
   it lives as an external companion package? Either is fine for us; I'd just
   like to build it the way that has the best chance of being useful to others.
2. **Namespace & conventions** — if upstream is on the table, is `mormot.ai.*`
   the naming you'd want (e.g. `mormot.ai.mcp.server`)? Any coding-style or CLA
   requirements I should follow from the start?
3. **Transport** — for Streamable HTTP, what's your recommended building block:
   plain `THttpServer`/`THttpAsyncServer` with manual chunked writes, or is there
   a pattern you'd point me at for long-lived streaming responses + an optional
   WebSocket upgrade?
4. **JSON-RPC API shape** — for a framework-grade API, would you prefer the
   envelopes/dispatch built on `TDocVariant` (what I have now) or on typed records
   with `mormot.core.rtti` serialization? Happy to follow your taste here.

I'm willing to maintain this and contribute it back. Thanks for mORMot — it has
been a joy to build a native FPC backend on it.

Best regards,
Sven

#43 Re: mORMot 1 » ICU extension for SQLite » 2024-09-14 06:44:44

Well, I just can't call the function sqlite3.db_config(sql.DB, SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION,1);
under Delphi Win64. There is an access violation directly in the sqlite library.
Both in the static version and in the DLL. I can't provide a more detailed analysis because I can't debug sqlite.

It works with Delphi Win32. But the extension is only Win64.

(I have tested the extension with DiSQLite3, there they works)

#45 Re: mORMot 1 » ICU extension for SQLite » 2024-08-13 14:17:20

ab wrote:

Please define "code crash".

---------------------------
GExperts Debugger Exception Notification
---------------------------
Project Project1.exe raised exception class EAccessViolation with message 'Access violation at address 0000000000D1EE5F. Read of address FFFFFFFFFFFFFFFF'.
---------------------------
[&Filter ...] [Ignore &All this Session] [&Break] [Additional &Info] [&Continue]
---------------------------
ThreadId=24592
ProcessId=1
ThreadName="Main"
ExceptionMessage="Access violation at address 0000000000D1EE5F. Read of address FFFFFFFFFFFFFFFF"
ExceptionName="EAccessViolation"
ExceptionDisplayName="$C0000005"
ExceptionAddress=00D1EE5F
FileName=<not available>
LineNumber=<not available>

---------------------------

#46 Re: mORMot 1 » ICU extension for SQLite » 2024-08-13 14:00:07

i have downloaded

https://github.com/asg017/sqlite-vec/re … _64.tar.gz

created a win64 vcl application

and

uses
  ...
  ,mormot.core.os,mormot.db.sql,mormot.db.raw.sqlite3,mormot.core.unicode
  ,mormot.core.base,mormot.db.raw.sqlite3.static,mormot.db.sql.sqlite3
  ,mormot.db.core,mormot.core.datetime

procedure TForm1.FormCreate(Sender: TObject);
var
  sql : TSQLDatabase;
  lLoad : Integer;
  lMsg : PUtf8Char;
begin
  sql := TSQLDatabase.Create(ExtractFilePath(Application.ExeName)+'sample.sql','');
  try
    sqlite3.db_config(sql.DB, SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, 1);
    lLoad:= sql.SQLite3Library.load_extension(sql.DB, PUtf8Char(ExtractFilePath(Application.ExeName)+'vec0.dll'), 0, lMsg);
    if not (lLoad= SQLITE_OK) then
      raise Exception.Create('Error Message');
    sql.Free;
  except
    on E:Exception do
      begin
      end;
  end;

end;

code crash at sqlite3.db_config(....

#47 Re: mORMot 1 » ICU extension for SQLite » 2024-08-13 07:38:13

Somehow none of it works. Or it doesn't work with my extension. Does anyone know more about this? I want to download the following extension.

https://github.com/asg017/sqlite-vec/re … .2-alpha.6

Can anyone help?

#48 Re: PDF Engine » SynPDF and PDF/3-A standard » 2023-11-01 09:57:19

ab wrote:

Any help is welcome to follow PDF/3-A support.

What help is needed? What is still to be done?

#49 Re: mORMot 1 » Embed SQLite C object files into application » 2018-08-16 08:46:48

with wxsqlite it works smile but i need a DB Explorer

Thanks

#50 Re: mORMot 1 » Embed SQLite C object files into application » 2018-08-16 07:09:22

i need encryption in Delphi Win64, so i need the external DLL.

Board footer

Powered by FluxBB