Commit a7332e27 authored by Misha Efimov's avatar Misha Efimov Committed by Commit Bot

Reland "[Cronet] Create a sample app for native api."

This reverts commit 1e0ee862.

Reason for revert: Don't use popen() as it is not available on Windows.

Original change's description:
> Revert "[Cronet] Create a sample app for native api."
>
> This reverts commit b54f7c7f.
>
> Reason for revert: breaking the tree
>
> Original change's description:
> > [Cronet] Create a sample app for native api.
> >
> > Bug: 786559
> > Cq-Include-Trybots: luci.chromium.try:ios-simulator-cronet;master.tryserver.chromium.android:android_cronet_tester
> > Change-Id: I56fa2a6f9f6277fe33ea78e7dc01beffd32cbed6
> > Reviewed-on: https://chromium-review.googlesource.com/c/1274426
> > Reviewed-by: Paul Jensen <pauljensen@chromium.org>
> > Commit-Queue: Misha Efimov <mef@chromium.org>
> > Cr-Commit-Position: refs/heads/master@{#600438}
>
> TBR=pauljensen@chromium.org,mef@chromium.org
>
> Change-Id: I3cc86a29982f4479a3260ec2522847416fe6547b
> No-Presubmit: true
> No-Tree-Checks: true
> No-Try: true
> Bug: 786559
> Cq-Include-Trybots: luci.chromium.try:ios-simulator-cronet;master.tryserver.chromium.android:android_cronet_tester
> Reviewed-on: https://chromium-review.googlesource.com/c/1286935
> Reviewed-by: Thomas Guilbert <tguilbert@chromium.org>
> Commit-Queue: Thomas Guilbert <tguilbert@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#600444}

TBR=tguilbert@chromium.org

Change-Id: I4935a11d8f20e97122fccd9451a201d135d5db8a
Bug: 786559
Cq-Include-Trybots: luci.chromium.try:ios-simulator-cronet;master.tryserver.chromium.android:android_cronet_tester
Reviewed-on: https://chromium-review.googlesource.com/c/1286745Reviewed-by: default avatarPaul Jensen <pauljensen@chromium.org>
Commit-Queue: Misha Efimov <mef@chromium.org>
Cr-Commit-Position: refs/heads/master@{#600743}
parent 5a9a88c1
...@@ -251,4 +251,32 @@ if (!is_ios && !is_android) { ...@@ -251,4 +251,32 @@ if (!is_ios && !is_android) {
"//net:test_support", "//net:test_support",
] ]
} }
executable("cronet_sample") {
testonly = true
sources = [
"native/sample/main.cc",
"native/sample/sample_executor.cc",
"native/sample/sample_executor.h",
"native/sample/sample_url_request_callback.cc",
"native/sample/sample_url_request_callback.h",
]
deps = [
"//components/cronet",
"//components/cronet/native:cronet_native_headers",
]
if (is_linux && !is_component_build) {
public_configs = [ "//build/config/gcc:rpath_for_built_shared_libraries" ]
}
}
test("cronet_sample_test") {
sources = [
"native/sample/test/sample_test.cc",
]
deps = [
":cronet_sample",
"//testing/gtest:gtest",
]
}
} }
# Files in this directory are copied externally and can't have any dependencies
include_rules = [
# TODO(mef): There doesn't appear to be a way to specify that no includes
# are allowed, so currently we just don't allow a dependency on //base, which
# should disqualify most code. It would be nice to be able to actual prevent
# all dependencies in the future.
"-base",
]
\ No newline at end of file
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <iostream>
#include "cronet_c.h"
#include "sample_executor.h"
#include "sample_url_request_callback.h"
Cronet_EnginePtr CreateCronetEngine() {
Cronet_EnginePtr cronet_engine = Cronet_Engine_Create();
Cronet_EngineParamsPtr engine_params = Cronet_EngineParams_Create();
Cronet_EngineParams_user_agent_set(engine_params, "CronetSample/1");
Cronet_EngineParams_enable_quic_set(engine_params, true);
Cronet_Engine_StartWithParams(cronet_engine, engine_params);
Cronet_EngineParams_Destroy(engine_params);
return cronet_engine;
}
void PerformRequest(Cronet_EnginePtr cronet_engine,
const std::string& url,
Cronet_ExecutorPtr executor) {
SampleUrlRequestCallback url_request_callback;
Cronet_UrlRequestPtr request = Cronet_UrlRequest_Create();
Cronet_UrlRequestParamsPtr request_params = Cronet_UrlRequestParams_Create();
Cronet_UrlRequestParams_http_method_set(request_params, "GET");
Cronet_UrlRequest_InitWithParams(
request, cronet_engine, url.c_str(), request_params,
url_request_callback.GetUrlRequestCallback(), executor);
Cronet_UrlRequestParams_Destroy(request_params);
Cronet_UrlRequest_Start(request);
url_request_callback.WaitForDone();
Cronet_UrlRequest_Destroy(request);
std::cout << "Response Data:" << std::endl
<< url_request_callback.response_as_string() << std::endl;
}
// Download a resource from the Internet. Optional argument must specify
// a valid URL.
int main(int argc, const char* argv[]) {
std::cout << "Hello from Cronet!\n";
Cronet_EnginePtr cronet_engine = CreateCronetEngine();
std::cout << "Cronet version: "
<< Cronet_Engine_GetVersionString(cronet_engine) << std::endl;
std::string url(argc > 1 ? argv[1] : "https://www.example.com");
std::cout << "URL: " << url << std::endl;
SampleExecutor executor;
PerformRequest(cronet_engine, url, executor.GetExecutor());
Cronet_Engine_Shutdown(cronet_engine);
Cronet_Engine_Destroy(cronet_engine);
return 0;
}
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "sample_executor.h"
SampleExecutor::SampleExecutor()
: executor_thread_(SampleExecutor::ThreadLoop, this),
executor_(Cronet_Executor_CreateWith(SampleExecutor::Execute)) {
Cronet_Executor_SetClientContext(executor_, this);
}
SampleExecutor::~SampleExecutor() {
ShutdownExecutor();
Cronet_Executor_Destroy(executor_);
}
Cronet_ExecutorPtr SampleExecutor::GetExecutor() {
return executor_;
}
void SampleExecutor::ShutdownExecutor() {
// Break tasks loop.
{
std::lock_guard<std::mutex> lock(lock_);
stop_thread_loop_ = true;
}
task_available_.notify_one();
// Wait for executor thread.
executor_thread_.join();
}
void SampleExecutor::RunTasksInQueue() {
// Process runnables in |task_queue_|.
while (true) {
Cronet_RunnablePtr runnable = nullptr;
{
// Wait for a task to run or stop signal.
std::unique_lock<std::mutex> lock(lock_);
while (task_queue_.empty() && !stop_thread_loop_)
task_available_.wait(lock);
if (stop_thread_loop_)
break;
if (task_queue_.empty())
continue;
runnable = task_queue_.front();
task_queue_.pop();
}
Cronet_Runnable_Run(runnable);
Cronet_Runnable_Destroy(runnable);
}
// Delete remaining tasks.
std::queue<Cronet_RunnablePtr> tasks_to_destroy;
{
std::unique_lock<std::mutex> lock(lock_);
tasks_to_destroy.swap(task_queue_);
}
while (!tasks_to_destroy.empty()) {
Cronet_Runnable_Destroy(tasks_to_destroy.front());
tasks_to_destroy.pop();
}
}
/* static */
void SampleExecutor::ThreadLoop(SampleExecutor* executor) {
executor->RunTasksInQueue();
}
void SampleExecutor::Execute(Cronet_RunnablePtr runnable) {
{
std::lock_guard<std::mutex> lock(lock_);
if (!stop_thread_loop_) {
task_queue_.push(runnable);
runnable = nullptr;
}
}
if (runnable) {
Cronet_Runnable_Destroy(runnable);
} else {
task_available_.notify_one();
}
}
/* static */
void SampleExecutor::Execute(Cronet_ExecutorPtr self,
Cronet_RunnablePtr runnable) {
auto* executor =
static_cast<SampleExecutor*>(Cronet_Executor_GetClientContext(self));
executor->Execute(runnable);
}
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_CRONET_NATIVE_SAMPLE_SAMPLE_EXECUTOR_H_
#define COMPONENTS_CRONET_NATIVE_SAMPLE_SAMPLE_EXECUTOR_H_
// Cronet sample is expected to be used outside of Chromium infrastructure,
// and as such has to rely on STL directly instead of //base alternatives.
#include <condition_variable>
#include <mutex>
#include <queue>
#include <thread>
#include "cronet_c.h"
// Sample implementation of Cronet_Executor interface using static
// methods to map C API into instance of C++ class.
class SampleExecutor {
public:
SampleExecutor();
~SampleExecutor();
// Gets Cronet_ExecutorPtr implemented by |this|.
Cronet_ExecutorPtr GetExecutor();
// Shuts down the executor, so all pendning tasks are destroyed without
// getting executed.
void ShutdownExecutor();
private:
// Runs tasks in |task_queue_| until |stop_thread_loop_| is set to true.
void RunTasksInQueue();
static void ThreadLoop(SampleExecutor* executor);
// Adds |runnable| to |task_queue_| to execute on |executor_thread_|.
void Execute(Cronet_RunnablePtr runnable);
// Implementation of Cronet_Executor methods.
static void Execute(Cronet_ExecutorPtr self, Cronet_RunnablePtr runnable);
// Synchronise access to |task_queue_| and |stop_thread_loop_|;
std::mutex lock_;
// Tasks to run.
std::queue<Cronet_RunnablePtr> task_queue_;
// Notified if task is added to |task_queue_| or |stop_thread_loop_| is set.
std::condition_variable task_available_;
// Set to true to stop running tasks.
bool stop_thread_loop_ = false;
// Thread on which tasks are executed.
std::thread executor_thread_;
Cronet_ExecutorPtr const executor_;
};
#endif // COMPONENTS_CRONET_NATIVE_SAMPLE_SAMPLE_EXECUTOR_H_
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "sample_url_request_callback.h"
#include <iostream>
SampleUrlRequestCallback::SampleUrlRequestCallback()
: callback_(Cronet_UrlRequestCallback_CreateWith(
SampleUrlRequestCallback::OnRedirectReceived,
SampleUrlRequestCallback::OnResponseStarted,
SampleUrlRequestCallback::OnReadCompleted,
SampleUrlRequestCallback::OnSucceeded,
SampleUrlRequestCallback::OnFailed,
SampleUrlRequestCallback::OnCanceled)) {
Cronet_UrlRequestCallback_SetClientContext(callback_, this);
}
SampleUrlRequestCallback::~SampleUrlRequestCallback() {
Cronet_UrlRequestCallback_Destroy(callback_);
}
Cronet_UrlRequestCallbackPtr SampleUrlRequestCallback::GetUrlRequestCallback() {
return callback_;
}
void SampleUrlRequestCallback::OnRedirectReceived(
Cronet_UrlRequestPtr request,
Cronet_UrlResponseInfoPtr info,
Cronet_String newLocationUrl) {
std::cout << "OnRedirectReceived called: " << newLocationUrl << std::endl;
Cronet_UrlRequest_FollowRedirect(request);
}
void SampleUrlRequestCallback::OnResponseStarted(
Cronet_UrlRequestPtr request,
Cronet_UrlResponseInfoPtr info) {
std::cout << "OnResponseStarted called." << std::endl;
std::cout << "HTTP Status: "
<< Cronet_UrlResponseInfo_http_status_code_get(info) << " "
<< Cronet_UrlResponseInfo_http_status_text_get(info) << std::endl;
// Create and allocate 32kb buffer.
Cronet_BufferPtr buffer = Cronet_Buffer_Create();
Cronet_Buffer_InitWithAlloc(buffer, 32 * 1024);
// Started reading the response.
Cronet_UrlRequest_Read(request, buffer);
}
void SampleUrlRequestCallback::OnReadCompleted(Cronet_UrlRequestPtr request,
Cronet_UrlResponseInfoPtr info,
Cronet_BufferPtr buffer,
uint64_t bytes_read) {
std::cout << "OnReadCompleted called: " << bytes_read << " bytes read."
<< std::endl;
std::string last_read_data(
reinterpret_cast<char*>(Cronet_Buffer_GetData(buffer)), bytes_read);
response_as_string_ += last_read_data;
// Continue reading the response.
Cronet_UrlRequest_Read(request, buffer);
}
void SampleUrlRequestCallback::OnSucceeded(Cronet_UrlRequestPtr request,
Cronet_UrlResponseInfoPtr info) {
std::cout << "OnSucceeded called." << std::endl;
SignalDone(true);
}
void SampleUrlRequestCallback::OnFailed(Cronet_UrlRequestPtr request,
Cronet_UrlResponseInfoPtr info,
Cronet_ErrorPtr error) {
std::cout << "OnFailed called: " << Cronet_Error_message_get(error)
<< std::endl;
last_error_message_ = Cronet_Error_message_get(error);
SignalDone(false);
}
void SampleUrlRequestCallback::OnCanceled(Cronet_UrlRequestPtr request,
Cronet_UrlResponseInfoPtr info) {
std::cout << "OnCanceled called." << std::endl;
SignalDone(false);
}
/* static */
SampleUrlRequestCallback* SampleUrlRequestCallback::GetThis(
Cronet_UrlRequestCallbackPtr self) {
return static_cast<SampleUrlRequestCallback*>(
Cronet_UrlRequestCallback_GetClientContext(self));
}
/* static */
void SampleUrlRequestCallback::OnRedirectReceived(
Cronet_UrlRequestCallbackPtr self,
Cronet_UrlRequestPtr request,
Cronet_UrlResponseInfoPtr info,
Cronet_String newLocationUrl) {
GetThis(self)->OnRedirectReceived(request, info, newLocationUrl);
}
/* static */
void SampleUrlRequestCallback::OnResponseStarted(
Cronet_UrlRequestCallbackPtr self,
Cronet_UrlRequestPtr request,
Cronet_UrlResponseInfoPtr info) {
GetThis(self)->OnResponseStarted(request, info);
}
/* static */
void SampleUrlRequestCallback::OnReadCompleted(
Cronet_UrlRequestCallbackPtr self,
Cronet_UrlRequestPtr request,
Cronet_UrlResponseInfoPtr info,
Cronet_BufferPtr buffer,
uint64_t bytesRead) {
GetThis(self)->OnReadCompleted(request, info, buffer, bytesRead);
}
/* static */
void SampleUrlRequestCallback::OnSucceeded(Cronet_UrlRequestCallbackPtr self,
Cronet_UrlRequestPtr request,
Cronet_UrlResponseInfoPtr info) {
GetThis(self)->OnSucceeded(request, info);
}
/* static */
void SampleUrlRequestCallback::OnFailed(Cronet_UrlRequestCallbackPtr self,
Cronet_UrlRequestPtr request,
Cronet_UrlResponseInfoPtr info,
Cronet_ErrorPtr error) {
GetThis(self)->OnFailed(request, info, error);
}
/* static */
void SampleUrlRequestCallback::OnCanceled(Cronet_UrlRequestCallbackPtr self,
Cronet_UrlRequestPtr request,
Cronet_UrlResponseInfoPtr info) {
GetThis(self)->OnCanceled(request, info);
}
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_CRONET_NATIVE_SAMPLE_SAMPLE_URL_REQUEST_CALLBACK_H_
#define COMPONENTS_CRONET_NATIVE_SAMPLE_SAMPLE_URL_REQUEST_CALLBACK_H_
// Cronet sample is expected to be used outside of Chromium infrastructure,
// and as such has to rely on STL directly instead of //base alternatives.
#include <future>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "cronet_c.h"
// Sample implementation of Cronet_UrlRequestCallback interface using static
// methods to map C API into instance of C++ class.
class SampleUrlRequestCallback {
public:
SampleUrlRequestCallback();
~SampleUrlRequestCallback();
// Gets Cronet_UrlRequestCallbackPtr implemented by |this|.
Cronet_UrlRequestCallbackPtr GetUrlRequestCallback();
// Waits until request is done.
void WaitForDone() { is_done_.wait(); }
// Returns error message if OnFailed callback is invoked.
std::string last_error_message() const { return last_error_message_; }
// Returns string representation of the received response.
std::string response_as_string() const { return response_as_string_; }
protected:
void OnRedirectReceived(Cronet_UrlRequestPtr request,
Cronet_UrlResponseInfoPtr info,
Cronet_String newLocationUrl);
void OnResponseStarted(Cronet_UrlRequestPtr request,
Cronet_UrlResponseInfoPtr info);
void OnReadCompleted(Cronet_UrlRequestPtr request,
Cronet_UrlResponseInfoPtr info,
Cronet_BufferPtr buffer,
uint64_t bytes_read);
void OnSucceeded(Cronet_UrlRequestPtr request,
Cronet_UrlResponseInfoPtr info);
void OnFailed(Cronet_UrlRequestPtr request,
Cronet_UrlResponseInfoPtr info,
Cronet_ErrorPtr error);
void OnCanceled(Cronet_UrlRequestPtr request, Cronet_UrlResponseInfoPtr info);
void SignalDone(bool success) { done_with_success_.set_value(success); }
static SampleUrlRequestCallback* GetThis(Cronet_UrlRequestCallbackPtr self);
// Implementation of Cronet_UrlRequestCallback methods.
static void OnRedirectReceived(Cronet_UrlRequestCallbackPtr self,
Cronet_UrlRequestPtr request,
Cronet_UrlResponseInfoPtr info,
Cronet_String newLocationUrl);
static void OnResponseStarted(Cronet_UrlRequestCallbackPtr self,
Cronet_UrlRequestPtr request,
Cronet_UrlResponseInfoPtr info);
static void OnReadCompleted(Cronet_UrlRequestCallbackPtr self,
Cronet_UrlRequestPtr request,
Cronet_UrlResponseInfoPtr info,
Cronet_BufferPtr buffer,
uint64_t bytesRead);
static void OnSucceeded(Cronet_UrlRequestCallbackPtr self,
Cronet_UrlRequestPtr request,
Cronet_UrlResponseInfoPtr info);
static void OnFailed(Cronet_UrlRequestCallbackPtr self,
Cronet_UrlRequestPtr request,
Cronet_UrlResponseInfoPtr info,
Cronet_ErrorPtr error);
static void OnCanceled(Cronet_UrlRequestCallbackPtr self,
Cronet_UrlRequestPtr request,
Cronet_UrlResponseInfoPtr info);
// Error message copied from |error| if OnFailed callback is invoked.
std::string last_error_message_;
// Accumulated string representation of the received response.
std::string response_as_string_;
// Promise that is set when request is done.
std::promise<bool> done_with_success_;
// Future that is signalled when request is done.
std::future<bool> is_done_ = done_with_success_.get_future();
Cronet_UrlRequestCallbackPtr const callback_;
};
#endif // COMPONENTS_CRONET_NATIVE_SAMPLE_SAMPLE_URL_REQUEST_CALLBACK_H_
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <cstdio>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include "testing/gtest/include/gtest/gtest.h"
namespace {
// Path to the test app used to locate sample app.
std::string s_test_app_path;
// Returns directory name with trailing separator extracted from the file path.
std::string DirName(const std::string& file_path) {
size_t pos = file_path.find_last_of("\\/");
if (std::string::npos == pos)
return std::string();
return file_path.substr(0, pos + 1);
}
// Runs |command_line| and returns string representation of its stdout.
std::string RunCommand(std::string command_line) {
std::string result_out = "command_result.tmp";
EXPECT_EQ(0, std::system((command_line + " >" + result_out).c_str()));
std::stringstream result;
result << std::ifstream(result_out).rdbuf();
std::remove(result_out.c_str());
return result.str();
}
// Test that cronet_sample runs and gets connection refused from localhost.
TEST(SampleTest, TestConnectionRefused) {
// Expect "cronet_sample" app to be located in same directory as the test.
std::string cronet_sample_path = DirName(s_test_app_path) + "cronet_sample";
std::string url = "http://localhost:99999";
std::string sample_out = RunCommand(cronet_sample_path + " " + url);
// Expect cronet sample to run and fail with net::ERR_INVALID_URL.
EXPECT_NE(std::string::npos, sample_out.find("net::ERR_INVALID_URL"));
}
} // namespace
int main(int argc, char** argv) {
s_test_app_path = argv[0];
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment