五、HTTP解析与响应

一、 前言

经过前面的配置,现在我们再加入HTTP报文的解析与响应就可以当作简单的服务器了!

本节代码均可在仓库TinyWebServer 中找到

二、前置知识

这里尽量让零基础小白也能听懂。

1 HTTP协议到底是什么?

简单来说就是协议。可能还是有点抽象,举个例子,买东西的过程,选择商品 -> 付钱 -> 卖家商品交付。

这个过程,就是协议,你给钱,我给商品得到过程,协议规定给钱而不是石头,这就是协议

2 HTTP规定的协议是什么呢?

分为请求报文和响应报文。

2.1 请求报文由四个部分组成:

  • 请求行: 包括请求方法、请求 URL、HTTP 协议和版本
  • 请求头: 一些键值对(表示客户端的一些信息)
  • 空行: 请求头之后是一个空行,通知服务器以下不再有请求头、空行后面的内容是请求体
  • 请求体: 用于发送POST、PUT等请求方法时发送的数据。

2.2 响应报文由四个部分组成:

  • 状态行: HTTP协议和版本、状态码、状态描述
  • 响应头
  • 空行
  • 响应体

值得一提的是,我们做的是HTTP后端服务,前端发送请求报文是由浏览器发送的,我们并不需要管,我们只需要能够解析发送过来的报文,以及对这个报文进行响应

3 HTTP版本

主流版本1.12.03.0
每一次的版本更新迭代都是解决现有问题(为了更快,更强!),HTTP也不例外,虽然笔者很想一步就用最好的3.0但对于初学者困难太多,太不友好,所以这里先用1.1协议(现在也仍然是主流,并未被淘汰)。

4 解析HTTP报文的方式

常见的方式有很多中如正则表达式方法、字符串分割方法、第三方库方法等等。这些要么低效要么就是增加项目依赖,不够轻量。那么我来介绍一种用的最多个效率最好是方式状态机方法

4.1 状态机方法优缺点:

  • 特点:使用有限状态机(FSM)来解析 HTTP 请求。
  • 优点:高效、内存使用少、适合流式处理。
  • 缺点:实现可能较为复杂。

4.2 状态机的实现方式

  1. 状态定义: 我们定义了一系列状态,如 METHOD, URL, VERSION, HEADER_NAME, HEADER_VALUE, BODY 等。
  2. 字符处理: 解析器逐字符处理输入,根据当前状态和输入字符决定下一步操作。
  3. 状态转换: 根据解析的内容,状态机在不同状态之间转换。例如,从 METHOD 状态遇到空格后转到 URL 状态。
  4. 增量解析: 我们的解析器支持增量解析,这意味着它可以处理部分数据,并在接收到更多数据时继续解析。
  5. 错误处理: 状态机设计允许我们在解析过程中随时检测和处理错误。

现在就实现了子类http1.1的状态机,后续会补上,所以这里定义状态机父类。

5 HTTP报文响应

这里笔者用简单工厂方法返回不同状态码方法。

5.1 什么是状态码

简单来说就是,解析请求报文后服务器返回给客户端的标识,比如成功了!或者没有这个资源,在或者服务器崩溃啦!

