Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(22)

Side by Side Diff: ios/chrome/common/physical_web/physical_web_request.mm

Issue 2023833002: [iOS] Upstream physical web classes. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Add comment about limiting the dependencies Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #import "ios/chrome/common/physical_web/physical_web_request.h"
6
7 #include "base/ios/weak_nsobject.h"
8 #include "base/logging.h"
9 #include "base/mac/foundation_util.h"
10 #include "base/mac/scoped_block.h"
11 #include "base/mac/scoped_nsobject.h"
12 #include "base/metrics/histogram.h"
13 #include "base/strings/sys_string_conversions.h"
14 #include "components/version_info/version_info.h"
15 #include "google_apis/google_api_keys.h"
16 #import "ios/chrome/common/physical_web/physical_web_device.h"
17 #import "ios/chrome/common/physical_web/physical_web_types.h"
18 #include "ios/web/public/user_agent.h"
19
20 typedef void (^SessionCompletionProceduralBlock)(NSData* data,
21 NSURLResponse* response,
22 NSError* error);
23
24 namespace {
25
26 NSString* const kUrlsKey = @"urls";
27 NSString* const kUrlKey = @"url";
28 NSString* const kResultsKey = @"results";
29 NSString* const kPageInfoKey = @"pageInfo";
30 NSString* const kIconKey = @"icon";
31 NSString* const kTitleKey = @"title";
32 NSString* const kDescriptionKey = @"description";
33 NSString* const kScannedUrlKey = @"scannedUrl";
34 NSString* const kResolvedUrlKey = @"resolvedUrl";
35
36 NSString* const kMetadataServiceUrl =
37 @"https://physicalweb.googleapis.com/v1alpha1/urls:resolve";
38 NSString* const kKeyQueryItemName = @"key";
39 NSString* const kHTTPPOSTRequestMethod = @"POST";
40
41 std::string GetUserAgent() {
42 static std::string user_agent;
43 static dispatch_once_t once_token;
44 dispatch_once(&once_token, ^{
45 std::string product("CriOS/");
46 product += version_info::GetVersionNumber();
47 user_agent = web::BuildUserAgentFromProduct(product);
48 });
49 return user_agent;
50 }
51
52 } // namespace
53
54 @implementation PhysicalWebRequest {
55 base::mac::ScopedBlock<physical_web::RequestFinishedBlock> block_;
56 base::scoped_nsobject<PhysicalWebDevice> device_;
57 base::scoped_nsobject<NSMutableURLRequest> request_;
58 base::scoped_nsobject<NSURLSessionDataTask> urlSessionTask_;
59 base::scoped_nsobject<NSMutableData> data_;
60 base::scoped_nsobject<NSDate> startDate_;
61 }
62
63 - (instancetype)initWithDevice:(PhysicalWebDevice*)device {
64 self = [super init];
65 if (self) {
66 device_.reset([device retain]);
67 }
68 return self;
69 }
70
71 - (instancetype)init {
72 NOTREACHED();
73 return nil;
74 }
75
76 - (void)cancel {
77 [urlSessionTask_ cancel];
78 block_.reset();
79 }
80
81 - (void)start:(physical_web::RequestFinishedBlock)block {
82 block_.reset([block copy]);
83 data_.reset([[NSMutableData alloc] init]);
84
85 // Creates the HTTP post request.
86 NSURLComponents* components =
87 [[NSURLComponents alloc] initWithString:kMetadataServiceUrl];
88 NSString* apiKey =
89 [NSString stringWithUTF8String:google_apis::GetAPIKey().c_str()];
90 [components
91 setQueryItems:@[ [NSURLQueryItem queryItemWithName:kKeyQueryItemName
92 value:apiKey] ]];
93 NSURL* url = [components URL];
94 request_.reset([[NSMutableURLRequest requestWithURL:url] retain]);
95 [request_ setHTTPMethod:kHTTPPOSTRequestMethod];
96
97 // body of the POST request.
98 NSDictionary* jsonBody =
99 @{ kUrlsKey : @[ @{kUrlKey : [[device_ url] absoluteString]} ] };
100 [request_ setHTTPBody:[NSJSONSerialization dataWithJSONObject:jsonBody
101 options:0
102 error:NULL]];
103 [request_ setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
104 [request_ setValue:base::SysUTF8ToNSString(GetUserAgent())
105 forHTTPHeaderField:@"User-Agent"];
106
107 startDate_.reset([[NSDate date] retain]);
108 // Starts the request.
109 NSURLSession* session =
110 [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration
111 ephemeralSessionConfiguration]
112 delegate:nil
113 delegateQueue:[NSOperationQueue mainQueue]];
114 base::WeakNSObject<PhysicalWebRequest> weakSelf(self);
115 SessionCompletionProceduralBlock completionHandler =
116 ^(NSData* data, NSURLResponse* response, NSError* error) {
117 base::scoped_nsobject<PhysicalWebRequest> strongSelf([weakSelf retain]);
118 if (!strongSelf) {
119 return;
120 }
121 if (error) {
122 [strongSelf callBlockWithError:error jsonObject:nil];
123 } else {
124 [strongSelf.get()->data_ appendData:data];
125 [strongSelf sessionDidFinishLoading];
126 }
127 };
128 urlSessionTask_.reset([
129 [session dataTaskWithRequest:request_ completionHandler:completionHandler]
130 retain]);
131 [urlSessionTask_ resume];
132 }
133
134 - (void)sessionDidFinishLoading {
135 NSError* error = nil;
136 NSDictionary* jsonObject =
137 [NSJSONSerialization JSONObjectWithData:data_ options:0 error:&error];
138 if (error != nil) {
139 [self callBlockWithError:error jsonObject:nil];
140 return;
141 }
142 if (![self isValidJSON:jsonObject]) {
143 [self callBlockWithError:
144 [NSError errorWithDomain:physical_web::kPhysicalWebErrorDomain
145 code:physical_web::ERROR_INVALID_JSON
146 userInfo:nil]
147 jsonObject:jsonObject];
148 return;
149 }
150 [self callBlockWithError:error jsonObject:jsonObject];
151 }
152
153 // Checks whether the JSON object has the correct format.
154 // The format should be similar to the following:
155 // {
156 // "results":[
157 // {
158 // "pageInfo":{
159 // "title":"Google"
160 // "description":"Search the world's information"
161 // "icon":"https://www.google.com/favicon.ico"
162 // }
163 // "scannedUrl":"https://www.google.com"
164 // "resolvedUrl":"https://www.google.com"
165 // }
166 // ]
167 // }
168 - (BOOL)isValidJSON:(id)jsonObject {
169 NSDictionary* dict = base::mac::ObjCCast<NSDictionary>(jsonObject);
170 if (!dict) {
171 return NO;
172 }
173 NSArray* list = base::mac::ObjCCast<NSArray>(dict[kResultsKey]);
174 if (!list) {
175 return NO;
176 }
177 if ([list count] != 1) {
178 return NO;
179 }
180 NSDictionary* item = base::mac::ObjCCast<NSDictionary>(list[0]);
181 if (!item) {
182 return NO;
183 }
184 return YES;
185 }
186
187 // Calls the block passed as parameter of -start: when the request is finished.
188 - (void)callBlockWithError:(NSError*)error
189 jsonObject:(NSDictionary*)jsonObject {
190 if (error) {
191 if (block_.get()) {
192 block_.get()(nil, error);
193 }
194 } else {
195 NSTimeInterval roundTripTime =
196 [[NSDate date] timeIntervalSinceDate:startDate_];
197 int roundTripTimeMS = 1000 * roundTripTime;
198 UMA_HISTOGRAM_COUNTS("PhysicalWeb.RoundTripTimeMilliseconds",
199 roundTripTimeMS);
200 NSArray* list = jsonObject[kResultsKey];
201 NSDictionary* item = list[0];
202
203 NSDictionary* pageInfo =
204 base::mac::ObjCCast<NSDictionary>(item[kPageInfoKey]);
205 NSString* scannedUrlString =
206 base::mac::ObjCCast<NSString>(item[kScannedUrlKey]);
207 NSString* resolvedUrlString =
208 base::mac::ObjCCast<NSString>(item[kResolvedUrlKey]);
209
210 // Verify required fields pageInfo, scannedUrl, and resolvedUrl are present.
211 if (pageInfo == nil || scannedUrlString == nil ||
212 resolvedUrlString == nil) {
213 error = [NSError errorWithDomain:physical_web::kPhysicalWebErrorDomain
214 code:physical_web::ERROR_INVALID_JSON
215 userInfo:nil];
216 if (block_.get()) {
217 block_.get()(nil, error);
218 }
219 return;
220 }
221
222 // Read optional fields.
223 NSString* iconString = base::mac::ObjCCast<NSString>(pageInfo[kIconKey]);
224 NSString* description =
225 base::mac::ObjCCast<NSString>(pageInfo[kDescriptionKey]);
226 NSString* title = base::mac::ObjCCast<NSString>(pageInfo[kTitleKey]);
227 NSURL* scannedUrl =
228 scannedUrlString ? [NSURL URLWithString:scannedUrlString] : nil;
229 NSURL* resolvedUrl =
230 resolvedUrlString ? [NSURL URLWithString:resolvedUrlString] : nil;
231 NSURL* icon = iconString ? [NSURL URLWithString:iconString] : nil;
232 base::scoped_nsobject<PhysicalWebDevice> device([[PhysicalWebDevice alloc]
233 initWithURL:resolvedUrl
234 requestURL:scannedUrl
235 icon:icon
236 title:title
237 description:description
238 transmitPower:[device_ transmitPower]
239 rssi:[device_ rssi]
240 rank:physical_web::kMaxRank]);
241 if (block_.get() != nil) {
242 block_.get()(device, nil);
243 }
244 }
245 }
246
247 @end
OLDNEW
« no previous file with comments | « ios/chrome/common/physical_web/physical_web_request.h ('k') | ios/chrome/common/physical_web/physical_web_scanner.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698