HTTP狀態管理java
原始的HTTP是被設計爲無狀態的,面向請求/響應的協議,沒有特殊規定有狀態的,貫穿一些邏輯相關的請求/響應交換的會話。因爲HTTP協議變得愈來愈普及和受歡迎,愈來愈多的從前沒有打算使用它的系統也開始爲應用程序來使用它,好比做爲電子商務應用程序的傳輸方式。所以,支持狀態管理就變得很是必要了。瀏覽器
網景公司,一度成爲Web客戶端和服務器軟件開發者的領導方向,在它們基於專有規範的產品中實現了對HTTP狀態管理的支持。以後,網景公司試圖經過發佈規範草案來規範這種機制。它們的努力經過RFC標準跟蹤促成了這些規範定義。然而,在不少應用程序中的狀態管理仍然基於網景公司的草案而不兼容官方的規範。不少主要的Web瀏覽器開發者以爲有必要保留那些極大促進標準片斷應用程序的兼容性。服務器
Cookie是HTTP代理和目標服務器能夠交流保持會話的狀態信息的令牌或短包。網景公司的工程師用它來指「魔法小甜餅」和粘住的名字。cookie
HttpClient使用Cookie接口來表明抽象的cookie令牌。在它的簡單形式中HTTP的cookie幾乎是名/值對。一般一個HTTP的cookie也包含一些屬性,好比版本號,合法的域名,指定cookie應用所在的源服務器URL子集的路徑,cookie的最長有效時間。ide
SetCookie接口表明由源服務器發送給HTTP代理的響應中的頭部信息Set-Cookie來維持一個對話狀態。SetCookie2接口和指定的Set-Cookie2方法擴展了SetCookie。google
SetCookie接口和額外的如獲取原始cookie屬性的能力,就像它們由源服務器指定的客戶端特定功能擴展了Cookie接口。這對生成Cookie頭部很重要,由於一些cookie規範須要。Cookie頭部應該包含在Set-Cookie或Set-Cookie2頭部中指定的特定屬性。spa
Cookie兼容網景公司的草案標準,可是版本0被認爲是不符合官方規範的。符合標準的cookie的指望版本是1。HttpClient能夠處理基於不一樣版本的cookie。線程
這裏有一個從新建立網景公司草案cookie示例:設計
BasicClientCookie netscapeCookie = new BasicClientCookie("name", "value");代理
netscapeCookie.setVersion(0);
netscapeCookie.setDomain(".mycompany.com");
netscapeCookie.setPath("/");
這是一個從新建立標準cookie的示例。要注意符合標準的cookie必須保留由源服務器發送的全部屬性:
BasicClientCookie stdCookie = new BasicClientCookie("name", "value");
stdCookie.setVersion(1);
stdCookie.setDomain(".mycompany.com");
stdCookie.setPath("/");
stdCookie.setSecure(true);
// 精確設置由服務器發送的屬性
stdCookie.setAttribute(ClientCookie.VERSION_ATTR, "1");
stdCookie.setAttribute(ClientCookie.DOMAIN_ATTR, ".mycompany.com");
這是一個從新建立Set-Cookie2兼容cookie的實例。要注意符合標準的cookie必須保留由源服務器發送的全部屬性:
BasicClientCookie2 stdCookie = new BasicClientCookie2("name", "value");
stdCookie.setVersion(1);
stdCookie.setDomain(".mycompany.com");
stdCookie.setPorts(new int[] {80,8080});
stdCookie.setPath("/");
stdCookie.setSecure(true);
// 精確設置由服務器發送的屬性
stdCookie.setAttribute(ClientCookie.VERSION_ATTR, "1");
stdCookie.setAttribute(ClientCookie.DOMAIN_ATTR, ".mycompany.com");
stdCookie.setAttribute(ClientCookie.PORT_ATTR, "80,8080");
CookieSpec接口表明了cookie管理的規範。Cookie管理規範但願以下幾點:
解析的Set-Cookie規則還有可選的Set-Cookie2頭部信息。
驗證解析cookie的規則。
格式化給定主機的Cookie頭部信息,原始端口和路徑。
HttpClient附帶了一些CookieSpec的實現:
網景公司草案:這個規範符合由網景通信發佈的原始草案規範。應當避免,除非有絕對的必要去兼容遺留代碼。
RFC 2109:官方HTTP狀態管理規範並取代的老版本,被RFC 2965取代。
RFC 2965:官方HTTP狀態管理規範。
瀏覽器兼容性:這個實現努力去密切模仿(mis)通用Web瀏覽器應用程序的實現。好比微軟的Internet Explorer和Mozilla的FireFox瀏覽器。
最佳匹配:’Meta’(元)cookie規範採用了一些基於又HTTP響應發送的cookie格式的cookie策略。它基本上聚合了以上全部的實現到以一個類中。
強烈建議使用Best Match策略,讓HttpClient在運行時基於執行上下文采用一些合適的兼容等級。
這些是用於定製HTTP狀態管理和獨立的cookie規範行爲的參數。
'http.protocol.cookie-datepatterns':定義了用於解析非標準的expires屬性的合法日期格式。只是對兼容不符合規定的,仍然使用網景公司草案定義的expires而不使用標準的max-age屬性服務器須要。這個參數指望獲得一個java.util.Collection類型的值。集合元素必須是java.lang.String類型,來兼容java.text.SimpleDateFormat的語法。若是這個參數沒有被設置,那麼默認的選擇就是CookieSpec實現規範的值。要注意這個參數的應用。
'http.protocol.single-cookie-header':定義了是否cookie應該強制到一個獨立的Cookie請求頭部信息中。不然,每一個cookie就被看成分離的Cookie頭部信息來格式化。這個參數指望獲得一個java.lang.Boolean類型的值。若是這個參數沒有被設置,那麼默認的選擇就是CookieSpec實現規範的值。要注意這個參數僅僅嚴格應用於cookie規範(RFC 2109和RFC 2965)。瀏覽器兼容性和網景公司草案策略將會放置全部的cookie到一個請求頭部信息中。
'http.protocol.cookie-policy':定義了用於HTTP狀態管理的cookie規範的名字。這個參數指望獲得一個java.lang.String類型的值。若是這個參數沒有被設置,那麼合法的日期格式就是CookieSpec實現規範的值。
HttpClient使用CookieSpecRegistry類維護一個可用的cookie規範註冊表。下面的規範對於每一個默認都是註冊過的:
兼容性:瀏覽器兼容性(寬鬆策略)。
網景:網景公司草案。
rfc2109:RFC 2109(過期的嚴格策略)。
rfc2965:RFC 2965(嚴格策略的標準符合)。
best-match:最佳匹配meta(元)策略。
Cookie策略能夠在HTTP客戶端被設置,若是須要,在HTTP請求級重寫。
HttpClient httpclient = new DefaultHttpClient();
// 對每一個默認的強制嚴格cookie策略
httpclient.getParams().setParameter(
ClientPNames.COOKIE_POLICY, CookiePolicy.RFC_2965);
HttpGet httpget = new HttpGet("http://www.broken-server.com/");
// 對這個請求覆蓋默認策略
httpget.getParams().setParameter(
ClientPNames.COOKIE_POLICY, CookiePolicy.BROWSER_COMPATIBILITY);
爲了實現定製cookie策略,咱們應該建立CookieSpec接口的定製實現類,建立一個CookieSpecFactory實現來建立和初始化定製實現的實例並和HttpClient註冊這個工廠。一旦定製實現被註冊了,它能夠和標準的cookie實現有相同的活性。
CookieSpecFactory csf = new CookieSpecFactory() {
public CookieSpec newInstance(HttpParams params) {
return new BrowserCompatSpec() {
@Override
public void validate(Cookie cookie, CookieOrigin origin)
throws MalformedCookieException {
// 這至關簡單
}
};
}
};
DefaultHttpClient httpclient = new DefaultHttpClient();
httpclient.getCookieSpecs().register("easy", csf);
httpclient.getParams().setParameter(
ClientPNames.COOKIE_POLICY, "easy");
HttpClient能夠和任意物理表示的實現了CookieStore接口的持久化cookie存儲一塊兒使用。默認的CookieStore實現稱爲BasicClientCookie,這是憑藉java.util.ArrayList的一個簡單實現。在BasicClientCookie對象中存儲的cookie當容器對象被垃圾回收機制回收時會丟失。若是須要,用戶能夠提供更復雜的實現。
DefaultHttpClient httpclient = new DefaultHttpClient();
// 建立一個本地的cookie store實例
CookieStore cookieStore = new MyCookieStore();
// 若是須要填充cookie
BasicClientCookie cookie = new BasicClientCookie("name", "value");
cookie.setVersion(0);
cookie.setDomain(".mycompany.com");
cookie.setPath("/");
cookieStore.addCookie(cookie);
// 設置存儲
httpclient.setCookieStore(cookieStore);
在HTTP請求執行的過程當中,HttpClient添加了下列和狀態管理相關的對象到執行上下文中:
'http.cookiespec-registry':CookieSpecRegistry實例表明了實際的cookie規範註冊表。這個屬性的值設置在本地內容中,優先於默認的。
'http.cookie-spec':CookieSpec實例表明真實的cookie規範。
'http.cookie-origin':CookieOrigin實例表明了真實的源服務器的詳細信息。
'http.cookie-store':CookieStore實例表明了真實的cookie存儲。設置在本地內容中的這個屬性的值優先於默認的。
本地的HttpContext對象能夠被用來定製HTTP狀態管理內容,先於請求執行或在請求執行以後檢查它的狀態:
HttpClient httpclient = new DefaultHttpClient();
HttpContext localContext = new BasicHttpContext();
HttpGet httpget = new HttpGet("http://localhost:8080/");
HttpResponse response = httpclient.execute(httpget, localContext);
CookieOrigin cookieOrigin = (CookieOrigin) localContext.getAttribute(
ClientContext.COOKIE_ORIGIN);
System.out.println("Cookie origin: " + cookieOrigin);
CookieSpec cookieSpec = (CookieSpec) localContext.getAttribute(
ClientContext.COOKIE_SPEC);
System.out.println("Cookie spec used: " + cookieSpec);
咱們可使用獨立的本地執行上下文來實現對每一個用戶(或每一個線程)狀態的管理。定義在本地內容中的cookie規範註冊表和cookie存儲將會優先於設置在HTTP客戶端級別中默認的那些。
HttpClient httpclient = new DefaultHttpClient();
// 建立cookie store的本地實例
CookieStore cookieStore = new BasicCookieStore();
// 建立本地的HTTP內容
HttpContext localContext = new BasicHttpContext();
// 綁定定製的cookie store到本地內容中
localContext.setAttribute(ClientContext.COOKIE_STORE, cookieStore);
HttpGet httpget = new HttpGet("http://www.google.com/");
// 做爲參數傳遞本地內容
HttpResponse response = httpclient.execute(httpget, localContext)