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