Skryté pastičky v Tomcatu aneb zpětná kompatibilita se všude nenosí
Nespoléhejte se na to, že, tak jako Java samotná, budou i základní knihovny a nástroje respektovat důležitost zpětné kompatibility. Například v případě Tomcatu se nám už několikrát stalo, že při upgradu na verzi, kde se mění pouze číslo patche, se kompletně rozpadla funkčnost aplikace. Poprvé to bylo myslím, když v patchi vyupgradovali na novější specku JSP a teď nám zase přihodili bombičku v podobě změny obsahu httpServletRequest.getPathInfo(), která nově vrací i tzv. path parametry.
Že nevíte o co se jedná? Jedná se o parametry připojené k url pomocí znaku středník. Zatím jsem neviděl mnoho případů, kdy by se k něčemu využívaly s vyjímkou jediného a tím je jsessionid parametr, který je používán k přenosu vygenerovaného SESSION_ID do doby než je známo, jestli klientská strana podporuje Cookies (pak už se toto id předává většinou pomocí cookie). Podrobnosti o zmatcích panujících v této části specky moc hezky rozebírá tento příspěvek (navíc má hezkou statistiku toho, jaký bordel v tom mají různé Servlet kontejnery): Java Servlet URI Parameters.
Vývojáři Tomcatu se rozhodli, že si tento nepořádek uklidí a udělali tak ve verzi 6.0.33. Jediné, podle čeho mohlo okolí zjistit (kromě své nefunkční aplikace) byl tento záznam v changelogu:
Improve handling of URLs with path parameters and prevent incorrect 404 responses that could occur when path parameters were present. (kkolinko)
Důvody které je k tomu vedly jsou rozepsány v těchto materiálech:
- Diskuse v Tomcat fóru vývojáře podobně překvapeného jako jsem já
- Odkazovaná chyba Spring Servlet Security, která vzniká právě zneužitím URI path parametrů
Záměr je to jistě bohulibý, ale provedení typicky Tomcatovské - nic neříkající záznam v changelogu a zvednutí čísla patche. Teď můžeme čekat jak se postupně v mnoha aplikacích začnou objevovat hlášení podobná tomuto: Incorrect handling of path parameters in PathDispatcher
Takže jestli se vám po upgradu na Apache Tomcat verze 6.0.33 rozpadne vaše webová aplikace, protože webový framework nepočítal s dodatečnými parametry v pathInfo (a to třeba i z toho důvodu, že si mohli vyložit Servlet specku stejně špatně jako dřív sami autoři Tomcatu), už víte o co jde. Při získávání pathInfo je nutné jej takto oříznout:
/**
* Removes path parameters from each path segment in the supplied path and truncates sequences of multiple '/'
* characters to a single '/'.
*
* @param path either the {@code servletPath} and {@code pathInfo} from the original request
*
* @return the supplied value, with path parameters removed and sequences of multiple '/' characters truncated,
* or null if the supplied path was null.
*/
private String strip(String path) {
if (path == null) {
return null;
}
int scIndex = path.indexOf(';');
if (scIndex < 0) {
int doubleSlashIndex = path.indexOf("//");
if (doubleSlashIndex < 0) {
// Most likely case, no parameters in any segment and no '//', so no stripping required
return path;
}
}
StringTokenizer st = new StringTokenizer(path, "/");
StringBuilder stripped = new StringBuilder(path.length());
if (path.charAt(0) == '/') {
stripped.append('/');
}
while(st.hasMoreTokens()) {
String segment = st.nextToken();
scIndex = segment.indexOf(';');
if (scIndex >= 0) {
segment = segment.substring(0, scIndex);
}
stripped.append(segment).append('/');
}
// Remove the trailing slash if the original path didn't have one
if (path.charAt(path.length() - 1) != '/') {
stripped.deleteCharAt(stripped.length() - 1);
}
return stripped.toString();
}
Poznámka ke kódu: Díky za upozornění Ariel na chybu v mém původním kódu pro ořez. Svůj původní kód jsem nahradil lepším, který jsem vybral přímo ze Springu, takže by již měl být v pořádku.
Všechno bych pochopil, co ale nechápu je, proč při nekompatibilních změnách tohoto typu autoři Tomcatu nepoužijí druhé verzovací číslo, aby upozornili na to, že tam může čekat vývojáře nějaký problém. V patchi podobné změny podle mého názoru nikdo nečeká. Určitě by se vyhnuli podobným "hate postům", který jsem právě teď stvořil já.
Fuj!
Komentáře