summaryrefslogtreecommitdiffstats
path: root/ios/web/net/request_group_util.mm
blob: 9a41cef393270b0125dfc8b13b91225b91b4d4fa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ios/web/net/request_group_util.h"

#include <Foundation/Foundation.h>

#include "base/base64.h"
#include "base/logging.h"
#include "base/rand_util.h"
#include "base/strings/sys_string_conversions.h"
#import "net/base/mac/url_conversions.h"
#include "url/gurl.h"

namespace {
// Minimum length for a request group ID. A shorter string is considered as an
// invalid ID.
const int kMinimumIDLength = 5;
}

namespace web {

// Generates a request-group ID used to correlate web requests with the embedder
// that triggered it. It is important that the return value should not be unique
// for different users. See crbug/355613 for context.
NSString* GenerateNewRequestGroupID() {
  const unsigned int kGroupSize = 1000;
  static unsigned long count = 0;
  static unsigned int offset = base::RandInt(0, kGroupSize - 1);

  unsigned long current = count++;
  if (current < kGroupSize)
    current = (current + offset) % kGroupSize;

  // The returned string must have a minimum of kMinimumIDLength characters, and
  // no spaces.
  // TODO(blundell): Develop a long-term solution to this problem.
  // crbug.com/329243
  return [NSString stringWithFormat:@"%06lu", current];
}

NSString* ExtractRequestGroupIDFromUserAgent(NSString* user_agent) {
  if (![user_agent length])
    return nil;

  // The request_group_id is wrapped by parenthesis in the last space-delimited
  // fragment.
  NSString* fragment =
      [[user_agent componentsSeparatedByString:@" "] lastObject];
  NSString* request_group_id =
      [fragment hasPrefix:@"("] && [fragment hasSuffix:@")"]
          ? [fragment substringWithRange:NSMakeRange(1, [fragment length] - 2)]
          : nil;
  // GTLService constructs user agents that end with "(gzip)". To avoid these
  // getting treated as having the request_group_id "gzip", short-circuit out if
  // the request_group_id is not long enough to be a valid request_group_id (all
  // valid request_group_id are at least kMinimumIDLength characters long).
  // TODO(blundell): Develop a long-term solution to this problem.
  // crbug.com/329243
  if ([request_group_id length] < kMinimumIDLength)
    return nil;
  return request_group_id;
}

NSString* AddRequestGroupIDToUserAgent(NSString* base_user_agent,
                                       NSString* request_group_id) {
  // TODO(blundell): Develop a long-term solution to this problem.
  // crbug.com/329243
  DCHECK([request_group_id length] >= kMinimumIDLength);
  return
      [NSString stringWithFormat:@"%@ (%@)", base_user_agent, request_group_id];
}

NSString* ExtractRequestGroupIDFromURL(NSURL* url) {
  GURL gurl = net::GURLWithNSURL(url);
  if (!gurl.has_username())
    return nil;

  std::string request_group_id_as_string;
  if (base::Base64Decode(gurl.username(), &request_group_id_as_string))
    return base::SysUTF8ToNSString(request_group_id_as_string);

  return nil;
}

NSURL* AddRequestGroupIDToURL(NSURL* base_url, NSString* request_group_id) {
  GURL url = net::GURLWithNSURL(base_url);
  std::string base64RequestGroupID;
  base::Base64Encode(base::SysNSStringToUTF8(request_group_id),
                     &base64RequestGroupID);
  GURL::Replacements replacements;
  replacements.SetUsernameStr(base64RequestGroupID);
  url = url.ReplaceComponents(replacements);
  return net::NSURLWithGURL(url);
}

NSString* ExtractRequestGroupIDFromRequest(NSURLRequest* request,
                                           NSString* application_scheme) {
  NSString* user_agent =
      [[request allHTTPHeaderFields] objectForKey:@"User-Agent"];
  NSString* request_group_id = ExtractRequestGroupIDFromUserAgent(user_agent);
  if (request_group_id)
    return request_group_id;
  if (application_scheme &&
      [[request.mainDocumentURL scheme]
          caseInsensitiveCompare:application_scheme] == NSOrderedSame) {
    return ExtractRequestGroupIDFromURL(request.mainDocumentURL);
  }
  return nil;
}

}  // namespace web