Home | History | Annotate | Download | only in test
      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