summaryrefslogtreecommitdiff
path: root/chromium/net/server
diff options
context:
space:
mode:
authorJocelyn Turcotte <jocelyn.turcotte@digia.com>2014-08-08 14:30:41 +0200
committerJocelyn Turcotte <jocelyn.turcotte@digia.com>2014-08-12 13:49:54 +0200
commitab0a50979b9eb4dfa3320eff7e187e41efedf7a9 (patch)
tree498dfb8a97ff3361a9f7486863a52bb4e26bb898 /chromium/net/server
parent4ce69f7403811819800e7c5ae1318b2647e778d1 (diff)
downloadqtwebengine-chromium-ab0a50979b9eb4dfa3320eff7e187e41efedf7a9.tar.gz
Update Chromium to beta version 37.0.2062.68
Change-Id: I188e3b5aff1bec75566014291b654eb19f5bc8ca Reviewed-by: Andras Becsi <andras.becsi@digia.com>
Diffstat (limited to 'chromium/net/server')
-rw-r--r--chromium/net/server/http_server.cc33
-rw-r--r--chromium/net/server/http_server.h4
-rw-r--r--chromium/net/server/http_server_request_info.cc17
-rw-r--r--chromium/net/server/http_server_request_info.h11
-rw-r--r--chromium/net/server/http_server_unittest.cc223
5 files changed, 262 insertions, 26 deletions
diff --git a/chromium/net/server/http_server.cc b/chromium/net/server/http_server.cc
index a51feb84401..f746f066e42 100644
--- a/chromium/net/server/http_server.cc
+++ b/chromium/net/server/http_server.cc
@@ -47,6 +47,13 @@ void HttpServer::SendOverWebSocket(int connection_id,
connection->web_socket_->Send(data);
}
+void HttpServer::SendRaw(int connection_id, const std::string& data) {
+ HttpConnection* connection = FindConnection(connection_id);
+ if (connection == NULL)
+ return;
+ connection->Send(data);
+}
+
void HttpServer::SendResponse(int connection_id,
const HttpServerResponseInfo& response) {
HttpConnection* connection = FindConnection(connection_id);
@@ -132,8 +139,10 @@ void HttpServer::DidRead(StreamListenSocket* socket,
if (!ParseHeaders(connection, &request, &pos))
break;
- std::string connection_header = request.GetHeaderValue("connection");
- if (connection_header == "Upgrade") {
+ // Sets peer address if exists.
+ socket->GetPeerAddress(&request.peer);
+
+ if (request.HasHeaderValue("connection", "upgrade")) {
connection->web_socket_.reset(WebSocket::CreateWebSocket(connection,
request,
&pos));
@@ -195,7 +204,7 @@ HttpServer::~HttpServer() {
// Input character types.
enum header_parse_inputs {
- INPUT_SPACE,
+ INPUT_LWS,
INPUT_CR,
INPUT_LF,
INPUT_COLON,
@@ -234,7 +243,8 @@ int parser_state[MAX_STATES][MAX_INPUTS] = {
int charToInput(char ch) {
switch(ch) {
case ' ':
- return INPUT_SPACE;
+ case '\t':
+ return INPUT_LWS;
case '\r':
return INPUT_CR;
case '\n':
@@ -260,6 +270,7 @@ bool HttpServer::ParseHeaders(HttpConnection* connection,
int next_state = parser_state[state][input];
bool transition = (next_state != state);
+ HttpServerRequestInfo::HeadersMap::iterator it;
if (transition) {
// Do any actions based on state transitions.
switch (state) {
@@ -281,10 +292,16 @@ bool HttpServer::ParseHeaders(HttpConnection* connection,
buffer.clear();
break;
case ST_VALUE:
- TrimWhitespaceASCII(buffer, TRIM_LEADING, &header_value);
- // TODO(mbelshe): Deal better with duplicate headers
- DCHECK(info->headers.find(header_name) == info->headers.end());
- info->headers[header_name] = header_value;
+ base::TrimWhitespaceASCII(buffer, base::TRIM_LEADING, &header_value);
+ it = info->headers.find(header_name);
+ // 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;
+ } else {
+ it->second.append(",");
+ it->second.append(header_value);
+ }
buffer.clear();
break;
case ST_SEPARATOR:
diff --git a/chromium/net/server/http_server.h b/chromium/net/server/http_server.h
index 51bec956889..4309d122f1e 100644
--- a/chromium/net/server/http_server.h
+++ b/chromium/net/server/http_server.h
@@ -47,6 +47,10 @@ class HttpServer : public StreamListenSocket::Delegate,
void AcceptWebSocket(int connection_id,
const HttpServerRequestInfo& request);
void SendOverWebSocket(int connection_id, const std::string& data);
+ // Sends the provided data directly to the given connection. No validation is
+ // performed that data constitutes a valid HTTP response. A valid HTTP
+ // response may be split across multiple calls to SendRaw.
+ void SendRaw(int connection_id, const std::string& data);
void SendResponse(int connection_id, const HttpServerResponseInfo& response);
void Send(int connection_id,
HttpStatusCode status_code,
diff --git a/chromium/net/server/http_server_request_info.cc b/chromium/net/server/http_server_request_info.cc
index 67965f29aa3..8b65bee50c1 100644
--- a/chromium/net/server/http_server_request_info.cc
+++ b/chromium/net/server/http_server_request_info.cc
@@ -22,4 +22,21 @@ std::string HttpServerRequestInfo::GetHeaderValue(
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
diff --git a/chromium/net/server/http_server_request_info.h b/chromium/net/server/http_server_request_info.h
index 62824187901..1b02655b742 100644
--- a/chromium/net/server/http_server_request_info.h
+++ b/chromium/net/server/http_server_request_info.h
@@ -8,6 +8,8 @@
#include <map>
#include <string>
+#include "net/base/ip_endpoint.h"
+
namespace net {
// Meta information about an HTTP request.
@@ -23,6 +25,15 @@ class HttpServerRequestInfo {
// lower case.
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.
+ IPEndPoint peer;
+
// Request method.
std::string method;
diff --git a/chromium/net/server/http_server_unittest.cc b/chromium/net/server/http_server_unittest.cc
index 0c3c97f0a90..207c454acfb 100644
--- a/chromium/net/server/http_server_unittest.cc
+++ b/chromium/net/server/http_server_unittest.cc
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <utility>
#include <vector>
#include "base/bind.h"
@@ -23,6 +24,7 @@
#include "net/base/ip_endpoint.h"
#include "net/base/net_errors.h"
#include "net/base/net_log.h"
+#include "net/base/test_completion_callback.h"
#include "net/server/http_server.h"
#include "net/server/http_server_request_info.h"
#include "net/socket/tcp_client_socket.h"
@@ -38,6 +40,8 @@ namespace net {
namespace {
+const int kMaxExpectedResponseLength = 2048;
+
void SetTimedOutAndQuitLoop(const base::WeakPtr<bool> timed_out,
const base::Closure& quit_loop_func) {
if (timed_out) {
@@ -86,6 +90,26 @@ class TestHttpClient {
Write();
}
+ bool Read(std::string* message) {
+ return Read(message, 1);
+ }
+
+ bool Read(std::string* message, int expected_bytes) {
+ int total_bytes_received = 0;
+ message->clear();
+ while (total_bytes_received < expected_bytes) {
+ net::TestCompletionCallback callback;
+ ReadInternal(callback.callback());
+ int bytes_received = callback.WaitForResult();
+ if (bytes_received <= 0)
+ return false;
+
+ total_bytes_received += bytes_received;
+ message->append(read_buffer_->data(), bytes_received);
+ }
+ return true;
+ }
+
private:
void OnConnect(const base::Closure& quit_loop, int result) {
connect_result_ = result;
@@ -108,6 +132,16 @@ class TestHttpClient {
Write();
}
+ void ReadInternal(const net::CompletionCallback& callback) {
+ read_buffer_ = new IOBufferWithSize(kMaxExpectedResponseLength);
+ int result = socket_->Read(read_buffer_,
+ kMaxExpectedResponseLength,
+ callback);
+ if (result != ERR_IO_PENDING)
+ callback.Run(result);
+ }
+
+ scoped_refptr<IOBufferWithSize> read_buffer_;
scoped_refptr<DrainableIOBuffer> write_buffer_;
scoped_ptr<TCPClientSocket> socket_;
int connect_result_;
@@ -128,7 +162,7 @@ class HttpServerTest : public testing::Test,
virtual void OnHttpRequest(int connection_id,
const HttpServerRequestInfo& info) OVERRIDE {
- requests_.push_back(info);
+ requests_.push_back(std::make_pair(info, connection_id));
if (requests_.size() == quit_after_request_count_)
run_loop_quit_func_.Run();
}
@@ -157,25 +191,52 @@ class HttpServerTest : public testing::Test,
return success;
}
+ HttpServerRequestInfo GetRequest(size_t request_index) {
+ return requests_[request_index].first;
+ }
+
+ int GetConnectionId(size_t request_index) {
+ return requests_[request_index].second;
+ }
+
protected:
scoped_refptr<HttpServer> server_;
IPEndPoint server_address_;
base::Closure run_loop_quit_func_;
- std::vector<HttpServerRequestInfo> requests_;
+ std::vector<std::pair<HttpServerRequestInfo, int> > requests_;
private:
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) {
TestHttpClient client;
ASSERT_EQ(OK, client.ConnectAndWait(server_address_));
client.Send("GET /test HTTP/1.1\r\n\r\n");
ASSERT_TRUE(RunUntilRequestsReceived(1));
- ASSERT_EQ("GET", requests_[0].method);
- ASSERT_EQ("/test", requests_[0].path);
- ASSERT_EQ("", requests_[0].data);
- ASSERT_EQ(0u, requests_[0].headers.size());
+ ASSERT_EQ("GET", GetRequest(0).method);
+ ASSERT_EQ("/test", GetRequest(0).path);
+ ASSERT_EQ("", GetRequest(0).data);
+ ASSERT_EQ(0u, GetRequest(0).headers.size());
+ ASSERT_TRUE(StartsWithASCII(GetRequest(0).peer.ToString(),
+ "127.0.0.1",
+ true));
}
TEST_F(HttpServerTest, RequestWithHeaders) {
@@ -198,16 +259,81 @@ TEST_F(HttpServerTest, RequestWithHeaders) {
client.Send("GET /test HTTP/1.1\r\n" + headers + "\r\n");
ASSERT_TRUE(RunUntilRequestsReceived(1));
- ASSERT_EQ("", requests_[0].data);
+ 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 = kHeaders[i][2];
- ASSERT_EQ(1u, requests_[0].headers.count(field)) << field;
- ASSERT_EQ(value, requests_[0].headers[field]) << kHeaders[i][0];
+ ASSERT_EQ(1u, GetRequest(0).headers.count(field)) << field;
+ ASSERT_EQ(value, GetRequest(0).headers[field]) << kHeaders[i][0];
}
}
+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) {
TestHttpClient client;
ASSERT_EQ(OK, client.ConnectAndWait(server_address_));
@@ -219,12 +345,25 @@ TEST_F(HttpServerTest, RequestWithBody) {
body.length(),
body.c_str()));
ASSERT_TRUE(RunUntilRequestsReceived(1));
- ASSERT_EQ(2u, requests_[0].headers.size());
- ASSERT_EQ(body.length(), requests_[0].data.length());
+ ASSERT_EQ(2u, GetRequest(0).headers.size());
+ ASSERT_EQ(body.length(), GetRequest(0).data.length());
ASSERT_EQ('a', body[0]);
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) {
class TestURLFetcherDelegate : public URLFetcherDelegate {
public:
@@ -260,6 +399,34 @@ TEST_F(HttpServerTest, RequestWithTooLargeBody) {
ASSERT_EQ(0u, requests_.size());
}
+TEST_F(HttpServerTest, Send200) {
+ TestHttpClient client;
+ ASSERT_EQ(OK, client.ConnectAndWait(server_address_));
+ client.Send("GET /test HTTP/1.1\r\n\r\n");
+ ASSERT_TRUE(RunUntilRequestsReceived(1));
+ server_->Send200(GetConnectionId(0), "Response!", "text/plain");
+
+ std::string response;
+ ASSERT_TRUE(client.Read(&response));
+ ASSERT_TRUE(StartsWithASCII(response, "HTTP/1.1 200 OK", true));
+ ASSERT_TRUE(EndsWith(response, "Response!", true));
+}
+
+TEST_F(HttpServerTest, SendRaw) {
+ TestHttpClient client;
+ ASSERT_EQ(OK, client.ConnectAndWait(server_address_));
+ client.Send("GET /test HTTP/1.1\r\n\r\n");
+ ASSERT_TRUE(RunUntilRequestsReceived(1));
+ server_->SendRaw(GetConnectionId(0), "Raw Data ");
+ server_->SendRaw(GetConnectionId(0), "More Data");
+ server_->SendRaw(GetConnectionId(0), "Third Piece of Data");
+
+ const std::string expected_response("Raw Data More DataThird Piece of Data");
+ std::string response;
+ ASSERT_TRUE(client.Read(&response, expected_response.length()));
+ ASSERT_EQ(expected_response, response);
+}
+
namespace {
class MockStreamListenSocket : public StreamListenSocket {
@@ -280,17 +447,17 @@ TEST_F(HttpServerTest, RequestWithBodySplitAcrossPackets) {
new MockStreamListenSocket(server_.get());
server_->DidAccept(NULL, make_scoped_ptr(socket));
std::string body("body");
- std::string request = base::StringPrintf(
+ std::string request_text = base::StringPrintf(
"GET /test HTTP/1.1\r\n"
"SomeHeader: 1\r\n"
"Content-Length: %" PRIuS "\r\n\r\n%s",
body.length(),
body.c_str());
- server_->DidRead(socket, request.c_str(), request.length() - 2);
+ server_->DidRead(socket, request_text.c_str(), request_text.length() - 2);
ASSERT_EQ(0u, requests_.size());
- server_->DidRead(socket, request.c_str() + request.length() - 2, 2);
+ server_->DidRead(socket, request_text.c_str() + request_text.length() - 2, 2);
ASSERT_EQ(1u, requests_.size());
- ASSERT_EQ(body, requests_[0].data);
+ ASSERT_EQ(body, GetRequest(0).data);
}
TEST_F(HttpServerTest, MultipleRequestsOnSameConnection) {
@@ -305,15 +472,35 @@ TEST_F(HttpServerTest, MultipleRequestsOnSameConnection) {
body.length(),
body.c_str()));
ASSERT_TRUE(RunUntilRequestsReceived(1));
- ASSERT_EQ(body, requests_[0].data);
+ ASSERT_EQ(body, GetRequest(0).data);
+
+ int client_connection_id = GetConnectionId(0);
+ server_->Send200(client_connection_id, "Content for /test", "text/plain");
+ std::string response1;
+ ASSERT_TRUE(client.Read(&response1));
+ ASSERT_TRUE(StartsWithASCII(response1, "HTTP/1.1 200 OK", true));
+ ASSERT_TRUE(EndsWith(response1, "Content for /test", true));
client.Send("GET /test2 HTTP/1.1\r\n\r\n");
ASSERT_TRUE(RunUntilRequestsReceived(2));
- ASSERT_EQ("/test2", requests_[1].path);
+ ASSERT_EQ("/test2", GetRequest(1).path);
+
+ ASSERT_EQ(client_connection_id, GetConnectionId(1));
+ server_->Send404(client_connection_id);
+ std::string response2;
+ ASSERT_TRUE(client.Read(&response2));
+ ASSERT_TRUE(StartsWithASCII(response2, "HTTP/1.1 404 Not Found", true));
client.Send("GET /test3 HTTP/1.1\r\n\r\n");
ASSERT_TRUE(RunUntilRequestsReceived(3));
- ASSERT_EQ("/test3", requests_[2].path);
+ ASSERT_EQ("/test3", GetRequest(2).path);
+
+ ASSERT_EQ(client_connection_id, GetConnectionId(2));
+ server_->Send200(client_connection_id, "Content for /test3", "text/plain");
+ std::string response3;
+ ASSERT_TRUE(client.Read(&response3));
+ ASSERT_TRUE(StartsWithASCII(response3, "HTTP/1.1 200 OK", true));
+ ASSERT_TRUE(EndsWith(response3, "Content for /test3", true));
}
} // namespace net