From 06f9e483c3efb35d162400f39e3b111ebf87c469 Mon Sep 17 00:00:00 2001 From: lost+skunk Date: Sat, 8 Feb 2025 21:13:13 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9F=D0=B5=D1=80=D0=B5=D1=85=D0=BE=D0=B4=20?= =?UTF-8?q?=D0=BD=D0=B0=20edge-triggered;=20=D1=83=D1=81=D1=82=D1=80=D0=B0?= =?UTF-8?q?=D0=BD=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=B6=D0=BE=D1=80=D0=B0=20CPU?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/uselesshttpd/package.d | 45 ++++++++++++++++++++++++++++------- source/uselesshttpd/util.d | 39 +++++++++++------------------- 2 files changed, 51 insertions(+), 33 deletions(-) diff --git a/source/uselesshttpd/package.d b/source/uselesshttpd/package.d index 3092894..8f287d7 100644 --- a/source/uselesshttpd/package.d +++ b/source/uselesshttpd/package.d @@ -27,6 +27,15 @@ struct Server { private shared int epfd, sock; 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; for (short i; i < address.length; ++i) if (address[i] == ':') {ipv6=true;break;} @@ -47,15 +56,22 @@ struct Server { err(bind(sock, cast(sockaddr*)&sockt, sockt.sizeof), "bind"); } + int hz; setsockopt(sock, - IPPROTO_TCP, TCP_NODELAY | SO_REUSEADDR | SO_REUSEPORT, - cast(void*)(new int), int.sizeof); + IPPROTO_TCP, SO_REUSEADDR, + 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"); serve!MD; } - void stop() { + void stop() @nogc { epfd.close(); sock.close(); } @@ -66,7 +82,7 @@ struct Server { epoll_event[MAX_EVENTS] evts; ev.data.fd = sock; - ev.events = EPOLLIN | EPOLLEXCLUSIVE; + ev.events = EPOLLIN; epoll_ctl(epfd, EPOLL_CTL_ADD, sock, &ev); for (;;) { @@ -77,23 +93,35 @@ struct Server { // sockaddr_in addr; // socklen_t al = sockaddr_in.sizeof; // 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); epoll_ctl(epfd, EPOLL_CTL_ADD, ev.data.fd, &ev); // 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: - ubyte[128] buf; + ubyte[256] buf; for (;;) { // обработчик запросов auto rd = recv(fd, cast(void*)buf, buf.sizeof, 0); if (rd < 1) break; 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 if (__traits(isStaticFunction, __traits(getMember, T, mm))) { foreach(attr; __traits(getAttributes, __traits(getMember, T, mm))) { static if (is(typeof(attr) == Location)) { - parseAndValidateURL(rqst.path, &rqst); if (rqst.path == attr.path) { Response rsp; __traits(getMember, T, mm)(&rsp, &rqst); @@ -123,7 +151,8 @@ struct Server { } } - Request parseReq(ubyte[] body) { // TODO: реализовать парсинг заголовков, оформленных хер пойми как + // TODO: реализовать парсинг заголовков, оформленных хер пойми как + Request parseReq(ubyte[] body) { int prev; short[] xxx; Request req; diff --git a/source/uselesshttpd/util.d b/source/uselesshttpd/util.d index 674b8ce..9db930b 100644 --- a/source/uselesshttpd/util.d +++ b/source/uselesshttpd/util.d @@ -30,27 +30,10 @@ string getStatus(short status) { return "WTF"; } -// FIXME: memory leak -void append(char[]* src, char symb) @nogc { - 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"); +short parseAndValidateURL(char[] url, Request* rqst) { + if (url.length > 2048) return 1; // too long url rqst.path = null; bool notArgumentPart; - scope (exit) append(&rqst.args, '&'); for (short i; i < url.length; ++i) { switch (url[i]) { case '?': @@ -58,22 +41,24 @@ void parseAndValidateURL(char[] url, Request* rqst) { notArgumentPart = true; break; case '/': - if (url.length > i+1 && url[i+1] != '/') append(&rqst.path, '/'); + if (url.length > i+1 && url[i+1] != '/') rqst.path ~= '/'; break; 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; case 'A': .. case 'Z': case 'a': .. case 'z': case '0': .. case '9': case '-', '_', '.', '~', '!', '$', '\'', '(', ')', '*', '+', ',', ';', '@', '[', ']', '|', '%': - if (notArgumentPart) append(&rqst.args, url[i]); - else append(&rqst.path, url[i]); + if (notArgumentPart) rqst.args ~= url[i]; + else rqst.path ~= url[i]; break; - default: throw new Exception("Malformed URL"); + default: return 1; // malformed url } } + rqst.args ~= '&'; + return 0; } struct Request { @@ -173,5 +158,9 @@ private static enum Statuses: short { // спизженно с https://github.co @("Service Unavailable") service_unavailable = 503, @("Gateway Timeout") gateway_timeout = 504, @("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 } \ No newline at end of file