原始的HTTP被設計成無狀態的面向請求響應的協議,它並沒有爲基於跨幾個邏輯相關的請求/響應交換的有狀態會話提供所需的功能。但是隨着HTTP協議越來越流行並且被應用,越來越多的系統開始用它作爲原本並不是它的作用的功能,例如,電子商務傳輸應用,這樣一來,對於狀態管理的支持成爲一個必要的功能。
那時網景公司作爲一個web客戶端和服務器端軟件的領導開發者在他們的一個基於特殊的說明的產品裏實現了對HTTP狀態管理的支持,後來,網景通過發佈一個知指導說明書試圖標準化這一機制。這些努力促成了通過RFC標準的正式定義。然而,大量應用的管理仍然基於網景的指導說明並且與官方的定義不兼容。所有主流瀏覽器的開發者感到必須和那些促成標準的應用保持兼容。
3.1 HTTP cookies
一個HTTP cookie 是一個印記或者一個HTTP代理和目標服務器之間可以交換用以維持一個會話的狀態信息的數據包。網景的工程師習慣於稱之爲“魔法cookie”。
HttpClient使用Cookie接口描述抽象cookie,HTTP cookie最簡單的形式是隻有名稱/值對。通常一個HTTP cookie也包含很多屬性,例如一個有效的域名,一個URLs的相對路徑,或者cookie的最大有效週期。
SetCookie接口描述了一組爲了保持會話狀態由原始服務器發送至HTTP代理的cookie響應頭。
ClientCookie接口繼承了Cookie接口並且添加了具體的功能,例如,當原始服務器明確提出返回原始cookie時返回cookie的功能。這對生成cookie頭非常重要,因爲一些cookie指導說明要求僅當這些cookie在cookie集合頭中被明確要求的時候cookie頭應該包含確定的屬性。
下面是一個創建客戶端cookie對象的例子:
BasicClientCookie cookie = new BasicClientCookie("name", "value"); // Set effective domain and path attributes cookie.setDomain(".mycompany.com"); cookie.setPath("/"); // Set attributes exactly as sent by the server cookie.setAttribute(ClientCookie.PATH_ATTR, "/"); cookie.setAttribute(ClientCookie.DOMAIN_ATTR, ".mycompany.com");
3.2 Cookie 規範
CookieSpec接口描述了cookie管理規範,cookie管理規範有望加強:
解析設置cookie頭的規則。
校驗解析cookie的規則。
對於給定原始主機、端口和路徑格式化cookie頭。
HttpClient提供了幾個CookieSpec的實現:
嚴格遵守標準的策略:狀態管理策略遵從RFC6265第四章定義的語法和詞法。
標準策略:狀態管理策略遵從一個更加鬆散的RFC6265第四章定義的語法和詞法,目的是位了和那些已經存在的沒有遵從標準協議的服務器交互。
由網景公司規劃的策略(廢棄):這個策略遵從網景公司發佈的原始定義,除非有絕對的需求否則應該避免。
RFC2965號策略(廢棄):狀態管理策略遵從廢棄的RFC2965定義的策略,請不要在新應用中使用。
RFC2109號策略(廢棄):狀態管理策略遵從廢棄的RFC2109定義的策略,請不要在新應用中使用。
瀏覽器兼容性策略(廢棄):此策略和老版本的瀏覽器例如IE和FireFox衝突,請不要再新應用中使用。
默認的策略:默認的cookie策略是一個複合的策略,它即遵從了RFC2965和RFC2109又遵從了基於被HTTP響應發送的cookie的屬性(例如版本屬性,現已廢棄)的網景公司出品的實現,這個策略在下一個標準的HttpClient版本發佈時過時。
忽略cookie的策略:所有的cookie被忽略。
強烈建議在新的應用中使用標準或嚴格遵從標準的策略。廢棄的規範應該只用於兼容系統。在下個HttpClient主要版本發佈的時候將移除那些支持廢棄規範的策略。
3.3 選擇cookie策略
cookie策略可以在HttpClient中設置並且如果需要還可以重寫HTTP請求。
RequestConfig globalConfig = RequestConfig.custom().setCookieSpec(CookieSpecs.DEFAULT).build(); CloseableHttpClient httpclient = HttpClients.custom().setDefaultRequestConfig(globalConfig).build(); RequestConfig localConfig = RequestConfig.copy(globalConfig).setCookieSpec(CookieSpecs.STANDARD_STRICT).build(); HttpGet httpGet = new HttpGet("/"); httpGet.setConfig(localConfig);
3.4 定製cookie策略
爲了實現定製cookie策略,你應該創建一個定製的CookieSpec接口的實現,創建一個CookieSpecProvider實現類去創建和初始化定製規範的實例並使用HttpClient註冊工廠。一旦定製的實現被註冊就可以以和標準cookie的實現相同的方式被激活。
PublicSuffixMatcher publicSuffixMatcher = PublicSuffixMatcherLoader.getDefault(); Registry<CookieSpecProvider> r = RegistryBuilder.<CookieSpecProvider>create().register(CookieSpecs.DEFAULT,new DefaultCookieSpecProvider(publicSuffixMatcher)) .register(CookieSpecs.STANDARD,new RFC6265CookieSpecProvider(publicSuffixMatcher)) .register("easy", new EasySpecProvider()).build(); RequestConfig requestConfig = RequestConfig.custom().setCookieSpec("easy").build(); CloseableHttpClient httpclient = HttpClients.custom().setDefaultCookieSpecRegistry(r).setDefaultRequestConfig(requestConfig).build();
3.5 cookie持久化
HttpClient可以處理任何實現了CookieStore接口的持久化cookie存儲。默認的CookieStore實現是BasicCookieStore,它是一個用ArrayList存儲的簡單實現。當容器對象進行垃圾回收的時候在BasicClientCookie對象存儲的cookie將會丟失,如果需要用戶可以提供更多複雜的實現。
// Create a local instance of cookie store CookieStore cookieStore = new BasicCookieStore(); // Populate cookies if needed BasicClientCookie cookie = new BasicClientCookie("name", "value"); cookie.setDomain(".mycompany.com"); cookie.setPath("/"); cookieStore.addCookie(cookie); // Set the store CloseableHttpClient httpclient = HttpClients.custom().setDefaultCookieStore(cookieStore).build();
3.6 HTTP狀態管理與執行上下文
此章節增加了下面的狀態管理相關對象:
Lookup實例代表了實際的cookie細節記錄。在本地上寫文中這個屬性值的設置優先級高於默認的。
CookieSpec實例表示實際的cookie細節。
CookieOrigin實例表示了原始服務器的實際細節。
CookieStore實例表示了實際的cookie存儲。在本地上寫文中這個屬性值的設置優先級高於默認的。
本地的HttpContext對象可以在執行請求之前被用於定製HTTP狀態管理上下文,或者在請求執行後檢查其狀態。也可以使用分離的執行上下文實現每個用戶(或每個線程)狀態管理。定義在本地上下文的cookie詳情記錄和cookie存儲優先級高於默認設置的級別。
CloseableHttpClient httpclient = <...> Lookup<CookieSpecProvider> cookieSpecReg = <...> CookieStore cookieStore = <...> HttpClientContext context = HttpClientContext.create(); context.setCookieSpecRegistry(cookieSpecReg); context.setCookieStore(cookieStore); HttpGet httpget = new HttpGet("http://somehost/"); CloseableHttpResponse response1 = httpclient.execute(httpget, context); <...> // Cookie origin details CookieOrigin cookieOrigin = context.getCookieOrigin(); // Cookie spec used CookieSpec cookieSpec = context.getCookieSpec();