// // Copyright (C) 2012 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #include "shill/http_proxy.h" #include #include #include #include #include #include #include "shill/mock_async_connection.h" #include "shill/mock_connection.h" #include "shill/mock_control.h" #include "shill/mock_device_info.h" #include "shill/mock_dns_client.h" #include "shill/mock_event_dispatcher.h" #include "shill/net/ip_address.h" #include "shill/net/mock_sockets.h" using base::StringPrintf; using std::string; using std::vector; using ::testing::_; using ::testing::AnyNumber; using ::testing::AtLeast; using ::testing::DoAll; using ::testing::Invoke; using ::testing::NiceMock; using ::testing::Return; using ::testing::ReturnArg; using ::testing::ReturnNew; using ::testing::ReturnRef; using ::testing::SetArgumentPointee; using ::testing::StrEq; using ::testing::StrictMock; using ::testing::Test; namespace shill { namespace { const char kBadHeaderMissingURL[] = "BLAH\r\n"; const char kBadHeaderMissingVersion[] = "BLAH http://hostname\r\n"; const char kBadHostnameLine[] = "GET HTTP/1.1 http://hostname\r\n"; const char kBasicGetHeader[] = "GET / HTTP/1.1\r\n"; const char kBasicGetHeaderWithURL[] = "GET http://www.chromium.org/ HTTP/1.1\r\n"; const char kBasicGetHeaderWithURLNoTrailingSlash[] = "GET http://www.chromium.org HTTP/1.1\r\n"; const char kConnectQuery[] = "CONNECT 10.10.10.10:443 HTTP/1.1\r\n" "Host: 10.10.10.10:443\r\n\r\n"; const char kQueryTemplate[] = "GET %s HTTP/%s\r\n%s" "User-Agent: Mozilla/5.0 (X11; CrOS i686 1299.0.2011) " "AppleWebKit/535.8 (KHTML, like Gecko) Chrome/17.0.936.0 Safari/535.8\r\n" "Accept: text/html,application/xhtml+xml,application/xml;" "q=0.9,*/*;q=0.8\r\n" "Accept-Encoding: gzip,deflate,sdch\r\n" "Accept-Language: en-US,en;q=0.8,ja;q=0.6\r\n" "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3\r\n" "Cookie: PREF=ID=xxxxxxxxxxxxxxxx:U=xxxxxxxxxxxxxxxx:FF=0:" "TM=1317340083:LM=1317390705:GM=1:S=_xxxxxxxxxxxxxxx; " "NID=52=xxxxxxxxxxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxx; " "HSID=xxxxxxxxxxxx-xxxx; APISID=xxxxxxxxxxxxxxxx/xxxxxxxxxxxxxxxxx; " "SID=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_xxxxxxxxxxx" "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-xxxxxxxxxxxxxxx" "xxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-xxxxxxxxxxxxxxxxxx" "_xxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxx-xx-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" "xxxxxxxxxxxxxxxx\r\n\r\n"; const char kInterfaceName[] = "int0"; const char kDNSServer0[] = "8.8.8.8"; const char kDNSServer1[] = "8.8.4.4"; const char kServerAddress[] = "10.10.10.10"; const char* kDNSServers[] = { kDNSServer0, kDNSServer1 }; const int kProxyFD = 10203; const int kServerFD = 10204; const int kClientFD = 10205; const int kServerPort = 40506; const int kConnectPort = 443; } // namespace MATCHER_P(IsIPAddress, address, "") { IPAddress ip_address(IPAddress::kFamilyIPv4); EXPECT_TRUE(ip_address.SetAddressFromString(address)); return ip_address.Equals(arg); } MATCHER_P(CallbackEq, callback, "") { return arg.Equals(callback); } class HTTPProxyTest : public Test { public: HTTPProxyTest() : interface_name_(kInterfaceName), server_async_connection_(nullptr), dns_servers_(kDNSServers, kDNSServers + 2), dns_client_(nullptr), device_info_( new NiceMock(&control_, nullptr, nullptr, nullptr)), connection_(new StrictMock(device_info_.get())), proxy_(connection_) {} protected: virtual void SetUp() { EXPECT_CALL(*connection_.get(), interface_name()) .WillRepeatedly(ReturnRef(interface_name_)); EXPECT_CALL(*connection_.get(), dns_servers()) .WillRepeatedly(ReturnRef(dns_servers_)); } virtual void TearDown() { if (proxy_.sockets_) { ExpectStop(); } const int proxy_fds[] = { proxy_.client_socket_, proxy_.server_socket_, proxy_.proxy_socket_ }; for (const int fd : proxy_fds) { if (fd != -1) { EXPECT_CALL(sockets_, Close(fd)); } } } string CreateRequest(const string& url, const string& http_version, const string& extra_lines) { string append_lines(extra_lines); if (append_lines.size()) { append_lines.append("\r\n"); } return StringPrintf(kQueryTemplate, url.c_str(), http_version.c_str(), append_lines.c_str()); } int InvokeGetSockName(int fd, struct sockaddr* addr_out, socklen_t* sockaddr_size) { struct sockaddr_in addr; EXPECT_EQ(kProxyFD, fd); EXPECT_GE(sizeof(sockaddr_in), *sockaddr_size); addr.sin_addr.s_addr = 0; addr.sin_port = kServerPort; memcpy(addr_out, &addr, sizeof(addr)); *sockaddr_size = sizeof(sockaddr_in); return 0; } void InvokeSyncConnect(const IPAddress& /*address*/, int /*port*/) { proxy_.OnConnectCompletion(true, kServerFD); } size_t FindInRequest(const string& find_string) { const ByteString& request_data = GetClientData(); string request_string( reinterpret_cast(request_data.GetConstData()), request_data.GetLength()); return request_string.find(find_string); } // Accessors const ByteString& GetClientData() { return proxy_.client_data_; } HTTPProxy* proxy() { return &proxy_; } HTTPProxy::State GetProxyState() { return proxy_.state_; } const ByteString& GetServerData() { return proxy_.server_data_; } MockSockets& sockets() { return sockets_; } MockEventDispatcher& dispatcher() { return dispatcher_; } // Expectations void ExpectClientReset() { EXPECT_EQ(-1, proxy_.client_socket_); EXPECT_TRUE(proxy_.client_version_.empty()); EXPECT_EQ(HTTPProxy::kDefaultServerPort, proxy_.server_port_); EXPECT_EQ(-1, proxy_.server_socket_); EXPECT_TRUE(proxy_.idle_timeout_.IsCancelled()); EXPECT_TRUE(proxy_.client_headers_.empty()); EXPECT_TRUE(proxy_.server_hostname_.empty()); EXPECT_TRUE(proxy_.client_data_.IsEmpty()); EXPECT_TRUE(proxy_.server_data_.IsEmpty()); EXPECT_FALSE(proxy_.read_client_handler_.get()); EXPECT_FALSE(proxy_.write_client_handler_.get()); EXPECT_FALSE(proxy_.read_server_handler_.get()); EXPECT_FALSE(proxy_.write_server_handler_.get()); EXPECT_FALSE(proxy_.is_route_requested_); } void ExpectReset() { EXPECT_FALSE(proxy_.accept_handler_.get()); EXPECT_EQ(proxy_.connection_.get(), connection_.get()); EXPECT_FALSE(proxy_.dispatcher_); EXPECT_FALSE(proxy_.dns_client_.get()); EXPECT_EQ(-1, proxy_.proxy_port_); EXPECT_EQ(-1, proxy_.proxy_socket_); EXPECT_FALSE(proxy_.server_async_connection_.get()); EXPECT_FALSE(proxy_.sockets_); EXPECT_EQ(HTTPProxy::kStateIdle, proxy_.state_); ExpectClientReset(); } void ExpectStart() { EXPECT_CALL(sockets(), Socket(_, _, _)) .WillOnce(Return(kProxyFD)); EXPECT_CALL(sockets(), Bind(kProxyFD, _, _)) .WillOnce(Return(0)); EXPECT_CALL(sockets(), GetSockName(kProxyFD, _, _)) .WillOnce(Invoke(this, &HTTPProxyTest::InvokeGetSockName)); EXPECT_CALL(sockets(), SetNonBlocking(kProxyFD)) .WillOnce(Return(0)); EXPECT_CALL(sockets(), Listen(kProxyFD, _)) .WillOnce(Return(0)); EXPECT_CALL(dispatcher_, CreateReadyHandler(kProxyFD, IOHandler::kModeInput, CallbackEq(proxy_.accept_callback_))) .WillOnce(ReturnNew()); } void ExpectStop() { if (dns_client_) { EXPECT_CALL(*dns_client_, Stop()) .Times(AtLeast(1)); } if (server_async_connection_) { EXPECT_CALL(*server_async_connection_, Stop()) .Times(AtLeast(1)); } if (proxy_.is_route_requested_) { EXPECT_CALL(*connection_.get(), ReleaseRouting()); } } void ExpectClientInput(int fd) { EXPECT_CALL(sockets(), Accept(kProxyFD, _, _)) .WillOnce(Return(fd)); EXPECT_CALL(sockets(), SetNonBlocking(fd)) .WillOnce(Return(0)); EXPECT_CALL(dispatcher(), CreateInputHandler(fd, CallbackEq(proxy_.read_client_callback_), _)) .WillOnce(ReturnNew()); ExpectTransactionTimeout(); ExpectClientHeaderTimeout(); } void ExpectTimeout(int timeout) { EXPECT_CALL(dispatcher_, PostDelayedTask(_, timeout * 1000)); } void ExpectClientHeaderTimeout() { ExpectTimeout(HTTPProxy::kClientHeaderTimeoutSeconds); } void ExpectConnectTimeout() { ExpectTimeout(HTTPProxy::kConnectTimeoutSeconds); } void ExpectInputTimeout() { ExpectTimeout(HTTPProxy::kInputTimeoutSeconds); } void ExpectRepeatedInputTimeout() { EXPECT_CALL(dispatcher_, PostDelayedTask(_, HTTPProxy::kInputTimeoutSeconds * 1000)) .Times(AnyNumber()); } void ExpectTransactionTimeout() { ExpectTimeout(HTTPProxy::kTransactionTimeoutSeconds); } void ExpectInClientResponse(const string& response_data) { string server_data(reinterpret_cast(proxy_.server_data_.GetData()), proxy_.server_data_.GetLength()); EXPECT_NE(string::npos, server_data.find(response_data)); } void ExpectClientError(int code, const string& error) { EXPECT_EQ(HTTPProxy::kStateFlushResponse, GetProxyState()); string status_line = StringPrintf("HTTP/1.1 %d ERROR", code); ExpectInClientResponse(status_line); ExpectInClientResponse(error); } void ExpectClientInternalError() { ExpectClientError(500, HTTPProxy::kInternalErrorMsg); } void ExpectClientVersion(const string& version) { EXPECT_EQ(version, proxy_.client_version_); } void ExpectServerHostname(const string& hostname) { EXPECT_EQ(hostname, proxy_.server_hostname_); } void ExpectFirstLine(const string& line) { EXPECT_EQ(line, proxy_.client_headers_[0] + "\r\n"); } void ExpectDNSRequest(const string& host, bool return_value) { EXPECT_CALL(*dns_client_, Start(StrEq(host), _)) .WillOnce(Return(return_value)); } void ExpectAsyncConnect(const string& address, int port, bool return_value) { EXPECT_CALL(*server_async_connection_, Start(IsIPAddress(address), port)) .WillOnce(Return(return_value)); } void ExpectSyncConnect(const string& address, int port) { EXPECT_CALL(*server_async_connection_, Start(IsIPAddress(address), port)) .WillOnce(DoAll(Invoke(this, &HTTPProxyTest::InvokeSyncConnect), Return(true))); } void ExpectClientData() { EXPECT_CALL(dispatcher(), CreateReadyHandler(kClientFD, IOHandler::kModeOutput, CallbackEq(proxy_.write_client_callback_))) .WillOnce(ReturnNew()); } void ExpectClientResult() { ExpectClientData(); ExpectInputTimeout(); } void ExpectServerInput() { EXPECT_CALL(dispatcher(), CreateInputHandler(kServerFD, CallbackEq(proxy_.read_server_callback_), _)) .WillOnce(ReturnNew()); ExpectInputTimeout(); } void ExpectServerOutput() { EXPECT_CALL(dispatcher(), CreateReadyHandler(kServerFD, IOHandler::kModeOutput, CallbackEq(proxy_.write_server_callback_))) .WillOnce(ReturnNew()); ExpectInputTimeout(); } void ExpectRepeatedServerOutput() { EXPECT_CALL(dispatcher(), CreateReadyHandler(kServerFD, IOHandler::kModeOutput, CallbackEq(proxy_.write_server_callback_))) .WillOnce(ReturnNew()); ExpectRepeatedInputTimeout(); } void ExpectTunnelClose() { EXPECT_CALL(sockets(), Close(kClientFD)) .WillOnce(Return(0)); EXPECT_CALL(sockets(), Close(kServerFD)) .WillOnce(Return(0)); ExpectStop(); } void ExpectRouteRequest() { EXPECT_CALL(*connection_.get(), RequestRouting()); } void ExpectRouteRelease() { EXPECT_CALL(*connection_.get(), ReleaseRouting()); } // Callers for various private routines in the proxy bool StartProxy() { bool ret = proxy_.Start(&dispatcher_, &sockets_); if (ret) { dns_client_ = new StrictMock(); // Passes ownership. proxy_.dns_client_.reset(dns_client_); server_async_connection_ = new StrictMock(); // Passes ownership. proxy_.server_async_connection_.reset(server_async_connection_); } return ret; } void AcceptClient(int fd) { proxy_.AcceptClient(fd); } void GetDNSResultFailure(const string& error_msg) { Error error(Error::kOperationFailed, error_msg); IPAddress address(IPAddress::kFamilyUnknown); proxy_.GetDNSResult(error, address); } void GetDNSResultSuccess(const IPAddress& address) { Error error; proxy_.GetDNSResult(error, address); } void OnConnectCompletion(bool result, int sockfd) { proxy_.OnConnectCompletion(result, sockfd); } void ReadFromClient(const string& data) { const unsigned char* ptr = reinterpret_cast(data.c_str()); vector data_bytes(ptr, ptr + data.length()); InputData proxy_data(data_bytes.data(), data_bytes.size()); proxy_.ReadFromClient(&proxy_data); } void ReadFromServer(const string& data) { const unsigned char* ptr = reinterpret_cast(data.c_str()); vector data_bytes(ptr, ptr + data.length()); InputData proxy_data(data_bytes.data(), data_bytes.size()); proxy_.ReadFromServer(&proxy_data); } void SendClientError(int code, const string& error) { proxy_.SendClientError(code, error); EXPECT_FALSE(proxy_.server_data_.IsEmpty()); } void StopClient() { EXPECT_CALL(*dns_client_, Stop()); EXPECT_CALL(*server_async_connection_, Stop()); proxy_.StopClient(); } void StopProxy() { ExpectStop(); proxy_.Stop(); server_async_connection_ = nullptr; dns_client_ = nullptr; ExpectReset(); } void WriteToClient(int fd) { proxy_.WriteToClient(fd); } void WriteToServer(int fd) { proxy_.WriteToServer(fd); } void SetupClient() { ExpectStart(); ASSERT_TRUE(StartProxy()); ExpectClientInput(kClientFD); AcceptClient(kProxyFD); EXPECT_EQ(HTTPProxy::kStateReadClientHeader, GetProxyState()); } void SetupConnectWithRequest(const string& url, const string& http_version, const string& extra_lines) { ExpectDNSRequest("www.chromium.org", true); ExpectRouteRequest(); ReadFromClient(CreateRequest(url, http_version, extra_lines)); IPAddress addr(IPAddress::kFamilyIPv4); EXPECT_TRUE(addr.SetAddressFromString(kServerAddress)); GetDNSResultSuccess(addr); } void SetupConnect() { SetupConnectWithRequest("/", "1.1", "Host: www.chromium.org:40506"); } void SetupConnectAsync() { SetupClient(); ExpectAsyncConnect(kServerAddress, kServerPort, true); ExpectConnectTimeout(); SetupConnect(); } void SetupConnectComplete() { SetupConnectAsync(); ExpectServerOutput(); OnConnectCompletion(true, kServerFD); EXPECT_EQ(HTTPProxy::kStateTunnelData, GetProxyState()); } void CauseReadError() { proxy_.OnReadError(string()); } private: const string interface_name_; // Owned by the HTTPProxy, but tracked here for EXPECT(). StrictMock* server_async_connection_; vector dns_servers_; // Owned by the HTTPProxy, but tracked here for EXPECT(). StrictMock* dns_client_; MockEventDispatcher dispatcher_; MockControl control_; std::unique_ptr device_info_; scoped_refptr connection_; StrictMock sockets_; HTTPProxy proxy_; // Destroy first, before anything it references. }; TEST_F(HTTPProxyTest, StartFailSocket) { EXPECT_CALL(sockets(), Socket(_, _, _)) .WillOnce(Return(-1)); EXPECT_FALSE(StartProxy()); ExpectReset(); } TEST_F(HTTPProxyTest, StartFailBind) { EXPECT_CALL(sockets(), Socket(_, _, _)) .WillOnce(Return(kProxyFD)); EXPECT_CALL(sockets(), Bind(kProxyFD, _, _)) .WillOnce(Return(-1)); EXPECT_CALL(sockets(), Close(kProxyFD)) .WillOnce(Return(0)); EXPECT_FALSE(StartProxy()); ExpectReset(); } TEST_F(HTTPProxyTest, StartFailGetSockName) { EXPECT_CALL(sockets(), Socket(_, _, _)) .WillOnce(Return(kProxyFD)); EXPECT_CALL(sockets(), Bind(kProxyFD, _, _)) .WillOnce(Return(0)); EXPECT_CALL(sockets(), GetSockName(kProxyFD, _, _)) .WillOnce(Return(-1)); EXPECT_CALL(sockets(), Close(kProxyFD)) .WillOnce(Return(0)); EXPECT_FALSE(StartProxy()); ExpectReset(); } TEST_F(HTTPProxyTest, StartFailSetNonBlocking) { EXPECT_CALL(sockets(), Socket(_, _, _)) .WillOnce(Return(kProxyFD)); EXPECT_CALL(sockets(), Bind(kProxyFD, _, _)) .WillOnce(Return(0)); EXPECT_CALL(sockets(), GetSockName(kProxyFD, _, _)) .WillOnce(Return(0)); EXPECT_CALL(sockets(), SetNonBlocking(kProxyFD)) .WillOnce(Return(-1)); EXPECT_CALL(sockets(), Close(kProxyFD)) .WillOnce(Return(0)); EXPECT_FALSE(StartProxy()); ExpectReset(); } TEST_F(HTTPProxyTest, StartFailListen) { EXPECT_CALL(sockets(), Socket(_, _, _)) .WillOnce(Return(kProxyFD)); EXPECT_CALL(sockets(), Bind(kProxyFD, _, _)) .WillOnce(Return(0)); EXPECT_CALL(sockets(), GetSockName(kProxyFD, _, _)) .WillOnce(Return(0)); EXPECT_CALL(sockets(), SetNonBlocking(kProxyFD)) .WillOnce(Return(0)); EXPECT_CALL(sockets(), Listen(kProxyFD, _)) .WillOnce(Return(-1)); EXPECT_CALL(sockets(), Close(kProxyFD)) .WillOnce(Return(0)); EXPECT_FALSE(StartProxy()); ExpectReset(); } TEST_F(HTTPProxyTest, StartSuccess) { ExpectStart(); EXPECT_TRUE(StartProxy()); } TEST_F(HTTPProxyTest, SendClientError) { SetupClient(); ExpectClientResult(); SendClientError(500, "This is an error"); ExpectClientError(500, "This is an error"); // We succeed in sending all but one byte of the client response. int buf_len = GetServerData().GetLength(); EXPECT_CALL(sockets(), Send(kClientFD, _, buf_len, 0)) .WillOnce(Return(buf_len - 1)); ExpectInputTimeout(); WriteToClient(kClientFD); EXPECT_EQ(1, GetServerData().GetLength()); EXPECT_EQ(HTTPProxy::kStateFlushResponse, GetProxyState()); // When we are able to send the last byte, we close the connection. EXPECT_CALL(sockets(), Send(kClientFD, _, 1, 0)) .WillOnce(Return(1)); EXPECT_CALL(sockets(), Close(kClientFD)) .WillOnce(Return(0)); ExpectStop(); WriteToClient(kClientFD); EXPECT_EQ(HTTPProxy::kStateWaitConnection, GetProxyState()); } TEST_F(HTTPProxyTest, ReadMissingURL) { SetupClient(); ExpectClientResult(); ReadFromClient(kBadHeaderMissingURL); ExpectClientError(501, "Server could not parse HTTP method"); } TEST_F(HTTPProxyTest, ReadMissingVersion) { SetupClient(); ExpectClientResult(); ReadFromClient(kBadHeaderMissingVersion); ExpectClientError(501, "Server only accepts HTTP/1.x requests"); } TEST_F(HTTPProxyTest, ReadBadHostname) { SetupClient(); ExpectClientResult(); ReadFromClient(kBadHostnameLine); ExpectClientInternalError(); } TEST_F(HTTPProxyTest, GoodFirstLineWithoutURL) { SetupClient(); ExpectClientHeaderTimeout(); ReadFromClient(kBasicGetHeader); ExpectClientVersion("1.1"); ExpectServerHostname(""); ExpectFirstLine(kBasicGetHeader); } TEST_F(HTTPProxyTest, GoodFirstLineWithURL) { SetupClient(); ExpectClientHeaderTimeout(); ReadFromClient(kBasicGetHeaderWithURL); ExpectClientVersion("1.1"); ExpectServerHostname("www.chromium.org"); ExpectFirstLine(kBasicGetHeader); } TEST_F(HTTPProxyTest, GoodFirstLineWithURLNoSlash) { SetupClient(); ExpectClientHeaderTimeout(); ReadFromClient(kBasicGetHeaderWithURLNoTrailingSlash); ExpectClientVersion("1.1"); ExpectServerHostname("www.chromium.org"); ExpectFirstLine(kBasicGetHeader); } TEST_F(HTTPProxyTest, NoHostInRequest) { SetupClient(); ExpectClientResult(); ReadFromClient(CreateRequest("/", "1.1", "")); ExpectClientError(400, "I don't know what host you want me to connect to"); } TEST_F(HTTPProxyTest, TooManyColonsInHost) { SetupClient(); ExpectClientResult(); ReadFromClient(CreateRequest("/", "1.1", "Host: www.chromium.org:80:40506")); ExpectClientError(400, "Too many colons in hostname"); } TEST_F(HTTPProxyTest, ClientReadError) { SetupClient(); EXPECT_CALL(sockets(), Close(kClientFD)) .WillOnce(Return(0)); ExpectStop(); CauseReadError(); ExpectClientReset(); } TEST_F(HTTPProxyTest, DNSRequestFailure) { SetupClient(); ExpectRouteRequest(); ExpectDNSRequest("www.chromium.org", false); ExpectClientResult(); ReadFromClient(CreateRequest("/", "1.1", "Host: www.chromium.org:40506")); ExpectClientError(502, "Could not resolve hostname"); } TEST_F(HTTPProxyTest, DNSRequestDelayedFailure) { SetupClient(); ExpectRouteRequest(); ExpectDNSRequest("www.chromium.org", true); ReadFromClient(CreateRequest("/", "1.1", "Host: www.chromium.org:40506")); ExpectClientResult(); const std::string not_found_error(DNSClient::kErrorNotFound); GetDNSResultFailure(not_found_error); ExpectClientError(502, string("Could not resolve hostname: ") + not_found_error); } TEST_F(HTTPProxyTest, TrailingClientData) { SetupClient(); ExpectRouteRequest(); ExpectDNSRequest("www.chromium.org", true); const string trailing_data("Trailing client data"); ReadFromClient(CreateRequest("/", "1.1", "Host: www.chromium.org:40506") + trailing_data); EXPECT_EQ(GetClientData().GetLength() - trailing_data.length(), FindInRequest(trailing_data)); EXPECT_EQ(HTTPProxy::kStateLookupServer, GetProxyState()); } TEST_F(HTTPProxyTest, LineContinuation) { SetupClient(); ExpectRouteRequest(); ExpectDNSRequest("www.chromium.org", true); string text_to_keep("X-Long-Header: this is one line\r\n" "\tand this is another"); ReadFromClient(CreateRequest("http://www.chromium.org/", "1.1", text_to_keep)); EXPECT_NE(string::npos, FindInRequest(text_to_keep)); } // NB: This tests two different things: // 1) That the system replaces the value for "Proxy-Connection" headers. // 2) That when it replaces a header, it also removes the text in the line // continuation. TEST_F(HTTPProxyTest, LineContinuationRemoval) { SetupClient(); ExpectRouteRequest(); ExpectDNSRequest("www.chromium.org", true); string text_to_remove("remove this text please"); ReadFromClient(CreateRequest("http://www.chromium.org/", "1.1", string("Proxy-Connection: stuff\r\n\t") + text_to_remove)); EXPECT_EQ(string::npos, FindInRequest(text_to_remove)); EXPECT_NE(string::npos, FindInRequest("Proxy-Connection: close\r\n")); } TEST_F(HTTPProxyTest, ConnectSynchronousFailure) { SetupClient(); ExpectAsyncConnect(kServerAddress, kServerPort, false); ExpectClientResult(); SetupConnect(); ExpectClientError(500, "Could not create socket to connect to server"); } TEST_F(HTTPProxyTest, ConnectAsyncConnectFailure) { SetupConnectAsync(); ExpectClientResult(); OnConnectCompletion(false, -1); ExpectClientError(500, "Socket connection delayed failure"); } TEST_F(HTTPProxyTest, ConnectSynchronousSuccess) { SetupClient(); ExpectSyncConnect(kServerAddress, 999); ExpectRepeatedServerOutput(); SetupConnectWithRequest("/", "1.1", "Host: www.chromium.org:999"); EXPECT_EQ(HTTPProxy::kStateTunnelData, GetProxyState()); } TEST_F(HTTPProxyTest, ConnectIPAddresss) { SetupClient(); ExpectSyncConnect(kServerAddress, 999); ExpectRepeatedServerOutput(); ExpectRouteRequest(); ReadFromClient(CreateRequest("/", "1.1", StringPrintf("Host: %s:999", kServerAddress))); EXPECT_EQ(HTTPProxy::kStateTunnelData, GetProxyState()); } TEST_F(HTTPProxyTest, ConnectAsyncConnectSuccess) { SetupConnectComplete(); } TEST_F(HTTPProxyTest, HTTPConnectMethod) { SetupClient(); ExpectAsyncConnect(kServerAddress, kConnectPort, true); ExpectConnectTimeout(); ExpectRouteRequest(); ReadFromClient(kConnectQuery); ExpectRepeatedInputTimeout(); ExpectClientData(); OnConnectCompletion(true, kServerFD); ExpectInClientResponse("HTTP/1.1 200 OK\r\n\r\n"); } TEST_F(HTTPProxyTest, TunnelData) { SetupConnectComplete(); // The proxy is waiting for the server to be ready to accept data. EXPECT_CALL(sockets(), Send(kServerFD, _, _, 0)) .WillOnce(Return(10)); ExpectServerInput(); WriteToServer(kServerFD); EXPECT_CALL(sockets(), Send(kServerFD, _, _, 0)) .WillOnce(ReturnArg<2>()); ExpectInputTimeout(); WriteToServer(kServerFD); EXPECT_EQ(HTTPProxy::kStateTunnelData, GetProxyState()); // Tunnel a reply back to the client. const string server_result("200 OK ... and so on"); ExpectClientResult(); ReadFromServer(server_result); EXPECT_EQ(server_result, string(reinterpret_cast( GetServerData().GetConstData()), GetServerData().GetLength())); // Allow part of the result string to be sent to the client. const int part = server_result.length() / 2; EXPECT_CALL(sockets(), Send(kClientFD, _, server_result.length(), 0)) .WillOnce(Return(part)); ExpectInputTimeout(); WriteToClient(kClientFD); EXPECT_EQ(HTTPProxy::kStateTunnelData, GetProxyState()); // The Server closes the connection while the client is still reading. ExpectInputTimeout(); ReadFromServer(""); EXPECT_EQ(HTTPProxy::kStateFlushResponse, GetProxyState()); // When the last part of the response is written to the client, we close // all connections. EXPECT_CALL(sockets(), Send(kClientFD, _, server_result.length() - part, 0)) .WillOnce(ReturnArg<2>()); ExpectTunnelClose(); WriteToClient(kClientFD); EXPECT_EQ(HTTPProxy::kStateWaitConnection, GetProxyState()); } TEST_F(HTTPProxyTest, TunnelDataFailWriteClient) { SetupConnectComplete(); EXPECT_CALL(sockets(), Send(kClientFD, _, _, 0)) .WillOnce(Return(-1)); ExpectTunnelClose(); WriteToClient(kClientFD); ExpectClientReset(); EXPECT_EQ(HTTPProxy::kStateWaitConnection, GetProxyState()); } TEST_F(HTTPProxyTest, TunnelDataFailWriteServer) { SetupConnectComplete(); EXPECT_CALL(sockets(), Send(kServerFD, _, _, 0)) .WillOnce(Return(-1)); ExpectTunnelClose(); WriteToServer(kServerFD); ExpectClientReset(); EXPECT_EQ(HTTPProxy::kStateWaitConnection, GetProxyState()); } TEST_F(HTTPProxyTest, TunnelDataFailReadServer) { SetupConnectComplete(); EXPECT_CALL(sockets(), Send(kServerFD, _, _, 0)) .WillOnce(Return(10)); ExpectServerInput(); WriteToServer(kServerFD); ExpectTunnelClose(); CauseReadError(); ExpectClientReset(); EXPECT_EQ(HTTPProxy::kStateWaitConnection, GetProxyState()); } TEST_F(HTTPProxyTest, TunnelDataFailClientClose) { SetupConnectComplete(); ExpectTunnelClose(); ReadFromClient(""); ExpectClientReset(); EXPECT_EQ(HTTPProxy::kStateWaitConnection, GetProxyState()); } TEST_F(HTTPProxyTest, TunnelDataFailServerClose) { SetupConnectComplete(); ExpectTunnelClose(); ReadFromServer(""); ExpectClientReset(); EXPECT_EQ(HTTPProxy::kStateWaitConnection, GetProxyState()); } TEST_F(HTTPProxyTest, StopClient) { SetupConnectComplete(); EXPECT_CALL(sockets(), Close(kClientFD)) .WillOnce(Return(0)); EXPECT_CALL(sockets(), Close(kServerFD)) .WillOnce(Return(0)); ExpectRouteRelease(); StopClient(); ExpectClientReset(); EXPECT_EQ(HTTPProxy::kStateWaitConnection, GetProxyState()); } } // namespace shill