Speculative crash fix; check for a null focused view before derefing.
[chromium-blink-merge.git] / ios / net / protocol_handler_util_unittest.mm
blobda39df6fd37e6f053df45b72f3341b5ae09639f3
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", new NetProtocolHandler);
114     job_factory_.SetProtocolHandler("data", new DataProtocolHandler);
115     request_context_->set_job_factory(&job_factory_);
116   }
118   NSURLResponse* BuildDataURLResponse(const std::string& mime_type,
119                                       const std::string& encoding,
120                                       const std::string& content) {
121     // Build an URL in the form "data:<mime_type>;charset=<encoding>,<content>"
122     // The ';' is removed if mime_type or charset is empty.
123     std::string url_string = std::string("data:") + mime_type;
124     if (!encoding.empty())
125       url_string += ";charset=" + encoding;
126     url_string += ",";
127     GURL url(url_string);
129     scoped_ptr<URLRequest> request(
130         request_context_->CreateRequest(url, DEFAULT_PRIORITY, this));
131     request->Start();
132     base::RunLoop loop;
133     loop.RunUntilIdle();
134     return GetNSURLResponseForRequest(request.get());
135   }
137   void CheckDataResponse(NSURLResponse* response,
138                          const std::string& mime_type,
139                          const std::string& encoding) {
140     EXPECT_NSEQ(base::SysUTF8ToNSString(mime_type), [response MIMEType]);
141     EXPECT_NSEQ(base::SysUTF8ToNSString(encoding), [response textEncodingName]);
142     // The response class must be NSURLResponse (and not NSHTTPURLResponse) when
143     // the scheme is "data".
144     EXPECT_TRUE([response isMemberOfClass:[NSURLResponse class]]);
145   }
147   void OnResponseStarted(URLRequest* request) override {}
148   void OnReadCompleted(URLRequest* request, int bytes_read) override {}
150  protected:
151   base::MessageLoop loop_;
152   URLRequestJobFactoryImpl job_factory_;
153   scoped_ptr<URLRequestContext> request_context_;
156 }  // namespace
158 TEST_F(ProtocolHandlerUtilTest, GetResponseDataSchemeTest) {
159   NSURLResponse* response;
160   // MIME type and charset are correctly carried over.
161   response = BuildDataURLResponse("#mime=type'", "$(charset-*", "content");
162   CheckDataResponse(response, "#mime=type'", "$(charset-*");
163   // Missing values are treated as default values.
164   response = BuildDataURLResponse("", "", "content");
165   CheckDataResponse(response, kTextPlain, kAscii);
168 TEST_F(ProtocolHandlerUtilTest, GetResponseHttpTest) {
169   // Create a request.
170   GURL url(std::string("http://url"));
171   scoped_ptr<URLRequest> request(
172       request_context_->CreateRequest(url, DEFAULT_PRIORITY, this));
173   request->Start();
174   // Create a response from the request.
175   NSURLResponse* response = GetNSURLResponseForRequest(request.get());
176   EXPECT_NSEQ([NSString stringWithUTF8String:kTextHtml], [response MIMEType]);
177   ASSERT_TRUE([response isKindOfClass:[NSHTTPURLResponse class]]);
178   NSHTTPURLResponse* http_response = (NSHTTPURLResponse*)response;
179   NSDictionary* headers = [http_response allHeaderFields];
180   // Check the headers, duplicates must be appended.
181   EXPECT_EQ(5u, [headers count]);
182   NSString* foo_header = [headers objectForKey:@"Foo"];
183   EXPECT_NSEQ(@"A,D,E", foo_header);
184   NSString* bar_header = [headers objectForKey:@"Bar"];
185   EXPECT_NSEQ(@"B,F", bar_header);
186   NSString* baz_header = [headers objectForKey:@"Baz"];
187   EXPECT_NSEQ(@"C", baz_header);
188   NSString* cache_header = [headers objectForKey:@"Cache-Control"];
189   EXPECT_NSEQ(@"no-store", cache_header);  // Cache-Control is overridden.
190   // Check the status.
191   EXPECT_EQ(request->GetResponseCode(), [http_response statusCode]);
194 TEST_F(ProtocolHandlerUtilTest, BadHttpContentType) {
195   // Create a request using the magic domain that triggers a garbage
196   // content-type in the test framework.
197   GURL url(std::string("http://badcontenttype"));
198   scoped_ptr<URLRequest> request(
199       request_context_->CreateRequest(url, DEFAULT_PRIORITY, this));
200   request->Start();
201   // Create a response from the request.
202   @try {
203     GetNSURLResponseForRequest(request.get());
204   }
205   @catch (id exception) {
206     FAIL() << "Exception while creating response";
207   }
210 TEST_F(ProtocolHandlerUtilTest, MultipleHttpContentType) {
211   // Create a request using the magic domain that triggers a garbage
212   // content-type in the test framework.
213   GURL url(std::string("http://multiplecontenttype"));
214   scoped_ptr<URLRequest> request(
215       request_context_->CreateRequest(url, DEFAULT_PRIORITY, this));
216   request->Start();
217   // Create a response from the request.
218   NSURLResponse* response = GetNSURLResponseForRequest(request.get());
219   EXPECT_NSEQ(@"text/plain", [response MIMEType]);
220   EXPECT_NSEQ(@"iso-8859-4", [response textEncodingName]);
221   NSHTTPURLResponse* http_response = (NSHTTPURLResponse*)response;
222   NSDictionary* headers = [http_response allHeaderFields];
223   NSString* content_type_header = [headers objectForKey:@"Content-Type"];
224   EXPECT_NSEQ(@"text/plain; charset=iso-8859-4", content_type_header);
227 TEST_F(ProtocolHandlerUtilTest, CopyHttpHeaders) {
228   GURL url(std::string("http://url"));
229   base::scoped_nsobject<NSMutableURLRequest> in_request(
230       [[NSMutableURLRequest alloc] initWithURL:NSURLWithGURL(url)]);
231   [in_request setAllHTTPHeaderFields:@{
232       @"Referer" : @"referrer",
233       @"User-Agent" : @"secret",
234       @"Accept" : @"money/cash",
235       @"Foo" : @"bar",
236   }];
237   scoped_ptr<URLRequest> out_request(
238       request_context_->CreateRequest(url, DEFAULT_PRIORITY, nullptr));
239   CopyHttpHeaders(in_request, out_request.get());
241   EXPECT_EQ("referrer", out_request->referrer());
242   const HttpRequestHeaders& headers = out_request->extra_request_headers();
243   EXPECT_FALSE(headers.HasHeader("User-Agent"));    // User agent is not copied.
244   EXPECT_FALSE(headers.HasHeader("Content-Type"));  // Only in POST requests.
245   std::string header;
246   EXPECT_TRUE(headers.GetHeader("Accept", &header));
247   EXPECT_EQ("money/cash", header);
248   EXPECT_TRUE(headers.GetHeader("Foo", &header));
249   EXPECT_EQ("bar", header);
252 TEST_F(ProtocolHandlerUtilTest, AddMissingHeaders) {
253   GURL url(std::string("http://url"));
254   base::scoped_nsobject<NSMutableURLRequest> in_request(
255       [[NSMutableURLRequest alloc] initWithURL:NSURLWithGURL(url)]);
256   scoped_ptr<URLRequest> out_request(
257       request_context_->CreateRequest(url, DEFAULT_PRIORITY, nullptr));
258   out_request->set_method("POST");
259   scoped_ptr<UploadElementReader> reader(
260       new UploadBytesElementReader(nullptr, 0));
261   out_request->set_upload(
262       ElementsUploadDataStream::CreateWithReader(reader.Pass(), 0));
263   CopyHttpHeaders(in_request, out_request.get());
265   // Some headers are added by default if missing.
266   const HttpRequestHeaders& headers = out_request->extra_request_headers();
267   std::string header;
268   EXPECT_TRUE(headers.GetHeader("Accept", &header));
269   EXPECT_EQ("*/*", header);
270   EXPECT_TRUE(headers.GetHeader("Content-Type", &header));
271   EXPECT_EQ("application/x-www-form-urlencoded", header);
274 }  // namespace net