博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
简约之美Jodd-http--深入源码理解http协议
阅读量:6935 次
发布时间:2019-06-27

本文共 8716 字,大约阅读时间需要 29 分钟。

Jodd 是一个开源的 Java 工具集, 包含一些实用的工具类和小型框架。简单,却很强大!

jodd-http是一个轻巧的HTTP客户端。现在我们以一个简单的示例从源码层看看是如何实现的?

 HttpRequest httpRequest = HttpRequest.get("http://jodd.org"); //1. 构建一个get请求    HttpResponse response = httpRequest.send(); //2.发送请求并接受响应信息    System.out.println(response);//3.打印响应信息

构建一个get请求

先复习一下http请求报文的格式:

wKioL1MpX-qwK1-PAAExXPRpR8M814.jpg

下图展示一般请求所带有的属性

wKiom1MphduAsu6XAAM_loPLbc0713.jpg

调用get方法构建http请求:

/**     * Builds a GET request.     */    public static HttpRequest get(String destination) {        return new HttpRequest()                .method("GET")                .set(destination);    }

method方法如下:

/**     * Specifies request method. It will be converted into uppercase.     */    public HttpRequest method(String method) {        this.method = method.toUpperCase();        return this;    }

set方法如下:

/**     * Sets the destination (method, host, port... ) at once.     */    public HttpRequest set(String destination) {        destination = destination.trim();        // http method        int ndx = destination.indexOf(' ');        if (ndx != -1) {            method = destination.substring(0, ndx).toUpperCase();            destination = destination.substring(ndx + 1);        }        // protocol        ndx = destination.indexOf("://");        if (ndx != -1) {            protocol = destination.substring(0, ndx);            destination = destination.substring(ndx + 3);        }        // host        ndx = destination.indexOf('/');        if (ndx == -1) {            ndx = destination.length();        }        if (ndx != 0) {            host = destination.substring(0, ndx);            destination = destination.substring(ndx);            // port            ndx = host.indexOf(':');            if (ndx == -1) {                port = DEFAULT_PORT;            } else {                port = Integer.parseInt(host.substring(ndx + 1));                host = host.substring(0, ndx);            }        }        // path + query        path(destination);        return this;    }

上述方法,根据destination解析出一下几个部分:

1. 方法:HTTP1.1支持7种请求方法:GET、POST、HEAD、OPTIONS、PUT、DELETE和TARCE。

2. 协议:http或者https

3. 主机:请求的服务器地址

4. 端口:请求的服务器端口

5. 路径+查询参数,其中参数以“?”开头,使用“&”连接

/**     * Sets request path. Query string is allowed.     * Adds a slash if path doesn't start with one.     * Query will be stripped out from the path.     * Previous query is discarded.     * @see #query()     */    public HttpRequest path(String path) {        // this must be the only place that sets the path        if (path.startsWith(StringPool.SLASH) == false) {            path = StringPool.SLASH + path;        }        int ndx = path.indexOf('?');        if (ndx != -1) {            String queryString = path.substring(ndx + 1);            path = path.substring(0, ndx);            query = HttpUtil.parseQuery(queryString, true);        } else {            query = HttpValuesMap.ofObjects();        }        this.path = path;        return this;    }

发送请求

先熟悉一下http响应报文的格式:

wKiom1MpmHWALc2UAADu14JLceA655.jpg

响应首部一般包含如下内容:

wKiom1MprnXiYF18AALhmNtc3OE334.jpg

/**     * {
@link #open() Opens connection} if not already open, sends request, * reads response and closes the request. If keep-alive mode is enabled * connection will not be closed. */ public HttpResponse send() { if (httpConnection == null) { open(); } // prepare http connection if (timeout != -1) { httpConnection.setTimeout(timeout); } // sends data HttpResponse httpResponse; try { OutputStream outputStream = httpConnection.getOutputStream(); sendTo(outputStream); InputStream inputStream = httpConnection.getInputStream(); httpResponse = HttpResponse.readFrom(inputStream); httpResponse.assignHttpRequest(this); } catch (IOException ioex) { throw new HttpException(ioex); } boolean keepAlive = httpResponse.isConnectionPersistent(); if (keepAlive == false) { // closes connection if keep alive is false, or if counter reached 0 httpConnection.close(); httpConnection = null; } return httpResponse; }

 

1. 打开HttpConnection

/**     * Opens a new {
@link HttpConnection connection} using * {
@link JoddHttp#httpConnectionProvider default connection provider}. */ public HttpRequest open() { return open(JoddHttp.httpConnectionProvider); } /** * Opens a new {
@link jodd.http.HttpConnection connection} * using given {
@link jodd.http.HttpConnectionProvider}. */ public HttpRequest open(HttpConnectionProvider httpConnectionProvider) { if (this.httpConnection != null) { throw new HttpException("Connection already opened"); } try { this.httpConnectionProvider = httpConnectionProvider; this.httpConnection = httpConnectionProvider.createHttpConnection(this); } catch (IOException ioex) { throw new HttpException(ioex); } return this; }

判断是否有连接,若没有连接则创建一个新的连接。

2. 创建连接实现

/**     * Creates new connection from current {
@link jodd.http.HttpRequest request}. * * @see #createSocket(String, int) */ public HttpConnection createHttpConnection(HttpRequest httpRequest) throws IOException { Socket socket; if (httpRequest.protocol().equalsIgnoreCase("https")) { SSLSocket sslSocket = createSSLSocket(httpRequest.host(), httpRequest.port()); sslSocket.startHandshake(); socket = sslSocket; } else { socket = createSocket(httpRequest.host(), httpRequest.port()); } return new SocketHttpConnection(socket); }

3. 创建socket

  根据协议的不同,http使用SocketFactory创建socket,https使用SSLSocketFactory创建SSLSocket。最终使用SocketHttpConnection进行包装。

SocketHttpConnection继承自HttpConnection,实现了socket的输入输出流连接。注意:https创建完SSLSocket时需要进行握手。

public class SocketHttpConnection implements HttpConnection {    protected final Socket socket;    public SocketHttpConnection(Socket socket) {        this.socket = socket;    }    public OutputStream getOutputStream() throws IOException {        return socket.getOutputStream();    }    public InputStream getInputStream() throws IOException {        return socket.getInputStream();    }    public void close() {        try {            socket.close();        } catch (IOException ignore) {        }    }    public void setTimeout(int milliseconds) {        try {            socket.setSoTimeout(milliseconds);        } catch (SocketException sex) {            throw new HttpException(sex);        }    }    /**     * Returns Socket used by this connection.     */    public Socket getSocket() {        return socket;    }}

 打开Connection的输出流发送信息,打开connection的输入流接受返回信息。

OutputStream outputStream = httpConnection.getOutputStream();            sendTo(outputStream);            InputStream inputStream = httpConnection.getInputStream();

发送过程:

protected HttpProgressListener httpProgressListener;    /**     * Sends request or response to output stream.     */    public void sendTo(OutputStream out) throws IOException {        Buffer buffer = buffer(true);        if (httpProgressListener == null) {            buffer.writeTo(out);        }        else {            buffer.writeTo(out, httpProgressListener);        }        out.flush();    }

将缓冲区的数据写入输出流,并发送。

接受数据并读取报文内容:

/**     * Reads response input stream and returns {
@link HttpResponse response}. * Supports both streamed and chunked response. */ public static HttpResponse readFrom(InputStream in) { InputStreamReader inputStreamReader; try { inputStreamReader = new InputStreamReader(in, StringPool.ISO_8859_1); } catch (UnsupportedEncodingException ignore) { return null; } BufferedReader reader = new BufferedReader(inputStreamReader); HttpResponse httpResponse = new HttpResponse(); // the first line String line; try { line = reader.readLine(); } catch (IOException ioex) { throw new HttpException(ioex); } if (line != null) { line = line.trim(); int ndx = line.indexOf(' '); httpResponse.httpVersion(line.substring(0, ndx)); int ndx2 = line.indexOf(' ', ndx + 1); if (ndx2 == -1) { ndx2 = line.length(); } httpResponse.statusCode(Integer.parseInt(line.substring(ndx, ndx2).trim())); httpResponse.statusPhrase(line.substring(ndx2).trim()); } httpResponse.readHeaders(reader); httpResponse.readBody(reader); return httpResponse; }

小结

  从上面的代码,我们可以看出http使用socket来建立和destination的连接,然后通过连接的输出流和输入流来进行通信。

参考文献:

【1】http://www.it165.net/admin/html/201403/2541.html

【2】http://jodd.org/doc/http.html

转载地址:http://thpjl.baihongyu.com/

你可能感兴趣的文章
添加反爬策略1-User-Agent
查看>>
C程序设计导引(3)
查看>>
自己造容器List
查看>>
为什么你不应该自行更新 Drupal 网站?
查看>>
Java并发包--线程池框架
查看>>
CSS学习总结3:CSS定位
查看>>
java_Cookie添加和删除
查看>>
hadoop 1.2.1 安装配置
查看>>
程序员教程和书籍收藏
查看>>
创建帧动画
查看>>
使用Xcode 7 beta免费真机调试iOS应用程序
查看>>
Java并发基础03. 传统线程互斥技术—synchronized
查看>>
数据工厂模式,多种数据库选择 web.config 写法及相关操作
查看>>
ios面试题
查看>>
圣杯布局、双飞翼布局与flex布局实现
查看>>
控制条
查看>>
23种设计模式(一) 单例模式
查看>>
移动自动化相关名词解释
查看>>
testAPI
查看>>
delphi 7里怎么隐藏PageControl控件的tabsheet标签
查看>>