We are trying to write an update server for our software using the TIdHTTPServer component. Currently we are serving an XML file that lists the available updates and their file versions etc.., when the client program finds a updated version it should start to download it using BITS.
Now this is where we have a problem, our programs are requesting the XML file and seeing there is an update available. It then creates a BITS job to download it, however BITS keeps reporting that the download failed. We can download the file using the same URL and IE/Firefox/Chrome.
so my question:
Is TIdHTTPServer compatible with BITS?
I ask this as I have discovered that there are these download requirements for bits to work.
HTTP Requirements for BITS Downloads
BITS supports HTTP and HTTPS downloads and uploads and requires that the server supports the HTTP/1.1 protocol. For downloads, the HTTP server's Head method must return the file size and its Get method must support the Content-Range and Content-Length headers. As a result, BITS only transfers static file content and generates an error if you try to transfer dynamic content, unless the ASP, ISAPI, or CGI script supports the Content-Range and Content-Length headers.
BITS can use an HTTP/1.0 server as long as it meets the Head and Get method requirements.
To support downloading ranges of a file, the server must support the following requirements:
Allow MIME headers to include the standard Content-Range and Content-Type headers, plus a maximum of 180 bytes of other headers. Allow a maximum of two CR/LFs between the HTTP headers and the first boundary string.
Just found a bug in indy that prevents transfer of files over 2.1GB when using range requests.
here it is
IdHTTPHeaderInfo.pas
aprox line 770
procedure TIdEntityRange.SetText(const AValue: String);
var
LValue, S: String;
begin
LValue := Trim(AValue);
if LValue <> '' then
begin
S := Fetch(LValue, '-'); {do not localize}
if S <> '' then begin
FStartPos := StrToIntDef(S, -1);
FEndPos := StrToIntDef(Fetch(LValue), -1);
FSuffixLength := -1;
end else begin
FStartPos := -1;
FEndPos := -1;
FSuffixLength := StrToIntDef(Fetch(LValue), -1);
end;
end else begin
FStartPos := -1;
FEndPos := -1;
FSuffixLength := -1;
end;
end;
This should be
procedure TIdEntityRange.SetText(const AValue: String);
var
LValue, S: String;
begin
LValue := Trim(AValue);
if LValue <> '' then
begin
S := Fetch(LValue, '-'); {do not localize}
if S <> '' then begin
FStartPos := StrToInt64Def(S, -1);
FEndPos := StrToInt64Def(Fetch(LValue), -1);
FSuffixLength := -1;
end else begin
FStartPos := -1;
FEndPos := -1;
FSuffixLength := StrToInt64Def(Fetch(LValue), -1);
end;
end else begin
FStartPos := -1;
FEndPos := -1;
FSuffixLength := -1;
end;
end;
One for Remy to fix
When you handle the OnCommandGet
event, you are given a TIdRequestHeaderInfo
, which descends from TIdEntityHeaderInfo
; that contains all the headers the request contained, and it even parses out some header values to read as properties, including ContentRangeStart
, ContentRangeEnd
, and ContentLength
.
You can use those properties to populate the stream that you assign to the TIdHTTPResponseInfo.ContentStream
property. The entire stream will get sent.
It's your job to differentiate between GET and HEAD requests; OnCommandGet
will get triggered either way. Check the IdHTTPRequestInfo.CommandType
property.
So, although Indy may not support BITS, it provides all the tools you need to write a program that does support BITS.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With