Совместим ли TIdHTTPServer с Microsoft BITS
Мы пытаемся написать сервер обновлений для нашего программного обеспечения, используя компонент TIdHTTPServer. В настоящее время мы обслуживаем файл XML, в котором перечислены доступные обновления и их версии файлов и т.д., Когда клиентская программа находит обновленную версию, она должна начать ее загружать с помощью BITS.
Теперь у нас есть проблема, наши программы запрашивают XML файл и видят, что доступно обновление. Затем он создает задание BITS для его загрузки, однако BITS продолжает сообщать, что загрузка не удалась. Мы можем загрузить файл с использованием того же URL-адреса и IE/Firefox/Chrome.
так что мой вопрос:
Совместим ли TIdHTTPServer с BITS?
Я спрашиваю об этом, поскольку я обнаружил, что есть требования к загрузке битов для работы.
Требования HTTP для загрузки BITS
BITS поддерживает загрузку и загрузку HTTP и HTTPS и требует, чтобы сервер поддерживал протокол HTTP/1.1. Для загрузки метод Header HTTP-сервера должен возвращать размер файла, а метод Get должен поддерживать заголовки Content-Range и Content-Length. В результате BITS передает только содержимое статического файла и генерирует ошибку, если вы пытаетесь передать динамический контент, если ASP, ISAPI или CGI script не поддерживают заголовки Content-Range и Content-Length.
BITS может использовать сервер HTTP/1.0, если он удовлетворяет требованиям метода Head и Get.
Чтобы поддерживать загрузку диапазонов файла, сервер должен поддерживать следующие требования:
Разрешить заголовкам MIME включать стандартные заголовки Content-Range и Content-Type плюс максимум 180 байтов других заголовков. Разрешить максимум два CR/LF между заголовками HTTP и первой граничной строкой.
Ответы
Ответ 1
Итак, ответ на этот вопрос:
Да TIdHTTPServer совместим с битами.
Но только если вы готовы самостоятельно выполнить эту работу.
Как предложили @Rob Kennedy и Myself, можно прочитать заголовки и отправить данные обратно с использованием запрошенных диапазонов, по одному фрагменту за раз.
Вот пример того, что я делаю в событии OnCommandGet
procedure TForm3.IdHTTPServer1CommandGet(AContext: TIdContext;
ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
var
Ranges : TIdEntityRanges;
DataChunk: TMemoryStream;
ReqFile: TFileStream;
ChunkLength: Int64;
Directory, FileName: string;
begin
Directory := 'H:';
case ARequestInfo.Ranges.Count of
0:
begin
//serve file normally
end;
1:
begin
//serve range of bytes specified for file
filename := Directory + ARequestInfo.Document;
if FileExists(FileName) then
begin
ReqFile := TFileStream.Create(FileName, fmOpenRead);
try
ChunkLength := Succ(ARequestInfo.Ranges.Ranges[0].EndPos - ARequestInfo.Ranges.Ranges[0].StartPos);
if ChunkLength > ReqFile.Size then
ChunkLength := ReqFile.Size;
DataChunk := TMemoryStream.Create;
DataChunk.Posistion := ARequestInfo.Ranges.Ranges[0].StartPos;
DataChunk.CopyFrom(ReqFile, ChunkLength);
AResponseInfo.ContentStream := DataChunk;
AResponseInfo.ContentType := IdHTTPServer1.MIMETable.GetFileMIMEType(FileName);
AResponseInfo.ContentRangeUnits := ARequestInfo.Ranges.Units;
AResponseInfo.ContentRangeStart := ARequestInfo.Ranges.Ranges[0].StartPos;
AResponseInfo.ContentRangeEnd := ARequestInfo.Ranges.Ranges[0].StartPos + Pred(ChunkLength);
AResponseInfo.ContentRangeInstanceLength := ReqFile.Size;
AResponseInfo.ResponseNo := 206;
finally
ReqFile.Free;
end;
end
else
AResponseInfo.ResponseNo := 404;
end
else
begin
//serve the file as multipart/byteranges
end;
end;
end;
Это отнюдь не закончено, но оно показывает основы ответа на запросы диапазона от BITS. Самое главное, что это работает.
Любые комментарии к коду будут оценены, конструктивная критика всегда приветствуется.
Ответ 2
Просто нашел ошибку в indy, которая предотвращает передачу файлов более 2,1 ГБ при использовании запросов диапазона.
здесь
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;
Это должно быть
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;
Один для Remy для исправления
Ответ 3
Когда вы обрабатываете событие OnCommandGet
, вам предоставляется TIdRequestHeaderInfo
, который спускается от TIdEntityHeaderInfo
; который содержит все заголовки, содержащиеся в запросе, и даже анализирует некоторые значения заголовков для чтения как свойства, включая ContentRangeStart
, ContentRangeEnd
и ContentLength
.
Эти свойства можно использовать для заполнения потока, который вы присваиваете свойству TIdHTTPResponseInfo.ContentStream
. Весь поток будет отправлен.
Это ваша работа, чтобы различать запросы GET и HEAD; OnCommandGet
будет срабатывать в любом случае. Проверьте свойство IdHTTPRequestInfo.CommandType
.
Итак, хотя Indy может не поддерживать BITS, он предоставляет все инструменты, необходимые для написания программы, поддерживающей BITS.