blob: 062536763cabb6d838684cdda78238d5cf474552 [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());
Eric Shienbrood9a245532012-03-07 14:20:39 -0500156 EXPECT_FALSE(request_->connect_completion_callback_.is_null());
157 EXPECT_FALSE(request_->dns_client_callback_.is_null());
158 EXPECT_FALSE(request_->read_server_callback_.is_null());
159 EXPECT_FALSE(request_->write_server_callback_.is_null());
Paul Stewart188a84a2012-01-20 16:28:15 -0800160 EXPECT_FALSE(request_->read_server_handler_.get());
161 EXPECT_FALSE(request_->write_server_handler_.get());
162 EXPECT_EQ(dns_client_, request_->dns_client_.get());
163 EXPECT_EQ(server_async_connection_,
164 request_->server_async_connection_.get());
165 EXPECT_TRUE(request_->server_hostname_.empty());
166 EXPECT_EQ(-1, request_->server_port_);
167 EXPECT_EQ(-1, request_->server_socket_);
168 EXPECT_EQ(HTTPRequest::kResultUnknown, request_->timeout_result_);
169 EXPECT_TRUE(request_->request_data_.IsEmpty());
170 EXPECT_TRUE(request_->response_data_.IsEmpty());
171 EXPECT_FALSE(request_->is_running_);
172 }
173 void ExpectStop() {
174 if (request_->server_socket_ != -1) {
175 EXPECT_CALL(sockets(), Close(kServerFD))
176 .WillOnce(Return(0));
177 }
178 EXPECT_CALL(*dns_client_, Stop())
179 .Times(AtLeast(1));
180 EXPECT_CALL(*server_async_connection_, Stop())
181 .Times(AtLeast(1));
182 EXPECT_CALL(*connection_.get(), ReleaseRouting());
183 }
184 void ExpectSetTimeout(int timeout) {
185 EXPECT_CALL(dispatcher_, PostDelayedTask(_, timeout * 1000))
186 .WillOnce(Return(true));
187 }
188 void ExpectSetConnectTimeout() {
189 ExpectSetTimeout(HTTPRequest::kConnectTimeoutSeconds);
190 }
191 void ExpectSetInputTimeout() {
192 ExpectSetTimeout(HTTPRequest::kInputTimeoutSeconds);
193 }
194 void ExpectInResponse(const string &expected_response_data) {
195 string response_string(
196 reinterpret_cast<char *>(request_->response_data_.GetData()),
197 request_->response_data_.GetLength());
198 EXPECT_NE(string::npos, response_string.find(expected_response_data));
199 }
200 void ExpectDNSRequest(const string &host, bool return_value) {
Paul Stewartbdb02e62012-02-22 16:24:33 -0800201 EXPECT_CALL(*dns_client_, Start(StrEq(host), _))
Paul Stewart188a84a2012-01-20 16:28:15 -0800202 .WillOnce(Return(return_value));
203 }
Paul Stewart188a84a2012-01-20 16:28:15 -0800204 void ExpectAsyncConnect(const string &address, int port,
205 bool return_value) {
206 EXPECT_CALL(*server_async_connection_, Start(IsIPAddress(address), port))
207 .WillOnce(Return(return_value));
208 if (return_value) {
209 ExpectSetConnectTimeout();
210 }
211 }
212 void InvokeSyncConnect(const IPAddress &/*address*/, int /*port*/) {
213 CallConnectCompletion(true, kServerFD);
214 }
215 void CallConnectCompletion(bool success, int fd) {
216 request_->OnConnectCompletion(success, fd);
217 }
218 void ExpectSyncConnect(const string &address, int port) {
219 EXPECT_CALL(*server_async_connection_, Start(IsIPAddress(address), port))
220 .WillOnce(DoAll(Invoke(this, &HTTPRequestTest::InvokeSyncConnect),
221 Return(true)));
222 }
223 void ExpectConnectFailure() {
224 EXPECT_CALL(*server_async_connection_, Start(_, _))
225 .WillOnce(Return(false));
226 }
227 void ExpectMonitorServerInput() {
228 EXPECT_CALL(dispatcher_,
229 CreateInputHandler(kServerFD,
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500230 CallbackEq(request_->read_server_callback_)))
Paul Stewart188a84a2012-01-20 16:28:15 -0800231 .WillOnce(ReturnNew<IOHandler>());
232 ExpectSetInputTimeout();
233 }
234 void ExpectMonitorServerOutput() {
235 EXPECT_CALL(dispatcher_,
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500236 CreateReadyHandler(
237 kServerFD, IOHandler::kModeOutput,
238 CallbackEq(request_->write_server_callback_)))
Paul Stewart188a84a2012-01-20 16:28:15 -0800239 .WillOnce(ReturnNew<IOHandler>());
240 ExpectSetInputTimeout();
241 }
242 void ExpectRouteRequest() {
243 EXPECT_CALL(*connection_.get(), RequestRouting());
244 }
245 void ExpectRouteRelease() {
246 EXPECT_CALL(*connection_.get(), ReleaseRouting());
247 }
248 void ExpectResultCallback(HTTPRequest::Result result) {
Paul Stewartbdb02e62012-02-22 16:24:33 -0800249 EXPECT_CALL(target_, ResultCallTarget(result, _));
Paul Stewart188a84a2012-01-20 16:28:15 -0800250 }
Paul Stewartbdb02e62012-02-22 16:24:33 -0800251 void InvokeResultVerify(HTTPRequest::Result result,
252 const ByteString &response_data) {
Paul Stewart188a84a2012-01-20 16:28:15 -0800253 EXPECT_EQ(HTTPRequest::kResultSuccess, result);
Paul Stewartbdb02e62012-02-22 16:24:33 -0800254 EXPECT_TRUE(expected_response_.Equals(response_data));
Paul Stewart188a84a2012-01-20 16:28:15 -0800255 }
256 void ExpectResultCallbackWithResponse(const string &response) {
257 expected_response_ = ByteString(response, false);
Paul Stewartbdb02e62012-02-22 16:24:33 -0800258 EXPECT_CALL(target_, ResultCallTarget(HTTPRequest::kResultSuccess, _))
Paul Stewart188a84a2012-01-20 16:28:15 -0800259 .WillOnce(Invoke(this, &HTTPRequestTest::InvokeResultVerify));
260 }
Paul Stewartbdb02e62012-02-22 16:24:33 -0800261 void ExpectReadEventCallback(const string &response) {
262 ByteString response_data(response, false);
263 EXPECT_CALL(target_, ReadEventCallTarget(ByteStringMatches(response_data)));
Paul Stewart188a84a2012-01-20 16:28:15 -0800264 }
Paul Stewartbdb02e62012-02-22 16:24:33 -0800265 void GetDNSResultFailure(const string &error_msg) {
266 Error error(Error::kOperationFailed, error_msg);
267 IPAddress address(IPAddress::kFamilyUnknown);
268 request_->GetDNSResult(error, address);
269 }
270 void GetDNSResultSuccess(const IPAddress &address) {
271 Error error;
272 request_->GetDNSResult(error, address);
Paul Stewart188a84a2012-01-20 16:28:15 -0800273 }
274 void OnConnectCompletion(bool result, int sockfd) {
275 request_->OnConnectCompletion(result, sockfd);
276 }
277 void ReadFromServer(const string &data) {
278 const unsigned char *ptr =
279 reinterpret_cast<const unsigned char *>(data.c_str());
280 vector<unsigned char> data_writable(ptr, ptr + data.length());
281 InputData server_data(data_writable.data(), data_writable.size());
282 request_->ReadFromServer(&server_data);
283 }
284 void WriteToServer(int fd) {
285 request_->WriteToServer(fd);
286 }
287 HTTPRequest::Result StartRequest(const string &url) {
288 HTTPURL http_url;
289 EXPECT_TRUE(http_url.ParseFromString(url));
290 return request_->Start(http_url,
291 target_.read_event_callback(),
292 target_.result_callback());
293 }
294 void SetupConnectWithURL(const string &url, const string &expected_hostname) {
295 ExpectRouteRequest();
296 ExpectDNSRequest(expected_hostname, true);
297 EXPECT_EQ(HTTPRequest::kResultInProgress, StartRequest(url));
298 IPAddress addr(IPAddress::kFamilyIPv4);
299 EXPECT_TRUE(addr.SetAddressFromString(kServerAddress));
Paul Stewartbdb02e62012-02-22 16:24:33 -0800300 GetDNSResultSuccess(addr);
Paul Stewart188a84a2012-01-20 16:28:15 -0800301 }
302 void SetupConnect() {
303 SetupConnectWithURL(kTextURL, kTextSiteName);
304 }
305 void SetupConnectAsync() {
306 ExpectAsyncConnect(kServerAddress, kServerPort, true);
307 SetupConnect();
308 }
309 void SetupConnectComplete() {
310 SetupConnectAsync();
311 ExpectMonitorServerOutput();
312 OnConnectCompletion(true, kServerFD);
313 }
314 void CallTimeoutTask() {
315 request_->TimeoutTask();
316 }
317
318 private:
319 const string interface_name_;
320 // Owned by the HTTPRequest, but tracked here for EXPECT().
321 StrictMock<MockAsyncConnection> *server_async_connection_;
322 vector<string> dns_servers_;
323 // Owned by the HTTPRequest, but tracked here for EXPECT().
324 StrictMock<MockDNSClient> *dns_client_;
325 StrictMock<MockEventDispatcher> dispatcher_;
326 MockControl control_;
327 scoped_ptr<MockDeviceInfo> device_info_;
328 scoped_refptr<MockConnection> connection_;
329 scoped_ptr<HTTPRequest> request_;
330 StrictMock<MockSockets> sockets_;
331 StrictMock<CallbackTarget> target_;
332 ByteString expected_response_;
333};
334
335TEST_F(HTTPRequestTest, Constructor) {
336 ExpectReset();
337}
338
339
340TEST_F(HTTPRequestTest, FailConnectNumericSynchronous) {
341 ExpectRouteRequest();
342 ExpectConnectFailure();
343 ExpectStop();
344 EXPECT_EQ(HTTPRequest::kResultConnectionFailure, StartRequest(kNumericURL));
345 ExpectReset();
346}
347
348TEST_F(HTTPRequestTest, FailConnectNumericAsynchronous) {
349 ExpectRouteRequest();
350 ExpectAsyncConnect(kServerAddress, HTTPURL::kDefaultHTTPPort, true);
351 EXPECT_EQ(HTTPRequest::kResultInProgress, StartRequest(kNumericURL));
352 ExpectResultCallback(HTTPRequest::kResultConnectionFailure);
353 ExpectStop();
354 CallConnectCompletion(false, -1);
355 ExpectReset();
356}
357
358TEST_F(HTTPRequestTest, FailConnectNumericTimeout) {
359 ExpectRouteRequest();
360 ExpectAsyncConnect(kServerAddress, HTTPURL::kDefaultHTTPPort, true);
361 EXPECT_EQ(HTTPRequest::kResultInProgress, StartRequest(kNumericURL));
362 ExpectResultCallback(HTTPRequest::kResultConnectionTimeout);
363 ExpectStop();
364 CallTimeoutTask();
365 ExpectReset();
366}
367
368TEST_F(HTTPRequestTest, SyncConnectNumeric) {
369 ExpectRouteRequest();
370 ExpectSyncConnect(kServerAddress, HTTPURL::kDefaultHTTPPort);
371 ExpectMonitorServerOutput();
372 EXPECT_EQ(HTTPRequest::kResultInProgress, StartRequest(kNumericURL));
373}
374
375TEST_F(HTTPRequestTest, FailDNSStart) {
376 ExpectRouteRequest();
377 ExpectDNSRequest(kTextSiteName, false);
378 ExpectStop();
379 EXPECT_EQ(HTTPRequest::kResultDNSFailure, StartRequest(kTextURL));
380 ExpectReset();
381}
382
383TEST_F(HTTPRequestTest, FailDNSFailure) {
384 ExpectRouteRequest();
385 ExpectDNSRequest(kTextSiteName, true);
386 EXPECT_EQ(HTTPRequest::kResultInProgress, StartRequest(kTextURL));
Paul Stewart188a84a2012-01-20 16:28:15 -0800387 ExpectResultCallback(HTTPRequest::kResultDNSFailure);
388 ExpectStop();
Paul Stewartbdb02e62012-02-22 16:24:33 -0800389 GetDNSResultFailure(DNSClient::kErrorNoData);
Paul Stewart188a84a2012-01-20 16:28:15 -0800390 ExpectReset();
391}
392
393TEST_F(HTTPRequestTest, FailDNSTimeout) {
394 ExpectRouteRequest();
395 ExpectDNSRequest(kTextSiteName, true);
396 EXPECT_EQ(HTTPRequest::kResultInProgress, StartRequest(kTextURL));
Paul Stewart188a84a2012-01-20 16:28:15 -0800397 ExpectResultCallback(HTTPRequest::kResultDNSTimeout);
398 ExpectStop();
Paul Stewartbdb02e62012-02-22 16:24:33 -0800399 const string error(DNSClient::kErrorTimedOut);
400 GetDNSResultFailure(error);
Paul Stewart188a84a2012-01-20 16:28:15 -0800401 ExpectReset();
402}
403
404TEST_F(HTTPRequestTest, FailConnectText) {
405 ExpectConnectFailure();
406 ExpectResultCallback(HTTPRequest::kResultConnectionFailure);
407 ExpectStop();
408 SetupConnect();
409 ExpectReset();
410}
411
412TEST_F(HTTPRequestTest, ConnectComplete) {
413 SetupConnectComplete();
414}
415
416TEST_F(HTTPRequestTest, RequestTimeout) {
417 SetupConnectComplete();
418 ExpectResultCallback(HTTPRequest::kResultRequestTimeout);
419 ExpectStop();
420 CallTimeoutTask();
421}
422
423TEST_F(HTTPRequestTest, RequestData) {
424 SetupConnectComplete();
425 EXPECT_EQ(0, FindInRequestData(string("GET ") + kPath));
426 EXPECT_NE(string::npos,
427 FindInRequestData(string("\r\nHost: ") + kTextSiteName));
428 ByteString request_data = GetRequestData();
429 EXPECT_CALL(sockets(), Send(kServerFD, _, request_data.GetLength(), 0))
430 .WillOnce(Return(request_data.GetLength() - 1));
431 ExpectSetInputTimeout();
432 WriteToServer(kServerFD);
433 EXPECT_CALL(sockets(), Send(kServerFD, _, 1, 0))
434 .WillOnce(Return(1));
435 ExpectMonitorServerInput();
436 WriteToServer(kServerFD);
437}
438
439TEST_F(HTTPRequestTest, ResponseTimeout) {
440 SetupConnectComplete();
441 ByteString request_data = GetRequestData();
442 EXPECT_CALL(sockets(), Send(kServerFD, _, request_data.GetLength(), 0))
443 .WillOnce(Return(request_data.GetLength()));
444 ExpectMonitorServerInput();
445 WriteToServer(kServerFD);
446 ExpectResultCallback(HTTPRequest::kResultResponseTimeout);
447 ExpectStop();
448 CallTimeoutTask();
449}
450
451TEST_F(HTTPRequestTest, ResponseData) {
452 SetupConnectComplete();
453 const string response0("hello");
Paul Stewartbdb02e62012-02-22 16:24:33 -0800454 ExpectReadEventCallback(response0);
Paul Stewart188a84a2012-01-20 16:28:15 -0800455 ExpectSetInputTimeout();
456 ReadFromServer(response0);
457 ExpectInResponse(response0);
458
459 const string response1(" to you");
Paul Stewartbdb02e62012-02-22 16:24:33 -0800460 ExpectReadEventCallback(response0 + response1);
Paul Stewart188a84a2012-01-20 16:28:15 -0800461 ExpectSetInputTimeout();
462 ReadFromServer(response1);
463 ExpectInResponse(response1);
464
465 ExpectResultCallbackWithResponse(response0 + response1);
466 ExpectStop();
467 ReadFromServer("");
Paul Stewartbdb02e62012-02-22 16:24:33 -0800468 ExpectReset();
Paul Stewart188a84a2012-01-20 16:28:15 -0800469}
470
471} // namespace shill