2024-08-15 23:21:48 +00:00
|
|
|
module handy_http_primitives.request;
|
|
|
|
|
2024-09-17 21:08:15 +00:00
|
|
|
import streams : InputStream;
|
|
|
|
import std.traits : isSomeString, EnumMembers;
|
2024-08-15 23:21:48 +00:00
|
|
|
|
|
|
|
import handy_http_primitives.multivalue_map;
|
2024-09-17 21:08:15 +00:00
|
|
|
import handy_http_primitives.optional;
|
2024-08-15 23:21:48 +00:00
|
|
|
|
2024-09-17 21:08:15 +00:00
|
|
|
/**
|
|
|
|
* The HTTP request struct which represents the content of an HTTP request as
|
|
|
|
* received by a server.
|
|
|
|
*/
|
|
|
|
struct ServerHttpRequest {
|
|
|
|
/// The HTTP version of the request.
|
2024-10-28 14:19:56 +00:00
|
|
|
const HttpVersion httpVersion = HttpVersion.V1;
|
2024-10-28 14:12:03 +00:00
|
|
|
/// The remote address of the client that sent this request.
|
|
|
|
const InternetAddress clientAddress;
|
2024-09-17 21:08:15 +00:00
|
|
|
/// The HTTP verb used in the request.
|
2024-10-28 14:12:03 +00:00
|
|
|
const string method = HttpMethod.GET;
|
2024-09-17 21:08:15 +00:00
|
|
|
/// The URL that was requested.
|
2024-10-28 14:12:03 +00:00
|
|
|
const string url = "";
|
2024-09-17 21:08:15 +00:00
|
|
|
/// A case-insensitive map of all request headers.
|
2024-08-15 23:21:48 +00:00
|
|
|
const(CaseInsensitiveStringMultiValueMap) headers;
|
2024-09-17 21:08:15 +00:00
|
|
|
/// A case-sensitive map of all URL query parameters.
|
2024-08-15 23:21:48 +00:00
|
|
|
const(StringMultiValueMap) queryParams;
|
2024-09-17 21:08:15 +00:00
|
|
|
/// The underlying stream used to read the body from the request.
|
2024-08-15 23:21:48 +00:00
|
|
|
InputStream!ubyte inputStream;
|
|
|
|
}
|
|
|
|
|
2024-09-17 21:08:15 +00:00
|
|
|
/**
|
2024-10-28 14:19:56 +00:00
|
|
|
* Enumeration of all possible HTTP request versions.
|
2024-09-17 21:08:15 +00:00
|
|
|
*/
|
|
|
|
public enum HttpVersion : ubyte {
|
2024-10-28 14:19:56 +00:00
|
|
|
/// HTTP Version 1, including versions 0.9, 1.0, and 1.1.
|
|
|
|
V1 = 1 << 1,
|
|
|
|
/// HTTP Version 2.
|
2024-09-17 21:08:15 +00:00
|
|
|
V2 = 1 << 2,
|
2024-10-28 14:19:56 +00:00
|
|
|
/// HTTP Version 3.
|
2024-09-17 21:08:15 +00:00
|
|
|
V3 = 1 << 3
|
|
|
|
}
|
|
|
|
|
2024-08-15 23:21:48 +00:00
|
|
|
/**
|
2024-10-28 14:12:03 +00:00
|
|
|
* Enumeration of all possible HTTP methods, excluding extensions like WebDAV.
|
2024-08-15 23:21:48 +00:00
|
|
|
*
|
|
|
|
* https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods
|
|
|
|
*/
|
2024-10-28 14:12:03 +00:00
|
|
|
public enum HttpMethod : string {
|
|
|
|
GET = "GET",
|
|
|
|
HEAD = "HEAD",
|
|
|
|
POST = "POST",
|
|
|
|
PUT = "PUT",
|
|
|
|
DELETE = "DELETE",
|
|
|
|
CONNECT = "CONNECT",
|
|
|
|
OPTIONS = "OPTIONS",
|
|
|
|
TRACE = "TRACE",
|
|
|
|
PATCH = "PATCH"
|
2024-08-15 23:21:48 +00:00
|
|
|
}
|
2024-09-17 21:08:15 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Attempts to parse an HttpMethod from a string.
|
|
|
|
* Params:
|
|
|
|
* s = The string to parse.
|
|
|
|
* Returns: An optional which may contain an HttpMethod, if one was parsed.
|
|
|
|
*/
|
|
|
|
Optional!HttpMethod parseHttpMethod(S)(S s) if (isSomeString!S) {
|
|
|
|
import std.uni : toUpper;
|
|
|
|
import std.string : strip;
|
|
|
|
static foreach (m; EnumMembers!HttpMethod) {
|
2024-10-28 14:12:03 +00:00
|
|
|
if (s == m) return Optional!HttpMethod.of(m);
|
2024-09-17 21:08:15 +00:00
|
|
|
}
|
|
|
|
const cleanStr = strip(toUpper(s));
|
|
|
|
static foreach (m; EnumMembers!HttpMethod) {
|
2024-10-28 14:12:03 +00:00
|
|
|
if (cleanStr == m) return Optional!HttpMethod.of(m);
|
2024-09-17 21:08:15 +00:00
|
|
|
}
|
|
|
|
return Optional!HttpMethod.empty;
|
|
|
|
}
|
|
|
|
|
|
|
|
unittest {
|
2024-10-28 14:19:56 +00:00
|
|
|
assert(parseHttpMethod("GET") == Optional!HttpMethod.of(HttpMethod.GET));
|
|
|
|
assert(parseHttpMethod("get") == Optional!HttpMethod.of(HttpMethod.GET));
|
|
|
|
assert(parseHttpMethod(" geT ") == Optional!HttpMethod.of(HttpMethod.GET));
|
|
|
|
assert(parseHttpMethod("PATCH") == Optional!HttpMethod.of(HttpMethod.PATCH));
|
|
|
|
assert(parseHttpMethod(" not a method!") == Optional!HttpMethod.empty);
|
|
|
|
assert(parseHttpMethod("") == Optional!HttpMethod.empty);
|
2024-09-17 21:08:15 +00:00
|
|
|
}
|
2024-10-28 14:12:03 +00:00
|
|
|
|
|
|
|
/// The data representing a remote IPv4 internet address, available as an int or bytes.
|
|
|
|
union IPv4InternetAddress {
|
|
|
|
const uint intValue;
|
|
|
|
const ubyte[4] bytes;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// The data representing a remote IPv6 internet address.
|
|
|
|
struct IPv6InternetAddress {
|
|
|
|
const ubyte[16] bytes;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A remote internet address, which is either IPv4 or IPv6. Check `isIPv6`.
|
|
|
|
struct InternetAddress {
|
|
|
|
/// True if this address is IPv6. False if this is an IPv4 address.
|
|
|
|
const bool isIPv6;
|
|
|
|
/// The port number assigned to the connecting client on this machine.
|
|
|
|
const ushort port;
|
|
|
|
union {
|
|
|
|
IPv4InternetAddress ipv4Address;
|
|
|
|
IPv6InternetAddress ipv6Address;
|
|
|
|
}
|
|
|
|
}
|