Совместим ли 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 10

3 ответа:

Только что обнаружена ошибка в indy, которая предотвращает передачу файлов свыше 2,1 ГБ при использовании запросов диапазона.

Вот оно

IdHTTPHeaderInfo.pas приблизительно строка 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 может не поддерживать биты, он предоставляет все инструменты, необходимые для написания программы, которая поддерживает биты.

Итак, ответ на этот вопрос таков:

Да 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;

Это ни в коем случае не закончено, но это показывает основы реагирования на диапазон запросов от битов. Самое главное, что это работает.

любые замечания по кодексу будут оценены, конструктивная критика всегда приветствуется.