blob: d24d13ddcb104c76336b0c93c94cd9cb5e12d286 [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
12#include <base/stringprintf.h>
13#include <gtest/gtest.h>
14
15#include "shill/ip_address.h"
16#include "shill/http_url.h"
17#include "shill/mock_async_connection.h"
18#include "shill/mock_connection.h"
19#include "shill/mock_control.h"
20#include "shill/mock_device_info.h"
21#include "shill/mock_dns_client.h"
22#include "shill/mock_event_dispatcher.h"
23#include "shill/mock_sockets.h"
24
25using base::StringPrintf;
26using std::string;
27using std::vector;
28using ::testing::_;
29using ::testing::AtLeast;
30using ::testing::DoAll;
31using ::testing::Invoke;
32using ::testing::NiceMock;
33using ::testing::Return;
34using ::testing::ReturnArg;
35using ::testing::ReturnNew;
36using ::testing::ReturnRef;
37using ::testing::SetArgumentPointee;
38using ::testing::StrEq;
39using ::testing::StrictMock;
40using ::testing::Test;
41
42namespace shill {
43
44namespace {
45const char kTextSiteName[] = "www.chromium.org";
46const char kTextURL[] = "http://www.chromium.org/path/to/resource";
47const char kNumericURL[] = "http://10.1.1.1";
48const char kPath[] = "/path/to/resource";
49const char kBadURL[] = "xxx";
50const char kInterfaceName[] = "int0";
51const char kDNSServer0[] = "8.8.8.8";
52const char kDNSServer1[] = "8.8.4.4";
53const char *kDNSServers[] = { kDNSServer0, kDNSServer1 };
54const char kServerAddress[] = "10.1.1.1";
55const int kServerFD = 10203;
56const int kServerPort = 80;
57} // namespace {}
58
59MATCHER_P(IsIPAddress, address, "") {
60 IPAddress ip_address(IPAddress::kFamilyIPv4);
61 EXPECT_TRUE(ip_address.SetAddressFromString(address));
62 return ip_address.Equals(arg);
63}
64
Paul Stewartbdb02e62012-02-22 16:24:33 -080065MATCHER_P(ByteStringMatches, byte_string, "") {
66 return byte_string.Equals(arg);
67}
68
Paul Stewart188a84a2012-01-20 16:28:15 -080069class HTTPRequestTest : public Test {
70 public:
71 HTTPRequestTest()
72 : interface_name_(kInterfaceName),
73 server_async_connection_(new StrictMock<MockAsyncConnection>()),
74 dns_servers_(kDNSServers, kDNSServers + 2),
75 dns_client_(new StrictMock<MockDNSClient>()),
76 device_info_(new NiceMock<MockDeviceInfo>(
77 &control_,
78 reinterpret_cast<EventDispatcher*>(NULL),
79 reinterpret_cast<Metrics*>(NULL),
80 reinterpret_cast<Manager*>(NULL))),
81 connection_(new StrictMock<MockConnection>(device_info_.get())) { }
82 protected:
83 class CallbackTarget {
84 public:
85 CallbackTarget()
86 : read_event_callback_(
87 NewCallback(this, &CallbackTarget::ReadEventCallTarget)),
88 result_callback_(
89 NewCallback(this, &CallbackTarget::ResultCallTarget)) {}
90
Paul Stewartbdb02e62012-02-22 16:24:33 -080091 MOCK_METHOD1(ReadEventCallTarget, void(const ByteString &response_data));
92 MOCK_METHOD2(ResultCallTarget, void(HTTPRequest::Result result,
93 const ByteString &response_data));
94 Callback1<const ByteString &>::Type *read_event_callback() {
Paul Stewart188a84a2012-01-20 16:28:15 -080095 return read_event_callback_.get();
96 }
Paul Stewartbdb02e62012-02-22 16:24:33 -080097 Callback2<HTTPRequest::Result,
98 const ByteString &>::Type *result_callback() {
Paul Stewart188a84a2012-01-20 16:28:15 -080099 return result_callback_.get();
100 }
101
102 private:
Paul Stewartbdb02e62012-02-22 16:24:33 -0800103 scoped_ptr<Callback1<const ByteString &>::Type> read_event_callback_;
104 scoped_ptr<Callback2<HTTPRequest::Result, const ByteString &>::Type>
105 result_callback_;
Paul Stewart188a84a2012-01-20 16:28:15 -0800106 };
107
108 virtual void SetUp() {
109 EXPECT_CALL(*connection_.get(), interface_name())
110 .WillRepeatedly(ReturnRef(interface_name_));
111 EXPECT_CALL(*connection_.get(), dns_servers())
112 .WillRepeatedly(ReturnRef(dns_servers_));
113
114 request_.reset(new HTTPRequest(connection_, &dispatcher_, &sockets_));
115 // Passes ownership.
116 request_->dns_client_.reset(dns_client_);
117 // Passes ownership.
118 request_->server_async_connection_.reset(server_async_connection_);
119 }
120 virtual void TearDown() {
121 if (request_->is_running_) {
122 ExpectStop();
123
124 // Subtle: Make sure the finalization of the request happens while our
125 // expectations are still active.
126 request_.reset();
127 }
128 }
129 size_t FindInRequestData(const string &find_string) {
130 string request_string(
131 reinterpret_cast<char *>(request_->request_data_.GetData()),
132 request_->request_data_.GetLength());
133 return request_string.find(find_string);
134 }
135 // Accessors
136 const ByteString &GetRequestData() {
137 return request_->request_data_;
138 }
139 HTTPRequest *request() { return request_.get(); }
140 MockSockets &sockets() { return sockets_; }
141
142 // Expectations
143 void ExpectReset() {
144 EXPECT_EQ(connection_.get(), request_->connection_.get());
145 EXPECT_EQ(&dispatcher_, request_->dispatcher_);
146 EXPECT_EQ(&sockets_, request_->sockets_);
147 EXPECT_FALSE(request_->result_callback_);
148 EXPECT_FALSE(request_->read_event_callback_);
149 EXPECT_TRUE(request_->task_factory_.empty());
Paul Stewart188a84a2012-01-20 16:28:15 -0800150 EXPECT_FALSE(request_->read_server_handler_.get());
151 EXPECT_FALSE(request_->write_server_handler_.get());
152 EXPECT_EQ(dns_client_, request_->dns_client_.get());
153 EXPECT_EQ(server_async_connection_,
154 request_->server_async_connection_.get());
155 EXPECT_TRUE(request_->server_hostname_.empty());
156 EXPECT_EQ(-1, request_->server_port_);
157 EXPECT_EQ(-1, request_->server_socket_);
158 EXPECT_EQ(HTTPRequest::kResultUnknown, request_->timeout_result_);
159 EXPECT_TRUE(request_->request_data_.IsEmpty());
160 EXPECT_TRUE(request_->response_data_.IsEmpty());
161 EXPECT_FALSE(request_->is_running_);
162 }
163 void ExpectStop() {
164 if (request_->server_socket_ != -1) {
165 EXPECT_CALL(sockets(), Close(kServerFD))
166 .WillOnce(Return(0));
167 }
168 EXPECT_CALL(*dns_client_, Stop())
169 .Times(AtLeast(1));
170 EXPECT_CALL(*server_async_connection_, Stop())
171 .Times(AtLeast(1));
172 EXPECT_CALL(*connection_.get(), ReleaseRouting());
173 }
174 void ExpectSetTimeout(int timeout) {
175 EXPECT_CALL(dispatcher_, PostDelayedTask(_, timeout * 1000))
176 .WillOnce(Return(true));
177 }
178 void ExpectSetConnectTimeout() {
179 ExpectSetTimeout(HTTPRequest::kConnectTimeoutSeconds);
180 }
181 void ExpectSetInputTimeout() {
182 ExpectSetTimeout(HTTPRequest::kInputTimeoutSeconds);
183 }
184 void ExpectInResponse(const string &expected_response_data) {
185 string response_string(
186 reinterpret_cast<char *>(request_->response_data_.GetData()),
187 request_->response_data_.GetLength());
188 EXPECT_NE(string::npos, response_string.find(expected_response_data));
189 }
190 void ExpectDNSRequest(const string &host, bool return_value) {
Paul Stewartbdb02e62012-02-22 16:24:33 -0800191 EXPECT_CALL(*dns_client_, Start(StrEq(host), _))
Paul Stewart188a84a2012-01-20 16:28:15 -0800192 .WillOnce(Return(return_value));
193 }
Paul Stewart188a84a2012-01-20 16:28:15 -0800194 void ExpectAsyncConnect(const string &address, int port,
195 bool return_value) {
196 EXPECT_CALL(*server_async_connection_, Start(IsIPAddress(address), port))
197 .WillOnce(Return(return_value));
198 if (return_value) {
199 ExpectSetConnectTimeout();
200 }
201 }
202 void InvokeSyncConnect(const IPAddress &/*address*/, int /*port*/) {
203 CallConnectCompletion(true, kServerFD);
204 }
205 void CallConnectCompletion(bool success, int fd) {
206 request_->OnConnectCompletion(success, fd);
207 }
208 void ExpectSyncConnect(const string &address, int port) {
209 EXPECT_CALL(*server_async_connection_, Start(IsIPAddress(address), port))
210 .WillOnce(DoAll(Invoke(this, &HTTPRequestTest::InvokeSyncConnect),
211 Return(true)));
212 }
213 void ExpectConnectFailure() {
214 EXPECT_CALL(*server_async_connection_, Start(_, _))
215 .WillOnce(Return(false));
216 }
217 void ExpectMonitorServerInput() {
218 EXPECT_CALL(dispatcher_,
219 CreateInputHandler(kServerFD,
220 request_->read_server_callback_.get()))
221 .WillOnce(ReturnNew<IOHandler>());
222 ExpectSetInputTimeout();
223 }
224 void ExpectMonitorServerOutput() {
225 EXPECT_CALL(dispatcher_,
226 CreateReadyHandler(kServerFD,
227 IOHandler::kModeOutput,
228 request_->write_server_callback_.get()))
229 .WillOnce(ReturnNew<IOHandler>());
230 ExpectSetInputTimeout();
231 }
232 void ExpectRouteRequest() {
233 EXPECT_CALL(*connection_.get(), RequestRouting());
234 }
235 void ExpectRouteRelease() {
236 EXPECT_CALL(*connection_.get(), ReleaseRouting());
237 }
238 void ExpectResultCallback(HTTPRequest::Result result) {
Paul Stewartbdb02e62012-02-22 16:24:33 -0800239 EXPECT_CALL(target_, ResultCallTarget(result, _));
Paul Stewart188a84a2012-01-20 16:28:15 -0800240 }
Paul Stewartbdb02e62012-02-22 16:24:33 -0800241 void InvokeResultVerify(HTTPRequest::Result result,
242 const ByteString &response_data) {
Paul Stewart188a84a2012-01-20 16:28:15 -0800243 EXPECT_EQ(HTTPRequest::kResultSuccess, result);
Paul Stewartbdb02e62012-02-22 16:24:33 -0800244 EXPECT_TRUE(expected_response_.Equals(response_data));
Paul Stewart188a84a2012-01-20 16:28:15 -0800245 }
246 void ExpectResultCallbackWithResponse(const string &response) {
247 expected_response_ = ByteString(response, false);
Paul Stewartbdb02e62012-02-22 16:24:33 -0800248 EXPECT_CALL(target_, ResultCallTarget(HTTPRequest::kResultSuccess, _))
Paul Stewart188a84a2012-01-20 16:28:15 -0800249 .WillOnce(Invoke(this, &HTTPRequestTest::InvokeResultVerify));
250 }
Paul Stewartbdb02e62012-02-22 16:24:33 -0800251 void ExpectReadEventCallback(const string &response) {
252 ByteString response_data(response, false);
253 EXPECT_CALL(target_, ReadEventCallTarget(ByteStringMatches(response_data)));
Paul Stewart188a84a2012-01-20 16:28:15 -0800254 }
Paul Stewartbdb02e62012-02-22 16:24:33 -0800255 void GetDNSResultFailure(const string &error_msg) {
256 Error error(Error::kOperationFailed, error_msg);
257 IPAddress address(IPAddress::kFamilyUnknown);
258 request_->GetDNSResult(error, address);
259 }
260 void GetDNSResultSuccess(const IPAddress &address) {
261 Error error;
262 request_->GetDNSResult(error, address);
Paul Stewart188a84a2012-01-20 16:28:15 -0800263 }
264 void OnConnectCompletion(bool result, int sockfd) {
265 request_->OnConnectCompletion(result, sockfd);
266 }
267 void ReadFromServer(const string &data) {
268 const unsigned char *ptr =
269 reinterpret_cast<const unsigned char *>(data.c_str());
270 vector<unsigned char> data_writable(ptr, ptr + data.length());
271 InputData server_data(data_writable.data(), data_writable.size());
272 request_->ReadFromServer(&server_data);
273 }
274 void WriteToServer(int fd) {
275 request_->WriteToServer(fd);
276 }
277 HTTPRequest::Result StartRequest(const string &url) {
278 HTTPURL http_url;
279 EXPECT_TRUE(http_url.ParseFromString(url));
280 return request_->Start(http_url,
281 target_.read_event_callback(),
282 target_.result_callback());
283 }
284 void SetupConnectWithURL(const string &url, const string &expected_hostname) {
285 ExpectRouteRequest();
286 ExpectDNSRequest(expected_hostname, true);
287 EXPECT_EQ(HTTPRequest::kResultInProgress, StartRequest(url));
288 IPAddress addr(IPAddress::kFamilyIPv4);
289 EXPECT_TRUE(addr.SetAddressFromString(kServerAddress));
Paul Stewartbdb02e62012-02-22 16:24:33 -0800290 GetDNSResultSuccess(addr);
Paul Stewart188a84a2012-01-20 16:28:15 -0800291 }
292 void SetupConnect() {
293 SetupConnectWithURL(kTextURL, kTextSiteName);
294 }
295 void SetupConnectAsync() {
296 ExpectAsyncConnect(kServerAddress, kServerPort, true);
297 SetupConnect();
298 }
299 void SetupConnectComplete() {
300 SetupConnectAsync();
301 ExpectMonitorServerOutput();
302 OnConnectCompletion(true, kServerFD);
303 }
304 void CallTimeoutTask() {
305 request_->TimeoutTask();
306 }
307
308 private:
309 const string interface_name_;
310 // Owned by the HTTPRequest, but tracked here for EXPECT().
311 StrictMock<MockAsyncConnection> *server_async_connection_;
312 vector<string> dns_servers_;
313 // Owned by the HTTPRequest, but tracked here for EXPECT().
314 StrictMock<MockDNSClient> *dns_client_;
315 StrictMock<MockEventDispatcher> dispatcher_;
316 MockControl control_;
317 scoped_ptr<MockDeviceInfo> device_info_;
318 scoped_refptr<MockConnection> connection_;
319 scoped_ptr<HTTPRequest> request_;
320 StrictMock<MockSockets> sockets_;
321 StrictMock<CallbackTarget> target_;
322 ByteString expected_response_;
323};
324
325TEST_F(HTTPRequestTest, Constructor) {
326 ExpectReset();
327}
328
329
330TEST_F(HTTPRequestTest, FailConnectNumericSynchronous) {
331 ExpectRouteRequest();
332 ExpectConnectFailure();
333 ExpectStop();
334 EXPECT_EQ(HTTPRequest::kResultConnectionFailure, StartRequest(kNumericURL));
335 ExpectReset();
336}
337
338TEST_F(HTTPRequestTest, FailConnectNumericAsynchronous) {
339 ExpectRouteRequest();
340 ExpectAsyncConnect(kServerAddress, HTTPURL::kDefaultHTTPPort, true);
341 EXPECT_EQ(HTTPRequest::kResultInProgress, StartRequest(kNumericURL));
342 ExpectResultCallback(HTTPRequest::kResultConnectionFailure);
343 ExpectStop();
344 CallConnectCompletion(false, -1);
345 ExpectReset();
346}
347
348TEST_F(HTTPRequestTest, FailConnectNumericTimeout) {
349 ExpectRouteRequest();
350 ExpectAsyncConnect(kServerAddress, HTTPURL::kDefaultHTTPPort, true);
351 EXPECT_EQ(HTTPRequest::kResultInProgress, StartRequest(kNumericURL));
352 ExpectResultCallback(HTTPRequest::kResultConnectionTimeout);
353 ExpectStop();
354 CallTimeoutTask();
355 ExpectReset();
356}
357
358TEST_F(HTTPRequestTest, SyncConnectNumeric) {
359 ExpectRouteRequest();
360 ExpectSyncConnect(kServerAddress, HTTPURL::kDefaultHTTPPort);
361 ExpectMonitorServerOutput();
362 EXPECT_EQ(HTTPRequest::kResultInProgress, StartRequest(kNumericURL));
363}
364
365TEST_F(HTTPRequestTest, FailDNSStart) {
366 ExpectRouteRequest();
367 ExpectDNSRequest(kTextSiteName, false);
368 ExpectStop();
369 EXPECT_EQ(HTTPRequest::kResultDNSFailure, StartRequest(kTextURL));
370 ExpectReset();
371}
372
373TEST_F(HTTPRequestTest, FailDNSFailure) {
374 ExpectRouteRequest();
375 ExpectDNSRequest(kTextSiteName, true);
376 EXPECT_EQ(HTTPRequest::kResultInProgress, StartRequest(kTextURL));
Paul Stewart188a84a2012-01-20 16:28:15 -0800377 ExpectResultCallback(HTTPRequest::kResultDNSFailure);
378 ExpectStop();
Paul Stewartbdb02e62012-02-22 16:24:33 -0800379 GetDNSResultFailure(DNSClient::kErrorNoData);
Paul Stewart188a84a2012-01-20 16:28:15 -0800380 ExpectReset();
381}
382
383TEST_F(HTTPRequestTest, FailDNSTimeout) {
384 ExpectRouteRequest();
385 ExpectDNSRequest(kTextSiteName, true);
386 EXPECT_EQ(HTTPRequest::kResultInProgress, StartRequest(kTextURL));
Paul Stewart188a84a2012-01-20 16:28:15 -0800387 ExpectResultCallback(HTTPRequest::kResultDNSTimeout);
388 ExpectStop();
Paul Stewartbdb02e62012-02-22 16:24:33 -0800389 const string error(DNSClient::kErrorTimedOut);
390 GetDNSResultFailure(error);
Paul Stewart188a84a2012-01-20 16:28:15 -0800391 ExpectReset();
392}
393
394TEST_F(HTTPRequestTest, FailConnectText) {
395 ExpectConnectFailure();
396 ExpectResultCallback(HTTPRequest::kResultConnectionFailure);
397 ExpectStop();
398 SetupConnect();
399 ExpectReset();
400}
401
402TEST_F(HTTPRequestTest, ConnectComplete) {
403 SetupConnectComplete();
404}
405
406TEST_F(HTTPRequestTest, RequestTimeout) {
407 SetupConnectComplete();
408 ExpectResultCallback(HTTPRequest::kResultRequestTimeout);
409 ExpectStop();
410 CallTimeoutTask();
411}
412
413TEST_F(HTTPRequestTest, RequestData) {
414 SetupConnectComplete();
415 EXPECT_EQ(0, FindInRequestData(string("GET ") + kPath));
416 EXPECT_NE(string::npos,
417 FindInRequestData(string("\r\nHost: ") + kTextSiteName));
418 ByteString request_data = GetRequestData();
419 EXPECT_CALL(sockets(), Send(kServerFD, _, request_data.GetLength(), 0))
420 .WillOnce(Return(request_data.GetLength() - 1));
421 ExpectSetInputTimeout();
422 WriteToServer(kServerFD);
423 EXPECT_CALL(sockets(), Send(kServerFD, _, 1, 0))
424 .WillOnce(Return(1));
425 ExpectMonitorServerInput();
426 WriteToServer(kServerFD);
427}
428
429TEST_F(HTTPRequestTest, ResponseTimeout) {
430 SetupConnectComplete();
431 ByteString request_data = GetRequestData();
432 EXPECT_CALL(sockets(), Send(kServerFD, _, request_data.GetLength(), 0))
433 .WillOnce(Return(request_data.GetLength()));
434 ExpectMonitorServerInput();
435 WriteToServer(kServerFD);
436 ExpectResultCallback(HTTPRequest::kResultResponseTimeout);
437 ExpectStop();
438 CallTimeoutTask();
439}
440
441TEST_F(HTTPRequestTest, ResponseData) {
442 SetupConnectComplete();
443 const string response0("hello");
Paul Stewartbdb02e62012-02-22 16:24:33 -0800444 ExpectReadEventCallback(response0);
Paul Stewart188a84a2012-01-20 16:28:15 -0800445 ExpectSetInputTimeout();
446 ReadFromServer(response0);
447 ExpectInResponse(response0);
448
449 const string response1(" to you");
Paul Stewartbdb02e62012-02-22 16:24:33 -0800450 ExpectReadEventCallback(response0 + response1);
Paul Stewart188a84a2012-01-20 16:28:15 -0800451 ExpectSetInputTimeout();
452 ReadFromServer(response1);
453 ExpectInResponse(response1);
454
455 ExpectResultCallbackWithResponse(response0 + response1);
456 ExpectStop();
457 ReadFromServer("");
Paul Stewartbdb02e62012-02-22 16:24:33 -0800458 ExpectReset();
Paul Stewart188a84a2012-01-20 16:28:15 -0800459}
460
461} // namespace shill