Added more comments, cleaned up code.

This commit is contained in:
Andrew Lalis 2024-09-17 17:08:15 -04:00
parent eae8fd6c0e
commit e411b4d270
4 changed files with 136 additions and 68 deletions

View File

@ -3,6 +3,10 @@ module handy_http_primitives.handler;
import handy_http_primitives.request;
import handy_http_primitives.response;
/**
* Defines the request handler interface, which is called upon to handle an
* incoming HTTP request.
*/
interface HttpRequestHandler {
void handle(in HttpRequest request, ref HttpResponse response);
void handle(ref ServerHttpRequest request, ref ServerHttpResponse response);
}

View File

@ -1,2 +1,7 @@
module handy_http_primitives;
public import handy_http_primitives.request;
public import handy_http_primitives.response;
public import handy_http_primitives.handler;
public import handy_http_primitives.optional;
public import handy_http_primitives.multivalue_map;

View File

@ -1,25 +1,47 @@
module handy_http_primitives.request;
import streams;
import streams : InputStream;
import std.traits : isSomeString, EnumMembers;
import handy_http_primitives.multivalue_map;
import handy_http_primitives.optional;
struct HttpRequest {
const ubyte httpVersion = 1;
const Method method = Method.GET;
const string url = "";
/**
* 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.
const HttpVersion httpVersion = HttpVersion.V1_1;
/// The HTTP verb used in the request.
const HttpMethod method = HttpMethod.GET;
/// The URL that was requested.
const(char[]) url = "";
/// A case-insensitive map of all request headers.
const(CaseInsensitiveStringMultiValueMap) headers;
/// A case-sensitive map of all URL query parameters.
const(StringMultiValueMap) queryParams;
/// The underlying stream used to read the body from the request.
InputStream!ubyte inputStream;
}
/**
* Enumeration of all possible HTTP request versions, as an unsigned byte for
* efficient storage.
*/
public enum HttpVersion : ubyte {
V1_1 = 1 << 1,
V2 = 1 << 2,
V3 = 1 << 3
}
/**
* Enumeration of all possible HTTP request methods as unsigned integer values
* for efficient logic.
*
* https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods
*/
public enum Method : ushort {
public enum HttpMethod : ushort {
GET = 1 << 0,
HEAD = 1 << 1,
POST = 1 << 2,
@ -30,3 +52,33 @@ public enum Method : ushort {
TRACE = 1 << 7,
PATCH = 1 << 8
}
/**
* 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;
import std.conv : to;
static foreach (m; EnumMembers!HttpMethod) {
if (s == to!string(m)) return Optional!HttpMethod.of(m);
}
const cleanStr = strip(toUpper(s));
static foreach (m; EnumMembers!HttpMethod) {
if (cleanStr == to!string(m)) return Optional!HttpMethod.of(m);
}
return Optional!HttpMethod.empty;
}
unittest {
alias R = Optional!HttpMethod;
assert(parseHttpMethod("GET") == R.of(HttpMethod.GET));
assert(parseHttpMethod("get") == R.of(HttpMethod.GET));
assert(parseHttpMethod(" geT ") == R.of(HttpMethod.GET));
assert(parseHttpMethod("PATCH") == R.of(HttpMethod.PATCH));
assert(parseHttpMethod(" not a method!") == R.empty);
assert(parseHttpMethod("") == R.empty);
}

View File

@ -4,9 +4,16 @@ import streams;
import handy_http_primitives.multivalue_map;
struct HttpResponse {
/**
* The response that's sent by a server back to a client after processing the
* client's HTTP request.
*/
struct ServerHttpResponse {
/// The response status.
StatusInfo status = HttpStatus.OK;
/// A multi-valued map containing all headers.
StringMultiValueMap headers;
/// The stream to which the response body is written.
OutputStream!ubyte outputStream;
}