Add new CefRequestHandler::OnResourceLoadComplete callback (issue #1781)

This commit is contained in:
Marshall Greenblatt 2015-12-04 13:59:14 -05:00
parent bd6e656747
commit 626fc561ca
11 changed files with 351 additions and 30 deletions

View File

@ -162,6 +162,18 @@ typedef struct _cef_request_handler_t {
struct _cef_browser_t* browser, struct _cef_frame_t* frame,
struct _cef_request_t* request, struct _cef_response_t* response);
///
// Called on the IO thread when a resource load has completed. |request| and
// |response| represent the request and response respectively and cannot be
// modified in this callback. |status| indicates the load completion status.
// |received_content_length| is the number of response bytes actually read.
///
void (CEF_CALLBACK *on_resource_load_complete)(
struct _cef_request_handler_t* self, struct _cef_browser_t* browser,
struct _cef_frame_t* frame, struct _cef_request_t* request,
struct _cef_response_t* response, cef_urlrequest_status_t status,
int64 received_content_length);
///
// Called on the IO thread when the browser needs credentials from the user.
// |isProxy| indicates whether the host is a proxy server. |host| contains the

View File

@ -78,6 +78,7 @@ class CefRequestHandler : public virtual CefBase {
public:
typedef cef_return_value_t ReturnValue;
typedef cef_termination_status_t TerminationStatus;
typedef cef_urlrequest_status_t URLRequestStatus;
typedef cef_window_open_disposition_t WindowOpenDisposition;
///
@ -180,6 +181,20 @@ class CefRequestHandler : public virtual CefBase {
return false;
}
///
// Called on the IO thread when a resource load has completed. |request| and
// |response| represent the request and response respectively and cannot be
// modified in this callback. |status| indicates the load completion status.
// |received_content_length| is the number of response bytes actually read.
///
/*--cef()--*/
virtual void OnResourceLoadComplete(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
CefRefPtr<CefResponse> response,
URLRequestStatus status,
int64 received_content_length) {}
///
// Called on the IO thread when the browser needs credentials from the user.
// |isProxy| indicates whether the host is a proxy server. |host| contains the

View File

@ -11,6 +11,7 @@
#include "libcef/browser/net/url_request_user_data.h"
#include "libcef/browser/thread_util.h"
#include "libcef/common/request_impl.h"
#include "libcef/common/response_impl.h"
#include "net/base/net_errors.h"
#include "net/http/http_util.h"
@ -269,6 +270,56 @@ int CefNetworkDelegate::OnBeforeURLRequest(
return net::OK;
}
void CefNetworkDelegate::OnCompleted(net::URLRequest* request, bool started) {
if (!started)
return;
CefRefPtr<CefBrowserHostImpl> browser =
CefBrowserHostImpl::GetBrowserForRequest(request);
if (browser.get()) {
CefRefPtr<CefClient> client = browser->GetClient();
if (client.get()) {
CefRefPtr<CefRequestHandler> handler = client->GetRequestHandler();
if (handler.get()) {
CefRefPtr<CefFrame> frame = browser->GetFrameForRequest(request);
CefRefPtr<CefRequestImpl> cefRequest = new CefRequestImpl();
cefRequest->Set(request);
cefRequest->SetReadOnly(true);
CefRefPtr<CefResponseImpl> cefResponse = new CefResponseImpl();
cefResponse->Set(request);
cefResponse->SetReadOnly(true);
cef_urlrequest_status_t status = UR_UNKNOWN;
switch (request->status().status()) {
case net::URLRequestStatus::SUCCESS:
status = UR_SUCCESS;
break;
case net::URLRequestStatus::CANCELED:
status = UR_CANCELED;
break;
case net::URLRequestStatus::FAILED:
status = UR_FAILED;
break;
default:
NOTREACHED();
break;
}
const int64 received_content_length =
request->received_response_content_length();
handler->OnResourceLoadComplete(browser.get(),
frame,
cefRequest.get(),
cefResponse.get(),
status,
received_content_length);
}
}
}
}
net::NetworkDelegate::AuthRequiredResponse CefNetworkDelegate::OnAuthRequired(
net::URLRequest* request,
const net::AuthChallengeInfo& auth_info,

View File

@ -25,6 +25,7 @@ class CefNetworkDelegate : public net::NetworkDelegateImpl {
const net::AuthChallengeInfo& auth_info,
const AuthCallback& callback,
net::AuthCredentials* credentials) override;
void OnCompleted(net::URLRequest* request, bool started) override;
bool OnCanAccessFile(const net::URLRequest& request,
const base::FilePath& path) const override;

View File

@ -155,6 +155,7 @@ CefResourceRequestJob::CefResourceRequestJob(
handler_(handler),
done_(false),
remaining_bytes_(0),
sent_bytes_(0),
response_cookies_save_index_(0),
weak_factory_(this) {
}
@ -239,7 +240,7 @@ bool CefResourceRequestJob::ReadRawData(net::IOBuffer* dest, int dest_size,
if (remaining_bytes_ == 0) {
// No more data to read.
*bytes_read = 0;
done_ = true;
DoneWithRequest();
return true;
} else if (remaining_bytes_ > 0 && remaining_bytes_ < dest_size) {
// The handler knows the content size beforehand.
@ -259,7 +260,7 @@ bool CefResourceRequestJob::ReadRawData(net::IOBuffer* dest, int dest_size,
if (!rv) {
// The handler has indicated completion of the request.
*bytes_read = 0;
done_ = true;
DoneWithRequest();
return true;
} else if (*bytes_read == 0) {
// Continue reading asynchronously. May happen multiple times in a row so
@ -274,6 +275,8 @@ bool CefResourceRequestJob::ReadRawData(net::IOBuffer* dest, int dest_size,
*bytes_read = dest_size;
}
sent_bytes_ += *bytes_read;
if (remaining_bytes_ > 0)
remaining_bytes_ -= *bytes_read;
@ -572,3 +575,11 @@ void CefResourceRequestJob::FetchResponseCookies(
cookies->push_back(value);
}
}
void CefResourceRequestJob::DoneWithRequest() {
DCHECK(!done_);
done_ = true;
if (request_)
request_->set_received_response_content_length(sent_bytes_);
}

View File

@ -60,11 +60,14 @@ class CefResourceRequestJob : public net::URLRequestJob {
void CookieHandled();
void FetchResponseCookies(std::vector<std::string>* cookies);
void DoneWithRequest();
CefRefPtr<CefResourceHandler> handler_;
bool done_;
CefRefPtr<CefResponse> response_;
GURL redirect_url_;
int64 remaining_bytes_;
int64 sent_bytes_;
CefRefPtr<CefRequest> cef_request_;
CefRefPtr<CefResourceRequestJobCallback> callback_;
scoped_refptr<net::HttpResponseHeaders> response_headers_;

View File

@ -234,6 +234,43 @@ int CEF_CALLBACK request_handler_on_resource_response(
return _retval;
}
void CEF_CALLBACK request_handler_on_resource_load_complete(
struct _cef_request_handler_t* self, cef_browser_t* browser,
cef_frame_t* frame, cef_request_t* request,
struct _cef_response_t* response, cef_urlrequest_status_t status,
int64 received_content_length) {
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
DCHECK(self);
if (!self)
return;
// Verify param: browser; type: refptr_diff
DCHECK(browser);
if (!browser)
return;
// Verify param: frame; type: refptr_diff
DCHECK(frame);
if (!frame)
return;
// Verify param: request; type: refptr_diff
DCHECK(request);
if (!request)
return;
// Verify param: response; type: refptr_diff
DCHECK(response);
if (!response)
return;
// Execute
CefRequestHandlerCppToC::Get(self)->OnResourceLoadComplete(
CefBrowserCToCpp::Wrap(browser),
CefFrameCToCpp::Wrap(frame),
CefRequestCToCpp::Wrap(request),
CefResponseCToCpp::Wrap(response),
status,
received_content_length);
}
int CEF_CALLBACK request_handler_get_auth_credentials(
struct _cef_request_handler_t* self, cef_browser_t* browser,
cef_frame_t* frame, int isProxy, const cef_string_t* host, int port,
@ -460,6 +497,8 @@ CefRequestHandlerCppToC::CefRequestHandlerCppToC() {
GetStruct()->get_resource_handler = request_handler_get_resource_handler;
GetStruct()->on_resource_redirect = request_handler_on_resource_redirect;
GetStruct()->on_resource_response = request_handler_on_resource_response;
GetStruct()->on_resource_load_complete =
request_handler_on_resource_load_complete;
GetStruct()->get_auth_credentials = request_handler_get_auth_credentials;
GetStruct()->on_quota_request = request_handler_on_quota_request;
GetStruct()->on_protocol_execution = request_handler_on_protocol_execution;

View File

@ -226,6 +226,43 @@ bool CefRequestHandlerCToCpp::OnResourceResponse(CefRefPtr<CefBrowser> browser,
return _retval?true:false;
}
void CefRequestHandlerCToCpp::OnResourceLoadComplete(
CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request, CefRefPtr<CefResponse> response,
URLRequestStatus status, int64 received_content_length) {
cef_request_handler_t* _struct = GetStruct();
if (CEF_MEMBER_MISSING(_struct, on_resource_load_complete))
return;
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
// Verify param: browser; type: refptr_diff
DCHECK(browser.get());
if (!browser.get())
return;
// Verify param: frame; type: refptr_diff
DCHECK(frame.get());
if (!frame.get())
return;
// Verify param: request; type: refptr_diff
DCHECK(request.get());
if (!request.get())
return;
// Verify param: response; type: refptr_diff
DCHECK(response.get());
if (!response.get())
return;
// Execute
_struct->on_resource_load_complete(_struct,
CefBrowserCppToC::Wrap(browser),
CefFrameCppToC::Wrap(frame),
CefRequestCppToC::Wrap(request),
CefResponseCppToC::Wrap(response),
status,
received_content_length);
}
bool CefRequestHandlerCToCpp::GetAuthCredentials(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame, bool isProxy, const CefString& host, int port,
const CefString& realm, const CefString& scheme,

View File

@ -48,6 +48,10 @@ class CefRequestHandlerCToCpp
bool OnResourceResponse(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame, CefRefPtr<CefRequest> request,
CefRefPtr<CefResponse> response) override;
void OnResourceLoadComplete(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame, CefRefPtr<CefRequest> request,
CefRefPtr<CefResponse> response, URLRequestStatus status,
int64 received_content_length) override;
bool GetAuthCredentials(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame, bool isProxy, const CefString& host, int port,
const CefString& realm, const CefString& scheme,

View File

@ -106,16 +106,21 @@ class NetNotifyTestHandler : public TestHandler {
cookie_manager_ = CefCookieManager::CreateManager(CefString(), true, NULL);
AddResource(url1_,
const std::string& resource1 =
"<html>"
"<head><script>document.cookie='name1=value1';</script></head>"
"<body>Nav1</body>"
"</html>", "text/html");
AddResource(url2_,
"</html>";
response_length1_ = static_cast<int64>(resource1.size());
AddResource(url1_, resource1, "text/html");
const std::string& resource2 =
"<html>"
"<head><script>document.cookie='name2=value2';</script></head>"
"<body>Nav2</body>"
"</html>", "text/html");
"</html>";
response_length2_ = static_cast<int64>(resource2.size());
AddResource(url2_, resource2, "text/html");
context_handler_ = new RequestContextHandler(this);
context_handler_->SetURL(url1_);
@ -173,6 +178,27 @@ class NetNotifyTestHandler : public TestHandler {
return TestHandler::GetResourceHandler(browser, frame, request);
}
void OnResourceLoadComplete(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
CefRefPtr<CefResponse> response,
URLRequestStatus status,
int64 received_content_length) override {
EXPECT_TRUE(CefCurrentlyOn(TID_IO));
EXPECT_EQ(UR_SUCCESS, status);
const std::string& url = request->GetURL();
if (url.find(url1_) == 0) {
got_resource_load_complete1_.yes();
EXPECT_EQ(response_length1_, received_content_length);
} else if (url.find(url2_) == 0) {
got_resource_load_complete2_.yes();
EXPECT_EQ(response_length2_, received_content_length);
} else {
EXPECT_TRUE(false); // Not reached
}
}
bool OnBeforeBrowse(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
@ -315,6 +341,7 @@ class NetNotifyTestHandler : public TestHandler {
EXPECT_TRUE(got_load_end1_) << " browser " << browser_id;
EXPECT_TRUE(got_before_resource_load1_) << " browser " << browser_id;
EXPECT_TRUE(got_get_resource_handler1_) << " browser " << browser_id;
EXPECT_TRUE(got_resource_load_complete1_) << " browser " << browser_id;
EXPECT_TRUE(got_get_cookie_manager1_) << " browser " << browser_id;
EXPECT_TRUE(got_cookie1_) << " browser " << browser_id;
EXPECT_TRUE(got_process_message1_) << " browser " << browser_id;
@ -322,6 +349,7 @@ class NetNotifyTestHandler : public TestHandler {
EXPECT_TRUE(got_load_end2_) << " browser " << browser_id;
EXPECT_TRUE(got_before_resource_load2_) << " browser " << browser_id;
EXPECT_TRUE(got_get_resource_handler2_) << " browser " << browser_id;
EXPECT_TRUE(got_resource_load_complete2_) << " browser " << browser_id;
EXPECT_TRUE(got_get_cookie_manager2_) << " browser " << browser_id;
EXPECT_TRUE(got_cookie2_) << " browser " << browser_id;
EXPECT_TRUE(got_process_message2_) << " browser " << browser_id;
@ -355,6 +383,7 @@ class NetNotifyTestHandler : public TestHandler {
TrackCallback got_load_end1_;
TrackCallback got_before_resource_load1_;
TrackCallback got_get_resource_handler1_;
TrackCallback got_resource_load_complete1_;
TrackCallback got_get_cookie_manager1_;
TrackCallback got_cookie1_;
TrackCallback got_process_message1_;
@ -362,12 +391,16 @@ class NetNotifyTestHandler : public TestHandler {
TrackCallback got_load_end2_;
TrackCallback got_before_resource_load2_;
TrackCallback got_get_resource_handler2_;
TrackCallback got_resource_load_complete2_;
TrackCallback got_get_cookie_manager2_;
TrackCallback got_cookie2_;
TrackCallback got_process_message2_;
TrackCallback got_before_browse2_will_delay_;
TrackCallback got_before_browse2_delayed_;
int64 response_length1_;
int64 response_length2_;
IMPLEMENT_REFCOUNTING(NetNotifyTestHandler);
};
@ -639,6 +672,29 @@ class ResourceResponseTest : public TestHandler {
response);
}
void OnResourceLoadComplete(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
CefRefPtr<CefResponse> response,
URLRequestStatus status,
int64 received_content_length) override {
EXPECT_IO_THREAD();
EXPECT_TRUE(browser.get());
EXPECT_EQ(browser_id_, browser->GetIdentifier());
EXPECT_TRUE(frame.get());
EXPECT_TRUE(frame->IsMain());
if (request->GetURL() == kResourceTestHtml) {
EXPECT_EQ(main_request_id_, request->GetIdentifier());
return;
}
EXPECT_EQ(sub_request_id_, request->GetIdentifier());
resource_test_->OnResourceLoadComplete(browser, frame, request, response,
status, received_content_length);
}
void OnLoadEnd(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
int httpStatusCode) override {
@ -675,7 +731,8 @@ class ResourceResponseTest : public TestHandler {
ResourceTest(const std::string& start_url,
size_t expected_resource_response_ct = 2U,
size_t expected_before_resource_load_ct = 1U,
size_t expected_resource_redirect_ct = 0U)
size_t expected_resource_redirect_ct = 0U,
size_t expected_resource_load_complete_ct = 1U)
: start_url_(start_url),
resource_response_ct_(0U),
expected_resource_response_ct_(expected_resource_response_ct),
@ -683,7 +740,10 @@ class ResourceResponseTest : public TestHandler {
expected_before_resource_load_ct_(expected_before_resource_load_ct),
get_resource_handler_ct_(0U),
resource_redirect_ct_(0U),
expected_resource_redirect_ct_(expected_resource_redirect_ct) {
expected_resource_redirect_ct_(expected_resource_redirect_ct),
resource_load_complete_ct_(0U),
expected_resource_load_complete_ct_(
expected_resource_load_complete_ct) {
}
virtual ~ResourceTest() {
}
@ -732,7 +792,7 @@ class ResourceResponseTest : public TestHandler {
EXPECT_EQ(200, response->GetStatus());
EXPECT_STREQ("OK", response->GetStatusText().ToString().c_str());
EXPECT_STREQ("text/javascript",
response->GetMimeType().ToString().c_str());
response->GetMimeType().ToString().c_str());
if (resource_response_ct_++ == 0U) {
// Always redirect at least one time.
@ -744,6 +804,23 @@ class ResourceResponseTest : public TestHandler {
return (resource_response_ct_ < expected_resource_response_ct_);
}
void OnResourceLoadComplete(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
CefRefPtr<CefResponse> response,
URLRequestStatus status,
int64 received_content_length) {
EXPECT_TRUE(CheckUrl(request->GetURL()));
// Verify the response returned by GetResourceHandler.
EXPECT_EQ(200, response->GetStatus());
EXPECT_STREQ("OK", response->GetStatusText().ToString().c_str());
EXPECT_STREQ("text/javascript",
response->GetMimeType().ToString().c_str());
resource_load_complete_ct_++;
}
virtual bool CheckUrl(const std::string& url) const {
return (url == start_url_);
}
@ -756,6 +833,8 @@ class ResourceResponseTest : public TestHandler {
EXPECT_EQ(expected_resource_response_ct_, get_resource_handler_ct_);
EXPECT_EQ(expected_before_resource_load_ct_, before_resource_load_ct_);
EXPECT_EQ(expected_resource_redirect_ct_, resource_redirect_ct_);
EXPECT_EQ(expected_resource_load_complete_ct_,
resource_load_complete_ct_);
}
protected:
@ -783,6 +862,8 @@ class ResourceResponseTest : public TestHandler {
size_t get_resource_handler_ct_;
size_t resource_redirect_ct_;
size_t expected_resource_redirect_ct_;
size_t resource_load_complete_ct_;
size_t expected_resource_load_complete_ct_;
TrackCallback got_resource_;
TrackCallback got_resource_retry_;

View File

@ -125,14 +125,16 @@ TEST(RequestTest, SetGet) {
namespace {
const char kTestUrl[] = "http://tests.com/run.html";
void CreateRequest(CefRefPtr<CefRequest>& request) {
request = CefRequest::Create();
EXPECT_TRUE(request.get() != NULL);
request->SetURL("http://tests/run.html");
request->SetURL(kTestUrl);
request->SetMethod("POST");
request->SetReferrer("http://tests/main.html",
request->SetReferrer("http://tests.com/main.html",
REFERRER_POLICY_NO_REFERRER_WHEN_DOWNGRADE);
CefRequest::HeaderMap headers;
@ -155,13 +157,19 @@ void CreateRequest(CefRefPtr<CefRequest>& request) {
class RequestSendRecvTestHandler : public TestHandler {
public:
RequestSendRecvTestHandler() {}
RequestSendRecvTestHandler()
: response_length_(0),
request_id_(0U) {}
void RunTest() override {
// Create the test request
// Create the test request.
CreateRequest(request_);
// Create the browser
const std::string& resource = "<html><body>SendRecv Test</body></html>";
response_length_ = static_cast<int64>(resource.size());
AddResource(kTestUrl, resource, "text/html");
// Create the browser.
CreateBrowser("about:blank");
// Time out the test after a reasonable period of time.
@ -171,7 +179,7 @@ class RequestSendRecvTestHandler : public TestHandler {
void OnAfterCreated(CefRefPtr<CefBrowser> browser) override {
TestHandler::OnAfterCreated(browser);
// Load the test request
// Load the test request.
browser->GetMainFrame()->LoadRequest(request_);
}
@ -180,10 +188,13 @@ class RequestSendRecvTestHandler : public TestHandler {
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
CefRefPtr<CefRequestCallback> callback) override {
// Verify that the request is the same
TestRequestEqual(request_, request, true);
EXPECT_EQ(RT_MAIN_FRAME, request->GetResourceType());
EXPECT_EQ(TT_LINK, request->GetTransitionType());
EXPECT_IO_THREAD();
request_id_ = request->GetIdentifier();
DCHECK_GT(request_id_, 0U);
TestRequest(request);
EXPECT_FALSE(request->IsReadOnly());
got_before_resource_load_.yes();
@ -194,23 +205,83 @@ class RequestSendRecvTestHandler : public TestHandler {
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request) override {
// Verify that the request is the same
TestRequestEqual(request_, request, true);
EXPECT_EQ(RT_MAIN_FRAME, request->GetResourceType());
EXPECT_EQ(TT_LINK, request->GetTransitionType());
EXPECT_IO_THREAD();
TestRequest(request);
EXPECT_FALSE(request->IsReadOnly());
got_resource_handler_.yes();
DestroyTest();
return TestHandler::GetResourceHandler(browser, frame, request);
}
// No results
return NULL;
bool OnResourceResponse(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
CefRefPtr<CefResponse> response) override {
EXPECT_IO_THREAD();
TestRequest(request);
EXPECT_FALSE(request->IsReadOnly());
TestResponse(response);
EXPECT_TRUE(response->IsReadOnly());
got_resource_response_.yes();
return false;
}
void OnResourceLoadComplete(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
CefRefPtr<CefResponse> response,
URLRequestStatus status,
int64 received_content_length) override {
EXPECT_IO_THREAD();
TestRequest(request);
EXPECT_TRUE(request->IsReadOnly());
TestResponse(response);
EXPECT_TRUE(response->IsReadOnly());
EXPECT_EQ(UR_SUCCESS, status);
EXPECT_EQ(response_length_, received_content_length);
got_resource_load_complete_.yes();
DestroyTest();
}
private:
void TestRequest(CefRefPtr<CefRequest> request) {
TestRequestEqual(request_, request, true);
EXPECT_EQ(request_id_, request->GetIdentifier());
EXPECT_EQ(RT_MAIN_FRAME, request->GetResourceType());
EXPECT_EQ(TT_LINK, request->GetTransitionType());
}
void TestResponse(CefRefPtr<CefResponse> response) {
EXPECT_EQ(200, response->GetStatus());
EXPECT_STREQ("OK", response->GetStatusText().ToString().c_str());
EXPECT_STREQ("text/html", response->GetMimeType().ToString().c_str());
}
void DestroyTest() override {
EXPECT_TRUE(got_before_resource_load_);
EXPECT_TRUE(got_resource_handler_);
EXPECT_TRUE(got_resource_response_);
EXPECT_TRUE(got_resource_load_complete_);
TestHandler::DestroyTest();
}
CefRefPtr<CefRequest> request_;
int64 response_length_;
uint64 request_id_;
TrackCallback got_before_resource_load_;
TrackCallback got_resource_handler_;
TrackCallback got_resource_response_;
TrackCallback got_resource_load_complete_;
IMPLEMENT_REFCOUNTING(RequestSendRecvTestHandler);
};
@ -222,10 +293,6 @@ TEST(RequestTest, SendRecv) {
CefRefPtr<RequestSendRecvTestHandler> handler =
new RequestSendRecvTestHandler();
handler->ExecuteTest();
EXPECT_TRUE(handler->got_before_resource_load_);
EXPECT_TRUE(handler->got_resource_handler_);
ReleaseAndWaitForDestructor(handler);
}