C/C++编程:http_header类的实现

1059 篇文章 288 订阅

前置知识

作用

  • HTTP 消息头用于描述资源或服务器或客户端的行为

分类

根据不同上下文,可将消息头分为:

  • General headers(通用头): 同时适用于请求和响应消息,但与最终消息主体中传输的数据无关的消息头。
  • Request headers(请求头): 包含更多有关要获取的资源或客户端本身信息的消息头。
  • Response headers(响应头): 包含有关响应的补充信息,如其位置或服务器本身(名称和版本等)的消息头。
  • Entity headers(实体头): 包含有关实体主体的更多信息,比如主体长(Content-Length)度或其MIME类型。

又因为:实体报头可能同时存在于 HTTP 请求和响应信息中。因此可以把实体头看成是通用头,所以它们可以分为:

  • 通用头: 既可以用于客户端请求又可以用于客户端响应
  • 请求头:仅用于客户端请求
  • 响应头:仅用于服务端响应

一般实现:我们可以将通用头实现为一个基础类,然后请求头和响应头作为其子类,这样它们就可以自然而然的继承通用头的属性和方法了。 缺点是:

  • 引入了多个类,对使用者不友好
  • 增加一些成本(理由请参见第三点),个人对继承的使用机会一般在于当需要用到动态时,但是这里的通用头对请求头和响应头的方法是一样的,因此没有必要引入继承机制
  • 更重要的是:一个header要么是请求头,要么是响应头;通用头只是请求头和响应头的组成部分(使用私有继承/成员变量实现);因此我们不能生成一个通用头的实例,也就是说它应该是一个纯虚类(这里会引入虚表,一定会增加成本),而纯虚类的使用场景一定是动态(个人理解),而这里无需用到多态。

继承机制的实现原理:函数指针和类型转换

因此这里用一个单独的类http_header来实现,通过一个布尔类型变量is_request_来区分这是请求头还是响应头(一个类要么是请求头,要么是响应头,不可能是其他)

#ifndef OCEANSTAR_HTTP_HTTP_HEADER_H
#define OCEANSTAR_HTTP_HTTP_HEADER_H

namespace oceanstar{
    class  http_header{
    public:
        /**
         * 判断是否是 HTTP 请求头
         * @return {bool} 返回 false 表明是 HTTP 响应头
         */
        bool is_request(void) const;
 		/**
         * 设置 HTTP 头是客户端的请求头还是服务器的响应头
         * @param onoff {bool} true 表示是请求头,否则表示响应头
         * @return {http_header&} 返回本对象的引用,便于用户连续操作
         */
        http_header& set_request_mode(bool onoff);
    private:
        bool is_request_;                     // 是请求头还是响应头
    };
}


#endif //OCEANSTAR_HTTP_HTTP_HEADER_H

#include "http_header.h"
namespace oceanstar{
    bool http_header::is_request() const
    {
        return is_request_;
    }

	http_header& http_header::set_request_mode(bool onoff)
	{
		is_request_ = onoff;
		return *this;
	}
}

格式

  • 一个请求头由名称(不区分大小写)后跟一个冒号“:”,冒号后跟具体的值(不带换行符)组成。该值前面的引导空白会被忽略

例如: Content-Language: de-DE

  • 一个请求头中有多个项
  • 客户可以禁止或者开启某些项

因此,我们新建一个类来表示 【项】,该类应该有三个成员变量:name_、value_、off_ (表示禁用1或者开启0)

 /* name-value 格式的条目 */
    class HTTP_HDR_ENTRY {
    public:
        HTTP_HDR_ENTRY(const char *name, const char *value){
            name_ = name;
            value_ = value;
            off_ = 0;
        }

        ~HTTP_HDR_ENTRY(){

        }

        const char * get_name() const{
            return name_.c_str();
        }

        const char * get_value() const{
            return value_.c_str();
        }

        void set_name(const char *name){
            name_ = name;
        }

        void set_value(const char *value){
            value_ = value;
        }

        std::string name_;
        std::string value_;
        int   off_;
    };

    inline HTTP_HDR_ENTRY * http_hdr_entry_new(const char *name, const char *value){
        return new HTTP_HDR_ENTRY(name, value);
    }

    inline void http_hdr_entry_free(HTTP_HDR_ENTRY * hdrEntry){
        delete hdrEntry;
    }

同时在一个http_header类中用std::list来存储这些项:

//
// Created by oceanstar on 2021/8/11.
//

#ifndef OCEANSTAR_HTTP_HTTP_HEADER_H
#define OCEANSTAR_HTTP_HTTP_HEADER_H

#include <protocol/HTTP_HDR_ENTRY.h>
#include <list>
namespace oceanstar{
    class  http_header{
    public:

         /**
         * 向 HTTP 头中添加字段
         * @param name {const char*} 字段名,非空指针
         * @param value {const char*} 字段值,非空指针
         * @param replace {bool} 如果存在重复项时是否自动覆盖旧数据
         * @return {http_header&} 返回本对象的引用,便于用户连续操作
         */
        http_header& add_entry(const char* name, const char* value, bool replace = true);
        /**
         * 从 HTTP 头中获得指定的头部字段
         * @param name {const char*} 字段名,非空指针
         * @return {const char*} 返回值 NULL 表示不存在
         */
        const char* get_entry(const char* name) const;
    private:
        bool is_request_;                     // 是请求头还是响应头
        std::list<HTTP_HDR_ENTRY*> entries_;
    };
}


#endif //OCEANSTAR_HTTP_HTTP_HEADER_H



//
// Created by oceanstar on 2021/8/11.
//

#include <string.h>
#include "http_header.h"
namespace oceanstar{
    http_header& http_header::add_entry(const char* name, const char* value, bool replace /* = true */){
        if (name == NULL || *name == 0 || value == NULL || *value == 0) {
            return *this;
        }

        if (replace) {
            std::list<HTTP_HDR_ENTRY*>::iterator it = entries_.begin();
            for (; it != entries_.end(); ++it) {
                if (strcasecmp((*it)->get_name(), name) == 0) {
                    (*it)->set_value(value) ;
                    return *this;
                }
            }
        }

        HTTP_HDR_ENTRY * entry = http_hdr_entry_new(name, value);
        entries_.push_back(entry);
        return  *this;
    }

    const char* http_header::get_entry(const char* name) const{
        for (std::list<HTTP_HDR_ENTRY*>::const_iterator cit = entries_.begin();
             cit != entries_.end(); ++cit) {

            if (strcasecmp((*cit)->get_name(), name) == 0) {
                return (*cit)->get_value();
            }
        }

        return NULL;
    }
}

Http头中除了有k-v格式的头外,还有另外格式的,如下:
在这里插入图片描述
另外,有些项目用的很频繁,比如说Connection,我们不能每次需要Connection时都去list中循环获取,因此,我们需要定义一些单独的变量

通用头

有些header既可以用于请求,又可以用于响应,我们先来实现这个

添加:HTTP版本

在这里插入图片描述

 		http_header(){
            version_[0]       = 0;
        }
 private:
        char  version_[8];                    // HTTP 协议版本号
    http_header& http_header::set_proto_version(const char* version)
    {
        if (version && *version) {
            strncpy(version_, version, sizeof(version_));
        }
        return *this;
    }

添加:用于分段请求的range字段

/**
         * 设置 HTTP 请求头(响应头)中的 Range 字段,用于分段请求(响应)数据,
         * 多用于支持断点续传的 WEB 服务器中
         * @param from {http_off_t} 起始偏移位置,下标从 0 开始,该
         *  值当 >= 0 时才有效
         * @param to {http_off_t} 请求结束偏移位置,下标从 0 开始,
         *  在请求头中当该值输入 < 0 时,则认为是请求从起始位置开始至最终长度位置
         * @return {http_header&} 返回本对象的引用,便于用户连续操作
         */
        http_header& set_range(long long from, long long to);
        /**
         * 对于响应头在分段传输前需要调用此函数设置数据体总长度
         * @param total {http_off_t} 仅对于响应头,该参数需要设为数据总长度
         * @return {http_header&}
         */
        http_header& set_range_total(long long total);
        /**
         * 获得由 set_range 设置的分段请求位置值
         * @param from {http_off_t*} 非空时存储起始位置偏移
         * @param to {http_off_t*} 非空时存储结束位置偏移
         */
        void get_range(long long int* from, long long int* to);
 private:
 		long long int range_from_ = -1;            // 请求头中,range 起始位置
        long long int range_to_ = -1;              // 请求头中,range 结束位置
        long long int range_total_ = -1;           // range 传输模式下记录数据总长度
