diff --git a/frameworks/js/napi/http/http_exec/include/http_exec.h b/frameworks/js/napi/http/http_exec/include/http_exec.h index a16f9420695b47cd40e1749b831385cdc6cc2dea..62bf7eacebcd64050eaedafa2ce972fa6d3e9fb7 100644 --- a/frameworks/js/napi/http/http_exec/include/http_exec.h +++ b/frameworks/js/napi/http/http_exec/include/http_exec.h @@ -104,6 +104,8 @@ private: static bool SetDnsResolvOption(CURL *curl, RequestContext *context); + static bool SetTCPOption(CURL *curl, RequestContext *context); + static bool SetCertPinnerOption(CURL *curl, RequestContext *context); static size_t OnWritingMemoryBody(const void *data, size_t size, size_t memBytes, void *userData); diff --git a/frameworks/js/napi/http/http_exec/src/http_exec.cpp b/frameworks/js/napi/http/http_exec/src/http_exec.cpp index eceea5a62e9326dc68837b9caba6e715eea1e07b..60f8b6f101ccfd370932035458ed80b8581c063f 100755 --- a/frameworks/js/napi/http/http_exec/src/http_exec.cpp +++ b/frameworks/js/napi/http/http_exec/src/http_exec.cpp @@ -475,18 +475,30 @@ void HttpExec::CacheCurlPerformanceTiming(CURL *handle, RequestContext *context) char *ip = nullptr; curl_easy_getinfo(handle, CURLINFO_PRIMARY_IP, &ip); int32_t errCode = context->IsExecOK() ? 0 : context->GetErrorCode(); + char *daddr = nullptr; + char *saddr = nullptr; + long dport = 0; + long sport = 0; + curl_easy_getinfo(handle, CURLINFO_LOCAL_IP, &saddr); + std::string anomSaddr = CommonUtils::ToAnonymousIp(saddr); + curl_easy_getinfo(handle, CURLINFO_LOCAL_PORT, &sport); + curl_easy_getinfo(handle, CURLINFO_PRIMARY_IP, &daddr); + std::string anomDaddr = CommonUtils::ToAnonymousIp(daddr); + curl_easy_getinfo(handle, CURLINFO_PRIMARY_PORT, &dport); NETSTACK_LOGI( "taskid=%{public}d" ", size:%{public}" CURL_FORMAT_CURL_OFF_T ", dns:%{public}.3f, connect:%{public}.3f, tls:%{public}.3f, firstSend:%{public}.3f" ", firstRecv:%{public}.3f, total:%{public}.3f, redirect:%{public}.3f" - ", errCode:%{public}d, RespCode:%{public}s, httpVer:%{public}s, method:%{public}s, osErr:%{public}ld", + ", errCode:%{public}d, RespCode:%{public}s, httpVer:%{public}s, method:%{public}s, osErr:%{public}ld" + ", saddr:%{public}s, sport:%{public}ld, daddr:%{public}s, dport:%{public}ld", context->GetTaskId(), size, dnsTime, connectTime == 0 ? 0 : connectTime - dnsTime, tlsTime == 0 ? 0 : tlsTime - connectTime, firstSendTime == 0 ? 0 : firstSendTime - std::max({dnsTime, connectTime, tlsTime}), firstRecvTime == 0 ? 0 : firstRecvTime - firstSendTime, totalTime, redirectTime, errCode, std::to_string(responseCode).c_str(), - std::to_string(httpVer).c_str(), context->options.GetMethod().c_str(), osErr); + std::to_string(httpVer).c_str(), context->options.GetMethod().c_str(), osErr, + anomSaddr.c_str(), sport, anomDaddr.c_str(), dport); #if HAS_NETMANAGER_BASE if (EventReport::GetInstance().IsValid()) { HttpPerfInfo httpPerfInfo; @@ -1379,6 +1391,7 @@ bool HttpExec::SetRequestOption(CURL *curl, RequestContext *context) SetDnsResolvOption(curl, context); SetDnsCacheOption(curl, context); SetIpResolve(curl, context); + SetTCPOption(curl, context); return true; } @@ -1900,6 +1913,29 @@ bool HttpExec::SetDnsCacheOption(CURL *curl, RequestContext *context) return true; } +bool HttpExec::SetTCPOption(CURL *curl, RequestContext *context) +{ + if (!context) { + NETSTACK_LOGE("context is nullptr"); + return false; + } + NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SOCKOPTDATA, &context->options, context); + NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SOCKOPTFUNCTION, + +[](void *clientp, curl_socket_t sock, curlsocktype type) -> int { + if (!clientp) { + return CURL_SOCKOPT_OK; + } + auto resp = reinterpret_cast(clientp); + HttpRequestOptions::TcpConfiguration config = resp->GetTCPOption(); + if (config.SetOptionToSocket(sock)) { + NETSTACK_LOGI("SetOptionToSocket userTimeout = %{public}d", config.userTimeout_); + } + + return CURL_SOCKOPT_OK; + }, context); + return true; +} + bool HttpExec::SetIpResolve(CURL *curl, RequestContext *context) { std::string addressFamily = context->options.GetAddressFamily(); diff --git a/frameworks/js/napi/http/options/include/http_request_options.h b/frameworks/js/napi/http/options/include/http_request_options.h index 77942a843531fab5d338eaa5f251cc44e93ffe0f..386e7c060bdb66c89596459fd6de700a8ac2a66b 100644 --- a/frameworks/js/napi/http/options/include/http_request_options.h +++ b/frameworks/js/napi/http/options/include/http_request_options.h @@ -86,6 +86,76 @@ class HttpRequestOptions final { public: HttpRequestOptions(); + // https://man7.org/linux/man-pages/man7/tcp.7.html + struct TcpConfiguration { + friend class HttpRequestOptions; + /* + TCP_KEEPIDLE (since Linux 2.4) + The time (in seconds) the connection needs to remain idle + before TCP starts sending keepalive probes, if the socket + option SO_KEEPALIVE has been set on this socket. This + option should not be used in code intended to be portable. + For example, if the server will send FIN packet after 60 seconds, we suggest that set to 61. + */ + int keepIdle_ = 60 * 5; // Default value according to Cloud Service. + /* + TCP_KEEPINTVL (since Linux 2.4) + The time (in seconds) between individual keepalive probes. + This option should not be used in code intended to be + portable. + == 1 means that we just probe once, if it fails, we close the connection. + */ + int keepCnt_ = 1; + /* + TCP_KEEPINTVL (since Linux 2.4) + The time (in seconds) between individual keepalive probes. + This option should not be used in code intended to be + portable. + */ + int keepInterval_ = 1; + + /* + TCP_USER_TIMEOUT (since Linux 2.6.37) + This option takes an unsigned int as an argument. When + the value is greater than 0, it specifies the maximum + amount of time in milliseconds that transmitted data may + remain unacknowledged, or buffered data may remain + untransmitted (due to zero window size) before TCP will + forcibly close the corresponding connection and return + ETIMEDOUT to the application. If the option value is + specified as 0, TCP will use the system default. + + Increasing user timeouts allows a TCP connection to + survive extended periods without end-to-end connectivity. + Decreasing user timeouts allows applications to "fail + fast", if so desired. Otherwise, failure may take up to + 20 minutes with the current system defaults in a normal + WAN environment. + + This option can be set during any state of a TCP + connection, but is effective only during the synchronized + states of a connection (ESTABLISHED, FIN-WAIT-1, FIN- + WAIT-2, CLOSE-WAIT, CLOSING, and LAST-ACK). Moreover, + when used with the TCP keepalive (SO_KEEPALIVE) option, + TCP_USER_TIMEOUT will override keepalive to determine when + to close a connection due to keepalive failure. + + The option has no effect on when TCP retransmits a packet, + nor when a keepalive probe is sent. + + This option, like many others, will be inherited by the + socket returned by accept(2), if it was set on the + listening socket. + + Further details on the user timeout feature can be found + in RFC 793 and RFC 5482 ("TCP User Timeout Option"). + */ + int userTimeout_ = HttpConstant::DEFAULT_READ_TIMEOUT; + + bool SetOptionToSocket(int sock); + void SetTcpUserTimeout(const uint32_t &timeout); + }; + void SetUrl(const std::string &url); void SetMethod(const std::string &method); @@ -179,6 +249,7 @@ public: std::vector GetMultiPartDataList(); [[nodiscard]] const TlsOption GetTlsOption() const; + [[nodiscard]] const TcpConfiguration GetTCPOption() const; [[nodiscard]] const ServerAuthentication GetServerAuthentication() const; @@ -247,6 +318,8 @@ private: ServerAuthentication serverAuthentication_; std::string addressFamily_; + + TcpConfiguration tcpOption_; }; } // namespace OHOS::NetStack::Http diff --git a/frameworks/js/napi/http/options/src/http_request_options.cpp b/frameworks/js/napi/http/options/src/http_request_options.cpp index 55967ff3392a01172a716c1728406bfb270d81c5..914139fd4eea085b0f1972efa79e5838083fef86 100644 --- a/frameworks/js/napi/http/options/src/http_request_options.cpp +++ b/frameworks/js/napi/http/options/src/http_request_options.cpp @@ -18,6 +18,9 @@ #include "netstack_common_utils.h" #include "netstack_log.h" +#if !defined(WINDOWS_PLATFORM) && !defined(MAC_PLATFORM) +#include +#endif #include "http_request_options.h" #include "secure_char.h" @@ -66,6 +69,7 @@ void HttpRequestOptions::SetHeader(const std::string &key, const std::string &va void HttpRequestOptions::SetReadTimeout(uint32_t readTimeout) { readTimeout_ = readTimeout; + tcpOption_.SetTcpUserTimeout(readTimeout); } void HttpRequestOptions::SetMaxLimit(uint32_t maxLimit) @@ -273,6 +277,11 @@ const TlsOption HttpRequestOptions::GetTlsOption() const return tlsOption_; } +const HttpRequestOptions::TcpConfiguration HttpRequestOptions::GetTCPOption() const +{ + return tcpOption_; +} + void HttpRequestOptions::SetServerAuthentication(const ServerAuthentication &serverAuthentication) { serverAuthentication_.authenticationType = serverAuthentication.authenticationType; @@ -359,4 +368,70 @@ std::string HttpRequestOptions::GetAddressFamily() const { return addressFamily_; } + +bool HttpRequestOptions::TcpConfiguration::SetOptionToSocket(int sock) +{ +#if !defined(WINDOWS_PLATFORM) && !defined(MAC_PLATFORM) + int proto = -1; + auto len = static_cast(sizeof(proto)); + /* + https://man7.org/linux/man-pages/man2/getsockopt.2.html + RETURN VALUE top + On success, zero is returned for the standard options. On error, + -1 is returned, and errno is set to indicate the error. + + Netfilter allows the programmer to define custom socket options + with associated handlers; for such options, the return value on + success is the value returned by the handler. + */ + auto res = getsockopt(sock, SOL_SOCKET, SO_PROTOCOL, &proto, &len); + if (res != 0 || proto != IPPROTO_TCP) { + return false; + } + if (setsockopt(sock, SOL_TCP, TCP_USER_TIMEOUT, &userTimeout_, sizeof(userTimeout_)) != 0) { + NETSTACK_LOGE("set TCP_USER_TIMEOUT failed, errno = %{public}d userTimeout = %{public}d sock = %{public}d", + errno, userTimeout_, sock); + return false; + } + + int keepAlive_ = 1; + /* + https://man7.org/linux/man-pages/man7/socket.7.html + SO_KEEPALIVE + Enable sending of keep-alive messages on connection- + oriented sockets. Expects an integer boolean flag. + + https://man7.org/linux/man-pages/man3/setsockopt.3p.html + RETURN VALUE top + Upon successful completion, setsockopt() shall return 0. + Otherwise, -1 shall be returned and errno set to indicate the + error. + */ + if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &keepAlive_, sizeof(keepAlive_)) != 0) { + NETSTACK_LOGE("set SO_KEEPALIVE failed, errno = %{public}d sock = %{public}d", errno, sock); + return false; + } + if (setsockopt(sock, SOL_TCP, TCP_KEEPIDLE, &keepIdle_, sizeof(keepIdle_)) != 0) { + NETSTACK_LOGE("set TCP_KEEPIDLE failed, errno = %{public}d keepIdle = %{public}d sock = %{public}d", + errno, keepIdle_, sock); + return false; + } + if (setsockopt(sock, SOL_TCP, TCP_KEEPCNT, &keepCnt_, sizeof(keepCnt_)) != 0) { + NETSTACK_LOGE("set TCP_KEEPCNT failed, errno = %{public}d keepCnt = %{public}d sock = %{public}d", + errno, keepCnt_, sock); + return false; + } + if (setsockopt(sock, SOL_TCP, TCP_KEEPINTVL, &keepInterval_, sizeof(keepInterval_)) != 0) { + NETSTACK_LOGE("set TCP_KEEPINTVL failed, errno = %{public}d keepInterval = %{public}d sock = %{public}d", + errno, keepInterval_, sock); + return false; + } +#endif + return true; +} + +void HttpRequestOptions::TcpConfiguration::SetTcpUserTimeout(const uint32_t &timeout) +{ + userTimeout_ = timeout; +} } // namespace OHOS::NetStack::Http \ No newline at end of file diff --git a/utils/common_utils/include/netstack_common_utils.h b/utils/common_utils/include/netstack_common_utils.h index e8728d43a421ccc8165d93b041d027966673670e..5c8a2e6a8f6fddfeca17ce5d50c1886d7671a145 100644 --- a/utils/common_utils/include/netstack_common_utils.h +++ b/utils/common_utils/include/netstack_common_utils.h @@ -103,5 +103,7 @@ bool IsCertPubKeyInPinned(const std::string &certPubKeyDigest, const std::string bool IsCleartextPermitted(const std::string &url, const std::string &protocol); bool IsValidPort(const uint32_t &Port); + +std::string ToAnonymousIp(const std::string &input); } // namespace OHOS::NetStack::CommonUtils #endif /* COMMUNICATIONNETSTACK_COMMON_UTILS_H */ diff --git a/utils/common_utils/src/netstack_common_utils.cpp b/utils/common_utils/src/netstack_common_utils.cpp index 793a3ea7b6bf7e7ecb10886e17ccd6a1a1b7b275..69018132a2958ee3d9d2f18357d6b25618cd7294 100644 --- a/utils/common_utils/src/netstack_common_utils.cpp +++ b/utils/common_utils/src/netstack_common_utils.cpp @@ -612,4 +612,20 @@ bool IsValidPort(const uint32_t &port) } return true; } + +std::string ToAnonymousIp(const std::string &input) +{ + std::string maskedResult = input; + // Mask ipv4 address. + if (std::regex_match(maskedResult, IP_PATTERN) || std::regex_match(maskedResult, IP_MASK_PATTERN)) { + MaskIpv4(maskedResult); + return maskedResult; + } + // Mask ipv6 address. + if (std::regex_match(maskedResult, IPV6_PATTERN) || std::regex_match(maskedResult, IPV6_MASK_PATTERN)) { + MaskIpv6(maskedResult); + return maskedResult; + } + return input; +} } // namespace OHOS::NetStack::CommonUtils \ No newline at end of file