| /* |
| * Copyright 2010 Google Inc. |
| * |
| * 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. |
| */ |
| |
| // Author: jmarantz@google.com (Joshua Marantz) |
| |
| // Unit-test the lru cache |
| |
| #include "net/instaweb/http/public/http_value.h" |
| #include "pagespeed/kernel/base/basictypes.h" |
| #include "pagespeed/kernel/base/google_message_handler.h" |
| #include "pagespeed/kernel/base/gtest.h" |
| #include "pagespeed/kernel/base/shared_string.h" |
| #include "pagespeed/kernel/base/string_util.h" |
| #include "pagespeed/kernel/http/http_names.h" |
| #include "pagespeed/kernel/http/response_headers.h" |
| |
| namespace { |
| const int kMaxSize = 100; |
| } |
| |
| namespace net_instaweb { |
| |
| class HTTPValueTest : public testing::Test { |
| protected: |
| HTTPValueTest() { } |
| |
| void FillResponseHeaders(ResponseHeaders* meta_data) { |
| meta_data->SetStatusAndReason(HttpStatus::kOK); |
| meta_data->set_major_version(1); |
| meta_data->set_minor_version(0); |
| meta_data->set_reason_phrase("OK"); |
| meta_data->Add("Cache-control", "max-age=300"); |
| } |
| |
| void CheckResponseHeaders(const ResponseHeaders& meta_data) { |
| ResponseHeaders expected; |
| FillResponseHeaders(&expected); |
| EXPECT_EQ(expected.ToString(), meta_data.ToString()); |
| } |
| |
| int64 ComputeContentsSize(HTTPValue* value) { |
| return value->ComputeContentsSize(); |
| } |
| |
| GoogleMessageHandler message_handler_; |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(HTTPValueTest); |
| }; |
| |
| TEST_F(HTTPValueTest, Empty) { |
| HTTPValue value; |
| EXPECT_TRUE(value.Empty()); |
| } |
| |
| TEST_F(HTTPValueTest, HeadersFirst) { |
| HTTPValue value; |
| ResponseHeaders headers, check_headers; |
| FillResponseHeaders(&headers); |
| value.SetHeaders(&headers); |
| value.Write("body", &message_handler_); |
| StringPiece body; |
| ASSERT_TRUE(value.ExtractContents(&body)); |
| EXPECT_EQ("body", body.as_string()); |
| EXPECT_EQ(body.size(), ComputeContentsSize(&value)); |
| ASSERT_TRUE(value.ExtractHeaders(&check_headers, &message_handler_)); |
| CheckResponseHeaders(check_headers); |
| } |
| |
| TEST_F(HTTPValueTest, ContentsFirst) { |
| HTTPValue value; |
| ResponseHeaders headers, check_headers; |
| FillResponseHeaders(&headers); |
| value.Write("body", &message_handler_); |
| value.SetHeaders(&headers); |
| StringPiece body; |
| ASSERT_TRUE(value.ExtractContents(&body)); |
| EXPECT_EQ("body", body.as_string()); |
| EXPECT_EQ(body.size(), ComputeContentsSize(&value)); |
| ASSERT_TRUE(value.ExtractHeaders(&check_headers, &message_handler_)); |
| CheckResponseHeaders(check_headers); |
| } |
| |
| TEST_F(HTTPValueTest, EmptyContentsFirst) { |
| HTTPValue value; |
| ResponseHeaders headers, check_headers; |
| FillResponseHeaders(&headers); |
| value.Write("", &message_handler_); |
| value.SetHeaders(&headers); |
| StringPiece body; |
| ASSERT_TRUE(value.ExtractContents(&body)); |
| EXPECT_EQ("", body.as_string()); |
| EXPECT_EQ(body.size(), ComputeContentsSize(&value)); |
| ASSERT_TRUE(value.ExtractHeaders(&check_headers, &message_handler_)); |
| CheckResponseHeaders(check_headers); |
| } |
| |
| TEST_F(HTTPValueTest, TestCopyOnWrite) { |
| HTTPValue v1; |
| v1.Write("Hello", &message_handler_); |
| StringPiece v1_contents, v2_contents, v3_contents; |
| ASSERT_TRUE(v1.ExtractContents(&v1_contents)); |
| EXPECT_TRUE(v1.unique()); |
| |
| // Test Link sharing |
| HTTPValue v2; |
| v2.Link(&v1); |
| EXPECT_FALSE(v1.unique()); |
| EXPECT_FALSE(v2.unique()); |
| ASSERT_TRUE(v2.ExtractContents(&v2_contents)); |
| EXPECT_EQ(v1_contents, v2_contents); |
| EXPECT_EQ(v1_contents.data(), v2_contents.data()); // buffer sharing |
| |
| HTTPValue v3; |
| v3.Link(&v1); |
| EXPECT_FALSE(v3.unique()); |
| ASSERT_TRUE(v3.ExtractContents(&v3_contents)); |
| EXPECT_EQ(v1_contents, v3_contents); |
| EXPECT_EQ(v1_contents.data(), v3_contents.data()); // buffer sharing |
| |
| // Now write something into v1. Due to copy-on-write semantics, v2 and |
| // will v3 not see it. |
| v1.Write(", World!", &message_handler_); |
| ASSERT_TRUE(v1.ExtractContents(&v1_contents)); |
| ASSERT_TRUE(v2.ExtractContents(&v2_contents)); |
| ASSERT_TRUE(v3.ExtractContents(&v3_contents)); |
| EXPECT_EQ("Hello, World!", v1_contents); |
| EXPECT_NE(v1_contents, v2_contents); |
| EXPECT_NE(v1_contents.data(), v2_contents.data()); // no buffer sharing |
| EXPECT_NE(v1_contents, v3_contents); |
| EXPECT_NE(v1_contents.data(), v3_contents.data()); // no buffer sharing |
| |
| // But v2 and v3 will remain connected to one another |
| EXPECT_EQ(v2_contents, v3_contents); |
| EXPECT_EQ(v2_contents.data(), v3_contents.data()); // buffer sharing |
| EXPECT_EQ(v1_contents.size(), ComputeContentsSize(&v1)); |
| EXPECT_EQ(v2_contents.size(), ComputeContentsSize(&v2)); |
| EXPECT_EQ(v3_contents.size(), ComputeContentsSize(&v3)); |
| } |
| |
| TEST_F(HTTPValueTest, TestShare) { |
| SharedString storage; |
| |
| { |
| HTTPValue value; |
| ResponseHeaders headers, check_headers; |
| FillResponseHeaders(&headers); |
| value.SetHeaders(&headers); |
| value.Write("body", &message_handler_); |
| storage = *value.share(); |
| } |
| |
| { |
| HTTPValue value; |
| ResponseHeaders check_headers; |
| ASSERT_TRUE(value.Link(&storage, &check_headers, &message_handler_)); |
| StringPiece body; |
| ASSERT_TRUE(value.ExtractContents(&body)); |
| EXPECT_EQ("body", body.as_string()); |
| CheckResponseHeaders(check_headers); |
| } |
| } |
| |
| TEST_F(HTTPValueTest, LinkEmpty) { |
| SharedString storage; |
| HTTPValue value; |
| ResponseHeaders headers; |
| ASSERT_FALSE(value.Link(&storage, &headers, &message_handler_)); |
| } |
| |
| TEST_F(HTTPValueTest, LinkCorrupt) { |
| SharedString storage("h"); |
| HTTPValue value; |
| ResponseHeaders headers; |
| ASSERT_FALSE(value.Link(&storage, &headers, &message_handler_)); |
| storage.Append("9999"); |
| ASSERT_FALSE(value.Link(&storage, &headers, &message_handler_)); |
| storage.Append("xyz"); |
| ASSERT_FALSE(value.Link(&storage, &headers, &message_handler_)); |
| storage.Assign("b"); |
| ASSERT_FALSE(value.Link(&storage, &headers, &message_handler_)); |
| storage.Append("9999"); |
| ASSERT_FALSE(value.Link(&storage, &headers, &message_handler_)); |
| storage.Append("xyz"); |
| ASSERT_FALSE(value.Link(&storage, &headers, &message_handler_)); |
| } |
| |
| class HTTPValueEncodeTest : public testing::Test { |
| public: |
| GoogleString Decode(StringPiece in) { |
| GoogleString out; |
| EXPECT_TRUE(HTTPValue::Decode(in, &out, &handler_)); |
| return out; |
| } |
| |
| GoogleString Encode(StringPiece in) { |
| GoogleString out; |
| EXPECT_TRUE(HTTPValue::Encode(in, &out, &handler_)); |
| return out; |
| } |
| |
| GoogleMessageHandler handler_; |
| }; |
| |
| TEST_F(HTTPValueEncodeTest, EncodeDecode) { |
| const char simple_http[] = |
| "HTTP/1.1 200 OK\r\n" |
| "Host: www.example.com\r\n" |
| "\r\n" |
| "Hello, world!"; |
| EXPECT_STREQ(simple_http, Decode(Encode(simple_http))); |
| |
| const char error_http[] = |
| "HTTP/1.0 0 Internal Server Error\r\n" |
| "\r\n"; |
| EXPECT_STREQ(error_http, Decode(Encode(error_http))); |
| |
| const char complex_http[] = |
| "HTTP/1.1 200 OK\r\n" |
| "Server: nginx/0.5.26\r\n" |
| "Date: Tue, 29 Nov 2011 16:21:28 GMT\r\n" |
| "Content-Type: text/html; charset=UTF-8\r\n" |
| "Connection: keep-alive\r\n" |
| "X-Powered-By: PHP/5.2.3-1ubuntu6.5\r\n" |
| "Set-Cookie: magento=gv8gxips44qykg76kgwyosgagsk1hl1g; expires=Thu, " |
| "29-Dec-2011 16:21:28 GMT; path=/; domain=www.toysdownunder.com\r\n" |
| "Set-Cookie: frontend=9bbc4bf255ec10d66245a02b3dda5ba4; expires=Thu, " |
| "08 Dec 2011 00:21:28 GMT; path=/; domain=www.toysdownunder.com\r\n" |
| "Expires: Thu, 19 Nov 1981 08:52:00 GMT\r\n" |
| "Cache-Control: no-store, no-cache, must-revalidate, post-check=0, " |
| "pre-check=0\r\n" |
| "Pragma: no-cache\r\n" |
| "X-Google-Cache-Control: remote-fetch\r\n" |
| "Via: HTTP/1.1 GWA\r\n" |
| "\r\n" |
| "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" " |
| "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n" |
| "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" " |
| "lang=\"en\">\n" |
| "<head>\n" |
| " <meta http-equiv=\"X-UA-Compatible\" content=\"IE=8\" />\n" |
| "<title> Toysdownunder.com - Arduino and Walkera Helicopters </title>\n" |
| "<meta http-equiv=\"Content-Type\" content=\"text/html; " |
| "charset=utf-8\" />\n" |
| "<meta name=\"verify-v1\" content=\"7sZcArzfEwR1uQyfxrhn4AdJnOcN6OlXf" |
| "666LZYnC94=\" />\n"; |
| EXPECT_STREQ(complex_http, Decode(Encode(complex_http))); |
| } |
| |
| TEST_F(HTTPValueEncodeTest, EncodeDecodeGold) { |
| const char example_http[] = |
| "HTTP/1.1 200 OK\r\n" |
| "Server: Apache/2.2.29 (Unix) mod_ssl/2.2.29 OpenSSL/1.0.1j DAV/2 " |
| "mod_fcgid/2.3.9\r\n" |
| "Last-Modified: Fri, 20 Feb 2015 18:10:04 GMT\r\n" |
| "Accept-Ranges: bytes\r\n" |
| "Content-Length: 21\r\n" |
| "X-Extra-Header: 1\r\n" |
| "Cache-Control: public, max-age=600\r\n" |
| "Content-Type: text/css\r\n" |
| "Etag: W/\"PSA-35DPOkCBal\"\r\n" |
| "Date: Fri, 15 May 2015 21:40:32 GMT\r\n" |
| "\r\n" |
| ".blue {color: blue;}\n"; |
| |
| const char header_first_golden_value_buf[] = |
| "hv\x1\0\0\b\xC8\x1\x12\x2OK\x18\x1 \x1(\xC0\xD8\xBA\xCC\xD5)0\x80\x89" |
| "\x96\xCC\xD5)8\x1@\x1JR\n\x6" |
| "Server\x12HApache/2.2.29 (Unix) mod_ssl/2.2.29 OpenSSL/1.0.1j DAV/2" |
| " mod_fcgid/2.3.9J.\n\r" |
| "Last-Modified\x12\x1D" "Fri, 20 Feb 2015 18:10:04 GMTJ\x16\n\r" |
| "Accept-Ranges\x12\x5" "bytesJ\x14\n\xE" |
| "Content-Length\x12\x2" "21J\x13\n\xE" |
| "X-Extra-Header\x12\x1" "1J$\n\r" |
| "Cache-Control\x12\x13public, max-age=600J\x18\n\f" |
| "Content-Type\x12\btext/cssJ\x1A\n\x4" |
| "Etag\x12\x12W/\"PSA-35DPOkCBal\"J%\n\x4" |
| "Date\x12\x1D" "Fri, 15 May 2015 21:40:32 GMTP" |
| "\xE0\xC8\xBA\xC1\xBA)X\xC0\xCF$h\0p\0." |
| "blue {color: blue;}\n"; |
| StringPiece header_first_golden_value( |
| header_first_golden_value_buf, |
| STATIC_STRLEN(header_first_golden_value_buf)); |
| |
| const char body_first_golden_value_buf[] = |
| "b\x15\0\0\0.blue {color: blue;}\n\b\xC8\x1\x12\x2OK\x18\x1 \x1(\xC0\xD8" |
| "\xBA\xCC\xD5)0\x80\x89\x96\xCC\xD5)8\x1@\x1JR\n\x6" |
| "Server\x12HApache/2.2.29 (Unix) mod_ssl/2.2.29 OpenSSL/1.0.1j DAV/2" |
| " mod_fcgid/2.3.9J.\n\r" |
| "Last-Modified\x12\x1D" "Fri, 20 Feb 2015 18:10:04 GMTJ\x16\n\r" |
| "Accept-Ranges\x12\x5" "bytesJ\x14\n\xE" |
| "Content-Length\x12\x2" "21J\x13\n\xE" |
| "X-Extra-Header\x12\x1" "1J$\n\r" |
| "Cache-Control\x12\x13public, max-age=600J\x18\n\f" |
| "Content-Type\x12\btext/cssJ\x1A\n\x4" |
| "Etag\x12\x12W/\"PSA-35DPOkCBal\"J%\n\x4" |
| "Date\x12\x1D" "Fri, 15 May 2015 21:40:32 GMTX\xC0\xCF$h\0p\0"; |
| StringPiece body_first_golden_value( |
| body_first_golden_value_buf, STATIC_STRLEN(body_first_golden_value_buf)); |
| |
| // These tests should work even if proto formats change. |
| EXPECT_STREQ(example_http, Decode(header_first_golden_value)); |
| EXPECT_STREQ(example_http, Decode(body_first_golden_value)); |
| |
| // Note: This might change when proto formats change. |
| // Note: Can't use STREQ, it doesn't check past embedded nulls. |
| EXPECT_EQ(header_first_golden_value, Encode(example_http)); |
| } |
| |
| TEST_F(HTTPValueEncodeTest, EncodeInvalid) { |
| GoogleString out; |
| EXPECT_FALSE(HTTPValue::Decode("invalid encoding", &out, &handler_)); |
| EXPECT_FALSE(HTTPValue::Encode("invalid http", &out, &handler_)); |
| } |
| |
| } // namespace net_instaweb |