Encabezado de rango HTTP

81

Estaba leyendo http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35 y tratando de averiguar cómo continuar con la descarga de un archivo.

Por ejemplo, supongamos que un archivo tiene una longitud de 100 bytes y tengo todos los 100 bytes. Sin embargo, no sé cuál debería ser el tamaño de archivo esperado, por lo que solicito el archivo y especifico un encabezado de rango que se ve así:

Range: bytes=100-

¿Es esta una solicitud de rango válida?

dhruvbird
fuente
5
Erm, el ejemplo debajo cita 'bytes = 9500-' como válido, así que ....
Wrikken
1
La referencia más actual es RFC7233 - httpwg.github.io/specs/rfc7233.html
Mark Nottingham
2
Primero puede hacer una solicitud HEAD y verificar la longitud del archivo.
Matheus Rocha

Respuestas:

54

Es una solicitud sintácticamente válida, pero no una solicitud satisfactoria. Si busca más en esa sección, verá:

Si un conjunto de rango de bytes sintácticamente válido incluye al menos una especificación de rango de bytes cuyo primer byte pos es menor que la longitud actual del cuerpo de la entidad, o al menos una especificación de rango de bytes de sufijo con una no - longitud de sufijo cero, entonces el conjunto de rango de bytes es satisfactorio. De lo contrario, el conjunto de rango de bytes no se puede satisfacer. Si el conjunto de rango de bytes no es satisfactorio, el servidor DEBE devolver una respuesta con un estado de 416 (rango solicitado no satisfactorio) . De lo contrario, el servidor DEBE devolver una respuesta con un estado de 206 (Contenido parcial) que contenga los rangos satisfactorios del cuerpo de la entidad.

Entonces creo que en su ejemplo, el servidor debería devolver un 416 ya que no es un rango de bytes válido para ese archivo.

Marc Novakowski
fuente
Entonces, ¿hay alguna forma en que un cliente pueda reanudar una descarga sin hacer una llamada HEAD para averiguar primero la longitud del contenido y luego hacer los cálculos y buscar el contenido real? Me refiero a algún tipo de direccionamiento abierto como "dame todos los bytes después de tal o cual byte ..."
dhruvbird
5
El cliente ya sabrá si tiene todos los datos de la solicitud original; debería haber recibido un encabezado Content-Length en la respuesta original o, si se trataba de una codificación fragmentada, habrá recibido un fragmento de longitud cero para indicar el la respuesta fue completa. Si no ha guardado este estado y solo tiene una porción de bytes en el disco, entonces sí, tendrá que hacer una solicitud HEAD o usar el encabezado Range para solicitar un rango de bytes, y si obtiene un 416 respuesta sabes que tienes todos los bytes.
Marc Novakowski
Creo que Expect-Continue te permite hacer fragmentos de transmisión más o menos como deseas.
MJB
@MarcNovakowski En realidad, considere el caso de wget y el uso de la bandera -c. Dado que wget no mantiene ningún metadato sobre el archivo completo, suponga que el tamaño del archivo en el disco es de 99 bytes. wget solicitará el rango de bytes "100-", y creo que el servidor debería responder con una respuesta de longitud 0, ya que la solicitud es solo 1 después del final del archivo.
dhruvbird
147

Como sugirió Wrikken , es una solicitud válida. También es bastante común cuando el cliente solicita medios o reanuda una descarga.

Un cliente a menudo probará para ver si el servidor maneja solicitudes de rango, además de buscar una Accept-Rangesrespuesta. Chrome siempre envía un Range: bytes=0-mensaje con su primera solicitud GET para un video, por lo que es algo que no puedes descartar.

Siempre que un cliente incluye Range:en su solicitud, incluso si tiene un formato incorrecto, espera una respuesta de contenido parcial (206). Cuando busca hacia adelante durante la reproducción de video HTML5, el navegador solo solicita el punto de partida. Por ejemplo:

Range: bytes=3744-

Por lo tanto, para que el cliente reproduzca el video correctamente, su servidor debe poder manejar estas solicitudes de rango incompletas.

