Index: chrome_frame/test/test_server.h |
=================================================================== |
--- chrome_frame/test/test_server.h (revision 0) |
+++ chrome_frame/test/test_server.h (revision 0) |
@@ -0,0 +1,296 @@ |
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#ifndef CHROME_FRAME_TEST_TEST_SERVER_H_ |
+#define CHROME_FRAME_TEST_TEST_SERVER_H_ |
+ |
+// Implementation of an HTTP server for tests. |
+// To instantiate the server, make sure you have a message loop on the |
+// current thread and then create an instance of the SimpleWebServer class. |
+// The server uses two basic concepts, a request and a response. |
+// The Response interface represents an item (e.g. a document) available from |
+// the server. A Request object represents a request from a client (e.g. a |
+// browser). There are several basic Response classes implemented in this file, |
+// all derived from the Response interface. |
+// |
+// Here's a simple example that starts a web server that can serve up |
+// a single document (http://localhost:1337/foo). |
+// All other requests will get a 404. |
+// |
+// MessageLoopForUI loop; |
+// test_server::SimpleWebServer server(1337); |
+// test_server::SimpleResponse document("/foo", "Hello World!"); |
+// test_server.AddResponse(&document); |
+// loop.MessageLoop::Run(); |
+// |
+// To close the web server, just go to http://localhost:1337/quit. |
+// |
+// All Response classes count how many times they have been accessed. Just |
+// call Response::accessed(). |
+// |
+// To implement a custom response object (e.g. to match against a request |
+// based on some data, serve up dynamic content or take some action on the |
+// server), just inherit from one of the response classes or directly from the |
+// Response interface and add your response object to the server's list of |
+// response objects. |
+ |
+#include <list> |
+#include <string> |
+ |
+#include "base/basictypes.h" |
+#include "base/file_util.h" |
+#include "net/base/listen_socket.h" |
+ |
+namespace test_server { |
+ |
+class Request { |
+ public: |
+ Request() : content_length_(0) { |
+ } |
+ |
+ void ParseHeaders(const std::string& headers); |
+ |
+ const std::string& method() const { |
+ return method_; |
+ } |
+ |
+ const std::string& path() const { |
+ return path_; |
+ } |
+ |
+ const std::string& headers() const { |
+ return headers_; |
+ } |
+ |
+ size_t content_length() const { |
+ return content_length_; |
+ } |
+ |
+ protected: |
+ std::string method_; |
+ std::string path_; |
+ std::string version_; |
+ std::string headers_; |
+ size_t content_length_; |
+ |
+ private: |
+ DISALLOW_COPY_AND_ASSIGN(Request); |
+}; |
+ |
+// Manages request headers for a single request. |
+// For each successful request that's made, the server will keep an instance |
+// of this class so that they can be checked even after the server has been |
+// shut down. |
+class Connection { |
+ public: |
+ explicit Connection(ListenSocket* sock) : socket_(sock) { |
+ } |
+ |
+ ~Connection() { |
+ } |
+ |
+ bool IsSame(const ListenSocket* socket) const { |
+ return socket_ == socket; |
+ } |
+ |
+ void AddData(const std::string& data) { |
+ data_ += data; |
+ } |
+ |
+ bool CheckRequestReceived(); |
+ |
+ const Request& request() const { |
+ return request_; |
+ } |
+ |
+ protected: |
+ scoped_refptr<ListenSocket> socket_; |
+ std::string data_; |
+ Request request_; |
+ |
+ private: |
+ DISALLOW_COPY_AND_ASSIGN(Connection); |
+}; |
+ |
+// Abstract interface with default implementations for some of the methods and |
+// a counter for how many times the response object has served requests. |
+class Response { |
+ public: |
+ Response() : accessed_(0) { |
+ } |
+ |
+ virtual ~Response() { |
+ } |
+ |
+ // Returns true if this response object should be used for a given request. |
+ virtual bool Matches(const Request& r) const = 0; |
+ |
+ // Response objects can optionally supply their own HTTP headers, completely |
+ // bypassing the default ones. |
+ virtual bool GetCustomHeaders(std::string* headers) const { |
+ return false; |
+ } |
+ |
+ // Optionally provide a content type. Return false if you don't specify |
+ // a content type. |
+ virtual bool GetContentType(std::string* content_type) const { |
+ return false; |
+ } |
+ |
+ virtual size_t ContentLength() const { |
+ return 0; |
+ } |
+ |
+ virtual void WriteContents(ListenSocket* socket) const { |
+ } |
+ |
+ void IncrementAccessCounter() { |
+ accessed_++; |
+ } |
+ |
+ size_t accessed() const { |
+ return accessed_; |
+ } |
+ |
+ protected: |
+ size_t accessed_; |
+ |
+ private: |
+ DISALLOW_COPY_AND_ASSIGN(Response); |
+}; |
+ |
+// Partial implementation of Response that matches a request's path. |
+// This is just a convenience implementation for the boilerplate implementation |
+// of Matches(). Don't instantiate directly. |
+class ResponseForPath : public Response { |
+ public: |
+ explicit ResponseForPath(const char* request_path) |
+ : request_path_(request_path) { |
+ } |
+ |
+ virtual bool Matches(const Request& r) const { |
+ return r.path().compare(request_path_) == 0; |
+ } |
+ |
+ protected: |
+ std::string request_path_; |
+ |
+ private: |
+ DISALLOW_COPY_AND_ASSIGN(ResponseForPath); |
+}; |
+ |
+// A very basic implementation of a response. |
+// A simple response matches a single document path on the server |
+// (e.g. "/foo") and returns a document in the form of a string. |
+class SimpleResponse : public ResponseForPath { |
+ public: |
+ SimpleResponse(const char* request_path, const std::string& contents) |
+ : ResponseForPath(request_path), contents_(contents) { |
+ } |
+ |
+ virtual void WriteContents(ListenSocket* socket) const { |
+ socket->Send(contents_.c_str(), contents_.length(), false); |
+ } |
+ |
+ virtual size_t ContentLength() const { |
+ return contents_.length(); |
+ } |
+ |
+ protected: |
+ std::string contents_; |
+ |
+ private: |
+ DISALLOW_COPY_AND_ASSIGN(SimpleResponse); |
+}; |
+ |
+// To serve up files from the web server, create an instance of FileResponse |
+// and add it to the server's list of responses. The content type of the |
+// file will be determined by calling FindMimeFromData which examines the |
+// contents of the file and performs registry lookups. |
+class FileResponse : public ResponseForPath { |
+ public: |
+ FileResponse(const char* request_path, const FilePath& file_path) |
+ : ResponseForPath(request_path), file_path_(file_path) { |
+ } |
+ |
+ virtual bool GetContentType(std::string* content_type) const; |
+ virtual void WriteContents(ListenSocket* socket) const; |
+ virtual size_t ContentLength() const; |
+ |
+ protected: |
+ FilePath file_path_; |
+ mutable scoped_ptr<file_util::MemoryMappedFile> file_; |
+ |
+ private: |
+ DISALLOW_COPY_AND_ASSIGN(FileResponse); |
+}; |
+ |
+// Returns a 302 (temporary redirect) to redirect the client from a path |
+// on the test server to a different URL. |
+class RedirectResponse : public ResponseForPath { |
+ public: |
+ RedirectResponse(const char* request_path, const std::string& redirect_url) |
+ : ResponseForPath(request_path), redirect_url_(redirect_url) { |
+ } |
+ |
+ virtual bool GetCustomHeaders(std::string* headers) const; |
+ |
+ protected: |
+ std::string redirect_url_; |
+ |
+ private: |
+ DISALLOW_COPY_AND_ASSIGN(RedirectResponse); |
+}; |
+ |
+// typedef for a list of connections. Used by SimpleWebServer. |
+typedef std::list<Connection*> ConnectionList; |
+ |
+// Implementation of a simple http server. |
+// Before creating an instance of the server, make sure the current thread |
+// has a message loop. |
+class SimpleWebServer : public ListenSocket::ListenSocketDelegate { |
+ public: |
+ explicit SimpleWebServer(int port); |
+ virtual ~SimpleWebServer(); |
+ |
+ void AddResponse(Response* response); |
+ |
+ // ListenSocketDelegate overrides. |
+ virtual void DidAccept(ListenSocket* server, ListenSocket* connection); |
+ virtual void DidRead(ListenSocket* connection, const std::string& data); |
+ virtual void DidClose(ListenSocket* sock); |
+ |
+ const ConnectionList& connections() { |
+ return connections_; |
+ } |
+ |
+ protected: |
+ class QuitResponse : public SimpleResponse { |
+ public: |
+ QuitResponse() |
+ : SimpleResponse("/quit", "So long and thanks for all the fish.") { |
+ } |
+ |
+ virtual void QuitResponse::WriteContents(ListenSocket* socket) const { |
+ SimpleResponse::WriteContents(socket); |
+ MessageLoop::current()->Quit(); |
+ } |
+ }; |
+ |
+ Response* FindResponse(const Request& request) const; |
+ Connection* FindConnection(const ListenSocket* socket) const; |
+ |
+ protected: |
+ scoped_refptr<ListenSocket> server_; |
+ ConnectionList connections_; |
+ std::list<Response*> responses_; |
+ QuitResponse quit_; |
+ |
+ private: |
+ DISALLOW_COPY_AND_ASSIGN(SimpleWebServer); |
+}; |
+ |
+} // namespace test_server |
+ |
+#endif // CHROME_FRAME_TEST_TEST_SERVER_H_ |