diff --git a/.github/workflows/ci.linux.arm.yml b/.github/workflows/ci.linux.arm.yml index d352064e..c7766b4b 100644 --- a/.github/workflows/ci.linux.arm.yml +++ b/.github/workflows/ci.linux.arm.yml @@ -2,9 +2,9 @@ name: Linux ARM on: push: - branches: [ "main" ] + branches: [ "main", "release/*" ] pull_request: - branches: [ "main" ] + branches: [ "main", "release/*" ] jobs: centos8-gcc921-epoll-release: diff --git a/.github/workflows/ci.linux.x86.yml b/.github/workflows/ci.linux.x86.yml index 79cd2cb6..940ac7ca 100644 --- a/.github/workflows/ci.linux.x86.yml +++ b/.github/workflows/ci.linux.x86.yml @@ -2,9 +2,9 @@ name: Linux x86 on: push: - branches: [ "main" ] + branches: [ "main", "release/*" ] pull_request: - branches: [ "main" ] + branches: [ "main", "release/*" ] jobs: centos8-gcc921-epoll-release: diff --git a/.github/workflows/ci.macos.arm.yml b/.github/workflows/ci.macos.arm.yml index 391ed891..c8e17718 100644 --- a/.github/workflows/ci.macos.arm.yml +++ b/.github/workflows/ci.macos.arm.yml @@ -2,9 +2,9 @@ name: macOS ARM on: push: - branches: [ "main" ] + branches: [ "main", "release/*" ] pull_request: - branches: [ "main" ] + branches: [ "main", "release/*" ] jobs: macOS-clang-debug: diff --git a/.github/workflows/ci.macos.yml b/.github/workflows/ci.macos.yml index 2b40153e..5e75d552 100644 --- a/.github/workflows/ci.macos.yml +++ b/.github/workflows/ci.macos.yml @@ -2,9 +2,9 @@ name: macOS on: push: - branches: [ "main" ] + branches: [ "main", "release/*" ] pull_request: - branches: [ "main" ] + branches: [ "main", "release/*" ] jobs: macOS-12-Monterey-debug: diff --git a/doc/docs/api/network.md b/doc/docs/api/network.md index abd63e13..d555cfd8 100644 --- a/doc/docs/api/network.md +++ b/doc/docs/api/network.md @@ -23,6 +23,9 @@ Network lib provides non-blocking socket implementations for clients and servers ISocketClient* new_tcp_socket_client(); ISocketServer* new_tcp_socket_server(); +ISocketClient* new_tcp_socket_client_ipv6(); +ISocketServer* new_tcp_socket_server_ipv6(); + ISocketClient* new_uds_client(); ISocketServer* new_uds_server(bool autoremove = false); @@ -42,6 +45,11 @@ ISocketClient* new_fstack_dpdk_socket_client(); ISocketServer* new_fstack_dpdk_socket_server(); ``` +:::note +An IPv6 socket server listening on `::0` can handle both v4 and v6 socket client. +::: + + #### Class Method ```cpp @@ -95,36 +103,55 @@ namespace net { } ``` -#### IPv4 Address +#### Address and Endpoint ```cpp namespace photon { namespace net { - union IPAddr { - // Save IP address as a 32 bits integer, or 4 bytes. - uint32_t addr = 0; - struct { uint8_t a, b, c, d; }; - - // Create from the Internet host address, in network byte order, or from string + struct IPAddr { + in6_addr addr; + // For compatibility, the default constructor is still 0.0.0.0 (IPv4) + IPAddr(); + // V6 constructor (Internet Address) + explicit IPAddr(in6_addr internet_addr); + // V6 constructor (Network byte order) + IPAddr(uint32_t nl1, uint32_t nl2, uint32_t nl3, uint32_t nl4); + // V4 constructor (Internet Address) + explicit IPAddr(in_addr internet_addr); + // V4 constructor (Network byte order) explicit IPAddr(uint32_t nl); - explicit IPAddr(const char* s) - + // String constructor + explicit IPAddr(const char* s); + // Check if it's actually an IPv4 address mapped in IPV6 + bool is_ipv4(); + // We regard the default IPv4 0.0.0.0 as undefined + bool undefined(); + // Should ONLY be used for IPv4 address uint32_t to_nl() const; - void from_nl(uint32_t nl); + bool is_loopback() const; + bool is_broadcast() const; + bool is_link_local() const; + bool operator==(const IPAddr& rhs) const; + bool operator!=(const IPAddr& rhs) const; + static IPAddr V6None(); + static IPAddr V6Any(); + static IPAddr V6Loopback(); + static IPAddr V4Broadcast(); + static IPAddr V4Any(); + static IPAddr V4Loopback(); }; // EndPoint represents IP address and port + // A default endpoint is undefined (0.0.0.0:0) struct EndPoint { IPAddr addr; - uint16_t port; - - // convert from the typical struct sockaddr_in - sockaddr_in to_sockaddr_in() const; - void from_sockaddr_in(const struct sockaddr_in& addr_in); + uint16_t port = 0; + EndPoint() = default; + EndPoint(IPAddr ip, uint16_t port); + bool is_ipv4() const; + bool operator==(const EndPoint& rhs) const; + bool operator!=(const EndPoint& rhs) const; + bool undefined() const; }; - - // operators to help logging IP addresses with alog - LogBuffer& operator << (LogBuffer& log, const IPAddr addr); - LogBuffer& operator << (LogBuffer& log, const EndPoint ep); } } ``` diff --git a/doc/docs/introduction/how-to-build.md b/doc/docs/introduction/how-to-build.md index 2562bf0a..963a048a 100644 --- a/doc/docs/introduction/how-to-build.md +++ b/doc/docs/introduction/how-to-build.md @@ -85,8 +85,7 @@ cmake --build build -j ```bash cd PhotonLibOS -# Use `brew info openssl` to find openssl path -cmake -B build -D OPENSSL_ROOT_DIR=/path/to/openssl/ +cmake -B build cmake --build build -j ``` @@ -178,3 +177,10 @@ ctest | PHOTON_ENABLE_FSTACK_DPDK | OFF | Enable F-Stack and DPDK. Requires both. | | PHOTON_ENABLE_EXTFS | OFF | Enable extfs. Requires `libe2fs` | +#### Example + +If there is any shared lib you don't want Photon to link to on local host, build its static from source. + +```bash +cmake -B build -D PHOTON_BUILD_DEPENDENCIES=ON -D PHOTON_GFLAGS_SOURCE=https://github.com/gflags/gflags/archive/refs/tags/v2.2.2.tar.gz +``` diff --git a/net/http/test/client_function_test.cpp b/net/http/test/client_function_test.cpp index 29151c0d..7af2d2df 100644 --- a/net/http/test/client_function_test.cpp +++ b/net/http/test/client_function_test.cpp @@ -84,11 +84,12 @@ TEST(http_client, get) { auto op2 = client->new_operation(Verb::GET, target); DEFER(delete op2); op2->req.headers.content_length(0); - client->call(op2); + int ret = client->call(op2); + GTEST_ASSERT_EQ(0, ret); char resp_body_buf[1024]; EXPECT_EQ(sizeof(socket_buf), op2->resp.resource_size()); - auto ret = op2->resp.read(resp_body_buf, sizeof(socket_buf)); + ret = op2->resp.read(resp_body_buf, sizeof(socket_buf)); EXPECT_EQ(sizeof(socket_buf), ret); resp_body_buf[sizeof(socket_buf) - 1] = '\0'; LOG_DEBUG(resp_body_buf); diff --git a/net/utils.cpp b/net/utils.cpp index 28827d77..2137b47e 100644 --- a/net/utils.cpp +++ b/net/utils.cpp @@ -256,23 +256,34 @@ bool Base64Decode(std::string_view in, std::string &out) { class DefaultResolver : public Resolver { public: - DefaultResolver(uint64_t cache_ttl, uint64_t resolve_timeout) - : dnscache_(cache_ttl), resolve_timeout_(resolve_timeout) {} + DefaultResolver(uint64_t cache_ttl, uint64_t resolve_timeout, bool ipv6) + : dnscache_(cache_ttl), resolve_timeout_(resolve_timeout), ipv6_(ipv6) {} ~DefaultResolver() { dnscache_.clear(); } IPAddr resolve(const char *host) override { auto ctr = [&]() -> IPAddr * { - auto *ip = new IPAddr(); + std::vector addrs; photon::semaphore sem; std::thread([&]() { - *ip = gethostbyname(host); + int ret = gethostbyname(host, addrs); + if (ret < 0) { + addrs.clear(); + } sem.signal(1); }).detach(); auto ret = sem.wait(1, resolve_timeout_); if (ret < 0 && errno == ETIMEDOUT) { LOG_WARN("Domain resolution for ` timeout!", host); + return new IPAddr; // undefined addr + } else if (addrs.empty()) { + LOG_WARN("Domain resolution for ` failed", host); + return new IPAddr; // undefined addr } - return ip; + for (auto& each : addrs) { + if ((each.is_ipv4() ^ !ipv6_) == 0) + return new IPAddr(each); + } + return new IPAddr; // undefined addr }; return *(dnscache_.borrow(host, ctr)); } @@ -287,10 +298,11 @@ class DefaultResolver : public Resolver { private: ObjectCache dnscache_; uint64_t resolve_timeout_; + bool ipv6_; }; -Resolver* new_default_resolver(uint64_t cache_ttl, uint64_t resolve_timeout) { - return new DefaultResolver(cache_ttl, resolve_timeout); +Resolver* new_default_resolver(uint64_t cache_ttl, uint64_t resolve_timeout, bool ipv6) { + return new DefaultResolver(cache_ttl, resolve_timeout, ipv6); } } // namespace net diff --git a/net/utils.h b/net/utils.h index 9420686e..b5457c3a 100644 --- a/net/utils.h +++ b/net/utils.h @@ -153,7 +153,7 @@ bool zerocopy_available(); */ class Resolver : public Object { public: - // When failed, IPAddr(0) should be returned. + // When failed, return an Undefined IPAddr // Normally dns servers return multiple ips in random order, choosing the first one should suffice. virtual IPAddr resolve(const char* host) = 0; virtual void resolve(const char* host, Delegate func) = 0; @@ -165,9 +165,10 @@ class Resolver : public Object { * * @param cache_ttl cache's lifetime in microseconds. * @param resolve_timeout timeout in microseconds for domain resolution. + * @param ipv6 specify v4 or v6 domain name * @return Resolver* */ -Resolver* new_default_resolver(uint64_t cache_ttl = 3600UL * 1000000, uint64_t resolve_timeout = -1); +Resolver* new_default_resolver(uint64_t cache_ttl = 3600UL * 1000000, uint64_t resolve_timeout = -1, bool ipv6 = false); } // namespace net }