diff options
author | stoyan@chromium.org <stoyan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-06-09 16:54:42 +0000 |
---|---|---|
committer | stoyan@chromium.org <stoyan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-06-09 16:54:42 +0000 |
commit | 0f53f5628ec01d743afade7de5e8ae350ca6880b (patch) | |
tree | 0c664dfb70e4c30b60719a4c24290aec44ca9dc1 /chrome_frame | |
parent | ce2b9f923c23700d24658b5118ab9c1d2f330d8b (diff) | |
download | chromium_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.cc | 114 | ||||
-rw-r--r-- | chrome_frame/test/test_server.h | 77 | ||||
-rw-r--r-- | chrome_frame/test/test_with_web_server.h | 54 |
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_ |