root/net/tools/flip_server/spdy_interface_test.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. HasStream
  2. TEST_P
  3. TEST_P
  4. TEST_P
  5. TEST_P
  6. TEST_P
  7. TEST_P
  8. TEST_P
  9. TEST_P
  10. TEST_P
  11. TEST_P
  12. TEST_P
  13. TEST_P
  14. TEST_P
  15. TEST_P
  16. TEST_P
  17. TEST_P
  18. TEST_P
  19. TEST_P
  20. TEST_P
  21. TEST_P
  22. TEST_P
  23. TEST_P
  24. TEST_P
  25. TEST_P
  26. TEST_P

// Copyright 2013 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/tools/flip_server/spdy_interface.h"

#include <list>

#include "base/memory/scoped_ptr.h"
#include "base/strings/string_piece.h"
#include "net/spdy/buffered_spdy_framer.h"
#include "net/tools/balsa/balsa_enums.h"
#include "net/tools/balsa/balsa_headers.h"
#include "net/tools/flip_server/flip_config.h"
#include "net/tools/flip_server/flip_test_utils.h"
#include "net/tools/flip_server/mem_cache.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace net {

using ::base::StringPiece;
using ::testing::_;
using ::testing::InSequence;
using ::testing::InvokeWithoutArgs;
using ::testing::Return;
using ::testing::SaveArg;
using ::testing::Values;

namespace {

struct StringSaver {
 public:
  StringSaver() : data(NULL), size(0) {}
  void Save() { string = std::string(data, size); }

  const char* data;
  size_t size;
  std::string string;
};

class SpdyFramerVisitor : public BufferedSpdyFramerVisitorInterface {
 public:
  virtual ~SpdyFramerVisitor() {}
  MOCK_METHOD1(OnError, void(SpdyFramer::SpdyError));
  MOCK_METHOD2(OnStreamError, void(SpdyStreamId, const std::string&));
  MOCK_METHOD6(OnSynStream,
               void(SpdyStreamId,
                    SpdyStreamId,
                    SpdyPriority,
                    bool,
                    bool,
                    const SpdyHeaderBlock&));
  MOCK_METHOD3(OnSynReply, void(SpdyStreamId, bool, const SpdyHeaderBlock&));
  MOCK_METHOD3(OnHeaders, void(SpdyStreamId, bool, const SpdyHeaderBlock&));
  MOCK_METHOD3(OnDataFrameHeader, void(SpdyStreamId, size_t, bool));
  MOCK_METHOD4(OnStreamFrameData, void(SpdyStreamId,
                                       const char*,
                                       size_t,
                                       bool));
  MOCK_METHOD1(OnSettings, void(bool clear_persisted));
  MOCK_METHOD3(OnSetting, void(SpdySettingsIds, uint8, uint32));
  MOCK_METHOD2(OnPing, void(SpdyPingId unique_id, bool is_ack));
  MOCK_METHOD2(OnRstStream, void(SpdyStreamId, SpdyRstStreamStatus));
  MOCK_METHOD2(OnGoAway, void(SpdyStreamId, SpdyGoAwayStatus));
  MOCK_METHOD2(OnWindowUpdate, void(SpdyStreamId, uint32));
  MOCK_METHOD2(OnPushPromise, void(SpdyStreamId, SpdyStreamId));
};

class FakeSMConnection : public SMConnection {
 public:
  FakeSMConnection(EpollServer* epoll_server,
                   SSLState* ssl_state,
                   MemoryCache* memory_cache,
                   FlipAcceptor* acceptor,
                   std::string log_prefix)
      : SMConnection(epoll_server,
                     ssl_state,
                     memory_cache,
                     acceptor,
                     log_prefix) {}

  MOCK_METHOD0(Cleanup, void());
  MOCK_METHOD8(InitSMConnection,
               void(SMConnectionPoolInterface*,
                    SMInterface*,
                    EpollServer*,
                    int,
                    std::string,
                    std::string,
                    std::string,
                    bool));
};

// This class is almost SpdySM, except one function.
// This class is the test target of tests in this file.
class TestSpdySM : public SpdySM {
 public:
  virtual ~TestSpdySM() {}
  TestSpdySM(SMConnection* connection,
             SMInterface* sm_http_interface,
             EpollServer* epoll_server,
             MemoryCache* memory_cache,
             FlipAcceptor* acceptor,
             SpdyMajorVersion version)
      : SpdySM(connection,
               sm_http_interface,
               epoll_server,
               memory_cache,
               acceptor,
               version) {}

  MOCK_METHOD2(FindOrMakeNewSMConnectionInterface,
               SMInterface*(const std::string&, const std::string&));
};

class SpdySMTestBase : public ::testing::TestWithParam<SpdyMajorVersion> {
 public:
  explicit SpdySMTestBase(FlipHandlerType type) {
    SSLState* ssl_state = NULL;
    mock_another_interface_.reset(new MockSMInterface);
    memory_cache_.reset(new MemoryCache);
    acceptor_.reset(new FlipAcceptor(type,
                                     "127.0.0.1",
                                     "8941",
                                     "ssl_cert_filename",
                                     "ssl_key_filename",
                                     "127.0.0.1",
                                     "8942",
                                     "127.0.0.1",
                                     "8943",
                                     1,
                                     0,
                                     true,
                                     1,
                                     false,
                                     true,
                                     NULL));
    epoll_server_.reset(new EpollServer);
    connection_.reset(new FakeSMConnection(epoll_server_.get(),
                                           ssl_state,
                                           memory_cache_.get(),
                                           acceptor_.get(),
                                           "log_prefix"));

    interface_.reset(new TestSpdySM(connection_.get(),
                                    mock_another_interface_.get(),
                                    epoll_server_.get(),
                                    memory_cache_.get(),
                                    acceptor_.get(),
                                    GetParam()));

    spdy_framer_.reset(new BufferedSpdyFramer(GetParam(), true));
    spdy_framer_visitor_.reset(new SpdyFramerVisitor);
    spdy_framer_->set_visitor(spdy_framer_visitor_.get());
  }

  virtual ~SpdySMTestBase() {
    if (acceptor_->listen_fd_ >= 0) {
      epoll_server_->UnregisterFD(acceptor_->listen_fd_);
      close(acceptor_->listen_fd_);
      acceptor_->listen_fd_ = -1;
    }
    OutputList& output_list = *connection_->output_list();
    for (OutputList::const_iterator i = output_list.begin();
         i != output_list.end();
         ++i) {
      delete *i;
    }
    output_list.clear();
  }

  bool HasStream(uint32 stream_id) {
    return interface_->output_ordering().ExistsInPriorityMaps(stream_id);
  }