http_header& http_header::set_range_total(http_off_t total)
{
	range_total_ = total;
	return *this;
}

http_header& http_header::set_range(http_off_t from, http_off_t to)
{
	range_from_ = from;
	if (to >= from) {
		range_to_ = to;
	} else {
		range_to_ = -1;
	}
	return *this;
}

void http_header::get_range(http_off_t* from, http_off_t* to)
{
	if (from) {
		*from = range_from_;
	}
	if (to) {
		*to = range_to_;
	}
}

添加:content_length && Content-Type

content_length和Content-Type是一起作用的

content_length

  • Content-Length是一个实体消息首部
    private:
        long long int content_length_ = -1;        // HTTP 数据体长度
   /**
         * 设置 HTTP 头中的 Content-Length 字段
         * @param n {int64} 设置值
         * @return {http_header&} 返回本对象的引用,便于用户连续操作
         */
        http_header& set_content_length(long long int n);
        long long int get_content_length() const
        {
            return content_length_;
        }

Content-Type

Content-Type也是一个实体报头
在这里插入图片描述

       /**
         * 设置 HTTP 头中的 Content-Type 字段
         * @param value {const char*} 设置值
         * @return {http_header&} 返回本对象的引用,便于用户连续操作
         */
        http_header& set_content_type(const char* value);
http_header& http_header::set_content_type(const char* value)
    {
        add_entry("Content-Type", value);
        return *this;
    }

添加:Connection–keep_alive(功能尚未完成)

在这里插入图片描述

		 /**
         * 设置 HTTP 头中的 Connection 字段,是否保持长连接
         * @param on {bool} 是否保持长连接
         * @return {http_header&} 返回本对象的引用,便于用户连续操作
         */
        http_header& set_keep_alive(bool on);

        /**
         * 检查当前头是否设置了保持长连接选项
         */
        bool get_keep_alive() const
        {
            return keep_alive_;
        }
private:
	bool keep_alive_;                     // 是否保持长连接
http_header& http_header::set_keep_alive(bool on)
{
	keep_alive_ = on;
	return *this;
}

添加:upgrade_

  • HTTP 101 Switching Protocol(协议切换)状态码表示服务器应客户端升级协议的请求(Upgrade (en-US)请求头)正在切换协议。
  • 服务器会发送一个Upgrade (en-US)响应头来表明其正在切换过去的协议。
    在这里插入图片描述
		http_header(){
            upgrade_          = NULL;
        }
        http_header& set_upgrade(const char* value = "websocket");
		const char* get_upgrade(void) const
		{
			return upgrade_;
		}
private: 
	char* upgrade_;
  // 这个函数一定是响应调用
  http_header& http_header::set_upgrade(const char* value /* = "websocket" */)
    {
        if (value && *value) {
            upgrade_ = strdup(value);
            status_ = 101;  // automatic set status_ to 101
        } else {
            upgrade_ = NULL;
        }
        return *this;
    }

添加:cookie

/**
         * 向 HTTP 头中添加 cookie
         * @param name {const char*} cookie 名
         * @param value {const char*} cookie 值
         * @param domain {const char*} 所属域
         * @param path {const char*} 存储路径
         * @param expires {time_t} 过期时间,当该值为 0 时表示不过期,
         *  > 0 时,则从现在起再增加 expires 即为过期时间,单位为秒
         * @return {http_header&} 返回本对象的引用,便于用户连续操作
         */
        http_header& add_cookie(const char* name, const char* value,
                                const char* domain = NULL, const char* path = NULL,
                                time_t expires = 0);
        /**
         * 从 HTTP 头中获得对应名称的 cookie 对象
         * @param name {const char*} cookie 名
         * @return {const HttpCookie*}
         */
         const http_cookie* get_cookie(const char* name) const;
