// This may look like C code, but it's really -*- C++ -*-
/*
 * Copyright (C) 2008 Emweb bv, Herent, Belgium.
 *
 * See the LICENSE file for terms of use.
 */
#ifndef WSERVER_H_
#define WSERVER_H_

#include <Wt/WApplication.h>
#include <Wt/WException.h>
#include <Wt/WLogger.h>

#include <chrono>
#include <shared_mutex>

namespace http {
  namespace server {
    class Server;
  }
}
namespace Wt {

class Configuration;
class WebController;
class WIOService;
#ifndef WT_TARGET_JAVA
class WWebSocketResource;
#endif // WT_TARGET_JAVA

#ifdef WT_CNOR
class ServletContext
{
  std::string getRealPath(std::string);
};
#endif //WT_CNOR
/*! \class WServer Wt/WServer.h Wt/WServer.h
 *  \brief A class encapsulating a web application server.
 *
 * This server class represents an instance of an application server.
 *
 * It offers support for multiple application entry points and control
 * over starting and stopping the server. This may be used as an
 * alternative to using WRun() when you wish to support multiple
 * application entry points, or for integrating a %Wt (stand-alone
 * httpd) server application into an existing application, with control
 * over starting and stopping the server as appropriate.
 *
 * As an example usage, consider the implementation of WRun(), which
 * starts the server until a Ctrl-C is pressed or a termination signal
 * has been received, or a restart is indicated using SIGHUP or a changed
 * binary (argv[0]):
 *
 * \code
int WRun(int argc, char *argv[], ApplicationCreator createApplication)
{
  try {
    // use argv[0] as the application name to match a suitable entry
    // in the Wt configuration file, and use the default configuration
    // file (which defaults to /etc/wt/wt_config.xml unless the environment
    // variable WT_CONFIG_XML is set)
    WServer server(argv[0]);

    // WTHTTP_CONFIGURATION is e.g. "/etc/wt/wthttpd"
    server.setServerConfiguration(argc, argv, WTHTTP_CONFIGURATION);

    // add a single entry point, at the default location (as determined
    // by the server configuration's deploy-path)
    server.addEntryPoint(Wt::EntryPointType::Application, createApplication);
    if (server.start()) {
      int sig = WServer::waitForShutdown();

      std::cerr << "Shutdown (signal = " << sig << ")" << std::endl;
      server.stop();

      if (sig == SIGHUP)
        WServer::restart(argc, argv, environ);
    }
  } catch (WServer::Exception& e) {
    std::cerr << e.what() << "\n";
    return 1;
  } catch (std::exception& e) {
    std::cerr << "exception: " << e.what() << "\n";
    return 1;
  }
}
 * \endcode
 */
#ifdef WT_WIN32
class WServer
#else // WT_WIN32
class WTCONNECTOR_API WServer
#endif // WT_WIN32
{
public:
  typedef std::function<std::string (std::size_t max_length, int purpose)>
    SslPasswordCallback;

#ifdef WT_CNOR
  ServletContext getServletContext();
#endif //WT_CNOR

  /*! \class Exception
   *  \brief Server %Exception class.
   */
  class WT_API Exception : public WException
  {
  public:
    Exception(const std::string& what);
  };

#ifndef WT_TARGET_JAVA
  /*! \class SessionInfo
   *
   * \brief Contains the information for one session.
   */
  struct WT_API SessionInfo
  {
    int64_t     processId; //!< The process id of the process the session is running in.
    std::string sessionId; //!< The session id.
  };
#endif // WT_TARGET_JAVA

  /*! \brief Creates a new server instance.
   *
   * The \p wtApplicationPath is used to match specific
   * application-settings in the %Wt configuration file. If no
   * specific match could be found, the general settings are used
   * (corresponding to the '*' selector).
   *
   * The %Wt application configuration is read from the
   * \p wtConfigurationFile. If empty, this defaults to the value
   * configured at build time.
   *
   * For more information on configuring %Wt applications, see \ref
   * configuration_sec "Configuration".
   *
   * \throws Exception : indicates a configuration problem.
   *
   * \sa setServerConfiguration()
   */
  WTCONNECTOR_API
    WServer(const std::string& wtApplicationPath = std::string(),
            const std::string& wtConfigurationFile = std::string());

  WServer(const WServer &) = delete;

  /*! \brief Creates a new server instance and configures it.
   *
   * This is equivalent to:
   * \code
    WServer server(argv[0]);
    server.setServerConfiguration(argc, argv, wtConfigurationFile);
    \endcode
   *
   * \throws Exception : indicates a configuration problem.
   *
   * \sa WServer(const std::string&, const std::string&)
   * \sa setServerConfiguration()
   */
  WTCONNECTOR_API
    WServer(int argc, char *argv[], const std::string& wtConfigurationFile = std::string());

  /*! \brief Creates a new server instance and configures it.
   *
   * This is equivalent to:
   * \code
    WServer server(applicationPath);
    server.setServerConfiguration(applicationPath, args, wtConfigurationFile);
    \endcode
   *
   * This version of the WServer constructor takes a std::string
   * for the application path, and a vector of arguments (not including
   * argv[0], the application path) instead of argc and argv,
   * for better convenience when arguments are not provided via
   * the command line.
   *
   * \throws Exception : indicates a configuration problem.
   *
   * \sa WServer(const std::string&, const std::string&)
   * \sa setServerConfiguration()
   */
  WTCONNECTOR_API
    WServer(const std::string &applicationPath,
            const std::vector<std::string> &args,
            const std::string& wtConfigurationFile = std::string());

  /*! \brief Destructor.
   *
   * If the server was still running, it is stopped first by calling
   * stop(). It is probably safer to call stop() first yourself, since
   * this allows exceptions to be caught.
   *
   * \sa isRunning(), stop()
   */
  WTCONNECTOR_API virtual ~WServer();

#ifndef WT_TARGET_JAVA
  /*! \brief Sets the I/O service.
   *
   * The server will use an I/O service for scheduling functions into a
   * thread-pool, and to implement asynchronous networking, whose call-back
   * funtions are scheduled in the same thread pool.
   *
   * By default, a server will create its own I/O service, but you may
   * configure it to reuse another I/O service.
   */
  WT_API void setIOService(WIOService& ioService);

  /*! \brief Returns the I/O service.
   *
   * \sa setIOService()
   */
  WT_API WIOService& ioService();
#endif

  /*! \brief Returns the server instance.
   *
   * Returns the single server instance. This may be useful when using
   * WRun(), which does not provide direct access to the instantiated
   * server, but still you want to use functions like
   * post().
   *
   * \note When instantiating multiple servers, this will simply return the
   *       last instance. You probably want to avoid this function then.
   */
  static WServer *instance() { return instance_; }

#ifndef WT_TARGET_JAVA
  /*! \brief Configures the HTTP(S) server or FastCGI process.
   *
   * Configures the HTTP(S) server using command-line arguments, a
   * configuration file, or both. The valid options are described in
   * \ref config_wthttpd "Built-in httpd configuration".
   *
   * The applications themselves are configured using the configuration
   * file passed to the constructor.
   *
   * The server configuration must be set before any other
   * functionality can be used.
   *
   * In case of FastCGI deployment, the \p serverConfigurationFile
   * argument is ignored, and depending on the command-line arguments,
   * this process may become a FastCGI protocol relay process which
   * never returning from this call but directs the FastCGI stream to
   * the correct session, rather than a Wt application server.
   *
   * \throws Exception : indicates a configuration problem.
   */
  WTCONNECTOR_API
    void setServerConfiguration(int argc, char *argv[],
                                const std::string& serverConfigurationFile
                                = std::string());

  /*! \brief Configures the HTTP(S) server or FastCGI process.
   *
   * Configures the HTTP(S) server using command-line arguments, a
   * configuration file, or both. The valid options are described in
   * \ref config_wthttpd "Built-in httpd configuration".
   *
   * The applications themselves are configured using the configuration
   * file passed to the constructor.
   *
   * The server configuration must be set before any other
   * functionality can be used.
   *
   * In case of FastCGI deployment, the \p serverConfigurationFile
   * argument is ignored, and depending on the command-line arguments,
   * this process may become a FastCGI protocol relay process which
   * never returning from this call but directs the FastCGI stream to
   * the correct session, rather than a Wt application server.
   *
   * This version of setServerConfiguration() takes a std::string
   * for the application path, and a vector of arguments (not including
   * argv[0], the application path) instead of argc and argv,
   * for better convenience when arguments are not provided via
   * the command line.
   *
   * \throws Exception : indicates a configuration problem.
   */
  WTCONNECTOR_API
    void setServerConfiguration(const std::string &applicationPath,
                                const std::vector<std::string> &args,
                                const std::string &serverConfigurationFile = std::string());

  /*! \brief Binds an entry-point to a callback function to create
   *         a new application.
   *
   * The \p path is the local URL at which the application is
   * deployed: when a user visits this URL, the callback will be
   * called to create a new application.
   *
   * If the \p path does not start with a slash ('/'), the deployment path
   * will be prepended. The deployment path defaults to '/' but can be overridden
   * in the wthttp connector with the --deploy-path option (see also \ref
   * config_wthttpd "Built-in httpd configuration"). This implies that when
   * the \p path is empty (the default), it is set to the deployment path.
   *
   * The optional \p favicon is a URL path (which should not
   * contain the host part!) to a favicon, which is the icon displayed
   * in the browser for your application. Alternatively, you may
   * specify a favicon using the "favicon" property in the
   * configuration file (see also \ref config_general).
   *
   * \sa removeEntryPoint()
   */
  WT_API void addEntryPoint(EntryPointType type, ApplicationCreator callback,
                            const std::string& path = std::string(),
                            const std::string& favicon = std::string());
#endif

  /*! \brief Binds a resource to a fixed path.
   *
   * Resources may either be private to a single session or
   * public. Use this method to add a public resource with a fixed
   * path.
   *
   * \throw Exception if an entrypoint was already registered at the given path
   *
   * \sa removeEntryPoint()
   */
  WT_API void addResource(const std::shared_ptr<WResource>& resource, const std::string& path);

#ifndef WT_TARGET_JAVA
  /*! \brief Binds a WebSocket resource to a fixed \p path.
   *
   * This will add a public resource that is deployed on a fixed \p path.
   * The resource will be accessible to any client that is able to access
   * the server on the \p path.
   *
   * \throw Exception if an entrypoint was already registered at the given path
   *
   * \sa removeEntryPoint()
   */
  WT_API void addResource(const std::shared_ptr<WWebSocketResource>& resource, const std::string& path);
#endif // WT_TARGET_JAVA

  /*! \brief Binds a resource to a fixed path.
   *
   * Resources may either be private to a single session or
   * public. Use this method to add a public resource with a fixed
   * path.
   *
   * \note Ownership of the resource is external to %WServer. The resource first needs
   *       to be \link removeEntryPoint() removed\endlink (while the server is
   *       \link stop() stopped\endlink) before being destroyed, or has to outlive the %WServer.
   *
   * \throw Exception if an entrypoint was already registered at the given path
   *
   * \sa removeEntryPoint()
   *
   * \deprecated Use addResource(const std::shared_ptr<WResource>&, const std::string&) instead.
   */
  WT_DEPRECATED("Use addResource(const std::shared_ptr<WResource>&, const std::string&) instead")
  WT_API void addResource(WResource *resource, const std::string& path);

  /*! \brief Removes an entry point.
   *
   * Use this method to remove an entry point or static resource.
   *
   * \note If the entry point is a resource, the resource will not be deleted
   *
   * \sa addEntryPoint(), addResource()
   */
  WT_API void removeEntryPoint(const std::string& path);

  /*! \brief Unbinds a resource from a fixed path.
   *
   * Use this method to remove a static resource.
   *
   * \note The resource object itself will not be destructed, and can
           still be in use in other threads. However, any new requests
           to its path will no longer be handled by the removed
           resource.
   *
   * \sa addResource()
   */
  WT_API void removeResource(const WResource* resource);

#ifndef WT_TARGET_JAVA
  /*! \brief Starts the server in the background.
   *
   * Returns whether the server could be successfully started.
   *
   * \throws Exception : indicates a problem starting the server.
   *
   * \sa isRunning(), stop()
   */
  WTCONNECTOR_API bool start();

  /*! \brief Stops the server.
   *
   * All active application sessions are terminated cleanly, and the
   * HTTP(S) server is shut down.
   *
   * \note This will also stop the underlying ioService(), and will block
   *       until all pending tasks have completed.
   *
   * \throw Exception : indicates a problem while stopping the server.
   *
   * \sa isRunning(), start()
   */
  WTCONNECTOR_API void stop();

  /*! \brief Returns whether the server is running.
   *
   * \sa start(), stop()
   */
  WTCONNECTOR_API bool isRunning() const;

  /*! \brief Starts the server, waits for shutdown, then stops the server.
   *
   * This is equivalent to:
   * \code
    if (start()) {
      WServer::waitForShutdown();
      stop();
    }
    \endcode
   * \sa start()
   * \sa stop()
   * \sa waitForShutdown()
   */
  WTCONNECTOR_API void run();

  /*! \brief Resumes the server.
   *
   * This closes and reopens the listen socket(s) for accepting new
   * TCP and/or SSL connections. This may be needed when the OS (like
   * IPhoneOS) has closed the sockets while suspending the
   * application.
   */
  WTCONNECTOR_API void resume();

  /*! \brief Waits for a shutdown signal.
   *
   * This static method blocks the current thread, waiting for a
   * shutdown signal. The implementation and details are platform
   * dependent, but this is usually Ctrl-C (SIGINT) or SIGKILL.
   *
   * This method is convenient if you want to customize how the server
   * is started (by instantiating a WServer object yourself, instead
   * of using Wt::WRun()), but still want to use %Wt as a standalone
   * server that cleanly terminates on interruption.
   *
   * This will also catch SIGHUP, to reread the configuration file.
   */
  WT_API static int waitForShutdown();

  /*! \brief A utility method to restart.
   *
   * This will result the application with the new image (argv[0]), effectively
   * loading a newly deployed version. <i>(Experimental, UNIX only)</i>
   */
  WT_API static void restart(int argc, char **argv, char **envp);

  /*! \brief A utility method to restart.
   *
   * This will result the application with the new image (applicationPath), effectively
   * loading a newly deployed version. <i>(Experimental, UNIX only)</i>
   *
   * This version of restart() takes a std::string
   * for the application path, and a vector of arguments (not including
   * argv[0], the application path) instead of argc and argv,
   * for better convenience when arguments are not provided via
   * the command line.
   */
  WT_API static void restart(const std::string &applicationPath,
                             const std::vector<std::string> &args);

  /*! \brief Returns the server HTTP port number.
   *
   * Returns -1 if the port is not known (i.e. because the connector is
   * not aware of how the http server is configured).
   *
   * \note If the server listens on multiple ports, only the first
   *       port is returned.
   */
  WTCONNECTOR_API int httpPort() const;

  WT_API void setAppRoot(const std::string& path);

  /*! \brief Returns the approot special property
   *
   * \sa WApplication::appRoot()
   */
  WT_API std::string appRoot() const;

  /*! \brief Returns the docroot (if using wthttp)
   *
   * If you're using wthttp, this returns the location passed to the
   * --docroot argument, just like WApplication::docRoot(). This allows access
   * to this parameter outside of the context of a WApplication.
   *
   * If you're using any other connector, this returns the empty string.
   */
   WTCONNECTOR_API std::string docRoot() const;

  /*! \brief Posts a function to a session.
   *
   * This is a thread-safe method to post a particular event
   * (implemented as a function object) to be run within the context
   * of a session, identified by its WApplication::sessionId(). The
   * method will safely handle the case where the session is being
   * terminated, and the session lock will be taken to execute the
   * function in the context of the session (with
   * WApplication::instance() pointing to the correct application),
   * just as with a request initiated by the browser. You will
   * typically also want to push the changes to the client using
   * server-initiated updates (WApplication::triggerUpdate()).
   *
   * The method returns immediately, and the function will be run
   * within the thread-pool that handles incoming web requests. In
   * this way, it avoids dead-lock scenarios.
   *
   * If a \p fallbackFunction is specified then in case the session
   * is dead, it is called instead.
   *
   * This provides a good alternative to grabbing the update lock of
   * an application to directly push changes to a session out of its
   * event loop.
   *
   * Note that if you post an event to a method of a widget (or other
   * object), it may still be that the targeted object has been
   * deleted, if the life-time of that object is not the same as the
   * life-time of the application. It may be useful to protect
   * yourself from this by using WApplication::bind().
   */
  WT_API void post(const std::string& sessionId,
                   const std::function<void ()>& function,
                   const std::function<void ()>& fallBackFunction
                     = std::function<void ()>());

  /*! \brief Posts a function to all currently active sessions.
   *
   * \sa post()
   */
  WT_API void postAll(const std::function<void ()>& function);

  /*! \brief Schedules a function to be executed in a session.
   *
   * The \p function will run in the session specified by \p sessionId,
   * after \p duration. If the session does not exist anymore,
   * \p fallBackFunction will be executed.
   *
   * \sa post()
   */
  WT_API void schedule(std::chrono::steady_clock::duration duration,
                       const std::string& sessionId,
                       const std::function<void ()>& function,
                       const std::function<void ()>& fallBackFunction
                         = std::function<void ()>());

  /*! \brief Change input method for server certificate passwords (http backend)
   *
   * The private server identity key may be protected by a password. If you
   * want to control how the password is retrieved, set a password handler
   * by calling this function. If no password handler is set, the OpenSSL
   * default handler will be used, which asks to enter the password on stdio.
   *
   * This function must be called before calling start().
   *
   * The max_length parameter is informational and indicates that the
   * underlying implementation will truncate the password to this length.
   */
  WTCONNECTOR_API void setSslPasswordCallback(const SslPasswordCallback& cb);

#endif // WT_TARGET_JAVA

#ifndef WT_TARGET_JAVA
  /*! \brief Reads a configuration property.
   *
   * As properties are unique to an executable location, they are defined
   * from the moment that setServerConfiguration() is invoked. Use this
   * method to access configuration properties outside of an active
   * session, e.g. from within the main() function.
   *
   * \sa WApplication::readConfigurationProperty()

   */
  WT_API bool readConfigurationProperty(const std::string& name,
                                        std::string& value) const;

#else
  /*! \brief Reads a configuration property.
   *
   * Tries to read a configured value for the property \p name. If no
   * value was configured, the default \p value is returned.
   *
   * \sa WApplication::readConfigurationProperty()
   */
  std::string *readConfigurationProperty(const std::string& name,
                                         const std::string& value);
#endif // WT_TARGET_JAVA

  /*! \brief Sets the resource object that provides localized strings.
   *
   * This is used only for WString::tr() used from within static
   * resources.
   *
   * The default value is 0.
   */
  WT_API void setLocalizedStrings(const std::shared_ptr<WLocalizedStrings>&
                                  stringResolver);

  /*! \brief Returns the resource object that provides localized strings.
   *
   * \sa setLocalizedStrings()
   */
  WT_API std::shared_ptr<WLocalizedStrings> localizedStrings() const;

#ifndef WT_TARGET_JAVA

  /*! \brief Retrieve information on all sessions.
   *
   * This is only implemented for the wthttp connector.
   *
   * If the dedicated process session policy is used, only the original
   * process has access to the full list of sessions. Public resources
   * (those registered with addResource()) run in the original process,
   * so they can access this list.
   */
  WTCONNECTOR_API std::vector<SessionInfo> sessions() const;

  void updateProcessSessionId(const std::string& sessionId);

  /*! \brief Returns the logger instance.
   *
   * This is the logger class used in WApplication::log() and
   * Wt::log() functions.
   */
  WT_API WLogger& logger();

  /*! \brief Sets a custom logger to redirect all logging to.
   *
   * Instead of using the server's default logger, this will send
   * all logging to some custom WLogSink.
   */
  WT_API void setCustomLogger(const WLogSink &customLogger);

  const WLogSink * customLogger() const;

#ifndef WT_TARGET_JAVA
  /*! \brief Sets the format for the access logger.
   *
   * The format string can contain the following placeholders:
   * - ${IP} : the remote IP address of the client
   * - ${METHOD} : the HTTP method of the request (e.g. GET, POST, ...)
   * - ${URI} : the URI of the request
   * - ${HTTP_VERSION} : the HTTP version of the request (e.g. HTTP/1.1)
   * - ${STATUS} : the HTTP status code of the response
   * - ${CONTENT} : the content's size of the request
   *
   * \note This is only available with the wthttp connector, and has no
   *       effect on other connectors.
   */
  WTCONNECTOR_API void setAccessLoggerFormat(const std::string& format);


  /*! \brief Returns the format for the access logger.
   *
   * \note This always retunrs an empty string when not using the
   *       wthttp connector.
   *
   * \sa setAccessLoggerFormat()
   */
  WTCONNECTOR_API std::string accessLoggerFormat() const;

  /*! \brief Returns the access logger if it exists.
   *
   * \note This always returns nullptr when not using the wthttp
   *       connector.
   *
   * \sa setAccessLoggerFormat(), setRedirectAccessLog()
   */
  WTCONNECTOR_API WLogger* accessLogger();

  /*! \brief Sets whether access log redirection is enabled.
   *
   * When access log redirection is enabled, the access log will be
   * logged like other log messages, using the server logger (default
   * or custom).
   *
   * \note This is only available with the wthttp connector, and has no
   *       effect with other connectors.
   *
   * \sa accessLogger(), setCustomLogger()
   */
  WTCONNECTOR_API void setRedirectAccessLog(bool redirect);

  /*! \brief Returns whether access log redirection is enabled.
   *
   * \note This always returns true when not using the wthttp
   *       connector.
   *
   * \sa setRedirectAccessLog(), accessLogger(), setCustomLogger()
   */
  WTCONNECTOR_API bool redirectAccessLog() const;
#endif // WT_TARGET_JAVA

  /*! \brief Adds an entry to the log.
   *
   * \sa Wt::log(), WApplication::log()
   */
  WT_API WLogEntry log(const std::string& type) const;

  WT_API void initLogger(const std::string& logFile,
                         const std::string& logConfig);

  /*! \brief Reflects whether the current process is a dedicated session process
   *
   * \note This will only be accurate after the WServer has been configured, either
   *       through setServerConfiguration() or one of the constructors that immediately
   *       configures the server.
   */
  WT_API bool dedicatedSessionProcess() const;
#endif // WT_TARGET_JAVA

  WT_API bool expireSessions();

  WT_API Configuration& configuration();

  WT_API WebController *controller();

#ifndef WT_TARGET_JAVA
  WT_API void scheduleStop();
#endif // WT_TARGET_JAVA

#ifdef WT_WIN32
  WT_API static void terminate();
#endif // WT_WIN32

#ifdef WT_TARGET_JAVA
  std::string getContextPath();
  int servletMajorVersion();
#endif

private:
  WebController *webController_;

#ifndef WT_TARGET_JAVA
  std::vector<std::shared_ptr<WWebSocketResource>> webSocketResources_;

  WLogger logger_;
  const WLogSink * customLogger_;
#endif // WT_TARGET_JAVA

  std::string application_, configurationFile_, appRoot_, description_;
  Configuration *configuration_;
  std::shared_ptr<WLocalizedStrings> localizedStrings_;
  mutable std::shared_mutex localizedStringsMutex_;

  bool ownsIOService_;
  WIOService *ioService_;

  bool dedicatedProcessEnabled_;

  struct Impl;
  Impl *impl_;

  WT_API void setConfiguration(const std::string& file,
                               const std::string& application);

  WT_API void setConfiguration(const std::string& file);
  const std::string& configurationFile() const {
    return configurationFile_;
  }

  WT_API void init(const std::string& wtApplicationPath,
                   const std::string& configurationFile);
  WT_API void destroy();
  WT_API void setCatchSignals(bool catchSignals);
  WT_API std::string prependDefaultPath(const std::string& path);


  WT_API static WServer *instance_;
  std::function<std::string (std::size_t max_length, int purpose)> sslPasswordCallback_;
#ifndef WT_TARGET_JAVA
  std::function<void ()> stopCallback_;
  std::function<void (const std::string& sessionId)> updateProcessSessionIdCallback_;
#endif // WT_TARGET_JAVA
};

}
#endif // WSERVER_H_
