/****************************************************************************
**
** https://www.qxorm.com/
** Copyright (C) 2013 XDL Team (ic-east.com)
**
** This file is part of the QxOrm library
**
** This software is provided 'as-is', without any express or implied
** warranty. In no event will the authors be held liable for any
** damages arising from the use of this software
**
** Commercial Usage
** Licensees holding valid commercial QxOrm licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and XDL Team
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file 'license.gpl3.txt' included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met : http://www.gnu.org/copyleft/gpl.html
**
** If you are unsure which license is appropriate for your use, or
** if you have questions regarding the use of this file, please contact :
** ic-east.com
**
****************************************************************************/

#ifdef _QX_ENABLE_QT_NETWORK

#include <QxPrecompiled.h>

#include <QtCore/qdir.h>
#include <QtCore/qfile.h>
#include <QtCore/qmutex.h>
#include <QtCore/qabstracteventdispatcher.h>

#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
#include <QtCore/qregularexpression.h>
#else // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
#include <QtCore/qregexp.h>
#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))

#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
#include <QtCore/qmimetype.h>
#include <QtCore/qmimedata.h>
#include <QtCore/qmimedatabase.h>
#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))

#include <QxHttpServer/QxHttpServer.h>

#include <QxService/QxThreadPool.h>
#include <QxService/QxConnect.h>

#include <QxCollection/QxCollection.h>
#include <QxCollection/QxCollectionIterator.h>

#include <QxCommon/QxException.h>
#include <QxCommon/QxExceptionCode.h>

#include <QxRestApi/QxRestApi.h>

#include <QxMemLeak/mem_leak.h>

namespace qx
{

   struct QxHttpServerDispatchSegment
   {

      enum type_segment
      {
         _static,
         _variable,
         _variable_and_type,
         _variable_and_regexp,
         _wildcard
      };

      type_segment m_type;       //!< HTTP server dispatcher segment type
      QString m_path;            //!< HTTP server dispatcher segment path (part of item path splitted by '/' character)
      qx_hash_result m_pathHash; //!< HTTP server dispatcher segment path hash (for optimization)
      QString m_varName;         //!< HTTP server dispatcher segment variable name
      QString m_varType;         //!< HTTP server dispatcher segment variable type (or regular expression)

#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
      QRegularExpression m_regExp; //!< HTTP server dispatcher segment regular expression
#else                              // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
      QRegExp m_regExp; //!< HTTP server dispatcher segment regular expression (Qt4 version)
#endif                             // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))

      QxHttpServerDispatchSegment() : m_type(_static), m_pathHash(0) { ; }
      ~QxHttpServerDispatchSegment() { ; }

      bool parse();
      bool check(const QString &path, qx_hash_result hash) const;
      void fillParam(const QString &path, QHash<QString, QVariant> &parameters) const;
   };

   struct QxHttpServerDispatchItem
   {

      QString m_command;                                              //!< HTTP server dispatcher item command (GET, POST, DELETE, PUT, etc...)
      qx_hash_result m_commandHash;                                   //!< HTTP server dispatcher item command hash (for optimization)
      bool m_commandWildcard;                                         //!< HTTP server dispatcher item wildcard command (means accept all commands)
      QString m_path;                                                 //!< HTTP server dispatcher item path (example : /foo/bar/<var1>/<var2:int>/*)
      QList<std::shared_ptr<QxHttpServerDispatchSegment>> m_segments; //!< HTTP server dispatcher item list of segments (path splitted by '/' character)
      QxHttpServer::type_fct_custom_request_handler m_fct;            //!< HTTP server dispatcher item callback (executed in its dedicated thread)

      QxHttpServerDispatchItem() : m_commandHash(0), m_commandWildcard(false) { ; }
      ~QxHttpServerDispatchItem() { ; }

      bool parse();
   };

   struct QxHttpServerDispatcher
   {

      qx::QxCollection<QString, std::shared_ptr<QxHttpServerDispatchItem>> m_lstDispatchItems; //!< HTTP server dispatcher items (associate url to callback)

      QxHttpServerDispatcher() { ; }
      ~QxHttpServerDispatcher() { ; }

      void dispatch(const QString &command, const QString &path, QxHttpServer::type_fct_custom_request_handler fct, long position);
      std::shared_ptr<QxHttpServerDispatchItem> find(qx::QxHttpRequest &request) const;
   };

   struct Q_DECL_HIDDEN QxHttpServer::QxHttpServerImpl
   {

      typedef std::shared_ptr<QxHttpServer::type_fct_custom_request_handler> type_fct_ptr;

      QMutex m_mutex;                                        //!< Mutex => 'QxHttpServer' is thread-safe (callbacks can be changed even if server is running)
      qx::service::QxThreadPool_ptr m_pThreadPool;           //!< HTTP server is managed by a thread pool
      type_fct_ptr m_fctCustomRequestHandler;                //!< Callback to manage requests and build responses (executed in its dedicated thread)
      std::unique_ptr<QxHttpServerDispatcher> m_pDispatcher; //!< HTTP server dispatcher (each callback is executed in its dedicated thread)
      type_fct_ptr m_fctBeforeDispatching;                   //!< Callback function called before handling the request (executed in its dedicated thread), can be used for logging for example
      type_fct_ptr m_fctAfterDispatching;                    //!< Callback function called after handling the request and generating the response (executed in its dedicated thread), can be used for logging for example
      QAbstractEventDispatcher *m_pEventDispatcher;          //!< Can be used to optimize socket implementation (using epoll on Linux for example), for example : https://github.com/sjinks/qt_eventdispatcher_epoll or https://github.com/connectedtable/qeventdispatcher_epoll

      QxHttpServerImpl() : m_pDispatcher(new QxHttpServerDispatcher()), m_pEventDispatcher(NULL) { ; }
      ~QxHttpServerImpl() { ; }
   };

   QxHttpServer::QxHttpServer(QObject *parent /* = NULL */) : QObject(parent), m_pImpl(new QxHttpServerImpl())
   {
      qRegisterMetaType<qx::QxHttpTransaction_ptr>("qx::QxHttpTransaction_ptr");
      qRegisterMetaType<qx::QxHttpTransaction_ptr>("QxHttpTransaction_ptr");
   }

   QxHttpServer::~QxHttpServer() { stopServer(); }

   void QxHttpServer::startServer()
   {
      if (m_pImpl->m_pThreadPool)
      {
         stopServer();
      }
      qx::service::QxConnect::getSingleton()->setModeHTTP(true);
      m_pImpl->m_pThreadPool.reset(new qx::service::QxThreadPool());

#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
      if (m_pImpl->m_pEventDispatcher)
      {
         m_pImpl->m_pThreadPool->setEventDispatcher(m_pImpl->m_pEventDispatcher);
      }
#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))

      QObject::connect(m_pImpl->m_pThreadPool.get(), SIGNAL(error(const QString &, qx::service::QxTransaction_ptr)), this, SLOT(onError(const QString &, qx::service::QxTransaction_ptr)));
      QObject::connect(m_pImpl->m_pThreadPool.get(), SIGNAL(serverIsRunning(bool, qx::service::QxServer *)), this, SLOT(onServerIsRunning(bool, qx::service::QxServer *)));
      QObject::connect(m_pImpl->m_pThreadPool.get(), SIGNAL(transactionStarted(qx::service::QxTransaction_ptr)), this, SLOT(onTransactionStarted(qx::service::QxTransaction_ptr)));
      QObject::connect(m_pImpl->m_pThreadPool.get(), SIGNAL(transactionFinished(qx::service::QxTransaction_ptr)), this, SLOT(onTransactionFinished(qx::service::QxTransaction_ptr)));
      QObject::connect(m_pImpl->m_pThreadPool.get(), SIGNAL(customRequestHandler(qx::service::QxTransaction_ptr)), this, SLOT(onCustomRequestHandler(qx::service::QxTransaction_ptr)), Qt::DirectConnection);
      m_pImpl->m_pThreadPool->start();
   }

