OLD | NEW |
| (Empty) |
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_ | |
OLD | NEW |