blob: 6ae878ae3cb018776692137867cf8ccb31e8d3a7 [file] [log] [blame]
Avi Drissmanea1be232022-09-14 23:29:061// Copyright 2012 The Chromium Authors
droger476922e02015-03-10 17:17:522// 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
Sylvain Defresnea383a752025-01-22 15:23:207#import <memory>
8#import <utility>
dcheng5e05b432016-01-14 04:41:459
Sylvain Defresnea383a752025-01-22 15:23:2010#import "base/memory/ptr_util.h"
11#import "base/run_loop.h"
12#import "base/strings/sys_string_conversions.h"
13#import "base/test/task_environment.h"
Tiago Vignattidd0987b2024-01-25 14:56:4614#import "net/base/apple/url_conversions.h"
Sylvain Defresnea383a752025-01-22 15:23:2015#import "net/base/elements_upload_data_stream.h"
16#import "net/base/upload_bytes_element_reader.h"
17#import "net/http/http_request_headers.h"
18#import "net/http/http_response_headers.h"
19#import "net/url_request/url_request.h"
20#import "net/url_request/url_request_context.h"
21#import "net/url_request/url_request_context_builder.h"
22#import "net/url_request/url_request_filter.h"
23#import "net/url_request/url_request_interceptor.h"
24#import "net/url_request/url_request_job.h"
25#import "net/url_request/url_request_test_util.h"
26#import "testing/gtest/include/gtest/gtest.h"
27#import "testing/gtest_mac.h"
28#import "testing/platform_test.h"
29#import "url/gurl.h"
droger476922e02015-03-10 17:17:5230
droger476922e02015-03-10 17:17:5231namespace net {
32namespace {
33
droger476922e02015-03-10 17:17:5234const char* kTextHtml = "text/html";
droger476922e02015-03-10 17:17:5235
36class HeadersURLRequestJob : public URLRequestJob {
37 public:
Matt Menkefd978852020-09-15 16:00:5738 explicit HeadersURLRequestJob(URLRequest* request) : URLRequestJob(request) {}
39
40 ~HeadersURLRequestJob() override {}
droger476922e02015-03-10 17:17:5241
42 void Start() override {
43 // Fills response headers and returns immediately.
44 NotifyHeadersComplete();
45 }
46
47 bool GetMimeType(std::string* mime_type) const override {
48 *mime_type = GetContentTypeValue();
49 return true;
50 }
51
52 void GetResponseInfo(HttpResponseInfo* info) override {
53 // This is called by NotifyHeadersComplete().
54 std::string header_string("HTTP/1.0 200 OK");
55 header_string.push_back('\0');
56 header_string += std::string("Cache-Control: max-age=600");
57 header_string.push_back('\0');
Matt Menke7ca3f7c2020-09-08 17:06:5858 if (request()->url().path_piece() == "/multiplecontenttype") {
droger476922e02015-03-10 17:17:5259 header_string += std::string(
60 "coNteNt-tYPe: text/plain; charset=iso-8859-4, image/png");
61 header_string.push_back('\0');
62 }
63 header_string += std::string("Content-Type: ") + GetContentTypeValue();
64 header_string.push_back('\0');
65 header_string += std::string("Foo: A");
66 header_string.push_back('\0');
67 header_string += std::string("Bar: B");
68 header_string.push_back('\0');
69 header_string += std::string("Baz: C");
70 header_string.push_back('\0');
71 header_string += std::string("Foo: D");
72 header_string.push_back('\0');
73 header_string += std::string("Foo: E");
74 header_string.push_back('\0');
75 header_string += std::string("Bar: F");
76 header_string.push_back('\0');
77 info->headers = new HttpResponseHeaders(header_string);
78 }
79
droger476922e02015-03-10 17:17:5280 protected:
droger476922e02015-03-10 17:17:5281 std::string GetContentTypeValue() const {
Sylvain Defresnecc2980bd2025-01-22 22:39:0482 if (request()->url().path_piece() == "/badcontenttype") {
droger476922e02015-03-10 17:17:5283 return "\xff";
Sylvain Defresnecc2980bd2025-01-22 22:39:0484 }
droger476922e02015-03-10 17:17:5285 return kTextHtml;
86 }
87};
88
Matt Menke7ca3f7c2020-09-08 17:06:5889class NetURLRequestInterceptor : public URLRequestInterceptor {
droger476922e02015-03-10 17:17:5290 public:
Matt Menkefd978852020-09-15 16:00:5791 std::unique_ptr<URLRequestJob> MaybeInterceptRequest(
92 URLRequest* request) const override {
93 return std::make_unique<HeadersURLRequestJob>(request);
droger476922e02015-03-10 17:17:5294 }
95};
96
Sylvain Defresne647780802017-10-09 14:59:2797class ProtocolHandlerUtilTest : public PlatformTest,
droger476922e02015-03-10 17:17:5298 public URLRequest::Delegate {
99 public:
Matt Menke7ca3f7c2020-09-08 17:06:58100 ProtocolHandlerUtilTest()
101 : task_environment_(base::test::TaskEnvironment::MainThreadType::IO),
Yutaka Hiranod81ac3f2022-03-23 15:08:34102 request_context_(net::CreateTestURLRequestContextBuilder()->Build()) {
Matt Menke7ca3f7c2020-09-08 17:06:58103 URLRequestFilter::GetInstance()->AddHostnameInterceptor(
104 "http", "foo.test", std::make_unique<NetURLRequestInterceptor>());
105 }
106
107 ~ProtocolHandlerUtilTest() override {
108 URLRequestFilter::GetInstance()->ClearHandlers();
droger476922e02015-03-10 17:17:52109 }
110
maksim.sisov5af90982016-09-05 11:26:28111 void OnResponseStarted(URLRequest* request, int net_error) override {}
droger476922e02015-03-10 17:17:52112 void OnReadCompleted(URLRequest* request, int bytes_read) override {}
113
114 protected:
Gabriel Charette7bde04d2019-09-05 12:57:30115 base::test::SingleThreadTaskEnvironment task_environment_;
dcheng942f39d72016-04-07 21:11:23116 std::unique_ptr<URLRequestContext> request_context_;
droger476922e02015-03-10 17:17:52117};
118
119} // namespace
120
droger476922e02015-03-10 17:17:52121TEST_F(ProtocolHandlerUtilTest, GetResponseHttpTest) {
122 // Create a request.
Matt Menke7ca3f7c2020-09-08 17:06:58123 GURL url("http://foo.test/");
dcheng942f39d72016-04-07 21:11:23124 std::unique_ptr<URLRequest> request(
davidben151423e2015-03-23 18:48:36125 request_context_->CreateRequest(url, DEFAULT_PRIORITY, this));
droger476922e02015-03-10 17:17:52126 request->Start();
127 // Create a response from the request.
128 NSURLResponse* response = GetNSURLResponseForRequest(request.get());
129 EXPECT_NSEQ([NSString stringWithUTF8String:kTextHtml], [response MIMEType]);
130 ASSERT_TRUE([response isKindOfClass:[NSHTTPURLResponse class]]);
131 NSHTTPURLResponse* http_response = (NSHTTPURLResponse*)response;
132 NSDictionary* headers = [http_response allHeaderFields];
133 // Check the headers, duplicates must be appended.
134 EXPECT_EQ(5u, [headers count]);
135 NSString* foo_header = [headers objectForKey:@"Foo"];
136 EXPECT_NSEQ(@"A,D,E", foo_header);
137 NSString* bar_header = [headers objectForKey:@"Bar"];
138 EXPECT_NSEQ(@"B,F", bar_header);
139 NSString* baz_header = [headers objectForKey:@"Baz"];
140 EXPECT_NSEQ(@"C", baz_header);
141 NSString* cache_header = [headers objectForKey:@"Cache-Control"];
142 EXPECT_NSEQ(@"no-store", cache_header); // Cache-Control is overridden.
143 // Check the status.
144 EXPECT_EQ(request->GetResponseCode(), [http_response statusCode]);
145}
146
147TEST_F(ProtocolHandlerUtilTest, BadHttpContentType) {
Matt Menke7ca3f7c2020-09-08 17:06:58148 // Create a request using the magic path that triggers a garbage
droger476922e02015-03-10 17:17:52149 // content-type in the test framework.
Matt Menke7ca3f7c2020-09-08 17:06:58150 GURL url("http://foo.test/badcontenttype");
dcheng942f39d72016-04-07 21:11:23151 std::unique_ptr<URLRequest> request(
davidben151423e2015-03-23 18:48:36152 request_context_->CreateRequest(url, DEFAULT_PRIORITY, this));
droger476922e02015-03-10 17:17:52153 request->Start();
154 // Create a response from the request.
155 @try {
156 GetNSURLResponseForRequest(request.get());
Sylvain Defresnecc2980bd2025-01-22 22:39:04157 } @catch (id exception) {
droger476922e02015-03-10 17:17:52158 FAIL() << "Exception while creating response";
159 }
160}
161
162TEST_F(ProtocolHandlerUtilTest, MultipleHttpContentType) {
Matt Menke7ca3f7c2020-09-08 17:06:58163 // Create a request using the magic path that triggers a garbage
droger476922e02015-03-10 17:17:52164 // content-type in the test framework.
Matt Menke7ca3f7c2020-09-08 17:06:58165 GURL url("http://foo.test/multiplecontenttype");
dcheng942f39d72016-04-07 21:11:23166 std::unique_ptr<URLRequest> request(
davidben151423e2015-03-23 18:48:36167 request_context_->CreateRequest(url, DEFAULT_PRIORITY, this));
droger476922e02015-03-10 17:17:52168 request->Start();
169 // Create a response from the request.
170 NSURLResponse* response = GetNSURLResponseForRequest(request.get());
171 EXPECT_NSEQ(@"text/plain", [response MIMEType]);
172 EXPECT_NSEQ(@"iso-8859-4", [response textEncodingName]);
173 NSHTTPURLResponse* http_response = (NSHTTPURLResponse*)response;
174 NSDictionary* headers = [http_response allHeaderFields];
175 NSString* content_type_header = [headers objectForKey:@"Content-Type"];
176 EXPECT_NSEQ(@"text/plain; charset=iso-8859-4", content_type_header);
177}
178
179TEST_F(ProtocolHandlerUtilTest, CopyHttpHeaders) {
Matt Menke7ca3f7c2020-09-08 17:06:58180 GURL url("http://foo.test/");
marq8e82dba2017-06-19 16:09:20181 NSMutableURLRequest* in_request =
182 [[NSMutableURLRequest alloc] initWithURL:NSURLWithGURL(url)];
droger476922e02015-03-10 17:17:52183 [in_request setAllHTTPHeaderFields:@{
Sylvain Defresnecc2980bd2025-01-22 22:39:04184 @"Referer" : @"referrer",
185 @"User-Agent" : @"secret",
186 @"Accept" : @"money/cash",
187 @"Foo" : @"bar",
droger476922e02015-03-10 17:17:52188 }];
dcheng942f39d72016-04-07 21:11:23189 std::unique_ptr<URLRequest> out_request(
davidben151423e2015-03-23 18:48:36190 request_context_->CreateRequest(url, DEFAULT_PRIORITY, nullptr));
droger476922e02015-03-10 17:17:52191 CopyHttpHeaders(in_request, out_request.get());
192
193 EXPECT_EQ("referrer", out_request->referrer());
194 const HttpRequestHeaders& headers = out_request->extra_request_headers();
droger476922e02015-03-10 17:17:52195 EXPECT_FALSE(headers.HasHeader("Content-Type")); // Only in POST requests.
Chris Fredrickson31c9e542024-08-14 15:47:03196 EXPECT_EQ("money/cash", headers.GetHeader("Accept"));
197 EXPECT_EQ("bar", headers.GetHeader("Foo"));
droger476922e02015-03-10 17:17:52198}
199
200TEST_F(ProtocolHandlerUtilTest, AddMissingHeaders) {
Matt Menke7ca3f7c2020-09-08 17:06:58201 GURL url("http://foo.test/");
marq8e82dba2017-06-19 16:09:20202 NSMutableURLRequest* in_request =
203 [[NSMutableURLRequest alloc] initWithURL:NSURLWithGURL(url)];
dcheng942f39d72016-04-07 21:11:23204 std::unique_ptr<URLRequest> out_request(
davidben151423e2015-03-23 18:48:36205 request_context_->CreateRequest(url, DEFAULT_PRIORITY, nullptr));
droger476922e02015-03-10 17:17:52206 out_request->set_method("POST");
kelsen liu05622172024-08-31 03:05:34207 auto reader = std::make_unique<UploadBytesElementReader>(
208 base::byte_span_from_cstring(""));
droger476922e02015-03-10 17:17:52209 out_request->set_upload(
Andrew Williams3fac2582024-09-18 18:37:39210 ElementsUploadDataStream::CreateWithReader(std::move(reader)));
droger476922e02015-03-10 17:17:52211 CopyHttpHeaders(in_request, out_request.get());
212
213 // Some headers are added by default if missing.
214 const HttpRequestHeaders& headers = out_request->extra_request_headers();
Chris Fredrickson31c9e542024-08-14 15:47:03215 EXPECT_EQ("*/*", headers.GetHeader("Accept"));
216 EXPECT_EQ("application/x-www-form-urlencoded",
217 headers.GetHeader("Content-Type"));
droger476922e02015-03-10 17:17:52218}
219
220} // namespace net