// Copyright 2013 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.

#import "ios/chrome/browser/net/metrics_network_client.h"

#import "base/ios/weak_nsobject.h"
#include "base/logging.h"
#include "base/metrics/sparse_histogram.h"
#include "base/strings/sys_string_conversions.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h"
#import "ios/chrome/browser/net/metrics_network_client_manager.h"
#include "ios/web/public/url_util.h"
#include "net/base/net_errors.h"
#include "net/http/http_response_headers.h"
#include "net/url_request/url_request.h"

@interface MetricsNetworkClient () {
  BOOL _histogramUpdated;
  // Pointer to the load time record for this request. This will be created
  // and owned by |_manager|, and it will remain valid as long as |_manager| is.
  PageLoadTimeRecord* _loadTimeRecord;
  // Pointer to the creating manager, which is owned by a tab. All network
  // requests for the tab are destroyed before the tab is, so this pointer
  // will always be valid as long as the owning client is alive.
  MetricsNetworkClientManager* _manager;
  // A pointer to the request, kept so it can be referred to later.
  scoped_refptr<net::HttpResponseHeaders> _nativeHeaders;
}
- (void)updateHistogram:(NSInteger)code;
@end

@implementation MetricsNetworkClient

- (void)updateHistogram:(NSInteger)code {
  DCHECK(!_histogramUpdated) << "Histogram should not be updated twice.";
  // The |error| must be in the |net::kErrorDomain|. All those errors are
  // defined in |net/base/net_error_list.h|, must be negative values that
  // fits in an |int|. See |net/base/net_errors.h| for more information.
  DCHECK_LE(code, 0) << "Net error codes should be negative.";
  DCHECK_GT(code, INT_MIN) << "Net error code should fit in an int.";
  // On iOS, we cannot distinguish between main frames, images and other
  // subresources. Consequently, all the codes are aggregated in
  // |ErrorCodesForMainFrame3|.
  // The other histograms (such as |ErrorCodesForHTTPSGoogleMainFrame2|,
  // |ErrorCodesForImages| and |ErrorCodesForSubresources2|) are not filled.
  UMA_HISTOGRAM_SPARSE_SLOWLY("Net.ErrorCodesForMainFrame3",
                              static_cast<int>(-code));
  _histogramUpdated = YES;
}

- (instancetype)initWithManager:(MetricsNetworkClientManager*)manager {
  if ((self = [super init])) {
    _manager = manager;
  }
  return self;
}

#pragma mark CRNNetworkClientProtocol methods

- (void)didCreateNativeRequest:(net::URLRequest*)nativeRequest {
  [super didCreateNativeRequest:nativeRequest];
  GURL url = web::GURLByRemovingRefFromGURL(nativeRequest->original_url());
  _loadTimeRecord =
      [_manager recordForPageLoad:url time:nativeRequest->creation_time()];
  _nativeHeaders = nativeRequest->response_headers();
}

- (void)didFailWithNSErrorCode:(NSInteger)nsErrorCode
                  netErrorCode:(int)netErrorCode {
  [super didFailWithNSErrorCode:nsErrorCode netErrorCode:netErrorCode];

  // Ignore NSURLErrorCancelled errors, which are sometimes created to
  // silently abort loads.

  if (nsErrorCode != NSURLErrorCancelled) {
    [self updateHistogram:netErrorCode];
  }
}

- (void)didFinishLoading {
  [super didFinishLoading];
  [self updateHistogram:net::OK];
  if (!_nativeHeaders)
    return;

  if (!_loadTimeRecord)
    return;

  [_loadTimeRecord
      setDataProxyUsed:data_reduction_proxy::HasDataReductionProxyViaHeader(
                           _nativeHeaders.get(), NULL)];
}

@end