1 // Copyright 2014 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.
5 #include "ios/web/net/request_group_util.h"
7 #include <Foundation/Foundation.h>
9 #include "base/base64.h"
10 #include "base/logging.h"
11 #include "base/rand_util.h"
12 #include "base/strings/sys_string_conversions.h"
13 #import "net/base/mac/url_conversions.h"
17 // Minimum length for a request group ID. A shorter string is considered as an
19 const int kMinimumIDLength = 5;
24 // Generates a request-group ID used to correlate web requests with the embedder
25 // that triggered it. It is important that the return value should not be unique
26 // for different users. See crbug/355613 for context.
27 NSString* GenerateNewRequestGroupID() {
28 const unsigned int kGroupSize = 1000;
29 static unsigned long count = 0;
30 static unsigned int offset = base::RandInt(0, kGroupSize - 1);
32 unsigned long current = count++;
33 if (current < kGroupSize)
34 current = (current + offset) % kGroupSize;
36 // The returned string must have a minimum of kMinimumIDLength characters, and
38 // TODO(blundell): Develop a long-term solution to this problem.
40 return [NSString stringWithFormat:@"%06lu", current];
43 NSString* ExtractRequestGroupIDFromUserAgent(NSString* user_agent) {
44 if (![user_agent length])
47 // The request_group_id is wrapped by parenthesis in the last space-delimited
50 [[user_agent componentsSeparatedByString:@" "] lastObject];
51 NSString* request_group_id =
52 [fragment hasPrefix:@"("] && [fragment hasSuffix:@")"]
53 ? [fragment substringWithRange:NSMakeRange(1, [fragment length] - 2)]
55 // GTLService constructs user agents that end with "(gzip)". To avoid these
56 // getting treated as having the request_group_id "gzip", short-circuit out if
57 // the request_group_id is not long enough to be a valid request_group_id (all
58 // valid request_group_id are at least kMinimumIDLength characters long).
59 // TODO(blundell): Develop a long-term solution to this problem.
61 if ([request_group_id length] < kMinimumIDLength)
63 return request_group_id;
66 NSString* AddRequestGroupIDToUserAgent(NSString* base_user_agent,
67 NSString* request_group_id) {
68 if (!request_group_id)
69 return base_user_agent;
70 // TODO(blundell): Develop a long-term solution to this problem.
72 DCHECK([request_group_id length] >= kMinimumIDLength);
74 [NSString stringWithFormat:@"%@ (%@)", base_user_agent, request_group_id];
77 NSString* ExtractRequestGroupIDFromURL(NSURL* url) {
78 GURL gurl = net::GURLWithNSURL(url);
79 if (!gurl.has_username())
82 std::string request_group_id_as_string;
83 if (base::Base64Decode(gurl.username(), &request_group_id_as_string))
84 return base::SysUTF8ToNSString(request_group_id_as_string);
89 NSURL* AddRequestGroupIDToURL(NSURL* base_url, NSString* request_group_id) {
90 GURL url = net::GURLWithNSURL(base_url);
91 std::string base64RequestGroupID;
92 base::Base64Encode(base::SysNSStringToUTF8(request_group_id),
93 &base64RequestGroupID);
94 GURL::Replacements replacements;
95 replacements.SetUsernameStr(base64RequestGroupID);
96 url = url.ReplaceComponents(replacements);
97 return net::NSURLWithGURL(url);
100 NSString* ExtractRequestGroupIDFromRequest(NSURLRequest* request,
101 NSString* application_scheme) {
102 NSString* user_agent =
103 [[request allHTTPHeaderFields] objectForKey:@"User-Agent"];
104 NSString* request_group_id = ExtractRequestGroupIDFromUserAgent(user_agent);
105 if (request_group_id)
106 return request_group_id;
107 if (application_scheme &&
108 [[request.mainDocumentURL scheme]
109 caseInsensitiveCompare:application_scheme] == NSOrderedSame) {
110 return ExtractRequestGroupIDFromURL(request.mainDocumentURL);