blob: 099d5b283a00fac451be64b1daa656b8c5fedb57 [file] [log] [blame]
// Copyright 2015 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 "base/macros.h"
#include "base/run_loop.h"
#include "mojo/public/cpp/application/application_impl.h"
#include "mojo/public/cpp/application/application_test_base.h"
#include "mojo/public/cpp/application/connect.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
#include "mojo/public/cpp/system/data_pipe.h"
#include "mojo/services/network/interfaces/network_service.mojom.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace mojo {
namespace service {
namespace {
const char kMessage[] = "Hello World\n";
const char kUrl1[] = "http://www.example.com/url1";
const char kUrl2[] = "http://www.example.com/url2";
const char kHeaderName[] = "X-Interceptor-Header";
void addHeader(Array<HttpHeaderPtr>* headers) {
HttpHeaderPtr header = HttpHeader::New();
header->name = kHeaderName;
header->value = kMessage;
headers->push_back(header.Pass());
}
class BaseInterceptor : public URLLoaderInterceptor {
public:
BaseInterceptor(InterfaceRequest<URLLoaderInterceptor> request)
: binding_(this, request.Pass()) {}
~BaseInterceptor() override {}
protected:
void InterceptRequest(URLRequestPtr request,
const InterceptRequestCallback& callback) override {
URLLoaderInterceptorResponsePtr interceptor_response =
URLLoaderInterceptorResponse::New();
interceptor_response->request = request.Pass();
callback.Run(interceptor_response.Pass());
}
void InterceptResponse(URLResponsePtr response,
const InterceptResponseCallback& callback) override {
URLLoaderInterceptorResponsePtr interceptor_response =
URLLoaderInterceptorResponse::New();
interceptor_response->response = response.Pass();
callback.Run(interceptor_response.Pass());
}
void InterceptFollowRedirect(
const InterceptFollowRedirectCallback& callback) override {
callback.Run(URLLoaderInterceptorResponsePtr());
}
private:
StrongBinding<URLLoaderInterceptor> binding_;
};
class SendHelloInterceptor : public BaseInterceptor {
public:
SendHelloInterceptor(InterfaceRequest<URLLoaderInterceptor> request)
: BaseInterceptor(request.Pass()) {}
~SendHelloInterceptor() override {}
protected:
void InterceptRequest(URLRequestPtr request,
const InterceptRequestCallback& callback) override {
URLResponsePtr response = URLResponse::New();
response->url = request->url;
response->status_code = 200;
response->status_line = "200 OK";
response->mime_type = "text/plain";
response->headers = request->headers.Pass();
uint32_t num_bytes = arraysize(kMessage);
MojoCreateDataPipeOptions options;
options.struct_size = sizeof(MojoCreateDataPipeOptions);
options.flags = MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE;
options.element_num_bytes = 1;
options.capacity_num_bytes = num_bytes;
DataPipe data_pipe(options);
response->body = data_pipe.consumer_handle.Pass();
MojoResult result =
WriteDataRaw(data_pipe.producer_handle.get(), kMessage, &num_bytes,
MOJO_WRITE_DATA_FLAG_ALL_OR_NONE);
EXPECT_EQ(MOJO_RESULT_OK, result);
URLLoaderInterceptorResponsePtr interceptor_response =
URLLoaderInterceptorResponse::New();
interceptor_response->response = response.Pass();
callback.Run(interceptor_response.Pass());
}
};
class CheckCallsInterceptor : public SendHelloInterceptor {
public:
CheckCallsInterceptor(InterfaceRequest<URLLoaderInterceptor> request)
: SendHelloInterceptor(request.Pass()) {}
~CheckCallsInterceptor() override {}
private:
void InterceptRequest(URLRequestPtr request,
const InterceptRequestCallback& callback) override {
SendHelloInterceptor::InterceptRequest(
request.Pass(),
[&callback](URLLoaderInterceptorResponsePtr interceptor_response) {
addHeader(&interceptor_response->response->headers);
callback.Run(interceptor_response.Pass());
});
}
void InterceptResponse(URLResponsePtr response,
const InterceptResponseCallback& callback) override {
SendHelloInterceptor::InterceptResponse(
response.Pass(),
[&callback](URLLoaderInterceptorResponsePtr interceptor_response) {
addHeader(&interceptor_response->response->headers);
callback.Run(interceptor_response.Pass());
});
}
};
class AddHeaderInterceptor : public BaseInterceptor {
public:
AddHeaderInterceptor(InterfaceRequest<URLLoaderInterceptor> request)
: BaseInterceptor(request.Pass()) {}
~AddHeaderInterceptor() override {}
private:
void InterceptRequest(URLRequestPtr request,
const InterceptRequestCallback& callback) override {
addHeader(&request->headers);
URLLoaderInterceptorResponsePtr interceptor_response =
URLLoaderInterceptorResponse::New();
interceptor_response->request = request.Pass();
callback.Run(interceptor_response.Pass());
}
};
class RedirectInterceptor : public AddHeaderInterceptor {
public:
RedirectInterceptor(InterfaceRequest<URLLoaderInterceptor> request)
: AddHeaderInterceptor(request.Pass()) {}
~RedirectInterceptor() override {}
private:
void InterceptResponse(URLResponsePtr response,
const InterceptRequestCallback& callback) override {
if (response->url == kUrl1) {
URLRequestPtr request = URLRequest::New();
request->url = kUrl2;
request->headers = response->headers.Pass();
URLLoaderInterceptorResponsePtr interceptor_response =
URLLoaderInterceptorResponse::New();
interceptor_response->request = request.Pass();
callback.Run(interceptor_response.Pass());
} else {
BaseInterceptor::InterceptResponse(response.Pass(), callback);
}
}
};
template <class I>
class URLLoaderInterceptorFactoryImpl : public URLLoaderInterceptorFactory {
public:
URLLoaderInterceptorFactoryImpl(
InterfaceRequest<URLLoaderInterceptorFactory> request)
: binding_(this, request.Pass()) {}
~URLLoaderInterceptorFactoryImpl() override {}
private:
void Create(
mojo::InterfaceRequest<URLLoaderInterceptor> interceptor) override {
new I(interceptor.Pass());
}
StrongBinding<URLLoaderInterceptorFactory> binding_;
};
class URLLoaderInterceptorAppTest : public test::ApplicationTestBase {
public:
URLLoaderInterceptorAppTest() {}
~URLLoaderInterceptorAppTest() override {}
void SetUp() override {
ApplicationTestBase::SetUp();
ConnectToService(application_impl()->shell(), "mojo:network_service",
GetProxy(&network_service_));
}
URLResponsePtr GetResponse(const std::string& url) {
URLRequestPtr request = URLRequest::New();
request->url = url;
URLLoaderPtr loader;
network_service_->CreateURLLoader(GetProxy(&loader));
URLResponsePtr response;
{
base::RunLoop loop;
loader->Start(request.Pass(), [&response, &loop](URLResponsePtr r) {
response = r.Pass();
loop.Quit();
});
loop.Run();
}
return response.Pass();
}
template <class I>
void AddInterceptor() {
URLLoaderInterceptorFactoryPtr factory;
new URLLoaderInterceptorFactoryImpl<I>(GetProxy(&factory));
network_service_->RegisterURLLoaderInterceptor(factory.Pass());
}
protected:
NetworkServicePtr network_service_;
DISALLOW_COPY_AND_ASSIGN(URLLoaderInterceptorAppTest);
};
} // namespace
TEST_F(URLLoaderInterceptorAppTest, TestSimpleInterceptor) {
AddInterceptor<SendHelloInterceptor>();
URLResponsePtr response = GetResponse(kUrl1);
EXPECT_TRUE(response);
EXPECT_EQ(kUrl1, response->url);
EXPECT_EQ(0u, response->headers.size());
char received_message[arraysize(kMessage)];
uint32_t num_bytes = arraysize(kMessage);
EXPECT_EQ(MOJO_RESULT_OK, ReadDataRaw(response->body.get(), received_message,
&num_bytes, MOJO_READ_DATA_FLAG_NONE));
EXPECT_EQ(arraysize(kMessage), num_bytes);
EXPECT_EQ(0, memcmp(kMessage, received_message, num_bytes));
}
// Tests that a header added to the request by an initial interceptor is
// preserved when the request is sent to a second interceptor.
TEST_F(URLLoaderInterceptorAppTest, AddHeader) {
AddInterceptor<SendHelloInterceptor>();
AddInterceptor<AddHeaderInterceptor>();
URLResponsePtr response = GetResponse(kUrl1);
EXPECT_TRUE(response);
EXPECT_EQ(kUrl1, response->url);
EXPECT_EQ(1u, response->headers.size());
HttpHeaderPtr header = response->headers[0].Pass();
EXPECT_EQ(kHeaderName, header->name);
EXPECT_EQ(kMessage, header->value);
}
// Tests that interceptor are called in the right order, in this case that the
// last interceptor can prevent the previous one to run.
TEST_F(URLLoaderInterceptorAppTest, InterceptorOrder) {
AddInterceptor<AddHeaderInterceptor>();
AddInterceptor<SendHelloInterceptor>();
URLResponsePtr response = GetResponse(kUrl1);
EXPECT_TRUE(response);
EXPECT_EQ(kUrl1, response->url);
EXPECT_EQ(0u, response->headers.size());
}
// Tests that an interceptor can make the loader load a new request after having
// received a first one.
TEST_F(URLLoaderInterceptorAppTest, RedirectOnResponse) {
AddInterceptor<SendHelloInterceptor>();
AddInterceptor<RedirectInterceptor>();
URLResponsePtr response = GetResponse(kUrl1);
EXPECT_TRUE(response);
EXPECT_EQ(kUrl2, response->url);
EXPECT_EQ(1u, response->headers.size());
}
TEST_F(URLLoaderInterceptorAppTest, CheckCallsInterceptor) {
AddInterceptor<CheckCallsInterceptor>();
URLResponsePtr response = GetResponse(kUrl1);
EXPECT_TRUE(response);
EXPECT_EQ(kUrl1, response->url);
EXPECT_EQ(1u, response->headers.size());
}
} // namespace service
} // namespace mojo