sylar高性能服务器-日志(P61-P66)内容记录

本文详细介绍了如何基于一个开源的HTTP解析协议,封装HTTP请求和响应报文,包括GET请求示例、响应报文格式、HttpMethod和HttpStatus枚举,以及HttpRequest和HttpResponse类的构造函数和执行函数的实现。
摘要由CSDN通过智能技术生成

P61-P66:HTTP协议封装

​ 本章节内容主要基于网上一个开源的Http解析协议,封装了对Http请求/响应报文使用状态机解析报文格式,保存到请求和响应报文对象中。

​ 由于最主要的Http解析用了现成的开源代码,所以这几节的内容还是比较容易,核心还是去理解所使用的http解析源码,下面的内容仅视频内容的记录,没有源码解析。

请求报文格式

GET / HTTP/1.1  #请求行
Host: www.baidu.com   #主机地址
Connection: keep-alive   #表示TCP未断开
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64;x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36   #产生请求的浏览器类型
Sec-Fetch-Dest: document
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: ......    #用户安全凭证
URI: http://www.sylar.top:80/page/xxx?id=10&v=20#fr
http  协议
www.sylar.top  host主机
80  post端口
/page/xxx 路径path
id=10&v=20 参数query
#fr fragment

响应报文格式

HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
Date: Mon, 05 Jun 2023 06:53:13 GMT
Link: <http://www.sylar.top/blog/index.php?rest_route=/>; rel="https://api.w.org/"
Server: nginx/1.12.2
Transfer-Encoding: chunked
X-Powered-By: PHP/7.0.33
Connection: close
Content-length: 45383

<!DOCTYPE html>
<html lang="zh-CN" class="no-js">
<head>
<meta charset="UTF-8">

HttpMethod

使用宏将所有请求方法转为枚举类的成员

enum class HttpMethod {
#define XX(num, name, string) name = num,
    HTTP_METHOD_MAP(XX)
#undef XX
    INVALID_METHOD
};

HttpStatus

使用宏将所有响应码转为枚举类的成员

enum class HttpStatus {
#define XX(code, name, desc) name = code,
    HTTP_STATUS_MAP(XX)
#undef XX
};

checkGetAs

// 获取Map中的key值,并转成对应类型,返回是否成功
template<class MapType,class T>
bool checkGetAs(const MapType& m, const std::string& key, T& val, const T& def = T()) {
    std::string str;
    // MapType 里面的元素类型举例->("content-length", "xxxxxxx")
    // 在容器中查找
    auto it = m.find(key);
    // 找不到返回失败,val调用相应的默认构造函数有一个默认值
    if(it == m.end()) {
        val = def;
        return false;
    }
    try {
        // 这里需要根据我们想要得到key值类型进行转换
        val = boost::lexical_cast<T>(it->second);
        return true;
    } catch (...) {
        val = def;
    }
    return false;
}

getAs

除了不能指定返回key值的类型,其它和上面相同

// 获取Map中的key值,并转成对应类型
template<class MapType,class T>
T getAs(const MapType& m, const std::string& key, const T& def = T()) {
    auto it = m.find(key);
    if(it == m.end()) {
        return def;
    }
    try {
        return boost::lexical_cast<T>(it->second);
    } catch(...) {

    }
    return def;
}

class HttpRequest

成员变量
// HTTP方法
HttpMethod m_method;
// HTTP版本
uint8_t m_version;
// http/1.1支持长连接
// 是否自动关闭
bool m_close;
// 请求路径
std::string m_path;
// 请求参数
std::string m_query;
// 请求fragment
std::string m_fragment;
// 请求消息体
std::string m_body;
// 请求头部MAP
MapType m_headers;
// 请求参数MAP
MapType m_params;
// 请求Cookie MAP
MapType m_cookies;

class HttpResponse

成员变量

提前把后面新增的变量写进来了

// 响应状态
HttpStatus m_status;
// 版本
uint8_t m_version;
// 是否自动关闭长链接
bool m_close;
// 是否为websocket
// bool m_websocket;   
// 响应消息体
std::string m_body;
// 响应原因
std::string m_reason;
// 响应头部MAP
MapType m_headers;
// cookies
// std::vector<std::string> m_cookies;

