mirror of
https://github.com/chromium/crashpad.git
synced 2024-12-26 23:01:05 +08:00
fuchsia: Implement TLS support in HTTPTransportSocket
With use_boringssl_for_http_transport_socket set, this also works on Linux, however the bots fail during run lacking libcrypto.so.1.1. So, not enabled on Linux until that's figured out. (Includes https://github.com/yhirose/cpp-httplib/pull/70, until it lands and I'll do a full roll of cpp-httplib then.) Bug: crashpad:30, crashpad:196 Change-Id: I987f6a87f8e47160c15e53fe1ce28611339069ff Reviewed-on: https://chromium-review.googlesource.com/1075726 Reviewed-by: Robert Sesek <rsesek@chromium.org> Reviewed-by: Joshua Peraza <jperaza@chromium.org> Commit-Queue: Scott Graham <scottmg@chromium.org>
This commit is contained in:
parent
5191992ae5
commit
5c49c59847
@ -230,6 +230,10 @@ base::FilePath TestPaths::BuildArtifact(
|
||||
directory = base::FilePath();
|
||||
#endif
|
||||
break;
|
||||
|
||||
case FileType::kCertificate:
|
||||
extension = FILE_PATH_LITERAL(".pem");
|
||||
break;
|
||||
}
|
||||
|
||||
return directory.Append(test_name + FILE_PATH_LITERAL("_") + artifact +
|
||||
|
@ -39,6 +39,9 @@ class TestPaths {
|
||||
//! \brief `.dll` will be used on Windows, and `.so` will be used on other
|
||||
//! platforms.
|
||||
kLoadableModule,
|
||||
|
||||
//! \brief `.pem` used for all platforms.
|
||||
kCertificate,
|
||||
};
|
||||
|
||||
//! \brief The architecture of the file requested of BuildArtifact().
|
||||
|
14
third_party/cpp-httplib/cpp-httplib/httplib.h
vendored
14
third_party/cpp-httplib/cpp-httplib/httplib.h
vendored
@ -1630,12 +1630,18 @@ inline int Server::bind_internal(const char* host, int port, int socket_flags)
|
||||
}
|
||||
|
||||
if (port == 0) {
|
||||
struct sockaddr_in sin;
|
||||
socklen_t len = sizeof(sin);
|
||||
if (getsockname(svr_sock_, reinterpret_cast<struct sockaddr *>(&sin), &len) == -1) {
|
||||
struct sockaddr_storage address;
|
||||
socklen_t len = sizeof(address);
|
||||
if (getsockname(svr_sock_, reinterpret_cast<struct sockaddr *>(&address), &len) == -1) {
|
||||
return -1;
|
||||
}
|
||||
return ntohs(sin.sin_port);
|
||||
if (address.ss_family == AF_INET) {
|
||||
return ntohs(reinterpret_cast<struct sockaddr_in*>(&address)->sin_port);
|
||||
} else if (address.ss_family == AF_INET6) {
|
||||
return ntohs(reinterpret_cast<struct sockaddr_in6*>(&address)->sin6_port);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
return port;
|
||||
}
|
||||
|
@ -14,6 +14,10 @@
|
||||
|
||||
import("../build/crashpad_buildconfig.gni")
|
||||
|
||||
declare_args() {
|
||||
use_boringssl_for_http_transport_socket = crashpad_is_fuchsia
|
||||
}
|
||||
|
||||
if (crashpad_is_mac) {
|
||||
if (crashpad_is_in_chromium) {
|
||||
import("//build/config/sysroot.gni")
|
||||
@ -246,6 +250,13 @@ static_library("util") {
|
||||
|
||||
if (crashpad_is_linux || crashpad_is_fuchsia) {
|
||||
sources += [ "net/http_transport_socket.cc" ]
|
||||
if (use_boringssl_for_http_transport_socket) {
|
||||
defines = [ "CRASHPAD_USE_BORINGSSL" ]
|
||||
libs = [
|
||||
"crypto",
|
||||
"ssl",
|
||||
]
|
||||
}
|
||||
} else if (crashpad_is_android) {
|
||||
sources += [ "net/http_transport_none.cc" ]
|
||||
}
|
||||
@ -379,11 +390,11 @@ static_library("util") {
|
||||
|
||||
if (crashpad_is_fuchsia) {
|
||||
sources += [
|
||||
"fuchsia/system_exception_port_key.h",
|
||||
"fuchsia/koid_utilities.cc",
|
||||
"fuchsia/koid_utilities.h",
|
||||
"fuchsia/scoped_task_suspend.cc",
|
||||
"fuchsia/scoped_task_suspend.h",
|
||||
"fuchsia/system_exception_port_key.h",
|
||||
"misc/capture_context_fuchsia.S",
|
||||
"misc/paths_fuchsia.cc",
|
||||
"process/process_memory_fuchsia.cc",
|
||||
@ -435,6 +446,17 @@ static_library("util") {
|
||||
}
|
||||
}
|
||||
|
||||
if (use_boringssl_for_http_transport_socket) {
|
||||
action("generate_test_server_key") {
|
||||
script = "net/generate_test_server_key.py"
|
||||
outputs = [
|
||||
"$root_out_dir/crashpad_util_test_cert.pem",
|
||||
"$root_out_dir/crashpad_util_test_key.pem",
|
||||
]
|
||||
data = outputs
|
||||
}
|
||||
}
|
||||
|
||||
if (!crashpad_is_android) {
|
||||
crashpad_executable("http_transport_test_server") {
|
||||
testonly = true
|
||||
@ -451,14 +473,21 @@ if (!crashpad_is_android) {
|
||||
]
|
||||
|
||||
if (crashpad_is_standalone) {
|
||||
remove_configs = [
|
||||
"//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors",
|
||||
]
|
||||
remove_configs = [ "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors" ]
|
||||
}
|
||||
|
||||
if (crashpad_is_win) {
|
||||
libs = [ "ws2_32.lib" ]
|
||||
}
|
||||
|
||||
if (use_boringssl_for_http_transport_socket) {
|
||||
data_deps = [ ":generate_test_server_key" ]
|
||||
defines = [ "CRASHPAD_USE_BORINGSSL" ]
|
||||
libs = [
|
||||
"crypto",
|
||||
"ssl",
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -610,6 +639,10 @@ source_set("util_test") {
|
||||
data_deps = [
|
||||
":http_transport_test_server",
|
||||
]
|
||||
|
||||
if (use_boringssl_for_http_transport_socket) {
|
||||
defines = [ "CRASHPAD_USE_BORINGSSL" ]
|
||||
}
|
||||
}
|
||||
|
||||
if (crashpad_is_mac) {
|
||||
|
26
util/net/generate_test_server_key.py
Executable file
26
util/net/generate_test_server_key.py
Executable file
@ -0,0 +1,26 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# Copyright 2018 The Crashpad Authors. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import subprocess
|
||||
|
||||
# GN requires a Python script for actions, so this just wraps the openssl
|
||||
# command needed to generate a test private key and a certificate. These names
|
||||
# must correspond to what TestPaths::BuildArtifact() constructs.
|
||||
key = 'crashpad_util_test_key.pem'
|
||||
cert = 'crashpad_util_test_cert.pem'
|
||||
subprocess.check_call(
|
||||
['openssl', 'req', '-x509', '-nodes', '-subj', '/CN=localhost',
|
||||
'-days', '365', '-newkey', 'rsa:2048', '-keyout', key, '-out', cert])
|
@ -52,4 +52,8 @@ void HTTPTransport::SetTimeout(double timeout) {
|
||||
timeout_ = timeout;
|
||||
}
|
||||
|
||||
void HTTPTransport::SetRootCACertificatePath(const base::FilePath& cert) {
|
||||
root_ca_certificate_path_ = cert;
|
||||
}
|
||||
|
||||
} // namespace crashpad
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/macros.h"
|
||||
#include "util/net/http_headers.h"
|
||||
|
||||
@ -71,6 +72,16 @@ class HTTPTransport {
|
||||
//! \param[in] timeout The request timeout, in seconds.
|
||||
void SetTimeout(double timeout);
|
||||
|
||||
//! \brief Sets a certificate file to be used in lieu of the system CA cert
|
||||
//! bundle.
|
||||
//!
|
||||
//! This is exposed primarily for testing with a self-signed certificate, and
|
||||
//! it isn't necessary to set it in normal use.
|
||||
//!
|
||||
//! \param[in] cert The filename of a file in PEM format containing the CA
|
||||
//! cert to be used for TLS connections.
|
||||
void SetRootCACertificatePath(const base::FilePath& cert);
|
||||
|
||||
//! \brief Performs the HTTP request with the configured parameters and waits
|
||||
//! for the execution to complete.
|
||||
//!
|
||||
@ -90,10 +101,14 @@ class HTTPTransport {
|
||||
const HTTPHeaders& headers() const { return headers_; }
|
||||
HTTPBodyStream* body_stream() const { return body_stream_.get(); }
|
||||
double timeout() const { return timeout_; }
|
||||
const base::FilePath& root_ca_certificate_path() const {
|
||||
return root_ca_certificate_path_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string url_;
|
||||
std::string method_;
|
||||
base::FilePath root_ca_certificate_path_;
|
||||
HTTPHeaders headers_;
|
||||
std::unique_ptr<HTTPBodyStream> body_stream_;
|
||||
double timeout_;
|
||||
|
@ -32,6 +32,10 @@
|
||||
#include "util/stdlib/string_number_conversion.h"
|
||||
#include "util/string/split_string.h"
|
||||
|
||||
#if defined(CRASHPAD_USE_BORINGSSL)
|
||||
#include <openssl/ssl.h>
|
||||
#endif
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
namespace {
|
||||
@ -56,6 +60,160 @@ struct ScopedAddrinfoTraits {
|
||||
using ScopedAddrinfo =
|
||||
base::ScopedGeneric<addrinfo*, ScopedAddrinfoTraits>;
|
||||
|
||||
class Stream {
|
||||
public:
|
||||
virtual ~Stream() = default;
|
||||
|
||||
virtual bool LoggingWrite(const void* data, size_t size) = 0;
|
||||
virtual bool LoggingRead(void* data, size_t size) = 0;
|
||||
virtual bool LoggingReadToEOF(std::string* contents) = 0;
|
||||
};
|
||||
|
||||
class FdStream : public Stream {
|
||||
public:
|
||||
explicit FdStream(int fd) : fd_(fd) { CHECK(fd_ >= 0); }
|
||||
|
||||
bool LoggingWrite(const void* data, size_t size) override {
|
||||
return LoggingWriteFile(fd_, data, size);
|
||||
}
|
||||
|
||||
bool LoggingRead(void* data, size_t size) override {
|
||||
return LoggingReadFileExactly(fd_, data, size);
|
||||
}
|
||||
|
||||
bool LoggingReadToEOF(std::string* result) override{
|
||||
return crashpad::LoggingReadToEOF(fd_, result);
|
||||
}
|
||||
|
||||
private:
|
||||
int fd_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(FdStream);
|
||||
};
|
||||
|
||||
#if defined(CRASHPAD_USE_BORINGSSL)
|
||||
class SSLStream : public Stream {
|
||||
public:
|
||||
SSLStream() = default;
|
||||
|
||||
bool Initialize(const base::FilePath& root_cert_path,
|
||||
int sock,
|
||||
const std::string& hostname) {
|
||||
SSL_library_init();
|
||||
|
||||
ctx_.reset(SSL_CTX_new(TLS_method()));
|
||||
if (!ctx_.is_valid()) {
|
||||
LOG(ERROR) << "SSL_CTX_new";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (SSL_CTX_set_min_proto_version(ctx_.get(), TLS1_2_VERSION) <= 0) {
|
||||
LOG(ERROR) << "SSL_CTX_set_min_proto_version";
|
||||
return false;
|
||||
}
|
||||
|
||||
SSL_CTX_set_verify(ctx_.get(), SSL_VERIFY_PEER, nullptr);
|
||||
SSL_CTX_set_verify_depth(ctx_.get(), 5);
|
||||
|
||||
if (!root_cert_path.empty()) {
|
||||
if (SSL_CTX_load_verify_locations(
|
||||
ctx_.get(), root_cert_path.value().c_str(), nullptr) <= 0) {
|
||||
LOG(ERROR) << "SSL_CTX_load_verify_locations";
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
#if defined(OS_LINUX)
|
||||
if (SSL_CTX_load_verify_locations(
|
||||
ctx_.get(), nullptr, "/etc/ssl/certs") <= 0) {
|
||||
LOG(ERROR) << "SSL_CTX_load_verify_locations";
|
||||
return false;
|
||||
}
|
||||
#elif defined(OS_FUCHSIA)
|
||||
if (SSL_CTX_load_verify_locations(
|
||||
ctx_.get(), "/config/ssl/cert.pem", nullptr) <= 0) {
|
||||
LOG(ERROR) << "SSL_CTX_load_verify_locations";
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
#error cert store location
|
||||
#endif
|
||||
}
|
||||
|
||||
ssl_.reset(SSL_new(ctx_.get()));
|
||||
if (!ssl_.is_valid()) {
|
||||
LOG(ERROR) << "SSL_new";
|
||||
return false;
|
||||
}
|
||||
|
||||
BIO* bio = BIO_new_socket(sock, BIO_NOCLOSE);
|
||||
if (!bio) {
|
||||
LOG(ERROR) << "BIO_new_socket";
|
||||
return false;
|
||||
}
|
||||
|
||||
// SSL_set_bio() takes ownership of |bio|.
|
||||
SSL_set_bio(ssl_.get(), bio, bio);
|
||||
|
||||
if (SSL_set_tlsext_host_name(ssl_.get(), hostname.c_str()) == 0) {
|
||||
LOG(ERROR) << "SSL_set_tlsext_host_name";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (SSL_connect(ssl_.get()) <= 0) {
|
||||
LOG(ERROR) << "SSL_connect";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LoggingWrite(const void* data, size_t size) override {
|
||||
return SSL_write(ssl_.get(), data, size) != 0;
|
||||
}
|
||||
|
||||
bool LoggingRead(void* data, size_t size) override {
|
||||
return SSL_read(ssl_.get(), data, size) != 0;
|
||||
}
|
||||
|
||||
bool LoggingReadToEOF(std::string* contents) override {
|
||||
contents->clear();
|
||||
char buffer[4096];
|
||||
FileOperationResult rv;
|
||||
while ((rv = SSL_read(ssl_.get(), buffer, sizeof(buffer))) > 0) {
|
||||
DCHECK_LE(static_cast<size_t>(rv), sizeof(buffer));
|
||||
contents->append(buffer, rv);
|
||||
}
|
||||
if (rv < 0) {
|
||||
LOG(ERROR) << "SSL_read";
|
||||
contents->clear();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
struct ScopedSSLCTXTraits {
|
||||
static SSL_CTX* InvalidValue() { return nullptr; }
|
||||
static void Free(SSL_CTX* ctx) { SSL_CTX_free(ctx); }
|
||||
};
|
||||
using ScopedSSLCTX = base::ScopedGeneric<SSL_CTX*, ScopedSSLCTXTraits>;
|
||||
|
||||
struct ScopedSSLTraits {
|
||||
static SSL* InvalidValue() { return nullptr; }
|
||||
static void Free(SSL* ssl) {
|
||||
SSL_shutdown(ssl);
|
||||
SSL_free(ssl);
|
||||
}
|
||||
};
|
||||
using ScopedSSL = base::ScopedGeneric<SSL*, ScopedSSLTraits>;
|
||||
|
||||
ScopedSSLCTX ctx_;
|
||||
ScopedSSL ssl_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(SSLStream);
|
||||
};
|
||||
#endif
|
||||
|
||||
bool WaitUntilSocketIsReady(int sock) {
|
||||
pollfd pollfds;
|
||||
pollfds.fd = sock;
|
||||
@ -167,14 +325,14 @@ base::ScopedFD CreateSocket(const std::string& hostname,
|
||||
return base::ScopedFD();
|
||||
}
|
||||
|
||||
bool WriteRequest(int sock,
|
||||
bool WriteRequest(Stream* stream,
|
||||
const std::string& method,
|
||||
const std::string& resource,
|
||||
const HTTPHeaders& headers,
|
||||
HTTPBodyStream* body_stream) {
|
||||
std::string request_line = base::StringPrintf(
|
||||
"%s %s HTTP/1.0\r\n", method.c_str(), resource.c_str());
|
||||
if (!LoggingWriteFile(sock, request_line.data(), request_line.size()))
|
||||
if (!stream->LoggingWrite(request_line.data(), request_line.size()))
|
||||
return false;
|
||||
|
||||
// Write headers, and determine if Content-Length has been specified.
|
||||
@ -188,7 +346,7 @@ bool WriteRequest(int sock,
|
||||
DCHECK(!chunked);
|
||||
}
|
||||
|
||||
if (!LoggingWriteFile(sock, header_str.data(), header_str.size()))
|
||||
if (!stream->LoggingWrite(header_str.data(), header_str.size()))
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -196,13 +354,13 @@ bool WriteRequest(int sock,
|
||||
if (chunked) {
|
||||
static constexpr const char kTransferEncodingChunked[] =
|
||||
"Transfer-Encoding: chunked\r\n";
|
||||
if (!LoggingWriteFile(
|
||||
sock, kTransferEncodingChunked, strlen(kTransferEncodingChunked))) {
|
||||
if (!stream->LoggingWrite(kTransferEncodingChunked,
|
||||
strlen(kTransferEncodingChunked))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!LoggingWriteFile(sock, kCRLFTerminator, strlen(kCRLFTerminator))) {
|
||||
if (!stream->LoggingWrite(kCRLFTerminator, strlen(kCRLFTerminator))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -267,7 +425,7 @@ bool WriteRequest(int sock,
|
||||
// sent to signal EOF. This will happen when processing the EOF indicated by
|
||||
// a 0 return from body_stream()->GetBytesBuffer() above.
|
||||
if (write_size != 0) {
|
||||
if (!LoggingWriteFile(sock, write_start, write_size))
|
||||
if (!stream->LoggingWrite(write_start, write_size))
|
||||
return false;
|
||||
}
|
||||
} while (data_bytes > 0);
|
||||
@ -275,11 +433,11 @@ bool WriteRequest(int sock,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReadLine(int sock, std::string* line) {
|
||||
bool ReadLine(Stream* stream, std::string* line) {
|
||||
line->clear();
|
||||
for (;;) {
|
||||
char byte;
|
||||
if (!LoggingReadFileExactly(sock, &byte, 1)) {
|
||||
if (!stream->LoggingRead(&byte, 1)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -293,9 +451,9 @@ bool StartsWith(const std::string& str, const char* with, size_t len) {
|
||||
return str.compare(0, len, with) == 0;
|
||||
}
|
||||
|
||||
bool ReadResponseLine(int sock) {
|
||||
bool ReadResponseLine(Stream* stream) {
|
||||
std::string response_line;
|
||||
if (!ReadLine(sock, &response_line)) {
|
||||
if (!ReadLine(stream, &response_line)) {
|
||||
LOG(ERROR) << "ReadLine";
|
||||
return false;
|
||||
}
|
||||
@ -305,10 +463,10 @@ bool ReadResponseLine(int sock) {
|
||||
StartsWith(response_line, kHttp11, strlen(kHttp11));
|
||||
}
|
||||
|
||||
bool ReadResponseHeaders(int sock, HTTPHeaders* headers) {
|
||||
bool ReadResponseHeaders(Stream* stream, HTTPHeaders* headers) {
|
||||
for (;;) {
|
||||
std::string line;
|
||||
if (!ReadLine(sock, &line)) {
|
||||
if (!ReadLine(stream, &line)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -330,21 +488,21 @@ bool ReadResponseHeaders(int sock, HTTPHeaders* headers) {
|
||||
}
|
||||
}
|
||||
|
||||
bool ReadContentChunked(int sock, std::string* body) {
|
||||
bool ReadContentChunked(Stream* stream, std::string* body) {
|
||||
// TODO(scottmg): https://crashpad.chromium.org/bug/196.
|
||||
LOG(ERROR) << "TODO(scottmg): chunked response read";
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ReadResponse(int sock, std::string* response_body) {
|
||||
bool ReadResponse(Stream* stream, std::string* response_body) {
|
||||
response_body->clear();
|
||||
|
||||
if (!ReadResponseLine(sock)) {
|
||||
if (!ReadResponseLine(stream)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
HTTPHeaders response_headers;
|
||||
if (!ReadResponseHeaders(sock, &response_headers)) {
|
||||
if (!ReadResponseHeaders(stream, &response_headers)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -359,7 +517,7 @@ bool ReadResponse(int sock, std::string* response_body) {
|
||||
|
||||
if (len) {
|
||||
response_body->resize(len, 0);
|
||||
return ReadFileExactly(sock, &(*response_body)[0], len);
|
||||
return stream->LoggingRead(&(*response_body)[0], len);
|
||||
}
|
||||
|
||||
it = response_headers.find("Transfer-Encoding");
|
||||
@ -368,8 +526,8 @@ bool ReadResponse(int sock, std::string* response_body) {
|
||||
chunked = true;
|
||||
}
|
||||
|
||||
return chunked ? ReadContentChunked(sock, response_body)
|
||||
: LoggingReadToEOF(sock, response_body);
|
||||
return chunked ? ReadContentChunked(stream, response_body)
|
||||
: stream->LoggingReadToEOF(response_body);
|
||||
}
|
||||
|
||||
bool HTTPTransportSocket::ExecuteSynchronously(std::string* response_body) {
|
||||
@ -378,16 +536,38 @@ bool HTTPTransportSocket::ExecuteSynchronously(std::string* response_body) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#if !defined(CRASHPAD_USE_BORINGSSL)
|
||||
CHECK(scheme == "http");
|
||||
#endif
|
||||
|
||||
base::ScopedFD sock(CreateSocket(hostname, port));
|
||||
if (!sock.is_valid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!WriteRequest(sock.get(), method(), resource, headers(), body_stream())) {
|
||||
#if defined(CRASHPAD_USE_BORINGSSL)
|
||||
std::unique_ptr<Stream> stream;
|
||||
if (scheme == "https") {
|
||||
auto ssl_stream = std::make_unique<SSLStream>();
|
||||
if (!ssl_stream->Initialize(
|
||||
root_ca_certificate_path(), sock.get(), hostname)) {
|
||||
LOG(ERROR) << "SSLStream Initialize";
|
||||
return false;
|
||||
}
|
||||
stream = std::move(ssl_stream);
|
||||
} else {
|
||||
stream = std::make_unique<FdStream>(sock.get());
|
||||
}
|
||||
#else // CRASHPAD_USE_BORINGSSL
|
||||
std::unique_ptr<Stream> stream(std::make_unique<FdStream>(sock.get()));
|
||||
#endif // CRASHPAD_USE_BORINGSSL
|
||||
|
||||
if (!WriteRequest(
|
||||
stream.get(), method(), resource, headers(), body_stream())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ReadResponse(sock.get(), response_body)) {
|
||||
if (!ReadResponse(stream.get(), response_body)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -42,12 +42,23 @@ namespace crashpad {
|
||||
namespace test {
|
||||
namespace {
|
||||
|
||||
#if defined(OS_WIN)
|
||||
std::string ToUTF8IfWin(const base::string16& x) {
|
||||
return base::UTF16ToUTF8(x);
|
||||
}
|
||||
#else
|
||||
std::string ToUTF8IfWin(const std::string& x) {
|
||||
return x;
|
||||
}
|
||||
#endif
|
||||
|
||||
class HTTPTransportTestFixture : public MultiprocessExec {
|
||||
public:
|
||||
using RequestValidator =
|
||||
void(*)(HTTPTransportTestFixture*, const std::string&);
|
||||
|
||||
HTTPTransportTestFixture(const HTTPHeaders& headers,
|
||||
HTTPTransportTestFixture(const base::FilePath::StringType& scheme,
|
||||
const HTTPHeaders& headers,
|
||||
std::unique_ptr<HTTPBodyStream> body_stream,
|
||||
uint16_t http_response_code,
|
||||
RequestValidator request_validator)
|
||||
@ -55,14 +66,33 @@ class HTTPTransportTestFixture : public MultiprocessExec {
|
||||
headers_(headers),
|
||||
body_stream_(std::move(body_stream)),
|
||||
response_code_(http_response_code),
|
||||
request_validator_(request_validator) {
|
||||
request_validator_(request_validator),
|
||||
cert_(),
|
||||
scheme_and_host_() {
|
||||
base::FilePath server_path = TestPaths::Executable().DirName().Append(
|
||||
FILE_PATH_LITERAL("http_transport_test_server")
|
||||
#if defined(OS_WIN)
|
||||
FILE_PATH_LITERAL(".exe")
|
||||
#endif
|
||||
);
|
||||
SetChildCommand(server_path, nullptr);
|
||||
|
||||
if (ToUTF8IfWin(scheme) == "http") {
|
||||
scheme_and_host_ = "http://localhost";
|
||||
SetChildCommand(server_path, nullptr);
|
||||
} else {
|
||||
std::vector<std::string> args;
|
||||
cert_ = TestPaths::BuildArtifact(FILE_PATH_LITERAL("util"),
|
||||
FILE_PATH_LITERAL("cert"),
|
||||
TestPaths::FileType::kCertificate);
|
||||
args.push_back(ToUTF8IfWin(cert_.value()));
|
||||
args.emplace_back(ToUTF8IfWin(
|
||||
TestPaths::BuildArtifact(FILE_PATH_LITERAL("util"),
|
||||
FILE_PATH_LITERAL("key"),
|
||||
TestPaths::FileType::kCertificate)
|
||||
.value()));
|
||||
SetChildCommand(server_path, &args);
|
||||
scheme_and_host_ = "https://localhost";
|
||||
}
|
||||
}
|
||||
|
||||
const HTTPHeaders& headers() { return headers_; }
|
||||
@ -94,7 +124,12 @@ class HTTPTransportTestFixture : public MultiprocessExec {
|
||||
// Now execute the HTTP request.
|
||||
std::unique_ptr<HTTPTransport> transport(HTTPTransport::Create());
|
||||
transport->SetMethod("POST");
|
||||
transport->SetURL(base::StringPrintf("http://127.0.0.1:%d/upload", port));
|
||||
|
||||
if (!cert_.empty()) {
|
||||
transport->SetRootCACertificatePath(cert_);
|
||||
}
|
||||
transport->SetURL(
|
||||
base::StringPrintf("%s:%d/upload", scheme_and_host_.c_str(), port));
|
||||
for (const auto& pair : headers_) {
|
||||
transport->SetHeader(pair.first, pair.second);
|
||||
}
|
||||
@ -128,6 +163,8 @@ class HTTPTransportTestFixture : public MultiprocessExec {
|
||||
std::unique_ptr<HTTPBodyStream> body_stream_;
|
||||
uint16_t response_code_;
|
||||
RequestValidator request_validator_;
|
||||
base::FilePath cert_;
|
||||
std::string scheme_and_host_;
|
||||
};
|
||||
|
||||
constexpr char kMultipartFormData[] = "multipart/form-data";
|
||||
@ -209,7 +246,10 @@ void ValidFormData(HTTPTransportTestFixture* fixture,
|
||||
EXPECT_EQ(request.substr(body_start), expected);
|
||||
}
|
||||
|
||||
TEST(HTTPTransport, ValidFormData) {
|
||||
class HTTPTransport
|
||||
: public testing::TestWithParam<base::FilePath::StringType> {};
|
||||
|
||||
TEST_P(HTTPTransport, ValidFormData) {
|
||||
HTTPMultipartBuilder builder;
|
||||
builder.SetFormData("key1", "test");
|
||||
builder.SetFormData("key2", "--abcdefg123");
|
||||
@ -217,12 +257,12 @@ TEST(HTTPTransport, ValidFormData) {
|
||||
HTTPHeaders headers;
|
||||
builder.PopulateContentHeaders(&headers);
|
||||
|
||||
HTTPTransportTestFixture test(
|
||||
HTTPTransportTestFixture test(GetParam(),
|
||||
headers, builder.GetBodyStream(), 200, &ValidFormData);
|
||||
test.Run();
|
||||
}
|
||||
|
||||
TEST(HTTPTransport, ValidFormData_Gzip) {
|
||||
TEST_P(HTTPTransport, ValidFormData_Gzip) {
|
||||
HTTPMultipartBuilder builder;
|
||||
builder.SetGzipEnabled(true);
|
||||
builder.SetFormData("key1", "test");
|
||||
@ -231,8 +271,8 @@ TEST(HTTPTransport, ValidFormData_Gzip) {
|
||||
HTTPHeaders headers;
|
||||
builder.PopulateContentHeaders(&headers);
|
||||
|
||||
HTTPTransportTestFixture test(headers, builder.GetBodyStream(), 200,
|
||||
&ValidFormData);
|
||||
HTTPTransportTestFixture test(
|
||||
GetParam(), headers, builder.GetBodyStream(), 200, &ValidFormData);
|
||||
test.Run();
|
||||
}
|
||||
|
||||
@ -245,11 +285,11 @@ void ErrorResponse(HTTPTransportTestFixture* fixture,
|
||||
EXPECT_EQ(content_type, kTextPlain);
|
||||
}
|
||||
|
||||
TEST(HTTPTransport, ErrorResponse) {
|
||||
TEST_P(HTTPTransport, ErrorResponse) {
|
||||
HTTPMultipartBuilder builder;
|
||||
HTTPHeaders headers;
|
||||
headers[kContentType] = kTextPlain;
|
||||
HTTPTransportTestFixture test(headers, builder.GetBodyStream(),
|
||||
HTTPTransportTestFixture test(GetParam(), headers, builder.GetBodyStream(),
|
||||
404, &ErrorResponse);
|
||||
test.Run();
|
||||
}
|
||||
@ -273,7 +313,7 @@ void UnchunkedPlainText(HTTPTransportTestFixture* fixture,
|
||||
EXPECT_EQ(request.substr(body_start + 2), kTextBody);
|
||||
}
|
||||
|
||||
TEST(HTTPTransport, UnchunkedPlainText) {
|
||||
TEST_P(HTTPTransport, UnchunkedPlainText) {
|
||||
std::unique_ptr<HTTPBodyStream> body_stream(
|
||||
new StringHTTPBodyStream(kTextBody));
|
||||
|
||||
@ -281,12 +321,13 @@ TEST(HTTPTransport, UnchunkedPlainText) {
|
||||
headers[kContentType] = kTextPlain;
|
||||
headers[kContentLength] = base::StringPrintf("%" PRIuS, strlen(kTextBody));
|
||||
|
||||
HTTPTransportTestFixture test(
|
||||
HTTPTransportTestFixture test(GetParam(),
|
||||
headers, std::move(body_stream), 200, &UnchunkedPlainText);
|
||||
test.Run();
|
||||
}
|
||||
|
||||
void RunUpload33k(bool has_content_length) {
|
||||
void RunUpload33k(const base::FilePath::StringType& scheme,
|
||||
bool has_content_length) {
|
||||
// On macOS, NSMutableURLRequest winds up calling into a CFReadStream’s Read()
|
||||
// callback with a 32kB buffer. Make sure that it’s able to get everything
|
||||
// when enough is available to fill this buffer, requiring more than one
|
||||
@ -303,6 +344,7 @@ void RunUpload33k(bool has_content_length) {
|
||||
base::StringPrintf("%" PRIuS, request_string.size());
|
||||
}
|
||||
HTTPTransportTestFixture test(
|
||||
scheme,
|
||||
headers,
|
||||
std::move(body_stream),
|
||||
200,
|
||||
@ -313,15 +355,31 @@ void RunUpload33k(bool has_content_length) {
|
||||
test.Run();
|
||||
}
|
||||
|
||||
TEST(HTTPTransport, Upload33k) {
|
||||
RunUpload33k(true);
|
||||
TEST_P(HTTPTransport, Upload33k) {
|
||||
RunUpload33k(GetParam(), true);
|
||||
}
|
||||
|
||||
TEST(HTTPTransport, Upload33k_LengthUnknown) {
|
||||
TEST_P(HTTPTransport, Upload33k_LengthUnknown) {
|
||||
// The same as Upload33k, but without declaring Content-Length ahead of time.
|
||||
RunUpload33k(false);
|
||||
RunUpload33k(GetParam(), false);
|
||||
}
|
||||
|
||||
#if defined(CRASHPAD_USE_BORINGSSL)
|
||||
// The test server requires BoringSSL or OpenSSL, so https in tests can only be
|
||||
// enabled where that's readily available. Additionally on Linux, the bots fail
|
||||
// lacking libcrypto.so.1.1, so disabled there for now. On Mac, they could also
|
||||
// likely be enabled relatively easily, if HTTPTransportMac learned to respect
|
||||
// the user-supplied cert.
|
||||
INSTANTIATE_TEST_CASE_P(HTTPTransport,
|
||||
HTTPTransport,
|
||||
testing::Values(FILE_PATH_LITERAL("http"),
|
||||
FILE_PATH_LITERAL("https")));
|
||||
#else
|
||||
INSTANTIATE_TEST_CASE_P(HTTPTransport,
|
||||
HTTPTransport,
|
||||
testing::Values(FILE_PATH_LITERAL("http")));
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
} // namespace test
|
||||
} // namespace crashpad
|
||||
|
@ -34,6 +34,10 @@
|
||||
#pragma warning(disable: 4244 4245 4267 4702)
|
||||
#endif
|
||||
|
||||
#if defined(CRASHPAD_USE_BORINGSSL)
|
||||
#define CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
#endif
|
||||
|
||||
#define CPPHTTPLIB_ZLIB_SUPPORT
|
||||
#include "third_party/cpp-httplib/cpp-httplib/httplib.h"
|
||||
|
||||
@ -45,9 +49,20 @@ namespace crashpad {
|
||||
namespace {
|
||||
|
||||
int HttpTransportTestServerMain(int argc, char* argv[]) {
|
||||
httplib::Server svr(httplib::HttpVersion::v1_0);
|
||||
std::unique_ptr<httplib::Server> server;
|
||||
if (argc == 1) {
|
||||
server.reset(new httplib::Server);
|
||||
#if defined(CRASHPAD_USE_BORINGSSL)
|
||||
} else if (argc == 3) {
|
||||
server.reset(new httplib::SSLServer(argv[1], argv[2]));
|
||||
#endif
|
||||
} else {
|
||||
LOG(ERROR) << "usage: http_transport_test_server [cert.pem key.pem]";
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!svr.is_valid()) {
|
||||
|
||||
if (!server->is_valid()) {
|
||||
LOG(ERROR) << "server creation failed";
|
||||
return 1;
|
||||
}
|
||||
@ -57,30 +72,30 @@ int HttpTransportTestServerMain(int argc, char* argv[]) {
|
||||
|
||||
std::string to_stdout;
|
||||
|
||||
svr.Post("/upload",
|
||||
[&response, &response_code, &svr, &to_stdout](
|
||||
const httplib::Request& req, httplib::Response& res) {
|
||||
res.status = response_code;
|
||||
if (response_code == 200) {
|
||||
res.set_content(std::string(response, 16) + "\r\n",
|
||||
"text/plain");
|
||||
} else {
|
||||
res.set_content("error", "text/plain");
|
||||
}
|
||||
server->Post("/upload",
|
||||
[&response, &response_code, &server, &to_stdout](
|
||||
const httplib::Request& req, httplib::Response& res) {
|
||||
res.status = response_code;
|
||||
if (response_code == 200) {
|
||||
res.set_content(std::string(response, 16) + "\r\n",
|
||||
"text/plain");
|
||||
} else {
|
||||
res.set_content("error", "text/plain");
|
||||
}
|
||||
|
||||
to_stdout += "POST /upload HTTP/1.0\r\n";
|
||||
for (const auto& h : req.headers) {
|
||||
to_stdout += base::StringPrintf(
|
||||
"%s: %s\r\n", h.first.c_str(), h.second.c_str());
|
||||
}
|
||||
to_stdout += "\r\n";
|
||||
to_stdout += req.body;
|
||||
to_stdout += "POST /upload HTTP/1.0\r\n";
|
||||
for (const auto& h : req.headers) {
|
||||
to_stdout += base::StringPrintf(
|
||||
"%s: %s\r\n", h.first.c_str(), h.second.c_str());
|
||||
}
|
||||
to_stdout += "\r\n";
|
||||
to_stdout += req.body;
|
||||
|
||||
svr.stop();
|
||||
});
|
||||
server->stop();
|
||||
});
|
||||
|
||||
uint16_t port =
|
||||
base::checked_cast<uint16_t>(svr.bind_to_any_port("127.0.0.1"));
|
||||
base::checked_cast<uint16_t>(server->bind_to_any_port("localhost"));
|
||||
|
||||
CheckedWriteFile(
|
||||
StdioFileHandle(StdioStream::kStandardOutput), &port, sizeof(port));
|
||||
@ -93,7 +108,7 @@ int HttpTransportTestServerMain(int argc, char* argv[]) {
|
||||
&response,
|
||||
sizeof(response));
|
||||
|
||||
svr.listen_after_bind();
|
||||
server->listen_after_bind();
|
||||
|
||||
LoggingWriteFile(StdioFileHandle(StdioStream::kStandardOutput),
|
||||
to_stdout.data(),
|
||||
@ -102,8 +117,8 @@ int HttpTransportTestServerMain(int argc, char* argv[]) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace crashpad
|
||||
} // namespace
|
||||
} // namespace crashpad
|
||||
|
||||
#if defined(OS_POSIX) || defined(OS_FUCHSIA)
|
||||
int main(int argc, char* argv[]) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user