Sane C++ Libraries
C++ Platform Abstraction Libraries
Loading...
Searching...
No Matches
SC::HttpAsyncFileServer Struct Reference

Http file server statically serves files from a directory. More...

#include <HttpAsyncFileServer.h>

Classes

struct  Stream
 Support class for HttpAsyncFileServer holding file stream and pipeline. More...
 
struct  StreamQueue
 

Public Member Functions

Result init (ThreadPool &threadPool, AsyncEventLoop &loop, StringSpan directoryToServe)
 Initialize the web server on the given file system directory to serve.
 
Result close ()
 Removes any reference to the arguments passed during init.
 
Result handleRequest (HttpAsyncFileServer::Stream &stream, HttpConnection &connection)
 Handles the request, serving the requested file (GET) or creating a new one (PUT/POST) Call this method in response to HttpConnectionsPool::onRequest.
 

Detailed Description

Http file server statically serves files from a directory.

This class registers the onRequest callback provided by HttpAsyncServer to serves files from a given directory.

Example using compile time set buffers for connections:

constexpr int MAX_CONNECTIONS = 16; // Max number of concurrent http connections
constexpr int REQUEST_SLICES = 2; // Number of slices of the request buffer for each connection
constexpr int REQUEST_SIZE = 1 * 1024; // How many bytes are allocated to stream data for each connection
constexpr int HEADER_SIZE = 8 * 1024; // How many bytes are dedicated to hold request and response headers
constexpr int NUM_FS_THREADS = 4; // Number of threads in the thread pool for async file stream operations
// This class is fixing buffer sizes at compile time for simplicity but it's possible to size them at runtime
using HttpConnectionType = HttpAsyncConnection<REQUEST_SLICES, REQUEST_SLICES, HEADER_SIZE, REQUEST_SIZE>;
// 1. Memory to hold all http connections (single array for simplicity).
// WebServerExample (SCExample) shows how to leverage virtual memory, to handle dynamic number of clients
HttpConnectionType connections[MAX_CONNECTIONS];
// 2. Memory used by the async file streams started by file server.
HttpAsyncFileServer::StreamQueue<REQUEST_SLICES> streams[MAX_CONNECTIONS];
// Initialize and start the http and the file server
HttpAsyncServer httpServer;
HttpAsyncFileServer fileServer;
ThreadPool threadPool;
if (eventLoop.needsThreadPoolForFileOperations()) // no thread pool needed for io_uring
{
SC_TEST_EXPECT(threadPool.create(NUM_FS_THREADS));
}
SC_TEST_EXPECT(httpServer.init(Span<HttpConnectionType>(connections)));
SC_TEST_EXPECT(httpServer.start(eventLoop, "127.0.0.1", 8090));
SC_TEST_EXPECT(fileServer.init(threadPool, eventLoop, webServerFolder));
// Forward all http requests to the file server in order to serve files
httpServer.onRequest = [&](HttpConnection& connection)
{ SC_ASSERT_RELEASE(fileServer.handleRequest(streams[connection.getConnectionID().getIndex()], connection)); };

Example using dynamically allocated buffers for connections:

