加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Java > 正文

简约之美Jodd-http--深入源码理解http协议

发布时间:2020-12-14 06:20:42 所属栏目:Java 来源:网络整理
导读:Jodd 是一个开源的 Java 工具集, 包含一些实用的工具类和小型框架。简单,却很强大! jodd-http是一个轻巧的HTTP客户端。现在我们以一个简单的示例从源码层看看是如何实现的? HttpRequest httpRequest = HttpRequest.get("http://jodd.org" = System.out.p

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

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

 HttpRequest httpRequest = HttpRequest.get("http://jodd.org"=System.out.println(response);//3.打印响应信息</span></pre>

构建一个get请求

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

wKioL1MpX-qwK1-PAAExXPRpR8M814.jpg

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

wKiom1MphduAsu6XAAM_loPLbc0713.jpg

调用get方法构建http请求:

"GET"

method方法如下:

.method =

set方法如下:

= </span><span style="color: #ff0000;"&gt;// http method</span> <span style="color: #0000ff;"&gt;int</span> ndx = destination.indexOf(' '<span style="color: #000000;"&gt;); </span><span style="color: #0000ff;"&gt;if</span> (ndx != -1<span style="color: #000000;"&gt;) { method </span>= destination.substring(0<span style="color: #000000;"&gt;,ndx).toUpperCase(); destination </span>= destination.substring(ndx + 1<span style="color: #000000;"&gt;); } </span><span style="color: #ff0000;"&gt;// protocol</span>

<span style="color: #000000;">
ndx = destination.indexOf("://"<span style="color: #000000;">);

    </span><span style="color: #0000ff;"&gt;if</span> (ndx != -1<span style="color: #000000;"&gt;) {
        protocol </span>= destination.substring(0<span style="color: #000000;"&gt;,ndx);
        destination </span>= destination.substring(ndx + 3<span style="color: #000000;"&gt;);
    }

    </span><span style="color: #ff0000;"&gt;// host</span>

<span style="color: #000000;">
ndx = destination.indexOf('/'<span style="color: #000000;">);

    </span><span style="color: #0000ff;"&gt;if</span> (ndx == -1<span style="color: #000000;"&gt;) {
        ndx </span>=<span style="color: #000000;"&gt; destination.length();
    }

    </span><span style="color: #0000ff;"&gt;if</span> (ndx != 0<span style="color: #000000;"&gt;) {

        host </span>= destination.substring(0<span style="color: #000000;"&gt;,ndx);
        destination </span>=<span style="color: #000000;"&gt; destination.substring(ndx);

        </span><span style="color: #ff0000;"&gt;// port</span>

<span style="color: #000000;">
ndx = host.indexOf(':'<span style="color: #000000;">);

        </span><span style="color: #0000ff;"&gt;if</span> (ndx == -1<span style="color: #000000;"&gt;) {
            port </span>=<span style="color: #000000;"&gt; DEFAULT_PORT;
        } </span><span style="color: #0000ff;"&gt;else</span><span style="color: #000000;"&gt; {
            port </span>= Integer.parseInt(host.substring(ndx + 1<span style="color: #000000;"&gt;));
            host </span>= host.substring(0<span style="color: #000000;"&gt;,ndx);
        }
    }

    </span><span style="color: #ff0000;"&gt;// path + query</span>

<span style="color: #000000;">
path(destination);

    </span><span style="color: #0000ff;"&gt;return</span> <span style="color: #0000ff;"&gt;this</span><span style="color: #000000;"&gt;;
}</span></pre>

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

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

2. 协议:http或者https

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

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

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

    <span style="color: #0000ff;"&gt;if</span> (path.startsWith(StringPool.SLASH) == <span style="color: #0000ff;"&gt;false</span><span style="color: #000000;"&gt;) {
        path </span>= StringPool.SLASH +<span style="color: #000000;"&gt; path;
    }

    </span><span style="color: #0000ff;"&gt;int</span> ndx = path.indexOf('?'<span style="color: #000000;"&gt;);

    </span><span style="color: #0000ff;"&gt;if</span> (ndx != -1<span style="color: #000000;"&gt;) {
        String queryString </span>= path.substring(ndx + 1<span style="color: #000000;"&gt;);

        path </span>= path.substring(0<span style="color: #000000;"&gt;,ndx);

        query </span>= HttpUtil.parseQuery(queryString,<span style="color: #0000ff;"&gt;true</span><span style="color: #000000;"&gt;);
    } </span><span style="color: #0000ff;"&gt;else</span><span style="color: #000000;"&gt; {
        query </span>=<span style="color: #000000;"&gt; HttpValuesMap.ofObjects();
    }

    </span><span style="color: #0000ff;"&gt;this</span>.path =<span style="color: #000000;"&gt; path;

    </span><span style="color: #0000ff;"&gt;return</span> <span style="color: #0000ff;"&gt;this</span><span style="color: #000000;"&gt;;
}</span></pre>

发送请求

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

wKiom1MpmHWALc2UAADu14JLceA655.jpg

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

wKiom1MprnXiYF18AALhmNtc3OE334.jpg

(httpConnection == }
    </span><span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; prepare http connection</span>

    <span style="color: #0000ff;"&gt;if</span> (timeout != -1<span style="color: #000000;"&gt;) {
        httpConnection.setTimeout(timeout);
    }

    </span><span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; sends data</span>

<span style="color: #000000;"> HttpResponse httpResponse;
<span style="color: #0000ff;">try<span style="color: #000000;"> {
OutputStream outputStream =<span style="color: #000000;"> httpConnection.getOutputStream();

        sendTo(outputStream);

        InputStream inputStream </span>=<span style="color: #000000;"&gt; httpConnection.getInputStream();

        httpResponse </span>=<span style="color: #000000;"&gt; HttpResponse.readFrom(inputStream);

        httpResponse.assignHttpRequest(</span><span style="color: #0000ff;"&gt;this</span><span style="color: #000000;"&gt;);
    } </span><span style="color: #0000ff;"&gt;catch</span><span style="color: #000000;"&gt; (IOException ioex) {
        </span><span style="color: #0000ff;"&gt;throw</span> <span style="color: #0000ff;"&gt;new</span><span style="color: #000000;"&gt; HttpException(ioex);
    }

    </span><span style="color: #0000ff;"&gt;boolean</span> keepAlive =<span style="color: #000000;"&gt; httpResponse.isConnectionPersistent();

    </span><span style="color: #0000ff;"&gt;if</span> (keepAlive == <span style="color: #0000ff;"&gt;false</span><span style="color: #000000;"&gt;) {
        </span><span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; closes connection if keep alive is false,or if counter reached 0</span>

<span style="color: #000000;"> httpConnection.close();
httpConnection = <span style="color: #0000ff;">null<span style="color: #000000;">;
}

    </span><span style="color: #0000ff;"&gt;return</span><span style="color: #000000;"&gt; httpResponse;
}</span></pre>

1. 打开HttpConnection

</span><span style="color: #008000;"&gt;/**</span><span style="color: #008000;"&gt; * Opens a new {</span><span style="color: #808080;"&gt;@link</span><span style="color: #008000;"&gt; jodd.http.HttpConnection connection} * using given {</span><span style="color: #808080;"&gt;@link</span><span style="color: #008000;"&gt; jodd.http.HttpConnectionProvider}. </span><span style="color: #008000;"&gt;*/</span> <span style="color: #0000ff;"&gt;public</span><span style="color: #000000;"&gt; HttpRequest open(HttpConnectionProvider httpConnectionProvider) { </span><span style="color: #0000ff;"&gt;if</span> (<span style="color: #0000ff;"&gt;this</span>.httpConnection != <span style="color: #0000ff;"&gt;null</span><span style="color: #000000;"&gt;) { </span><span style="color: #0000ff;"&gt;throw</span> <span style="color: #0000ff;"&gt;new</span> HttpException("Connection already opened"<span style="color: #000000;"&gt;); } </span><span style="color: #0000ff;"&gt;try</span><span style="color: #000000;"&gt; { </span><span style="color: #0000ff;"&gt;this</span>.httpConnectionProvider =<span style="color: #000000;"&gt; httpConnectionProvider; </span><span style="color: #ff0000;"&gt;this.httpConnection = httpConnectionProvider.createHttpConnection(this</span><span style="color: #000000;"&gt;<span style="color: #ff0000;"&gt;);</span> } </span><span style="color: #0000ff;"&gt;catch</span><span style="color: #000000;"&gt; (IOException ioex) { </span><span style="color: #0000ff;"&gt;throw</span> <span style="color: #0000ff;"&gt;new</span><span style="color: #000000;"&gt; HttpException(ioex); } </span><span style="color: #0000ff;"&gt;return</span> <span style="color: #0000ff;"&gt;this</span><span style="color: #000000;"&gt;; }</span></pre>

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

2. 创建连接实现

HttpConnection createHttpConnection(HttpRequest httpRequest) </span><span style="color: #0000ff;"&gt;if</span> (httpRequest.protocol().equalsIgnoreCase("https"<span style="color: #000000;"&gt;)) { SSLSocket sslSocket </span>=<span style="color: #000000;"&gt; createSSLSocket(httpRequest.host(),httpRequest.port()); sslSocket.startHandshake(); socket </span>=<span style="color: #000000;"&gt; sslSocket; } </span><span style="color: #0000ff;"&gt;else</span><span style="color: #000000;"&gt; { socket </span>=<span style="color: #000000;"&gt; createSocket(httpRequest.host(),httpRequest.port()); } </span><span style="color: #0000ff;"&gt;return</span> <span style="color: #0000ff;"&gt;new</span><span style="color: #000000;"&gt; SocketHttpConnection(socket); }</span></pre>

3. 创建socket

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

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

SocketHttpConnection </span><span style="color: #0000ff;"&gt;protected</span> <span style="color: #0000ff;"&gt;final</span><span style="color: #000000;"&gt; Socket socket; </span><span style="color: #0000ff;"&gt;public</span><span style="color: #000000;"&gt; SocketHttpConnection(Socket socket) { </span><span style="color: #0000ff;"&gt;this</span>.socket =<span style="color: #000000;"&gt; socket; } </span><span style="color: #0000ff;"&gt;public</span> OutputStream getOutputStream() <span style="color: #0000ff;"&gt;throws</span><span style="color: #000000;"&gt; IOException { </span><span style="color: #0000ff;"&gt;return</span><span style="color: #000000;"&gt; socket.getOutputStream(); } </span><span style="color: #0000ff;"&gt;public</span> InputStream getInputStream() <span style="color: #0000ff;"&gt;throws</span><span style="color: #000000;"&gt; IOException { </span><span style="color: #0000ff;"&gt;return</span><span style="color: #000000;"&gt; socket.getInputStream(); } </span><span style="color: #0000ff;"&gt;public</span> <span style="color: #0000ff;"&gt;void</span><span style="color: #000000;"&gt; close() { </span><span style="color: #0000ff;"&gt;try</span><span style="color: #000000;"&gt; { socket.close(); } </span><span style="color: #0000ff;"&gt;catch</span><span style="color: #000000;"&gt; (IOException ignore) { } } </span><span style="color: #0000ff;"&gt;public</span> <span style="color: #0000ff;"&gt;void</span> setTimeout(<span style="color: #0000ff;"&gt;int</span><span style="color: #000000;"&gt; milliseconds) { </span><span style="color: #0000ff;"&gt;try</span><span style="color: #000000;"&gt; { socket.setSoTimeout(milliseconds); } </span><span style="color: #0000ff;"&gt;catch</span><span style="color: #000000;"&gt; (SocketException sex) { </span><span style="color: #0000ff;"&gt;throw</span> <span style="color: #0000ff;"&gt;new</span><span style="color: #000000;"&gt; HttpException(sex); } } </span><span style="color: #008000;"&gt;/**</span><span style="color: #008000;"&gt; * Returns <code>Socket</code> used by this connection. </span><span style="color: #008000;"&gt;*/</span> <span style="color: #0000ff;"&gt;public</span><span style="color: #000000;"&gt; Socket getSocket() { </span><span style="color: #0000ff;"&gt;return</span><span style="color: #000000;"&gt; socket; }

}

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

OutputStream outputStream = <span style="color: #ff0000;"&gt;sendTo(outputStream);</span> InputStream inputStream </span>= httpConnection.getInputStream();</pre>

发送过程:

</span><span style="color: #008000;"&gt;/**</span><span style="color: #008000;"&gt; * Sends request or response to output stream. </span><span style="color: #008000;"&gt;*/</span> <span style="color: #0000ff;"&gt;public</span> <span style="color: #0000ff;"&gt;void</span> sendTo(OutputStream out) <span style="color: #0000ff;"&gt;throws</span><span style="color: #000000;"&gt; IOException { Buffer buffer </span>= buffer(<span style="color: #0000ff;"&gt;true</span><span style="color: #000000;"&gt;); </span><span style="color: #0000ff;"&gt;if</span> (httpProgressListener == <span style="color: #0000ff;"&gt;null</span><span style="color: #000000;"&gt;) { buffer.writeTo(out); } </span><span style="color: #0000ff;"&gt;else</span><span style="color: #000000;"&gt; { buffer.writeTo(out,httpProgressListener); } out.flush(); }</span></pre>

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

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

} = HttpResponse httpResponse </span>= <span style="color: #0000ff;"&gt;new</span><span style="color: #000000;"&gt; HttpResponse(); </span><span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; the first line</span>

<span style="color: #000000;"> String line;
<span style="color: #0000ff;">try<span style="color: #000000;"> {
line =<span style="color: #000000;"> reader.readLine();
} <span style="color: #0000ff;">catch<span style="color: #000000;"> (IOException ioex) {
<span style="color: #0000ff;">throw <span style="color: #0000ff;">new<span style="color: #000000;"> HttpException(ioex);
}

    </span><span style="color: #0000ff;"&gt;if</span> (line != <span style="color: #0000ff;"&gt;null</span><span style="color: #000000;"&gt;) {

        line </span>=<span style="color: #000000;"&gt; line.trim();

        </span><span style="color: #0000ff;"&gt;int</span> ndx = line.indexOf(' '<span style="color: #000000;"&gt;);
        httpResponse.httpVersion(line.substring(</span>0<span style="color: #000000;"&gt;,ndx));

        </span><span style="color: #0000ff;"&gt;int</span> ndx2 = line.indexOf(' ',ndx + 1<span style="color: #000000;"&gt;);
        </span><span style="color: #0000ff;"&gt;if</span> (ndx2 == -1<span style="color: #000000;"&gt;) {
            ndx2 </span>=<span style="color: #000000;"&gt; line.length();
        }
        httpResponse.statusCode(Integer.parseInt(line.substring(ndx,ndx2).trim()));

        httpResponse.statusPhrase(line.substring(ndx2).trim());
    }

    httpResponse.readHeaders(reader);
    httpResponse.readBody(reader);

    </span><span style="color: #0000ff;"&gt;return</span><span style="color: #000000;"&gt; httpResponse;
}</span></pre>

小结

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

参考文献:

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

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

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读