Puede manejar el tipo de 'rango' que especificó en su pregunta de dos maneras:

Primero, puede responder con el punto de partida solicitado que se indica en la respuesta, luego la longitud total del archivo menos uno (el rango de bytes solicitado tiene un índice cero). Por ejemplo:

Solicitud:

GET /BigBuckBunny_320x180.mp4 
Range: bytes=100-

Respuesta:

206 Partial Content
Content-Type: video/mp4
Content-Length: 64656927
Accept-Ranges: bytes
Content-Range: bytes 100-64656926/64656927

En segundo lugar, puede responder con el punto de partida que se indica en la solicitud y una longitud de archivo abierta (tamaño). Esto es para webcasts u otros medios donde se desconoce la duración total. Por ejemplo:

Solicitud:

GET /BigBuckBunny_320x180.mp4
Range: bytes=100-

Respuesta:

206 Partial Content
Content-Type: video/mp4
Content-Length: 64656927
Accept-Ranges: bytes
Content-Range: bytes 100-64656926/*

Consejos:

Siempre debe responder con la longitud del contenido incluida en el rango. Si el rango está completo, de principio a fin, entonces la longitud del contenido es simplemente la diferencia:

Solicitud: Rango: bytes = 500-1000

Respuesta: Rango de contenido: bytes 500-1000 / 123456

Recuerde que el rango tiene un índice cero, por Range: bytes=0-999lo que en realidad está solicitando 1000 bytes, no 999, así que responda con algo como:

Content-Length: 1000
Content-Range: bytes 0-999/123456

O:

Content-Length: 1000
Content-Range: bytes 0-999/*

Pero evite el último método si es posible porque algunos reproductores multimedia intentan calcular la duración a partir del tamaño del archivo. Si su solicitud es de contenido multimedia, que es mi corazonada, entonces debe incluir su duración en la respuesta. Esto se hace con el siguiente formato:

X-Content-Duration: 63.23 

Debe ser un punto flotante. A diferencia Content-Length, este valor no tiene por qué ser exacto. Se usa para ayudar al jugador a buscar en el video. Si está transmitiendo un webcast y solo tiene una idea general de cuánto tiempo durará, es mejor incluir su duración estimada en lugar de ignorarla por completo. Entonces, para un webcast de dos horas, podría incluir algo como:

X-Content-Duration: 7200.00 

Con algunos tipos de medios, como webm, también debe incluir el tipo de contenido, como:

Content-Type: video/webm 

Todos estos son necesarios para que los medios se reproduzcan correctamente, especialmente en HTML5. Si no da una duración, el jugador puede intentar averiguar la duración (para permitir la búsqueda) a partir del tamaño de su archivo, pero esto no será exacto. Esto está bien y es necesario para transmisiones por Internet o transmisión en vivo, pero no es ideal para la reproducción de archivos de video. Puede extraer la duración utilizando un software como FFMPEG y guardarlo en una base de datos o incluso el nombre del archivo.

X-Content-Durationse está eliminando gradualmente a favor de Content-Duration, por lo que también lo incluiría. Una respuesta básica a una solicitud "0-" incluiría al menos lo siguiente:

HTTP/1.1 206 Partial Content
Date: Sun, 08 May 2013 06:37:54 GMT
Server: Apache/2.0.52 (Red Hat)
Accept-Ranges: bytes
Content-Length: 3980
Content-Range: bytes 0-3979/3980
Content-Type: video/webm
X-Content-Duration: 2054.53
Content-Duration: 2054.53

Un punto más: Chrome siempre comienza su primera solicitud de video con lo siguiente:

Range: bytes=0-

Algunos servidores enviarán una respuesta 200 regular como respuesta, que acepta (pero con opciones de reproducción limitadas), pero intente enviar un 206 en su lugar para mostrar que su servidor maneja los rangos. RFC 2616 dice que es aceptable ignorar los encabezados de rango.

Victor Stoddard
fuente
¿Qué haces si el contenido es una transmisión de video en vivo que no tiene una duración fija?
Joel Barsotti
@Joel, debes responder con una duración incluso si no lo sabes. En ese caso, intente con 0.0. Para el cliente, la duración no importa de todos modos, ya que normalmente no puede escanear una transmisión en vivo. Si el 0.0 no funciona, prueba algo realmente alto como 1000000.00.
Victor Stoddard
@VictorStoddard ¿se puede aplicar la transmisión fragmentada a la descarga regular de archivos donde no hay un encabezado Range presente en la solicitud del cliente? ¿Cómo debería responder el servidor en ese caso?
gkiko
@gkiko No hay mucha diferencia más que usar el encabezado Transfer-Encoding en lugar de Content-Length en Chunked Transfer Encoding. Los fragmentos pueden provenir de un solo archivo y el servidor puede establecer el tamaño del fragmento. El cliente debe almacenar y juntar los fragmentos a medida que los recibe. Alternativamente, HTTP Streaming utiliza segmentos pregrabados de un archivo multimedia, donde se guardan en el servidor como partes individuales (archivos ts). Estos segmentos se sirven mediante solicitudes GET de archivos HTTP regulares obtenidas de un archivo de índice. Descubrí que segmentar es complicado, pero eso fue hace años.
Victor Stoddard
Content-Length: 64656927 Accept-Ranges: bytes Content-Range: bytes 100-64656926 ¿Por qué Content-Length no es '64656827'?
iwind
7

Al contrario de la respuesta de Mark Novakowski, que por alguna razón ha sido votada por muchos, sí, es una solicitud válida y satisfactoria.

De hecho, el estándar, como señaló Wrikken, constituye un ejemplo de este tipo. En la práctica, Firefox responde a tales solicitudes como se esperaba (con un código 206), y esto es exactamente lo que uso para implementar la descarga progresiva, es decir, solo obtengo la cola de un archivo de registro largo que crece en tiempo real con el sondeo.

Francesco Potortì
fuente
2
Lea la respuesta de Marc Novakowki nuevamente. "satisfactorio" tiene un significado particular en el RFC, que citó. Esta solicitud no se puede satisfacer porque los bytes solicitados superan la longitud del archivo.
Scott Lamb
1
Firefox no es el elemento de software que responde a la solicitud, es un servidor http
Colin D
Sí, lo siento, me refiero a Apache
Francesco Potortì
5

Para las personas que se encuentran con la respuesta de Victor Stoddard anterior en 2019, y se vuelven esperanzados y con ojos saltones, tenga en cuenta que:

a) Se eliminó la compatibilidad con X-Content-Duration en Firefox 41: https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Releases/41#HTTP

b) Creo que solo era compatible con Firefox para audio .ogg y video .ogv, no para otros tipos.

c) No veo que alguna vez haya sido compatible con Chrome, pero eso puede ser solo una falta de investigación de mi parte. Pero su presencia o ausencia parece no tener ningún efecto de una forma u otra para los videos webm u ogv a partir de hoy en Chrome 71.

d) No puedo encontrar ningún lugar donde 'Content-Duration' reemplazara 'X-Content-Duration' para nada, no creo que 'X-Content-Duration' haya vivido lo suficiente como para que haya un nombre de encabezado sucesor.

Creo que esto significa que, a partir de hoy, si desea entregar contenedores webm u ogv que contienen transmisiones que no conocen su duración (por ejemplo, la salida de una tubería ffpeg) a Chrome o FF, y desea que se puedan limpiar en un elemento de video HTML 5, probablemente no tenga suerte. Firefox 64.0 hace un intento poco entusiasta de hacer que estos se puedan borrar, ya sea que sirva o no a través de solicitudes de rango, pero se confunde y lanza una rueda giratoria hasta que la transmisión se descarga por completo si busca unas cuantas veces más de lo que cree apropiado. Chrome ni siquiera lo intenta, simplemente no se escucha y no le permitirá fregar hasta que termine de reproducirse toda la transmisión .

Chris McDonough
fuente
Aquí hay un hilo largo de los desarrolladores de FF que hablan sobre la compatibilidad con este tipo de archivos. bugzilla.mozilla.org/show_bug.cgi?id=657791
Chris McDonough