blob: b5990e10a4f032a79a124cff79662e3983d831ac [file] [log] [blame]
/*
* Copyright (C) 2012 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "modules/websockets/WebSocketExtensionDispatcher.h"
#include "modules/websockets/WebSocketExtensionParser.h"
#include "modules/websockets/WebSocketExtensionProcessor.h"
#include "wtf/text/CString.h"
#include "wtf/text/StringHash.h"
#include <gtest/gtest.h>
using namespace WebCore;
namespace {
class WebSocketExtensionDispatcherTest;
class MockWebSocketExtensionProcessor : public WebSocketExtensionProcessor {
public:
MockWebSocketExtensionProcessor(const String& name, WebSocketExtensionDispatcherTest* test)
: WebSocketExtensionProcessor(name)
, m_test(test)
{
}
virtual String handshakeString() OVERRIDE { return extensionToken(); }
virtual bool processResponse(const HashMap<String, String>&) OVERRIDE;
private:
WebSocketExtensionDispatcherTest* m_test;
};
class WebSocketExtensionDispatcherTest : public testing::Test {
public:
WebSocketExtensionDispatcherTest() { }
void SetUp() { }
void TearDown() { }
void addMockProcessor(const String& extensionToken)
{
m_extensions.addProcessor(adoptPtr(new MockWebSocketExtensionProcessor(extensionToken, this)));
}
void appendResult(const String& extensionToken, const HashMap<String, String>& parameters)
{
m_parsedExtensionTokens.append(extensionToken);
m_parsedParameters.append(parameters);
}
protected:
WebSocketExtensionDispatcher m_extensions;
Vector<String> m_parsedExtensionTokens;
Vector<HashMap<String, String> > m_parsedParameters;
};
bool MockWebSocketExtensionProcessor::processResponse(const HashMap<String, String>& parameters)
{
m_test->appendResult(extensionToken(), parameters);
return true;
}
TEST_F(WebSocketExtensionDispatcherTest, TestSingle)
{
addMockProcessor("deflate-frame");
EXPECT_TRUE(m_extensions.processHeaderValue("deflate-frame"));
EXPECT_EQ(1UL, m_parsedExtensionTokens.size());
EXPECT_EQ("deflate-frame", m_parsedExtensionTokens[0]);
EXPECT_EQ("deflate-frame", m_extensions.acceptedExtensions());
EXPECT_EQ(0UL, m_parsedParameters[0].size());
}
TEST_F(WebSocketExtensionDispatcherTest, TestParameters)
{
addMockProcessor("mux");
EXPECT_TRUE(m_extensions.processHeaderValue("mux; max-channels=4; flow-control "));
EXPECT_EQ(1UL, m_parsedExtensionTokens.size());
EXPECT_EQ("mux", m_parsedExtensionTokens[0]);
EXPECT_EQ(2UL, m_parsedParameters[0].size());
HashMap<String, String>::iterator parameter = m_parsedParameters[0].find("max-channels");
EXPECT_TRUE(parameter != m_parsedParameters[0].end());
EXPECT_EQ("4", parameter->value);
parameter = m_parsedParameters[0].find("flow-control");
EXPECT_TRUE(parameter != m_parsedParameters[0].end());
EXPECT_TRUE(parameter->value.isNull());
}
TEST_F(WebSocketExtensionDispatcherTest, TestMultiple)
{
struct {
String token;
HashMap<String, String> parameters;
} expected[2];
expected[0].token = "mux";
expected[0].parameters.add("max-channels", "4");
expected[0].parameters.add("flow-control", String());
expected[1].token = "deflate-frame";
addMockProcessor("mux");
addMockProcessor("deflate-frame");
EXPECT_TRUE(m_extensions.processHeaderValue("mux ; max-channels =4;flow-control, deflate-frame "));
EXPECT_TRUE(m_extensions.acceptedExtensions().find("mux") != kNotFound);
EXPECT_TRUE(m_extensions.acceptedExtensions().find("deflate-frame") != kNotFound);
for (size_t i = 0; i < sizeof(expected) / sizeof(expected[0]); ++i) {
EXPECT_EQ(expected[i].token, m_parsedExtensionTokens[i]);
const HashMap<String, String>& expectedParameters = expected[i].parameters;
const HashMap<String, String>& parsedParameters = m_parsedParameters[i];
EXPECT_EQ(expected[i].parameters.size(), m_parsedParameters[i].size());
for (HashMap<String, String>::const_iterator iterator = expectedParameters.begin(); iterator != expectedParameters.end(); ++iterator) {
HashMap<String, String>::const_iterator parsed = parsedParameters.find(iterator->key);
EXPECT_TRUE(parsed != parsedParameters.end());
if (iterator->value.isNull())
EXPECT_TRUE(parsed->value.isNull());
else
EXPECT_EQ(iterator->value, parsed->value);
}
}
}
TEST_F(WebSocketExtensionDispatcherTest, TestQuotedString)
{
addMockProcessor("x-foo");
ASSERT_TRUE(m_extensions.processHeaderValue("x-foo; param1=\"quoted-string\"; param2=\"quoted\\.string\""));
EXPECT_EQ(2UL, m_parsedParameters[0].size());
EXPECT_EQ("quoted-string", m_parsedParameters[0].get("param1"));
EXPECT_EQ("quoted.string", m_parsedParameters[0].get("param2"));
}
TEST_F(WebSocketExtensionDispatcherTest, TestInvalid)
{
const char* inputs[] = {
"\"x-foo\"",
"x-baz",
"x-foo\\",
"x-(foo)",
"x-foo; ",
"x-foo; bar=",
"x-foo; bar=x y",
"x-foo; bar=\"mismatch quote",
"x-foo; bar=\"\\\"",
"x-foo; \"bar\"=baz",
"x-foo; bar=\"\"",
"x-foo; bar=\" \"",
"x-foo; bar=\"bar baz\"",
"x-foo; bar=\"bar,baz\"",
"x-foo; bar=\"ba\xffr,baz\"",
"x-foo x-bar",
"x-foo, x-baz"
"x-foo, ",
};
for (size_t i = 0; i < sizeof(inputs) / sizeof(inputs[0]); ++i) {
m_extensions.reset();
addMockProcessor("x-foo");
addMockProcessor("x-bar");
EXPECT_FALSE(m_extensions.processHeaderValue(inputs[i]));
EXPECT_TRUE(m_extensions.acceptedExtensions().isNull());
}
}
}