blob: d0222326331f35d9e51315bfe4cd11fa575bc66a [file] [log] [blame]
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/spdy/spdy_io_buffer.h"
#include "googleurl/src/gurl.h"
#include "net/base/mock_host_resolver.h"
#include "net/base/ssl_config_service_defaults.h"
#include "net/base/test_completion_callback.h"
#include "net/http/http_network_session.h"
#include "net/proxy/proxy_service.h"
#include "net/socket/socket_test_util.h"
#include "net/spdy/spdy_session.h"
#include "net/spdy/spdy_session_pool.h"
#include "net/spdy/spdy_stream.h"
#include "testing/platform_test.h"
namespace net {
// TODO(cbentzel): Expose compression setter/getter in public SpdySession
// interface rather than going through all these contortions.
class SpdySessionTest : public PlatformTest {
public:
static void TurnOffCompression() {
spdy::SpdyFramer::set_enable_compression_default(false);
}
};
namespace {
// Helper to manage the lifetimes of the dependencies for a
// SpdyNetworkTransaction.
class SessionDependencies {
public:
// Default set of dependencies -- "null" proxy service.
SessionDependencies()
: host_resolver(new MockHostResolver),
proxy_service(ProxyService::CreateNull()),
ssl_config_service(new SSLConfigServiceDefaults),
spdy_session_pool(new SpdySessionPool) {
}
scoped_refptr<MockHostResolverBase> host_resolver;
scoped_refptr<ProxyService> proxy_service;
scoped_refptr<SSLConfigService> ssl_config_service;
MockClientSocketFactory socket_factory;
scoped_refptr<SpdySessionPool> spdy_session_pool;
};
HttpNetworkSession* CreateSession(SessionDependencies* session_deps) {
return new HttpNetworkSession(NULL,
session_deps->host_resolver,
session_deps->proxy_service,
&session_deps->socket_factory,
session_deps->ssl_config_service,
session_deps->spdy_session_pool,
NULL,
NULL);
}
// Test the SpdyIOBuffer class.
TEST_F(SpdySessionTest, SpdyIOBuffer) {
std::priority_queue<SpdyIOBuffer> queue_;
const size_t kQueueSize = 100;
// Insert 100 items; pri 100 to 1.
for (size_t index = 0; index < kQueueSize; ++index) {
SpdyIOBuffer buffer(new IOBuffer(), 0, kQueueSize - index, NULL);
queue_.push(buffer);
}
// Insert several priority 0 items last.
const size_t kNumDuplicates = 12;
IOBufferWithSize* buffers[kNumDuplicates];
for (size_t index = 0; index < kNumDuplicates; ++index) {
buffers[index] = new IOBufferWithSize(index+1);
queue_.push(SpdyIOBuffer(buffers[index], buffers[index]->size(), 0, NULL));
}
EXPECT_EQ(kQueueSize + kNumDuplicates, queue_.size());
// Verify the P0 items come out in FIFO order.
for (size_t index = 0; index < kNumDuplicates; ++index) {
SpdyIOBuffer buffer = queue_.top();
EXPECT_EQ(0, buffer.priority());
EXPECT_EQ(index + 1, buffer.size());
queue_.pop();
}
int priority = 1;
while (queue_.size()) {
SpdyIOBuffer buffer = queue_.top();
EXPECT_EQ(priority++, buffer.priority());
queue_.pop();
}
}
static const unsigned char kGoAway[] = {
0x80, 0x01, 0x00, 0x07, // header
0x00, 0x00, 0x00, 0x04, // flags, len
0x00, 0x00, 0x00, 0x00, // last-accepted-stream-id
};
TEST_F(SpdySessionTest, GoAway) {
SessionDependencies session_deps;
session_deps.host_resolver->set_synchronous_mode(true);
MockConnect connect_data(false, OK);
MockRead reads[] = {
MockRead(false, reinterpret_cast<const char*>(kGoAway),
arraysize(kGoAway)),
MockRead(false, 0, 0) // EOF
};
StaticSocketDataProvider data(reads, arraysize(reads), NULL, 0);
data.set_connect_data(connect_data);
session_deps.socket_factory.AddSocketDataProvider(&data);
SSLSocketDataProvider ssl(false, OK);
session_deps.socket_factory.AddSSLSocketDataProvider(&ssl);
scoped_refptr<HttpNetworkSession> http_session(CreateSession(&session_deps));
const std::string kTestHost("www.foo.com");
const int kTestPort = 80;
HostPortPair test_host_port_pair;
test_host_port_pair.host = kTestHost;
test_host_port_pair.port = kTestPort;
scoped_refptr<SpdySessionPool> spdy_session_pool(
http_session->spdy_session_pool());
EXPECT_FALSE(spdy_session_pool->HasSession(test_host_port_pair));
scoped_refptr<SpdySession> session =
spdy_session_pool->Get(
test_host_port_pair, http_session.get(), BoundNetLog());
EXPECT_TRUE(spdy_session_pool->HasSession(test_host_port_pair));
TCPSocketParams tcp_params(kTestHost, kTestPort, MEDIUM, GURL(), false);
int rv = session->Connect(kTestHost, tcp_params, MEDIUM);
ASSERT_EQ(OK, rv);
// Flush the SpdySession::OnReadComplete() task.
MessageLoop::current()->RunAllPending();
EXPECT_FALSE(spdy_session_pool->HasSession(test_host_port_pair));
scoped_refptr<SpdySession> session2 =
spdy_session_pool->Get(
test_host_port_pair, http_session.get(), BoundNetLog());
// Delete the first session.
session = NULL;
// Delete the second session.
spdy_session_pool->Remove(session2);
session2 = NULL;
}
// kPush is a server-issued SYN_STREAM with stream id 2, and
// associated stream id 1. It also includes 3 headers of path,
// status, and HTTP version.
static const uint8 kPush[] = {
0x80, 0x01, 0x00, 0x01, // SYN_STREAM for SPDY v1.
0x00, 0x00, 0x00, 0x3b, // No flags 59 bytes after this 8 byte header.
0x00, 0x00, 0x00, 0x02, // Stream ID of 2
0x00, 0x00, 0x00, 0x01, // Associate Stream ID of 1
0x00, 0x00, 0x00, 0x03, // Priority 0, 3 name/value pairs in block below.
0x00, 0x04, 'p', 'a', 't', 'h',
0x00, 0x07, '/', 'f', 'o', 'o', '.', 'j', 's',
0x00, 0x06, 's', 't', 'a', 't', 'u', 's',
0x00, 0x03, '2', '0', '0',
0x00, 0x07, 'v', 'e', 'r', 's', 'i', 'o', 'n',
0x00, 0x08, 'H', 'T', 'T', 'P', '/', '1', '.', '1',
};
} // namespace
TEST_F(SpdySessionTest, GetPushStream) {
SpdySessionTest::TurnOffCompression();
SessionDependencies session_deps;
session_deps.host_resolver->set_synchronous_mode(true);
MockConnect connect_data(false, OK);
MockRead reads[] = {
MockRead(false, reinterpret_cast<const char*>(kPush),
arraysize(kPush)),
MockRead(true, ERR_IO_PENDING, 0) // EOF
};
StaticSocketDataProvider data(reads, arraysize(reads), NULL, 0);
data.set_connect_data(connect_data);
session_deps.socket_factory.AddSocketDataProvider(&data);
SSLSocketDataProvider ssl(false, OK);
session_deps.socket_factory.AddSSLSocketDataProvider(&ssl);
scoped_refptr<HttpNetworkSession> http_session(CreateSession(&session_deps));
const std::string kTestHost("www.foo.com");
const int kTestPort = 80;
HostPortPair test_host_port_pair;
test_host_port_pair.host = kTestHost;
test_host_port_pair.port = kTestPort;
scoped_refptr<SpdySessionPool> spdy_session_pool(
http_session->spdy_session_pool());
EXPECT_FALSE(spdy_session_pool->HasSession(test_host_port_pair));
scoped_refptr<SpdySession> session =
spdy_session_pool->Get(
test_host_port_pair, http_session.get(), BoundNetLog());
EXPECT_TRUE(spdy_session_pool->HasSession(test_host_port_pair));
// No push streams should exist in the beginning.
std::string test_push_path = "/foo.js";
scoped_refptr<SpdyStream> first_stream = session->GetPushStream(
test_push_path);
EXPECT_EQ(static_cast<SpdyStream*>(NULL), first_stream.get());
// Read in the data which contains a server-issued SYN_STREAM.
TCPSocketParams tcp_params(test_host_port_pair, MEDIUM, GURL(), false);
int rv = session->Connect(kTestHost, tcp_params, MEDIUM);
ASSERT_EQ(OK, rv);
MessageLoop::current()->RunAllPending();
// An unpushed path should not work.
scoped_refptr<SpdyStream> unpushed_stream = session->GetPushStream(
"/unpushed_path");
EXPECT_EQ(static_cast<SpdyStream*>(NULL), unpushed_stream.get());
// The pushed path should be found.
scoped_refptr<SpdyStream> second_stream = session->GetPushStream(
test_push_path);
ASSERT_NE(static_cast<SpdyStream*>(NULL), second_stream.get());
EXPECT_EQ(test_push_path, second_stream->path());
EXPECT_EQ(2U, second_stream->stream_id());
EXPECT_EQ(0, second_stream->priority());
// Clean up
second_stream = NULL;
session = NULL;
spdy_session_pool->CloseAllSessions();
// RunAllPending needs to be called here because the
// ClientSocketPoolBase posts a task to clean up and destroy the
// underlying socket.
MessageLoop::current()->RunAllPending();
}
} // namespace net