diff options
author | Jocelyn Turcotte <jocelyn.turcotte@digia.com> | 2014-08-08 14:30:41 +0200 |
---|---|---|
committer | Jocelyn Turcotte <jocelyn.turcotte@digia.com> | 2014-08-12 13:49:54 +0200 |
commit | ab0a50979b9eb4dfa3320eff7e187e41efedf7a9 (patch) | |
tree | 498dfb8a97ff3361a9f7486863a52bb4e26bb898 /chromium/net/server | |
parent | 4ce69f7403811819800e7c5ae1318b2647e778d1 (diff) | |
download | qtwebengine-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.cc | 33 | ||||
-rw-r--r-- | chromium/net/server/http_server.h | 4 | ||||
-rw-r--r-- | chromium/net/server/http_server_request_info.cc | 17 | ||||
-rw-r--r-- | chromium/net/server/http_server_request_info.h | 11 | ||||
-rw-r--r-- | chromium/net/server/http_server_unittest.cc | 223 |
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 |