1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#ifndef CHROME_FRAME_TEST_TEST_SERVER_H_ 6#define CHROME_FRAME_TEST_TEST_SERVER_H_ 7 8// Implementation of an HTTP server for tests. 9// To instantiate the server, make sure you have a message loop on the 10// current thread and then create an instance of the SimpleWebServer class. 11// The server uses two basic concepts, a request and a response. 12// The Response interface represents an item (e.g. a document) available from 13// the server. A Request object represents a request from a client (e.g. a 14// browser). There are several basic Response classes implemented in this file, 15// all derived from the Response interface. 16// 17// Here's a simple example that starts a web server that can serve up 18// a single document (http://<server.host()>:1337/foo). 19// All other requests will get a 404. 20// 21// MessageLoopForUI loop; 22// test_server::SimpleWebServer server(1337); 23// test_server::SimpleResponse document("/foo", "Hello World!"); 24// test_server.AddResponse(&document); 25// loop.MessageLoop::Run(); 26// 27// To close the web server, just go to http://<server.host()>:1337/quit. 28// 29// All Response classes count how many times they have been accessed. Just 30// call Response::accessed(). 31// 32// To implement a custom response object (e.g. to match against a request 33// based on some data, serve up dynamic content or take some action on the 34// server), just inherit from one of the response classes or directly from the 35// Response interface and add your response object to the server's list of 36// response objects. 37 38#include <list> 39#include <string> 40 41#include "base/basictypes.h" 42#include "base/files/file_path.h" 43#include "base/files/memory_mapped_file.h" 44#include "base/message_loop/message_loop.h" 45#include "net/socket/stream_listen_socket.h" 46 47namespace test_server { 48 49class Request { 50 public: 51 Request() : content_length_(0) { 52 } 53 54 void ParseHeaders(const std::string& headers); 55 56 const std::string& method() const { 57 return method_; 58 } 59 60 const std::string& path() const { 61 return path_; 62 } 63 64 // Returns the argument section of a GET path. 65 // Note: does currently not work for POST request. 66 std::string arguments() const { 67 std::string ret; 68 std::string::size_type pos = path_.find('?'); 69 if (pos != std::string::npos) 70 ret = path_.substr(pos + 1); 71 return ret; 72 } 73 74 const std::string& headers() const { 75 return headers_; 76 } 77 78 const std::string& content() const { 79 return content_; 80 } 81 82 size_t content_length() const { 83 return content_length_; 84 } 85 86 bool AllContentReceived() const { 87 return method_.length() && content_.size() >= content_length_; 88 } 89 90 void OnDataReceived(const std::string& data); 91 92 protected: 93 std::string method_; 94 std::string path_; 95 std::string version_; 96 std::string headers_; 97 std::string content_; 98 size_t content_length_; 99 100 private: 101 DISALLOW_COPY_AND_ASSIGN(Request); 102}; 103 104// Manages request headers for a single request. 105// For each successful request that's made, the server will keep an instance 106// of this class so that they can be checked even after the server has been 107// shut down. 108class Connection { 109 public: 110 explicit Connection(net::StreamListenSocket* sock) : socket_(sock) { 111 } 112 113 ~Connection() { 114 } 115 116 bool IsSame(const net::StreamListenSocket* socket) const { 117 return socket_ == socket; 118 } 119 120 const Request& request() const { 121 return request_; 122 } 123 124 Request& request() { 125 return request_; 126 } 127 128 void OnSocketClosed() { 129 socket_ = NULL; 130 } 131 132 protected: 133 scoped_refptr<net::StreamListenSocket> socket_; 134 Request request_; 135 136 private: 137 DISALLOW_COPY_AND_ASSIGN(Connection); 138}; 139 140// Abstract interface with default implementations for some of the methods and 141// a counter for how many times the response object has served requests. 142class Response { 143 public: 144 Response() : accessed_(0) { 145 } 146 147 virtual ~Response() { 148 } 149 150 // Returns true if this response object should be used for a given request. 151 virtual bool Matches(const Request& r) const = 0; 152 153 // Response objects can optionally supply their own HTTP headers, completely 154 // bypassing the default ones. 155 virtual bool GetCustomHeaders(std::string* headers) const { 156 return false; 157 } 158 159 // Optionally provide a content type. Return false if you don't specify 160 // a content type. 161 virtual bool GetContentType(std::string* content_type) const { 162 return false; 163 } 164 165 virtual size_t ContentLength() const { 166 return 0; 167 } 168 169 virtual void WriteContents(net::StreamListenSocket* socket) const { 170 } 171 172 virtual void IncrementAccessCounter() { 173 accessed_++; 174 } 175 176 size_t accessed() const { 177 return accessed_; 178 } 179 180 protected: 181 size_t accessed_; 182 183 private: 184 DISALLOW_COPY_AND_ASSIGN(Response); 185}; 186 187// Partial implementation of Response that matches a request's path. 188// This is just a convenience implementation for the boilerplate implementation 189// of Matches(). Don't instantiate directly. 190class ResponseForPath : public Response { 191 public: 192 explicit ResponseForPath(const char* request_path) 193 : request_path_(request_path) { 194 } 195 196 virtual ~ResponseForPath(); 197 198 virtual bool Matches(const Request& r) const { 199 std::string path = r.path(); 200 std::string::size_type pos = path.find('?'); 201 if (pos != std::string::npos) 202 path = path.substr(0, pos); 203 return path.compare(request_path_) == 0; 204 } 205 206 protected: 207 std::string request_path_; 208 209 private: 210 DISALLOW_COPY_AND_ASSIGN(ResponseForPath); 211}; 212 213// A very basic implementation of a response. 214// A simple response matches a single document path on the server 215// (e.g. "/foo") and returns a document in the form of a string. 216class SimpleResponse : public ResponseForPath { 217 public: 218 SimpleResponse(const char* request_path, const std::string& contents) 219 : ResponseForPath(request_path), contents_(contents) { 220 } 221 222 virtual ~SimpleResponse(); 223 224 virtual void WriteContents(net::StreamListenSocket* socket) const { 225 socket->Send(contents_.c_str(), contents_.length(), false); 226 } 227 228 virtual size_t ContentLength() const { 229 return contents_.length(); 230 } 231 232 protected: 233 std::string contents_; 234 235 private: 236 DISALLOW_COPY_AND_ASSIGN(SimpleResponse); 237}; 238 239// To serve up files from the web server, create an instance of FileResponse 240// and add it to the server's list of responses. The content type of the 241// file will be determined by calling FindMimeFromData which examines the 242// contents of the file and performs registry lookups. 243class FileResponse : public ResponseForPath { 244 public: 245 FileResponse(const char* request_path, const base::FilePath& file_path) 246 : ResponseForPath(request_path), file_path_(file_path) { 247 } 248 249 virtual bool GetContentType(std::string* content_type) const; 250 virtual void WriteContents(net::StreamListenSocket* socket) const; 251 virtual size_t ContentLength() const; 252 253 protected: 254 base::FilePath file_path_; 255 mutable scoped_ptr<base::MemoryMappedFile> file_; 256 257 private: 258 DISALLOW_COPY_AND_ASSIGN(FileResponse); 259}; 260 261// Returns a 302 (temporary redirect) to redirect the client from a path 262// on the test server to a different URL. 263class RedirectResponse : public ResponseForPath { 264 public: 265 RedirectResponse(const char* request_path, const std::string& redirect_url) 266 : ResponseForPath(request_path), redirect_url_(redirect_url) { 267 } 268 269 virtual bool GetCustomHeaders(std::string* headers) const; 270 271 protected: 272 std::string redirect_url_; 273 274 private: 275 DISALLOW_COPY_AND_ASSIGN(RedirectResponse); 276}; 277 278// typedef for a list of connections. Used by SimpleWebServer. 279typedef std::list<Connection*> ConnectionList; 280 281// Implementation of a simple http server. 282// Before creating an instance of the server, make sure the current thread 283// has a message loop. 284class SimpleWebServer : public net::StreamListenSocket::Delegate { 285 public: 286 // Constructs a server listening at the given port on a local IPv4 address. 287 // An address on a NIC is preferred over the loopback address. 288 explicit SimpleWebServer(int port); 289 290 // Constructs a server listening at the given address:port. 291 SimpleWebServer(const std::string& address, int port); 292 virtual ~SimpleWebServer(); 293 294 void AddResponse(Response* response); 295 296 // Ownership of response objects is by default assumed to be outside 297 // of the SimpleWebServer class. 298 // However, if the caller doesn't wish to maintain a list of response objects 299 // but rather let this class hold the only references to those objects, 300 // the caller can call this method to delete the objects as part of 301 // the cleanup process. 302 void DeleteAllResponses(); 303 304 // StreamListenSocket::Delegate overrides. 305 virtual void DidAccept(net::StreamListenSocket* server, 306 net::StreamListenSocket* connection); 307 virtual void DidRead(net::StreamListenSocket* connection, 308 const char* data, 309 int len); 310 virtual void DidClose(net::StreamListenSocket* sock); 311 312 // Returns the host on which the server is listening. This is suitable for 313 // use in URLs for resources served by this instance. 314 const std::string& host() const { 315 return host_; 316 } 317 318 const ConnectionList& connections() const { 319 return connections_; 320 } 321 322 protected: 323 class QuitResponse : public SimpleResponse { 324 public: 325 QuitResponse() 326 : SimpleResponse("/quit", "So long and thanks for all the fish.") { 327 } 328 329 virtual void WriteContents(net::StreamListenSocket* socket) const { 330 SimpleResponse::WriteContents(socket); 331 base::MessageLoop::current()->Quit(); 332 } 333 }; 334 335 Response* FindResponse(const Request& request) const; 336 Connection* FindConnection(const net::StreamListenSocket* socket) const; 337 338 std::string host_; 339 scoped_refptr<net::StreamListenSocket> server_; 340 ConnectionList connections_; 341 std::list<Response*> responses_; 342 QuitResponse quit_; 343 344 private: 345 void Construct(const std::string& address, int port); 346 DISALLOW_COPY_AND_ASSIGN(SimpleWebServer); 347}; 348 349// Simple class holding incoming HTTP request. Can send the HTTP response 350// at different rate - small chunks, on regular interval. 351class ConfigurableConnection : public base::RefCounted<ConfigurableConnection> { 352 public: 353 struct SendOptions { 354 enum Speed { IMMEDIATE, DELAYED, IMMEDIATE_HEADERS_DELAYED_CONTENT }; 355 SendOptions() : speed_(IMMEDIATE), chunk_size_(0), timeout_(0) { } 356 SendOptions(Speed speed, int chunk_size, int64 timeout) 357 : speed_(speed), chunk_size_(chunk_size), timeout_(timeout) { 358 } 359 360 Speed speed_; 361 int chunk_size_; 362 int64 timeout_; 363 }; 364 365 explicit ConfigurableConnection(net::StreamListenSocket* sock) 366 : socket_(sock), 367 cur_pos_(0) {} 368 369 // Send HTTP response with provided |headers| and |content|. Appends 370 // "Context-Length:" header if the |content| is not empty. 371 void Send(const std::string& headers, const std::string& content); 372 373 // Send HTTP response with provided |headers| and |content|. Appends 374 // "Context-Length:" header if the |content| is not empty. 375 // Use the |options| to tweak the network speed behaviour. 376 void SendWithOptions(const std::string& headers, const std::string& content, 377 const SendOptions& options); 378 379 private: 380 friend class HTTPTestServer; 381 // Sends a chunk of the response and queues itself as a task for sending 382 // next chunk of |data_|. 383 void SendChunk(); 384 385 // Closes the connection by releasing this instance's reference on its socket. 386 void Close(); 387 388 scoped_refptr<net::StreamListenSocket> socket_; 389 Request r_; 390 SendOptions options_; 391 std::string data_; 392 int cur_pos_; 393 394 DISALLOW_COPY_AND_ASSIGN(ConfigurableConnection); 395}; 396 397// Simple class used as a base class for mock webserver. 398// Override virtual functions Get and Post and use passed ConfigurableConnection 399// instance to send the response. 400class HTTPTestServer : public net::StreamListenSocket::Delegate { 401 public: 402 HTTPTestServer(int port, const std::wstring& address, 403 base::FilePath root_dir); 404 virtual ~HTTPTestServer(); 405 406 // HTTP GET request is received. Override in derived classes. 407 // |connection| can be used to send the response. 408 virtual void Get(ConfigurableConnection* connection, 409 const std::wstring& path, const Request& r) = 0; 410 411 // HTTP POST request is received. Override in derived classes. 412 // |connection| can be used to send the response 413 virtual void Post(ConfigurableConnection* connection, 414 const std::wstring& path, const Request& r) = 0; 415 416 // Return the appropriate url with the specified path for this server. 417 std::wstring Resolve(const std::wstring& path); 418 419 base::FilePath root_dir() { return root_dir_; } 420 421 protected: 422 int port_; 423 std::wstring address_; 424 base::FilePath root_dir_; 425 426 private: 427 typedef std::list<scoped_refptr<ConfigurableConnection> > ConnectionList; 428 ConnectionList::iterator FindConnection( 429 const net::StreamListenSocket* socket); 430 scoped_refptr<ConfigurableConnection> ConnectionFromSocket( 431 const net::StreamListenSocket* socket); 432 433 // StreamListenSocket::Delegate overrides. 434 virtual void DidAccept(net::StreamListenSocket* server, 435 net::StreamListenSocket* socket); 436 virtual void DidRead(net::StreamListenSocket* socket, 437 const char* data, int len); 438 virtual void DidClose(net::StreamListenSocket* socket); 439 440 scoped_refptr<net::StreamListenSocket> server_; 441 ConnectionList connection_list_; 442 443 DISALLOW_COPY_AND_ASSIGN(HTTPTestServer); 444}; 445 446} // namespace test_server 447 448#endif // CHROME_FRAME_TEST_TEST_SERVER_H_ 449