简约之美Jodd-http--深入源码理解http协议
Jodd 是一個開源的 Java 工具集, 包含一些實用的工具類和小型框架。簡單,卻很強(qiáng)大!
jodd-http是一個輕巧的HTTP客戶端。現(xiàn)在我們以一個簡單的示例從源碼層看看是如何實現(xiàn)的?
HttpRequest httpRequest = HttpRequest.get("http://jodd.org"); //1. 構(gòu)建一個get請求HttpResponse response = httpRequest.send(); //2.發(fā)送請求并接受響應(yīng)信息System.out.println(response);//3.打印響應(yīng)信息構(gòu)建一個get請求
先復(fù)習(xí)一下http請求報文的格式:
下圖展示一般請求所帶有的屬性
調(diào)用get方法構(gòu)建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 methodint 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;}上述方法,根據(jù)destination解析出一下幾個部分:
1. 方法:HTTP1.1支持7種請求方法:GET、POST、HEAD、OPTIONS、PUT、DELETE和TARCE。
2. 協(xié)議:http或者h(yuǎn)ttps
3. 主機(jī):請求的服務(wù)器地址
4. 端口:請求的服務(wù)器端口
5. 路徑+查詢參數(shù),其中參數(shù)以“?”開頭,使用“&”連接
/*** 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 pathif (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;}發(fā)送請求
先熟悉一下http響應(yīng)報文的格式:
響應(yīng)首部一般包含如下內(nèi)容:
/*** {@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 connectionif (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;}判斷是否有連接,若沒有連接則創(chuàng)建一個新的連接。
2. 創(chuàng)建連接實現(xiàn)
/*** 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. 創(chuàng)建socket
根據(jù)協(xié)議的不同,http使用SocketFactory創(chuàng)建socket,https使用SSLSocketFactory創(chuàng)建SSLSocket。最終使用SocketHttpConnection進(jìn)行包裝。
SocketHttpConnection繼承自HttpConnection,實現(xiàn)了socket的輸入輸出流連接。注意:https創(chuàng)建完SSLSocket時需要進(jìn)行握手。
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 <code>Socket</code> used by this connection.*/public Socket getSocket() {return socket;} }?打開Connection的輸出流發(fā)送信息,打開connection的輸入流接受返回信息。
OutputStream outputStream = httpConnection.getOutputStream();sendTo(outputStream);InputStream inputStream = httpConnection.getInputStream();發(fā)送過程:
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();}將緩沖區(qū)的數(shù)據(jù)寫入輸出流,并發(fā)送。
接受數(shù)據(jù)并讀取報文內(nèi)容:
/*** 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;}小結(jié)
? 從上面的代碼,我們可以看出http使用socket來建立和destination的連接,然后通過連接的輸出流和輸入流來進(jìn)行通信。
參考文獻(xiàn):
【1】http://www.it165.net/admin/html/201403/2541.html
【2】http://jodd.org/doc/http.html
轉(zhuǎn)載于:https://www.cnblogs.com/davidwang456/p/4569283.html
總結(jié)
以上是生活随笔為你收集整理的简约之美Jodd-http--深入源码理解http协议的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: spring security源码分析之
- 下一篇: 简约之美Jodd-http--应用一箩筐