Compare commits

..

11 Commits

Author SHA1 Message Date
klzgrad
49153d97ef Add continuous integration and tests 2022-05-22 01:46:04 +08:00
klzgrad
406dbcc176 Add build scripts 2022-05-22 01:46:04 +08:00
klzgrad
e54adb3445 Add example config.json 2022-05-22 01:46:04 +08:00
klzgrad
85c6cc1a3c Add README 2022-05-22 01:46:04 +08:00
klzgrad
26f7e7bf3e Add LICENSE 2022-05-22 01:46:04 +08:00
klzgrad
083699b5b1 Add source import tool 2022-05-22 01:46:04 +08:00
klzgrad
cf8c61a5ff Add Cronet CGO SDK builder
bidi_example.cc does not work in cross compile yet, because libstdc++-dev
is not installed in cross compile sysroots.
2022-05-22 01:46:03 +08:00
klzgrad
c17764fb27 Add initial implementation of Naive client 2022-05-22 01:43:32 +08:00
klzgrad
2a0e13ae53 net, grpc_support: Set NetworkIsolationKey from header
If BidirectionalStream request contains a -network-isolation-key
header, it is used to set the network isolation key of the stream.
The header itself is removed and not transmitted.

The header value should be a valid URL with different host and port
for each different network isolation key. Invalid header value is
reported by returning error from bidirectional_stream_start.

Network isolation takes effect only if it is enabled by experimental
option of

  "feature_list": {
    "enable-features": "PartitionConnectionsByNetworkIsolationKey"
  }
2022-05-22 01:43:32 +08:00
klzgrad
b39a709937 cronet: Support setting feature list from experimental option 2022-05-22 01:43:32 +08:00
klzgrad
0ddeea513d cronet: Use fixed proxy resolution from experimental option proxy_server 2022-05-22 01:43:32 +08:00
11 changed files with 115 additions and 19 deletions

View File

