diff --git a/.idea/editor.xml b/.idea/editor.xml
index 270a895..7366f17 100644
--- a/.idea/editor.xml
+++ b/.idea/editor.xml
@@ -1,6 +1,102 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -244,101 +340,244 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/cpp/uds_server/.gitignore b/examples/cpp/uds_server/.gitignore
new file mode 100644
index 0000000..3c34c2f
--- /dev/null
+++ b/examples/cpp/uds_server/.gitignore
@@ -0,0 +1 @@
+*build*
\ No newline at end of file
diff --git a/examples/cpp/uds_server/CMakeLists.txt b/examples/cpp/uds_server/CMakeLists.txt
new file mode 100644
index 0000000..1948c66
--- /dev/null
+++ b/examples/cpp/uds_server/CMakeLists.txt
@@ -0,0 +1,15 @@
+cmake_minimum_required(VERSION 3.11)
+
+project(netkit-example LANGUAGES CXX)
+set(CMAKE_CXX_STANDARD 23)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+set(CMAKE_CXX_EXTENSIONS OFF)
+set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
+
+find_package(netkit)
+
+add_executable(
+ netkit-example
+ main.cpp
+)
+target_link_libraries(netkit-example PRIVATE netkit::netkit)
diff --git a/examples/cpp/uds_server/main.cpp b/examples/cpp/uds_server/main.cpp
new file mode 100644
index 0000000..a206344
--- /dev/null
+++ b/examples/cpp/uds_server/main.cpp
@@ -0,0 +1,20 @@
+#include
+#include
+#include
+#include
+
+int main() {
+ netkit::sock::addr addr("/tmp/test.sock");
+ netkit::sock::sync_sock sock(addr, netkit::sock::type::unix);
+
+ sock.bind();
+ sock.listen();
+
+ while (true) {
+ auto rec = sock.accept();
+ auto buffer = rec->recv().data;
+ std::cout << buffer << "\n";
+ }
+
+ return 0;
+}
diff --git a/include/netkit-c/sock/sync_sock.h b/include/netkit-c/sock/sync_sock.h
index a4a4114..f982e18 100644
--- a/include/netkit-c/sock/sync_sock.h
+++ b/include/netkit-c/sock/sync_sock.h
@@ -27,6 +27,7 @@ NETKIT_C_API netkit_recv_result_t* netkit_recv_result_create(void);
NETKIT_C_API void netkit_recv_result_destroy(netkit_recv_result_t* recv_result);
NETKIT_C_API netkit_recv_status_t netkit_sync_sock_recv(netkit_sync_sock_t* sock, netkit_recv_result_t* out, int timeout_seconds, const char* match, size_t eof);
+NETKIT_C_API netkit_recv_status_t netkit_sync_sock_basic_recv(netkit_sync_sock_t* sock, netkit_recv_result_t* out);
NETKIT_C_API void netkit_sync_sock_close(netkit_sync_sock_t* sock);
NETKIT_C_API netkit_sock_addr_t* netkit_sync_sock_get_peer(netkit_sync_sock_t* sock);
diff --git a/include/netkit/http/request_handler.hpp b/include/netkit/http/request_handler.hpp
index 5af38d6..c95109f 100644
--- a/include/netkit/http/request_handler.hpp
+++ b/include/netkit/http/request_handler.hpp
@@ -139,306 +139,312 @@ namespace netkit::http::server {
return;
}
- if (settings.read_from_session_file == nullptr) {
- settings.read_from_session_file = default_read_from_session_file;
- }
- if (settings.write_to_session_file == nullptr) {
- settings.write_to_session_file = default_write_to_session_file;
- }
+ bool close = false;
- request req{};
- std::string raw{};
- std::string headers = client_sock->recv(5, "\r\n\r\n").data;
- const auto headers_vec = get_headers(headers);
- if (headers.empty()) {
- return;
- }
- raw += headers;
-
- bool is_chunked = false;
- std::size_t content_length = 0;
-
- auto status_line = get_status_line(headers);
- req.method = status_line.method;
-
- std::istringstream header_stream(headers);
- std::string line;
- while (std::getline(header_stream, line) && line != "\r") {
- if (line.starts_with("Transfer-Encoding:") && line.find("chunked") != std::string::npos) {
- is_chunked = true;
- } else if (line.starts_with("Content-Length:")) {
- try {
- content_length = std::stoul(line.substr(15));
- } catch (...) {
- break;
- }
- } else if (line.starts_with("Expect:") && line.find("100-continue") != std::string::npos) {
- client_sock->send("HTTP/1.1 100 Continue\r\n\r\n");
- } else if (line.starts_with("Expect:") && line.find("100-continue") == std::string::npos) {
- std::string response = "HTTP/1.1 417 Expectation Failed\r\n"
- "Content-Length: 0\r\n"
- "Connection: close\r\n"
- "\r\n";
-
- client_sock->send(response);
- return;
- } else if (line.starts_with("Upgrade:") && line.find("websocket") != std::string::npos) {
- std::string response = "HTTP/1.1 426 Upgrade Required\r\n"
- "Content-Length: 0\r\n"
- "Connection: close\r\n"
- "\r\n";
-
- client_sock->send(response);
- return;
- } else if (line.starts_with("Connection:") && line.find("close") != std::string::npos) {
- std::string response = "HTTP/1.1 200 OK\r\n"
- "Content-Length: 0\r\n"
- "Connection: close\r\n"
- "\r\n";
+ while (!close) {
+ if (settings.read_from_session_file == nullptr) {
+ settings.read_from_session_file = default_read_from_session_file;
+ }
+ if (settings.write_to_session_file == nullptr) {
+ settings.write_to_session_file = default_write_to_session_file;
+ }
- client_sock->send(response);
+ request req{};
+ std::string raw{};
+ std::string headers = client_sock->recv(5, "\r\n\r\n").data;
+ const auto headers_vec = get_headers(headers);
+ if (headers.empty()) {
return;
}
- }
+ raw += headers;
+
+ bool is_chunked = false;
+ std::size_t content_length = 0;
+
+ auto status_line = get_status_line(headers);
+ req.method = status_line.method;
+
+ std::istringstream header_stream(headers);
+ std::string line;
+ while (std::getline(header_stream, line) && line != "\r") {
+ if (line.starts_with("Transfer-Encoding:") && line.find("chunked") != std::string::npos) {
+ is_chunked = true;
+ } else if (line.starts_with("Content-Length:")) {
+ try {
+ content_length = std::stoul(line.substr(15));
+ } catch (...) {
+ break;
+ }
+ } else if (line.starts_with("Expect:") && line.find("100-continue") != std::string::npos) {
+ client_sock->send("HTTP/1.1 100 Continue\r\n\r\n");
+ } else if (line.starts_with("Expect:") && line.find("100-continue") == std::string::npos) {
+ std::string response = "HTTP/1.1 417 Expectation Failed\r\n"
+ "Content-Length: 0\r\n"
+ "Connection: close\r\n"
+ "\r\n";
+
+ client_sock->send(response);
+ return;
+ } else if (line.starts_with("Upgrade:") && line.find("websocket") != std::string::npos) {
+ std::string response = "HTTP/1.1 426 Upgrade Required\r\n"
+ "Content-Length: 0\r\n"
+ "Connection: close\r\n"
+ "\r\n";
+
+ client_sock->send(response);
+ return;
+ } else if (line.starts_with("Connection:") && line.find("close") != std::string::npos) {
+ close = true;
+ }
+ }
- if (is_chunked && (req.method == "POST" || req.method == "PUT" || req.method == "PATCH" || req.method == "DELETE")) {
- std::string chunked = client_sock->overflow_bytes();
- client_sock->clear_overflow_bytes();
+ if (is_chunked && (req.method == "POST" || req.method == "PUT" || req.method == "PATCH" || req.method == "DELETE")) {
+ std::string chunked = client_sock->overflow_bytes();
+ client_sock->clear_overflow_bytes();
- while (chunked.find("0\r\n\r\n") == std::string::npos) {
- auto res = client_sock->recv(5, "", 0); // no eof
- if (res.status == sock::recv_status::closed) break;
- if (res.status == sock::recv_status::timeout) throw socket_error("recv timeout");
- if (res.data.empty()) continue;
- chunked += res.data;
- }
+ while (chunked.find("0\r\n\r\n") == std::string::npos) {
+ auto res = client_sock->recv(5, "", 0); // no eof
+ if (res.status == sock::recv_status::closed) break;
+ if (res.status == sock::recv_status::timeout) close = true;
+ if (res.data.empty()) continue;
+ chunked += res.data;
+ }
- std::string decoded = utility::decode_chunked(chunked);
- raw = headers + decoded;
- req.raw_body = raw;
- req.body = decoded;
- } else if (req.method == "POST" || req.method == "PUT" || req.method == "PATCH" || req.method == "DELETE") {
- std::string body = client_sock->overflow_bytes();
- client_sock->clear_overflow_bytes();
-
- while (body.size() < content_length) {
- auto res = client_sock->recv(30, "", 0);
- if (res.status == sock::recv_status::closed) break;
- if (res.status == sock::recv_status::timeout) throw socket_error("recv timeout");
- if (res.data.empty()) continue;
- body += res.data;
- }
+ std::string decoded = utility::decode_chunked(chunked);
+ raw = headers + decoded;
+ req.raw_body = raw;
+ req.body = decoded;
+ } else if (req.method == "POST" || req.method == "PUT" || req.method == "PATCH" || req.method == "DELETE") {
+ std::string body = client_sock->overflow_bytes();
+ client_sock->clear_overflow_bytes();
+
+ while (body.size() < content_length) {
+ auto res = client_sock->recv(5, "", 0);
+ if (res.status == sock::recv_status::closed) break;
+ if (res.status == sock::recv_status::timeout) close = true;
+ if (res.data.empty()) continue;
+ body += res.data;
+ }
- req.body = body;
- req.raw_body = headers + body;
- } else {
- req.raw_body = headers;
- }
+ req.body = body;
+ req.raw_body = headers + body;
+ } else {
+ req.raw_body = headers;
+ }
- if (req.raw_body.empty() || req.raw_body.size() > settings.max_request_size) {
- return;
- }
+ if (req.raw_body.empty() || req.raw_body.size() > settings.max_request_size) {
+ return;
+ }
- req.ip_address = [&]() -> std::string {
- if (settings.trust_x_forwarded_for) {
- for (const auto& it : headers_vec) {
- if (it.first == "X-Forwarded-For") {
- auto ips = netkit::utility::split(it.second, ",");
- for (const auto& ip : ips) {
- if (netkit::network::is_ipv4(ip) || netkit::network::is_ipv6(ip)) {
- return ip;
+ req.ip_address = [&]() -> std::string {
+ if (settings.trust_x_forwarded_for) {
+ for (const auto& it : headers_vec) {
+ if (it.first == "X-Forwarded-For") {
+ auto ips = netkit::utility::split(it.second, ",");
+ for (const auto& ip : ips) {
+ if (netkit::network::is_ipv4(ip) || netkit::network::is_ipv6(ip)) {
+ return ip;
+ }
}
}
}
}
- }
- return {};
- }();
+ return {};
+ }();
- if (req.ip_address.empty()) {
- req.ip_address = client_sock->get_peer().get_ip();
- }
+ if (req.ip_address.empty()) {
+ req.ip_address = client_sock->get_peer().get_ip();
+ }
- if (!netkit::network::is_ipv4(req.ip_address) && !netkit::network::is_ipv6(req.ip_address)) {
- throw parsing_error("invalid IP address: " + req.ip_address);
- }
+ if (!netkit::network::is_ipv4(req.ip_address) && !netkit::network::is_ipv6(req.ip_address)) {
+ throw parsing_error("invalid IP address: " + req.ip_address);
+ }
- if (std::ranges::find(settings.blacklisted_ips, req.ip_address) != settings.blacklisted_ips.end()) {
- return;
- }
+ if (std::ranges::find(settings.blacklisted_ips, req.ip_address) != settings.blacklisted_ips.end()) {
+ return;
+ }
- req.version = [&]() {
- if (status_line.http_version == "HTTP/1.0") {
- return 10;
- } else if (status_line.http_version == "HTTP/1.1") {
- return 11;
+ req.version = [&]() {
+ if (status_line.http_version == "HTTP/1.0") {
+ return 10;
+ } else if (status_line.http_version == "HTTP/1.1") {
+ return 11;
+ } else {
+ throw parsing_error("unsupported HTTP version: " + status_line.http_version);
+ }
+ }();
+ auto full_path = status_line.path;
+ if (full_path.empty() || full_path[0] != '/') {
+ throw parsing_error("invalid path: " + full_path);
+ }
+ auto query_pos = full_path.find('?');
+ if (query_pos != std::string::npos) {
+ req.endpoint = full_path.substr(0, query_pos);
+ auto query_str = full_path.substr(query_pos + 1);
+ req.query = netkit::utility::parse_fields(query_str);
} else {
- throw parsing_error("unsupported HTTP version: " + status_line.http_version);
+ req.endpoint = full_path;
}
- }();
- auto full_path = status_line.path;
- if (full_path.empty() || full_path[0] != '/') {
- throw parsing_error("invalid path: " + full_path);
- }
- auto query_pos = full_path.find('?');
- if (query_pos != std::string::npos) {
- req.endpoint = full_path.substr(0, query_pos);
- auto query_str = full_path.substr(query_pos + 1);
- req.query = netkit::utility::parse_fields(query_str);
- } else {
- req.endpoint = full_path;
- }
- req.fields = netkit::utility::parse_fields(req.body);
- for (const auto& it : headers_vec) {
- if (it.first == "Content-Type") {
- req.content_type = it.second;
- } else if (it.first == "User-Agent") {
- req.user_agent = it.second;
- } else if (it.first == "Cookie") {
- req.cookies = get_cookies_from_request(it.second);
+ req.fields = netkit::utility::parse_fields(req.body);
+ for (const auto& it : headers_vec) {
+ if (it.first == "Content-Type") {
+ req.content_type = it.second;
+ } else if (it.first == "User-Agent") {
+ req.user_agent = it.second;
+ } else if (it.first == "Cookie") {
+ req.cookies = get_cookies_from_request(it.second);
+ }
}
- }
- std::string session_id{};
- bool session_id_found = false;
- for (const auto& it : req.cookies) {
- if (it.name == settings.session_cookie_name && !it.value.empty() && settings.enable_session) {
- session_id = it.value;
- session_id_found = true;
- break;
+ std::string session_id{};
+ bool session_id_found = false;
+ for (const auto& it : req.cookies) {
+ if (it.name == settings.session_cookie_name && !it.value.empty() && settings.enable_session) {
+ session_id = it.value;
+ session_id_found = true;
+ break;
+ }
}
- }
- bool erase_associated = false;
- if (session_id_found) {
- std::erase(session_id, '/');
- std::filesystem::path session_file = settings.session_directory + "/session_" + session_id + ".txt";
- req.session = settings.read_from_session_file(session_file.string());
- req.session_id = session_id;
+ bool erase_associated = false;
+ if (session_id_found) {
+ std::erase(session_id, '/');
+ std::filesystem::path session_file = settings.session_directory + "/session_" + session_id + ".txt";
+ req.session = settings.read_from_session_file(session_file.string());
+ req.session_id = session_id;
- if (!std::filesystem::exists(session_file)) {
- erase_associated = true;
- // remove associated session cookies and session cookie from request
- for (const auto& it : settings.associated_session_cookies) {
+ if (!std::filesystem::exists(session_file)) {
+ erase_associated = true;
+ // remove associated session cookies and session cookie from request
+ for (const auto& it : settings.associated_session_cookies) {
+ req.cookies.erase(
+ std::remove_if(req.cookies.begin(), req.cookies.end(),
+ [&it](const cookie& cookie) {
+ return cookie.name == it;
+ }),
+ req.cookies.end()
+ );
+ }
req.cookies.erase(
std::remove_if(req.cookies.begin(), req.cookies.end(),
- [&it](const cookie& cookie) {
- return cookie.name == it;
+ [this, &settings](const cookie& cookie) {
+ return cookie.name == settings.session_cookie_name;
}),
req.cookies.end()
);
+
+ req.session.clear();
+ req.session_id.clear();
+ } else {
+ req.session = settings.read_from_session_file(session_file.string());
+ req.session_id = session_id;
}
- req.cookies.erase(
- std::remove_if(req.cookies.begin(), req.cookies.end(),
- [this, &settings](const cookie& cookie) {
- return cookie.name == settings.session_cookie_name;
- }),
- req.cookies.end()
- );
-
- req.session.clear();
- req.session_id.clear();
- } else {
- req.session = settings.read_from_session_file(session_file.string());
- req.session_id = session_id;
}
- }
- auto response = callback(req);
- std::stringstream net_response;
- net_response << "HTTP/1.1 " << response.http_status << " " << netkit::http::get_message(response.http_status).value_or("Unknown") << "\r\n";
- if (!response.content_type.empty()) net_response << "Content-Type: " << response.content_type << "\r\n";
- if (!response.allow_origin.empty()) net_response << "Access-Control-Allow-Origin: " << response.allow_origin << "\r\n";
- if (!response.location.empty()) {
- net_response << "Location: " << response.location << "\r\n";
- }
- if (!response.headers.empty()) {
- for (const auto& it : response.headers) {
- net_response << it.name << ": " << it.data << "\r\n";
+ auto response = callback(req);
+ std::stringstream net_response;
+ net_response << "HTTP/1.1 " << response.http_status << " " << netkit::http::get_message(response.http_status).value_or("Unknown") << "\r\n";
+ if (!response.content_type.empty()) net_response << "Content-Type: " << response.content_type << "\r\n";
+ if (!response.allow_origin.empty()) net_response << "Access-Control-Allow-Origin: " << response.allow_origin << "\r\n";
+ if (!response.location.empty()) {
+ net_response << "Location: " << response.location << "\r\n";
+ }
+ if (!response.headers.empty()) {
+ for (const auto& it : response.headers) {
+ net_response << it.name << ": " << it.data << "\r\n";
+ }
+ }
+ if (response.redirection == redirect_type::temporary) {
+ net_response << "Cache-Control: no-cache\r\n";
+ } else if (response.redirection == redirect_type::permanent) {
+ net_response << "Cache-Control: no-store\r\n";
}
- }
- if (response.redirection == redirect_type::temporary) {
- net_response << "Cache-Control: no-cache\r\n";
- } else if (response.redirection == redirect_type::permanent) {
- net_response << "Cache-Control: no-store\r\n";
- }
- if (!session_id_found && settings.enable_session) {
- session_id = utility::generate_random_string();
- response.cookies.push_back({.name = settings.session_cookie_name, .value = session_id, .expires = 0, .path = "/", .same_site = "Strict", .http_only = true, .secure = settings.session_is_secure});
- } else if (settings.enable_session) {
- std::string session_file = settings.session_directory + "/session_" + session_id + ".txt";
- std::unordered_map stored = settings.read_from_session_file(session_file);
+ if (!session_id_found && settings.enable_session) {
+ session_id = utility::generate_random_string();
+ response.cookies.push_back({.name = settings.session_cookie_name, .value = session_id, .expires = 0, .path = "/", .same_site = "Strict", .http_only = true, .secure = settings.session_is_secure});
+ } else if (settings.enable_session) {
+ std::string session_file = settings.session_directory + "/session_" + session_id + ".txt";
+ std::unordered_map stored = settings.read_from_session_file(session_file);
- for (const auto& it : response.session) {
- stored[it.first] = it.second;
+ for (const auto& it : response.session) {
+ stored[it.first] = it.second;
+ }
+
+ settings.write_to_session_file(session_file, stored);
}
- settings.write_to_session_file(session_file, stored);
- }
+ for (const auto& it : response.cookies) {
+ std::string cookie_str = it.name + "=" + it.value + "; ";
+ if (it.expires != 0) {
+ cookie_str += "Expires=" + utility::convert_unix_millis_to_gmt(it.expires) + "; ";
+ } else {
+ cookie_str += "Expires=session; ";
+ }
+ if (it.http_only) {
+ cookie_str += "HttpOnly; ";
+ }
+ if (it.secure) {
+ cookie_str += "Secure; ";
+ }
+ if (!it.path.empty()) {
+ cookie_str += "Path=" + it.path + "; ";
+ }
+ if (!it.domain.empty()) {
+ cookie_str += "Domain=" + it.domain + "; ";
+ }
+ if (!it.same_site.empty() && (it.same_site == "Strict" || it.same_site == "Lax" || it.same_site == "None")) {
+ cookie_str += "SameSite=" + it.same_site + "; ";
+ }
+ for (const auto& attribute : it.attributes) {
+ cookie_str += attribute + "; ";
+ }
+ for (const auto& attribute : it.extra_attributes) {
+ cookie_str += attribute.first + "=" + attribute.second + "; ";
+ }
- for (const auto& it : response.cookies) {
- std::string cookie_str = it.name + "=" + it.value + "; ";
- if (it.expires != 0) {
- cookie_str += "Expires=" + utility::convert_unix_millis_to_gmt(it.expires) + "; ";
- } else {
- cookie_str += "Expires=session; ";
- }
- if (it.http_only) {
- cookie_str += "HttpOnly; ";
+ net_response << "Set-Cookie: " << cookie_str << "\r\n";
}
- if (it.secure) {
- cookie_str += "Secure; ";
- }
- if (!it.path.empty()) {
- cookie_str += "Path=" + it.path + "; ";
- }
- if (!it.domain.empty()) {
- cookie_str += "Domain=" + it.domain + "; ";
- }
- if (!it.same_site.empty() && (it.same_site == "Strict" || it.same_site == "Lax" || it.same_site == "None")) {
- cookie_str += "SameSite=" + it.same_site + "; ";
- }
- for (const auto& attribute : it.attributes) {
- cookie_str += attribute + "; ";
- }
- for (const auto& attribute : it.extra_attributes) {
- cookie_str += attribute.first + "=" + attribute.second + "; ";
+
+ if (erase_associated) {
+ for (const auto& it : settings.associated_session_cookies) {
+ response.delete_cookies.push_back(it);
+ }
}
- net_response << "Set-Cookie: " << cookie_str << "\r\n";
- }
+ for (const auto& it : response.delete_cookies) {
+ std::string cookie_str = it + "=; Expires=Thu, 01 Jan 1970 00:00:00 GMT; ";
+ net_response << "Set-Cookie: " << cookie_str << "\r\n";
+ }
- if (erase_associated) {
- for (const auto& it : settings.associated_session_cookies) {
- response.delete_cookies.push_back(it);
+ if (response.stop) {
+ return;
}
- }
- for (const auto& it : response.delete_cookies) {
- std::string cookie_str = it + "=; Expires=Thu, 01 Jan 1970 00:00:00 GMT; ";
- net_response << "Set-Cookie: " << cookie_str << "\r\n";
- }
+ for (const auto& it : response.headers) {
+ if (it.name == "Content-Length") {
+ continue;
+ }
+ net_response << it.name << ": " << it.data << "\r\n";
+ }
- if (response.stop) {
- return;
- }
+ if (close) {
+ net_response << "Connection: close\r\n";
+ }
- for (const auto& it : response.headers) {
- net_response << it.name << ": " << it.data << "\r\n";
- }
+ if (!response.body.empty()) {
+ net_response << "Content-Length: " << response.body.size() << "\r\n";
+ } else {
+ net_response << "Content-Length: 0\r\n";
+ }
- net_response << "Connection: close\r\n";
+ net_response << "\r\n";
+ net_response << response.body;
- if (!response.body.empty()) {
- net_response << "Content-Length: " << response.body.size() << "\r\n";
+ client_sock->send(net_response.str());
}
- net_response << "\r\n";
- net_response << response.body;
-
- client_sock->send(net_response.str());
client_sock->close();
}
};
diff --git a/include/netkit/sock/basic_sync_sock.hpp b/include/netkit/sock/basic_sync_sock.hpp
index 34fec54..7dae063 100644
--- a/include/netkit/sock/basic_sync_sock.hpp
+++ b/include/netkit/sock/basic_sync_sock.hpp
@@ -37,7 +37,7 @@ namespace netkit::sock {
[[nodiscard]] virtual recv_result recv(int timeout_seconds, const std::string& match) = 0;
[[nodiscard]] virtual recv_result recv(int timeout_seconds, const std::string& match, size_t eof) = 0;
[[nodiscard]] virtual recv_result recv(int timeout_seconds, size_t eof) = 0;
- [[nodiscard]] virtual recv_result primitive_recv() = 0;
+ [[nodiscard]] virtual recv_result recv() = 0;
[[nodiscard]] virtual std::string overflow_bytes() const = 0;
virtual addr& get_addr() = 0;
[[nodiscard]] virtual const addr& get_addr() const = 0;
diff --git a/include/netkit/sock/sync_sock.hpp b/include/netkit/sock/sync_sock.hpp
index c29c5e9..bb99e75 100644
--- a/include/netkit/sock/sync_sock.hpp
+++ b/include/netkit/sock/sync_sock.hpp
@@ -124,7 +124,12 @@ namespace netkit::sock {
* @return The received data as a sock_recv_result object.
*/
[[nodiscard]] recv_result recv(int timeout_seconds, const std::string& match, size_t eof) override;
- [[nodiscard]] recv_result primitive_recv() override;
+ /**
+ * @brief Receive data from the server.
+ * @note This is a recv() implementation that behaves like a Unix recv() call would.
+ * @return The received data as a sock_recv_result object.
+ */
+ [[nodiscard]] recv_result recv() override;
/* @brief Receive data from the server.
* @param timeout_seconds The timeout in seconds (-1 means wait indefinitely).
diff --git a/src/c/sock/sync_sock.cpp b/src/c/sock/sync_sock.cpp
index ef858da..59b0b29 100644
--- a/src/c/sock/sync_sock.cpp
+++ b/src/c/sock/sync_sock.cpp
@@ -304,6 +304,38 @@ extern "C" NETKIT_C_API netkit_recv_status_t netkit_sync_sock_recv(netkit_sync_s
}
}
+extern "C" NETKIT_C_API netkit_recv_status_t netkit_sync_sock_basic_recv(netkit_sync_sock_t* sock, netkit_recv_result_t* out) {
+ if (!sock || !out) {
+ return RECV_ERROR;
+ }
+
+ try {
+ auto ret = sock->impl->recv();
+ delete[] out->data;
+
+ out->size = ret.data.size();
+ out->data = new char[out->size + 1];
+
+ std::memcpy(out->data, ret.data.data(), out->size);
+ out->data[out->size] = '\0';
+
+ switch (ret.status) {
+ case netkit::sock::recv_status::success:
+ return RECV_SUCCESS;
+ case netkit::sock::recv_status::timeout:
+ return RECV_TIMEOUT;
+ case netkit::sock::recv_status::closed:
+ return RECV_CLOSED;
+ default:
+ return RECV_ERROR;
+ }
+ } catch (const std::exception& e) {
+ return RECV_ERROR;
+ } catch (...) {
+ return RECV_ERROR;
+ }
+}
+
extern "C" NETKIT_C_API void netkit_sync_sock_close(netkit_sync_sock_t* sock) {
if (!sock) {
return;
diff --git a/src/sock/openssl/ssl_sync_sock_openssl.cpp b/src/sock/openssl/ssl_sync_sock_openssl.cpp
index 93291bb..36f7bc9 100644
--- a/src/sock/openssl/ssl_sync_sock_openssl.cpp
+++ b/src/sock/openssl/ssl_sync_sock_openssl.cpp
@@ -345,7 +345,7 @@ void netkit::sock::ssl_sync_sock::drain_write_bio() const {
}
void netkit::sock::ssl_sync_sock::feed_read_bio_blocking() const {
- auto res = underlying_sock_->primitive_recv();
+ auto res = underlying_sock_->recv();
if (res.status == sock::recv_status::closed) {
if (!handshake_complete_) {
diff --git a/src/sock/sock_peer.cpp b/src/sock/sock_peer.cpp
index 9fe3f57..293a9b3 100644
--- a/src/sock/sock_peer.cpp
+++ b/src/sock/sock_peer.cpp
@@ -39,9 +39,9 @@ netkit::sock::addr netkit::sock::get_peer(fd_t sockfd) {
inet_ntop(AF_INET, &(addr_in->sin_addr), ip_str, sizeof(ip_str));
port = ntohs(addr_in->sin_port);
} else if (addr_storage.ss_family == AF_INET6) {
- auto* addr_in6 = reinterpret_cast(&addr_storage);
- inet_ntop(AF_INET6, &(addr_in6->sin6_addr), ip_str, sizeof(ip_str));
- port = ntohs(addr_in6->sin6_port);
+ auto* addr_in6 = reinterpret_cast(&addr_storage);
+ inet_ntop(AF_INET6, &(addr_in6->sin6_addr), ip_str, sizeof(ip_str));
+ port = ntohs(addr_in6->sin6_port);
} else {
throw ip_error("unsupported address family");
}
diff --git a/src/sock/sync_sock.cpp b/src/sock/sync_sock.cpp
index f1e4103..3f2bf35 100644
--- a/src/sock/sync_sock.cpp
+++ b/src/sock/sync_sock.cpp
@@ -180,9 +180,11 @@ void netkit::sock::sync_sock::set_sock_opts(opt opts) const {
netkit::sock::sync_sock::sync_sock(const sock::addr& addr, sock::type t, opt opts) : addr_(addr), type_(t) {
this->sockfd = -1;
- if (addr.get_ip().empty() && !addr.is_file_path()) {
- throw socket_error("IP address/file path is empty");
- }
+ if (!addr.is_file_path()) {
+ if (addr.get_ip().empty()) {
+ throw socket_error("IP address/file path is empty");
+ }
+ }
if (t != type::unix) {
this->sockfd = ::socket(addr.is_ipv6() ? AF_INET6 : AF_INET,
@@ -363,6 +365,10 @@ std::unique_ptr netkit::sock::sync_sock::accept()
throw socket_error("failed to accept connection: " + std::string(strerror(errno)));
}
+ if (this->type_ == type::unix) {
+ return std::make_unique(client_sockfd, sock::addr(reinterpret_cast(&client_addr)->sun_path), this->type_);
+ }
+
auto peer = sock::get_peer(client_sockfd);
return std::make_unique(client_sockfd, peer, this->type_);
@@ -514,7 +520,7 @@ netkit::sock::recv_result netkit::sock::sync_sock::recv(const int timeout_second
}
}
-netkit::sock::recv_result netkit::sock::sync_sock::primitive_recv() {
+netkit::sock::recv_result netkit::sock::sync_sock::recv() {
for (;;) {
char buf[8192];
ssize_t n = ::recv(this->sockfd, buf, sizeof(buf), 0);
@@ -619,7 +625,7 @@ netkit::sock::recv_result netkit::sock::sync_sock::recv(const int timeout_second
}
}
-netkit::sock::recv_result netkit::sock::sync_sock::primitive_recv() {
+netkit::sock::recv_result netkit::sock::sync_sock::recv() {
constexpr size_t buffer_size = 8192;
char buf[buffer_size];
@@ -679,4 +685,4 @@ void netkit::sock::sync_sock::close() {
#endif
[[nodiscard]] netkit::sock::addr netkit::sock::sync_sock::get_peer() const {
return netkit::sock::get_peer(this->sockfd);
-}
\ No newline at end of file
+}
diff --git a/tests/test.cpp b/tests/test.cpp
index 0d72619..f54513f 100644
--- a/tests/test.cpp
+++ b/tests/test.cpp
@@ -5,6 +5,7 @@
#include
#include
#include
+#include
TEST_CASE("Ensure addr works", "[sock_addr]") {
netkit::sock::addr addr("google.com", 443, netkit::sock::addr_type::hostname);
@@ -128,4 +129,47 @@ TEST_CASE("Ensure general utility functions work", "[utility]") {
REQUIRE(tokens[4] == "five");
std::string joined = netkit::utility::join(tokens, ",");
REQUIRE(joined == to_split);
+}
+
+TEST_CASE("Ensure basic sockets work", "[socket]") {
+ std::thread t([]() {
+ netkit::sock::addr addr("127.0.0.1", 1337, netkit::sock::addr_type::ipv4);
+ netkit::sock::sync_sock sock(addr, netkit::sock::type::tcp);
+
+ sock.bind();
+ sock.listen();
+
+ while (true) {
+ auto result = sock.recv(-1);
+ if (result.status != netkit::sock::recv_status::success) {
+ return;
+ }
+
+ if (result.status == netkit::sock::recv_status::success) {
+ std::string data = result.data;
+ REQUIRE(data == "Hello, World!");
+ sock.send("Hello, World!");
+ }
+ }
+
+ sock.unbind();
+ });
+ t.detach();
+
+ std::this_thread::sleep_for(std::chrono::milliseconds(1000));
+
+ netkit::sock::addr addr("127.0.0.1", 1337, netkit::sock::addr_type::ipv4);
+ netkit::sock::sync_sock sock(addr, netkit::sock::type::tcp);
+
+ sock.connect();
+ sock.send("Hello, World!");
+ auto result = sock.recv(-1);
+ if (result.status == netkit::sock::recv_status::success) {
+ std::string data = result.data;
+ REQUIRE(data == "Hello, World!");
+ } else {
+ REQUIRE(false); // fail the test if we didn't receive a response
+ }
+
+ t.join();
}
\ No newline at end of file