class HttpRequestParser

成员变量
/// http_parser
http_parser m_parser;
/// HttpRequest结构
HttpRequest::ptr m_data;
/// 错误码
/// 1000: invalid method
/// 1001: invalid version
/// 1002: invalid field
int m_error;
构造函数

当解析到时调用回调函数,将对应的数据保存到报文中。

HttpRequestParser::HttpRequestParser() 
    :m_error(0) {
    m_data.reset(new sylar::http::HttpRequest);
    http_parser_init(&m_parser);
    m_parser.request_method = on_request_method;
    m_parser.request_uri = on_request_uri;
    m_parser.fragment = on_request_fragment;
    m_parser.request_path = on_request_path;
    m_parser.query_string = on_request_query;
    m_parser.http_version = on_request_version;
    m_parser.header_done = on_request_header_done;
    m_parser.http_field = on_request_http_field;
    m_parser.data = this;
}
execute执行函数
size_t HttpRequestParser::execute(char* data, size_t len) {
    size_t offset = http_parser_execute(&m_parser, data, len, 0);
    memmove(data, data + offset, (len - offset));
    return offset;
}

class HttpResponseParser

成员函数
/// httpclient_parser
httpclient_parser m_parser;
/// HttpResponse
HttpResponse::ptr m_data;
/// 错误码
/// 1001: invalid version
/// 1002: invalid field
int m_error;

测试

测试代码

#include "sylar/http/http_parser.h"
#include "sylar/log.h"

static sylar::Logger::ptr g_logger = SYLAR_LOG_ROOT();

const char test_request_data[] = "GET / HTTP/1.1\r\n"
                                "Host: www.sylar.top\r\n"
                                "Content-Length: 10\r\n\r\n"
                                "1234567890";

void test_request() {
    sylar::http::HttpRequestParser parser;
    std::string tmp = test_request_data;
    size_t s = parser.execute(&tmp[0], tmp.size());
    SYLAR_LOG_ERROR(g_logger) << "execute rt=" << s
        << "has_error=" << parser.hasError()
        << " is_finished=" << parser.isFinished()
        << " total=" << tmp.size()
        << " content_length=" << parser.getContentLength();
    tmp.resize(tmp.size() - s);
    SYLAR_LOG_INFO(g_logger) << parser.getData()->toString();
    // SYLAR_LOG_INFO(g_logger) << tmp;
}

const char test_response_data[] = "HTTP/1.1 200 OK\r\n"
        "Date: Tue, 04 Jun 2019 15:43:56 GMT\r\n"
        "Server: Apache\r\n"
        "Last-Modified: Tue, 12 Jan 2010 13:48:00 GMT\r\n"
        "ETag: \"51-47cf7e6ee8400\"\r\n"
        "Accept-Ranges: bytes\r\n"
        "Content-Length: 81\r\n"
        "Cache-Control: max-age=86400\r\n"
        "Expires: Wed, 05 Jun 2019 15:43:56 GMT\r\n"
        "Connection: Close\r\n"
        "Content-Type: text/html\r\n\r\n"
        "<html>\r\n"
        "<meta http-equiv=\"refresh\" content=\"0;url=http://www.baidu.com/\">\r\n"
        "</html>\r\n";

void test_response() {
    sylar::http::HttpResponseParser parser;
    std::string tmp = test_response_data;
    size_t s = parser.execute(&tmp[0], tmp.size(), true);
    SYLAR_LOG_ERROR(g_logger) << "execute rt=" << s
        << " has_error=" << parser.hasError()
        << " is_finished=" << parser.isFinished()
        << " total=" << tmp.size()
        << " content_length=" << parser.getContentLength()
        << " tmp[s]=" << tmp[s];

    tmp.resize(tmp.size() - s);

    SYLAR_LOG_INFO(g_logger) << parser.getData()->toString();
    SYLAR_LOG_INFO(g_logger) << tmp;
}

int main(int argc, char** argv) {
    test_request();
    SYLAR_LOG_INFO(g_logger) << "--------------";
    test_response();
    return 0;
}

结果

image-20240309195911249

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

madkeyboard

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值