Совместим ли TIdHTTPServer с Microsoft BITS
Мы пытаемся написать сервер обновлений для нашего программного обеспечения, используя компонент TIdHTTPServer. В настоящее время мы обслуживаем XML-файл, в котором перечислены доступные обновления и их версии файлов и т. д.., когда клиентская программа находит обновленную версию, она должна начать загружать ее с помощью BITS.
Теперь у нас есть проблема, наши программы запрашивают файл XML и видят, что доступно обновление. Затем он создает задание BITS, чтобы загрузить его, однако BITS продолжает сообщать что загрузка не удалась. Мы можем скачать файл, используя тот же URL и IE / Firefox / Chrome.
Итак, мой вопрос:
Совместим ли TIdHTTPServer с BITS?
я спрашиваю об этом, поскольку обнаружил, что существуют эти требования к загрузке для работы битов.
HTTP требования для загрузки битов
BITS поддерживает загрузку и загрузку по протоколу HTTP и HTTPS и требует, чтобы сервер поддерживал протокол HTTP/1.1. Для закачки, метод Head HTTP-сервера должен возвращать размер файла, а метод Get должен поддерживать заголовки Content-Range и Content-Length. В результате BITS передает только статическое содержимое файла и выдает ошибку при попытке передачи динамического содержимого, если только сценарий ASP, ISAPI или CGI не поддерживает заголовки Content-Range и Content-Length.
BITS может использовать сервер HTTP / 1.0, если он соответствует требованиям Head и Get метода.
Для поддержки диапазонов загрузки a файл, сервер должен поддерживать следующие требования:
Разрешить заголовкам MIME включать стандартные заголовки Content-Range и Content-Type, а также максимум 180 байт других заголовков. Разрешить не более двух КЛ/ОФС между HTTP-заголовки и первые строки границы.
3 ответа:
Только что обнаружена ошибка в indy, которая предотвращает передачу файлов свыше 2,1 ГБ при использовании запросов диапазона.
Вот оно
IdHTTPHeaderInfo.pas
приблизительно строка 770procedure 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 запросы;
Таким образом, хотя Indy может не поддерживать биты, он предоставляет все инструменты, необходимые для написания программы, которая поддерживает биты.OnCommandGet
будет срабатывать либо путь. Проверьте СВОЙСТВОIdHTTPRequestInfo.CommandType
.
Итак, ответ на этот вопрос таков:
Да TIdHTTPServer совместим с битами.
но только в том случае, если вы готовы выполнить эту работу самостоятельно.
Как предложили @Rob Kennedy и я, можно прочитать заголовки и отправить данные обратно, используя запрошенные диапазоны, по одному куску за раз.
Вот пример того, что я делаю в событии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;
Это ни в коем случае не закончено, но это показывает основы реагирования на диапазон запросов от битов. Самое главное, что это работает.
любые замечания по кодексу будут оценены, конструктивная критика всегда приветствуется.