blob: 5318b0b5410df05dcf426c78da676f1d3684d8d3 [file] [log] [blame]
Paul Stewart58a577b2012-01-10 11:18:52 -08001// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
Paul Stewartf65320c2011-10-13 14:34:52 -07002// 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_proxy.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/mock_async_connection.h"
Paul Stewartc8f4bef2011-12-13 09:45:51 -080017#include "shill/mock_connection.h"
18#include "shill/mock_control.h"
19#include "shill/mock_device_info.h"
Paul Stewartf65320c2011-10-13 14:34:52 -070020#include "shill/mock_dns_client.h"
21#include "shill/mock_event_dispatcher.h"
22#include "shill/mock_sockets.h"
23
24using base::StringPrintf;
25using std::string;
26using std::vector;
27using ::testing::_;
28using ::testing::AtLeast;
29using ::testing::DoAll;
30using ::testing::Invoke;
Paul Stewartc8f4bef2011-12-13 09:45:51 -080031using ::testing::NiceMock;
Paul Stewartf65320c2011-10-13 14:34:52 -070032using ::testing::Return;
33using ::testing::ReturnArg;
34using ::testing::ReturnNew;
35using ::testing::ReturnRef;
36using ::testing::SetArgumentPointee;
37using ::testing::StrEq;
38using ::testing::StrictMock;
39using ::testing::Test;
40
41namespace shill {
42
43namespace {
Paul Stewart58a577b2012-01-10 11:18:52 -080044const char kBadHeaderMissingURL[] = "BLAH\r\n";
45const char kBadHeaderMissingVersion[] = "BLAH http://hostname\r\n";
Paul Stewartf65320c2011-10-13 14:34:52 -070046const char kBadHostnameLine[] = "GET HTTP/1.1 http://hostname\r\n";
47const char kBasicGetHeader[] = "GET / HTTP/1.1\r\n";
48const char kBasicGetHeaderWithURL[] =
49 "GET http://www.chromium.org/ HTTP/1.1\r\n";
50const char kBasicGetHeaderWithURLNoTrailingSlash[] =
51 "GET http://www.chromium.org HTTP/1.1\r\n";
Paul Stewart58a577b2012-01-10 11:18:52 -080052const char kConnectQuery[] =
53 "CONNECT 10.10.10.10:443 HTTP/1.1\r\n"
54 "Host: 10.10.10.10:443\r\n\r\n";
Paul Stewartf65320c2011-10-13 14:34:52 -070055const char kQueryTemplate[] = "GET %s HTTP/%s\r\n%s"
56 "User-Agent: Mozilla/5.0 (X11; CrOS i686 1299.0.2011) "
57 "AppleWebKit/535.8 (KHTML, like Gecko) Chrome/17.0.936.0 Safari/535.8\r\n"
58 "Accept: text/html,application/xhtml+xml,application/xml;"
59 "q=0.9,*/*;q=0.8\r\n"
60 "Accept-Encoding: gzip,deflate,sdch\r\n"
61 "Accept-Language: en-US,en;q=0.8,ja;q=0.6\r\n"
62 "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3\r\n"
63 "Cookie: PREF=ID=xxxxxxxxxxxxxxxx:U=xxxxxxxxxxxxxxxx:FF=0:"
64 "TM=1317340083:LM=1317390705:GM=1:S=_xxxxxxxxxxxxxxx; "
65 "NID=52=xxxxxxxxxxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
66 "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxx; "
67 "HSID=xxxxxxxxxxxx-xxxx; APISID=xxxxxxxxxxxxxxxx/xxxxxxxxxxxxxxxxx; "
68 "SID=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_xxxxxxxxxxx"
69 "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-xxxxxxxxxxxxxxx"
70 "xxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-xxxxxxxxxxxxxxxxxx"
71 "_xxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxx-xx-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
72 "xxxxxxxxxxxxxxxx\r\n\r\n";
73const char kInterfaceName[] = "int0";
74const char kDNSServer0[] = "8.8.8.8";
75const char kDNSServer1[] = "8.8.4.4";
76const char kServerAddress[] = "10.10.10.10";
77const char *kDNSServers[] = { kDNSServer0, kDNSServer1 };
78const int kProxyFD = 10203;
79const int kServerFD = 10204;
80const int kClientFD = 10205;
81const int kServerPort = 40506;
Paul Stewart58a577b2012-01-10 11:18:52 -080082const int kConnectPort = 443;
Paul Stewartf65320c2011-10-13 14:34:52 -070083} // namespace {}
84
85MATCHER_P(IsIPAddress, address, "") {
86 IPAddress ip_address(IPAddress::kFamilyIPv4);
87 EXPECT_TRUE(ip_address.SetAddressFromString(address));
88 return ip_address.Equals(arg);
89}
90
91class HTTPProxyTest : public Test {
92 public:
93 HTTPProxyTest()
Paul Stewartc8f4bef2011-12-13 09:45:51 -080094 : interface_name_(kInterfaceName),
95 server_async_connection_(NULL),
Paul Stewartf65320c2011-10-13 14:34:52 -070096 dns_servers_(kDNSServers, kDNSServers + 2),
97 dns_client_(NULL),
Paul Stewartc8f4bef2011-12-13 09:45:51 -080098 device_info_(new NiceMock<MockDeviceInfo>(
99 &control_,
100 reinterpret_cast<EventDispatcher*>(NULL),
Thieu Le3426c8f2012-01-11 17:35:11 -0800101 reinterpret_cast<Metrics*>(NULL),
Paul Stewartc8f4bef2011-12-13 09:45:51 -0800102 reinterpret_cast<Manager*>(NULL))),
103 connection_(new StrictMock<MockConnection>(device_info_.get())),
104 proxy_(connection_) { }
Paul Stewartf65320c2011-10-13 14:34:52 -0700105 protected:
Paul Stewartc8f4bef2011-12-13 09:45:51 -0800106 virtual void SetUp() {
107 EXPECT_CALL(*connection_.get(), interface_name())
108 .WillRepeatedly(ReturnRef(interface_name_));
109 EXPECT_CALL(*connection_.get(), dns_servers())
110 .WillRepeatedly(ReturnRef(dns_servers_));
111 }
Paul Stewartf65320c2011-10-13 14:34:52 -0700112 virtual void TearDown() {
113 if (proxy_.sockets_) {
114 ExpectStop();
115 }
116 }
117 string CreateRequest(const string &url, const string &http_version,
118 const string &extra_lines) {
119 string append_lines(extra_lines);
120 if (append_lines.size()) {
121 append_lines.append("\r\n");
122 }
123 return StringPrintf(kQueryTemplate, url.c_str(), http_version.c_str(),
124 append_lines.c_str());
125 }
126 int InvokeGetSockName(int fd, struct sockaddr *addr_out,
127 socklen_t *sockaddr_size) {
128 struct sockaddr_in addr;
129 EXPECT_EQ(kProxyFD, fd);
130 EXPECT_GE(sizeof(sockaddr_in), *sockaddr_size);
131 addr.sin_addr.s_addr = 0;
132 addr.sin_port = kServerPort;
133 memcpy(addr_out, &addr, sizeof(addr));
134 *sockaddr_size = sizeof(sockaddr_in);
135 return 0;
136 }
137 void InvokeSyncConnect(const IPAddress &/*address*/, int /*port*/) {
138 proxy_.OnConnectCompletion(true, kServerFD);
139 }
140 size_t FindInRequest(const string &find_string) {
141 const ByteString &request_data = GetClientData();
142 string request_string(
143 reinterpret_cast<const char *>(request_data.GetConstData()),
144 request_data.GetLength());
145 return request_string.find(find_string);
146 }
147 // Accessors
148 const ByteString &GetClientData() {
149 return proxy_.client_data_;
150 }
151 HTTPProxy *proxy() { return &proxy_; }
152 HTTPProxy::State GetProxyState() {
153 return proxy_.state_;
154 }
155 const ByteString &GetServerData() {
156 return proxy_.server_data_;
157 }
158 MockSockets &sockets() { return sockets_; }
159 MockEventDispatcher &dispatcher() { return dispatcher_; }
160
161
162 // Expectations
163 void ExpectClientReset() {
164 EXPECT_EQ(-1, proxy_.client_socket_);
165 EXPECT_TRUE(proxy_.client_version_.empty());
166 EXPECT_EQ(HTTPProxy::kDefaultServerPort, proxy_.server_port_);
167 EXPECT_EQ(-1, proxy_.server_socket_);
168 EXPECT_FALSE(proxy_.idle_timeout_);
169 EXPECT_TRUE(proxy_.client_headers_.empty());
170 EXPECT_TRUE(proxy_.server_hostname_.empty());
171 EXPECT_TRUE(proxy_.client_data_.IsEmpty());
172 EXPECT_TRUE(proxy_.server_data_.IsEmpty());
173 EXPECT_FALSE(proxy_.read_client_handler_.get());
174 EXPECT_FALSE(proxy_.write_client_handler_.get());
175 EXPECT_FALSE(proxy_.read_server_handler_.get());
176 EXPECT_FALSE(proxy_.write_server_handler_.get());
Paul Stewartc8f4bef2011-12-13 09:45:51 -0800177 EXPECT_FALSE(proxy_.is_route_requested_);
Paul Stewartf65320c2011-10-13 14:34:52 -0700178 }
179 void ExpectReset() {
180 EXPECT_FALSE(proxy_.accept_handler_.get());
Paul Stewartc8f4bef2011-12-13 09:45:51 -0800181 EXPECT_EQ(proxy_.connection_.get(), connection_.get());
Paul Stewartf65320c2011-10-13 14:34:52 -0700182 EXPECT_FALSE(proxy_.dispatcher_);
183 EXPECT_FALSE(proxy_.dns_client_.get());
184 EXPECT_EQ(-1, proxy_.proxy_port_);
185 EXPECT_EQ(-1, proxy_.proxy_socket_);
186 EXPECT_FALSE(proxy_.server_async_connection_.get());
187 EXPECT_FALSE(proxy_.sockets_);
188 EXPECT_EQ(HTTPProxy::kStateIdle, proxy_.state_);
189 ExpectClientReset();
190 }
191 void ExpectStart() {
192 EXPECT_CALL(sockets(), Socket(_, _, _))
193 .WillOnce(Return(kProxyFD));
194 EXPECT_CALL(sockets(), Bind(kProxyFD, _, _))
195 .WillOnce(Return(0));
196 EXPECT_CALL(sockets(), GetSockName(kProxyFD, _, _))
197 .WillOnce(Invoke(this, &HTTPProxyTest::InvokeGetSockName));
198 EXPECT_CALL(sockets(), SetNonBlocking(kProxyFD))
199 .WillOnce(Return(0));
200 EXPECT_CALL(sockets(), Listen(kProxyFD, _))
201 .WillOnce(Return(0));
202 EXPECT_CALL(dispatcher_, CreateReadyHandler(kProxyFD,
203 IOHandler::kModeInput,
204 proxy_.accept_callback_.get()))
205 .WillOnce(ReturnNew<IOHandler>());
206 }
207 void ExpectStop() {
208 if (dns_client_) {
209 EXPECT_CALL(*dns_client_, Stop())
210 .Times(AtLeast(1));
211 }
212 if (server_async_connection_) {
213 EXPECT_CALL(*server_async_connection_, Stop())
214 .Times(AtLeast(1));
215 }
Paul Stewartc8f4bef2011-12-13 09:45:51 -0800216 if (proxy_.is_route_requested_) {
217 EXPECT_CALL(*connection_.get(), ReleaseRouting());
218 }
Paul Stewartf65320c2011-10-13 14:34:52 -0700219 }
220 void ExpectClientInput(int fd) {
221 EXPECT_CALL(sockets(), Accept(kProxyFD, _, _))
222 .WillOnce(Return(fd));
223 EXPECT_CALL(sockets(), SetNonBlocking(fd))
224 .WillOnce(Return(0));
225 EXPECT_CALL(dispatcher(),
226 CreateInputHandler(fd, proxy_.read_client_callback_.get()))
227 .WillOnce(ReturnNew<IOHandler>());
228 ExpectTransactionTimeout();
229 ExpectClientHeaderTimeout();
230 }
231 void ExpectTimeout(int timeout) {
232 EXPECT_CALL(dispatcher_, PostDelayedTask(_, timeout * 1000))
233 .WillOnce(Return(true));
234 }
235 void ExpectClientHeaderTimeout() {
236 ExpectTimeout(HTTPProxy::kClientHeaderTimeoutSeconds);
237 }
238 void ExpectConnectTimeout() {
239 ExpectTimeout(HTTPProxy::kConnectTimeoutSeconds);
240 }
241 void ExpectInputTimeout() {
242 ExpectTimeout(HTTPProxy::kInputTimeoutSeconds);
243 }
244 void ExpectRepeatedInputTimeout() {
245 EXPECT_CALL(dispatcher_,
246 PostDelayedTask(_, HTTPProxy::kInputTimeoutSeconds * 1000))
247 .WillRepeatedly(Return(true));
248 }
249 void ExpectTransactionTimeout() {
250 ExpectTimeout(HTTPProxy::kTransactionTimeoutSeconds);
251 }
Paul Stewart58a577b2012-01-10 11:18:52 -0800252 void ExpectInClientResponse(const string &response_data) {
253 string server_data(reinterpret_cast<char *>(proxy_.server_data_.GetData()),
254 proxy_.server_data_.GetLength());
255 EXPECT_NE(string::npos, server_data.find(response_data));
256 }
Paul Stewartf65320c2011-10-13 14:34:52 -0700257 void ExpectClientError(int code, const string &error) {
258 EXPECT_EQ(HTTPProxy::kStateFlushResponse, GetProxyState());
259 string status_line = StringPrintf("HTTP/1.1 %d ERROR", code);
Paul Stewart58a577b2012-01-10 11:18:52 -0800260 ExpectInClientResponse(status_line);
261 ExpectInClientResponse(error);
Paul Stewartf65320c2011-10-13 14:34:52 -0700262 }
263 void ExpectClientInternalError() {
264 ExpectClientError(500, HTTPProxy::kInternalErrorMsg);
265 }
266 void ExpectClientVersion(const string &version) {
267 EXPECT_EQ(version, proxy_.client_version_);
268 }
269 void ExpectServerHostname(const string &hostname) {
270 EXPECT_EQ(hostname, proxy_.server_hostname_);
271 }
272 void ExpectFirstLine(const string &line) {
273 EXPECT_EQ(line, proxy_.client_headers_[0] + "\r\n");
274 }
275 void ExpectDNSRequest(const string &host, bool return_value) {
Paul Stewartbdb02e62012-02-22 16:24:33 -0800276 EXPECT_CALL(*dns_client_, Start(StrEq(host), _))
Paul Stewartf65320c2011-10-13 14:34:52 -0700277 .WillOnce(Return(return_value));
278 }
Paul Stewartf65320c2011-10-13 14:34:52 -0700279 void ExpectAsyncConnect(const string &address, int port,
280 bool return_value) {
281 EXPECT_CALL(*server_async_connection_, Start(IsIPAddress(address), port))
282 .WillOnce(Return(return_value));
283 }
284 void ExpectSyncConnect(const string &address, int port) {
285 EXPECT_CALL(*server_async_connection_, Start(IsIPAddress(address), port))
286 .WillOnce(DoAll(Invoke(this, &HTTPProxyTest::InvokeSyncConnect),
287 Return(true)));
288 }
Paul Stewart58a577b2012-01-10 11:18:52 -0800289 void ExpectClientData() {
Paul Stewartf65320c2011-10-13 14:34:52 -0700290 EXPECT_CALL(dispatcher(),
291 CreateReadyHandler(kClientFD,
292 IOHandler::kModeOutput,
293 proxy_.write_client_callback_.get()))
294 .WillOnce(ReturnNew<IOHandler>());
Paul Stewart58a577b2012-01-10 11:18:52 -0800295 }
296 void ExpectClientResult() {
297 ExpectClientData();
Paul Stewartf65320c2011-10-13 14:34:52 -0700298 ExpectInputTimeout();
299 }
300 void ExpectServerInput() {
301 EXPECT_CALL(dispatcher(),
302 CreateInputHandler(kServerFD,
303 proxy_.read_server_callback_.get()))
304 .WillOnce(ReturnNew<IOHandler>());
305 ExpectInputTimeout();
306 }
307 void ExpectServerOutput() {
308 EXPECT_CALL(dispatcher(),
309 CreateReadyHandler(kServerFD,
310 IOHandler::kModeOutput,
311 proxy_.write_server_callback_.get()))
312 .WillOnce(ReturnNew<IOHandler>());
313 ExpectInputTimeout();
314 }
315 void ExpectRepeatedServerOutput() {
316 EXPECT_CALL(dispatcher(),
317 CreateReadyHandler(kServerFD,
318 IOHandler::kModeOutput,
319 proxy_.write_server_callback_.get()))
320 .WillOnce(ReturnNew<IOHandler>());
321 ExpectRepeatedInputTimeout();
322 }
Paul Stewartf65320c2011-10-13 14:34:52 -0700323 void ExpectTunnelClose() {
324 EXPECT_CALL(sockets(), Close(kClientFD))
325 .WillOnce(Return(0));
326 EXPECT_CALL(sockets(), Close(kServerFD))
327 .WillOnce(Return(0));
328 ExpectStop();
329 }
Paul Stewartc8f4bef2011-12-13 09:45:51 -0800330 void ExpectRouteRequest() {
331 EXPECT_CALL(*connection_.get(), RequestRouting());
332 }
333 void ExpectRouteRelease() {
334 EXPECT_CALL(*connection_.get(), ReleaseRouting());
335 }
Paul Stewartf65320c2011-10-13 14:34:52 -0700336
337 // Callers for various private routines in the proxy
338 bool StartProxy() {
339 bool ret = proxy_.Start(&dispatcher_, &sockets_);
340 if (ret) {
341 dns_client_ = new StrictMock<MockDNSClient>();
342 // Passes ownership.
343 proxy_.dns_client_.reset(dns_client_);
344 server_async_connection_ = new StrictMock<MockAsyncConnection>();
345 // Passes ownership.
346 proxy_.server_async_connection_.reset(server_async_connection_);
347 }
348 return ret;
349 }
350 void AcceptClient(int fd) {
351 proxy_.AcceptClient(fd);
352 }
Paul Stewartbdb02e62012-02-22 16:24:33 -0800353 void GetDNSResultFailure(const string &error_msg) {
354 Error error(Error::kOperationFailed, error_msg);
355 IPAddress address(IPAddress::kFamilyUnknown);
356 proxy_.GetDNSResult(error, address);
357 }
358 void GetDNSResultSuccess(const IPAddress &address) {
359 Error error;
360 proxy_.GetDNSResult(error, address);
Paul Stewartf65320c2011-10-13 14:34:52 -0700361 }
362 void OnConnectCompletion(bool result, int sockfd) {
363 proxy_.OnConnectCompletion(result, sockfd);
364 }
365 void ReadFromClient(const string &data) {
366 const unsigned char *ptr =
367 reinterpret_cast<const unsigned char *>(data.c_str());
368 vector<unsigned char> data_bytes(ptr, ptr + data.length());
369 InputData proxy_data(data_bytes.data(), data_bytes.size());
370 proxy_.ReadFromClient(&proxy_data);
371 }
372 void ReadFromServer(const string &data) {
373 const unsigned char *ptr =
374 reinterpret_cast<const unsigned char *>(data.c_str());
375 vector<unsigned char> data_bytes(ptr, ptr + data.length());
376 InputData proxy_data(data_bytes.data(), data_bytes.size());
377 proxy_.ReadFromServer(&proxy_data);
378 }
379 void SendClientError(int code, const string &error) {
380 proxy_.SendClientError(code, error);
381 EXPECT_FALSE(proxy_.server_data_.IsEmpty());
382 }
383 void StopClient() {
384 EXPECT_CALL(*dns_client_, Stop());
385 EXPECT_CALL(*server_async_connection_, Stop());
386 proxy_.StopClient();
387 }
388 void StopProxy() {
389 ExpectStop();
390 proxy_.Stop();
391 server_async_connection_ = NULL;
392 dns_client_ = NULL;
393 ExpectReset();
394 }
395 void WriteToClient(int fd) {
396 proxy_.WriteToClient(fd);
397 }
398 void WriteToServer(int fd) {
399 proxy_.WriteToServer(fd);
400 }
401
402 void SetupClient() {
403 ExpectStart();
404 ASSERT_TRUE(StartProxy());
405 ExpectClientInput(kClientFD);
406 AcceptClient(kProxyFD);
407 EXPECT_EQ(HTTPProxy::kStateReadClientHeader, GetProxyState());
408 }
409 void SetupConnectWithRequest(const string &url, const string &http_version,
410 const string &extra_lines) {
411 ExpectDNSRequest("www.chromium.org", true);
Paul Stewartc8f4bef2011-12-13 09:45:51 -0800412 ExpectRouteRequest();
Paul Stewartf65320c2011-10-13 14:34:52 -0700413 ReadFromClient(CreateRequest(url, http_version, extra_lines));
414 IPAddress addr(IPAddress::kFamilyIPv4);
415 EXPECT_TRUE(addr.SetAddressFromString(kServerAddress));
Paul Stewartbdb02e62012-02-22 16:24:33 -0800416 GetDNSResultSuccess(addr);
Paul Stewartf65320c2011-10-13 14:34:52 -0700417 }
418 void SetupConnect() {
419 SetupConnectWithRequest("/", "1.1", "Host: www.chromium.org:40506");
420 }
421 void SetupConnectAsync() {
422 SetupClient();
423 ExpectAsyncConnect(kServerAddress, kServerPort, true);
424 ExpectConnectTimeout();
425 SetupConnect();
426 }
427 void SetupConnectComplete() {
428 SetupConnectAsync();
429 ExpectServerOutput();
430 OnConnectCompletion(true, kServerFD);
431 EXPECT_EQ(HTTPProxy::kStateTunnelData, GetProxyState());
432 }
433
434 private:
Paul Stewartc8f4bef2011-12-13 09:45:51 -0800435 const string interface_name_;
Paul Stewartf65320c2011-10-13 14:34:52 -0700436 // Owned by the HTTPProxy, but tracked here for EXPECT().
437 StrictMock<MockAsyncConnection> *server_async_connection_;
438 vector<string> dns_servers_;
439 // Owned by the HTTPProxy, but tracked here for EXPECT().
440 StrictMock<MockDNSClient> *dns_client_;
441 MockEventDispatcher dispatcher_;
Paul Stewartc8f4bef2011-12-13 09:45:51 -0800442 MockControl control_;
443 scoped_ptr<MockDeviceInfo> device_info_;
444 scoped_refptr<MockConnection> connection_;
Paul Stewartf65320c2011-10-13 14:34:52 -0700445 HTTPProxy proxy_;
446 StrictMock<MockSockets> sockets_;
447};
448
449TEST_F(HTTPProxyTest, StartFailSocket) {
450 EXPECT_CALL(sockets(), Socket(_, _, _))
451 .WillOnce(Return(-1));
452 EXPECT_FALSE(StartProxy());
453 ExpectReset();
454}
455
456TEST_F(HTTPProxyTest, StartFailBind) {
457 EXPECT_CALL(sockets(), Socket(_, _, _))
458 .WillOnce(Return(kProxyFD));
459 EXPECT_CALL(sockets(), Bind(kProxyFD, _, _))
460 .WillOnce(Return(-1));
461 EXPECT_CALL(sockets(), Close(kProxyFD))
462 .WillOnce(Return(0));
463 EXPECT_FALSE(StartProxy());
464 ExpectReset();
465}
466
467TEST_F(HTTPProxyTest, StartFailGetSockName) {
468 EXPECT_CALL(sockets(), Socket(_, _, _))
469 .WillOnce(Return(kProxyFD));
470 EXPECT_CALL(sockets(), Bind(kProxyFD, _, _))
471 .WillOnce(Return(0));
472 EXPECT_CALL(sockets(), GetSockName(kProxyFD, _, _))
473 .WillOnce(Return(-1));
474 EXPECT_CALL(sockets(), Close(kProxyFD))
475 .WillOnce(Return(0));
476 EXPECT_FALSE(StartProxy());
477 ExpectReset();
478}
479
480TEST_F(HTTPProxyTest, StartFailSetNonBlocking) {
481 EXPECT_CALL(sockets(), Socket(_, _, _))
482 .WillOnce(Return(kProxyFD));
483 EXPECT_CALL(sockets(), Bind(kProxyFD, _, _))
484 .WillOnce(Return(0));
485 EXPECT_CALL(sockets(), GetSockName(kProxyFD, _, _))
486 .WillOnce(Return(0));
487 EXPECT_CALL(sockets(), SetNonBlocking(kProxyFD))
488 .WillOnce(Return(-1));
489 EXPECT_CALL(sockets(), Close(kProxyFD))
490 .WillOnce(Return(0));
491 EXPECT_FALSE(StartProxy());
492 ExpectReset();
493}
494
495TEST_F(HTTPProxyTest, StartFailListen) {
496 EXPECT_CALL(sockets(), Socket(_, _, _))
497 .WillOnce(Return(kProxyFD));
498 EXPECT_CALL(sockets(), Bind(kProxyFD, _, _))
499 .WillOnce(Return(0));
500 EXPECT_CALL(sockets(), GetSockName(kProxyFD, _, _))
501 .WillOnce(Return(0));
502 EXPECT_CALL(sockets(), SetNonBlocking(kProxyFD))
503 .WillOnce(Return(0));
504 EXPECT_CALL(sockets(), Listen(kProxyFD, _))
505 .WillOnce(Return(-1));
506 EXPECT_CALL(sockets(), Close(kProxyFD))
507 .WillOnce(Return(0));
508 EXPECT_FALSE(StartProxy());
509 ExpectReset();
510}
511
512TEST_F(HTTPProxyTest, StartSuccess) {
513 ExpectStart();
514 EXPECT_TRUE(StartProxy());
515}
516
517TEST_F(HTTPProxyTest, SendClientError) {
518 SetupClient();
519 ExpectClientResult();
520 SendClientError(500, "This is an error");
521 ExpectClientError(500, "This is an error");
522
523 // We succeed in sending all but one byte of the client response.
524 int buf_len = GetServerData().GetLength();
525 EXPECT_CALL(sockets(), Send(kClientFD, _, buf_len, 0))
526 .WillOnce(Return(buf_len - 1));
527 ExpectInputTimeout();
528 WriteToClient(kClientFD);
529 EXPECT_EQ(1, GetServerData().GetLength());
530 EXPECT_EQ(HTTPProxy::kStateFlushResponse, GetProxyState());
531
532 // When we are able to send the last byte, we close the connection.
533 EXPECT_CALL(sockets(), Send(kClientFD, _, 1, 0))
534 .WillOnce(Return(1));
535 EXPECT_CALL(sockets(), Close(kClientFD))
536 .WillOnce(Return(0));
537 ExpectStop();
538 WriteToClient(kClientFD);
539 EXPECT_EQ(HTTPProxy::kStateWaitConnection, GetProxyState());
540}
541
Paul Stewart58a577b2012-01-10 11:18:52 -0800542TEST_F(HTTPProxyTest, ReadMissingURL) {
Paul Stewartf65320c2011-10-13 14:34:52 -0700543 SetupClient();
544 ExpectClientResult();
Paul Stewart58a577b2012-01-10 11:18:52 -0800545 ReadFromClient(kBadHeaderMissingURL);
546 ExpectClientError(501, "Server could not parse HTTP method");
547}
548
549TEST_F(HTTPProxyTest, ReadMissingVersion) {
550 SetupClient();
551 ExpectClientResult();
552 ReadFromClient(kBadHeaderMissingVersion);
Paul Stewartf65320c2011-10-13 14:34:52 -0700553 ExpectClientError(501, "Server only accepts HTTP/1.x requests");
554}
555
556TEST_F(HTTPProxyTest, ReadBadHostname) {
557 SetupClient();
558 ExpectClientResult();
559 ReadFromClient(kBadHostnameLine);
560 ExpectClientInternalError();
561}
562
563TEST_F(HTTPProxyTest, GoodFirstLineWithoutURL) {
564 SetupClient();
565 ExpectClientHeaderTimeout();
566 ReadFromClient(kBasicGetHeader);
567 ExpectClientVersion("1.1");
568 ExpectServerHostname("");
569 ExpectFirstLine(kBasicGetHeader);
570}
571
572TEST_F(HTTPProxyTest, GoodFirstLineWithURL) {
573 SetupClient();
574 ExpectClientHeaderTimeout();
575 ReadFromClient(kBasicGetHeaderWithURL);
576 ExpectClientVersion("1.1");
577 ExpectServerHostname("www.chromium.org");
578 ExpectFirstLine(kBasicGetHeader);
579}
580
581TEST_F(HTTPProxyTest, GoodFirstLineWithURLNoSlash) {
582 SetupClient();
583 ExpectClientHeaderTimeout();
584 ReadFromClient(kBasicGetHeaderWithURLNoTrailingSlash);
585 ExpectClientVersion("1.1");
586 ExpectServerHostname("www.chromium.org");
587 ExpectFirstLine(kBasicGetHeader);
588}
589
590TEST_F(HTTPProxyTest, NoHostInRequest) {
591 SetupClient();
592 ExpectClientResult();
593 ReadFromClient(CreateRequest("/", "1.1", ""));
594 ExpectClientError(400, "I don't know what host you want me to connect to");
595}
596
597TEST_F(HTTPProxyTest, TooManyColonsInHost) {
598 SetupClient();
599 ExpectClientResult();
600 ReadFromClient(CreateRequest("/", "1.1", "Host: www.chromium.org:80:40506"));
601 ExpectClientError(400, "Too many colons in hostname");
602}
603
604TEST_F(HTTPProxyTest, DNSRequestFailure) {
605 SetupClient();
Paul Stewartc8f4bef2011-12-13 09:45:51 -0800606 ExpectRouteRequest();
Paul Stewartf65320c2011-10-13 14:34:52 -0700607 ExpectDNSRequest("www.chromium.org", false);
608 ExpectClientResult();
609 ReadFromClient(CreateRequest("/", "1.1", "Host: www.chromium.org:40506"));
610 ExpectClientError(502, "Could not resolve hostname");
611}
612
613TEST_F(HTTPProxyTest, DNSRequestDelayedFailure) {
614 SetupClient();
Paul Stewartc8f4bef2011-12-13 09:45:51 -0800615 ExpectRouteRequest();
Paul Stewartf65320c2011-10-13 14:34:52 -0700616 ExpectDNSRequest("www.chromium.org", true);
617 ReadFromClient(CreateRequest("/", "1.1", "Host: www.chromium.org:40506"));
618 ExpectClientResult();
619 const std::string not_found_error(DNSClient::kErrorNotFound);
Paul Stewartbdb02e62012-02-22 16:24:33 -0800620 GetDNSResultFailure(not_found_error);
Paul Stewartf65320c2011-10-13 14:34:52 -0700621 ExpectClientError(502, string("Could not resolve hostname: ") +
622 not_found_error);
623}
624
625TEST_F(HTTPProxyTest, TrailingClientData) {
626 SetupClient();
Paul Stewartc8f4bef2011-12-13 09:45:51 -0800627 ExpectRouteRequest();
Paul Stewartf65320c2011-10-13 14:34:52 -0700628 ExpectDNSRequest("www.chromium.org", true);
629 const string trailing_data("Trailing client data");
630 ReadFromClient(CreateRequest("/", "1.1", "Host: www.chromium.org:40506") +
631 trailing_data);
632 EXPECT_EQ(GetClientData().GetLength() - trailing_data.length(),
633 FindInRequest(trailing_data));
634 EXPECT_EQ(HTTPProxy::kStateLookupServer, GetProxyState());
635}
636
637TEST_F(HTTPProxyTest, LineContinuation) {
638 SetupClient();
Paul Stewartc8f4bef2011-12-13 09:45:51 -0800639 ExpectRouteRequest();
Paul Stewartf65320c2011-10-13 14:34:52 -0700640 ExpectDNSRequest("www.chromium.org", true);
641 string text_to_keep("X-Long-Header: this is one line\r\n"
642 "\tand this is another");
643 ReadFromClient(CreateRequest("http://www.chromium.org/", "1.1",
644 text_to_keep));
645 EXPECT_NE(string::npos, FindInRequest(text_to_keep));
646}
647
648// NB: This tests two different things:
649// 1) That the system replaces the value for "Proxy-Connection" headers.
650// 2) That when it replaces a header, it also removes the text in the line
651// continuation.
652TEST_F(HTTPProxyTest, LineContinuationRemoval) {
653 SetupClient();
Paul Stewartc8f4bef2011-12-13 09:45:51 -0800654 ExpectRouteRequest();
Paul Stewartf65320c2011-10-13 14:34:52 -0700655 ExpectDNSRequest("www.chromium.org", true);
656 string text_to_remove("remove this text please");
657 ReadFromClient(CreateRequest("http://www.chromium.org/", "1.1",
658 string("Proxy-Connection: stuff\r\n\t") +
659 text_to_remove));
660 EXPECT_EQ(string::npos, FindInRequest(text_to_remove));
661 EXPECT_NE(string::npos, FindInRequest("Proxy-Connection: close\r\n"));
662}
663
664TEST_F(HTTPProxyTest, ConnectSynchronousFailure) {
665 SetupClient();
666 ExpectAsyncConnect(kServerAddress, kServerPort, false);
667 ExpectClientResult();
668 SetupConnect();
669 ExpectClientError(500, "Could not create socket to connect to server");
670}
671
672TEST_F(HTTPProxyTest, ConnectAsyncConnectFailure) {
673 SetupConnectAsync();
674 ExpectClientResult();
675 OnConnectCompletion(false, -1);
676 ExpectClientError(500, "Socket connection delayed failure");
677}
678
679TEST_F(HTTPProxyTest, ConnectSynchronousSuccess) {
680 SetupClient();
681 ExpectSyncConnect(kServerAddress, 999);
682 ExpectRepeatedServerOutput();
683 SetupConnectWithRequest("/", "1.1", "Host: www.chromium.org:999");
684 EXPECT_EQ(HTTPProxy::kStateTunnelData, GetProxyState());
685}
686
687TEST_F(HTTPProxyTest, ConnectIPAddresss) {
688 SetupClient();
689 ExpectSyncConnect(kServerAddress, 999);
690 ExpectRepeatedServerOutput();
Paul Stewartc8f4bef2011-12-13 09:45:51 -0800691 ExpectRouteRequest();
Paul Stewartf65320c2011-10-13 14:34:52 -0700692 ReadFromClient(CreateRequest("/", "1.1",
693 StringPrintf("Host: %s:999", kServerAddress)));
694 EXPECT_EQ(HTTPProxy::kStateTunnelData, GetProxyState());
695}
696
697TEST_F(HTTPProxyTest, ConnectAsyncConnectSuccess) {
698 SetupConnectComplete();
699}
700
Paul Stewart58a577b2012-01-10 11:18:52 -0800701TEST_F(HTTPProxyTest, HTTPConnectMethod) {
702 SetupClient();
703 ExpectAsyncConnect(kServerAddress, kConnectPort, true);
704 ExpectConnectTimeout();
705 ExpectRouteRequest();
706 ReadFromClient(kConnectQuery);
707 ExpectRepeatedInputTimeout();
708 ExpectClientData();
709 OnConnectCompletion(true, kServerFD);
710 ExpectInClientResponse("HTTP/1.1 200 OK\r\n\r\n");
711}
712
Paul Stewartf65320c2011-10-13 14:34:52 -0700713TEST_F(HTTPProxyTest, TunnelData) {
714 SetupConnectComplete();
715
716 // The proxy is waiting for the server to be ready to accept data.
717 EXPECT_CALL(sockets(), Send(kServerFD, _, _, 0))
718 .WillOnce(Return(10));
719 ExpectServerInput();
720 WriteToServer(kServerFD);
721 EXPECT_CALL(sockets(), Send(kServerFD, _, _, 0))
722 .WillOnce(ReturnArg<2>());
723 ExpectInputTimeout();
724 WriteToServer(kServerFD);
725 EXPECT_EQ(HTTPProxy::kStateTunnelData, GetProxyState());
726
727 // Tunnel a reply back to the client.
728 const string server_result("200 OK ... and so on");
729 ExpectClientResult();
730 ReadFromServer(server_result);
731 EXPECT_EQ(server_result,
732 string(reinterpret_cast<const char *>(
733 GetServerData().GetConstData()),
734 GetServerData().GetLength()));
735
736 // Allow part of the result string to be sent to the client.
737 const int part = server_result.length() / 2;
738 EXPECT_CALL(sockets(), Send(kClientFD, _, server_result.length(), 0))
739 .WillOnce(Return(part));
740 ExpectInputTimeout();
741 WriteToClient(kClientFD);
742 EXPECT_EQ(HTTPProxy::kStateTunnelData, GetProxyState());
743
744 // The Server closes the connection while the client is still reading.
745 ExpectInputTimeout();
746 ReadFromServer("");
747 EXPECT_EQ(HTTPProxy::kStateFlushResponse, GetProxyState());
748
749 // When the last part of the response is written to the client, we close
750 // all connections.
751 EXPECT_CALL(sockets(), Send(kClientFD, _, server_result.length() - part, 0))
752 .WillOnce(ReturnArg<2>());
753 ExpectTunnelClose();
754 WriteToClient(kClientFD);
755 EXPECT_EQ(HTTPProxy::kStateWaitConnection, GetProxyState());
756}
757
758TEST_F(HTTPProxyTest, TunnelDataFailWriteClient) {
759 SetupConnectComplete();
760 EXPECT_CALL(sockets(), Send(kClientFD, _, _, 0))
761 .WillOnce(Return(-1));
762 ExpectTunnelClose();
763 WriteToClient(kClientFD);
764 ExpectClientReset();
765 EXPECT_EQ(HTTPProxy::kStateWaitConnection, GetProxyState());
766}
767
768TEST_F(HTTPProxyTest, TunnelDataFailWriteServer) {
769 SetupConnectComplete();
770 EXPECT_CALL(sockets(), Send(kServerFD, _, _, 0))
771 .WillOnce(Return(-1));
772 ExpectTunnelClose();
773 WriteToServer(kServerFD);
774 ExpectClientReset();
775 EXPECT_EQ(HTTPProxy::kStateWaitConnection, GetProxyState());
776}
777
778TEST_F(HTTPProxyTest, TunnelDataFailClientClose) {
779 SetupConnectComplete();
780 ExpectTunnelClose();
781 ReadFromClient("");
782 ExpectClientReset();
783 EXPECT_EQ(HTTPProxy::kStateWaitConnection, GetProxyState());
784}
785
786TEST_F(HTTPProxyTest, TunnelDataFailServerClose) {
787 SetupConnectComplete();
788 ExpectTunnelClose();
789 ReadFromServer("");
790 ExpectClientReset();
791 EXPECT_EQ(HTTPProxy::kStateWaitConnection, GetProxyState());
792}
793
794TEST_F(HTTPProxyTest, StopClient) {
795 SetupConnectComplete();
796 EXPECT_CALL(sockets(), Close(kClientFD))
797 .WillOnce(Return(0));
798 EXPECT_CALL(sockets(), Close(kServerFD))
799 .WillOnce(Return(0));
Paul Stewartc8f4bef2011-12-13 09:45:51 -0800800 ExpectRouteRelease();
Paul Stewartf65320c2011-10-13 14:34:52 -0700801 StopClient();
802 ExpectClientReset();
803 EXPECT_EQ(HTTPProxy::kStateWaitConnection, GetProxyState());
804}
805
806} // namespace shill