2010-12-10 11 views
10

Estamos intentando escribir un servidor de actualización para nuestro software utilizando el componente TIdHTTPServer. Actualmente estamos sirviendo un archivo XML que enumera las actualizaciones disponibles y sus versiones de archivos, etc., cuando el programa cliente encuentre una versión actualizada, debería comenzar a descargarla usando BITS.Es TIdHTTPServer Compatible con Microsoft BITS

Ahora aquí es donde tenemos un problema, nuestros programas están solicitando el archivo XML y viendo que hay una actualización disponible. Luego, crea un trabajo BITS para descargarlo, sin embargo, BITS sigue informando que la descarga falló. Podemos descargar el archivo usando la misma URL e IE/Firefox/Chrome.

así que mi pregunta:

Es TIdHTTPServer compatible con BITS?

Pregunto esto porque he descubierto que existen estos requisitos de descarga para que los bits funcionen.
HTTP Requirements for BITS Downloads

BITS soporta cargas y descargas HTTP y HTTPS y requiere que el servidor soporta el protocolo HTTP/1.1. Para las descargas, el método Head del servidor HTTP debe devolver el tamaño del archivo y su método Get debe admitir los encabezados Content-Range y Content-Length. Como resultado, BITS solo transfiere contenido de archivos estáticos y genera un error si intenta transferir contenido dinámico, a menos que el script ASP, ISAPI o CGI admita los encabezados Content-Range y Content-Length.

BITS puede usar un servidor HTTP/1.0 siempre que cumpla con los requisitos de Head y Get.

Para apoyar la descarga de los rangos de un archivo, el servidor debe ser compatible con los siguientes requisitos:

Permitir cabeceras MIME para incluir el contenido-gama estándar y las cabeceras Content-Type, más un máximo de 180 bytes de otras cabeceras. Deje un máximo de dos CR/LF entre los encabezados HTTP y la primera cadena de límite.

+0

Bien, habiendo mirado los rastros del wireshark y la fuente de Indy parece que el TIdHTTPServer no soporta los encabezados Range/Content-Range para las solicitudes de recuperación de rango. – MikeT

+1

Entonces, es posible leer el encabezado Range desde la solicitud GET, luego leer los bytes solicitados en una secuencia de memoria desde el archivo fuente, asignar la secuencia de memoria a la secuencia de contenido para enviar y finalmente establecer el encabezado de rango de contenido. – MikeT

+0

TIdHTTPServer es compatible con los encabezados 'Content-Type',' Content-Range' y 'Range', pero es responsabilidad del manejador de eventos OnCommandGet de la aplicación mirarlos y enviar los datos correctos en consecuencia. La clase TIdHTTPHeaderEntityInfo tiene propiedades ContentType, ContentRangeStart, ContentRangeEnd, ContentRangeInstanceLength y Range disponibles. TIdHTTPServer no maneja los datos reales que se extienden de forma nativa, el código del usuario tiene que administrarlos. –

Respuesta

4

Así que la respuesta a esta pregunta es:

Sí TIdHTTPServer es Bits compatible.

Pero solo si está preparado para hacer el trabajo usted mismo.

Según lo sugerido por @Rob Kennedy and Myself, es posible leer los encabezados y enviar los datos de vuelta utilizando los rangos solicitados, un fragmento a la vez.

Aquí es un ejemplo de lo que estoy haciendo en el caso 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; 

Esto es de ninguna manera terminados pero muestra los fundamentos de responder a las solicitudes de intervalo de BITS. Lo más importante es que funciona.

Cualquier comentario sobre el código sería apreciado, la crítica constructiva siempre es bienvenida.

4

Cuando maneje el evento OnCommandGet, se le da un TIdRequestHeaderInfo, que desciende de TIdEntityHeaderInfo; que contiene todos los encabezados que contiene la solicitud, e incluso analiza algunos valores de encabezado para leerlos como propiedades, incluidos ContentRangeStart, ContentRangeEnd y ContentLength.

Puede usar esas propiedades para rellenar la secuencia que asigna a la propiedad TIdHTTPResponseInfo.ContentStream. Se enviará la secuencia completa .

Es su trabajo diferenciar entre las solicitudes GET y HEAD; OnCommandGet se activará de cualquier manera. Compruebe la propiedad IdHTTPRequestInfo.CommandType.

Por lo tanto, aunque Indy puede no ser compatible con BITS, proporciona todas las herramientas que necesita para escribir un programa que admite BITS.

+0

Gracias por la información que estamos buscando en este momento y voy a publicar nuestros resultados. cualquier ayuda de Remy Lebeau sería apreciada – MikeT

+2

A mitad de camino parece que ContentRangeStart/End devuelve sus valores predeterminados de -1. como BITS envía rango = bytes = 0-1999. Para obtener el Rango que envía BITS, necesita usar la propiedad Rangos en TIdRequestHeaderInfo. Esto contiene una lista de los rangos especificados en el encabezado de solicitud. por lo tanto, en el caso de BITS ARequestInfo.Ranges [0] .StartPos; y ARequestInfo.Ranges [0] .EndPos; obtendrá la información requerida, también puede verificar la unidad para ver si está en bytes, en cuyo caso debe verificar ARequestInfo.Ranges.Units – MikeT

+0

@MikeT: las propiedades ContentRange ... son propiedades de RESPUESTA, no de SOLICITUD . Debe usar la propiedad Range al recibir una solicitud y luego usar las propiedades ContentRange ... al enviar una respuesta. –

6

Acabo de encontrar un error en indy que impide la transferencia de archivos de más de 2,1 GB cuando se utilizan solicitudes de rango.

aquí es

IdHTTPHeaderInfo.pasaprox línea 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; 

Esto debería ser

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; 

Uno de Remy para fijar

+3

La corrección se ha registrado. –

+0

@Remy Lebeau - TeamB Gracias, acabo de actualizar mi copia de SVN. – MikeT