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

Другие вопросы по тегам