当前位置 博文首页 > wanggao的专栏:muduo学习笔记:net部分之Http--HttpRequest、Ht

    wanggao的专栏:muduo学习笔记:net部分之Http--HttpRequest、Ht

    作者:[db:作者] 时间:2021-09-09 09:41

    前文【muduo学习笔记:net部分之Http-协议简介】简单介绍了HTTP的协议,本文使用muduo网络库简单实现HTTP的请求体和响应体。

    客户端发送请求,通过muduo库之后服务端收到的数据存放于Buffer中,之后解析成HttpRequest请求对象,再创建一个HttpResponse响应对象并格式化成Buffer返回给客户端。服务端解析客户端请求的Buffer使用HttpContext类。

    1、HttpRequest 类

    发送端构造一个HttpRequest,调用成员函数设置请求头、请求体等。调用成员函数请求头字段,使用 const char* start, const char* end 来传递一个字符串。主要是因为基于TCP的请求数据都保存在Buffer中,通过解析Buffer中数据进行传递HTTP报文信息。

    class HttpRequest : public muduo::copyable
    {
     public:
      enum Method  { kInvalid, kGet, kPost, kHead, kPut, kDelete };  //设计支持的请求类型
      enum Version { kUnknown, kHttp10, kHttp11 };  // HTTP版本
    
      HttpRequest() : method_(kInvalid), version_(kUnknown) {}  //默认构造函数
    
      void setVersion(Version v) { version_ = v; }      // 版本
      Version getVersion() const { return version_; }
    
      bool setMethod(const char* start, const char* end)  // 根据字符串设定请求方法
      {
        assert(method_ == kInvalid);
        string m(start, end);
        if      (m == "GET")  {  method_ = kGet;  }
        else if (m == "POST") {  method_ = kPost; }
        else if (m == "HEAD") {  method_ = kHead; }
        else if (m == "PUT")  {  method_ = kPut;  }
        else if (m == "DELETE"){ method_ = kDelete; }
        else                   { method_ = kInvalid;}
        return method_ != kInvalid;
      }
      Method method() const { return method_; }
    
      const char* methodString() const  // 请类型字符串
      {
        const char* result = "UNKNOWN";
        switch(method_)
        {
          case kGet:    result = "GET";    break;
          case kPost:   result = "POST";   break;
          case kHead:   result = "HEAD";   break;
          case kPut:    result = "PUT";    break;
          case kDelete: result = "DELETE"; break;
          default: break;
        }
        return result;
      }
      // 请求行的URL
      void setPath(const char* start, const char* end) { path_.assign(start, end); } 
      const string& path() const { return path_; }
      
      void setQuery(const char* start, const char* end) { query_.assign(start, end);  }
      const string& query() const { return query_; }
    
      void setReceiveTime(Timestamp t) { receiveTime_ = t; }
      Timestamp receiveTime() const  { return receiveTime_; }
    
      // 请求头的添加键值对
      void addHeader(const char* start, const char* colon, const char* end)
      {
        string field(start, colon);  // 要求冒号前无空格
        ++colon;
        while (colon < end && isspace(*colon)) {  // 过滤冒号后的空格
           ++colon; 
        }
        string value(colon, end);
        while (!value.empty() && isspace(value[value.size()-1])){
          value.resize(value.size()-1);
        }
        headers_[field] = value;
      }
      // 请求头部查找键的值
      string getHeader(const string& field) const
      {
        string result;
        std::map<string, string>::const_iterator it = headers_.find(field);
        if (it != headers_.end()){
          result = it->second;
        }
        return result;
      }
    
      const std::map<string, string>& headers() const { return headers_; }
    
      void swap(HttpRequest& that)  // 交换
      {
        std::swap(method_, that.method_);
        std::swap(version_, that.version_);
        path_.swap(that.path_);
        query_.swap(that.query_);
        receiveTime_.swap(that.receiveTime_);
        headers_.swap(that.headers_);
      }
    
     private:
      Method method_;						// 请求行 - 请求方法
      Version version_;						// 请求行 - HTTP版本
      string path_;							// 请求行 - URL
      string query_;						// 请求体
      Timestamp receiveTime_;				// 请求事件
      std::map<string, string> headers_;	// 请求头部
    };
    

    添加请求头键值对的字符串中,冒号前不能有空格,例如"key:value"或"key: value"是正确的,"key :value"就是错误的。

    请求行中的URL中可能带有请求参数,以问号"?"分割,后面的请求参数使用键值对方式,并作为请求体保存。

    2、HttpResponse 类

    主要用于构造一个HttpResponse,调用成员函数设置响应头部、响应体,调用appendToBuffer()格式化到Buffer中,回复给客户端。

    class HttpResponse : public muduo::copyable
    {
     public:
      enum HttpStatusCode{
        kUnknown,
        k200Ok = 200,					// 正常
        k301MovedPermanently = 301,	    // 资源不可访问,重定向
        k400BadRequest = 400,			// 请求错误(域名不存在、请求不正确)
        k404NotFound = 404,				// 通常是URL不正确(或者因为服务不再提供)
      };
    
      explicit HttpResponse(bool close) : statusCode_(kUnknown), closeConnection_(close){}
    
      void setStatusCode(HttpStatusCode code) { statusCode_ = code; }
    
      void setStatusMessage(const string& message) { statusMessage_ = message; }
    
      void setCloseConnection(bool on) { closeConnection_ = on; }
    
      bool closeConnection() const { return closeConnection_; }
    
      void setContentType(const string& contentType) { addHeader("Content-Type", contentType); }
    
      // FIXME: replace string with StringPiece
      void addHeader(const string& key, const string& value) { headers_[key] = value; }
    
      void setBody(const string& body) { body_ = body; }
    
      void appendToBuffer(Buffer* output) const;  // 将整个HttpRespose对象按照协议输出到Buffer中
    
     private:
      std::map<string, string> headers_;   	// 响应头部,键值对
      HttpStatusCode statusCode_;		   	// 响应行 - 状态码
      // FIXME: add http version
      string statusMessage_;				// 响应行 - 状态码文字描述
      bool closeConnection_;				// 是否关闭连接
      string body_;  						// 响应体
    };
    

    函数HttpResponse::appendToBuffer(Buffer* output)默认使用HTTP1.1版本,按照HTTP协议对HttpResponse对象进行格式化输出到Buffer中。

    void HttpResponse::appendToBuffer(Buffer* output) const
    {
      char buf[32];
      // 响应行
      snprintf(buf, sizeof buf, "HTTP/1.1 %d ", statusCode_);
      output->append(buf)