Переход на edge-triggered; устранение жора CPU

This commit is contained in:
lost+skunk 2025-02-08 21:13:13 +03:00
parent ca1662c403
commit 06f9e483c3
2 changed files with 51 additions and 33 deletions

View File

@ -27,6 +27,15 @@ struct Server {
private shared int epfd, sock; private shared int epfd, sock;
void start(MD...)() { // TODO: реализовать "слушальщика" хрюникс сокетов (AF_UNIX) void start(MD...)() { // TODO: реализовать "слушальщика" хрюникс сокетов (AF_UNIX)
// import core.stdc.signal;
// extern(C) void z (int s) nothrow @nogc @system {
// signal(s, SIG_IGN);
// this.epfd.close();
// this.sock.close();
// raise(0);
// }
// signal(SIGINT, &z);
bool ipv6; bool ipv6;
for (short i; i < address.length; ++i) for (short i; i < address.length; ++i)
if (address[i] == ':') {ipv6=true;break;} if (address[i] == ':') {ipv6=true;break;}
@ -47,15 +56,22 @@ struct Server {
err(bind(sock, cast(sockaddr*)&sockt, sockt.sizeof), "bind"); err(bind(sock, cast(sockaddr*)&sockt, sockt.sizeof), "bind");
} }
int hz;
setsockopt(sock, setsockopt(sock,
IPPROTO_TCP, TCP_NODELAY | SO_REUSEADDR | SO_REUSEPORT, IPPROTO_TCP, SO_REUSEADDR,
cast(void*)(new int), int.sizeof); cast(void*)hz, int.sizeof);
setsockopt(sock,
IPPROTO_TCP, TCP_NODELAY,
cast(void*)hz, int.sizeof);
// setsockopt(sock,
// IPPROTO_TCP, SO_REUSEPORT,
// cast(void*)hz, int.sizeof);
err(listen(sock, 512), "listen"); err(listen(sock, 512), "listen");
serve!MD; serve!MD;
} }
void stop() { void stop() @nogc {
epfd.close(); epfd.close();
sock.close(); sock.close();
} }
@ -66,7 +82,7 @@ struct Server {
epoll_event[MAX_EVENTS] evts; epoll_event[MAX_EVENTS] evts;
ev.data.fd = sock; ev.data.fd = sock;
ev.events = EPOLLIN | EPOLLEXCLUSIVE; ev.events = EPOLLIN;
epoll_ctl(epfd, EPOLL_CTL_ADD, sock, &ev); epoll_ctl(epfd, EPOLL_CTL_ADD, sock, &ev);
for (;;) { for (;;) {
@ -77,23 +93,35 @@ struct Server {
// sockaddr_in addr; // sockaddr_in addr;
// socklen_t al = sockaddr_in.sizeof; // socklen_t al = sockaddr_in.sizeof;
// ev.data.fd = accept4(sock, cast(sockaddr*)&addr, &al, SOCK_NONBLOCK); // ev.data.fd = accept4(sock, cast(sockaddr*)&addr, &al, SOCK_NONBLOCK);
ev.events = EPOLLIN | EPOLLRDHUP | EPOLLET;
ev.data.fd = accept4(sock, null, null, SOCK_NONBLOCK); ev.data.fd = accept4(sock, null, null, SOCK_NONBLOCK);
epoll_ctl(epfd, EPOLL_CTL_ADD, ev.data.fd, &ev); epoll_ctl(epfd, EPOLL_CTL_ADD, ev.data.fd, &ev);
// ip = cast(string)inet_ntoa(addr.sin_addr); // ip = cast(string)inet_ntoa(addr.sin_addr);
} }
// if (ev.data.fd == -1) { if (evts[i].events & EPOLLRDHUP) {
epoll_ctl(epfd, EPOLL_CTL_DEL, fd, null);
close(fd);
break;
}
rd: rd:
ubyte[128] buf; ubyte[256] buf;
for (;;) { // обработчик запросов for (;;) { // обработчик запросов
auto rd = recv(fd, cast(void*)buf, buf.sizeof, 0); auto rd = recv(fd, cast(void*)buf, buf.sizeof, 0);
if (rd < 1) break; if (rd < 1) break;
auto rqst = parseReq(buf); auto rqst = parseReq(buf);
if (parseAndValidateURL(rqst.path, &rqst)) {
static auto resp = "HTTP/1.1 400 Bad Request\r\nContent-length: 15\r\n\r\n400 Bad Request";
write(fd, cast(void*)resp, resp.length);
continue;
}
static foreach (mm; __traits(allMembers, T)) { static foreach (mm; __traits(allMembers, T)) {
static if (__traits(isStaticFunction, __traits(getMember, T, mm))) { static if (__traits(isStaticFunction, __traits(getMember, T, mm))) {
foreach(attr; __traits(getAttributes, __traits(getMember, T, mm))) { foreach(attr; __traits(getAttributes, __traits(getMember, T, mm))) {
static if (is(typeof(attr) == Location)) { static if (is(typeof(attr) == Location)) {
parseAndValidateURL(rqst.path, &rqst);
if (rqst.path == attr.path) { if (rqst.path == attr.path) {
Response rsp; Response rsp;
__traits(getMember, T, mm)(&rsp, &rqst); __traits(getMember, T, mm)(&rsp, &rqst);
@ -123,7 +151,8 @@ struct Server {
} }
} }
Request parseReq(ubyte[] body) { // TODO: реализовать парсинг заголовков, оформленных хер пойми как // TODO: реализовать парсинг заголовков, оформленных хер пойми как
Request parseReq(ubyte[] body) {
int prev; int prev;
short[] xxx; short[] xxx;
Request req; Request req;

View File

@ -30,27 +30,10 @@ string getStatus(short status) {
return "WTF"; return "WTF";
} }
// FIXME: memory leak short parseAndValidateURL(char[] url, Request* rqst) {
void append(char[]* src, char symb) @nogc { if (url.length > 2048) return 1; // too long url
import core.memory: pureMalloc, pureFree;
auto arr =
cast (char[])
pureMalloc(src.length + 1)
[0..src.length + 1];
arr[$-1..$] = symb;
arr[0..$-1] = *src;
*src = arr;
arr = null;
pureFree(arr.ptr);
}
void parseAndValidateURL(char[] url, Request* rqst) {
if (url.length > 2048) throw new Exception("Too long URL");
rqst.path = null; rqst.path = null;
bool notArgumentPart; bool notArgumentPart;
scope (exit) append(&rqst.args, '&');
for (short i; i < url.length; ++i) { for (short i; i < url.length; ++i) {
switch (url[i]) { switch (url[i]) {
case '?': case '?':
@ -58,22 +41,24 @@ void parseAndValidateURL(char[] url, Request* rqst) {
notArgumentPart = true; notArgumentPart = true;
break; break;
case '/': case '/':
if (url.length > i+1 && url[i+1] != '/') append(&rqst.path, '/'); if (url.length > i+1 && url[i+1] != '/') rqst.path ~= '/';
break; break;
case '=', '&': case '=', '&':
if (notArgumentPart && url[i-1] != url[i]) append(&rqst.args, url[i]); if (notArgumentPart && url[i-1] != url[i]) rqst.args ~= url[i];
break; break;
case 'A': .. case 'Z': case 'A': .. case 'Z':
case 'a': .. case 'z': case 'a': .. case 'z':
case '0': .. case '9': case '0': .. case '9':
case '-', '_', '.', '~', '!', '$', '\'', '(', ')', '*', '+', ',', ';', '@', '[', ']', '|', '%': case '-', '_', '.', '~', '!', '$', '\'', '(', ')', '*', '+', ',', ';', '@', '[', ']', '|', '%':
if (notArgumentPart) append(&rqst.args, url[i]); if (notArgumentPart) rqst.args ~= url[i];
else append(&rqst.path, url[i]); else rqst.path ~= url[i];
break; break;
default: throw new Exception("Malformed URL"); default: return 1; // malformed url
} }
} }
rqst.args ~= '&';
return 0;
} }
struct Request { struct Request {
@ -173,5 +158,9 @@ private static enum Statuses: short { // спизженно с https://github.co
@("Service Unavailable") service_unavailable = 503, @("Service Unavailable") service_unavailable = 503,
@("Gateway Timeout") gateway_timeout = 504, @("Gateway Timeout") gateway_timeout = 504,
@("HTTP Version Not Supported") http_version_not_supported = 505, @("HTTP Version Not Supported") http_version_not_supported = 505,
@("Variant Also Negotiates") variant_also_negotiates = 506 @("Variant Also Negotiates") variant_also_negotiates = 506,
@("Insufficient Storage") insufficient_storage = 507, // (WebDAV)
@("Loop Detected") loop_detected = 508, // (WebDAV)
@("Not Extended") not_extended = 510,
@("Network Authentication Required") network_authentication_required = 511
} }