private:
	std::list<http_cookie*> cookies_;      // cookies 集合
   const http_cookie* http_header::get_cookie(const char* name) const{
        if (name == NULL || *name == 0) {
            return NULL;
        }

        for (std::list<http_cookie*>::const_iterator cit = cookies_.begin();
             cit != cookies_.end(); ++cit) {

            if (!strcasecmp((*cit)->getName(), name)) {
                return *cit;
            }
        }

        return NULL;
    }
    http_header& http_header::add_cookie(const char* name, const char* value,
                                         const char* domain /* = NULL */, const char* path /* = NULL */,
                                         time_t expires /* = 0 */){
        if (name == NULL || *name == 0 || value == NULL) {
            return *this;
        }
        
        http_cookie * cookie = http_cookie_new(name, value);
        if (domain && *domain) {
            cookie->setDomain(domain);
        }
        if (path && *path) {
            cookie->setPath(path);
        }
        if (expires > 0) {
            cookie->setExpires(expires);
        }
        cookies_.push_back(cookie);
        return *this;
    }

生成通用头

   void http_header::build_common(std::string& buf) const{
        if (!entries_.empty()) {
            std::list<HTTP_HDR_ENTRY*>::const_iterator it = entries_.begin();
            for (; it != entries_.end(); ++it) {
                buf += format("%s: %s\r\n", (*it)->get_name(), (*it)->get_value());
            }
        }

        if (chunked_transfer_) {
            buf += format("Transfer-Encoding: chunked\r\n");
        } else if (content_length_ >= 0) {
            buf += format("Transfer-Encoding: %d\r\n", content_length_);
        }

        if (!is_request_ && cgi_mode_) {  // 是响应而且是cgi响应就直接返回
            return;
        }

        if (upgrade_ && *upgrade_) {
            buf += format("Upgrade: %s\r\nConnection: Upgrade\r\n", upgrade_);
        } else if (keep_alive_) {
            buf += format("Connection: Keep-Alive\r\n");
        } else {
            buf += format("Connection: Close\r\n");
        }
    }

请求头

添加:请求方法

在这里插入图片描述

    // HTTP 请求方法
    typedef enum
    {
        HTTP_METHOD_UNKNOWN,    // 未知方法
        HTTP_METHOD_GET,        // GET 方法
        HTTP_METHOD_POST,       // POST 方法
        HTTP_METHOD_PUT,        // PUT 方法
        HTTP_METHOD_CONNECT,    // CONNECT 方法
        HTTP_METHOD_PURGE,      // PURGE 方法
        HTTP_METHOD_DELETE,     // DELETE 方法
        HTTP_METHOD_HEAD,       // HEAD 方法
        HTTP_METHOD_OPTION,     // OPTION 方法
        HTTP_METHOD_PROPFIND,	// PROPFIND 方法
        HTTP_METHOD_PATCH,	// PATCH 方法
        HTTP_METHOD_OTHER,	// 其它的方法
    } http_method_t;
//
// Created by oceanstar on 2021/8/11.
//

#ifndef OCEANSTAR_HTTP_HTTP_HEADER_H
#define OCEANSTAR_HTTP_HTTP_HEADER_H

#include <protocol/HTTP_HDR_ENTRY.h>
#include <list>
#include "http_type.h"

namespace oceanstar{
    class  http_header{
    public:
        /**
	 * 设置 HTTP 协议的请求方法,如果不调用此函数,则默认用 GET 方法
	 * @param method {http_method_t} HTTP 请求方法
	 * @return {http_header&} 返回本对象的引用,便于用户连续操作
	 */
        http_header& set_method(http_method_t method);

        /**
         * 设置 HTTP 协议的请求方法,本函数允许用户扩展 HTTP 请求方法,
         * 通过该函数设置的请求方法仅影响 HTTP 请求过程
         * @param method {const char*} 请求方法
         * @return {http_header&} 返回本对象的引用,便于用户连续操作
         */
        http_header& set_method(const char* method);

        /**
         * 当作为请求头时,本函数取得当前邮件头的请求方法
         * @param buf {string*} 存储用字符串表示的请求方法
         * @return {http_method_t}
         */
        http_method_t get_method(std::string* buf = NULL) const;
    private:
        bool is_request_;                     // 是请求头还是响应头
        std::list<HTTP_HDR_ENTRY*> entries_;
        http_method_t method_;                // HTTP 请求的方法
        char  method_s_[64];                  // HTTP 请求方法以字符串表示
    };
}


#endif //OCEANSTAR_HTTP_HTTP_HEADER_H

#define CP(x, y) strncpy(x, y, sizeof(x))

    http_header& http_header::set_method(http_method_t method)
    {
        method_ = method;

        switch(method_) {
            case HTTP_METHOD_GET:
                CP(method_s_, "GET");
                break;
            case HTTP_METHOD_POST:
                CP(method_s_, "POST");
                break;
            case HTTP_METHOD_PUT:
                CP(method_s_, "PUT");
                break;
            case HTTP_METHOD_CONNECT:
                CP(method_s_, "CONNECT");
                break;
            case HTTP_METHOD_PURGE:
                CP(method_s_, "PURGE");
                break;
            case  HTTP_METHOD_DELETE:
                CP(method_s_, "DELETE");
                break;
            case  HTTP_METHOD_HEAD:
                CP(method_s_, "HEAD");
                break;
            case  HTTP_METHOD_OPTION:
                CP(method_s_, "OPTIONS");
                break;
            case HTTP_METHOD_PROPFIND:
                CP(method_s_, "PROPFIND");
                break;
            case HTTP_METHOD_PATCH:
                CP(method_s_, "PATCH");
                break;
            default:
                CP(method_s_, "UNKNOWN");
                break;
        }
        return *this;
    }

    http_header& http_header::set_method(const char* method)
    {
        if (strcasecmp(method, "GET") == 0) {
            method_ = HTTP_METHOD_GET;
        } else if (strcasecmp(method, "POST") == 0) {
            method_ = HTTP_METHOD_POST;
        } else if (strcasecmp(method, "PUT") == 0) {
            method_ = HTTP_METHOD_PUT;
        } else if (strcasecmp(method, "CONNECT") == 0) {
            method_ = HTTP_METHOD_CONNECT;
        } else if (strcasecmp(method, "PURGE") == 0) {
            method_ = HTTP_METHOD_PURGE;
        } else if (strcasecmp(method, "DELETE") == 0) {
            method_ = HTTP_METHOD_DELETE;
        } else if (strcasecmp(method, "HEAD") == 0) {
            method_ = HTTP_METHOD_HEAD;
        } else if (strcasecmp(method, "OPTIONS") == 0) {
            method_ = HTTP_METHOD_OPTION;
        } else if (strcasecmp(method, "PROPFIND") == 0) {
            method_ = HTTP_METHOD_PROPFIND;
        } else if (strcasecmp(method, "PATCH") == 0) {
            method_ = HTTP_METHOD_PATCH;
        } else if (*method != 0) {
            method_ = HTTP_METHOD_OTHER;
        } else {
            method_ = HTTP_METHOD_UNKNOWN;
        }

        CP(method_s_, method);

        return *this;
    }

    http_method_t http_header::get_method(std::string* buf /* = NULL */) const
    {
        if (buf) {
            *buf = method_s_;
        }
        return method_;
    }

添加:请求路径

在这里插入图片描述
分析:一个请求URI中能够获取到的东西有:

  • 请求参数
  • 请求路径
  • 请求主机

添加:请求参数

因为一个请求中可能有多个参数,因此用list存储

    class  http_header{
    public:
        /**
		 * 向请求的 URL 中添加参数对,当只有参数名没有参数值时则:
		 * 1、参数名非空串,但参数值为空指针,则 URL 参数中只有:{name}
		 * 2、参数名非空串,但参数值为空串,则 URL参数中为:{name}=
		 * @param name {const char*} 参数名,不能为空指针
		 * @param value {const char*} 参数值,当为空指针时,仅添加参数名,
		 * @return {http_header&} 返回本对象的引用,便于用户连续操作
		 */
        http_header& add_param(const char* name, const char* value);
    private:
        std::list<http_param*> params_;       // 请求参数集合
   http_header& http_header::add_param(const char* name, const char* value){
        if (name == NULL || *name == 0) {
            return *this;
        }

        std::list<http_param*>::iterator it = params_.begin();
        for (; it != params_.end(); ++it) {
            if (strcasecmp((*it)->name, name) == 0) {
                if (value) {
                    (*it)->value = strdup(value);
                } else {
                    (*it)->value = NULL;
                }
                return *this;
            }
        }

        http_param* param = (http_param*) malloc(sizeof(http_param));
        param->name = strdup(name);
        if (value) {
            param->value = strdup(value);
        } else {
            param->value = NULL;
        }
        params_.push_back(param);
        return *this;
    }

添加:请求路径+请求主机

	/**
	 * 设置请求的 URL,url 格式示例如下:
	 * 1、http://www.test.com/
	 * 2、/cgi-bin/test.cgi
	 * 3、http://www.test.com/cgi-bin/test.cgi
	 * 3、http://www.test.com/cgi-bin/test.cgi?name=value
	 * 4、/cgi-bin/test.cgi?name=value
	 * 5、http://www.test.com
	 * 如果该 url 中有主机字段,则内部自动添加主机;
	 * 如果该 url 中有参数字段,则内部自动进行处理并调用 add_param 方法;
	 * 调用该函数后用户仍可以调用 add_param 等函数添加其它参数;
	 * 当参数字段只有参数名没有参数值时,该参数将会被忽略,所以如果想
	 * 单独添加参数名,应该调用 add_param 方法来添加
	 * @param url {const char*} 请求的 url,非空指针
	 * @param encoding {bool} 是否对存在于 url 中的参数进行 url 编码
	 * @return {http_header&} 返回本对象的引用,便于用户连续操作
	 */
	http_header& set_url(const char* url, bool encoding = true);

	/**
	 * 设置 HTTP 请求头的 HOST 字段
	 * @param value {const char*} 请求头的 HOST 字段值
	 * @return {http_header&} 返回本对象的引用,便于用户连续操作
	 */
	http_header& set_host(const char* value);

	/**
	 * 获得设置的 HTTP 请求头中的 HOST 字段
	 * @return {const char*} 返回空指针表示没有设置 HOST 字段
	 */
	const char* get_host() const
	{
		return host_[0] == 0 ? NULL : host_;
	}
private:
        char  method_s_[64];                  // HTTP 请求方法以字符串表示
        char* url_;                           // HTTP 请求的 URL
        char  host_[256];                     // HTTP 请求头中的 HOST 字段
http_header& http_header::set_host(const char* value)
{
	if (value && *value) {
		CP(host_, value);
	}
	return *this;
}

  http_header& http_header::set_url(const char* url, bool encoding /* = true */)
    {
        assert(url && *url);

        is_request_ = true;
        size_t len = strlen(url);

        // 多分配两个字节:'\0' 及可能添加的 '/'
        url_ = (char*) malloc(len + 2);
        memcpy(url_, url, len);
        url_[len] = 0;

        char* ptr;
        if (strncasecmp(url_, "http://", sizeof("http://") - 1) == 0) {
            ptr = url_ + sizeof("http://") - 1;
        } else if (strncasecmp(url_, "https://", sizeof("https://") - 1) == 0) {
            ptr = url_+ sizeof("https://") -1;
        } else if (strncasecmp(url_, "ws://", sizeof("ws://") - 1) == 0) {
            ptr = url_ + sizeof("ws://") - 1;
        } else if (strncasecmp(url_, "wss://", sizeof("wss://") - 1) == 0) {
            ptr = url_ + sizeof("wss://") - 1;
        } else {
            ptr = url_;
        }

        char* params, *slash;

        // 开始提取 host 字段

        // 当 url 中只有相对路径时
        if (ptr == url_) {
            if (encoding) {
                params = strchr(ptr, '?');
            } else {
                params = NULL;
            }
        }

            // 当 url 为绝对路径时
        else if ((slash = strchr(ptr, '/')) != NULL && slash > ptr) {
            size_t n = slash - ptr + 1;
            if (n > sizeof(host_)) {
                n = sizeof(host_);
            }

            // 添加主机地址
            strncpy(host_, ptr, n);
            if (encoding) {
                params = strchr(slash, '?');
            } else {
                params = NULL;
            }
        }
            // 当 url 为绝对路径且主机地址后没有 '/'
        else {
            // 这是安全的,因为在前面给 url_ 分配内存时多了一个字节
            if (slash == NULL) {
                url_[len] = '/';
                url_[len + 1] = 0;
            }

            if (encoding) {
                params = strchr(ptr, '?');
            } else {
                params = NULL;
            }
        }

        if (params == NULL) {
            return *this;
        }

        *params++ = 0;
        if (*params == 0) {
            return *this;
        }

        oceanstar::url_coder coder;
        coder.decode(params);
        const std::vector<URL_NV*>& tokens = coder.get_params();
        std::vector<URL_NV*>::const_iterator cit = tokens.begin();
        for (; cit != tokens.end(); ++cit) {
            add_param((*cit)->name, (*cit)->value);
        }

        return *this;
    }

添加: HTTP 请求头中是否允许接收压缩数据(暂缺)

		 /**
         * 设置 HTTP 请求头中是否允许接收压缩数据,对应的 HTTP 头字段为:
         * Accept-Encoding: gzip, deflate
         * @param on {bool} 如果为 true 则自动添加 HTTP 压缩头请求
         * @return {http_header&} 返回本对象的引用,便于用户连续操作
         */
        http_header& accept_gzip(bool on){
            accept_compress_ = false;
            return *this;
        }
private: 
   bool accept_compress_;                // 是否接收压缩数据

添加:ws

  • 定义:
	const char* get_ws_origin(void) const
	{
		return ws_origin_;
	}

	const char* get_ws_key(void) const
	{
		return ws_sec_key_;
	}

	const char* get_ws_protocol(void) const
	{
		return ws_sec_proto_;
	}

	int get_ws_version(void) const
	{
		return ws_sec_ver_;
	}
	const char* get_ws_accept(void) const
	{
		return ws_sec_accept_;
	}
private: 
 // just for websocket
        char* ws_origin_ = NULL;
        char* ws_sec_key_ = NULL;
        char* ws_sec_proto_ = NULL;
        int   ws_sec_ver_ = NULL;
        char* ws_sec_accept_ = NULL;
  • 方法:
   		 http_header& set_ws_origin(const char* url){
            if (url && *url) {
                ws_origin_ = strdup(url);
            }
            return *this;
        }

        http_header& set_ws_protocol(const char* proto){
            if (proto && *proto) {
                ws_sec_proto_ = strdup(proto);
            }
            return *this;
        }

        http_header& set_ws_version(int ver){
            ws_sec_ver_ = ver;
            return *this;
        }

        http_header& set_ws_accept(const char* key){
            if (key && *key) {
                ws_sec_key_ = strdup(key);
            }
            return *this;
        }

在这里插入图片描述

http_header& set_ws_key(const void* key, size_t len){
            if (key && len > 0) {
                acl_uint64 n = acl_hash_crc64(key, len);
                char buf[24];
                snprintf(buf, sizeof(buf), "%llu", n);
                char * hex_enc = (char *)acl_hex_encode(buf, strlen(buf));
                char * base64_enc = (char *)acl_base64_encode(hex_enc, strlen(hex_enc));
                ws_sec_key_ = strdup(base64_enc);
                free(base64_enc);
                free(hex_enc);
            }
            return *this;
        }

        http_header& set_ws_key(const char* key){
            if (key && *key) {
                return set_ws_key(key, strlen(key));
            } else {
                return *this;
            }
        }
  • 生成请求头时:

添加:重定向

重定向时,本header的url_会变

public: 
	/**
	 * url 重定向
	 * @param url {const char*} 重定向的 URL,格式为:
	 *  http://xxx.xxx.xxx/xxx 或 /xxx
	 *  如果是前者,则自动从中取出 HOST 字段,如果是后者,则
	 *  延用之前的 HOST
	 */
	bool redirect(const char* url);

	/**
	 * 设置重定向次数,如果该值 == 0 则不主动进行重定向,否则
	 * 进行重定向且重定向的次数由该值决定
	 * @param n {int} 允许重定向的次数
	 * @return {http_header&} 返回本对象的引用,便于用户连续操作
	 */
	http_header& set_redirect(unsigned int n = 5);

	/**
	 * 获取通过 set_redirect 设置的允许的最大重定向次数
	 * @return {unsigned int}
	 */
	unsigned int get_redirect(void) const;

	/**
	 * 当需要重定向时,会主动调用此函数允许子类做一些重置工作
	 */
	virtual void redicrect_reset(void) {}
    bool http_header::redirect(const char* url)
    {
        if (url == NULL || *url == 0) {
            logger_error("url null");
            return false;
        }

        size_t n = 0;

        // url: http[s]://xxx.xxx.xxx/xxx or /xxx
        if (strncasecmp(url, "http://", sizeof("http://") - 1) == 0) {
            n = sizeof("http://") - 1;
        } else if (strncasecmp(url, "https://", sizeof("https://") - 1) == 0) {
            n = sizeof("https://") - 1;
        }
        if (url_) {
            url_ = NULL;
        }

        if (n > 0) {
            url += n;
            char* ptr = strdup(url);
            char* p = strchr(ptr, '/');
            if (p) {
                *p = 0;
            }
            if (*ptr == 0) {
                logger_error("invalid url(%s)", url);
                return false;
            }
            set_host(ptr);
            if (*(p + 1)) {
                *p = '/';
                url_ = p;
            } else {
                url_ = strdup("/");
            }
        } else {
            url_ = strdup(url);
        }

        return true;
    }

    http_header& http_header::set_redirect(unsigned int n /* = 5 */)
    {
        nredirect_ = n;
        return *this;
    }

    unsigned int http_header::get_redirect() const
    {
        return nredirect_;
    }

生成请求头

  /**
         * 创建 HTTP 请求头数据
         * @param buf {string&} 存储结果数据
         * @return {bool} 创建请求头中否成功
         */
        bool build_request(std::string& buf) const;

响应头

添加:设置响应状态字

     /**
         * 设置 HTTP 响应头中的响应状态字
         * @param status {int} 状态字如:1xx, 2xx, 3xx, 4xx, 5xx
         * @return {http_header&} 返回本对象的引用,便于用户连续操作
         */
        http_header& set_status(int status);

        /**
         * 获得响应头中的 HTTP 状态字
         * @return {int} HTTP 响应状态码:1xx, 2xx, 3xx, 4xx, 5xx
         */
        int get_status(void) const
        {
            return status_;
        }
http_header& http_header::set_status(int status)
{
	status_ = status;
	is_request_ = false;
	return *this;
}

添加:设置是否是分块传输

        /**
         * 设置 HTTP 响应头中的 chunked 传输标志
         * @param on {bool}
         * @return {http_header&}
         */
        http_header& set_chunked(bool on);

        /**
         * 判断当前 HTTP 传输是否采用 chunked 传输方式
         * @return {bool}
         */
        bool chunked_transfer(void) const
        {
            return chunked_transfer_;
        }
    private:
        bool chunked_transfer_;               // 是否为 chunked 传输模式
http_header& http_header::set_chunked(bool on)
{
	chunked_transfer_ = on;
	return *this;
}

添加:设置cgi_mode(没有实现)

     /**
         * 设置是否用来生成 CGI 格式的响应头
         * @param on {bool} 是否 CGI 格式响应头
         * @return {http_header&} 返回本对象的引用,便于用户连续操作
         */
        http_header& set_cgi_mode(bool on);

        /**
         * 是否设置了 CGI 模式
         * @return {bool}
         */
        bool is_cgi_mode() const
        {
            return cgi_mode_;
        }
private:
	bool cgi_mode_;                       // 是否 CGI 响应头
http_header& http_header::set_cgi_mode(bool on)
{
	cgi_mode_ = on;
	if (cgi_mode_) {
		is_request_ = false;
	}
	return *this;
}

添加:设置是否压缩【没有实现】

        /**
         * 设置传输的数据是否采用 gzip 方式进行压缩
         * @param on {bool}
         * @return {http_header&}
         */
        http_header& set_transfer_gzip(bool on);

        /**
         * 获得当前的数据传输是否设置了采用 gzip 压缩方式
         * @return {bool}
         */
        bool is_transfer_gzip() const
        {
            return transfer_gzip_;
        }

    private:
        bool transfer_gzip_;                  // 数据是否采用 gzip 压缩

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值