Added testing module, more docs, and response writing methods.
Build and Test Module / build-and-test (push) Successful in 5s
Details
Build and Test Module / build-and-test (push) Successful in 5s
Details
This commit is contained in:
parent
799da3ff34
commit
bb1d04cfa3
|
@ -1,3 +1,7 @@
|
|||
/**
|
||||
* Defines the core request handler interface that's the starting point for
|
||||
* all HTTP request processing.
|
||||
*/
|
||||
module handy_http_primitives.handler;
|
||||
|
||||
import handy_http_primitives.request;
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
/**
|
||||
* The handy_http_primitives module defines a set of primitive types and
|
||||
* interfaces that are shared among all Handy-Http libraries, and form the
|
||||
* basis of how requests are handled.
|
||||
*/
|
||||
module handy_http_primitives;
|
||||
|
||||
public import handy_http_primitives.request;
|
||||
|
@ -6,3 +11,4 @@ public import handy_http_primitives.handler;
|
|||
public import handy_http_primitives.optional;
|
||||
public import handy_http_primitives.multivalue_map;
|
||||
public import handy_http_primitives.builder;
|
||||
public import handy_http_primitives.testing;
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
/**
|
||||
* Defines the request structure and associated types that are generally used
|
||||
* when dealing with a client's HTTP request.
|
||||
*/
|
||||
module handy_http_primitives.request;
|
||||
|
||||
import streams;
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
/**
|
||||
* Defines the HTTP response structure and associated types that are generally
|
||||
* used when formulating a response to a client's request.
|
||||
*/
|
||||
module handy_http_primitives.response;
|
||||
|
||||
import streams : OutputStream;
|
||||
|
@ -15,6 +19,40 @@ struct ServerHttpResponse {
|
|||
StringMultiValueMap headers;
|
||||
/// The stream to which the response body is written.
|
||||
OutputStream!ubyte outputStream;
|
||||
|
||||
/**
|
||||
* Writes an array of bytes to the response's output stream.
|
||||
* Params:
|
||||
* bytes = The bytes to write.
|
||||
* contentType = The declared content type of the data, which is written
|
||||
* as the "Content-Type" header.
|
||||
*/
|
||||
void writeBodyBytes(ubyte[] bytes, string contentType = ContentTypes.APPLICATION_OCTET_STREAM) {
|
||||
import std.conv : to;
|
||||
headers.add("Content-Type", contentType);
|
||||
headers.add("Content-Length", to!string(bytes.length));
|
||||
// We trust that when we write to the output stream, the transport
|
||||
// implementation will handle properly formatting the headers and other
|
||||
// HTTP boilerplate response content prior to actually writing the body.
|
||||
auto result = outputStream.writeToStream(bytes);
|
||||
if (result.hasError) {
|
||||
throw new Exception(
|
||||
"Failed to write bytes to the response's output stream: " ~
|
||||
cast(string) result.error.message
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a string of content to the response's output stream.
|
||||
* Params:
|
||||
* content = The content to write.
|
||||
* contentType = The declared content type of the data, which is written
|
||||
* as the "Content-Type" header.
|
||||
*/
|
||||
void writeBodyString(string content, string contentType = ContentTypes.TEXT_PLAIN) {
|
||||
writeBodyBytes(cast(ubyte[]) content, contentType);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -106,10 +144,19 @@ enum HttpStatus : StatusInfo {
|
|||
enum ContentTypes : string {
|
||||
APPLICATION_JSON = "application/json",
|
||||
APPLICATION_XML = "application/xml",
|
||||
APPLICATION_OCTET_STREAM = "application/octet-stream",
|
||||
APPLICATION_PDF = "application/pdf",
|
||||
|
||||
TEXT_PLAIN = "text/plain",
|
||||
TEXT_HTML = "text/html",
|
||||
TEXT_CSS = "text/css"
|
||||
TEXT_CSS = "text/css",
|
||||
TEXT_CSV = "text/csv",
|
||||
TEXT_JAVASCRIPT = "text/javascript",
|
||||
TEXT_MARKDOWN = "text/markdown",
|
||||
|
||||
IMAGE_JPEG = "image/jpeg",
|
||||
IMAGE_PNG = "image/png",
|
||||
IMAGE_SVG = "image/svg+xml"
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
/**
|
||||
* The testing module defines helper methods for testing your HTTP handling
|
||||
* code.
|
||||
*/
|
||||
module handy_http_primitives.testing;
|
||||
|
||||
import handy_http_primitives.response;
|
||||
|
||||
/**
|
||||
* Asserts that the given response's status matches an expected status.
|
||||
* Params:
|
||||
* response = The response to check.
|
||||
* expectedStatus = The expected status that the response should have.
|
||||
*/
|
||||
void assertStatus(in ServerHttpResponse response, in StatusInfo expectedStatus) {
|
||||
import std.format : format;
|
||||
assert(
|
||||
expectedStatus == response.status,
|
||||
format!"The HTTP response's status of %d (%s) didn't match the expected status %d (%s)."(
|
||||
response.status.code,
|
||||
response.status.text,
|
||||
expectedStatus.code,
|
||||
expectedStatus.text
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
unittest {
|
||||
import handy_http_primitives.builder;
|
||||
ServerHttpResponseBuilder()
|
||||
.withStatus(HttpStatus.OK)
|
||||
.build()
|
||||
.assertStatus(HttpStatus.OK);
|
||||
}
|
||||
|
||||
// Some common status assertions:
|
||||
|
||||
void assertStatusOk(in ServerHttpResponse response) {
|
||||
assertStatus(response, HttpStatus.OK);
|
||||
}
|
||||
|
||||
void assertStatusNotFound(in ServerHttpResponse response) {
|
||||
assertStatus(response, HttpStatus.NOT_FOUND);
|
||||
}
|
||||
|
||||
void assertStatusBadRequest(in ServerHttpResponse response) {
|
||||
assertStatus(response, HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
void assertStatusUnauthorized(in ServerHttpResponse response) {
|
||||
assertStatus(response, HttpStatus.UNAUTHORIZED);
|
||||
}
|
||||
|
||||
void assertStatusForbidden(in ServerHttpResponse response) {
|
||||
assertStatus(response, HttpStatus.FORBIDDEN);
|
||||
}
|
||||
|
||||
void assertStatusInternalServerError(in ServerHttpResponse response) {
|
||||
assertStatus(response, HttpStatus.FORBIDDEN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the given response has a header with a given value.
|
||||
* Params:
|
||||
* response = The response to check.
|
||||
* header = The name of the header to check the value of.
|
||||
* expectedValue = The expected value of the header.
|
||||
*/
|
||||
void assertHasHeader(in ServerHttpResponse response, string header, string expectedValue) {
|
||||
import std.format : format;
|
||||
assert(
|
||||
response.headers.contains(header),
|
||||
format!"The HTTP response doesn't have a header named \"%s\"."(header)
|
||||
);
|
||||
string value = response.headers.getFirst(header).orElseThrow();
|
||||
assert(
|
||||
value == expectedValue,
|
||||
format!"The HTTP response's header \"%s\" with value \"%s\" didn't match the expected value \"%s\"."(
|
||||
header,
|
||||
value,
|
||||
expectedValue
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
unittest {
|
||||
import streams;
|
||||
import handy_http_primitives.builder;
|
||||
ArrayOutputStream!ubyte bufferOut = byteArrayOutputStream();
|
||||
ServerHttpResponse r1 = ServerHttpResponseBuilder()
|
||||
.withOutputStream(&bufferOut)
|
||||
.build();
|
||||
r1.writeBodyString("Hello, world!");
|
||||
r1.assertHasHeader("Content-Type", ContentTypes.TEXT_PLAIN);
|
||||
}
|
Loading…
Reference in New Issue