| Index: chrome/browser/extensions/api/web_request/web_request_api_unittest.cc
|
| diff --git a/chrome/browser/extensions/api/web_request/web_request_api_unittest.cc b/chrome/browser/extensions/api/web_request/web_request_api_unittest.cc
|
| index 3976343d1fe1ad0409f7b65c89e5bf31989b5215..b848a533a65b026b0f464444cda7902685b5fc55 100644
|
| --- a/chrome/browser/extensions/api/web_request/web_request_api_unittest.cc
|
| +++ b/chrome/browser/extensions/api/web_request/web_request_api_unittest.cc
|
| @@ -5,14 +5,17 @@
|
| #include <queue>
|
| #include <map>
|
|
|
| +#include "base/base64.h"
|
| #include "base/bind.h"
|
| #include "base/callback.h"
|
| #include "base/file_util.h"
|
| +#include "base/json/json_reader.h"
|
| #include "base/json/json_string_value_serializer.h"
|
| #include "base/memory/weak_ptr.h"
|
| #include "base/message_loop.h"
|
| #include "base/path_service.h"
|
| #include "base/stl_util.h"
|
| +#include "base/string_piece.h"
|
| #include "base/utf_string_conversions.h"
|
| #include "chrome/browser/content_settings/cookie_settings.h"
|
| #include "chrome/browser/extensions/api/web_request/web_request_api.h"
|
| @@ -37,6 +40,7 @@
|
| namespace helpers = extension_web_request_api_helpers;
|
| namespace keys = extension_web_request_api_constants;
|
|
|
| +using chrome::VersionInfo;
|
| using helpers::CalculateOnAuthRequiredDelta;
|
| using helpers::CalculateOnBeforeRequestDelta;
|
| using helpers::CalculateOnBeforeSendHeadersDelta;
|
| @@ -75,6 +79,35 @@ bool Contains(const Collection& collection, const Key& key) {
|
| collection.end();
|
| }
|
|
|
| +// Parses |json_string| as a Value. Returns NULL on failure.
|
| +scoped_ptr<const Value> GetValueFromJson(const base::StringPiece& json_string) {
|
| + return scoped_ptr<const Value>(base::JSONReader::Read(json_string));
|
| +}
|
| +
|
| +// Parses the JSON data attached to the |message| and tries to return it.
|
| +// Returns NULL on failure.
|
| +void GetPartOfMessageArguments(IPC::Message* message,
|
| + scoped_ptr<const DictionaryValue>* out) {
|
| + ASSERT_EQ(ExtensionMsg_MessageInvoke::ID, message->type());
|
| + ExtensionMsg_MessageInvoke::Param param;
|
| + Value* temp_value = NULL;
|
| + ASSERT_TRUE(ExtensionMsg_MessageInvoke::Read(message, ¶m));
|
| + ASSERT_GE(param.c.GetSize(), 2u);
|
| + ASSERT_TRUE(param.c.Get(1, &temp_value));
|
| + std::string args;
|
| + ASSERT_TRUE(temp_value->GetAsString(&args));
|
| + scoped_ptr<const Value> value = GetValueFromJson(args);
|
| + ASSERT_TRUE(value.get() != NULL);
|
| + const ListValue* list = NULL;
|
| + ASSERT_TRUE(value->GetAsList(&list));
|
| + ASSERT_EQ(1u, list->GetSize());
|
| + const DictionaryValue* dictionary_raw;
|
| + ASSERT_TRUE(list->GetDictionary(0, &dictionary_raw));
|
| + scoped_ptr<const DictionaryValue> dictionary(dictionary_raw->DeepCopy());
|
| + out->swap(dictionary);
|
| + ASSERT_TRUE(dictionary.get() == NULL);
|
| +}
|
| +
|
| } // namespace
|
|
|
| // A mock event router that responds to events with a pre-arranged queue of
|
| @@ -136,6 +169,12 @@ class ExtensionWebRequestTest : public testing::Test {
|
| context_->Init();
|
| }
|
|
|
| + // Fires a URLRequest with the specified |method|, |content_type| and the
|
| + // data from |bytes|.
|
| + void FireURLRequestWithData(const std::string& method,
|
| + const char* content_type,
|
| + const char* bytes, size_t bytes_length);
|
| +
|
| MessageLoopForIO message_loop_;
|
| content::TestBrowserThread ui_thread_;
|
| content::TestBrowserThread io_thread_;
|
| @@ -401,7 +440,206 @@ TEST_F(ExtensionWebRequestTest, SimulateChancelWhileBlocked) {
|
| ExtensionWebRequestEventRouter::GetInstance()->RemoveEventListener(
|
| &profile_, extension_id, kEventName + "/1");
|
| ExtensionWebRequestEventRouter::GetInstance()->RemoveEventListener(
|
| - &profile_, extension_id, kEventName2 + "/1");
|
| + &profile_, extension_id, kEventName2 + "/1");
|
| +}
|
| +
|
| +namespace {
|
| +
|
| +// Create the numerical representation of |values|, strings passed as
|
| +// extraInfoSpec by the event handler. Returns true on success, otherwise false.
|
| +bool GenerateInfoSpec(const std::string& values, int* result) {
|
| + // Create a ListValue of strings.
|
| + std::vector<std::string> split_values;
|
| + scoped_ptr<base::ListValue> list_value(new base::ListValue());
|
| + size_t num_values = Tokenize(values, ",", &split_values);
|
| + for (size_t i = 0; i < num_values ; ++i)
|
| + list_value->Append(new base::StringValue(split_values[i]));
|
| + return ExtensionWebRequestEventRouter::ExtraInfoSpec::InitFromValue(
|
| + *list_value, result);
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +void ExtensionWebRequestTest::FireURLRequestWithData(
|
| + const std::string& method,
|
| + const char* content_type,
|
| + const char* bytes, size_t bytes_length) {
|
| + // The request URL can be arbitrary but must have an HTTP or HTTPS scheme.
|
| + GURL request_url("http://www.example.com");
|
| + net::URLRequest request(request_url, &delegate_, context_.get());
|
| + request.set_method(method);
|
| + if (content_type != NULL)
|
| + request.SetExtraRequestHeaderByName(net::HttpRequestHeaders::kContentType,
|
| + content_type,
|
| + true /* overwrite */);
|
| + request.AppendBytesToUpload(bytes, bytes_length);
|
| + ipc_sender_.PushTask(base::Bind(&base::DoNothing));
|
| + request.Start();
|
| +}
|
| +
|
| +TEST_F(ExtensionWebRequestTest, AccessRequestBodyData) {
|
| + // We verify that URLRequest body is accessible to OnBeforeRequest listeners.
|
| + // These testing steps are repeated twice in a row:
|
| + // 1. Register an extension requesting "body" in ExtraInfoSpec and file a
|
| + // POST URLRequest with a multipart-encoded form. See it getting parsed.
|
| + // 2. Do the same, but without requesting "body". Nothing should be parsed.
|
| + // 3. With "body" again, fire a POST URLRequest with a chunked upload. Get
|
| + // the error message.
|
| + // 4. With "body", fire a POST URLRequest which is not a parseable HTML form.
|
| + // Raw data in Base64 encoding should be returned.
|
| + // 5. Do the same, but with a PUT method. Result should be the same.
|
| + // Each of these steps is done once as if the channel was DEV or CANARY,
|
| + // and once as if it was BETA or STABLE. It is checked that parsed data is
|
| + // available on DEV/CANARY but not on BETA/STABLE.
|
| + const std::string kMethodPost("POST");
|
| + const std::string kMethodPut("PUT");
|
| +
|
| + const char kPlainData[] = "abcd1234\n";
|
| +#define kBoundary "THIS_IS_A_BOUNDARY"
|
| +#define kBlock "--" kBoundary "\r\n" \
|
| + "Content-Disposition: form-data; name=\"text\"\r\n" \
|
| + "\r\n" \
|
| + "test text\r\n" \
|
| + "--" kBoundary "--"
|
| + // Input.
|
| + const char kMultipartBytes[] = kBlock;
|
| + // Expected output.
|
| + std::string expectedPlainData;
|
| + ASSERT_TRUE(base::Base64Encode(kPlainData, &expectedPlainData));
|
| + const std::string kBodyPath(keys::kBodyKey);
|
| + const std::string kParsedFormPath(kBodyPath + "." + keys::kBodyParsedFormKey);
|
| + const std::string kRawPath(kBodyPath + "." + keys::kBodyRawKey);
|
| + const std::string kErrorPath(kBodyPath + "." + keys::kBodyErrorKey);
|
| + const std::string* const kPath[] = {
|
| + &kParsedFormPath,
|
| + &kBodyPath,
|
| + &kErrorPath,
|
| + &kRawPath,
|
| + &kRawPath
|
| + };
|
| + // parsedForm
|
| + const char kFormDataString[] = "{\"text\":[\"test text\"]}";
|
| + scoped_ptr<const Value> form_data(GetValueFromJson(kFormDataString));
|
| + ASSERT_TRUE(form_data.get() != NULL);
|
| + ASSERT_TRUE(form_data->GetType() == Value::TYPE_DICTIONARY);
|
| + // error
|
| + const StringValue error("Not supported: data is uploaded chunked.");
|
| + // raw
|
| + const StringValue raw(expectedPlainData);
|
| + // Summary.
|
| + const Value* const kExpected[] = {
|
| + form_data.get(),
|
| + NULL,
|
| + &error,
|
| + &raw,
|
| + &raw,
|
| + NULL, NULL, NULL, NULL, NULL // These are for the disabled cases.
|
| + };
|
| + // Header.
|
| + const char kMultipart[] = "multipart/form-data; boundary=" kBoundary;
|
| +#undef kBlock
|
| +#undef kBoundary
|
| +
|
| + // Set up a dummy extension name.
|
| + ExtensionWebRequestEventRouter::RequestFilter filter;
|
| + std::string extension_id("1");
|
| + const std::string string_spec_post("blocking,body");
|
| + const std::string string_spec_no_post("blocking");
|
| + int extra_info_spec_empty = 0;
|
| + int extra_info_spec_body = 0;
|
| +
|
| + for (int pass = 0; pass < 2; ++pass) {
|
| + WebRequestSetChannelForTesting(
|
| + pass > 0 ? VersionInfo::CHANNEL_BETA : VersionInfo::CHANNEL_CANARY);
|
| +
|
| + // Part 1.
|
| + // Subscribe to OnBeforeRequest with request body requirement.
|
| + ASSERT_TRUE(GenerateInfoSpec(string_spec_post, &extra_info_spec_body));
|
| + const std::string kEventName(keys::kOnBeforeRequest);
|
| + base::WeakPtrFactory<TestIPCSender> ipc_sender_factory(&ipc_sender_);
|
| + ExtensionWebRequestEventRouter::GetInstance()->AddEventListener(
|
| + &profile_, extension_id, extension_id, kEventName, kEventName + "/1",
|
| + filter, extra_info_spec_body, ipc_sender_factory.GetWeakPtr());
|
| +
|
| + FireURLRequestWithData(
|
| + kMethodPost, kMultipart, kMultipartBytes, strlen(kMultipartBytes));
|
| +
|
| + MessageLoop::current()->RunAllPending();
|
| +
|
| + WebRequestSetChannelForTesting(
|
| + pass > 0 ? VersionInfo::CHANNEL_STABLE : VersionInfo::CHANNEL_DEV);
|
| +
|
| + // Part 2.
|
| + // Now remove the requirement of request body.
|
| + ASSERT_TRUE(
|
| + GenerateInfoSpec(string_spec_no_post, &extra_info_spec_empty));
|
| + ExtensionWebRequestEventRouter::GetInstance()->RemoveEventListener(
|
| + &profile_, extension_id, kEventName + "/1");
|
| + ExtensionWebRequestEventRouter::GetInstance()->AddEventListener(
|
| + &profile_, extension_id, extension_id, kEventName, kEventName + "/1",
|
| + filter, extra_info_spec_empty, ipc_sender_factory.GetWeakPtr());
|
| +
|
| + FireURLRequestWithData(
|
| + kMethodPost, kMultipart, kMultipartBytes, strlen(kMultipartBytes));
|
| +
|
| + // Part 3.
|
| + // Get back the requirement of request body.
|
| + ExtensionWebRequestEventRouter::GetInstance()->RemoveEventListener(
|
| + &profile_, extension_id, kEventName + "/1");
|
| + ExtensionWebRequestEventRouter::GetInstance()->AddEventListener(
|
| + &profile_, extension_id, extension_id, kEventName, kEventName + "/1",
|
| + filter, extra_info_spec_body, ipc_sender_factory.GetWeakPtr());
|
| + { // Fire the request with chunked upload.
|
| + GURL request_url("http://www.example.com");
|
| + net::URLRequest request(request_url, &delegate_, context_.get());
|
| + request.set_method("POST");
|
| + request.EnableChunkedUpload();
|
| + const char kDummyBytes[] = "dummy";
|
| + request.AppendChunkToUpload(kDummyBytes, strlen(kDummyBytes), true);
|
| + net::HttpRequestHeaders headers(request.extra_request_headers());
|
| + headers.SetHeader(net::HttpRequestHeaders::kTransferEncoding, "chunked");
|
| + request.SetExtraRequestHeaders(headers);
|
| + ipc_sender_.PushTask(base::Bind(&base::DoNothing));
|
| + request.Start();
|
| + }
|
| +
|
| + // Part 4.
|
| + // Now send a POST request with body which is not parseable as a form.
|
| + FireURLRequestWithData(
|
| + kMethodPost, NULL /*no header*/, kPlainData, strlen(kPlainData));
|
| +
|
| + // Part 5.
|
| + // Now send a PUT request with the same body as above.
|
| + FireURLRequestWithData(
|
| + kMethodPut, NULL /*no header*/, kPlainData, strlen(kPlainData));
|
| +
|
| + MessageLoop::current()->RunAllPending();
|
| +
|
| + // Clean-up.
|
| + ExtensionWebRequestEventRouter::GetInstance()->RemoveEventListener(
|
| + &profile_, extension_id, kEventName + "/1");
|
| + }
|
| +
|
| + WebRequestResetChannelForTesting();
|
| +
|
| + IPC::Message* message = NULL;
|
| + TestIPCSender::SentMessages::const_iterator i = ipc_sender_.sent_begin();
|
| + for (size_t test = 0; test < arraysize(kExpected); ++test) {
|
| + EXPECT_NE(i, ipc_sender_.sent_end());
|
| + message = (i++)->get();
|
| + scoped_ptr<const DictionaryValue> details;
|
| + GetPartOfMessageArguments(message, &details);
|
| + ASSERT_TRUE(details != NULL);
|
| + const Value* result = NULL;
|
| + EXPECT_EQ(
|
| + kExpected[test] != NULL,
|
| + details->Get(*(kPath[test % arraysize(kPath)]), &result));
|
| + if (kExpected[test] != NULL) {
|
| + EXPECT_TRUE(kExpected[test]->Equals(result));
|
| + }
|
| + }
|
| +
|
| + EXPECT_EQ(i, ipc_sender_.sent_end());
|
| }
|
|
|
| struct HeaderModificationTest_Header {
|
| @@ -631,16 +869,8 @@ namespace {
|
|
|
| void TestInitFromValue(const std::string& values, bool expected_return_code,
|
| int expected_extra_info_spec) {
|
| - // Create a ListValue of strings.
|
| - std::vector<std::string> split_values;
|
| - scoped_ptr<base::ListValue> list_value(new base::ListValue());
|
| - size_t num_values = Tokenize(values, ",", &split_values);
|
| - for (size_t i = 0; i < num_values ; ++i)
|
| - list_value->Append(new base::StringValue(split_values[i]));
|
| int actual_info_spec;
|
| - bool actual_return_code =
|
| - ExtensionWebRequestEventRouter::ExtraInfoSpec::InitFromValue(
|
| - *list_value, &actual_info_spec);
|
| + bool actual_return_code = GenerateInfoSpec(values, &actual_info_spec);
|
| EXPECT_EQ(expected_return_code, actual_return_code);
|
| if (expected_return_code)
|
| EXPECT_EQ(expected_extra_info_spec, actual_info_spec);
|
| @@ -667,6 +897,17 @@ TEST_F(ExtensionWebRequestTest, InitFromValue) {
|
| "asyncBlocking",
|
| true,
|
| ExtensionWebRequestEventRouter::ExtraInfoSpec::ASYNC_BLOCKING);
|
| + WebRequestSetChannelForTesting(VersionInfo::CHANNEL_BETA);
|
| + TestInitFromValue(
|
| + "body",
|
| + true,
|
| + 0);
|
| + WebRequestSetChannelForTesting(VersionInfo::CHANNEL_DEV);
|
| + TestInitFromValue(
|
| + "body",
|
| + true,
|
| + ExtensionWebRequestEventRouter::ExtraInfoSpec::BODY);
|
| + WebRequestResetChannelForTesting();
|
|
|
| // Multiple valid values are bitwise-or'ed.
|
| TestInitFromValue(
|
| @@ -1646,4 +1887,3 @@ TEST(ExtensionWebRequestHelpersTest, TestMergeOnAuthRequiredResponses) {
|
| EXPECT_TRUE(ContainsKey(conflicting_extensions, "extid2"));
|
| EXPECT_EQ(3u, capturing_net_log.GetSize());
|
| }
|
| -
|
|
|