分5大类

  • 1xx:指示信息,表示请求已接收,继续处理
  • 2xx:成功,表示请求已被成功接受,处理。
    • 200 OK:客户端请求成功
    • 204 No Content:无内容。服务器成功处理,但未返回内容。一般用在只是客户端向服务器发送信息,而服务器不用向客户端返回什么信息的情况。不会刷新页面。
    • 206 Partial Content:服务器已经完成了部分GET请求(客户端进行了范围请求)。响应报文中包含Content-Range指定范围的实体内容
  • 3xx:重定向
    • 301 Moved Permanently:永久重定向,表示请求的资源已经永久的搬到了其他位置。
    • 302 Found:临时重定向,表示请求的资源临时搬到了其他位置
    • 303 See Other:临时重定向,应使用GET定向获取请求资源。303功能与302一样,区别只是303明确客户端应该使用GET访问
    • 307 Temporary Redirect:临时重定向,和302有着相同含义。POST不会变成GET
    • 304 Not Modified:表示客户端发送附带条件的请求(GET方法请求报文中的IF…)时,条件不满足。返回304时,不包含任何响应主体。
  • 4xx:客户端错误
    • 400 Bad Request:客户端请求有语法错误,服务器无法理解。
    • 401 Unauthorized:请求未经授权,这个状态代码必须和WWW- Authenticate报头域一起使用。
    • 403 Forbidden:服务器收到请求,但是拒绝提供服务
    • 404 Not Found:请求资源不存在。比如,输入了错误的url
    • 415 Unsupported media type:不支持的媒体类型
  • 5xx:服务器端错误,服务器未能实现合法的请求。
    • 500 Internal Server Error:服务器发生不可预期的错误。
    • 503 Server Unavailable:服务器当前不能处理客户端的请求,一段时间后可能恢复正常

状态码很多我也只列出来了一点,服务器上也是只实现了大类,后面用到了,再加吧。

6 client_context的变化

很好理解因为他是存储每个客户端的消息的,当然在这里可以解析客户端的消息。

7 server的变化

添加一些方法来处理不同的请求,生成相对应的响应报文,以及设置资源目录的路径。

三、代码实现

// http_parser.h
#pragma once

#include <functional>
#include <optional>
#include <sstream>
#include <string>
#include <string_view>
#include <unordered_map>
#include <vector>

enum class HttpMethod { GET, HEAD, POST, PUT, DELETE, CONNECT, OPTIONS, TRACE, PATCH, UNKNOWN };
enum class HttpVersion { HTTP_1_0, HTTP_1_1, HTTP_2_0, UNKNOWN };

struct HttpRequest {
    HttpMethod method = HttpMethod::UNKNOWN;
    std::string url;
    HttpVersion version = HttpVersion::UNKNOWN;
    std::unordered_map<std::string, std::string> headers;
    std::vector<char> body;

    HttpRequest();
};

class IHttpParser {
public:
    using ErrorCallback = std::function<void(const std::string &)>;

    virtual ~IHttpParser() = default;
    virtual void reset() = 0;
    virtual std::optional<HttpRequest> parse(std::string_view data) = 0;
    virtual void setErrorCallback(ErrorCallback cb) = 0;
};

class HttpParser : public IHttpParser {
public:
    HttpParser();
    void reset() override;
    std::optional<HttpRequest> parse(std::string_view data) override;
    void setErrorCallback(ErrorCallback cb) override;

    static std::unordered_map<std::string, std::string> parseQueryParams(const std::string &url);

private:
    enum class ParserState { METHOD, URL, VERSION, HEADER_NAME, HEADER_VALUE, BODY, COMPLETE, ERROR };

    ParserState state;
    HttpRequest request;
    std::string currentHeaderName;
    std::string currentHeaderValue;
    size_t contentLength;
    ErrorCallback errorCallback;

    bool parseChar(char c);
    bool parseMethod(char c);
    bool parseUrl(char c);
    bool parseVersion(char c);
    bool parseHeaderName(char c);
    bool parseHeaderValue(char c);
    bool parseBody(char c);
    void processHeader();
    static HttpMethod stringToMethod(std::string_view method);
    static std::string trim(const std::string &s);
};

std::string_view toString(HttpMethod method);
std::string_view toString(HttpVersion version);
// http_parser.cpp

#include "http_parser.h"
#include <algorithm>
#include <array>
#include <cctype>
#include <sstream>

std::string_view toString(HttpMethod method) {
    static constexpr std::array<std::string_view, 10> methodStrings = {
        "GET", "POST", "PUT", "DELETE", "HEAD", "OPTIONS", "TRACE", "CONNECT", "PATCH", "UNKNOWN"
    };
    return methodStrings[static_cast<int>(method)];
}

std::string_view toString(HttpVersion version) {
    static constexpr std::array<std::string_view, 4> versionStrings = {
        "HTTP/1.0", "HTTP/1.1", "HTTP/2.0", "UNKNOWN"
    };
    return versionStrings[static_cast<int>(version)];
}

HttpRequest::HttpRequest() : method(HttpMethod::UNKNOWN), version(HttpVersion::UNKNOWN) {}

HttpParser::HttpParser() {
    reset();
}

void HttpParser::reset() {
    state = ParserState::METHOD;
    request = HttpRequest{};
    currentHeaderName.clear();
    currentHeaderValue.clear();
    contentLength = 0;
}

std::optional<HttpRequest> HttpParser::parse(std::string_view data) {
    for (char c : data) {
        if (!parseChar(c)) {
            if (errorCallback) {
                errorCallback("Parsing error");
            }
            return std::nullopt;
        }
        if (state == ParserState::COMPLETE) {
            auto result = std::move(request);
            reset();
            return result;
        }
    }
    return std::nullopt;
}

void HttpParser::setErrorCallback(ErrorCallback cb) {
    errorCallback = std::move(cb);
}

bool HttpParser::parseChar(char c) {
    switch (state) {
        case ParserState::METHOD: return parseMethod(c);
        case ParserState::URL: return parseUrl(c);
        case ParserState::VERSION: return parseVersion(c);
        case ParserState::HEADER_NAME: return parseHeaderName(c);
        case ParserState::HEADER_VALUE: return parseHeaderValue(c);
        case ParserState::BODY: return parseBody(c);
        default: return false;
    }
}

bool HttpParser::parseMethod(char c) {
    if (c == ' ') {
        request.method = stringToMethod(request.url);
        request.url.clear();
        state = ParserState::URL;
    } else {
        request.url += c;
    }
    return true;
}

bool HttpParser::parseUrl(char c) {
    if (c == ' ') {
        state = ParserState::VERSION;
    } else {
        request.url += c;
    }
    return true;
}

bool HttpParser::parseVersion(char c) {
    static const std::string_view httpVersion = "HTTP/";
    static size_t versionIndex = 0;

    if (versionIndex < httpVersion.length()) {
        if (c == httpVersion[versionIndex]) {
            ++versionIndex;
        } else {
            return false;
        }
    } else if (c == '1' || c == '2') {
        request.version = (c == '1') ? HttpVersion::HTTP_1_1 : HttpVersion::HTTP_2_0;
        versionIndex = 0;
        state = ParserState::HEADER_NAME;
    } else {
        return false;
    }
    return true;
}

bool HttpParser::parseHeaderName(char c) {
    if (c == ':') {
        state = ParserState::HEADER_VALUE;
    } else if (c == '\r') {
        // Skip carriage return
    } else if (c == '\n') {
        if (!currentHeaderName.empty()) {
            processHeader();
        } else {
            state = (contentLength > 0) ? ParserState::BODY : ParserState::COMPLETE;
        }
    } else {
        currentHeaderName += std::tolower(c);
    }
    return true;
}

bool HttpParser::parseHeaderValue(char c) {
    if (c == '\r') {
        // Skip carriage return
    } else if (c == '\n') {
        processHeader();
        state = ParserState::HEADER_NAME;
    } else {
        currentHeaderValue += c;
    }
    return true;
}

bool HttpParser::parseBody(char c) {
    request.body.push_back(c);
    if (request.body.size() == contentLength) {
        state = ParserState::COMPLETE;
    }
    return true;
}

void HttpParser::processHeader() {
    if (currentHeaderName == "content-length") {
        contentLength = std::stoul(currentHeaderValue);
    }
    request.headers[currentHeaderName] = trim(currentHeaderValue);
    currentHeaderName.clear();
    currentHeaderValue.clear();
}

HttpMethod HttpParser::stringToMethod(std::string_view method) {
    static const std::unordered_map<std::string_view, HttpMethod> methodMap = {
        {"GET", HttpMethod::GET}, {"POST", HttpMethod::POST}, {"PUT", HttpMethod::PUT},
        {"DELETE", HttpMethod::DELETE}, {"HEAD", HttpMethod::HEAD}, {"OPTIONS", HttpMethod::OPTIONS},
        {"TRACE", HttpMethod::TRACE}, {"CONNECT", HttpMethod::CONNECT}, {"PATCH", HttpMethod::PATCH}
    };
    auto it = methodMap.find(method);
    return (it != methodMap.end()) ? it->second : HttpMethod::UNKNOWN;
}

std::string HttpParser::trim(const std::string& s) {
    auto start = std::find_if_not(s.begin(), s.end(), ::isspace);
    auto end = std::find_if_not(s.rbegin(), s.rend(), ::isspace).base();
    return (start < end) ? std::string(start, end) : std::string();
}

std::unordered_map<std::string, std::string> HttpParser::parseQueryParams(const std::string& url) {
    std::unordered_map<std::string, std::string> params;
    size_t pos = url.find('?');
    if (pos != std::string::npos) {
        std::string query = url.substr(pos + 1);
        std::istringstream iss(query);
        std::string pair;
        while (std::getline(iss, pair, '&')) {
            size_t eq_pos = pair.find('=');
            if (eq_pos != std::string::npos) {
                std::string key = pair.substr(0, eq_pos);
                std::string value = pair.substr(eq_pos + 1);
                params[key] = value;
            }
        }
    }
    return params;
}
// http_response.h
#pragma once

#include <string>
#include <string_view>
#include <unordered_map>
#include <vector>

class HttpResponse {
public:
    int status_code = 200;
    std::string status_message = "OK";
    std::unordered_map<std::string, std::string> headers;
    std::vector<char> body;

    HttpResponse() = default;
    HttpResponse(int code, std::string_view message);

    void setBody(std::string_view content);
    void setBody(const std::vector<char>& content);
    std::string toString() const;
};

class HttpResponseFactory {
public:
    static HttpResponse createNotFoundResponse();
    static HttpResponse createOkResponse(const std::vector<char>& body, std::string_view contentType);
    static HttpResponse createRedirectResponse(std::string_view location);
    static HttpResponse createServerErrorResponse();
};
// http_response.cpp
#include "http_response.h"
#include <sstream>

HttpResponse::HttpResponse(int code, std::string_view message)
    : status_code(code), status_message(message) {}

void HttpResponse::setBody(std::string_view content) {
    body.assign(content.begin(), content.end());
    headers["Content-Length"] = std::to_string(body.size());
}

void HttpResponse::setBody(const std::vector<char>& content) {
    body = content;
    headers["Content-Length"] = std::to_string(body.size());
}

std::string HttpResponse::toString() const {
    std::ostringstream response;
    response << "HTTP/1.1 " << status_code << " " << status_message << "\r\n";
    for (const auto& [key, value] : headers) {
        response << key << ": " << value << "\r\n";
    }
    response << "\r\n";
    return response.str();
}

HttpResponse HttpResponseFactory::createNotFoundResponse() {
    HttpResponse response(404, "Not Found");
    response.setBody("<html><body><h1>404 Not Found</h1></body></html>");
    response.headers["Content-Type"] = "text/html";
    return response;
}

HttpResponse HttpResponseFactory::createOkResponse(const std::vector<char>& body, std::string_view contentType) {
    HttpResponse response(200, "OK");
    response.setBody(body);
    response.headers["Content-Type"] = std::string(contentType);
    return response;
}

HttpResponse HttpResponseFactory::createRedirectResponse(std::string_view location) {
    HttpResponse response(302, "Found");
    response.headers["Location"] = std::string(location);
    return response;
}

HttpResponse HttpResponseFactory::createServerErrorResponse() {
    HttpResponse response(500, "Internal Server Error");
    response.setBody("<html><body><h1>500 Internal Server Error</h1></body></html>");
    response.headers["Content-Type"] = "text/html";
    return response;
}
// server.h
#pragma once
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/epoll.h>  // 添加这行
#include <sys/socket.h>
#include <unistd.h>  // 添加这行

