自己動手寫Web容器之TomJetty之三:掀起請求蓋頭來

傳送門 ☞ 輪子的專欄 ☞ 轉載請註明 ☞ http://blog.csdn.net/leverage_1229

       前面我們對於實現TomJetty做了一些知識鋪墊和複習,息知了HTTP請求的頭部的組成元素。目前的TomJetty服務器已經能夠成功啓動,可是請求一旦過來卻又看不懂^_^。所以本文就來解析HTTP請求頭,剖析它的各部分。讓TomJetty服務器能夠明白它的意圖。

1HTTP請求頭解析

(1)編寫一個RequestHeader類,用戶封裝請求頭對象。

package cn.lynn.tomjetty;

import java.util.HashMap;

/**
 * 封裝請求頭
 * @author lynnli1229
 */
public class RequestHeader {

    private String method;
    private String url;
    private String protocal;
    private String accept;
    private String accept_language;
    private String user_agent;
    private String accept_encoding;
    private String ip;
    private String port;
    private String connection;
    private String cookie;
    
    // 存放請求頭鍵值對
    private HashMap<String, String> map;
    // 存放請求頭文本
    private String txt;

    // 省略getter()和setter()方法

    @Override
    public String toString() {
        return "RequestHeader [" + "\n"
            + method + " " + url + " " + protocal + "\n"
            + "Accept: " + accept + "\n"
            + "Accept-Language: " + accept_language + "\n"
            + "User-Agent: " + user_agent + "\n"
            + "Accept-Encoding: " + accept_encoding + "\n"
            + "Host: " + ip + ":" + port + "\n"
            + "Connection: " + connection + "\n"
            + "Cookie: " + cookie + "\n"
            + "]";
    }
}

(2)設計一個IRequestHeaderParser接口,並聲明parse()方法,用於解析請求頭文本內容。

package cn.lynn.tomjetty;

public interface IReqestHeaderParser {

    public RequestHeader parse(String txt) throws Exception;

}

(3)提供IRequestHeaderParser接口的實現類RequestHeaderParserImpl,用於執行解析操作。注意一下,我們是以IE瀏覽器請求頭爲例,其他版本瀏覽器請求頭的參數順序會有變動,但整體差別不大。

package cn.lynn.tomjetty;

import java.util.HashMap;

public class RequestHeaderParserImpl implements IReqestHeaderParser {

    /**
     * 解析HTTP請求頭
     */
    public RequestHeader parse(String txt) throws Exception {
        RequestHeader header = new RequestHeader();
        header.setTxt(txt);

        // 截取請求頭第一行
        String firstLine = txt.substring(0, txt.indexOf("\n"));
        String method = firstLine.substring(0, firstLine.indexOf(" "));
        String url = firstLine.substring(firstLine.indexOf(" ") + 1, firstLine.lastIndexOf(" "));
        String protocal = firstLine.substring(firstLine.lastIndexOf(" ") + 1, firstLine.length());
        
        header.setMethod(method);// 獲取Accept參數值,存放到RequestHeader對象當中
        header.setUrl(url);// 獲取Accept參數值,存放到RequestHeader對象當中
        header.setProtocal(protocal);// 獲取Accept參數值,存放到RequestHeader對象當中
        
        String[] lines = txt.split("\n");
        HashMap<String, String> map = new HashMap<String, String>();
        // 從請求頭第二行開始分隔,因爲第一行沒有冒號
        for (int i = 1; i < lines.length; i++) {
            String[] result = lines[i].split(": ");
            map.put(result[0], (result.length <= 1) ? "" : result[1].replace('\n', ' ').trim());
        }
        header.setMap(map);
        header.setAccept(map.get("Accept")); // 獲取Accept參數值,存放到RequestHeader對象當中
        header.setAccept_language(map.get("Accept-Language")); // 獲取Accept-Language參數值,存放到RequestHeader對象當中
        header.setUser_agent(map.get("User-Agent")); // 獲取User-Agent參數值,存放到RequestHeader對象當中
        header.setAccept_encoding(map.get("Accept-Encoding")); // 獲取Accept-Encoding參數值,存放到RequestHeader對象當中
        header.setIp(map.get("Host").split(":")[0]); // 獲取Host參數的IP參數值,存放到RequestHeader對象當中
        header.setPort(map.get("Host").split(":")[1]); // 獲取Host參數的Port參數值,存放到RequestHeader對象當中
        header.setConnection(map.get("Connection")); // 獲取Connection參數值,存放到RequestHeader對象當中
        header.setCookie(map.get("Cookie")); // 獲取Cookie參數值,存放到RequestHeader對象當中

        return header;
    }

}

(4)在TomJetty類的run()方法中加入如下代碼,用於將解析請求頭的結果打印到控制檯上。

try {
    InputStream in = socket.getInputStream(); // 獲取客戶端發送的字節流
    OutputStream out = socket.getOutputStream(); // 獲取服務端響應的字節流
    byte[] b = new byte[1024 * 1024]; // 設置字節緩衝區
    in.read(b); // 讀取客戶端字節流(字節流的請求頭)
    String txt = new String(b).trim(); // 將請求頭封裝成String,準備交給解析器解析
    IReqestHeaderParser parser = (IReqestHeaderParser) Class.forName(TomJettyUtil.getValue("tomjetty.requestheader.class"))
        .newInstance(); // 使用工廠設計模式從tomjetty.config中加載請求頭解析器的實例
    RequestHeader header = parser.parse(txt); // 終於可以解析了
    System.out.println(header);
} catch (Exception e) {
    e.printStackTrace();
}

2解析效果展示


        在IE瀏覽器中輸入上述地址後,控制檯打印如下:

RequestHeader [
GET /hello.jsp HTTP/1.1
Accept: image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/msword, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/QVOD, application/QVOD, */*
Accept-Language: zh-cn
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; .NET CLR 2.0.50727; KB974488)
Accept-Encoding: gzip, deflate
Host: 127.0.0.1:9527
Connection: Keep-Alive
Cookie: null
]

        寫到這裏,我們終於完成了對HTTP請求頭的解析操作,感覺代碼比預想的要多,其中字符串的拆分是關鍵,其他也就沒什麼了。OK!現在的TomJetty已經能夠讀懂請求的內容了,接下來就要着手爲該請求提供服務了。可是服務的方式有很多種,其中響應靜態頁面請求就是很基本的一種。所以,在接下來的一節中我們將處理TomJetty服務器響應靜態網頁請求的問題。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章