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:
Scott Graham 2018-05-29 12:51:41 -07:00 committed by Commit Bot
parent 5191992ae5
commit 5c49c59847
10 changed files with 417 additions and 73 deletions

View File

@ -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 +

View File

@ -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().

View File

@ -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;
}

View File

@ -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) {

View 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])

View File

@ -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

View File

@ -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_;

View File

@ -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;
}

View File

@ -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 CFReadStreams Read()
// callback with a 32kB buffer. Make sure that its 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

View File

@ -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[]) {