目錄
設計思路?
成員設計
模塊實現
設計思路?
首先我們要先知道HTTP的請求的流程是什么樣子的,不然我們會學的很迷糊。對于HTTP請求如何到來以及去往哪里,我們應該很清楚的知道
HTTP請求在服務器系統中的傳遞流程是一個多層次的過程:
- 客戶端發起請求 - 用戶在瀏覽器或應用中發起HTTP請求
- 網絡傳輸 - 請求通過網絡傳輸到服務器
- 服務器接收 - 服務器的TCP/IP協議棧接收數據包并將其傳遞給操作系統
- 網絡庫接收 - 在muduo這樣的網絡庫中,數據通過以下路徑流動:
- 操作系統的socket接口接收原始數據
- 網絡庫的EventLoop檢測到socket可讀事件
- TcpConnection將數據讀入其內部Buffer
- HTTP解析 - 數據進入HTTP解析層:
- TcpServer將連接和數據傳遞給HttpServer
- HttpServer創建HttpContext對象處理每個連接
- HttpContext從Buffer中讀取數據并進行解析
- 解析結果存儲在HttpRequest對象中
- 應用處理 - 最終HttpRequest被傳遞給用戶注冊的回調函數進行業務邏輯處理
HttpRequest通常由HttpContext創建和填充,然后傳遞給處理HTTP請求的回調函數或處理器。它本質上是HTTP請求信息的容器,使服務器能夠方便地訪問和處理客戶端的請求內容。
在整個HTTP處理流程中,HttpRequest的角色是保存從網絡層解析出的HTTP請求信息,供應用層使用。
HTTP請求格式
成員設計
HTTP請求中的主要要素包括:
- 請求行:
- 請求方法(GET、POST、PUT、DELETE等)
- URL,包含:
- 資源路徑
- 查詢參數(鍵值對形式)
- 協議版本(如HTTP/1.1)
- 請求頭部:
- 多個鍵值對格式的頭部字段
- 常見的如Content-Type、User-Agent、Host等
- 請求正文:
- 根據Content-Length或Transfer-Encoding確定長度
- 內容格式由Content-Type決定
首先請求行中,有請求方法,url ,協議版本,而url中又分為資源路徑和參數,參數是kv的形式,所以我們需要使用一個map來保存
而請求頭部中,都是一些kv格式的屬性,我們也是使用一個map來保存
最后就是正文部分,正文部分是交給上層業務邏輯去處理的,我們只需要按照請求頭部中的Content-Length提取出來就行了。
那么我們需要保存的就是 : 請求方法,資源路徑,參數,協議版本,頭部字段,正文 ,當然,由于可能會存在中間的處理過程,比如對請求行的解析,我們會使用正則表達式來進行,我們可以再存儲一個 std::smatch 來保存正則提取出來的結果。
由于HttpRequest后續我們是交給 上下文模塊來進行設置的,為了方便,我們就直接將成員設置為公有的了,便于直接訪問。
同時,對于參數和請求頭部,我們可以提供結構,用來插入kv形式的參數和請求頭部,以及查詢是否有某個參數或者請求頭部。
再HttpRequest的頭部字段中,有一個很重要的信息就是正文長度,我們可以提供一個接口用來獲取正文長度。
最后再提供一個接口用于判斷長短連接,長短連接后續我們會用到。
然后如果是短連接的話,我們每次接收完也需要把數據給清除了。所以也需要個接口
模塊實現
代碼挺簡單的,跟著思路走就很容易寫出來的
class HttpRequest
{
public:string _method; //存儲請求方法string _path; //存儲資源路徑string _version; //存儲協議版本string _body; //存儲正文unordered_map<string, string> _headers; //存儲請求頭部unordered_map<string, string> _params; //存儲查詢字符串smatch _matches; //資源路徑正則提取
private:bool HasHeader(const sting &key) const //給獲取報文長度函數提供的{auto it = _headers.find(key);if(it == _headers.end()){return false;}return it->second;}string GetHeader(const sting &key) const //給獲取報文長度函數提供的{auto it = _headers.find(key);if(it == _headers.end()){return "";}return it->second;}public:HttpRequest():_version("HTTP/1.1"){}void clear()//清空類數據{_method.clear();_path.clear();_version("HTTP/1.1");_body.clear();_headers.clear();_params.clear();smatch matches;_matches.swap(matches);}void SetHeader(const string &key, const string &val)//添加請求頭部{_headers.insert(key, val);}void SetParam(const string &key, const string &val)//添加查詢字符串{_params.insert(key, val);}size_t GetLength()//獲取正文長度{bool ret = HasHeader("Content-Length");if(ret == false){return 0;}string len = GetHeader("Content-Length");return stol(len);}//是否是短鏈接bool Close() const{if(HasHeader("Connection") == true && GetHeader("Connection") == "keep-alive");{return false;}return true;}
};