Commit f9a24d5d authored by Olivier Li's avatar Olivier Li Committed by Commit Bot

Add chrome_cleaner/http directory

Bug: 830892
Change-Id: I4e22700df6983b89e0c516c4cb7534f612a28d5e
Reviewed-on: https://chromium-review.googlesource.com/1086047Reviewed-by: default avatarproberge <proberge@chromium.org>
Reviewed-by: default avatarRamin Halavati <rhalavati@chromium.org>
Reviewed-by: default avatarEric Roman <eroman@chromium.org>
Commit-Queue: Oliver Li <olivierli@chromium.org>
Cr-Commit-Position: refs/heads/master@{#564886}
parent a869adee
...@@ -11,6 +11,7 @@ test("chrome_cleaner_unittests") { ...@@ -11,6 +11,7 @@ test("chrome_cleaner_unittests") {
deps = [ deps = [
# Tests from sub-directories. # Tests from sub-directories.
"//chrome/chrome_cleaner/http:unittest_sources",
"//chrome/chrome_cleaner/strings:unittest_sources", "//chrome/chrome_cleaner/strings:unittest_sources",
# Dependencies of the test harness. # Dependencies of the test harness.
......
# Copyright (c) 2018 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.
source_set("http_status_codes") {
sources = [
"http_status_codes.h",
]
}
source_set("http") {
sources = [
"error_utils.cc",
"error_utils.h",
"http_agent.h",
"http_agent_factory.cc",
"http_agent_factory.h",
"http_agent_impl.cc",
"http_agent_impl.h",
"http_response.h",
"internet_helpers.cc",
"internet_helpers.h",
"user_agent.cc",
"user_agent.h",
]
libs = [ "Winhttp.lib" ]
deps = [
"//base:base",
"//net/traffic_annotation",
]
}
source_set("unittest_sources") {
testonly = true
sources = [
"error_utils_unittest.cc",
"internet_helpers_unittest.cc",
"internet_unittest_helpers.cc",
"internet_unittest_helpers.h",
"user_agent_unittest.cc",
]
deps = [
":http",
"//base:base",
"//base/test:test_support",
"//testing/gmock",
"//testing/gtest",
]
}
source_set("mock_http_agent_factory") {
testonly = true
sources = [
"mock_http_agent_factory.cc",
"mock_http_agent_factory.h",
]
deps = [
":http",
":http_status_codes",
"//base",
"//testing/gmock",
"//testing/gtest",
]
}
include_rules = [
"+net/traffic_annotation",
]
// Copyright (c) 2018 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 "chrome/chrome_cleaner/http/error_utils.h"
#include <atlbase.h>
#include <shlwapi.h>
#include <string>
#include "base/stl_util.h"
#include "base/strings/string_util.h"
namespace common {
std::ostream& operator<<(std::ostream& os, const LogHr& hr) {
// Looks up the human-readable system message for the HRESULT code
// and since we're not passing any params to FormatMessage, we don't
// want inserts expanded.
const DWORD kFlags =
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;
char error_text[4096] = {'\0'};
::FormatMessageA(kFlags, 0, hr.hr_, 0, error_text, base::size(error_text),
NULL);
std::string error(error_text);
base::TrimWhitespaceASCII(error, base::TRIM_ALL, &error);
return os << "[hr=0x" << std::hex << hr.hr_ << ", msg=" << error << "]";
}
std::ostream& operator<<(std::ostream& os, const LogWe& we) {
// Looks up the human-readable system message for the Windows error code
// and since we're not passing any params to FormatMessage, we don't
// want inserts expanded.
const DWORD kFlags =
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;
char error_text[4096] = {'\0'};
::FormatMessageA(kFlags, 0, we.we_, 0, error_text, base::size(error_text),
NULL);
std::string error(error_text);
base::TrimWhitespaceASCII(error, base::TRIM_ALL, &error);
return os << "[we=" << we.we_ << ", msg=" << error << "]";
}
} // namespace common
// Copyright (c) 2018 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.
#ifndef CHROME_CHROME_CLEANER_HTTP_ERROR_UTILS_H_
#define CHROME_CHROME_CLEANER_HTTP_ERROR_UTILS_H_
#include <windows.h>
#include <wtypes.h>
#include <ostream>
namespace common {
// Logs HRESULTs verbosely, with the error code and human-readable error
// text if available.
class LogHr {
public:
explicit LogHr(HRESULT hr) : hr_(hr) {}
private:
HRESULT hr_;
friend std::ostream& operator<<(std::ostream&, const LogHr&);
};
std::ostream& operator<<(std::ostream& os, const LogHr& hr);
// Logs Windows errors verbosely, with the error code and human-readable error
// text if available.
class LogWe {
public:
LogWe() : we_(::GetLastError()) {}
explicit LogWe(DWORD we) : we_(we) {}
private:
DWORD we_;
friend std::ostream& operator<<(std::ostream&, const LogWe&);
};
std::ostream& operator<<(std::ostream& os, const LogWe& we);
} // namespace common
#endif // CHROME_CHROME_CLEANER_HTTP_ERROR_UTILS_H_
// Copyright (c) 2018 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 "chrome/chrome_cleaner/http/error_utils.h"
#include <ostream>
#include <string>
#include "testing/gtest/include/gtest/gtest.h"
namespace common {
TEST(ErrorUtils, HrLog) {
{
std::ostringstream stream;
stream << LogHr(S_OK);
std::string str = stream.str();
EXPECT_NE(str.find("0x0,"), std::string::npos);
EXPECT_NE(str.find("msg="), std::string::npos);
}
{
std::ostringstream stream;
stream << LogHr(E_FAIL);
std::string str = stream.str();
EXPECT_NE(str.find("0x80004005,"), std::string::npos);
EXPECT_NE(str.find("msg=Unspecified error"), std::string::npos);
}
}
TEST(ErrorUtils, WeLog) {
{
std::ostringstream stream;
stream << LogWe(ERROR_SUCCESS);
std::string str = stream.str();
EXPECT_NE(str.find("[we=0,"), std::string::npos);
EXPECT_NE(str.find("msg=The operation completed successfully"),
std::string::npos);
}
{
std::ostringstream stream;
stream << LogWe(ERROR_INVALID_FUNCTION);
std::string str = stream.str();
EXPECT_NE(str.find("[we=1,"), std::string::npos);
EXPECT_NE(str.find("msg=Incorrect function"), std::string::npos);
}
}
} // namespace common
// Copyright (c) 2018 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.
#ifndef CHROME_CHROME_CLEANER_HTTP_HTTP_AGENT_H_
#define CHROME_CHROME_CLEANER_HTTP_HTTP_AGENT_H_
#include <stdint.h>
#include <memory>
#include <string>
#include "base/strings/string16.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
namespace chrome_cleaner {
class HttpResponse;
// Defines an interface for issuing HTTP requests.
class HttpAgent {
public:
virtual ~HttpAgent() {}
// Issues an HTTP POST request.
// @param host The target host.
// @param port The target port.
// @param path The resource path.
// @param secure Whether to use HTTPS.
// @param extra_headers Zero or more CRLF-delimited HTTP header lines to
// include in the request.
// @param body The request body.
// @param traffic_annotation provides the required description for auditing.
// Please refer to //docs/network_traffic_annotations.md for more details.
// @returns NULL if the request fails for any reason. Otherwise, returns an
// HttpResponse that may be used to access the HTTP response.
virtual std::unique_ptr<HttpResponse> Post(
const base::string16& host,
uint16_t port,
const base::string16& path,
bool secure,
const base::string16& extra_headers,
const std::string& body,
const net::NetworkTrafficAnnotationTag& traffic_annotation) = 0;
// Issues an HTTP GET request.
// @param host The target host.
// @param port The target port.
// @param path The resource path.
// @param secure Whether to use HTTPS.
// @param extra_headers Zero or more CRLF-delimited HTTP header lines to
// include in the request.
// @param traffic_annotation provides the required description for auditing.
// Please refer to //docs/network_traffic_annotations.md for more details.
// @returns NULL if the request fails for any reason. Otherwise, returns an
// HttpResponse that may be used to access the HTTP response.
virtual std::unique_ptr<HttpResponse> Get(
const base::string16& host,
uint16_t port,
const base::string16& path,
bool secure,
const base::string16& extra_headers,
const net::NetworkTrafficAnnotationTag& traffic_annotation) = 0;
};
} // namespace chrome_cleaner
#endif // CHROME_CHROME_CLEANER_HTTP_HTTP_AGENT_H_
// Copyright (c) 2018 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 "chrome/chrome_cleaner/http/http_agent_factory.h"
#include <memory>
#include "base/file_version_info.h"
#include "base/logging.h"
#include "base/win/current_module.h"
#include "chrome/chrome_cleaner/http/http_agent_impl.h"
namespace chrome_cleaner {
HttpAgentFactory::~HttpAgentFactory() = default;
std::unique_ptr<chrome_cleaner::HttpAgent> HttpAgentFactory::CreateHttpAgent() {
std::unique_ptr<FileVersionInfo> file_version_info(
FileVersionInfo::CreateFileVersionInfoForModule(CURRENT_MODULE()));
DCHECK(file_version_info.get());
if (file_version_info.get()) {
return std::make_unique<chrome_cleaner::HttpAgentImpl>(
file_version_info->product_short_name(),
file_version_info->product_version());
} else {
LOG(ERROR) << "Unable to get version string for Chrome Cleanup Tool.";
return std::make_unique<chrome_cleaner::HttpAgentImpl>(
L"Chrome Cleanup Tool", L"0.0.99");
}
}
} // namespace chrome_cleaner
// Copyright (c) 2018 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.
#ifndef CHROME_CHROME_CLEANER_HTTP_HTTP_AGENT_FACTORY_H_
#define CHROME_CHROME_CLEANER_HTTP_HTTP_AGENT_FACTORY_H_
#include <memory>
namespace chrome_cleaner {
class HttpAgent;
} // namespace chrome_cleaner
namespace chrome_cleaner {
// Factory for creating HttpAgent objects. The default implementation will
// create chrome_cleaner::HttpAgentImpl objects. Used to allow tests to mock out
// an HttpAgent (see mock_http_agent_factory.{h,cc}).
class HttpAgentFactory {
public:
virtual ~HttpAgentFactory();
// Returns an HttpAgent instance.
virtual std::unique_ptr<chrome_cleaner::HttpAgent> CreateHttpAgent();
};
} // namespace chrome_cleaner
#endif // CHROME_CHROME_CLEANER_HTTP_HTTP_AGENT_FACTORY_H_
This diff is collapsed.
// Copyright (c) 2018 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.
#ifndef CHROME_CHROME_CLEANER_HTTP_HTTP_AGENT_IMPL_H_
#define CHROME_CHROME_CLEANER_HTTP_HTTP_AGENT_IMPL_H_
#include <stdint.h>
#include <memory>
#include <string>
#include "base/macros.h"
#include "base/strings/string16.h"
#include "chrome/chrome_cleaner/http/http_agent.h"
namespace chrome_cleaner {
// Implements HttpAgent using WinHttp. Respects the user proxy settings if any.
class HttpAgentImpl : public HttpAgent {
public:
// Constructs an HttpAgentImpl.
// @param product_name The product name to include in the User-Agent header.
// @param product_version The product version to include in the User-Agent
// header.
HttpAgentImpl(const base::string16& product_name,
const base::string16& product_version);
~HttpAgentImpl() override;
// HttpAgent implementation
std::unique_ptr<HttpResponse> Post(
const base::string16& host,
uint16_t port,
const base::string16& path,
bool secure,
const base::string16& extra_headers,
const std::string& body,
const net::NetworkTrafficAnnotationTag& traffic_annotation) override;
std::unique_ptr<HttpResponse> Get(
const base::string16& host,
uint16_t port,
const base::string16& path,
bool secure,
const base::string16& extra_headers,
const net::NetworkTrafficAnnotationTag& traffic_annotation) override;
private:
base::string16 user_agent_;
DISALLOW_COPY_AND_ASSIGN(HttpAgentImpl);
};
} // namespace chrome_cleaner
#endif // CHROME_CHROME_CLEANER_HTTP_HTTP_AGENT_IMPL_H_
// Copyright (c) 2018 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.
#ifndef CHROME_CHROME_CLEANER_HTTP_HTTP_RESPONSE_H_
#define CHROME_CHROME_CLEANER_HTTP_HTTP_RESPONSE_H_
#include <stdint.h>
#include "base/strings/string16.h"
namespace chrome_cleaner {
// Provides access to the headers and content of the response to a previously
// issued HTTP request.
class HttpResponse {
public:
virtual ~HttpResponse() {}
// Retrieves the response status code.
// @param status_code Receives the status code.
// @returns true if successful.
virtual bool GetStatusCode(uint16_t* status_code) = 0;
// Retrieves the specified content length, if any.
// @param has_content_length Is set to true if the content length is
// specified.
// @param content_length Receives the content length (if specified).
// @returns true if the content length is retrieved or not specified in the
// response.
virtual bool GetContentLength(bool* has_content_length,
size_t* content_length) = 0;
// Retrieves the specified Content-Type header value, if any.
// @param has_content_type Is set to true if the content type is
// specified.
// @param content_type Receives the content type (if specified).
// @returns true if the content type is retrieved or not specified in the
// response.
virtual bool GetContentType(bool* has_content_type,
base::string16* content_type) = 0;
// Checks the response body stream.
// @param has_data Is set to true if data is available to read
// @returns true if successful.
virtual bool HasData(bool* has_data) = 0;
// Reads from the response body stream.
// @param buffer The location into which data will be read.
// @param count On invocation, the maximum length to read into buffer. Upon
// successful return, the number of bytes read.
// @returns true if successful.
virtual bool ReadData(char* buffer, size_t* count) = 0;
};
} // namespace chrome_cleaner
#endif // CHROME_CHROME_CLEANER_HTTP_HTTP_RESPONSE_H_
// Copyright (c) 2018 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.
#ifndef CHROME_CHROME_CLEANER_HTTP_HTTP_STATUS_CODES_H_
#define CHROME_CHROME_CLEANER_HTTP_HTTP_STATUS_CODES_H_
// Useful Http status codes. Adapted from net/http/http_status_code_list.h.
enum class HttpStatus {
// Informational 1xx
kContinue = 100,
kSwitchingProtocols = 101,
// Successful 2xx
kOk = 200,
kCreated = 201,
kAccepted = 202,
kNonAuthoritativeInformation = 203,
kNoContent = 204,
kResetContent = 205,
kPartialContent = 206,
// Redirection 3xx
kMultipleChoices = 300,
kMovedPermanently = 301,
kFound = 302,
kSeeOther = 303,
kNotModified = 304,
kUseProxy = 305,
// 306 is no longer used.
kTemporaryRedirect = 307,
kPermanentRedirect = 308,
// Client error 4xx
kBadRequest = 400,
kUnauthorized = 401,
kPaymentRequired = 402,
kForbidden = 403,
kNotFound = 404,
kMethodNotAllowed = 405,
kNotAcceptable = 406,
kProxyAuthenticationRequired = 407,
kRequestTimeout = 408,
kConflict = 409,
kGone = 410,
kLengthRequired = 411,
kPreconditionFailed = 412,
kRequestEntityTooLarge = 413,
kRequestUriTooLong = 414,
kUnsupportedMediaType = 415,
kRequestedRangeNotSatisfiable = 416,
kExpectationFailed = 417,
// Server error 5xx
kInternalServerError = 500,
kNotImplemented = 501,
kBadGateway = 502,
kServiceUnavailable = 503,
kGatewayTimeout = 504,
kVersionNotSupported = 505,
};
#endif // CHROME_CHROME_CLEANER_HTTP_HTTP_STATUS_CODES_H_
// Copyright (c) 2018 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.
// Much of this file has been adapted from Chromium (net/http/http_util.cc) and
// Breakpad (common/linux/http_upload.cc).
// See http://www.ietf.org/rfc/rfc2388.txt for a description of the
// multipart/form-data HTTP message type implemented in this file.
#include "chrome/chrome_cleaner/http/internet_helpers.h"
#include <wchar.h>
#include <winhttp.h> // NOLINT
#include <algorithm>
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_tokenizer.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
namespace chrome_cleaner {
namespace {
// Returns the index of the closing quote of the string, if any. |start| points
// at the opening quote.
size_t FindStringEnd(const base::string16& line,
size_t start,
base::char16 delim) {
DCHECK_LT(start, line.length());
DCHECK_EQ(line[start], delim);
DCHECK((delim == L'"') || (delim == L'\''));
const base::char16 set[] = {delim, L'\\', L'\0'};
for (size_t end = line.find_first_of(set, start + 1);
end != base::string16::npos; end = line.find_first_of(set, end + 2)) {
if (line[end] != L'\\')
return end;
}
return line.length();
}
const base::char16 kHttpLws[] = L" \t";
bool IsLWS(base::char16 c) {
return ::wcschr(kHttpLws, c) != NULL;
}
void TrimLWS(base::string16::const_iterator* begin,
base::string16::const_iterator* end) {
// Skip leading whitespace.
while (*begin < *end && IsLWS((*begin)[0]))
++(*begin);
// Skip trailing whitespace.
while (*begin < *end && IsLWS((*end)[-1]))
--(*end);
}
} // namespace
void ParseContentType(const base::string16& content_type_str,
base::string16* mime_type,
base::string16* charset,
bool* had_charset,
base::string16* boundary) {
const base::string16::const_iterator begin = content_type_str.begin();
// Trim leading and trailing whitespace from type. We include '(' in the
// trailing trim set to catch media-type comments, which are not at all
// standard, but may occur in rare cases.
size_t type_val = content_type_str.find_first_not_of(kHttpLws);
type_val = std::min(type_val, content_type_str.length());
size_t type_end = content_type_str.find_first_of(
base::string16(kHttpLws) + L";(", type_val);
if (type_end == base::string16::npos)
type_end = content_type_str.length();
size_t charset_val = 0;
size_t charset_end = 0;
bool type_has_charset = false;
// Iterate over parameters.
size_t param_start = content_type_str.find_first_of(';', type_end);
if (param_start != std::string::npos) {
base::StringTokenizerT<base::string16, base::string16::const_iterator>
tokenizer(begin + param_start, content_type_str.end(), L";");
tokenizer.set_quote_chars(L"\"");
while (tokenizer.GetNext()) {
base::string16::const_iterator equals_sign =
std::find(tokenizer.token_begin(), tokenizer.token_end(), L'=');
if (equals_sign == tokenizer.token_end())
continue;
base::string16::const_iterator param_name_begin = tokenizer.token_begin();
base::string16::const_iterator param_name_end = equals_sign;
TrimLWS(&param_name_begin, &param_name_end);
base::string16::const_iterator param_value_begin = equals_sign + 1;
base::string16::const_iterator param_value_end = tokenizer.token_end();
DCHECK(param_value_begin <= tokenizer.token_end());
TrimLWS(&param_value_begin, &param_value_end);
if (base::LowerCaseEqualsASCII(
base::StringPiece16(param_name_begin, param_name_end),
"charset")) {
charset_val = param_value_begin - begin;
charset_end = param_value_end - begin;
type_has_charset = true;
} else if (base::LowerCaseEqualsASCII(
base::StringPiece16(param_name_begin, param_name_end),
"boundary")) {
if (boundary)
boundary->assign(param_value_begin, param_value_end);
}
}
}
if (type_has_charset) {
// Trim leading and trailing whitespace from charset_val. We include '(' in
// the trailing trim set to catch media-type comments, which are not at all
// standard, but may occur in rare cases.
charset_val = content_type_str.find_first_not_of(kHttpLws, charset_val);
charset_val = std::min(charset_val, charset_end);
base::char16 first_char = content_type_str[charset_val];
if (first_char == L'"' || first_char == L'\'') {
charset_end = FindStringEnd(content_type_str, charset_val, first_char);
++charset_val;
DCHECK(charset_end >= charset_val);
} else {
charset_end = std::min(content_type_str.find_first_of(
base::string16(kHttpLws) + L";(", charset_val),
charset_end);
}
}
// If the server sent "*/*", it is meaningless, so do not store it.
// Also, if type_val is the same as mime_type, then just update the charset
// However, if charset is empty and mime_type hasn't changed, then don't
// wipe-out an existing charset. We also want to reject a mime-type if it does
// not include a slash. Some servers give junk after the charset parameter,
// which may include a comma, so this check makes us a bit more tolerant.
if (content_type_str.length() != 0 && content_type_str != L"*/*" &&
content_type_str.find_first_of(L'/') != base::string16::npos) {
// The common case here is that mime_type is empty.
bool eq = !mime_type->empty() &&
base::LowerCaseEqualsASCII(
base::StringPiece16(begin + type_val, begin + type_end),
base::WideToUTF8(*mime_type).data());
if (!eq) {
mime_type->assign(base::ToLowerASCII(
base::StringPiece16(begin + type_val, begin + type_end)));
}
if ((!eq && *had_charset) || type_has_charset) {
*had_charset = true;
charset->assign(base::ToLowerASCII(
base::StringPiece16(begin + charset_val, begin + charset_end)));
}
}
}
bool DecomposeUrl(const base::string16& url,
base::string16* scheme,
base::string16* host,
uint16_t* port,
base::string16* path) {
DCHECK(scheme);
DCHECK(host);
DCHECK(path);
wchar_t scheme_buffer[16], host_buffer[256], path_buffer[256];
URL_COMPONENTS components;
memset(&components, 0, sizeof(components));
components.dwStructSize = sizeof(components);
components.lpszScheme = scheme_buffer;
components.dwSchemeLength = sizeof(scheme_buffer) / sizeof(scheme_buffer[0]);
components.lpszHostName = host_buffer;
components.dwHostNameLength = sizeof(host_buffer) / sizeof(host_buffer[0]);
components.lpszUrlPath = path_buffer;
components.dwUrlPathLength = sizeof(path_buffer) / sizeof(path_buffer[0]);
if (!::WinHttpCrackUrl(url.c_str(), 0, 0, &components))
return false;
*scheme = scheme_buffer;
*host = host_buffer;
*path = path_buffer;
*port = components.nPort;
return true;
}
base::string16 ComposeUrl(const base::string16& host,
uint16_t port,
const base::string16& path,
bool secure) {
if (secure) {
if (port == 443)
return L"https://" + host + path;
return L"https://" + host + L':' + base::UintToString16(port) + path;
}
if (port == 80)
return L"http://" + host + path;
return L"http://" + host + L':' + base::UintToString16(port) + path;
}
base::string16 GenerateMultipartHttpRequestBoundary() {
// The boundary has 27 '-' characters followed by 16 hex digits.
static const base::char16 kBoundaryPrefix[] = L"---------------------------";
static const size_t kBoundaryLength = 27 + 16;
// Generate some random numbers to fill out the boundary.
int r0 = rand();
int r1 = rand();
// Add one character for the NULL termination.
base::char16 temp[kBoundaryLength + 1];
::swprintf(temp, sizeof(temp) / sizeof(*temp), L"%s%08X%08X", kBoundaryPrefix,
r0, r1);
return base::string16(temp, kBoundaryLength);
}
base::string16 GenerateMultipartHttpRequestContentTypeHeader(
const base::string16 boundary) {
return L"Content-Type: multipart/form-data; boundary=" + boundary;
}
std::string GenerateMultipartHttpRequestBody(
const std::map<base::string16, base::string16>& parameters,
const std::string& upload_file,
const base::string16& file_part_name,
const base::string16& boundary) {
DCHECK(!boundary.empty());
DCHECK(!file_part_name.empty());
std::string boundary_utf8 = base::WideToUTF8(boundary);
std::string request_body;
// Append each of the parameter pairs as a form-data part.
for (const auto& entry : parameters) {
request_body.append("--" + boundary_utf8 + "\r\n");
request_body.append("Content-Disposition: form-data; name=\"" +
base::WideToUTF8(entry.first) + "\"\r\n\r\n" +
base::WideToUTF8(entry.second) + "\r\n");
}
std::string file_part_name_utf8 = base::WideToUTF8(file_part_name);
request_body.append("--" + boundary_utf8 + "\r\n");
request_body.append(
"Content-Disposition: form-data; "
"name=\"" +
file_part_name_utf8 +
"\"; "
"filename=\"" +
file_part_name_utf8 + "\"\r\n");
request_body.append("Content-Type: application/octet-stream\r\n");
request_body.append("\r\n");
request_body.append(upload_file);
request_body.append("\r\n");
request_body.append("--" + boundary_utf8 + "--\r\n");
return request_body;
}
} // namespace chrome_cleaner
// Copyright (c) 2018 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.
#ifndef CHROME_CHROME_CLEANER_HTTP_INTERNET_HELPERS_H_
#define CHROME_CHROME_CLEANER_HTTP_INTERNET_HELPERS_H_
#include <Windows.h> // NOLINT
#include <stdint.h>
#include <map>
#include <string>
#include "base/strings/string16.h"
namespace chrome_cleaner {
// Parses the value of a Content-Type header.
// @param content_type_str The value of a Content-Type HTTP header (without the
// label or ':').
// @param mime_type Receives the specified mime-type, if any, in lower-case.
// Unmodified otherwise.
// @param charset Receives the specified charset, if any, in lower-case.
// Unmodified otherwise.
// @param boundary Optional. Receives the (quoted) value of the boundary
// parameter, if any. Unmodified otherwise.
void ParseContentType(const base::string16& content_type_str,
base::string16* mime_type,
base::string16* charset,
bool* had_charset,
base::string16* boundary);
// Parses an URL.
// @param url The URL to parse.
// @param scheme Receives the parsed scheme.
// @param host Receives the parsed host.
// @param port Receives the parsed port (or the implicit default port).
// @param path Receives the parsed path.
// @returns true if the URL is successfully parsed.
bool DecomposeUrl(const base::string16& url,
base::string16* scheme,
base::string16* host,
uint16_t* port,
base::string16* path);
// Composes an HTTP or HTTPS URL.
// @param host The URL host component.
// @param port The URL port component.
// @param path The URL path component.
// @returns The composed URL.
base::string16 ComposeUrl(const base::string16& host,
uint16_t port,
const base::string16& path,
bool secure);
// @returns A random string to be used as a multipart MIME message boundary.
base::string16 GenerateMultipartHttpRequestBoundary();
// Generates an appropriate Content-Type header (starting with "Content-Type:")
// for a multi-part HTTP message.
// @param boundary The MIME boundary to use.
// @returns An HTTP Content-Type header suitable for the multipart message
// generated by GenerateMultipartHttpRequestBody.
base::string16 GenerateMultipartHttpRequestContentTypeHeader(
const base::string16 boundary);
// Generates a multipart HTTP message body.
// @param parameters HTTP request parameters to be encoded in the body.
// @param upload_file File contents to be encoded in the body.
// @param file_part_name The parameter name to be assigned to the file part.
// @param boundary The MIME boundary to use.
// @returns A multipart HTTP message body.
std::string GenerateMultipartHttpRequestBody(
const std::map<base::string16, base::string16>& parameters,
const std::string& upload_file,
const base::string16& file_part_name,
const base::string16& boundary);
} // namespace chrome_cleaner
#endif // CHROME_CHROME_CLEANER_HTTP_INTERNET_HELPERS_H_
// Copyright (c) 2018 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 "chrome/chrome_cleaner/http/internet_helpers.h"
#include <string>
#include "base/stl_util.h"
#include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/chrome_cleaner/http/internet_unittest_helpers.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace chrome_cleaner {
TEST(InternetHelpersTest, ParseContentType) {
const struct {
const base::char16* content_type;
const base::char16* expected_mime_type;
const base::char16* expected_charset;
const bool expected_had_charset;
const base::char16* expected_boundary;
} tests[] = {
{L"text/html; charset=utf-8", L"text/html", L"utf-8", true, L""},
{L"text/html; charset=", L"text/html", L"", true, L""},
{L"text/html; charset", L"text/html", L"", false, L""},
{L"text/html; charset='", L"text/html", L"", true, L""},
{L"text/html; charset='utf-8'", L"text/html", L"utf-8", true, L""},
{L"text/html; charset=\"utf-8\"", L"text/html", L"utf-8", true, L""},
{L"text/html; charset =utf-8", L"text/html", L"utf-8", true, L""},
{L"text/html; charset= utf-8", L"text/html", L"utf-8", true, L""},
{L"text/html; charset=utf-8 ", L"text/html", L"utf-8", true, L""},
{L"text/html; boundary=\"WebKit-ada-df-dsf-adsfadsfs\"", L"text/html",
L"", false, L"\"WebKit-ada-df-dsf-adsfadsfs\""},
{L"text/html; boundary =\"WebKit-ada-df-dsf-adsfadsfs\"", L"text/html",
L"", false, L"\"WebKit-ada-df-dsf-adsfadsfs\""},
{L"text/html; boundary= \"WebKit-ada-df-dsf-adsfadsfs\"", L"text/html",
L"", false, L"\"WebKit-ada-df-dsf-adsfadsfs\""},
{L"text/html; boundary= \"WebKit-ada-df-dsf-adsfadsfs\" ", L"text/html",
L"", false, L"\"WebKit-ada-df-dsf-adsfadsfs\""},
{L"text/html; boundary=\"WebKit-ada-df-dsf-adsfadsfs \"", L"text/html",
L"", false, L"\"WebKit-ada-df-dsf-adsfadsfs \""},
{L"text/html; boundary=WebKit-ada-df-dsf-adsfadsfs", L"text/html", L"",
false, L"WebKit-ada-df-dsf-adsfadsfs"},
};
for (size_t i = 0; i < base::size(tests); ++i) {
base::string16 mime_type;
base::string16 charset;
bool had_charset = false;
base::string16 boundary;
ParseContentType(tests[i].content_type, &mime_type, &charset, &had_charset,
&boundary);
EXPECT_EQ(tests[i].expected_mime_type, mime_type) << "i=" << i;
EXPECT_EQ(tests[i].expected_charset, charset) << "i=" << i;
EXPECT_EQ(tests[i].expected_had_charset, had_charset) << "i=" << i;
EXPECT_EQ(tests[i].expected_boundary, boundary) << "i=" << i;
}
}
TEST(InternetHelpersTest, ComposeAndDecomposeUrl) {
const struct {
const base::char16* url;
const base::char16* scheme;
const base::char16* host;
uint16_t port;
const base::char16* path;
} tests[] = {
{L"http://example.com/", L"http", L"example.com", 80, L"/"},
{L"https://example.com/", L"https", L"example.com", 443, L"/"},
{L"https://sub.example.com/", L"https", L"sub.example.com", 443, L"/"},
{L"https://example.com:9999/", L"https", L"example.com", 9999, L"/"},
{L"http://example.com/a/b/c", L"http", L"example.com", 80, L"/a/b/c"},
};
for (size_t i = 0; i < base::size(tests); ++i) {
base::string16 scheme, host, path;
uint16_t port = 0;
EXPECT_TRUE(DecomposeUrl(tests[i].url, &scheme, &host, &port, &path))
<< "i=" << i;
EXPECT_EQ(tests[i].scheme, scheme) << "i=" << i;
EXPECT_EQ(tests[i].host, host) << "i=" << i;
EXPECT_EQ(tests[i].port, port) << "i=" << i;
EXPECT_EQ(tests[i].path, path) << "i=" << i;
EXPECT_EQ(tests[i].url,
ComposeUrl(tests[i].host, tests[i].port, tests[i].path,
tests[i].scheme == base::string16(L"https")));
}
const base::char16* invalid_urls[] = {
L"", L"example.com", L"example.com/foo",
L"/foo/bar", L"example.com:80", L"http://",
L"http:", L"http:/example.com", L"http:example.com"};
for (size_t i = 0; i < base::size(invalid_urls); ++i) {
base::string16 scheme, host, path;
uint16_t port = 0;
EXPECT_FALSE(DecomposeUrl(invalid_urls[i], &scheme, &host, &port, &path))
<< "i=" << i;
}
}
TEST(InternetHelpersTest, GenerateMultipartHttpRequestBoundary) {
base::string16 boundary1 = GenerateMultipartHttpRequestBoundary();
base::string16 boundary2 = GenerateMultipartHttpRequestBoundary();
EXPECT_FALSE(boundary1.empty());
EXPECT_FALSE(boundary2.empty());
EXPECT_NE(boundary1, boundary2);
ASSERT_EQ(base::string16::npos,
boundary1.find_first_not_of(L"-0123456789abcdefABCDEF"));
}
TEST(InternetHelpersTest, GenerateMultipartHttpRequestContentTypeHeader) {
base::string16 boundary = GenerateMultipartHttpRequestBoundary();
base::string16 content_type_header =
GenerateMultipartHttpRequestContentTypeHeader(boundary);
size_t semicolon = content_type_header.find(L':');
ASSERT_NE(base::string16::npos, semicolon);
base::string16 mime_type, charset, parsed_boundary;
bool had_charset = false;
ParseContentType(base::string16(content_type_header.begin() + semicolon + 1,
content_type_header.end()),
&mime_type, &charset, &had_charset, &parsed_boundary);
EXPECT_EQ(boundary, parsed_boundary);
EXPECT_TRUE(charset.empty());
EXPECT_FALSE(had_charset);
EXPECT_EQ(L"multipart/form-data", mime_type);
}
TEST(InternetHelpersTest, GenerateMultipartHttpRequestBody) {
std::map<base::string16, base::string16> parameters;
parameters[L"param"] = L"value";
base::string16 boundary = GenerateMultipartHttpRequestBoundary();
std::string file = "file contents";
base::string16 file_part_name = L"file_name";
std::string body = GenerateMultipartHttpRequestBody(parameters, file,
file_part_name, boundary);
ExpectMultipartMimeMessageIsPlausible(boundary, parameters, file,
base::WideToUTF8(file_part_name), body);
}
} // namespace chrome_cleaner
// Copyright (c) 2018 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 "chrome/chrome_cleaner/http/internet_unittest_helpers.h"
#include <algorithm>
#include "base/strings/utf_string_conversions.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace chrome_cleaner {
void ExpectMultipartMimeMessageIsPlausible(
const base::string16& boundary,
const std::map<base::string16, base::string16>& parameters,
const std::string& file,
const std::string& file_part_name,
const std::string& body) {
std::string::const_iterator range_begin = body.begin();
if (!parameters.empty()) {
std::string key = base::WideToUTF8(parameters.begin()->first);
std::string value = base::WideToUTF8(parameters.begin()->second);
range_begin = std::search(range_begin, body.end(), key.begin(), key.end());
EXPECT_NE(range_begin, body.end());
range_begin =
std::search(range_begin, body.end(), value.begin(), value.end());
EXPECT_NE(range_begin, body.end());
}
range_begin =
std::search(range_begin, body.end(), boundary.begin(), boundary.end());
EXPECT_NE(range_begin, body.end());
range_begin = std::search(range_begin, body.end(), file_part_name.begin(),
file_part_name.end());
EXPECT_NE(range_begin, body.end());
range_begin = std::search(range_begin, body.end(), file.begin(), file.end());
EXPECT_NE(range_begin, body.end());
}
} // namespace chrome_cleaner
// Copyright (c) 2018 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.
#ifndef CHROME_CHROME_CLEANER_HTTP_INTERNET_UNITTEST_HELPERS_H_
#define CHROME_CHROME_CLEANER_HTTP_INTERNET_UNITTEST_HELPERS_H_
#include <map>
#include <string>
#include "base/strings/string16.h"
namespace chrome_cleaner {
// Verifies that the supplied multipart MIME message body is plausibly
// formatted. Adds non-fatal GTest failures if verification fails.
// @param boundary The boundary specified in the Content-Type header that
// accompanied the body.
// @param parameters The parameters that are expected to be encoded in the body.
// @param file The file contents that are expdected to be encoded in the body.
// @param file_part_name The name expected to be assigned to the file parameter.
void ExpectMultipartMimeMessageIsPlausible(
const base::string16& boundary,
const std::map<base::string16, base::string16>& parameters,
const std::string& file,
const std::string& file_part_name,
const std::string& body);
} // namespace chrome_cleaner
#endif // CHROME_CHROME_CLEANER_HTTP_INTERNET_UNITTEST_HELPERS_H_
// Copyright (c) 2018 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 "chrome/chrome_cleaner/http/mock_http_agent_factory.h"
#include <stdint.h>
#include <algorithm>
#include <memory>
#include <string>
#include <vector>
#include "base/logging.h"
#include "chrome/chrome_cleaner/http/http_agent.h"
#include "chrome/chrome_cleaner/http/http_agent_factory.h"
#include "chrome/chrome_cleaner/http/http_response.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace chrome_cleaner {
namespace {
// Class that provides a response based on how the MockHttpAgentConfig is
// configured.
class MockHttpResponse : public chrome_cleaner::HttpResponse {
public:
explicit MockHttpResponse(MockHttpAgentConfig* config) : config_(config) {
DCHECK(config);
}
~MockHttpResponse() override = default;
// chrome_cleaner::HttpResponse:
bool GetStatusCode(uint16_t* status_code) override {
if (config_->GetCurrentCalls().get_status_code_succeeds) {
*status_code = static_cast<uint16_t>(
config_->GetCurrentCalls().get_status_code_result);
}
return config_->GetCurrentCalls().get_status_code_succeeds;
}
bool GetContentLength(bool* has_content_length,
size_t* content_length) override {
ADD_FAILURE() << "This method should not be called.";
return false;
}
bool GetContentType(bool* has_content_type,
base::string16* content_type) override {
ADD_FAILURE() << "This method should not be called.";
return false;
}
bool HasData(bool* has_data) override {
if (config_->GetCurrentCalls().has_data_succeeds)
*has_data = !config_->GetCurrentCalls().read_data_result.empty();
return config_->GetCurrentCalls().has_data_succeeds;
}
bool ReadData(char* buffer, size_t* count) override {
MockHttpAgentConfig::Calls& calls = config_->GetCurrentCalls();
bool succeeds = calls.read_data_succeeds_by_default;
if (!calls.read_data_success_sequence.empty()) {
succeeds = calls.read_data_success_sequence[0];
calls.read_data_success_sequence.erase(
calls.read_data_success_sequence.begin());
}
if (succeeds)
config_->ReadData(buffer, count);
return succeeds;
}
private:
MockHttpAgentConfig* config_{nullptr};
DISALLOW_COPY_AND_ASSIGN(MockHttpResponse);
};
// Class that acts as an HttpAgent based on how the MockHttpAgentConfig is
// configured.
class MockHttpAgent : public chrome_cleaner::HttpAgent {
public:
explicit MockHttpAgent(MockHttpAgentConfig* config) : config_(config) {
DCHECK(config);
}
~MockHttpAgent() override = default;
// chrome_cleaner::HttpAgent:
std::unique_ptr<chrome_cleaner::HttpResponse> Post(
const base::string16& host,
uint16_t port,
const base::string16& path,
bool secure,
const base::string16& extra_headers,
const std::string& body,
const net::NetworkTrafficAnnotationTag& /*traffic_annotation*/) override {
const bool post_succeeds = config_->GetCurrentCalls().request_succeeds;
MockHttpAgentConfig::RequestData post_data;
post_data.host = host;
post_data.port = port;
post_data.path = path;
post_data.secure = secure;
post_data.extra_headers = extra_headers;
post_data.body = body;
config_->AddRequestData(post_data);
if (post_succeeds)
return std::make_unique<MockHttpResponse>(config_);
return std::unique_ptr<MockHttpResponse>();
}
// chrome_cleaner::HttpAgent:
std::unique_ptr<chrome_cleaner::HttpResponse> Get(
const base::string16& host,
uint16_t port,
const base::string16& path,
bool secure,
const base::string16& extra_headers,
const net::NetworkTrafficAnnotationTag& /*traffic_annotation*/) override {
const bool get_succeeds = config_->GetCurrentCalls().request_succeeds;
MockHttpAgentConfig::RequestData get_data;
get_data.host = host;
get_data.port = port;
get_data.path = path;
get_data.secure = secure;
get_data.extra_headers = extra_headers;
config_->AddRequestData(get_data);
if (get_succeeds)
return std::make_unique<MockHttpResponse>(config_);
return std::unique_ptr<MockHttpResponse>();
}
private:
MockHttpAgentConfig* config_{nullptr};
DISALLOW_COPY_AND_ASSIGN(MockHttpAgent);
};
} // namespace
MockHttpAgentConfig::Calls::Calls(HttpStatus status)
: get_status_code_result(status) {}
MockHttpAgentConfig::Calls::Calls(const Calls& other) = default;
MockHttpAgentConfig::Calls::~Calls() = default;
MockHttpAgentConfig::Calls& MockHttpAgentConfig::Calls::operator=(
const MockHttpAgentConfig::Calls& other) = default;
MockHttpAgentConfig::RequestData::RequestData() = default;
MockHttpAgentConfig::RequestData::RequestData(const RequestData& other) =
default;
MockHttpAgentConfig::RequestData::~RequestData() = default;
MockHttpAgentConfig::RequestData& MockHttpAgentConfig::RequestData::operator=(
const MockHttpAgentConfig::RequestData& other) = default;
MockHttpAgentConfig::MockHttpAgentConfig() = default;
MockHttpAgentConfig::~MockHttpAgentConfig() = default;
size_t MockHttpAgentConfig::AddCalls(const Calls& calls) {
calls_.push_back(calls);
return calls_.size() - 1;
}
MockHttpAgentConfig::Calls& MockHttpAgentConfig::GetCurrentCalls() {
if (current_index_ >= calls_.size()) {
static Calls default_calls(HttpStatus::kOk);
ADD_FAILURE() << "Did not expect more than " << calls_.size() << " tries";
return default_calls;
}
return calls_[current_index_];
}
void MockHttpAgentConfig::ReadData(char* buffer, size_t* count) {
if (current_index_ >= calls_.size()) {
ADD_FAILURE() << "Reading data for an unexpected call";
*count = 0;
return;
}
Calls& calls = calls_[current_index_];
*count = std::min(*count, calls.read_data_result.size());
memcpy(buffer, calls.read_data_result.c_str(), *count);
calls.read_data_result = calls.read_data_result.substr(*count);
}
void MockHttpAgentConfig::AddRequestData(const RequestData& request_data) {
ASSERT_EQ(request_data_.size(), current_index_)
<< "MockHttpAgentConfig does not support creating multiple agents "
<< "without calling Post or Get on each before creating the next one. "
<< "Suggest adding support to MockHttpAgentConfig for that if necessary, "
<< "or updating your code to avoid this.";
request_data_.push_back(request_data);
}
MockHttpAgentFactory::MockHttpAgentFactory(MockHttpAgentConfig* config)
: config_(config) {
DCHECK(config);
}
std::unique_ptr<chrome_cleaner::HttpAgent>
MockHttpAgentFactory::CreateHttpAgent() {
// Set the configuration index to the next one (one per HttpAgent).
if (config_->current_index_ == MockHttpAgentConfig::kInvalidIndex)
config_->current_index_ = 0;
else
++config_->current_index_;
return std::make_unique<MockHttpAgent>(config_);
}
} // namespace chrome_cleaner
// Copyright (c) 2018 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.
#ifndef CHROME_CHROME_CLEANER_HTTP_MOCK_HTTP_AGENT_FACTORY_H_
#define CHROME_CHROME_CLEANER_HTTP_MOCK_HTTP_AGENT_FACTORY_H_
#include <stdint.h>
#include <memory>
#include <string>
#include <vector>
#include "base/macros.h"
#include "base/strings/string16.h"
#include "chrome/chrome_cleaner/http/http_agent_factory.h"
#include "chrome/chrome_cleaner/http/http_status_codes.h"
namespace chrome_cleaner {
class HttpAgent;
} // namespace chrome_cleaner
namespace chrome_cleaner {
// Stores configuration and results for a mock HttpAgent object created by
// the MockHttpAgentFactory class below.
class MockHttpAgentConfig {
public:
// Class used to configure how the various methods should behave when called.
struct Calls {
explicit Calls(HttpStatus status);
Calls(const Calls& other);
~Calls();
Calls& operator=(const Calls& other);
// Whether a call to Post or Get on the HttpAgent should succeed or not. If
// it does, an HttpResponse object will be returned and will behave
// according to the configuration set below. Otherwise, the Post or Get
// method will return null.
bool request_succeeds{true};
// The rest of this struct configures the HttpResponse that will be
// returned.
bool get_status_code_succeeds{true};
HttpStatus get_status_code_result{HttpStatus::kOk};
bool has_data_succeeds{true};
// If |read_data_success_sequence| contains one or more values, those will
// be returned by ReadData in that sequence. When the sequence is empty,
// |read_data_succeeds_by_default| will be returned for subsequent calls.
std::vector<bool> read_data_success_sequence;
bool read_data_succeeds_by_default{true};
std::string read_data_result;
};
// Struct that stores the values passed to Post or Get for validation.
struct RequestData {
RequestData();
RequestData(const RequestData& other);
~RequestData();
RequestData& operator=(const RequestData& other);
base::string16 host;
uint16_t port;
base::string16 path;
bool secure;
base::string16 extra_headers;
std::string body;
};
MockHttpAgentConfig();
~MockHttpAgentConfig();
// Adds a call configuration. There should be one configuration for each
// expected call to Post or Get on the HttpAgent (the test will fail
// otherwise). Returns the index of the new configuration.
size_t AddCalls(const Calls& calls);
// Returns the current Calls configuration.
Calls& GetCurrentCalls();
// Reads up to |*count| bytes from the |calls_.read_data_result| call
// configuration string, and returns the data in |buffer|. |count| will be
// updated with the number of bytes actually read.
void ReadData(char* buffer, size_t* count);
// Returns the number of calls to Post() that were recorded so far.
size_t num_request_data() const { return request_data_.size(); }
// Returns the RequestData for the |index|th call to Post().
const RequestData& request_data(size_t index) const {
return request_data_[index];
}
// Adds the data passed to a call to Post() or Get(). This should be called
// only once per Calls configuration. Used by the mock HttpAgent when Post or
// Get is called.
void AddRequestData(const RequestData& request_data);
friend class MockHttpAgentFactory;
private:
// Invalid configuration index.
static const size_t kInvalidIndex = static_cast<size_t>(-1);
// List of call configuration for every step of the call sequence. When a new
// HttpAgent is created, the test moves to the next calls configuration.
std::vector<Calls> calls_;
// The request data for every call to Post or Get (in sequence).
std::vector<RequestData> request_data_;
// The index of the current Calls configuration being used.
size_t current_index_{kInvalidIndex};
DISALLOW_COPY_AND_ASSIGN(MockHttpAgentConfig);
};
// HttpAgent factory that creates mock HttpAgent objects that are controlled by
// a MockHttpAgentConfig object.
class MockHttpAgentFactory : public HttpAgentFactory {
public:
explicit MockHttpAgentFactory(MockHttpAgentConfig* config);
// HttpAgentFactory:
std::unique_ptr<chrome_cleaner::HttpAgent> CreateHttpAgent() override;
private:
MockHttpAgentConfig* config_{nullptr};
DISALLOW_COPY_AND_ASSIGN(MockHttpAgentFactory);
};
} // namespace chrome_cleaner
#endif // CHROME_CHROME_CLEANER_HTTP_MOCK_HTTP_AGENT_FACTORY_H_
// Copyright (c) 2018 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 "chrome/chrome_cleaner/http/user_agent.h"
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
namespace chrome_cleaner {
namespace {
const base::char16* ArchitectureToString(UserAgent::Architecture architecture) {
switch (architecture) {
case UserAgent::WOW64:
return L"; WOW64";
case UserAgent::X64:
return L"; Win64; x64";
case UserAgent::IA64:
return L"; Win64; IA64";
case UserAgent::X86:
return L"";
default:
NOTREACHED();
return L"";
}
}
} // namespace
UserAgent::UserAgent(const base::string16& product_name,
const base::string16& product_version)
: product_name_(product_name),
product_version_(product_version),
os_major_version_(0),
os_minor_version_(0),
architecture_(X86) {}
UserAgent::~UserAgent() {}
base::string16 UserAgent::AsString() {
return product_name_ + L"/" + product_version_ + L" (Windows NT " +
base::IntToString16(os_major_version_) + L"." +
base::IntToString16(os_minor_version_) +
ArchitectureToString(architecture_) + L") WinHTTP/" + winhttp_version_;
}
} // namespace chrome_cleaner
// Copyright (c) 2018 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.
#ifndef CHROME_CHROME_CLEANER_HTTP_USER_AGENT_H_
#define CHROME_CHROME_CLEANER_HTTP_USER_AGENT_H_
#include <stdint.h>
#include "base/macros.h"
#include "base/strings/string16.h"
namespace chrome_cleaner {
// Collects the various properties that go into the Chrome Cleanup Tool
// user-agent string and formats them.
class UserAgent {
public:
enum Architecture { X86, WOW64, X64, IA64 };
// Creates a default-initialized instance. This does not query platform
// attributes. The client must do so.
// @param product_name The product name.
// @param product_version The product version.
UserAgent(const base::string16& product_name,
const base::string16& product_version);
~UserAgent();
// @returns A string suitable for use as the value of a User-Agent header, and
// incorporating the various properties of this class.
base::string16 AsString();
// Sets the OS version.
// @param major_version The OS major version number.
// @param minor_version The OS minor version number.
void set_os_version(int32_t major_version, int32_t minor_version) {
os_major_version_ = major_version;
os_minor_version_ = minor_version;
}
// Sets the platform architecture.
// @param architecture The platform architecture.
void set_architecture(Architecture architecture) {
architecture_ = architecture;
}
// Sets the WinHttp library version.
// @winhttp_version The WinHttp library version.
void set_winhttp_version(const base::string16& winhttp_version) {
winhttp_version_ = winhttp_version;
}
private:
base::string16 product_name_;
base::string16 product_version_;
int32_t os_major_version_;
int32_t os_minor_version_;
Architecture architecture_;
base::string16 winhttp_version_;
DISALLOW_COPY_AND_ASSIGN(UserAgent);
};
} // namespace chrome_cleaner
#endif // CHROME_CHROME_CLEANER_HTTP_USER_AGENT_H_
// Copyright (c) 2018 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 "chrome/chrome_cleaner/http/user_agent.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace chrome_cleaner {
TEST(UserAgentTest, BasicTest) {
UserAgent user_agent(L"product", L"1.0");
user_agent.set_os_version(11, 13);
user_agent.set_winhttp_version(L"super_duper");
user_agent.set_architecture(UserAgent::WOW64);
EXPECT_EQ(L"product/1.0 (Windows NT 11.13; WOW64) WinHTTP/super_duper",
user_agent.AsString());
}
} // namespace chrome_cleaner
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment