yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
ios_urlsession_http_client.mm
Go to the documentation of this file.
2
3#include "absl/status/status.h"
4#include "absl/strings/str_cat.h"
5
6#if defined(__APPLE__)
7#include <TargetConditionals.h>
8#endif
9
10#if defined(__APPLE__) && (TARGET_OS_IPHONE == 1 || TARGET_IPHONE_SIMULATOR == 1)
11
12#import <Foundation/Foundation.h>
13
14namespace yaze::cli::ios {
15namespace {
16
17NSString* ToNSString(const std::string& value) {
18 if (value.empty()) {
19 return @"";
20 }
21 return [NSString stringWithUTF8String:value.c_str()];
22}
23
24std::string ToStdString(NSString* value) {
25 if (!value) {
26 return {};
27 }
28 const char* cstr = [value UTF8String];
29 return cstr ? std::string(cstr) : std::string();
30}
31
32} // namespace
33
34absl::StatusOr<UrlSessionHttpResponse> UrlSessionHttpRequest(
35 const std::string& method, const std::string& url,
36 const std::map<std::string, std::string>& headers,
37 const std::string& body, int timeout_ms) {
38 if (method.empty()) {
39 return absl::InvalidArgumentError("UrlSessionHttpRequest: empty method");
40 }
41 if (url.empty()) {
42 return absl::InvalidArgumentError("UrlSessionHttpRequest: empty url");
43 }
44
45 @autoreleasepool {
46 NSString* url_string = ToNSString(url);
47 NSURL* ns_url = [NSURL URLWithString:url_string];
48 if (!ns_url) {
49 return absl::InvalidArgumentError(
50 absl::StrCat("UrlSessionHttpRequest: invalid url: ", url));
51 }
52
53 NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:ns_url];
54 request.HTTPMethod = ToNSString(method);
55 if (timeout_ms > 0) {
56 request.timeoutInterval = static_cast<NSTimeInterval>(timeout_ms) / 1000.0;
57 }
58
59 for (const auto& [key, value] : headers) {
60 NSString* header_key = ToNSString(key);
61 NSString* header_value = ToNSString(value);
62 if (header_key.length == 0) {
63 continue;
64 }
65 [request setValue:header_value forHTTPHeaderField:header_key];
66 }
67
68 if (!body.empty()) {
69 NSData* data = [NSData dataWithBytes:body.data() length:body.size()];
70 request.HTTPBody = data;
71 }
72
73 __block NSData* response_data = nil;
74 __block NSURLResponse* response = nil;
75 __block NSError* error = nil;
76
77 dispatch_semaphore_t sem = dispatch_semaphore_create(0);
78 NSURLSessionDataTask* task =
79 [[NSURLSession sharedSession] dataTaskWithRequest:request
80 completionHandler:^(NSData* data,
81 NSURLResponse* resp,
82 NSError* err) {
83 response_data = data;
84 response = resp;
85 error = err;
86 dispatch_semaphore_signal(sem);
87 }];
88 [task resume];
89
90 if (timeout_ms <= 0) {
91 dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
92 } else {
93 dispatch_time_t deadline =
94 dispatch_time(DISPATCH_TIME_NOW, (int64_t)timeout_ms * NSEC_PER_MSEC);
95 if (dispatch_semaphore_wait(sem, deadline) != 0) {
96 [task cancel];
97 return absl::DeadlineExceededError("Request timed out");
98 }
99 }
100
101 if (error) {
102 std::string message = ToStdString(error.localizedDescription);
103 if (message.empty()) {
104 message = "URLSession request failed";
105 }
106 return absl::UnavailableError(message);
107 }
108
109 UrlSessionHttpResponse out;
110 if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
111 NSHTTPURLResponse* http = static_cast<NSHTTPURLResponse*>(response);
112 out.status_code = static_cast<int>(http.statusCode);
113 NSDictionary<NSString*, id>* all_headers = http.allHeaderFields;
114 for (id key in all_headers) {
115 NSString* key_str = [key isKindOfClass:[NSString class]]
116 ? static_cast<NSString*>(key)
117 : [key description];
118 NSString* value_str =
119 [[all_headers objectForKey:key] isKindOfClass:[NSString class]]
120 ? static_cast<NSString*>([all_headers objectForKey:key])
121 : [[all_headers objectForKey:key] description];
122 out.headers[ToStdString(key_str)] = ToStdString(value_str);
123 }
124 }
125
126 if (response_data) {
127 out.body.assign(reinterpret_cast<const char*>(response_data.bytes),
128 static_cast<size_t>(response_data.length));
129 }
130
131 return out;
132 }
133}
134
135} // namespace yaze::cli::ios
136
137#else
138
139namespace yaze::cli::ios {
140
141absl::StatusOr<UrlSessionHttpResponse> UrlSessionHttpRequest(
142 const std::string&, const std::string&,
143 const std::map<std::string, std::string>&, const std::string&, int) {
144 return absl::UnimplementedError(
145 "UrlSessionHttpRequest is only available on iOS targets");
146}
147
148} // namespace yaze::cli::ios
149
150#endif
151
absl::StatusOr< UrlSessionHttpResponse > UrlSessionHttpRequest(const std::string &method, const std::string &url, const std::map< std::string, std::string > &headers, const std::string &body, int timeout_ms)