Add PhotonHttp1Transport implementation.
Build and Test Module / build-and-test (push) Failing after 15s Details
Build and Test Module / integration-tests (push) Failing after 17s Details

This commit is contained in:
Andrew Lalis 2025-09-17 10:08:38 -04:00
parent 7ff80c8a9f
commit 8862aab12e
4 changed files with 79 additions and 2 deletions

View File

@ -5,8 +5,9 @@
"copyright": "Copyright © 2024, Andrew Lalis", "copyright": "Copyright © 2024, Andrew Lalis",
"dependencies": { "dependencies": {
"handy-http-primitives": "~>1.8", "handy-http-primitives": "~>1.8",
"streams": "~>3.6", "photon": "~>0.15.0",
"slf4d": "~>4.0" "slf4d": "~>4.0",
"streams": "~>3.6"
}, },
"description": "Implementations of HTTP transport protocols.", "description": "Implementations of HTTP transport protocols.",
"license": "CC0", "license": "CC0",

View File

@ -2,6 +2,8 @@
"fileVersion": 1, "fileVersion": 1,
"versions": { "versions": {
"handy-http-primitives": "1.8.0", "handy-http-primitives": "1.8.0",
"photon": "0.15.0",
"sharded-map": "2.7.0",
"slf4d": "4.1.1", "slf4d": "4.1.1",
"streams": "3.6.0" "streams": "3.6.0"
} }

View File

@ -0,0 +1,67 @@
module handy_http_transport.http1.photon;
import handy_http_transport.http1.transport;
import handy_http_primitives;
import slf4d;
import photon;
import std.socket;
/**
* An implementation of Http1Transport which uses Dimitry Olshansky's Photon
* library for asynchronous task processing. A main fiber is started which
* accepts incoming client sockets, and a fiber is spawned for each client so
* its request can be handled asynchronously.
*/
class PhotonHttp1Transport : Http1Transport {
this(HttpRequestHandler handler, ushort port = 8080) {
super(handler, port);
}
override void runServer() {
initPhoton();
go(() {
Socket serverSocket = new TcpSocket();
serverSocket.setOption(SocketOptionLevel.SOCKET, SocketOption.REUSEADDR, 1);
serverSocket.bind(parseAddress("127.0.0.1", port));
debugF!"Bound the server socket to %s"(serverSocket.localAddress);
serverSocket.listen(1024);
debug_("Server is now listening.");
while (super.isRunning) {
try {
trace("Waiting to accept a new socket.");
Socket clientSocket = serverSocket.accept();
trace("Accepted a new socket.");
go(() => handleClient(clientSocket, requestHandler));
trace("Added handleClient() task to the task pool.");
} catch (SocketAcceptException e) {
warn("Failed to accept socket connection.", e);
}
}
serverSocket.close();
});
runScheduler();
}
override void stop() {
super.stop();
// Send a dummy request to cause the server's blocking accept() call to end.
try {
Socket dummySocket = new TcpSocket(new InternetAddress("127.0.0.1", port));
dummySocket.shutdown(SocketShutdown.BOTH);
dummySocket.close();
} catch (SocketOSException e) {
warn("Failed to send empty request to stop server.", e);
}
}
}
unittest {
testHttp1Transport(new PhotonHttp1Transport(
HttpRequestHandler.of((req, resp) {
resp.status = HttpStatus.OK;
}),
8080
));
}

View File

@ -59,6 +59,11 @@ version(unittest) {
void testHttp1Transport(Http1Transport transport) { void testHttp1Transport(Http1Transport transport) {
import core.thread; import core.thread;
import std.string; import std.string;
import slf4d.default_provider;
auto loggingProvider = new DefaultProvider(Levels.DEBUG);
configureLoggingProvider(loggingProvider);
infoF!"Testing Http1Transport implementation: %s"(transport); infoF!"Testing Http1Transport implementation: %s"(transport);
Thread thread = transport.startInNewThread(); Thread thread = transport.startInNewThread();
@ -102,6 +107,8 @@ version(unittest) {
info("Testing is complete. Stopping the server."); info("Testing is complete. Stopping the server.");
transport.stop(); transport.stop();
thread.join(); thread.join();
resetLoggingState();
} }
} }