// Copyright (c) 2011 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_http_stream.h"
#include "net/spdy/spdy_session.h"
#include "net/spdy/spdy_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace net {
class SpdyHttpStreamTest : public testing::Test {
public:
OrderedSocketData* data() { return data_; }
protected:
SpdyHttpStreamTest() {}
void EnableCompression(bool enabled) {
spdy::SpdyFramer::set_enable_compression_default(enabled);
}
virtual void TearDown() {
MessageLoop::current()->RunAllPending();
}
int InitSession(MockRead* reads, size_t reads_count,
MockWrite* writes, size_t writes_count,
HostPortPair& host_port_pair) {
HostPortProxyPair pair(host_port_pair, ProxyServer::Direct());
data_ = new OrderedSocketData(reads, reads_count, writes, writes_count);
session_deps_.socket_factory->AddSocketDataProvider(data_.get());
http_session_ = SpdySessionDependencies::SpdyCreateSession(&session_deps_);
session_ = http_session_->spdy_session_pool()->Get(pair, BoundNetLog());
transport_params_ = new TransportSocketParams(host_port_pair,
MEDIUM, GURL(), false, false);
TestCompletionCallback callback;
scoped_ptr<ClientSocketHandle> connection(new ClientSocketHandle);
EXPECT_EQ(ERR_IO_PENDING,
connection->Init(host_port_pair.ToString(),
transport_params_,
MEDIUM,
&callback,
http_session_->transport_socket_pool(),
BoundNetLog()));
EXPECT_EQ(OK, callback.WaitForResult());
return session_->InitializeWithSocket(connection.release(), false, OK);
}
SpdySessionDependencies session_deps_;
scoped_refptr<OrderedSocketData> data_;
scoped_refptr<HttpNetworkSession> http_session_;
scoped_refptr<SpdySession> session_;
scoped_refptr<TransportSocketParams> transport_params_;
};
TEST_F(SpdyHttpStreamTest, SendRequest) {
EnableCompression(false);
SpdySession::SetSSLMode(false);
scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST));
MockWrite writes[] = {
CreateMockWrite(*req.get(), 1),
};
scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0, 1));
MockRead reads[] = {
CreateMockRead(*resp, 2),
MockRead(false, 0, 3) // EOF
};
HostPortPair host_port_pair("www.google.com", 80);
HostPortProxyPair pair(host_port_pair, ProxyServer::Direct());
EXPECT_EQ(OK, InitSession(reads, arraysize(reads), writes, arraysize(writes),
host_port_pair));
HttpRequestInfo request;
request.method = "GET";
request.url = GURL("http://www.google.com/");
TestCompletionCallback callback;
HttpResponseInfo response;
HttpRequestHeaders headers;
BoundNetLog net_log;
scoped_ptr<SpdyHttpStream> http_stream(
new SpdyHttpStream(session_.get(), true));
ASSERT_EQ(
OK,
http_stream->InitializeStream(&request, net_log, NULL));
EXPECT_EQ(ERR_IO_PENDING,
http_stream->SendRequest(headers, NULL, &response, &callback));
EXPECT_TRUE(http_session_->spdy_session_pool()->HasSession(pair));
// This triggers the MockWrite and read 2
callback.WaitForResult();
// This triggers read 3. The empty read causes the session to shut down.
data()->CompleteRead();
// Because we abandoned the stream, we don't expect to find a session in the
// pool anymore.
EXPECT_FALSE(http_session_->spdy_session_pool()->HasSession(pair));
EXPECT_TRUE(data()->at_read_eof());
EXPECT_TRUE(data()->at_write_eof());
}
TEST_F(SpdyHttpStreamTest, SendChunkedPost) {
EnableCompression(false);
SpdySession::SetSSLMode(false);
UploadDataStream::set_merge_chunks(false);
scoped_ptr<spdy::SpdyFrame> req(ConstructChunkedSpdyPost(NULL, 0));
scoped_ptr<spdy::SpdyFrame> chunk1(ConstructSpdyBodyFrame(1, false));
scoped_ptr<spdy::SpdyFrame> chunk2(ConstructSpdyBodyFrame(1, true));
MockWrite writes[] = {
CreateMockWrite(*req.get(), 1),
CreateMockWrite(*chunk1, 2), // POST upload frames
CreateMockWrite(*chunk2, 3),
};
scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyPostSynReply(NULL, 0));
MockRead reads[] = {
CreateMockRead(*resp, 4),
CreateMockRead(*chunk1, 5),
CreateMockRead(*chunk2, 5),
MockRead(false, 0, 6) // EOF
};
HostPortPair host_port_pair("www.google.com", 80);
HostPortProxyPair pair(host_port_pair, ProxyServer::Direct());
EXPECT_EQ(OK, InitSession(reads, arraysize(reads), writes, arraysize(writes),
host_port_pair));
HttpRequestInfo request;
request.method = "POST";
request.url = GURL("http://www.google.com/");
request.upload_data = new UploadData();
request.upload_data->set_is_chunked(true);
request.upload_data->AppendChunk(kUploadData, kUploadDataSize, false);
request.upload_data->AppendChunk(kUploadData, kUploadDataSize, true);
TestCompletionCallback callback;
HttpResponseInfo response;
HttpRequestHeaders headers;
BoundNetLog net_log;
SpdyHttpStream http_stream(session_.get(), true);
ASSERT_EQ(
OK,
http_stream.InitializeStream(&request, net_log, NULL));
UploadDataStream* upload_stream =
UploadDataStream::Create(request.upload_data, NULL);
EXPECT_EQ(ERR_IO_PENDING, http_stream.SendRequest(
headers, upload_stream, &response, &callback));
EXPECT_TRUE(http_session_->spdy_session_pool()->HasSession(pair));
// This triggers the MockWrite and read 2
callback.WaitForResult();
// This triggers read 3. The empty read causes the session to shut down.
data()->CompleteRead();
MessageLoop::current()->RunAllPending();
// Because we abandoned the stream, we don't expect to find a session in the
// pool anymore.
EXPECT_FALSE(http_session_->spdy_session_pool()->HasSession(pair));
EXPECT_TRUE(data()->at_read_eof());
EXPECT_TRUE(data()->at_write_eof());
}
// Test case for bug: http://code.google.com/p/chromium/issues/detail?id=50058
TEST_F(SpdyHttpStreamTest, SpdyURLTest) {
EnableCompression(false);
SpdySession::SetSSLMode(false);
const char * const full_url = "http://www.google.com/foo?query=what#anchor";
const char * const base_url = "http://www.google.com/foo?query=what";
scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(base_url, false, 1, LOWEST));
MockWrite writes[] = {
CreateMockWrite(*req.get(), 1),
};
scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0, 1));
MockRead reads[] = {
CreateMockRead(*resp, 2),
MockRead(false, 0, 3) // EOF
};
HostPortPair host_port_pair("www.google.com", 80);
HostPortProxyPair pair(host_port_pair, ProxyServer::Direct());
EXPECT_EQ(OK, InitSession(reads, arraysize(reads), writes, arraysize(writes),
host_port_pair));
HttpRequestInfo request;
request.method = "GET";
request.url = GURL(full_url);
TestCompletionCallback callback;
HttpResponseInfo response;
HttpRequestHeaders headers;
BoundNetLog net_log;
scoped_ptr<SpdyHttpStream> http_stream(new SpdyHttpStream(session_, true));
ASSERT_EQ(
OK,
http_stream->InitializeStream(&request, net_log, NULL));
EXPECT_EQ(ERR_IO_PENDING,
http_stream->SendRequest(headers, NULL, &response, &callback));
spdy::SpdyHeaderBlock* spdy_header =
http_stream->stream()->spdy_headers().get();
EXPECT_TRUE(spdy_header != NULL);
if (spdy_header->find("url") != spdy_header->end())
EXPECT_EQ("/foo?query=what", spdy_header->find("url")->second);
else
FAIL() << "No url is set in spdy_header!";
// This triggers the MockWrite and read 2
callback.WaitForResult();
// This triggers read 3. The empty read causes the session to shut down.
data()->CompleteRead();
// Because we abandoned the stream, we don't expect to find a session in the
// pool anymore.
EXPECT_FALSE(http_session_->spdy_session_pool()->HasSession(pair));
EXPECT_TRUE(data()->at_read_eof());
EXPECT_TRUE(data()->at_write_eof());
}
// TODO(willchan): Write a longer test for SpdyStream that exercises all
// methods.
} // namespace net