|
26 | 26 | #include <system/SystemError.h>
|
27 | 27 |
|
28 | 28 | #include <CommonCrypto/CommonDigest.h>
|
29 |
| -#include <Network/Network.h> |
| 29 | +#include <Foundation/Foundation.h> |
30 | 30 |
|
31 | 31 | #include <unistd.h>
|
32 | 32 |
|
|
41 | 41 | constexpr const char * kErrorSizeMismatch = "The response size does not match the expected size: ";
|
42 | 42 | } // namespace
|
43 | 43 |
|
| 44 | +@interface NSURLSessionDelegateAllowAll : NSObject <NSURLSessionDelegate> |
| 45 | +@end |
| 46 | + |
| 47 | +@implementation NSURLSessionDelegateAllowAll |
| 48 | +- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * credential))completionHandler |
| 49 | +{ |
| 50 | + completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]); |
| 51 | +} |
| 52 | +@end |
| 53 | + |
44 | 54 | namespace chip {
|
45 | 55 | namespace tool {
|
46 | 56 | namespace https {
|
47 | 57 | namespace {
|
48 |
| - constexpr uint16_t kResponseBufferSize = 4096; |
49 |
| - constexpr uint64_t kConnectionTimeoutSeconds = 10; |
50 | 58 | constexpr uint64_t kSendTimeoutSeconds = 10;
|
51 |
| - constexpr uint64_t kReceiveTimeoutSeconds = 10; |
52 |
| - constexpr const char * kDispatchQueueName = "com.chip.httpsrequest"; |
53 |
| - constexpr const char * kContentContextName = "httpsrequest"; |
54 |
| - constexpr const char * kErrorSendHTTPRequest = "Failed to send HTTP request"; |
55 |
| - constexpr const char * kErrorSendHTTPRequestTimeout = "Failed to send HTTP request: timeout"; |
56 |
| - constexpr const char * kErrorReceiveHTTPResponse = "Failed to read HTTP response"; |
57 |
| - constexpr const char * kErrorReceiveHTTPResponseTimeout = "Failed to read HTTP response: timeout"; |
58 |
| - constexpr const char * kErrorConnection = "Failed to connect to: "; |
59 |
| - constexpr const char * kErrorConnectionTimeout = "Timeout connecting to: "; |
60 |
| - constexpr const char * kErrorConnectionUnknowState = "Unknown connection state"; |
| 59 | + constexpr const char * kErrorReceiveHTTPResponse = "Failed to read HTTP response: "; |
| 60 | + constexpr const char * kErrorReceiveHTTPResponseTimeout = "Failed to read HTTP response (timeout): "; |
61 | 61 | constexpr const char * kErrorDigestMismatch = "The response digest does not match the expected digest";
|
62 | 62 |
|
63 |
| - constexpr sec_protocol_verify_t NULL_VERIFIER = ^(sec_protocol_metadata_t metadata, |
64 |
| - sec_trust_t trust_ref, |
65 |
| - sec_protocol_verify_complete_t complete) { |
66 |
| - complete(true); |
67 |
| - }; |
68 |
| - |
69 |
| - class HTTPSSessionHolder { |
70 |
| - public: |
71 |
| - HTTPSSessionHolder() {}; |
72 |
| - |
73 |
| - ~HTTPSSessionHolder() |
74 |
| - { |
75 |
| - VerifyOrReturn(nullptr != mConnection); |
76 |
| - nw_connection_cancel(mConnection); |
77 |
| - mConnection = nullptr; |
78 |
| - } |
79 |
| - |
80 |
| - CHIP_ERROR Init(std::string & hostname, uint16_t port, HttpsSecurityMode securityMode) |
81 |
| - { |
82 |
| - __auto_type semaphore = dispatch_semaphore_create(0); |
83 |
| - __block CHIP_ERROR result = CHIP_NO_ERROR; |
84 |
| - __auto_type queue = dispatch_queue_create(kDispatchQueueName, DISPATCH_QUEUE_SERIAL); |
85 |
| - |
86 |
| - __auto_type endpoint = nw_endpoint_create_host(hostname.c_str(), std::to_string(port).c_str()); |
87 |
| - |
88 |
| - nw_parameters_configure_protocol_block_t tls_options; |
89 |
| - switch (securityMode) { |
90 |
| - case HttpsSecurityMode::kDefault: { |
91 |
| - tls_options = NW_PARAMETERS_DEFAULT_CONFIGURATION; |
92 |
| - break; |
93 |
| - } |
94 |
| - case HttpsSecurityMode::kDisableValidation: { |
95 |
| - tls_options = ^(nw_protocol_options_t options) { |
96 |
| - sec_protocol_options_t sec_options = nw_tls_copy_sec_protocol_options(options); |
97 |
| - sec_protocol_options_set_verify_block(sec_options, NULL_VERIFIER, queue); |
98 |
| - }; |
99 |
| - break; |
100 |
| - } |
101 |
| - case HttpsSecurityMode::kDisableHttps: { |
102 |
| - tls_options = NW_PARAMETERS_DISABLE_PROTOCOL; |
103 |
| - break; |
104 |
| - } |
105 |
| - } |
106 |
| - |
107 |
| - // NW_PARAMETERS_DISABLE_PROTOCOL |
108 |
| - nw_parameters_t parameters = nw_parameters_create_secure_tcp(tls_options, NW_PARAMETERS_DEFAULT_CONFIGURATION); |
109 |
| - |
110 |
| - mConnection = nw_connection_create(endpoint, parameters); |
111 |
| - VerifyOrReturnError(nullptr != mConnection, CHIP_ERROR_INTERNAL); |
112 |
| - |
113 |
| - nw_connection_set_state_changed_handler(mConnection, ^(nw_connection_state_t state, nw_error_t error) { |
114 |
| - switch (state) { |
115 |
| - case nw_connection_state_waiting: |
116 |
| - case nw_connection_state_preparing: |
117 |
| - break; |
118 |
| - case nw_connection_state_ready: |
119 |
| - result = CHIP_NO_ERROR; |
120 |
| - dispatch_semaphore_signal(semaphore); |
121 |
| - break; |
122 |
| - case nw_connection_state_failed: |
123 |
| - case nw_connection_state_cancelled: |
124 |
| - ChipLogError(chipTool, "%s%s", kErrorConnection, hostname.c_str()); |
125 |
| - result = CHIP_ERROR_NOT_CONNECTED; |
126 |
| - dispatch_semaphore_signal(semaphore); |
127 |
| - default: |
128 |
| - ChipLogError(chipTool, "%s", kErrorConnectionUnknowState); |
129 |
| - break; |
130 |
| - } |
131 |
| - }); |
132 |
| - |
133 |
| - nw_connection_set_queue(mConnection, queue); |
134 |
| - nw_connection_start(mConnection); |
135 |
| - |
136 |
| - if (dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, kConnectionTimeoutSeconds * NSEC_PER_SEC)) != 0) { |
137 |
| - ChipLogError(chipTool, "%s%s", kErrorConnectionTimeout, hostname.c_str()); |
138 |
| - return CHIP_ERROR_TIMEOUT; |
139 |
| - } |
140 |
| - |
141 |
| - nw_connection_set_state_changed_handler(mConnection, nullptr); |
142 |
| - return result; |
143 |
| - } |
144 |
| - |
145 |
| - CHIP_ERROR SendRequest(std::string & request) |
146 |
| - { |
147 |
| - __auto_type semaphore = dispatch_semaphore_create(0); |
148 |
| - __block CHIP_ERROR result = CHIP_NO_ERROR; |
149 |
| - |
150 |
| - __auto_type context = nw_content_context_create(kContentContextName); |
151 |
| - __auto_type data = dispatch_data_create(request.c_str(), request.size(), dispatch_get_main_queue(), DISPATCH_DATA_DESTRUCTOR_DEFAULT); |
152 |
| - nw_connection_send(mConnection, data, context, true, ^(nw_error_t error) { |
153 |
| - if (nullptr != error) { |
154 |
| - ChipLogError(chipTool, "%s", kErrorSendHTTPRequest); |
155 |
| - result = CHIP_ERROR_BAD_REQUEST; |
156 |
| - } |
157 |
| - dispatch_semaphore_signal(semaphore); |
158 |
| - }); |
159 |
| - |
160 |
| - if (dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, kSendTimeoutSeconds * NSEC_PER_SEC)) != 0) { |
161 |
| - ChipLogError(chipTool, "%s", kErrorSendHTTPRequestTimeout); |
162 |
| - return CHIP_ERROR_TIMEOUT; |
163 |
| - } |
164 |
| - |
165 |
| - return result; |
| 63 | + CHIP_ERROR SendRequest(const std::string & hostname, uint16_t port, const std::string & path, HttpsSecurityMode securityMode, std::string & response) |
| 64 | + { |
| 65 | + std::string urlString = (securityMode == HttpsSecurityMode::kDisableHttps ? "http://" : "https://") + hostname + ":" + std::to_string(port) + path; |
| 66 | + __auto_type * requestURL = [NSURL URLWithString:[NSString stringWithUTF8String:urlString.c_str()]]; |
| 67 | + __auto_type * urlRequest = [NSMutableURLRequest requestWithURL:requestURL]; |
| 68 | + [urlRequest setHTTPMethod:@"GET"]; |
| 69 | + [urlRequest setValue:@"application/json" forHTTPHeaderField:@"Accept"]; |
| 70 | + [urlRequest setValue:@"close" forHTTPHeaderField:@"Connection"]; |
| 71 | + |
| 72 | + __auto_type * config = [NSURLSessionConfiguration defaultSessionConfiguration]; |
| 73 | + |
| 74 | + NSURLSession * session; |
| 75 | + if (securityMode == HttpsSecurityMode::kDisableValidation) { |
| 76 | + session = [NSURLSession sessionWithConfiguration:config |
| 77 | + delegate:(id<NSURLSessionDelegate>) [[NSURLSessionDelegateAllowAll alloc] init] |
| 78 | + delegateQueue:nil]; |
| 79 | + } else { |
| 80 | + session = [NSURLSession sessionWithConfiguration:config]; |
166 | 81 | }
|
167 | 82 |
|
168 |
| - CHIP_ERROR ReceiveResponse(std::string & response) |
169 |
| - { |
170 |
| - __auto_type semaphore = dispatch_semaphore_create(0); |
171 |
| - __block CHIP_ERROR result = CHIP_NO_ERROR; |
172 |
| - __block std::string receivedData; |
173 |
| - |
174 |
| - nw_connection_receive(mConnection, 1, kResponseBufferSize, ^(dispatch_data_t content, nw_content_context_t context, bool isComplete, nw_error_t error) { |
175 |
| - if (nullptr != error) { |
176 |
| - ChipLogError(chipTool, "%s", kErrorReceiveHTTPResponse); |
177 |
| - result = CHIP_ERROR_INTERNAL; |
178 |
| - } else if (nullptr != content) { |
179 |
| - size_t total_size = dispatch_data_get_size(content); |
180 |
| - receivedData.reserve(total_size); |
181 |
| - |
182 |
| - dispatch_data_apply(content, ^(dispatch_data_t region, size_t offset, const void * buffer, size_t size) { |
183 |
| - receivedData.append(static_cast<const char *>(buffer), size); |
184 |
| - return true; |
185 |
| - }); |
186 |
| - |
187 |
| - result = CHIP_NO_ERROR; |
188 |
| - } |
189 |
| - |
190 |
| - dispatch_semaphore_signal(semaphore); |
191 |
| - }); |
192 |
| - |
193 |
| - if (dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, kReceiveTimeoutSeconds * NSEC_PER_SEC)) != 0) { |
194 |
| - ChipLogError(chipTool, "%s", kErrorReceiveHTTPResponseTimeout); |
195 |
| - return CHIP_ERROR_TIMEOUT; |
196 |
| - } |
197 |
| - |
198 |
| - response = receivedData; |
199 |
| - return result; |
| 83 | + dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); |
| 84 | + __block CHIP_ERROR rv = CHIP_NO_ERROR; |
| 85 | + __block std::string receivedData; |
| 86 | + NSURLSessionDataTask * task = [session dataTaskWithRequest:urlRequest |
| 87 | + completionHandler:^(NSData * data, NSURLResponse * resp, NSError * error) { |
| 88 | + if (error) { |
| 89 | + ChipLogError(chipTool, "%s%s", kErrorReceiveHTTPResponse, [[error localizedDescription] UTF8String]); |
| 90 | + rv = CHIP_ERROR_BAD_REQUEST; |
| 91 | + } else { |
| 92 | + receivedData.assign((const char *) [data bytes], [data length]); |
| 93 | + rv = CHIP_NO_ERROR; |
| 94 | + } |
| 95 | + dispatch_semaphore_signal(semaphore); |
| 96 | + }]; |
| 97 | + |
| 98 | + [task resume]; |
| 99 | + |
| 100 | + if (dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, kSendTimeoutSeconds * NSEC_PER_SEC)) != 0) { |
| 101 | + ChipLogError(chipTool, "%s%s", kErrorReceiveHTTPResponseTimeout, hostname.c_str()); |
| 102 | + return CHIP_ERROR_TIMEOUT; |
200 | 103 | }
|
201 | 104 |
|
202 |
| - private: |
203 |
| - nw_connection_t mConnection = nullptr; |
204 |
| - }; |
205 |
| - |
206 |
| - std::string BuildRequest(std::string & hostname, std::string & path) |
207 |
| - { |
208 |
| - return "GET " + path + " HTTP/1.1\r\n" + // |
209 |
| - "Host: " + hostname + "\r\n" + // |
210 |
| - "Accept: application/json\r\n" + // |
211 |
| - "Connection: close\r\n\r\n"; // |
212 |
| - } |
213 |
| - |
214 |
| - CHIP_ERROR RemoveHeader(std::string & response) |
215 |
| - { |
216 |
| - // TODO: Parse the response status. Why are we doing HTTP by hand? |
217 |
| - size_t headerEnd = response.find("\r\n\r\n"); |
218 |
| - VerifyOrReturnError(std::string::npos != headerEnd, CHIP_ERROR_INVALID_ARGUMENT); |
219 |
| - |
220 |
| - auto body = response.substr(headerEnd + 4); |
221 |
| - response = body; |
222 |
| - |
223 |
| - return CHIP_NO_ERROR; |
| 105 | + response = receivedData; |
| 106 | + return rv; |
224 | 107 | }
|
225 | 108 |
|
226 | 109 | CHIP_ERROR MaybeCheckResponseSize(const std::string & response, const chip::Optional<uint32_t> & optionalExpectedSize)
|
@@ -348,14 +231,8 @@ CHIP_ERROR Request(std::string hostname, uint16_t port, std::string path, Json::
|
348 | 231 | }
|
349 | 232 | ChipLogDetail(chipTool, "%s request to %s:%u%s", protocol, hostname.c_str(), port, path.c_str());
|
350 | 233 |
|
351 |
| - std::string request = BuildRequest(hostname, path); |
352 | 234 | std::string response;
|
353 |
| - |
354 |
| - HTTPSSessionHolder session; |
355 |
| - ReturnErrorOnFailure(session.Init(hostname, port, securityMode)); |
356 |
| - ReturnErrorOnFailure(session.SendRequest(request)); |
357 |
| - ReturnErrorOnFailure(session.ReceiveResponse(response)); |
358 |
| - ReturnErrorOnFailure(RemoveHeader(response)); |
| 235 | + ReturnErrorOnFailure(SendRequest(hostname, port, path, securityMode, response)); |
359 | 236 | ReturnErrorOnFailure(MaybeCheckResponseSize(response, optionalExpectedSize));
|
360 | 237 | ReturnErrorOnFailure(MaybeCheckResponseDigest(response, optionalExpectedDigest));
|
361 | 238 | ReturnErrorOnFailure(ConvertResponseToJSON(response, jsonResponse));
|
|
0 commit comments