Copilot commented on code in PR #3179:
URL: https://github.com/apache/brpc/pull/3179#discussion_r2647470691


##########
src/brpc/socket.cpp:
##########
@@ -1283,8 +1283,10 @@ int Socket::Connect(const timespec* abstime,
         _ssl_state = SSL_OFF;
     }
     struct sockaddr_storage serv_addr;
+    struct sockaddr_storage cli_addr;
     socklen_t addr_size = 0;
-    if (butil::endpoint2sockaddr(remote_side(), &serv_addr, &addr_size) != 0) {
+    if (butil::endpoint2sockaddr(remote_side(), &serv_addr, &addr_size) != 0 ||
+        butil::endpoint2sockaddr(local_side(), &cli_addr, &addr_size) != 0) {

Review Comment:
   The code unconditionally calls endpoint2sockaddr on local_side(), but when 
client_host is not specified, local_side() will be default-initialized to 
IP_ANY with port 0. This will cause endpoint2sockaddr to succeed with an 
INADDR_ANY address, and the subsequent bind() call will always execute even 
when no client IP binding is intended. While binding to INADDR_ANY typically 
succeeds, this is unnecessary overhead and could potentially cause issues in 
certain network configurations. Consider checking if local_side() is non-empty 
before converting it to sockaddr.



##########
src/brpc/socket_map.cpp:
##########
@@ -92,8 +92,9 @@ SocketMap* get_or_new_client_side_socket_map() {
 int SocketMapInsert(const SocketMapKey& key, SocketId* id,
                     const std::shared_ptr<SocketSSLContext>& ssl_ctx,
                     bool use_rdma,
-                    const HealthCheckOption& hc_option) {
-    return get_or_new_client_side_socket_map()->Insert(key, id, ssl_ctx, 
use_rdma, hc_option);
+                    const HealthCheckOption& hc_option,
+                    butil::EndPoint& client_end_point) {

Review Comment:
   The client_end_point parameter should be passed by const reference for 
consistency and correctness. Change "butil::EndPoint& client_end_point" to 
"const butil::EndPoint& client_end_point".
   ```suggestion
                       const butil::EndPoint& client_end_point) {
   ```



##########
src/brpc/socket.cpp:
##########
@@ -1283,8 +1283,10 @@ int Socket::Connect(const timespec* abstime,
         _ssl_state = SSL_OFF;
     }
     struct sockaddr_storage serv_addr;
+    struct sockaddr_storage cli_addr;
     socklen_t addr_size = 0;
-    if (butil::endpoint2sockaddr(remote_side(), &serv_addr, &addr_size) != 0) {
+    if (butil::endpoint2sockaddr(remote_side(), &serv_addr, &addr_size) != 0 ||
+        butil::endpoint2sockaddr(local_side(), &cli_addr, &addr_size) != 0) {

Review Comment:
   The addr_size variable is shared between both endpoint2sockaddr calls, but 
it's only set by the first call (for remote_side). If local_side() and 
remote_side() have different address families (e.g., IPv4 vs IPv6), the 
addr_size from the remote_side conversion may not be appropriate for the 
local_side conversion. Consider using separate addr_size variables for each 
conversion: one for serv_addr and one for cli_addr.



##########
src/brpc/channel.h:
##########
@@ -148,6 +148,10 @@ struct ChannelOptions {
     // Its priority is higher than FLAGS_health_check_path and 
FLAGS_health_check_timeout_ms.
     // When it is not set, FLAGS_health_check_path and 
FLAGS_health_check_timeout_ms will take effect.
     HealthCheckOption hc_option;
+
+    // IP address or host name of the client

Review Comment:
   The documentation comment is too brief. Consider adding more details about 
the expected format of client_host (IP address or hostname), behavior when the 
value is invalid, and any platform-specific considerations. For example, 
clarify whether IPv4, IPv6, or both are supported, and mention that the 
IP/hostname will be bound to the client socket before connecting to the server.
   ```suggestion
       // Optional local address to bind the client-side socket to before
       // connecting to the server.
       //
       // Expected format:
       //   - An IP address (IPv4 or IPv6) or a resolvable host name.
       //   - Typically specified without port; the system chooses an ephemeral
       //     source port unless otherwise configured.
       //
       // Behavior:
       //   - When empty (""), the channel does not explicitly bind a local
       //     address and the OS selects an appropriate source address.
       //   - When non-empty, the value is resolved (if a host name) and used to
       //     bind the client socket's local address before connecting.
       //   - If the address/host name is invalid, cannot be resolved, or is not
       //     usable on the current platform or interface, channel 
initialization
       //     or subsequent connection attempts may fail with a network error.
       //
       // Notes:
       //   - Both IPv4 and IPv6 may be used, subject to OS and environment
       //     support.
       //   - Platform-specific socket behavior (for example, how wildcards are
       //     interpreted) is determined by the underlying operating system.
   ```



##########
src/brpc/socket.cpp:
##########
@@ -1297,6 +1299,10 @@ int Socket::Connect(const timespec* abstime,
     // We need to do async connect (to manage the timeout by ourselves).
     CHECK_EQ(0, butil::make_non_blocking(sockfd));
     
+    if (::bind(sockfd, (struct sockaddr*)& cli_addr, addr_size) != 0) {

Review Comment:
   There's an extra space in the cast: "(struct sockaddr*)& cli_addr" should be 
"(struct sockaddr*)&cli_addr" (no space between '&' and 'cli_addr') to be 
consistent with line 1307 where the same pattern is used correctly.
   ```suggestion
       if (::bind(sockfd, (struct sockaddr*)&cli_addr, addr_size) != 0) {
   ```



##########
src/brpc/socket.cpp:
##########
@@ -1297,6 +1299,10 @@ int Socket::Connect(const timespec* abstime,
     // We need to do async connect (to manage the timeout by ourselves).
     CHECK_EQ(0, butil::make_non_blocking(sockfd));
     
+    if (::bind(sockfd, (struct sockaddr*)& cli_addr, addr_size) != 0) {
+        LOG(FATAL) << "Fail to bind socket, errno=" << strerror(errno);

Review Comment:
   The strerror(errno) call should be replaced with berror() or PLOG macro to 
be consistent with error handling patterns used elsewhere in this file (see 
lines 1290, 1295, 1309, etc.). Also, errno might not be set if bind succeeds, 
so it should be saved immediately after the bind() call returns an error.
   ```suggestion
           const int saved_errno = errno;
           LOG(FATAL) << "Fail to bind socket: " << berror(saved_errno);
   ```



##########
src/brpc/socket.cpp:
##########
@@ -1297,6 +1299,10 @@ int Socket::Connect(const timespec* abstime,
     // We need to do async connect (to manage the timeout by ourselves).
     CHECK_EQ(0, butil::make_non_blocking(sockfd));
     
+    if (::bind(sockfd, (struct sockaddr*)& cli_addr, addr_size) != 0) {
+        LOG(FATAL) << "Fail to bind socket, errno=" << strerror(errno);
+        return -1;
+    }

Review Comment:
   The bind() call is executed unconditionally, even when local_side() is 
default-initialized (IP_ANY with port 0). This means bind() will be called for 
all connections, not just those where the user explicitly specified a 
client_host. Consider only calling bind() when local_side() represents a valid 
non-default endpoint. You can check this by comparing local_side() against 
butil::EndPoint() or checking if the IP is not IP_ANY.



##########
src/brpc/socket_map.h:
##########
@@ -82,18 +82,21 @@ struct SocketMapKeyHasher {
 int SocketMapInsert(const SocketMapKey& key, SocketId* id,
                     const std::shared_ptr<SocketSSLContext>& ssl_ctx,
                     bool use_rdma,
-                    const HealthCheckOption& hc_option);
+                    const HealthCheckOption& hc_option,
+                    butil::EndPoint& client_end_point);

Review Comment:
   The client_end_point parameter is passed by non-const reference, but it's 
only used as an input parameter (not modified). This should be passed by const 
reference to indicate that the function doesn't modify the parameter and to 
allow temporary objects to be passed. Change "butil::EndPoint& 
client_end_point" to "const butil::EndPoint& client_end_point".
   ```suggestion
                       const butil::EndPoint& client_end_point);
   ```



##########
src/brpc/socket_map.h:
##########
@@ -155,17 +158,20 @@ class SocketMap {
     int Insert(const SocketMapKey& key, SocketId* id,
                const std::shared_ptr<SocketSSLContext>& ssl_ctx,
                bool use_rdma,
-               const HealthCheckOption& hc_option);
+               const HealthCheckOption& hc_option,
+               butil::EndPoint& client_end_point);

Review Comment:
   The client_end_point parameter should be passed by const reference for 
consistency and correctness. Change "butil::EndPoint& client_end_point" to 
"const butil::EndPoint& client_end_point".
   ```suggestion
                  const butil::EndPoint& client_end_point);
   ```



##########
src/brpc/channel.cpp:
##########
@@ -397,6 +410,13 @@ int Channel::Init(const char* ns_url,
             _options.mutable_ssl_options()->sni_name = _service_name;
         }
     }
+    butil::EndPoint client_end_point;
+    if (!_options.client_host.empty() &&
+        butil::str2ip(_options.client_host.c_str(), &client_end_point.ip) != 0 
&&
+        butil::hostname2ip(_options.client_host.c_str(), &client_end_point.ip) 
!= 0) {
+        LOG(ERROR) << "Invalid client host=`" << _options.client_host << '\'';
+        return -1;
+    }

Review Comment:
   The logic for parsing client_host is duplicated in both InitSingle and Init 
methods (lines 370-376 and 413-419). Consider extracting this common logic into 
a helper method to avoid code duplication and ensure consistency. This will 
make maintenance easier and reduce the risk of divergence between the two 
implementations.



##########
src/brpc/socket_map.cpp:
##########
@@ -229,7 +230,8 @@ void SocketMap::ShowSocketMapInBvarIfNeed() {
 int SocketMap::Insert(const SocketMapKey& key, SocketId* id,
                       const std::shared_ptr<SocketSSLContext>& ssl_ctx,
                       bool use_rdma,
-                      const HealthCheckOption& hc_option) {
+                      const HealthCheckOption& hc_option,
+                      butil::EndPoint& client_end_point) {

Review Comment:
   The client_end_point parameter should be passed by const reference for 
consistency and correctness. Change "butil::EndPoint& client_end_point" to 
"const butil::EndPoint& client_end_point".
   ```suggestion
                         const butil::EndPoint& client_end_point) {
   ```



##########
src/brpc/socket.cpp:
##########
@@ -1297,6 +1299,10 @@ int Socket::Connect(const timespec* abstime,
     // We need to do async connect (to manage the timeout by ourselves).
     CHECK_EQ(0, butil::make_non_blocking(sockfd));
     
+    if (::bind(sockfd, (struct sockaddr*)& cli_addr, addr_size) != 0) {
+        LOG(FATAL) << "Fail to bind socket, errno=" << strerror(errno);

Review Comment:
   Using LOG(FATAL) for bind() failure will terminate the process. This is 
overly severe for what could be a user configuration error (e.g., invalid 
client IP). Consider using LOG(ERROR) or LOG(WARNING) instead and allowing the 
error to propagate up so that the application can handle it gracefully. The 
function already returns -1 on error, which is the appropriate error handling 
mechanism.
   ```suggestion
           LOG(ERROR) << "Fail to bind socket, errno=" << strerror(errno);
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to