#include <cstring>
#include <functional>
#include <iostream>
#include <memory>
#include <mutex>
#include <unordered_map>
#include <vector>

#include "client_context.h"
#include "http_parser.h"
#include "thread_pool.h"

const int MAX_EVENTS = 10;
const int BUFFER_SIZE = 1024;

class IServer {
public:
    virtual ~IServer() = default;
    virtual void run() = 0;
};

class Server : public IServer {
public:
    using RequestHandler = std::function<HttpResponse(const HttpRequest &)>;

    Server(int port);
    void run() override;
    void registerHandler(HttpMethod method, const std::string &path, RequestHandler handler);
    void setPublicDirectory(const std::string &path);

private:
    int server_fd;
    int epoll_fd;
    ThreadPool pool;
    std::unordered_map<int, std::shared_ptr<ClientContext>> clients;
    std::mutex clients_mutex;
    std::unordered_map<HttpMethod, std::unordered_map<std::string, RequestHandler>> handlers;
    std::string publicDirectory;
    std::unordered_map<std::string, std::string> mimeTypes;

    void handleNewConnection();
    void handleClientEvent(epoll_event &event);
    void handleRead(int client_fd);
    void handleWrite(int client_fd);
    void removeClient(int client_fd);
    void modifyEpollEvent(int fd, uint32_t events);
    
    // 新增方法
    HttpResponse generateResponse(const HttpRequest &request);
    HttpResponse handleGetRequest(const HttpRequest &request);
    HttpResponse handleHeadRequest(const HttpRequest &request);
    HttpResponse handlePostRequest(const HttpRequest &request);
    HttpResponse handlePutRequest(const HttpRequest &request);
    HttpResponse handleDeleteRequest(const HttpRequest &request);
    HttpResponse handleOptionsRequest(const HttpRequest &request);
    HttpResponse createMethodNotAllowedResponse();
    void addCommonHeaders(HttpResponse &response);
    std::string getCurrentDate() const;

    HttpResponse serveStaticFile(const HttpRequest &request);
    std::string getMimeType(const std::string &filename);
};
// server.cpp

#include "server.h"
#include <cstring>
#include <iostream>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <fstream>
#include<filesystem>

Server::Server(int port) : pool(4) {
    server_fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
    if (server_fd < 0) {
        throw std::runtime_error("Socket creation failed");
    }

    sockaddr_in server_addr;
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = INADDR_ANY;
    server_addr.sin_port = htons(port);

    if (bind(server_fd, (sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
        close(server_fd);
        throw std::runtime_error("Bind failed");
    }

    if (listen(server_fd, SOMAXCONN) < 0) {
        close(server_fd);
        throw std::runtime_error("Listen failed");
    }

    epoll_fd = epoll_create1(0);
    if (epoll_fd < 0) {
        close(server_fd);
        throw std::runtime_error("epoll_create1 failed");
    }

    epoll_event event;
    event.data.fd = server_fd;
    event.events = EPOLLIN | EPOLLET;
    if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &event) < 0) {
        close(server_fd);
        close(epoll_fd);
        throw std::runtime_error("epoll_ctl failed");
    }

    setPublicDirectory("./public");  // 设置公共目录

    // 初始化MIME类型映射
    mimeTypes = {
        {".html", "text/html"},
        {".css", "text/css"},
        {".js", "application/javascript"},
        {".json", "application/json"},
        {".png", "image/png"},
        {".jpg", "image/jpeg"},
        {".jpeg", "image/jpeg"},
        {".gif", "image/gif"},
        {".svg", "image/svg+xml"},
        {".ico", "image/x-icon"},
        {".webp", "image/webp"}
    };
}

void Server::run() {
    std::vector<epoll_event> events(MAX_EVENTS);

    while (true) {
        int event_count = epoll_wait(epoll_fd, events.data(), MAX_EVENTS, -1);

        if (event_count < 0) {
            std::cerr << "epoll_wait failed: " << strerror(errno) << std::endl;
            break;
        }

        for (int i = 0; i < event_count; i++) {
            if (events[i].data.fd == server_fd) {
                handleNewConnection();
            } else {
                handleClientEvent(events[i]);
            }
        }
    }
}

void Server::registerHandler(HttpMethod method, const std::string &path, RequestHandler handler) {
    handlers[method][path] = std::move(handler);
}

void Server::handleNewConnection() {
    while (true) {
        sockaddr_in client_addr;
        socklen_t client_len = sizeof(client_addr);
        int client_fd = accept4(server_fd, (sockaddr *)&client_addr, &client_len, SOCK_NONBLOCK);
        if (client_fd < 0) {
            if (errno == EAGAIN || errno == EWOULDBLOCK) {
                break;
            } else {
                std::cerr << "Accept failed: " << strerror(errno) << std::endl;
                break;
            }
        }

        char client_ip[INET_ADDRSTRLEN];
        inet_ntop(AF_INET, &(client_addr.sin_addr), client_ip, INET_ADDRSTRLEN);
        std::cout << "New connection from " << client_ip << ":" << ntohs(client_addr.sin_port) << std::endl;

        epoll_event event;
        event.data.fd = client_fd;
        event.events = EPOLLIN | EPOLLET;
        int epoll_ctl_result = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &event);
        if (epoll_ctl_result < 0) {
            std::cerr << "epoll_ctl failed for client socket: " << strerror(errno) << std::endl;
            close(client_fd);
        } else {
            std::lock_guard<std::mutex> lock(clients_mutex);
            clients[client_fd] = std::make_shared<ClientContext>();
            std::cout << "Client " << client_fd << " added to epoll" << std::endl;
        }
    }
}

void Server::handleClientEvent(epoll_event &event) {
    int client_fd = event.data.fd;
    if (event.events & (EPOLLERR | EPOLLHUP)) {
        if (event.events & EPOLLERR) {
            std::cerr << "Error event for client " << client_fd << std::endl;
        }
        if (event.events & EPOLLHUP) {
            std::cout << "Hangup event for client " << client_fd << std::endl;
        }
        removeClient(client_fd);
    } else {
        if (event.events & EPOLLIN) {
            std::cout << "Read event for client " << client_fd << std::endl;
            handleRead(client_fd);
        }
        if (event.events & EPOLLOUT) {
            std::cout << "Write event for client " << client_fd << std::endl;
            handleWrite(client_fd);
        }
    }
}

void Server::handleRead(int client_fd) {
    pool.enqueue([this, client_fd] {
        std::shared_ptr<ClientContext> client;
        {
            std::lock_guard<std::mutex> lock(clients_mutex);
            auto it = clients.find(client_fd);
            if (it == clients.end() || !it->second->isActive()) {
                std::cout << "Client " << client_fd << " not found or not active, skipping read handling" << std::endl;
                return;
            }
            client = it->second;
        }

        std::string buffer(BUFFER_SIZE, 0);
        while (true) {
            int read_len = read(client_fd, buffer.data(), buffer.size());
            if (read_len < 0) {
                if (errno == EAGAIN || errno == EWOULDBLOCK)
                    break;
                else {
                    std::cerr << "Read failed on socket " << client_fd << std::endl;
                    removeClient(client_fd);
                    break;
                }
            } else if (read_len == 0) {
                std::cout << "Client disconnected: " << client_fd << std::endl;
                removeClient(client_fd);
                break;
            } else {
                auto request = client->parser.parse(std::string_view(buffer.data(), read_len));
                if (request) {
                    HttpResponse response = generateResponse(*request);
                    client->pushResponse(response);
                    client->setWriteReady(true);
                    modifyEpollEvent(client_fd, EPOLLIN | EPOLLOUT);
                }
            }
        }
    });
}

