From 722fa7f63bb1602fd95366668fc3fb484dc03702 Mon Sep 17 00:00:00 2001 From: lost+skunk Date: Sun, 26 Jan 2025 01:19:53 +0300 Subject: [PATCH] =?UTF-8?q?=D0=BD=D0=B0=D0=B7=D0=B2=D0=B0=D0=BD=D0=B8?= =?UTF-8?q?=D0=B5=20=D0=BA=D0=BE=D0=BC=D0=BC=D0=B8=D1=82=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib.d | 172 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ lib.o | Bin 0 -> 29860 bytes main.d | 23 ++++++++ main.o | Bin 0 -> 56596 bytes util.d | 98 ++++++++++++++++++++++++++++++++ 5 files changed, 293 insertions(+) create mode 100644 lib.d create mode 100644 lib.o create mode 100644 main.d create mode 100644 main.o create mode 100644 util.d diff --git a/lib.d b/lib.d new file mode 100644 index 0000000..acd1243 --- /dev/null +++ b/lib.d @@ -0,0 +1,172 @@ +module lib; + +/* + lost+skunk , 2025; + Licensed under WTFPL +*/ + +import core.stdc.stdio; +import core.sys.linux.epoll; +import core.sys.linux.netinet.tcp; +import core.sys.posix.netinet.in_; +import core.sys.posix.unistd; +import core.sys.posix.sys.socket; + +import util; + +struct Response { + short status = 200; + ubyte[] body; + string[string] headers; + string mimetype = "application/octet-stream"; +} + +struct Request { + enum Methods { + GET = "GET", + PUT = "PUT", + POST = "POST", + DELETE = "DELETE", + OPTIONS = "OPTIONS", + // остальное лень реализовывать, да и не трэба.. + } + + Methods method; + string path, body; + string[string] headers; +} + +struct Server { + string address; + short port; + + private shared int epfd, sock; + void start(MD...)() { // реализовать прокидывания структуры/класса для роутинга + sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, cast(void*)(new int), int.sizeof); + setsockopt(sock, IPPROTO_TCP, SO_REUSEADDR, cast(void*)(new int), int.sizeof); + + sockaddr_in sockt; + sockt.sin_family = AF_INET; + sockt.sin_port = htons(port); + sockt.sin_addr.s_addr = inet_addr(cast(char*)address); + + err(bind(sock, cast(sockaddr*)&sockt, sockt.sizeof), "bind"); + err(listen(sock, 512), "listen"); + serve!MD; + } + + void shutdown() { + epfd.close(); + sock.close(); + } + + void serve(T...)() { + epfd = epoll_create(MAX_CLIENTS); + epoll_event ev; + epoll_event[MAX_EVENTS] evts; + + ev.events = EPOLLIN | EPOLLOUT | EPOLLET; + ev.data.fd = sock; + epoll_ctl(epfd, EPOLL_CTL_ADD, sock, &ev); + + for (;;) { + auto w = epoll_wait(epfd, &evts[0], MAX_EVENTS, -1); + for (int i = 0; i < w; ++i) { + auto fd = evts[i].data.fd; + if (fd == sock) { + sockaddr_in addr; + socklen_t al = sockaddr_in.sizeof; + + ev.events = EPOLLIN | EPOLLET | EPOLLRDHUP | EPOLLHUP; + ev.data.fd = accept4(sock, cast(sockaddr*)&addr, &al, SOCK_NONBLOCK); + epoll_ctl(epfd, EPOLL_CTL_ADD, ev.data.fd, &ev); + } else if (evts[i].events & EPOLLIN) { rd: + ubyte[1024] buf; + for (;;) { // обработчик запросов + if (read(fd, cast(void*)buf, buf.sizeof) > 0) { + auto rqst = parseReq(cast(string)buf); + 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)) { + if (rqst.path == attr.path) { + Response rsp; + __traits(getMember, T, mm)(&rsp, &rqst); + + char[] headers; + foreach(header,content;rsp.headers) + headers ~= "\r\n" ~ header ~ ": " ~ content; + auto head = + "HTTP/1.1 " ~ intToStr(rsp.status) ~ ' ' ~ getStatus(rsp.status) // временно + ~ "\r\nContent-length: " ~ intToStr(rsp.body.length) + ~ "\r\nContent-Type: " ~ rsp.mimetype + ~ headers + ~ "\r\n\r\n"; + + write(fd, cast(void*)head, head.length); + write(fd, cast(void*)rsp.body, rsp.body.length); + goto rd; + } else continue; + } + } + } + } + static auto resp="HTTP/1.1 404 Not Found\r\nContent-length: 0\r\n\r\n"; + write(fd, cast(void*)resp, resp.length); + } else break; + } + } + if (evts[i].events & (EPOLLRDHUP | EPOLLHUP)) { + epoll_ctl(epfd, EPOLL_CTL_DEL, fd, null); + close(fd); + break; + } + } + } + } + + Request parseReq(string body) { // TODO: реализовать парсинг заголовков, оформленных хер пойми как + int prev; + short[] xxx; + Request req; + + for (short i; i < body.length; ++i) { + if (body[i] == '\r') { + auto splitted = body[prev..i]; + for (short x = 1; x < splitted.length; ++x) { + if (prev == 0) { // для прочего говна (метода, пути и протокола) + if (splitted[x] == ' ') + xxx ~= x; + else if (xxx.length == 2) { + req.method = splitted[0..xxx[0]]; + req.path = splitted[xxx[0]+1..xxx[1]]; + // if (splitted[xxx[1]..$] != " HTTP/1.1") + // throw new Exception("Unsupported HTTP version"); + } else continue; + } else if (splitted[x-1] == ':') { // для заголовков + req.headers[splitted[0..x-1]] = splitted[x+1..$]; + break; + } + } + + prev = i+2; + if (body[prev] == '\r') { + req.body = body[prev+2..$]; + break; + } + } + } + return req; + } +} + +private: +enum BACKLOG = 512; +enum MAX_EVENTS = 512; +enum MAX_CLIENTS = 512; +enum MAX_MESSAGE_LEN = 2048; +enum SOCK_NONBLOCK = 0x800; +enum MAX_RESPONSES = 512; + +extern (C) int accept4(int, sockaddr*, socklen_t*, int); \ No newline at end of file diff --git a/lib.o b/lib.o new file mode 100644 index 0000000000000000000000000000000000000000..059e15966beae4f41dcd0b42344cd5fd4e6248c8 GIT binary patch literal 29860 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 X%3l4WbJTw$`2FhtF(3UIAN~IW?eeQ^ literal 0 HcmV?d00001 diff --git a/main.d b/main.d new file mode 100644 index 0000000..af63654 --- /dev/null +++ b/main.d @@ -0,0 +1,23 @@ +module app; // обязательно +import lib, util: Location; +void main() { + auto srv = Server("127.0.0.1", 3003); + srv.start!app; + // srv.shutdown; +} + +@Location("/skunk") +void huy(Response* w, Request* r) { + w.body = cast(ubyte[])"skunk"; + return; +} + +@Location("/skunks") +void z(Response* w, Request* r) { + w.headers["X-Powered-By"] = "D"; + w.headers["Lang"] = "D"; + w.status = 410; + w.mimetype = "text/plain;charset=utf-8"; + w.body = cast(ubyte[])"скунсы"; + return; +} \ No newline at end of file diff --git a/main.o b/main.o new file mode 100644 index 0000000000000000000000000000000000000000..b3b78c7ec791c437cd0a2f8669371b40f32c6c7c GIT binary patch literal 56596 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 CZ_qvf literal 0 HcmV?d00001 diff --git a/util.d b/util.d new file mode 100644 index 0000000..e9ee022 --- /dev/null +++ b/util.d @@ -0,0 +1,98 @@ +module util; + +char[] intToStr(T)(T num) { + char[] buf; + for(short i; num > 0; ++i) { + buf = (num % 10 + '0')~buf; + num /= 10; + } + return buf; +} + +void err(int e, string msg) { + if (e != 0) + throw new Exception("Something went wrong: failed to "~msg~'.'); +} + +// UDA +struct Location { string path; } +// enum method; + +string getStatus(short status) { + static foreach(mmbr; __traits(allMembers, Statuses)) + if (__traits(getMember, Statuses, mmbr) == status) + return __traits(getAttributes, __traits(getMember, Statuses, mmbr))[0]; + return ""; +} + +private static enum Statuses: short { // спизженно с https://github.com/zigzap/zap/blob/675c65b509d48c21a8d1fa4c5ec53fc407643a3b/src/http.zig#L6 + // Information responses + @("Continue") continuee = 100, + @("Switching Protocols") switching_protocols = 101, + @("Processing") processing = 102, // (WebDAV) + @("Early Hints") early_hints = 103, + + // Successful responses + @("OK") ok = 200, + @("Created") created = 201, + @("Accepted") accepted = 202, + @("Non-Authoritative Information") non_authoritative_information = 203, + @("No Content") no_content = 204, + @("Reset Content") reset_content = 205, + @("Partial Content") partial_content = 206, + @("Multi-Status") multi_status = 207, // (WebDAV) + @("Already Reported") already_reported = 208, // (WebDAV) + @("IM Used") im_used = 226, // (HTTP Delta encoding) + + // Redirection messages + @("Multiple Choices") multiple_choices = 300, + @("Moved Permanently") moved_permanently = 301, + @("Found") found = 302, + @("See Other") see_other = 303, + @("Not Modified") not_modified = 304, + @("Use Proxy") use_proxy = 305, + @("Unused") unused = 306, + @("Temporary Redirect") temporary_redirect = 307, + @("Permanent Redirect") permanent_redirect = 308, + + // Client error responses + @("Bad Request") bad_request = 400, + @("Unauthorized") unauthorized = 401, + @("Payment Required") payment_required = 402, + @("Forbidden") forbidden = 403, + @("Not Found") not_found = 404, + @("Method Not Allowed") method_not_allowed = 405, + @("Not Acceptable") not_acceptable = 406, + @("Proxy Authentication Required") proxy_authentication_required = 407, + @("Request Timeout") request_timeout = 408, + @("Conflict") conflict = 409, + @("Gone") gone = 410, + @("Length Required") length_required = 411, + @("Precondition Failed") precondition_failed = 412, + @("Payload Too Large") payload_too_large = 413, + @("URI Too Long") uri_too_long = 414, + @("Unsupported Media Type") unsupported_media_type = 415, + @("Range Not Satisfiable") range_not_satisfiable = 416, + @("Expectation Failed") expectation_failed = 417, + @("I'm a teapot") im_a_teapot = 418, + @("Misdirected Request") misdirected_request = 421, + @("Unprocessable Content") unprocessable_content = 422, // (WebDAV) + @("Locked") locked = 423, // (WebDAV) + @("Failed Dependency") failed_dependency = 424, // (WebDAV) + @("Too Early") too_early = 425, + @("Upgrade Required") upgrade_required = 426, + @("Precondition Required") precondition_required = 428, + @("Too Many Requests") too_many_requests = 429, + @("Request Header Fields Too Large") request_header_fields_too_large = 431, + @("Unavailable For Legal Reasons") unavailable_for_legal_reasons = 451, + + // Server error responses + @("Internal Server Error") internal_server_error = 500, + @("Not Implemented") not_implemented = 501, + @("Bad Gateway") bad_gateway = 502, + @("Service Unavailable") service_unavailable = 503, + @("Gateway Timeout") gateway_timeout = 504, + @("HTTP Version Not Supported") http_version_not_supported = 505, + @("Variant Also Negotiates") variant_also_negoti + +} \ No newline at end of file