uselesshttp.d/lib.d

172 lines
6.5 KiB
D
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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);