使用Jmeter登录WordPress的问题(Cookie管理)

1. 背景简介
最近在开始对Xen/KVM的嵌套虚拟化(nested virtualization)做一些性能测试并收集一些性能数据,以便证明各种特性(如virtual EPT、VMCS shadowing等)在嵌套虚拟化中带来的性能提高。其中,我们还考虑到当前Web应用服务器是使用虚拟化技术的一个重要方面,所以专门设计了对Web应用的性能测试。对于我们俩这样做虚拟化团队来说,平时是没有什么特别重要的线上Web服务的,我就开始找一些Web测试的benchmark,最开始找到了的SPECweb2009,但经过简单的分析和试用,发现它配置和使用真的太复杂了(对于非专职Web性能测试来说),而且主要是它衡量是一定的电力功率的情况下支撑的同时在线用户数,这个指标一般来说并不是多数Web开发者关心的(从以前的Web测试经验来说,一般最关心的是响应时间和TPS),所以我还是觉得不用specweb了。由于我的博客是使用WordPress,对它还是比较熟悉了,所以我选择最新的WordPress来作为Web性能测试的服务器端程序吧。 我初步选择了主页浏览和用户登录这两个基本场景来做分析。当然,我还是继续选用了曾经非常熟悉的JMeter来作为Web性能测试工具,最新版本是JMeter 2.9(离我曾经常用的2.3版本已经两年多了,不过这次还借机会根据遇到的问题看了点JMeter的源代码)。


2. 问题描述:
在JMeter中做WordPress的用户登录场景时,却遇到了问题:在JMeter中总是登录不成功,打不开wp-admin页面,而通过Firefox浏览器都是可以正常登录。


3. 初步分析
根据经验,是需要在JMeter中添加Cookie Manager来管理Cookie,这个是必须的,不过添加Cookie Manager后依然不能登录Wordpress。
依然怀疑是cookie问题,网上搜索找到了这篇:使用Jmeter登录WordPress
这里描述的问题,和我遇到的类似,也可以使用里面提到的解决方法(不过这不是太好的方法,还不是问题的本质)。


4. 深入分析
在找到上面的临时解决方法后,确实可以暂时工作了,不过我还是在想为什么第二次请求wp-admin页面时就缺少cookie呢,而且需要Cookie Manager显式地添加cookie。
通过JMeter的查看结果树功能,分别分析其请求和相应过程,其中登录的Post请求和相应的部分内容如下:

POST http://192.168.52.11/wp-login.php
POST data:
log=admin&pwd=123456&wp-submit=Log+In&redirect_to=http%3A%2F%2F192.168.52.11%2Fwp-admin%2F&testcookie=1

Response headers:
HTTP/1.1 302 Found
Date: Sat, 20 Apr 2013 17:14:45 GMT
Server: Apache/2.2.15 (Red Hat)
X-Powered-By: PHP/5.3.3
Expires: Wed, 11 Jan 1984 05:00:00 GMT
Cache-Control: no-cache, must-revalidate, max-age=0
Pragma: no-cache
Set-Cookie: wordpress_test_cookie=WP+Cookie+check; path=/
X-Frame-Options: SAMEORIGIN
Set-Cookie:
wordpress_81eeec675c437946cd44da8ab3073e4b=admin%7C1366650885%7Ceaad49425daa629073c9d2d3862e0afb;
path=/wp-content/plugins; httponly
Set-Cookie:
wordpress_81eeec675c437946cd44da8ab3073e4b=admin%7C1366650885%7Ceaad49425daa629073c9d2d3862e0afb;
path=/wp-admin; httponly
Set-Cookie:
wordpress_logged_in_81eeec675c437946cd44da8ab3073e4b=admin%7C1366650885%7C23f9129257590e96bcfd97c8aae6f622;
path=/; httponly
Location: http://192.168.52.11/wp-admin/
Content-Length: 0
Connection: close
Content-Type: text/html; charset=UTF-8

可以看到,POST登录信息之后,WordPress返回关于/wp-admin路径的Cookie的,但是自动跳转到/wp-admin时并没有把这里的“wordpress_81eeec675c43…”这样的Cookie发送出去。所以,我就开始怀疑为啥JMeter没有正确地发送Cookie,花了一个小时也没找到JMeter的Cookie Manager在使用Cookie的任何bug。我忽然想起来,或许是JMeter收到上面的response header中的Set-Cookie,但是没有正确地保存下来。这是我查看了JMeter运行的日志jmeter.log,发现有如下信息:

2013/04/21 01:04:32 INFO - jmeter.protocol.http.control.CookieManager: Settings: Delete null: true Check: true Allow variable: true Save: false Prefix: COOKIE_

2013/04/21 01:04:35 WARN - jmeter.protocol.http.control.HC3CookieHandler: Not storing invalid cookie: for URL http://192.168.52.11/wp-login.php (Illegal path attribute "/wp-content/plugins". Path of origin: "/wp-login.php")
2013/04/21 01:04:35 WARN - jmeter.protocol.http.control.HC3CookieHandler: Not storing invalid cookie: for URL http://192.168.52.11/wp-login.php (Illegal path attribute "/wp-admin". Path of origin: "/wp-login.php")

这就是证实了我的判断,是HttpClient认为从/wp-login.php发来的关于/wp-admin路径的cookie是无效的(invalid),所以没有保存下来的。
然后根据这里的信息,结合JMeter和HttpClient的代码,可以看到如下有用的逻辑。
首先,设置是否检查Cookie的有效性,代码在:
apache-jmeter-2.9/src/protocol/http/org/apache/jmeter/protocol/http/control
CookieManager.java

然后在 HC3CookieHandler.java 中调用CookieSpec类的validate方法来检查Cookie,如下:

