OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2006-2008 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://localhost: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://localhost: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/file_util.h" |
| 43 #include "net/base/listen_socket.h" |
| 44 |
| 45 namespace test_server { |
| 46 |
| 47 class Request { |
| 48 public: |
| 49 Request() : content_length_(0) { |
| 50 } |
| 51 |
| 52 void ParseHeaders(const std::string& headers); |
| 53 |
| 54 const std::string& method() const { |
| 55 return method_; |
| 56 } |
| 57 |
| 58 const std::string& path() const { |
| 59 return path_; |
| 60 } |
| 61 |
| 62 const std::string& headers() const { |
| 63 return headers_; |
| 64 } |
| 65 |
| 66 size_t content_length() const { |
| 67 return content_length_; |
| 68 } |
| 69 |
| 70 protected: |
| 71 std::string method_; |
| 72 std::string path_; |
| 73 std::string version_; |
| 74 std::string headers_; |
| 75 size_t content_length_; |
| 76 |
| 77 private: |
| 78 DISALLOW_COPY_AND_ASSIGN(Request); |
| 79 }; |
| 80 |
| 81 // Manages request headers for a single request. |
| 82 // For each successful request that's made, the server will keep an instance |
| 83 // of this class so that they can be checked even after the server has been |
| 84 // shut down. |
| 85 class Connection { |
| 86 public: |
| 87 explicit Connection(ListenSocket* sock) : socket_(sock) { |
| 88 } |
| 89 |
| 90 ~Connection() { |
| 91 } |
| 92 |
| 93 bool IsSame(const ListenSocket* socket) const { |
| 94 return socket_ == socket; |
| 95 } |
| 96 |
| 97 void AddData(const std::string& data) { |
| 98 data_ += data; |
| 99 } |
| 100 |
| 101 bool CheckRequestReceived(); |
| 102 |
| 103 const Request& request() const { |
| 104 return request_; |
| 105 } |
| 106 |
| 107 protected: |
| 108 scoped_refptr<ListenSocket> socket_; |
| 109 std::string data_; |
| 110 Request request_; |
| 111 |
| 112 private: |
| 113 DISALLOW_COPY_AND_ASSIGN(Connection); |
| 114 }; |
| 115 |
| 116 // Abstract interface with default implementations for some of the methods and |
| 117 // a counter for how many times the response object has served requests. |
| 118 class Response { |
| 119 public: |
| 120 Response() : accessed_(0) { |
| 121 } |
| 122 |
| 123 virtual ~Response() { |
| 124 } |
| 125 |
| 126 // Returns true if this response object should be used for a given request. |
| 127 virtual bool Matches(const Request& r) const = 0; |
| 128 |
| 129 // Response objects can optionally supply their own HTTP headers, completely |
| 130 // bypassing the default ones. |
| 131 virtual bool GetCustomHeaders(std::string* headers) const { |
| 132 return false; |
| 133 } |
| 134 |
| 135 // Optionally provide a content type. Return false if you don't specify |
| 136 // a content type. |
| 137 virtual bool GetContentType(std::string* content_type) const { |
| 138 return false; |
| 139 } |
| 140 |
| 141 virtual size_t ContentLength() const { |
| 142 return 0; |
| 143 } |
| 144 |
| 145 virtual void WriteContents(ListenSocket* socket) const { |
| 146 } |
| 147 |
| 148 void IncrementAccessCounter() { |
| 149 accessed_++; |
| 150 } |
| 151 |
| 152 size_t accessed() const { |
| 153 return accessed_; |
| 154 } |
| 155 |
| 156 protected: |
| 157 size_t accessed_; |
| 158 |
| 159 private: |
| 160 DISALLOW_COPY_AND_ASSIGN(Response); |
| 161 }; |
| 162 |
| 163 // Partial implementation of Response that matches a request's path. |
| 164 // This is just a convenience implementation for the boilerplate implementation |
| 165 // of Matches(). Don't instantiate directly. |
| 166 class ResponseForPath : public Response { |
| 167 public: |
| 168 explicit ResponseForPath(const char* request_path) |
| 169 : request_path_(request_path) { |
| 170 } |
| 171 |
| 172 virtual bool Matches(const Request& r) const { |
| 173 return r.path().compare(request_path_) == 0; |
| 174 } |
| 175 |
| 176 protected: |
| 177 std::string request_path_; |
| 178 |
| 179 private: |
| 180 DISALLOW_COPY_AND_ASSIGN(ResponseForPath); |
| 181 }; |
| 182 |
| 183 // A very basic implementation of a response. |
| 184 // A simple response matches a single document path on the server |
| 185 // (e.g. "/foo") and returns a document in the form of a string. |
| 186 class SimpleResponse : public ResponseForPath { |
| 187 public: |
| 188 SimpleResponse(const char* request_path, const std::string& contents) |
| 189 : ResponseForPath(request_path), contents_(contents) { |
| 190 } |
| 191 |
| 192 virtual void WriteContents(ListenSocket* socket) const { |
| 193 socket->Send(contents_.c_str(), contents_.length(), false); |
| 194 } |
| 195 |
| 196 virtual size_t ContentLength() const { |
| 197 return contents_.length(); |
| 198 } |
| 199 |
| 200 protected: |
| 201 std::string contents_; |
| 202 |
| 203 private: |
| 204 DISALLOW_COPY_AND_ASSIGN(SimpleResponse); |
| 205 }; |
| 206 |
| 207 // To serve up files from the web server, create an instance of FileResponse |
| 208 // and add it to the server's list of responses. The content type of the |
| 209 // file will be determined by calling FindMimeFromData which examines the |
| 210 // contents of the file and performs registry lookups. |
| 211 class FileResponse : public ResponseForPath { |
| 212 public: |
| 213 FileResponse(const char* request_path, const FilePath& file_path) |
| 214 : ResponseForPath(request_path), file_path_(file_path) { |
| 215 } |
| 216 |
| 217 virtual bool GetContentType(std::string* content_type) const; |
| 218 virtual void WriteContents(ListenSocket* socket) const; |
| 219 virtual size_t ContentLength() const; |
| 220 |
| 221 protected: |
| 222 FilePath file_path_; |
| 223 mutable scoped_ptr<file_util::MemoryMappedFile> file_; |
| 224 |
| 225 private: |
| 226 DISALLOW_COPY_AND_ASSIGN(FileResponse); |
| 227 }; |
| 228 |
| 229 // Returns a 302 (temporary redirect) to redirect the client from a path |
| 230 // on the test server to a different URL. |
| 231 class RedirectResponse : public ResponseForPath { |
| 232 public: |
| 233 RedirectResponse(const char* request_path, const std::string& redirect_url) |
| 234 : ResponseForPath(request_path), redirect_url_(redirect_url) { |
| 235 } |
| 236 |
| 237 virtual bool GetCustomHeaders(std::string* headers) const; |
| 238 |
| 239 protected: |
| 240 std::string redirect_url_; |
| 241 |
| 242 private: |
| 243 DISALLOW_COPY_AND_ASSIGN(RedirectResponse); |
| 244 }; |
| 245 |
| 246 // typedef for a list of connections. Used by SimpleWebServer. |
| 247 typedef std::list<Connection*> ConnectionList; |
| 248 |
| 249 // Implementation of a simple http server. |
| 250 // Before creating an instance of the server, make sure the current thread |
| 251 // has a message loop. |
| 252 class SimpleWebServer : public ListenSocket::ListenSocketDelegate { |
| 253 public: |
| 254 explicit SimpleWebServer(int port); |
| 255 virtual ~SimpleWebServer(); |
| 256 |
| 257 void AddResponse(Response* response); |
| 258 |
| 259 // ListenSocketDelegate overrides. |
| 260 virtual void DidAccept(ListenSocket* server, ListenSocket* connection); |
| 261 virtual void DidRead(ListenSocket* connection, const std::string& data); |
| 262 virtual void DidClose(ListenSocket* sock); |
| 263 |
| 264 const ConnectionList& connections() { |
| 265 return connections_; |
| 266 } |
| 267 |
| 268 protected: |
| 269 class QuitResponse : public SimpleResponse { |
| 270 public: |
| 271 QuitResponse() |
| 272 : SimpleResponse("/quit", "So long and thanks for all the fish.") { |
| 273 } |
| 274 |
| 275 virtual void QuitResponse::WriteContents(ListenSocket* socket) const { |
| 276 SimpleResponse::WriteContents(socket); |
| 277 MessageLoop::current()->Quit(); |
| 278 } |
| 279 }; |
| 280 |
| 281 Response* FindResponse(const Request& request) const; |
| 282 Connection* FindConnection(const ListenSocket* socket) const; |
| 283 |
| 284 protected: |
| 285 scoped_refptr<ListenSocket> server_; |
| 286 ConnectionList connections_; |
| 287 std::list<Response*> responses_; |
| 288 QuitResponse quit_; |
| 289 |
| 290 private: |
| 291 DISALLOW_COPY_AND_ASSIGN(SimpleWebServer); |
| 292 }; |
| 293 |
| 294 } // namespace test_server |
| 295 |
| 296 #endif // CHROME_FRAME_TEST_TEST_SERVER_H_ |
OLD | NEW |