void Server::handleWrite(int client_fd) {
    pool.enqueue([this, client_fd] {
        std::shared_ptr<ClientContext> client;
        {
            std::lock_guard<std::mutex> lock(clients_mutex);
            auto it = clients.find(client_fd);
            if (it == clients.end() || !it->second->isActive()) {
                return;
            }
            client = it->second;
        }

        if (!client->isWriteReady() || !client->hasResponses()) return;

        HttpResponse response = client->popResponse();
        std::string headers = response.toString();
        
        // 发送头部
        if (send(client_fd, headers.c_str(), headers.length(), 0) < 0) {
            std::cerr << "Failed to send headers" << std::endl;
            removeClient(client_fd);
            return;
        }

        // 发送主体
        size_t total_sent = 0;
        while (total_sent < response.body.size()) {
            ssize_t sent = send(client_fd, response.body.data() + total_sent, response.body.size() - total_sent, 0);
            if (sent < 0) {
                if (errno == EAGAIN || errno == EWOULDBLOCK) {
                    // 资源暂时不可用,稍后重试
                    continue;
                } else {
                    std::cerr << "Send error" << std::endl;
                    removeClient(client_fd);
                    return;
                }
            }
            total_sent += sent;
        }

        if (!client->hasResponses()) {
            client->setWriteReady(false);
            modifyEpollEvent(client_fd, EPOLLIN);
        }
    });
}

void Server::removeClient(int client_fd) {
    std::shared_ptr<ClientContext> client;
    {
        std::lock_guard<std::mutex> lock(clients_mutex);
        auto it = clients.find(client_fd);
        if (it != clients.end()) {
            client = it->second;
            clients.erase(it);
        }
    }

    if (client) {
        client->deactivate();
        int epoll_ctl_result = epoll_ctl(epoll_fd, EPOLL_CTL_DEL, client_fd, nullptr);
        if (epoll_ctl_result < 0) {
            std::cerr << "Failed to remove client from epoll: " << strerror(errno) << std::endl;
        }
        close(client_fd);
    }
}

void Server::modifyEpollEvent(int fd, uint32_t events) {
    epoll_event event;
    event.data.fd = fd;
    event.events = events | EPOLLET;
    if (epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fd, &event) < 0) {
        std::cerr << "Failed to modify epoll event for fd " << fd << std::endl;
    }
}

HttpResponse Server::generateResponse(const HttpRequest &request) {
    switch (request.method) {
        case HttpMethod::GET:
            return handleGetRequest(request);
        case HttpMethod::HEAD:
            return handleHeadRequest(request);
        case HttpMethod::POST:
            return handlePostRequest(request);
        case HttpMethod::PUT:
            return handlePutRequest(request);
        case HttpMethod::DELETE:
            return handleDeleteRequest(request);
        case HttpMethod::OPTIONS:
            return handleOptionsRequest(request);
        // ... 其他方法 ...
        default:
            return createMethodNotAllowedResponse();
    }
}

void Server::setPublicDirectory(const std::string &path) {
    publicDirectory = path;
}

HttpResponse Server::serveStaticFile(const HttpRequest& request) {
    std::filesystem::path filePath = publicDirectory + request.url;
    
    if (std::filesystem::is_directory(filePath)) {
        filePath /= "index.html";
    }

    if (std::filesystem::exists(filePath) && std::filesystem::is_regular_file(filePath)) {
        std::ifstream file(filePath, std::ios::binary);
        if (file) {
            HttpResponse response;
            response.status_code = 200;
            response.status_message = "OK";
            
            // 读取文件内容
            file.seekg(0, std::ios::end);
            size_t size = file.tellg();
            file.seekg(0, std::ios::beg);
            response.body.resize(size);
            file.read(response.body.data(), size);

            response.headers["Content-Type"] = getMimeType(filePath.string());
            response.headers["Content-Length"] = std::to_string(response.body.size());
            return response;
        }
    }

    // 文件不存在,返回 404
    HttpResponse response;
    response.status_code = 404;
    response.status_message = "Not Found";
    response.setBody("<html><body><h1>404 Not Found</h1></body></html>");
    response.headers["Content-Type"] = "text/html";
    response.headers["Content-Length"] = std::to_string(response.body.size());
    return response;
}

