mirror of
https://git.macaw.me/skunky/uselesshttp.d.git
synced 2025-04-28 03:55:09 +03:00
ALPHA: URL parser
This commit is contained in:
parent
04f918a27a
commit
ca1662c403
6 changed files with 264 additions and 200 deletions
177
source/uselesshttpd/util.d
Normal file
177
source/uselesshttpd/util.d
Normal file
|
@ -0,0 +1,177 @@
|
|||
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
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue