前段时间一个同事在项目那边日志中报了很多警告,内容是如下这样:
警告: java.lang.IllegalArgumentException: Control character: ffce incookie value or attribute. COOKIE: uc1=lltime=1362664120&cookie14=UoLa%2FgYsjAaa2Q%3D%3D&existShop=true&cookie16=VFC%2FuZ9az08KUQ56dCrZDlbNdA%3D%3D&cookie21=UIHiLt3xSifiVqTH8o%2F0Qw%3D%3D&tag=0&cookie15=WqG3DMC9VAQiUQ%3D%3D;unb=1599477041; _nk_=%5Cu6211%5Cu4EEC%5Cu662F%5Cu5C0Fm; _l_g_=Ug%3D%3D; v=0;cookie2=94e97f84990927f9a94f481a9163b2d5; sg=m13;lastgetwwmsg=MTM2MjY2NDQ1Nw%3D%3D;cookie1=AHt%2BQDOLk7aCMZ0ZrSrfQP4X8Rzo3dfwwMyH74sDz4Q%3D; cookie17=UoTV5YhjXVmj2g%3D%3D;_tb_token_=5f3e03abe138d; mpp=t%3D1%26m%3D%26h%3D1362672860152%26l%3D1362664473890;l=我们是小m::1362670804377::11;x=e%3D1%26p%3D*%26s%3D0%26c%3D1%26f%3D0%26g%3D0%26t%3D0; tg=5;_cc_=V32FPkk%2Fhw%3D%3D; t=82e
0358dd008e1bc374d96050e25dcf5;tracknick=%5Cu6211%5Cu4EEC%5Cu662F%5Cu5C0Fm;uc2=wuf=http%3A%2F%2Fm0.mail.sina.com.cn%2Fclassic%2Findex.php;mt=cp=0&ci=0_0; cna=0EG/CbL/OkwCAW6ncHu5A+dL;CNZZDATA30070035=cnzz_eid%3D35484131-1351603898-http%3A%2F%2Fwww.taobao.com%2F%3Fspm%3Da1z02.1.0.23.Y0l4Gu%26ntime%3D1362664616%26cnzz_a%3D4%26retime%3D1362668703518%26sin%3D%26ltime%3D1362668703518%26rtime%3D3;showPopup=3
2013-3-80:22:27 org.apache.tomcat.util.http.Cookies processCookies
通过警告的内容可以看出这个是由于cookie中有中文导致的。同事那边判断为子账号登录中文并未转码所致。由于这段时间正在看tomcat的源码,那就来分析一下cookie有中文为什么会抛出这个异常吧!
1. 首先找个有cookie的域名,我把www.renren.com绑定到127.0.0.1
2. 启动tomcat,找到CoyoteAdapter这个请求处理的适配器类,在解析cookie的部分打断点,CoyoteAdapter类postParseRequest这个方法:
3. 在浏览器中拦截www.renren.com这个请求,将cookie中的一个value改为汉字:
这里我敲了一个“我”字。
4. 进入eclipse调试定位到这行代码跑了异常:
打印在控制台的信息是:
警告: java.lang.IllegalArgumentException: Control character: ffe6 in cookie value orattribute. COOKIE: anonymid=鎴?6ly3rd-ep9utw; _r01_=1;_de=BABDC25FBCDA16FDDDEB086511A3F25034DF20B0B3AA6FF7
可见与上面服务器日志抛出来的异常是同一个。在来看抛异常的这段代码:
public static final boolean isV0Separator(final char c) {
if (c < 0x20 || c >= 0x7f) {
if (c != 0x09) {
throw newIllegalArgumentException("Control character: " + Integer.toHexString((c & 0xffff))
+ " in cookie value or attribute.");
}
}
return V0_SEPARATOR_FLAGS[c];
}
这个判断的意思是,如果字符的ascii 小于16进制的20和大于等于16进制的7f,并且不等于16进制的9就会抛出这个异常。翻译一下为:如果这个字符的ascii代码不在 [32,127)并且ascii代码不等于9(对应ascii的制表符)就会抛出这个异常。
异常的代码路径是Cookies类的processCookies方法的第139行:
5. 为什么tomcat为什么会有这个规定呢?
查了一下http规范在RFC2965文档上有这么一段:
网上查了一段cookie的规范也提到:
由此可见tomcat这么限制是遵循http规范的,至于为什么没有把所有的ascii全部涵盖进来,这个还没有仔细研究,有知道的可以分享一下。
6. 所以上面那个服务器的异常解决方案应该就是讲cookie中的中文编码了,毕竟最好不要违反http规范。