std::string Server::getMimeType(const std::string& filename) {
    size_t dotPos = filename.find_last_of('.');
    if (dotPos != std::string::npos) {
        std::string ext = filename.substr(dotPos);
        std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
        auto it = mimeTypes.find(ext);
        if (it != mimeTypes.end()) {
            return it->second;
        }
    }
    return "application/octet-stream";  // 默认二进制流
}

HttpResponse Server::handleGetRequest(const HttpRequest &request) {
    // 现有的静态文件服务逻辑
    return serveStaticFile(request);
}

HttpResponse Server::handleHeadRequest(const HttpRequest &request) {
    HttpResponse response = handleGetRequest(request);
    response.body.clear();  // HEAD 请求不返回响应体
    return response;
}

HttpResponse Server::handlePostRequest(const HttpRequest &request) {
    // 处理 POST 请求的逻辑
    // 例如,可以解析表单数据或 JSON 数据
    // ...
}

HttpResponse Server::handlePutRequest(const HttpRequest &request) {
    // 处理 PUT 请求的逻辑
    // 例如,更新资源
    // ...
}

HttpResponse Server::handleDeleteRequest(const HttpRequest &request) {
    // 处理 DELETE 请求的逻辑
    // 例如,删除资源
    // ...
}

HttpResponse Server::handleOptionsRequest(const HttpRequest &request) {
    HttpResponse response;
    response.status_code = 200;
    response.status_message = "OK";
    response.headers["Allow"] = "GET, HEAD, POST, PUT, DELETE, OPTIONS";
    return response;
}

HttpResponse Server::createMethodNotAllowedResponse() {
    HttpResponse response;
    response.status_code = 405;
    response.status_message = "Method Not Allowed";
    response.headers["Allow"] = "GET, HEAD, POST, PUT, DELETE, OPTIONS";
    return response;
}

void Server::addCommonHeaders(HttpResponse &response) {
    response.headers["Server"] = "YourServerName/1.0";
    response.headers["Date"] = getCurrentDate();  // 实现 getCurrentDate() 方法返回当前时间
    response.headers["Connection"] = "close";  // 或者 "keep-alive",取决于您的实现
}

std::string Server::getCurrentDate() const {
    auto now = std::chrono::system_clock::now();
    auto in_time_t = std::chrono::system_clock::to_time_t(now);

    std::stringstream ss;
    ss << std::put_time(std::gmtime(&in_time_t), "%a, %d %b %Y %H:%M:%S GMT");
    return ss.str();
}
#pragma once
#include <atomic>
#include <mutex>
#include <queue>
#include <string>
#include "http_parser.h"
#include "http_response.h"  // 添加这行

class ClientContext {
public:
    ClientContext();
    void pushResponse(const HttpResponse &response);
    bool hasResponses() const;
    HttpResponse popResponse();
    void setWriteReady(bool ready);
    bool isWriteReady() const;
    bool isActive() const;
    void deactivate();
    HttpParser parser;

private:
    std::queue<HttpResponse> response_queue;
    bool write_ready;
    mutable std::mutex mtx;
    std::atomic<bool> active;
};
#include "client_context.h"

ClientContext::ClientContext() : write_ready(false), active(true) {}

void ClientContext::pushResponse(const HttpResponse &response) {
    std::lock_guard<std::mutex> lock(mtx);
    response_queue.push(response);
}

bool ClientContext::hasResponses() const {
    std::lock_guard<std::mutex> lock(mtx);
    return !response_queue.empty();
}

HttpResponse ClientContext::popResponse() {
    std::lock_guard<std::mutex> lock(mtx);
    HttpResponse response = response_queue.front();
    response_queue.pop();
    return response;
}

void ClientContext::setWriteReady(bool ready) {
    std::lock_guard<std::mutex> lock(mtx);
    write_ready = ready;
}

bool ClientContext::isWriteReady() const {
    std::lock_guard<std::mutex> lock(mtx);
    return write_ready;
}

bool ClientContext::isActive() const {
    return active;
}

void ClientContext::deactivate() {
    active = false;
}

四、最后

具体代码可以看我的仓库。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值