// there should not be a "content-length" header
SetHeader (hhContentLength, '');
if not HeadersSent then SendHeaders;
I would suggest exiting early if the headers have already been sent, as it would be too late by then to modify them. Also, since you are modifying the headers anyway, you should just add the required
'Transfer-Encoding: chunked' header before then sending the headers and chunks.
More importantly, your code is sending the entire
ContentStream as a single chunk, which defeats the purpose of chunking. I would suggest reading from the
ContentStream in a loop and sending a chunk on each iteration. That way, you can then use a
ContentStream which doesn't need to provide all of its data up front.
Also, you are not sending the terminating 0-length chunk to signal the end-of-data.
Try something more like this:
type
TChunkableResponse = class(TFpHttpConnectionResponse)
public
procedure SendChunked;
end;
...
procedure TChunkableResponse.SendChunked;
var
len: AnsiString;
buf: array[0..1023] of byte;
numRead: Integer;
streamToSend: TStream;
freeStream: Boolean;
begin
if HeadersSent then
begin
inherited SendContent;
Exit;
end;
streamToSend := ContentStream;
try
freeStream := not Assigned(streamToSend);
if freeStream then
begin
streamToSend := TMemoryStream.Create;
Contents.SaveToStream(streamToSend);
streamToSend.Position := 0;
end;
// there should not be a "content-length" header
SetHeader (hhContentLength, '');
// there should be a "transfer-encoding" header
SetHeader (hhTransferEncoding, 'chunked');
SendHeaders;
repeat
numRead := streamToSend.Read(buf[0], Length(buf));
if numRead <= 0 then Break;
// send chunk length
len := Format('%x'#13#10, [numRead]);
Connection.Socket.WriteBuffer (len[1], Length(len));
// send chunk content
Connection.Socket.WriteBuffer (buf[0], numRead);
// send "end of chunk"
Connection.Socket.WriteByte ($0D);
Connection.Socket.WriteByte ($0A);
until False;
// send last chunk
len := '0'#13#10;
Connection.Socket.WriteBuffer (len[1], Length(len));
finally
if freeStream then
streamToSend.Free;
end;
end;
Now, that being side, the whole purpose of my suggestion of making a
TFpHttpConnectionResponse descendant was to not introduce a completely new send method, but to override the virtual
DoSendContent() method so the existing
SendContent() method would "just work".
For example:
type
TChunkableResponse = class(TFpHttpConnectionResponse)
private
FSendContentChunked: Boolean;
protected
procedure DoSendHeaders(Headers : TStrings); override;
procedure DoSendContent; override;
end;
...
procedure TChunkableResponse.DoSendHeaders(Headers : TStrings);
var
I: Integer;
Hdr: string;
function IsSendingChunked: Boolean;
begin
Result := False;
for I := 0 to Headers.Count-1 do
begin
Hdr := Lowercase(Headers[I]);
if StartsStr('transfer-encoding:', Hdr) and (Pos('chunked', Hdr, 19) > 0) then
begin
Result := True;
Break;
end;
end;
end;
procedure RemoveContentLength;
begin
for I := 0 to Headers.Count-1 do
begin
Hdr := Headers[I];
if StartsText('Content-Length:', Hdr) then
begin
Headers.Delete(I);
Break;
end;
end;
end;
begin
FSendContentChunked := IsSendingChunked;
if FSendContentChunked then RemoveContentLength;
inherited DoSendHeaders(Headers);
end;
procedure TChunkableResponse.DoSendContent;
var
len: AnsiString;
buf: array[0..1023] of byte;
numRead: Integer;
streamToSend: TStream;
freeStream: Boolean;
begin
if not FSendContentChunked then
begin
inherited DoSendContent;
Exit;
end;
streamToSend := ContentStream;
try
freeStream := not Assigned(streamToSend);
if freeStream then
begin
streamToSend := TMemoryStream.Create;
Contents.SaveToStream(streamToSend);
streamToSend.Position := 0;
end;
repeat
numRead := streamToSend.Read(buf[0], Length(buf));
if numRead <= 0 then Break;
// send chunk length
len := Format('%x'#13#10, [numRead]);
Connection.Socket.WriteBuffer (len[1], Length(len));
// send chunk content
Connection.Socket.WriteBuffer (buf[0], numRead);
// send "end of chunk"
Connection.Socket.WriteByte ($0D);
Connection.Socket.WriteByte ($0A);
until False;
// send last chunk
len := '0'#13#10;
Connection.Socket.WriteBuffer (len[1], Length(len));
finally
if freeStream then
streamToSend.Free;
end;
end;
This way, all you have to do is add a
'Transfer-Encoding: chunked' header to the
Response and the server's default logic should chunk it automatically.