   void QxHttpServer::stopServer()
   {
      if (!m_pImpl->m_pThreadPool)
      {
         return;
      }
      m_pImpl->m_pThreadPool.reset();
      Q_EMIT serverStatusChanged(false);
   }

   void QxHttpServer::setCustomRequestHandler(QxHttpServer::type_fct_custom_request_handler fct)
   {
      QMutexLocker locker(&m_pImpl->m_mutex);
      if (!fct)
      {
         m_pImpl->m_fctCustomRequestHandler.reset();
         return;
      }
      m_pImpl->m_fctCustomRequestHandler = std::make_shared<QxHttpServer::type_fct_custom_request_handler>();
      (*m_pImpl->m_fctCustomRequestHandler) = fct;
   }

   QxHttpServer &QxHttpServer::dispatch(const QString &command, const QString &path, QxHttpServer::type_fct_custom_request_handler fct, long position /* = -1 */)
   {
      QMutexLocker locker(&m_pImpl->m_mutex);
      m_pImpl->m_pDispatcher->dispatch(command.toUpper(), path, fct, position);
      return (*this);
   }

   void QxHttpServer::beforeDispatching(QxHttpServer::type_fct_custom_request_handler fct)
   {
      QMutexLocker locker(&m_pImpl->m_mutex);
      if (!fct)
      {
         m_pImpl->m_fctBeforeDispatching.reset();
         return;
      }
      m_pImpl->m_fctBeforeDispatching = std::make_shared<QxHttpServer::type_fct_custom_request_handler>();
      (*m_pImpl->m_fctBeforeDispatching) = fct;
   }

   void QxHttpServer::afterDispatching(QxHttpServer::type_fct_custom_request_handler fct)
   {
      QMutexLocker locker(&m_pImpl->m_mutex);
      if (!fct)
      {
         m_pImpl->m_fctAfterDispatching.reset();
         return;
      }
      m_pImpl->m_fctAfterDispatching = std::make_shared<QxHttpServer::type_fct_custom_request_handler>();
      (*m_pImpl->m_fctAfterDispatching) = fct;
   }

   void QxHttpServer::clearDispatcher()
   {
      QMutexLocker locker(&m_pImpl->m_mutex);
      m_pImpl->m_pDispatcher.reset(new QxHttpServerDispatcher());
   }

#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
   void QxHttpServer::setEventDispatcher(QAbstractEventDispatcher *pEventDispatcher)
   {
      m_pImpl->m_pEventDispatcher = pEventDispatcher;
   }
#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))

   void QxHttpServer::onError(const QString &err, qx::service::QxTransaction_ptr transaction)
   {
      qx::QxHttpTransaction_ptr httpTransaction = std::dynamic_pointer_cast<qx::QxHttpTransaction>(transaction);
      if (transaction && (!httpTransaction))
      {
         qAssertMsg(false, "[QxOrm] qx::QxHttpServer::onError", "unable to get HTTP transaction using std::dynamic_pointer_cast");
      }
      Q_EMIT error(err, httpTransaction);
   }

   void QxHttpServer::onServerIsRunning(bool bIsRunning, qx::service::QxServer *pServer)
   {
      Q_UNUSED(pServer);
      if (bIsRunning)
      {
         qx::service::QxConnect *serverSettings = qx::service::QxConnect::getSingleton();
         QString msg = "HTTP server is running (";
         msg += "port: " + QString::number(serverSettings->getPort());
         msg += ", threads: " + QString::number(serverSettings->getThreadCount());
#ifndef QT_NO_SSL
         msg += ", SSL: " + (serverSettings->getSSLEnabled() ? QString("enabled") : QString("disabled"));
#else  // QT_NO_SSL
         msg += ", SSL: disabled";
#endif // QT_NO_SSL
         msg += ")";
         qDebug("[QxOrm] %s", qPrintable(msg));
      }
      else
      {
         qDebug("[QxOrm] %s", "HTTP server has been stopped");
      }
      Q_EMIT serverStatusChanged(bIsRunning);
   }

   void QxHttpServer::onTransactionStarted(qx::service::QxTransaction_ptr transaction)
   {
      qx::QxHttpTransaction_ptr httpTransaction = std::dynamic_pointer_cast<qx::QxHttpTransaction>(transaction);
      if (transaction && (!httpTransaction))
      {
         qAssertMsg(false, "[QxOrm] qx::QxHttpServer::onTransactionStarted", "unable to get HTTP transaction using std::dynamic_pointer_cast");
      }
      Q_EMIT transactionStarted(httpTransaction);
   }

   void QxHttpServer::onTransactionFinished(qx::service::QxTransaction_ptr transaction)
   {
      qx::QxHttpTransaction_ptr httpTransaction = std::dynamic_pointer_cast<qx::QxHttpTransaction>(transaction);
      if (transaction && (!httpTransaction))
      {
         qAssertMsg(false, "[QxOrm] qx::QxHttpServer::onTransactionFinished", "unable to get HTTP transaction using std::dynamic_pointer_cast");
      }
      Q_EMIT transactionFinished(httpTransaction);
   }

   void QxHttpServer::onCustomRequestHandler(qx::service::QxTransaction_ptr transaction)
   {
      // Check if transaction is valid
      qx::QxHttpTransaction_ptr httpTransaction = std::dynamic_pointer_cast<qx::QxHttpTransaction>(transaction);
      if (transaction && (!httpTransaction))
      {
         qAssertMsg(false, "[QxOrm] qx::QxHttpServer::onCustomRequestHandler", "unable to get HTTP transaction using std::dynamic_pointer_cast");
         return;
      }
      if (!httpTransaction)
      {
         qAssertMsg(false, "[QxOrm] qx::QxHttpServer::onCustomRequestHandler", "HTTP transaction is empty");
         return;
      }

      // Get request and response instances
      qx::QxHttpRequest &request = httpTransaction->request();
      qx::QxHttpResponse &response = httpTransaction->response();

      // Get custom request handler + before/after dispatching callbacks (thread-safe)
      std::shared_ptr<QxHttpServer::type_fct_custom_request_handler> fctCustomHandler;
      std::shared_ptr<QxHttpServer::type_fct_custom_request_handler> fctBeforeDispatching;
      std::shared_ptr<QxHttpServer::type_fct_custom_request_handler> fctAfterDispatching;
      {
         QMutexLocker locker(&m_pImpl->m_mutex);
         fctCustomHandler = m_pImpl->m_fctCustomRequestHandler;
         fctBeforeDispatching = m_pImpl->m_fctBeforeDispatching;
         fctAfterDispatching = m_pImpl->m_fctAfterDispatching;
      }

      // Execute before dispatching callback
      if (fctBeforeDispatching && (*fctBeforeDispatching))
      {
         try
         {
            (*fctBeforeDispatching)(request, response);
         }
         catch (const qx::exception &x)
         {
            qx_bool xb = x.toQxBool();
            httpTransaction->setMessageReturn(xb);
         }
         catch (const std::exception &e)
         {
            httpTransaction->setMessageReturn(qx_bool(500, e.what()));
         }
         catch (...)
         {
            httpTransaction->setMessageReturn(qx_bool(500, "[QxOrm] Unknown server error (before dispatching)"));
         }
      }

      // Get dispatcher item callback (thread-safe)
      std::shared_ptr<QxHttpServerDispatchItem> itemDispatch;
      {
         QMutexLocker locker(&m_pImpl->m_mutex);
         itemDispatch = m_pImpl->m_pDispatcher->find(request);
      }

      try
      {
         // Execute dispatcher item callback or custom request handler
         if (itemDispatch && itemDispatch->m_fct)
         {
            itemDispatch->m_fct(request, response);
         }
         else if (fctCustomHandler && (*fctCustomHandler))
         {
            (*fctCustomHandler)(request, response);
         }
         else
         {
            httpTransaction->setMessageReturn(qx_bool(501, "[QxOrm] Not implemented : no route found for URL (" + request.command() + " - " + request.url().path() + ") : you must provide a request handler (callback) using qx::QxHttpServer::dispatch() or qx::QxHttpServer::setCustomRequestHandler() method"));
         }
      }
      catch (const qx::exception &x)
      {
         qx_bool xb = x.toQxBool();
         httpTransaction->setMessageReturn(xb);
      }
      catch (const std::exception &e)
      {
         httpTransaction->setMessageReturn(qx_bool(500, e.what()));
      }
      catch (...)
      {
         httpTransaction->setMessageReturn(qx_bool(500, "[QxOrm] Unknown server error"));
      }

      // Execute after dispatching callback
      if (fctAfterDispatching && (*fctAfterDispatching))
      {
         try
         {
            (*fctAfterDispatching)(request, response);
         }
         catch (const qx::exception &x)
         {
            qx_bool xb = x.toQxBool();
            httpTransaction->setMessageReturn(xb);
         }
         catch (const std::exception &e)
         {
            httpTransaction->setMessageReturn(qx_bool(500, e.what()));
         }
         catch (...)
         {
            httpTransaction->setMessageReturn(qx_bool(500, "[QxOrm] Unknown server error (after dispatching)"));
         }
      }
   }

   void QxHttpServerDispatcher::dispatch(const QString &command, const QString &path, QxHttpServer::type_fct_custom_request_handler fct, long position)
   {
      QString key = command + "|" + path;
      if (m_lstDispatchItems.exist(key))
      {
         m_lstDispatchItems.removeByKey(key);
      }
      if ((!fct) || (path.isEmpty()) || (command.isEmpty()))
      {
         return;
      }

      std::shared_ptr<QxHttpServerDispatchItem> item = std::make_shared<QxHttpServerDispatchItem>();
      item->m_command = command;
      item->m_path = ((path == "/") ? QString("/*") : path);
      item->m_fct = fct;
      if (!item->parse())
      {
         qAssert(false);
         return;
      }
      if (item->m_segments.count() <= 0)
      {
         return;
      }
      if (position < 0)
      {
         m_lstDispatchItems.insert(key, item);
      }
      else
      {
         m_lstDispatchItems.insert(position, key, item);
      }
   }

   std::shared_ptr<QxHttpServerDispatchItem> QxHttpServerDispatcher::find(qx::QxHttpRequest &request) const
   {
      // Check and prepare input data
      if (m_lstDispatchItems.count() <= 0)
      {
         return std::shared_ptr<QxHttpServerDispatchItem>();
      }
      QString requestCommand = request.command().toUpper();
      qx_hash_result requestCommandHash = qHash(requestCommand);
      QString requestPath = request.url().path();
      if (requestPath == "/")
      {
         requestPath = "/*";
      }
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
      QStringList requestSegments = requestPath.split("/", Qt::SkipEmptyParts);
#else  // (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
      QStringList requestSegments = requestPath.split("/", QString::SkipEmptyParts);
#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
      QList<qx_hash_result> requestSegmentsHash;
      requestSegmentsHash.reserve(requestSegments.count());
      Q_FOREACH (QString data, requestSegments)
      {
         requestSegmentsHash.append(qHash(data));
      }
      if (requestSegments.count() <= 0)
      {
         return std::shared_ptr<QxHttpServerDispatchItem>();
      }

      // Iterate over all dispatcher items
      qx::QxCollectionIterator<QString, std::shared_ptr<QxHttpServerDispatchItem>> itr(m_lstDispatchItems);
      while (itr.next())
      {
         // Check dispatcher item command and segments count
         const std::shared_ptr<QxHttpServerDispatchItem> &item = itr.value();
         if (!item)
         {
            qAssert(false);
            continue;
         }
         if ((item->m_commandHash != requestCommandHash) && (!item->m_commandWildcard))
         {
            continue;
         }
         if (item->m_segments.count() > requestSegments.count())
         {
            continue;
         }
         if (!item->m_fct)
         {
            continue;
         }

         // Iterate over all dispatcher item segments to check if everything matched
         bool bAllSegmentsOk = true;
         for (int i = 0; i < item->m_segments.count(); i++)
         {
            const std::shared_ptr<QxHttpServerDispatchSegment> &segment = item->m_segments.at(i);
            if (!segment)
            {
               qAssert(false);
               continue;
            }
            if (!segment->check(requestSegments.at(i), requestSegmentsHash.at(i)))
            {
               bAllSegmentsOk = false;
               break;
            }
         }
         if (!bAllSegmentsOk)
         {
            continue;
         }

         // Iterate over all dispatcher item segments to fill URL parameters
         QHash<QString, QVariant> parameters;
         for (int i = 0; i < item->m_segments.count(); i++)
         {
            const std::shared_ptr<QxHttpServerDispatchSegment> &segment = item->m_segments.at(i);
            if (!segment)
            {
               qAssert(false);
               continue;
            }
            segment->fillParam(requestSegments.at(i), parameters);
         }

         // We have found our dispatcher item
         request.dispatchParams() = parameters;
         return item;
      }
      return std::shared_ptr<QxHttpServerDispatchItem>();
   }

   bool QxHttpServerDispatchItem::parse()
   {
      m_segments.clear();
      m_commandHash = qHash(m_command);
      m_commandWildcard = (m_command == "*");
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
      QStringList segments = m_path.split("/", Qt::SkipEmptyParts);
#else  // (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
      QStringList segments = m_path.split("/", QString::SkipEmptyParts);
#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
      Q_FOREACH (QString data, segments)
      {
         data = data.trimmed();
         if (data.isEmpty())
         {
            continue;
         }
         std::shared_ptr<QxHttpServerDispatchSegment> segment = std::make_shared<QxHttpServerDispatchSegment>();
         segment->m_path = data;
         if (!segment->parse())
         {
            qAssert(false);
            return false;
         }
         m_segments.append(segment);
      }
      return true;
   }

   bool QxHttpServerDispatchSegment::parse()
   {
      if (m_path.isEmpty())
      {
         return true;
      }
      if (m_path.startsWith("<") && m_path.endsWith(">"))
      {
         QString path = m_path;
         path.remove(0, 1);
         path.chop(1);
         int pos = path.indexOf(":");
         if (pos >= 0)
         {
            m_varName = path.left(pos).trimmed();
            m_varType = path.mid(pos + 1).trimmed();
            m_type = QxHttpServerDispatchSegment::_variable_and_type;
            if (m_varType.startsWith("{") && m_varType.endsWith("}"))
            {
               m_varType.remove(0, 1);
               m_varType.chop(1);
               m_regExp.setPattern(m_varType);
               m_type = QxHttpServerDispatchSegment::_variable_and_regexp;
            }
         }
         else
         {
            m_varName = path.trimmed();
            m_type = QxHttpServerDispatchSegment::_variable;
         }
      }
      else if (m_path == "*")
      {
         m_type = QxHttpServerDispatchSegment::_wildcard;
      }
      else
      {
         m_type = QxHttpServerDispatchSegment::_static;
         m_pathHash = qHash(m_path);
      }
      return true;
   }

   bool QxHttpServerDispatchSegment::check(const QString &path, qx_hash_result hash) const
   {
      bool result = false;
      switch (m_type)
      {
      case QxHttpServerDispatchSegment::_static:
         qAssert((hash != 0) && (m_pathHash != 0));
         result = (hash == m_pathHash);
         break;
      case QxHttpServerDispatchSegment::_variable:
         qAssert(!m_varName.isEmpty());
         result = (!path.isEmpty());
         break;
      case QxHttpServerDispatchSegment::_variable_and_type:
         qAssert(!m_varName.isEmpty() && !m_varType.isEmpty());
         if ((m_varType == "int") || (m_varType == "long"))
         {
            qlonglong val = path.toLongLong(&result, 10);
            Q_UNUSED(val);
         }
         if ((m_varType == "float") || (m_varType == "double"))
         {
            double val = path.toDouble(&result);
            Q_UNUSED(val);
         }
         else if (m_varType == "string")
         {
            result = (!path.isEmpty());
         }
         break;
      case QxHttpServerDispatchSegment::_variable_and_regexp:
         qAssert(!m_varName.isEmpty() && !m_varType.isEmpty());
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
         result = ((!path.isEmpty()) && (m_regExp.match(path).hasMatch()));
#else  // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
         result = ((!path.isEmpty()) && (m_regExp.exactMatch(path)));
#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
         break;
      case QxHttpServerDispatchSegment::_wildcard:
         result = true;
         break;
      default:
         break;
      }
      return result;
   }

   void QxHttpServerDispatchSegment::fillParam(const QString &path, QHash<QString, QVariant> &parameters) const
   {
      QVariant vParamValue;
      bool bAddParam = true;
      switch (m_type)
      {
      case QxHttpServerDispatchSegment::_variable:
         vParamValue = QVariant(path);
         break;
      case QxHttpServerDispatchSegment::_variable_and_type:
         vParamValue = QVariant(path);
         break;
      case QxHttpServerDispatchSegment::_variable_and_regexp:
         vParamValue = QVariant(path);
         break;
      default:
         bAddParam = false;
         break;
      }
      if ((bAddParam) && (!m_varName.isEmpty()))
      {
         parameters.insert(m_varName, vParamValue);
      }
   }

   void QxHttpServer::buildResponseStaticFile(qx::QxHttpRequest &request, qx::QxHttpResponse &response, const QString &serverPath, qlonglong chunkedSize /* = 0 */)
   {
      // Check HTTP method GET
      QByteArray requestPath = request.url().path().toLatin1();
      if (request.command() != "GET")
      {
         response.status() = 400;
         response.data() = "Unknown command '" + request.command().toLatin1() + "' to get server static file '" + requestPath + "'";
         return;
      }

      // Check root server path to deliver static files
      QDir dirServerPath(serverPath);
      if (!dirServerPath.exists())
      {
         response.status() = 404;
         response.data() = "Server path for static files not found : " + serverPath.toLatin1();
         return;
      }

      // Check if requested static file exists on server
      QString filePath = dirServerPath.filePath(requestPath.right(requestPath.size() - 1));
      if (!QFile::exists(filePath))
      {
         response.status() = 404;
         response.data() = "Server static file not found : " + requestPath;
         return;
      }

      // Check if requested static file is inside root server path (to avoid ../)
      QDir dirFilePath(filePath);
      QString canonicalServerPath = dirServerPath.canonicalPath();
      QString canonicalFilePath = dirFilePath.canonicalPath();
      if (!canonicalFilePath.startsWith(canonicalServerPath))
      {
         response.status() = 403;
         response.data() = "Cannot access to server static file : " + requestPath;
         return;
      }

      // Try to open static file in read-only mode
      QFile file(filePath);
      if (!file.open(QFile::ReadOnly))
      {
         response.status() = 403;
         response.data() = "Cannot open server static file : " + requestPath;
         return;
      }

#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
      QMimeDatabase mimeDatabase;
      QMimeType mimeType = mimeDatabase.mimeTypeForFile(filePath);
      response.headers().insert("Content-Type", mimeType.name().toLatin1());
#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))

      // Read file content
      if (chunkedSize > 0)
      {
         while (!file.atEnd())
         {
            if (!response.writeChunked(file.read(chunkedSize)))
            {
               return;
            }
         }
      }
      else
      {
         response.data() = file.readAll();
      }
      file.close();
   }

   void QxHttpServer::buildResponseQxRestApi(qx::QxHttpRequest &request, qx::QxHttpResponse &response)
   {
      qx::QxRestApi api;
      QString result = api.processRequest(request.data());
      response.headers().insert("Content-Type", "application/json; charset=utf-8");
      response.data() = result.toUtf8();
   }

} // namespace qx

#endif // _QX_ENABLE_QT_NETWORK
