Home > Corpo da requisição e da resposta sem cópias
[!IMPORTANT]
Em desenvolvimento (2.0). Ainda não está numa versão lançada e pode mudar.Nível: avançado
Em resumo, para quem só usa:
Body.Content para TMemoryStream: leia como TStream;SpoolAbove (vai para o disco).O resto da página é para quem precisa do detalhe. Até a 1.2 o corpo era copiado de
2 a 5 vezes entre o motor e a rota: um upload de 100 MB passava por meio gigabyte.
Desde a 2.0 existe um corpo por requisição (no servidor) e um por resposta
(no cliente). Os parâmetros leem esse corpo onde ele está, sem copiar, e a
compressão e a criptografia só acontecem na hora de enviar, escrevendo direto
no que vai para a rede.
Na prática, com um corpo de 8 MB:
| situação | 1.2 | 2.0 |
|---|---|---|
| servidor recebe e a rota lê | 3x o corpo além dele mesmo | nada além do corpo |
| servidor responde um texto que a rota já tem | 3x | nada |
| servidor responde com gzip | 3x | 5% (o gzip de saída) |
cliente recebe e lê ResponseText |
4x | 1x (a própria string) |
| cliente envia | 2x | nada |
Content, AsString e AsStream| forma | o que é | custo |
|---|---|---|
Body.Content |
o próprio corpo, onde ele está | nenhum |
Body.AsString |
o corpo como texto | uma cópia (nenhuma quando o motor entregou uma string: mORMot2, fpHTTP, CGI) |
Body.AsStream |
uma cópia que você libera | uma cópia |
Use Content quando só vai ler:
procedure TForm1.Importa(ARequest: TRALRequest; AResponse: TRALResponse);
var
vCorpo: TStream;
begin
vCorpo := ARequest.Body.Content; // não libere: é do pedido
MeuLeitorCSV.LoadFromStream(vCorpo);
AResponse.Answer(HTTP_OK, 'importado', rctTEXTPLAIN);
end;
O corpo recebido é somente leitura: escrever em Body.Content levanta
exceção. Quem precisa alterar o corpo pega uma cópia com AsStream.
A classe de Content depende do tamanho, e por isso não converta para
TMemoryStream:
TMemoryStream);TRALChunkedStream, em blocos de 1023 KB;SpoolAbove (desligado por padrão) é um arquivo temporário.Todas são TStream e se leem igual. Um código que faz
TMemoryStream(Body.Content).Memory deixa de funcionar acima de 8 MB.
Num multipart, cada parte (ParamByName('arquivo').Content) é uma janela sobre
o corpo que chegou, com as mesmas regras.
| forma | o que acontece |
|---|---|
Answer(status, texto) |
a resposta guarda a sua string, sem copiar |
Answer(status, stream, tipo) |
copia o stream; você continua dono dele e o libera |
Answer(status, stream, tipo, True) |
a resposta fica com o stream e o libera; não mexa mais nele |
AResponse.BodyStream |
um stream para escrever o corpo direto, que a resposta envia |
// um arquivo gerado pela rota: a resposta fica com ele
vPlanilha := TMemoryStream.Create;
GeraPlanilha(vPlanilha);
AResponse.Answer(HTTP_OK, vPlanilha, 'application/vnd.ms-excel', True);
// escrever direto no corpo da resposta
AResponse.ContentType := rctAPPLICATIONJSON;
MinhaQuery.SaveToStream(AResponse.BodyStream);
BodyStream devolve sempre o mesmo stream enquanto ele for o corpo, então
várias escritas se acumulam. O tipo do corpo é o ContentType da resposta,
mesmo que você o defina depois de escrever.
RequestText/RequestStream no servidor e ResponseText/ResponseStream noResponseText/ResponseStream no servidor são o que a rota respondeu,OnResponse que liaResponseText recebe o texto, não gzip.ResponseStream deve passar a usarTakeWireStream (veja "Para quem escreve um motor").Imagens (menos SVG), áudio, vídeo, zip, gzip, 7z, rar, bzip2, xz, zstd, brotli,
pdf e fontes woff/woff2 saem sem Content-Encoding, mesmo com gzip pedido.
Comprimir de novo gasta processador e um buffer do tamanho do corpo e não reduz
nada. Qualquer cliente HTTP lê as duas formas, então nada muda para quem recebe.
TRALCompressPlugin.SkipCompressedTypes (padrão True) eSkipContentTypes, uma lista de tipos a mais (application/x-meu, ou um/, como model/).TRALClient.SkipCompressedTypes (padrão True) eSkipCompressTypes.SkipCompressedTypes := False volta ao comportamento da 1.2.
SpoolAboveTRALLimitsPlugin.SpoolAbove (servidor) e TRALClient.SpoolAbove (cliente),
em bytes, desligados por padrão (0). Acima desse tamanho, o corpo que o RAL
monta vai para um arquivo temporário, apagado quando o pedido ou a resposta é
liberado. A memória deixa de crescer com o corpo: medido, 1 MB de memória para
um corpo de 256 MB.
RALSpoolFolderRALStream). Precisa de permissão de escrita.SpoolAbove, o RAL apaga os que têm mais de um dia.Win32: um processo de 32 bits tem 2 GB de endereços, já fragmentados.
Corpos de algumas centenas de MB, em Win32, só com SpoolAbove.
SetWireBody(AStream, APosse), SetWireBody(ATexto) ouSetWireBody(APonteiro, ATamanho), com ContentEncoding eContentEncription já preenchidos. A posse (TRALBodyOwnership) diz de quemboBorrowed (do motor, que o mantém vivo até o fim do pedido),boBorrowedWritable (idem, e o RAL pode decifrar ali mesmo), boOwned (o RALboCopy.TakeWireStream (um stream que o motor libera) ou TakeWireString.ContentType e ContentEncoding depois da chamada: eles dizem o que