    // ------------------------------- //
    // -------- Start of File -------- //
    // ------------------------------- //
    // ----------------------------------------------------------- //
    // C++ Source Code File Name: m_thread.cpp
    // C++ Compiler Used: MSVC, GCC
    // Produced By: DataReel Software Development Team
    // File Creation Date: 10/17/2001
    // Date Last Modified: 06/17/2016
    // Copyright (c) 2001-2024 DataReel Software Development
    // ----------------------------------------------------------- //
    // ------------- Program Description and Details ------------- //
    // ----------------------------------------------------------- //
    /*
    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
    USA

    Multi-threaded server framework using the gxSSL and gxThread
    libraries.
    */
    // ----------------------------------------------------------- //
    #include "gxdlcode.h"

    #if defined (__USE_ANSI_CPP__) // Use the ANSI Standard C++ library
    #include <iostream>
    #else // Use the old iostream library by default
    #include <iostream.h>
    #endif // __USE_ANSI_CPP__

    #include <string.h>
    #include "m_thread.h"

    // --------------------------------------------------------------
    // Globals variable initialzation
    // --------------------------------------------------------------
    ServerConfig ServerConfigSruct;
    ServerConfig *servercfg = &ServerConfigSruct;
    // --------------------------------------------------------------

    void ClientSocket_t::FreeSSL()
    {
      if(openssl) {
        openssl->ReleaseCTX(); // Do not free Context by ~gxSSL() call
        delete openssl;
        openssl = 0;
      }
    }

    int CheckSocketError(gxSocket *s, const char *mesg, int report_error)
    // Test the socket for an error condition and report the message
    // if the "report_error" variable is true. The "mesg" string is used
    // display a message with the reported error. Returns true if an
    // error was detected for false of no errors where detected.
    {
      if(s->GetSocketError() == gxSOCKET_NO_ERROR) {
        // No socket errors reported
        return 0;
      }
      if(report_error) { // Reporting the error to an output device
        if(mesg) {
          PrintMessage(mesg, "\n", s->SocketExceptionMessage());
        }
        else {
          PrintMessage(s->SocketExceptionMessage());
        }
      }
      return 1;
    }

    int CheckThreadError(gxThread_t *thread, const char *mesg, int report_error)
    // Test the thread for an error condition and report the message
    // if the "report_error" variable is true. The "mesg" string is used
    // display a message with the reported error. Returns true if an
    // error was detected for false of no errors where detected.
    {
      if(thread->GetThreadError() == gxTHREAD_NO_ERROR) {
        // No thread errors reported
        return 0;
      }
      if(report_error) { // Reporting the error to an output device
        if(mesg) {
          PrintMessage(mesg, "\n", thread->ThreadExceptionMessage());
        }
        else {
          PrintMessage(thread->ThreadExceptionMessage());
        }
      }
      return 1;
    }

    void ReportError(const char *s1, const char *s2, const char *s3)
    // Thread safe error reporting function.
    {
      PrintMessage(s1, s2, s3);
    }

    void PrintMessage(const char *s1, const char *s2, const char *s3)
    // Thread safe write function that will not allow access to
    // the critical section until the write operation is complete.
    {
      servercfg->display_lock.MutexLock();

      // Ensure that all threads have finished writing to the device
      int num_try = 0;
      while(servercfg->display_is_locked != 0) {
        // Block this thread from its own execution if a another thread
        // is writing to the device
        if(++num_try < DISPLAY_THREAD_RETRIES) {
          servercfg->display_cond.ConditionWait(&servercfg->display_lock);
        }
        else {
          return; // Could not write string to the device
        }
      }

      // Tell other threads to wait until write is complete
      servercfg->display_is_locked = 1;

      // ********** Enter Critical Section ******************* //
      GXSTD::cout << "\n";
      if(s1) GXSTD::cout << s1;
      if(s2) GXSTD::cout << s2;
      if(s3) GXSTD::cout << s3;
      GXSTD::cout << "\n";
      GXSTD::cout.flush(); // Flush the ostream buffer to the stdio
      // ********** Leave Critical Section ******************* //

      // Tell other threads that this write is complete
      servercfg->display_is_locked = 0;

      // Wake up the next thread waiting on this condition
      servercfg->display_cond.ConditionSignal();
      servercfg->display_lock.MutexUnlock();
    }

    ServerConfig::ServerConfig()
    {
      port = 80;
      accept_clients = 1;
      display_is_locked = 0;
      client_request_pool = new thrPool;
    }

    ServerConfig::~ServerConfig()
    {
      if(client_request_pool) delete client_request_pool;
    }

    int ServerThread::InitServer(int max_connections)
    // Initialize the server. Returns a non-zero value if any
    // errors ocurr.
    {
      if(InitSocketLibrary() == 0) {
        if(InitSocket(SOCK_STREAM, servercfg->port) < 0) return 1;
      }
      else {
        return 1;
      }

      int val = 1;
      if(SetSockOpt(gxsocket, SOL_SOCKET, SO_REUSEADDR,
    		&val, sizeof(val)) < 0) {
        CheckSocketError((gxSocket *)this, "Error initializing server");
        Close();
        return 1;
      }

      // Bind the name to the socket
      if(Bind() < 0) {
        CheckSocketError((gxSocket *)this, "Error initializing server");
        Close();
        return 1;
      }

      // Listen for connections with a specified backlog
      if(Listen(max_connections) < 0) {
        CheckSocketError((gxSocket *)this, "Error initializing server");
        Close();
        return 1;
      }

      return 0;
    }

    void *ServerThread::ThreadEntryRoutine(gxThread_t *thread)
    {
      while(servercfg->accept_clients) { // Loop accepting client connections

        // Block the server until a client requests service
        if(Accept() < 0) {
          PrintMessage("Error accepting client connections");
          return 0;
        }

        // NOTE: Getting client info for statistical purposes only
        char client_name[gxsMAX_NAME_LEN]; int r_port = -1;
        GetClientInfo(client_name, r_port);
        PrintMessage("Received client request from ", client_name);

        ClientSocket_t *s = new ClientSocket_t;
        if(!s) {
          ReportError("A fatal memory allocation error occurred\n \
    Shutting down the server");
          CloseSocket();
          return (void *)0;
        }

        SSL_CTX_set_cipher_list(openssl.GetCTX(), "ALL");

        // Record the file descriptor assigned by the Operating System
        s->client_socket = GetRemoteSocket();

        s->openssl = new gxSSL(openssl.GetCTX());
        if(!s->openssl) {
          PrintMessage("A fatal memory allocation error occurred\n \
    Shutting down the server");
          delete s;
          return (void *)0;
        }

        // Destroy all the client threads that have exited
        RebuildThreadPool(servercfg->client_request_pool);

        // Create thread per cleint
        gxThread_t *rthread = \
          request_thread.CreateThread(servercfg->client_request_pool, (void *)s);
        if(CheckThreadError(rthread, "Error starting client request thread")) {
          delete s;
          return (void *)0;
        }
      }

      return (void *)0;
    }

    void ServerThread::ThreadExitRoutine(gxThread_t *thread)
    // Thread exit function used to close the server thread.
    {
      CloseSocket(); // Close the server side socket
    }

    void ServerThread::ThreadCleanupHandler(gxThread_t *thread)
    // Thread cleanup handler used in the event that the thread is
    // canceled.
    {
      CloseSocket(); // Close the server side socket
    }

    void *ClientRequestThread::ThreadEntryRoutine(gxThread_t *thread)
    {
      // Extract the client socket from the thread parameter
      ClientSocket_t *s = (ClientSocket_t *)thread->GetThreadParm();

      // Process the client request
      HandleClientRequest(s);

      return (void *)0;
    }

    void ClientRequestThread::ThreadExitRoutine(gxThread_t *thread)
    // Thread exit function used to close the client thread.
    {
      // Extract the client socket from the thread parameter
      ClientSocket_t *s = (ClientSocket_t *)thread->GetThreadParm();

      // Close the client socket and free its resources
      s->openssl->CloseSSLSocket();
      Close(s->client_socket);
      delete s;
    }

    void ClientRequestThread::ThreadCleanupHandler(gxThread_t *thread)
    // Thread cleanup handler used in the event that the thread is
    // canceled.
    {
      // Extract the client socket from the thread parameter
      ClientSocket_t *s = (ClientSocket_t *)thread->GetThreadParm();

      // Close the client socket and free its resources
      s->openssl->CloseSSLSocket();
      Close(s->client_socket);
      delete s;
    }

    void ClientRequestThread::HandleClientRequest(ClientSocket_t *s)
    // Function used to handle a client request.
    {
      if(s->openssl->OpenSSLSocket(s->client_socket) != gxSSL_NO_ERROR) {
        PrintMessage("Error opening SSL socket");
        PrintMessage(s->openssl->SSLErrorMessage());
        socket_error = gxSOCKET_SSL_ERROR;
        return;
      }

      if(s->openssl->AcceptSSLSocket() != gxSSL_NO_ERROR) {
        PrintMessage("Error accepting SSL socket");
        PrintMessage(s->openssl->SSLErrorMessage());
        socket_error = gxSOCKET_SSL_ERROR;
        gxString err;
        gxSSL::GlobalSSLError(err);
        PrintMessage(err.c_str());
        return;
      }

      // NOTE: The demo processes a single HTTP GET request per client
      const int rxbuf = 1024;
      char request[rxbuf];

      // Block until the client sends some data
      int rv = s->openssl->RawReadSSLSocket(request, rxbuf);

      // A socket error occurred
      if(rv <= 0) {
        PrintMessage("Error reading SSL socket");
        PrintMessage(s->openssl->SSLErrorMessage());
        socket_error = gxSOCKET_RECEIVE_ERROR;
        return;
      }

      request[rv] = 0; // Null terminate the request string

      // At this point we need to parse the client request and process it.
      // For simplicity sake these operations have been omitted.
      // ... (parsing operation)
      // ... (processing operation)

      // Simply send a test page (with a header) back to the client.
      const char *test_page = "HTTP/1.0 200 OK\r\nServer: gxThread\r\nConnection: \
    close\r\n\r\n<HTML>\n<HEAD>\n<TITLE> gxThread Test Page </TITLE>\n</HEAD>\
    <BODY><CENTER><H1>gxThread Test Page</H1></CENTER><HR><PRE>Response from the \
    multi-threaded HTTP server</PRE><HR><CENTER>End of document</CENTER>\n</BODY>\
    \n</HTML>";

      // Blocking send that will not return until all bytes are written
      //  Send(s->client_socket, test_page, strlen(test_page));
      //  CheckSocketError((gxSocket *)this, "Error sending page to client");

      rv = s->openssl->SendSSLSocket(test_page, strlen(test_page));
      if(s->openssl->HasError()) {
        if(s->openssl->GetSSLError() == gxSSL_DISCONNECTED_ERROR) {
          PrintMessage("Error writing to SSL socket");
          PrintMessage(s->openssl->SSLErrorMessage());
          socket_error = gxSOCKET_DISCONNECTED_ERROR;
        }
        else {
          PrintMessage("Error writing to SSL socket");
          PrintMessage(s->openssl->SSLErrorMessage());
          socket_error = gxSOCKET_TRANSMIT_ERROR;
        }
      }
      if(rv != (int)strlen(test_page)) {
        PrintMessage("Error writing to SSL socket");
        PrintMessage(s->openssl->SSLErrorMessage());
        socket_error = gxSOCKET_TRANSMIT_ERROR;
      }
      CheckSocketError((gxSocket *)this, "Error sending page to client");
    }
    // ----------------------------------------------------------- //
    // ------------------------------- //
    // --------- End of File --------- //
    // ------------------------------- //