13 關於HttpClient自動儲存Cookie
前言
下面是我以前想做的一個專門為了HXBlog “刷訪問” 的工具,, 當時 直接使用的我的HXCrawler進行傳送請求, 但是 很遺憾失敗了,,
也就是 雖然我傳送了”requestTime”個請求, 但是 該部落格的”訪問次數”依然僅僅增加了一次
因為 不知道為何傳送第一個請求之後, 後面的所有請求都帶上了第一次請求所得到的sessionId, 但是 問題在於, 我這裡是每一次 都新建了一個CrawlerConfig啊, 因此 每次傳送的請求應該都沒有什麼關係啊, 為什麼 會出現這種情況呢,,
然後 今天上午花了一上午的時間[9月4日],來探索這個問題…
不過 好在有所收穫, 請往下看 ↓
示例程式碼片
/**
* file name : Test23PostRequestsForHXBlog.java
* created at : 上午10:51:05 2016年8月6日
* created by 970655147
*/
package com.hx.test;
public class Test23PostRequestsForHXBlog {
// 給HXBlog傳送請求[刷資料]
public static void main(String[] args) throws Exception {
// String requestUrl = "http://localhost:8080/HXBlog/action/blogGetAction?blogId=38&tag=all";
String requestUrl = "http://localhost:8088/HXBlog/action/blogGetAction?blogId=16&tag=all";
int requestTime = 20;
flushVisit(requestUrl, requestTime);
}
// 給給定的url刷頁面
// 真是不明白 為何刷不起呢?? 我的每一次請求都沒有帶東西啊, 但是debug服務端, 卻又發現了sessoin, 以及cookie, 搞不懂,,
// 那麼 這樣看來刷贊估計也會gg掉了,,
public static void flushVisit(String requestUrl, int requestTime) throws Exception {
Crawler crawler = HtmlCrawler.getInstance();
for(int i=0; i<requestTime; i++) {
// CrawlerConfig config = HtmlCrawlerConfig.get();
// // config.addHeader(Tools.USER_AGENT, "abc");
// config.setTimeout(30 * 1000);
// config.addCookie("key", "val");
// Page page = crawler.getPage(requestUrl, config );
// String content = page.getContent();
// -------------------------------------------------------------------------------
String content = Request.Get(requestUrl).execute().returnContent().asString();
// -------------------------------------------------------------------------------
// 擦 果然是httpClient在作怪
// 可是 這是為何呢? --2016.09.04
// 臥槽 歷經一個小時的debug終於找到問題之所在了,, 但是 httpClient為什麼要快取cookie呢??[估計是為了 減輕服務端的負載吧, 以及方便使用者吧[從此不用在維護cookie了..] ]
// URL url = new URL(requestUrl);
// URLConnection con = url.openConnection();
// String content = Tools.getContent(con.getInputStream() );
Log.log(JSONObject.fromObject(content).getJSONObject("blog").getString("visited") );
// Tools.sleep(1000);
}
}
}
“/action/blogGetAction” 這個action是獲取對應的播客的資訊的action, 其業務中會校驗當前使用者是否看過當前播客, 如果當前使用者沒有看過當前播客, 那麼 當前播客的”訪問次數” + 1
可以看到這裡我有三次嘗試[使用”—–”分割],
第一次是 直接使用HXCrawler進行傳送請求, 然後 當時就出現了這個怪問題,, 百思不得其解, 因此 後來便放棄了
第二次是 今天上午我使用HttpClient的相關API直接傳送請求, 我懷疑是否是我的HXCrawler在哪裡封裝存在問題, 但是 結果這個問題依然存在
第三次是 我開始懷疑是否是HttpClient在幫我做了某些事情,, 因此 使用原生的java相關API傳送請求, 結果 “刷訪問” 居然生效了,,
好了, 開始此次”旅行”吧, 一下內容中刪去了一些我探索過程中碰到的”牛角尖”[花時間的但是無用的部分]
我這裡環境為 : win7 + jdk1.7.40 + httpClient4.5
1. 首先 我懷疑是否是tomcat幫我做了一些事情
因此 我截取了一下伺服器獲取到的header, 下面的是第一次傳送請求的header 和第二次傳送請求的header
=== MimeHeaders ===
host = localhost:8088
connection = Keep-Alive
user-agent = Apache-HttpClient/4.5 (Java/1.7.0_40)
accept-encoding = gzip,deflate
=== MimeHeaders ===
host = localhost:8088
connection = Keep-Alive
user-agent = Apache-HttpClient/4.5 (Java/1.7.0_40)
cookie = JSESSIONID=5E6E98A6635B8CFFE609165763C05C73; isVisited_16=visited
accept-encoding = gzip,deflate
看到了吧, 原來客戶端傳送請求的時候 就多了一個cookie頭, 裡面有兩個cookie, 一個是ssessionId, 另外一個是HXBlog中建立的Cookie
然後 我就想啊, 在第一二次嘗試中, 我都沒有特別的配置”Request.Get”獲取到的請求啊, 為什麼會增加了一個cookie頭呢
2. 然後 我開始懷疑是否是HttpClient幫我做了些什麼
然後 下載相關原始碼, 走一走這個流程, … 省略一個小時
ProtocolExec.execute
然後 問題出來了, 在執行httpProcessor.process之前我的request.headers是這樣子的
然後 執行了httpProcessor.process 之後, request. headers變成這樣了
內容如下
[
Host: localhost:8088,
Connection: Keep-Alive,
User-Agent: Apache-HttpClient/4.5 (Java/1.7.0_40),
Accept-Encoding: gzip,deflate
]
是不是很眼熟啊, 對了 就是上面伺服器第一次獲取到的header
然後 下面是第二次請求的httpProcessor.process處理之後的結果
[
Host: localhost:8088,
Connection: Keep-Alive,
User-Agent: Apache-HttpClient/4.5 (Java/1.7.0_40),
Cookie: JSESSIONID=6F477DD72CD80E3102CA3D13ED03213F; isVisited_16=visited,
Accept-Encoding: gzip,deflate
]
ok, 看到了吧, 結果 已經出現來了, 問題 就在於這個httpProcessor.process
////////////////////////////////////////////////// sepr //////////////////////////////
經過以上的步驟就找到了問題之所在, 下面我們再來詳細的瞅瞅到底HttpClient對於cookie的處理吧
我們這裡分析一下第一次傳送請求, 獲取到結果之後, HttpClient對於Cookie的處理, 以及第二次傳送請求之前對於”快取的Cookie”的封裝
1. 第一次傳送請求得到Response之後對於Cookie的處理
ProtocolExec. execute
ImmutableHttpProcessor.process
ResponseProcessCookies.process
ResponseProcessCookies.processCookies
那麼照這裡看來, 似乎 cookie被儲存到了clientContext.cookieStore
那麼 他是如何在下一次傳送請求的時候注入到cookie到request呢?? 因為”Request.Get.execute“傳入的localContext引數可是為null啊,
Request.Get.execute
那麼 我們接著看一下localContext.cookieStore的來源吧
InternalHttpClient.doExecute
InternalHttpClient. setupContext
看到了吧, clientContext.cookieStore來自於 InternalHttpClient.cookieStore, 這個InternalHttpClient物件也就是我們前面”Request.Get.execute”中的Executor.CLIENT引數, 因為 傳送多次請求 他是”單例”, 因此 多次請求之間的資料 得以傳遞
2. 那麼接下來我們看一下第二次請求的時候, 對於cookie的注入
ProtocolExec. execute
ImmutableHttpProcessor.process
RequestAddCookies.process
cookieSpec對於給定的cookie進行校驗, 判斷”快取的Cookie” 是否屬於當前需要請求的域, 以及 相關安全性驗證
驗證完成之後, 放入matchedCookies, 然後 之後 新增到request的header中
好了,, 原因終於找到了, 但是 還有一點, 如果 需要我不想要HttpClient幫我”快取cookie”呢? 那麼 就使用HttpClientBuilder了
HttpClient的子類挺多的, 我們這裡 只討論的是”Request.Get”中使用的”InternalHttpClient”
完