blob: 15c1bd59864b04fbffd32b2d06be599b2d5fbac3 [file] [log] [blame]
droger476922e02015-03-10 17:17:521// 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.
4
dcheng5e05b432016-01-14 04:41:455#import "ios/net/protocol_handler_util.h"
6
dcheng942f39d72016-04-07 21:11:237#include <memory>
dcheng5e05b432016-01-14 04:41:458#include <utility>
9
dcheng942f39d72016-04-07 21:11:2310#include "base/memory/ptr_util.h"
droger476922e02015-03-10 17:17:5211#include "base/run_loop.h"
12#include "base/strings/sys_string_conversions.h"
Gabriel Charettec7108742019-08-23 03:31:4013#include "base/test/task_environment.h"
droger476922e02015-03-10 17:17:5214#include "net/base/elements_upload_data_stream.h"
15#import "net/base/mac/url_conversions.h"
16#include "net/base/upload_bytes_element_reader.h"
17#include "net/http/http_request_headers.h"
18#include "net/http/http_response_headers.h"
19#include "net/url_request/data_protocol_handler.h"
20#include "net/url_request/url_request.h"
21#include "net/url_request/url_request_job.h"
22#include "net/url_request/url_request_job_factory.h"
23#include "net/url_request/url_request_job_factory_impl.h"
24#include "net/url_request/url_request_test_util.h"
25#include "testing/gtest/include/gtest/gtest.h"
26#include "testing/gtest_mac.h"
Sylvain Defresne647780802017-10-09 14:59:2727#include "testing/platform_test.h"
droger476922e02015-03-10 17:17:5228#include "url/gurl.h"
29
marq8e82dba2017-06-19 16:09:2030#if !defined(__has_feature) || !__has_feature(objc_arc)
31#error "This file requires ARC support."
32#endif
33
droger476922e02015-03-10 17:17:5234// When C++ exceptions are disabled, the C++ library defines |try| and
35// |catch| so as to allow exception-expecting C++ code to build properly when
36// language support for exceptions is not present. These macros interfere
37// with the use of |@try| and |@catch| in Objective-C files such as this one.
38// Undefine these macros here, after everything has been #included, since
39// there will be no C++ uses and only Objective-C uses from this point on.
40#undef try
41#undef catch
42
43namespace net {
44namespace {
45
droger476922e02015-03-10 17:17:5246const char* kTextHtml = "text/html";
47const char* kTextPlain = "text/plain";
48const char* kAscii = "US-ASCII";
49
50class HeadersURLRequestJob : public URLRequestJob {
51 public:
52 HeadersURLRequestJob(URLRequest* request)
53 : URLRequestJob(request, nullptr) {}
54
55 void Start() override {
56 // Fills response headers and returns immediately.
57 NotifyHeadersComplete();
58 }
59
60 bool GetMimeType(std::string* mime_type) const override {
61 *mime_type = GetContentTypeValue();
62 return true;
63 }
64
65 void GetResponseInfo(HttpResponseInfo* info) override {
66 // This is called by NotifyHeadersComplete().
67 std::string header_string("HTTP/1.0 200 OK");
68 header_string.push_back('\0');
69 header_string += std::string("Cache-Control: max-age=600");
70 header_string.push_back('\0');
71 if (request()->url().DomainIs("multiplecontenttype")) {
72 header_string += std::string(
73 "coNteNt-tYPe: text/plain; charset=iso-8859-4, image/png");
74 header_string.push_back('\0');
75 }
76 header_string += std::string("Content-Type: ") + GetContentTypeValue();
77 header_string.push_back('\0');
78 header_string += std::string("Foo: A");
79 header_string.push_back('\0');
80 header_string += std::string("Bar: B");
81 header_string.push_back('\0');
82 header_string += std::string("Baz: C");
83 header_string.push_back('\0');
84 header_string += std::string("Foo: D");
85 header_string.push_back('\0');
86 header_string += std::string("Foo: E");
87 header_string.push_back('\0');
88 header_string += std::string("Bar: F");
89 header_string.push_back('\0');
90 info->headers = new HttpResponseHeaders(header_string);
91 }
92
droger476922e02015-03-10 17:17:5293 protected:
94 ~HeadersURLRequestJob() override {}
95
96 std::string GetContentTypeValue() const {
97 if (request()->url().DomainIs("badcontenttype"))
98 return "\xff";
99 return kTextHtml;
100 }
101};
102
103class NetProtocolHandler : public URLRequestJobFactory::ProtocolHandler {
104 public:
105 URLRequestJob* MaybeCreateJob(
106 URLRequest* request,
107 NetworkDelegate* network_delegate) const override {
108 return new HeadersURLRequestJob(request);
109 }
110};
111
Sylvain Defresne647780802017-10-09 14:59:27112class ProtocolHandlerUtilTest : public PlatformTest,
droger476922e02015-03-10 17:17:52113 public URLRequest::Delegate {
114 public:
115 ProtocolHandlerUtilTest() : request_context_(new TestURLRequestContext) {
116 // Ownership of the protocol handlers is transferred to the factory.
svaldez5d58c9e2015-08-24 21:36:20117 job_factory_.SetProtocolHandler("http",
dcheng942f39d72016-04-07 21:11:23118 base::WrapUnique(new NetProtocolHandler));
svaldez5d58c9e2015-08-24 21:36:20119 job_factory_.SetProtocolHandler("data",
dcheng942f39d72016-04-07 21:11:23120 base::WrapUnique(new DataProtocolHandler));
droger476922e02015-03-10 17:17:52121 request_context_->set_job_factory(&job_factory_);
122 }
123
124 NSURLResponse* BuildDataURLResponse(const std::string& mime_type,
125 const std::string& encoding,
126 const std::string& content) {
127 // Build an URL in the form "data:<mime_type>;charset=<encoding>,<content>"
128 // The ';' is removed if mime_type or charset is empty.
129 std::string url_string = std::string("data:") + mime_type;
130 if (!encoding.empty())
131 url_string += ";charset=" + encoding;
132 url_string += ",";
133 GURL url(url_string);
134
dcheng942f39d72016-04-07 21:11:23135 std::unique_ptr<URLRequest> request(
davidben151423e2015-03-23 18:48:36136 request_context_->CreateRequest(url, DEFAULT_PRIORITY, this));
droger476922e02015-03-10 17:17:52137 request->Start();
138 base::RunLoop loop;
139 loop.RunUntilIdle();
140 return GetNSURLResponseForRequest(request.get());
141 }
142
143 void CheckDataResponse(NSURLResponse* response,
144 const std::string& mime_type,
145 const std::string& encoding) {
146 EXPECT_NSEQ(base::SysUTF8ToNSString(mime_type), [response MIMEType]);
147 EXPECT_NSEQ(base::SysUTF8ToNSString(encoding), [response textEncodingName]);
148 // The response class must be NSURLResponse (and not NSHTTPURLResponse) when
149 // the scheme is "data".
150 EXPECT_TRUE([response isMemberOfClass:[NSURLResponse class]]);
151 }
152
maksim.sisov5af90982016-09-05 11:26:28153 void OnResponseStarted(URLRequest* request, int net_error) override {}
droger476922e02015-03-10 17:17:52154 void OnReadCompleted(URLRequest* request, int bytes_read) override {}
155
156 protected:
Gabriel Charettedfa36042019-08-19 17:30:11157 base::test::TaskEnvironment task_environment_;
droger476922e02015-03-10 17:17:52158 URLRequestJobFactoryImpl job_factory_;
dcheng942f39d72016-04-07 21:11:23159 std::unique_ptr<URLRequestContext> request_context_;
droger476922e02015-03-10 17:17:52160};
161
162} // namespace
163
164TEST_F(ProtocolHandlerUtilTest, GetResponseDataSchemeTest) {
165 NSURLResponse* response;
166 // MIME type and charset are correctly carried over.
Stephen McGruer1eb762a2018-05-18 17:04:44167 response = BuildDataURLResponse("?mime=type'", "$(charset-*", "content");
168 CheckDataResponse(response, "?mime=type'", "$(charset-*");
droger476922e02015-03-10 17:17:52169 // Missing values are treated as default values.
170 response = BuildDataURLResponse("", "", "content");
171 CheckDataResponse(response, kTextPlain, kAscii);
172}
173
174TEST_F(ProtocolHandlerUtilTest, GetResponseHttpTest) {
175 // Create a request.
176 GURL url(std::string("http://url"));
dcheng942f39d72016-04-07 21:11:23177 std::unique_ptr<URLRequest> request(
davidben151423e2015-03-23 18:48:36178 request_context_->CreateRequest(url, DEFAULT_PRIORITY, this));
droger476922e02015-03-10 17:17:52179 request->Start();
180 // Create a response from the request.
181 NSURLResponse* response = GetNSURLResponseForRequest(request.get());
182 EXPECT_NSEQ([NSString stringWithUTF8String:kTextHtml], [response MIMEType]);
183 ASSERT_TRUE([response isKindOfClass:[NSHTTPURLResponse class]]);
184 NSHTTPURLResponse* http_response = (NSHTTPURLResponse*)response;
185 NSDictionary* headers = [http_response allHeaderFields];
186 // Check the headers, duplicates must be appended.
187 EXPECT_EQ(5u, [headers count]);
188 NSString* foo_header = [headers objectForKey:@"Foo"];
189 EXPECT_NSEQ(@"A,D,E", foo_header);
190 NSString* bar_header = [headers objectForKey:@"Bar"];
191 EXPECT_NSEQ(@"B,F", bar_header);
192 NSString* baz_header = [headers objectForKey:@"Baz"];
193 EXPECT_NSEQ(@"C", baz_header);
194 NSString* cache_header = [headers objectForKey:@"Cache-Control"];
195 EXPECT_NSEQ(@"no-store", cache_header); // Cache-Control is overridden.
196 // Check the status.
197 EXPECT_EQ(request->GetResponseCode(), [http_response statusCode]);
198}
199
200TEST_F(ProtocolHandlerUtilTest, BadHttpContentType) {
201 // Create a request using the magic domain that triggers a garbage
202 // content-type in the test framework.
203 GURL url(std::string("http://badcontenttype"));
dcheng942f39d72016-04-07 21:11:23204 std::unique_ptr<URLRequest> request(
davidben151423e2015-03-23 18:48:36205 request_context_->CreateRequest(url, DEFAULT_PRIORITY, this));
droger476922e02015-03-10 17:17:52206 request->Start();
207 // Create a response from the request.
208 @try {
209 GetNSURLResponseForRequest(request.get());
210 }
211 @catch (id exception) {
212 FAIL() << "Exception while creating response";
213 }
214}
215
216TEST_F(ProtocolHandlerUtilTest, MultipleHttpContentType) {
217 // Create a request using the magic domain that triggers a garbage
218 // content-type in the test framework.
219 GURL url(std::string("http://multiplecontenttype"));
dcheng942f39d72016-04-07 21:11:23220 std::unique_ptr<URLRequest> request(
davidben151423e2015-03-23 18:48:36221 request_context_->CreateRequest(url, DEFAULT_PRIORITY, this));
droger476922e02015-03-10 17:17:52222 request->Start();
223 // Create a response from the request.
224 NSURLResponse* response = GetNSURLResponseForRequest(request.get());
225 EXPECT_NSEQ(@"text/plain", [response MIMEType]);
226 EXPECT_NSEQ(@"iso-8859-4", [response textEncodingName]);
227 NSHTTPURLResponse* http_response = (NSHTTPURLResponse*)response;
228 NSDictionary* headers = [http_response allHeaderFields];
229 NSString* content_type_header = [headers objectForKey:@"Content-Type"];
230 EXPECT_NSEQ(@"text/plain; charset=iso-8859-4", content_type_header);
231}
232
233TEST_F(ProtocolHandlerUtilTest, CopyHttpHeaders) {
234 GURL url(std::string("http://url"));
marq8e82dba2017-06-19 16:09:20235 NSMutableURLRequest* in_request =
236 [[NSMutableURLRequest alloc] initWithURL:NSURLWithGURL(url)];
droger476922e02015-03-10 17:17:52237 [in_request setAllHTTPHeaderFields:@{
238 @"Referer" : @"referrer",
239 @"User-Agent" : @"secret",
240 @"Accept" : @"money/cash",
241 @"Foo" : @"bar",
242 }];
dcheng942f39d72016-04-07 21:11:23243 std::unique_ptr<URLRequest> out_request(
davidben151423e2015-03-23 18:48:36244 request_context_->CreateRequest(url, DEFAULT_PRIORITY, nullptr));
droger476922e02015-03-10 17:17:52245 CopyHttpHeaders(in_request, out_request.get());
246
247 EXPECT_EQ("referrer", out_request->referrer());
248 const HttpRequestHeaders& headers = out_request->extra_request_headers();
249 EXPECT_FALSE(headers.HasHeader("User-Agent")); // User agent is not copied.
250 EXPECT_FALSE(headers.HasHeader("Content-Type")); // Only in POST requests.
251 std::string header;
252 EXPECT_TRUE(headers.GetHeader("Accept", &header));
253 EXPECT_EQ("money/cash", header);
254 EXPECT_TRUE(headers.GetHeader("Foo", &header));
255 EXPECT_EQ("bar", header);
256}
257
258TEST_F(ProtocolHandlerUtilTest, AddMissingHeaders) {
259 GURL url(std::string("http://url"));
marq8e82dba2017-06-19 16:09:20260 NSMutableURLRequest* in_request =
261 [[NSMutableURLRequest alloc] initWithURL:NSURLWithGURL(url)];
dcheng942f39d72016-04-07 21:11:23262 std::unique_ptr<URLRequest> out_request(
davidben151423e2015-03-23 18:48:36263 request_context_->CreateRequest(url, DEFAULT_PRIORITY, nullptr));
droger476922e02015-03-10 17:17:52264 out_request->set_method("POST");
dcheng942f39d72016-04-07 21:11:23265 std::unique_ptr<UploadElementReader> reader(
droger476922e02015-03-10 17:17:52266 new UploadBytesElementReader(nullptr, 0));
267 out_request->set_upload(
dcheng5e05b432016-01-14 04:41:45268 ElementsUploadDataStream::CreateWithReader(std::move(reader), 0));
droger476922e02015-03-10 17:17:52269 CopyHttpHeaders(in_request, out_request.get());
270
271 // Some headers are added by default if missing.
272 const HttpRequestHeaders& headers = out_request->extra_request_headers();
273 std::string header;
274 EXPECT_TRUE(headers.GetHeader("Accept", &header));
275 EXPECT_EQ("*/*", header);
276 EXPECT_TRUE(headers.GetHeader("Content-Type", &header));
277 EXPECT_EQ("application/x-www-form-urlencoded", header);
278}
279
280} // namespace net