HttpComponents-Core 学习
HttpComponents-Core 学习
官方文档:http://hc.apache.org/httpcomponents-core-4.4.x/tutorial/html/index.html
点击进入 我的为知笔记链接(带格式)
前言
HttpCore是一组实现了大部分HTTP协议的组件的机会,虽然没有全部实现,但已足够用来开发完全功能的客户端和服务端HTTP服务!
HttpCore Scope 范围
- 一致性的API,用于构建client/proxy/server side HTTP 服务。
- 一致性的API,用于构建同步和异步的HTTP服务。
- 低层级组件的集合,基于阻塞式(经典)和非阻塞式(NIO)模型。
HttpCore Goals 目标
- 实现HTTP传输的大部分功能。
- 在性能和 API的 清晰性 & 表达性 之间平衡。
- 小的内存占用。
- 自包含的库(没有任何外部依赖,除了JRE)。
HttpCore不是什么?
- 不是HttpClient的替代品。
- 不是Servlet APIs的替代品。
第一章 1.基础
1.1 HTTP messages
1.1.1 结构
一个HTTP message 由一个消息头和一个可选的消息体组成。一个HTTP request 的消息头 由一个请求行 和一组header字段构成。一个HTTP response 的消息头由一个状态行 和一组header字段构成。 所有的HTTP messages 必须包含协议版本。一些HTTP messages 可能包含一个内容体(content body)。
HttpCore 贴切地遵守该定义,来定义了 HTTP message对象模型,并提供了对HTTP message元素的序列化(格式化)和反序列化(解析)的扩展支持。
1.1.2 基本操作
1.1.2.1 HTTP request message
HTTP request 是从 客户端 发送到 服务端 的message。该message的第一行包含了method、uri、以及协议版本。
HttpRequest request = new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1);
1.1.2.2 HTTP response message
HTTP response 是从 服务端 返回到 客户端 的message,是在服务端接收并解释了一个request message之后。 第一行由协议版本、状态码、状态文字构成。
HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK");
1.1.2.3 HTTP message 的公共属性和方法
一个HTTP message 可能包含多个headers 以描述该message的属性,例如内容长度、内容类型等到。
HttpCore 提供了方法来获取、添加、移除、以及遍历这些headers:
HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK");
response.addHeader("Set-Cookie", "c1=a; path=/; domain=localhost");
response.addHeader("Set-Cookie", "c2=b; path=\"/\", c3=c; domain=\"localhost\"");
Header h1 = response.getFirstHeader("Set-Cookie");
Header h2 = response.getLastHeader("Set-Cookie");
Header[] hs = response.getHeaders("Set-Cookie");
还有一种高效的方式来获取某个key对应的所有headers:
HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK");
response.addHeader("Set-Cookie", "c1=a; path=/; domain=localhost");
response.addHeader("Set-Cookie", "c2=b; path=\"/\", c3=c; domain=\"localhost\"");
HeaderIterator it = response.headerIterator("Set-Cookie");
while (it.hasNext()) {
System.out.println(it.next());
}
还提供了便捷的方法来将 HTTP message 解析成单独的header元素:
HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK");
response.addHeader("Set-Cookie", "c1=a; path=/; domain=localhost");
response.addHeader("Set-Cookie", "c2=b; path=\"/\", c3=c; domain=\"localhost\"");
HeaderElementIterator it = new BasicHeaderElementIterator(
response.headerIterator("Set-Cookie"));while (it.hasNext()) {
HeaderElement elem = it.nextElement();
System.out.println(elem.getName() + " = " + elem.getValue());
NameValuePair[] params = elem.getParameters();
for (int i = 0; i < params.length; i++) {
System.out.println(" " + params[i]);
}
}
1.1.3 HTTP entity
HTTP messages 可以携带一个 content entity,这是可选的。
那些使用entities的Requests,通常叫做 entity-enclosing requests。HTTP spec 定义了两种entity-enclosing methods:POST 和 PUT。
Response 通常enclose a content entity。 当然也有例外,如对HEAD method的响应,如204 No Content、304 Not Modified、205 Reset Content responses。
依据内容的目的不同,HttpCore 划分出三种类型的entity:
- streamed(流的):该content 是接收自一个stream。尤其,该类别包含了接收自一个连接的entity。一般不可重复。
- self-contained (自包容的):该content 是在内存中,或者通过无关于一个连接或者其他entity的方法获取到。通常可重复。
- wrapping (包装的):获取自其他entity。
1.1.3.1 可重复的entity
一个entity是可重复的,意味着它的content可以被多次读取。仅适用于self-contained entity (如ByteArrayEntity 或 StringEntity)。
1.1.3.2 使用HTTP entity
由于 entity 可以是binary 也可以是 character content,所以它支持character encodings。--天然支持编码。
从entity中读取content,可以通过 HttpEntity#getContent() 方法来获取InputStream,也可以 为HttpEntity#writeTo(OutputStream)方法提供一个输出流,候着会将所有内容一次性地返回到给定的流中。 请注意:一些非流式的entity(如self-contained),可能无法将其content有效地表现为InputStream。 对于这些entity,可以只实现HttpEntity#writeTo(OutputStream) 方法,而对HttpEntity#getContent()采取抛出UnsupportedOperationException操作。
EntityUtils类,暴露了一些静态方法,以简化 从entity中提取content或信息的过程。与直接读取InputStream相比,你可以使用该类的方法来获取完整的content body --以字符串或者byte array的形式。
当从来信 中接收到entity时,可以使用 HttpEntity#getContentType() 和 HttpEntity#getContentLength() 来读取公有的元数据,如Content-Type 和 Content-Length headers(如果可用)。因为Content-Type header 可能为诸如text/plain 或text/html之类的mime-types 包含一个字符编码,用可以使用 HttpEntity#getContentEncoding() 来读取该信息。 如果 headers不可用,长度返回-1,content type为NULL。 如果Content-Type header可用, 会返回一个Header对象。
当为去信 创建一个entity时, 必须提供元数据:
StringEntity myEntity = new StringEntity("important message", Consts.UTF_8);
System.out.println(myEntity.getContentType());
System.out.println(myEntity.getContentLength());
System.out.println(EntityUtils.toString(myEntity));
System.out.println(EntityUtils.toByteArray(myEntity).length);
1.1.3.3 确保释放系统资源
为了确保正确地释放系统资源,用户必须close 与entity关联的content stream。
HttpResponse response;
HttpEntity entity = response.getEntity();
if (entity != null) {
InputStream instream = entity.getContent();
try {
// do something useful
} finally {
instream.close();
}
}
当处理 streamed entity时,用户可用使用 EntityUtils#consume(HttpEntity) 来确保entity content被完全地consumed,并且底层的stream被关闭。
1.1.4 创建entity
有几种方式来创建entity。 HttpCore 提供了下列实现:
1.1.4.1 BasicHttpEnitty
顾名思义,它代表了一种底层的stream。
该entity 有一个empty constructor。 构造之后,no content,content length为 -1。
用户需要设置content stream,以及可选地设置length。
BasicHttpEntity myEntity = new BasicHttpEntity();
myEntity.setContent(someInputStream);
myEntity.setContentLength(340); // sets the length to 340; optionally
1.1.4.2 ByteArrayEntity
这是一个self-contained、可重复的entity,它从一个给定的byte array获取content。
ByteArrayEntity myEntity = new ByteArrayEntity(new byte[] {1,2,3}, ContentType.APPLICATION_OCTET_STREAM);
1.1.4.3 StringEntity
这是一个self-contained、可重复的entity,从java.lang.String对象中获取content。
StringBuilder sb = new StringBuilder();
Map<String, String> env = System.getenv();
for (Map.Entry<String, String> envEntry : env.entrySet()) {
sb.append(envEntry.getKey())
.append(": ").append(envEntry.getValue())
.append("\r\n");
}
// construct without a character encoding (defaults to ISO-8859-1). TODO: 不推荐
HttpEntity myEntity1 = new StringEntity(sb.toString());
// alternatively construct with an encoding (mime type defaults to "text/plain"). TODO: 推荐
HttpEntity myEntity2 = new StringEntity(sb.toString(), Consts.UTF_8);
// alternatively construct with an encoding and a mime type
HttpEntity myEntity3 = new StringEntity(sb.toString(), ContentType.create("text/plain", Consts.UTF_8));
1.1.4.4 InputStreamEntity
这是一个streamed、不可重复的entity,从一个输入流获取content。通过提供输入流和content length 来构造。提供的 content length 是用来限制读取的数据量,但是,如果给定负值,那会读取全部数据。
InputStream instream = getSomeInputStream();
InputStreamEntity myEntity = new InputStreamEntity(instream, 16);
1.1.4.5 FileEntity
这是一个self-contained、可重复的entity,从一个文静中获取content。
HttpEntity entity = new FileEntity(staticFile, ContentType.create("application/java-archive"));
1.1.4.6 HttpEntityWrapper
这是创建wrapped entity的基类。该entity会持有被封装的entity的引用,并代理所有对其的调用。 wrapping entity的实现可用继承自该类,只需要重写那些不应该被代理到被封装的entity的方法即可。
1.1.4.7 BufferedHttpEntity
这是HttpEntityWrapper的子类。 通过提供另一个entity来构造。它会读取提供的entity的content,并将其缓存进内存。
这使得其可以从一个不可重复的entity得到一个可重复的entity。 如果提供的entity本身可重复,它会简单地pass the calls。
myNonRepeatableEntity.setContent(someInputStream);
BufferedHttpEntity myBufferedEntity = new BufferedHttpEntity(myNonRepeatableEntity);
1.2 HTTP 协议 处理器
HTTP协议拦截器是一个程序,该程序实现了HTTP协议的特定方面(aspect)。通常,协议拦截器作用于一个特定的header或者一组相关联的headers -- 无论是来信或者去信。协议拦截器 也可以操作message中的content entity;透明的内容压缩/解压缩是一个很好的例子。通常这是通过装饰器模式来实现:一个wrapper entity class来装饰原有的entity。 数个协议拦截器可以结合起来形成一个逻辑单元。
HTTP 协议处理器 是一组协议拦截器的集合,其实现了 'Chain of Responsibility' pattern,每一个独立的协议拦截器负责HTTP协议的一个特定方面。
通常,拦截器的执行顺序不会影响彼此,因为它们不依赖于执行上下文的特定状态。如果依赖了,那就必须以特定的顺序来执行!
协议拦截器必须是线程安全的。类似于servlets,协议拦截器不应该使用实例变量,除非是同步的。
1.2.1 标准的协议拦截器
HttpCore 提供了数个最基本的协议拦截器,用于客户端和服务端HTTP处理。
1.2.1.1 RequestContent
这是用于outgoing request的最重要的拦截器。负责限制content length,通过添加Content-Length或者Transfer-Content header实现。客户端必须。
1.2.1.2 ResponseContent
这是用于outgoing response的最重要的拦截器。负责限制content length,通过添加Content-Length或者Transfer-Content header实现。服务端必须。
1.2.1.3 RequestConnControl
负责为outgoing request添加 Connection header,这对于管理HTTP/1.0连接来说很重要。客户端推荐。
1.2.1.4 ResponseConnControl
负责为outgoing response添加 Connection header,这对于管理HTTP/1.0连接来说很重要。服务端推荐。
1.2.1.5 RequestDate
负责为outgoing request 添加Date header。客户端可选。
1.2.1.6 ResponseDate
负责为outgoing response添加Date header。服务端推荐。
1.2.1.7 RequestExpectContinue
用于通过添加Expect header 来启用'expect-continue'握手。推荐用于客户端协议处理器。客户端推荐。
1.2.1.8 RequestTargetHost
负责添加Host header。客户端必须。
1.2.1.9 RequestUserAgent
负责添加User-Agent header。客户端推荐。
1.2.1.10 ResponseServer
负责添加Server header。 服务端推荐。
1.2.2 使用协议处理器
通常,HTTP协议处理器用于预处理来信,以及后处理出信。
如下:
HttpProcessor httpproc = HttpProcessorBuilder.create()
// Required protocol interceptors
.add(new RequestContent())
.add(new RequestTargetHost())
// Recommended protocol interceptors
.add(new RequestConnControl())
.add(new RequestUserAgent("MyAgent-HTTP/1.1"))
// Optional protocol interceptors
.add(new RequestExpectContinue(true))
.build();
HttpCoreContext context = HttpCoreContext.create();
HttpRequest request = new BasicHttpRequest("GET", "/");
httpproc.process(request, context);
或者:
HttpResponse response = <...>
httpproc.process(response, context);
请注意:BasicHttpProcessor 不会同步访问其内部结构,因此,可能线程不安全。
1.3 HTTP execution context 执行上下文
起初的时候,HTTP被设计成一个无状态的、面向响应-请求的协议。然而,实际的应用经常需要在数个逻辑相关的请求-响应交换中保持状态信息。为了使得应用能够维持一个处理状态,HttpCore 允许HTTP message 在一个特定的execution context中执行,这个执行上下文也被叫做 HTTP context。
多个逻辑相关的message可以参与进同一个逻辑session -- 只要连续的请求中重复使用同一个context。HTTP context 功能类似于 Map<String, Object>。只是一组逻辑相关的KV的集合。
请注意,HttpContext可以包含任意对象,因此,在多线程之间共享可能不安全。请确认HttpContext实例在同一时刻只被一个线程访问。
1.3.1 Context sharing 上下文共享
协议拦截器 可以通过共享信息来合作,如:经由一个HTTP execution context来共享同一个处理状态。
HTTP context 是一个结构,可被用于将一个attribute name映射到attribute value。内部,HTTP context 的实现通常是由HashMap支持的。HTTP context 最基本的目标是有效利用多个逻辑相关的组件之间共享的信息。HTTP context 可以用于存储一个处理状态。多个逻辑相关的消息可以参与进同一个逻辑会话 -- 只要这连续的消息之间重用了同一个context。
HttpProcessor httpproc = HttpProcessorBuilder.create()
.add(new HttpRequestInterceptor() {
public void process(
HttpRequest request,
HttpContext context) throws HttpException, IOException {
String id = (String) context.getAttribute("session-id");
if (id != null) {
request.addHeader("Session-ID", id);
}
}
})
.build();
HttpCoreContext context = HttpCoreContext.create();
HttpRequest request = new BasicHttpRequest("GET", "/");
httpproc.process(request, context);
第二章 2. 阻塞式I/O模型
Java中的阻塞式I/O,代表了一个非常高效的、便捷的I/O模型,非常适合高性能应用(并发连接数 中等)。现代JVMs能够有效的进行context 切换,并且 阻塞式I/O 模型应该提供最佳性能 -- 只要并发连接数低于1000,且连接主要是传输数据时。然而,对于那些 在多数时间内都空闲的连接的应用来说,非阻塞式I/O模型可能是一个更好的选择。
2.1 阻塞式HTTP 连接
HTTP 连接负责 HTTP message的序列化和反序列化。 用户很少需要直接使用HTTP 连接对象。有更高级的协议组件用于执行和处理HTTP请求。
然而,在某些情况下,仍然需要直接操作HTTP连接,例如,获取某些属性:连接状态、socket超时、local/remote地址等。
需要谨记在心:HTTP连接是线程不安全的! 我们强烈建议 限制只有一个线程来与HTTP connection交互。HttpConnection接口中能够由其他线程安全调用的唯一方法是:HttpConnection#shutdown()。
2.1.1 使用阻塞式HTTP 连接
HttpCore没有 为打开连接 提供完全的支持,因为创建一个新连接的过程 -- 特别是在客户端 -- 可能非常复杂,当涉及到一个或多个认证 和/或 通道代理时。相反,阻塞式HTTP连接可用于任何network socket。
Socket socket = <...>
DefaultBHttpClientConnection conn = new DefaultBHttpClientConnection(8 * 1024);
conn.bind(socket);
System.out.println(conn.isOpen());
HttpConnectionMetrics metrics = conn.getMetrics();
System.out.println(metrics.getRequestCount());
System.out.println(metrics.getResponseCount());
System.out.println(metrics.getReceivedBytesCount());
System.out.println(metrics.getSentBytesCount());
未完待续
ps:未知笔记的链接里已经把除了NIO部分的全放上去了。
HttpComponents-Core 学习的更多相关文章
- EntityFramework Core 学习系列(一)Creating Model
EntityFramework Core 学习系列(一)Creating Model Getting Started 使用Command Line 来添加 Package dotnet add pa ...
- ASP.NET Core学习系列
.NET Core ASP.NET Core ASP.NET Core学习之一 入门简介 ASP.NET Core学习之二 菜鸟踩坑 ASP.NET Core学习之三 NLog日志 ASP.NET C ...
- Nancy in .Net Core学习笔记 - 初识Nancy
前言 去年11月份参加了青岛MVP线下活动,会上老MVP衣明志介绍了Nancy, 一直没有系统的学习一下,最近正好有空,就结合.NET Core学习总结了一下. 注: 本文中大部分内容都是对官网文档的 ...
- .NET CORE学习笔记系列(2)——依赖注入[7]: .NET Core DI框架[服务注册]
原文https://www.cnblogs.com/artech/p/net-core-di-07.html 包含服务注册信息的IServiceCollection对象最终被用来创建作为DI容器的IS ...
- .NET CORE学习笔记系列(2)——依赖注入[6]: .NET Core DI框架[编程体验]
原文https://www.cnblogs.com/artech/p/net-core-di-06.html 毫不夸张地说,整个ASP.NET Core框架是建立在一个依赖注入框架之上的,它在应用启动 ...
- .NET CORE学习笔记系列(2)——依赖注入[5]: 创建一个简易版的DI框架[下篇]
为了让读者朋友们能够对.NET Core DI框架的实现原理具有一个深刻而认识,我们采用与之类似的设计构架了一个名为Cat的DI框架.在上篇中我们介绍了Cat的基本编程模式,接下来我们就来聊聊Cat的 ...
- .NET CORE学习笔记系列(2)——依赖注入[4]: 创建一个简易版的DI框架[上篇]
原文https://www.cnblogs.com/artech/p/net-core-di-04.html 本系列文章旨在剖析.NET Core的依赖注入框架的实现原理,到目前为止我们通过三篇文章从 ...
- .NET CORE学习笔记系列(2)——依赖注入【3】依赖注入模式
原文:https://www.cnblogs.com/artech/p/net-core-di-03.html IoC主要体现了这样一种设计思想:通过将一组通用流程的控制权从应用转移到框架中以实现对流 ...
- .NET CORE学习笔记系列(2)——依赖注入【2】基于IoC的设计模式
原文:https://www.cnblogs.com/artech/p/net-core-di-02.html 正如我们在<控制反转>提到过的,很多人将IoC理解为一种“面向对象的设计模式 ...
- .NET CORE学习笔记系列(2)——依赖注入【1】控制反转IOC
原文:https://www.cnblogs.com/artech/p/net-core-di-01.html 一.流程控制的反转 IoC的全名Inverse of Control,翻译成中文就是“控 ...
随机推荐
- Node和Electron开发入门(四):操作PC端文件系统
一.调用PC端默认方式打开本地文件 在main.js里 // 打开系统本地文件或者网页链接 const {shell} = require('electron'); // Open a local f ...
- Mybatis根据配置文件获取session(多数据源)
1.config.xml <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE configura ...
- 使用jQuery清空file文件域的解决方案
使用jQuery清空file文件域的解决方案 var file = $("#file") file.after(file.clone().val("")); f ...
- Python(六)之文件对象
Python文件对象 明确文件系统: 获取文件对象: var_name = open(file_name[mode,[bufsize]]) 缓冲: 0:禁用 1:使用缓冲,只缓冲一行数据 2+:指定缓 ...
- hystrix服务降级和服务熔断的区别
故事的背景是这样的:由于小强在工作中碰到一些问题,于是想请教一下业界大牛小壮.于是发生了下面的两个场景: 小强在拿起常用手机拨号时发现该手机没有能够拨通,所以就拿出了备用手机拨通了某A的电话,这个过程 ...
- 浅谈C#中的深拷贝(DeepCopy)与浅拷贝(MemberwiseClone)
场景:MVVM模式中数据双向绑定,想实现编辑.保存.撤销操作时也双向绑定了,不能实现撤销操作. 后来通过搜索找到了继承IEditableObject接口实现撤销操作,但是对其中使用了Memberwis ...
- JS压缩工具配置
1.打开文件夹 修改yui.reg 里面修改为自己的目录. 2.yuicompressor.bat 修改SET YUIFOLDER=E:\JS常用工具\JS_compres\build 为自己的目录
- python 查找文件内容
输入查找的文件夹路径,要查找的内容关键字(可以指定多个),要查找的文件类型(可以是多个),搜索出符合条件的文件,并记录所有符合条件的行号及行内容. 写的感觉有点冗余,但好歹还能使用^-^,主要是方便手 ...
- python List的一些相关操作
把一些基础的东西归类整理,作记录. 添加元素 a=[7,8,9,10] a.append('a') #在最后位置添加 a.insert(1,'b') #在指定位置添加 删除元素 del a[1 ...
- Redis Java连接操作
安装 要在Java程序中使用使用操作Redis,需要确保有Redis的Java驱动程序和Java设置在机器上.可以检查看Java教程-学习如何在机器上安装Java.现在,让我们来看看如何设置Redis ...