Pregunta

Tengo una regla de reescritura que redirige a / si no hay Accept-Language está presente y alguien intenta visita ?lang=en. Funciona bien, excepto por las cabeceras devueltas. Vary: accept-language no se encuentra en la respuesta.

RewriteCond %{HTTP:Accept-Language} ^$  
RewriteCond %{QUERY_STRING}         ^lang=en  
RewriteRule ^$                      http://www.example.com/?     [R=301,L]

El Apache documentación especifica:

Si una cabecera HTTP se utiliza en una condición se añade esta cabecera a la cabecera de la respuesta en caso de Variar la condición se evalúa a true en la solicitud. No se añade, si las condiciones de falsa evalúa la solicitud.

Las condiciones son definitivamente emparejando y redirigir, así que no entiendo por qué Apache no está agregando el idioma variar. Uno puede ver por qué esto sería un problema real si un proxy caché que eran a? Lang = es siempre redirige a / independientemente del enviado encabezado Accept-Language.

¿Fue útil?

Solución

Después mira a escondidas en el bajo vientre de mala muerte sistema de manejo de petición de Apache, resulta que la documentación es algo engañoso ... Pero antes de entrar en la explicación, por lo que puedo decir que estás a merced de Apache en este uno.

El problema Client

En primer lugar, no se agregará el nombre de encabezado a la Vary cabecera de respuesta si no es enviado por el cliente. Esto es debido a la forma en mod_rewrite construye el valor para esa cabecera internamente.

Se busca el encabezado por su nombre utilizando apr_table_get() , mesa de cabecera de la solicitud, y el nombre que ha proporcionado:

const char *val = apr_table_get(ctx->r->headers_in, name);

Si name no es una llave en la tabla, esta función devolverá NULL. Este es un problema, porque inmediatamente después de que este es un cheque contra val:

if (val) {
   // Set the structure member ctx->vary_this
}

ctx->vary_this se utiliza en una base por-RewriteCond a nombres de encabezado acumulan que deben ser ensamblados en la final Vary cabecera *. Dado que ninguna asignación o va a ocurrir si no hay ningún valor, una referencia (pero no enviado) cabecea no aparecerá en Vary anexar. La documentación no indica explícitamente, por lo que puede o no haber sido lo que se esperaba.

* Como un aparte, el NV (sin variar) bandera y ignore-on-fracaso funcionalidad se implementa mediante el establecimiento de ctx->vary_this a NULL, la prevención de su adición a la cabecera de respuesta.

Sin embargo, es posible que usted envió Accept-Language , pero estaba en blanco. En este caso, la cadena vacía pasará la comprobación anterior, y el nombre del encabezado se añadió a Vary por mod_rewrite de lo que se ha descrito anteriormente. Teniendo esto en cuenta, he utilizado la siguiente petición para diagnosticar lo que estaba pasando:

User-Agent: Fiddler
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: 
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Connection: keep-alive
Host: 129.168.0.123

Esto no funciona bien, pero ¿por qué? mod_rewrite definitivamente establece las cabeceras cuando la regla y combinar condición (ctx->vary es un agregado de ctx->vary_this en todas las condiciones controladas):

if (ctx->vary) {
    apr_table_merge(r->headers_out, "Vary", ctx->vary);
}

Esto se puede verificar con una declaración de registro, y r->headers_out es la variable utilizada al generar los encabezados de respuesta. Dado algo definitivamente va mal embargo, tiene que haber problemas después se ejecutan las reglas.

El problema .htaccess

En la actualidad, usted parece ser la definición de sus reglas en .htaccess, o una sección <Directory>. Esto significa que mod_rewrite está funcionando en fase de corrección de Apache, y el mecanismo que utiliza para llevar a cabo realmente vuelve a escribir aquí está muy desordenado. Supongamos por un segundo que no hay cambio de dirección externa, ya que tenía un problema, incluso sin ella (y voy a llegar a la cuestión con el redireccionamiento más adelante).

Después de realizar una reescritura, es demasiado tarde en el procesamiento de solicitudes para el módulo de mapa en realidad en un archivo. Lo que hace es asignar lugar como manejador de la solicitud "contenido" y cuando los alcances de solicitud de ese punto, se realiza una llamada a ap_internal_redirect(). Esto conduce a la creación de un nuevo objeto de solicitud, uno que no contenga la tabla headers_out del original.

Si se asume que mod_rewrite no causa más redirecciones, la respuesta se genera a partir del nueva Solicitud de objeto, que nunca tendrá la apropiada cabeceras (originales) asignado a la misma. Es posible evitar esto mediante el trabajo en un contexto por servidor (en la configuración principal o en una <VirtualHost>), pero ...

El problema de redireccionamiento

Por desgracia, resulta que de todas formas es en gran medida irrelevante, Ya que incluso si hacemos uso mod_rewrite en un contexto de servidor, la ruta de la respuesta tarda en caso de una redirección todavía hace que los encabezados que el conjunto de módulos que se desechó.

Cuando la solicitud es recibida por Apache, a través de una cadena de llamadas a funciones que se abre paso a ap_process_request(). Esto a su vez llama ap_process_request_internal(), donde el grueso de la solicitud importante analizar los pasos se producen (incluyendo la invocación de mod_rewrite). Se devuelve un código de estado entero, que en el caso de su redireccionamiento pasa a ser fijado a 301.

La mayoría de las solicitudes de devolución OK (que tiene un valor de 0), lo que lleva inmediatamente a ap_finalize_request_protocol(). Sin embargo, eso es no es el caso aquí :

if (access_status == OK) {
    ap_finalize_request_protocol(r);
}
else {
    r->status = HTTP_OK;
    ap_die(access_status, r);
}

ap_die() hace alguna manipulación adicional (como volver la espalda al código de respuesta 301), y en este caso particular extremos con una llamada a ap_send_error_response().

Por suerte, esto es, finalmente, la raíz del problema. Aunque podría parecer que las cosas no son "assbackwards", y esto provoca la destrucción de las cabeceras originales. Incluso hay un comentario sobre él en la fuente :

if (!r->assbackwards) {
    apr_table_t *tmp = r->headers_out;

    /* For all HTTP/1.x responses for which we generate the message,
     * we need to avoid inheriting the "normal status" header fields
     * that may have been set by the request handler before the
     * error or redirect, except for Location on external redirects.
     */
    r->headers_out = r->err_headers_out;
    r->err_headers_out = tmp;
    apr_table_clear(r->err_headers_out);

    if (ap_is_HTTP_REDIRECT(status) || (status == HTTP_CREATED)) {
        if ((location != NULL) && *location) {
            apr_table_setn(r->headers_out, "Location", location);
        }
        //...
    }
//...
}

Tome en cuenta que r->headers_out se sustituye, y la tabla original se borra. Esa mesa tenía toda la información que se espera que aparezca en la respuesta, por lo que ahora se ha perdido.

Conclusión

Si no redirigir y se definen las reglas en un contexto por servidor, todo parece funcionar correctamente. Sin embargo, esto no es lo que desea. Puedo ver una posible solución, pero no estoy seguro de si sería aceptable, por no hablar de la necesidad de volver a compilar el servidor.

En cuanto a la Vary: Accept-Encoding, sólo puedo asumir que proviene de un módulo diferente que se comporta de una manera que permite a la cabecera para colarse a través. También estoy seguro de por qué Gumbo no tenían un problema cuando se trata de la misma.

A modo de referencia, que estaba buscando en el 2.2.14 y 2.2 tronco código fuente , y yo estaba modificando y corriendo Apache 2.2.15. No parece haber diferencias significativas entre las versiones en las secciones de código relacionadas.

Otros consejos

Es posible que desee probar algo parecido a lo siguiente como solución alternativa:

<LocationMatch "^.*lang\=">
    Header onsuccess merge Vary "Accept-Language"
</LocationMatch>
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top