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.
 
void setUseAsyncFileSend (bool value)
 Controls whether to use AsyncFileSend optimization for GET requests (default: true)
 
bool getUseAsyncFileSend () const
 Gets whether AsyncFileSend optimization is used for GET requests.
 
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 = 1; // Max number of concurrent http connections (1 disables keep-alive)
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;
const uint16_t serverPort = report.mapPort(8090);
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", serverPort));
SC_TEST_EXPECT(fileServer.init(threadPool, eventLoop, webServerFolder));
fileServer.setUseAsyncFileSend(useAsyncFileSend);
// 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.

◆ getUseAsyncFileSend()

bool SC::HttpAsyncFileServer::getUseAsyncFileSend ( ) const
inlinenodiscard

Gets whether AsyncFileSend optimization is used for GET requests.

◆ 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.

◆ setUseAsyncFileSend()

void SC::HttpAsyncFileServer::setUseAsyncFileSend ( bool value)

Controls whether to use AsyncFileSend optimization for GET requests (default: true)


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