embedded_test_server.cc revision b2df76ea8fec9e32f6f3718986dba0d95315b29c
15870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent// Use of this source code is governed by a BSD-style license that can be
35870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent// found in the LICENSE file.
45870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent
55870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent#include "net/test/embedded_test_server/embedded_test_server.h"
65870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent
75870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent#include "base/bind.h"
85870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent#include "base/run_loop.h"
95870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent#include "base/stl_util.h"
105870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent#include "base/string_util.h"
115870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent#include "base/stringprintf.h"
125870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent#include "net/test/embedded_test_server/http_connection.h"
135870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent#include "net/test/embedded_test_server/http_request.h"
145870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent#include "net/test/embedded_test_server/http_response.h"
155870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent#include "net/tools/fetch/http_listen_socket.h"
165870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent
175870e071aabfbde0d570142acf440ab4dca97e04Eric Laurentnamespace net {
185870e071aabfbde0d570142acf440ab4dca97e04Eric Laurentnamespace test_server {
195870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent
205870e071aabfbde0d570142acf440ab4dca97e04Eric Laurentnamespace {
215870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent
225870e071aabfbde0d570142acf440ab4dca97e04Eric Laurentconst int kPort = 8040;
235870e071aabfbde0d570142acf440ab4dca97e04Eric Laurentconst char kIp[] = "127.0.0.1";
245870e071aabfbde0d570142acf440ab4dca97e04Eric Laurentconst int kRetries = 10;
255870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent
265870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent// Callback to handle requests with default predefined response for requests
275870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent// matching the address |url|.
285870e071aabfbde0d570142acf440ab4dca97e04Eric Laurentscoped_ptr<HttpResponse> HandleDefaultRequest(const GURL& url,
295870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent                                              const HttpResponse& response,
305870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent                                              const HttpRequest& request) {
315870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent  const GURL request_url = url.Resolve(request.relative_url);
325870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent  if (url.path() != request_url.path())
335870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent    return scoped_ptr<HttpResponse>(NULL);
345870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent  return scoped_ptr<HttpResponse>(new HttpResponse(response));
355870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent}
365870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent
375870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent}  // namespace
385870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent
395870e071aabfbde0d570142acf440ab4dca97e04Eric LaurentHttpListenSocket::HttpListenSocket(const SocketDescriptor socket_descriptor,
405870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent                                   StreamListenSocket::Delegate* delegate)
415870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent    : TCPListenSocket(socket_descriptor, delegate) {
425870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent  DCHECK(thread_checker_.CalledOnValidThread());
435870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent}
445870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent
455870e071aabfbde0d570142acf440ab4dca97e04Eric Laurentvoid HttpListenSocket::Listen() {
465870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent  DCHECK(thread_checker_.CalledOnValidThread());
475870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent  TCPListenSocket::Listen();
485870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent}
495870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent
505870e071aabfbde0d570142acf440ab4dca97e04Eric LaurentHttpListenSocket::~HttpListenSocket() {
515870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent  DCHECK(thread_checker_.CalledOnValidThread());
525870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent}
535870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent
545870e071aabfbde0d570142acf440ab4dca97e04Eric LaurentEmbeddedTestServer::EmbeddedTestServer(
555870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent    const scoped_refptr<base::SingleThreadTaskRunner>& io_thread)
565870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent    : io_thread_(io_thread),
575870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent      port_(-1),
585870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent      weak_factory_(this) {
595870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent  DCHECK(io_thread_);
605870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent  DCHECK(thread_checker_.CalledOnValidThread());
615870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent}
625870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent
635870e071aabfbde0d570142acf440ab4dca97e04Eric LaurentEmbeddedTestServer::~EmbeddedTestServer() {
645870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent  DCHECK(thread_checker_.CalledOnValidThread());
655870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent}
665870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent
675870e071aabfbde0d570142acf440ab4dca97e04Eric Laurentbool EmbeddedTestServer::InitializeAndWaitUntilReady() {
685870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent  DCHECK(thread_checker_.CalledOnValidThread());
695870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent
705870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent  base::RunLoop run_loop;
715870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent  if (!io_thread_->PostTaskAndReply(
725870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent          FROM_HERE,
735870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent          base::Bind(&EmbeddedTestServer::InitializeOnIOThread,
745870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent                     base::Unretained(this)),
755870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent          run_loop.QuitClosure())) {
765870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent    return false;
775870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent  }
785870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent  run_loop.Run();
795870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent
805870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent  return Started();
815870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent}
825870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent
835870e071aabfbde0d570142acf440ab4dca97e04Eric Laurentbool EmbeddedTestServer::ShutdownAndWaitUntilComplete() {
845870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent  DCHECK(thread_checker_.CalledOnValidThread());
855870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent
865870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent  base::RunLoop run_loop;
875870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent  if (!io_thread_->PostTaskAndReply(
885870e071aabfbde0d570142acf440ab4dca97e04Eric Laurent          FROM_HERE,
89          base::Bind(&EmbeddedTestServer::ShutdownOnIOThread,
90                     base::Unretained(this)),
91          run_loop.QuitClosure())) {
92    return false;
93  }
94  run_loop.Run();
95
96  return true;
97}
98
99void EmbeddedTestServer::InitializeOnIOThread() {
100  DCHECK(io_thread_->BelongsToCurrentThread());
101  DCHECK(!Started());
102
103  int retries_left = kRetries + 1;
104  int try_port = kPort;
105
106  while (retries_left > 0) {
107    SocketDescriptor socket_descriptor = TCPListenSocket::CreateAndBind(
108        kIp,
109        try_port);
110    if (socket_descriptor != TCPListenSocket::kInvalidSocket) {
111      listen_socket_ = new HttpListenSocket(socket_descriptor, this);
112      listen_socket_->Listen();
113      base_url_ = GURL(base::StringPrintf("http://%s:%d", kIp, try_port));
114      port_ = try_port;
115      break;
116    }
117    retries_left--;
118    try_port++;
119  }
120}
121
122void EmbeddedTestServer::ShutdownOnIOThread() {
123  DCHECK(io_thread_->BelongsToCurrentThread());
124
125  listen_socket_ = NULL;  // Release the listen socket.
126  STLDeleteContainerPairSecondPointers(connections_.begin(),
127                                       connections_.end());
128  connections_.clear();
129}
130
131void EmbeddedTestServer::HandleRequest(HttpConnection* connection,
132                               scoped_ptr<HttpRequest> request) {
133  DCHECK(io_thread_->BelongsToCurrentThread());
134
135  for (size_t i = 0; i < request_handlers_.size(); ++i) {
136    scoped_ptr<HttpResponse> response =
137        request_handlers_[i].Run(*request.get());
138    if (response.get()) {
139      connection->SendResponse(response.Pass());
140      return;
141    }
142  }
143
144  LOG(WARNING) << "Request not handled. Returning 404: "
145               << request->relative_url;
146  scoped_ptr<HttpResponse> not_found_response(new HttpResponse());
147  not_found_response->set_code(NOT_FOUND);
148  connection->SendResponse(not_found_response.Pass());
149
150  // Drop the connection, since we do not support multiple requests per
151  // connection.
152  connections_.erase(connection->socket_.get());
153  delete connection;
154}
155
156GURL EmbeddedTestServer::GetURL(const std::string& relative_url) const {
157  DCHECK(StartsWithASCII(relative_url, "/", true /* case_sensitive */))
158      << relative_url;
159  return base_url_.Resolve(relative_url);
160}
161
162void EmbeddedTestServer::RegisterRequestHandler(
163    const HandleRequestCallback& callback) {
164  request_handlers_.push_back(callback);
165}
166
167void EmbeddedTestServer::DidAccept(StreamListenSocket* server,
168                           StreamListenSocket* connection) {
169  DCHECK(io_thread_->BelongsToCurrentThread());
170
171  HttpConnection* http_connection = new HttpConnection(
172      connection,
173      base::Bind(&EmbeddedTestServer::HandleRequest,
174                 weak_factory_.GetWeakPtr()));
175  connections_[connection] = http_connection;
176}
177
178void EmbeddedTestServer::DidRead(StreamListenSocket* connection,
179                         const char* data,
180                         int length) {
181  DCHECK(io_thread_->BelongsToCurrentThread());
182
183  HttpConnection* http_connection = FindConnection(connection);
184  if (http_connection == NULL) {
185    LOG(WARNING) << "Unknown connection.";
186    return;
187  }
188  http_connection->ReceiveData(std::string(data, length));
189}
190
191void EmbeddedTestServer::DidClose(StreamListenSocket* connection) {
192  DCHECK(io_thread_->BelongsToCurrentThread());
193
194  HttpConnection* http_connection = FindConnection(connection);
195  if (http_connection == NULL) {
196    LOG(WARNING) << "Unknown connection.";
197    return;
198  }
199  delete http_connection;
200  connections_.erase(connection);
201}
202
203HttpConnection* EmbeddedTestServer::FindConnection(
204    StreamListenSocket* socket) {
205  DCHECK(io_thread_->BelongsToCurrentThread());
206
207  std::map<StreamListenSocket*, HttpConnection*>::iterator it =
208      connections_.find(socket);
209  if (it == connections_.end()) {
210    return NULL;
211  }
212  return it->second;
213}
214
215}  // namespace test_server
216}  // namespace net
217