@ -111,6 +111,18 @@ if (false) {
configs -= [ "//build/config/compiler:thin_archive" ] configs -= [ "//build/config/compiler:thin_archive" ]
} }
executable("cronet_example") {
testonly = true
sources = [ "native/sample/bidi_example.cc" ]
deps = [
"//components/cronet",
"//components/cronet/native:cronet_native_headers",
]
if ((is_linux || is_chromeos) && !is_component_build) {
public_configs = [ "//build/config/gcc:rpath_for_built_shared_libraries" ]
}
}
executable("cronet_example_external") { executable("cronet_example_external") {
testonly = true testonly = true
no_default_deps = true no_default_deps = true

View File

@ -146,7 +146,8 @@ void PostTaskToInitThread(const base::Location& posted_from,
g_init_task_executor->task_runner()->PostTask(posted_from, std::move(task)); g_init_task_executor->task_runner()->PostTask(posted_from, std::move(task));
} }
void EnsureInitialized() { void EnsureInitialized(const char* /*enable_features*/,
const char* /*disable_features*/) {
if (g_init_task_executor) { if (g_init_task_executor) {
// Ensure that init is done on the init thread. // Ensure that init is done on the init thread.
g_init_thread_init_done.Wait(); g_init_thread_init_done.Wait();

View File

@ -357,9 +357,10 @@ CronetContext::NetworkTasks::BuildDefaultURLRequestContext(
SetSharedURLRequestContextBuilderConfig(&context_builder); SetSharedURLRequestContextBuilderConfig(&context_builder);
const auto proxy_server_it = const auto proxy_server_it =
context_config_->experimental_options.find("proxy_server"); context_config_->effective_experimental_options.find("proxy_server");
std::string proxy_server_str = "direct://"; std::string proxy_server_str = "direct://";
if (proxy_server_it != context_config_->experimental_options.end()) { if (proxy_server_it !=
context_config_->effective_experimental_options.end()) {
const base::Value& value = proxy_server_it->second; const base::Value& value = proxy_server_it->second;
if (value.is_string()) { if (value.is_string()) {
proxy_server_str = value.GetString(); proxy_server_str = value.GetString();

View File

@ -31,7 +31,8 @@ void PostTaskToInitThread(const base::Location& posted_from,
// or binding to an existing thread, to run initialization and process // or binding to an existing thread, to run initialization and process
// network notifications on. The implementation must be thread-safe and // network notifications on. The implementation must be thread-safe and
// idempotent, and must complete initialization before returning. // idempotent, and must complete initialization before returning.
void EnsureInitialized(); void EnsureInitialized(const char* enable_features = nullptr,
const char* disable_features = nullptr);
// Creates a proxy config service appropriate for this platform that fetches the // Creates a proxy config service appropriate for this platform that fetches the
// system proxy settings. Cronet will call this API only after a prior call // system proxy settings. Cronet will call this API only after a prior call

View File

@ -22,14 +22,18 @@ namespace cronet {
namespace { namespace {
scoped_refptr<base::SingleThreadTaskRunner> InitializeAndCreateTaskRunner() { scoped_refptr<base::SingleThreadTaskRunner> InitializeAndCreateTaskRunner(
const char* enable_features,
const char* disable_features) {
// Cronet tests sets AtExitManager as part of TestSuite, so statically linked // Cronet tests sets AtExitManager as part of TestSuite, so statically linked
// library is not allowed to set its own. // library is not allowed to set its own.
#if !defined(CRONET_TESTS_IMPLEMENTATION) #if !defined(CRONET_TESTS_IMPLEMENTATION)
std::ignore = new base::AtExitManager; std::ignore = new base::AtExitManager;
#endif #endif
base::FeatureList::InitializeInstance(std::string(), std::string()); base::FeatureList::InitializeInstance(
enable_features ? enable_features : "",
disable_features ? disable_features : "");
// Note that in component builds this ThreadPoolInstance will be shared with // Note that in component builds this ThreadPoolInstance will be shared with
// the calling process, if it also depends on //base. In particular this means // the calling process, if it also depends on //base. In particular this means
@ -40,16 +44,19 @@ scoped_refptr<base::SingleThreadTaskRunner> InitializeAndCreateTaskRunner() {
return base::ThreadPool::CreateSingleThreadTaskRunner({}); return base::ThreadPool::CreateSingleThreadTaskRunner({});
} }
base::SingleThreadTaskRunner* InitTaskRunner() { base::SingleThreadTaskRunner* InitTaskRunner(
const char* enable_features = nullptr,
const char* disable_features = nullptr) {
static scoped_refptr<base::SingleThreadTaskRunner> init_task_runner = static scoped_refptr<base::SingleThreadTaskRunner> init_task_runner =
InitializeAndCreateTaskRunner(); InitializeAndCreateTaskRunner(enable_features, disable_features);
return init_task_runner.get(); return init_task_runner.get();
} }
} // namespace } // namespace
void EnsureInitialized() { void EnsureInitialized(const char* enable_features,
std::ignore = InitTaskRunner(); const char* disable_features) {
std::ignore = InitTaskRunner(enable_features, disable_features);
} }
bool OnInitThread() { bool OnInitThread() {

View File

@ -7,6 +7,7 @@
#include <unordered_set> #include <unordered_set>
#include <utility> #include <utility>
#include "base/base_switches.h"
#include "base/bind.h" #include "base/bind.h"
#include "base/callback_helpers.h" #include "base/callback_helpers.h"
#include "base/files/file_path.h" #include "base/files/file_path.h"
@ -102,6 +103,25 @@ Cronet_EngineImpl::~Cronet_EngineImpl() {
Cronet_RESULT Cronet_EngineImpl::StartWithParams( Cronet_RESULT Cronet_EngineImpl::StartWithParams(
Cronet_EngineParamsPtr params) { Cronet_EngineParamsPtr params) {
absl::optional<base::Value::DictStorage> experimental_options =
URLRequestContextConfig::ParseExperimentalOptions(
params->experimental_options);
if (experimental_options) {
const auto& iter = experimental_options->find("feature_list");
if (iter != experimental_options->end()) {
const base::Value& feature_list = iter->second;
if (feature_list.is_dict()) {
const std::string* enable_features =
feature_list.GetDict().FindString(switches::kEnableFeatures);
const std::string* disable_features =
feature_list.GetDict().FindString(switches::kDisableFeatures);
cronet::EnsureInitialized(
enable_features ? enable_features->c_str() : nullptr,
disable_features ? disable_features->c_str() : nullptr);
}
}
}
cronet::EnsureInitialized(); cronet::EnsureInitialized();
base::AutoLock lock(lock_); base::AutoLock lock(lock_);

View File

@ -107,6 +107,13 @@ Cronet_EnginePtr CreateCronetEngine() {
Cronet_EnginePtr cronet_engine = Cronet_Engine_Create(); Cronet_EnginePtr cronet_engine = Cronet_Engine_Create();
Cronet_EngineParamsPtr engine_params = Cronet_EngineParams_Create(); Cronet_EngineParamsPtr engine_params = Cronet_EngineParams_Create();
Cronet_EngineParams_user_agent_set(engine_params, "Cronet"); Cronet_EngineParams_user_agent_set(engine_params, "Cronet");
Cronet_EngineParams_experimental_options_set(engine_params, R"({
"ssl_key_log_file": "/tmp/keys",
"feature_list": {
"enable-features": "PartitionConnectionsByNetworkIsolationKey"
},
"proxy_server": "socks5://127.0.0.1:1080"
})");
Cronet_Engine_StartWithParams(cronet_engine, engine_params); Cronet_Engine_StartWithParams(cronet_engine, engine_params);
Cronet_EngineParams_Destroy(engine_params); Cronet_EngineParams_Destroy(engine_params);
return cronet_engine; return cronet_engine;
@ -122,17 +129,26 @@ int main(int argc, const char* argv[]) {
stream_engine* cronet_stream_engine = stream_engine* cronet_stream_engine =
Cronet_Engine_GetStreamEngine(cronet_engine); Cronet_Engine_GetStreamEngine(cronet_engine);
Cronet_Engine_StartNetLogToFile(cronet_engine, "/tmp/log.json", true);
BidirectionalStreamCallback stream_callback; BidirectionalStreamCallback stream_callback;
stream_callback.stream = bidirectional_stream_create( stream_callback.stream = bidirectional_stream_create(
cronet_stream_engine, &stream_callback, stream_callback.callback()); cronet_stream_engine, &stream_callback, stream_callback.callback());
bidirectional_stream_start(stream_callback.stream, url, 0, "GET", nullptr,
true); bidirectional_stream_header headers[] = {
{"-network-isolation-key", "http://a"},
};
const bidirectional_stream_header_array headers_array = {1, 1, headers};
if (bidirectional_stream_start(stream_callback.stream, url, 0, "GET",
&headers_array, true) < 0) {
stream_callback.done = true;
}
puts("bidirectional_stream_start"); puts("bidirectional_stream_start");
while (!stream_callback.done) { while (!stream_callback.done) {
std::this_thread::sleep_for(std::chrono::milliseconds(100)); std::this_thread::sleep_for(std::chrono::milliseconds(100));
} }
bidirectional_stream_destroy(stream_callback.stream); bidirectional_stream_destroy(stream_callback.stream);
Cronet_Engine_StopNetLog(cronet_engine);
Cronet_Engine_Shutdown(cronet_engine); Cronet_Engine_Shutdown(cronet_engine);
Cronet_Engine_Destroy(cronet_engine); Cronet_Engine_Destroy(cronet_engine);
return 0; return 0;

View File

@ -758,6 +758,23 @@ void URLRequestContextConfig::SetContextBuilderExperimentalOptions(
continue; continue;
} }
session_params->spdy_go_away_on_ip_change = iter.second.GetBool(); session_params->spdy_go_away_on_ip_change = iter.second.GetBool();
} else if (iter.first == "proxy_server") {
if (!iter.second.is_string()) {
LOG(ERROR) << "\"" << iter.first << "\" config params \"" << iter.second
<< "\" is not a string";
effective_experimental_options.erase(iter.first);
continue;
}
// Handled in CronetContext::NetworkTasks::BuildDefaultURLRequestContext.
} else if (iter.first == "feature_list") {
if (!iter.second.is_dict()) {
LOG(ERROR) << "\"" << iter.first << "\" config params \"" << iter.second
<< "\" is not a dictionary value";
effective_experimental_options.erase(iter.first);
continue;
}
// Already handled in Cronet_EngineImpl::StartWithParams.
// Only checks and reports errors here.
} else { } else {
LOG(WARNING) << "Unrecognized Cronet experimental option \"" << iter.first LOG(WARNING) << "Unrecognized Cronet experimental option \"" << iter.first
<< "\" with params \"" << iter.second; << "\" with params \"" << iter.second;

View File

@ -216,6 +216,12 @@ struct URLRequestContextConfig {
// not specify for other targets. // not specify for other targets.
absl::optional<double> network_thread_priority); absl::optional<double> network_thread_priority);
// Parses experimental options from their JSON format to the format used
// internally.
// Returns an empty optional if the operation was unsuccessful.
static absl::optional<base::Value::DictStorage> ParseExperimentalOptions(
std::string unparsed_experimental_options);
private: private:
URLRequestContextConfig( URLRequestContextConfig(
// Enable QUIC. // Enable QUIC.
@ -253,12 +259,6 @@ struct URLRequestContextConfig {
// not specify for other targets. // not specify for other targets.
absl::optional<double> network_thread_priority); absl::optional<double> network_thread_priority);
// Parses experimental options from their JSON format to the format used
// internally.
// Returns an empty optional if the operation was unsuccessful.
static absl::optional<base::Value::DictStorage> ParseExperimentalOptions(
std::string unparsed_experimental_options);
// Makes appropriate changes to settings in |this|. // Makes appropriate changes to settings in |this|.
void SetContextConfigExperimentalOptions(); void SetContextConfigExperimentalOptions();

View File

@ -21,6 +21,7 @@
#include "net/base/io_buffer.h" #include "net/base/io_buffer.h"
#include "net/base/net_errors.h" #include "net/base/net_errors.h"
#include "net/base/request_priority.h" #include "net/base/request_priority.h"
#include "net/base/schemeful_site.h"
#include "net/http/bidirectional_stream.h" #include "net/http/bidirectional_stream.h"
#include "net/http/bidirectional_stream_request_info.h" #include "net/http/bidirectional_stream_request_info.h"
#include "net/http/http_network_session.h" #include "net/http/http_network_session.h"
@ -96,8 +97,20 @@ int BidirectionalStream::Start(const char* url,
request_info->priority = static_cast<net::RequestPriority>(priority); request_info->priority = static_cast<net::RequestPriority>(priority);
// Http method is a token, just as header name. // Http method is a token, just as header name.
request_info->method = method; request_info->method = method;
if (!net::HttpUtil::IsValidHeaderName(request_info->method)) if (!net::HttpUtil::IsValidHeaderName(request_info->method)) {
LOG(ERROR) << "Invalid method " << request_info->method;
return -1; return -1;
}
std::string network_isolation_key_header;
if (headers.GetHeader("-network-isolation-key",
&network_isolation_key_header)) {
net::SchemefulSite site(GURL{network_isolation_key_header});
if (site.opaque()) {
LOG(ERROR) << "Invalid -network-isolation-key "
<< network_isolation_key_header;
return -1;
}
}
request_info->extra_headers.CopyFrom(headers); request_info->extra_headers.CopyFrom(headers);
request_info->end_stream_on_headers = end_of_stream; request_info->end_stream_on_headers = end_of_stream;
write_end_of_stream_ = end_of_stream; write_end_of_stream_ = end_of_stream;

View File

@ -207,6 +207,14 @@ void BidirectionalStream::StartRequest(const SSLConfig& ssl_config) {
HttpRequestInfo http_request_info; HttpRequestInfo http_request_info;
http_request_info.url = request_info_->url; http_request_info.url = request_info_->url;
http_request_info.method = request_info_->method; http_request_info.method = request_info_->method;
std::string network_isolation_key_header;
if (request_info_->extra_headers.GetHeader("-network-isolation-key",
&network_isolation_key_header)) {
request_info_->extra_headers.RemoveHeader("-network-isolation-key");
net::SchemefulSite site(GURL{network_isolation_key_header});
CHECK(!site.opaque());
http_request_info.network_isolation_key = NetworkIsolationKey(site, site);
}
http_request_info.extra_headers = request_info_->extra_headers; http_request_info.extra_headers = request_info_->extra_headers;
http_request_info.socket_tag = request_info_->socket_tag; http_request_info.socket_tag = request_info_->socket_tag;
stream_request_ = stream_request_ =