From 2d53386ba9367938455e66313bd83b3b0ec0877d Mon Sep 17 00:00:00 2001 From: Vivien Nicolas Date: Tue, 11 Mar 2025 12:46:17 +0100 Subject: [PATCH] [darwin-framework-tool] Use NSURLSession instead of the NetworkFramework for reading DCL content --- .../commands/dcl/HTTPSRequest.mm | 231 ++++-------------- 1 file changed, 54 insertions(+), 177 deletions(-) diff --git a/examples/darwin-framework-tool/commands/dcl/HTTPSRequest.mm b/examples/darwin-framework-tool/commands/dcl/HTTPSRequest.mm index 8502e228284c97..28f2a6edf59752 100644 --- a/examples/darwin-framework-tool/commands/dcl/HTTPSRequest.mm +++ b/examples/darwin-framework-tool/commands/dcl/HTTPSRequest.mm @@ -26,7 +26,7 @@ #include #include -#include +#include #include @@ -41,186 +41,69 @@ constexpr const char * kErrorSizeMismatch = "The response size does not match the expected size: "; } // namespace +@interface NSURLSessionDelegateAllowAll : NSObject +@end + +@implementation NSURLSessionDelegateAllowAll +- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * credential))completionHandler +{ + completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]); +} +@end + namespace chip { namespace tool { namespace https { namespace { - constexpr uint16_t kResponseBufferSize = 4096; - constexpr uint64_t kConnectionTimeoutSeconds = 10; constexpr uint64_t kSendTimeoutSeconds = 10; - constexpr uint64_t kReceiveTimeoutSeconds = 10; - constexpr const char * kDispatchQueueName = "com.chip.httpsrequest"; - constexpr const char * kContentContextName = "httpsrequest"; - constexpr const char * kErrorSendHTTPRequest = "Failed to send HTTP request"; - constexpr const char * kErrorSendHTTPRequestTimeout = "Failed to send HTTP request: timeout"; - constexpr const char * kErrorReceiveHTTPResponse = "Failed to read HTTP response"; - constexpr const char * kErrorReceiveHTTPResponseTimeout = "Failed to read HTTP response: timeout"; - constexpr const char * kErrorConnection = "Failed to connect to: "; - constexpr const char * kErrorConnectionTimeout = "Timeout connecting to: "; - constexpr const char * kErrorConnectionUnknowState = "Unknown connection state"; + constexpr const char * kErrorReceiveHTTPResponse = "Failed to read HTTP response: "; + constexpr const char * kErrorReceiveHTTPResponseTimeout = "Failed to read HTTP response (timeout): "; constexpr const char * kErrorDigestMismatch = "The response digest does not match the expected digest"; - constexpr sec_protocol_verify_t NULL_VERIFIER = ^(sec_protocol_metadata_t metadata, - sec_trust_t trust_ref, - sec_protocol_verify_complete_t complete) { - complete(true); - }; - - class HTTPSSessionHolder { - public: - HTTPSSessionHolder() {}; - - ~HTTPSSessionHolder() - { - VerifyOrReturn(nullptr != mConnection); - nw_connection_cancel(mConnection); - mConnection = nullptr; - } - - CHIP_ERROR Init(std::string & hostname, uint16_t port, HttpsSecurityMode securityMode) - { - __auto_type semaphore = dispatch_semaphore_create(0); - __block CHIP_ERROR result = CHIP_NO_ERROR; - __auto_type queue = dispatch_queue_create(kDispatchQueueName, DISPATCH_QUEUE_SERIAL); - - __auto_type endpoint = nw_endpoint_create_host(hostname.c_str(), std::to_string(port).c_str()); - - nw_parameters_configure_protocol_block_t tls_options; - switch (securityMode) { - case HttpsSecurityMode::kDefault: { - tls_options = NW_PARAMETERS_DEFAULT_CONFIGURATION; - break; - } - case HttpsSecurityMode::kDisableValidation: { - tls_options = ^(nw_protocol_options_t options) { - sec_protocol_options_t sec_options = nw_tls_copy_sec_protocol_options(options); - sec_protocol_options_set_verify_block(sec_options, NULL_VERIFIER, queue); - }; - break; - } - case HttpsSecurityMode::kDisableHttps: { - tls_options = NW_PARAMETERS_DISABLE_PROTOCOL; - break; - } - } - - // NW_PARAMETERS_DISABLE_PROTOCOL - nw_parameters_t parameters = nw_parameters_create_secure_tcp(tls_options, NW_PARAMETERS_DEFAULT_CONFIGURATION); - - mConnection = nw_connection_create(endpoint, parameters); - VerifyOrReturnError(nullptr != mConnection, CHIP_ERROR_INTERNAL); - - nw_connection_set_state_changed_handler(mConnection, ^(nw_connection_state_t state, nw_error_t error) { - switch (state) { - case nw_connection_state_waiting: - case nw_connection_state_preparing: - break; - case nw_connection_state_ready: - result = CHIP_NO_ERROR; - dispatch_semaphore_signal(semaphore); - break; - case nw_connection_state_failed: - case nw_connection_state_cancelled: - ChipLogError(chipTool, "%s%s", kErrorConnection, hostname.c_str()); - result = CHIP_ERROR_NOT_CONNECTED; - dispatch_semaphore_signal(semaphore); - default: - ChipLogError(chipTool, "%s", kErrorConnectionUnknowState); - break; - } - }); - - nw_connection_set_queue(mConnection, queue); - nw_connection_start(mConnection); - - if (dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, kConnectionTimeoutSeconds * NSEC_PER_SEC)) != 0) { - ChipLogError(chipTool, "%s%s", kErrorConnectionTimeout, hostname.c_str()); - return CHIP_ERROR_TIMEOUT; - } - - nw_connection_set_state_changed_handler(mConnection, nullptr); - return result; - } - - CHIP_ERROR SendRequest(std::string & request) - { - __auto_type semaphore = dispatch_semaphore_create(0); - __block CHIP_ERROR result = CHIP_NO_ERROR; - - __auto_type context = nw_content_context_create(kContentContextName); - __auto_type data = dispatch_data_create(request.c_str(), request.size(), dispatch_get_main_queue(), DISPATCH_DATA_DESTRUCTOR_DEFAULT); - nw_connection_send(mConnection, data, context, true, ^(nw_error_t error) { - if (nullptr != error) { - ChipLogError(chipTool, "%s", kErrorSendHTTPRequest); - result = CHIP_ERROR_BAD_REQUEST; - } - dispatch_semaphore_signal(semaphore); - }); - - if (dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, kSendTimeoutSeconds * NSEC_PER_SEC)) != 0) { - ChipLogError(chipTool, "%s", kErrorSendHTTPRequestTimeout); - return CHIP_ERROR_TIMEOUT; - } - - return result; + CHIP_ERROR SendRequest(const std::string & hostname, uint16_t port, const std::string & path, HttpsSecurityMode securityMode, std::string & response) + { + std::string urlString = (securityMode == HttpsSecurityMode::kDisableHttps ? "http://" : "https://") + hostname + ":" + std::to_string(port) + path; + __auto_type * requestURL = [NSURL URLWithString:[NSString stringWithUTF8String:urlString.c_str()]]; + __auto_type * urlRequest = [NSMutableURLRequest requestWithURL:requestURL]; + [urlRequest setHTTPMethod:@"GET"]; + [urlRequest setValue:@"application/json" forHTTPHeaderField:@"Accept"]; + [urlRequest setValue:@"close" forHTTPHeaderField:@"Connection"]; + + __auto_type * config = [NSURLSessionConfiguration defaultSessionConfiguration]; + + NSURLSession * session; + if (securityMode == HttpsSecurityMode::kDisableValidation) { + session = [NSURLSession sessionWithConfiguration:config + delegate:(id) [[NSURLSessionDelegateAllowAll alloc] init] + delegateQueue:nil]; + } else { + session = [NSURLSession sessionWithConfiguration:config]; } - CHIP_ERROR ReceiveResponse(std::string & response) - { - __auto_type semaphore = dispatch_semaphore_create(0); - __block CHIP_ERROR result = CHIP_NO_ERROR; - __block std::string receivedData; - - nw_connection_receive(mConnection, 1, kResponseBufferSize, ^(dispatch_data_t content, nw_content_context_t context, bool isComplete, nw_error_t error) { - if (nullptr != error) { - ChipLogError(chipTool, "%s", kErrorReceiveHTTPResponse); - result = CHIP_ERROR_INTERNAL; - } else if (nullptr != content) { - size_t total_size = dispatch_data_get_size(content); - receivedData.reserve(total_size); - - dispatch_data_apply(content, ^(dispatch_data_t region, size_t offset, const void * buffer, size_t size) { - receivedData.append(static_cast(buffer), size); - return true; - }); - - result = CHIP_NO_ERROR; - } - - dispatch_semaphore_signal(semaphore); - }); - - if (dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, kReceiveTimeoutSeconds * NSEC_PER_SEC)) != 0) { - ChipLogError(chipTool, "%s", kErrorReceiveHTTPResponseTimeout); - return CHIP_ERROR_TIMEOUT; - } - - response = receivedData; - return result; + dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + __block CHIP_ERROR rv = CHIP_NO_ERROR; + __block std::string receivedData; + NSURLSessionDataTask * task = [session dataTaskWithRequest:urlRequest + completionHandler:^(NSData * data, NSURLResponse * resp, NSError * error) { + if (error) { + ChipLogError(chipTool, "%s%s", kErrorReceiveHTTPResponse, [[error localizedDescription] UTF8String]); + rv = CHIP_ERROR_BAD_REQUEST; + } else { + receivedData.assign((const char *) [data bytes], [data length]); + rv = CHIP_NO_ERROR; + } + dispatch_semaphore_signal(semaphore); + }]; + + [task resume]; + + if (dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, kSendTimeoutSeconds * NSEC_PER_SEC)) != 0) { + ChipLogError(chipTool, "%s%s", kErrorReceiveHTTPResponseTimeout, hostname.c_str()); + return CHIP_ERROR_TIMEOUT; } - private: - nw_connection_t mConnection = nullptr; - }; - - std::string BuildRequest(std::string & hostname, std::string & path) - { - return "GET " + path + " HTTP/1.1\r\n" + // - "Host: " + hostname + "\r\n" + // - "Accept: application/json\r\n" + // - "Connection: close\r\n\r\n"; // - } - - CHIP_ERROR RemoveHeader(std::string & response) - { - // TODO: Parse the response status. Why are we doing HTTP by hand? - size_t headerEnd = response.find("\r\n\r\n"); - VerifyOrReturnError(std::string::npos != headerEnd, CHIP_ERROR_INVALID_ARGUMENT); - - auto body = response.substr(headerEnd + 4); - response = body; - - return CHIP_NO_ERROR; + response = receivedData; + return rv; } CHIP_ERROR MaybeCheckResponseSize(const std::string & response, const chip::Optional & optionalExpectedSize) @@ -348,14 +231,8 @@ CHIP_ERROR Request(std::string hostname, uint16_t port, std::string path, Json:: } ChipLogDetail(chipTool, "%s request to %s:%u%s", protocol, hostname.c_str(), port, path.c_str()); - std::string request = BuildRequest(hostname, path); std::string response; - - HTTPSSessionHolder session; - ReturnErrorOnFailure(session.Init(hostname, port, securityMode)); - ReturnErrorOnFailure(session.SendRequest(request)); - ReturnErrorOnFailure(session.ReceiveResponse(response)); - ReturnErrorOnFailure(RemoveHeader(response)); + ReturnErrorOnFailure(SendRequest(hostname, port, path, securityMode, response)); ReturnErrorOnFailure(MaybeCheckResponseSize(response, optionalExpectedSize)); ReturnErrorOnFailure(MaybeCheckResponseDigest(response, optionalExpectedDigest)); ReturnErrorOnFailure(ConvertResponseToJSON(response, jsonResponse));