blob: 14160079a1e233480f16ff0158b636e084c3bc0e [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"
droger476922e02015-03-10 17:17:5219#include "net/url_request/url_request.h"
Matt Menke7ca3f7c2020-09-08 17:06:5820#include "net/url_request/url_request_filter.h"
21#include "net/url_request/url_request_interceptor.h"
droger476922e02015-03-10 17:17:5222#include "net/url_request/url_request_job.h"
droger476922e02015-03-10 17:17:5223#include "net/url_request/url_request_test_util.h"
24#include "testing/gtest/include/gtest/gtest.h"
25#include "testing/gtest_mac.h"
Sylvain Defresne647780802017-10-09 14:59:2726#include "testing/platform_test.h"
droger476922e02015-03-10 17:17:5227#include "url/gurl.h"
28
marq8e82dba2017-06-19 16:09:2029#if !defined(__has_feature) || !__has_feature(objc_arc)
30#error "This file requires ARC support."
31#endif
32
droger476922e02015-03-10 17:17:5233// When C++ exceptions are disabled, the C++ library defines |try| and
34// |catch| so as to allow exception-expecting C++ code to build properly when
35// language support for exceptions is not present. These macros interfere
36// with the use of |@try| and |@catch| in Objective-C files such as this one.
37// Undefine these macros here, after everything has been #included, since
38// there will be no C++ uses and only Objective-C uses from this point on.
39#undef try
40#undef catch
41
42namespace net {
43namespace {
44
droger476922e02015-03-10 17:17:5245const char* kTextHtml = "text/html";
droger476922e02015-03-10 17:17:5246
47class HeadersURLRequestJob : public URLRequestJob {
48 public:
Matt Menkefd978852020-09-15 16:00:5749 explicit HeadersURLRequestJob(URLRequest* request) : URLRequestJob(request) {}
50
51 ~HeadersURLRequestJob() override {}
droger476922e02015-03-10 17:17:5252
53 void Start() override {
54 // Fills response headers and returns immediately.
55 NotifyHeadersComplete();
56 }
57
58 bool GetMimeType(std::string* mime_type) const override {
59 *mime_type = GetContentTypeValue();
60 return true;
61 }
62
63 void GetResponseInfo(HttpResponseInfo* info) override {
64 // This is called by NotifyHeadersComplete().
65 std::string header_string("HTTP/1.0 200 OK");
66 header_string.push_back('\0');
67 header_string += std::string("Cache-Control: max-age=600");
68 header_string.push_back('\0');
Matt Menke7ca3f7c2020-09-08 17:06:5869 if (request()->url().path_piece() == "/multiplecontenttype") {
droger476922e02015-03-10 17:17:5270 header_string += std::string(
71 "coNteNt-tYPe: text/plain; charset=iso-8859-4, image/png");
72 header_string.push_back('\0');
73 }
74 header_string += std::string("Content-Type: ") + GetContentTypeValue();
75 header_string.push_back('\0');
76 header_string += std::string("Foo: A");
77 header_string.push_back('\0');
78 header_string += std::string("Bar: B");
79 header_string.push_back('\0');
80 header_string += std::string("Baz: C");
81 header_string.push_back('\0');
82 header_string += std::string("Foo: D");
83 header_string.push_back('\0');
84 header_string += std::string("Foo: E");
85 header_string.push_back('\0');
86 header_string += std::string("Bar: F");
87 header_string.push_back('\0');
88 info->headers = new HttpResponseHeaders(header_string);
89 }
90
droger476922e02015-03-10 17:17:5291 protected:
droger476922e02015-03-10 17:17:5292 std::string GetContentTypeValue() const {
Matt Menke7ca3f7c2020-09-08 17:06:5893 if (request()->url().path_piece() == "/badcontenttype")
droger476922e02015-03-10 17:17:5294 return "\xff";
95 return kTextHtml;
96 }
97};
98
Matt Menke7ca3f7c2020-09-08 17:06:5899class NetURLRequestInterceptor : public URLRequestInterceptor {
droger476922e02015-03-10 17:17:52100 public:
Matt Menkefd978852020-09-15 16:00:57101 std::unique_ptr<URLRequestJob> MaybeInterceptRequest(
102 URLRequest* request) const override {
103 return std::make_unique<HeadersURLRequestJob>(request);
droger476922e02015-03-10 17:17:52104 }
105};
106
Sylvain Defresne647780802017-10-09 14:59:27107class ProtocolHandlerUtilTest : public PlatformTest,
droger476922e02015-03-10 17:17:52108 public URLRequest::Delegate {
109 public:
Matt Menke7ca3f7c2020-09-08 17:06:58110 ProtocolHandlerUtilTest()
111 : task_environment_(base::test::TaskEnvironment::MainThreadType::IO),
112 request_context_(std::make_unique<TestURLRequestContext>()) {
113 URLRequestFilter::GetInstance()->AddHostnameInterceptor(
114 "http", "foo.test", std::make_unique<NetURLRequestInterceptor>());
115 }
116
117 ~ProtocolHandlerUtilTest() override {
118 URLRequestFilter::GetInstance()->ClearHandlers();
droger476922e02015-03-10 17:17:52119 }
120
maksim.sisov5af90982016-09-05 11:26:28121 void OnResponseStarted(URLRequest* request, int net_error) override {}
droger476922e02015-03-10 17:17:52122 void OnReadCompleted(URLRequest* request, int bytes_read) override {}
123
124 protected:
Gabriel Charette7bde04d2019-09-05 12:57:30125 base::test::SingleThreadTaskEnvironment task_environment_;
dcheng942f39d72016-04-07 21:11:23126 std::unique_ptr<URLRequestContext> request_context_;
droger476922e02015-03-10 17:17:52127};
128
129} // namespace
130
droger476922e02015-03-10 17:17:52131TEST_F(ProtocolHandlerUtilTest, GetResponseHttpTest) {
132 // Create a request.
Matt Menke7ca3f7c2020-09-08 17:06:58133 GURL url("http://foo.test/");
dcheng942f39d72016-04-07 21:11:23134 std::unique_ptr<URLRequest> request(
davidben151423e2015-03-23 18:48:36135 request_context_->CreateRequest(url, DEFAULT_PRIORITY, this));
droger476922e02015-03-10 17:17:52136 request->Start();
137 // Create a response from the request.
138 NSURLResponse* response = GetNSURLResponseForRequest(request.get());
139 EXPECT_NSEQ([NSString stringWithUTF8String:kTextHtml], [response MIMEType]);
140 ASSERT_TRUE([response isKindOfClass:[NSHTTPURLResponse class]]);
141 NSHTTPURLResponse* http_response = (NSHTTPURLResponse*)response;
142 NSDictionary* headers = [http_response allHeaderFields];
143 // Check the headers, duplicates must be appended.
144 EXPECT_EQ(5u, [headers count]);
145 NSString* foo_header = [headers objectForKey:@"Foo"];
146 EXPECT_NSEQ(@"A,D,E", foo_header);
147 NSString* bar_header = [headers objectForKey:@"Bar"];
148 EXPECT_NSEQ(@"B,F", bar_header);
149 NSString* baz_header = [headers objectForKey:@"Baz"];
150 EXPECT_NSEQ(@"C", baz_header);
151 NSString* cache_header = [headers objectForKey:@"Cache-Control"];
152 EXPECT_NSEQ(@"no-store", cache_header); // Cache-Control is overridden.
153 // Check the status.
154 EXPECT_EQ(request->GetResponseCode(), [http_response statusCode]);
155}
156
157TEST_F(ProtocolHandlerUtilTest, BadHttpContentType) {
Matt Menke7ca3f7c2020-09-08 17:06:58158 // Create a request using the magic path that triggers a garbage
droger476922e02015-03-10 17:17:52159 // content-type in the test framework.
Matt Menke7ca3f7c2020-09-08 17:06:58160 GURL url("http://foo.test/badcontenttype");
dcheng942f39d72016-04-07 21:11:23161 std::unique_ptr<URLRequest> request(
davidben151423e2015-03-23 18:48:36162 request_context_->CreateRequest(url, DEFAULT_PRIORITY, this));
droger476922e02015-03-10 17:17:52163 request->Start();
164 // Create a response from the request.
165 @try {
166 GetNSURLResponseForRequest(request.get());
167 }
168 @catch (id exception) {
169 FAIL() << "Exception while creating response";
170 }
171}
172
173TEST_F(ProtocolHandlerUtilTest, MultipleHttpContentType) {
Matt Menke7ca3f7c2020-09-08 17:06:58174 // Create a request using the magic path that triggers a garbage
droger476922e02015-03-10 17:17:52175 // content-type in the test framework.
Matt Menke7ca3f7c2020-09-08 17:06:58176 GURL url("http://foo.test/multiplecontenttype");
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(@"text/plain", [response MIMEType]);
183 EXPECT_NSEQ(@"iso-8859-4", [response textEncodingName]);
184 NSHTTPURLResponse* http_response = (NSHTTPURLResponse*)response;
185 NSDictionary* headers = [http_response allHeaderFields];
186 NSString* content_type_header = [headers objectForKey:@"Content-Type"];
187 EXPECT_NSEQ(@"text/plain; charset=iso-8859-4", content_type_header);
188}
189
190TEST_F(ProtocolHandlerUtilTest, CopyHttpHeaders) {
Matt Menke7ca3f7c2020-09-08 17:06:58191 GURL url("http://foo.test/");
marq8e82dba2017-06-19 16:09:20192 NSMutableURLRequest* in_request =
193 [[NSMutableURLRequest alloc] initWithURL:NSURLWithGURL(url)];
droger476922e02015-03-10 17:17:52194 [in_request setAllHTTPHeaderFields:@{
195 @"Referer" : @"referrer",
196 @"User-Agent" : @"secret",
197 @"Accept" : @"money/cash",
198 @"Foo" : @"bar",
199 }];
dcheng942f39d72016-04-07 21:11:23200 std::unique_ptr<URLRequest> out_request(
davidben151423e2015-03-23 18:48:36201 request_context_->CreateRequest(url, DEFAULT_PRIORITY, nullptr));
droger476922e02015-03-10 17:17:52202 CopyHttpHeaders(in_request, out_request.get());
203
204 EXPECT_EQ("referrer", out_request->referrer());
205 const HttpRequestHeaders& headers = out_request->extra_request_headers();
droger476922e02015-03-10 17:17:52206 EXPECT_FALSE(headers.HasHeader("Content-Type")); // Only in POST requests.
207 std::string header;
208 EXPECT_TRUE(headers.GetHeader("Accept", &header));
209 EXPECT_EQ("money/cash", header);
210 EXPECT_TRUE(headers.GetHeader("Foo", &header));
211 EXPECT_EQ("bar", header);
212}
213
214TEST_F(ProtocolHandlerUtilTest, AddMissingHeaders) {
Matt Menke7ca3f7c2020-09-08 17:06:58215 GURL url("http://foo.test/");
marq8e82dba2017-06-19 16:09:20216 NSMutableURLRequest* in_request =
217 [[NSMutableURLRequest alloc] initWithURL:NSURLWithGURL(url)];
dcheng942f39d72016-04-07 21:11:23218 std::unique_ptr<URLRequest> out_request(
davidben151423e2015-03-23 18:48:36219 request_context_->CreateRequest(url, DEFAULT_PRIORITY, nullptr));
droger476922e02015-03-10 17:17:52220 out_request->set_method("POST");
dcheng942f39d72016-04-07 21:11:23221 std::unique_ptr<UploadElementReader> reader(
droger476922e02015-03-10 17:17:52222 new UploadBytesElementReader(nullptr, 0));
223 out_request->set_upload(
dcheng5e05b432016-01-14 04:41:45224 ElementsUploadDataStream::CreateWithReader(std::move(reader), 0));
droger476922e02015-03-10 17:17:52225 CopyHttpHeaders(in_request, out_request.get());
226
227 // Some headers are added by default if missing.
228 const HttpRequestHeaders& headers = out_request->extra_request_headers();
229 std::string header;
230 EXPECT_TRUE(headers.GetHeader("Accept", &header));
231 EXPECT_EQ("*/*", header);
232 EXPECT_TRUE(headers.GetHeader("Content-Type", &header));
233 EXPECT_EQ("application/x-www-form-urlencoded", header);
234}
235
236} // namespace net