Commit 13385b5a authored by Yuwei Huang's avatar Yuwei Huang Committed by Commit Bot

[remoting] Introduce class for executing REST/protobuf requests

This CL introduces a ProtobufHttpClient class for sending REST/protobuf
requests over HTTP using Chromium net libraries. This is the first step
of moving away from gRPC.

Bug: 1103416
Change-Id: Ic9b32e166c27f1098fcf24da831e1bdf836fa95a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2293422
Auto-Submit: Yuwei Huang <yuweih@chromium.org>
Reviewed-by: default avatarJohn Abd-El-Malek <jam@chromium.org>
Reviewed-by: default avatarJoe Downing <joedow@chromium.org>
Commit-Queue: Yuwei Huang <yuweih@chromium.org>
Cr-Commit-Position: refs/heads/master@{#787938}
parent d23b1436
......@@ -2,6 +2,8 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import("//third_party/protobuf/proto_library.gni")
source_set("base") {
sources = [
"auto_thread.cc",
......@@ -23,6 +25,12 @@ source_set("base") {
"leaky_bucket.cc",
"leaky_bucket.h",
"name_value_map.h",
"protobuf_http_client.cc",
"protobuf_http_client.h",
"protobuf_http_request.cc",
"protobuf_http_request.h",
"protobuf_http_status.cc",
"protobuf_http_status.h",
"rate_counter.cc",
"rate_counter.h",
"result.h",
......@@ -145,6 +153,10 @@ static_library("test_support") {
]
}
proto_library("test_proto") {
sources = [ "protobuf_http_client_test_messages.proto" ]
}
source_set("unit_tests") {
testonly = true
......@@ -157,6 +169,7 @@ source_set("unit_tests") {
"compound_buffer_unittest.cc",
"leaky_bucket_unittest.cc",
"oauth_token_getter_proxy_unittest.cc",
"protobuf_http_client_unittest.cc",
"rate_counter_unittest.cc",
"result_unittest.cc",
"rsa_key_pair_unittest.cc",
......@@ -173,6 +186,7 @@ source_set("unit_tests") {
]
deps = [
":test_proto",
":test_support",
"//base",
"//mojo/core/embedder",
......
......@@ -4,9 +4,11 @@ include_rules = [
"+mojo/core/embedder",
"+net",
"+services/network/public/cpp",
"+services/network/test",
"+third_party/breakpad",
"+third_party/google_trust_services",
"+third_party/grpc",
"+third_party/protobuf",
"+third_party/zlib",
"+ui/base",
]
// Copyright 2020 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 "remoting/base/protobuf_http_client.h"
#include "base/strings/stringprintf.h"
#include "net/base/load_flags.h"
#include "net/base/net_errors.h"
#include "remoting/base/oauth_token_getter.h"
#include "remoting/base/protobuf_http_request.h"
#include "remoting/base/protobuf_http_status.h"
#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/public/cpp/simple_url_loader.h"
#include "third_party/protobuf/src/google/protobuf/message_lite.h"
#include "url/gurl.h"
namespace {
constexpr char kAuthorizationHeaderFormat[] = "Authorization: Bearer %s";
constexpr int kMaxResponseSizeKb = 512;
} // namespace
namespace remoting {
ProtobufHttpClient::ProtobufHttpClient(
const std::string& server_endpoint,
OAuthTokenGetter* token_getter,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
: server_endpoint_(server_endpoint),
token_getter_(token_getter),
url_loader_factory_(url_loader_factory) {}
ProtobufHttpClient::~ProtobufHttpClient() = default;
void ProtobufHttpClient::ExecuteRequest(
std::unique_ptr<ProtobufHttpRequest> request) {
DCHECK(request->request_message);
DCHECK(!request->path.empty());
DCHECK(request->response_callback_);
if (!request->authenticated) {
DoExecuteRequest(std::move(request), OAuthTokenGetter::Status::SUCCESS, {},
{});
return;
}
DCHECK(token_getter_);
token_getter_->CallWithToken(
base::BindOnce(&ProtobufHttpClient::DoExecuteRequest,
weak_factory_.GetWeakPtr(), std::move(request)));
}
void ProtobufHttpClient::CancelPendingRequests() {
weak_factory_.InvalidateWeakPtrs();
}
void ProtobufHttpClient::DoExecuteRequest(
std::unique_ptr<ProtobufHttpRequest> request,
OAuthTokenGetter::Status status,
const std::string& user_email,
const std::string& access_token) {
if (status != OAuthTokenGetter::Status::SUCCESS) {
LOG(ERROR) << "Failed to fetch access token. Status: " << status;
request->OnResponse(
ProtobufHttpStatus(net::HttpStatusCode::HTTP_UNAUTHORIZED), nullptr);
return;
}
auto resource_request = std::make_unique<network::ResourceRequest>();
resource_request->url = GURL("https://" + server_endpoint_ + request->path);
resource_request->load_flags =
net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE;
resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
resource_request->method = net::HttpRequestHeaders::kPostMethod;
if (status == OAuthTokenGetter::Status::SUCCESS && !access_token.empty()) {
resource_request->headers.AddHeaderFromString(
base::StringPrintf(kAuthorizationHeaderFormat, access_token.c_str()));
} else {
VLOG(1) << "Attempting to execute request without access token";
}
std::unique_ptr<network::SimpleURLLoader> send_url_loader =
network::SimpleURLLoader::Create(std::move(resource_request),
request->traffic_annotation);
send_url_loader->SetTimeoutDuration(request->timeout_duration);
send_url_loader->AttachStringForUpload(
request->request_message->SerializeAsString(), "application/x-protobuf");
send_url_loader->DownloadToString(
url_loader_factory_.get(),
base::BindOnce(&ProtobufHttpClient::OnResponse,
weak_factory_.GetWeakPtr(), std::move(request),
std::move(send_url_loader)),
kMaxResponseSizeKb);
}
void ProtobufHttpClient::OnResponse(
std::unique_ptr<ProtobufHttpRequest> request,
std::unique_ptr<network::SimpleURLLoader> url_loader,
std::unique_ptr<std::string> response_body) {
net::Error net_error = static_cast<net::Error>(url_loader->NetError());
if (net_error == net::Error::ERR_HTTP_RESPONSE_CODE_FAILURE &&
(!url_loader->ResponseInfo() || !url_loader->ResponseInfo()->headers)) {
LOG(ERROR) << "Can't find response header.";
net_error = net::Error::ERR_INVALID_RESPONSE;
}
ProtobufHttpStatus status =
(net_error == net::Error::ERR_HTTP_RESPONSE_CODE_FAILURE ||
net_error == net::Error::OK)
? ProtobufHttpStatus(static_cast<net::HttpStatusCode>(
url_loader->ResponseInfo()->headers->response_code()))
: ProtobufHttpStatus(net_error);
request->OnResponse(status, std::move(response_body));
}
} // namespace remoting
// Copyright 2020 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 REMOTING_BASE_PROTOBUF_HTTP_CLIENT_H_
#define REMOTING_BASE_PROTOBUF_HTTP_CLIENT_H_
#include <memory>
#include <string>
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
#include "remoting/base/oauth_token_getter.h"
namespace network {
class SharedURLLoaderFactory;
class SimpleURLLoader;
} // namespace network
namespace remoting {
struct ProtobufHttpRequest;
// Helper class for executing REST/Protobuf requests over HTTP.
class ProtobufHttpClient final {
public:
// |server_endpoint| is the hostname of the server.
// |token_getter| is nullable if none of the requests are authenticated.
// |token_getter| must outlive |this|.
ProtobufHttpClient(
const std::string& server_endpoint,
OAuthTokenGetter* token_getter,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
~ProtobufHttpClient();
ProtobufHttpClient(const ProtobufHttpClient&) = delete;
ProtobufHttpClient& operator=(const ProtobufHttpClient&) = delete;
// Executes a unary request. Caller will not be notified of the result if
// CancelPendingRequests() is called or |this| is destroyed.
void ExecuteRequest(std::unique_ptr<ProtobufHttpRequest> request);
// Tries to cancel all pending requests. Note that this prevents request
// callbacks from being called but does not necessarily stop pending requests
// from being sent.
void CancelPendingRequests();
private:
void DoExecuteRequest(std::unique_ptr<ProtobufHttpRequest> request,
OAuthTokenGetter::Status status,
const std::string& user_email,
const std::string& access_token);
void OnResponse(std::unique_ptr<ProtobufHttpRequest> request,
std::unique_ptr<network::SimpleURLLoader> url_loader,
std::unique_ptr<std::string> response_body);
std::string server_endpoint_;
OAuthTokenGetter* token_getter_;
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
base::WeakPtrFactory<ProtobufHttpClient> weak_factory_{this};
};
} // namespace remoting
#endif // REMOTING_BASE_PROTOBUF_HTTP_CLIENT_H_
\ No newline at end of file
// Copyright 2020 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.
syntax = "proto3";
option optimize_for = LITE_RUNTIME;
package remoting;
message EchoRequest {
string text = 1;
}
message EchoResponse {
string text = 1;
}
// Copyright 2020 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 "remoting/base/protobuf_http_client.h"
#include <memory>
#include "base/run_loop.h"
#include "base/test/bind_test_util.h"
#include "base/test/gmock_callback_support.h"
#include "base/test/mock_callback.h"
#include "base/test/task_environment.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "remoting/base/protobuf_http_client_test_messages.pb.h"
#include "remoting/base/protobuf_http_request.h"
#include "remoting/base/protobuf_http_status.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
#include "services/network/test/test_url_loader_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace remoting {
namespace {
using ::base::test::RunOnceCallback;
using ::testing::_;
using MockEchoResponseCallback =
base::MockCallback<base::OnceCallback<void(const ProtobufHttpStatus&,
std::unique_ptr<EchoResponse>)>>;
constexpr char kTestServerEndpoint[] = "test.com";
constexpr char kTestRpcPath[] = "/v1/echo:echo";
constexpr char kTestFullUrl[] = "https://test.com/v1/echo:echo";
constexpr char kRequestText[] = "This is a request";
constexpr char kResponseText[] = "This is a response";
constexpr char kAuthorizationHeaderKey[] = "Authorization";
constexpr char kFakeAccessToken[] = "fake_access_token";
constexpr char kFakeAccessTokenHeaderValue[] = "Bearer fake_access_token";
MATCHER_P(HasStatusCode, status_code, "") {
return arg.http_status_code() == status_code;
}
MATCHER_P(HasNetError, net_error, "") {
return arg.net_error() == net_error;
}
MATCHER(IsResponseText, "") {
return arg->text() == kResponseText;
}
MATCHER(IsNullResponse, "") {
return arg.get() == nullptr;
}
class MockOAuthTokenGetter : public OAuthTokenGetter {
public:
MOCK_METHOD1(CallWithToken, void(TokenCallback));
MOCK_METHOD0(InvalidateCache, void());
};
std::unique_ptr<ProtobufHttpRequest> CreateDefaultTestRequest() {
auto request =
std::make_unique<ProtobufHttpRequest>(TRAFFIC_ANNOTATION_FOR_TESTS);
auto request_message = std::make_unique<EchoRequest>();
request_message->set_text(kRequestText);
request->request_message = std::move(request_message);
request->SetResponseCallback(
base::DoNothing::Once<const ProtobufHttpStatus&,
std::unique_ptr<EchoResponse>>());
request->path = kTestRpcPath;
return request;
}
std::string CreateDefaultResponseContent() {
EchoResponse response;
response.set_text(kResponseText);
return response.SerializeAsString();
}
} // namespace
class ProtobufHttpClientTest : public testing::Test {
protected:
base::test::SingleThreadTaskEnvironment task_environment_;
MockOAuthTokenGetter mock_token_getter_;
network::TestURLLoaderFactory test_url_loader_factory_;
scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_ =
base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
&test_url_loader_factory_);
ProtobufHttpClient client_{kTestServerEndpoint, &mock_token_getter_,
test_shared_loader_factory_};
};
TEST_F(ProtobufHttpClientTest, SendRequestAndDecodeResponse) {
base::RunLoop run_loop;
EXPECT_CALL(mock_token_getter_, CallWithToken(_))
.WillOnce(RunOnceCallback<0>(OAuthTokenGetter::Status::SUCCESS, "",
kFakeAccessToken));
MockEchoResponseCallback response_callback;
EXPECT_CALL(response_callback,
Run(HasStatusCode(net::HTTP_OK), IsResponseText()))
.WillOnce([&]() { run_loop.Quit(); });
auto request = CreateDefaultTestRequest();
request->SetResponseCallback(response_callback.Get());
client_.ExecuteRequest(std::move(request));
// Verify request.
ASSERT_TRUE(test_url_loader_factory_.IsPending(kTestFullUrl));
ASSERT_EQ(1, test_url_loader_factory_.NumPending());
auto* pending_request = test_url_loader_factory_.GetPendingRequest(0);
std::string auth_header;
ASSERT_TRUE(pending_request->request.headers.GetHeader(
kAuthorizationHeaderKey, &auth_header));
ASSERT_EQ(kFakeAccessTokenHeaderValue, auth_header);
auto& data_element =
pending_request->request.request_body->elements()->front();
std::string request_body_data =
std::string(data_element.bytes(), data_element.length());
EchoRequest request_message;
ASSERT_TRUE(request_message.ParseFromString(request_body_data));
ASSERT_EQ(kRequestText, request_message.text());
// Respond.
test_url_loader_factory_.AddResponse(kTestFullUrl,
CreateDefaultResponseContent());
run_loop.Run();
}
TEST_F(ProtobufHttpClientTest,
SendUnauthenticatedRequest_TokenGetterNotCalled) {
EXPECT_CALL(mock_token_getter_, CallWithToken(_)).Times(0);
auto request = CreateDefaultTestRequest();
request->authenticated = false;
client_.ExecuteRequest(std::move(request));
// Verify that the request is sent with no auth header.
ASSERT_TRUE(test_url_loader_factory_.IsPending(kTestFullUrl));
ASSERT_EQ(1, test_url_loader_factory_.NumPending());
auto* pending_request = test_url_loader_factory_.GetPendingRequest(0);
ASSERT_FALSE(
pending_request->request.headers.HasHeader(kAuthorizationHeaderKey));
}
TEST_F(ProtobufHttpClientTest,
FailedToFetchAuthToken_RejectsWithUnauthorizedError) {
base::RunLoop run_loop;
EXPECT_CALL(mock_token_getter_, CallWithToken(_))
.WillOnce(
RunOnceCallback<0>(OAuthTokenGetter::Status::AUTH_ERROR, "", ""));
MockEchoResponseCallback response_callback;
EXPECT_CALL(response_callback,
Run(HasStatusCode(net::HTTP_UNAUTHORIZED), IsNullResponse()))
.WillOnce([&]() { run_loop.Quit(); });
auto request = CreateDefaultTestRequest();
request->SetResponseCallback(response_callback.Get());
client_.ExecuteRequest(std::move(request));
run_loop.Run();
}
TEST_F(ProtobufHttpClientTest, FailedToParseResponse_GetsInvalidResponseError) {
base::RunLoop run_loop;
EXPECT_CALL(mock_token_getter_, CallWithToken(_))
.WillOnce(RunOnceCallback<0>(OAuthTokenGetter::Status::SUCCESS, "",
kFakeAccessToken));
MockEchoResponseCallback response_callback;
EXPECT_CALL(
response_callback,
Run(HasNetError(net::Error::ERR_INVALID_RESPONSE), IsNullResponse()))
.WillOnce([&]() { run_loop.Quit(); });
auto request = CreateDefaultTestRequest();
request->SetResponseCallback(response_callback.Get());
client_.ExecuteRequest(std::move(request));
// Respond.
test_url_loader_factory_.AddResponse(kTestFullUrl, "Invalid content");
run_loop.Run();
}
TEST_F(ProtobufHttpClientTest, ServerRespondsWithError) {
base::RunLoop run_loop;
EXPECT_CALL(mock_token_getter_, CallWithToken(_))
.WillOnce(RunOnceCallback<0>(OAuthTokenGetter::Status::SUCCESS, "", ""));
MockEchoResponseCallback response_callback;
EXPECT_CALL(response_callback,
Run(HasStatusCode(net::HTTP_UNAUTHORIZED), IsNullResponse()))
.WillOnce([&]() { run_loop.Quit(); });
auto request = CreateDefaultTestRequest();
request->SetResponseCallback(response_callback.Get());
client_.ExecuteRequest(std::move(request));
test_url_loader_factory_.AddResponse(kTestFullUrl, "",
net::HttpStatusCode::HTTP_UNAUTHORIZED);
run_loop.Run();
}
TEST_F(ProtobufHttpClientTest, CancelPendingRequests_CallbackNotCalled) {
base::RunLoop run_loop;
OAuthTokenGetter::TokenCallback token_callback;
EXPECT_CALL(mock_token_getter_, CallWithToken(_))
.WillOnce([&](OAuthTokenGetter::TokenCallback callback) {
token_callback = std::move(callback);
});
auto request = CreateDefaultTestRequest();
client_.ExecuteRequest(std::move(request));
client_.CancelPendingRequests();
ASSERT_TRUE(token_callback);
std::move(token_callback)
.Run(OAuthTokenGetter::Status::SUCCESS, "", kFakeAccessToken);
// Verify no request.
ASSERT_FALSE(test_url_loader_factory_.IsPending(kTestFullUrl));
}
} // namespace remoting
// Copyright 2020 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 "remoting/base/protobuf_http_request.h"
namespace remoting {
ProtobufHttpRequest::ProtobufHttpRequest(
const net::NetworkTrafficAnnotationTag& traffic_annotation)
: traffic_annotation(traffic_annotation) {}
ProtobufHttpRequest::~ProtobufHttpRequest() = default;
void ProtobufHttpRequest::OnResponse(
const ProtobufHttpStatus& status,
std::unique_ptr<std::string> response_body) {
std::move(response_callback_)
.Run(status.ok() ? ParseResponse(std::move(response_body)) : status);
}
ProtobufHttpStatus ProtobufHttpRequest::ParseResponse(
std::unique_ptr<std::string> response_body) {
if (!response_body) {
LOG(ERROR) << "Server returned no response body";
return ProtobufHttpStatus(net::ERR_EMPTY_RESPONSE);
}
if (!response_message_->ParseFromString(*response_body)) {
LOG(ERROR) << "Failed to parse response body";
return ProtobufHttpStatus(net::ERR_INVALID_RESPONSE);
}
return ProtobufHttpStatus::OK;
}
} // namespace remoting
// Copyright 2020 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 REMOTING_BASE_PROTOBUF_HTTP_REQUEST_H_
#define REMOTING_BASE_PROTOBUF_HTTP_REQUEST_H_
#include <memory>
#include <string>
#include "base/bind.h"
#include "base/callback.h"
#include "base/time/time.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
#include "remoting/base/protobuf_http_status.h"
#include "third_party/protobuf/src/google/protobuf/message_lite.h"
namespace remoting {
// A simple unary request. Caller needs to set all public members and call
// SetResponseCallback() before passing it to ProtobufHttpClient.
struct ProtobufHttpRequest final {
template <typename ResponseType>
using ResponseCallback =
base::OnceCallback<void(const ProtobufHttpStatus& status,
std::unique_ptr<ResponseType> response)>;
explicit ProtobufHttpRequest(
const net::NetworkTrafficAnnotationTag& traffic_annotation);
~ProtobufHttpRequest();
const net::NetworkTrafficAnnotationTag traffic_annotation;
std::unique_ptr<google::protobuf::MessageLite> request_message;
std::string path;
bool authenticated = true;
base::TimeDelta timeout_duration = base::TimeDelta::FromSeconds(30);
// Sets the response callback. |ResponseType| needs to be a protobuf message
// type.
template <typename ResponseType>
void SetResponseCallback(ResponseCallback<ResponseType> callback) {
auto response = std::make_unique<ResponseType>();
response_message_ = response.get();
response_callback_ = base::BindOnce(
[](std::unique_ptr<ResponseType> response,
ResponseCallback<ResponseType> callback,
const ProtobufHttpStatus& status) {
if (!status.ok()) {
response.reset();
}
std::move(callback).Run(status, std::move(response));
},
std::move(response), std::move(callback));
}
private:
friend class ProtobufHttpClient;
// To be called by ProtobufHttpClient.
void OnResponse(const ProtobufHttpStatus& status,
std::unique_ptr<std::string> response_body);
// Parses |response_body| and writes it to |response_message_|.
ProtobufHttpStatus ParseResponse(std::unique_ptr<std::string> response_body);
// This is owned by |response_callback_|.
google::protobuf::MessageLite* response_message_;
base::OnceCallback<void(const ProtobufHttpStatus&)> response_callback_;
};
} // namespace remoting
#endif // REMOTING_BASE_PROTOBUF_HTTP_REQUEST_H_
// Copyright 2020 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 "remoting/base/protobuf_http_status.h"
#include "net/http/http_status_code.h"
namespace remoting {
const ProtobufHttpStatus& ProtobufHttpStatus::OK =
ProtobufHttpStatus(net::HttpStatusCode::HTTP_OK);
ProtobufHttpStatus::ProtobufHttpStatus(net::HttpStatusCode http_status_code)
: http_status_code_(http_status_code),
net_error_(net::Error::ERR_HTTP_RESPONSE_CODE_FAILURE),
error_message_(net::GetHttpReasonPhrase(http_status_code)) {
DCHECK_LE(0, http_status_code) << "Invalid http status code";
}
ProtobufHttpStatus::ProtobufHttpStatus(net::Error net_error)
: http_status_code_(-1),
net_error_(net_error),
error_message_(net::ErrorToString(net_error)) {
DCHECK_NE(net::Error::OK, net_error) << "Use the HttpStatusCode overload";
DCHECK_NE(net::Error::ERR_HTTP_RESPONSE_CODE_FAILURE, net_error)
<< "Use the HttpStatusCode overload";
}
ProtobufHttpStatus::~ProtobufHttpStatus() = default;
bool ProtobufHttpStatus::ok() const {
return http_status_code_ == net::HttpStatusCode::HTTP_OK;
}
} // namespace remoting
// Copyright 2020 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 REMOTING_BASE_PROTOBUF_HTTP_STATUS_H_
#define REMOTING_BASE_PROTOBUF_HTTP_STATUS_H_
#include <string>
#include "net/base/net_errors.h"
#include "net/http/http_status_code.h"
namespace remoting {
class ProtobufHttpStatus {
public:
// An OK pre-defined instance.
static const ProtobufHttpStatus& OK;
explicit ProtobufHttpStatus(net::HttpStatusCode http_status_code);
explicit ProtobufHttpStatus(net::Error net_error);
~ProtobufHttpStatus();
// Indicates whether the http request was successful based on the status code.
bool ok() const;
// The http status code, or -1 if the request fails to make, in which case
// the underlying error can be found by calling net_error().
int http_status_code() const { return http_status_code_; }
// The net error. If the error is ERR_HTTP_RESPONSE_CODE_FAILURE, the status
// code can be retrieved by calling http_status_code().
net::Error net_error() const { return net_error_; }
// The message that describes the error.
const std::string& error_message() const { return error_message_; }
private:
int http_status_code_;
net::Error net_error_;
std::string error_message_;
};
} // namespace remoting
#endif // REMOTING_BASE_PROTOBUF_HTTP_STATUS_H_
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