Domanda

ho una regola di riscrittura che reindirizza a / se non Accept-Language è presente e qualcuno tenta di visita ?lang=en. Funziona bene, tranne che per le intestazioni restituite. Vary: accept-language non è presente nella risposta.

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

L'Apache specifica documentazione:

Se una intestazione HTTP viene utilizzato in una condizione si aggiunge questa intestazione all'intestazione della risposta in caso Variare la condizione restituisce true per la richiesta. Esso non viene aggiunto se la condizione restituisce false per la richiesta.

Le condizioni sono decisamente abbinando e riorientare, quindi non capisco perché Apache non è l'aggiunta della lingua variare. Si può capire perché questo sarebbe un vero problema se un proxy dovesse cache? Lang = it reindirizza sempre a / a prescindere dalla inviati accept-language intestazione.

È stato utile?

Soluzione

Dopo sbirciare nella squallido ventre del sistema di gestione delle richieste di Apache, si scopre che la documentazione è un po 'fuorviante ... Ma prima di entrare nella spiegazione, da quello che posso dire che sei in balia di Apache su questo uno.

Il Cliente Problema

In primo luogo, il nome di intestazione non verrà aggiunto alla Vary intestazione di risposta, se non è inviato dal client. Ciò è dovuto al modo mod_rewrite costruisce il valore di tale intestazione internamente .

Si cerca il colpo di testa di nome utilizzando apr_table_get() , tabella di intestazione della richiesta, e il nome che hai fornito:

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

Se name non è una chiave nella tabella, questa funzione restituisce NULL. Questo è un problema, perché subito dopo si tratta di un controllo contro val:

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

ctx->vary_this viene utilizzato su una base per-RewriteCond ai nomi di intestazione accumulare che devono essere assemblati in finale Vary di intestazione *. Poiché nessun cessione o aggiungendo si verificherà se non esiste un valore, un riferimento (ma non inviati) intestazione apparirà mai in Vary. La documentazione non afferma esplicitamente questo, quindi può o non può essere stato quello che vi aspettavate.

* Per inciso, il NV (senza variare) flag e ignorare contro guasto funzionale è implementata impostando ctx->vary_this a NULL, impedendo la sua aggiunta alla risposta di intestazione.

Tuttavia, è possibile che hai mandato Accept-Language , ma era vuota. In questo caso, la stringa vuota passerà il controllo sopra, e il nome di intestazione verrà aggiunto alla Vary da mod_rewrite da ciò che è descritto sopra. Tenendo questo in mente, ho usato la seguente richiesta per diagnosticare cosa stava succedendo:

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

Questo non funziona neanche, ma perché? mod_rewrite stabilisce definitivamente le intestazioni quando la regola e condizione di corrispondenza (ctx->vary è un aggregato di ctx->vary_this in tutte le condizioni controllato):

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

Questo può essere verificato con una dichiarazione di registro e r->headers_out è la variabile utilizzata durante la generazione delle intestazioni di risposta. Dato qualcosa è sicuramente però sbagliato, ci deve essere liscia dopo vengono eseguite le regole.

Il .htaccess Problema

Al momento, in cui sembra essere definire le regole in .htaccess, o di una sezione <Directory>. Ciò significa che mod_rewrite sta operando in fase di correzione Apache, e il meccanismo impiegato per eseguire effettivamente riscritture qui è molto disordinato. Supponiamo per un attimo non c'è reindirizzamento esterno, dal momento che hai avuto problemi a anche senza di essa (e ve ne parlerò la questione con il reindirizzamento in seguito).

Dopo aver eseguito una riscrittura, è troppo tardi nella elaborazione della richiesta per il modulo per mappare in realtà in un file. Ciò che fa, invece, è in sé assegnare come conduttore e quando la richiesta di del "contenuto" raggiunge richiesta quel punto, effettua una chiamata a ap_internal_redirect(). Questo porta alla creazione di un nuovo oggetto di richiesta, uno che non contiene la tabella headers_out dall'originale.

Supponendo che mod_rewrite provoca ulteriori reindirizzamenti, la risposta è generato dal nuovo richiesta di oggetto, che non avrà mai l'appropriato (originali) intestazioni assegnato ad esso. E 'possibile aggirare questo lavorando in un contesto per server (nella configurazione principale o in una <VirtualHost>), ma ...

Il reindirizzamento Problema

Purtroppo, si scopre che è comunque in gran parte irrilevanti, Dal momento che anche se facciamo uso mod_rewrite in un contesto del server, il percorso prende la risposta in caso di reindirizzamento causa ancora le intestazioni che il set modulo da gettato fuori.

Quando la richiesta è ricevuta da Apache, attraverso una catena di chiamate di funzione si fa strada ap_process_request(). Questo a sua volta chiama ap_process_request_internal(), dove la maggior parte della richiesta importante parsing passi verifica (compresa l'invocazione di mod_rewrite). Esso restituisce un codice di stato intero, che nel caso del reindirizzamento capita di essere impostato a 301.

La maggior parte delle richieste di ritorno OK (che ha un valore pari a 0), che porta immediatamente a ap_finalize_request_protocol(). Tuttavia, questo è non è il caso qui :

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

ap_die() fa qualche ulteriore manipolazione (come restituire il codice di risposta al 301), in questo caso particolare, termina con una chiamata a ap_send_error_response().

Per fortuna, questo è finalmente radice del problema. Anche se potrebbe sembrare che le cose non sono "assbackwards", e questo provoca la distruzione delle intestazioni originali. C'è anche un commento su di esso nella fonte :

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);
        }
        //...
    }
//...
}

Prendete nota che r->headers_out viene sostituito, e la tabella originale viene cancellata. Questo tavolo aveva tutte le informazioni che ci si aspettava di presentarsi nella risposta, così ora si è perso.

Conclusione

Se non si reindirizzare e si definiscono le regole in un contesto per-server, tutto sembra funzionare correttamente. Tuttavia, questo non è ciò che si desidera. Vedo un potenziale soluzione, ma non sono sicuro se sarebbe accettabile, per non parlare della necessità di ricompilare il server.

Per quanto riguarda il Vary: Accept-Encoding, che può assumere solo proviene da un modulo differente che si comporta in modo tale da consentire l'intestazione di nascosto attraverso. Sono, inoltre, non sicuro perché Gumbo non ha avuto un problema quando si cerca di esso.

Per riferimento, stavo guardando la 2.2.14 e 2.2 tronco codice sorgente , e mi è stato modificando e funzionante Apache 2.2.15. Non sembra esserci differenze significative tra le versioni nelle sezioni di codice relativi.

Altri suggerimenti

Si consiglia di provare qualcosa di simile a quanto segue come una soluzione:

<LocationMatch "^.*lang\=">
    Header onsuccess merge Vary "Accept-Language"
</LocationMatch>
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top