OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
| 5 #import <Cronet/Cronet.h> |
5 #import <Foundation/Foundation.h> | 6 #import <Foundation/Foundation.h> |
| 7 |
6 #include <stdint.h> | 8 #include <stdint.h> |
7 | 9 |
8 #import <CrNet/CrNet.h> | |
9 | |
10 #include "base/logging.h" | 10 #include "base/logging.h" |
11 #include "base/mac/scoped_nsobject.h" | 11 #include "base/mac/scoped_nsobject.h" |
12 #include "base/strings/sys_string_conversions.h" | 12 #include "base/strings/sys_string_conversions.h" |
13 #import "ios/third_party/gcdwebserver/src/GCDWebServer/Core/GCDWebServer.h" | 13 #include "components/cronet/ios/test/quic_test_server.h" |
14 #import "ios/third_party/gcdwebserver/src/GCDWebServer/Responses/GCDWebServerDat
aResponse.h" | 14 #include "components/cronet/ios/test/test_server.h" |
15 #include "net/base/mac/url_conversions.h" | 15 #include "net/base/mac/url_conversions.h" |
| 16 #include "net/base/net_errors.h" |
| 17 #include "net/cert/mock_cert_verifier.h" |
16 #include "testing/gtest/include/gtest/gtest.h" | 18 #include "testing/gtest/include/gtest/gtest.h" |
17 #include "testing/gtest_mac.h" | 19 #include "testing/gtest_mac.h" |
| 20 |
18 #include "url/gurl.h" | 21 #include "url/gurl.h" |
19 | 22 |
20 @interface TestDelegate : NSObject<NSURLSessionDataDelegate, | 23 @interface TestDelegate : NSObject<NSURLSessionDataDelegate, |
21 NSURLSessionDelegate, | 24 NSURLSessionDelegate, |
22 NSURLSessionTaskDelegate> | 25 NSURLSessionTaskDelegate> |
23 | 26 |
24 // Completion semaphore for this TestDelegate. When the request this delegate is | 27 // Completion semaphore for this TestDelegate. When the request this delegate is |
25 // attached to finishes (either successfully or with an error), this delegate | 28 // attached to finishes (either successfully or with an error), this delegate |
26 // signals this semaphore. | 29 // signals this semaphore. |
27 @property(assign, nonatomic) dispatch_semaphore_t semaphore; | 30 @property(assign, nonatomic) dispatch_semaphore_t semaphore; |
28 | 31 |
29 // Total count of bytes received by the request this delegate is attached to. | 32 // Body of response received by the request this delegate is attached to. |
30 @property(nonatomic) unsigned long receivedBytes; | 33 @property(retain, nonatomic) NSString* responseBody; |
31 | 34 |
32 // Error the request this delegate is attached to failed with, if any. | 35 // Error the request this delegate is attached to failed with, if any. |
33 @property(retain, nonatomic) NSError* error; | 36 @property(retain, nonatomic) NSError* error; |
34 | 37 |
35 @end | 38 @end |
36 | 39 |
37 @implementation TestDelegate | 40 @implementation TestDelegate |
38 @synthesize semaphore = _semaphore; | 41 @synthesize semaphore = _semaphore; |
39 @synthesize receivedBytes = _receivedBytes; | 42 @synthesize responseBody = _responseBody; |
40 @synthesize error = _error; | 43 @synthesize error = _error; |
41 | 44 |
42 - (id)init { | 45 - (id)init { |
43 if (self = [super init]) { | 46 if (self = [super init]) { |
44 _semaphore = dispatch_semaphore_create(0); | 47 _semaphore = dispatch_semaphore_create(0); |
45 } | 48 } |
46 return self; | 49 return self; |
47 } | 50 } |
48 | 51 |
49 - (void)dealloc { | 52 - (void)dealloc { |
(...skipping 27 matching lines...) Expand all Loading... |
77 dataTask:(NSURLSessionDataTask*)dataTask | 80 dataTask:(NSURLSessionDataTask*)dataTask |
78 didReceiveResponse:(NSURLResponse*)response | 81 didReceiveResponse:(NSURLResponse*)response |
79 completionHandler:(void (^)(NSURLSessionResponseDisposition disposition)) | 82 completionHandler:(void (^)(NSURLSessionResponseDisposition disposition)) |
80 completionHandler { | 83 completionHandler { |
81 completionHandler(NSURLSessionResponseAllow); | 84 completionHandler(NSURLSessionResponseAllow); |
82 } | 85 } |
83 | 86 |
84 - (void)URLSession:(NSURLSession*)session | 87 - (void)URLSession:(NSURLSession*)session |
85 dataTask:(NSURLSessionDataTask*)dataTask | 88 dataTask:(NSURLSessionDataTask*)dataTask |
86 didReceiveData:(NSData*)data { | 89 didReceiveData:(NSData*)data { |
87 _receivedBytes += (unsigned long)data.length; | 90 NSString* stringData = |
| 91 [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; |
| 92 if (_responseBody == nil) { |
| 93 _responseBody = stringData; |
| 94 } else { |
| 95 _responseBody = [_responseBody stringByAppendingString:stringData]; |
| 96 } |
88 } | 97 } |
89 | 98 |
90 - (void)URLSession:(NSURLSession*)session | 99 - (void)URLSession:(NSURLSession*)session |
91 dataTask:(NSURLSessionDataTask*)dataTask | 100 dataTask:(NSURLSessionDataTask*)dataTask |
92 willCacheResponse:(NSCachedURLResponse*)proposedResponse | 101 willCacheResponse:(NSCachedURLResponse*)proposedResponse |
93 completionHandler: | 102 completionHandler: |
94 (void (^)(NSCachedURLResponse* cachedResponse))completionHandler { | 103 (void (^)(NSCachedURLResponse* cachedResponse))completionHandler { |
95 completionHandler(proposedResponse); | 104 completionHandler(proposedResponse); |
96 } | 105 } |
97 | 106 |
98 @end | 107 @end |
99 | 108 |
| 109 namespace cronet { |
100 // base::TimeDelta would normally be ideal for this but it does not support | 110 // base::TimeDelta would normally be ideal for this but it does not support |
101 // nanosecond resolution. | 111 // nanosecond resolution. |
102 static const int64_t ns_in_second = 1000000000LL; | 112 static const int64_t ns_in_second = 1000000000LL; |
103 const char kUserAgent[] = "CrNetTest/1.0.0.0"; | 113 const char kUserAgent[] = "CronetTest/1.0.0.0"; |
| 114 |
| 115 // TODO(mef): Create common header file to declare this. |
| 116 void StartCronetIfNecessary(); |
104 | 117 |
105 class HttpTest : public ::testing::Test { | 118 class HttpTest : public ::testing::Test { |
106 protected: | 119 protected: |
107 HttpTest() {} | 120 HttpTest() {} |
108 ~HttpTest() override {} | 121 ~HttpTest() override {} |
109 | 122 |
110 void SetUp() override { | 123 void SetUp() override { |
111 [CrNet setUserAgent:base::SysUTF8ToNSString(kUserAgent) partial:NO]; | 124 StartCronetIfNecessary(); |
112 [CrNet install]; | |
113 NSURLSessionConfiguration* config = | 125 NSURLSessionConfiguration* config = |
114 [NSURLSessionConfiguration ephemeralSessionConfiguration]; | 126 [NSURLSessionConfiguration ephemeralSessionConfiguration]; |
115 [CrNet installIntoSessionConfiguration:config]; | 127 [Cronet installIntoSessionConfiguration:config]; |
116 delegate_.reset([[TestDelegate alloc] init]); | 128 delegate_.reset([[TestDelegate alloc] init]); |
117 NSURLSession* session = [NSURLSession sessionWithConfiguration:config | 129 NSURLSession* session = [NSURLSession sessionWithConfiguration:config |
118 delegate:delegate_ | 130 delegate:delegate_ |
119 delegateQueue:nil]; | 131 delegateQueue:nil]; |
120 // Take a reference to the session and store it so it doesn't get | 132 // Take a reference to the session and store it so it doesn't get |
121 // deallocated until this object does. | 133 // deallocated until this object does. |
122 session_.reset([session retain]); | 134 session_.reset([session retain]); |
123 web_server_.reset([[GCDWebServer alloc] init]); | 135 StartQuicTestServer(); |
| 136 TestServer::Start(); |
124 } | 137 } |
125 | 138 |
126 void TearDown() override { | 139 void TearDown() override { |
127 [CrNet uninstall]; | 140 ShutdownQuicTestServer(); |
128 [web_server_ stop]; | 141 TestServer::Shutdown(); |
129 } | |
130 | |
131 // Starts a GCDWebServer instance on localhost port 8080, and remembers the | |
132 // root URL for later; tests can use GetURL() to produce a URL referring to a | |
133 // specific resource under the root URL. | |
134 void StartWebServer() { | |
135 [web_server_ startWithPort:8080 bonjourName:nil]; | |
136 server_root_ = net::GURLWithNSURL([web_server_ serverURL]); | |
137 } | |
138 | |
139 // Registers a fixed response |text| to be returned to requests for |path|, | |
140 // which is relative to |server_root_|. | |
141 void RegisterPathText(const std::string& path, const std::string& text) { | |
142 NSString* nspath = base::SysUTF8ToNSString(path); | |
143 NSData* data = [NSData dataWithBytes:text.c_str() length:text.length()]; | |
144 [web_server_ addGETHandlerForPath:nspath | |
145 staticData:data | |
146 contentType:@"text/plain" | |
147 cacheAge:30]; | |
148 } | |
149 | |
150 void RegisterPathHandler(const std::string& path, | |
151 GCDWebServerProcessBlock handler) { | |
152 NSString* nspath = base::SysUTF8ToNSString(path); | |
153 [web_server_ addHandlerForMethod:@"GET" | |
154 path:nspath | |
155 requestClass:NSClassFromString(@"GCDWebServerRequest") | |
156 processBlock:handler]; | |
157 } | 142 } |
158 | 143 |
159 // Launches the supplied |task| and blocks until it completes, with a timeout | 144 // Launches the supplied |task| and blocks until it completes, with a timeout |
160 // of 1 second. | 145 // of 1 second. |
161 void StartDataTaskAndWaitForCompletion(NSURLSessionDataTask* task) { | 146 void StartDataTaskAndWaitForCompletion(NSURLSessionDataTask* task) { |
162 [task resume]; | 147 [task resume]; |
163 int64_t deadline_ns = 1 * ns_in_second; | 148 int64_t deadline_ns = 1 * ns_in_second; |
164 dispatch_semaphore_wait([delegate_ semaphore], | 149 dispatch_semaphore_wait([delegate_ semaphore], |
165 dispatch_time(DISPATCH_TIME_NOW, deadline_ns)); | 150 dispatch_time(DISPATCH_TIME_NOW, deadline_ns)); |
166 } | 151 } |
167 | 152 |
168 // Returns a URL to refer to the resource named |path| served by the test | |
169 // server. If |path| starts with a /, the leading / will be stripped. | |
170 GURL GetURL(const std::string& path) { | |
171 std::string real_path = path[0] == '/' ? path.substr(1) : path; | |
172 return server_root_.Resolve(real_path); | |
173 } | |
174 | |
175 // Some convenience functions for working with GCDWebServerRequest and | |
176 // GCDWebServerResponse. | |
177 | |
178 // Returns true if the value for the request header |header| is not nil and | |
179 // contains the string |target|. | |
180 bool HeaderValueContains(GCDWebServerRequest* request, | |
181 const std::string& header, | |
182 const std::string& target) { | |
183 NSString* key = base::SysUTF8ToNSString(header); | |
184 NSString* needle = base::SysUTF8ToNSString(target); | |
185 NSString* haystack = request.headers[key]; | |
186 if (!haystack) | |
187 return false; | |
188 return [haystack rangeOfString:needle].location != NSNotFound; | |
189 } | |
190 | |
191 base::scoped_nsobject<NSURLSession> session_; | 153 base::scoped_nsobject<NSURLSession> session_; |
192 base::scoped_nsobject<TestDelegate> delegate_; | 154 base::scoped_nsobject<TestDelegate> delegate_; |
193 | |
194 private: | |
195 base::scoped_nsobject<GCDWebServer> web_server_; | |
196 GURL server_root_; | |
197 }; | 155 }; |
198 | 156 |
199 TEST_F(HttpTest, NSURLSessionReceivesData) { | 157 TEST_F(HttpTest, NSURLSessionReceivesData) { |
200 const char kPath[] = "/foo"; | 158 NSURL* url = net::NSURLWithGURL(GURL(kTestServerUrl)); |
201 const char kData[] = "foobar"; | |
202 RegisterPathText(kPath, kData); | |
203 StartWebServer(); | |
204 | |
205 NSURL* url = net::NSURLWithGURL(GetURL(kPath)); | |
206 NSURLSessionDataTask* task = [session_ dataTaskWithURL:url]; | 159 NSURLSessionDataTask* task = [session_ dataTaskWithURL:url]; |
207 StartDataTaskAndWaitForCompletion(task); | 160 StartDataTaskAndWaitForCompletion(task); |
208 EXPECT_EQ(nil, [delegate_ error]); | 161 EXPECT_EQ(nil, [delegate_ error]); |
209 EXPECT_EQ(strlen(kData), [delegate_ receivedBytes]); | 162 EXPECT_STREQ(kHelloBodyValue, [[delegate_ responseBody] UTF8String]); |
| 163 } |
| 164 |
| 165 TEST_F(HttpTest, GetGlobalMetricsDeltas) { |
| 166 NSData* delta1 = [Cronet getGlobalMetricsDeltas]; |
| 167 |
| 168 NSURL* url = net::NSURLWithGURL(GURL(kTestServerUrl)); |
| 169 NSURLSessionDataTask* task = [session_ dataTaskWithURL:url]; |
| 170 StartDataTaskAndWaitForCompletion(task); |
| 171 EXPECT_EQ(nil, [delegate_ error]); |
| 172 EXPECT_STREQ(kHelloBodyValue, [[delegate_ responseBody] UTF8String]); |
| 173 |
| 174 NSData* delta2 = [Cronet getGlobalMetricsDeltas]; |
| 175 EXPECT_FALSE([delta2 isEqualToData:delta1]); |
210 } | 176 } |
211 | 177 |
212 TEST_F(HttpTest, SdchDisabledByDefault) { | 178 TEST_F(HttpTest, SdchDisabledByDefault) { |
213 const char kPath[] = "/sdchtest"; | 179 NSURL* url = |
214 RegisterPathHandler(kPath, | 180 net::NSURLWithGURL(GURL(TestServer::GetEchoHeaderURL("Accept-Encoding"))); |
215 ^GCDWebServerResponse* (GCDWebServerRequest* req) { | |
216 EXPECT_FALSE(HeaderValueContains(req, "Accept-Encoding", "sdch")); | |
217 return [GCDWebServerDataResponse responseWithText:@"woot!"]; | |
218 }); | |
219 StartWebServer(); | |
220 NSURL* url = net::NSURLWithGURL(GetURL(kPath)); | |
221 NSURLSessionDataTask* task = [session_ dataTaskWithURL:url]; | 181 NSURLSessionDataTask* task = [session_ dataTaskWithURL:url]; |
222 StartDataTaskAndWaitForCompletion(task); | 182 StartDataTaskAndWaitForCompletion(task); |
223 EXPECT_EQ(nil, [delegate_ error]); | 183 EXPECT_EQ(nil, [delegate_ error]); |
224 EXPECT_TRUE([delegate_ receivedBytes]); | 184 EXPECT_FALSE([[delegate_ responseBody] containsString:@"sdch"]); |
| 185 } |
| 186 |
| 187 TEST_F(HttpTest, NSURLSessionAcceptLanguage) { |
| 188 NSURL* url = |
| 189 net::NSURLWithGURL(GURL(TestServer::GetEchoHeaderURL("Accept-Language"))); |
| 190 NSURLSessionDataTask* task = [session_ dataTaskWithURL:url]; |
| 191 StartDataTaskAndWaitForCompletion(task); |
| 192 EXPECT_EQ(nil, [delegate_ error]); |
| 193 ASSERT_STREQ("en-US,en", [[delegate_ responseBody] UTF8String]); |
225 } | 194 } |
226 | 195 |
227 TEST_F(HttpTest, SetUserAgentIsExact) { | 196 TEST_F(HttpTest, SetUserAgentIsExact) { |
228 const char kPath[] = "/uatest"; | 197 NSURL* url = |
229 RegisterPathHandler(kPath, ^GCDWebServerResponse*(GCDWebServerRequest* req) { | 198 net::NSURLWithGURL(GURL(TestServer::GetEchoHeaderURL("User-Agent"))); |
230 EXPECT_STREQ(kUserAgent, | |
231 [[req.headers valueForKey:@"User-Agent"] UTF8String]); | |
232 return [GCDWebServerDataResponse responseWithText:@"yay!"]; | |
233 }); | |
234 StartWebServer(); | |
235 NSURL* url = net::NSURLWithGURL(GetURL(kPath)); | |
236 NSURLSessionDataTask* task = [session_ dataTaskWithURL:url]; | 199 NSURLSessionDataTask* task = [session_ dataTaskWithURL:url]; |
237 StartDataTaskAndWaitForCompletion(task); | 200 StartDataTaskAndWaitForCompletion(task); |
238 EXPECT_EQ(nil, [delegate_ error]); | 201 EXPECT_EQ(nil, [delegate_ error]); |
239 EXPECT_TRUE([delegate_ receivedBytes]); | 202 EXPECT_STREQ(kUserAgent, [[delegate_ responseBody] UTF8String]); |
240 } | 203 } |
241 | 204 |
242 // TODO(ellyjones): There needs to be a test that enabling SDCH works, but | 205 } // namespace cronet |
243 // because CrNet is static and 'uninstall' only disables it, there is no way to | |
244 // have an individual test enable or disable SDCH. | |
245 // Probably there is a way to get gtest tests to run in a separate process, but | |
246 // I'm not sure what it is. | |
OLD | NEW |