Coverage for /Syzygy/kasko/http_agent_impl.cc

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
0.0%00358.C++source

Line-by-line coverage:

   1    :  // Copyright 2014 Google Inc. All Rights Reserved.
   2    :  //
   3    :  // Licensed under the Apache License, Version 2.0 (the "License");
   4    :  // you may not use this file except in compliance with the License.
   5    :  // You may obtain a copy of the License at
   6    :  //
   7    :  //     http://www.apache.org/licenses/LICENSE-2.0
   8    :  //
   9    :  // Unless required by applicable law or agreed to in writing, software
  10    :  // distributed under the License is distributed on an "AS IS" BASIS,
  11    :  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12    :  // See the License for the specific language governing permissions and
  13    :  // limitations under the License.
  14    :  
  15    :  #include "syzygy/kasko/http_agent_impl.h"
  16    :  
  17    :  #include <windows.h>
  18    :  #include <winhttp.h>
  19    :  
  20    :  #include <memory>
  21    :  #include <string>
  22    :  
  23    :  #include "base/file_version_info.h"
  24    :  #include "base/logging.h"
  25    :  #include "base/macros.h"
  26    :  #include "base/sys_info.h"
  27    :  #include "base/strings/string16.h"
  28    :  #include "base/win/scoped_handle.h"
  29    :  #include "base/win/windows_version.h"
  30    :  #include "syzygy/common/com_utils.h"
  31    :  #include "syzygy/kasko/http_response.h"
  32    :  #include "syzygy/kasko/internet_helpers.h"
  33    :  #include "syzygy/kasko/user_agent.h"
  34    :  
  35  m :  namespace kasko {
  36    :  
  37  m :  namespace {
  38    :  
  39  m :  class WinHttpHandleTraits {
  40  m :   public:
  41  m :    typedef HINTERNET Handle;
  42  m :    static bool CloseHandle(HINTERNET handle) {
  43  m :      return ::WinHttpCloseHandle(handle) == TRUE;
  44  m :    }
  45  m :    static bool IsHandleValid(HINTERNET handle) { return handle != nullptr; }
  46  m :    static HINTERNET NullHandle() { return nullptr; }
  47    :  
  48  m :   private:
  49  m :    DISALLOW_IMPLICIT_CONSTRUCTORS(WinHttpHandleTraits);
  50  m :  };
  51    :  
  52  m :  typedef base::win::GenericScopedHandle<WinHttpHandleTraits,
  53  m :                                         base::win::DummyVerifierTraits>
  54  m :      ScopedWinHttpHandle;
  55    :  
  56    :  // A helper class that retrieves and frees user proxy settings.
  57  m :  class AutoWinHttpProxyConfig {
  58  m :   public:
  59  m :    AutoWinHttpProxyConfig() : proxy_config_() {}
  60  m :    ~AutoWinHttpProxyConfig() {
  61  m :      if (proxy_config_.lpszAutoConfigUrl)
  62  m :        ::GlobalFree(proxy_config_.lpszAutoConfigUrl);
  63  m :      if (proxy_config_.lpszProxy)
  64  m :        ::GlobalFree(proxy_config_.lpszProxy);
  65  m :      if (proxy_config_.lpszProxyBypass)
  66  m :        ::GlobalFree(proxy_config_.lpszProxyBypass);
  67  m :    }
  68    :  
  69    :    // Attempts to load the user proxy settings. If successful, returns true.
  70  m :    bool Load() {
  71  m :      if (::WinHttpGetIEProxyConfigForCurrentUser(&proxy_config_))
  72  m :        return true;
  73    :  
  74  m :      LOG(ERROR) << "WinHttpGetIEProxyConfigForCurrentUser() failed: "
  75  m :                 << ::common::LogWe();
  76  m :      return false;
  77  m :    }
  78    :  
  79  m :    DWORD access_type() const {
  80  m :      return (proxy() == WINHTTP_NO_PROXY_NAME) ? WINHTTP_ACCESS_TYPE_NO_PROXY
  81  m :                                                : WINHTTP_ACCESS_TYPE_NAMED_PROXY;
  82  m :    }
  83    :  
  84    :    // Indicates whether proxy auto-detection is enabled.
  85  m :    const bool auto_detect() const { return proxy_config_.fAutoDetect == TRUE; }
  86    :  
  87    :    // Returns the proxy auto-configuration URL, or an empty string if automatic
  88    :    // proxy configuration is disabled. Only valid after a successful call to
  89    :    // Load().
  90  m :    const base::char16* auto_config_url() const {
  91  m :      return proxy_config_.lpszAutoConfigUrl ? proxy_config_.lpszAutoConfigUrl
  92  m :                                             : L"";
  93  m :    }
  94    :  
  95    :    // Returns the proxy configuration string that should be passed to
  96    :    // WinHttpOpen.
  97  m :    const base::char16* proxy() const {
  98  m :      return (proxy_config_.lpszProxy && proxy_config_.lpszProxy[0] != 0)
  99  m :                 ? proxy_config_.lpszProxy
 100  m :                 : WINHTTP_NO_PROXY_NAME;
 101  m :    }
 102    :  
 103    :    // Returns the proxy bypass configuration string that should be passed to
 104    :    // WinHttpOpen. Only valid after a successful call to Load().
 105  m :    const base::char16* proxy_bypass() const {
 106  m :      return access_type() == WINHTTP_ACCESS_TYPE_NO_PROXY
 107  m :                 ? WINHTTP_NO_PROXY_BYPASS
 108  m :                 : proxy_config_.lpszProxyBypass;
 109  m :    }
 110    :  
 111  m :   private:
 112  m :    WINHTTP_CURRENT_USER_IE_PROXY_CONFIG proxy_config_;
 113    :  
 114  m :    DISALLOW_COPY_AND_ASSIGN(AutoWinHttpProxyConfig);
 115  m :  };
 116    :  
 117    :  // A helper class that retrieves and frees URL-specific proxy settings.
 118  m :  class AutoWinHttpUrlProxyConfig {
 119  m :   public:
 120    :    // Constructs an instance that will use the auto-configuration URL (if any)
 121    :    // from |proxy_config| to retrieve URL-specific proxy settings.
 122  m :    explicit AutoWinHttpUrlProxyConfig(const AutoWinHttpProxyConfig& proxy_config)
 123  m :        : auto_detect_(proxy_config.auto_detect()),
 124  m :          auto_config_url_(proxy_config.auto_config_url()),
 125  m :          is_valid_(false),
 126  m :          url_proxy_config_() {}
 127    :  
 128  m :    ~AutoWinHttpUrlProxyConfig() {
 129  m :      if (url_proxy_config_.lpszProxy)
 130  m :        ::GlobalFree(url_proxy_config_.lpszProxy);
 131  m :      if (url_proxy_config_.lpszProxyBypass)
 132  m :        ::GlobalFree(url_proxy_config_.lpszProxyBypass);
 133  m :    }
 134    :  
 135    :    // Loads URL-specific proxy settings for |url| using |session|. Returns true
 136    :    // if auto-configuration is disabled or if the settings are successfully
 137    :    // loaded.
 138  m :    bool Load(HINTERNET session, const base::string16& url) {
 139    :      // http://msdn.microsoft.com/en-us/library/fze2ytx2(v=vs.110).aspx implies
 140    :      // that auto-detection is to be used before a specified configuration file.
 141    :  
 142    :      // TODO(erikwright): It's not clear if an error from WinHttpGetProxyForUrl
 143    :      // means that no proxy is detected and we should proceed with a direct
 144    :      // connection or that something unexpected happened. In the latter case we
 145    :      // should presumably log an error and possibly not attempt a direct
 146    :      // connection. Manual testing will be required to verify the behaviour of
 147    :      // this code in different proxy scenarios.
 148  m :      if (auto_detect_) {
 149  m :        WINHTTP_AUTOPROXY_OPTIONS options = {0};
 150  m :        options.dwFlags =
 151  m :            WINHTTP_AUTOPROXY_AUTO_DETECT | WINHTTP_AUTOPROXY_RUN_OUTPROCESS_ONLY;
 152  m :        options.dwAutoDetectFlags =
 153  m :            WINHTTP_AUTO_DETECT_TYPE_DHCP | WINHTTP_AUTO_DETECT_TYPE_DNS_A;
 154  m :        if (::WinHttpGetProxyForUrl(session, url.c_str(), &options,
 155  m :                                    &url_proxy_config_)) {
 156  m :          is_valid_ = true;
 157  m :          return true;
 158  m :        }
 159    :  
 160  m :        switch (::GetLastError()) {
 161  m :          case ERROR_WINHTTP_AUTODETECTION_FAILED:
 162  m :          case ERROR_WINHTTP_AUTO_PROXY_SERVICE_ERROR:
 163  m :            break;
 164  m :          default:
 165  m :            LOG(ERROR) << "Unexpected error durring "
 166  m :                          "WinHttpGetProxyForUrl(WINHTTP_AUTOPROXY_AUTO_DETECT)"
 167  m :                       << ::common::LogWe();
 168  m :            return false;
 169  m :        }
 170  m :      }
 171    :  
 172    :      // Auto-detection is disabled or did not detect a configuration.
 173  m :      if (!auto_config_url_.empty()) {
 174  m :        WINHTTP_AUTOPROXY_OPTIONS options = {0};
 175  m :        options.dwFlags = WINHTTP_AUTOPROXY_CONFIG_URL;
 176  m :        options.lpszAutoConfigUrl = auto_config_url_.c_str();
 177    :  
 178  m :        if (::WinHttpGetProxyForUrl(session, url.c_str(), &options,
 179  m :                                    &url_proxy_config_)) {
 180  m :          is_valid_ = true;
 181  m :          return true;
 182  m :        }
 183    :  
 184  m :        LOG(ERROR)
 185  m :            << "WinHttpGetProxyForUrl(WINHTTP_AUTOPROXY_CONFIG_URL) failed: "
 186  m :            << ::common::LogWe();
 187  m :        return false;
 188  m :      }
 189  m :      return true;
 190  m :    }
 191    :  
 192    :    // Returns the loaded settings, or NULL if auto-configuration is disabled.
 193    :    // Only valid after a successful call to Load().
 194  m :    WINHTTP_PROXY_INFO* get() {
 195  m :      return is_valid_ ? &url_proxy_config_ : nullptr;
 196  m :    }
 197    :  
 198  m :   private:
 199  m :    bool auto_detect_;
 200  m :    base::string16 auto_config_url_;
 201  m :    bool is_valid_;
 202  m :    WINHTTP_PROXY_INFO url_proxy_config_;
 203    :  
 204  m :    DISALLOW_COPY_AND_ASSIGN(AutoWinHttpUrlProxyConfig);
 205  m :  };
 206    :  
 207    :  // Implements HttpResponse using the WinHTTP API.
 208  m :  class HttpResponseImpl : public HttpResponse {
 209  m :   public:
 210  m :    ~HttpResponseImpl() override;
 211    :  
 212    :    // Issues the request defined by its parameters and, if successful, returns an
 213    :    // HttpResponse that may be used to access the response. See HttpAgent::Post
 214    :    // for a description of the parameters.
 215  m :    static std::unique_ptr<HttpResponse> Create(
 216  m :        const base::string16& user_agent,
 217  m :        const base::string16& host,
 218  m :        uint16_t port,
 219  m :        const base::string16& path,
 220  m :        bool secure,
 221  m :        const base::string16& extra_headers,
 222  m :        const std::string& body);
 223    :  
 224    :    // HttpResponse implementation.
 225  m :    bool GetStatusCode(uint16_t* status_code) override;
 226  m :    bool GetContentLength(bool* has_content_length,
 227  m :                          size_t* content_length) override;
 228  m :    bool GetContentType(bool* has_content_type,
 229  m :                        base::string16* content_type) override;
 230  m :    bool HasData(bool* has_data) override;
 231  m :    bool ReadData(char* buffer, size_t* count) override;
 232    :  
 233  m :   private:
 234  m :    HttpResponseImpl();
 235    :  
 236    :    // Invokes WinHttpQueryHeaders. If the header indicated by |info_level| is
 237    :    // present its value will be read into |buffer| (having size |buffer_length|).
 238    :    // |header_present| will indicate whether the header was found. The result is
 239    :    // true if the header is successfully read or determined to be absent.
 240  m :    bool QueryHeader(DWORD info_level,
 241  m :                     bool* header_present,
 242  m :                     void* buffer,
 243  m :                     DWORD buffer_length);
 244    :  
 245    :    // WinHttp handles used for the request.
 246  m :    ScopedWinHttpHandle session_;
 247  m :    ScopedWinHttpHandle connection_;
 248  m :    ScopedWinHttpHandle request_;
 249    :  
 250  m :    DISALLOW_COPY_AND_ASSIGN(HttpResponseImpl);
 251  m :  };
 252    :  
 253  m :  HttpResponseImpl::~HttpResponseImpl() {}
 254    :  
 255    :  // static
 256  m :  std::unique_ptr<HttpResponse> HttpResponseImpl::Create(
 257  m :      const base::string16& user_agent,
 258  m :      const base::string16& host,
 259  m :      uint16_t port,
 260  m :      const base::string16& path,
 261  m :      bool secure,
 262  m :      const base::string16& extra_headers,
 263  m :      const std::string& body) {
 264    :    // Retrieve the user's proxy configuration.
 265  m :    AutoWinHttpProxyConfig proxy_config;
 266  m :    if (!proxy_config.Load())
 267  m :      return std::unique_ptr<HttpResponse>();
 268    :  
 269    :    // Tentatively create an instance. We will return it if we are able to
 270    :    // successfully initialize it.
 271  m :    std::unique_ptr<HttpResponseImpl> instance(new HttpResponseImpl);
 272    :  
 273    :    // Open a WinHTTP session.
 274  m :    instance->session_.Set(
 275  m :        ::WinHttpOpen(user_agent.c_str(), proxy_config.access_type(),
 276  m :                      proxy_config.proxy(), proxy_config.proxy_bypass(), 0));
 277  m :    if (!instance->session_.IsValid()) {
 278  m :      LOG(ERROR) << "WinHttpOpen() failed: " << ::common::LogWe();
 279  m :      return std::unique_ptr<HttpResponse>();
 280  m :    }
 281    :  
 282    :    // Look up URL-specific proxy settings. If this fails, we will fall back to
 283    :    // working without a proxy.
 284  m :    AutoWinHttpUrlProxyConfig url_proxy_config(proxy_config);
 285  m :    url_proxy_config.Load(instance->session_.Get(),
 286  m :                          ComposeUrl(host, port, path, secure));
 287    :  
 288    :    // Connect to a host/port.
 289  m :    instance->connection_.Set(
 290  m :        ::WinHttpConnect(instance->session_.Get(), host.c_str(), port, 0));
 291  m :    if (!instance->connection_.IsValid()) {
 292  m :      LOG(ERROR) << "WinHttpConnect() failed with host " << host << " and port "
 293  m :                 << port << ": " << ::common::LogWe();
 294  m :      return std::unique_ptr<HttpResponse>();
 295  m :    }
 296    :  
 297    :    // Initiate a request. This doesn't actually send the request yet.
 298  m :    instance->request_.Set(
 299  m :        ::WinHttpOpenRequest(instance->connection_.Get(), L"POST", path.c_str(),
 300  m :                             NULL,  // version
 301  m :                             NULL,  // referer
 302  m :                             NULL,  // accept types
 303  m :                             secure ? WINHTTP_FLAG_SECURE : 0));
 304  m :    if (!instance->connection_.IsValid()) {
 305  m :      LOG(ERROR) << "WinHttpConnect() failed with host " << host << " and port "
 306  m :                 << port << ": " << ::common::LogWe();
 307  m :      return std::unique_ptr<HttpResponse>();
 308  m :    }
 309    :  
 310    :    // Disable cookies and authentication. This request should be completely
 311    :    // stateless and untied to any identity of any sort.
 312  m :    DWORD option_value = WINHTTP_DISABLE_COOKIES | WINHTTP_DISABLE_AUTHENTICATION;
 313  m :    if (!::WinHttpSetOption(instance->request_.Get(),
 314  m :                            WINHTTP_OPTION_DISABLE_FEATURE, &option_value,
 315  m :                            sizeof(option_value))) {
 316  m :      LOG(ERROR) << "WinHttpSetOption(WINHTTP_DISABLE_COOKIES | "
 317  m :                    "WINHTTP_DISABLE_AUTHENTICATION) failed: "
 318  m :                 << ::common::LogWe();
 319  m :      return std::unique_ptr<HttpResponse>();
 320  m :    }
 321    :  
 322    :    // If this URL is configured to use a proxy, set that up now.
 323  m :    if (url_proxy_config.get()) {
 324  m :      if (!::WinHttpSetOption(instance->request_.Get(), WINHTTP_OPTION_PROXY,
 325  m :                              url_proxy_config.get(),
 326  m :                              sizeof(*url_proxy_config.get()))) {
 327  m :        LOG(ERROR) << "WinHttpSetOption(WINHTTP_OPTION_PROXY) failed: "
 328  m :                   << ::common::LogWe();
 329  m :        return std::unique_ptr<HttpResponse>();
 330  m :      }
 331  m :    }
 332    :  
 333    :    // Send the request.
 334  m :    if (!::WinHttpSendRequest(instance->request_.Get(), extra_headers.c_str(),
 335  m :                              static_cast<DWORD>(-1),
 336  m :                              const_cast<char*>(body.data()),
 337  m :                              static_cast<DWORD>(body.size()),
 338  m :                              static_cast<DWORD>(body.size()), NULL)) {
 339  m :      LOG(ERROR) << "Failed to send HTTP request to host " << host << " and port "
 340  m :                 << port << ": " << ::common::LogWe();
 341  m :      return std::unique_ptr<HttpResponse>();
 342  m :    }
 343    :  
 344    :    // This seems to read at least all headers from the response. The remainder of
 345    :    // the body, if any, may be read during subsequent calls to WinHttpReadData().
 346  m :    if (!::WinHttpReceiveResponse(instance->request_.Get(), 0)) {
 347  m :      LOG(ERROR) << "Failed to complete HTTP request to host " << host
 348  m :                 << " and port " << port << ": " << ::common::LogWe();
 349  m :      return std::unique_ptr<HttpResponse>();
 350  m :    }
 351    :  
 352  m :    return std::move(instance);
 353  m :  }
 354    :  
 355  m :  bool HttpResponseImpl::GetStatusCode(uint16_t* status_code) {
 356  m :    DCHECK(status_code);
 357    :  
 358  m :    bool has_status_code = false;
 359  m :    DWORD status_code_buffer = 0;
 360    :  
 361  m :    if (QueryHeader(WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER,
 362  m :                    &has_status_code, &status_code_buffer,
 363  m :                    sizeof(status_code_buffer))) {
 364  m :      DCHECK(has_status_code);
 365  m :      *status_code = status_code_buffer;
 366  m :      return true;
 367  m :    }
 368    :  
 369  m :    return false;
 370  m :  }
 371    :  
 372  m :  bool HttpResponseImpl::GetContentLength(bool* has_content_length,
 373  m :                                          size_t* content_length) {
 374  m :    DCHECK(has_content_length);
 375  m :    DCHECK(content_length);
 376    :  
 377  m :    DWORD content_length_header_value = 0;
 378    :  
 379  m :    if (QueryHeader(WINHTTP_QUERY_CONTENT_LENGTH | WINHTTP_QUERY_FLAG_NUMBER,
 380  m :                    has_content_length, &content_length_header_value,
 381  m :                    sizeof(content_length_header_value))) {
 382  m :      if (has_content_length)
 383  m :        *content_length = content_length_header_value;
 384  m :      return true;
 385  m :    }
 386    :  
 387  m :    return false;
 388  m :  }
 389    :  
 390  m :  bool HttpResponseImpl::GetContentType(bool* has_content_type,
 391  m :                                        base::string16* content_type) {
 392  m :    DCHECK(has_content_type);
 393  m :    DCHECK(content_type);
 394    :  
 395  m :    base::char16 content_type_buffer[256] = {0};
 396    :  
 397  m :    if (QueryHeader(WINHTTP_QUERY_CONTENT_TYPE, has_content_type,
 398  m :                    &content_type_buffer, sizeof(content_type_buffer))) {
 399  m :      if (has_content_type)
 400  m :        *content_type = content_type_buffer;
 401  m :      return true;
 402  m :    }
 403    :  
 404  m :    return false;
 405  m :  }
 406    :  
 407  m :  bool HttpResponseImpl::HasData(bool* has_data) {
 408  m :    DCHECK(has_data);
 409    :  
 410  m :    DWORD leftover_data = 0;
 411  m :    if (!::WinHttpQueryDataAvailable(request_.Get(), &leftover_data)) {
 412  m :      LOG(ERROR) << "WinHttpQueryDataAvailable failed: " << ::common::LogWe();
 413  m :      return false;
 414  m :    }
 415  m :    if (leftover_data != 0)
 416  m :      *has_data = true;
 417  m :    else
 418  m :      *has_data = false;
 419  m :    return true;
 420  m :  }
 421    :  
 422  m :  bool HttpResponseImpl::ReadData(char* buffer, size_t* count) {
 423  m :    DCHECK(buffer);
 424  m :    DCHECK(count);
 425    :  
 426  m :    DWORD size_read = 0;
 427  m :    if (!::WinHttpReadData(request_.Get(), buffer, *count, &size_read)) {
 428  m :      LOG(ERROR) << "Failed to read response body: " << ::common::LogWe();
 429  m :      return false;
 430  m :    }
 431  m :    *count = size_read;
 432  m :    return true;
 433  m :  }
 434    :  
 435  m :  HttpResponseImpl::HttpResponseImpl() {}
 436    :  
 437  m :  bool HttpResponseImpl::QueryHeader(DWORD info_level,
 438  m :                                     bool* header_present,
 439  m :                                     void* buffer,
 440  m :                                     DWORD buffer_length) {
 441  m :    if (::WinHttpQueryHeaders(request_.Get(), info_level,
 442  m :                              WINHTTP_HEADER_NAME_BY_INDEX, buffer,
 443  m :                              &buffer_length, 0)) {
 444  m :      *header_present = true;
 445  m :      return true;
 446  m :    }
 447    :  
 448  m :    if (::GetLastError() == ERROR_WINHTTP_HEADER_NOT_FOUND) {
 449  m :      *header_present = false;
 450  m :      return true;
 451  m :    }
 452    :  
 453  m :    LOG(ERROR) << "WinHttpQueryHeaders failed:" << ::common::LogWe();
 454  m :    return false;
 455  m :  }
 456    :  
 457  m :  base::string16 GetWinHttpVersion() {
 458  m :    HMODULE win_http_module = nullptr;
 459  m :    if (::GetModuleHandleEx(0, L"winhttp.dll", &win_http_module)) {
 460  m :      std::unique_ptr<FileVersionInfo> win_http_module_version_info(
 461  m :          FileVersionInfo::CreateFileVersionInfoForModule(win_http_module));
 462  m :      ::FreeLibrary(win_http_module);
 463  m :      if (win_http_module_version_info)
 464  m :        return win_http_module_version_info->product_version();
 465  m :    }
 466  m :    return L"?";
 467  m :  }
 468    :  
 469    :  // Adapted from Chromium content/common/user_agent.cc
 470  m :  void GetOSAndCPU(UserAgent* user_agent) {
 471  m :    int32_t os_major_version = 0;
 472  m :    int32_t os_minor_version = 0;
 473  m :    int32_t os_bugfix_version = 0;
 474  m :    base::SysInfo::OperatingSystemVersionNumbers(&os_major_version,
 475  m :                                                 &os_minor_version,
 476  m :                                                 &os_bugfix_version);
 477  m :    user_agent->set_os_version(os_major_version, os_minor_version);
 478    :  
 479  m :    base::win::OSInfo* os_info = base::win::OSInfo::GetInstance();
 480  m :    if (os_info->wow64_status() == base::win::OSInfo::WOW64_ENABLED) {
 481  m :      user_agent->set_architecture(UserAgent::WOW64);
 482  m :    } else {
 483  m :      base::win::OSInfo::WindowsArchitecture windows_architecture =
 484  m :          os_info->architecture();
 485  m :      if (windows_architecture == base::win::OSInfo::X64_ARCHITECTURE)
 486  m :        user_agent->set_architecture(UserAgent::X64);
 487  m :      else if (windows_architecture == base::win::OSInfo::IA64_ARCHITECTURE)
 488  m :        user_agent->set_architecture(UserAgent::IA64);
 489  m :      else
 490  m :        user_agent->set_architecture(UserAgent::X86);
 491  m :    }
 492  m :  }
 493    :  
 494  m :  }  // namespace
 495    :  
 496  m :  HttpAgentImpl::HttpAgentImpl(const base::string16& product_name,
 497  m :                               const base::string16& product_version) {
 498  m :    UserAgent user_agent(product_name, product_version);
 499  m :    user_agent.set_winhttp_version(GetWinHttpVersion());
 500  m :    GetOSAndCPU(&user_agent);
 501  m :    user_agent_ = user_agent.AsString();
 502  m :  }
 503    :  
 504  m :  HttpAgentImpl::~HttpAgentImpl() {}
 505    :  
 506  m :  std::unique_ptr<HttpResponse> HttpAgentImpl::Post(
 507  m :      const base::string16& host,
 508  m :      uint16_t port,
 509  m :      const base::string16& path,
 510  m :      bool secure,
 511  m :      const base::string16& extra_headers,
 512  m :      const std::string& body) {
 513  m :    return HttpResponseImpl::Create(user_agent_, host, port, path, secure,
 514  m :                                    extra_headers, body);
 515  m :  }
 516    :  
 517  m :  }  // namespace kasko

Coverage information generated Fri Jul 29 11:00:21 2016.