diff options
author | yhirano@chromium.org <yhirano@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-08-12 06:01:35 +0000 |
---|---|---|
committer | yhirano@chromium.org <yhirano@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-08-12 06:01:35 +0000 |
commit | 3985bc158abafb260f387f3d6add0d4477bd6137 (patch) | |
tree | d6d8758c9b5ed49a128136546acd318de96631c8 | |
parent | a4ebea4fa1240921aea7735c73b0072991235c77 (diff) | |
download | chromium_src-3985bc158abafb260f387f3d6add0d4477bd6137.zip chromium_src-3985bc158abafb260f387f3d6add0d4477bd6137.tar.gz chromium_src-3985bc158abafb260f387f3d6add0d4477bd6137.tar.bz2 |
Add unittests for balsa classes.
Add unittests for net::BalsaFrame, net::BalsaBuffer and net::BalsaHeaders.
This CL does not change any behavior.
R=rch@chromium.org
BUG=267354
Committed: https://src.chromium.org/viewvc/chrome?view=rev&revision=216572
Review URL: https://chromiumcodereview.appspot.com/22573002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@216948 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | net/net.gyp | 2 | ||||
-rw-r--r-- | net/tools/flip_server/balsa_frame_test.cc | 599 | ||||
-rw-r--r-- | net/tools/flip_server/balsa_headers.h | 1 | ||||
-rw-r--r-- | net/tools/flip_server/balsa_headers_test.cc | 392 |
4 files changed, 994 insertions, 0 deletions
diff --git a/net/net.gyp b/net/net.gyp index c087749..cedc05b 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -1870,6 +1870,8 @@ 'flip_balsa_and_epoll_library', ], 'sources': [ + 'tools/flip_server/balsa_frame_test.cc', + 'tools/flip_server/balsa_headers_test.cc', 'tools/flip_server/mem_cache_test.cc', 'tools/flip_server/simple_buffer.cc', 'tools/flip_server/simple_buffer.h', diff --git a/net/tools/flip_server/balsa_frame_test.cc b/net/tools/flip_server/balsa_frame_test.cc new file mode 100644 index 0000000..3bb9a42 --- /dev/null +++ b/net/tools/flip_server/balsa_frame_test.cc @@ -0,0 +1,599 @@ +// 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/balsa_frame.h" + +#include <iterator> + +#include "base/memory/scoped_ptr.h" +#include "base/strings/string_piece.h" +#include "net/tools/flip_server/balsa_enums.h" +#include "net/tools/flip_server/balsa_headers.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { + +namespace { + +using ::base::StringPiece; +using ::testing::_; +using ::testing::InSequence; +using ::testing::SaveArg; + +class Visitor : public BalsaVisitorInterface { + public: + virtual ~Visitor() {} + MOCK_METHOD2(ProcessBodyInput, void(const char*, size_t)); + MOCK_METHOD2(ProcessBodyData, void(const char*, size_t)); + MOCK_METHOD2(ProcessHeaderInput, void(const char*, size_t)); + MOCK_METHOD2(ProcessTrailerInput, void(const char*, size_t)); + MOCK_METHOD1(ProcessHeaders, void(const BalsaHeaders&)); + MOCK_METHOD8(ProcessRequestFirstLine, void(const char*, + size_t, + const char*, + size_t, + const char*, + size_t, + const char*, + size_t)); + MOCK_METHOD8(ProcessResponseFirstLine, void(const char*, + size_t, + const char*, + size_t, + const char*, + size_t, + const char*, + size_t)); + MOCK_METHOD2(ProcessChunkExtensions, void(const char*, size_t)); + MOCK_METHOD1(ProcessChunkLength, void(size_t)); + MOCK_METHOD0(HeaderDone, void()); + MOCK_METHOD0(MessageDone, void()); + MOCK_METHOD1(HandleHeaderError, void(BalsaFrame*)); + MOCK_METHOD1(HandleHeaderWarning, void(BalsaFrame*)); + MOCK_METHOD1(HandleChunkingError, void(BalsaFrame*)); + MOCK_METHOD1(HandleBodyError, void(BalsaFrame*)); +}; + +class BalsaFrameTest : public ::testing::Test { + public: + virtual void SetUp() OVERRIDE { + frame_.reset(new BalsaFrame); + frame_headers_.reset(new BalsaHeaders); + visitor_.reset(new Visitor); + frame_->set_balsa_visitor(visitor_.get()); + }; + + protected: + scoped_ptr<BalsaFrame> frame_; + scoped_ptr<BalsaHeaders> frame_headers_; + scoped_ptr<Visitor> visitor_; +}; + +TEST_F(BalsaFrameTest, EmptyFrame) { + ASSERT_EQ(BalsaFrameEnums::READING_HEADER_AND_FIRSTLINE, + frame_->ParseState()); + ASSERT_FALSE(frame_->MessageFullyRead()); + ASSERT_FALSE(frame_->Error()); + ASSERT_EQ(NULL, frame_->const_balsa_headers()); + ASSERT_EQ(NULL, frame_->balsa_headers()); + ASSERT_EQ(NULL, frame_->headers()); + ASSERT_EQ(NULL, frame_->mutable_headers()); + ASSERT_EQ(0u, frame_->BytesSafeToSplice()); + ASSERT_TRUE(frame_->is_request()); + ASSERT_FALSE(frame_->request_was_head()); +} + +TEST_F(BalsaFrameTest, EmptyRequest) { + const char input[] = "\r\n"; + frame_->set_balsa_headers(frame_headers_.get()); + + { + InSequence s; + // No visitor callback should be called. + } + size_t read = frame_->ProcessInput(input, strlen(input)); + EXPECT_EQ(2u, read); + ASSERT_EQ(BalsaFrameEnums::READING_HEADER_AND_FIRSTLINE, + frame_->ParseState()); + ASSERT_FALSE(frame_->Error()); + ASSERT_EQ(BalsaFrameEnums::NO_ERROR, frame_->ErrorCode()); + ASSERT_EQ(0u, frame_->BytesSafeToSplice()); +} + +TEST_F(BalsaFrameTest, GetRequest) { + const char input[] = "GET / HTTP/1.0\r\nkey1: value1\r\n\r\n"; + const char* line = NULL; + size_t line_length = 0; + const char* method = NULL; + size_t method_length = 0; + const char* request_uri = NULL; + size_t request_uri_length = 0; + const char* version = NULL; + size_t version_length = 0; + const char* header = NULL; + size_t header_length = 0; + + { + InSequence s; + EXPECT_CALL(*visitor_, ProcessRequestFirstLine(_, _, _, _, _, _, _, _)) + .WillOnce(DoAll(SaveArg<0>(&line), + SaveArg<1>(&line_length), + SaveArg<2>(&method), + SaveArg<3>(&method_length), + SaveArg<4>(&request_uri), + SaveArg<5>(&request_uri_length), + SaveArg<6>(&version), + SaveArg<7>(&version_length))); + EXPECT_CALL(*visitor_, ProcessHeaderInput(_, _)) + .WillOnce(DoAll(SaveArg<0>(&header), SaveArg<1>(&header_length))); + EXPECT_CALL(*visitor_, ProcessHeaders(_)); + EXPECT_CALL(*visitor_, HeaderDone()); + EXPECT_CALL(*visitor_, MessageDone()); + } + + frame_->set_balsa_headers(frame_headers_.get()); + ASSERT_EQ(frame_headers_.get(), frame_->const_balsa_headers()); + ASSERT_EQ(frame_headers_.get(), frame_->balsa_headers()); + ASSERT_EQ(frame_headers_.get(), frame_->headers()); + ASSERT_EQ(frame_headers_.get(), frame_->mutable_headers()); + + size_t read = frame_->ProcessInput(input, strlen(input)); + ASSERT_EQ(strlen(input), read); + ASSERT_EQ(BalsaFrameEnums::MESSAGE_FULLY_READ, frame_->ParseState()); + ASSERT_TRUE(frame_->MessageFullyRead()); + ASSERT_FALSE(frame_->Error()); + ASSERT_EQ(0u, frame_->BytesSafeToSplice()); + ASSERT_EQ("GET / HTTP/1.0", StringPiece(line, line_length)); + ASSERT_EQ("GET", StringPiece(method, method_length)); + ASSERT_EQ("/", StringPiece(request_uri, request_uri_length)); + ASSERT_EQ("HTTP/1.0", StringPiece(version, version_length)); + ASSERT_EQ(input, StringPiece(header, header_length)); +} + +TEST_F(BalsaFrameTest, HeadResponse) { + const char input[] = "HTTP/1.1 200 OK\r\n" + "Content-type: text/plain\r\n" + "Content-Length: 14\r\n\r\n"; + const char* line = NULL; + size_t line_length = 0; + const char* version = NULL; + size_t version_length = 0; + const char* status = NULL; + size_t status_length = 0; + const char* reason = NULL; + size_t reason_length = 0; + const char* header = NULL; + size_t header_length = 0; + + frame_->set_balsa_headers(frame_headers_.get()); + frame_->set_is_request(false); + frame_->set_request_was_head(true); + + { + InSequence s; + EXPECT_CALL(*visitor_, ProcessResponseFirstLine(_, _, _, _, _, _, _, _)) + .WillOnce(DoAll(SaveArg<0>(&line), + SaveArg<1>(&line_length), + SaveArg<2>(&version), + SaveArg<3>(&version_length), + SaveArg<4>(&status), + SaveArg<5>(&status_length), + SaveArg<6>(&reason), + SaveArg<7>(&reason_length))); + EXPECT_CALL(*visitor_, ProcessHeaderInput(_, _)) + .WillOnce(DoAll(SaveArg<0>(&header), SaveArg<1>(&header_length))); + EXPECT_CALL(*visitor_, ProcessHeaders(_)); + EXPECT_CALL(*visitor_, HeaderDone()); + EXPECT_CALL(*visitor_, MessageDone()); + } + + size_t read = frame_->ProcessInput(input, strlen(input)); + ASSERT_EQ(strlen(input), read); + ASSERT_EQ(BalsaFrameEnums::MESSAGE_FULLY_READ, frame_->ParseState()); + ASSERT_TRUE(frame_->MessageFullyRead()); + ASSERT_FALSE(frame_->Error()); + ASSERT_EQ(0u, frame_->BytesSafeToSplice()); + + ASSERT_EQ("HTTP/1.1 200 OK", StringPiece(line, line_length)); + ASSERT_EQ("HTTP/1.1", StringPiece(version, version_length)); + ASSERT_EQ("200", StringPiece(status, status_length)); + ASSERT_EQ("OK", StringPiece(reason, reason_length)); + ASSERT_EQ("HTTP/1.1 200 OK\r\n" + "Content-type: text/plain\r\n" + "Content-Length: 14\r\n\r\n", + StringPiece(header, header_length)); +} + +TEST_F(BalsaFrameTest, GetResponse) { + const char input[] = "HTTP/1.1 200 OK\r\n" + "Content-type: text/plain\r\n" + "Content-Length: 14\r\n\r\n" + "hello, world\r\n"; + const char* line = NULL; + size_t line_length = 0; + const char* version = NULL; + size_t version_length = 0; + const char* status = NULL; + size_t status_length = 0; + const char* reason = NULL; + size_t reason_length = 0; + const char* header = NULL; + size_t header_length = 0; + const char* body = NULL; + size_t body_length = 0; + const char* body_data = NULL; + size_t body_data_length = 0; + testing::MockFunction<void(int)> checkpoint; + + frame_->set_balsa_headers(frame_headers_.get()); + frame_->set_is_request(false); + + { + InSequence s; + EXPECT_CALL(*visitor_, ProcessResponseFirstLine(_, _, _, _, _, _, _, _)) + .WillOnce(DoAll(SaveArg<0>(&line), + SaveArg<1>(&line_length), + SaveArg<2>(&version), + SaveArg<3>(&version_length), + SaveArg<4>(&status), + SaveArg<5>(&status_length), + SaveArg<6>(&reason), + SaveArg<7>(&reason_length))); + EXPECT_CALL(*visitor_, ProcessHeaderInput(_, _)) + .WillOnce(DoAll(SaveArg<0>(&header), SaveArg<1>(&header_length))); + EXPECT_CALL(*visitor_, ProcessHeaders(_)); + EXPECT_CALL(*visitor_, HeaderDone()); + EXPECT_CALL(checkpoint, Call(0)); + EXPECT_CALL(*visitor_, ProcessBodyInput(_, _)) + .WillOnce(DoAll(SaveArg<0>(&body), SaveArg<1>(&body_length))); + EXPECT_CALL(*visitor_, ProcessBodyData(_, _)) + .WillOnce(DoAll(SaveArg<0>(&body_data), SaveArg<1>(&body_data_length))); + EXPECT_CALL(*visitor_, MessageDone()); + } + + size_t read = frame_->ProcessInput(input, strlen(input)); + ASSERT_EQ(65u, read); + ASSERT_EQ(BalsaFrameEnums::READING_CONTENT, frame_->ParseState()); + checkpoint.Call(0); + read += frame_->ProcessInput(&input[read], strlen(input) - read); + ASSERT_EQ(strlen(input), read); + ASSERT_EQ(BalsaFrameEnums::MESSAGE_FULLY_READ, frame_->ParseState()); + ASSERT_TRUE(frame_->MessageFullyRead()); + ASSERT_FALSE(frame_->Error()); + ASSERT_EQ(0u, frame_->BytesSafeToSplice()); + + ASSERT_EQ("HTTP/1.1 200 OK", StringPiece(line, line_length)); + ASSERT_EQ("HTTP/1.1", StringPiece(version, version_length)); + ASSERT_EQ("200", StringPiece(status, status_length)); + ASSERT_EQ("OK", StringPiece(reason, reason_length)); + ASSERT_EQ("HTTP/1.1 200 OK\r\n" + "Content-type: text/plain\r\n" + "Content-Length: 14\r\n\r\n", + StringPiece(header, header_length)); + ASSERT_EQ("hello, world\r\n", StringPiece(body, body_length)); + ASSERT_EQ("hello, world\r\n", StringPiece(body_data, body_data_length)); +} + +TEST_F(BalsaFrameTest, Reset) { + const char input[] = "GET / HTTP/1.0\r\nkey1: value1\r\n\r\n"; + + { + InSequence s; + EXPECT_CALL(*visitor_, ProcessRequestFirstLine(_, _, _, _, _, _, _, _)); + EXPECT_CALL(*visitor_, ProcessHeaderInput(_, _)); + EXPECT_CALL(*visitor_, ProcessHeaders(_)); + EXPECT_CALL(*visitor_, HeaderDone()); + EXPECT_CALL(*visitor_, MessageDone()); + } + + frame_->set_balsa_headers(frame_headers_.get()); + + size_t read = frame_->ProcessInput(input, strlen(input)); + ASSERT_EQ(strlen(input), read); + ASSERT_EQ(BalsaFrameEnums::MESSAGE_FULLY_READ, frame_->ParseState()); + ASSERT_TRUE(frame_->MessageFullyRead()); + ASSERT_FALSE(frame_->Error()); + + frame_->Reset(); + ASSERT_EQ(BalsaFrameEnums::READING_HEADER_AND_FIRSTLINE, + frame_->ParseState()); + ASSERT_FALSE(frame_->MessageFullyRead()); + ASSERT_FALSE(frame_->Error()); +} + +TEST_F(BalsaFrameTest, InvalidStatusCode) { + const char input[] = "HTTP/1.1 InvalidStatusCode OK\r\n" + "Content-type: text/plain\r\n" + "Content-Length: 14\r\n\r\n" + "hello, world\r\n"; + + frame_->set_balsa_headers(frame_headers_.get()); + frame_->set_is_request(false); + + { + InSequence s; + EXPECT_CALL(*visitor_, HandleHeaderError(frame_.get())); + } + + size_t read = frame_->ProcessInput(input, strlen(input)); + ASSERT_EQ(30u, read); + ASSERT_EQ(BalsaFrameEnums::PARSE_ERROR, frame_->ParseState()); + ASSERT_EQ(BalsaFrameEnums::FAILED_CONVERTING_STATUS_CODE_TO_INT, + frame_->ErrorCode()); + ASSERT_FALSE(frame_->MessageFullyRead()); + ASSERT_TRUE(frame_->Error()); + ASSERT_EQ(0u, frame_->BytesSafeToSplice()); +} + +TEST_F(BalsaFrameTest, ResetError) { + const char input[] = "HTTP/1.1 InvalidStatusCode OK\r\n" + "Content-type: text/plain\r\n" + "Content-Length: 14\r\n\r\n" + "hello, world\r\n"; + + frame_->set_balsa_headers(frame_headers_.get()); + frame_->set_is_request(false); + + { + InSequence s; + EXPECT_CALL(*visitor_, HandleHeaderError(frame_.get())); + } + + size_t read = frame_->ProcessInput(input, strlen(input)); + ASSERT_EQ(30u, read); + ASSERT_EQ(BalsaFrameEnums::PARSE_ERROR, frame_->ParseState()); + ASSERT_EQ(BalsaFrameEnums::FAILED_CONVERTING_STATUS_CODE_TO_INT, + frame_->ErrorCode()); + ASSERT_FALSE(frame_->MessageFullyRead()); + ASSERT_TRUE(frame_->Error()); + ASSERT_EQ(0u, frame_->BytesSafeToSplice()); + + frame_->Reset(); + ASSERT_EQ(BalsaFrameEnums::READING_HEADER_AND_FIRSTLINE, + frame_->ParseState()); + ASSERT_FALSE(frame_->MessageFullyRead()); + ASSERT_FALSE(frame_->Error()); +} + +TEST_F(BalsaFrameTest, RequestURITooLong) { + const char input[] = "GET / HTTP/1.0\r\n\r\n"; + + frame_->set_balsa_headers(frame_headers_.get()); + frame_->set_max_request_uri_length(0); + + { + InSequence s; + EXPECT_CALL(*visitor_, HandleHeaderError(frame_.get())); + } + + size_t read = frame_->ProcessInput(input, strlen(input)); + ASSERT_EQ(15u, read); + ASSERT_EQ(BalsaFrameEnums::PARSE_ERROR, frame_->ParseState()); + ASSERT_EQ(BalsaFrameEnums::REQUEST_URI_TOO_LONG, frame_->ErrorCode()); + ASSERT_FALSE(frame_->MessageFullyRead()); + ASSERT_TRUE(frame_->Error()); + ASSERT_EQ(0u, frame_->BytesSafeToSplice()); +} + +TEST_F(BalsaFrameTest, HeadersTooLong) { + const char input[] = "GET / HTTP/1.0\r\n\r\n"; + + frame_->set_balsa_headers(frame_headers_.get()); + frame_->set_max_header_length(0); + + { + InSequence s; + EXPECT_CALL(*visitor_, HandleHeaderError(frame_.get())); + } + + size_t read = frame_->ProcessInput(input, strlen(input)); + ASSERT_EQ(0u, read); + ASSERT_EQ(BalsaFrameEnums::PARSE_ERROR, frame_->ParseState()); + ASSERT_EQ(BalsaFrameEnums::HEADERS_TOO_LONG, frame_->ErrorCode()); + ASSERT_FALSE(frame_->MessageFullyRead()); + ASSERT_TRUE(frame_->Error()); + ASSERT_EQ(0u, frame_->BytesSafeToSplice()); +} + +TEST_F(BalsaFrameTest, InvalidHeader) { + const char input[] = "GET / HTTP/1.0\r\n" + "foo bar baz\r\n" + "Content-Type: text/plain\r\n\r\n"; + const char* line = NULL; + size_t line_length = 0; + const char* method = NULL; + size_t method_length = 0; + const char* request_uri = NULL; + size_t request_uri_length = 0; + const char* version = NULL; + size_t version_length = 0; + + frame_->set_balsa_headers(frame_headers_.get()); + + { + InSequence s; + EXPECT_CALL(*visitor_, ProcessRequestFirstLine(_, _, _, _, _, _, _, _)) + .WillOnce(DoAll(SaveArg<0>(&line), + SaveArg<1>(&line_length), + SaveArg<2>(&method), + SaveArg<3>(&method_length), + SaveArg<4>(&request_uri), + SaveArg<5>(&request_uri_length), + SaveArg<6>(&version), + SaveArg<7>(&version_length))); + EXPECT_CALL(*visitor_, ProcessHeaderInput(_, _)); + EXPECT_CALL(*visitor_, HandleHeaderWarning(frame_.get())); + EXPECT_CALL(*visitor_, ProcessHeaders(_)); + EXPECT_CALL(*visitor_, HeaderDone()); + EXPECT_CALL(*visitor_, MessageDone()); + } + + size_t read = frame_->ProcessInput(input, strlen(input)); + ASSERT_EQ(strlen(input), read); + ASSERT_EQ(BalsaFrameEnums::MESSAGE_FULLY_READ, frame_->ParseState()); + ASSERT_EQ(BalsaFrameEnums::HEADER_MISSING_COLON, frame_->ErrorCode()); + ASSERT_TRUE(frame_->MessageFullyRead()); + ASSERT_FALSE(frame_->Error()); + ASSERT_EQ(0u, frame_->BytesSafeToSplice()); + ASSERT_EQ("GET / HTTP/1.0", StringPiece(line, line_length)); + ASSERT_EQ("GET", StringPiece(method, method_length)); + ASSERT_EQ("/", StringPiece(request_uri, request_uri_length)); + ASSERT_EQ("HTTP/1.0", StringPiece(version, version_length)); + ASSERT_EQ(2, std::distance(frame_headers_->header_lines_begin(), + frame_headers_->header_lines_end())); +} + +TEST_F(BalsaFrameTest, GetResponseSplit) { + const char input[] = "HTTP/1.1 200 OK\r\n" + "Content-type: text/plain\r\n" + "Content-Length: 14\r\n\r\n" + "hello"; + const char input2[] = ", world\r\n"; + const char* body1 = NULL; + size_t body1_length = 0; + const char* body1_data = NULL; + size_t body1_data_length = 0; + const char* body2 = NULL; + size_t body2_length = 0; + const char* body2_data = NULL; + size_t body2_data_length = 0; + testing::MockFunction<void(int)> checkpoint; + + frame_->set_balsa_headers(frame_headers_.get()); + frame_->set_is_request(false); + + { + InSequence s; + EXPECT_CALL(*visitor_, ProcessResponseFirstLine(_, _, _, _, _, _, _, _)); + EXPECT_CALL(*visitor_, ProcessHeaderInput(_, _)); + EXPECT_CALL(*visitor_, ProcessHeaders(_)); + EXPECT_CALL(*visitor_, HeaderDone()); + EXPECT_CALL(checkpoint, Call(0)); + EXPECT_CALL(*visitor_, ProcessBodyInput(_, _)) + .WillOnce(DoAll(SaveArg<0>(&body1), SaveArg<1>(&body1_length))); + EXPECT_CALL(*visitor_, ProcessBodyData(_, _)) + .WillOnce(DoAll(SaveArg<0>(&body1_data), + SaveArg<1>(&body1_data_length))); + EXPECT_CALL(checkpoint, Call(1)); + EXPECT_CALL(*visitor_, ProcessBodyInput(_, _)) + .WillOnce(DoAll(SaveArg<0>(&body2), SaveArg<1>(&body2_length))); + EXPECT_CALL(*visitor_, ProcessBodyData(_, _)) + .WillOnce(DoAll(SaveArg<0>(&body2_data), + SaveArg<1>(&body2_data_length))); + EXPECT_CALL(*visitor_, MessageDone()); + } + + size_t read = frame_->ProcessInput(input, strlen(input)); + ASSERT_EQ(65u, read); + ASSERT_EQ(BalsaFrameEnums::READING_CONTENT, frame_->ParseState()); + checkpoint.Call(0); + read += frame_->ProcessInput(&input[read], strlen(input) - read); + ASSERT_EQ(strlen(input), read); + ASSERT_EQ(BalsaFrameEnums::READING_CONTENT, frame_->ParseState()); + checkpoint.Call(1); + ASSERT_EQ(9u, frame_->BytesSafeToSplice()); + read = frame_->ProcessInput(input2, strlen(input2)); + ASSERT_EQ(strlen(input2), read); + + ASSERT_EQ(BalsaFrameEnums::MESSAGE_FULLY_READ, frame_->ParseState()); + ASSERT_TRUE(frame_->MessageFullyRead()); + ASSERT_FALSE(frame_->Error()); + ASSERT_EQ(0u, frame_->BytesSafeToSplice()); + ASSERT_EQ("hello", StringPiece(body1, body1_length)); + ASSERT_EQ("hello", StringPiece(body1_data, body1_data_length)); + ASSERT_EQ(", world\r\n", StringPiece(body2, body2_length)); + ASSERT_EQ(", world\r\n", StringPiece(body2_data, body2_data_length)); +} + +TEST_F(BalsaFrameTest, GetResponseBytesSpliced) { + const char input[] = "HTTP/1.1 200 OK\r\n" + "Content-type: text/plain\r\n" + "Content-Length: 14\r\n\r\n" + "hello"; + testing::MockFunction<void(int)> checkpoint; + + frame_->set_balsa_headers(frame_headers_.get()); + frame_->set_is_request(false); + + { + InSequence s; + EXPECT_CALL(*visitor_, ProcessResponseFirstLine(_, _, _, _, _, _, _, _)); + EXPECT_CALL(*visitor_, ProcessHeaderInput(_, _)); + EXPECT_CALL(*visitor_, ProcessHeaders(_)); + EXPECT_CALL(*visitor_, HeaderDone()); + EXPECT_CALL(checkpoint, Call(0)); + EXPECT_CALL(*visitor_, ProcessBodyInput(_, _)); + EXPECT_CALL(*visitor_, ProcessBodyData(_, _)); + EXPECT_CALL(checkpoint, Call(1)); + EXPECT_CALL(checkpoint, Call(2)); + EXPECT_CALL(*visitor_, MessageDone()); + } + + size_t read = frame_->ProcessInput(input, strlen(input)); + ASSERT_EQ(65u, read); + ASSERT_EQ(BalsaFrameEnums::READING_CONTENT, frame_->ParseState()); + checkpoint.Call(0); + read += frame_->ProcessInput(&input[read], strlen(input) - read); + ASSERT_EQ(strlen(input), read); + ASSERT_EQ(BalsaFrameEnums::READING_CONTENT, frame_->ParseState()); + ASSERT_EQ(9u, frame_->BytesSafeToSplice()); + checkpoint.Call(1); + frame_->BytesSpliced(5); + ASSERT_EQ(BalsaFrameEnums::READING_CONTENT, frame_->ParseState()); + ASSERT_EQ(4u, frame_->BytesSafeToSplice()); + checkpoint.Call(2); + frame_->BytesSpliced(4); + ASSERT_EQ(BalsaFrameEnums::MESSAGE_FULLY_READ, frame_->ParseState()); + + ASSERT_TRUE(frame_->MessageFullyRead()); + ASSERT_FALSE(frame_->Error()); + ASSERT_EQ(0u, frame_->BytesSafeToSplice()); +} + +TEST_F(BalsaFrameTest, GetResponseBytesSplicedTooMany) { + const char input[] = "HTTP/1.1 200 OK\r\n" + "Content-type: text/plain\r\n" + "Content-Length: 14\r\n\r\n" + "hello"; + testing::MockFunction<void(int)> checkpoint; + + frame_->set_balsa_headers(frame_headers_.get()); + frame_->set_is_request(false); + + { + InSequence s; + EXPECT_CALL(*visitor_, ProcessResponseFirstLine(_, _, _, _, _, _, _, _)); + EXPECT_CALL(*visitor_, ProcessHeaderInput(_, _)); + EXPECT_CALL(*visitor_, ProcessHeaders(_)); + EXPECT_CALL(*visitor_, HeaderDone()); + EXPECT_CALL(checkpoint, Call(0)); + EXPECT_CALL(*visitor_, ProcessBodyInput(_, _)); + EXPECT_CALL(*visitor_, ProcessBodyData(_, _)); + EXPECT_CALL(checkpoint, Call(1)); + EXPECT_CALL(*visitor_, HandleBodyError(frame_.get())); + } + + size_t read = frame_->ProcessInput(input, strlen(input)); + ASSERT_EQ(65u, read); + ASSERT_EQ(BalsaFrameEnums::READING_CONTENT, frame_->ParseState()); + checkpoint.Call(0); + read += frame_->ProcessInput(&input[read], strlen(input) - read); + ASSERT_EQ(strlen(input), read); + ASSERT_EQ(BalsaFrameEnums::READING_CONTENT, frame_->ParseState()); + ASSERT_EQ(9u, frame_->BytesSafeToSplice()); + checkpoint.Call(1); + frame_->BytesSpliced(99); + ASSERT_EQ(BalsaFrameEnums::PARSE_ERROR, frame_->ParseState()); + ASSERT_FALSE(frame_->MessageFullyRead()); + ASSERT_TRUE(frame_->Error()); + ASSERT_EQ( + BalsaFrameEnums::CALLED_BYTES_SPLICED_AND_EXCEEDED_SAFE_SPLICE_AMOUNT, + frame_->ErrorCode()); + ASSERT_EQ(0u, frame_->BytesSafeToSplice()); +} + +} // namespace + +} // namespace net diff --git a/net/tools/flip_server/balsa_headers.h b/net/tools/flip_server/balsa_headers.h index 6fabdd4..2c8b083 100644 --- a/net/tools/flip_server/balsa_headers.h +++ b/net/tools/flip_server/balsa_headers.h @@ -64,6 +64,7 @@ class BalsaBuffer { // BalsaHeaders implementation, yet be testable. friend class BalsaBufferTestSpouse; friend class BalsaHeaders; + friend class BalsaBufferTest; // The BufferBlock is a structure used internally by the // BalsaBuffer class to store the base buffer pointers to diff --git a/net/tools/flip_server/balsa_headers_test.cc b/net/tools/flip_server/balsa_headers_test.cc new file mode 100644 index 0000000..241fee1 --- /dev/null +++ b/net/tools/flip_server/balsa_headers_test.cc @@ -0,0 +1,392 @@ +// 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/balsa_headers.h" + +#include <iterator> +#include <string> +#include <vector> + +#include "base/memory/scoped_ptr.h" +#include "base/strings/string_piece.h" +#include "net/tools/flip_server/balsa_enums.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { + +using ::base::StringPiece; + +class BalsaBufferTest : public ::testing::Test { + public: + virtual void SetUp() OVERRIDE { + buffer_.reset(new BalsaBuffer); + anotherBuffer_.reset(new BalsaBuffer); + } + + protected: + scoped_ptr<BalsaBuffer> buffer_; + scoped_ptr<BalsaBuffer> anotherBuffer_; +}; + +namespace { + +class BalsaHeadersTest: public ::testing::Test { + public: + virtual void SetUp() OVERRIDE { + headers_.reset(new BalsaHeaders); + } + + protected: + scoped_ptr<BalsaHeaders> headers_; +}; + +class StringBuffer { + public: + void Write(const char* p, size_t size) { + string_ += std::string(p, size); + } + const std::string& string() {return string_;} + + private: + std::string string_; +}; + +TEST_F(BalsaBufferTest, EmptyBuffer) { + ASSERT_EQ(1u, buffer_->num_blocks()); +} + +TEST_F(BalsaBufferTest, Write) { + size_t index1, index2; + StringPiece sp1 = buffer_->Write(StringPiece("hello"), &index1); + StringPiece sp2 = buffer_->Write(StringPiece(", world"), &index2); + + ASSERT_EQ(2u, buffer_->num_blocks()); + ASSERT_EQ("hello", sp1); + ASSERT_EQ(", world", sp2); + ASSERT_EQ(1u, index1); + ASSERT_EQ(1u, index2); + ASSERT_EQ("hello, world", + StringPiece(buffer_->GetPtr(1), buffer_->bytes_used(1))); +} + +TEST_F(BalsaBufferTest, WriteLongData) { + size_t index1, index2, index3; + std::string as(2, 'a'); + std::string bs(BalsaBuffer::kDefaultBlocksize + 1, 'b'); + std::string cs(4, 'c'); + + StringPiece sp1 = buffer_->Write(as, &index1); + StringPiece sp2 = buffer_->Write(bs, &index2); + StringPiece sp3 = buffer_->Write(cs, &index3); + + ASSERT_EQ(3u, buffer_->num_blocks()); + ASSERT_EQ(as, sp1); + ASSERT_EQ(bs, sp2); + ASSERT_EQ(cs, sp3); + ASSERT_EQ(1u, index1); + ASSERT_EQ(2u, index2); + ASSERT_EQ(1u, index3); + ASSERT_EQ("aacccc", StringPiece(buffer_->GetPtr(1), buffer_->bytes_used(1))); + ASSERT_EQ(sp2, StringPiece(buffer_->GetPtr(2), buffer_->bytes_used(2))); +} + +TEST_F(BalsaBufferTest, WriteToContiguousBuffer) { + std::string as(2, 'a'); + std::string bs(BalsaBuffer::kDefaultBlocksize + 1, 'b'); + std::string cs(4, 'c'); + + buffer_->WriteToContiguousBuffer(as); + buffer_->WriteToContiguousBuffer(bs); + buffer_->WriteToContiguousBuffer(cs); + + ASSERT_EQ(1u, buffer_->num_blocks()); + ASSERT_EQ(as + bs + cs, + StringPiece(buffer_->GetPtr(0), buffer_->bytes_used(0))); +} + +TEST_F(BalsaBufferTest, NoMoreWriteToContiguousBuffer) { + size_t index1, index2; + StringPiece sp1 = buffer_->Write(StringPiece("hello"), &index1); + buffer_->NoMoreWriteToContiguousBuffer(); + StringPiece sp2 = buffer_->Write(StringPiece(", world"), &index2); + + ASSERT_EQ(2u, buffer_->num_blocks()); + ASSERT_EQ("hello", sp1); + ASSERT_EQ(", world", sp2); + ASSERT_EQ(1u, index1); + ASSERT_EQ(0u, index2); + ASSERT_EQ(sp1, StringPiece(buffer_->GetPtr(1), buffer_->bytes_used(1))); + ASSERT_EQ(sp2, StringPiece(buffer_->GetPtr(0), buffer_->bytes_used(0))); +} + +TEST_F(BalsaBufferTest, Clear) { + buffer_->Write("hello", NULL); + ASSERT_EQ(2u, buffer_->num_blocks()); + buffer_->Clear(); + ASSERT_EQ(1u, buffer_->num_blocks()); +} + +TEST_F(BalsaBufferTest, Swap) { + buffer_->Write("hello", NULL); + + ASSERT_EQ(2u, buffer_->num_blocks()); + ASSERT_EQ(1u, anotherBuffer_->num_blocks()); + + buffer_->Swap(anotherBuffer_.get()); + + ASSERT_EQ(1u, buffer_->num_blocks()); + ASSERT_EQ(2u, anotherBuffer_->num_blocks()); + ASSERT_EQ("hello", + StringPiece(anotherBuffer_->GetPtr(1), + anotherBuffer_->bytes_used(1))); +} + +TEST_F(BalsaBufferTest, CopyFrom) { + buffer_->Write("hello", NULL); + + ASSERT_EQ(2u, buffer_->num_blocks()); + ASSERT_EQ(1u, anotherBuffer_->num_blocks()); + + anotherBuffer_->CopyFrom(*buffer_); + + ASSERT_EQ(2u, buffer_->num_blocks()); + ASSERT_EQ(2u, anotherBuffer_->num_blocks()); + ASSERT_EQ("hello", StringPiece(buffer_->GetPtr(1), buffer_->bytes_used(1))); + ASSERT_EQ("hello", + StringPiece(anotherBuffer_->GetPtr(1), + anotherBuffer_->bytes_used(1))); +} + +TEST_F(BalsaHeadersTest, AppendHeader) { + headers_->AppendHeader("key1", "value1"); + headers_->AppendHeader("key2", "value2"); + headers_->AppendHeader("key3", "value3"); + headers_->AppendHeader("key3", "value3.1"); + headers_->AppendHeader("key3", "value3.2"); + + ASSERT_EQ(5, std::distance(headers_->header_lines_begin(), + headers_->header_lines_end())); + ASSERT_EQ("value1", headers_->GetHeader("key1")); + ASSERT_EQ("value2", headers_->GetHeader("key2")); + ASSERT_EQ("value3", headers_->GetHeader("key3")); + + std::vector<base::StringPiece> v1, v2, v3; + std::string s1, s2, s3; + headers_->GetAllOfHeader("key1", &v1); + headers_->GetAllOfHeader("key2", &v2); + headers_->GetAllOfHeader("key3", &v3); + headers_->GetAllOfHeaderAsString("key1", &s1); + headers_->GetAllOfHeaderAsString("key2", &s2); + headers_->GetAllOfHeaderAsString("key3", &s3); + + ASSERT_EQ(1u, v1.size()); + ASSERT_EQ(1u, v2.size()); + ASSERT_EQ(3u, v3.size()); + ASSERT_EQ("value1", v1[0]); + ASSERT_EQ("value2", v2[0]); + ASSERT_EQ("value3", v3[0]); + ASSERT_EQ("value3.1", v3[1]); + ASSERT_EQ("value3.2", v3[2]); + ASSERT_EQ("value1", s1); + ASSERT_EQ("value2", s2); + ASSERT_EQ("value3,value3.1,value3.2", s3); +} + +TEST_F(BalsaHeadersTest, ReplaceOrAppendHeader) { + headers_->ReplaceOrAppendHeader("key1", "value1"); + headers_->ReplaceOrAppendHeader("key1", "value2"); + + ASSERT_EQ(1, std::distance(headers_->header_lines_begin(), + headers_->header_lines_end())); + ASSERT_EQ("value2", headers_->GetHeader("key1")); + + std::vector<base::StringPiece> v; + headers_->GetAllOfHeader("key1", &v); + + ASSERT_EQ(1u, v.size()); + ASSERT_EQ("value2", v[0]); +} + +TEST_F(BalsaHeadersTest, AppendToHeader) { + headers_->AppendToHeader("key1", "value1"); + headers_->AppendToHeader("keY1", "value2"); + + ASSERT_EQ(1, std::distance(headers_->header_lines_begin(), + headers_->header_lines_end())); + ASSERT_EQ("value1,value2", headers_->GetHeader("key1")); + + std::vector<base::StringPiece> v; + std::string s; + headers_->GetAllOfHeader("key1", &v); + headers_->GetAllOfHeaderAsString("keY1", &s); + + ASSERT_EQ(1u, v.size()); + ASSERT_EQ("value1,value2", v[0]); + ASSERT_EQ("value1,value2", s); +} + +TEST_F(BalsaHeadersTest, PrepentToHeader) { + headers_->PrependToHeader("key1", "value1"); + headers_->PrependToHeader("key1", "value2"); + + ASSERT_EQ(1, std::distance(headers_->header_lines_begin(), + headers_->header_lines_end())); + ASSERT_EQ("value2,value1", headers_->GetHeader("key1")); + + std::vector<base::StringPiece> v; + std::string s; + headers_->GetAllOfHeader("key1", &v); + headers_->GetAllOfHeaderAsString("key1", &s); + + ASSERT_EQ(1u, v.size()); + ASSERT_EQ("value2,value1", v[0]); + ASSERT_EQ("value2,value1", s); +} + +TEST_F(BalsaHeadersTest, HasHeader) { + headers_->AppendHeader("key1", "value1"); + + ASSERT_TRUE(headers_->HasHeader("key1")); + ASSERT_FALSE(headers_->HasHeader("value1")); + ASSERT_FALSE(headers_->HasHeader("key2")); +} + +TEST_F(BalsaHeadersTest, HasNonEmptyHeader) { + headers_->AppendHeader("key1", "value1"); + headers_->AppendHeader("key2", ""); + + ASSERT_TRUE(headers_->HasNonEmptyHeader("key1")); + ASSERT_FALSE(headers_->HasNonEmptyHeader("key2")); + ASSERT_FALSE(headers_->HasNonEmptyHeader("key3")); +} + +TEST_F(BalsaHeadersTest, GetHeaderPosition) { + headers_->AppendHeader("key1", "value1"); + headers_->AppendHeader("key2", "value2"); + headers_->AppendHeader("key3", "value3"); + + BalsaHeaders::const_header_lines_iterator i = + headers_->GetHeaderPosition("key2"); + + ASSERT_EQ(headers_->header_lines_end(), + headers_->GetHeaderPosition("foobar")); + ASSERT_EQ(headers_->header_lines_begin(), + headers_->GetHeaderPosition("key1")); + ASSERT_NE(headers_->header_lines_end(), i); + ASSERT_EQ("key2", i->first); + ASSERT_EQ("value2", i->second); + ++i; + ASSERT_EQ("key3", i->first); + ASSERT_EQ("value3", i->second); + ++i; + ASSERT_EQ(headers_->header_lines_end(), i); +} + +TEST_F(BalsaHeadersTest, GetIteratorForKey) { + headers_->AppendHeader("key1", "value1"); + headers_->AppendHeader("key2", "value2"); + headers_->AppendHeader("key1", "value1.1"); + headers_->AppendHeader("key3", "value3"); + headers_->AppendHeader("KEY1", "value1.2"); + + BalsaHeaders::const_header_lines_key_iterator i = + headers_->GetIteratorForKey("key1"); + + ASSERT_EQ(headers_->header_lines_key_end(), + headers_->GetIteratorForKey("foobar")); + ASSERT_NE(headers_->header_lines_key_end(), i); + ASSERT_EQ("key1", i->first); + ASSERT_EQ("value1", i->second); + ++i; + ASSERT_EQ("key1", i->first); + ASSERT_EQ("value1.1", i->second); + ++i; + ASSERT_EQ("KEY1", i->first); + ASSERT_EQ("value1.2", i->second); + ++i; + ASSERT_EQ(headers_->header_lines_key_end(), i); +} + +TEST_F(BalsaHeadersTest, RemoveAllOfHeader) { + headers_->AppendHeader("key1", "value1"); + headers_->AppendHeader("key2", "value2"); + headers_->AppendHeader("key1", "value1.1"); + headers_->AppendHeader("key3", "value3"); + headers_->AppendHeader("key1", "value1.2"); + headers_->AppendHeader("kEY1", "value1.3"); + + ASSERT_EQ(6, std::distance(headers_->header_lines_begin(), + headers_->header_lines_end())); + headers_->RemoveAllOfHeader("key1"); + ASSERT_EQ(2, std::distance(headers_->header_lines_begin(), + headers_->header_lines_end())); +} + +TEST_F(BalsaHeadersTest, RemoveAllHeadersWithPrefix) { + headers_->AppendHeader("1key", "value1"); + headers_->AppendHeader("2key", "value2"); + headers_->AppendHeader("1kEz", "value1.1"); + headers_->AppendHeader("key3", "value3"); + headers_->AppendHeader("1KEEjkladf", "value1.2"); + + ASSERT_EQ(5, std::distance(headers_->header_lines_begin(), + headers_->header_lines_end())); + headers_->RemoveAllHeadersWithPrefix("1ke"); + ASSERT_EQ(2, std::distance(headers_->header_lines_begin(), + headers_->header_lines_end())); +} + +TEST_F(BalsaHeadersTest, WriteRequestHeaderAndEndingToBuffer) { + headers_->AppendHeader("key1", "value1"); + headers_->AppendHeader("key2", "value2"); + headers_->AppendHeader("key1", "value1.1"); + + headers_->SetRequestFirstlineFromStringPieces("GET", "/", "HTTP/1.0"); + + std::string expected = "GET / HTTP/1.0\r\n" + "key1: value1\r\n" + "key2: value2\r\n" + "key1: value1.1\r\n\r\n"; + StringBuffer buffer; + headers_->WriteHeaderAndEndingToBuffer(&buffer); + ASSERT_EQ(expected, buffer.string()); +} + +TEST_F(BalsaHeadersTest, WriteResponseHeaderAndEndingToBuffer) { + headers_->AppendHeader("key1", "value1"); + headers_->AppendHeader("key2", "value2"); + headers_->AppendHeader("key1", "value1.1"); + + headers_->SetResponseFirstlineFromStringPieces("HTTP/1.0", "200", "OK"); + + std::string expected = "HTTP/1.0 200 OK\r\n" + "key1: value1\r\n" + "key2: value2\r\n" + "key1: value1.1\r\n\r\n"; + StringBuffer buffer; + headers_->WriteHeaderAndEndingToBuffer(&buffer); + ASSERT_EQ(expected, buffer.string()); +} + +TEST_F(BalsaHeadersTest, RequestFirstLine) { + headers_->SetRequestFirstlineFromStringPieces("HEAD", "/path", "HTTP/1.1"); + + ASSERT_EQ("HEAD /path HTTP/1.1", headers_->first_line()); + ASSERT_EQ("HEAD", headers_->request_method()); + ASSERT_EQ("/path", headers_->request_uri()); + ASSERT_EQ("HTTP/1.1", headers_->request_version()); +} + +TEST_F(BalsaHeadersTest, ResponseFirstLine) { + headers_->SetRequestFirstlineFromStringPieces("HTTP/1.0", "403", "FORBIDDEN"); + + ASSERT_EQ("HTTP/1.0 403 FORBIDDEN", headers_->first_line()); + ASSERT_EQ("HTTP/1.0", headers_->response_version()); + ASSERT_EQ("403", headers_->response_code()); + ASSERT_EQ("FORBIDDEN", headers_->response_reason_phrase()); +} + +} // namespace + +} // namespace net |