main.cpp 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. /*
  2. * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
  3. * Copyright (c) 2021, Max Wipfli <mail@maxwipfli.ch>
  4. * Copyright (c) 2022, Thomas Keppler <serenity@tkeppler.de>
  5. *
  6. * SPDX-License-Identifier: BSD-2-Clause
  7. */
  8. #include <AK/String.h>
  9. #include <LibCore/ArgsParser.h>
  10. #include <LibCore/EventLoop.h>
  11. #include <LibCore/MappedFile.h>
  12. #include <LibCore/System.h>
  13. #include <LibCore/TCPServer.h>
  14. #include <LibFileSystem/FileSystem.h>
  15. #include <LibHTTP/HttpRequest.h>
  16. #include <LibMain/Main.h>
  17. #include <WebServer/Client.h>
  18. #include <WebServer/Configuration.h>
  19. #include <stdio.h>
  20. #include <unistd.h>
  21. ErrorOr<int> serenity_main(Main::Arguments arguments)
  22. {
  23. static auto const default_listen_address = TRY("0.0.0.0"_string);
  24. static auto const default_port = 8000;
  25. static auto const default_document_root_path = TRY("/www"_string);
  26. DeprecatedString listen_address = default_listen_address.to_deprecated_string();
  27. int port = default_port;
  28. DeprecatedString username;
  29. DeprecatedString password;
  30. DeprecatedString document_root_path = default_document_root_path.to_deprecated_string();
  31. Core::ArgsParser args_parser;
  32. args_parser.add_option(listen_address, "IP address to listen on", "listen-address", 'l', "listen_address");
  33. args_parser.add_option(port, "Port to listen on", "port", 'p', "port");
  34. args_parser.add_option(username, "HTTP basic authentication username", "user", 'U', "username");
  35. args_parser.add_option(password, "HTTP basic authentication password", "pass", 'P', "password");
  36. args_parser.add_positional_argument(document_root_path, "Path to serve the contents of", "path", Core::ArgsParser::Required::No);
  37. args_parser.parse(arguments);
  38. auto ipv4_address = IPv4Address::from_string(listen_address);
  39. if (!ipv4_address.has_value()) {
  40. warnln("Invalid listen address: {}", listen_address);
  41. return 1;
  42. }
  43. if ((u16)port != port) {
  44. warnln("Invalid port number: {}", port);
  45. return 1;
  46. }
  47. if (username.is_empty() != password.is_empty()) {
  48. warnln("Both username and password are required for HTTP basic authentication.");
  49. return 1;
  50. }
  51. auto real_document_root_path = TRY(FileSystem::real_path(document_root_path));
  52. if (!FileSystem::exists(real_document_root_path)) {
  53. warnln("Root path does not exist: '{}'", document_root_path);
  54. return 1;
  55. }
  56. TRY(Core::System::pledge("stdio accept rpath inet unix"));
  57. Optional<HTTP::HttpRequest::BasicAuthenticationCredentials> credentials;
  58. if (!username.is_empty() && !password.is_empty())
  59. credentials = HTTP::HttpRequest::BasicAuthenticationCredentials { username, password };
  60. WebServer::Configuration configuration(real_document_root_path, credentials);
  61. Core::EventLoop loop;
  62. auto server = TRY(Core::TCPServer::try_create());
  63. server->on_ready_to_accept = [&] {
  64. auto maybe_client_socket = server->accept();
  65. if (maybe_client_socket.is_error()) {
  66. warnln("Failed to accept the client: {}", maybe_client_socket.error());
  67. return;
  68. }
  69. auto maybe_buffered_socket = Core::BufferedTCPSocket::create(maybe_client_socket.release_value());
  70. if (maybe_buffered_socket.is_error()) {
  71. warnln("Could not obtain a buffered socket for the client: {}", maybe_buffered_socket.error());
  72. return;
  73. }
  74. // FIXME: Propagate errors
  75. MUST(maybe_buffered_socket.value()->set_blocking(true));
  76. auto client = WebServer::Client::construct(maybe_buffered_socket.release_value(), server);
  77. client->start();
  78. };
  79. TRY(server->listen(ipv4_address.value(), port));
  80. out("Listening on ");
  81. out("\033]8;;http://{}:{}\033\\", ipv4_address.value(), server->local_port());
  82. out("{}:{}", ipv4_address.value(), server->local_port());
  83. outln("\033]8;;\033\\");
  84. TRY(Core::System::unveil("/etc/timezone", "r"));
  85. TRY(Core::System::unveil("/res/icons", "r"));
  86. TRY(Core::System::unveil(real_document_root_path, "r"sv));
  87. TRY(Core::System::unveil(nullptr, nullptr));
  88. TRY(Core::System::pledge("stdio accept rpath"));
  89. return loop.exec();
  90. }