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.
5 #ifndef CHROME_FRAME_TEST_TEST_SERVER_H_
6 #define CHROME_FRAME_TEST_TEST_SERVER_H_
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.
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.
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();
27 // To close the web server, just go to http://<server.host()>:1337/quit.
29 // All Response classes count how many times they have been accessed. Just
30 // call Response::accessed().
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
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"
47 namespace test_server {
51 Request() : content_length_(0) {
54 void ParseHeaders(const std::string& headers);
56 const std::string& method() const {
60 const std::string& path() const {
64 // Returns the argument section of a GET path.
65 // Note: does currently not work for POST request.
66 std::string arguments() const {
68 std::string::size_type pos = path_.find('?');
69 if (pos != std::string::npos)
70 ret = path_.substr(pos + 1);
74 const std::string& headers() const {
78 const std::string& content() const {
82 size_t content_length() const {
83 return content_length_;
86 bool AllContentReceived() const {
87 return method_.length() && content_.size() >= content_length_;
90 void OnDataReceived(const std::string& data);
98 size_t content_length_;
101 DISALLOW_COPY_AND_ASSIGN(Request);
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
110 explicit Connection(scoped_ptr<net::StreamListenSocket> sock)
111 : socket_(sock.Pass()) {
117 bool IsSame(const net::StreamListenSocket* socket) const {
118 return socket_ == socket;
121 const Request& request() const {
129 void OnSocketClosed() {
134 scoped_ptr<net::StreamListenSocket> socket_;
138 DISALLOW_COPY_AND_ASSIGN(Connection);
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.
145 Response() : accessed_(0) {
148 virtual ~Response() {
151 // Returns true if this response object should be used for a given request.
152 virtual bool Matches(const Request& r) const = 0;
154 // Response objects can optionally supply their own HTTP headers, completely
155 // bypassing the default ones.
156 virtual bool GetCustomHeaders(std::string* headers) const {
160 // Optionally provide a content type. Return false if you don't specify
162 virtual bool GetContentType(std::string* content_type) const {
166 virtual size_t ContentLength() const {
170 virtual void WriteContents(net::StreamListenSocket* socket) const {
173 virtual void IncrementAccessCounter() {
177 size_t accessed() const {
185 DISALLOW_COPY_AND_ASSIGN(Response);
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 {
193 explicit ResponseForPath(const char* request_path)
194 : request_path_(request_path) {
197 virtual ~ResponseForPath();
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;
208 std::string request_path_;
211 DISALLOW_COPY_AND_ASSIGN(ResponseForPath);
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 {
219 SimpleResponse(const char* request_path, const std::string& contents)
220 : ResponseForPath(request_path), contents_(contents) {
223 virtual ~SimpleResponse();
225 virtual void WriteContents(net::StreamListenSocket* socket) const {
226 socket->Send(contents_.c_str(), contents_.length(), false);
229 virtual size_t ContentLength() const {
230 return contents_.length();
234 std::string contents_;
237 DISALLOW_COPY_AND_ASSIGN(SimpleResponse);
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 {
246 FileResponse(const char* request_path, const base::FilePath& file_path)
247 : ResponseForPath(request_path), file_path_(file_path) {
250 virtual bool GetContentType(std::string* content_type) const;
251 virtual void WriteContents(net::StreamListenSocket* socket) const;
252 virtual size_t ContentLength() const;
255 base::FilePath file_path_;
256 mutable scoped_ptr<base::MemoryMappedFile> file_;
259 DISALLOW_COPY_AND_ASSIGN(FileResponse);
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 {
266 RedirectResponse(const char* request_path, const std::string& redirect_url)
267 : ResponseForPath(request_path), redirect_url_(redirect_url) {
270 virtual bool GetCustomHeaders(std::string* headers) const;
273 std::string redirect_url_;
276 DISALLOW_COPY_AND_ASSIGN(RedirectResponse);
279 // typedef for a list of connections. Used by SimpleWebServer.
280 typedef std::list<Connection*> ConnectionList;
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 {
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);
291 // Constructs a server listening at the given address:port.
292 SimpleWebServer(const std::string& address, int port);
293 virtual ~SimpleWebServer();
295 void AddResponse(Response* response);
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();
305 // StreamListenSocket::Delegate overrides.
306 virtual void DidAccept(net::StreamListenSocket* server,
307 scoped_ptr<net::StreamListenSocket> connection);
308 virtual void DidRead(net::StreamListenSocket* connection,
311 virtual void DidClose(net::StreamListenSocket* sock);
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 {
319 const ConnectionList& connections() const {
324 class QuitResponse : public SimpleResponse {
327 : SimpleResponse("/quit", "So long and thanks for all the fish.") {
330 virtual void WriteContents(net::StreamListenSocket* socket) const {
331 SimpleResponse::WriteContents(socket);
332 base::MessageLoop::current()->Quit();
336 Response* FindResponse(const Request& request) const;
337 Connection* FindConnection(const net::StreamListenSocket* socket) const;
340 scoped_ptr<net::StreamListenSocket> server_;
341 ConnectionList connections_;
342 std::list<Response*> responses_;
346 void Construct(const std::string& address, int port);
347 DISALLOW_COPY_AND_ASSIGN(SimpleWebServer);
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> {
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) {
366 explicit ConfigurableConnection(scoped_ptr<net::StreamListenSocket> sock)
367 : socket_(sock.Pass()),
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);
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);
381 friend class HTTPTestServer;
382 // Sends a chunk of the response and queues itself as a task for sending
383 // next chunk of |data_|.
386 // Closes the connection by releasing this instance's reference on its socket.
389 scoped_ptr<net::StreamListenSocket> socket_;
391 SendOptions options_;
395 DISALLOW_COPY_AND_ASSIGN(ConfigurableConnection);
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 {
403 HTTPTestServer(int port, const std::wstring& address,
404 base::FilePath root_dir);
405 virtual ~HTTPTestServer();
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;
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;
417 // Return the appropriate url with the specified path for this server.
418 std::wstring Resolve(const std::wstring& path);
420 base::FilePath root_dir() { return root_dir_; }
424 std::wstring address_;
425 base::FilePath root_dir_;
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);
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);
441 scoped_ptr<net::StreamListenSocket> server_;
442 ConnectionList connection_list_;
444 DISALLOW_COPY_AND_ASSIGN(HTTPTestServer);
447 } // namespace test_server
449 #endif // CHROME_FRAME_TEST_TEST_SERVER_H_