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 47 namespace test_server { 48 49 class 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. 108 class Connection { 109 public: 110 explicit Connection(scoped_ptr<net::StreamListenSocket> sock) 111 : socket_(sock.Pass()) { 112 } 113 114 ~Connection() { 115 } 116 117 bool IsSame(const net::StreamListenSocket* socket) const { 118 return socket_ == socket; 119 } 120 121 const Request& request() const { 122 return request_; 123 } 124 125 Request& request() { 126 return request_; 127 } 128 129 void OnSocketClosed() { 130 socket_.reset(); 131 } 132 133 protected: 134 scoped_ptr<net::StreamListenSocket> socket_; 135 Request request_; 136 137 private: 138 DISALLOW_COPY_AND_ASSIGN(Connection); 139 }; 140 141 // Abstract interface with default implementations for some of the methods and 142 // a counter for how many times the response object has served requests. 143 class Response { 144 public: 145 Response() : accessed_(0) { 146 } 147 148 virtual ~Response() { 149 } 150 151 // Returns true if this response object should be used for a given request. 152 virtual bool Matches(const Request& r) const = 0; 153 154 // Response objects can optionally supply their own HTTP headers, completely 155 // bypassing the default ones. 156 virtual bool GetCustomHeaders(std::string* headers) const { 157 return false; 158 } 159 160 // Optionally provide a content type. Return false if you don't specify 161 // a content type. 162 virtual bool GetContentType(std::string* content_type) const { 163 return false; 164 } 165 166 virtual size_t ContentLength() const { 167 return 0; 168 } 169 170 virtual void WriteContents(net::StreamListenSocket* socket) const { 171 } 172 173 virtual void IncrementAccessCounter() { 174 accessed_++; 175 } 176 177 size_t accessed() const { 178 return accessed_; 179 } 180 181 protected: 182 size_t accessed_; 183 184 private: 185 DISALLOW_COPY_AND_ASSIGN(Response); 186 }; 187 188 // Partial implementation of Response that matches a request's path. 189 // This is just a convenience implementation for the boilerplate implementation 190 // of Matches(). Don't instantiate directly. 191 class ResponseForPath : public Response { 192 public: 193 explicit ResponseForPath(const char* request_path) 194 : request_path_(request_path) { 195 } 196 197 virtual ~ResponseForPath(); 198 199 virtual bool Matches(const Request& r) const { 200 std::string path = r.path(); 201 std::string::size_type pos = path.find('?'); 202 if (pos != std::string::npos) 203 path = path.substr(0, pos); 204 return path.compare(request_path_) == 0; 205 } 206 207 protected: 208 std::string request_path_; 209 210 private: 211 DISALLOW_COPY_AND_ASSIGN(ResponseForPath); 212 }; 213 214 // A very basic implementation of a response. 215 // A simple response matches a single document path on the server 216 // (e.g. "/foo") and returns a document in the form of a string. 217 class SimpleResponse : public ResponseForPath { 218 public: 219 SimpleResponse(const char* request_path, const std::string& contents) 220 : ResponseForPath(request_path), contents_(contents) { 221 } 222 223 virtual ~SimpleResponse(); 224 225 virtual void WriteContents(net::StreamListenSocket* socket) const { 226 socket->Send(contents_.c_str(), contents_.length(), false); 227 } 228 229 virtual size_t ContentLength() const { 230 return contents_.length(); 231 } 232 233 protected: 234 std::string contents_; 235 236 private: 237 DISALLOW_COPY_AND_ASSIGN(SimpleResponse); 238 }; 239 240 // To serve up files from the web server, create an instance of FileResponse 241 // and add it to the server's list of responses. The content type of the 242 // file will be determined by calling FindMimeFromData which examines the 243 // contents of the file and performs registry lookups. 244 class FileResponse : public ResponseForPath { 245 public: 246 FileResponse(const char* request_path, const base::FilePath& file_path) 247 : ResponseForPath(request_path), file_path_(file_path) { 248 } 249 250 virtual bool GetContentType(std::string* content_type) const; 251 virtual void WriteContents(net::StreamListenSocket* socket) const; 252 virtual size_t ContentLength() const; 253 254 protected: 255 base::FilePath file_path_; 256 mutable scoped_ptr<base::MemoryMappedFile> file_; 257 258 private: 259 DISALLOW_COPY_AND_ASSIGN(FileResponse); 260 }; 261 262 // Returns a 302 (temporary redirect) to redirect the client from a path 263 // on the test server to a different URL. 264 class RedirectResponse : public ResponseForPath { 265 public: 266 RedirectResponse(const char* request_path, const std::string& redirect_url) 267 : ResponseForPath(request_path), redirect_url_(redirect_url) { 268 } 269 270 virtual bool GetCustomHeaders(std::string* headers) const; 271 272 protected: 273 std::string redirect_url_; 274 275 private: 276 DISALLOW_COPY_AND_ASSIGN(RedirectResponse); 277 }; 278 279 // typedef for a list of connections. Used by SimpleWebServer. 280 typedef std::list<Connection*> ConnectionList; 281 282 // Implementation of a simple http server. 283 // Before creating an instance of the server, make sure the current thread 284 // has a message loop. 285 class SimpleWebServer : public net::StreamListenSocket::Delegate { 286 public: 287 // Constructs a server listening at the given port on a local IPv4 address. 288 // An address on a NIC is preferred over the loopback address. 289 explicit SimpleWebServer(int port); 290 291 // Constructs a server listening at the given address:port. 292 SimpleWebServer(const std::string& address, int port); 293 virtual ~SimpleWebServer(); 294 295 void AddResponse(Response* response); 296 297 // Ownership of response objects is by default assumed to be outside 298 // of the SimpleWebServer class. 299 // However, if the caller doesn't wish to maintain a list of response objects 300 // but rather let this class hold the only references to those objects, 301 // the caller can call this method to delete the objects as part of 302 // the cleanup process. 303 void DeleteAllResponses(); 304 305 // StreamListenSocket::Delegate overrides. 306 virtual void DidAccept(net::StreamListenSocket* server, 307 scoped_ptr<net::StreamListenSocket> connection); 308 virtual void DidRead(net::StreamListenSocket* connection, 309 const char* data, 310 int len); 311 virtual void DidClose(net::StreamListenSocket* sock); 312 313 // Returns the host on which the server is listening. This is suitable for 314 // use in URLs for resources served by this instance. 315 const std::string& host() const { 316 return host_; 317 } 318 319 const ConnectionList& connections() const { 320 return connections_; 321 } 322 323 protected: 324 class QuitResponse : public SimpleResponse { 325 public: 326 QuitResponse() 327 : SimpleResponse("/quit", "So long and thanks for all the fish.") { 328 } 329 330 virtual void WriteContents(net::StreamListenSocket* socket) const { 331 SimpleResponse::WriteContents(socket); 332 base::MessageLoop::current()->Quit(); 333 } 334 }; 335 336 Response* FindResponse(const Request& request) const; 337 Connection* FindConnection(const net::StreamListenSocket* socket) const; 338 339 std::string host_; 340 scoped_ptr<net::StreamListenSocket> server_; 341 ConnectionList connections_; 342 std::list<Response*> responses_; 343 QuitResponse quit_; 344 345 private: 346 void Construct(const std::string& address, int port); 347 DISALLOW_COPY_AND_ASSIGN(SimpleWebServer); 348 }; 349 350 // Simple class holding incoming HTTP request. Can send the HTTP response 351 // at different rate - small chunks, on regular interval. 352 class ConfigurableConnection : public base::RefCounted<ConfigurableConnection> { 353 public: 354 struct SendOptions { 355 enum Speed { IMMEDIATE, DELAYED, IMMEDIATE_HEADERS_DELAYED_CONTENT }; 356 SendOptions() : speed_(IMMEDIATE), chunk_size_(0), timeout_(0) { } 357 SendOptions(Speed speed, int chunk_size, int64 timeout) 358 : speed_(speed), chunk_size_(chunk_size), timeout_(timeout) { 359 } 360 361 Speed speed_; 362 int chunk_size_; 363 int64 timeout_; 364 }; 365 366 explicit ConfigurableConnection(scoped_ptr<net::StreamListenSocket> sock) 367 : socket_(sock.Pass()), 368 cur_pos_(0) {} 369 370 // Send HTTP response with provided |headers| and |content|. Appends 371 // "Context-Length:" header if the |content| is not empty. 372 void Send(const std::string& headers, const std::string& content); 373 374 // Send HTTP response with provided |headers| and |content|. Appends 375 // "Context-Length:" header if the |content| is not empty. 376 // Use the |options| to tweak the network speed behaviour. 377 void SendWithOptions(const std::string& headers, const std::string& content, 378 const SendOptions& options); 379 380 private: 381 friend class HTTPTestServer; 382 // Sends a chunk of the response and queues itself as a task for sending 383 // next chunk of |data_|. 384 void SendChunk(); 385 386 // Closes the connection by releasing this instance's reference on its socket. 387 void Close(); 388 389 scoped_ptr<net::StreamListenSocket> socket_; 390 Request r_; 391 SendOptions options_; 392 std::string data_; 393 int cur_pos_; 394 395 DISALLOW_COPY_AND_ASSIGN(ConfigurableConnection); 396 }; 397 398 // Simple class used as a base class for mock webserver. 399 // Override virtual functions Get and Post and use passed ConfigurableConnection 400 // instance to send the response. 401 class HTTPTestServer : public net::StreamListenSocket::Delegate { 402 public: 403 HTTPTestServer(int port, const std::wstring& address, 404 base::FilePath root_dir); 405 virtual ~HTTPTestServer(); 406 407 // HTTP GET request is received. Override in derived classes. 408 // |connection| can be used to send the response. 409 virtual void Get(ConfigurableConnection* connection, 410 const std::wstring& path, const Request& r) = 0; 411 412 // HTTP POST request is received. Override in derived classes. 413 // |connection| can be used to send the response 414 virtual void Post(ConfigurableConnection* connection, 415 const std::wstring& path, const Request& r) = 0; 416 417 // Return the appropriate url with the specified path for this server. 418 std::wstring Resolve(const std::wstring& path); 419 420 base::FilePath root_dir() { return root_dir_; } 421 422 protected: 423 int port_; 424 std::wstring address_; 425 base::FilePath root_dir_; 426 427 private: 428 typedef std::list<scoped_refptr<ConfigurableConnection> > ConnectionList; 429 ConnectionList::iterator FindConnection( 430 const net::StreamListenSocket* socket); 431 scoped_refptr<ConfigurableConnection> ConnectionFromSocket( 432 const net::StreamListenSocket* socket); 433 434 // StreamListenSocket::Delegate overrides. 435 virtual void DidAccept(net::StreamListenSocket* server, 436 scoped_ptr<net::StreamListenSocket> socket); 437 virtual void DidRead(net::StreamListenSocket* socket, 438 const char* data, int len); 439 virtual void DidClose(net::StreamListenSocket* socket); 440 441 scoped_ptr<net::StreamListenSocket> server_; 442 ConnectionList connection_list_; 443 444 DISALLOW_COPY_AND_ASSIGN(HTTPTestServer); 445 }; 446 447 } // namespace test_server 448 449 #endif // CHROME_FRAME_TEST_TEST_SERVER_H_ 450