// Copyright (c) 2012 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 NET_TEST_EMBEDDED_TEST_SERVER_EMBEDDED_TEST_SERVER_H_
#define NET_TEST_EMBEDDED_TEST_SERVER_EMBEDDED_TEST_SERVER_H_

#include <stdint.h>

#include <map>
#include <memory>
#include <string>
#include <vector>

#include "base/callback.h"
#include "base/compiler_specific.h"
#include "base/files/file_path.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/threading/thread.h"
#include "base/threading/thread_checker.h"
#include "crypto/rsa_private_key.h"
#include "net/base/address_list.h"
#include "net/base/host_port_pair.h"
#include "net/base/ip_endpoint.h"
#include "net/cert/x509_certificate.h"
#include "net/socket/ssl_server_socket.h"
#include "net/socket/stream_socket.h"
#include "net/socket/tcp_server_socket.h"
#include "net/ssl/ssl_server_config.h"
#include "url/gurl.h"

namespace net {

class StreamSocket;
class TCPServerSocket;

namespace test_server {

class EmbeddedTestServerConnectionListener;
class HttpConnection;
class HttpResponse;
struct HttpRequest;

// Class providing an HTTP server for testing purpose. This is a basic server
// providing only an essential subset of HTTP/1.1 protocol. Especially,
// it assumes that the request syntax is correct. It *does not* support
// a Chunked Transfer Encoding.
//
// The common use case for unit tests is below:
//
// void SetUp() {
//   test_server_.reset(new EmbeddedTestServer());
//   ASSERT_TRUE(test_server_.Start());
//   test_server_->RegisterRequestHandler(
//       base::Bind(&FooTest::HandleRequest, base::Unretained(this)));
// }
//
// std::unique_ptr<HttpResponse> HandleRequest(const HttpRequest& request) {
//   GURL absolute_url = test_server_->GetURL(request.relative_url);
//   if (absolute_url.path() != "/test")
//     return std::unique_ptr<HttpResponse>();
//
//   std::unique_ptr<BasicHttpResponse> http_response(new BasicHttpResponse());
//   http_response->set_code(test_server::SUCCESS);
//   http_response->set_content("hello");
//   http_response->set_content_type("text/plain");
//   return http_response;
// }
//
// For a test that spawns another process such as browser_tests, it is
// suggested to call Start in SetUpOnMainThread after the process is spawned.
//  If you have to do it before the process spawns, you need to first setup the
// listen socket so that there is no no other threads running while spawning
// the process. To do so, please follow the following example:
//
// void SetUp() {
//   ASSERT_TRUE(embedded_test_server()->InitializeAndListen());
//   ...
//   InProcessBrowserTest::SetUp();
// }
//
// void SetUpOnMainThread() {
//   // Starts the accept IO thread.
//   embedded_test_server()->StartAcceptingConnections();
// }
//
class EmbeddedTestServer {
 public:
  enum Type {
    TYPE_HTTP,
    TYPE_HTTPS,
  };

  enum ServerCertificate {
    CERT_OK,

    CERT_MISMATCHED_NAME,
    CERT_EXPIRED,

    // A certificate with invalid notBefore and notAfter times. Windows'
    // certificate library will not parse this certificate.
    CERT_BAD_VALIDITY,

    // Cross-signed certificate to test PKIX path building. Contains an
    // intermediate cross-signed by an unknown root, while the client (via
    // TestRootStore) is expected to have a self-signed version of the
    // intermediate.
    CERT_CHAIN_WRONG_ROOT,

    // Causes the testserver to use a hostname that is a domain
    // instead of an IP.
    CERT_COMMON_NAME_IS_DOMAIN,
  };

  typedef base::Callback<std::unique_ptr<HttpResponse>(
      const HttpRequest& request)>
      HandleRequestCallback;
  typedef base::Callback<void(const HttpRequest& request)>
      MonitorRequestCallback;

  // Creates a http test server. Start() must be called to start the server.
  // |type| indicates the protocol type of the server (HTTP/HTTPS).
  EmbeddedTestServer();
  explicit EmbeddedTestServer(Type type);
  ~EmbeddedTestServer();

  // Sets a connection listener, that would be notified when various connection
  // events happen. May only be called before the server is started. Caller
  // maintains ownership of the listener.
  void SetConnectionListener(EmbeddedTestServerConnectionListener* listener);

  // Initializes and waits until the server is ready to accept requests.
  // This is the equivalent of calling InitializeAndListen() followed by
  // StartAcceptingConnections().
  // Returns whether a listening socket has been successfully created.
  bool Start() WARN_UNUSED_RESULT;

  // Starts listening for incoming connections but will not yet accept them.
  // Returns whether a listening socket has been succesfully created.
  bool InitializeAndListen() WARN_UNUSED_RESULT;

  // Starts the Accept IO Thread and begins accepting connections.
  void StartAcceptingConnections();

  // Shuts down the http server and waits until the shutdown is complete.
  bool ShutdownAndWaitUntilComplete() WARN_UNUSED_RESULT;

  // Checks if the server has started listening for incoming connections.
  bool Started() const {
    return listen_socket_.get() != NULL;
  }

  HostPortPair host_port_pair() const {
    return HostPortPair::FromURL(base_url_);
  }

  // Returns the base URL to the server, which looks like
  // http://127.0.0.1:<port>/, where <port> is the actual port number used by
  // the server.
  const GURL& base_url() const { return base_url_; }

  // Returns a URL to the server based on the given relative URL, which
  // should start with '/'. For example: GetURL("/path?query=foo") =>
  // http://127.0.0.1:<port>/path?query=foo.
  GURL GetURL(const std::string& relative_url) const;

  // Similar to the above method with the difference that it uses the supplied
  // |hostname| for the URL instead of 127.0.0.1. The hostname should be
  // resolved to 127.0.0.1.
  GURL GetURL(const std::string& hostname,
              const std::string& relative_url) const;

  // Returns the address list needed to connect to the server.
  bool GetAddressList(AddressList* address_list) const WARN_UNUSED_RESULT;

  // Returns the port number used by the server.
  uint16_t port() const { return port_; }

  void SetSSLConfig(ServerCertificate cert, const SSLServerConfig& ssl_config);
  void SetSSLConfig(ServerCertificate cert);

  // Returns the file name of the certificate the server is using. The test
  // certificates can be found in net/data/ssl/certificates/.
  std::string GetCertificateName() const;

  // Returns the certificate that the server is using.
  scoped_refptr<X509Certificate> GetCertificate() const;

  // Registers request handler which serves files from |directory|.
  // For instance, a request to "/foo.html" is served by "foo.html" under
  // |directory|. Files under sub directories are also handled in the same way
  // (i.e. "/foo/bar.html" is served by "foo/bar.html" under |directory|).
  // TODO(svaldez): Merge ServeFilesFromDirectory and
  // ServeFilesFromSourceDirectory.
  void ServeFilesFromDirectory(const base::FilePath& directory);

  // Serves files relative to DIR_SOURCE_ROOT.
  void ServeFilesFromSourceDirectory(const std::string& relative);
  void ServeFilesFromSourceDirectory(const base::FilePath& relative);

  // Registers the default handlers and serve additional files from the
  // |directory| directory, relative to DIR_SOURCE_ROOT.
  void AddDefaultHandlers(const base::FilePath& directory);

  // The most general purpose method. Any request processing can be added using
  // this method. Takes ownership of the object. The |callback| is called
  // on UI thread.
  void RegisterRequestHandler(const HandleRequestCallback& callback);

  // Adds request monitors. The |callback| is called before any handlers are
  // called, but can not respond it. This is useful to monitor requests that
  // will be handled by other request handlers.
  void RegisterRequestMonitor(const MonitorRequestCallback& callback);

  // Adds default handlers, including those added by AddDefaultHandlers, to be
  // tried after all other user-specified handlers have been tried.
  void RegisterDefaultHandler(const HandleRequestCallback& callback);

  bool FlushAllSocketsAndConnectionsOnUIThread();
  void FlushAllSocketsAndConnections();

 private:
  // Shuts down the server.
  void ShutdownOnIOThread();

  // Upgrade the TCP connection to one over SSL.
  std::unique_ptr<StreamSocket> DoSSLUpgrade(
      std::unique_ptr<StreamSocket> connection);
  // Handles async callback when the SSL handshake has been completed.
  void OnHandshakeDone(HttpConnection* connection, int rv);

  // Begins accepting new client connections.
  void DoAcceptLoop();
  // Handles async callback when there is a new client socket. |rv| is the
  // return value of the socket Accept.
  void OnAcceptCompleted(int rv);
  // Adds the new |socket| to the list of clients and begins the reading
  // data.
  void HandleAcceptResult(std::unique_ptr<StreamSocket> socket);

  // Attempts to read data from the |connection|'s socket.
  void ReadData(HttpConnection* connection);
  // Handles async callback when new data has been read from the |connection|.
  void OnReadCompleted(HttpConnection* connection, int rv);
  // Parses the data read from the |connection| and returns true if the entire
  // request has been received.
  bool HandleReadResult(HttpConnection* connection, int rv);

  // Closes and removes the connection upon error or completion.
  void DidClose(HttpConnection* connection);

  // Handles a request when it is parsed. It passes the request to registered
  // request handlers and sends a http response.
  void HandleRequest(HttpConnection* connection,
                     std::unique_ptr<HttpRequest> request);

  // Initializes the SSLServerContext so that SSLServerSocket connections may
  // share the same cache
  void InitializeSSLServerContext();

  HttpConnection* FindConnection(StreamSocket* socket);

  // Posts a task to the |io_thread_| and waits for a reply.
  bool PostTaskToIOThreadAndWait(
      const base::Closure& closure) WARN_UNUSED_RESULT;

  const bool is_using_ssl_;

  std::unique_ptr<base::Thread> io_thread_;

  std::unique_ptr<TCPServerSocket> listen_socket_;
  std::unique_ptr<StreamSocket> accepted_socket_;

  EmbeddedTestServerConnectionListener* connection_listener_;
  uint16_t port_;
  GURL base_url_;
  IPEndPoint local_endpoint_;

  // Owns the HttpConnection objects.
  std::map<StreamSocket*, HttpConnection*> connections_;

  // Vector of registered and default request handlers and monitors.
  std::vector<HandleRequestCallback> request_handlers_;
  std::vector<MonitorRequestCallback> request_monitors_;
  std::vector<HandleRequestCallback> default_request_handlers_;

  base::ThreadChecker thread_checker_;

  net::SSLServerConfig ssl_config_;
  ServerCertificate cert_;
  std::unique_ptr<SSLServerContext> context_;

  base::WeakPtrFactory<EmbeddedTestServer> weak_factory_;

  DISALLOW_COPY_AND_ASSIGN(EmbeddedTestServer);
};

}  // namespace test_server

// TODO(svaldez): Refactor EmbeddedTestServer to be in the net namespace.
using test_server::EmbeddedTestServer;

}  // namespace net

#endif  // NET_TEST_EMBEDDED_TEST_SERVER_EMBEDDED_TEST_SERVER_H_
