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()); |
} |
- |