Index: ios/net/protocol_handler_util_unittest.mm |
diff --git a/ios/net/protocol_handler_util_unittest.mm b/ios/net/protocol_handler_util_unittest.mm |
new file mode 100644 |
index 0000000000000000000000000000000000000000..d2bdc72ce4b21d6aa024805bdc50bb2858d0cb5e |
--- /dev/null |
+++ b/ios/net/protocol_handler_util_unittest.mm |
@@ -0,0 +1,274 @@ |
+// Copyright 2012 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/mac/scoped_nsobject.h" |
+#include "base/memory/scoped_ptr.h" |
+#include "base/message_loop/message_loop.h" |
+#include "base/run_loop.h" |
+#include "base/strings/sys_string_conversions.h" |
+#import "ios/net/protocol_handler_util.h" |
+#include "net/base/elements_upload_data_stream.h" |
+#import "net/base/mac/url_conversions.h" |
+#include "net/base/upload_bytes_element_reader.h" |
+#include "net/http/http_request_headers.h" |
+#include "net/http/http_response_headers.h" |
+#include "net/url_request/data_protocol_handler.h" |
+#include "net/url_request/url_request.h" |
+#include "net/url_request/url_request_job.h" |
+#include "net/url_request/url_request_job_factory.h" |
+#include "net/url_request/url_request_job_factory_impl.h" |
+#include "net/url_request/url_request_test_util.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+#include "testing/gtest_mac.h" |
+#include "url/gurl.h" |
+ |
+// When C++ exceptions are disabled, the C++ library defines |try| and |
+// |catch| so as to allow exception-expecting C++ code to build properly when |
+// language support for exceptions is not present. These macros interfere |
+// with the use of |@try| and |@catch| in Objective-C files such as this one. |
+// Undefine these macros here, after everything has been #included, since |
+// there will be no C++ uses and only Objective-C uses from this point on. |
+#undef try |
+#undef catch |
+ |
+namespace net { |
+namespace { |
+ |
+const int kResponseCode = 200; |
+const char* kTextHtml = "text/html"; |
+const char* kTextPlain = "text/plain"; |
+const char* kAscii = "US-ASCII"; |
+ |
+class HeadersURLRequestJob : public URLRequestJob { |
+ public: |
+ HeadersURLRequestJob(URLRequest* request) |
+ : URLRequestJob(request, nullptr) {} |
+ |
+ void Start() override { |
+ // Fills response headers and returns immediately. |
+ NotifyHeadersComplete(); |
+ } |
+ |
+ bool GetMimeType(std::string* mime_type) const override { |
+ *mime_type = GetContentTypeValue(); |
+ return true; |
+ } |
+ |
+ void GetResponseInfo(HttpResponseInfo* info) override { |
+ // This is called by NotifyHeadersComplete(). |
+ std::string header_string("HTTP/1.0 200 OK"); |
+ header_string.push_back('\0'); |
+ header_string += std::string("Cache-Control: max-age=600"); |
+ header_string.push_back('\0'); |
+ if (request()->url().DomainIs("multiplecontenttype")) { |
+ header_string += std::string( |
+ "coNteNt-tYPe: text/plain; charset=iso-8859-4, image/png"); |
+ header_string.push_back('\0'); |
+ } |
+ header_string += std::string("Content-Type: ") + GetContentTypeValue(); |
+ header_string.push_back('\0'); |
+ header_string += std::string("Foo: A"); |
+ header_string.push_back('\0'); |
+ header_string += std::string("Bar: B"); |
+ header_string.push_back('\0'); |
+ header_string += std::string("Baz: C"); |
+ header_string.push_back('\0'); |
+ header_string += std::string("Foo: D"); |
+ header_string.push_back('\0'); |
+ header_string += std::string("Foo: E"); |
+ header_string.push_back('\0'); |
+ header_string += std::string("Bar: F"); |
+ header_string.push_back('\0'); |
+ info->headers = new HttpResponseHeaders(header_string); |
+ } |
+ |
+ int GetResponseCode() const override { |
+ return kResponseCode; |
+ } |
+ protected: |
+ ~HeadersURLRequestJob() override {} |
+ |
+ std::string GetContentTypeValue() const { |
+ if (request()->url().DomainIs("badcontenttype")) |
+ return "\xff"; |
+ return kTextHtml; |
+ } |
+}; |
+ |
+class NetProtocolHandler : public URLRequestJobFactory::ProtocolHandler { |
+ public: |
+ URLRequestJob* MaybeCreateJob( |
+ URLRequest* request, |
+ NetworkDelegate* network_delegate) const override { |
+ return new HeadersURLRequestJob(request); |
+ } |
+}; |
+ |
+class ProtocolHandlerUtilTest : public testing::Test, |
+ public URLRequest::Delegate { |
+ public: |
+ ProtocolHandlerUtilTest() : request_context_(new TestURLRequestContext) { |
+ // Ownership of the protocol handlers is transferred to the factory. |
+ job_factory_.SetProtocolHandler("http", new NetProtocolHandler); |
+ job_factory_.SetProtocolHandler("data", new DataProtocolHandler); |
+ request_context_->set_job_factory(&job_factory_); |
+ } |
+ |
+ NSURLResponse* BuildDataURLResponse(const std::string& mime_type, |
+ const std::string& encoding, |
+ const std::string& content) { |
+ // Build an URL in the form "data:<mime_type>;charset=<encoding>,<content>" |
+ // The ';' is removed if mime_type or charset is empty. |
+ std::string url_string = std::string("data:") + mime_type; |
+ if (!encoding.empty()) |
+ url_string += ";charset=" + encoding; |
+ url_string += ","; |
+ GURL url(url_string); |
+ |
+ scoped_ptr<URLRequest> request( |
+ request_context_->CreateRequest(url, DEFAULT_PRIORITY, this, nullptr)); |
+ request->Start(); |
+ base::RunLoop loop; |
+ loop.RunUntilIdle(); |
+ return GetNSURLResponseForRequest(request.get()); |
+ } |
+ |
+ void CheckDataResponse(NSURLResponse* response, |
+ const std::string& mime_type, |
+ const std::string& encoding) { |
+ EXPECT_NSEQ(base::SysUTF8ToNSString(mime_type), [response MIMEType]); |
+ EXPECT_NSEQ(base::SysUTF8ToNSString(encoding), [response textEncodingName]); |
+ // The response class must be NSURLResponse (and not NSHTTPURLResponse) when |
+ // the scheme is "data". |
+ EXPECT_TRUE([response isMemberOfClass:[NSURLResponse class]]); |
+ } |
+ |
+ void OnResponseStarted(URLRequest* request) override {} |
+ void OnReadCompleted(URLRequest* request, int bytes_read) override {} |
+ |
+ protected: |
+ base::MessageLoop loop_; |
+ URLRequestJobFactoryImpl job_factory_; |
+ scoped_ptr<URLRequestContext> request_context_; |
+}; |
+ |
+} // namespace |
+ |
+TEST_F(ProtocolHandlerUtilTest, GetResponseDataSchemeTest) { |
+ NSURLResponse* response; |
+ // MIME type and charset are correctly carried over. |
+ response = BuildDataURLResponse("#mime=type'", "$(charset-*", "content"); |
+ CheckDataResponse(response, "#mime=type'", "$(charset-*"); |
+ // Missing values are treated as default values. |
+ response = BuildDataURLResponse("", "", "content"); |
+ CheckDataResponse(response, kTextPlain, kAscii); |
+} |
+ |
+TEST_F(ProtocolHandlerUtilTest, GetResponseHttpTest) { |
+ // Create a request. |
+ GURL url(std::string("http://url")); |
+ scoped_ptr<URLRequest> request( |
+ request_context_->CreateRequest(url, DEFAULT_PRIORITY, this, nullptr)); |
+ request->Start(); |
+ // Create a response from the request. |
+ NSURLResponse* response = GetNSURLResponseForRequest(request.get()); |
+ EXPECT_NSEQ([NSString stringWithUTF8String:kTextHtml], [response MIMEType]); |
+ ASSERT_TRUE([response isKindOfClass:[NSHTTPURLResponse class]]); |
+ NSHTTPURLResponse* http_response = (NSHTTPURLResponse*)response; |
+ NSDictionary* headers = [http_response allHeaderFields]; |
+ // Check the headers, duplicates must be appended. |
+ EXPECT_EQ(5u, [headers count]); |
+ NSString* foo_header = [headers objectForKey:@"Foo"]; |
+ EXPECT_NSEQ(@"A,D,E", foo_header); |
+ NSString* bar_header = [headers objectForKey:@"Bar"]; |
+ EXPECT_NSEQ(@"B,F", bar_header); |
+ NSString* baz_header = [headers objectForKey:@"Baz"]; |
+ EXPECT_NSEQ(@"C", baz_header); |
+ NSString* cache_header = [headers objectForKey:@"Cache-Control"]; |
+ EXPECT_NSEQ(@"no-store", cache_header); // Cache-Control is overridden. |
+ // Check the status. |
+ EXPECT_EQ(request->GetResponseCode(), [http_response statusCode]); |
+} |
+ |
+TEST_F(ProtocolHandlerUtilTest, BadHttpContentType) { |
+ // Create a request using the magic domain that triggers a garbage |
+ // content-type in the test framework. |
+ GURL url(std::string("http://badcontenttype")); |
+ scoped_ptr<URLRequest> request( |
+ request_context_->CreateRequest(url, DEFAULT_PRIORITY, this, nullptr)); |
+ request->Start(); |
+ // Create a response from the request. |
+ @try { |
+ GetNSURLResponseForRequest(request.get()); |
+ } |
+ @catch (id exception) { |
+ FAIL() << "Exception while creating response"; |
+ } |
+} |
+ |
+TEST_F(ProtocolHandlerUtilTest, MultipleHttpContentType) { |
+ // Create a request using the magic domain that triggers a garbage |
+ // content-type in the test framework. |
+ GURL url(std::string("http://multiplecontenttype")); |
+ scoped_ptr<URLRequest> request( |
+ request_context_->CreateRequest(url, DEFAULT_PRIORITY, this, nullptr)); |
+ request->Start(); |
+ // Create a response from the request. |
+ NSURLResponse* response = GetNSURLResponseForRequest(request.get()); |
+ EXPECT_NSEQ(@"text/plain", [response MIMEType]); |
+ EXPECT_NSEQ(@"iso-8859-4", [response textEncodingName]); |
+ NSHTTPURLResponse* http_response = (NSHTTPURLResponse*)response; |
+ NSDictionary* headers = [http_response allHeaderFields]; |
+ NSString* content_type_header = [headers objectForKey:@"Content-Type"]; |
+ EXPECT_NSEQ(@"text/plain; charset=iso-8859-4", content_type_header); |
+} |
+ |
+TEST_F(ProtocolHandlerUtilTest, CopyHttpHeaders) { |
+ GURL url(std::string("http://url")); |
+ base::scoped_nsobject<NSMutableURLRequest> in_request( |
+ [[NSMutableURLRequest alloc] initWithURL:NSURLWithGURL(url)]); |
+ [in_request setAllHTTPHeaderFields:@{ |
+ @"Referer" : @"referrer", |
+ @"User-Agent" : @"secret", |
+ @"Accept" : @"money/cash", |
+ @"Foo" : @"bar", |
+ }]; |
+ scoped_ptr<URLRequest> out_request( |
+ request_context_->CreateRequest(url, DEFAULT_PRIORITY, nullptr, nullptr)); |
+ CopyHttpHeaders(in_request, out_request.get()); |
+ |
+ EXPECT_EQ("referrer", out_request->referrer()); |
+ const HttpRequestHeaders& headers = out_request->extra_request_headers(); |
+ EXPECT_FALSE(headers.HasHeader("User-Agent")); // User agent is not copied. |
+ EXPECT_FALSE(headers.HasHeader("Content-Type")); // Only in POST requests. |
+ std::string header; |
+ EXPECT_TRUE(headers.GetHeader("Accept", &header)); |
+ EXPECT_EQ("money/cash", header); |
+ EXPECT_TRUE(headers.GetHeader("Foo", &header)); |
+ EXPECT_EQ("bar", header); |
+} |
+ |
+TEST_F(ProtocolHandlerUtilTest, AddMissingHeaders) { |
+ GURL url(std::string("http://url")); |
+ base::scoped_nsobject<NSMutableURLRequest> in_request( |
+ [[NSMutableURLRequest alloc] initWithURL:NSURLWithGURL(url)]); |
+ scoped_ptr<URLRequest> out_request( |
+ request_context_->CreateRequest(url, DEFAULT_PRIORITY, nullptr, nullptr)); |
+ out_request->set_method("POST"); |
+ scoped_ptr<UploadElementReader> reader( |
+ new UploadBytesElementReader(nullptr, 0)); |
+ out_request->set_upload( |
+ ElementsUploadDataStream::CreateWithReader(reader.Pass(), 0)); |
+ CopyHttpHeaders(in_request, out_request.get()); |
+ |
+ // Some headers are added by default if missing. |
+ const HttpRequestHeaders& headers = out_request->extra_request_headers(); |
+ std::string header; |
+ EXPECT_TRUE(headers.GetHeader("Accept", &header)); |
+ EXPECT_EQ("*/*", header); |
+ EXPECT_TRUE(headers.GetHeader("Content-Type", &header)); |
+ EXPECT_EQ("application/x-www-form-urlencoded", header); |
+} |
+ |
+} // namespace net |