Big optimizations
This commit is contained in:
parent
8523bcd5cd
commit
0f5ce5b524
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
build/server
|
58
.vscode/settings.json
vendored
58
.vscode/settings.json
vendored
@ -1,58 +0,0 @@
|
|||||||
{
|
|
||||||
"files.associations": {
|
|
||||||
"*.mllo.php": "blade",
|
|
||||||
"*.template": "blade",
|
|
||||||
"array": "cpp",
|
|
||||||
"atomic": "cpp",
|
|
||||||
"bit": "cpp",
|
|
||||||
"cctype": "cpp",
|
|
||||||
"clocale": "cpp",
|
|
||||||
"cmath": "cpp",
|
|
||||||
"compare": "cpp",
|
|
||||||
"concepts": "cpp",
|
|
||||||
"cstdarg": "cpp",
|
|
||||||
"cstddef": "cpp",
|
|
||||||
"cstdint": "cpp",
|
|
||||||
"cstdio": "cpp",
|
|
||||||
"cstdlib": "cpp",
|
|
||||||
"ctime": "cpp",
|
|
||||||
"cwchar": "cpp",
|
|
||||||
"cwctype": "cpp",
|
|
||||||
"deque": "cpp",
|
|
||||||
"string": "cpp",
|
|
||||||
"unordered_map": "cpp",
|
|
||||||
"vector": "cpp",
|
|
||||||
"exception": "cpp",
|
|
||||||
"algorithm": "cpp",
|
|
||||||
"functional": "cpp",
|
|
||||||
"iterator": "cpp",
|
|
||||||
"memory": "cpp",
|
|
||||||
"memory_resource": "cpp",
|
|
||||||
"numeric": "cpp",
|
|
||||||
"optional": "cpp",
|
|
||||||
"random": "cpp",
|
|
||||||
"ratio": "cpp",
|
|
||||||
"string_view": "cpp",
|
|
||||||
"system_error": "cpp",
|
|
||||||
"tuple": "cpp",
|
|
||||||
"type_traits": "cpp",
|
|
||||||
"utility": "cpp",
|
|
||||||
"initializer_list": "cpp",
|
|
||||||
"iosfwd": "cpp",
|
|
||||||
"iostream": "cpp",
|
|
||||||
"istream": "cpp",
|
|
||||||
"limits": "cpp",
|
|
||||||
"new": "cpp",
|
|
||||||
"numbers": "cpp",
|
|
||||||
"ostream": "cpp",
|
|
||||||
"semaphore": "cpp",
|
|
||||||
"span": "cpp",
|
|
||||||
"sstream": "cpp",
|
|
||||||
"stdexcept": "cpp",
|
|
||||||
"stop_token": "cpp",
|
|
||||||
"streambuf": "cpp",
|
|
||||||
"thread": "cpp",
|
|
||||||
"cinttypes": "cpp",
|
|
||||||
"typeinfo": "cpp"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,4 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
enum class HttpMethod : uint8_t {
|
enum class HttpMethod : uint8_t {
|
||||||
|
@ -1,8 +1,119 @@
|
|||||||
//
|
#pragma once
|
||||||
// Created by sky on 6/12/25.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef HTTP_RESPONSE_H
|
#include <string>
|
||||||
#define HTTP_RESPONSE_H
|
#include <string_view>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <array>
|
||||||
|
|
||||||
#endif //HTTP_RESPONSE_H
|
using std::string_view;
|
||||||
|
|
||||||
|
struct HttpResponse {
|
||||||
|
int status = 200;
|
||||||
|
std::string body;
|
||||||
|
std::string content_type = "text/plain";
|
||||||
|
std::unordered_map<std::string, std::string> headers;
|
||||||
|
|
||||||
|
void set_json(const std::string& json) {
|
||||||
|
body = json;
|
||||||
|
content_type = "application/json";
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_text(const std::string& text) {
|
||||||
|
body = text;
|
||||||
|
content_type = "text/plain";
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_html(const std::string& html) {
|
||||||
|
body = html;
|
||||||
|
content_type = "text/html";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class HttpResponseBuilder {
|
||||||
|
private:
|
||||||
|
static constexpr size_t BUFFER_SIZE = 4096;
|
||||||
|
static constexpr const char* STATUS_LINES[] = {
|
||||||
|
"HTTP/1.1 200 OK\r\n",
|
||||||
|
"HTTP/1.1 201 Created\r\n",
|
||||||
|
"HTTP/1.1 400 Bad Request\r\n",
|
||||||
|
"HTTP/1.1 404 Not Found\r\n",
|
||||||
|
"HTTP/1.1 500 Internal Server Error\r\n"
|
||||||
|
};
|
||||||
|
|
||||||
|
static int get_status_index(int status) {
|
||||||
|
switch (status) {
|
||||||
|
case 200: return 0;
|
||||||
|
case 201: return 1;
|
||||||
|
case 400: return 2;
|
||||||
|
case 404: return 3;
|
||||||
|
case 500: return 4;
|
||||||
|
default: return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
static std::string build_response(const HttpResponse& response, string_view version = "HTTP/1.1") {
|
||||||
|
std::string result;
|
||||||
|
result.reserve(BUFFER_SIZE);
|
||||||
|
|
||||||
|
// Status line
|
||||||
|
int status_idx = get_status_index(response.status);
|
||||||
|
if (status_idx >= 0) {
|
||||||
|
result += STATUS_LINES[status_idx];
|
||||||
|
} else {
|
||||||
|
result += version;
|
||||||
|
result += " ";
|
||||||
|
result += std::to_string(response.status);
|
||||||
|
result += " Unknown\r\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Content headers
|
||||||
|
result += "Content-Type: ";
|
||||||
|
result += response.content_type;
|
||||||
|
result += "\r\nContent-Length: ";
|
||||||
|
result += std::to_string(response.body.size());
|
||||||
|
result += "\r\n";
|
||||||
|
|
||||||
|
// Custom headers
|
||||||
|
for (const auto& [key, value] : response.headers) {
|
||||||
|
result += key;
|
||||||
|
result += ": ";
|
||||||
|
result += value;
|
||||||
|
result += "\r\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connection handling
|
||||||
|
bool keep_alive = version != "HTTP/1.0";
|
||||||
|
if (response.headers.find("Connection") == response.headers.end()) {
|
||||||
|
result += keep_alive ? "Connection: keep-alive\r\n" : "Connection: close\r\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
result += "\r\n";
|
||||||
|
result += response.body;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fast path for common responses
|
||||||
|
static std::string build_error_response(int status, string_view message, string_view version = "HTTP/1.1") {
|
||||||
|
std::string result;
|
||||||
|
result.reserve(256);
|
||||||
|
|
||||||
|
int status_idx = get_status_index(status);
|
||||||
|
if (status_idx >= 0) {
|
||||||
|
result += STATUS_LINES[status_idx];
|
||||||
|
} else {
|
||||||
|
result += version;
|
||||||
|
result += " ";
|
||||||
|
result += std::to_string(status);
|
||||||
|
result += " Error\r\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
result += "Content-Type: text/plain\r\nContent-Length: ";
|
||||||
|
result += std::to_string(message.size());
|
||||||
|
result += "\r\nConnection: close\r\n\r\n";
|
||||||
|
result += message;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
23
router.hpp
23
router.hpp
@ -1,6 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "http_common.hpp"
|
#include "http_common.hpp"
|
||||||
#include "http_parser.hpp"
|
#include "http_parser.hpp"
|
||||||
|
#include "http_response.hpp"
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@ -9,27 +11,6 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
using std::string_view;
|
using std::string_view;
|
||||||
struct HttpResponse {
|
|
||||||
int status = 200;
|
|
||||||
std::string body;
|
|
||||||
std::string content_type = "text/plain";
|
|
||||||
std::unordered_map<std::string, std::string> headers;
|
|
||||||
|
|
||||||
void set_json(const std::string& json) {
|
|
||||||
body = json;
|
|
||||||
content_type = "application/json";
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_text(const std::string& text) {
|
|
||||||
body = text;
|
|
||||||
content_type = "text/plain";
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_html(const std::string& html) {
|
|
||||||
body = html;
|
|
||||||
content_type = "text/html";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
using Handler = std::function<void(const HttpRequest&, HttpResponse&)>;
|
using Handler = std::function<void(const HttpRequest&, HttpResponse&)>;
|
||||||
|
|
||||||
|
124
server.hpp
124
server.hpp
@ -1,27 +1,64 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "sockets/epoll.hpp"
|
#include "epoll_socket.hpp"
|
||||||
#include "router.hpp"
|
#include "router.hpp"
|
||||||
#include "http_parser.hpp"
|
#include "http_parser.hpp"
|
||||||
|
#include "http_response.hpp"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
class HttpServer {
|
class HttpServer {
|
||||||
public:
|
public:
|
||||||
explicit HttpServer(uint16_t port, Router& router) : socket_(port), router_(router) {
|
explicit HttpServer(uint16_t port, Router& router) : port_(port), router_(router) {}
|
||||||
socket_.on_connection([this](int fd) { handle_connection(fd); });
|
|
||||||
socket_.on_data([this](int fd) { handle_data(fd); });
|
~HttpServer() {
|
||||||
socket_.on_disconnect([this](int fd) { handle_disconnect(fd); });
|
stop();
|
||||||
|
for (auto& worker : workers_) {
|
||||||
|
if (worker->thread.joinable()) {
|
||||||
|
worker->thread.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool start() { return socket_.start(); }
|
bool start() {
|
||||||
void run() { socket_.run(); }
|
unsigned int num_cores = std::thread::hardware_concurrency();
|
||||||
void stop() { socket_.stop(); }
|
if (num_cores == 0) num_cores = 1;
|
||||||
|
|
||||||
|
workers_.reserve(num_cores);
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < num_cores; ++i) {
|
||||||
|
auto worker = std::make_unique<Worker>(port_, router_);
|
||||||
|
if (!worker->socket.start()) return false;
|
||||||
|
workers_.push_back(std::move(worker));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& worker : workers_) {
|
||||||
|
worker->thread = std::thread([&worker]() { worker->socket.run(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void run() {
|
||||||
|
for (auto& worker : workers_) {
|
||||||
|
if (worker->thread.joinable()) {
|
||||||
|
worker->thread.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void stop() {
|
||||||
|
for (auto& worker : workers_) {
|
||||||
|
worker->socket.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Utility function to extract path parameters
|
|
||||||
static std::string get_path_param(string_view path, size_t segment_index = 0) {
|
static std::string get_path_param(string_view path, size_t segment_index = 0) {
|
||||||
size_t start = 0;
|
size_t start = 0;
|
||||||
size_t current_segment = 0;
|
size_t current_segment = 0;
|
||||||
@ -46,11 +83,19 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr int BUFFER_SIZE = 8192;
|
static constexpr int BUFFER_SIZE = 65536;
|
||||||
|
|
||||||
EpollSocket socket_;
|
struct Worker {
|
||||||
Router& router_;
|
EpollSocket socket;
|
||||||
std::array<char, BUFFER_SIZE> buffer_;
|
Router& router;
|
||||||
|
std::array<char, BUFFER_SIZE> buffer;
|
||||||
|
std::thread thread;
|
||||||
|
|
||||||
|
Worker(uint16_t port, Router& r) : socket(port), router(r) {
|
||||||
|
socket.on_connection([this](int fd) { handle_connection(fd); });
|
||||||
|
socket.on_data([this](int fd) { handle_data(fd); });
|
||||||
|
socket.on_disconnect([this](int fd) { handle_disconnect(fd); });
|
||||||
|
}
|
||||||
|
|
||||||
void handle_connection(int client_fd) {
|
void handle_connection(int client_fd) {
|
||||||
// Client connected
|
// Client connected
|
||||||
@ -58,7 +103,7 @@ private:
|
|||||||
|
|
||||||
void handle_data(int client_fd) {
|
void handle_data(int client_fd) {
|
||||||
while (true) {
|
while (true) {
|
||||||
ssize_t bytes_read = read(client_fd, buffer_.data(), buffer_.size());
|
ssize_t bytes_read = read(client_fd, buffer.data(), buffer.size());
|
||||||
|
|
||||||
if (bytes_read == -1) {
|
if (bytes_read == -1) {
|
||||||
if (errno == EAGAIN || errno == EWOULDBLOCK) break;
|
if (errno == EAGAIN || errno == EWOULDBLOCK) break;
|
||||||
@ -67,7 +112,7 @@ private:
|
|||||||
|
|
||||||
if (bytes_read == 0) return;
|
if (bytes_read == 0) return;
|
||||||
|
|
||||||
std::string request_data(buffer_.data(), bytes_read);
|
std::string request_data(buffer.data(), bytes_read);
|
||||||
if (request_data.find("\r\n\r\n") != std::string::npos) {
|
if (request_data.find("\r\n\r\n") != std::string::npos) {
|
||||||
process_request(client_fd, request_data);
|
process_request(client_fd, request_data);
|
||||||
}
|
}
|
||||||
@ -82,46 +127,23 @@ private:
|
|||||||
HttpRequest req = HttpParser::parse(request_data);
|
HttpRequest req = HttpParser::parse(request_data);
|
||||||
|
|
||||||
if (!req.valid) {
|
if (!req.valid) {
|
||||||
send_error_response(client_fd, "Bad Request", 400);
|
send_error_response(client_fd, "Bad Request", 400, req.version);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpResponse response;
|
HttpResponse response;
|
||||||
|
|
||||||
// Try to route the request (router will populate req.params)
|
if (router.handle(req, response)) {
|
||||||
if (router_.handle(req, response)) {
|
send_http_response(client_fd, response, req.version);
|
||||||
send_http_response(client_fd, response);
|
|
||||||
} else {
|
} else {
|
||||||
response.status = 404;
|
response.status = 404;
|
||||||
response.set_text("Not Found");
|
response.set_text("Not Found");
|
||||||
send_http_response(client_fd, response);
|
send_http_response(client_fd, response, req.version);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void send_http_response(int client_fd, const HttpResponse& response) {
|
void send_http_response(int client_fd, const HttpResponse& response, string_view version) {
|
||||||
std::string http_response = "HTTP/1.1 " + std::to_string(response.status);
|
std::string http_response = HttpResponseBuilder::build_response(response, version);
|
||||||
|
|
||||||
switch (response.status) {
|
|
||||||
case 200: http_response += " OK"; break;
|
|
||||||
case 201: http_response += " Created"; break;
|
|
||||||
case 400: http_response += " Bad Request"; break;
|
|
||||||
case 404: http_response += " Not Found"; break;
|
|
||||||
case 500: http_response += " Internal Server Error"; break;
|
|
||||||
default: http_response += " Unknown"; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
http_response += "\r\n";
|
|
||||||
http_response += "Content-Type: " + response.content_type + "\r\n";
|
|
||||||
http_response += "Content-Length: " + std::to_string(response.body.size()) + "\r\n";
|
|
||||||
|
|
||||||
// Add custom headers
|
|
||||||
for (const auto& [key, value] : response.headers) {
|
|
||||||
http_response += key + ": " + value + "\r\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
http_response += "Connection: keep-alive\r\n\r\n";
|
|
||||||
http_response += response.body;
|
|
||||||
|
|
||||||
send_raw_response(client_fd, http_response);
|
send_raw_response(client_fd, http_response);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,11 +161,13 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void send_error_response(int client_fd, const std::string& message, int status) {
|
void send_error_response(int client_fd, const std::string& message, int status, string_view version) {
|
||||||
HttpResponse response;
|
std::string response = HttpResponseBuilder::build_error_response(status, message, version);
|
||||||
response.status = status;
|
send_raw_response(client_fd, response);
|
||||||
response.set_text(message);
|
|
||||||
response.headers["Connection"] = "close";
|
|
||||||
send_http_response(client_fd, response);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
uint16_t port_;
|
||||||
|
Router& router_;
|
||||||
|
std::vector<std::unique_ptr<Worker>> workers_;
|
||||||
|
};
|
@ -1,182 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <liburing.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <netinet/in.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <functional>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
enum class OpType : uint8_t {
|
|
||||||
ACCEPT,
|
|
||||||
READ,
|
|
||||||
WRITE
|
|
||||||
};
|
|
||||||
|
|
||||||
struct IoData {
|
|
||||||
OpType type;
|
|
||||||
int fd;
|
|
||||||
char* buffer;
|
|
||||||
size_t size;
|
|
||||||
};
|
|
||||||
|
|
||||||
class IoUringSocket {
|
|
||||||
public:
|
|
||||||
using ConnectionHandler = std::function<void(int client_fd)>;
|
|
||||||
using DataHandler = std::function<void(int client_fd, const char* data, size_t len)>;
|
|
||||||
using DisconnectHandler = std::function<void(int client_fd)>;
|
|
||||||
|
|
||||||
explicit IoUringSocket(uint16_t port = 8080, int queue_depth = 256)
|
|
||||||
: port_(port), queue_depth_(queue_depth) {}
|
|
||||||
|
|
||||||
~IoUringSocket() {
|
|
||||||
if (server_fd_ != -1) close(server_fd_);
|
|
||||||
io_uring_queue_exit(&ring_);
|
|
||||||
for (auto& [fd, buffer] : buffers_) {
|
|
||||||
delete[] buffer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool start() {
|
|
||||||
if (!create_server_socket()) return false;
|
|
||||||
if (io_uring_queue_init(queue_depth_, &ring_, 0) < 0) return false;
|
|
||||||
submit_accept();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void run() {
|
|
||||||
while (running_) {
|
|
||||||
io_uring_cqe* cqe;
|
|
||||||
int ret = io_uring_wait_cqe(&ring_, &cqe);
|
|
||||||
if (ret < 0) break;
|
|
||||||
|
|
||||||
handle_completion(cqe);
|
|
||||||
io_uring_cqe_seen(&ring_, cqe);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void stop() { running_ = false; }
|
|
||||||
|
|
||||||
// Event handlers
|
|
||||||
void on_connection(ConnectionHandler handler) { on_connection_ = std::move(handler); }
|
|
||||||
void on_data(DataHandler handler) { on_data_ = std::move(handler); }
|
|
||||||
void on_disconnect(DisconnectHandler handler) { on_disconnect_ = std::move(handler); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
static constexpr int BUFFER_SIZE = 8192;
|
|
||||||
|
|
||||||
uint16_t port_;
|
|
||||||
int queue_depth_;
|
|
||||||
int server_fd_ = -1;
|
|
||||||
bool running_ = true;
|
|
||||||
io_uring ring_;
|
|
||||||
|
|
||||||
std::unordered_map<int, char*> buffers_;
|
|
||||||
ConnectionHandler on_connection_;
|
|
||||||
DataHandler on_data_;
|
|
||||||
DisconnectHandler on_disconnect_;
|
|
||||||
|
|
||||||
bool create_server_socket() {
|
|
||||||
server_fd_ = socket(AF_INET, SOCK_STREAM, 0);
|
|
||||||
if (server_fd_ == -1) return false;
|
|
||||||
|
|
||||||
int opt = 1;
|
|
||||||
if (setsockopt(server_fd_, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
sockaddr_in addr{};
|
|
||||||
addr.sin_family = AF_INET;
|
|
||||||
addr.sin_addr.s_addr = INADDR_ANY;
|
|
||||||
addr.sin_port = htons(port_);
|
|
||||||
|
|
||||||
if (bind(server_fd_, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) == -1) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return listen(server_fd_, SOMAXCONN) != -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void submit_accept() {
|
|
||||||
auto* data = new IoData{OpType::ACCEPT, server_fd_, nullptr, 0};
|
|
||||||
|
|
||||||
io_uring_sqe* sqe = io_uring_get_sqe(&ring_);
|
|
||||||
io_uring_prep_accept(sqe, server_fd_, nullptr, nullptr, 0);
|
|
||||||
io_uring_sqe_set_data(sqe, data);
|
|
||||||
io_uring_submit(&ring_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void submit_read(int client_fd) {
|
|
||||||
if (buffers_.find(client_fd) == buffers_.end()) {
|
|
||||||
buffers_[client_fd] = new char[BUFFER_SIZE];
|
|
||||||
}
|
|
||||||
|
|
||||||
auto* data = new IoData{OpType::READ, client_fd, buffers_[client_fd], BUFFER_SIZE};
|
|
||||||
|
|
||||||
io_uring_sqe* sqe = io_uring_get_sqe(&ring_);
|
|
||||||
io_uring_prep_read(sqe, client_fd, data->buffer, data->size, 0);
|
|
||||||
io_uring_sqe_set_data(sqe, data);
|
|
||||||
io_uring_submit(&ring_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void handle_completion(io_uring_cqe* cqe) {
|
|
||||||
auto* data = static_cast<IoData*>(io_uring_cqe_get_data(cqe));
|
|
||||||
if (!data) return;
|
|
||||||
|
|
||||||
switch (data->type) {
|
|
||||||
case OpType::ACCEPT:
|
|
||||||
handle_accept(cqe->res);
|
|
||||||
submit_accept(); // Continue accepting
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OpType::READ:
|
|
||||||
handle_read(data->fd, cqe->res, data->buffer);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OpType::WRITE:
|
|
||||||
// Write completed, nothing special needed
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
delete data;
|
|
||||||
}
|
|
||||||
|
|
||||||
void handle_accept(int result) {
|
|
||||||
if (result < 0) return;
|
|
||||||
|
|
||||||
int client_fd = result;
|
|
||||||
if (on_connection_) on_connection_(client_fd);
|
|
||||||
submit_read(client_fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
void handle_read(int client_fd, int result, char* buffer) {
|
|
||||||
if (result <= 0) {
|
|
||||||
if (on_disconnect_) on_disconnect_(client_fd);
|
|
||||||
cleanup_client(client_fd);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (on_data_) on_data_(client_fd, buffer, result);
|
|
||||||
submit_read(client_fd); // Continue reading
|
|
||||||
}
|
|
||||||
|
|
||||||
void cleanup_client(int client_fd) {
|
|
||||||
close(client_fd);
|
|
||||||
if (buffers_.find(client_fd) != buffers_.end()) {
|
|
||||||
delete[] buffers_[client_fd];
|
|
||||||
buffers_.erase(client_fd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
void write_async(int client_fd, const char* data, size_t len) {
|
|
||||||
auto* io_data = new IoData{OpType::WRITE, client_fd, nullptr, len};
|
|
||||||
|
|
||||||
io_uring_sqe* sqe = io_uring_get_sqe(&ring_);
|
|
||||||
io_uring_prep_write(sqe, client_fd, data, len, 0);
|
|
||||||
io_uring_sqe_set_data(sqe, io_data);
|
|
||||||
io_uring_submit(&ring_);
|
|
||||||
}
|
|
||||||
};
|
|
Loading…
x
Reference in New Issue
Block a user