uselesshttp.d/source/uselesshttpd/util.d
2025-02-07 01:45:14 +03:00

177 lines
5.9 KiB
D
Raw 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 uselesshttpd.util;
/*
lost+skunk <git.macaw.me/skunky>, 2025;
Licensed under WTFPL
*/
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 "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");
rqst.path = null;
bool notArgumentPart;
scope (exit) append(&rqst.args, '&');
for (short i; i < url.length; ++i) {
switch (url[i]) {
case '?':
if (notArgumentPart) goto default;
notArgumentPart = true;
break;
case '/':
if (url.length > i+1 && url[i+1] != '/') append(&rqst.path, '/');
break;
case '=', '&':
if (notArgumentPart && url[i-1] != url[i]) append(&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]);
break;
default: throw new Exception("Malformed URL");
}
}
}
struct Request {
enum Methods {
GET = "GET",
PUT = "PUT",
POST = "POST",
DELETE = "DELETE",
OPTIONS = "OPTIONS",
// остальное лень реализовывать, да и не трэба..
}
Methods method;
void[] body;
string[string] headers;
char[] path;
char[] args;
char[] getArgument(string arg) @nogc nothrow {
short split, prev;
for (short i; i < args.length; ++i) {
if (args[i] == '=') split = i;
else if (args[i] == '&') {
if (arg == args[prev..split]) return args[split+1..i];
prev = ++i;
}
}
return null;
}
}
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_negotiates = 506
}