blob: 6e8338e9ae2ca51f2b867dc642bb10ae6f5ae4e1 [file] [log] [blame]
Paul Stewart188a84a2012-01-20 16:28:15 -08001// Copyright (c) 2012 The Chromium OS 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 "shill/http_request.h"
6
7#include <netinet/in.h>
8
9#include <string>
10#include <vector>
11
Eric Shienbrood3e20a232012-02-16 11:35:56 -050012#include <base/bind.h>
Paul Stewart188a84a2012-01-20 16:28:15 -080013#include <base/stringprintf.h>
14#include <gtest/gtest.h>
15
16#include "shill/ip_address.h"
17#include "shill/http_url.h"
18#include "shill/mock_async_connection.h"
19#include "shill/mock_connection.h"
20#include "shill/mock_control.h"
21#include "shill/mock_device_info.h"
22#include "shill/mock_dns_client.h"
23#include "shill/mock_event_dispatcher.h"
24#include "shill/mock_sockets.h"
25
Eric Shienbrood3e20a232012-02-16 11:35:56 -050026using base::Bind;
27using base::Callback;
Paul Stewart188a84a2012-01-20 16:28:15 -080028using base::StringPrintf;
Eric Shienbrood3e20a232012-02-16 11:35:56 -050029using base::Unretained;
Paul Stewart188a84a2012-01-20 16:28:15 -080030using std::string;
31using std::vector;
32using ::testing::_;
33using ::testing::AtLeast;
34using ::testing::DoAll;
35using ::testing::Invoke;
36using ::testing::NiceMock;
37using ::testing::Return;
38using ::testing::ReturnArg;
39using ::testing::ReturnNew;
40using ::testing::ReturnRef;
41using ::testing::SetArgumentPointee;
42using ::testing::StrEq;
43using ::testing::StrictMock;
44using ::testing::Test;
45
46namespace shill {
47
48namespace {
49const char kTextSiteName[] = "www.chromium.org";
50const char kTextURL[] = "http://www.chromium.org/path/to/resource";
51const char kNumericURL[] = "http://10.1.1.1";
52const char kPath[] = "/path/to/resource";
53const char kBadURL[] = "xxx";
54const char kInterfaceName[] = "int0";
55const char kDNSServer0[] = "8.8.8.8";
56const char kDNSServer1[] = "8.8.4.4";
57const char *kDNSServers[] = { kDNSServer0, kDNSServer1 };
58const char kServerAddress[] = "10.1.1.1";
59const int kServerFD = 10203;
60const int kServerPort = 80;
61} // namespace {}
62
63MATCHER_P(IsIPAddress, address, "") {
64 IPAddress ip_address(IPAddress::kFamilyIPv4);
65 EXPECT_TRUE(ip_address.SetAddressFromString(address));
66 return ip_address.Equals(arg);
67}
68
Paul Stewartbdb02e62012-02-22 16:24:33 -080069MATCHER_P(ByteStringMatches, byte_string, "") {
70 return byte_string.Equals(arg);
71}
72
Eric Shienbrood3e20a232012-02-16 11:35:56 -050073MATCHER_P(CallbackEq, callback, "") {
74 return arg.Equals(callback);
75}
76
Paul Stewart188a84a2012-01-20 16:28:15 -080077class HTTPRequestTest : public Test {
78 public:
79 HTTPRequestTest()
80 : interface_name_(kInterfaceName),
81 server_async_connection_(new StrictMock<MockAsyncConnection>()),
82 dns_servers_(kDNSServers, kDNSServers + 2),
83 dns_client_(new StrictMock<MockDNSClient>()),
84 device_info_(new NiceMock<MockDeviceInfo>(
85 &control_,
86 reinterpret_cast<EventDispatcher*>(NULL),
87 reinterpret_cast<Metrics*>(NULL),
88 reinterpret_cast<Manager*>(NULL))),
89 connection_(new StrictMock<MockConnection>(device_info_.get())) { }
90 protected:
91 class CallbackTarget {
92 public:
93 CallbackTarget()
94 : read_event_callback_(
Eric Shienbrood3e20a232012-02-16 11:35:56 -050095 Bind(&CallbackTarget::ReadEventCallTarget, Unretained(this))),
Paul Stewart188a84a2012-01-20 16:28:15 -080096 result_callback_(
Eric Shienbrood3e20a232012-02-16 11:35:56 -050097 Bind(&CallbackTarget::ResultCallTarget, Unretained(this))) {}
Paul Stewart188a84a2012-01-20 16:28:15 -080098
Paul Stewartbdb02e62012-02-22 16:24:33 -080099 MOCK_METHOD1(ReadEventCallTarget, void(const ByteString &response_data));
100 MOCK_METHOD2(ResultCallTarget, void(HTTPRequest::Result result,
101 const ByteString &response_data));
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500102 const Callback<void(const ByteString &)> &read_event_callback() {
103 return read_event_callback_;
Paul Stewart188a84a2012-01-20 16:28:15 -0800104 }
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500105 const Callback<void(HTTPRequest::Result,
106 const ByteString &)> &result_callback() {
107 return result_callback_;
Paul Stewart188a84a2012-01-20 16:28:15 -0800108 }
109
110 private:
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500111 Callback<void(const ByteString &)> read_event_callback_;
112 Callback<void(HTTPRequest::Result, const ByteString &)> result_callback_;
Paul Stewart188a84a2012-01-20 16:28:15 -0800113 };
114
115 virtual void SetUp() {
116 EXPECT_CALL(*connection_.get(), interface_name())
117 .WillRepeatedly(ReturnRef(interface_name_));
118 EXPECT_CALL(*connection_.get(), dns_servers())
119 .WillRepeatedly(ReturnRef(dns_servers_));
120
121 request_.reset(new HTTPRequest(connection_, &dispatcher_, &sockets_));
122 // Passes ownership.
123 request_->dns_client_.reset(dns_client_);
124 // Passes ownership.
125 request_->server_async_connection_.reset(server_async_connection_);
126 }
127 virtual void TearDown() {
128 if (request_->is_running_) {
129 ExpectStop();
130
131 // Subtle: Make sure the finalization of the request happens while our
132 // expectations are still active.
133 request_.reset();
134 }
135 }
136 size_t FindInRequestData(const string &find_string) {
137 string request_string(
138 reinterpret_cast<char *>(request_->request_data_.GetData()),
139 request_->request_data_.GetLength());
140 return request_string.find(find_string);
141 }
142 // Accessors
143 const ByteString &GetRequestData() {
144 return request_->request_data_;
145 }
146 HTTPRequest *request() { return request_.get(); }
147 MockSockets &sockets() { return sockets_; }
148
149 // Expectations
150 void ExpectReset() {
151 EXPECT_EQ(connection_.get(), request_->connection_.get());
152 EXPECT_EQ(&dispatcher_, request_->dispatcher_);
153 EXPECT_EQ(&sockets_, request_->sockets_);
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500154 EXPECT_TRUE(request_->result_callback_.is_null());
155 EXPECT_TRUE(request_->read_event_callback_.is_null());
156 EXPECT_FALSE(request_->weak_ptr_factory_.HasWeakPtrs());
Paul Stewart188a84a2012-01-20 16:28:15 -0800157 EXPECT_FALSE(request_->read_server_handler_.get());
158 EXPECT_FALSE(request_->write_server_handler_.get());
159 EXPECT_EQ(dns_client_, request_->dns_client_.get());
160 EXPECT_EQ(server_async_connection_,
161 request_->server_async_connection_.get());
162 EXPECT_TRUE(request_->server_hostname_.empty());
163 EXPECT_EQ(-1, request_->server_port_);
164 EXPECT_EQ(-1, request_->server_socket_);
165 EXPECT_EQ(HTTPRequest::kResultUnknown, request_->timeout_result_);
166 EXPECT_TRUE(request_->request_data_.IsEmpty());
167 EXPECT_TRUE(request_->response_data_.IsEmpty());
168 EXPECT_FALSE(request_->is_running_);
169 }
170 void ExpectStop() {
171 if (request_->server_socket_ != -1) {
172 EXPECT_CALL(sockets(), Close(kServerFD))
173 .WillOnce(Return(0));
174 }
175 EXPECT_CALL(*dns_client_, Stop())
176 .Times(AtLeast(1));
177 EXPECT_CALL(*server_async_connection_, Stop())
178 .Times(AtLeast(1));
179 EXPECT_CALL(*connection_.get(), ReleaseRouting());
180 }
181 void ExpectSetTimeout(int timeout) {
182 EXPECT_CALL(dispatcher_, PostDelayedTask(_, timeout * 1000))
183 .WillOnce(Return(true));
184 }
185 void ExpectSetConnectTimeout() {
186 ExpectSetTimeout(HTTPRequest::kConnectTimeoutSeconds);
187 }
188 void ExpectSetInputTimeout() {
189 ExpectSetTimeout(HTTPRequest::kInputTimeoutSeconds);
190 }
191 void ExpectInResponse(const string &expected_response_data) {
192 string response_string(
193 reinterpret_cast<char *>(request_->response_data_.GetData()),
194 request_->response_data_.GetLength());
195 EXPECT_NE(string::npos, response_string.find(expected_response_data));
196 }
197 void ExpectDNSRequest(const string &host, bool return_value) {
Paul Stewartbdb02e62012-02-22 16:24:33 -0800198 EXPECT_CALL(*dns_client_, Start(StrEq(host), _))
Paul Stewart188a84a2012-01-20 16:28:15 -0800199 .WillOnce(Return(return_value));
200 }
Paul Stewart188a84a2012-01-20 16:28:15 -0800201 void ExpectAsyncConnect(const string &address, int port,
202 bool return_value) {
203 EXPECT_CALL(*server_async_connection_, Start(IsIPAddress(address), port))
204 .WillOnce(Return(return_value));
205 if (return_value) {
206 ExpectSetConnectTimeout();
207 }
208 }
209 void InvokeSyncConnect(const IPAddress &/*address*/, int /*port*/) {
210 CallConnectCompletion(true, kServerFD);
211 }
212 void CallConnectCompletion(bool success, int fd) {
213 request_->OnConnectCompletion(success, fd);
214 }
215 void ExpectSyncConnect(const string &address, int port) {
216 EXPECT_CALL(*server_async_connection_, Start(IsIPAddress(address), port))
217 .WillOnce(DoAll(Invoke(this, &HTTPRequestTest::InvokeSyncConnect),
218 Return(true)));
219 }
220 void ExpectConnectFailure() {
221 EXPECT_CALL(*server_async_connection_, Start(_, _))
222 .WillOnce(Return(false));
223 }
224 void ExpectMonitorServerInput() {
225 EXPECT_CALL(dispatcher_,
226 CreateInputHandler(kServerFD,
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500227 CallbackEq(request_->read_server_callback_)))
Paul Stewart188a84a2012-01-20 16:28:15 -0800228 .WillOnce(ReturnNew<IOHandler>());
229 ExpectSetInputTimeout();
230 }
231 void ExpectMonitorServerOutput() {
232 EXPECT_CALL(dispatcher_,
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500233 CreateReadyHandler(
234 kServerFD, IOHandler::kModeOutput,
235 CallbackEq(request_->write_server_callback_)))
Paul Stewart188a84a2012-01-20 16:28:15 -0800236 .WillOnce(ReturnNew<IOHandler>());
237 ExpectSetInputTimeout();
238 }
239 void ExpectRouteRequest() {
240 EXPECT_CALL(*connection_.get(), RequestRouting());
241 }
242 void ExpectRouteRelease() {
243 EXPECT_CALL(*connection_.get(), ReleaseRouting());
244 }
245 void ExpectResultCallback(HTTPRequest::Result result) {
Paul Stewartbdb02e62012-02-22 16:24:33 -0800246 EXPECT_CALL(target_, ResultCallTarget(result, _));
Paul Stewart188a84a2012-01-20 16:28:15 -0800247 }
Paul Stewartbdb02e62012-02-22 16:24:33 -0800248 void InvokeResultVerify(HTTPRequest::Result result,
249 const ByteString &response_data) {
Paul Stewart188a84a2012-01-20 16:28:15 -0800250 EXPECT_EQ(HTTPRequest::kResultSuccess, result);
Paul Stewartbdb02e62012-02-22 16:24:33 -0800251 EXPECT_TRUE(expected_response_.Equals(response_data));
Paul Stewart188a84a2012-01-20 16:28:15 -0800252 }
253 void ExpectResultCallbackWithResponse(const string &response) {
254 expected_response_ = ByteString(response, false);
Paul Stewartbdb02e62012-02-22 16:24:33 -0800255 EXPECT_CALL(target_, ResultCallTarget(HTTPRequest::kResultSuccess, _))
Paul Stewart188a84a2012-01-20 16:28:15 -0800256 .WillOnce(Invoke(this, &HTTPRequestTest::InvokeResultVerify));
257 }
Paul Stewartbdb02e62012-02-22 16:24:33 -0800258 void ExpectReadEventCallback(const string &response) {
259 ByteString response_data(response, false);
260 EXPECT_CALL(target_, ReadEventCallTarget(ByteStringMatches(response_data)));
Paul Stewart188a84a2012-01-20 16:28:15 -0800261 }
Paul Stewartbdb02e62012-02-22 16:24:33 -0800262 void GetDNSResultFailure(const string &error_msg) {
263 Error error(Error::kOperationFailed, error_msg);
264 IPAddress address(IPAddress::kFamilyUnknown);
265 request_->GetDNSResult(error, address);
266 }
267 void GetDNSResultSuccess(const IPAddress &address) {
268 Error error;
269 request_->GetDNSResult(error, address);
Paul Stewart188a84a2012-01-20 16:28:15 -0800270 }
271 void OnConnectCompletion(bool result, int sockfd) {
272 request_->OnConnectCompletion(result, sockfd);
273 }
274 void ReadFromServer(const string &data) {
275 const unsigned char *ptr =
276 reinterpret_cast<const unsigned char *>(data.c_str());
277 vector<unsigned char> data_writable(ptr, ptr + data.length());
278 InputData server_data(data_writable.data(), data_writable.size());
279 request_->ReadFromServer(&server_data);
280 }
281 void WriteToServer(int fd) {
282 request_->WriteToServer(fd);
283 }
284 HTTPRequest::Result StartRequest(const string &url) {
285 HTTPURL http_url;
286 EXPECT_TRUE(http_url.ParseFromString(url));
287 return request_->Start(http_url,
288 target_.read_event_callback(),
289 target_.result_callback());
290 }
291 void SetupConnectWithURL(const string &url, const string &expected_hostname) {
292 ExpectRouteRequest();
293 ExpectDNSRequest(expected_hostname, true);
294 EXPECT_EQ(HTTPRequest::kResultInProgress, StartRequest(url));
295 IPAddress addr(IPAddress::kFamilyIPv4);
296 EXPECT_TRUE(addr.SetAddressFromString(kServerAddress));
Paul Stewartbdb02e62012-02-22 16:24:33 -0800297 GetDNSResultSuccess(addr);
Paul Stewart188a84a2012-01-20 16:28:15 -0800298 }
299 void SetupConnect() {
300 SetupConnectWithURL(kTextURL, kTextSiteName);
301 }
302 void SetupConnectAsync() {
303 ExpectAsyncConnect(kServerAddress, kServerPort, true);
304 SetupConnect();
305 }
306 void SetupConnectComplete() {
307 SetupConnectAsync();
308 ExpectMonitorServerOutput();
309 OnConnectCompletion(true, kServerFD);
310 }
311 void CallTimeoutTask() {
312 request_->TimeoutTask();
313 }
314
315 private:
316 const string interface_name_;
317 // Owned by the HTTPRequest, but tracked here for EXPECT().
318 StrictMock<MockAsyncConnection> *server_async_connection_;
319 vector<string> dns_servers_;
320 // Owned by the HTTPRequest, but tracked here for EXPECT().
321 StrictMock<MockDNSClient> *dns_client_;
322 StrictMock<MockEventDispatcher> dispatcher_;
323 MockControl control_;
324 scoped_ptr<MockDeviceInfo> device_info_;
325 scoped_refptr<MockConnection> connection_;
326 scoped_ptr<HTTPRequest> request_;
327 StrictMock<MockSockets> sockets_;
328 StrictMock<CallbackTarget> target_;
329 ByteString expected_response_;
330};
331
332TEST_F(HTTPRequestTest, Constructor) {
333 ExpectReset();
334}
335
336
337TEST_F(HTTPRequestTest, FailConnectNumericSynchronous) {
338 ExpectRouteRequest();
339 ExpectConnectFailure();
340 ExpectStop();
341 EXPECT_EQ(HTTPRequest::kResultConnectionFailure, StartRequest(kNumericURL));
342 ExpectReset();
343}
344
345TEST_F(HTTPRequestTest, FailConnectNumericAsynchronous) {
346 ExpectRouteRequest();
347 ExpectAsyncConnect(kServerAddress, HTTPURL::kDefaultHTTPPort, true);
348 EXPECT_EQ(HTTPRequest::kResultInProgress, StartRequest(kNumericURL));
349 ExpectResultCallback(HTTPRequest::kResultConnectionFailure);
350 ExpectStop();
351 CallConnectCompletion(false, -1);
352 ExpectReset();
353}
354
355TEST_F(HTTPRequestTest, FailConnectNumericTimeout) {
356 ExpectRouteRequest();
357 ExpectAsyncConnect(kServerAddress, HTTPURL::kDefaultHTTPPort, true);
358 EXPECT_EQ(HTTPRequest::kResultInProgress, StartRequest(kNumericURL));
359 ExpectResultCallback(HTTPRequest::kResultConnectionTimeout);
360 ExpectStop();
361 CallTimeoutTask();
362 ExpectReset();
363}
364
365TEST_F(HTTPRequestTest, SyncConnectNumeric) {
366 ExpectRouteRequest();
367 ExpectSyncConnect(kServerAddress, HTTPURL::kDefaultHTTPPort);
368 ExpectMonitorServerOutput();
369 EXPECT_EQ(HTTPRequest::kResultInProgress, StartRequest(kNumericURL));
370}
371
372TEST_F(HTTPRequestTest, FailDNSStart) {
373 ExpectRouteRequest();
374 ExpectDNSRequest(kTextSiteName, false);
375 ExpectStop();
376 EXPECT_EQ(HTTPRequest::kResultDNSFailure, StartRequest(kTextURL));
377 ExpectReset();
378}
379
380TEST_F(HTTPRequestTest, FailDNSFailure) {
381 ExpectRouteRequest();
382 ExpectDNSRequest(kTextSiteName, true);
383 EXPECT_EQ(HTTPRequest::kResultInProgress, StartRequest(kTextURL));
Paul Stewart188a84a2012-01-20 16:28:15 -0800384 ExpectResultCallback(HTTPRequest::kResultDNSFailure);
385 ExpectStop();
Paul Stewartbdb02e62012-02-22 16:24:33 -0800386 GetDNSResultFailure(DNSClient::kErrorNoData);
Paul Stewart188a84a2012-01-20 16:28:15 -0800387 ExpectReset();
388}
389
390TEST_F(HTTPRequestTest, FailDNSTimeout) {
391 ExpectRouteRequest();
392 ExpectDNSRequest(kTextSiteName, true);
393 EXPECT_EQ(HTTPRequest::kResultInProgress, StartRequest(kTextURL));
Paul Stewart188a84a2012-01-20 16:28:15 -0800394 ExpectResultCallback(HTTPRequest::kResultDNSTimeout);
395 ExpectStop();
Paul Stewartbdb02e62012-02-22 16:24:33 -0800396 const string error(DNSClient::kErrorTimedOut);
397 GetDNSResultFailure(error);
Paul Stewart188a84a2012-01-20 16:28:15 -0800398 ExpectReset();
399}
400
401TEST_F(HTTPRequestTest, FailConnectText) {
402 ExpectConnectFailure();
403 ExpectResultCallback(HTTPRequest::kResultConnectionFailure);
404 ExpectStop();
405 SetupConnect();
406 ExpectReset();
407}
408
409TEST_F(HTTPRequestTest, ConnectComplete) {
410 SetupConnectComplete();
411}
412
413TEST_F(HTTPRequestTest, RequestTimeout) {
414 SetupConnectComplete();
415 ExpectResultCallback(HTTPRequest::kResultRequestTimeout);
416 ExpectStop();
417 CallTimeoutTask();
418}
419
420TEST_F(HTTPRequestTest, RequestData) {
421 SetupConnectComplete();
422 EXPECT_EQ(0, FindInRequestData(string("GET ") + kPath));
423 EXPECT_NE(string::npos,
424 FindInRequestData(string("\r\nHost: ") + kTextSiteName));
425 ByteString request_data = GetRequestData();
426 EXPECT_CALL(sockets(), Send(kServerFD, _, request_data.GetLength(), 0))
427 .WillOnce(Return(request_data.GetLength() - 1));
428 ExpectSetInputTimeout();
429 WriteToServer(kServerFD);
430 EXPECT_CALL(sockets(), Send(kServerFD, _, 1, 0))
431 .WillOnce(Return(1));
432 ExpectMonitorServerInput();
433 WriteToServer(kServerFD);
434}
435
436TEST_F(HTTPRequestTest, ResponseTimeout) {
437 SetupConnectComplete();
438 ByteString request_data = GetRequestData();
439 EXPECT_CALL(sockets(), Send(kServerFD, _, request_data.GetLength(), 0))
440 .WillOnce(Return(request_data.GetLength()));
441 ExpectMonitorServerInput();
442 WriteToServer(kServerFD);
443 ExpectResultCallback(HTTPRequest::kResultResponseTimeout);
444 ExpectStop();
445 CallTimeoutTask();
446}
447
448TEST_F(HTTPRequestTest, ResponseData) {
449 SetupConnectComplete();
450 const string response0("hello");
Paul Stewartbdb02e62012-02-22 16:24:33 -0800451 ExpectReadEventCallback(response0);
Paul Stewart188a84a2012-01-20 16:28:15 -0800452 ExpectSetInputTimeout();
453 ReadFromServer(response0);
454 ExpectInResponse(response0);
455
456 const string response1(" to you");
Paul Stewartbdb02e62012-02-22 16:24:33 -0800457 ExpectReadEventCallback(response0 + response1);
Paul Stewart188a84a2012-01-20 16:28:15 -0800458 ExpectSetInputTimeout();
459 ReadFromServer(response1);
460 ExpectInResponse(response1);
461
462 ExpectResultCallbackWithResponse(response0 + response1);
463 ExpectStop();
464 ReadFromServer("");
Paul Stewartbdb02e62012-02-22 16:24:33 -0800465 ExpectReset();
Paul Stewart188a84a2012-01-20 16:28:15 -0800466}
467
468} // namespace shill