Added more tests.
Build and Test Module / build-and-test (push) Successful in 13s
Details
Build and Test Module / build-and-test (push) Successful in 13s
Details
This commit is contained in:
parent
4d352a0ffa
commit
b06cb7547f
|
@ -11,7 +11,7 @@ import streams;
|
|||
* Returns: The string that was read, or a stream error.
|
||||
*/
|
||||
Either!(string, "value", StreamError, "error") consumeUntil(S)(
|
||||
S inputStream,
|
||||
ref S inputStream,
|
||||
string target
|
||||
) if (isByteInputStream!S) {
|
||||
ubyte[1024] buffer;
|
||||
|
@ -61,13 +61,22 @@ ptrdiff_t indexOf(string s, char c, size_t offset = 0) {
|
|||
string stripSpaces(string s) {
|
||||
if (s.length == 0) return s;
|
||||
ptrdiff_t startIdx = 0;
|
||||
while (s[startIdx] == ' ' && startIdx < s.length) startIdx++;
|
||||
while (startIdx < s.length && s[startIdx] == ' ') startIdx++;
|
||||
s = s[startIdx .. $];
|
||||
if (s.length == 0) return "";
|
||||
ptrdiff_t endIdx = s.length - 1;
|
||||
while (s[endIdx] == ' ' && endIdx >= 0) endIdx--;
|
||||
return s[0 .. endIdx + 1];
|
||||
}
|
||||
|
||||
unittest {
|
||||
assert(stripSpaces("") == "");
|
||||
assert(stripSpaces(" ") == "");
|
||||
assert(stripSpaces("test") == "test");
|
||||
assert(stripSpaces(" test") == "test");
|
||||
assert(stripSpaces(" test string ") == "test string");
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to append an unsigned integer value to a char buffer. It is
|
||||
* assumed that there's enough space to write the value.
|
||||
|
|
|
@ -92,10 +92,45 @@ void handleClient(Socket clientSocket, HttpRequestHandler requestHandler) {
|
|||
} catch (Exception e) {
|
||||
import std.stdio;
|
||||
stderr.writeln("Exception thrown while handling request: " ~ e.msg);
|
||||
} catch (Throwable t) {
|
||||
import std.stdio;
|
||||
stderr.writeln("Throwable error while handling request: " ~ t.msg);
|
||||
throw t;
|
||||
}
|
||||
inputStream.closeStream();
|
||||
}
|
||||
|
||||
// Test case where we use a local socket pair to test the full handleClient
|
||||
// workflow from the HttpRequestHandler's point of view.
|
||||
unittest {
|
||||
Socket[2] sockets = socketPair();
|
||||
Socket clientSocket = sockets[0];
|
||||
Socket serverSocket = sockets[1];
|
||||
const requestContent =
|
||||
"POST /data HTTP/1.1\r\n" ~
|
||||
"Content-Type: application/json\r\n" ~
|
||||
"Content-Length: 22\r\n" ~
|
||||
"\r\n" ~
|
||||
"{\"x\": 5, \"flag\": true}";
|
||||
clientSocket.send(cast(ubyte[]) requestContent);
|
||||
|
||||
class TestHandler : HttpRequestHandler {
|
||||
import std.conv;
|
||||
|
||||
void handle(ref ServerHttpRequest request, ref ServerHttpResponse response) {
|
||||
assert(request.headers["Content-Type"] == ["application/json"]);
|
||||
assert("Content-Length" in request.headers && request.headers["Content-Length"].length > 0);
|
||||
ulong contentLength = request.headers["Content-Length"][0].to!ulong;
|
||||
assert(contentLength == 22);
|
||||
ubyte[22] bodyBuffer;
|
||||
auto readResult = request.inputStream.readFromStream(bodyBuffer);
|
||||
assert(readResult.hasCount && readResult.count == 22);
|
||||
assert(cast(string) bodyBuffer == "{\"x\": 5, \"flag\": true}");
|
||||
}
|
||||
}
|
||||
handleClient(serverSocket, new TestHandler());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a ClientAddress value from a socket's address information.
|
||||
* Params:
|
||||
|
@ -171,6 +206,45 @@ HttpRequestParseResult readHttpRequest(S)(S inputStream, in ClientAddress addr)
|
|||
));
|
||||
}
|
||||
|
||||
unittest {
|
||||
import streams;
|
||||
|
||||
auto makeStream(string text) {
|
||||
return arrayInputStreamFor(cast(ubyte[]) text);
|
||||
}
|
||||
|
||||
// Basic HTTP request.
|
||||
ArrayInputStream!ubyte s1 = makeStream(
|
||||
"GET /test?x=5 HTTP/1.1\r\n" ~
|
||||
"Accept: text/plain\r\n" ~
|
||||
"\r\n"
|
||||
);
|
||||
auto r1 = readHttpRequest(&s1, ClientAddress.unknown());
|
||||
assert(r1.hasRequest);
|
||||
assert(r1.request.httpVersion == HttpVersion.V1);
|
||||
assert(r1.request.method == HttpMethod.GET);
|
||||
assert(r1.request.url == "/test?x=5");
|
||||
const r1ExpectedHeaders = ["Accept": ["text/plain"]];
|
||||
assert(r1.request.headers == r1ExpectedHeaders);
|
||||
assert(r1.request.clientAddress == ClientAddress.unknown());
|
||||
|
||||
// POST request with body. Test that the body is read correctly.
|
||||
ArrayInputStream!ubyte s2 = makeStream(
|
||||
"POST /data HTTP/1.1\r\n" ~
|
||||
"Content-Type: text/plain\r\n" ~
|
||||
"Content-Length: 12\r\n" ~
|
||||
"\r\n" ~
|
||||
"Hello world!"
|
||||
);
|
||||
auto r2 = readHttpRequest(&s2, ClientAddress.unknown());
|
||||
assert(r2.hasRequest);
|
||||
assert(r2.request.method == HttpMethod.POST);
|
||||
ubyte[12] r2BodyBuffer;
|
||||
StreamResult r2BodyReadResult = s2.readFromStream(r2BodyBuffer);
|
||||
assert(r2BodyReadResult.count == 12);
|
||||
assert(cast(string) r2BodyBuffer == "Hello world!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses HTTP headers from an input stream, and returns them as an associative
|
||||
* array mapping header names to their list of values.
|
||||
|
@ -201,15 +275,36 @@ Either!(string[][string], "headers", StreamError, "error") parseHeaders(S)(S inp
|
|||
return Either!(string[][string], "headers", StreamError, "error")(headers);
|
||||
}
|
||||
|
||||
// unittest {
|
||||
// class TestHandler : HttpRequestHandler {
|
||||
// void handle(ref ServerHttpRequest request, ref ServerHttpResponse response) {
|
||||
// response.status = HttpStatus.OK;
|
||||
// response.headers.add("Content-Type", "application/json");
|
||||
// response.outputStream.writeToStream(cast(ubyte[]) "{\"a\": 1}");
|
||||
// }
|
||||
// }
|
||||
unittest {
|
||||
import streams;
|
||||
|
||||
// HttpTransport tp = new Http1Transport(new TestHandler(), 8080);
|
||||
// tp.start();
|
||||
// }
|
||||
auto makeStream(string text) {
|
||||
return arrayInputStreamFor(cast(ubyte[]) text);
|
||||
}
|
||||
|
||||
// Basic valid headers.
|
||||
auto r1 = parseHeaders(makeStream("Content-Type: application/json\r\n\r\n"));
|
||||
assert(r1.hasHeaders);
|
||||
assert("Content-Type" in r1.headers);
|
||||
assert(r1.headers["Content-Type"] == ["application/json"]);
|
||||
|
||||
// Multiple headers.
|
||||
auto r2 = parseHeaders(makeStream("Accept: text, json, image\r\nContent-Length: 1234\r\n\r\n"));
|
||||
assert(r2.hasHeaders);
|
||||
assert("Accept" in r2.headers);
|
||||
assert(r2.headers["Accept"] == ["text, json, image"]);
|
||||
assert(r2.headers["Content-Length"] == ["1234"]);
|
||||
|
||||
// Basic invalid header string.
|
||||
auto r3 = parseHeaders(makeStream("Invalid headers"));
|
||||
assert(r3.hasError);
|
||||
|
||||
// No trailing \r\n
|
||||
auto r4 = parseHeaders(makeStream("Content-Type: application/json"));
|
||||
assert(r4.hasError);
|
||||
|
||||
// Empty headers.
|
||||
auto r5 = parseHeaders(makeStream("\r\n"));
|
||||
assert(r5.hasHeaders);
|
||||
assert(r5.headers.length == 0);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue