PHP解析Http協議&協議複習

PHP解析Http協議&協議複習

最近在整理自己的知識體系,發現很多基礎已經忘得差不多了,所以重新學習一次。

Http協議

HTTP是一個應用層協議,由請求和響應構成,是一個標準的客戶端服務器模型。HTTP是一個無狀態的協議。

請求報文示例
在這裏插入圖片描述

GET /public/api/index?id=1 HTTP/1.1

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8

Accept-Encoding: gzip, deflate

Accept-Language: zh-CN,zh;q=0.9

Cache-Control: max-age=0

Connection: keep-alive

Host: 192.168.10.10:8088

Upgrade-Insecure-Requests: 1

User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36


響應報文示例
在這裏插入圖片描述

HTTP/1.1 200 OK
 
Date: Sun, 25 Oct 2015 15:43:47 GMT
 
Server: Apache
 
Set-Cookie: PHPSESSID=4v2actjf96v614r2kh36380kq6; path=/
 
Expires: Thu, 19 Nov 1981 08:52:00 GMT
 
Cache-Control: private
 
Pragma: no-cache
 
Vary: Accept-Encoding
 
Content-Length: 5105
 
Connection: close
 
Content-Type: text/html; charset=utf-8
 
 
<html>
....
</html>

代碼

<?php

/*php實現監聽socket並實時解析Http後返回信息*/

$_POST = $_GET = $_COOKIE = $_REQUEST = $_SESSION = $_FILES =  array();
//創建一個tcp套接字,並監聽8088端口
if($web = stream_socket_server('tcp://0.0.0.0:8088',$errno,$errstr)){
    while(true){
        //阻塞式讀取
        $conn = @stream_socket_accept($web);
        if($conn){
            //這裏簡單點只讀取最多2048數據
            //定長傳輸可以加入Content-Length判斷body的長度
            //如果是對數據進行分塊傳輸,則判斷Transfer-Encoding: chunked,按照truncked協議分批讀取數據
            $string = fread($conn,2048);
            // var_dump(json_encode($string));
            list($_GET, $_POST, $_COOKIE, $_SERVER, $_FILES) = parse_http($string);
            fwrite($conn,response(json_encode(array('get'=>$_GET, 'post'=>$_POST, 'cookie'=>$_COOKIE, 'server'=>$_SERVER, 'files'=>$_FILES))));
            fclose($conn);
        }
    }
}else{
    die($errstr);
}

//響應
function response($str){
    $content = "HTTP/1.1 200 OK\r\nServer: vruan_web/1.0.0\r\nContent-Length: " . strlen($str)."\r\nContent-type: application/json;charset=UTF-8"."\r\n\r\n{$str}";
    return $content;
}
 
