Tomcat源码分析之getParameter(String)与getQueryString()
2014-05-18 16:32
447 查看
本文有些地方的描述对某些人来说可能比较罗嗦,如果想直接进入正题,可阅读“源码分析”节。但本文是自己一步步分析解决问题思路的记录,虽然有些地方的思考还不是很深入,主要是由于时间不是很充裕(虽然花了三天时间,但感觉还是不够),我会在后续的博文中,结合自己遇到的实际问题或在论坛中看到的别人提出的问题,一步步的带着问题深入分析tomcat源码,这种带着问题进行源码分析的方式,比较有针对性,不至于让自己迷失在源码的汪洋之中。如果大家对博客格式或其他方面有比较好的建议,欢迎指出,非常感谢。本次源码分析的目标是:弄清楚org.apache.catalina.conector.RequestFacade::getQueryString()以及getParameter(String)的不同之处及其各自的具体实现,达到此目标即完成任务。
![](http://static.oschina.net/uploads/space/2013/0407/132152_32tt_271937.png)
从文件A中读取一组以空格为分隔符的的字符串,然后将这些字符串一行一行的写入到另外一个文件B中。
如文件A的格式为:
Aaa bbbbb cdefggg …..
文件B的格式为:
Aaa
Bbbbb
Cdefgggg
….程序代码:
这种情况下,发现文件B中的内容为乱码,而如果将上述程序中的部分代码改为下面这样,则会得到期望的结果。
出现这种情况的原因,我也不是很理解,原文中关于该贴的回答,我觉得和问题没有任何关系,大多数人都在讲如何解决这个问题,而没有提到出现上述情况的原因。经过该贴和其他一些相关帖的了解,我发现引言中提出的问题貌似和hashmap的encoding没有任何关系,可能存在别的原因,于是自己写了一个简单的servlet来实践一下。
在运行的时候,得到的结果是:
![](http://static.oschina.net/uploads/space/2013/0408/080836_Po8g_271937.jpg)
getParameter()得到的值是乱码,而通过getQueryString()解析后存放在map中的值是经过utf-8编码的。对于getParameter()是乱码,这个原因比较明显,由于浏览器默认的urlencoding一般是utf-8,而tomcat中默认的URIEncoding是ISO-8859-1不是utf-8(为什么默认的编码是iso-8859-1?耐心看完本文后,就会明白),当客户端的请求到达tomcat的时候,tomcat就会用其他的编码方式去decode utf-8编码,那么自然就会出现乱码(具体的tomcat是如何处理queryString的,请继续阅读后面的源码分析节),所以解决方法是在tomcat的配置文件server.xml中加入如下配置(URIEncoding="utf-8"):
通过上述配置文件的修改,我们得到的测试结果如下:
![](http://static.oschina.net/uploads/space/2013/0408/080856_XLtP_271937.jpg)
经上述分析,我们可以得出,getParameter()的值是根据tomcat中设置的URIEncoding编码进行decode后得到的值,而对于getQueryString() tomcat没有对其进行decode操作,保留了原有的urlencoding编码方式。至此,我们基本可以推测,出现引言中的情况的原因是:由于客户端对http get请求的url编码方式与tomcat中定义的URIEncoding不一致,导致tomcat服务器利用另外一种解码方式来解码客户端的url,这样必然会出现中文乱码现象。而放入Map中的字符串为什么没有出现乱码?原因就在于getQueryString()没有对客户端的url进行decode,因而保留了原有的客户端utf-8编码,所以在后面的使用过程中,如果利用utf-8对其解码,则不会出现中文乱码现象。
![](http://static.oschina.net/uploads/space/2013/0408/080907_DopW_271937.jpg)
通过上述打印结果,我们可以看到其具体实现类为org.apache.catalina.connector.RequestFacade,所以下一步我们的工作就是具体的分析这个类是如何处理的,也就是分析两个函数的处理流程,一是RequestFacade::getQueryString(),另外一个是RequestFacade::getParameter(String)。首先要获得tomcat的源码,通常的做法是在eclipse中通过egit插件,将远程的git库clone下来,然后再导入工程。所有的准备工作就绪后,接下来就是具体的源码分析工作了:从org.apache.catalina.connector.RequestFacade这个类,我们可以看到,这是一个使用了façade模式的包装类,所以我们需要先了解一下façade模式的相关知识。
通过以上分析可以看出,其处理流程比较简单,通过一步步的delegate,最后真正做工作的是coyote.Request,所以我们接下来只需要分析该类是如何处理。相关函数源码如下:
coyote.Request::queryString()做的工作非常简单,仅是返回类型为MessageBytes的queryMB字段,但这个字段是何时被赋值的呢?这是一个非常有必要弄清的问题,因为极有可能会在赋值之前进行decode操作。
我们如何定位queryMB这个变量是在什么时候赋值的呢?在eclipse中,选中queryMB,点击鼠标右键,选择open call hierarchy,可以看到queryMB在哪些地方被调用,截图如下所示:
![](http://static.oschina.net/uploads/space/2013/0407/133231_sLos_271937.png)
从上图可以看出,有三个地方调用了queryMB,分别是:
该函数是获得一个queryMB对象,既然获得了该对象,那么很有可能在获得对象后对其进行某些操作如赋值操作。
顾名思义,queryMB.recycle()是对queryMB的重新回收利用,对该对象进行reset操作,和赋值没有任何联系。
Request()构造函数中,对其成员变量parameters进行了赋值,和queryMB的赋值没有关系。根据上述三种情况的分析,我们得出只有在第一种情况最有可能出现赋值操作,所以接下来将继续分析queryString()被哪些函数所调用,如下图所示:
![](http://static.oschina.net/uploads/space/2013/0407/133358_lUjL_271937.png)
从截图看出共有七个函数调用了queryString(),从函数名,我们可以简单的判断出,只有parseRequestLine(boolean)这个函数最有可能对其进行赋值,这个函数是解析http请求request line信息。
从上述代码,我们可以看到,在解析http request line的时候,的确对queryMB进行了操作,直接从inputbuffer中获得字节信息,并对queryMB进行赋值。
附录中,有上述每个函数的具体实现源码,有需要的同学可在此处查看http://my.oschina.net/gschen/blog/120740。从上述流程,我们可以看到,最终的处理函数是Parameter::processParameters(byte[],int,int),接下来将重点分析该方法。Parameter::processParameters(byte[],int,int,String)该函数有四个参数,第一个参数类型是byte[],是handleQueryParameter()函数中,获得一份queryMB的拷贝,然后传给processParameters(MessageBytes,String),再传给processParameters(byte[],int,int,String)
第二个和第三个参数类型都为int,分别是queryString的开始位置和queryString的长度如下:
第四个参数为String类型,意思是利用何种方式进行解码,如果未定义,则使用默认的编码方式解码( 关于tomcat什么时候解析配置文件,获得connector节中的URIEncoding编码信息,并传到本函数的encoding,将在后面的博文中一步步的详细阐述 :tomcat源码分析之解析server.xml )。大致的处理流程是,一步步的解析queryMB,然后将解析到的每一个parameter添加到一个HashMap<String, ArrayList<String>>中,最后在这个hashmap中根据name find到自己需要的value。
Parameters::handleQueryParameters()函数中先是得到queryMB的一份拷贝,这样可以避免对queryMB直接操作,破坏原始的信息,接着交由Parameters::processParameters(DecodedQuery, String)处理,最后交由Parameter::processParameters(byte,int,int)处理,该函数第一个参数是queryMB的一份拷贝,函数的基本功能是对该拷贝进行解析,得到一个个的解码后的parameter,再add到paramHashValues这样的一个HashMap<String, ArrayList<String>>中去。
还记得前面提出的默认编码问题吗?您猜对了,就是在这儿定义了默认的default encoding
基本思想是:遍历字节数组,依次得到name和value值,然后调用urlDecoder对name和value进行解码,最后调用addParameter(name,value)方法添加到Parameter::HashMap<String, ArrayList<string>>中去。queryString参数解析算法描述
算法点评上述算法的精髓在于四个位置indicator和两个boolean变量,在完成一次parameter解析后,通过nameStart,nameEnd获得parameter.name的值,通过valueStart, valueEnd获得parameter.value的值,而parameterComplete用来标识一次parameter解析是否完成,parsingName用来标识是否正在解析名称(为什么需要这个标识?因为有些时候,parameter.value可能为空如name=&passwd=123这种情况下)。算法源码
上述代码通过一次遍历处理,得到nameStart, nameEnd, valueStart, valueEnd四个indicator,这样便可得到name, value值。在得到parameter.name和parameter.value后,接着就需要对其进行urldecode操作,decode完成之后,调用addParameter(name, value)方法将其添加到hashmap中。
上述代码是对tmpName和tmpValue进行urldecode操作,然后将解码后的信息addParameter。关于decode的一些说明:在得到name和value后,调用UDecoder对其解码,如果tomcat的server.xml中未定义URIEncoding,则使用默认的"ISO-8859-1"对其进行解码。
引言
问题的引出是由于前些天在oschina上看到的一篇帖子http://www.oschina.net/question/820641_104356,![](http://static.oschina.net/uploads/space/2013/0407/132152_32tt_271937.png)
问题分析
起初的分析思路也是受帖子作者的影响,心想出现这种情况是否是因为hashmap destroy encoding导致的,所以就google了一下hashmap encoding,得到一个比较相关的答案http://stackoverflow.com/questions/8427488/hashmap-destroys-encoding,这篇帖子中出现的情况也比较奇怪。程序功能描述如下:从文件A中读取一组以空格为分隔符的的字符串,然后将这些字符串一行一行的写入到另外一个文件B中。
如文件A的格式为:
Aaa bbbbb cdefggg …..
文件B的格式为:
Aaa
Bbbbb
Cdefgggg
….程序代码:
01 | final StringBuffer fileData = new StringBuffer( 1000 ); |
02 |
03 | final BufferedReader reader = new BufferedReader( |
04 |
05 | new FileReader( "fileIn.txt" )); |
06 |
07 | char [] buf = new char [ 1024 ]; |
08 |
09 | int numRead = 0 ; |
10 |
11 | while ((numRead = reader.read(buf)) != - 1 ) |
12 |
13 | { |
14 |
15 | final String readData = String.valueOf(buf, 0 , numRead); |
16 |
17 | fileData.append(readData); |
18 |
19 | buf = new char [ 1024 ]; |
20 |
21 | } |
22 |
23 | reader.close(); |
24 |
25 | String mergedContent = fileData.toString(); |
26 |
27 | mergedContent = mergedContent.replaceAll( "\\<.*?>" , " " ); |
28 |
29 | mergedContent = mergedContent.replaceAll( "\\r\\n|\\r|\\n" , " " ); |
30 |
31 | final BufferedWriter out = new BufferedWriter( |
32 |
33 | new OutputStreamWriter( |
34 |
35 | new FileOutputStream( "fileOut.txt" ))); |
36 |
37 | final HashMap<String, String> wordsMap = new HashMap<String, String>(); |
38 |
39 | final String test[] = mergedContent.split( " " ); |
40 |
41 | for ( final String string : test) |
42 |
43 | { |
44 | wordsMap.put(string, string); |
45 |
46 | } |
47 | for ( final String string : wordsMap.values()) |
48 |
49 | { |
50 | out.write(string + "\n" ); |
51 |
52 | } |
53 | out.close(); |
01 | ... |
02 |
03 | for ( final String string : test) |
04 | { |
05 | out.write(string + "\n" ); |
06 | //wordsMap.put(string, string); |
07 |
08 | } |
09 |
10 | //for (final String string : wordsMap.values()) |
11 |
12 | //{ |
13 |
14 | // out.write(string + "\n"); |
15 |
16 | //} |
17 | out.close(); |
实践
首先是问题重现,我写了一个简单的servlet如下所示://请求的url为:http://localhost:8080/demo/1.do?addr=上海01 | @Override |
02 | protected void doGet(HttpServletRequest req, HttpServletResponse resp) |
03 | throws ServletException, IOException { |
04 | //System.out.println(req); |
05 | System.out.println( "Request::getParameter(addr) is: " + req.getParameter( "addr" )); |
06 | String queryString = req.getQueryString(); |
07 | System.out.println( "queryString is: " +queryString); |
08 | String[] params = queryString.split( "[=]" ); |
09 |
10 | Map<String, String> map = new HashMap<String, String>(); |
11 |
12 | map.put(params[ 0 ], params[ 1 ]); |
13 | System.out.println( "Map::get(addr) is: " +map.get(params[ 0 ])); |
14 | return ; |
15 | } |
![](http://static.oschina.net/uploads/space/2013/0408/080836_Po8g_271937.jpg)
getParameter()得到的值是乱码,而通过getQueryString()解析后存放在map中的值是经过utf-8编码的。对于getParameter()是乱码,这个原因比较明显,由于浏览器默认的urlencoding一般是utf-8,而tomcat中默认的URIEncoding是ISO-8859-1不是utf-8(为什么默认的编码是iso-8859-1?耐心看完本文后,就会明白),当客户端的请求到达tomcat的时候,tomcat就会用其他的编码方式去decode utf-8编码,那么自然就会出现乱码(具体的tomcat是如何处理queryString的,请继续阅读后面的源码分析节),所以解决方法是在tomcat的配置文件server.xml中加入如下配置(URIEncoding="utf-8"):
1 | <Connector port= "8080" protocol= "HTTP/1.1" |
2 | connectionTimeout= "20000" |
3 | redirectPort= "8443" |
4 | URIEncoding= "utf-8" |
5 | /> |
![](http://static.oschina.net/uploads/space/2013/0408/080856_XLtP_271937.jpg)
经上述分析,我们可以得出,getParameter()的值是根据tomcat中设置的URIEncoding编码进行decode后得到的值,而对于getQueryString() tomcat没有对其进行decode操作,保留了原有的urlencoding编码方式。至此,我们基本可以推测,出现引言中的情况的原因是:由于客户端对http get请求的url编码方式与tomcat中定义的URIEncoding不一致,导致tomcat服务器利用另外一种解码方式来解码客户端的url,这样必然会出现中文乱码现象。而放入Map中的字符串为什么没有出现乱码?原因就在于getQueryString()没有对客户端的url进行decode,因而保留了原有的客户端utf-8编码,所以在后面的使用过程中,如果利用utf-8对其解码,则不会出现中文乱码现象。
源码分析
经过上述实践,基本可以确定问题的原因,但为了进一步的加以验证,我试着分析了一下tomcat在处理getParameter()和getQueryString()的不同。由于HttpServletRequest为一接口,故我们看不到其getParameter()和getQueryString()具体实现,所以我们首先需要确定request的具体实现类是什么,我们在刚才的servlet中加入如下代码:System.out.pritnln(req);![](http://static.oschina.net/uploads/space/2013/0408/080907_DopW_271937.jpg)
通过上述打印结果,我们可以看到其具体实现类为org.apache.catalina.connector.RequestFacade,所以下一步我们的工作就是具体的分析这个类是如何处理的,也就是分析两个函数的处理流程,一是RequestFacade::getQueryString(),另外一个是RequestFacade::getParameter(String)。首先要获得tomcat的源码,通常的做法是在eclipse中通过egit插件,将远程的git库clone下来,然后再导入工程。所有的准备工作就绪后,接下来就是具体的源码分析工作了:从org.apache.catalina.connector.RequestFacade这个类,我们可以看到,这是一个使用了façade模式的包装类,所以我们需要先了解一下façade模式的相关知识。
Façade模式介绍
facade模式的核心是为子系统的一组接口提供一个统一的界面,方便客户端使用子系统,客户端也不必关心子系统的具体实现。facade设计模式的适用情况:1. 原来的类提供的接口比较多,也比较复杂,而我们只需要使用其部分接口;2. 原类提供的接口只能部分的满足我们的需要,且不希望重写一个新类来代替原类;...在本文中,RequestFacade是对Request的一个封装,由于Request本身提供的接口非常之多,而本系统中只需要使用其部分功能,在实际分析过程中,我们发现Request的具体工作最后delegate到底层的coyote.Request去做。RequestFacade::getQueryString()分析
如何进行源码的阅读和分析?我一般的思路是,先分析正常的处理逻辑,对于那些日志,错误处理,变量定义等等可以先不用关注,从而达到快速了解整体架构或关键流程。基于上述思路,我们得到其处理流程如下:1 | -RequestFacade::getQueryString() |
2 |
3 | -Request::getQueryString() |
4 |
5 | -org.apache.coyote.Request::queryString()::toString() |
org.apache.catalina.connector.RequestFacade::getQueryString()
1 | @Override |
2 | public String getQueryString() { |
3 | if (request == null ) { |
4 | throw new IllegalStateException( |
5 | sm.getString( "requestFacade.nullRequest" )); |
6 | } |
7 | return request.getQueryString(); |
8 | } |
org.apache.catalina.connector.Request::getQueryString()
01 | /** |
02 |
03 | * Return the query string associated with this request. |
04 |
05 | */ |
06 |
07 | @Override |
08 | public String getQueryString() { |
09 | return coyoteRequest.queryString().toString(); |
10 | } |
org.apache.coyote.Request::queryString()
1 | public MessageBytes queryString() { |
2 | return queryMB; |
3 | } |
queryMB赋值分析
接下来探讨下queryMB是在何时被赋值的?queryMB是org.apache.coyote.Request的一个私有成员变量,其数据类型为MessageBytes,定义如下:1 | private MessageBytes queryMB = MessageBytes.newInstance(); |
![](http://static.oschina.net/uploads/space/2013/0407/133231_sLos_271937.png)
从上图可以看出,有三个地方调用了queryMB,分别是:
1 | public MessageBytes queryString() { |
2 | return queryMB; |
3 | } |
1 | public void recycle() { |
2 | …. |
3 | queryMB.recycle(); |
4 | …. |
5 | } |
1 | public Request() { |
2 | parameters.setQuery(queryMB); |
3 | parameters.setURLDecoder(urlDecoder); |
4 | } |
![](http://static.oschina.net/uploads/space/2013/0407/133358_lUjL_271937.png)
从截图看出共有七个函数调用了queryString(),从函数名,我们可以简单的判断出,只有parseRequestLine(boolean)这个函数最有可能对其进行赋值,这个函数是解析http请求request line信息。
01 | /** |
02 | * Read the request line. This function is meant to be used during the |
03 | * HTTP request header parsing. Do NOT attempt to read the request body |
04 | * using it. |
05 | * |
06 | * @throws IOException If an exception occurs during the underlying socket |
07 | * read operations, or if the given buffer is not big enough to accommodate |
08 | * the whole line. |
09 | * @return true if data is properly fed; false if no data is available |
10 | * immediately and thread should be freed |
11 | */ |
12 | @Override |
13 | public boolean parseRequestLine( boolean useAvailableData) |
14 | throws IOException { |
15 | …. |
16 | if (questionPos >= 0 ) { |
17 | request.queryString().setBytes(buf, questionPos + 1 , |
18 | end - questionPos - 1 ); |
19 | request.requestURI().setBytes(buf, start, questionPos - start); |
20 | } else { |
21 | request.requestURI().setBytes(buf, start, end - start); |
22 | } |
23 | …. |
24 | } |
1 | request.queryString().setBytes(buf, questionPos + 1 , |
2 | end - questionPos - 1 ); |
getQueryString()总结
由上层的Request一步步的delegate到底层,最后返回coyote.Request::queryMB()字段,而该字段是由底层直接解析http request line信息,并将得到的字节数组直接赋值给coyote.Request::queryMB。(首先在connector.RequestFacade中调用getQueryString(),然后转交给connector.Request::getQueryString()处理,最后交由最底层的类coyote.Request直接调用getQueryString()返回该对象中保存的类型为MessageBytes的queryMB字段值,而queryMB是在解析http request line的时候,直接得到原始的bytes信息,然后保存在queryMB中,至此,上层调用的getQueryString()返回的是,未经上层任何处理,直接解析Http request line的字节信息。)RequestFacade::getParameter()分析(我们知道在web开发中,处理的比较多的是http get请求和http post请求,对于get请求我们可直接由url通过getParameter()方法获得,但对于post请求就会有requsetBody,那么tomcat又是如何处理的?请看后续博文分析)继续上述getQueryString()的思路,我们先得到getParameter()的正常处理流程,如下:01 | -RequestFacade::getParameter(String) |
02 |
03 | -Request::getParameter(String) |
04 |
05 | -Request::parseParameters() |
06 |
07 | -coyote.Request::getParameters() |
08 |
09 | -Parameters::setLimit( int ) |
10 |
11 | -Parameters::setEncoding(String) |
12 |
13 | -Parameters::handleQueryParameters() |
14 |
15 | -decodedQuery.duplicate(MessageBytes) |
16 |
17 | -Parameters::processParameters(MessageBytes, String) |
18 |
19 | -Parameters::processParameters( byte [], int , int ,String) |
20 | -coyote.Request::getParameters()::getParameter(String) |
21 |
22 | -Parameters::paramHashValues.get(String) |
01 | // -------------------- Processing -------------------- |
02 | /** Process the query string into parameters |
03 | */ |
04 | public void handleQueryParameters() { |
05 | ... |
06 | try { |
07 | decodedQuery.duplicate( queryMB ); |
08 | } catch (IOException e) { |
09 | // Can't happen, as decodedQuery can't overflow |
10 | e.printStackTrace(); |
11 | } |
12 | processParameters( decodedQuery, queryStringEncoding ); |
13 | } |
1 | public void processParameters( MessageBytes data, String encoding ) { |
2 | ... |
3 | ByteChunk bc=data.getByteChunk(); |
4 | processParameters( bc.getBytes(), bc.getOffset(), |
5 | bc.getLength(), getCharset(encoding)); |
6 | } |
Parameters::handleQueryParameters()函数中先是得到queryMB的一份拷贝,这样可以避免对queryMB直接操作,破坏原始的信息,接着交由Parameters::processParameters(DecodedQuery, String)处理,最后交由Parameter::processParameters(byte,int,int)处理,该函数第一个参数是queryMB的一份拷贝,函数的基本功能是对该拷贝进行解析,得到一个个的解码后的parameter,再add到paramHashValues这样的一个HashMap<String, ArrayList<String>>中去。
01 | // -------------------- Parameter parsing -------------------- |
02 | // we are called from a single thread - we can do it the hard way |
03 | // if needed |
04 | ByteChunk tmpName= new ByteChunk(); |
05 | ByteChunk tmpValue= new ByteChunk(); |
06 | private final ByteChunk origName= new ByteChunk(); |
07 | private final ByteChunk origValue= new ByteChunk(); |
08 | CharChunk tmpNameC= new CharChunk( 1024 ); |
09 | public static final String DEFAULT_ENCODING = "ISO-8859-1" ; |
10 | private static final Charset DEFAULT_CHARSET = |
11 | Charset.forName(DEFAULT_ENCODING); |
1 | public static final String DEFAULT_ENCODING = "ISO-8859-1" ; |
01 | pos: 开始位置 |
02 | end: 结束位置 |
03 | while (pos < end) |
04 | nameStart: 初始化为pos,参数名称开始位置 |
05 | nameEnd: 初始化为- 1 ,参数名称结束位置,通过nameStart和nameEnd可获得参数名称 |
06 | valueStart: 初始化为- 1 ,参数值开始位置 |
07 | valueEnd: 初始化为- 1 ,参数值结束位置,通过valueStart和valueEnd可获得参数值 |
08 | parsingName:布尔类型,初始化为 true ,用来标识是否正在解析名称 |
09 | parameterComplete: 布尔类型,初始化为 false ,用来标识一个parameter是否解析完成 |
10 |
11 | do |
12 | swtich(当前位置pos对应的字节) |
13 | '=' : |
14 | 是否正在解析参数名称,是则nameEnd = pos, parsingName = false , pos++, valueStart = pos; |
15 | 否则pos++; |
16 | '&' : |
17 | 是否正在解析参数名称,如果是,则nameEnd=pos,否则valueEnd = pos, pos++; |
18 | parameterComplete=ture参数整体解析完成 |
19 | pos++; |
20 | default : |
21 | pos++; |
22 | while (parameter未解析完成 且 pos < end) |
23 |
24 | if (pos == end) |
25 | if (nameEnd == - 1 ) |
26 | nameEnd = pos; |
27 | if (valueStart > - 1 && valueEnd == - 1 ) |
28 | valueEnd = pos; |
29 | end while |
01 | int pos = start; |
02 | int end = start + len; |
03 |
04 | while (pos < end) { |
05 | int nameStart = pos; |
06 | int nameEnd = - 1 ; |
07 | int valueStart = - 1 ; |
08 | int valueEnd = - 1 ; |
09 |
10 | boolean parsingName = true ; |
11 | boolean decodeName = false ; |
12 | boolean decodeValue = false ; |
13 | boolean parameterComplete = false ; |
14 |
15 | do { |
16 | switch (bytes[pos]) { |
17 | case '=' : |
18 | if (parsingName) { |
19 | // Name finished. Value starts from next character |
20 | nameEnd = pos; |
21 | parsingName = false ; |
22 | valueStart = ++pos; |
23 | } else { |
24 | // Equals character in value |
25 | pos++; |
26 | } |
27 | break ; |
28 | case '&' : |
29 | if (parsingName) { |
30 | // Name finished. No value. |
31 | nameEnd = pos; |
32 | } else { |
33 | // Value finished |
34 | valueEnd = pos; |
35 | } |
36 | parameterComplete = true ; |
37 | pos++; |
38 | break ; |
39 | case '%' : |
40 | case '+' : |
41 | // Decoding required |
42 | if (parsingName) { |
43 | decodeName = true ; |
44 | } else { |
45 | decodeValue = true ; |
46 | } |
47 | pos ++; |
48 | break ; |
49 | default : |
50 | pos ++; |
51 | break ; |
52 | } |
53 | } while (!parameterComplete && pos < end); |
54 |
55 | if (pos == end) { |
56 | if (nameEnd == - 1 ) { |
57 | nameEnd = pos; |
58 | } else if (valueStart > - 1 && valueEnd == - 1 ){ |
59 | valueEnd = pos; |
60 | } |
61 | } |
62 | ... |
63 | } |
01 | tmpName.setBytes(bytes, nameStart, nameEnd - nameStart); |
02 | if (valueStart >= 0 ) { |
03 | tmpValue.setBytes(bytes, valueStart, valueEnd - valueStart); |
04 | } else { |
05 | tmpValue.setBytes(bytes, 0 , 0 ); |
06 | } |
07 |
08 | try { |
09 | String name; |
10 | String value; |
11 |
12 | if (decodeName) { |
13 | urlDecode(tmpName); |
14 | } |
15 | tmpName.setCharset(charset); |
16 | name = tmpName.toString(); |
17 |
18 | if (valueStart >= 0 ) { |
19 | if (decodeValue) { |
20 | urlDecode(tmpValue); |
21 | } |
22 | tmpValue.setCharset(charset); |
23 | value = tmpValue.toString(); |
24 | } else { |
25 | value = "" ; |
26 | } |
27 |
28 | try { |
29 | addParameter(name, value); |
30 | } catch (IllegalStateException ise) { <span></span> // Hitting limit stops processing further params but does |
31 | ... |
32 | } |
33 | } catch (IOException e) { |
34 | ... |
35 | } |
36 |
37 | tmpName.recycle(); |
38 | tmpValue.recycle(); |
Futuer work
在本次源码分析过程中,尚有一些未解决的问题,将在以后分析的过程中,逐步的解决。问题列表:1. tomcat是在什么时候加载server.xml配置文件的,得到URIEncoding值的;2. digest是如何解析xml文件的;3. 底层的coyote是如何实现的;.....下一篇将分析该贴中http://www.oschina.net/question/853764_103942出现问题的原因本文中提到的相关函数源码,我已经集中放在另外一篇博客方便大家集中查阅http://my.oschina.net/gschen/blog/120740。相关文章推荐
- tomcat源码分析之getParameter(String)与getQueryString()
- tomcat源码分析之getQueryString()与getParameter(String)
- String源码分析之getByte乱码
- JavaWeb笔记getParameter,getParameterValues,getParameterMap,getQueryString
- AFNetworking3.1.0源码分析(三)AFHTTPRequestSerializer 之AFQueryStringPair
- getResource(String name)用法及源码分析
- Tomcat源码分析之StringManager与单例模式
- tomcat 4.1.30启动过程的源码分析
- TOMCAT源码分析(消息处理)
- TOMCAT源码分析(消息处理)
- TOMCAT源码分析及启动过程
- In Websphere Application Server 6.1 , request.getQueryString() return null.
- TOMCAT源码分析(启动框架)
- ajaxpro get query string
- tomcat 4.1.30启动过程的源码分析
- String转换成Integer源码分析
- Tomcat源码分析(一)
- Tomcat4.1.24源码分析导引
- tomcat源码分析的几个文章
- TOMCAT源码分析(启动框架)