[C++] 简单解析http请求

#include <iostream>

#include <string>
#include <map>
#include <vector>
#include <regex>

class HttpRequest {
public:
    enum Method {
        GET,
        POST,
        UNKNOWN
    };

    enum Error {
        SUCCESS,
        FORMAT_ERROR,
        INCOMPLETE_REQUEST
    };

    HttpRequest() : method(Method::UNKNOWN) {}

    Error parse(const std::string& request) {

        std::vector<std::string> header_body = split(request, "\r\n\r\n");
        if (!header_body.size()) {
            return FORMAT_ERROR;
        }

        std::vector<std::string> lines = split(header_body[0], "\r\n");
        for (size_t i = 0; i < lines.size(); ++i) {
            if (i == 0) {
                std::vector<std::string> tokens = split(lines[i], " ");
                if (tokens.size() != 3) {
                    return FORMAT_ERROR;
                }
                if (tokens[0] == "GET") {
                    method = Method::GET;
                } else if (tokens[0] == "POST") {
                    method = Method::POST;
                    if (header_body.size() == 2) {
                        body = header_body[1];
                    }
                } else {
                    method = Method::UNKNOWN;
                }
                path = tokens[1];
                continue;
            }
            
            std::string & line = lines[i];
            if (line.find(":") != std::string::npos) {
                std::vector<std::string> kv = split(line, ": ");
                if (kv.size() != 2) {
                    return FORMAT_ERROR;
                }
                headers[kv[0]] = kv[1];
            }
        }

        if (method == Method::POST) {
            size_t content_length = std::strtold(headers["Content-Length"].c_str(), NULL);
            if (body.size() != content_length) {
                std::cout << body.size() << ":" << content_length << std::endl;
                return INCOMPLETE_REQUEST;
            }
        }
        return SUCCESS;
    }

    void dump() {
        std::cout << "method: " << method << std::endl;
        std::cout << "path: " << path << std::endl;
        std::cout << "headers: " << std::endl;
        for (auto & kv : headers) {
            std::cout << kv.first << ": " << kv.second << std::endl;
        }
        std::cout << "body: " << body << std::endl;
    }

private:
    Method method;
    std::string path;
    std::map<std::string, std::string> headers;
    std::string body;

    std::vector<std::string> split(const std::string &str, const std::string &delim) {
        std::vector<std::string> tokens;
        std::regex re(delim); // 创建正则表达式对象
        std::sregex_token_iterator it(str.begin(), str.end(), re, -1);
        std::sregex_token_iterator end;

        while (it != end) {
            tokens.push_back(*it++);
        }

        return tokens;
    }
};

int main() {
    std::string get_request = "GET /index.html HTTP/1.1\r\n"
                          "Host: www.example.com\r\n"
                          "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36\r\n";

    HttpRequest req;
    req.parse(get_request);
    req.dump();

    std::string post_request = "POST /submit HTTP/1.1\r\n"
                            "Host: www.example.com\r\n"
                            "Content-Type: application/x-www-form-urlencoded\r\n"
                            "Content-Length: 15\r\n"
                            "\r\n"
                            "name=John&age=30";

    std::string post_request2 = "POST / HTTP/1.1\r\n"
                                "Host: 127.0.0.1:8899\r\n"
                                "User-Agent: curl/7.81.0\r\n"
                                "Accept: */*\r\n"
                                "Content-Length: 18\r\n"
                                "Content-Type: application/x-www-form-urlencoded\r\n"
                                "\r\n"
                                "SELECT * FROM fff;";

    int ret = req.parse(post_request2);
    req.dump();
    if (ret != HttpRequest::SUCCESS) {
        std::cout << "parse error: " << ret << std::endl;
    }

    return 0;
}

仅实现简单的解析功能,并未考虑性能和http的完整支持。需要说明INCOMPLETE_REQUEST结果是为了解决tcp一次接收不到完整包的情况,可能需要多次解析

posted @ 2024-08-05 14:31  Startu  阅读(34)  评论(0编辑  收藏  举报