4#include "Stroika/Frameworks/StroikaPreComp.h"
8#include "Stroika/Foundation/Characters/String2Int.h"
12#include "Stroika/Foundation/Execution/CommandLine.h"
13#include "Stroika/Foundation/Execution/Module.h"
14#include "Stroika/Foundation/Execution/SignalHandlers.h"
15#include "Stroika/Foundation/Execution/TimeOutException.h"
16#include "Stroika/Foundation/Execution/WaitableEvent.h"
17#include "Stroika/Foundation/IO/Network/HTTP/Exception.h"
18#include "Stroika/Foundation/IO/Network/HTTP/Headers.h"
19#include "Stroika/Foundation/IO/Network/HTTP/Methods.h"
23#include "Stroika/Frameworks/WebServer/ConnectionManager.h"
24#include "Stroika/Frameworks/WebServer/FileSystemRequestHandler.h"
25#include "Stroika/Frameworks/WebServer/Router.h"
27#include "AppVersion.h"
42using Stroika::Frameworks::WebServer::Response;
51 const FileSystemRequestHandler::Options kFileSystemRouterOptions_{[] () {
54 return FileSystemRequestHandler::Options{.fURLPrefix2Strip =
"/Files/"_k,
56 .fCacheControlSettings = cacheControlSettings};
62 const Headers kDefaultResponseHeaders_{[] () {
64 h.
server =
"Stroika-Sample-WebServer/"_k + AppVersion::kVersion.AsMajorMinorString ();
88 MyWebServer_ (uint16_t portNumber)
97 Route{
""_RegEx, DefaultPage_}
102 ,
Route{HTTP::MethodsRegEx::kPost,
"SetAppState"_RegEx, SetAppState_}
107 ,
Route{
"FRED/?"_RegEx,
108 [] (
Request&, Response& response) {
109 response.contentType = DataExchange::InternetMediaTypes::kText_PLAIN;
110 response.write (
"FRED"sv);
121 ,
Route{
"stats"_RegEx, [
this] (
Message& m) { PrintServerStats_ (m);} }
127 cerr <<
"Listening on {}..."_f(fConnectionMgr_.bindings ()) << endl;
131 static void DefaultPage_ (
Request& request, Response& response)
136 if (request.
url ().LookupQueryArg (
"useChunked"sv) ==
"true"sv) {
137 response.automaticTransferChunkSize = 25;
139 else if (request.
url ().LookupQueryArg (
"useChunked"sv) ==
"false"sv) {
140 response.automaticTransferChunkSize = Response::kNoChunkedTransfer;
146 response.contentType = DataExchange::InternetMediaTypes::kHTML;
150 response.writeln (
"<html><body>"sv);
151 response.writeln (
"<p>Run the service (under the debugger if you wish)</p>"sv);
152 response.writeln (
"<ul>"sv);
153 response.writeln (
"<li>curl -v http://localhost:8080/ OR</li>"sv);
154 response.writeln (
"<li>curl -v http://localhost:8080/?useChunked=true OR</li>"sv);
155 response.writeln (
"<li>curl -v http://localhost:8080/FRED OR (to see error handling)</li>"sv);
156 response.writeln (
"<li>curl -v -H \"Content-Type: application/json\" -X POST -d '{\"AppState\":\"Start\"}' http://localhost:8080/SetAppState</li>"sv);
157 response.writeln (
"<li>curl -v http://localhost:8080/Files/index.html</li>"sv);
158 response.writeln (
"<li>curl -v http://localhost:8080/stats OR</li>"sv);
159 response.writeln (
"</ul>"sv);
160 response.writeln (
"</body></html>"sv);
162 void PrintServerStats_ (
Message& m)
164 auto stats = this->fConnectionMgr_.statistics ();
165 auto connections = this->fConnectionMgr_.connections ();
167 r.contentType = DataExchange::InternetMediaTypes::kText_PLAIN;
168 r.writeln (
"statistics: {");
169 r.writeln (
" {},"_f(stats));
171 r.writeln (
"connections: [");
172 for (
auto s : connections) {
175 r.writeln (
" {},"_f(s));
180 static void SetAppState_ (
Message& message)
182 message.
rwResponse ().contentType = DataExchange::InternetMediaTypes::kHTML;
184 message.
rwResponse ().writeln (
"<html><body><p>Hi SetAppState ("sv + argsAsString +
")</p></body></html>");
189int main (
int argc,
const char* argv[])
194#if qStroika_Foundation_Common_Platform_POSIX
198 uint16_t portNumber = 8080;
201 .fLongName =
"port"sv, .fSupportsArgument =
true, .fHelpOptionText =
"specify webserver listen port (default {})"_f(portNumber)};
203 .fLongName =
"quit-after"sv, .fSupportsArgument =
true, .fHelpOptionText =
"automatically quit after <argument> seconds"sv};
207 cmdLine.Validate (kAllOptions_);
210 cerr << Characters::ToString (current_exception ()).AsNarrowSDKString () << endl;
211 cerr << cmdLine.GenerateUsage (kAllOptions_).AsNarrowSDKString () << endl;
214 if (cmdLine.Has (StandardCommandLineOptions::kHelp)) {
215 cerr << cmdLine.GenerateUsage (kAllOptions_).AsNarrowSDKString () << endl;
221 if (
auto o = cmdLine.GetArgument (kPortO_)) {
222 portNumber = Characters::String2Int<uint16_t> (*o);
224 if (
auto o = cmdLine.GetArgument (kQuitAfterO_)) {
225 quitAfter =
Time::DurationSeconds{Characters::FloatConversion::ToFloat<Time::DurationSeconds::rep> (*o)};
227 MyWebServer_ myWebServer{portNumber};
231 cerr <<
"Timed out - so - exiting..." << endl;
235 cerr <<
"Error encountered: " << Characters::ToString (current_exception ()).AsNarrowSDKString () <<
" - terminating..." << endl;
chrono::duration< double > DurationSeconds
chrono::duration<double> - a time span (length of time) measured in seconds, but high precision.
RegularExpression is a compiled regular expression which can be used to match on a String class.
String is like std::u32string, except it is much easier to use, often much more space efficient,...
A generalization of a vector: a container whose elements are keyed by the natural numbers.
nonvirtual void SetSignalHandlers(SignalID signal)
static SignalHandlerRegistry & Get()
static const SignalHandler kIGNORED
nonvirtual void Wait(Time::DurationSeconds timeout=Time::kInfinity)
Common::Property< URI > url
Duration is a chrono::duration<double> (=.
Common::ReadOnlyProperty< Response & > rwResponse
Common::ReadOnlyProperty< Request & > rwRequest
this represents a HTTP request object for the WebServer module
filesystem::path GetEXEDir()
Traversal::Iterable< SocketAddress > SocketAddresses(const Traversal::Iterable< InternetAddress > &internetAddresses, PortType portNumber)
Traversal::Iterable< InternetAddress > InternetAddresses_Any(InternetProtocol::IP::IPVersionSupport ipSupport=InternetProtocol::IP::IPVersionSupport::eDEFAULT)
Ptr New(const InputStream::Ptr< byte > &src, optional< AutomaticCodeCvtFlags > codeCvtFlags={}, optional< SeekableFlag > seekable={}, ReadAhead readAhead=eReadAheadAllowed)
Create an InputStream::Ptr<Character> from the arguments (usually binary source) - which can be used ...
optional< uint32_t > fMaxAge
ConnectionManager::Options specify things like default headers, caching policies, binding flags (not ...
optional< Socket::BindFlags > fBindFlags