/*
*  函數:parse_http
*  描述:解析http協議
*/
function parse_http($http)
{
        // 初始化
        $_POST = $_GET = $_COOKIE = $_REQUEST = $_SESSION = $_FILES =  array();
        $GLOBALS['HTTP_RAW_POST_DATA'] = '';
        // 需要設置的變量名
        $_SERVER = array (
              'QUERY_STRING' => '',
              'REQUEST_METHOD' => '',
              'REQUEST_URI' => '',
              'SERVER_PROTOCOL' => '',
              'SERVER_SOFTWARE' => '',
              'SERVER_NAME' => '',
              'HTTP_HOST' => '',
              'HTTP_USER_AGENT' => '',
              'HTTP_ACCEPT' => '',
              'HTTP_ACCEPT_LANGUAGE' => '',
              'HTTP_ACCEPT_ENCODING' => '',
              'HTTP_COOKIE' => '',
              'HTTP_CONNECTION' => '',
              'REMOTE_ADDR' => '',
              'REMOTE_PORT' => '0',
        );
         
        // 將header分割成數組
        list($http_header, $http_body) = explode("\r\n\r\n", $http, 2);

        $header_data = explode("\r\n", $http_header);
         
        list($_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI'], $_SERVER['SERVER_PROTOCOL']) = explode(' ', $header_data[0]);
         
        unset($header_data[0]);
        foreach($header_data as $content)
        {
            // \r\n\r\n
            if(empty($content))
            {
                continue;
            }
            list($key, $value) = explode(':', $content, 2);
            $key = strtolower($key);
            $value = trim($value);
            switch($key)
            {
                // HTTP_HOST
                case 'host':
                    $_SERVER['HTTP_HOST'] = $value;
                    $tmp = explode(':', $value);
                    $_SERVER['SERVER_NAME'] = $tmp[0];
                    if(isset($tmp[1]))
                    {
                        $_SERVER['SERVER_PORT'] = $tmp[1];
                    }
                    break;
                // cookie
                case 'cookie':
                    $_SERVER['HTTP_COOKIE'] = $value;
                    //將字符串解析成多個變量
                    parse_str(str_replace('; ', '&', $_SERVER['HTTP_COOKIE']), $_COOKIE);
                    break;
                // user-agent
                case 'user-agent':
                    $_SERVER['HTTP_USER_AGENT'] = $value;
                    break;
                // accept
                case 'accept':
                    $_SERVER['HTTP_ACCEPT'] = $value;
                    break;
                // accept-language
                case 'accept-language':
                    $_SERVER['HTTP_ACCEPT_LANGUAGE'] = $value;
                    break;
                // accept-encoding
                case 'accept-encoding':
                    $_SERVER['HTTP_ACCEPT_ENCODING'] = $value;
                    break;
                // connection
                case 'connection':
                    $_SERVER['HTTP_CONNECTION'] = $value;
                    break;
                case 'referer':
                    $_SERVER['HTTP_REFERER'] = $value;
                    break;
                case 'if-modified-since':
                    $_SERVER['HTTP_IF_MODIFIED_SINCE'] = $value;
                    break;
                case 'if-none-match':
                    $_SERVER['HTTP_IF_NONE_MATCH'] = $value;
                    break;
                case 'content-type':
                    // 不同的請求類型:application/x-www-form-urlencoded application/json multipart/form-data text/xml
                    //demo: Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryrGKCBY7qhFd3TrwA
                    if(!preg_match('/boundary="?(\S+)"?/', $value, $match))
                    {
                        $_SERVER['CONTENT_TYPE'] = $value;
                    }
                    else
                    {
                        $_SERVER['CONTENT_TYPE'] = 'multipart/form-data';
                        $http_post_boundary = '--'.$match[1];
                    }
                    break;
            }
        }
         
        // 需要解析$_POST
        if($_SERVER['REQUEST_METHOD'] === 'POST')
        {   
            //上傳文件處理
            if(isset($_SERVER['CONTENT_TYPE']) && $_SERVER['CONTENT_TYPE'] === 'multipart/form-data')
            {
                parse_upload_files($http_body, $http_post_boundary);
            }
            else
            {
                parse_str($http_body, $_POST);
                // $GLOBALS['HTTP_RAW_POST_DATA']
                $GLOBALS['HTTP_RAW_POST_DATA'] = $http_body;
            }
        }
         
        // QUERY_STRING
        $_SERVER['QUERY_STRING'] = parse_url($_SERVER['REQUEST_URI'], PHP_URL_QUERY);
        if($_SERVER['QUERY_STRING'])
        {
            // $GET
            parse_str($_SERVER['QUERY_STRING'], $_GET);
        }
        else
        {
            $_SERVER['QUERY_STRING'] = '';
        }
         
        // REQUEST
        // $_REQUEST = array_merge($_GET, $_POST);
         
        return array($_GET, $_POST, $_COOKIE, $_SERVER, $_FILES);
 }
 
/*
*  函數:parse_upload_files
*  描述:解析上傳的文件
*/
function parse_upload_files($http_body, $http_post_boundary)
{   
    //去除最後一行boundary
    $http_body = substr($http_body, 0, strlen($http_body) - (strlen($http_post_boundary) + 4));
    $boundary_data_array = explode($http_post_boundary."\r\n", $http_body);
    if($boundary_data_array[0] === '')
    {
        unset($boundary_data_array[0]);
    }
    foreach($boundary_data_array as $boundary_data_buffer)
    {
        /**
            Content-Disposition: form-data; name="text"
            
            title
            ------WebKitFormBoundaryrGKCBY7qhFd3TrwA
            Content-Disposition: form-data; name="file"; filename="chrome.png"
            Content-Type: image/png
            
            PNG ... content of chrome.png ...
         */
        list($boundary_header_buffer, $boundary_value) = explode("\r\n\r\n", $boundary_data_buffer, 2);
        // 去掉末尾\r\n
        $boundary_value = substr($boundary_value, 0, -2);
        foreach (explode("\r\n", $boundary_header_buffer) as $item)
        {
            list($header_key, $header_value) = explode(": ", $item);
            $header_key = strtolower($header_key);
            switch ($header_key)
            {
                case "content-disposition":
                    // 是文件
                    if(preg_match('/name=".*?"; filename="(.*?)"$/', $header_value, $match))
                    {
                        $_FILES[] = array(
                            'file_name' => $match[1],
                            'file_data' => $boundary_value,
                            'file_size' => strlen($boundary_value),
                        );
                        continue;
                    }
                    // 是post field
                    else
                    {
                        // 收集post
                        if(preg_match('/name="(.*?)"$/', $header_value, $match))
                        {
                            $_POST[$match[1]] = $boundary_value;
                        }
                    }
                    break;
            }
        }
    }
}

定義及參考資料

https://www.cnblogs.com/guguli/p/4758937.html
http://caibaojian.com/http-protocol.html

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