httpcore系列(二)理解httpcore发送请求的类对象

在阅读了httpcore系列(一)初识httpcore后,我们浅显地了解了发送http请求需要构建一个从客户端到服务端的Httprequest对象,接受一个从服务端到客户端的HttpResponse对象,然而我们并不清楚其中的缘由,接下来我将聊聊如何将Httprequest作为参数输入,HttpResponse作为结果返回的来龙去脉。

官方样例

我们看看官方给的同步GET请求样例,代码如下。

package org.apache.http.examples;

import java.net.Socket;

import org.apache.http.ConnectionReuseStrategy;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.impl.DefaultBHttpClientConnection;
import org.apache.http.impl.DefaultConnectionReuseStrategy;
import org.apache.http.message.BasicHttpRequest;
import org.apache.http.protocol.HttpCoreContext;
import org.apache.http.protocol.HttpProcessor;
import org.apache.http.protocol.HttpProcessorBuilder;
import org.apache.http.protocol.HttpRequestExecutor;
import org.apache.http.protocol.RequestConnControl;
import org.apache.http.protocol.RequestContent;
import org.apache.http.protocol.RequestExpectContinue;
import org.apache.http.protocol.RequestTargetHost;
import org.apache.http.protocol.RequestUserAgent;
import org.apache.http.util.EntityUtils;

/**
 * Elemental example for executing multiple GET requests sequentially.
 */
public class ElementalHttpGet {

    public static void main(String[] args) throws Exception {
        HttpProcessor httpproc = HttpProcessorBuilder.create()
            .add(new RequestContent())
            .add(new RequestTargetHost())
            .add(new RequestConnControl())
            .add(new RequestUserAgent("Test/1.1"))
            .add(new RequestExpectContinue(true)).build();

        HttpRequestExecutor httpexecutor = new HttpRequestExecutor();

        HttpCoreContext coreContext = HttpCoreContext.create();
        HttpHost host = new HttpHost("localhost", 8080);
        coreContext.setTargetHost(host);

        DefaultBHttpClientConnection conn = new DefaultBHttpClientConnection(8 * 1024);
        ConnectionReuseStrategy connStrategy = DefaultConnectionReuseStrategy.INSTANCE;

        try {

            String[] targets = {
                    "/",
                    "/servlets-examples/servlet/RequestInfoExample",
                    "/somewhere%20in%20pampa"};

            for (int i = 0; i < targets.length; i++) {
                if (!conn.isOpen()) {
                    Socket socket = new Socket(host.getHostName(), host.getPort());
                    conn.bind(socket);
                }
                BasicHttpRequest request = new BasicHttpRequest("GET", targets[i]);
                System.out.println(">> Request URI: " + request.getRequestLine().getUri());

                httpexecutor.preProcess(request, httpproc, coreContext);
                HttpResponse response = httpexecutor.execute(request, conn, coreContext);
                httpexecutor.postProcess(response, httpproc, coreContext);

                System.out.println("<< Response: " + response.getStatusLine());
                System.out.println(EntityUtils.toString(response.getEntity()));
                System.out.println("==============");
                if (!connStrategy.keepAlive(response, coreContext)) {
                    conn.close();
                } else {
                    System.out.println("Connection kept alive...");
                }
            }
        } finally {
            conn.close();
        }
    }
}

你肯定敏锐地发现了这么一行代码!
HttpResponse response = httpexecutor.execute(request, conn, coreContext);,返回值就是我们需要的HttpResponse对象,所以我们只要清楚函数的调用者httpexecutor:HttpRequestExecutor对象,参数conn:DefaultBHttpClientConnection对象,coreContext:HttpCoreContext,就会明白httpcore如何发送的GET请求。

聊聊样例中那些陌生的对象

HttpProcessor

接口HttpProcessor的定义public interface HttpProcessor extends HttpRequestInterceptor, HttpResponseInterceptor,可知其继承自HttpRequestInterceptor,HttpResponseInterceptor,所以先讲讲这两类接口。

  • HttpRequestInterceptor 请求拦截器
    实现类主要是为请求添加请求头。
    在这里插入图片描述
    观察其任意实现类,在process方法里大致实现都是判断是否是属于HttpEntityEnclosingRequest实例,是否含有某一请求头,然后再进行某类请求头的设置。
    如例:RequestDate
 @Override
    public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
      Args.notNull(request, "HTTP request");
      if ((request instanceof HttpEntityEnclosingRequest) &&
            !request.containsHeader(HTTP.DATE_HEADER)) {
            final String httpdate = DATE_GENERATOR.getCurrentDate();
            request.setHeader(HTTP.DATE_HEADER, httpdate);
        }
    }    
  • HttpResponseInterceptor 响应拦截器
    实现类主要是为响应添加响应头。
    在这里插入图片描述
    观察其任意实现类,大体与上述所说的请求拦截器的作用一致,唯一不同的点是其作用于响应,为响应添加响应头。
    如例:ResponseDate
public void process(final HttpResponse response, final HttpContext context) throws HttpException, IOException {
        Args.notNull(response, "HTTP response");
        final int status = response.getStatusLine().getStatusCode();
        if ((status >= HttpStatus.SC_OK) &&
            !response.containsHeader(HTTP.DATE_HEADER)) {
            final String httpdate = DATE_GENERATOR.getCurrentDate();
            response.setHeader(HTTP.DATE_HEADER, httpdate);
        }
    }
  • HttpProcessor
    我们来看看HttpProcessor的实现类ImmutableHttpProcessor,可知其是一组请求拦截器、一组响应拦截器的管理类,在process方法里遍历每一类拦截器,通过调用各类拦截器各自的process实现,给请求添加请求头,给响应添加响应头。
private final HttpRequestInterceptor[] requestInterceptors;
private final HttpResponseInterceptor[] responseInterceptors;

@Override
public void process(final HttpRequest request, final HttpContext context) throws IOException, HttpException {
        for (final HttpRequestInterceptor requestInterceptor : this.requestInterceptors) {
            requestInterceptor.process(request, context);
        }
    }

@Override
public void process(final HttpResponse response, final HttpContext context) throws IOException, HttpException {
        for (final HttpResponseInterceptor responseInterceptor : this.responseInterceptors) {
            responseInterceptor.process(response, context);
        }
    }

样例中的这部分代码,是不是应该明白了呢?通过构造者模式为请求准备了一组请求拦截器,用于设置各种请求头。

 HttpProcessor httpproc = HttpProcessorBuilder.create()
            .add(new RequestContent())
            .add(new RequestTargetHost())
            .add(new RequestConnControl())
            .add(new RequestUserAgent("Test/1.1"))
            .add(new RequestExpectContinue(true)).build();

HttpCoreContext

我们经常会看见XXXContext对象,Context表示环境,上下文的意思。那么HttpCoreContext 我们可以理解为http的核心上下文,描述着http相关的核心信息。

HttpCoreContext的部分代码如下:

public static HttpCoreContext create() {
       return new HttpCoreContext(new BasicHttpContext());
    }

public void setAttribute(final String id, final Object obj) {
        context.setAttribute(id, obj);
    }

public Object getAttribute(final String id) {
        return context.getAttribute(id);
    }

样例代码中有一小部分代码如下:

HttpCoreContext coreContext = HttpCoreContext.create();
HttpHost host = new HttpHost("localhost", 8080);
coreContext.setTargetHost(host);

我们发现HttpCoreContext.create()的调用,其本质创建BasicHttpContext对象,它是HttpContext的基本实现类,通过调用HttpCoreContext 的setAttribute方法将相关的属性添加至context:BasicHttpContext对象中,而在BasicHttpContext中,通过private final Map<String, Object> map保存这些属性。

DefaultBHttpClientConnection

类图如下:
在这里插入图片描述
通过类图可知:DefaultBHttpClientConnection继承了BHttpConnectionBase类,实现了HttpClientConnection接口。

  • HttpConnection : A generic HTTP connection, useful on client and server side,描述了关于http连接的通用信息。

  • HttpInetConnection:An HTTP connection over the Internet Protocol (IP).描述了连接在传输层相关的信息。

  • HttpClientConnection: A client-side HTTP connection, which can be used for sending requests and receiving responses. 客户端的http连接,用于发送请求,接受响应。

所以我们可以把DefaultBHttpClientConnection理解为客户端与服务端的桥梁,用于请求的发送、响应接受。在创建连接对象时,我们需要指定连接的地址,也就是样例中创建的Socket对象,指定地址和端口,然后将其绑定至连接。

如何理解用于发送请求,接受响应?
见DefaultBHttpClientConnection部分源码如下:

@Override
    public void sendRequestHeader(final HttpRequest request)
            throws HttpException, IOException {
        Args.notNull(request, "HTTP request");
        ensureOpen();
        this.requestWriter.write(request);
        onRequestSubmitted(request);
        incrementRequestCount();
    }

    @Override
    public void sendRequestEntity(final HttpEntityEnclosingRequest request)
            throws HttpException, IOException {
        Args.notNull(request, "HTTP request");
        ensureOpen();
        final HttpEntity entity = request.getEntity();
        if (entity == null) {
            return;
        }
        final OutputStream outStream = prepareOutput(request);
        entity.writeTo(outStream);
        outStream.close();
    }

    @Override
    public HttpResponse receiveResponseHeader() throws HttpException, IOException {
        ensureOpen();
        final HttpResponse response = this.responseParser.parse();
        onResponseReceived(response);
        if (response.getStatusLine().getStatusCode() >= HttpStatus.SC_OK) {
            incrementResponseCount();
        }
        return response;
    }

    @Override
    public void receiveResponseEntity(
            final HttpResponse response) throws HttpException, IOException {
        Args.notNull(response, "HTTP response");
        ensureOpen();
        final HttpEntity entity = prepareInput(response);
        response.setEntity(entity);
    }

RequestExecutor

HttpRequestExecutor,我们只看其名,就可以感受到它是HttpRuquest的执行器,究竟官方怎么定义的呢?

HttpRequestExecutor is a client side HTTP protocol handler based on the blocking (classic) I/O model. HttpRequestExecutor relies on HttpProcessor to generate mandatory protocol headers for all outgoing messages and apply common, cross-cutting message transformations to all incoming and outgoing messages. Application specific processing can be implemented outside HttpRequestExecutor once the request has been executed and a response has been received.

大致讲了这么几点

  • HttpRequestExecutor是一个基于经典阻塞 I/O模型的客户端HTTP协议处理对象。

  • 它依赖于HttpProcessor,对所有传出的消息(也就是发送的请求)添加协议头(也就是我们所说的请求头);对于所有传出/传入的消息都将应用通用的、跨领域的消息转换。

在这里插入图片描述
通过对外暴露的函数

  • public void preProcess( HttpRequest , HttpProcessor , HttpContext)
    对请求的预处理,实质是调用HttpProcessor的相关操作,给请求添加请求头。

  • public void postProcess( HttpRequest , HttpProcessor , HttpContext)

  • 对响应后处理,实质是调用HttpProcessor的相关操作,给响应添加响应头。

  • public void execute(HttpRequest , HttpProcessor , HttpContext)
    执行请求的发送
    源码如下:

public HttpResponse execute(
            final HttpRequest request,
            final HttpClientConnection conn,
            final HttpContext context) throws IOException, HttpException {
        Args.notNull(request, "HTTP request");
        Args.notNull(conn, "Client connection");
        Args.notNull(context, "HTTP context");
        try {
            HttpResponse response = doSendRequest(request, conn, context);
            if (response == null) {
                response = doReceiveResponse(request, conn, context);
            }
            return response;
        } catch (final IOException ex) {
            closeConnection(conn);
            throw ex;
        } catch (final HttpException ex) {
            closeConnection(conn);
            throw ex;
        } catch (final RuntimeException ex) {
            closeConnection(conn);
            throw ex;
        }
    }

本质调用其内部的doSendRequest(request, conn, context)或doReceiveResponse(request, conn, context)方法; 查看doSendRequest或doReceiveResponse的源码,其调用DefaultBHttpClientConnection的send系列与receive系列方法,可见请求最终的发送以及响应最终的接收是DefaultBHttpClientConnection对象。

总结

通过对关键对象的梳理,我相信我们对发送请求过程中各对象的作用也有了初步地理解,这也便于我们今后开发时对各对象灵活的运用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值