OLD | NEW |
(Empty) | |
| 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 |
| 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" |
| 25 |
| 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 |
| 34 |
| 35 namespace net { |
| 36 namespace { |
| 37 |
| 38 const int kResponseCode = 200; |
| 39 const char* kTextHtml = "text/html"; |
| 40 const char* kTextPlain = "text/plain"; |
| 41 const char* kAscii = "US-ASCII"; |
| 42 |
| 43 class HeadersURLRequestJob : public URLRequestJob { |
| 44 public: |
| 45 HeadersURLRequestJob(URLRequest* request) |
| 46 : URLRequestJob(request, nullptr) {} |
| 47 |
| 48 void Start() override { |
| 49 // Fills response headers and returns immediately. |
| 50 NotifyHeadersComplete(); |
| 51 } |
| 52 |
| 53 bool GetMimeType(std::string* mime_type) const override { |
| 54 *mime_type = GetContentTypeValue(); |
| 55 return true; |
| 56 } |
| 57 |
| 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 } |
| 85 |
| 86 int GetResponseCode() const override { |
| 87 return kResponseCode; |
| 88 } |
| 89 protected: |
| 90 ~HeadersURLRequestJob() override {} |
| 91 |
| 92 std::string GetContentTypeValue() const { |
| 93 if (request()->url().DomainIs("badcontenttype")) |
| 94 return "\xff"; |
| 95 return kTextHtml; |
| 96 } |
| 97 }; |
| 98 |
| 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 } |
| 106 }; |
| 107 |
| 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 } |
| 117 |
| 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); |
| 128 |
| 129 scoped_ptr<URLRequest> request( |
| 130 request_context_->CreateRequest(url, DEFAULT_PRIORITY, this, nullptr)); |
| 131 request->Start(); |
| 132 base::RunLoop loop; |
| 133 loop.RunUntilIdle(); |
| 134 return GetNSURLResponseForRequest(request.get()); |
| 135 } |
| 136 |
| 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 } |
| 146 |
| 147 void OnResponseStarted(URLRequest* request) override {} |
| 148 void OnReadCompleted(URLRequest* request, int bytes_read) override {} |
| 149 |
| 150 protected: |
| 151 base::MessageLoop loop_; |
| 152 URLRequestJobFactoryImpl job_factory_; |
| 153 scoped_ptr<URLRequestContext> request_context_; |
| 154 }; |
| 155 |
| 156 } // namespace |
| 157 |
| 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); |
| 166 } |
| 167 |
| 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, nullptr)); |
| 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]); |
| 192 } |
| 193 |
| 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, nullptr)); |
| 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 } |
| 208 } |
| 209 |
| 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, nullptr)); |
| 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); |
| 225 } |
| 226 |
| 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, nullptr)); |
| 239 CopyHttpHeaders(in_request, out_request.get()); |
| 240 |
| 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); |
| 250 } |
| 251 |
| 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, 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()); |
| 264 |
| 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); |
| 272 } |
| 273 |
| 274 } // namespace net |
OLD | NEW |