您的位置:首页 > 理论基础 > 计算机网络

畅游HttpCore

2017-10-22 15:50 10 查看

欢迎

非常欢迎阅读本文,本文主要介绍HttpCore是如何工作的。

你应该知道HTTP是一种用于在客户端与服务端进行消息交换的协议。它使用的特别广泛,它通常运行在TCP/IP或者安全的TLS/SSL之上。

Apache有一个HTTP协议的客户端实现Commons HttpClient。通俗地讲,称它为基于3.X的或者老代码。很多人都从中受益,但老代码在设计上有一些严重的限制。例如,有一个叫做HttpMethodBase的类,它同时表示一个请求和一个响应,它同时实现了两者的逻辑。这种将不同的功能放在一起的设计,让它很难维护,也很难进行扩展。

因此,我们启动了它的后继项目,HttpComponents。基于老代码中经验和教训,使用新的方法来实现HTTP协议。最终,使用不现的模块来处理不同的方面。从HttpCore的名字可以看出来,这个模块处于核心的地位。它被定义为其他模块的基础。

HttpCore处理HTTP消息的表示、传输逻辑包括发送和接收消息。它还被定义为一种基础框架,方便其他模块进行扩展其功能。与老代码不同,它不仅仅是HTTP客户端的实现,同时也可以做为服务端。正是因为它与老代码有这么大的不同,我们才决定将其放在新的包层次结构中,以免引起你的困惑。



Q:我有一个问题。
A:哦?什么问题,请讲。
Q:如果把它放在新的包层次结构中,那使用老代码的应用程序就不是使用新的了啊?
A:是的,由于老代码的设计上的限制,我们需要改动API。应用程序必须被重写,才能使用新的代码。没有办法避开这个问题。所有的新的包和老代码可以同时使用在相同的环境中,如Servlet引擎。


下面跟随我一起来了解org.apache.http。

消息

首先,我们需要处理消息的表示方式。如果你不知如何表示一个消息,你就不能发送或者接收它,对吧?因此,我们有一些用于创建HTTP消息的接口。RequestLine用于请求,StatusLine用于响应,它们都包含ProtocolVersion。ProtocolVersion是非常基础的,所以将其定义为类,而不是接口,当然,我们有HttpVersion继承自它。Header包含名字和值,值可以包含多个HeaderElement。最后还有一个表示消息体的HttpEntity。


Q:HttpVersion继承自ProtocolVersion?HttpCore中的协议不都是HTTP吗?

A:当前不是了。至少有一个不一样的协议,SIP(会话发起协议),它与HTTP有着相似的消息格式,只有协议的名字和版本是不同的。正是因为它们如此相似,我们试图保留它。

Q:明白了。


除了上述创建消息的接口,还要收集消息。每一个HttpMessage都有头,可以按照需求进行添加或者删除。HttpRequest添加请求行,HttpEntityEnclosingRequest是一个实体(后面都称为Entity)。HttpResponse添加状态行和一个Entity。为了方便集成到框架的工厂模式之中,有一些用于创建请求和响应的工厂接口。


Q:没有不带Entity的响应吗?

A:是的。你不需要给一个响应提供一个Entity,将它设置为null就好。对于请求,我们事先就知道是否需要一个Entity。对于GET请求,是不需要Entity的,这就是为什么提供了两个不同接口的原因。响应多数情况下是有Entity的,只有的极特别的情况下才是空。但是事先来处理一个响应是否有Entity使得API处理起来很麻烦,所以我们使用了相同的接口,都包含Entity。

Q:你说可以向HttpMessage添加或者从它删除头部,如果我想要一个只读的消息怎么办呢?

A:我们放弃了对消息是否可以修改进行区分的做法。因为那样将添加大量的接口,而且传播对类型的检查和向下转型。HttpCore面向的是知道她们在做什么的人群。如果你需要消息保持不变,简单的做法就是你不要去修改它

。如果你执意在阻止修改的话,你可以自己实现这个一个接口,当修改的方法被调用时抛出异常。


