Coverage for /Syzygy/kasko/http_agent_impl.cc

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

Coverage information generated Thu Jan 14 17:40:38 2016.