uselesshttp.d/lib.d

172 lines
6.5 KiB
D
Raw Permalink Normal View History

2025-01-25 22:19:53 +00:00
module lib;
/*
lost+skunk <git.macaw.me/skunky>, 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);