HttpServer: Handling of multiple header fields with the same name and multiple...

HttpServer: Handling of multiple header fields with the same name and multiple values of "Connection".

BUG=370437

Review URL: https://codereview.chromium.org/274813002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@269619 0039d316-1c4b-4281-b951-d872f2087c98
parent 7dfcda1c
...@@ -142,8 +142,7 @@ void HttpServer::DidRead(StreamListenSocket* socket, ...@@ -142,8 +142,7 @@ void HttpServer::DidRead(StreamListenSocket* socket,
// Sets peer address if exists. // Sets peer address if exists.
socket->GetPeerAddress(&request.peer); socket->GetPeerAddress(&request.peer);
std::string connection_header = request.GetHeaderValue("connection"); if (request.HasHeaderValue("connection", "upgrade")) {
if (connection_header == "Upgrade") {
connection->web_socket_.reset(WebSocket::CreateWebSocket(connection, connection->web_socket_.reset(WebSocket::CreateWebSocket(connection,
request, request,
&pos)); &pos));
...@@ -205,7 +204,7 @@ HttpServer::~HttpServer() { ...@@ -205,7 +204,7 @@ HttpServer::~HttpServer() {
// Input character types. // Input character types.
enum header_parse_inputs { enum header_parse_inputs {
INPUT_SPACE, INPUT_LWS,
INPUT_CR, INPUT_CR,
INPUT_LF, INPUT_LF,
INPUT_COLON, INPUT_COLON,
...@@ -244,7 +243,8 @@ int parser_state[MAX_STATES][MAX_INPUTS] = { ...@@ -244,7 +243,8 @@ int parser_state[MAX_STATES][MAX_INPUTS] = {
int charToInput(char ch) { int charToInput(char ch) {
switch(ch) { switch(ch) {
case ' ': case ' ':
return INPUT_SPACE; case '\t':
return INPUT_LWS;
case '\r': case '\r':
return INPUT_CR; return INPUT_CR;
case '\n': case '\n':
...@@ -270,6 +270,7 @@ bool HttpServer::ParseHeaders(HttpConnection* connection, ...@@ -270,6 +270,7 @@ bool HttpServer::ParseHeaders(HttpConnection* connection,
int next_state = parser_state[state][input]; int next_state = parser_state[state][input];
bool transition = (next_state != state); bool transition = (next_state != state);
HttpServerRequestInfo::HeadersMap::iterator it;
if (transition) { if (transition) {
// Do any actions based on state transitions. // Do any actions based on state transitions.
switch (state) { switch (state) {
...@@ -292,9 +293,15 @@ bool HttpServer::ParseHeaders(HttpConnection* connection, ...@@ -292,9 +293,15 @@ bool HttpServer::ParseHeaders(HttpConnection* connection,
break; break;
case ST_VALUE: case ST_VALUE:
base::TrimWhitespaceASCII(buffer, base::TRIM_LEADING, &header_value); base::TrimWhitespaceASCII(buffer, base::TRIM_LEADING, &header_value);
// TODO(mbelshe): Deal better with duplicate headers it = info->headers.find(header_name);
DCHECK(info->headers.find(header_name) == info->headers.end()); // See last paragraph ("Multiple message-header fields...")
// of www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
if (it == info->headers.end()) {
info->headers[header_name] = header_value; info->headers[header_name] = header_value;
} else {
it->second.append(",");
it->second.append(header_value);
}
buffer.clear(); buffer.clear();
break; break;
case ST_SEPARATOR: case ST_SEPARATOR:
......
...@@ -22,4 +22,21 @@ std::string HttpServerRequestInfo::GetHeaderValue( ...@@ -22,4 +22,21 @@ std::string HttpServerRequestInfo::GetHeaderValue(
return std::string(); return std::string();
} }
bool HttpServerRequestInfo::HasHeaderValue(
const std::string& header_name,
const std::string& header_value) const {
DCHECK_EQ(StringToLowerASCII(header_value), header_value);
std::string complete_value = GetHeaderValue(header_name);
StringToLowerASCII(&complete_value);
std::vector<std::string> value_items;
Tokenize(complete_value, ",", &value_items);
for (std::vector<std::string>::iterator it = value_items.begin();
it != value_items.end(); ++it) {
base::TrimString(*it, " \t", &*it);
if (*it == header_value)
return true;
}
return false;
}
} // namespace net } // namespace net
...@@ -25,6 +25,12 @@ class HttpServerRequestInfo { ...@@ -25,6 +25,12 @@ class HttpServerRequestInfo {
// lower case. // lower case.
std::string GetHeaderValue(const std::string& header_name) const; std::string GetHeaderValue(const std::string& header_name) const;
// Checks for item in comma-separated header value for given header name.
// Both |header_name| and |header_value| should be lower case.
bool HasHeaderValue(
const std::string& header_name,
const std::string& header_value) const;
// Request peer address. // Request peer address.
IPEndPoint peer; IPEndPoint peer;
......
...@@ -209,6 +209,22 @@ class HttpServerTest : public testing::Test, ...@@ -209,6 +209,22 @@ class HttpServerTest : public testing::Test,
size_t quit_after_request_count_; size_t quit_after_request_count_;
}; };
class WebSocketTest : public HttpServerTest {
virtual void OnHttpRequest(int connection_id,
const HttpServerRequestInfo& info) OVERRIDE {
NOTREACHED();
}
virtual void OnWebSocketRequest(int connection_id,
const HttpServerRequestInfo& info) OVERRIDE {
HttpServerTest::OnHttpRequest(connection_id, info);
}
virtual void OnWebSocketMessage(int connection_id,
const std::string& data) OVERRIDE {
}
};
TEST_F(HttpServerTest, Request) { TEST_F(HttpServerTest, Request) {
TestHttpClient client; TestHttpClient client;
ASSERT_EQ(OK, client.ConnectAndWait(server_address_)); ASSERT_EQ(OK, client.ConnectAndWait(server_address_));
...@@ -253,6 +269,71 @@ TEST_F(HttpServerTest, RequestWithHeaders) { ...@@ -253,6 +269,71 @@ TEST_F(HttpServerTest, RequestWithHeaders) {
} }
} }
TEST_F(HttpServerTest, RequestWithDuplicateHeaders) {
TestHttpClient client;
ASSERT_EQ(OK, client.ConnectAndWait(server_address_));
const char* kHeaders[][3] = {
{"FirstHeader", ": ", "1"},
{"DuplicateHeader", ": ", "2"},
{"MiddleHeader", ": ", "3"},
{"DuplicateHeader", ": ", "4"},
{"LastHeader", ": ", "5"},
};
std::string headers;
for (size_t i = 0; i < arraysize(kHeaders); ++i) {
headers +=
std::string(kHeaders[i][0]) + kHeaders[i][1] + kHeaders[i][2] + "\r\n";
}
client.Send("GET /test HTTP/1.1\r\n" + headers + "\r\n");
ASSERT_TRUE(RunUntilRequestsReceived(1));
ASSERT_EQ("", GetRequest(0).data);
for (size_t i = 0; i < arraysize(kHeaders); ++i) {
std::string field = StringToLowerASCII(std::string(kHeaders[i][0]));
std::string value = (field == "duplicateheader") ? "2,4" : kHeaders[i][2];
ASSERT_EQ(1u, GetRequest(0).headers.count(field)) << field;
ASSERT_EQ(value, GetRequest(0).headers[field]) << kHeaders[i][0];
}
}
TEST_F(HttpServerTest, HasHeaderValueTest) {
TestHttpClient client;
ASSERT_EQ(OK, client.ConnectAndWait(server_address_));
const char* kHeaders[] = {
"Header: Abcd",
"HeaderWithNoWhitespace:E",
"HeaderWithWhitespace : \t f \t ",
"DuplicateHeader: g",
"HeaderWithComma: h, i ,j",
"DuplicateHeader: k",
"EmptyHeader:",
"EmptyHeaderWithWhitespace: \t ",
"HeaderWithNonASCII: \xf7",
};
std::string headers;
for (size_t i = 0; i < arraysize(kHeaders); ++i) {
headers += std::string(kHeaders[i]) + "\r\n";
}
client.Send("GET /test HTTP/1.1\r\n" + headers + "\r\n");
ASSERT_TRUE(RunUntilRequestsReceived(1));
ASSERT_EQ("", GetRequest(0).data);
ASSERT_TRUE(GetRequest(0).HasHeaderValue("header", "abcd"));
ASSERT_FALSE(GetRequest(0).HasHeaderValue("header", "bc"));
ASSERT_TRUE(GetRequest(0).HasHeaderValue("headerwithnowhitespace", "e"));
ASSERT_TRUE(GetRequest(0).HasHeaderValue("headerwithwhitespace", "f"));
ASSERT_TRUE(GetRequest(0).HasHeaderValue("duplicateheader", "g"));
ASSERT_TRUE(GetRequest(0).HasHeaderValue("headerwithcomma", "h"));
ASSERT_TRUE(GetRequest(0).HasHeaderValue("headerwithcomma", "i"));
ASSERT_TRUE(GetRequest(0).HasHeaderValue("headerwithcomma", "j"));
ASSERT_TRUE(GetRequest(0).HasHeaderValue("duplicateheader", "k"));
ASSERT_FALSE(GetRequest(0).HasHeaderValue("emptyheader", "x"));
ASSERT_FALSE(GetRequest(0).HasHeaderValue("emptyheaderwithwhitespace", "x"));
ASSERT_TRUE(GetRequest(0).HasHeaderValue("headerwithnonascii", "\xf7"));
}
TEST_F(HttpServerTest, RequestWithBody) { TEST_F(HttpServerTest, RequestWithBody) {
TestHttpClient client; TestHttpClient client;
ASSERT_EQ(OK, client.ConnectAndWait(server_address_)); ASSERT_EQ(OK, client.ConnectAndWait(server_address_));
...@@ -270,6 +351,19 @@ TEST_F(HttpServerTest, RequestWithBody) { ...@@ -270,6 +351,19 @@ TEST_F(HttpServerTest, RequestWithBody) {
ASSERT_EQ('c', *body.rbegin()); ASSERT_EQ('c', *body.rbegin());
} }
TEST_F(WebSocketTest, RequestWebSocket) {
TestHttpClient client;
ASSERT_EQ(OK, client.ConnectAndWait(server_address_));
client.Send(
"GET /test HTTP/1.1\r\n"
"Upgrade: WebSocket\r\n"
"Connection: SomethingElse, Upgrade\r\n"
"Sec-WebSocket-Version: 8\r\n"
"Sec-WebSocket-Key: key\r\n"
"\r\n");
ASSERT_TRUE(RunUntilRequestsReceived(1));
}
TEST_F(HttpServerTest, RequestWithTooLargeBody) { TEST_F(HttpServerTest, RequestWithTooLargeBody) {
class TestURLFetcherDelegate : public URLFetcherDelegate { class TestURLFetcherDelegate : public URLFetcherDelegate {
public: public:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment