1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-25 22:45:06 +00:00
serenity/Userland/Services/WebServer/main.cpp
Thomas Keppler 1d6528b94b WebServer: Use new String type for default option values
We've also pulled out the default root path instead of folding it in
with the receiving variables, so that it's uniform across all options
with default values.
2022-12-26 09:38:03 +01:00

110 lines
4.1 KiB
C++

/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2021, Max Wipfli <mail@maxwipfli.ch>
* Copyright (c) 2022, Thomas Keppler <serenity@tkeppler.de>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/String.h>
#include <LibCore/ArgsParser.h>
#include <LibCore/EventLoop.h>
#include <LibCore/File.h>
#include <LibCore/MappedFile.h>
#include <LibCore/System.h>
#include <LibCore/TCPServer.h>
#include <LibHTTP/HttpRequest.h>
#include <LibMain/Main.h>
#include <WebServer/Client.h>
#include <WebServer/Configuration.h>
#include <stdio.h>
#include <unistd.h>
ErrorOr<int> serenity_main(Main::Arguments arguments)
{
static auto const default_listen_address = TRY(String::from_utf8("0.0.0.0"sv));
static auto const default_port = 8000;
static auto const default_document_root_path = TRY(String::from_utf8("/www"sv));
DeprecatedString listen_address = default_listen_address.to_deprecated_string();
int port = default_port;
DeprecatedString username;
DeprecatedString password;
DeprecatedString document_root_path = default_document_root_path.to_deprecated_string();
Core::ArgsParser args_parser;
args_parser.add_option(listen_address, "IP address to listen on", "listen-address", 'l', "listen_address");
args_parser.add_option(port, "Port to listen on", "port", 'p', "port");
args_parser.add_option(username, "HTTP basic authentication username", "user", 'U', "username");
args_parser.add_option(password, "HTTP basic authentication password", "pass", 'P', "password");
args_parser.add_positional_argument(document_root_path, "Path to serve the contents of", "path", Core::ArgsParser::Required::No);
args_parser.parse(arguments);
auto ipv4_address = IPv4Address::from_string(listen_address);
if (!ipv4_address.has_value()) {
warnln("Invalid listen address: {}", listen_address);
return 1;
}
if ((u16)port != port) {
warnln("Invalid port number: {}", port);
return 1;
}
if (username.is_empty() != password.is_empty()) {
warnln("Both username and password are required for HTTP basic authentication.");
return 1;
}
auto real_document_root_path = Core::File::real_path_for(document_root_path);
if (!Core::File::exists(real_document_root_path)) {
warnln("Root path does not exist: '{}'", document_root_path);
return 1;
}
TRY(Core::System::pledge("stdio accept rpath inet unix"));
Optional<HTTP::HttpRequest::BasicAuthenticationCredentials> credentials;
if (!username.is_empty() && !password.is_empty())
credentials = HTTP::HttpRequest::BasicAuthenticationCredentials { username, password };
WebServer::Configuration configuration(real_document_root_path, credentials);
Core::EventLoop loop;
auto server = TRY(Core::TCPServer::try_create());
server->on_ready_to_accept = [&] {
auto maybe_client_socket = server->accept();
if (maybe_client_socket.is_error()) {
warnln("Failed to accept the client: {}", maybe_client_socket.error());
return;
}
auto maybe_buffered_socket = Core::Stream::BufferedTCPSocket::create(maybe_client_socket.release_value());
if (maybe_buffered_socket.is_error()) {
warnln("Could not obtain a buffered socket for the client: {}", maybe_buffered_socket.error());
return;
}
// FIXME: Propagate errors
MUST(maybe_buffered_socket.value()->set_blocking(true));
auto client = WebServer::Client::construct(maybe_buffered_socket.release_value(), server);
client->start();
};
TRY(server->listen(ipv4_address.value(), port));
out("Listening on ");
out("\033]8;;http://{}:{}\033\\", ipv4_address.value(), port);
out("{}:{}", ipv4_address.value(), port);
outln("\033]8;;\033\\");
TRY(Core::System::unveil("/etc/timezone", "r"));
TRY(Core::System::unveil("/res/icons", "r"));
TRY(Core::System::unveil(real_document_root_path, "r"sv));
TRY(Core::System::unveil(nullptr, nullptr));
TRY(Core::System::pledge("stdio accept rpath"));
return loop.exec();
}