原始的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();