Home | History | Annotate | Download | only in WebServer
      1 /**
      2   @file
      3   Web server application
      4 
      5   Copyright (c) 2011-2012, Intel Corporation
      6   All rights reserved. This program and the accompanying materials
      7   are licensed and made available under the terms and conditions of the BSD License
      8   which accompanies this distribution.  The full text of the license may be found at
      9   http://opensource.org/licenses/bsd-license.php
     10 
     11   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     12   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     13 
     14 **/
     15 
     16 #include <WebServer.h>
     17 
     18 DT_WEB_SERVER mWebServer;   ///<  Web server's control structure
     19 
     20 
     21 /**
     22   Add a port to the list of ports to be polled.
     23 
     24   @param [in] pWebServer    The web server control structure address.
     25 
     26   @param [in] SocketFD      The socket's file descriptor to add to the list.
     27 
     28   @retval EFI_SUCCESS       The port was successfully added
     29   @retval EFI_NO_RESOURCES  Insufficient memory to add the port
     30 
     31 **/
     32 EFI_STATUS
     33 PortAdd (
     34   IN DT_WEB_SERVER * pWebServer,
     35   IN int SocketFD
     36   )
     37 {
     38   nfds_t Index;
     39   size_t LengthInBytes;
     40   nfds_t MaxEntries;
     41   nfds_t MaxEntriesNew;
     42   struct pollfd * pFdList;
     43   struct pollfd * pFdListNew;
     44   WSDT_PORT ** ppPortListNew;
     45   WSDT_PORT * pPort;
     46   EFI_STATUS Status;
     47 
     48   DBG_ENTER ( );
     49 
     50   //
     51   //  Use for/break instead of goto
     52   //
     53   for ( ; ; ) {
     54     //
     55     //  Assume success
     56     //
     57     Status = EFI_SUCCESS;
     58 
     59     //
     60     //  Create a new list if necessary
     61     //
     62     pFdList = pWebServer->pFdList;
     63     MaxEntries = pWebServer->MaxEntries;
     64     if ( pWebServer->Entries >= MaxEntries ) {
     65       MaxEntriesNew = 16 + MaxEntries;
     66 
     67       //
     68       //  The current FD list is full
     69       //  Allocate a new FD list
     70       //
     71       LengthInBytes = sizeof ( *pFdList ) * MaxEntriesNew;
     72       Status = gBS->AllocatePool ( EfiRuntimeServicesData,
     73                                    LengthInBytes,
     74                                    (VOID **)&pFdListNew );
     75       if ( EFI_ERROR ( Status )) {
     76         DEBUG (( DEBUG_ERROR | DEBUG_POOL,
     77                   "ERROR - Failed to allocate the FD list, Status: %r\r\n",
     78                   Status ));
     79         break;
     80       }
     81 
     82       //
     83       //  Allocate a new port list
     84       //
     85       LengthInBytes = sizeof ( *ppPortListNew ) * MaxEntriesNew;
     86       Status = gBS->AllocatePool ( EfiRuntimeServicesData,
     87                                    LengthInBytes,
     88                                    (VOID **) &ppPortListNew );
     89       if ( EFI_ERROR ( Status )) {
     90         DEBUG (( DEBUG_ERROR | DEBUG_POOL,
     91                   "ERROR - Failed to allocate the port list, Status: %r\r\n",
     92                   Status ));
     93 
     94         //
     95         //  Free the new FD list
     96         //
     97         gBS->FreePool ( pFdListNew );
     98         break;
     99       }
    100 
    101       //
    102       //  Duplicate the FD list
    103       //
    104       Index = MaxEntries;
    105       if ( NULL != pFdList ) {
    106         CopyMem ( pFdListNew,
    107                   pFdList,
    108                   Index * sizeof ( *pFdList ));
    109       }
    110 
    111       //
    112       //  Initialize the new entries in the FD list
    113       //
    114       for ( ; MaxEntriesNew > Index; Index++ ) {
    115         pFdListNew[ Index ].fd = -1;
    116         pFdListNew[ Index ].events = 0;
    117         pFdListNew[ Index ].revents = 0;
    118       }
    119 
    120       //
    121       //  Free the old FD list
    122       //
    123       if ( NULL != pFdList ) {
    124         gBS->FreePool ( pFdList );
    125       }
    126 
    127       //
    128       //  Switch to the new FD list
    129       //
    130       pWebServer->pFdList = pFdListNew;
    131       pFdList = pWebServer->pFdList;
    132 
    133       //
    134       //  Duplicate the port list
    135       //
    136       Index = MaxEntries;
    137       if ( NULL != pWebServer->ppPortList ) {
    138         CopyMem ( ppPortListNew,
    139                   pWebServer->ppPortList,
    140                   Index * sizeof ( *ppPortListNew ));
    141       }
    142 
    143       //
    144       //  Initialize the new entries in the port list
    145       //
    146       for ( ; MaxEntriesNew > Index; Index++ ) {
    147         ppPortListNew[ Index ] = NULL;
    148       }
    149 
    150       //
    151       //  Free the old port list
    152       //
    153       if ( NULL != pWebServer->ppPortList ) {
    154         gBS->FreePool ( pWebServer->ppPortList );
    155       }
    156 
    157       //
    158       //  Switch to the new port list
    159       //
    160       pWebServer->ppPortList = ppPortListNew;
    161 
    162       //
    163       //  Update the list size
    164       //
    165       pWebServer->MaxEntries = MaxEntriesNew;
    166     }
    167 
    168     //
    169     //  Allocate a new port
    170     //
    171     LengthInBytes = sizeof ( *pPort );
    172     Status = gBS->AllocatePool ( EfiRuntimeServicesData,
    173                                  LengthInBytes,
    174                                  (VOID **)&pPort );
    175     if ( EFI_ERROR ( Status )) {
    176       DEBUG (( DEBUG_ERROR | DEBUG_POOL,
    177                 "ERROR - Failed to allocate the port, Status: %r\r\n",
    178                 Status ));
    179       break;
    180     }
    181 
    182     //
    183     //  Initialize the port
    184     //
    185     pPort->RequestLength = 0;
    186     pPort->TxBytes = 0;
    187 
    188     //
    189     //  Add the socket to the FD list
    190     //
    191     pFdList[ pWebServer->Entries ].fd = SocketFD;
    192     pFdList[ pWebServer->Entries ].events = POLLRDNORM
    193                                              | POLLHUP;
    194     pFdList[ pWebServer->Entries ].revents = 0;
    195 
    196     //
    197     //  Add the port to the port list
    198     //
    199     pWebServer->ppPortList[ pWebServer->Entries ] = pPort;
    200 
    201     //
    202     //  Account for the new entry
    203     //
    204     pWebServer->Entries += 1;
    205     DEBUG (( DEBUG_PORT_WORK | DEBUG_INFO,
    206               "WebServer handling %d ports\r\n",
    207               pWebServer->Entries ));
    208 
    209     //
    210     //  All done
    211     //
    212     break;
    213   }
    214 
    215   //
    216   //  Return the operation status
    217   //
    218   DBG_EXIT_STATUS ( Status );
    219   return Status;
    220 }
    221 
    222 
    223 /**
    224   Remove a port from the list of ports to be polled.
    225 
    226   @param [in] pWebServer    The web server control structure address.
    227 
    228   @param [in] SocketFD      The socket's file descriptor to add to the list.
    229 
    230 **/
    231 VOID
    232 PortRemove (
    233   IN DT_WEB_SERVER * pWebServer,
    234   IN int SocketFD
    235   )
    236 {
    237   nfds_t Entries;
    238   nfds_t Index;
    239   struct pollfd * pFdList;
    240   WSDT_PORT ** ppPortList;
    241 
    242   DBG_ENTER ( );
    243 
    244   //
    245   //  Attempt to remove the entry from the list
    246   //
    247   Entries = pWebServer->Entries;
    248   pFdList = pWebServer->pFdList;
    249   ppPortList = pWebServer->ppPortList;
    250   for ( Index = 0; Entries > Index; Index++ ) {
    251     //
    252     //  Locate the specified socket file descriptor
    253     //
    254     if ( SocketFD == pFdList[ Index ].fd ) {
    255       //
    256       //  Determine if this is the listen port
    257       //
    258       if ( SocketFD == pWebServer->HttpListenPort ) {
    259         pWebServer->HttpListenPort = -1;
    260       }
    261 
    262       //
    263       //  Close the socket
    264       //
    265       close ( SocketFD );
    266 
    267       //
    268       //  Free the port structure
    269       //
    270       gBS->FreePool ( ppPortList[ Index ]);
    271 
    272       //
    273       //  Remove this port from the list by copying
    274       //  the rest of the list down one entry
    275       //
    276       Entries -= 1;
    277       for ( ; Entries > Index; Index++ ) {
    278         pFdList[ Index ] = pFdList[ Index + 1 ];
    279         ppPortList[ Index ] = ppPortList[ Index + 1 ];
    280       }
    281       pFdList[ Index ].fd = -1;
    282       pFdList[ Index ].events = 0;
    283       pFdList[ Index ].revents = 0;
    284       ppPortList[ Index ] = NULL;
    285 
    286       //
    287       //  Update the number of entries in the list
    288       //
    289       pWebServer->Entries = Entries;
    290       DEBUG (( DEBUG_PORT_WORK | DEBUG_INFO,
    291                 "WebServer handling %d ports\r\n",
    292                 pWebServer->Entries ));
    293       break;
    294     }
    295   }
    296 
    297   DBG_EXIT ( );
    298 }
    299 
    300 
    301 /**
    302   Process the work for the sockets.
    303 
    304   @param [in] pWebServer    The web server control structure address.
    305 
    306   @param [in] SocketFD      The socket's file descriptor to add to the list.
    307 
    308   @param [in] events        everts is a bitmask of the work to be done
    309 
    310   @param [in] pPort         The address of a WSDT_PORT structure
    311 
    312   @retval EFI_SUCCESS       The operation was successful
    313   @retval EFI_DEVICE_ERROR  Error, close the port
    314 
    315 **/
    316 EFI_STATUS
    317 PortWork (
    318   IN DT_WEB_SERVER * pWebServer,
    319   IN int SocketFD,
    320   IN INTN events,
    321   IN WSDT_PORT * pPort
    322   )
    323 {
    324   BOOLEAN bDone;
    325   size_t LengthInBytes;
    326   int NewSocket;
    327   EFI_STATUS OpStatus;
    328   struct sockaddr_in6 RemoteAddress;
    329   socklen_t RemoteAddressLength;
    330   EFI_STATUS Status;
    331 
    332   DEBUG (( DEBUG_PORT_WORK, "Entering PortWork\r\n" ));
    333 
    334   //
    335   //  Assume success
    336   //
    337   OpStatus = EFI_SUCCESS;
    338 
    339   //
    340   //  Handle input events
    341   //
    342   if ( 0 != ( events & POLLRDNORM )) {
    343     //
    344     //  Determine if this is a connection attempt
    345     //
    346     if (( SocketFD == pWebServer->HttpListenPort )
    347       || ( SocketFD == pWebServer->HttpListenPort6 )) {
    348       //
    349       //  Handle connection attempts
    350       //  Accepts arrive as read events
    351       //
    352       RemoteAddressLength = sizeof ( RemoteAddress );
    353       NewSocket = accept ( SocketFD,
    354                            (struct sockaddr *)&RemoteAddress,
    355                            &RemoteAddressLength );
    356       if ( -1 != NewSocket ) {
    357         if ( 0 != NewSocket ) {
    358           //
    359           //  Add this port to the list monitored by the web server
    360           //
    361           Status = PortAdd ( pWebServer, NewSocket );
    362           if ( EFI_ERROR ( Status )) {
    363             DEBUG (( DEBUG_ERROR,
    364                       "ERROR - Failed to add the port 0x%08x, Status: %r\r\n",
    365                       NewSocket,
    366                       Status ));
    367 
    368             //
    369             //  Done with the new socket
    370             //
    371             close ( NewSocket );
    372           }
    373         }
    374         else {
    375           DEBUG (( DEBUG_ERROR,
    376                     "ERROR - Socket not available!\r\n" ));
    377         }
    378 
    379         //
    380         //  Leave the listen port open
    381         //
    382       }
    383       else {
    384         //
    385         //  Listen port error
    386         //  Close the listen port by returning error status
    387         //
    388         OpStatus = EFI_DEVICE_ERROR;
    389         DEBUG (( DEBUG_ERROR,
    390                   "ERROR - Failed to accept new connection, errno: 0x%08x\r\n",
    391                   errno ));
    392       }
    393     }
    394     else {
    395       //
    396       //  Handle the data received event
    397       //
    398       if ( 0 == pPort->RequestLength ) {
    399         //
    400         //  Receive the page request
    401         //
    402         pPort->RequestLength = recv ( SocketFD,
    403                                       &pPort->Request[0],
    404                                       DIM ( pPort->Request ),
    405                                       0 );
    406         if ( -1 == pPort->RequestLength ) {
    407           //
    408           //  Receive error detected
    409           //  Close the port
    410           //
    411           OpStatus = EFI_DEVICE_ERROR;
    412         }
    413         else {
    414           DEBUG (( DEBUG_REQUEST,
    415                     "0x%08x: Socket - Received %d bytes of HTTP request\r\n",
    416                     SocketFD,
    417                     pPort->RequestLength ));
    418 
    419           //
    420           //  Process the request
    421           //
    422           OpStatus = HttpRequest ( SocketFD, pPort, &bDone );
    423           if ( bDone ) {
    424             //
    425             //  Notify the upper layer to close the socket
    426             //
    427             OpStatus = EFI_DEVICE_ERROR;
    428           }
    429         }
    430       }
    431       else {
    432         //
    433         //  Receive the file data
    434         //
    435         LengthInBytes = recv ( SocketFD,
    436                                &pPort->RxBuffer[0],
    437                                DIM ( pPort->RxBuffer ),
    438                                0 );
    439         if ( -1 == LengthInBytes ) {
    440           //
    441           //  Receive error detected
    442           //  Close the port
    443           //
    444           OpStatus = EFI_DEVICE_ERROR;
    445         }
    446         else {
    447           DEBUG (( DEBUG_REQUEST,
    448                     "0x%08x: Socket - Received %d bytes of file data\r\n",
    449                     SocketFD,
    450                     LengthInBytes ));
    451 
    452           //
    453           // TODO: Process the file data
    454           //
    455         }
    456       }
    457     }
    458   }
    459 
    460   //
    461   //  Handle the close event
    462   //
    463   if ( 0 != ( events & POLLHUP )) {
    464     //
    465     //  Close the port
    466     //
    467     OpStatus = EFI_DEVICE_ERROR;
    468   }
    469 
    470   //
    471   //  Return the operation status
    472   //
    473   DEBUG (( DEBUG_PORT_WORK,
    474             "Exiting PortWork, Status: %r\r\n",
    475             OpStatus ));
    476   return OpStatus;
    477 }
    478 
    479 
    480 /**
    481   Scan the list of sockets and process any pending work
    482 
    483   @param [in] pWebServer    The web server control structure address.
    484 
    485 **/
    486 VOID
    487 SocketPoll (
    488   IN DT_WEB_SERVER * pWebServer
    489   )
    490 {
    491   int FDCount;
    492   struct pollfd * pPoll;
    493   WSDT_PORT ** ppPort;
    494   EFI_STATUS Status;
    495 
    496   DEBUG (( DEBUG_SOCKET_POLL, "Entering SocketPoll\r\n" ));
    497 
    498   //
    499   //  Determine if any ports are active
    500   //
    501   FDCount = poll ( pWebServer->pFdList,
    502                    pWebServer->Entries,
    503                    CLIENT_POLL_DELAY );
    504   if ( -1 == FDCount ) {
    505     DEBUG (( DEBUG_ERROR | DEBUG_SOCKET_POLL,
    506               "ERROR - errno: %d\r\n",
    507               errno ));
    508   }
    509 
    510   pPoll = pWebServer->pFdList;
    511   ppPort = pWebServer->ppPortList;
    512   while ( 0 < FDCount ) {
    513     //
    514     //  Walk the list of ports to determine what work needs to be done
    515     //
    516     if ( 0 != pPoll->revents ) {
    517       //
    518       //  Process this port
    519       //
    520       Status = PortWork ( pWebServer,
    521                           pPoll->fd,
    522                           pPoll->revents,
    523                           *ppPort );
    524       pPoll->revents = 0;
    525 
    526       //
    527       //  Close the port if necessary
    528       //
    529       if ( EFI_ERROR ( Status )) {
    530         PortRemove ( pWebServer, pPoll->fd );
    531         pPoll -= 1;
    532         ppPort -= 1;
    533       }
    534 
    535       //
    536       //  Account for this file descriptor
    537       //
    538       FDCount -= 1;
    539     }
    540 
    541     //
    542     //  Set the next port
    543     //
    544     pPoll += 1;
    545     ppPort += 1;
    546   }
    547 
    548   DEBUG (( DEBUG_SOCKET_POLL, "Exiting SocketPoll\r\n" ));
    549 }
    550 
    551 
    552 /**
    553   Create an HTTP port for the web server
    554 
    555   This routine polls the network layer to create an HTTP port for the
    556   web server.  More than one attempt may be necessary since it may take
    557   some time to get the IP address and initialize the upper layers of
    558   the network stack.
    559 
    560   After the HTTP port is created, the socket layer will manage the
    561   coming and going of the network connections until the last network
    562   connection is broken.
    563 
    564   @param [in] pWebServer    The web server control structure address.
    565   @param [in] AddressFamily Address family for the network connection
    566   @param [in] Protocol      Protocol to use for the network connection
    567   @param [in] HttpPort      Port number for the HTTP connection
    568   @param [out] pPort        Address of the port
    569 
    570 **/
    571 VOID
    572 WebServerListen (
    573   IN DT_WEB_SERVER * pWebServer,
    574   IN sa_family_t AddressFamily,
    575   IN int Protocol,
    576   IN UINT16 HttpPort,
    577   OUT int * pPort
    578   )
    579 {
    580   union {
    581     struct sockaddr_in v4;
    582     struct sockaddr_in6 v6;
    583   } WebServerAddress;
    584   int SocketStatus;
    585   EFI_STATUS Status;
    586 
    587   DEBUG (( DEBUG_SERVER_LISTEN, "Entering WebServerListen\r\n" ));
    588 
    589   //
    590   //  Attempt to create the socket for the web server
    591   //
    592   * pPort = socket ( AddressFamily, SOCK_STREAM, Protocol );
    593   if ( -1 != *pPort ) {
    594     //
    595     //  Build the socket address
    596     //
    597     ZeroMem ( &WebServerAddress, sizeof ( WebServerAddress ));
    598     if ( AF_INET == AddressFamily ) {
    599       WebServerAddress.v4.sin_len = sizeof ( WebServerAddress.v4 );
    600       WebServerAddress.v4.sin_family = AddressFamily;
    601       WebServerAddress.v4.sin_port = htons ( HttpPort );
    602     }
    603     else {
    604       WebServerAddress.v6.sin6_len = sizeof ( WebServerAddress.v6 );
    605       WebServerAddress.v6.sin6_family = AddressFamily;
    606       WebServerAddress.v6.sin6_port = htons ( HttpPort );
    607       WebServerAddress.v6.sin6_scope_id = __IPV6_ADDR_SCOPE_GLOBAL;
    608     }
    609 
    610     //
    611     //  Bind the socket to the HTTP port
    612     //
    613     SocketStatus = bind ( *pPort,
    614                           (struct sockaddr *) &WebServerAddress,
    615                           WebServerAddress.v4.sin_len );
    616     if ( -1 != SocketStatus ) {
    617       //
    618       //  Enable connections to the HTTP port
    619       //
    620       SocketStatus = listen ( *pPort, SOMAXCONN );
    621       if ( -1 != SocketStatus ) {
    622         //
    623         //  Add the HTTP port to the list of ports to poll
    624         //
    625         Status = PortAdd ( pWebServer, *pPort );
    626         if ( EFI_ERROR ( Status )) {
    627           SocketStatus = -1;
    628         }
    629         else {
    630           DEBUG (( DEBUG_PORT_WORK,
    631                     "Listening on Tcp%d:%d\r\n",
    632                     ( AF_INET == AddressFamily ) ? 4 : 6,
    633                     HttpPort ));
    634         }
    635       }
    636     }
    637 
    638     //
    639     //  Release the socket if necessary
    640     //
    641     if ( -1 == SocketStatus ) {
    642       close ( *pPort );
    643       *pPort = -1;
    644     }
    645   }
    646 
    647   DEBUG (( DEBUG_SERVER_LISTEN, "Exiting WebServerListen\r\n" ));
    648 }
    649 
    650 
    651 /**
    652   Entry point for the web server application.
    653 
    654   @param [in] Argc  The number of arguments
    655   @param [in] Argv  The argument value array
    656 
    657   @retval  0        The application exited normally.
    658   @retval  Other    An error occurred.
    659 **/
    660 int
    661 main (
    662   IN int Argc,
    663   IN char **Argv
    664   )
    665 {
    666   UINT16 HttpPort;
    667   UINTN Index;
    668   DT_WEB_SERVER * pWebServer;
    669   EFI_STATUS Status;
    670   UINT64 TriggerTime;
    671 
    672   //
    673   //  Get the HTTP port
    674   //
    675   HttpPort = PcdGet16 ( WebServer_HttpPort );
    676   DEBUG (( DEBUG_HTTP_PORT,
    677             "HTTP Port: %d\r\n",
    678             HttpPort ));
    679 
    680   //
    681   //  Create a timer event to start HTTP port
    682   //
    683   pWebServer = &mWebServer;
    684   Status = gBS->CreateEvent ( EVT_TIMER,
    685                               TPL_WEB_SERVER,
    686                               NULL,
    687                               NULL,
    688                               &pWebServer->TimerEvent );
    689   if ( !EFI_ERROR ( Status )) {
    690     TriggerTime = HTTP_PORT_POLL_DELAY * ( 1000 * 10 );
    691     Status = gBS->SetTimer ( pWebServer->TimerEvent,
    692                              TimerPeriodic,
    693                              TriggerTime );
    694     if ( !EFI_ERROR ( Status )) {
    695       //
    696       //  Run the web server forever
    697       //
    698       pWebServer->HttpListenPort = -1;
    699       pWebServer->HttpListenPort6 = -1;
    700       pWebServer->bRunning = TRUE;
    701       do {
    702         //
    703         //  Poll the network layer to create the HTTP port
    704         //  for the web server.  More than one attempt may
    705         //  be necessary since it may take some time to get
    706         //  the IP address and initialize the upper layers
    707         //  of the network stack.
    708         //
    709         if (( -1 == pWebServer->HttpListenPort )
    710           || ( -1 == pWebServer->HttpListenPort6 )) {
    711           do {
    712             //
    713             //  Wait a while before polling for a connection
    714             //
    715             if ( EFI_SUCCESS != gBS->CheckEvent ( pWebServer->TimerEvent )) {
    716               if ( 0 != pWebServer->Entries ) {
    717                   break;
    718               }
    719               gBS->WaitForEvent ( 1, &pWebServer->TimerEvent, &Index );
    720             }
    721 
    722             //
    723             //  Poll for a network connection
    724             //
    725             if ( -1 == pWebServer->HttpListenPort ) {
    726               WebServerListen ( pWebServer,
    727                                 AF_INET,
    728                                 IPPROTO_TCP,
    729                                 HttpPort,
    730                                 &pWebServer->HttpListenPort );
    731             }
    732             if ( -1 == pWebServer->HttpListenPort6 ) {
    733               WebServerListen ( pWebServer,
    734                                 AF_INET6,
    735                                 IPPROTO_TCP,
    736                                 HttpPort,
    737                                 &pWebServer->HttpListenPort6 );
    738             }
    739 
    740             //
    741             //  Continue polling while both network connections are
    742             //  not present
    743             //
    744           } while ( 0 == pWebServer->Entries );
    745         }
    746 
    747         //
    748         //  Poll the sockets for activity while both network
    749         //  connections are connected
    750         //
    751         do {
    752           SocketPoll ( pWebServer );
    753         } while ( pWebServer->bRunning
    754                 && ( -1 != pWebServer->HttpListenPort )
    755                 && ( -1 != pWebServer->HttpListenPort6 ));
    756 
    757         //
    758         //  Continue polling the network connections until both
    759         //  TCP4 and TCP6 are connected
    760         //
    761       } while ( pWebServer->bRunning );
    762 
    763       //
    764       //  Stop the timer
    765       //
    766       gBS->SetTimer ( pWebServer->TimerEvent,
    767                       TimerCancel,
    768                       0 );
    769     }
    770 
    771     //
    772     //  Done with the timer event
    773     //
    774     gBS->CloseEvent ( pWebServer->TimerEvent );
    775   }
    776 
    777   //
    778   //  Return the final status
    779   //
    780   DBG_EXIT_STATUS ( Status );
    781   return Status;
    782 }
    783