diff --git a/examples/book_server/main.cc b/examples/book_server/main.cc index ef5380b..e551dce 100644 --- a/examples/book_server/main.cc +++ b/examples/book_server/main.cc @@ -34,7 +34,7 @@ int main(int argc, char* argv[]) { std::cout << "Book photos will be saved to: " << photo_dir << std::endl; try { - webcc::Server server{ port }; // No doc root + webcc::Server server{ asio::ip::tcp::v4(), port }; server.Route("/books", std::make_shared(), diff --git a/examples/form_server.cc b/examples/form_server.cc index 8b9d452..5245183 100644 --- a/examples/form_server.cc +++ b/examples/form_server.cc @@ -45,7 +45,7 @@ int main(int argc, char* argv[]) { std::uint16_t port = static_cast(std::atoi(argv[1])); try { - webcc::Server server(port); + webcc::Server server{ asio::ip::tcp::v4(), port }; server.Route("/upload", std::make_shared(), { "POST" }); diff --git a/examples/hello_world_server.cc b/examples/hello_world_server.cc index 550009b..9473408 100644 --- a/examples/hello_world_server.cc +++ b/examples/hello_world_server.cc @@ -40,7 +40,7 @@ int main(int argc, const char* argv[]) { LOG_USER("Sleep seconds: %d", sleep_seconds); try { - webcc::Server server{ 8080 }; + webcc::Server server{ asio::ip::tcp::v4(), 8080 }; auto view = std::make_shared(sleep_seconds); server.Route("/", view); diff --git a/examples/server_states.cc b/examples/server_states.cc index 873d92f..bbd2a6c 100644 --- a/examples/server_states.cc +++ b/examples/server_states.cc @@ -17,7 +17,7 @@ int main() { WEBCC_LOG_INIT("", webcc::LOG_CONSOLE); try { - webcc::Server server{ 8080 }; + webcc::Server server{ asio::ip::tcp::v4(), 8080 }; server.Route("/", std::make_shared()); diff --git a/examples/static_file_server.cc b/examples/static_file_server.cc index 22866c9..d5642bf 100644 --- a/examples/static_file_server.cc +++ b/examples/static_file_server.cc @@ -23,7 +23,7 @@ int main(int argc, char* argv[]) { std::string doc_root = argv[2]; try { - webcc::Server server{ port, doc_root }; + webcc::Server server{ asio::ip::tcp::v4(), port, doc_root }; if (argc == 4) { server.set_file_chunk_size(std::atoi(argv[3])); diff --git a/unittest/url_unittest.cc b/unittest/url_unittest.cc index e3c7786..6d7e33c 100644 --- a/unittest/url_unittest.cc +++ b/unittest/url_unittest.cc @@ -81,3 +81,14 @@ TEST(UrlTest, Full) { EXPECT_EQ("/path/to", url.path()); EXPECT_EQ("key=value", url.query()); } + + +TEST(UrlTest, IPv6) { + webcc::Url url("http://[::1]:8080"); + + EXPECT_EQ("http", url.scheme()); + EXPECT_EQ("[::1]", url.host()); + EXPECT_EQ("8080", url.port()); + EXPECT_EQ("", url.path()); + EXPECT_EQ("", url.query()); +} diff --git a/webcc/client.cc b/webcc/client.cc index 4588656..4ede0df 100644 --- a/webcc/client.cc +++ b/webcc/client.cc @@ -102,7 +102,9 @@ void Client::DoConnect(RequestPtr request, const std::string& default_port) { LOG_VERB("Resolve host (%s)...", request->host().c_str()); std::error_code ec; - auto endpoints = resolver.resolve(tcp::v4(), request->host(), port, ec); + + // The protocol depends on the `host`, both V4 and V6 are supported. + auto endpoints = resolver.resolve(request->host(), port, ec); if (ec) { LOG_ERRO("Host resolve error (%s): %s, %s.", ec.message().c_str(), diff --git a/webcc/server.cc b/webcc/server.cc index bb2d9ff..0229e1f 100644 --- a/webcc/server.cc +++ b/webcc/server.cc @@ -16,9 +16,15 @@ using tcp = asio::ip::tcp; namespace webcc { -Server::Server(std::uint16_t port, const std::filesystem::path& doc_root) - : port_(port), doc_root_(doc_root), file_chunk_size_(1024), running_(false), - acceptor_(io_context_), signals_(io_context_) { +Server::Server(asio::ip::tcp protocol, std::uint16_t port, + const sfs::path& doc_root) + : protocol_(protocol), + port_(port), + doc_root_(doc_root), + file_chunk_size_(1024), + running_(false), + acceptor_(io_context_), + signals_(io_context_) { AddSignals(); } @@ -112,7 +118,7 @@ void Server::AsyncWaitSignals() { bool Server::Listen(std::uint16_t port) { std::error_code ec; - tcp::endpoint endpoint(tcp::v4(), port); + tcp::endpoint endpoint(protocol_, port); // Open the acceptor. acceptor_.open(endpoint.protocol(), ec); @@ -296,7 +302,7 @@ bool Server::MatchViewOrStatic(const std::string& method, // Try to match a static file. if (method == methods::kGet && !doc_root_.empty()) { - std::filesystem::path path = doc_root_ / url; + sfs::path path = doc_root_ / url; if (!sfs::is_directory(path) && sfs::exists(path)) { return true; } @@ -313,7 +319,7 @@ ResponsePtr Server::ServeStatic(RequestPtr request) { return {}; } - std::filesystem::path path = doc_root_ / request->url().path(); + sfs::path path = doc_root_ / request->url().path(); try { // NOTE: FileBody might throw Error::kFileError. diff --git a/webcc/server.h b/webcc/server.h index 853d4f7..fa968c5 100644 --- a/webcc/server.h +++ b/webcc/server.h @@ -20,8 +20,8 @@ namespace webcc { class Server : public Router { public: - explicit Server(std::uint16_t port, - const std::filesystem::path& doc_root = {}); + Server(asio::ip::tcp protocol, std::uint16_t port, + const std::filesystem::path& doc_root = {}); ~Server() = default; @@ -94,6 +94,9 @@ private: ResponsePtr ServeStatic(RequestPtr request); private: + // tcp::v4() or tcp::v6() + asio::ip::tcp protocol_; + // Port number. std::uint16_t port_; diff --git a/webcc/url.cc b/webcc/url.cc index 86daf2f..3837bb1 100644 --- a/webcc/url.cc +++ b/webcc/url.cc @@ -274,10 +274,15 @@ void Url::Parse(const std::string& str) { } if (!host_.empty()) { - p = host_.find(':'); + // Check if there's a port. + p = host_.find_last_of(':'); if (p != std::string::npos) { - port_ = host_.substr(p + 1); - host_ = host_.substr(0, p); + // For IPv6: [::1]:8080 + std::size_t bracket = host_.find_last_of(']'); + if (bracket == std::string::npos || p > bracket) { + port_ = host_.substr(p + 1); + host_ = host_.substr(0, p); + } } } }