summaryrefslogtreecommitdiffstats
path: root/chrome_frame
diff options
context:
space:
mode:
authorstoyan@chromium.org <stoyan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-06-09 16:54:42 +0000
committerstoyan@chromium.org <stoyan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-06-09 16:54:42 +0000
commit0f53f5628ec01d743afade7de5e8ae350ca6880b (patch)
tree0c664dfb70e4c30b60719a4c24290aec44ca9dc1 /chrome_frame
parentce2b9f923c23700d24658b5118ab9c1d2f330d8b (diff)
downloadchromium_src-0f53f5628ec01d743afade7de5e8ae350ca6880b.zip
chromium_src-0f53f5628ec01d743afade7de5e8ae350ca6880b.tar.gz
chromium_src-0f53f5628ec01d743afade7de5e8ae350ca6880b.tar.bz2
Yet another test web server. Supports speed throttle and dump of the traffic.
Allows binding on 0.0.0.0 to allow connections from a VM (for example). Simple GMock class and Actions. Review URL: http://codereview.chromium.org/2620006 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@49266 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome_frame')
-rw-r--r--chrome_frame/test/test_server.cc114
-rw-r--r--chrome_frame/test/test_server.h77
-rw-r--r--chrome_frame/test/test_with_web_server.h54
3 files changed, 245 insertions, 0 deletions
diff --git a/chrome_frame/test/test_server.cc b/chrome_frame/test/test_server.cc
index 79950e9..942333a 100644
--- a/chrome_frame/test/test_server.cc
+++ b/chrome_frame/test/test_server.cc
@@ -215,4 +215,118 @@ void SimpleWebServer::DidClose(ListenSocket* sock) {
}
}
+HTTPTestServer::HTTPTestServer(int port, const char* address) {
+ net::EnsureWinsockInit();
+ server_ = ListenSocket::Listen(address, port, this);
+}
+
+HTTPTestServer::~HTTPTestServer() {
+}
+
+std::list<scoped_refptr<ConfigurableConnection>>::iterator
+HTTPTestServer::FindConnection(const ListenSocket* socket) {
+ ConnectionList::iterator it;
+ for (it = connection_list_.begin(); it != connection_list_.end(); ++it) {
+ if ((*it)->socket_ == socket) {
+ break;
+ }
+ }
+
+ return it;
+}
+
+scoped_refptr<ConfigurableConnection> HTTPTestServer::ConnectionFromSocket(
+ const ListenSocket* socket) {
+ ConnectionList::iterator it = FindConnection(socket);
+ if (it != connection_list_.end())
+ return *it;
+ return NULL;
+}
+
+void HTTPTestServer::DidAccept(ListenSocket* server, ListenSocket* socket) {
+ connection_list_.push_back(new ConfigurableConnection(socket));
+}
+
+void HTTPTestServer::DidRead(ListenSocket* socket, const std::string& data) {
+ scoped_refptr<ConfigurableConnection> connection =
+ ConnectionFromSocket(socket);
+ if (connection) {
+ connection->r_.OnDataReceived(data);
+ if (connection->r_.AllContentReceived()) {
+ if (LowerCaseEqualsASCII(connection->r_.method(), "post"))
+ this->Post(connection, connection->r_.path(), connection->r_);
+ else
+ this->Get(connection, connection->r_.path(), connection->r_);
+ }
+ }
+}
+
+void HTTPTestServer::DidClose(ListenSocket* socket) {
+ ConnectionList::iterator it = FindConnection(socket);
+ DCHECK(it != connection_list_.end());
+ connection_list_.erase(it);
+}
+
+void ConfigurableConnection::SendChunk() {
+ int size = (int)data_.size();
+ const char* chunk_ptr = data_.c_str() + cur_pos_;
+ int bytes_to_send = std::min(options_.chunk_size_, size - cur_pos_);
+
+ socket_->Send(chunk_ptr, bytes_to_send);
+ DLOG(INFO) << "Sent(" << cur_pos_ << "," << bytes_to_send
+ << "): " << base::StringPiece(chunk_ptr, bytes_to_send);
+
+ cur_pos_ += bytes_to_send;
+ if (cur_pos_ < size) {
+ MessageLoop::current()->PostDelayedTask(FROM_HERE, NewRunnableMethod(this,
+ &ConfigurableConnection::SendChunk), options_.timeout_);
+ } else {
+ socket_ = 0; // close the connection.
+ }
+}
+
+void ConfigurableConnection::Send(const std::string& headers,
+ const std::string& content) {
+ SendOptions options(SendOptions::IMMEDIATE, 0, 0);
+ SendWithOptions(headers, content, options);
+}
+
+void ConfigurableConnection::SendWithOptions(const std::string& headers,
+ const std::string& content,
+ const SendOptions& options) {
+ std::string content_length_header;
+ if (!content.empty() &&
+ std::string::npos == headers.find("Context-Length:")) {
+ content_length_header = StringPrintf("Content-Length: %u\r\n",
+ content.size());
+ }
+
+ // Save the options.
+ options_ = options;
+
+ if (options_.speed_ == SendOptions::IMMEDIATE) {
+ socket_->Send(headers);
+ socket_->Send(content_length_header, true);
+ socket_->Send(content);
+ socket_ = 0; // close the connection.
+ return;
+ }
+
+ if (options_.speed_ == SendOptions::IMMEDIATE_HEADERS_DELAYED_CONTENT) {
+ socket_->Send(headers);
+ socket_->Send(content_length_header, true);
+ DLOG(INFO) << "Headers sent: " << headers << content_length_header;
+ data_.append(content);
+ }
+
+ if (options_.speed_ == SendOptions::DELAYED) {
+ data_ = headers;
+ data_.append(content_length_header);
+ data_.append("\r\n");
+ }
+
+ MessageLoop::current()->PostDelayedTask(FROM_HERE,
+ NewRunnableMethod(this, &ConfigurableConnection::SendChunk),
+ options.timeout_);
+}
} // namespace test_server
diff --git a/chrome_frame/test/test_server.h b/chrome_frame/test/test_server.h
index 58fd160..b827ceb 100644
--- a/chrome_frame/test/test_server.h
+++ b/chrome_frame/test/test_server.h
@@ -321,6 +321,83 @@ class SimpleWebServer : public ListenSocket::ListenSocketDelegate {
DISALLOW_COPY_AND_ASSIGN(SimpleWebServer);
};
+// Simple class holding incoming HTTP request. Can send the HTTP response
+// at different rate - small chunks, on regular interval.
+class ConfigurableConnection : public base::RefCounted<ConfigurableConnection> {
+ public:
+ struct SendOptions {
+ enum Speed { IMMEDIATE, DELAYED, IMMEDIATE_HEADERS_DELAYED_CONTENT };
+ SendOptions() : speed_(IMMEDIATE), chunk_size_(0), timeout_(0) { }
+ SendOptions(Speed speed, int chunk_size, int64 timeout)
+ : speed_(speed), chunk_size_(chunk_size), timeout_(timeout) {
+ }
+
+ Speed speed_;
+ int chunk_size_;
+ int64 timeout_;
+ };
+
+ explicit ConfigurableConnection(ListenSocket* sock)
+ : socket_(sock), cur_pos_(0) { }
+
+ // Send HTTP response with provided |headers| and |content|. Appends
+ // "Context-Length:" header if the |content| is not empty.
+ void Send(const std::string& headers, const std::string& content);
+
+ // Send HTTP response with provided |headers| and |content|. Appends
+ // "Context-Length:" header if the |content| is not empty.
+ // Use the |options| to tweak the network speed behaviour.
+ void SendWithOptions(const std::string& headers, const std::string& content,
+ const SendOptions& options);
+
+ private:
+ friend class HTTPTestServer;
+ // Sends a chunk of the response and queues itself as a task for sending
+ // next chunk of |data_|.
+ void SendChunk();
+
+ scoped_refptr<ListenSocket> socket_;
+ Request r_;
+ SendOptions options_;
+ std::string data_;
+ int cur_pos_;
+
+ DISALLOW_COPY_AND_ASSIGN(ConfigurableConnection);
+};
+
+// Simple class used as a base class for mock webserver.
+// Override virtual functions Get and Post and use passed ConfigurableConnection
+// instance to send the response.
+class HTTPTestServer : public ListenSocket::ListenSocketDelegate {
+ public:
+ explicit HTTPTestServer(int port, const char* address);
+ virtual ~HTTPTestServer();
+ // HTTP GET request is received. Override in derived classes.
+ // |connection| can be used to send the response.
+ virtual void Get(ConfigurableConnection* connection,
+ const std::string& path, const Request& r) = 0;
+ // HTTP POST request is received. Override in derived classes.
+ // |connection| can be used to send the response
+ virtual void Post(ConfigurableConnection* connection,
+ const std::string& path, const Request& r) = 0;
+
+private:
+ typedef std::list<scoped_refptr<ConfigurableConnection> > ConnectionList;
+ ConnectionList::iterator FindConnection(const ListenSocket* socket);
+ scoped_refptr<ConfigurableConnection> ConnectionFromSocket(
+ const ListenSocket* socket);
+
+ // ListenSocketDelegate overrides.
+ virtual void DidAccept(ListenSocket* server, ListenSocket* socket);
+ virtual void DidRead(ListenSocket* socket, const std::string& data);
+ virtual void DidClose(ListenSocket* socket);
+
+ scoped_refptr<ListenSocket> server_;
+ ConnectionList connection_list_;
+ DISALLOW_COPY_AND_ASSIGN(HTTPTestServer);
+};
+
+
} // namespace test_server
#endif // CHROME_FRAME_TEST_TEST_SERVER_H_
diff --git a/chrome_frame/test/test_with_web_server.h b/chrome_frame/test/test_with_web_server.h
index 16b03bc..30466b9b 100644
--- a/chrome_frame/test/test_with_web_server.h
+++ b/chrome_frame/test/test_with_web_server.h
@@ -11,6 +11,7 @@
#include "chrome_frame/test/http_server.h"
#include "chrome_frame/test/test_server.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/gmock/include/gmock/gmock.h"
// Include without path to make GYP build see it.
#include "chrome_tab.h" // NOLINT
@@ -176,4 +177,57 @@ class SimpleWebServerTest {
int port_;
};
+// Simple Gmock friendly web server. Sample usage:
+// MockWebServer mock(9999, "0.0.0.0");
+// EXPECT_CALL(mock, Get(_, StrEq("/favicon.ico"), _)).WillRepeatedly(SendFast(
+// "HTTP/1.1 404 Not Found"
+// "text/html; charset=UTF-8", EmptyString()));
+//
+// EXPECT_CALL(mock, Get(_, StrEq("/book"), _)).WillRepeatedly(Send(
+// "HTTP/1.1 302 Found\r\n"
+// "Connection: close\r\n"
+// "Content-Type: text/html\r\n"
+// "Location: library\r\n",
+// "<html>Lalalala</html>", 3, 1000));
+//
+// EXPECT_CALL(mock, Get(_, StrEq("/library"), _)).WillRepeatedly(Send(
+// "HTTP/1.1 200 OK\r\n"
+// "Connection: close\r\n"
+// "Content-Type: text/html\r\n",
+// "<html><meta http-equiv=\"X-UA-Compatible\" content=\"chrome=1\" />"
+// "<body>Rendered in CF.</body></html>", 4, 1000));
+
+class MockWebServer : public test_server::HTTPTestServer {
+ public:
+ MockWebServer(int port, const char* address = "127.0.0.1")
+ : test_server::HTTPTestServer(port, address) {}
+ MOCK_METHOD3(Get, void(test_server::ConfigurableConnection* connection,
+ const std::string& path,
+ const test_server::Request&r));
+ MOCK_METHOD3(Post, void(test_server::ConfigurableConnection* connection,
+ const std::string& path,
+ const test_server::Request&r));
+};
+
+ACTION_P2(SendFast, headers, content) {
+ arg0->Send(headers, content);
+}
+
+ACTION_P4(Send, headers, content, chunk, timeout) {
+ test_server::ConfigurableConnection::SendOptions options(
+ test_server::ConfigurableConnection::SendOptions::
+ IMMEDIATE_HEADERS_DELAYED_CONTENT, chunk, timeout);
+ arg0->SendWithOptions(std::string(headers),
+ std::string(content),
+ options);
+}
+
+ACTION_P4(SendSlow, headers, content, chunk, timeout) {
+ test_server::ConfigurableConnection::SendOptions options(
+ test_server::ConfigurableConnection::SendOptions::DELAYED, chunk, timeout);
+ arg0->SendWithOptions(std::string(headers),
+ std::string(content),
+ options);
+}
+
#endif // CHROME_FRAME_TEST_TEST_WITH_WEB_SERVER_H_