接下来看看这个包里还有些什么东西,你会发现最重要的东西,这就是org.apache.http.message包。注意,所有的消息表示接口都有一些基础的实现类,但当你使用HttpCore写应用程序的时候不需要接触这些实现类。


Q:我不能看到GetRequest和PostRequest等的实现吗?

A:是的,在核心中并不包含这些类。核心就是当之无愧的核心。如果你需要一个GET请求,你只需要创建一个基本的请求,然后将GET作为HTTP方法的名字,对于POST和PUT也同样如此,除非你想用它们创建一个基础的实现封装请求。在客户端你可以找到一些默认的HTTP方法便利的类,对于核心来说,它们是多余的。

Q:为什么可以使用GET方法创建实体封装请求,GET请求不没有Entity吗,这一点我不太明白?

A:哈哈,你可以用核心类来做一些愚蠢的事情。核心就是核心,这意味着使用它的人应该清楚的知道他们在做什么。也许你想创建一个带实体的GET方法,用于测试服务器是如何应对非法请求的?

Q:这倒是挺有意思的,我从来没有想过这样做。


对于基础的实现还有其他的问题吗?好的,我们继续下一个问题。我们来看一看org.apache.http.entity。你可以在这里面看到一系列的消息实体。除了它们不再绑定为客户端,消息实体与老代码中实现基本没有什么差别。在老代码中,可以从字符串、字节数组、文件、输入流获取实体的内容。在连接中收到的消息,对应于BasicHttpEntity。在下面会看到连接的内容。我们还做一些工作,用于封装、缓存实体。使用EntityTemplate可以简化对一个实体的实现。


Q:在老代码中实体有多个组成部分吗?

A:根本上来讲,是这样的。但它们并不在核心当中。即使对于老代码来讲,它也被认为已经越出范围。它是在httpmime中的模块,而我们直接使用的是mime4j,它同样添加了额外的依赖。HttpCore根本没有外部的依赖。


对于实体还有问题吗?没有我们就开始介绍连接。

连接

从客户端发消息给服务端,或者从服务端返回消息给客户端都需要连接。在接口层,提供了HttpConnection,允许检查连接是否打开,关闭连接,断开连接,获取统计数据。实际用于发送或者接收数据使用的是HttpClientConnection或者HttpServerConnection,具体使用哪一个取决于你要做什么。显然,客户端连接允许你发送请求并接收响应,服务端连接允许你接收请求并发送响应。消息的发送或者获取就是通过我们上面介绍的连接进行的。发送消息头部和消息的实体需要两次调用。这样可以显式处理expect-continue的握手。

Q:我没有看到打开连接的方法啊?

A:你有鹰一样的眼睛,不是吗?你是对的,在接口中,没有打开一个连接的方法。在我们决定这一点上,花了大量的时间来讨论这个问题,打开一个连接比想象的更加复杂,所以我们没有将它们放在核心当中。

Q:如果我不能打开一个连接,那我该怎么使用连接呢?

A:API与实现不是一回事。在API中并没有类似open的方法,但你的实现中可以提供这样的方法。我们提供的默认实现,提供一个打开的套接口,你可以在任何时候创建它。

Q:套接口?我在接口当中也没有看到啊。是否可以设置TCP/IP的一些设置呢?

A:当前,了解套接口是十分有用的。但是,套接口并不属于核心API。有人可能会使用API而不是Java套接口与本地的库进行通信。我们会在下面介绍正确的实现方式。

Q:这是否意味着需要将实现类向下转型以获取IP和端口呢?

A:不,没有那么糟。有一个HttpInetConnection接口,提供了对本地及远程的IP和端口的访问,这只是一个可选的接口,但它是被默认实现支持的。你只需要向下转型为接口,而不是实现类。




在你问更多的问题之前,我们需要介绍点其他的东西,org.apache.http.impl,所有的连接的实现类都在这。不要迷惑,这样做只是为了代码的可维护性,你需要了解的只有两个类,DefaultHttpClientConnection和DefaultHttpServerConnection。提供了bind方法,传递一个打开的套接口,就打开了一个连接。由于继承自基类,也有getSocket方法。
https://wiki.apache.org/HttpComponents/GuidedTourOfHttpCore
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