今天在测试一个content-type为text/plain的API时发现后端requestBody乱码了,而线上正常。

自己本地使用jetty8版本,插件自带版本,而线上使用jetty9。

最开始没有特别注意版本的差异,毕竟这个插件也用了很久了一直没问题,就先从请求分析起。

检查了下发送的请求中没有设置charset,但项目里是设置了spring的编码过滤器,CharacterEncodingFilter中的逻辑:

String encoding = getEncoding();
if (encoding != null) {
if (isForceRequestEncoding() || request.getCharacterEncoding() == null) {
request.setCharacterEncoding(encoding);
}
if (isForceResponseEncoding()) {
response.setCharacterEncoding(encoding);
}
}

encoding配置里设置为UTF-8,强制设置没有开启,所以在request没有设置字符编码时会进行设置。

但奇怪的是乱码的请求里是先被设置成了ISO-8859-1,在想会不会是gateway转发影响,进行抓包分析但还是没有看见charset。

远程debug了正常的测试环境发现字符编码没有被预先设置。

这就非常尴尬了,虽然测试环境没有问题,但本地这个问题的确是存在的,万一上了预发环境也有问题就麻烦了,看了一会儿diff没发现有配置上的变更。

在同事的提醒下,也只有jetty版本的原因了。

拉取了下jetty8的源码(jetty-8.8.0-SNAPSHOT),查看了一下,发现的确有个坑。

AbstractHttpConnection中的parsedHeader负责解析header,其中对应charset的解析代码:

case HttpHeaders.CONTENT_TYPE_ORDINAL:
value = MimeTypes.CACHE.lookup(value);
_charset=MimeTypes.getCharsetFromContentType(value);
break;

解析出来的_charset会在headerComplete中设置进request内:

if(_charset!=null)
_request.setCharacterEncodingUnchecked(_charset);

主要的判断代码在MimeTypes

    public static String getCharsetFromContentType(Buffer value)
{
if (value instanceof CachedBuffer)
{
switch(((CachedBuffer)value).getOrdinal())
{
case TEXT_HTML_8859_1_ORDINAL:
case TEXT_PLAIN_8859_1_ORDINAL:
case TEXT_XML_8859_1_ORDINAL:
return StringUtil.__ISO_8859_1; case TEXT_HTML_UTF_8_ORDINAL:
case TEXT_PLAIN_UTF_8_ORDINAL:
case TEXT_XML_UTF_8_ORDINAL:
case TEXT_JSON_UTF_8_ORDINAL:
return StringUtil.__UTF8;
}
}
//下面是用来解析charset的
int i=value.getIndex();
final int end=value.putIndex();
int state=0;
int start=0;
boolean quote=false;
for (;i<end;i++)
{
final byte b = value.peek(i); if (quote && state!=10)
{
if ('"'==b)
quote=false;
continue;
} switch(state)
{
case 0:
if ('"'==b)
{
quote=true;
break;
}
if (';'==b)
state=1;
break; case 1: if ('c'==b) state=2; else if (' '!=b) state=0; break;
case 2: if ('h'==b) state=3; else state=0;break;
case 3: if ('a'==b) state=4; else state=0;break;
case 4: if ('r'==b) state=5; else state=0;break;
case 5: if ('s'==b) state=6; else state=0;break;
case 6: if ('e'==b) state=7; else state=0;break;
case 7: if ('t'==b) state=8; else state=0;break; case 8: if ('='==b) state=9; else if (' '!=b) state=0; break; case 9:
if (' '==b)
break;
if ('"'==b)
{
quote=true;
start=i+1;
state=10;
break;
}
start=i;
state=10;
break; case 10:
if (!quote && (';'==b || ' '==b )||
(quote && '"'==b ))
return CACHE.lookup(value.peek(start,i-start)).toString(StringUtil.__UTF8);
}
}
if (state==10)
return CACHE.lookup(value.peek(start,i-start)).toString(StringUtil.__UTF8);
//默认路径
return (String)__encodings.get(value);
}

简单来看首先检查是否是预设的一些content-type,如果是的话直接返回。

而传入的text/plain不在默认的范围内,接下里的代码是寻找charset或者"charset",而传入的内容里并没有。

最后走到了return (String)__encodings.get(value);,通过上面的代码可以找到这个属性的设置:

final ResourceBundle encoding = ResourceBundle.getBundle("org/eclipse/jetty/http/encoding");
final Enumeration i = encoding.getKeys();
while(i.hasMoreElements())
{
final Buffer type = normalizeMimeType((String)i.nextElement());
__encodings.put(type,encoding.getString(type.toString()));
}

使用了ResourceBundle,对应的资源文件内容:

text/html	= ISO-8859-1
text/plain = ISO-8859-1
text/xml = UTF-8
text/json = UTF-8

... ...默认值为ISO-8859-1。

而jetty9呢,切回master分支,jetty9设置的代码直接在Request类中了,如下:

    @Override
public String getCharacterEncoding()
{
if (_characterEncoding==null)
getContentType();
return _characterEncoding;
} @Override
public String getContentType()
{
MetaData.Request metadata = _metaData;
String content_type = metadata==null?null:metadata.getFields().get(HttpHeader.CONTENT_TYPE);
if (_characterEncoding==null && content_type!=null)
{
MimeTypes.Type mime = MimeTypes.CACHE.get(content_type);
String charset = (mime == null || mime.getCharset() == null) ? MimeTypes.getCharsetFromContentType(content_type) : mime.getCharset().toString();
if (charset != null)
_characterEncoding=charset;
}
return content_type;
}

类似的逻辑还是继续走MimeTypes的方法,但最后发生了变化:

           if (state==10)
return StringUtil.normalizeCharset(value,start,i-start); return null;
}

已经没有那个默认逻辑,直接返回null了。

这个改动已经非常久远了。

避免踩这种坑,勤升版本很重要啊... ...

感觉又荒废了时光...

jetty8 text/plain默认字符编码的坑的更多相关文章

  1. mysql安装后改动port号password默认字符编码

    1.改动password grant all privileges on *.* to 'root'@'localhost' identified by 'new password'; 2.改动por ...

  2. ubuntu下修改mysql默认字符编码出现的Job failed to start解决办法

    ubuntu下修改mysql默认字符编码出现的Job failed to start解决办法 前几天卸掉了用了好多年的Windows,安装了Ubuntu12.04,就开始各种搭环境.今天装好了MySQ ...

  3. MySQL基础配置之mysql的默认字符编码的设置(my.ini设置字符编码) - 转载

    MySQL基础配置之mysql的默认字符编码的设置(my.ini设置字符编码) MySQL的默认编码是Latin1,不支持中文,那么如何修改MySQL的默认编码呢,下面以设置UTF-8为例来说明. 需 ...

  4. php向mariaDB插入数据时乱码问题解决 --- mysqli_set_charset(设置默认字符编码)

    参考文章: https://www.w3schools.com/php/func_mysqli_set_charset.asp http://php.net/manual/zh/mysqli.set- ...

  5. 那些年java MD5加密字符编码的坑

    相信做过MD5加密的童鞋都遇到过字符编码的坑,一般加密出来的结果和其他人不一样都是字符编码不一致导致的,比如类文件的字符编码.浏览器的字符编码等和对方不一致,所以就需要转码统一字符. 以下是笔者转码过 ...

  6. MySQL基础配置之mysql的默认字符编码的设置(my.ini设置字符编码)

    MySQL基础配置之mysql的默认字符编码的设置(my.ini设置字符编码) MySQL的默认编码是Latin1,不支持中文,那么如何修改MySQL的默认编码呢,下面以设置UTF-8为例来说明. 需 ...

  7. c#字符编码,System.Text.Encoding类,字符编码大全:如Unicode编码、GB18030、UTF-8,UTF-7,GB2312,ASCII,UTF32,Big5

    本页列出来目前window下所有支持的字符编码  ---c#通过 System.Text.Encoding.GetEncodings()获取,里面可以对其进行查询,筛选,对同一个字符,在不同编码进行查 ...

  8. eclipse设置新建jsp文件默认字符编码为utf-8

    在使用Eclipse开发中,编码默认是ISO-8859-1,不支持中文.这样我们每次新建文件都要手动修改编码,非常麻烦.其实我们可以设置文件默认编码,今后再新建文件时就不用修改编码了. 1.打开Ecl ...

  9. mysql中设置默认字符编码为utf-8

    使用过Linux的同志就知道,在Linux下安装mysql,尤其是使用yum安装的时候,我们是没法选择其默认的字符编码方式.这个就是一个比较头痛的问题,如果Linux数据库中使用到中文的时候,乱码问题 ...

随机推荐

  1. 安卓学习第一节--环境搭建及Android Studio 安装

    1.安装JDK 2.安装AS 安装参考网址 https://www.cnblogs.com/xiadewang/p/7820377.html 下载网址: http://www.android-stud ...

  2. django2.0 + python3.6 在centos7 下部署生产环境的一些注意事项

    一:mysql 与环境选用的坑 目前, 在生产环境部署django有三种方式: 1. apache + mod_wsgi 2. nginx + uwsigi 3. nginx + supervisor ...

  3. 数据统计 任务的一点感想 , sql 使用中的坑。

    需求: 多张表(个数不定,需求不是非常明确,只有一个大致需求)根据业务需求统计出一些数据 (按天统计,数据有多条校验规则)进行上传. 注意: 校验数据是否正确是需要第三放来反馈的,而且第三方的测试环境 ...

  4. Idea增加Idiff merger工具

    File -- setting --- tolls -- diff & merge 选择使用外部diff工具和外部merge工具,选择winmerge工具目录. 就可以再version con ...

  5. left join中where与on的区别

    举例进行说明,我们现在有两个表,即商品表(products)与sales_detail(销售记录表).我们主要是通过这两个表来对MySQL关联left join 条件on与where 条件的不同之处进 ...

  6. Xshell连接不上阿里云服务器

    心血来潮买了一台1核2g内存,外加40g系统盘的阿里云ecs服务器,在配置xshell连接服务器一直无法连接,试了很多种方法,各种心累,不过最后还是找到了原因,是因为在服务器上没有配置安全组规则,附上 ...

  7. Scrum冲刺阶段3

    成员今日完成的任务 人员 任务 何承华 美化主界面 陈宇 后端设计 丁培辉 美化主界面 温志铭 主页面的设计 杨宇潇 主页面的设计 张主强 服务器构建 成员遇到的问题 人员 问题 何承华 主页面美化意 ...

  8. 关于python的展望

    在未接触这门课程以前,我完全对编程一类的操作毫无兴趣.但在短短的两星期时间里,我改变了想法,原因有二.其一是老师幽默,其二是课程实用性高.我希望课程以后可以继续沿用现在由浅入深,给予足够提示的方式,引 ...

  9. 周报数据采集之生存图片(execl方法)

    https://blog.csdn.net/Luzaofa/article/details/81675364 Python之Excel chart另存为图片大家好,好久没有更新博客了,这一段时间有点忙 ...

  10. curl安装和使用

    curl可以看作命令行浏览器 1.开启gzip请求 # curl -I http://www.sina.com.cn/ -H Accept-Encoding:gzip,defalte 2.监控网页的响 ...