<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    BinCat

    BinCat

    大家好,我是BinCat,一個基于JavaEE API實現的超簡單(不安全的非標準的??,僅用于學習Java容器原理)的Web Server

    temp_paste_image_060b117e682e40715171017c22358241

    Http請求協議解析

    Http協議(超文本傳輸協議,HyperText Transfer Protocol)是一種用于分布式、協作式和超媒體信息系統的應用層協議。HTTP是萬維網的數據通信的基礎。要想能夠處理Http請求就必須先解析Http請求,不同的Http請求方式對應的數據包也是不一樣的。

    GET請求包示例:

    
    GET / HTTP/1.1
    
    Host: localhost:8080
    
    User-Agent: curl/7.64.1
    
    Accept: */*
    

    POST請求包示例:

    
    POST /?s=java HTTP/1.1
    
    Host: localhost:8080
    
    User-Agent: curl/7.64.1
    
    Accept: */*
    
    Cookie: Hm_lvt_f4c571d9b8811113b4f18e87a6dbe619=1597582351; Hm_lpvt_f4c571d9b889b22224f18e87a6dbe619=1599562693; JSESSIONID=LgxJ127kT7ymIGbC2T1TeipnMP9_2_CqJQjmrqOb
    
    Content-Length: 17
    
    Content-Type: application/x-www-form-urlencoded
    
    id=123&name=admin
    

    解析Http簡要流程

    解析POST請求的簡單流程如下(非multipart或chunked請求):

    1. 解析第一行的Http協議信息。

    2. 解析Http請求Header信息。

    3. 解析請求主體(Body)部分。

    解析Http請求協議信息

    接下來我們將以上述的POST包解析為例簡單的實現Http協議解析。如上POST包,第一行數據中包含了請求方式、請求的URL地址以及Http協議版本信息(空格隔開):POST /?s=java HTTP/1.1。那么我們只需要使用空白符號將字符串切割成數組即可完成解析。

    解析Http請求協議示例代碼片段:

    
    // 從Socket中讀取一行數據,讀取請求的URL
    
    String str = dis.readLine();
    
    // 切割請求Http協議信息
    
    String[] strs = str.split("\\s+");
    
    // 解析Http請求方法類型
    
    String method = strs[0];
    
    // 解析Http請求URL地址
    
    String url = strs[1];
    
    // 解析Http請求版本信息
    
    String httpVersion = strs[2];
    

    解析Http請求Header信息

    解析完Http請求協議后就應該繼續解析Http Header信息了,Http請求頭從第二行開始到一個空白行結束,Header中的鍵值對以:分割,如下:

    
    Host: localhost:8080
    
    User-Agent: curl/7.64.1
    
    Accept: */*
    
    Content-Length: 17
    
    Content-Type: application/x-www-form-urlencoded
    

    解析Http頭示例代碼片段:

    
    // 創建Header對象
    
    Map<String, String> header = new ConcurrentHashMap<String, String>();
    
    // 解析請求頭信息
    
    while (true) {
    
        // 按行讀取Header頭信息
    
        String line = dis.readLine();
    
        // 當讀取到空行時停止解析Header
    
        if ("".equals(line)) {
    
            break;
    
        }
    
        // 切割Header的Key/Value
    
        String[] headers = line.split(":\\s*", -1);
    
        header.put(headers[0], headers[1]);
    
    }
    

    解析完Header后剩下的也就是最后的Http請求主體部分了,瀏覽器會將請求的參數以&為連接符拼接出多個參數,參數名稱和參數值以=分割,并且參數值默認會使用URL編碼,如下:

    
    id=123&name=admin
    

    解析body中的請求參數時需要先從Header中讀取請求的主體大小,即:Content-Length,因為body中允許出現換行\n等特殊內容,所以解析body時應該按字節讀取數據。除此之外,解析Body中的請求參數之前應該先解析URL中的請求參數,即GET傳參部分:/?s=java,然后再解析body中的參數。

    解析Http GET參數代碼片段:

    
    // 解析GET請求參數
    
    if (url.contains("?")) {
    
        String[] parameterStrs = url.split("\\?");
    
        this.requestURL = parameterStrs[0];
    
        // 初始化Http請求的QueryString
    
        this.queryString = parameterStrs[1];
    
        // 按"&"切割GET請求的參數
    
        String[] parameters = queryString.split("&");
    
        // 解析GET請求參數
    
        for (String parameter : parameters) {
    
            String[] tmp = parameter.split("=", -1);
    
            if (tmp.length == 2) {
    
                parameterMap.put(tmp[0], new String[]{URLDecoder.decode(tmp[1])});
    
            }
    
        }
    
    }
    

    Cookie解析

    Cookie是非常Http請求中非常重要的用戶憑證,Cookie位于請求頭中的cookie字段,多個Cookie;分割,Cookie的參數和參數值以=切分。Cookie中會存儲一個叫JSESSIONID(Java標準容器中叫JSESSIONID),用于識別服務器端存儲的用戶會話信息。

    示例Cookie:

    
    Cookie: Hm_lvt_f4c571d9b8811113b4f18e87a6dbe619=1597582351; Hm_lpvt_f4c571d9b889b22224f18e87a6dbe619=1599562693; JSESSIONID=LgxJ127kT7ymIGbC2T1TeipnMP9_2_CqJQjmrqOb
    

    示例Cookie解析代碼片段:

    
    // 解析Cookie
    
    if (headerMap.containsKey("cookie")) {
    
        // 切分Cookie字符串
    
        String[] cookies = headerMap.get("cookie").split(";\\s+", -1);
    
        // 初始化Cookie數組長度
    
        this.cookie = new Cookie[cookies.length];
    
        for (int i = 0; i < cookies.length; i++) {
    
            String   cookieStr = cookies[i];
    
            String[] tmp       = cookieStr.split("=", -1);
    
            if (tmp.length == 2) {
    
                // 創建Cookie對象
    
                this.cookie[i] = new Cookie(tmp[0], URLDecoder.decode(tmp[1]));
    
            }
    
        }
    
    }
    

    解析Http請求主體

    解析Http主體代碼片段:

    
    if ("POST".equalsIgnoreCase(method)) {
    
        String contentType = header.get("Content-Type");
    
        // 解析POST請求參數
    
        if ("application/x-www-form-urlencoded".equalsIgnoreCase(contentType)) {
    
            // 獲取請求的主體長度
    
            int contentLength = Integer.parseInt(header.get("Content-Length"));
    
            // 創建一個和請求體一樣大小的緩沖區
    
            byte[] bytes = new byte[contentLength];
    
            // 讀取POST主體內容
    
            dis.read(bytes);
    
            // 解析POST請求內容
    
            String body = new String(bytes, "ISO8859-1");
    
            // 按"&"切割POST請求的參數
    
            String[] parameters = body.split("&");
    
            // 解析POST請求參數
    
            for (String parameter : parameters) {
    
                String[] tmp = parameter.split("=", -1);
    
                if (tmp.length == 2) {
    
                    parameterMap.put(tmp[0], URLDecoder.decode(tmp[1]));
    
                }
    
            }
    
        }
    
    }
    

    本文章首發在 網安wangan.com 網站上。

    上一篇 下一篇
    討論數量: 0
    只看當前版本


    暫無話題~
    亚洲 欧美 自拍 唯美 另类