HttpAsyncServer httpServer;
HttpAsyncFileServer fileServer;
ThreadPool threadPool;
static constexpr size_t MAX_CONNECTIONS = 1000000; // Reserve space for max 1 million connections
static constexpr size_t MAX_READ_QUEUE = 10; // Max number of read queue buffers for each connection
static constexpr size_t MAX_WRITE_QUEUE = 10; // Max number of write queue buffers for each connection
static constexpr size_t MAX_BUFFERS = 10; // Max number of write queue buffers for each connection
static constexpr size_t MAX_REQUEST_SIZE = 1024 * 1024; // Max number of bytes to stream data for each connection
static constexpr size_t MAX_HEADER_SIZE = 32 * 1024; // Max number of bytes to hold request and response headers
static constexpr size_t NUM_FS_THREADS = 4; // Number of threads for async file stream operations
VirtualArray<HttpAsyncConnectionBase> clients = {MAX_CONNECTIONS};
// For simplicity just hardcode a read queue of 3 for file streams
VirtualArray<HttpAsyncFileServer::StreamQueue<3>> fileStreams = {MAX_CONNECTIONS};
VirtualArray<AsyncReadableStream::Request> allReadQueues = {MAX_CONNECTIONS * MAX_READ_QUEUE};
VirtualArray<AsyncWritableStream::Request> allWriteQueues = {MAX_CONNECTIONS * MAX_WRITE_QUEUE};
VirtualArray<AsyncBufferView> allBuffers = {MAX_CONNECTIONS * MAX_BUFFERS};
VirtualArray<char> allHeaders = {MAX_CONNECTIONS * MAX_HEADER_SIZE};
VirtualArray<char> allStreams = {MAX_CONNECTIONS * MAX_REQUEST_SIZE};
Result start()
{
SC_TRY(assignConnectionMemory(static_cast<size_t>(modelState.maxClients)));
// Optimization: only create a thread pool for FS operations if needed (i.e. when async backend != io_uring)
if (eventLoop->needsThreadPoolForFileOperations())
{
SC_TRY(threadPool.create(NUM_FS_THREADS));
}
// Initialize and start http and file servers, delegating requests to the latter in order to serve files
SC_TRY(httpServer.init(clients.toSpan()));
SC_TRY(httpServer.start(*eventLoop, modelState.interface.view(), static_cast<uint16_t>(modelState.port)));
SC_TRY(fileServer.init(threadPool, *eventLoop, modelState.directory.view()));
httpServer.onRequest = [&](HttpConnection& connection)
{
HttpAsyncFileServer::Stream& stream = fileStreams.toSpan()[connection.getConnectionID().getIndex()];
SC_ASSERT_RELEASE(fileServer.handleRequest(stream, connection));
};
return Result(true);
}
Result assignConnectionMemory(size_t numClients)
{
SC_TRY(clients.resize(numClients));
SC_TRY(fileStreams.resize(numClients));
SC_TRY(allReadQueues.resize(numClients * modelState.asyncConfiguration.readQueueSize));
SC_TRY(allWriteQueues.resize(numClients * modelState.asyncConfiguration.writeQueueSize));
SC_TRY(allBuffers.resize(numClients * modelState.asyncConfiguration.buffersQueueSize));
SC_TRY(allHeaders.resize(numClients * modelState.asyncConfiguration.headerBytesLength));
SC_TRY(allStreams.resize(numClients * modelState.asyncConfiguration.streamBytesLength));
HttpAsyncConnectionBase::Memory memory;
memory.allBuffers = allBuffers;
memory.allReadQueue = allReadQueues;
memory.allWriteQueue = allWriteQueues;
memory.allHeaders = allHeaders;
memory.allStreams = allStreams;
SC_TRY(memory.assignTo(modelState.asyncConfiguration, clients.toSpan()));
return Result(true);
}
Result runtimeResize()
{
const size_t numClients =
max(static_cast<size_t>(modelState.maxClients), httpServer.getConnections().getHighestActiveConnection());
SC_TRY(assignConnectionMemory(numClients));
SC_TRY(httpServer.resize(clients.toSpan()));
return Result(true);
}

Member Function Documentation

◆ close()

Result SC::HttpAsyncFileServer::close ( )

Removes any reference to the arguments passed during init.

◆ handleRequest()

Result SC::HttpAsyncFileServer::handleRequest ( HttpAsyncFileServer::Stream & stream,
HttpConnection & connection )

Handles the request, serving the requested file (GET) or creating a new one (PUT/POST) Call this method in response to HttpConnectionsPool::onRequest.

◆ init()

Result SC::HttpAsyncFileServer::init ( ThreadPool & threadPool,
AsyncEventLoop & loop,
StringSpan directoryToServe )

Initialize the web server on the given file system directory to serve.


The documentation for this struct was generated from the following file: