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:
hai007
2022-12-28 10:28:32 -08:00
committed by Copybara-Service
parent 6ef8a4c00a
commit 88e350e5d2
38 changed files with 2926 additions and 2 deletions
@@ -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