Совместим ли TIdHTTPServer с Microsoft BITS
Мы пытаемся написать сервер обновлений для нашего программного обеспечения, используя компонент TIdHTTPServer. В настоящее время мы обслуживаем XML-файл, в котором перечислены доступные обновления, их версии и т. Д., Когда клиентская программа находит обновленную версию, она должна начать загружать ее с помощью BITS.
Теперь у нас проблема: наши программы запрашивают файл XML и видят, что доступно обновление. Затем он создает задание BITS для его загрузки, однако BITS продолжает сообщать, что загрузка не удалась. Мы можем скачать файл, используя тот же URL и IE/Firefox/Chrome.
итак мой вопрос
Совместим ли TIdHTTPServer с битами?
Я спрашиваю об этом, поскольку я обнаружил, что существуют эти требования к загрузке для работы битов.
Требования HTTP для загрузки BITS
BITS поддерживает загрузку и выгрузку HTTP и HTTPS и требует, чтобы сервер поддерживал протокол HTTP/1.1. Для загрузки метод HTTP-сервера HTTP должен возвращать размер файла, а метод Get должен поддерживать заголовки Content-Range и Content-Length. В результате BITS передает только статическое содержимое файла и выдает ошибку, если вы пытаетесь передать динамическое содержимое, если только сценарии ASP, ISAPI или CGI не поддерживают заголовки Content-Range и Content-Length.
BITS может использовать сервер HTTP/1.0, если он соответствует требованиям метода Head и Get.
Для поддержки диапазонов загрузки файла сервер должен поддерживать следующие требования:
Разрешить заголовкам MIME включать стандартные заголовки Content-Range и Content-Type, а также максимум 180 байтов других заголовков. Разрешить не более двух CR/LF между заголовками HTTP и первой строкой границы.
3 ответа
Таким образом, ответ на этот вопрос:
Да 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. Самое главное это работает.
Любые комментарии к кодексу будут оценены, конструктивная критика всегда приветствуется.
Только что обнаружил в 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;
Один для Реми, чтобы исправить
Когда вы обрабатываете OnCommandGet
событие, вам дают TIdRequestHeaderInfo
, который происходит от TIdEntityHeaderInfo
; он содержит все заголовки, содержащиеся в запросе, и даже анализирует некоторые значения заголовков для чтения как свойства, включая ContentRangeStart
, ContentRangeEnd
, а также ContentLength
,
Вы можете использовать эти свойства для заполнения потока, который вы назначаете TIdHTTPResponseInfo.ContentStream
имущество. Весь поток будет отправлен.
Ваша задача - различать запросы GET и HEAD; OnCommandGet
будет срабатывать в любом случае. Проверить IdHTTPRequestInfo.CommandType
имущество.
Таким образом, хотя Indy может не поддерживать BITS, она предоставляет все инструменты, необходимые для написания программы, которая поддерживает BITS.