droger | 476922e0 | 2015-03-10 17:17:52 | [diff] [blame] | 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. |
| 4 | |
dcheng | 5e05b43 | 2016-01-14 04:41:45 | [diff] [blame] | 5 | #import "ios/net/protocol_handler_util.h" |
| 6 | |
dcheng | 942f39d7 | 2016-04-07 21:11:23 | [diff] [blame] | 7 | #include <memory> |
dcheng | 5e05b43 | 2016-01-14 04:41:45 | [diff] [blame] | 8 | #include <utility> |
| 9 | |
dcheng | 942f39d7 | 2016-04-07 21:11:23 | [diff] [blame] | 10 | #include "base/memory/ptr_util.h" |
droger | 476922e0 | 2015-03-10 17:17:52 | [diff] [blame] | 11 | #include "base/run_loop.h" |
| 12 | #include "base/strings/sys_string_conversions.h" |
Gabriel Charette | c710874 | 2019-08-23 03:31:40 | [diff] [blame] | 13 | #include "base/test/task_environment.h" |
droger | 476922e0 | 2015-03-10 17:17:52 | [diff] [blame] | 14 | #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" |
droger | 476922e0 | 2015-03-10 17:17:52 | [diff] [blame] | 19 | #include "net/url_request/url_request.h" |
Matt Menke | 7ca3f7c | 2020-09-08 17:06:58 | [diff] [blame] | 20 | #include "net/url_request/url_request_filter.h" |
| 21 | #include "net/url_request/url_request_interceptor.h" |
droger | 476922e0 | 2015-03-10 17:17:52 | [diff] [blame] | 22 | #include "net/url_request/url_request_job.h" |
droger | 476922e0 | 2015-03-10 17:17:52 | [diff] [blame] | 23 | #include "net/url_request/url_request_test_util.h" |
| 24 | #include "testing/gtest/include/gtest/gtest.h" |
| 25 | #include "testing/gtest_mac.h" |
Sylvain Defresne | 64778080 | 2017-10-09 14:59:27 | [diff] [blame] | 26 | #include "testing/platform_test.h" |
droger | 476922e0 | 2015-03-10 17:17:52 | [diff] [blame] | 27 | #include "url/gurl.h" |
| 28 | |
marq | 8e82dba | 2017-06-19 16:09:20 | [diff] [blame] | 29 | #if !defined(__has_feature) || !__has_feature(objc_arc) |
| 30 | #error "This file requires ARC support." |
| 31 | #endif |
| 32 | |
droger | 476922e0 | 2015-03-10 17:17:52 | [diff] [blame] | 33 | // 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 | |
| 42 | namespace net { |
| 43 | namespace { |
| 44 | |
droger | 476922e0 | 2015-03-10 17:17:52 | [diff] [blame] | 45 | const char* kTextHtml = "text/html"; |
droger | 476922e0 | 2015-03-10 17:17:52 | [diff] [blame] | 46 | |
| 47 | class HeadersURLRequestJob : public URLRequestJob { |
| 48 | public: |
Matt Menke | fd97885 | 2020-09-15 16:00:57 | [diff] [blame^] | 49 | explicit HeadersURLRequestJob(URLRequest* request) : URLRequestJob(request) {} |
| 50 | |
| 51 | ~HeadersURLRequestJob() override {} |
droger | 476922e0 | 2015-03-10 17:17:52 | [diff] [blame] | 52 | |
| 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 Menke | 7ca3f7c | 2020-09-08 17:06:58 | [diff] [blame] | 69 | if (request()->url().path_piece() == "/multiplecontenttype") { |
droger | 476922e0 | 2015-03-10 17:17:52 | [diff] [blame] | 70 | 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 | |
droger | 476922e0 | 2015-03-10 17:17:52 | [diff] [blame] | 91 | protected: |
droger | 476922e0 | 2015-03-10 17:17:52 | [diff] [blame] | 92 | std::string GetContentTypeValue() const { |
Matt Menke | 7ca3f7c | 2020-09-08 17:06:58 | [diff] [blame] | 93 | if (request()->url().path_piece() == "/badcontenttype") |
droger | 476922e0 | 2015-03-10 17:17:52 | [diff] [blame] | 94 | return "\xff"; |
| 95 | return kTextHtml; |
| 96 | } |
| 97 | }; |
| 98 | |
Matt Menke | 7ca3f7c | 2020-09-08 17:06:58 | [diff] [blame] | 99 | class NetURLRequestInterceptor : public URLRequestInterceptor { |
droger | 476922e0 | 2015-03-10 17:17:52 | [diff] [blame] | 100 | public: |
Matt Menke | fd97885 | 2020-09-15 16:00:57 | [diff] [blame^] | 101 | std::unique_ptr<URLRequestJob> MaybeInterceptRequest( |
| 102 | URLRequest* request) const override { |
| 103 | return std::make_unique<HeadersURLRequestJob>(request); |
droger | 476922e0 | 2015-03-10 17:17:52 | [diff] [blame] | 104 | } |
| 105 | }; |
| 106 | |
Sylvain Defresne | 64778080 | 2017-10-09 14:59:27 | [diff] [blame] | 107 | class ProtocolHandlerUtilTest : public PlatformTest, |
droger | 476922e0 | 2015-03-10 17:17:52 | [diff] [blame] | 108 | public URLRequest::Delegate { |
| 109 | public: |
Matt Menke | 7ca3f7c | 2020-09-08 17:06:58 | [diff] [blame] | 110 | 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(); |
droger | 476922e0 | 2015-03-10 17:17:52 | [diff] [blame] | 119 | } |
| 120 | |
maksim.sisov | 5af9098 | 2016-09-05 11:26:28 | [diff] [blame] | 121 | void OnResponseStarted(URLRequest* request, int net_error) override {} |
droger | 476922e0 | 2015-03-10 17:17:52 | [diff] [blame] | 122 | void OnReadCompleted(URLRequest* request, int bytes_read) override {} |
| 123 | |
| 124 | protected: |
Gabriel Charette | 7bde04d | 2019-09-05 12:57:30 | [diff] [blame] | 125 | base::test::SingleThreadTaskEnvironment task_environment_; |
dcheng | 942f39d7 | 2016-04-07 21:11:23 | [diff] [blame] | 126 | std::unique_ptr<URLRequestContext> request_context_; |
droger | 476922e0 | 2015-03-10 17:17:52 | [diff] [blame] | 127 | }; |
| 128 | |
| 129 | } // namespace |
| 130 | |
droger | 476922e0 | 2015-03-10 17:17:52 | [diff] [blame] | 131 | TEST_F(ProtocolHandlerUtilTest, GetResponseHttpTest) { |
| 132 | // Create a request. |
Matt Menke | 7ca3f7c | 2020-09-08 17:06:58 | [diff] [blame] | 133 | GURL url("http://foo.test/"); |
dcheng | 942f39d7 | 2016-04-07 21:11:23 | [diff] [blame] | 134 | std::unique_ptr<URLRequest> request( |
davidben | 151423e | 2015-03-23 18:48:36 | [diff] [blame] | 135 | request_context_->CreateRequest(url, DEFAULT_PRIORITY, this)); |
droger | 476922e0 | 2015-03-10 17:17:52 | [diff] [blame] | 136 | 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 | |
| 157 | TEST_F(ProtocolHandlerUtilTest, BadHttpContentType) { |
Matt Menke | 7ca3f7c | 2020-09-08 17:06:58 | [diff] [blame] | 158 | // Create a request using the magic path that triggers a garbage |
droger | 476922e0 | 2015-03-10 17:17:52 | [diff] [blame] | 159 | // content-type in the test framework. |
Matt Menke | 7ca3f7c | 2020-09-08 17:06:58 | [diff] [blame] | 160 | GURL url("http://foo.test/badcontenttype"); |
dcheng | 942f39d7 | 2016-04-07 21:11:23 | [diff] [blame] | 161 | std::unique_ptr<URLRequest> request( |
davidben | 151423e | 2015-03-23 18:48:36 | [diff] [blame] | 162 | request_context_->CreateRequest(url, DEFAULT_PRIORITY, this)); |
droger | 476922e0 | 2015-03-10 17:17:52 | [diff] [blame] | 163 | 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 | |
| 173 | TEST_F(ProtocolHandlerUtilTest, MultipleHttpContentType) { |
Matt Menke | 7ca3f7c | 2020-09-08 17:06:58 | [diff] [blame] | 174 | // Create a request using the magic path that triggers a garbage |
droger | 476922e0 | 2015-03-10 17:17:52 | [diff] [blame] | 175 | // content-type in the test framework. |
Matt Menke | 7ca3f7c | 2020-09-08 17:06:58 | [diff] [blame] | 176 | GURL url("http://foo.test/multiplecontenttype"); |
dcheng | 942f39d7 | 2016-04-07 21:11:23 | [diff] [blame] | 177 | std::unique_ptr<URLRequest> request( |
davidben | 151423e | 2015-03-23 18:48:36 | [diff] [blame] | 178 | request_context_->CreateRequest(url, DEFAULT_PRIORITY, this)); |
droger | 476922e0 | 2015-03-10 17:17:52 | [diff] [blame] | 179 | 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 | |
| 190 | TEST_F(ProtocolHandlerUtilTest, CopyHttpHeaders) { |
Matt Menke | 7ca3f7c | 2020-09-08 17:06:58 | [diff] [blame] | 191 | GURL url("http://foo.test/"); |
marq | 8e82dba | 2017-06-19 16:09:20 | [diff] [blame] | 192 | NSMutableURLRequest* in_request = |
| 193 | [[NSMutableURLRequest alloc] initWithURL:NSURLWithGURL(url)]; |
droger | 476922e0 | 2015-03-10 17:17:52 | [diff] [blame] | 194 | [in_request setAllHTTPHeaderFields:@{ |
| 195 | @"Referer" : @"referrer", |
| 196 | @"User-Agent" : @"secret", |
| 197 | @"Accept" : @"money/cash", |
| 198 | @"Foo" : @"bar", |
| 199 | }]; |
dcheng | 942f39d7 | 2016-04-07 21:11:23 | [diff] [blame] | 200 | std::unique_ptr<URLRequest> out_request( |
davidben | 151423e | 2015-03-23 18:48:36 | [diff] [blame] | 201 | request_context_->CreateRequest(url, DEFAULT_PRIORITY, nullptr)); |
droger | 476922e0 | 2015-03-10 17:17:52 | [diff] [blame] | 202 | CopyHttpHeaders(in_request, out_request.get()); |
| 203 | |
| 204 | EXPECT_EQ("referrer", out_request->referrer()); |
| 205 | const HttpRequestHeaders& headers = out_request->extra_request_headers(); |
droger | 476922e0 | 2015-03-10 17:17:52 | [diff] [blame] | 206 | 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 | |
| 214 | TEST_F(ProtocolHandlerUtilTest, AddMissingHeaders) { |
Matt Menke | 7ca3f7c | 2020-09-08 17:06:58 | [diff] [blame] | 215 | GURL url("http://foo.test/"); |
marq | 8e82dba | 2017-06-19 16:09:20 | [diff] [blame] | 216 | NSMutableURLRequest* in_request = |
| 217 | [[NSMutableURLRequest alloc] initWithURL:NSURLWithGURL(url)]; |
dcheng | 942f39d7 | 2016-04-07 21:11:23 | [diff] [blame] | 218 | std::unique_ptr<URLRequest> out_request( |
davidben | 151423e | 2015-03-23 18:48:36 | [diff] [blame] | 219 | request_context_->CreateRequest(url, DEFAULT_PRIORITY, nullptr)); |
droger | 476922e0 | 2015-03-10 17:17:52 | [diff] [blame] | 220 | out_request->set_method("POST"); |
dcheng | 942f39d7 | 2016-04-07 21:11:23 | [diff] [blame] | 221 | std::unique_ptr<UploadElementReader> reader( |
droger | 476922e0 | 2015-03-10 17:17:52 | [diff] [blame] | 222 | new UploadBytesElementReader(nullptr, 0)); |
| 223 | out_request->set_upload( |
dcheng | 5e05b43 | 2016-01-14 04:41:45 | [diff] [blame] | 224 | ElementsUploadDataStream::CreateWithReader(std::move(reader), 0)); |
droger | 476922e0 | 2015-03-10 17:17:52 | [diff] [blame] | 225 | 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 |