Roll src/third_party/WebKit d9c6159:8139f33 (svn 201974:201975)
[chromium-blink-merge.git] / ios / net / protocol_handler_util_unittest.mm
blob1193dc75a66dd4ab50d33673ae56ae861102a7a5
1 // Copyright 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "base/mac/scoped_nsobject.h"
6 #include "base/memory/scoped_ptr.h"
7 #include "base/message_loop/message_loop.h"
8 #include "base/run_loop.h"
9 #include "base/strings/sys_string_conversions.h"
10 #import "ios/net/protocol_handler_util.h"
11 #include "net/base/elements_upload_data_stream.h"
12 #import "net/base/mac/url_conversions.h"
13 #include "net/base/upload_bytes_element_reader.h"
14 #include "net/http/http_request_headers.h"
15 #include "net/http/http_response_headers.h"
16 #include "net/url_request/data_protocol_handler.h"
17 #include "net/url_request/url_request.h"
18 #include "net/url_request/url_request_job.h"
19 #include "net/url_request/url_request_job_factory.h"
20 #include "net/url_request/url_request_job_factory_impl.h"
21 #include "net/url_request/url_request_test_util.h"
22 #include "testing/gtest/include/gtest/gtest.h"
23 #include "testing/gtest_mac.h"
24 #include "url/gurl.h"
26 // When C++ exceptions are disabled, the C++ library defines |try| and
27 // |catch| so as to allow exception-expecting C++ code to build properly when
28 // language support for exceptions is not present.  These macros interfere
29 // with the use of |@try| and |@catch| in Objective-C files such as this one.
30 // Undefine these macros here, after everything has been #included, since
31 // there will be no C++ uses and only Objective-C uses from this point on.
32 #undef try
33 #undef catch
35 namespace net {
36 namespace {
38 const int kResponseCode = 200;
39 const char* kTextHtml = "text/html";
40 const char* kTextPlain = "text/plain";
41 const char* kAscii = "US-ASCII";
43 class HeadersURLRequestJob : public URLRequestJob {
44  public:
45   HeadersURLRequestJob(URLRequest* request)
46       : URLRequestJob(request, nullptr) {}
48   void Start() override {
49     // Fills response headers and returns immediately.
50     NotifyHeadersComplete();
51   }
53   bool GetMimeType(std::string* mime_type) const override {
54     *mime_type = GetContentTypeValue();
55     return true;
56   }
58   void GetResponseInfo(HttpResponseInfo* info) override {
59     // This is called by NotifyHeadersComplete().
60     std::string header_string("HTTP/1.0 200 OK");
61     header_string.push_back('\0');
62     header_string += std::string("Cache-Control: max-age=600");
63     header_string.push_back('\0');
64     if (request()->url().DomainIs("multiplecontenttype")) {
65       header_string += std::string(
66           "coNteNt-tYPe: text/plain; charset=iso-8859-4, image/png");
67       header_string.push_back('\0');
68     }
69     header_string += std::string("Content-Type: ") + GetContentTypeValue();
70     header_string.push_back('\0');
71     header_string += std::string("Foo: A");
72     header_string.push_back('\0');
73     header_string += std::string("Bar: B");
74     header_string.push_back('\0');
75     header_string += std::string("Baz: C");
76     header_string.push_back('\0');
77     header_string += std::string("Foo: D");
78     header_string.push_back('\0');
79     header_string += std::string("Foo: E");
80     header_string.push_back('\0');
81     header_string += std::string("Bar: F");
82     header_string.push_back('\0');
83     info->headers = new HttpResponseHeaders(header_string);
84   }
86   int GetResponseCode() const override {
87     return kResponseCode;
88   }
89  protected:
90   ~HeadersURLRequestJob() override {}
92   std::string GetContentTypeValue() const {
93     if (request()->url().DomainIs("badcontenttype"))
94       return "\xff";
95     return kTextHtml;
96   }
99 class NetProtocolHandler : public URLRequestJobFactory::ProtocolHandler {
100  public:
101   URLRequestJob* MaybeCreateJob(
102       URLRequest* request,
103       NetworkDelegate* network_delegate) const override {
104     return new HeadersURLRequestJob(request);
105   }
108 class ProtocolHandlerUtilTest : public testing::Test,
109                                 public URLRequest::Delegate {
110  public:
111   ProtocolHandlerUtilTest() : request_context_(new TestURLRequestContext) {
112     // Ownership of the protocol handlers is transferred to the factory.
113     job_factory_.SetProtocolHandler("http",
114                                     make_scoped_ptr(new NetProtocolHandler));
115     job_factory_.SetProtocolHandler("data",
116                                     make_scoped_ptr(new DataProtocolHandler));
117     request_context_->set_job_factory(&job_factory_);
118   }
120   NSURLResponse* BuildDataURLResponse(const std::string& mime_type,
121                                       const std::string& encoding,
122                                       const std::string& content) {
123     // Build an URL in the form "data:<mime_type>;charset=<encoding>,<content>"
124     // The ';' is removed if mime_type or charset is empty.
125     std::string url_string = std::string("data:") + mime_type;
126     if (!encoding.empty())
127       url_string += ";charset=" + encoding;
128     url_string += ",";
129     GURL url(url_string);
131     scoped_ptr<URLRequest> request(
132         request_context_->CreateRequest(url, DEFAULT_PRIORITY, this));
133     request->Start();
134     base::RunLoop loop;
135     loop.RunUntilIdle();
136     return GetNSURLResponseForRequest(request.get());
137   }
139   void CheckDataResponse(NSURLResponse* response,
140                          const std::string& mime_type,
141                          const std::string& encoding) {
142     EXPECT_NSEQ(base::SysUTF8ToNSString(mime_type), [response MIMEType]);
143     EXPECT_NSEQ(base::SysUTF8ToNSString(encoding), [response textEncodingName]);
144     // The response class must be NSURLResponse (and not NSHTTPURLResponse) when
145     // the scheme is "data".
146     EXPECT_TRUE([response isMemberOfClass:[NSURLResponse class]]);
147   }
149   void OnResponseStarted(URLRequest* request) override {}
150   void OnReadCompleted(URLRequest* request, int bytes_read) override {}
152  protected:
153   base::MessageLoop loop_;
154   URLRequestJobFactoryImpl job_factory_;
155   scoped_ptr<URLRequestContext> request_context_;
158 }  // namespace
160 TEST_F(ProtocolHandlerUtilTest, GetResponseDataSchemeTest) {
161   NSURLResponse* response;
162   // MIME type and charset are correctly carried over.
163   response = BuildDataURLResponse("#mime=type'", "$(charset-*", "content");
164   CheckDataResponse(response, "#mime=type'", "$(charset-*");
165   // Missing values are treated as default values.
166   response = BuildDataURLResponse("", "", "content");
167   CheckDataResponse(response, kTextPlain, kAscii);
170 TEST_F(ProtocolHandlerUtilTest, GetResponseHttpTest) {
171   // Create a request.
172   GURL url(std::string("http://url"));
173   scoped_ptr<URLRequest> request(
174       request_context_->CreateRequest(url, DEFAULT_PRIORITY, this));
175   request->Start();
176   // Create a response from the request.
177   NSURLResponse* response = GetNSURLResponseForRequest(request.get());
178   EXPECT_NSEQ([NSString stringWithUTF8String:kTextHtml], [response MIMEType]);
179   ASSERT_TRUE([response isKindOfClass:[NSHTTPURLResponse class]]);
180   NSHTTPURLResponse* http_response = (NSHTTPURLResponse*)response;
181   NSDictionary* headers = [http_response allHeaderFields];
182   // Check the headers, duplicates must be appended.
183   EXPECT_EQ(5u, [headers count]);
184   NSString* foo_header = [headers objectForKey:@"Foo"];
185   EXPECT_NSEQ(@"A,D,E", foo_header);
186   NSString* bar_header = [headers objectForKey:@"Bar"];
187   EXPECT_NSEQ(@"B,F", bar_header);
188   NSString* baz_header = [headers objectForKey:@"Baz"];
189   EXPECT_NSEQ(@"C", baz_header);
190   NSString* cache_header = [headers objectForKey:@"Cache-Control"];
191   EXPECT_NSEQ(@"no-store", cache_header);  // Cache-Control is overridden.
192   // Check the status.
193   EXPECT_EQ(request->GetResponseCode(), [http_response statusCode]);
196 TEST_F(ProtocolHandlerUtilTest, BadHttpContentType) {
197   // Create a request using the magic domain that triggers a garbage
198   // content-type in the test framework.
199   GURL url(std::string("http://badcontenttype"));
200   scoped_ptr<URLRequest> request(
201       request_context_->CreateRequest(url, DEFAULT_PRIORITY, this));
202   request->Start();
203   // Create a response from the request.
204   @try {
205     GetNSURLResponseForRequest(request.get());
206   }
207   @catch (id exception) {
208     FAIL() << "Exception while creating response";
209   }
212 TEST_F(ProtocolHandlerUtilTest, MultipleHttpContentType) {
213   // Create a request using the magic domain that triggers a garbage
214   // content-type in the test framework.
215   GURL url(std::string("http://multiplecontenttype"));
216   scoped_ptr<URLRequest> request(
217       request_context_->CreateRequest(url, DEFAULT_PRIORITY, this));
218   request->Start();
219   // Create a response from the request.
220   NSURLResponse* response = GetNSURLResponseForRequest(request.get());
221   EXPECT_NSEQ(@"text/plain", [response MIMEType]);
222   EXPECT_NSEQ(@"iso-8859-4", [response textEncodingName]);
223   NSHTTPURLResponse* http_response = (NSHTTPURLResponse*)response;
224   NSDictionary* headers = [http_response allHeaderFields];
225   NSString* content_type_header = [headers objectForKey:@"Content-Type"];
226   EXPECT_NSEQ(@"text/plain; charset=iso-8859-4", content_type_header);
229 TEST_F(ProtocolHandlerUtilTest, CopyHttpHeaders) {
230   GURL url(std::string("http://url"));
231   base::scoped_nsobject<NSMutableURLRequest> in_request(
232       [[NSMutableURLRequest alloc] initWithURL:NSURLWithGURL(url)]);
233   [in_request setAllHTTPHeaderFields:@{
234       @"Referer" : @"referrer",
235       @"User-Agent" : @"secret",
236       @"Accept" : @"money/cash",
237       @"Foo" : @"bar",
238   }];
239   scoped_ptr<URLRequest> out_request(
240       request_context_->CreateRequest(url, DEFAULT_PRIORITY, nullptr));
241   CopyHttpHeaders(in_request, out_request.get());
243   EXPECT_EQ("referrer", out_request->referrer());
244   const HttpRequestHeaders& headers = out_request->extra_request_headers();
245   EXPECT_FALSE(headers.HasHeader("User-Agent"));    // User agent is not copied.
246   EXPECT_FALSE(headers.HasHeader("Content-Type"));  // Only in POST requests.
247   std::string header;
248   EXPECT_TRUE(headers.GetHeader("Accept", &header));
249   EXPECT_EQ("money/cash", header);
250   EXPECT_TRUE(headers.GetHeader("Foo", &header));
251   EXPECT_EQ("bar", header);
254 TEST_F(ProtocolHandlerUtilTest, AddMissingHeaders) {
255   GURL url(std::string("http://url"));
256   base::scoped_nsobject<NSMutableURLRequest> in_request(
257       [[NSMutableURLRequest alloc] initWithURL:NSURLWithGURL(url)]);
258   scoped_ptr<URLRequest> out_request(
259       request_context_->CreateRequest(url, DEFAULT_PRIORITY, nullptr));
260   out_request->set_method("POST");
261   scoped_ptr<UploadElementReader> reader(
262       new UploadBytesElementReader(nullptr, 0));
263   out_request->set_upload(
264       ElementsUploadDataStream::CreateWithReader(reader.Pass(), 0));
265   CopyHttpHeaders(in_request, out_request.get());
267   // Some headers are added by default if missing.
268   const HttpRequestHeaders& headers = out_request->extra_request_headers();
269   std::string header;
270   EXPECT_TRUE(headers.GetHeader("Accept", &header));
271   EXPECT_EQ("*/*", header);
272   EXPECT_TRUE(headers.GetHeader("Content-Type", &header));
273   EXPECT_EQ("application/x-www-form-urlencoded", header);
276 }  // namespace net