最后在HttpClient中的检查Cookie的validate()方法的判断逻辑如下:
src/java/org/apache/commons/httpclient/cookie/CookieSpecBase.java
(在线代码浏览:http://hc.apache.org/httpclient-3.x/xref/org/apache/commons/httpclient/cookie/CookieSpecBase.html)

这里的原因是,设置Cookie的response对应的request路径是/wp-login.php,而它却设置了/wp-admin(和/wp-content/plugins)路径的Cookie,JMeter不接受。
另外,进一步分析为什么HttpClient有这样的检查呢,原因是HTTP协议的RFC2109中关于Cookie检查的规定中指出了哪些类型的Cookie是应该被拒绝的。

To prevent possible security or privacy violations, a user agent
rejects a cookie (shall not store its information) if any of the
following is true:

* The value for the Path attribute is not a prefix of the request-
URI.

* The value for the Domain attribute contains no embedded dots or
does not start with a dot.

* The value for the request-host does not domain-match the Domain
attribute.

* The request-host is a FQDN (not IP address) and has the form HD,
where D is the value of the Domain attribute, and H is a string
that contains one or more dots.

RFC2109和RFC2965(较新)都是值得参考的,见:http://www.ietf.org/rfc/rfc2109.txt 和 http://www.ietf.org/rfc/rfc2965.txt
根据HTTP协议的规定,所以HttpClient提供了validate方法来检测Cookie,JMeter也是调用HttpClient而已(话说,支持HC3和HC4两种版本的HttpClient)。

5. 解决方案
当我把问题分析清楚之后,那么解决方案就是那么显而易见了,有如下两种思路(第一种比较方便):
1. 在 bin/user.properties 或 bin/jmeter.properties 添加(或修改)使其不检查cookie,配置项为:
CookieManager.check.cookies=false
2. 可以考虑修改WordPress的response中对Cookie的设置方法(因为它目前这样做并不太规范,尽管IE、Firefox、Chrome等浏览器一般都能正常使用Cookie),不过这个方法比较麻烦的(作者也尚未实践)。
另外,在分析和解决JMeter的问题时,可以设置log的等级为DEBUG,在bin/jmeter.properties 中设置:
log_level.jmeter=DEBUG
(其默认值为INFO, 会打印:Jmeter的 FATAL_ERROR, ERROR, WARN, INFO 这4个级别的log;全部的log级别为:FATAL_ERROR, ERROR, WARN, INFO, and DEBUG。)也可以在JMeter的GUI中选中“Help”->“Enable debug”选项。
设置“CookieManager.check.cookies=false”后,JMeter就可以登录WordPress了,如果设置了DEBUG,则可以在jmeter.log中看到如下的信息:

2013/04/21 01:23:28 INFO - jmeter.protocol.http.control.CookieManager: Settings: Delete null: true Check: false Allow variable: true Save: false Prefix: COOKIE_

2013/04/21 01:23:30 DEBUG - jmeter.protocol.http.control.HC3CookieHandler: Received Cookie: wordpress_81eeec675c437946cd44da8ab3073e4b=admin%7C1366650885%7Ceaad49425daa629073c9d2d3862e0afb; path=/wp-content/plugins; httponly From: http://192.168.52.11/wp-login.php
2013/04/21 01:23:30 DEBUG - jmeter.protocol.http.control.CookieManager: Add cookie to store 192.168.52.11 TRUE /wp-content/plugins FALSE 0 wordpress_81eeec675c437946cd44da8ab3073e4b admin%7C1366650885%7Ceaad49425daa629073c9d2d3862e0afb
2013/04/21 01:23:30 DEBUG - jmeter.protocol.http.control.HC3CookieHandler: Received Cookie: wordpress_81eeec675c437946cd44da8ab3073e4b=admin%7C1366650885%7Ceaad49425daa629073c9d2d3862e0afb; path=/wp-admin; httponly From: http://192.168.52.11/wp-login.php
2013/04/21 01:23:30 DEBUG - jmeter.protocol.http.control.CookieManager: Add cookie to store 192.168.52.11 TRUE /wp-admin FALSE 0 wordpress_81eeec675c437946cd44da8ab3073e4b admin%7C1366650885%7Ceaad49425daa629073c9d2d3862e0afb

可以看到CookieManager的Check被设置为false了,关于/wp-admin路径的Cookie已经JMeter被正常接收和设置。


6. 经验总结
1. 遇到问题,记得看仔细看log
2. 若有debug选项,一定要代开DEBUG然后分析各种log
3. 用Google找英文资料,看看别人是否遇到过类似的(中文资料太少了)
4. 多看官方的manual文档,想到对应的部分仔细阅读
5. 如果是开源软件,根据一些log信息,查看相应的代码逻辑
6. 对于HTTP等协议,找到相关的RFC来读读吧,这才是权威的,一些代码、工具等都是协议的实现而已


7. 参考文档
RFC2109: http://www.ietf.org/rfc/rfc2109.txt
RFC2965: http://www.ietf.org/rfc/rfc2965.txt
JMeter manual: http://jmeter.apache.org/usermanual/component_reference.html#HTTP_Cookie_Manager
一个讨论:http://jmeter.512774.n5.nabble.com/Where-do-I-find-all-of-these-cool-settings-for-the-properties-files-td5713678.html
JMeter登录WordPress问题:http://yhz61010.iteye.com/blog/1721697
介绍Cookie Manager(中文):http://desert3.iteye.com/blog/1397643
http://hc.apache.org/httpclient-3.x/xref/org/apache/commons/httpclient/cookie/CookieSpecBase.html

master

Stay hungry, stay foolish.

发表评论

电子邮件地址不会被公开。 必填项已用*标注