 protected:
  scoped_ptr<MockSMInterface> mock_another_interface_;
  scoped_ptr<MemoryCache> memory_cache_;
  scoped_ptr<FlipAcceptor> acceptor_;
  scoped_ptr<EpollServer> epoll_server_;
  scoped_ptr<FakeSMConnection> connection_;
  scoped_ptr<TestSpdySM> interface_;
  scoped_ptr<BufferedSpdyFramer> spdy_framer_;
  scoped_ptr<SpdyFramerVisitor> spdy_framer_visitor_;
};

class SpdySMProxyTest : public SpdySMTestBase {
 public:
  SpdySMProxyTest() : SpdySMTestBase(FLIP_HANDLER_PROXY) {}
  virtual ~SpdySMProxyTest() {}
};

class SpdySMServerTest : public SpdySMTestBase {
 public:
  SpdySMServerTest() : SpdySMTestBase(FLIP_HANDLER_SPDY_SERVER) {}
  virtual ~SpdySMServerTest() {}
};

INSTANTIATE_TEST_CASE_P(SpdySMProxyTest,
                        SpdySMProxyTest,
                        Values(SPDY2, SPDY3, SPDY4));
INSTANTIATE_TEST_CASE_P(SpdySMServerTest, SpdySMServerTest, Values(SPDY2));

TEST_P(SpdySMProxyTest, InitSMConnection) {
  {
    InSequence s;
    EXPECT_CALL(*connection_, InitSMConnection(_, _, _, _, _, _, _, _));
  }
  interface_->InitSMConnection(
      NULL, NULL, epoll_server_.get(), -1, "", "", "", false);
}

TEST_P(SpdySMProxyTest, OnSynStream_SPDY2) {
  if (GetParam() != SPDY2) {
    // This test case is for SPDY2.
    return;
  }
  BufferedSpdyFramerVisitorInterface* visitor = interface_.get();
  scoped_ptr<MockSMInterface> mock_interface(new MockSMInterface);
  uint32 stream_id = 92;
  uint32 associated_id = 43;
  std::string expected = "GET /path HTTP/1.0\r\n"
      "Host: 127.0.0.1\r\n"
      "hoge: fuga\r\n\r\n";
  SpdyHeaderBlock block;
  block["method"] = "GET";
  block["url"] = "/path";
  block["scheme"] = "http";
  block["version"] = "HTTP/1.0";
  block["hoge"] = "fuga";
  StringSaver saver;
  {
    InSequence s;
    EXPECT_CALL(*interface_, FindOrMakeNewSMConnectionInterface(_, _))
        .WillOnce(Return(mock_interface.get()));
    EXPECT_CALL(*mock_interface, SetStreamID(stream_id));
    EXPECT_CALL(*mock_interface, ProcessWriteInput(_, _))
        .WillOnce(DoAll(SaveArg<0>(&saver.data),
                        SaveArg<1>(&saver.size),
                        InvokeWithoutArgs(&saver, &StringSaver::Save),
                        Return(0)));
  }
  visitor->OnSynStream(stream_id, associated_id, 0, false, false, block);
  ASSERT_EQ(expected, saver.string);
}

TEST_P(SpdySMProxyTest, OnSynStream) {
  if (GetParam() == SPDY2) {
    // This test case is not for SPDY2.
    return;
  }
  BufferedSpdyFramerVisitorInterface* visitor = interface_.get();
  scoped_ptr<MockSMInterface> mock_interface(new MockSMInterface);
  uint32 stream_id = 92;
  uint32 associated_id = 43;
  std::string expected = "GET /path HTTP/1.1\r\n"
      "Host: 127.0.0.1\r\n"
      "foo: bar\r\n\r\n";
  SpdyHeaderBlock block;
  block[":method"] = "GET";
  block[":host"] = "www.example.com";
  block[":path"] = "/path";
  block[":scheme"] = "http";
  block["foo"] = "bar";
  StringSaver saver;
  {
    InSequence s;
    EXPECT_CALL(*interface_,
                FindOrMakeNewSMConnectionInterface(_, _))
        .WillOnce(Return(mock_interface.get()));
    EXPECT_CALL(*mock_interface, SetStreamID(stream_id));
    EXPECT_CALL(*mock_interface, ProcessWriteInput(_, _))
        .WillOnce(DoAll(SaveArg<0>(&saver.data),
                        SaveArg<1>(&saver.size),
                        InvokeWithoutArgs(&saver, &StringSaver::Save),
                        Return(0)));
  }
  visitor->OnSynStream(stream_id, associated_id, 0, false, false, block);
  ASSERT_EQ(expected, saver.string);
}

TEST_P(SpdySMProxyTest, OnStreamFrameData_SPDY2) {
  if (GetParam() != SPDY2) {
    // This test case is for SPDY2.
    return;
  }
  BufferedSpdyFramerVisitorInterface* visitor = interface_.get();
  scoped_ptr<MockSMInterface> mock_interface(new MockSMInterface);
  uint32 stream_id = 92;
  uint32 associated_id = 43;
  SpdyHeaderBlock block;
  testing::MockFunction<void(int)> checkpoint;  // NOLINT

  scoped_ptr<SpdyFrame> frame(spdy_framer_->CreatePingFrame(12, false));
  block["method"] = "GET";
  block["url"] = "http://www.example.com/path";
  block["scheme"] = "http";
  block["version"] = "HTTP/1.0";
  {
    InSequence s;
    EXPECT_CALL(*interface_, FindOrMakeNewSMConnectionInterface(_, _))
        .WillOnce(Return(mock_interface.get()));
    EXPECT_CALL(*mock_interface, SetStreamID(stream_id));
    EXPECT_CALL(*mock_interface, ProcessWriteInput(_, _)).Times(1);
    EXPECT_CALL(checkpoint, Call(0));
    EXPECT_CALL(*mock_interface,
                ProcessWriteInput(frame->data(), frame->size())).Times(1);
  }

  visitor->OnSynStream(stream_id, associated_id, 0, false, false, block);
  checkpoint.Call(0);
  visitor->OnStreamFrameData(stream_id, frame->data(), frame->size(), true);
}

TEST_P(SpdySMProxyTest, OnStreamFrameData) {
  if (GetParam() == SPDY2) {
    // This test case is not for SPDY2.
    return;
  }
  BufferedSpdyFramerVisitorInterface* visitor = interface_.get();
  scoped_ptr<MockSMInterface> mock_interface(new MockSMInterface);
  uint32 stream_id = 92;
  uint32 associated_id = 43;
  SpdyHeaderBlock block;
  testing::MockFunction<void(int)> checkpoint;  // NOLINT

  scoped_ptr<SpdyFrame> frame(spdy_framer_->CreatePingFrame(12, false));
  block[":method"] = "GET";
  block[":host"] = "www.example.com";
  block[":path"] = "/path";
  block[":scheme"] = "http";
  block["foo"] = "bar";
  {
    InSequence s;
    EXPECT_CALL(*interface_,
                FindOrMakeNewSMConnectionInterface(_, _))
        .WillOnce(Return(mock_interface.get()));
    EXPECT_CALL(*mock_interface, SetStreamID(stream_id));
    EXPECT_CALL(*mock_interface, ProcessWriteInput(_, _)).Times(1);
    EXPECT_CALL(checkpoint, Call(0));
    EXPECT_CALL(*mock_interface,
                ProcessWriteInput(frame->data(), frame->size())).Times(1);
  }

  visitor->OnSynStream(stream_id, associated_id, 0, false, false, block);
  checkpoint.Call(0);
  visitor->OnStreamFrameData(stream_id, frame->data(), frame->size(), true);
}

TEST_P(SpdySMProxyTest, OnRstStream) {
  BufferedSpdyFramerVisitorInterface* visitor = interface_.get();
  uint32 stream_id = 82;
  MemCacheIter mci;
  mci.stream_id = stream_id;

  {
    BalsaHeaders headers;
    std::string filename = "foobar";
    memory_cache_->InsertFile(&headers, filename, "");
    mci.file_data = memory_cache_->GetFileData(filename);
  }

  interface_->AddToOutputOrder(mci);
  ASSERT_TRUE(HasStream(stream_id));
  visitor->OnRstStream(stream_id, RST_STREAM_INVALID);
  ASSERT_FALSE(HasStream(stream_id));
}

TEST_P(SpdySMProxyTest, ProcessReadInput) {
  ASSERT_EQ(SpdyFramer::SPDY_RESET, interface_->spdy_framer()->state());
  interface_->ProcessReadInput("", 1);
  ASSERT_EQ(SpdyFramer::SPDY_READING_COMMON_HEADER,
            interface_->spdy_framer()->state());
}

TEST_P(SpdySMProxyTest, ResetForNewConnection) {
  uint32 stream_id = 13;
  MemCacheIter mci;
  mci.stream_id = stream_id;
  // incomplete input
  const char input[] = {'\0', '\0', '\0'};

  {
    BalsaHeaders headers;
    std::string filename = "foobar";
    memory_cache_->InsertFile(&headers, filename, "");
    mci.file_data = memory_cache_->GetFileData(filename);
  }

  interface_->AddToOutputOrder(mci);
  ASSERT_TRUE(HasStream(stream_id));
  interface_->ProcessReadInput(input, sizeof(input));
  ASSERT_NE(SpdyFramer::SPDY_RESET, interface_->spdy_framer()->state());

  interface_->ResetForNewConnection();
  ASSERT_FALSE(HasStream(stream_id));
  ASSERT_TRUE(interface_->spdy_framer() == NULL);
}

TEST_P(SpdySMProxyTest, CreateFramer) {
  interface_->ResetForNewConnection();
  interface_->CreateFramer(SPDY2);
  ASSERT_TRUE(interface_->spdy_framer() != NULL);
  ASSERT_EQ(interface_->spdy_version(), SPDY2);

  interface_->ResetForNewConnection();
  interface_->CreateFramer(SPDY3);
  ASSERT_TRUE(interface_->spdy_framer() != NULL);
  ASSERT_EQ(interface_->spdy_version(), SPDY3);
}

TEST_P(SpdySMProxyTest, PostAcceptHook) {
  interface_->PostAcceptHook();

  ASSERT_EQ(1u, connection_->output_list()->size());
  std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin();
  DataFrame* df = *i++;

  {
    InSequence s;
    EXPECT_CALL(*spdy_framer_visitor_, OnSettings(false));
    EXPECT_CALL(*spdy_framer_visitor_,
                OnSetting(SETTINGS_MAX_CONCURRENT_STREAMS, 0u, 100u));
  }
  spdy_framer_->ProcessInput(df->data, df->size);
}

TEST_P(SpdySMProxyTest, NewStream) {
  // TODO(yhirano): SpdySM::NewStream leads to crash when
  // acceptor_->flip_handler_type_ != FLIP_HANDLER_SPDY_SERVER.
  // It should be fixed though I don't know the solution now.
}

TEST_P(SpdySMProxyTest, AddToOutputOrder) {
  uint32 stream_id = 13;
  MemCacheIter mci;
  mci.stream_id = stream_id;

  {
    BalsaHeaders headers;
    std::string filename = "foobar";
    memory_cache_->InsertFile(&headers, filename, "");
    mci.file_data = memory_cache_->GetFileData(filename);
  }

  interface_->AddToOutputOrder(mci);
  ASSERT_TRUE(HasStream(stream_id));
}

TEST_P(SpdySMProxyTest, SendErrorNotFound_SPDY2) {
  if (GetParam() != SPDY2) {
    // This test is for SPDY2.
    return;
  }
  uint32 stream_id = 82;
  SpdyHeaderBlock actual_header_block;
  const char* actual_data;
  size_t actual_size;
  testing::MockFunction<void(int)> checkpoint;  // NOLINT

  interface_->SendErrorNotFound(stream_id);

  ASSERT_EQ(2u, connection_->output_list()->size());

  {
    InSequence s;
    if (GetParam() < SPDY4) {
      EXPECT_CALL(*spdy_framer_visitor_, OnSynReply(stream_id, false, _))
          .WillOnce(SaveArg<2>(&actual_header_block));
    } else {
      EXPECT_CALL(*spdy_framer_visitor_, OnHeaders(stream_id, false, _))
          .WillOnce(SaveArg<2>(&actual_header_block));
    }
    EXPECT_CALL(checkpoint, Call(0));
    EXPECT_CALL(*spdy_framer_visitor_,
                OnDataFrameHeader(stream_id, _, true));
    EXPECT_CALL(*spdy_framer_visitor_,
                OnStreamFrameData(stream_id, _, _, false)).Times(1)
        .WillOnce(DoAll(SaveArg<1>(&actual_data),
                        SaveArg<2>(&actual_size)));
    EXPECT_CALL(*spdy_framer_visitor_,
                OnStreamFrameData(stream_id, NULL, 0, true)).Times(1);
  }

  std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin();
  DataFrame* df = *i++;
  spdy_framer_->ProcessInput(df->data, df->size);
  checkpoint.Call(0);
  df = *i++;
  spdy_framer_->ProcessInput(df->data, df->size);

  ASSERT_EQ(2, spdy_framer_->frames_received());
  ASSERT_EQ(2u, actual_header_block.size());
  ASSERT_EQ("404 Not Found", actual_header_block["status"]);
  ASSERT_EQ("HTTP/1.1", actual_header_block["version"]);
  ASSERT_EQ("wtf?", StringPiece(actual_data, actual_size));
}

TEST_P(SpdySMProxyTest, SendErrorNotFound) {
  if (GetParam() == SPDY2) {
    // This test is not for SPDY2.
    return;
  }
  uint32 stream_id = 82;
  SpdyHeaderBlock actual_header_block;
  const char* actual_data;
  size_t actual_size;
  testing::MockFunction<void(int)> checkpoint;  // NOLINT

  interface_->SendErrorNotFound(stream_id);

  ASSERT_EQ(2u, connection_->output_list()->size());

  {
    InSequence s;
    if (GetParam() < SPDY4) {
      EXPECT_CALL(*spdy_framer_visitor_,
                  OnSynReply(stream_id, false, _))
          .WillOnce(SaveArg<2>(&actual_header_block));
    } else {
      EXPECT_CALL(*spdy_framer_visitor_,
                  OnHeaders(stream_id, false, _))
          .WillOnce(SaveArg<2>(&actual_header_block));
    }
    EXPECT_CALL(checkpoint, Call(0));
    EXPECT_CALL(*spdy_framer_visitor_,
                OnDataFrameHeader(stream_id, _, true));
    EXPECT_CALL(*spdy_framer_visitor_,
                OnStreamFrameData(stream_id, _, _, false)).Times(1)
        .WillOnce(DoAll(SaveArg<1>(&actual_data),
                        SaveArg<2>(&actual_size)));
    EXPECT_CALL(*spdy_framer_visitor_,
                OnStreamFrameData(stream_id, NULL, 0, true)).Times(1);
  }

  std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin();
  DataFrame* df = *i++;
  spdy_framer_->ProcessInput(df->data, df->size);
  checkpoint.Call(0);
  df = *i++;
  spdy_framer_->ProcessInput(df->data, df->size);

  ASSERT_EQ(2, spdy_framer_->frames_received());
  ASSERT_EQ(2u, actual_header_block.size());
  ASSERT_EQ("404 Not Found", actual_header_block[":status"]);
  ASSERT_EQ("HTTP/1.1", actual_header_block[":version"]);
  ASSERT_EQ("wtf?", StringPiece(actual_data, actual_size));
}

TEST_P(SpdySMProxyTest, SendSynStream_SPDY2) {
  if (GetParam() != SPDY2) {
    // This test is for SPDY2.
    return;
  }
  uint32 stream_id = 82;
  BalsaHeaders headers;
  SpdyHeaderBlock actual_header_block;
  headers.AppendHeader("key1", "value1");
  headers.SetRequestFirstlineFromStringPieces("GET", "/path", "HTTP/1.0");

  interface_->SendSynStream(stream_id, headers);

  ASSERT_EQ(1u, connection_->output_list()->size());
  std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin();
  DataFrame* df = *i++;

  {
    InSequence s;
    EXPECT_CALL(*spdy_framer_visitor_,
                OnSynStream(stream_id, 0, _, false, false, _))
        .WillOnce(SaveArg<5>(&actual_header_block));
  }

  spdy_framer_->ProcessInput(df->data, df->size);
  ASSERT_EQ(1, spdy_framer_->frames_received());
  ASSERT_EQ(4u, actual_header_block.size());
  ASSERT_EQ("GET", actual_header_block["method"]);
  ASSERT_EQ("HTTP/1.0", actual_header_block["version"]);
  ASSERT_EQ("/path", actual_header_block["url"]);
  ASSERT_EQ("value1", actual_header_block["key1"]);
}

TEST_P(SpdySMProxyTest, SendSynStream) {
  if (GetParam() == SPDY2) {
    // This test is not for SPDY2.
    return;
  }
  uint32 stream_id = 82;
  BalsaHeaders headers;
  SpdyHeaderBlock actual_header_block;
  headers.AppendHeader("key1", "value1");
  headers.AppendHeader("Host", "www.example.com");
  headers.SetRequestFirstlineFromStringPieces("GET", "/path", "HTTP/1.1");

  interface_->SendSynStream(stream_id, headers);

  ASSERT_EQ(1u, connection_->output_list()->size());
  std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin();
  DataFrame* df = *i++;

  {
    InSequence s;
    EXPECT_CALL(*spdy_framer_visitor_,
                OnSynStream(stream_id, 0, _, false, false, _))
        .WillOnce(SaveArg<5>(&actual_header_block));
  }

  spdy_framer_->ProcessInput(df->data, df->size);
  ASSERT_EQ(1, spdy_framer_->frames_received());
  ASSERT_EQ(5u, actual_header_block.size());
  ASSERT_EQ("GET", actual_header_block[":method"]);
  ASSERT_EQ("HTTP/1.1", actual_header_block[":version"]);
  ASSERT_EQ("/path", actual_header_block[":path"]);
  ASSERT_EQ("www.example.com", actual_header_block[":host"]);
  ASSERT_EQ("value1", actual_header_block["key1"]);
}

TEST_P(SpdySMProxyTest, SendSynReply_SPDY2) {
  if (GetParam() != SPDY2) {
    // This test is for SPDY2.
    return;
  }
  uint32 stream_id = 82;
  BalsaHeaders headers;
  SpdyHeaderBlock actual_header_block;
  headers.AppendHeader("key1", "value1");
  headers.SetResponseFirstlineFromStringPieces("HTTP/1.1", "200", "OK");

  interface_->SendSynReply(stream_id, headers);

  ASSERT_EQ(1u, connection_->output_list()->size());
  std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin();
  DataFrame* df = *i++;

  {
    InSequence s;
    if (GetParam() < SPDY4) {
      EXPECT_CALL(*spdy_framer_visitor_, OnSynReply(stream_id, false, _))
          .WillOnce(SaveArg<2>(&actual_header_block));
    } else {
      EXPECT_CALL(*spdy_framer_visitor_, OnHeaders(stream_id, false, _))
          .WillOnce(SaveArg<2>(&actual_header_block));
    }
  }

  spdy_framer_->ProcessInput(df->data, df->size);
  ASSERT_EQ(1, spdy_framer_->frames_received());
  ASSERT_EQ(3u, actual_header_block.size());
  ASSERT_EQ("200 OK", actual_header_block["status"]);
  ASSERT_EQ("HTTP/1.1", actual_header_block["version"]);
  ASSERT_EQ("value1", actual_header_block["key1"]);
}

TEST_P(SpdySMProxyTest, SendSynReply) {
  if (GetParam() == SPDY2) {
    // This test is not for SPDY2.
    return;
  }
  uint32 stream_id = 82;
  BalsaHeaders headers;
  SpdyHeaderBlock actual_header_block;
  headers.AppendHeader("key1", "value1");
  headers.SetResponseFirstlineFromStringPieces("HTTP/1.1", "200", "OK");

  interface_->SendSynReply(stream_id, headers);

  ASSERT_EQ(1u, connection_->output_list()->size());
  std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin();
  DataFrame* df = *i++;

  {
    InSequence s;
    if (GetParam() < SPDY4) {
      EXPECT_CALL(*spdy_framer_visitor_, OnSynReply(stream_id, false, _))
          .WillOnce(SaveArg<2>(&actual_header_block));
    } else {
      EXPECT_CALL(*spdy_framer_visitor_, OnHeaders(stream_id, false, _))
          .WillOnce(SaveArg<2>(&actual_header_block));
    }
  }

  spdy_framer_->ProcessInput(df->data, df->size);
  ASSERT_EQ(1, spdy_framer_->frames_received());
  ASSERT_EQ(3u, actual_header_block.size());
  ASSERT_EQ("200 OK", actual_header_block[":status"]);
  ASSERT_EQ("HTTP/1.1", actual_header_block[":version"]);
  ASSERT_EQ("value1", actual_header_block["key1"]);
}

TEST_P(SpdySMProxyTest, SendDataFrame) {
  uint32 stream_id = 133;
  SpdyDataFlags flags = DATA_FLAG_NONE;
  const char* actual_data;
  size_t actual_size;

  interface_->SendDataFrame(stream_id, "hello", 5, flags, true);

  ASSERT_EQ(1u, connection_->output_list()->size());
  std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin();
  DataFrame* df = *i++;

  {
    InSequence s;
    EXPECT_CALL(*spdy_framer_visitor_,
                OnDataFrameHeader(stream_id, _, false));
    EXPECT_CALL(*spdy_framer_visitor_,
                OnStreamFrameData(stream_id, _, _, false))
        .WillOnce(DoAll(SaveArg<1>(&actual_data), SaveArg<2>(&actual_size)));
  }

  spdy_framer_->ProcessInput(df->data, df->size);
  ASSERT_EQ(1, spdy_framer_->frames_received());
  ASSERT_EQ("hello", StringPiece(actual_data, actual_size));
}

TEST_P(SpdySMProxyTest, SendLongDataFrame) {
  uint32 stream_id = 133;
  SpdyDataFlags flags = DATA_FLAG_NONE;
  const char* actual_data;
  size_t actual_size;

  std::string data = std::string(kSpdySegmentSize, 'a') +
                     std::string(kSpdySegmentSize, 'b') + "c";
  interface_->SendDataFrame(stream_id, data.data(), data.size(), flags, true);

  {
    InSequence s;
    for (int i = 0; i < 3; ++i) {
        EXPECT_CALL(*spdy_framer_visitor_,
                    OnDataFrameHeader(stream_id, _, false));
        EXPECT_CALL(*spdy_framer_visitor_,
                    OnStreamFrameData(stream_id, _, _, false))
            .WillOnce(DoAll(SaveArg<1>(&actual_data),
                            SaveArg<2>(&actual_size)));
    }
  }

  ASSERT_EQ(3u, connection_->output_list()->size());
  std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin();
  DataFrame* df = *i++;
  spdy_framer_->ProcessInput(df->data, df->size);
  ASSERT_EQ(std::string(kSpdySegmentSize, 'a'),
            StringPiece(actual_data, actual_size));

  df = *i++;
  spdy_framer_->ProcessInput(df->data, df->size);
  ASSERT_EQ(std::string(kSpdySegmentSize, 'b'),
            StringPiece(actual_data, actual_size));

  df = *i++;
  spdy_framer_->ProcessInput(df->data, df->size);
  ASSERT_EQ("c", StringPiece(actual_data, actual_size));
}

TEST_P(SpdySMProxyTest, SendEOF_SPDY2) {
  // This test is for SPDY2.
  if (GetParam() != SPDY2) {
    return;
  }

  uint32 stream_id = 82;
  // SPDY2 data frame
  char empty_data_frame[] = {'\0', '\0', '\0', '\x52', '\x1', '\0', '\0', '\0'};
  MemCacheIter mci;
  mci.stream_id = stream_id;

  {
    BalsaHeaders headers;
    std::string filename = "foobar";
    memory_cache_->InsertFile(&headers, filename, "");
    mci.file_data = memory_cache_->GetFileData(filename);
  }

  interface_->AddToOutputOrder(mci);
  ASSERT_TRUE(HasStream(stream_id));
  interface_->SendEOF(stream_id);
  ASSERT_FALSE(HasStream(stream_id));

  ASSERT_EQ(1u, connection_->output_list()->size());
  std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin();
  DataFrame* df = *i++;
  ASSERT_EQ(StringPiece(empty_data_frame, sizeof(empty_data_frame)),
            StringPiece(df->data, df->size));
}

TEST_P(SpdySMProxyTest, SendEmptyDataFrame_SPDY2) {
  // This test is for SPDY2.
  if (GetParam() != SPDY2) {
    return;
  }

  uint32 stream_id = 133;
  SpdyDataFlags flags = DATA_FLAG_NONE;
  // SPDY2 data frame
  char expected[] = {'\0', '\0', '\0', '\x85', '\0', '\0', '\0', '\0'};

  interface_->SendDataFrame(stream_id, "hello", 0, flags, true);

  ASSERT_EQ(1u, connection_->output_list()->size());
  std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin();
  DataFrame* df = *i++;

  ASSERT_EQ(StringPiece(expected, sizeof(expected)),
            StringPiece(df->data, df->size));
}

TEST_P(SpdySMServerTest, OnSynStream) {
  BufferedSpdyFramerVisitorInterface* visitor = interface_.get();
  uint32 stream_id = 82;
  SpdyHeaderBlock spdy_headers;
  spdy_headers["url"] = "http://www.example.com/path";
  spdy_headers["method"] = "GET";
  spdy_headers["scheme"] = "http";
  spdy_headers["version"] = "HTTP/1.1";

  {
    BalsaHeaders headers;
    memory_cache_->InsertFile(&headers, "GET_/path", "");
  }
  visitor->OnSynStream(stream_id, 0, 0, true, true, spdy_headers);
  ASSERT_TRUE(HasStream(stream_id));
}

TEST_P(SpdySMServerTest, NewStream) {
  uint32 stream_id = 13;
  std::string filename = "foobar";

  {
    BalsaHeaders headers;
    memory_cache_->InsertFile(&headers, filename, "");
  }

  interface_->NewStream(stream_id, 0, filename);
  ASSERT_TRUE(HasStream(stream_id));
}

TEST_P(SpdySMServerTest, NewStreamError) {
  uint32 stream_id = 82;
  SpdyHeaderBlock actual_header_block;
  const char* actual_data;
  size_t actual_size;
  testing::MockFunction<void(int)> checkpoint;  // NOLINT

  interface_->NewStream(stream_id, 0, "nonexistingfile");

  ASSERT_EQ(2u, connection_->output_list()->size());

  {
    InSequence s;
    if (GetParam() < SPDY4) {
      EXPECT_CALL(*spdy_framer_visitor_, OnSynReply(stream_id, false, _))
          .WillOnce(SaveArg<2>(&actual_header_block));
    } else {
      EXPECT_CALL(*spdy_framer_visitor_, OnHeaders(stream_id, false, _))
          .WillOnce(SaveArg<2>(&actual_header_block));
    }
    EXPECT_CALL(checkpoint, Call(0));
    EXPECT_CALL(*spdy_framer_visitor_,
                OnDataFrameHeader(stream_id, _, true));
    EXPECT_CALL(*spdy_framer_visitor_,
                OnStreamFrameData(stream_id, _, _, false)).Times(1)
        .WillOnce(DoAll(SaveArg<1>(&actual_data),
                        SaveArg<2>(&actual_size)));
    EXPECT_CALL(*spdy_framer_visitor_,
                OnStreamFrameData(stream_id, NULL, 0, true)).Times(1);
  }

  std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin();
  DataFrame* df = *i++;
  spdy_framer_->ProcessInput(df->data, df->size);
  checkpoint.Call(0);
  df = *i++;
  spdy_framer_->ProcessInput(df->data, df->size);

  ASSERT_EQ(2, spdy_framer_->frames_received());
  ASSERT_EQ(2u, actual_header_block.size());
  ASSERT_EQ("404 Not Found", actual_header_block["status"]);
  ASSERT_EQ("HTTP/1.1", actual_header_block["version"]);
  ASSERT_EQ("wtf?", StringPiece(actual_data, actual_size));
}

}  // namespace

}  // namespace net

/* [<][>][^][v][top][bottom][index][help] */