Codificación de transferencia: gzip frente a codificación de contenido: gzip

98

¿Cuál es el estado actual de las cosas cuando se trata de si hacer

Transfer-Encoding: gzip

o un

Content-Encoding: gzip

cuando quiero permitir que los clientes, por ejemplo, con un ancho de banda limitado, indiquen su voluntad de aceptar una respuesta comprimida y el servidor tenga la última palabra sobre si comprimir o no .

Esto último es lo que hacen, por ejemplo, mod_deflate e IIS de Apache, si deja que se encargue de la compresión. Dependiendo del tamaño del contenido a comprimir, hará lo adicional Transfer-Encoding: chunked.

También incluirá un Vary: Accept-Encoding, que ya insinúa el problema. Content-Encodingparece ser parte de la entidad, por lo que cambiar las Content-Encodingcantidades a un cambio de la entidad, es decir, un Accept-Encodingencabezado diferente significa, por ejemplo, que un caché no puede usar su versión en caché de la entidad por lo demás idéntica.

¿Hay una respuesta definitiva a esto que me he perdido (y que no está enterrada dentro de un mensaje en un hilo largo en algún grupo de noticias de Apache)?

Mi impresión actual es:

  • Transfer-Encoding sería, de hecho, la forma correcta de hacer lo que se hace principalmente con Content-Encoding mediante implementaciones de servidor y cliente existentes
  • Content-Encoding, debido a sus implicaciones semánticas, conlleva un par de problemas (¿qué debe hacer el servidor ETagcuando comprime de forma transparente una respuesta?)
  • La razón es chicken'n'egg: los navegadores no lo admiten porque los servidores no lo hacen porque los navegadores no

Así que supongo que la forma correcta sería un Transfer-Encoding: gzip(o, si además trozo el cuerpo, se convertiría Transfer-Encoding: gzip, chunked ). Y no hay razón para tocar Varyo ETagcualquier otro encabezado en ese caso, ya que es una cosa a nivel de transporte.

Por ahora no me importa demasiado el 'salto a salto' Transfer-Encoding, algo que a los demás parece preocuparles en primer lugar, porque los proxies pueden descomprimir y reenviar sin comprimir al cliente. Sin embargo, los proxies podrían reenviarlo tal como está (comprimido), si la solicitud original tiene el Accept-Encodingencabezado adecuado , que en el caso de todos los navegadores que conozco es un hecho.

Por cierto, este problema tiene al menos una década, consulte, por ejemplo, https://bugzilla.mozilla.org/show_bug.cgi?id=68517 .

Se agradecerá cualquier aclaración al respecto. Tanto en términos de lo que se considera compatible con los estándares como de lo que se considera práctico. Por ejemplo, las bibliotecas cliente HTTP que solo admitan una "codificación de contenido" transparente serían un argumento en contra de la practicidad.

Eugene Beresovksy
fuente
2
Relacionado: stackapps.com/questions/916/…
Jo Liss
Me encontré con esto. Curl en PHP 5.3 no lo entiende Transfer-Encoding:gzip, aunque la línea de comandos curl sí. Para estar seguro, envíe ambos, a menos que esté combinando chunked y gzip.
Seva Alekseyev
1
@SevaAlekseyev enviar ambos estaría muy mal - los clientes podrían intentar descomprimir dos veces
Joshua Wise
Esto es algo que también me ha molestado para siempre ( pregunta que hice ) ... según una de las respuestas a la pregunta que citó @JoLiss, hay una forma perfectamente lógica, semánticamente coherente y compatible con los estándares de comprimir los cuerpos de solicitud / respuesta ... y básicamente ningún cliente / servidor lo usa ni lo admite. 🤦🏻‍
Dan Lenski

Respuestas:

35

Citando a Roy T. Fielding , uno de los autores de RFC 2616:

cambiar la codificación de contenido sobre la marcha de manera inconsistente (ni "nunca" ni "siempre) hace que sea imposible que las solicitudes posteriores relacionadas con ese contenido (p. ej., PUT o GET condicional) se manejen correctamente. Esta es, por supuesto, la razón por la que realizar La codificación de contenido sobre la marcha es una idea estúpida, y por eso agregué Transfer-Encoding a HTTP como la forma correcta de realizar la codificación sobre la marcha sin cambiar el recurso.

Fuente: https://issues.apache.org/bugzilla/show_bug.cgi?id=39727#c31

En otras palabras: no realice la codificación de contenido sobre la marcha , ¡use la codificación de transferencia en su lugar!

Editar: es decir, a menos que desee ofrecer contenido comprimido con gzip a clientes que solo entienden la codificación de contenido . Que, lamentablemente, parece ser la mayoría de ellos. Pero tenga en cuenta que abandona los dominios de la especificación y puede encontrarse con problemas como el mencionado por Fielding y otros, por ejemplo, cuando se trata de caché de proxies.

Eugene Beresovsky
fuente
3
Entonces, si lo hago bien: 1. La codificación de contenido se refiere a la codificación del contenido en el servidor en abstracto, es decir, el contenido será servido consistentemente en la codificación especificada por el servidor. 2. La codificación de transferencia se refiere a la codificación que el servidor decidió utilizar para entregarla al agente de usuario en este caso, es decir, en esta respuesta. Solo asegurándome de no malinterpretar tu respuesta.
dot slash hack
30
@KemHeyndels Sobre la derecha. Dicho de otra manera: según las especificaciones, Transfer-Encoding es un detalle de la capa de transporte puro , es decir, un proxy intermedio es libre de deshacer, por ejemplo, la compresión gzip en ese nivel, mientras que Content-Encoding es una propiedad de la capa empresarial , que un proxy no sería permitido cambiar, además de otras ramificaciones (ETags, etc.). Sin embargo, de acuerdo con la realidad , TE no se usa normalmente para la compresión, y muchos servidores / clientes ni siquiera lo admiten de fábrica, mientras que CE se usa más o menos de la forma en que se pretendía que se usara TE : como detalle de la capa de transporte .
Eugene Beresovsky
1
Entonces, ¿estamos obligados por la realidad a ignorar el consejo de Roy T. Fielding?
truco de dot slash
11
@KemHeyndels Está obligado por el idealismo a salir y primero agregar soporte TE a todas las implementaciones de cliente / servidor HTTP de código abierto. Luego, consiga un empleo en todas las empresas que tengan implementaciones HTTP de código cerrado (creo que eso es solo Microsoft de todos modos) y agregue la función allí también. Después de eso, la realidad y la especificación coincidirán. ;) (Y HTTP 2.0 habrá sido lanzado, haciendo que el problema desaparezca de todos modos)
Eugene Beresovsky
10
Indicar que es compatible con Transfer-Encoding todavía no deja en claro que admite gzip sobre Transfer-Encoding, por lo que eso no le compra nada. La indicación se hace al revés : cualquier cliente que pueda hacer gzip a través de Transfer-Encoding le informará al servidor configurando TE: gzip. Y luego su servidor debería seguir la ruta de Codificación de Transferencia. Si el cliente solo lo dice Accept-Encoding: gzip, debe hacerlo de la Content-Encodingmanera. Si el cliente no especifica ninguno en su solicitud, el servidor no debe hacer gzip en absoluto.
Eugene Beresovsky
27

El uso correcto , como se define en RFC 2616 y realmente implementado en la naturaleza, es que el cliente envíe un Accept-Encodingencabezado de solicitud (el cliente puede especificar múltiples codificaciones). El servidor puede entonces, y solo entonces, codificar la respuesta de acuerdo con las codificaciones admitidas por el cliente (si los datos del archivo aún no están almacenados en esa codificación), indicar en el Content-Encodingencabezado de respuesta qué codificación se está utilizando. A continuación, el cliente puede leer los datos del socket según el Transfer-Encoding(es decir, chunked) y luego decodificarlos según el Content-Encoding(es decir:) gzip.

Entonces, en su caso, el cliente enviaría un Accept-Encoding: gzipencabezado de solicitud, y luego el servidor puede decidir comprimir (si aún no lo ha hecho) y enviar un encabezado de respuesta Content-Encoding: gzipopcional Transfer-Encoding: chunked.

Y sí, el Transfer-Encodingencabezado se puede usar en solicitudes, pero solo para HTTP 1.1, que requiere que tanto las implementaciones de cliente como de servidor admitan la chunkedcodificación en ambas direcciones.

ETagidentifica de forma única los datos de recursos en el servidor, no los datos que se transmiten realmente. Si un recurso de URL determinado cambia su ETagvalor, significa que los datos del lado del servidor para ese recurso han cambiado.

Remy Lebeau
fuente
14
La codificación de contenido es una característica de la entidad identificada por Request-URI En otras palabras: Diferente Content-Encodingrequiere diferenteETag Esto es por cierto de lo que se trata el error mod_deflate al que me refiero en mi respuesta. Me hace preguntarme por qué este detalle a nivel de aplicación está en el estándar HTTP en primer lugar. Transfer-EncodingSin embargo, cuando se utiliza un ajuste de nivel de transporte, no es necesario cambiar el ETag. Excepto que nadie ha implementado Transfer-Enc.
Eugene Beresovsky
2
Content-Encoding no es para la codificación "sobre la marcha". RFC 2616 dice "La codificación de transferencia ... difiere de la codificación de contenido en que la codificación de transferencia es una propiedad del mensaje, no de la entidad" ( tools.ietf.org/html/rfc2616#section-14.41 ) y "La codificación de contenido es una característica de la entidad identificada por el Request-URI. Normalmente, el cuerpo de la entidad se almacena con esta codificación" ( tools.ietf.org/html/rfc2616#section-14.11 ). Así que voto en contra.
Robert
Lo que he descrito es lo que se " aplica realmente en la naturaleza ", independientemente de Content-Encodingfrente Transfer-Encoding. Sí, gzip debería ser una propiedad de la transferencia de un recurso, si se realiza sobre la marcha. Por otro lado, si el recurso se almacena comprimido en el servidor, debería ser una propiedad del contenido del recurso en su lugar, si se envía tal cual. Pero lo que debería ser y lo que realmente es no siempre son lo mismo.
Remy Lebeau