mirror of
https://github.com/kidfromjupiter/nearby.git
synced 2026-09-14 14:46:12 -04:00
Integrate network related part which both nearby sharing and fastpair are adopted.
Details can be seen in the `Integrate reusable internal components into third_party` part of `server access design doc` (go/fast_pair_server_data_access) PiperOrigin-RevId: 498208011
This commit is contained in:
@@ -416,6 +416,7 @@ let package = Package(
|
||||
"internal/platform/implementation/BUILD",
|
||||
"internal/platform/BUILD",
|
||||
"internal/analytics/BUILD",
|
||||
"internal/network/BUILD",
|
||||
// tests
|
||||
"connections/listeners_test.cc",
|
||||
"connections/strategy_test.cc",
|
||||
@@ -520,6 +521,13 @@ let package = Package(
|
||||
"internal/platform/byte_utils_test.cc",
|
||||
"internal/platform/direct_executor_test.cc",
|
||||
"internal/platform/borrowable_test.cc",
|
||||
"internal/platform/implementation/windows/http_loader_test.cc",
|
||||
"internal/network/utils_test.cc",
|
||||
"internal/network/url_test.cc",
|
||||
"internal/network/http_response_test.cc",
|
||||
"internal/network/http_request_test.cc",
|
||||
"internal/network/http_client_impl_test.cc",
|
||||
"internal/network/http_status_code_test.cc",
|
||||
// simulation
|
||||
"connections/implementation/offline_simulation_user.cc",
|
||||
"connections/implementation/simulation_user.cc",
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
licenses(["notice"])
|
||||
|
||||
cc_library(
|
||||
name = "types",
|
||||
srcs = [
|
||||
"http_request.cc",
|
||||
"http_response.cc",
|
||||
"http_status_code.cc",
|
||||
"url.cc",
|
||||
"utils.cc",
|
||||
],
|
||||
hdrs = [
|
||||
"http_body.h",
|
||||
"http_client.h",
|
||||
"http_client_factory.h",
|
||||
"http_request.h",
|
||||
"http_response.h",
|
||||
"http_status_code.h",
|
||||
"url.h",
|
||||
"utils.h",
|
||||
],
|
||||
visibility = [
|
||||
"//connections:__subpackages__",
|
||||
"//fastpair:__subpackages__",
|
||||
"//internal:__pkg__",
|
||||
"//internal/platform:__subpackages__",
|
||||
"//internal/platform/implementation:__subpackages__",
|
||||
"//location/nearby/cpp/fastpair:__subpackages__",
|
||||
"//presence:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
"@com_google_absl//absl/container:flat_hash_map",
|
||||
"@com_google_absl//absl/status",
|
||||
"@com_google_absl//absl/status:statusor",
|
||||
"@com_google_absl//absl/strings",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "nearby_http_client",
|
||||
srcs = [
|
||||
"http_client_impl.cc",
|
||||
],
|
||||
hdrs = [
|
||||
"debug.h",
|
||||
"http_client_factory_impl.h",
|
||||
"http_client_impl.h",
|
||||
],
|
||||
defines = ["_SILENCE_CLANG_COROUTINE_MESSAGE"],
|
||||
visibility = [
|
||||
"//connections:__subpackages__",
|
||||
"//fastpair:__subpackages__",
|
||||
"//internal:__pkg__",
|
||||
"//internal/platform:__subpackages__",
|
||||
"//internal/platform/implementation:__subpackages__",
|
||||
"//location/nearby/cpp/fastpair:__subpackages__",
|
||||
"//presence:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
":types",
|
||||
"//internal/platform:logging",
|
||||
"//internal/platform/implementation:platform",
|
||||
"@com_google_absl//absl/base:core_headers",
|
||||
"@com_google_absl//absl/container:flat_hash_map",
|
||||
"@com_google_absl//absl/strings:str_format",
|
||||
"@com_google_absl//absl/synchronization",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "nearby_http_client_test",
|
||||
size = "small",
|
||||
timeout = "short",
|
||||
srcs = [
|
||||
"http_client_impl_test.cc",
|
||||
"http_request_test.cc",
|
||||
"http_response_test.cc",
|
||||
"http_status_code_test.cc",
|
||||
"url_test.cc",
|
||||
"utils_test.cc",
|
||||
],
|
||||
shard_count = 8,
|
||||
deps = [
|
||||
":nearby_http_client",
|
||||
":types",
|
||||
"//internal/platform/implementation:comm",
|
||||
"//internal/platform/implementation:platform",
|
||||
"//internal/platform/implementation/g3",
|
||||
"@com_github_protobuf_matchers//protobuf-matchers",
|
||||
"@com_google_absl//absl/container:flat_hash_map",
|
||||
"@com_google_absl//absl/status",
|
||||
"@com_google_absl//absl/status:statusor",
|
||||
"@com_google_absl//absl/strings",
|
||||
"@com_google_googletest//:gtest_main",
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,34 @@
|
||||
// Copyright 2022 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#ifndef THIRD_PARTY_NEARBY_INTERNAL_NETWORK_DEBUG_H_
|
||||
#define THIRD_PARTY_NEARBY_INTERNAL_NETWORK_DEBUG_H_
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace network {
|
||||
namespace debug {
|
||||
|
||||
// Indicates to whether output HTTP request.
|
||||
constexpr bool kRequestEnabled = false;
|
||||
|
||||
// Indicates to whether output HTTP response.
|
||||
constexpr bool kResponseEnabled = false;
|
||||
|
||||
} // namespace debug
|
||||
} // namespace network
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // THIRD_PARTY_NEARBY_INTERNAL_NETWORK_DEBUG_H_
|
||||
@@ -0,0 +1,67 @@
|
||||
// Copyright 2022 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#ifndef THIRD_PARTY_NEARBY_INTERNAL_NETWORK_HTTP_BODY_H_
|
||||
#define THIRD_PARTY_NEARBY_INTERNAL_NETWORK_HTTP_BODY_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace network {
|
||||
|
||||
class HttpBody {
|
||||
public:
|
||||
HttpBody() = default;
|
||||
~HttpBody() = default;
|
||||
|
||||
HttpBody(const HttpBody&) = default;
|
||||
HttpBody& operator=(const HttpBody&) = default;
|
||||
HttpBody(HttpBody&&) = default;
|
||||
HttpBody& operator=(HttpBody&&) = default;
|
||||
|
||||
void SetData(const char* data, size_t size) {
|
||||
if (data == nullptr) {
|
||||
size = 0;
|
||||
}
|
||||
data_.assign(data, size);
|
||||
}
|
||||
|
||||
void SetData(absl::string_view data) {
|
||||
data_.assign(data.data(), data.size());
|
||||
}
|
||||
|
||||
bool empty() { return data_.empty(); }
|
||||
|
||||
const char* data() const { return &data_[0]; }
|
||||
|
||||
size_t size() const { return data_.size(); }
|
||||
|
||||
absl::string_view GetRawData() const { return data_; }
|
||||
|
||||
private:
|
||||
std::string data_;
|
||||
};
|
||||
|
||||
using HttpRequestBody = HttpBody;
|
||||
using HttpResponseBody = HttpBody;
|
||||
|
||||
} // namespace network
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // THIRD_PARTY_NEARBY_INTERNAL_NETWORK_HTTP_BODY_H_
|
||||
@@ -0,0 +1,41 @@
|
||||
// Copyright 2021 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#ifndef THIRD_PARTY_NEARBY_INTERNAL_NETWORK_HTTP_CLIENT_H_
|
||||
#define THIRD_PARTY_NEARBY_INTERNAL_NETWORK_HTTP_CLIENT_H_
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "absl/status/statusor.h"
|
||||
#include "internal/network/http_request.h"
|
||||
#include "internal/network/http_response.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace network {
|
||||
|
||||
class HttpClient {
|
||||
public:
|
||||
virtual ~HttpClient() = default;
|
||||
|
||||
virtual void StartRequest(
|
||||
const HttpRequest& request,
|
||||
std::function<void(const absl::StatusOr<HttpResponse>&)> callback) = 0;
|
||||
};
|
||||
|
||||
} // namespace network
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // THIRD_PARTY_NEARBY_INTERNAL_NETWORK_HTTP_CLIENT_H_
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright 2022 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#ifndef THIRD_PARTY_NEARBY_INTERNAL_NETWORK_HTTP_CLIENT_FACTORY_H_
|
||||
#define THIRD_PARTY_NEARBY_INTERNAL_NETWORK_HTTP_CLIENT_FACTORY_H_
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "internal/network/http_client.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace network {
|
||||
|
||||
class HttpClientFactory {
|
||||
public:
|
||||
virtual ~HttpClientFactory() = default;
|
||||
|
||||
virtual std::unique_ptr<HttpClient> CreateInstance() = 0;
|
||||
};
|
||||
|
||||
} // namespace network
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // THIRD_PARTY_NEARBY_INTERNAL_NETWORK_HTTP_CLIENT_FACTORY_H_
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright 2022 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#ifndef THIRD_PARTY_NEARBY_INTERNAL_NETWORK_HTTP_CLIENT_FACTORY_IMPL_H_
|
||||
#define THIRD_PARTY_NEARBY_INTERNAL_NETWORK_HTTP_CLIENT_FACTORY_IMPL_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "internal/network/http_client_factory.h"
|
||||
#include "internal/network/http_client_impl.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace network {
|
||||
|
||||
class HttpClientFactoryImpl : public HttpClientFactory {
|
||||
public:
|
||||
std::unique_ptr<HttpClient> CreateInstance() override {
|
||||
return std::make_unique<NearbyHttpClient>();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace network
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // THIRD_PARTY_NEARBY_INTERNAL_NETWORK_HTTP_CLIENT_FACTORY_IMPL_H_
|
||||
@@ -0,0 +1,123 @@
|
||||
// Copyright 2021 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#include "internal/network/http_client_impl.h"
|
||||
|
||||
#include <chrono> // NOLINT
|
||||
#include <functional>
|
||||
#include <future> // NOLINT
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
#include <utility>
|
||||
|
||||
#include "internal/network/debug.h"
|
||||
#include "internal/platform/implementation/platform.h"
|
||||
#include "internal/platform/logging.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace network {
|
||||
|
||||
void NearbyHttpClient::StartRequest(
|
||||
const HttpRequest& request,
|
||||
std::function<void(const absl::StatusOr<HttpResponse>&)> callback) {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
CleanThreads();
|
||||
|
||||
std::future<void> http_thread = std::async(std::launch::async, [&, request,
|
||||
callback]() {
|
||||
api::WebRequest web_request;
|
||||
web_request.url = request.GetUrl().GetUrlPath();
|
||||
web_request.method = absl::StrCat(request.GetMethodString());
|
||||
for (const auto& header : request.GetAllHeaders()) {
|
||||
for (const auto& value : header.second) {
|
||||
web_request.headers.emplace(header.first, value);
|
||||
}
|
||||
}
|
||||
web_request.body = absl::StrCat(request.GetBody().GetRawData());
|
||||
|
||||
if (debug::kRequestEnabled) {
|
||||
std::stringstream request_stream;
|
||||
request_stream << "HTTP REQUEST====>" << std::endl;
|
||||
request_stream << web_request.method << " " << web_request.url
|
||||
<< std::endl;
|
||||
for (const auto& header : web_request.headers) {
|
||||
request_stream << header.first << ": " << header.second << std::endl;
|
||||
}
|
||||
request_stream << std::endl;
|
||||
request_stream << "body size: " << request.GetBody().GetRawData().size()
|
||||
<< std::endl;
|
||||
NEARBY_LOGS(VERBOSE) << request_stream.str();
|
||||
}
|
||||
|
||||
absl::StatusOr<api::WebResponse> web_response =
|
||||
api::ImplementationPlatform::SendRequest(web_request);
|
||||
|
||||
if (!web_response.ok()) {
|
||||
if (callback != nullptr) {
|
||||
callback(web_response.status());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (debug::kResponseEnabled) {
|
||||
std::stringstream response_stream;
|
||||
response_stream << "HTTP RESPONSE====>" << std::endl;
|
||||
response_stream << "url: " << web_request.url << std::endl;
|
||||
response_stream << web_response->status_code << " "
|
||||
<< web_response->status_text << std::endl;
|
||||
for (const auto& header : web_response->headers) {
|
||||
response_stream << header.first << ": " << header.second << std::endl;
|
||||
}
|
||||
response_stream << std::endl;
|
||||
response_stream << "body size: " << web_response->body.size()
|
||||
<< std::endl;
|
||||
NEARBY_LOGS(VERBOSE) << response_stream.str();
|
||||
}
|
||||
|
||||
HttpResponse response;
|
||||
|
||||
response.SetStatusCode(
|
||||
static_cast<HttpStatusCode>(web_response->status_code));
|
||||
response.SetReasonPhrase(web_response->status_text);
|
||||
for (const auto& header : web_response->headers) {
|
||||
response.AddHeader(header.first, header.second);
|
||||
}
|
||||
response.SetBody(web_response->body);
|
||||
|
||||
if (callback != nullptr) {
|
||||
callback(response);
|
||||
}
|
||||
});
|
||||
|
||||
http_threads_.push_back(std::move(http_thread));
|
||||
}
|
||||
|
||||
void NearbyHttpClient::CleanThreads() {
|
||||
auto it = http_threads_.begin();
|
||||
|
||||
while (it != http_threads_.end()) {
|
||||
// Delete the thread if it is ready
|
||||
auto status = it->wait_for(std::chrono::seconds(0));
|
||||
if (status == std::future_status::ready) {
|
||||
it = http_threads_.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace network
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -0,0 +1,57 @@
|
||||
// Copyright 2022 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#ifndef THIRD_PARTY_NEARBY_INTERNAL_NETWORK_HTTP_CLIENT_IMPL_H_
|
||||
#define THIRD_PARTY_NEARBY_INTERNAL_NETWORK_HTTP_CLIENT_IMPL_H_
|
||||
|
||||
#include <functional>
|
||||
#include <future> // NOLINT
|
||||
#include <thread> // NOLINT
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/base/thread_annotations.h"
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "internal/network/http_client.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace network {
|
||||
|
||||
class NearbyHttpClient : public HttpClient {
|
||||
public:
|
||||
NearbyHttpClient() = default;
|
||||
~NearbyHttpClient() override = default;
|
||||
|
||||
NearbyHttpClient(const NearbyHttpClient&) = default;
|
||||
NearbyHttpClient& operator=(const NearbyHttpClient&) = default;
|
||||
NearbyHttpClient(NearbyHttpClient&&) = default;
|
||||
NearbyHttpClient& operator=(NearbyHttpClient&&) = default;
|
||||
|
||||
void StartRequest(const HttpRequest& request,
|
||||
std::function<void(const absl::StatusOr<HttpResponse>&)>
|
||||
callback) override ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
private:
|
||||
void CleanThreads() ABSL_SHARED_LOCKS_REQUIRED(mutex_);
|
||||
|
||||
absl::Mutex mutex_;
|
||||
std::vector<std::future<void>> http_threads_ ABSL_GUARDED_BY(mutex_);
|
||||
};
|
||||
|
||||
} // namespace network
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // THIRD_PARTY_NEARBY_INTERNAL_NETWORK_HTTP_CLIENT_IMPL_H_
|
||||
@@ -0,0 +1,275 @@
|
||||
// Copyright 2021 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#include "internal/network/http_client_impl.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "protobuf-matchers/protocol-buffer-matchers.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "absl/container/flat_hash_map.h"
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/status/statusor.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "internal/network/http_status_code.h"
|
||||
#include "internal/platform/implementation/http_loader.h"
|
||||
#include "internal/platform/implementation/platform.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace api {
|
||||
namespace {
|
||||
|
||||
struct HttpTestContext {
|
||||
WebRequest web_request;
|
||||
WebResponse web_response;
|
||||
absl::Status status;
|
||||
};
|
||||
|
||||
HttpTestContext* GetContext() {
|
||||
static HttpTestContext* context = new HttpTestContext();
|
||||
return context;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// Mock web implementation of the platform
|
||||
absl::StatusOr<WebResponse> ImplementationPlatform::SendRequest(
|
||||
const WebRequest& request) {
|
||||
GetContext()->web_request = request;
|
||||
if (GetContext()->status.ok()) {
|
||||
return GetContext()->web_response;
|
||||
}
|
||||
return GetContext()->status;
|
||||
}
|
||||
|
||||
} // namespace api
|
||||
|
||||
namespace network {
|
||||
|
||||
using ::testing::SizeIs;
|
||||
|
||||
class NearbyHttpClientTest : public ::testing::Test {
|
||||
public:
|
||||
void SetUp() override {
|
||||
api::GetContext()->web_request = api::WebRequest();
|
||||
api::GetContext()->web_response = api::WebResponse();
|
||||
api::GetContext()->status = absl::Status();
|
||||
}
|
||||
|
||||
void MockFailedResponse(absl::Status status) {
|
||||
api::GetContext()->status = status;
|
||||
}
|
||||
|
||||
void MockResponse(network::HttpStatusCode status,
|
||||
absl::string_view status_message,
|
||||
const std::multimap<std::string, std::string>& headers,
|
||||
absl::string_view body) {
|
||||
api::WebResponse web_response;
|
||||
web_response.status_code = static_cast<int>(status);
|
||||
web_response.status_text = absl::StrCat(status_message);
|
||||
web_response.headers = headers;
|
||||
web_response.body = absl::StrCat(body);
|
||||
api::GetContext()->web_response = web_response;
|
||||
}
|
||||
|
||||
api::WebRequest GetWebRequest() { return api::GetContext()->web_request; }
|
||||
|
||||
absl::StatusOr<HttpResponse> GetResponse(
|
||||
absl::string_view url, HttpRequestMethod method,
|
||||
const std::multimap<std::string, std::string>& headers,
|
||||
absl::string_view body) {
|
||||
absl::StatusOr<HttpResponse> result;
|
||||
absl::StatusOr<Url> request_url = Url::Create(url);
|
||||
if (!request_url.ok()) {
|
||||
return request_url.status();
|
||||
}
|
||||
|
||||
HttpRequest request{request_url.value()};
|
||||
auto it = headers.begin();
|
||||
while (it != headers.end()) {
|
||||
request.AddHeader(it->first, it->second);
|
||||
++it;
|
||||
}
|
||||
request.SetMethod(method);
|
||||
request.SetBody(body);
|
||||
|
||||
absl::Notification notification;
|
||||
client_.StartRequest(
|
||||
request, [&result, ¬ification](
|
||||
const absl::StatusOr<HttpResponse>& http_response) {
|
||||
result = http_response;
|
||||
notification.Notify();
|
||||
});
|
||||
|
||||
// No timeout to emulate original behavior
|
||||
notification.WaitForNotification();
|
||||
return result;
|
||||
}
|
||||
|
||||
void CheckHeader(const std::multimap<std::string, std::string>& headers,
|
||||
absl::string_view key, absl::string_view expected_value) {
|
||||
auto it = headers.find(std::string(key));
|
||||
ASSERT_TRUE(it != headers.end());
|
||||
|
||||
bool found = false;
|
||||
while (it != headers.end()) {
|
||||
if (it->second == absl::StrCat(expected_value)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
|
||||
EXPECT_TRUE(found);
|
||||
}
|
||||
|
||||
private:
|
||||
NearbyHttpClient client_;
|
||||
};
|
||||
|
||||
namespace {
|
||||
TEST_F(NearbyHttpClientTest, TestGet) {
|
||||
MockResponse(HttpStatusCode::kHttpOk, "OK", {{"Content_Type", "text/html"}},
|
||||
"web content");
|
||||
auto result =
|
||||
GetResponse("http://www.google.com", HttpRequestMethod::kGet, {}, "");
|
||||
|
||||
// Checks request.
|
||||
api::WebRequest web_request = GetWebRequest();
|
||||
EXPECT_EQ(web_request.url, "http://www.google.com");
|
||||
EXPECT_EQ(web_request.method, "GET");
|
||||
|
||||
// Checks response.
|
||||
ASSERT_TRUE(result.ok());
|
||||
EXPECT_EQ(result->GetStatusCode(), HttpStatusCode::kHttpOk);
|
||||
EXPECT_EQ(result->GetBody().GetRawData(), "web content");
|
||||
ASSERT_THAT(result->GetAllHeaders(), SizeIs(1));
|
||||
EXPECT_EQ(result->GetAllHeaders().at("Content_Type")[0], "text/html");
|
||||
}
|
||||
|
||||
TEST_F(NearbyHttpClientTest, TestGetWithQuery) {
|
||||
MockResponse(HttpStatusCode::kHttpOk, "OK", {{"Content_Type", "text/html"}},
|
||||
"web content");
|
||||
auto result = GetResponse("http://www.google.com?name=name1&age=36",
|
||||
HttpRequestMethod::kGet, {}, "");
|
||||
|
||||
// Checks request.
|
||||
api::WebRequest web_request = GetWebRequest();
|
||||
EXPECT_EQ(web_request.url, "http://www.google.com?name=name1&age=36");
|
||||
EXPECT_EQ(web_request.method, "GET");
|
||||
|
||||
// Checks response.
|
||||
ASSERT_TRUE(result.ok());
|
||||
EXPECT_EQ(result->GetStatusCode(), HttpStatusCode::kHttpOk);
|
||||
}
|
||||
|
||||
TEST_F(NearbyHttpClientTest, TestGetWithErrorResult) {
|
||||
MockFailedResponse(absl::InternalError("no connection."));
|
||||
auto result = GetResponse("http://www.google.com?name=name1&age=36",
|
||||
HttpRequestMethod::kGet, {}, "");
|
||||
|
||||
// Checks request.
|
||||
api::WebRequest web_request = GetWebRequest();
|
||||
EXPECT_EQ(web_request.url, "http://www.google.com?name=name1&age=36");
|
||||
EXPECT_EQ(web_request.method, "GET");
|
||||
|
||||
// Checks response.
|
||||
EXPECT_FALSE(result.ok());
|
||||
}
|
||||
|
||||
TEST_F(NearbyHttpClientTest, TestPost) {
|
||||
MockResponse(HttpStatusCode::kHttpNoContent, "OK",
|
||||
{{"Content_Type", "text/html"}}, "");
|
||||
auto result =
|
||||
GetResponse("http://www.google.com", HttpRequestMethod::kPost, {}, "");
|
||||
|
||||
// Checks request.
|
||||
api::WebRequest web_request = GetWebRequest();
|
||||
EXPECT_EQ(web_request.url, "http://www.google.com");
|
||||
EXPECT_EQ(web_request.method, "POST");
|
||||
|
||||
// Checks response.
|
||||
ASSERT_TRUE(result.ok());
|
||||
EXPECT_EQ(result->GetStatusCode(), HttpStatusCode::kHttpNoContent);
|
||||
HttpResponseBody body = result->GetBody();
|
||||
EXPECT_TRUE(body.empty());
|
||||
}
|
||||
|
||||
TEST_F(NearbyHttpClientTest, TestPostWithHeader) {
|
||||
MockResponse(HttpStatusCode::kHttpNoContent, "OK",
|
||||
{{"Content_Type", "text/html"}}, "");
|
||||
auto result =
|
||||
GetResponse("http://www.google.com", HttpRequestMethod::kPost,
|
||||
{{"Content_Type", "text/json"}, {"size", "596"}}, "");
|
||||
|
||||
// Checks request.
|
||||
api::WebRequest web_request = GetWebRequest();
|
||||
EXPECT_EQ(web_request.url, "http://www.google.com");
|
||||
EXPECT_EQ(web_request.method, "POST");
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
CheckHeader(web_request.headers, "Content_Type", "text/json"));
|
||||
ASSERT_NO_FATAL_FAILURE(CheckHeader(web_request.headers, "size", "596"));
|
||||
|
||||
// Checks response.
|
||||
ASSERT_TRUE(result.ok());
|
||||
EXPECT_EQ(result->GetStatusCode(), HttpStatusCode::kHttpNoContent);
|
||||
EXPECT_EQ(result->GetBody().GetRawData(), "");
|
||||
}
|
||||
|
||||
TEST_F(NearbyHttpClientTest, TestPostWithErrorResult) {
|
||||
MockFailedResponse(absl::UnauthenticatedError("no user."));
|
||||
auto result =
|
||||
GetResponse("http://www.google.com", HttpRequestMethod::kPost,
|
||||
{{"Content_Type", "text/json"}, {"size", "596"}}, "");
|
||||
|
||||
// Checks request.
|
||||
api::WebRequest web_request = GetWebRequest();
|
||||
EXPECT_EQ(web_request.url, "http://www.google.com");
|
||||
EXPECT_EQ(web_request.method, "POST");
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
CheckHeader(web_request.headers, "Content_Type", "text/json"));
|
||||
ASSERT_NO_FATAL_FAILURE(CheckHeader(web_request.headers, "size", "596"));
|
||||
|
||||
// Checks response.
|
||||
ASSERT_FALSE(result.ok());
|
||||
}
|
||||
|
||||
TEST_F(NearbyHttpClientTest, TestRequestWithCleanThreads) {
|
||||
MockResponse(HttpStatusCode::kHttpOk, "OK", {{"Content_Type", "text/html"}},
|
||||
"web content");
|
||||
auto result =
|
||||
GetResponse("http://www.google.com", HttpRequestMethod::kGet, {}, "");
|
||||
|
||||
// Checks request.
|
||||
api::WebRequest web_request = GetWebRequest();
|
||||
EXPECT_EQ(web_request.url, "http://www.google.com");
|
||||
EXPECT_EQ(web_request.method, "GET");
|
||||
|
||||
// Checks response.
|
||||
ASSERT_TRUE(result.ok());
|
||||
|
||||
result =
|
||||
GetResponse("http://www.youtube.com", HttpRequestMethod::kGet, {}, "");
|
||||
ASSERT_TRUE(result.ok());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace network
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -0,0 +1,104 @@
|
||||
// Copyright 2022 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#include "internal/network/http_request.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace network {
|
||||
|
||||
HttpRequest::HttpRequest(const Url& url) : url_(url) {
|
||||
method_ = HttpRequestMethod::kGet;
|
||||
}
|
||||
|
||||
void HttpRequest::SetUrl(const Url& url) { url_ = url; }
|
||||
|
||||
const Url& HttpRequest::GetUrl() const { return url_; }
|
||||
|
||||
void HttpRequest::SetMethod(const HttpRequestMethod& method) {
|
||||
method_ = method;
|
||||
}
|
||||
|
||||
const HttpRequestMethod& HttpRequest::GetMethod() const { return method_; }
|
||||
|
||||
absl::string_view HttpRequest::GetMethodString() const {
|
||||
switch (method_) {
|
||||
case HttpRequestMethod::kConnect:
|
||||
return "CONNECT";
|
||||
case HttpRequestMethod::kDelete:
|
||||
return "DELETE";
|
||||
case HttpRequestMethod::kGet:
|
||||
return "GET";
|
||||
case HttpRequestMethod::kHead:
|
||||
return "HEAD";
|
||||
case HttpRequestMethod::kOptions:
|
||||
return "OPTIONS";
|
||||
case HttpRequestMethod::kPatch:
|
||||
return "PATCH";
|
||||
case HttpRequestMethod::kPost:
|
||||
return "POST";
|
||||
case HttpRequestMethod::kPut:
|
||||
return "PUT";
|
||||
case HttpRequestMethod::kTrace:
|
||||
return "TRACE";
|
||||
}
|
||||
}
|
||||
|
||||
void HttpRequest::AddHeader(absl::string_view header, absl::string_view value) {
|
||||
auto it = headers_.find(header);
|
||||
if (it == headers_.end()) {
|
||||
headers_.emplace(header, std::vector<std::string>({std::string(value)}));
|
||||
} else {
|
||||
it->second.push_back(std::string(value));
|
||||
}
|
||||
}
|
||||
|
||||
void HttpRequest::RemoveHeader(absl::string_view header) {
|
||||
headers_.erase(header);
|
||||
}
|
||||
|
||||
const absl::flat_hash_map<std::string, std::vector<std::string>>&
|
||||
HttpRequest::GetAllHeaders() const {
|
||||
return headers_;
|
||||
}
|
||||
|
||||
void HttpRequest::AddQueryParameter(absl::string_view query,
|
||||
absl::string_view value) {
|
||||
url_.AddQueryParameter(query, value);
|
||||
}
|
||||
|
||||
void HttpRequest::RemoveQueryParameter(absl::string_view query) {
|
||||
url_.RemoveQueryParameter(query);
|
||||
}
|
||||
const Url::QueryParameters& HttpRequest::GetAllQueryParameters() const {
|
||||
return url_.GetAllQueryStrings();
|
||||
}
|
||||
|
||||
void HttpRequest::SetBody(const HttpRequestBody& body) { body_ = body; }
|
||||
|
||||
void HttpRequest::SetBody(absl::string_view body) {
|
||||
body_.SetData(body.data(), body.size());
|
||||
}
|
||||
|
||||
const HttpRequestBody& HttpRequest::GetBody() const { return body_; }
|
||||
|
||||
} // namespace network
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -0,0 +1,92 @@
|
||||
// Copyright 2022 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#ifndef THIRD_PARTY_NEARBY_INTERNAL_NETWORK_HTTP_REQUEST_H_
|
||||
#define THIRD_PARTY_NEARBY_INTERNAL_NETWORK_HTTP_REQUEST_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/container/flat_hash_map.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "internal/network/http_body.h"
|
||||
#include "internal/network/url.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace network {
|
||||
|
||||
enum class HttpRequestMethod {
|
||||
kOptions,
|
||||
kGet,
|
||||
kHead,
|
||||
kPost,
|
||||
kPut,
|
||||
kDelete,
|
||||
kTrace,
|
||||
kConnect,
|
||||
kPatch
|
||||
};
|
||||
|
||||
class HttpRequest {
|
||||
public:
|
||||
HttpRequest() = default;
|
||||
explicit HttpRequest(const Url& url);
|
||||
~HttpRequest() = default;
|
||||
|
||||
HttpRequest(const HttpRequest&) = default;
|
||||
HttpRequest& operator=(const HttpRequest&) = default;
|
||||
HttpRequest(HttpRequest&&) = default;
|
||||
HttpRequest& operator=(HttpRequest&&) = default;
|
||||
|
||||
void SetUrl(const Url& url);
|
||||
const Url& GetUrl() const;
|
||||
|
||||
void SetMethod(const HttpRequestMethod& method);
|
||||
const HttpRequestMethod& GetMethod() const;
|
||||
absl::string_view GetMethodString() const;
|
||||
|
||||
void AddHeader(absl::string_view header, absl::string_view value);
|
||||
void RemoveHeader(absl::string_view header);
|
||||
const absl::flat_hash_map<std::string, std::vector<std::string>>&
|
||||
GetAllHeaders() const;
|
||||
|
||||
void AddQueryParameter(absl::string_view query, absl::string_view value);
|
||||
void RemoveQueryParameter(absl::string_view query);
|
||||
const Url::QueryParameters& GetAllQueryParameters() const;
|
||||
|
||||
void SetBody(const HttpRequestBody& body);
|
||||
void SetBody(absl::string_view body);
|
||||
const HttpRequestBody& GetBody() const;
|
||||
|
||||
private:
|
||||
// The url of the request
|
||||
Url url_;
|
||||
|
||||
// The request method: GET, POST, etc.
|
||||
HttpRequestMethod method_ = HttpRequestMethod::kGet;
|
||||
|
||||
// The request headers, may include repeat keys
|
||||
absl::flat_hash_map<std::string, std::vector<std::string>> headers_;
|
||||
|
||||
// The request body, it may be empty.
|
||||
HttpRequestBody body_;
|
||||
};
|
||||
|
||||
} // namespace network
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // THIRD_PARTY_NEARBY_INTERNAL_NETWORK_HTTP_REQUEST_H_
|
||||
@@ -0,0 +1,88 @@
|
||||
// Copyright 2022 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#include "internal/network/http_request.h"
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "protobuf-matchers/protocol-buffer-matchers.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "internal/network/http_body.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace network {
|
||||
namespace {
|
||||
|
||||
using ::testing::SizeIs;
|
||||
|
||||
TEST(HttpRequest, TestBuildRequest) {
|
||||
auto url = Url::Create("http://www.google.com");
|
||||
ASSERT_TRUE(url.ok());
|
||||
|
||||
HttpRequest request_explicit(*url);
|
||||
EXPECT_EQ(request_explicit.GetMethodString(), "GET");
|
||||
EXPECT_EQ(request_explicit.GetUrl(), *url);
|
||||
|
||||
HttpRequest request;
|
||||
EXPECT_EQ(request.GetMethodString(), "GET");
|
||||
request.SetUrl(*url);
|
||||
EXPECT_EQ(request.GetUrl(), *url);
|
||||
request.AddQueryParameter("name", "google");
|
||||
EXPECT_THAT(request.GetAllQueryParameters(), SizeIs(1));
|
||||
request.AddQueryParameter("name", "android");
|
||||
EXPECT_THAT(request.GetAllQueryParameters(), SizeIs(2));
|
||||
request.AddHeader("content-type", "text/html");
|
||||
EXPECT_THAT(request.GetAllHeaders(), SizeIs(1));
|
||||
request.SetMethod(HttpRequestMethod::kPost);
|
||||
EXPECT_EQ(request.GetMethodString(), "POST");
|
||||
EXPECT_EQ(request.GetMethod(), HttpRequestMethod::kPost);
|
||||
request.RemoveQueryParameter("name");
|
||||
EXPECT_THAT(request.GetAllQueryParameters(), SizeIs(0));
|
||||
request.RemoveHeader("content-type");
|
||||
EXPECT_THAT(request.GetAllHeaders(), SizeIs(0));
|
||||
request.SetBody("test body");
|
||||
EXPECT_EQ(request.GetBody().GetRawData(), "test body");
|
||||
HttpRequestBody body;
|
||||
body.SetData("new body");
|
||||
request.SetBody(body);
|
||||
EXPECT_EQ(request.GetBody().GetRawData(), "new body");
|
||||
body.SetData(nullptr, 100);
|
||||
request.SetBody(body);
|
||||
EXPECT_TRUE(request.GetBody().GetRawData().empty());
|
||||
}
|
||||
|
||||
TEST(HttpRequest, TestGetMethodString) {
|
||||
HttpRequest request;
|
||||
request.SetMethod(HttpRequestMethod::kPost);
|
||||
EXPECT_EQ(request.GetMethodString(), "POST");
|
||||
request.SetMethod(HttpRequestMethod::kPut);
|
||||
EXPECT_EQ(request.GetMethodString(), "PUT");
|
||||
request.SetMethod(HttpRequestMethod::kDelete);
|
||||
EXPECT_EQ(request.GetMethodString(), "DELETE");
|
||||
request.SetMethod(HttpRequestMethod::kConnect);
|
||||
EXPECT_EQ(request.GetMethodString(), "CONNECT");
|
||||
request.SetMethod(HttpRequestMethod::kHead);
|
||||
EXPECT_EQ(request.GetMethodString(), "HEAD");
|
||||
request.SetMethod(HttpRequestMethod::kOptions);
|
||||
EXPECT_EQ(request.GetMethodString(), "OPTIONS");
|
||||
request.SetMethod(HttpRequestMethod::kPatch);
|
||||
EXPECT_EQ(request.GetMethodString(), "PATCH");
|
||||
request.SetMethod(HttpRequestMethod::kTrace);
|
||||
EXPECT_EQ(request.GetMethodString(), "TRACE");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace network
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -0,0 +1,74 @@
|
||||
// Copyright 2021 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#include "internal/network/http_response.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/strings/str_cat.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace network {
|
||||
|
||||
void HttpResponse::SetStatusCode(const HttpStatusCode& status_code) {
|
||||
status_code_ = status_code;
|
||||
}
|
||||
|
||||
HttpStatusCode HttpResponse::GetStatusCode() const { return status_code_; }
|
||||
|
||||
void HttpResponse::SetReasonPhrase(absl::string_view reason_phrase) {
|
||||
reason_phrase_ = std::string(reason_phrase);
|
||||
}
|
||||
|
||||
absl::string_view HttpResponse::GetReasonPhrase() const {
|
||||
return reason_phrase_;
|
||||
}
|
||||
|
||||
void HttpResponse::AddHeader(absl::string_view header,
|
||||
absl::string_view value) {
|
||||
auto it = headers_.find(header);
|
||||
if (it == headers_.end()) {
|
||||
headers_.emplace(header, std::vector<std::string>({std::string(value)}));
|
||||
} else {
|
||||
it->second.push_back(std::string(value));
|
||||
}
|
||||
}
|
||||
|
||||
void HttpResponse::SetHeaders(
|
||||
const absl::flat_hash_map<std::string, std::vector<std::string>>& headers) {
|
||||
headers_ = headers;
|
||||
}
|
||||
|
||||
const absl::flat_hash_map<std::string, std::vector<std::string>>&
|
||||
HttpResponse::GetAllHeaders() const {
|
||||
return headers_;
|
||||
}
|
||||
|
||||
void HttpResponse::RemoveHeader(absl::string_view header) {
|
||||
headers_.erase(header);
|
||||
}
|
||||
|
||||
void HttpResponse::SetBody(const HttpResponseBody& body) { body_ = body; }
|
||||
|
||||
void HttpResponse::SetBody(absl::string_view body) {
|
||||
body_.SetData(body.data(), body.size());
|
||||
}
|
||||
|
||||
const HttpResponseBody& HttpResponse::GetBody() const { return body_; }
|
||||
|
||||
} // namespace network
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -0,0 +1,77 @@
|
||||
// Copyright 2021 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#ifndef THIRD_PARTY_NEARBY_INTERNAL_NETWORK_HTTP_RESPONSE_H_
|
||||
#define THIRD_PARTY_NEARBY_INTERNAL_NETWORK_HTTP_RESPONSE_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/container/flat_hash_map.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "internal/network/http_body.h"
|
||||
#include "internal/network/http_request.h"
|
||||
#include "internal/network/http_status_code.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace network {
|
||||
|
||||
class HttpResponse {
|
||||
public:
|
||||
HttpResponse() = default;
|
||||
~HttpResponse() = default;
|
||||
|
||||
HttpResponse(const HttpResponse&) = default;
|
||||
HttpResponse& operator=(const HttpResponse&) = default;
|
||||
HttpResponse(HttpResponse&&) = default;
|
||||
HttpResponse& operator=(HttpResponse&&) = default;
|
||||
|
||||
void SetStatusCode(const HttpStatusCode& status_code);
|
||||
HttpStatusCode GetStatusCode() const;
|
||||
|
||||
void SetReasonPhrase(absl::string_view reason_phrase);
|
||||
absl::string_view GetReasonPhrase() const;
|
||||
|
||||
void AddHeader(absl::string_view header, absl::string_view value);
|
||||
void RemoveHeader(absl::string_view header);
|
||||
void SetHeaders(const absl::flat_hash_map<std::string,
|
||||
std::vector<std::string>>& headers);
|
||||
const absl::flat_hash_map<std::string, std::vector<std::string>>&
|
||||
GetAllHeaders() const;
|
||||
|
||||
void SetBody(const HttpResponseBody& body);
|
||||
void SetBody(absl::string_view body);
|
||||
const HttpResponseBody& GetBody() const;
|
||||
|
||||
private:
|
||||
// The status code returned from remote server
|
||||
HttpStatusCode status_code_ = HttpStatusCode::kHttpOk;
|
||||
|
||||
// Provides a short textual description of the Status Code.
|
||||
std::string reason_phrase_;
|
||||
|
||||
// The response headers returned from remove server
|
||||
absl::flat_hash_map<std::string, std::vector<std::string>> headers_;
|
||||
|
||||
// The response content from remote server, it may be empty
|
||||
HttpResponseBody body_;
|
||||
};
|
||||
|
||||
} // namespace network
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // THIRD_PARTY_NEARBY_INTERNAL_NETWORK_HTTP_RESPONSE_H_
|
||||
@@ -0,0 +1,47 @@
|
||||
// Copyright 2022 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#include "internal/network/http_response.h"
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "protobuf-matchers/protocol-buffer-matchers.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace network {
|
||||
namespace {
|
||||
|
||||
using ::testing::SizeIs;
|
||||
|
||||
TEST(HttpResponse, TestBuildResponse) {
|
||||
HttpResponse response;
|
||||
response.SetStatusCode(HttpStatusCode::kHttpNotFound);
|
||||
EXPECT_EQ(response.GetStatusCode(), HttpStatusCode::kHttpNotFound);
|
||||
response.SetReasonPhrase("OK");
|
||||
EXPECT_EQ(response.GetReasonPhrase(), "OK");
|
||||
response.AddHeader("Content-Type", "text/html");
|
||||
EXPECT_THAT(response.GetAllHeaders(), SizeIs(1));
|
||||
response.RemoveHeader("Content-Type");
|
||||
EXPECT_THAT(response.GetAllHeaders(), SizeIs(0));
|
||||
HttpResponseBody body;
|
||||
body.SetData("test body");
|
||||
response.SetBody(body);
|
||||
EXPECT_EQ(response.GetBody().GetRawData(), "test body");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace network
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -0,0 +1,118 @@
|
||||
// Copyright 2021 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#include "internal/network/http_status_code.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace network {
|
||||
|
||||
absl::string_view GetHttpReasonPhrase(HttpStatusCode code) {
|
||||
switch (code) {
|
||||
case HttpStatusCode::kHttpContinue:
|
||||
return "Continue";
|
||||
case HttpStatusCode::kHttpSwitchingProtocols:
|
||||
return "Switching Protocols";
|
||||
case HttpStatusCode::kEarlyHints:
|
||||
return "Early Hints";
|
||||
case HttpStatusCode::kHttpOk:
|
||||
return "OK";
|
||||
case HttpStatusCode::kHttpCreated:
|
||||
return "Created";
|
||||
case HttpStatusCode::kHttpAccepted:
|
||||
return "Accepted";
|
||||
case HttpStatusCode::kHttpNonAuthoritativeInformation:
|
||||
return "Non-Authoritative Information";
|
||||
case HttpStatusCode::kHttpNoContent:
|
||||
return "No Content";
|
||||
case HttpStatusCode::kHttpResetContent:
|
||||
return "Reset Content";
|
||||
case HttpStatusCode::kHttpPartialContent:
|
||||
return "Partial Content";
|
||||
case HttpStatusCode::kHttpMultipleChocies:
|
||||
return "Multiple Choices";
|
||||
case HttpStatusCode::kHttpMovedPermancently:
|
||||
return "Moved Permanently";
|
||||
case HttpStatusCode::kHttpFound:
|
||||
return "Found";
|
||||
case HttpStatusCode::kHttpSeeOther:
|
||||
return "See Other";
|
||||
case HttpStatusCode::kHttpNotModified:
|
||||
return "Not Modified";
|
||||
case HttpStatusCode::kHttpUseProxy:
|
||||
return "Use Proxy";
|
||||
case HttpStatusCode::kHttpTemporaryRedirect:
|
||||
return "Temporary Redirect";
|
||||
case HttpStatusCode::kHttpPermanentRedirect:
|
||||
return "Permanent Redirect";
|
||||
case HttpStatusCode::kHttpBadRequest:
|
||||
return "Bad Request";
|
||||
case HttpStatusCode::kHttpUnauthorized:
|
||||
return "Unauthorized";
|
||||
case HttpStatusCode::kHttpPaymentRequest:
|
||||
return "Payment Required";
|
||||
case HttpStatusCode::kHttpForbidden:
|
||||
return "Forbidden";
|
||||
case HttpStatusCode::kHttpNotFound:
|
||||
return "Not Found";
|
||||
case HttpStatusCode::kHttpMethodNotAllowed:
|
||||
return "Method Not Allowed";
|
||||
case HttpStatusCode::kHttpNotAcceptable:
|
||||
return "Not Acceptable";
|
||||
case HttpStatusCode::kHttpProxyAuthenticationRequired:
|
||||
return "Proxy Authentication Required";
|
||||
case HttpStatusCode::kHttpRequestTimeout:
|
||||
return "Request Timeout";
|
||||
case HttpStatusCode::kHttpConflict:
|
||||
return "Conflict";
|
||||
case HttpStatusCode::kHttpGone:
|
||||
return "Gone";
|
||||
case HttpStatusCode::kHttpLengthRequired:
|
||||
return "Length Required";
|
||||
case HttpStatusCode::kHttpPreconditionFailed:
|
||||
return "Precondition Failed";
|
||||
case HttpStatusCode::kHttpRequestEntityTooLarge:
|
||||
return "Request Entity Too Large";
|
||||
case HttpStatusCode::kHttpRequestUriTooLong:
|
||||
return "Request-URI Too Long";
|
||||
case HttpStatusCode::kHttpUnsupportedMediaType:
|
||||
return "Unsupported Media Type";
|
||||
case HttpStatusCode::kHttpRequestedRangeNotSatisfiable:
|
||||
return "Requested Range Not Satisfiable";
|
||||
case HttpStatusCode::kHttpExpectationFailed:
|
||||
return "Expectation Failed";
|
||||
case HttpStatusCode::kHttpInvalidXprivetToken:
|
||||
return "Invalid XPrivet Token";
|
||||
case HttpStatusCode::kHttpTooEarly:
|
||||
return "Too Early";
|
||||
case HttpStatusCode::kHttpTooManyRequests:
|
||||
return "Too Many Requests";
|
||||
case HttpStatusCode::kHttpInternalServerError:
|
||||
return "Internal Server Error";
|
||||
case HttpStatusCode::kHttpNotImplemented:
|
||||
return "Not Implemented";
|
||||
case HttpStatusCode::kHttpBadGateway:
|
||||
return "Bad Gateway";
|
||||
case HttpStatusCode::kHttpServiceUnavailable:
|
||||
return "Service Unavailable";
|
||||
case HttpStatusCode::kHttpGatewayTimeout:
|
||||
return "Gateway Timeout";
|
||||
case HttpStatusCode::kHttpVersionNotSupported:
|
||||
return "HTTP Version Not Supported";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace network
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -0,0 +1,93 @@
|
||||
// Copyright 2021 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#ifndef THIRD_PARTY_NEARBY_INTERNAL_NETWORK_HTTP_STATUS_CODE_H_
|
||||
#define THIRD_PARTY_NEARBY_INTERNAL_NETWORK_HTTP_STATUS_CODE_H_
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace network {
|
||||
|
||||
// HTTP response status codes are separated into five categories. Details can be
|
||||
// found in https://en.wikipedia.org/wiki/List_of_HTTP_status_codes
|
||||
enum class HttpStatusCode {
|
||||
// Informational 1xx: The request has been accepted and needs to be processed
|
||||
// further.
|
||||
kHttpContinue = 100,
|
||||
kHttpSwitchingProtocols = 101,
|
||||
kEarlyHints = 103,
|
||||
|
||||
// Information 2xx: The request has been accepted and approved by the server.
|
||||
kHttpOk = 200,
|
||||
kHttpCreated = 201,
|
||||
kHttpAccepted = 202,
|
||||
kHttpNonAuthoritativeInformation = 203,
|
||||
kHttpNoContent = 204,
|
||||
kHttpResetContent = 205,
|
||||
kHttpPartialContent = 206,
|
||||
|
||||
// Redirection 3xx: The request needs client's further action to be completed.
|
||||
kHttpMultipleChocies = 300,
|
||||
kHttpMovedPermancently = 301,
|
||||
kHttpFound = 302,
|
||||
kHttpSeeOther = 303,
|
||||
kHttpNotModified = 304,
|
||||
kHttpUseProxy = 305,
|
||||
// 306 is no longer used.
|
||||
kHttpTemporaryRedirect = 307,
|
||||
kHttpPermanentRedirect = 308,
|
||||
|
||||
// Client error 4xx: There may be errors in the client that prevent the server
|
||||
// from processing the request.
|
||||
kHttpBadRequest = 400,
|
||||
kHttpUnauthorized = 401,
|
||||
kHttpPaymentRequest = 402,
|
||||
kHttpForbidden = 403,
|
||||
kHttpNotFound = 404,
|
||||
kHttpMethodNotAllowed = 405,
|
||||
kHttpNotAcceptable = 406,
|
||||
kHttpProxyAuthenticationRequired = 407,
|
||||
kHttpRequestTimeout = 408,
|
||||
kHttpConflict = 409,
|
||||
kHttpGone = 410,
|
||||
kHttpLengthRequired = 411,
|
||||
kHttpPreconditionFailed = 412,
|
||||
kHttpRequestEntityTooLarge = 413,
|
||||
kHttpRequestUriTooLong = 414,
|
||||
kHttpUnsupportedMediaType = 415,
|
||||
kHttpRequestedRangeNotSatisfiable = 416,
|
||||
kHttpExpectationFailed = 417,
|
||||
// 418 returned by Cloud Print.
|
||||
kHttpInvalidXprivetToken = 418,
|
||||
kHttpTooEarly = 425,
|
||||
kHttpTooManyRequests = 429,
|
||||
|
||||
// Server error 5xx
|
||||
kHttpInternalServerError = 500,
|
||||
kHttpNotImplemented = 501,
|
||||
kHttpBadGateway = 502,
|
||||
kHttpServiceUnavailable = 503,
|
||||
kHttpGatewayTimeout = 504,
|
||||
kHttpVersionNotSupported = 505
|
||||
};
|
||||
|
||||
absl::string_view GetHttpReasonPhrase(HttpStatusCode code);
|
||||
|
||||
} // namespace network
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // THIRD_PARTY_NEARBY_INTERNAL_NETWORK_HTTP_STATUS_CODE_H_
|
||||
@@ -0,0 +1,109 @@
|
||||
// Copyright 2021 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#include "internal/network/http_status_code.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace network {
|
||||
namespace {
|
||||
|
||||
TEST(HttpStatusCode, TestReturnCorrectReasonPhrase) {
|
||||
EXPECT_EQ(GetHttpReasonPhrase(HttpStatusCode::kHttpContinue), "Continue");
|
||||
EXPECT_EQ(GetHttpReasonPhrase(HttpStatusCode::kHttpSwitchingProtocols),
|
||||
"Switching Protocols");
|
||||
EXPECT_EQ(GetHttpReasonPhrase(HttpStatusCode::kEarlyHints), "Early Hints");
|
||||
EXPECT_EQ(GetHttpReasonPhrase(HttpStatusCode::kHttpOk), "OK");
|
||||
EXPECT_EQ(GetHttpReasonPhrase(HttpStatusCode::kHttpCreated), "Created");
|
||||
EXPECT_EQ(GetHttpReasonPhrase(HttpStatusCode::kHttpAccepted), "Accepted");
|
||||
EXPECT_EQ(
|
||||
GetHttpReasonPhrase(HttpStatusCode::kHttpNonAuthoritativeInformation),
|
||||
"Non-Authoritative Information");
|
||||
EXPECT_EQ(GetHttpReasonPhrase(HttpStatusCode::kHttpNoContent), "No Content");
|
||||
EXPECT_EQ(GetHttpReasonPhrase(HttpStatusCode::kHttpResetContent),
|
||||
"Reset Content");
|
||||
EXPECT_EQ(GetHttpReasonPhrase(HttpStatusCode::kHttpPartialContent),
|
||||
"Partial Content");
|
||||
EXPECT_EQ(GetHttpReasonPhrase(HttpStatusCode::kHttpMultipleChocies),
|
||||
"Multiple Choices");
|
||||
EXPECT_EQ(GetHttpReasonPhrase(HttpStatusCode::kHttpMovedPermancently),
|
||||
"Moved Permanently");
|
||||
EXPECT_EQ(GetHttpReasonPhrase(HttpStatusCode::kHttpFound), "Found");
|
||||
EXPECT_EQ(GetHttpReasonPhrase(HttpStatusCode::kHttpSeeOther), "See Other");
|
||||
EXPECT_EQ(GetHttpReasonPhrase(HttpStatusCode::kHttpNotModified),
|
||||
"Not Modified");
|
||||
EXPECT_EQ(GetHttpReasonPhrase(HttpStatusCode::kHttpUseProxy), "Use Proxy");
|
||||
EXPECT_EQ(GetHttpReasonPhrase(HttpStatusCode::kHttpTemporaryRedirect),
|
||||
"Temporary Redirect");
|
||||
EXPECT_EQ(GetHttpReasonPhrase(HttpStatusCode::kHttpPermanentRedirect),
|
||||
"Permanent Redirect");
|
||||
EXPECT_EQ(GetHttpReasonPhrase(HttpStatusCode::kHttpBadRequest),
|
||||
"Bad Request");
|
||||
EXPECT_EQ(GetHttpReasonPhrase(HttpStatusCode::kHttpUnauthorized),
|
||||
"Unauthorized");
|
||||
EXPECT_EQ(GetHttpReasonPhrase(HttpStatusCode::kHttpPaymentRequest),
|
||||
"Payment Required");
|
||||
EXPECT_EQ(GetHttpReasonPhrase(HttpStatusCode::kHttpForbidden), "Forbidden");
|
||||
EXPECT_EQ(GetHttpReasonPhrase(HttpStatusCode::kHttpNotFound), "Not Found");
|
||||
EXPECT_EQ(GetHttpReasonPhrase(HttpStatusCode::kHttpMethodNotAllowed),
|
||||
"Method Not Allowed");
|
||||
EXPECT_EQ(GetHttpReasonPhrase(HttpStatusCode::kHttpNotAcceptable),
|
||||
"Not Acceptable");
|
||||
EXPECT_EQ(
|
||||
GetHttpReasonPhrase(HttpStatusCode::kHttpProxyAuthenticationRequired),
|
||||
"Proxy Authentication Required");
|
||||
EXPECT_EQ(GetHttpReasonPhrase(HttpStatusCode::kHttpRequestTimeout),
|
||||
"Request Timeout");
|
||||
EXPECT_EQ(GetHttpReasonPhrase(HttpStatusCode::kHttpConflict), "Conflict");
|
||||
EXPECT_EQ(GetHttpReasonPhrase(HttpStatusCode::kHttpGone), "Gone");
|
||||
EXPECT_EQ(GetHttpReasonPhrase(HttpStatusCode::kHttpLengthRequired),
|
||||
"Length Required");
|
||||
EXPECT_EQ(GetHttpReasonPhrase(HttpStatusCode::kHttpPreconditionFailed),
|
||||
"Precondition Failed");
|
||||
EXPECT_EQ(GetHttpReasonPhrase(HttpStatusCode::kHttpRequestEntityTooLarge),
|
||||
"Request Entity Too Large");
|
||||
EXPECT_EQ(GetHttpReasonPhrase(HttpStatusCode::kHttpUnsupportedMediaType),
|
||||
"Unsupported Media Type");
|
||||
EXPECT_EQ(
|
||||
GetHttpReasonPhrase(HttpStatusCode::kHttpRequestedRangeNotSatisfiable),
|
||||
"Requested Range Not Satisfiable");
|
||||
EXPECT_EQ(GetHttpReasonPhrase(HttpStatusCode::kHttpExpectationFailed),
|
||||
"Expectation Failed");
|
||||
EXPECT_EQ(GetHttpReasonPhrase(HttpStatusCode::kHttpInvalidXprivetToken),
|
||||
"Invalid XPrivet Token");
|
||||
EXPECT_EQ(GetHttpReasonPhrase(HttpStatusCode::kHttpTooEarly), "Too Early");
|
||||
EXPECT_EQ(GetHttpReasonPhrase(HttpStatusCode::kHttpTooManyRequests),
|
||||
"Too Many Requests");
|
||||
EXPECT_EQ(GetHttpReasonPhrase(HttpStatusCode::kHttpInternalServerError),
|
||||
"Internal Server Error");
|
||||
EXPECT_EQ(GetHttpReasonPhrase(HttpStatusCode::kHttpNotImplemented),
|
||||
"Not Implemented");
|
||||
EXPECT_EQ(GetHttpReasonPhrase(HttpStatusCode::kHttpBadGateway),
|
||||
"Bad Gateway");
|
||||
EXPECT_EQ(GetHttpReasonPhrase(HttpStatusCode::kHttpServiceUnavailable),
|
||||
"Service Unavailable");
|
||||
EXPECT_EQ(GetHttpReasonPhrase(HttpStatusCode::kHttpGatewayTimeout),
|
||||
"Gateway Timeout");
|
||||
EXPECT_EQ(GetHttpReasonPhrase(HttpStatusCode::kHttpVersionNotSupported),
|
||||
"HTTP Version Not Supported");
|
||||
EXPECT_EQ(GetHttpReasonPhrase(HttpStatusCode::kHttpRequestUriTooLong),
|
||||
"Request-URI Too Long");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace network
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -0,0 +1,259 @@
|
||||
// Copyright 2021 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#include "internal/network/url.h"
|
||||
|
||||
#include <ostream>
|
||||
#include <regex> //NOLINT
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "internal/network/utils.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace network {
|
||||
|
||||
absl::StatusOr<Url> Url::Create(absl::string_view url_string) {
|
||||
Url url;
|
||||
if (!url.SetUrlPath(url_string)) {
|
||||
return absl::InvalidArgumentError("bad url.");
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
bool Url::SetUrlPath(absl::string_view url_path) {
|
||||
return ApplyUrlString(url_path);
|
||||
}
|
||||
|
||||
std::string Url::GetUrlPath() const {
|
||||
return scheme_ + "://" + host_ + GetPortString() + path_ + GetQueryString() +
|
||||
GetFragmentString();
|
||||
}
|
||||
|
||||
absl::string_view Url::GetScheme() const { return scheme_; }
|
||||
|
||||
absl::string_view Url::GetHostName() const { return host_; }
|
||||
|
||||
absl::string_view Url::GetPath() const { return path_; }
|
||||
|
||||
uint16_t Url::GetPort() const { return port_; }
|
||||
|
||||
absl::string_view Url::GetFragment() const { return fragment_; }
|
||||
|
||||
bool Url::ApplyUrlString(absl::string_view url_string) {
|
||||
// Refer to: https://www.rfc-editor.org/rfc/rfc3986#page-50
|
||||
std::regex url_reg(
|
||||
R"(^(([^:\/?#]+):)?(//([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?)",
|
||||
std::regex::extended);
|
||||
|
||||
std::smatch matches;
|
||||
std::string url{url_string};
|
||||
|
||||
std::regex_search(url, matches, url_reg);
|
||||
scheme_ = matches[2];
|
||||
if (!(scheme_ == "http" || scheme_ == "https")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string authority = matches[4];
|
||||
if (authority.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t pos = authority.find(':');
|
||||
if (pos != std::string::npos) {
|
||||
host_ = authority.substr(0, pos);
|
||||
port_ = std::stoi(authority.substr(pos + 1));
|
||||
} else {
|
||||
host_ = authority;
|
||||
port_ = scheme_ == "http" ? 80 : 443;
|
||||
}
|
||||
path_ = matches[5];
|
||||
if (path_ == "/") {
|
||||
path_ = "";
|
||||
}
|
||||
|
||||
query_parameters_.clear();
|
||||
std::string query = matches[7];
|
||||
if (!query.empty()) {
|
||||
// split by &
|
||||
size_t start = 0;
|
||||
size_t end = 0;
|
||||
while ((end = query.find('&', start)) != std::string::npos) {
|
||||
std::string kv = query.substr(start, end - start);
|
||||
start = end + 1;
|
||||
size_t eq_pos = kv.find('=');
|
||||
if (eq_pos <= 0 || eq_pos == std::string::npos) {
|
||||
continue;
|
||||
}
|
||||
query_parameters_.push_back(
|
||||
{UrlDecode(kv.substr(0, eq_pos)), UrlDecode(kv.substr(eq_pos + 1))});
|
||||
}
|
||||
|
||||
if (start < query.size()) {
|
||||
size_t eq_pos = query.find('=', start);
|
||||
if (eq_pos > start && eq_pos < query.size() - 1) {
|
||||
query_parameters_.push_back(
|
||||
{UrlDecode(query.substr(start, eq_pos - start)),
|
||||
UrlDecode(query.substr(eq_pos + 1))});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fragment_ = matches[9];
|
||||
return true;
|
||||
}
|
||||
|
||||
void Url::AddQueryParameter(absl::string_view query, absl::string_view value) {
|
||||
query_parameters_.push_back({absl::StrCat(query), absl::StrCat(value)});
|
||||
}
|
||||
|
||||
void Url::RemoveQueryParameter(absl::string_view query) {
|
||||
auto it = query_parameters_.begin();
|
||||
|
||||
while (it != query_parameters_.end()) {
|
||||
if (it->first == query) {
|
||||
it = query_parameters_.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> Url::GetQueryValues(absl::string_view query) const {
|
||||
std::vector<std::string> values;
|
||||
auto it = query_parameters_.begin();
|
||||
while (it != query_parameters_.end()) {
|
||||
if (it->first == query) {
|
||||
values.push_back(it->second);
|
||||
}
|
||||
++it;
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
const Url::QueryParameters& Url::GetAllQueryStrings() const {
|
||||
return query_parameters_;
|
||||
}
|
||||
|
||||
std::string Url::GetQueryString() const {
|
||||
if (query_parameters_.empty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string query = "";
|
||||
auto it = query_parameters_.begin();
|
||||
bool first = true;
|
||||
|
||||
while (it != query_parameters_.end()) {
|
||||
if (first) {
|
||||
query += "?";
|
||||
first = false;
|
||||
} else {
|
||||
query += "&";
|
||||
}
|
||||
|
||||
query += UrlEncode(it->first) + "=" + UrlEncode(it->second);
|
||||
|
||||
it++;
|
||||
}
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
std::string Url::GetPortString() const {
|
||||
if ((scheme_ == "http" && port_ == 80) ||
|
||||
(scheme_ == "https" && port_ == 443)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return absl::StrCat(":", port_);
|
||||
}
|
||||
|
||||
std::string Url::GetFragmentString() const {
|
||||
if (fragment_.empty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return "#" + fragment_;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const Url& url) {
|
||||
os << url.GetUrlPath();
|
||||
return os;
|
||||
}
|
||||
|
||||
bool operator==(const Url& url1, const Url& url2) {
|
||||
if (url1.GetScheme() != url2.GetScheme()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (url1.GetHostName() != url2.GetHostName()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (url1.GetPort() != url2.GetPort()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (url1.GetPath() != url2.GetPath()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto queries1 = url1.GetAllQueryStrings();
|
||||
auto queries2 = url2.GetAllQueryStrings();
|
||||
if (queries1.size() != queries2.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<bool> state;
|
||||
state.reserve(queries1.size());
|
||||
for (int i = 0; i < queries1.size(); ++i) {
|
||||
state.push_back(false);
|
||||
}
|
||||
|
||||
for (int i = 0; i < queries1.size(); ++i) {
|
||||
bool found = false;
|
||||
for (int j = 0; j < queries2.size(); ++j) {
|
||||
if (queries1[i].first == queries2[j].first &&
|
||||
queries1[i].second == queries2[j].second) {
|
||||
if (state[j]) {
|
||||
return false;
|
||||
}
|
||||
state[j] = true;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (url1.GetFragment() != url2.GetFragment()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace network
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -0,0 +1,84 @@
|
||||
// Copyright 2022 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#ifndef THIRD_PARTY_NEARBY_INTERNAL_NETWORK_URL_H_
|
||||
#define THIRD_PARTY_NEARBY_INTERNAL_NETWORK_URL_H_
|
||||
|
||||
#include <iostream>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/status/statusor.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace network {
|
||||
|
||||
class Url {
|
||||
public:
|
||||
using QueryParameters = std::vector<std::pair<std::string, std::string>>;
|
||||
|
||||
Url() = default;
|
||||
~Url() = default;
|
||||
|
||||
Url(const Url&) = default;
|
||||
Url& operator=(const Url&) = default;
|
||||
Url(Url&&) = default;
|
||||
Url& operator=(Url&&) = default;
|
||||
|
||||
static absl::StatusOr<Url> Create(absl::string_view url_string);
|
||||
|
||||
bool SetUrlPath(absl::string_view url_path);
|
||||
std::string GetUrlPath() const;
|
||||
|
||||
absl::string_view GetScheme() const;
|
||||
absl::string_view GetHostName() const;
|
||||
absl::string_view GetPath() const;
|
||||
uint16_t GetPort() const;
|
||||
absl::string_view GetFragment() const;
|
||||
|
||||
void AddQueryParameter(absl::string_view query, absl::string_view value);
|
||||
void RemoveQueryParameter(absl::string_view query);
|
||||
std::vector<std::string> GetQueryValues(absl::string_view query) const;
|
||||
const QueryParameters& GetAllQueryStrings() const;
|
||||
|
||||
private:
|
||||
bool ApplyUrlString(absl::string_view url_string);
|
||||
std::string GetQueryString() const;
|
||||
std::string GetPortString() const;
|
||||
std::string GetFragmentString() const;
|
||||
|
||||
// The absolute requested URL encoded in ASCII per the rules of RFC-2396.
|
||||
std::string scheme_;
|
||||
std::string host_;
|
||||
uint16_t port_;
|
||||
std::string path_;
|
||||
std::string fragment_;
|
||||
|
||||
// Query strings of the request, no URL encode
|
||||
QueryParameters query_parameters_;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const Url& url);
|
||||
|
||||
bool operator==(const Url& url1, const Url& url2);
|
||||
|
||||
} // namespace network
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // THIRD_PARTY_NEARBY_INTERNAL_NETWORK_URL_H_
|
||||
@@ -0,0 +1,159 @@
|
||||
// Copyright 2021 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
#include "internal/network/url.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "protobuf-matchers/protocol-buffer-matchers.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace network {
|
||||
namespace {
|
||||
|
||||
TEST(Url, TestCreateUrl) {
|
||||
auto url = Url::Create("http://www.google.com");
|
||||
ASSERT_TRUE(url.ok());
|
||||
|
||||
EXPECT_EQ(url->GetScheme(), "http");
|
||||
EXPECT_EQ(url->GetPort(), 80);
|
||||
EXPECT_EQ(url->GetHostName(), "www.google.com");
|
||||
|
||||
url = Url::Create("https://www.google.com");
|
||||
ASSERT_TRUE(url.ok());
|
||||
EXPECT_EQ(url->GetScheme(), "https");
|
||||
EXPECT_EQ(url->GetPort(), 443);
|
||||
|
||||
url = Url::Create("https://www.google.com:8443");
|
||||
ASSERT_TRUE(url.ok());
|
||||
EXPECT_EQ(url->GetPort(), 8443);
|
||||
EXPECT_EQ(url->GetUrlPath(), "https://www.google.com:8443");
|
||||
}
|
||||
|
||||
TEST(Url, TestUrlWithPath) {
|
||||
auto url = Url::Create("http://www.google.com/users/user/1234");
|
||||
ASSERT_TRUE(url.ok());
|
||||
EXPECT_EQ(url->GetPath(), "/users/user/1234");
|
||||
}
|
||||
|
||||
TEST(Url, TestUrlWithQuery) {
|
||||
auto url = Url::Create("http://www.google.com/user?name=google&age=45");
|
||||
ASSERT_TRUE(url.ok());
|
||||
auto query_values = url->GetQueryValues("name");
|
||||
EXPECT_THAT(query_values, testing::SizeIs(1));
|
||||
EXPECT_EQ(query_values[0], "google");
|
||||
}
|
||||
|
||||
TEST(Url, TestUrlWithOneQuery) {
|
||||
auto url = Url::Create("http://www.google.com/user?name=google&");
|
||||
ASSERT_TRUE(url.ok());
|
||||
auto query_values = url->GetQueryValues("name");
|
||||
EXPECT_EQ(1u, query_values.size());
|
||||
EXPECT_EQ(query_values[0], "google");
|
||||
}
|
||||
|
||||
TEST(Url, TestUrlWithFragment) {
|
||||
auto url =
|
||||
Url::Create("http://www.google.com/user?name=google&pos=mtv#hello");
|
||||
ASSERT_TRUE(url.ok());
|
||||
auto query_values = url->GetQueryValues("pos");
|
||||
EXPECT_EQ(1u, query_values.size());
|
||||
EXPECT_EQ(query_values[0], "mtv");
|
||||
EXPECT_EQ(url->GetUrlPath(),
|
||||
"http://www.google.com/user?name=google&pos=mtv#hello");
|
||||
url->RemoveQueryParameter("pos");
|
||||
EXPECT_EQ(0u, url->GetQueryValues("pos").size());
|
||||
}
|
||||
|
||||
TEST(Url, TestGetUrl) {
|
||||
auto url = Url::Create("http://www.google.com/user?name=google&pos=mtv");
|
||||
ASSERT_TRUE(url.ok());
|
||||
EXPECT_EQ(url->GetUrlPath(),
|
||||
"http://www.google.com/user?name=google&pos=mtv");
|
||||
}
|
||||
|
||||
TEST(Url, TestAddQueryString) {
|
||||
auto url = Url::Create("http://www.google.com/user?name=google&pos=mtv");
|
||||
ASSERT_TRUE(url.ok());
|
||||
url->AddQueryParameter("test", "good");
|
||||
auto new_url = Url::Create(url->GetUrlPath());
|
||||
ASSERT_TRUE(new_url.ok());
|
||||
auto query_values = new_url->GetQueryValues("test");
|
||||
EXPECT_EQ(1u, query_values.size());
|
||||
EXPECT_EQ(query_values[0], "good");
|
||||
}
|
||||
|
||||
TEST(Url, TestAddQueryStringWithEncode) {
|
||||
auto url = Url::Create("http://www.google.com");
|
||||
ASSERT_TRUE(url.ok());
|
||||
url->AddQueryParameter("test", "good & bad");
|
||||
EXPECT_EQ(url->GetUrlPath(), "http://www.google.com?test=good%20%26%20bad");
|
||||
auto new_url = Url::Create(url->GetUrlPath());
|
||||
ASSERT_TRUE(new_url.ok());
|
||||
auto query_values = new_url->GetQueryValues("test");
|
||||
EXPECT_EQ(1u, query_values.size());
|
||||
EXPECT_EQ(query_values[0], "good & bad");
|
||||
}
|
||||
|
||||
TEST(Url, TestInvalidUrl) {
|
||||
auto url = Url::Create("http:/www.google.com");
|
||||
ASSERT_FALSE(url.ok());
|
||||
url = Url::Create("ftp://www.google.com");
|
||||
ASSERT_FALSE(url.ok());
|
||||
url = Url::Create("::::hjskoiskjk");
|
||||
ASSERT_FALSE(url.ok());
|
||||
}
|
||||
|
||||
TEST(Url, TestStreamOutput) {
|
||||
std::ostringstream stream;
|
||||
auto url = Url::Create("https://www.google.com");
|
||||
ASSERT_TRUE(url.ok());
|
||||
stream << url.value();
|
||||
EXPECT_EQ(stream.str(), "https://www.google.com");
|
||||
}
|
||||
|
||||
TEST(Url, TestCompareUrl) {
|
||||
auto url1 = Url::Create("https://www.google.com");
|
||||
auto url2 = Url::Create("https://www.google.com/");
|
||||
ASSERT_TRUE(url1.ok());
|
||||
ASSERT_TRUE(url2.ok());
|
||||
ASSERT_EQ(url1, url2);
|
||||
url1 = Url::Create("https://www.google.com/home?name=test&age=38");
|
||||
ASSERT_NE(url1, url2);
|
||||
url2 = Url::Create("https://www.google.com/home?age=38&name=test");
|
||||
ASSERT_EQ(url1, url2);
|
||||
url2 = Url::Create("https://www.google.com/home?age=38&name=test&grade=5");
|
||||
ASSERT_NE(url1, url2);
|
||||
url2 = Url::Create("https://www.google.com/home?age=38&name=test#fragment");
|
||||
ASSERT_NE(url1, url2);
|
||||
url2 = Url::Create("https://www.google.com/home?age1=38&name1=test");
|
||||
ASSERT_NE(url1, url2);
|
||||
url2 = Url::Create("http://www.google.com/home?age1=38&name1=test");
|
||||
ASSERT_NE(url1, url2);
|
||||
url2 = Url::Create("https://www.google1.com/home?age1=38&name1=test");
|
||||
ASSERT_NE(url1, url2);
|
||||
url2 = Url::Create("https://www.google.com:8433/home?age=38&name=test");
|
||||
ASSERT_NE(url1, url2);
|
||||
url2 = Url::Create("https://www.google.com/home?age=38&age=38");
|
||||
ASSERT_NE(url2, url1);
|
||||
url2 = Url::Create("https://www.google.com/home?age=38&name=test&kk&=888");
|
||||
ASSERT_EQ(url1, url2);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace network
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -0,0 +1,92 @@
|
||||
// Copyright 2022 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#include "internal/network/utils.h"
|
||||
|
||||
#include <cctype>
|
||||
#include <iomanip>
|
||||
#include <ios>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace network {
|
||||
|
||||
// Encodes string to URL-encoded string.
|
||||
//
|
||||
// If a character c is a decimal digit, an uppercase or lowercase letter, or in
|
||||
// [-_.!~*'()] then nothing is done; otherwise c is encoded as %XX where XX is
|
||||
// the hex value of the character c.
|
||||
std::string UrlEncode(absl::string_view str) {
|
||||
std::ostringstream encoded_string_stream;
|
||||
encoded_string_stream.fill(0);
|
||||
encoded_string_stream << std::hex;
|
||||
auto it = str.begin();
|
||||
while (it != str.end()) {
|
||||
char ch = (*it);
|
||||
if (std::isalnum(ch) || ch == '-' || ch == '_' || ch == '.' || ch == '!' ||
|
||||
ch == '~' || ch == '*' || ch == '\'' || ch == '(' || ch == ')') {
|
||||
encoded_string_stream << ch;
|
||||
} else {
|
||||
encoded_string_stream << std::uppercase;
|
||||
encoded_string_stream << '%' << std::setw(2) << (ch & 0xFF);
|
||||
encoded_string_stream << std::nouppercase;
|
||||
}
|
||||
|
||||
++it;
|
||||
}
|
||||
|
||||
return encoded_string_stream.str();
|
||||
}
|
||||
|
||||
// Decodes URL string to normal string.
|
||||
//
|
||||
// Does the reverse operation comparing to UrlEncode.
|
||||
// More details can refer to https://www.ietf.org/rfc/rfc2396.txt
|
||||
std::string UrlDecode(absl::string_view url_string) {
|
||||
std::ostringstream decoded_string_stream;
|
||||
decoded_string_stream.fill(0);
|
||||
decoded_string_stream << std::hex;
|
||||
auto it = url_string.begin();
|
||||
|
||||
while (it != url_string.end()) {
|
||||
char ch = (*it);
|
||||
|
||||
if (ch == '%') {
|
||||
if (it + 1 != url_string.end() && it + 2 != url_string.end()) {
|
||||
char chh = *(it + 1);
|
||||
char chl = *(it + 2);
|
||||
ch = (chh >= '0' && chh <= '9' ? chh - '0'
|
||||
: std::tolower(chh) - 'a' + 10)
|
||||
<< 4 |
|
||||
(chl >= '0' && chl <= '9' ? chl - '0'
|
||||
: std::tolower(chl) - 'a' + 10);
|
||||
decoded_string_stream << ch;
|
||||
it += 3;
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
decoded_string_stream << ch;
|
||||
}
|
||||
|
||||
++it;
|
||||
}
|
||||
|
||||
return decoded_string_stream.str();
|
||||
}
|
||||
|
||||
} // namespace network
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -0,0 +1,45 @@
|
||||
// Copyright 2022 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#ifndef THIRD_PARTY_NEARBY_INTERNAL_NETWORK_UTILS_H_
|
||||
#define THIRD_PARTY_NEARBY_INTERNAL_NETWORK_UTILS_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "internal/network/url.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace network {
|
||||
|
||||
// Encodes string to URL-encoded format. URLs can only be sent over the Internet
|
||||
// using the ASCII character-set. This method converts characters into a format
|
||||
// that can be transmitted over the Internet.
|
||||
//
|
||||
// @param str The string to be encoded.
|
||||
// @return Encoded URL string from input.
|
||||
std::string UrlEncode(absl::string_view str);
|
||||
|
||||
// Decodes URL string to normal string.
|
||||
//
|
||||
// @param url_string URL string to be decoded.
|
||||
// @return Decoded string.
|
||||
std::string UrlDecode(absl::string_view url_string);
|
||||
|
||||
} // namespace network
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // THIRD_PARTY_NEARBY_INTERNAL_NETWORK_UTILS_H_
|
||||
@@ -0,0 +1,48 @@
|
||||
// Copyright 2022 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#include "internal/network/utils.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace network {
|
||||
namespace {
|
||||
|
||||
TEST(UrlEncode, TestEncode) {
|
||||
EXPECT_EQ(UrlEncode("abc12&()*t"), "abc12%26()*t");
|
||||
}
|
||||
|
||||
TEST(UrlDecode, TestDecode) {
|
||||
EXPECT_EQ(UrlDecode("abc12%26()*t"), "abc12&()*t");
|
||||
}
|
||||
|
||||
TEST(UrlDecode, TestDecodeMatch) {
|
||||
std::string encode =
|
||||
UrlEncode("fsfs 1892427891$^%$*#$@ZDRIYIUHiheiuah bfd~+_()[]\\|©");
|
||||
std::string decode = UrlDecode(encode);
|
||||
|
||||
EXPECT_EQ(decode, "fsfs 1892427891$^%$*#$@ZDRIYIUHiheiuah bfd~+_()[]\\|©");
|
||||
EXPECT_EQ(encode,
|
||||
"fsfs%201892427891%24%5E%25%24*%23%24%40ZDRIYIUHiheiuah%20bfd~%2B_("
|
||||
")%5B%5D%5C%7C%C2%A9");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace network
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -100,7 +100,9 @@ cc_library(
|
||||
visibility = [
|
||||
"//connections:__subpackages__",
|
||||
"//fastpair:__subpackages__",
|
||||
"//internal:__pkg__",
|
||||
"//internal/crypto:__subpackages__",
|
||||
"//internal/network:__subpackages__",
|
||||
"//internal/platform:__subpackages__",
|
||||
"//internal/proto/analytics:__subpackages__",
|
||||
"//location/nearby/analytics/cpp:__subpackages__",
|
||||
@@ -393,11 +395,11 @@ cc_library(
|
||||
"//presence:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
":base",
|
||||
":cancellation_flag",
|
||||
":logging",
|
||||
":types",
|
||||
":uuid",
|
||||
"//internal/platform:base",
|
||||
"//internal/platform:cancellation_flag",
|
||||
"//internal/platform/implementation:comm",
|
||||
"//internal/platform/implementation:platform",
|
||||
"@com_google_absl//absl/container:flat_hash_map",
|
||||
|
||||
@@ -59,6 +59,7 @@ cc_library(
|
||||
"bluetooth_classic.h",
|
||||
"credential_callbacks.h",
|
||||
"credential_storage.h",
|
||||
"http_loader.h",
|
||||
"server_sync.h",
|
||||
"wifi.h",
|
||||
"wifi_direct.h",
|
||||
@@ -74,6 +75,8 @@ cc_library(
|
||||
defines = ["NO_WEBRTC"],
|
||||
visibility = [
|
||||
"//connections/implementation:__subpackages__",
|
||||
"//internal:__pkg__",
|
||||
"//internal/network:__subpackages__",
|
||||
"//internal/platform:__pkg__",
|
||||
"//internal/platform/implementation:__subpackages__",
|
||||
"//presence:__pkg__",
|
||||
@@ -88,6 +91,7 @@ cc_library(
|
||||
"@com_google_absl//absl/container:flat_hash_map",
|
||||
"@com_google_absl//absl/functional:any_invocable",
|
||||
"@com_google_absl//absl/status",
|
||||
"@com_google_absl//absl/status:statusor",
|
||||
"@com_google_absl//absl/strings",
|
||||
"@com_google_absl//absl/strings:str_format",
|
||||
],
|
||||
@@ -101,15 +105,20 @@ cc_library(
|
||||
defines = ["NO_WEBRTC"],
|
||||
visibility = [
|
||||
"//connections/implementation:__subpackages__",
|
||||
"//fastpair:__subpackages__",
|
||||
"//googlemac/iPhone/Shared/Nearby/Connections:__subpackages__",
|
||||
"//internal:__pkg__",
|
||||
"//internal/network:__subpackages__",
|
||||
"//internal/platform:__pkg__",
|
||||
"//internal/platform/implementation:__subpackages__",
|
||||
"//location/nearby/analytics/cpp:__subpackages__",
|
||||
"//location/nearby/cpp/fastpair:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
":comm",
|
||||
":types",
|
||||
"//internal/platform:base",
|
||||
"@com_google_absl//absl/status:statusor",
|
||||
"@com_google_absl//absl/strings",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "internal/platform/implementation/platform.h"
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
#include "internal/platform/implementation/apple/atomic_boolean.h"
|
||||
#include "internal/platform/implementation/apple/atomic_uint32.h"
|
||||
@@ -170,6 +171,12 @@ std::unique_ptr<WifiDirectMedium> ImplementationPlatform::CreateWifiDirectMedium
|
||||
std::unique_ptr<WebRtcMedium> ImplementationPlatform::CreateWebRtcMedium() { return nullptr; }
|
||||
#endif
|
||||
|
||||
// TODO(b/261511669): Add implementation.
|
||||
absl::StatusOr<WebResponse> ImplementationPlatform::SendRequest(
|
||||
const WebRequest& request) {
|
||||
return absl::UnimplementedError("");
|
||||
}
|
||||
|
||||
} // namespace api
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
@@ -122,7 +122,9 @@ cc_library(
|
||||
visibility = [
|
||||
"//connections:__subpackages__",
|
||||
"//fastpair:__subpackages__",
|
||||
"//internal:__pkg__",
|
||||
"//internal/analytics:__subpackages__",
|
||||
"//internal/network:__subpackages__",
|
||||
"//internal/platform:__subpackages__",
|
||||
"//internal/proto/analytics:__subpackages__",
|
||||
"//location/nearby/cpp/fastpair:__subpackages__",
|
||||
@@ -140,7 +142,9 @@ cc_library(
|
||||
"//internal/platform/implementation/shared:count_down_latch",
|
||||
"//internal/platform/implementation/shared:file",
|
||||
"@com_google_absl//absl/base:core_headers",
|
||||
"@com_google_absl//absl/container:flat_hash_map",
|
||||
"@com_google_absl//absl/memory",
|
||||
"@com_google_absl//absl/status:statusor",
|
||||
"@com_google_absl//absl/strings",
|
||||
"@com_google_absl//absl/time",
|
||||
],
|
||||
|
||||
@@ -21,7 +21,9 @@
|
||||
|
||||
#include "file/base/path.h"
|
||||
#include "absl/memory/memory.h"
|
||||
#include "absl/status/statusor.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/time/time.h"
|
||||
#include "internal/platform/implementation/atomic_boolean.h"
|
||||
#include "internal/platform/implementation/atomic_reference.h"
|
||||
@@ -205,6 +207,11 @@ std::unique_ptr<WebRtcMedium> ImplementationPlatform::CreateWebRtcMedium() {
|
||||
}
|
||||
#endif
|
||||
|
||||
absl::StatusOr<WebResponse> ImplementationPlatform::SendRequest(
|
||||
const api::WebRequest& request) {
|
||||
return absl::UnimplementedError("");
|
||||
}
|
||||
|
||||
std::unique_ptr<Mutex> ImplementationPlatform::CreateMutex(Mutex::Mode mode) {
|
||||
if (mode == Mutex::Mode::kRecursive)
|
||||
return std::make_unique<g3::RecursiveMutex>();
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
// Copyright 2022 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#ifndef THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_HTTP_LOADER_H_
|
||||
#define THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_HTTP_LOADER_H_
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace api {
|
||||
|
||||
struct WebRequest {
|
||||
std::string url;
|
||||
std::string method;
|
||||
std::multimap<std::string, std::string> headers;
|
||||
std::string body;
|
||||
};
|
||||
|
||||
struct WebResponse {
|
||||
int status_code;
|
||||
std::string status_text;
|
||||
std::multimap<std::string, std::string> headers;
|
||||
std::string body;
|
||||
};
|
||||
|
||||
} // namespace api
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_HTTP_LOADER_H_
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "absl/status/statusor.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "internal/platform/implementation/atomic_boolean.h"
|
||||
#include "internal/platform/implementation/atomic_reference.h"
|
||||
@@ -30,6 +31,7 @@
|
||||
#include "internal/platform/implementation/count_down_latch.h"
|
||||
#include "internal/platform/implementation/credential_storage.h"
|
||||
#include "internal/platform/implementation/crypto.h"
|
||||
#include "internal/platform/implementation/http_loader.h"
|
||||
#include "internal/platform/implementation/input_file.h"
|
||||
#include "internal/platform/implementation/log_message.h"
|
||||
#include "internal/platform/implementation/mutex.h"
|
||||
@@ -130,6 +132,15 @@ class ImplementationPlatform {
|
||||
#ifndef NO_WEBRTC
|
||||
static std::unique_ptr<WebRtcMedium> CreateWebRtcMedium();
|
||||
#endif
|
||||
|
||||
// Gets HTTP response from remote server.
|
||||
//
|
||||
// @param request Webrequest
|
||||
//
|
||||
// @return returns absl::FailedPreconditionError if having platform error.
|
||||
// return WebResponse if HTTP status code between 200 and 300.
|
||||
// other cases will return absl Status in error.
|
||||
static absl::StatusOr<WebResponse> SendRequest(const WebRequest& request);
|
||||
};
|
||||
|
||||
} // namespace api
|
||||
|
||||
@@ -62,6 +62,7 @@ cc_library(
|
||||
"executor.h",
|
||||
"file.h",
|
||||
"file_path.h",
|
||||
"http_loader.h",
|
||||
"mutex.h",
|
||||
"scheduled_executor.h",
|
||||
"server_sync.h",
|
||||
@@ -84,6 +85,9 @@ cc_library(
|
||||
"@com_google_absl//absl/container:flat_hash_map",
|
||||
"@com_google_absl//absl/container:flat_hash_set",
|
||||
"@com_google_absl//absl/memory",
|
||||
"@com_google_absl//absl/status",
|
||||
"@com_google_absl//absl/status:statusor",
|
||||
"@com_google_absl//absl/strings",
|
||||
"@com_google_absl//absl/synchronization",
|
||||
"@com_google_absl//absl/types:optional",
|
||||
],
|
||||
@@ -128,6 +132,7 @@ cc_library(
|
||||
"executor.cc",
|
||||
"file.cc",
|
||||
"file_path.cc",
|
||||
"http_loader.cc",
|
||||
"platform.cc",
|
||||
"scheduled_executor.cc",
|
||||
"submittable_executor.cc",
|
||||
@@ -157,6 +162,7 @@ cc_library(
|
||||
":types",
|
||||
"//internal/platform:base",
|
||||
"//internal/platform:cancellation_flag",
|
||||
"//internal/platform:logging",
|
||||
"//internal/platform:types",
|
||||
"//internal/platform/implementation:platform",
|
||||
"//internal/platform/implementation:types",
|
||||
@@ -164,6 +170,8 @@ cc_library(
|
||||
"//internal/platform/implementation/shared:file",
|
||||
"//internal/platform/implementation/windows/generated:types",
|
||||
"//internal/platform/implementation/windows/json:types",
|
||||
"@com_google_absl//absl/container:flat_hash_map",
|
||||
"@com_google_absl//absl/status",
|
||||
"@com_google_absl//absl/strings",
|
||||
"@com_google_absl//absl/strings:str_format",
|
||||
],
|
||||
@@ -199,6 +207,7 @@ cc_test(
|
||||
"crypto_test.cc",
|
||||
"executor_test.cc",
|
||||
"file_path_test.cc",
|
||||
"http_loader_test.cc",
|
||||
"scheduled_executor_test.cc",
|
||||
"submittable_executor_test.cc",
|
||||
"thread_pool_test.cc",
|
||||
@@ -216,6 +225,8 @@ cc_test(
|
||||
"//internal/platform/implementation/windows",
|
||||
"//internal/platform/implementation/windows/generated:types",
|
||||
"@com_github_protobuf_matchers//protobuf-matchers",
|
||||
"@com_google_absl//absl/status",
|
||||
"@com_google_absl//absl/strings",
|
||||
"@com_google_googletest//:gtest_main",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -16,6 +16,7 @@ licenses(["notice"])
|
||||
cc_library(
|
||||
name = "types",
|
||||
linkopts = [
|
||||
"wininet.lib",
|
||||
"advapi32.lib",
|
||||
"comdlg32.lib",
|
||||
"gdi32.lib",
|
||||
|
||||
@@ -0,0 +1,413 @@
|
||||
// Copyright 2021 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#include "internal/platform/implementation/windows/http_loader.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/status/statusor.h"
|
||||
#include "absl/strings/ascii.h"
|
||||
#include "absl/strings/numbers.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "internal/platform/logging.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace windows {
|
||||
namespace {
|
||||
|
||||
constexpr DWORD kSchemaMaximumLength = 10;
|
||||
constexpr DWORD kHostNameMaximumLength = 256;
|
||||
|
||||
using ::location::nearby::api::WebResponse;
|
||||
|
||||
} // namespace
|
||||
|
||||
absl::StatusOr<WebResponse> HttpLoader::GetResponse() {
|
||||
absl::Status status;
|
||||
|
||||
status = ParseUrl();
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
status = ConnectWebServer();
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
// Sends request to web server.
|
||||
status = SendRequest();
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
// Processes response from web server
|
||||
absl::StatusOr<WebResponse> result = ProcessResponse();
|
||||
if (!result.ok()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
DisconnectWebServer();
|
||||
return result;
|
||||
}
|
||||
|
||||
absl::StatusOr<int> HttpLoader::QueryStatusCode(HINTERNET file_handle) {
|
||||
int result;
|
||||
std::string status_code;
|
||||
absl::Status status;
|
||||
status = QueryResponseInfo(file_handle, HTTP_QUERY_STATUS_CODE, status_code);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
if (!absl::SimpleAtoi(
|
||||
absl::string_view(status_code.data(), status_code.size() - 1),
|
||||
&result)) {
|
||||
return absl::InternalError("Failed to parse status code.");
|
||||
}
|
||||
|
||||
if (result < 0) {
|
||||
return absl::InternalError("Invalid status code.");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
absl::StatusOr<std::string> HttpLoader::QueryStatusText(
|
||||
HINTERNET request_handle) {
|
||||
absl::Status status;
|
||||
std::string status_text;
|
||||
|
||||
status =
|
||||
QueryResponseInfo(request_handle, HTTP_QUERY_STATUS_TEXT, status_text);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
// status_text is ended with char '\0', remove it in returned result.
|
||||
return status_text.substr(0, status_text.size() - 1);
|
||||
}
|
||||
|
||||
absl::StatusOr<std::multimap<std::string, std::string>>
|
||||
HttpLoader::QueryResponseHeaders(HINTERNET request_handle) {
|
||||
absl::Status status;
|
||||
std::string headers_string;
|
||||
|
||||
status = QueryResponseInfo(request_handle, HTTP_QUERY_RAW_HEADERS_CRLF,
|
||||
headers_string);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
std::multimap<std::string, std::string> headers;
|
||||
// Parse headers in response
|
||||
size_t start = 0;
|
||||
size_t pos = 0;
|
||||
while ((pos = headers_string.find("\r\n", start)) != std::string::npos) {
|
||||
std::string header = headers_string.substr(start, pos - start);
|
||||
// Get key and value in header
|
||||
size_t split_pos = 0;
|
||||
if ((split_pos = header.find(": ")) != std::string::npos) {
|
||||
std::string key = header.substr(0, split_pos);
|
||||
std::string value = header.substr(split_pos + 2);
|
||||
headers.emplace(key, value);
|
||||
}
|
||||
|
||||
start = pos + 2;
|
||||
}
|
||||
|
||||
return headers;
|
||||
}
|
||||
|
||||
absl::Status HttpLoader::QueryResponseInfo(HINTERNET request_handle,
|
||||
DWORD info_level,
|
||||
std::string& info) {
|
||||
DWORD buffer_size = 0;
|
||||
BOOL query_result = HttpQueryInfoA(request_handle, info_level, nullptr,
|
||||
&buffer_size, nullptr);
|
||||
|
||||
if (query_result) {
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
|
||||
info.resize(buffer_size);
|
||||
query_result = HttpQueryInfoA(request_handle, info_level, info.data(),
|
||||
&buffer_size, nullptr);
|
||||
|
||||
if (query_result) {
|
||||
return absl::OkStatus();
|
||||
}
|
||||
}
|
||||
|
||||
return absl::InvalidArgumentError("Failed to query HTTP information.");
|
||||
}
|
||||
|
||||
absl::Status HttpLoader::ParseUrl() {
|
||||
const std::string& url = request_.url;
|
||||
URL_COMPONENTSA url_components;
|
||||
char schema[kSchemaMaximumLength];
|
||||
char host_name[kHostNameMaximumLength];
|
||||
std::string url_path;
|
||||
url_path.resize(url.size());
|
||||
|
||||
memset(&url_components, 0, sizeof(URL_COMPONENTSA));
|
||||
url_components.dwStructSize = sizeof(URL_COMPONENTSA);
|
||||
url_components.lpszScheme = schema;
|
||||
url_components.dwSchemeLength = kSchemaMaximumLength;
|
||||
url_components.lpszHostName = host_name;
|
||||
url_components.dwHostNameLength = kHostNameMaximumLength;
|
||||
url_components.lpszUrlPath = url_path.data();
|
||||
url_components.dwUrlPathLength = url.size();
|
||||
|
||||
if (!InternetCrackUrlA(url.data(), url.size(), 0, &url_components)) {
|
||||
return absl::InvalidArgumentError("Invalid URL.");
|
||||
}
|
||||
|
||||
schema_.assign(url_components.lpszScheme, url_components.dwSchemeLength);
|
||||
|
||||
if (!(schema_ == "http" || schema_ == "https")) {
|
||||
return absl::InvalidArgumentError("URL supports HTTP and HTTPS only.");
|
||||
}
|
||||
|
||||
if (schema_ == "https") {
|
||||
is_secure_ = true;
|
||||
}
|
||||
|
||||
host_.assign(url_components.lpszHostName, url_components.dwHostNameLength);
|
||||
path_.assign(url_components.lpszUrlPath, url_components.dwUrlPathLength);
|
||||
port_ = url_components.nPort;
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status HttpLoader::ConnectWebServer() {
|
||||
internet_handle_ = InternetOpenA("Mozilla/5.0", /*Agent*/
|
||||
INTERNET_OPEN_TYPE_PRECONFIG, /*Access Type*/
|
||||
nullptr, /*Proxy*/
|
||||
nullptr, /*Proxy bypass*/
|
||||
0); /*Flags*/
|
||||
|
||||
if (internet_handle_ == nullptr) {
|
||||
NEARBY_LOGS(ERROR) << "Failed to open internet with error "
|
||||
<< GetLastError() << ".";
|
||||
return absl::FailedPreconditionError(absl::StrCat(GetLastError()));
|
||||
}
|
||||
|
||||
connect_handle_ = InternetConnectA(internet_handle_, /*Internet*/
|
||||
host_.c_str(), /*Server name*/
|
||||
port_, /*Port*/
|
||||
nullptr, /*User name*/
|
||||
nullptr, /*Password*/
|
||||
INTERNET_SERVICE_HTTP, /*Service*/
|
||||
0, /*Flags*/
|
||||
0); /*Context*/
|
||||
|
||||
if (connect_handle_ == nullptr) {
|
||||
NEARBY_LOGS(ERROR) << "Failed to connect remote web server with error "
|
||||
<< GetLastError() << ".";
|
||||
InternetCloseHandle(internet_handle_);
|
||||
return absl::FailedPreconditionError(absl::StrCat(GetLastError()));
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status HttpLoader::SendRequest() {
|
||||
DWORD flags = INTERNET_FLAG_NO_AUTO_REDIRECT;
|
||||
if (is_secure_) {
|
||||
flags |= INTERNET_FLAG_SECURE;
|
||||
}
|
||||
|
||||
request_handle_ =
|
||||
HttpOpenRequestA(connect_handle_, request_.method.c_str(), /*Method*/
|
||||
path_.c_str(), /*Path*/
|
||||
nullptr, /*HTTP version*/
|
||||
nullptr, /*Referrer*/
|
||||
nullptr, /*Accept types*/
|
||||
flags, /*Internet options*/
|
||||
0);
|
||||
|
||||
if (request_handle_ == nullptr) {
|
||||
NEARBY_LOGS(ERROR)
|
||||
<< "Failed to open request to remote web server with error "
|
||||
<< GetLastError() << ".";
|
||||
InternetCloseHandle(internet_handle_);
|
||||
InternetCloseHandle(connect_handle_);
|
||||
|
||||
return absl::FailedPreconditionError(absl::StrCat(GetLastError()));
|
||||
}
|
||||
|
||||
// Prepare headers
|
||||
LPCSTR headers_ptr = nullptr;
|
||||
DWORD headers_size = 0;
|
||||
std::string request_headers;
|
||||
for (const auto& header : request_.headers) {
|
||||
if (header.second.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
request_headers.append(header.first + ": " + header.second + "\r\n");
|
||||
}
|
||||
|
||||
if (!request_headers.empty()) {
|
||||
headers_ptr = request_headers.data();
|
||||
headers_size = request_headers.size();
|
||||
}
|
||||
|
||||
LPVOID data_ptr = nullptr;
|
||||
DWORD data_size = 0;
|
||||
|
||||
// Prepare request data
|
||||
if (!request_.body.empty()) {
|
||||
data_ptr = (LPVOID)(request_.body.data());
|
||||
data_size = request_.body.size();
|
||||
}
|
||||
|
||||
BOOL result = HttpSendRequestA(request_handle_, /*Request*/
|
||||
headers_ptr, /*Headers*/
|
||||
headers_size, /*Header size*/
|
||||
data_ptr, /*Data*/
|
||||
data_size); /*Data size*/
|
||||
|
||||
if (result == FALSE) {
|
||||
NEARBY_LOGS(ERROR)
|
||||
<< "Failed to send request to remote web server with error "
|
||||
<< GetLastError() << ".";
|
||||
InternetCloseHandle(request_handle_);
|
||||
InternetCloseHandle(connect_handle_);
|
||||
InternetCloseHandle(internet_handle_);
|
||||
|
||||
return absl::FailedPreconditionError(absl::StrCat(GetLastError()));
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::StatusOr<WebResponse> HttpLoader::ProcessResponse() {
|
||||
absl::Status status;
|
||||
WebResponse web_response;
|
||||
auto status_code = QueryStatusCode(request_handle_);
|
||||
if (!status_code.ok()) {
|
||||
return absl::InternalError("Failed to read HTTP status");
|
||||
}
|
||||
|
||||
web_response.status_code = status_code.value();
|
||||
auto status_text = QueryStatusText(request_handle_);
|
||||
if (!status_text.ok()) {
|
||||
return absl::InternalError("Failed to read HTTP status");
|
||||
}
|
||||
|
||||
web_response.status_text = status_text.value();
|
||||
auto headers = QueryResponseHeaders(request_handle_);
|
||||
if (!headers.ok()) {
|
||||
headers.status();
|
||||
}
|
||||
web_response.headers = *headers;
|
||||
|
||||
// Get response data
|
||||
char buffer[kReceiveBufferSize];
|
||||
|
||||
while (true) {
|
||||
DWORD read_size;
|
||||
BOOL read_result = InternetReadFile(request_handle_, buffer,
|
||||
kReceiveBufferSize, &read_size);
|
||||
|
||||
if (read_result) {
|
||||
if (read_size == 0) {
|
||||
break;
|
||||
} else {
|
||||
// Append data to response
|
||||
web_response.body.append(buffer, read_size);
|
||||
}
|
||||
} else {
|
||||
NEARBY_LOGS(ERROR)
|
||||
<< "Failed to read response from remote web server with error "
|
||||
<< GetLastError() << ".";
|
||||
InternetCloseHandle(request_handle_);
|
||||
InternetCloseHandle(connect_handle_);
|
||||
InternetCloseHandle(internet_handle_);
|
||||
return absl::FailedPreconditionError(absl::StrCat(GetLastError()));
|
||||
}
|
||||
}
|
||||
|
||||
status = HTTPCodeToStatus(web_response.status_code, web_response.status_text);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
return web_response;
|
||||
}
|
||||
|
||||
void HttpLoader::DisconnectWebServer() {
|
||||
if (request_handle_ != nullptr) {
|
||||
InternetCloseHandle(request_handle_);
|
||||
request_handle_ = nullptr;
|
||||
}
|
||||
|
||||
if (connect_handle_ != nullptr) {
|
||||
InternetCloseHandle(connect_handle_);
|
||||
connect_handle_ = nullptr;
|
||||
}
|
||||
|
||||
if (internet_handle_ != nullptr) {
|
||||
InternetCloseHandle(internet_handle_);
|
||||
internet_handle_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
absl::Status HttpLoader::HTTPCodeToStatus(int status_code,
|
||||
absl::string_view status_message) {
|
||||
switch (status_code) {
|
||||
case 400:
|
||||
return absl::InvalidArgumentError(status_message);
|
||||
case 401:
|
||||
return absl::UnauthenticatedError(status_message);
|
||||
case 403:
|
||||
return absl::PermissionDeniedError(status_message);
|
||||
case 404:
|
||||
return absl::NotFoundError(status_message);
|
||||
case 409:
|
||||
return absl::AbortedError(status_message);
|
||||
case 416:
|
||||
return absl::OutOfRangeError(status_message);
|
||||
case 429:
|
||||
return absl::ResourceExhaustedError(status_message);
|
||||
case 499:
|
||||
return absl::CancelledError(status_message);
|
||||
case 504:
|
||||
return absl::DeadlineExceededError(status_message);
|
||||
case 501:
|
||||
return absl::UnimplementedError(status_message);
|
||||
case 503:
|
||||
return absl::UnavailableError(status_message);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (status_code >= 200 && status_code < 300) {
|
||||
return absl::OkStatus();
|
||||
} else if (status_code >= 400 && status_code < 500) {
|
||||
return absl::FailedPreconditionError(status_message);
|
||||
} else if (status_code >= 500 && status_code < 600) {
|
||||
return absl::InternalError(status_message);
|
||||
}
|
||||
return absl::UnknownError(status_message);
|
||||
}
|
||||
|
||||
} // namespace windows
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -0,0 +1,87 @@
|
||||
// Copyright 2022 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#ifndef THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_WINDOWS_HTTP_LOADER_H_
|
||||
#define THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_WINDOWS_HTTP_LOADER_H_
|
||||
|
||||
#include <windows.h> // NOLINT
|
||||
#include <wininet.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/status/statusor.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "internal/platform/implementation/http_loader.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace windows {
|
||||
|
||||
// HttpLoader is used to get HTTP response from remote server.
|
||||
//
|
||||
// HttpLoader gets HTTP request information from caller, and calling Windows
|
||||
// WinInet APIs to get HTTP response. The platform handles HTTP/HTTPS sessions.
|
||||
class HttpLoader {
|
||||
public:
|
||||
explicit HttpLoader(const location::nearby::api::WebRequest& request)
|
||||
: request_(request) {}
|
||||
~HttpLoader() = default;
|
||||
|
||||
absl::StatusOr<location::nearby::api::WebResponse> GetResponse();
|
||||
|
||||
private:
|
||||
// Defines the buffer size. It is used to init a buffer for receiving HTTP
|
||||
// response. The unit is byte.
|
||||
static constexpr int kReceiveBufferSize = 8 * 1024;
|
||||
|
||||
absl::Status ConnectWebServer();
|
||||
absl::Status SendRequest();
|
||||
absl::StatusOr<location::nearby::api::WebResponse> ProcessResponse();
|
||||
void DisconnectWebServer();
|
||||
|
||||
absl::StatusOr<int> QueryStatusCode(HINTERNET file_handle);
|
||||
absl::StatusOr<std::string> QueryStatusText(HINTERNET request_handle);
|
||||
absl::StatusOr<std::multimap<std::string, std::string>> QueryResponseHeaders(
|
||||
HINTERNET request_handle);
|
||||
absl::Status QueryResponseInfo(HINTERNET request_handle, DWORD info_level,
|
||||
std::string& info);
|
||||
|
||||
absl::Status ParseUrl();
|
||||
|
||||
// Converts HTTP status code to absl Status.
|
||||
//
|
||||
// @param status_code HTTP status code, such 200, 404 etc.
|
||||
// @param status_message short description of the status code.
|
||||
// @return converted absl status.
|
||||
absl::Status HTTPCodeToStatus(int status_code,
|
||||
absl::string_view status_message);
|
||||
|
||||
location::nearby::api::WebRequest request_;
|
||||
std::string host_;
|
||||
std::string path_;
|
||||
std::string schema_;
|
||||
bool is_secure_ = false;
|
||||
int port_ = 80;
|
||||
|
||||
HINTERNET internet_handle_ = nullptr;
|
||||
HINTERNET connect_handle_ = nullptr;
|
||||
HINTERNET request_handle_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace windows
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_WINDOWS_HTTP_LOADER_H_
|
||||
@@ -0,0 +1,53 @@
|
||||
// Copyright 2022 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#include "internal/platform/implementation/windows/http_loader.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace windows {
|
||||
namespace {
|
||||
using ::location::nearby::api::WebRequest;
|
||||
|
||||
TEST(HttpLoader, DISABLED_TestGetUrl) {
|
||||
WebRequest request;
|
||||
request.url = "https://www.google.com?id=456#fragment";
|
||||
request.method = "GET";
|
||||
auto response = HttpLoader(request).GetResponse();
|
||||
ASSERT_TRUE(response.ok());
|
||||
EXPECT_EQ(response->status_code, 200);
|
||||
}
|
||||
|
||||
TEST(HttpLoader, DISABLED_TestGetNotExistingUrl) {
|
||||
WebRequest request;
|
||||
request.url = "https://www.abcdefgabcdefg.com";
|
||||
request.method = "GET";
|
||||
EXPECT_FALSE(HttpLoader(request).GetResponse().ok());
|
||||
}
|
||||
|
||||
TEST(HttpLoader, DISABLED_TestInvalidUrl) {
|
||||
WebRequest request;
|
||||
request.url = "https:/www.abcdefgabcdefg.com/name?id=456";
|
||||
request.method = "GET";
|
||||
EXPECT_FALSE(HttpLoader(request).GetResponse().ok());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace windows
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -33,6 +33,9 @@
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include "absl/status/statusor.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "internal/platform/implementation/http_loader.h"
|
||||
#include "internal/platform/implementation/shared/count_down_latch.h"
|
||||
#include "internal/platform/implementation/windows/atomic_boolean.h"
|
||||
#include "internal/platform/implementation/windows/atomic_reference.h"
|
||||
@@ -45,6 +48,7 @@
|
||||
#include "internal/platform/implementation/windows/file.h"
|
||||
#include "internal/platform/implementation/windows/file_path.h"
|
||||
#include "internal/platform/implementation/windows/future.h"
|
||||
#include "internal/platform/implementation/windows/http_loader.h"
|
||||
#include "internal/platform/implementation/windows/listenable_future.h"
|
||||
#include "internal/platform/implementation/windows/log_message.h"
|
||||
#include "internal/platform/implementation/windows/mutex.h"
|
||||
@@ -298,6 +302,12 @@ std::unique_ptr<WebRtcMedium> ImplementationPlatform::CreateWebRtcMedium() {
|
||||
return absl::make_unique<windows::WebRtcMedium>();
|
||||
}
|
||||
|
||||
absl::StatusOr<WebResponse> ImplementationPlatform::SendRequest(
|
||||
const WebRequest& request) {
|
||||
windows::HttpLoader http_loader{request};
|
||||
return http_loader.GetResponse();
|
||||
}
|
||||
|
||||
} // namespace api
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
Reference in New Issue
Block a user