summaryrefslogtreecommitdiff
path: root/AppPkg/Applications/Sockets/WebServer/WebServer.c
diff options
context:
space:
mode:
Diffstat (limited to 'AppPkg/Applications/Sockets/WebServer/WebServer.c')
-rw-r--r--AppPkg/Applications/Sockets/WebServer/WebServer.c853
1 files changed, 853 insertions, 0 deletions
diff --git a/AppPkg/Applications/Sockets/WebServer/WebServer.c b/AppPkg/Applications/Sockets/WebServer/WebServer.c
new file mode 100644
index 0000000000..00bf4b6fef
--- /dev/null
+++ b/AppPkg/Applications/Sockets/WebServer/WebServer.c
@@ -0,0 +1,853 @@
+/*++
+ This file contains an 'Intel UEFI Application' and is
+ licensed for Intel CPUs and chipsets under the terms of your
+ license agreement with Intel or your vendor. This file may
+ be modified by the user, subject to additional terms of the
+ license agreement
+--*/
+/*++
+
+Copyright (c) 2011 Intel Corporation. All rights reserved
+This software and associated documentation (if any) is furnished
+under a license and may only be used or copied in accordance
+with the terms of the license. Except as permitted by such
+license, no part of this software or documentation may be
+reproduced, stored in a retrieval system, or transmitted in any
+form or by any means without the express written consent of
+Intel Corporation.
+
+--*/
+
+/** @file
+ This is a simple shell application
+
+ This should be executed with "/Param2 Val1" and "/Param1" as the 2 command line options!
+
+**/
+
+#include <WebServer.h>
+
+DT_WEB_SERVER mWebServer; ///< Web server's control structure
+
+
+/**
+ Add a port to the list of ports to be polled.
+
+ @param [in] pWebServer The web server control structure address.
+
+ @param [in] SocketFD The socket's file descriptor to add to the list.
+
+ @retval EFI_SUCCESS The port was successfully added
+ @retval EFI_NO_RESOURCES Insufficient memory to add the port
+
+**/
+EFI_STATUS
+PortAdd (
+ IN DT_WEB_SERVER * pWebServer,
+ IN int SocketFD
+ )
+{
+ nfds_t Index;
+ size_t LengthInBytes;
+ nfds_t MaxEntries;
+ nfds_t MaxEntriesNew;
+ struct pollfd * pFdList;
+ struct pollfd * pFdListNew;
+ WSDT_PORT ** ppPortListNew;
+ WSDT_PORT * pPort;
+ EFI_STATUS Status;
+
+ DBG_ENTER ( );
+
+ //
+ // Use for/break instead of goto
+ //
+ for ( ; ; ) {
+ //
+ // Assume success
+ //
+ Status = EFI_SUCCESS;
+
+ //
+ // Create a new list if necessary
+ //
+ pFdList = pWebServer->pFdList;
+ MaxEntries = pWebServer->MaxEntries;
+ if ( pWebServer->Entries >= MaxEntries ) {
+ MaxEntriesNew = 16 + MaxEntries;
+
+ //
+ // The current FD list is full
+ // Allocate a new FD list
+ //
+ LengthInBytes = sizeof ( *pFdList ) * MaxEntriesNew;
+ Status = gBS->AllocatePool ( EfiRuntimeServicesData,
+ LengthInBytes,
+ (VOID **)&pFdListNew );
+ if ( EFI_ERROR ( Status )) {
+ DEBUG (( DEBUG_ERROR | DEBUG_POOL,
+ "ERROR - Failed to allocate the FD list, Status: %r\r\n",
+ Status ));
+ break;
+ }
+
+ //
+ // Allocate a new port list
+ //
+ LengthInBytes = sizeof ( *ppPortListNew ) * MaxEntriesNew;
+ Status = gBS->AllocatePool ( EfiRuntimeServicesData,
+ LengthInBytes,
+ (VOID **) &ppPortListNew );
+ if ( EFI_ERROR ( Status )) {
+ DEBUG (( DEBUG_ERROR | DEBUG_POOL,
+ "ERROR - Failed to allocate the port list, Status: %r\r\n",
+ Status ));
+
+ //
+ // Free the new FD list
+ //
+ gBS->FreePool ( pFdListNew );
+ break;
+ }
+
+ //
+ // Duplicate the FD list
+ //
+ Index = MaxEntries;
+ if ( NULL != pFdList ) {
+ CopyMem ( pFdListNew,
+ pFdList,
+ Index * sizeof ( *pFdList ));
+ }
+
+ //
+ // Initialize the new entries in the FD list
+ //
+ for ( ; MaxEntriesNew > Index; Index++ ) {
+ pFdListNew [ Index ].fd = -1;
+ pFdListNew [ Index ].events = 0;
+ pFdListNew [ Index ].revents = 0;
+ }
+
+ //
+ // Free the old FD list
+ //
+ if ( NULL != pFdList ) {
+ gBS->FreePool ( pFdList );
+ }
+
+ //
+ // Switch to the new FD list
+ //
+ pWebServer->pFdList = pFdListNew;
+ pFdList = pWebServer->pFdList;
+
+ //
+ // Duplicate the port list
+ //
+ Index = MaxEntries;
+ if ( NULL != pWebServer->ppPortList ) {
+ CopyMem ( ppPortListNew,
+ pWebServer->ppPortList,
+ Index * sizeof ( *ppPortListNew ));
+ }
+
+ //
+ // Initialize the new entries in the port list
+ //
+ for ( ; MaxEntriesNew > Index; Index++ ) {
+ ppPortListNew [ Index ] = NULL;
+ }
+
+ //
+ // Free the old port list
+ //
+ if ( NULL != pWebServer->ppPortList ) {
+ gBS->FreePool ( pWebServer->ppPortList );
+ }
+
+ //
+ // Switch to the new port list
+ //
+ pWebServer->ppPortList = ppPortListNew;
+
+ //
+ // Update the list size
+ //
+ pWebServer->MaxEntries = MaxEntriesNew;
+ }
+
+ //
+ // Allocate a new port
+ //
+ LengthInBytes = sizeof ( *pPort );
+ Status = gBS->AllocatePool ( EfiRuntimeServicesData,
+ LengthInBytes,
+ (VOID **)&pPort );
+ if ( EFI_ERROR ( Status )) {
+ DEBUG (( DEBUG_ERROR | DEBUG_POOL,
+ "ERROR - Failed to allocate the port, Status: %r\r\n",
+ Status ));
+ break;
+ }
+
+ //
+ // Initialize the port
+ //
+ pPort->RequestLength = 0;
+ pPort->TxBytes = 0;
+
+ //
+ // Add the socket to the FD list
+ //
+ pFdList [ pWebServer->Entries ].fd = SocketFD;
+ pFdList [ pWebServer->Entries ].events = POLLRDNORM
+ | POLLHUP;
+ pFdList [ pWebServer->Entries ].revents = 0;
+
+ //
+ // Add the port to the port list
+ //
+ pWebServer->ppPortList [ pWebServer->Entries ] = pPort;
+
+ //
+ // Account for the new entry
+ //
+ pWebServer->Entries += 1;
+ DEBUG (( DEBUG_PORT_WORK | DEBUG_INFO,
+ "WebServer handling %d ports\r\n",
+ pWebServer->Entries ));
+
+ //
+ // All done
+ //
+ break;
+ }
+
+ //
+ // Return the operation status
+ //
+ DBG_EXIT_STATUS ( Status );
+ return Status;
+}
+
+
+/**
+ Remove a port from the list of ports to be polled.
+
+ @param [in] pWebServer The web server control structure address.
+
+ @param [in] SocketFD The socket's file descriptor to add to the list.
+
+**/
+VOID
+PortRemove (
+ IN DT_WEB_SERVER * pWebServer,
+ IN int SocketFD
+ )
+{
+ nfds_t Entries;
+ nfds_t Index;
+ struct pollfd * pFdList;
+ WSDT_PORT ** ppPortList;
+
+ DBG_ENTER ( );
+
+ //
+ // Attempt to remove the entry from the list
+ //
+ Entries = pWebServer->Entries;
+ pFdList = pWebServer->pFdList;
+ ppPortList = pWebServer->ppPortList;
+ for ( Index = 0; Entries > Index; Index++ ) {
+ //
+ // Locate the specified socket file descriptor
+ //
+ if ( SocketFD == pFdList [ Index ].fd ) {
+ //
+ // Determine if this is the listen port
+ //
+ if ( SocketFD == pWebServer->HttpListenPort ) {
+ pWebServer->HttpListenPort = -1;
+ }
+
+ //
+ // Close the socket
+ //
+ close ( SocketFD );
+
+ //
+ // Free the port structure
+ //
+ gBS->FreePool ( ppPortList [ Index ]);
+
+ //
+ // Remove this port from the list by copying
+ // the rest of the list down one entry
+ //
+ Entries -= 1;
+ for ( ; Entries > Index; Index++ ) {
+ pFdList [ Index ] = pFdList [ Index + 1 ];
+ ppPortList [ Index ] = ppPortList [ Index + 1 ];
+ }
+ pFdList [ Index ].fd = -1;
+ pFdList [ Index ].events = 0;
+ pFdList [ Index ].revents = 0;
+ ppPortList [ Index ] = NULL;
+
+ //
+ // Update the number of entries in the list
+ //
+ pWebServer->Entries = Entries;
+ DEBUG (( DEBUG_PORT_WORK | DEBUG_INFO,
+ "WebServer handling %d ports\r\n",
+ pWebServer->Entries ));
+ break;
+ }
+ }
+
+ DBG_EXIT ( );
+}
+
+
+/**
+ Process the work for the sockets.
+
+ @param [in] pWebServer The web server control structure address.
+
+ @param [in] SocketFD The socket's file descriptor to add to the list.
+
+ @param [in] events everts is a bitmask of the work to be done
+
+ @param [in] pPort The address of a WSDT_PORT structure
+
+ @retval EFI_SUCCESS The operation was successful
+ @retval EFI_DEVICE_ERROR Error, close the port
+
+**/
+EFI_STATUS
+PortWork (
+ IN DT_WEB_SERVER * pWebServer,
+ IN int SocketFD,
+ IN INTN events,
+ IN WSDT_PORT * pPort
+ )
+{
+ BOOLEAN bDone;
+ size_t LengthInBytes;
+ int NewSocket;
+ EFI_STATUS OpStatus;
+ struct sockaddr RemoteAddress;
+ socklen_t RemoteAddressLength;
+ EFI_STATUS Status;
+
+ DEBUG (( DEBUG_PORT_WORK, "Entering PortWork\r\n" ));
+
+ //
+ // Assume success
+ //
+ OpStatus = EFI_SUCCESS;
+
+ //
+ // Handle input events
+ //
+ if ( 0 != ( events & POLLRDNORM )) {
+ //
+ // Determine if this is a connection attempt
+ //
+ if ( SocketFD == pWebServer->HttpListenPort ) {
+ //
+ // Handle connection attempts
+ // Accepts arrive as read events
+ //
+ RemoteAddressLength = sizeof ( RemoteAddress );
+ NewSocket = accept ( SocketFD,
+ &RemoteAddress,
+ &RemoteAddressLength );
+ if ( -1 != NewSocket ) {
+ if ( 0 != NewSocket ) {
+ //
+ // Add this port to the list monitored by the web server
+ //
+ Status = PortAdd ( pWebServer, NewSocket );
+ if ( EFI_ERROR ( Status )) {
+ DEBUG (( DEBUG_ERROR,
+ "ERROR - Failed to add the port 0x%08x, Status: %r\r\n",
+ NewSocket,
+ Status ));
+
+ //
+ // Done with the new socket
+ //
+ close ( NewSocket );
+ }
+ }
+ else {
+ DEBUG (( DEBUG_ERROR,
+ "ERROR - Socket not available!\r\n" ));
+ }
+
+ //
+ // Leave the listen port open
+ //
+ }
+ else {
+ //
+ // Listen port error
+ // Close the listen port by returning error status
+ //
+ OpStatus = EFI_DEVICE_ERROR;
+ DEBUG (( DEBUG_ERROR,
+ "ERROR - Failed to accept new connection, errno: 0x%08x\r\n",
+ errno ));
+ }
+ }
+ else {
+ //
+ // Handle the data received event
+ //
+ if ( 0 == pPort->RequestLength ) {
+ //
+ // Receive the page request
+ //
+ pPort->RequestLength = recv ( SocketFD,
+ &pPort->Request[0],
+ DIM ( pPort->Request ),
+ 0 );
+ if ( -1 == pPort->RequestLength ) {
+ //
+ // Receive error detected
+ // Close the port
+ //
+ OpStatus = EFI_DEVICE_ERROR;
+ }
+ else {
+ DEBUG (( DEBUG_REQUEST,
+ "0x%08x: Socket - Received %d bytes of HTTP request\r\n",
+ SocketFD,
+ pPort->RequestLength ));
+
+ //
+ // Process the request
+ //
+ OpStatus = HttpRequest ( SocketFD, pPort, &bDone );
+ if ( bDone ) {
+ //
+ // Notify the upper layer to close the socket
+ //
+ OpStatus = EFI_DEVICE_ERROR;
+ }
+ }
+ }
+ else
+ {
+ //
+ // Receive the file data
+ //
+ LengthInBytes = recv ( SocketFD,
+ &pPort->RxBuffer[0],
+ DIM ( pPort->RxBuffer ),
+ 0 );
+ if ( -1 == LengthInBytes ) {
+ //
+ // Receive error detected
+ // Close the port
+ //
+ OpStatus = EFI_DEVICE_ERROR;
+ }
+ else {
+ DEBUG (( DEBUG_REQUEST,
+ "0x%08x: Socket - Received %d bytes of file data\r\n",
+ SocketFD,
+ LengthInBytes ));
+
+ //
+ // TODO: Process the file data
+ //
+ }
+ }
+ }
+ }
+
+ //
+ // Handle the close event
+ //
+ if ( 0 != ( events & POLLHUP )) {
+ //
+ // Close the port
+ //
+ OpStatus = EFI_DEVICE_ERROR;
+ }
+
+ //
+ // Return the operation status
+ //
+ DEBUG (( DEBUG_PORT_WORK,
+ "Exiting PortWork, Status: %r\r\n",
+ OpStatus ));
+ return OpStatus;
+}
+
+
+/**
+ Scan the list of sockets and process any pending work
+
+ @param [in] pWebServer The web server control structure address.
+
+**/
+VOID
+SocketPoll (
+ IN DT_WEB_SERVER * pWebServer
+ )
+{
+ int FDCount;
+ struct pollfd * pPoll;
+ WSDT_PORT ** ppPort;
+ EFI_STATUS Status;
+
+ DEBUG (( DEBUG_SOCKET_POLL, "Entering SocketPoll\r\n" ));
+
+ //
+ // Determine if any ports are active
+ //
+ FDCount = poll ( pWebServer->pFdList,
+ pWebServer->Entries,
+ CLIENT_POLL_DELAY );
+ if ( -1 == FDCount ) {
+ DEBUG (( DEBUG_ERROR | DEBUG_SOCKET_POLL,
+ "ERROR - errno: %d\r\n",
+ errno ));
+ }
+
+ pPoll = pWebServer->pFdList;
+ ppPort = pWebServer->ppPortList;
+ while ( 0 < FDCount ) {
+ //
+ // Walk the list of ports to determine what work needs to be done
+ //
+ if ( 0 != pPoll->revents ) {
+ //
+ // Process this port
+ //
+ Status = PortWork ( pWebServer,
+ pPoll->fd,
+ pPoll->revents,
+ *ppPort );
+ pPoll->revents = 0;
+
+ //
+ // Close the port if necessary
+ //
+ if ( EFI_ERROR ( Status )) {
+ PortRemove ( pWebServer, pPoll->fd );
+ pPoll -= 1;
+ ppPort -= 1;
+ }
+
+ //
+ // Account for this file descriptor
+ //
+ FDCount -= 1;
+ }
+
+ //
+ // Set the next port
+ //
+ pPoll += 1;
+ ppPort += 1;
+ }
+
+ DEBUG (( DEBUG_SOCKET_POLL, "Exiting SocketPoll\r\n" ));
+}
+
+
+/**
+ Create the HTTP port for the web server
+
+ This routine polls the network layer to create the HTTP port for the
+ web server. More than one attempt may be necessary since it may take
+ some time to get the IP address and initialize the upper layers of
+ the network stack.
+
+ After the HTTP port is created, the socket layer will manage the
+ coming and going of the network connections until the last network
+ connection is broken.
+
+ @param [in] pWebServer The web server control structure address.
+
+**/
+VOID
+WebServerTimer (
+ IN DT_WEB_SERVER * pWebServer
+ )
+{
+ UINT16 HttpPort;
+ struct sockaddr_in WebServerAddress;
+ int SocketStatus;
+ EFI_STATUS Status;
+
+ DEBUG (( DEBUG_SERVER_TIMER, "Entering WebServerTimer\r\n" ));
+
+ //
+ // Open the HTTP port on the server
+ //
+ do {
+ do {
+ //
+ // Complete the client operations
+ //
+ SocketPoll ( pWebServer );
+
+ //
+ // Wait for a while
+ //
+ Status = gBS->CheckEvent ( pWebServer->TimerEvent );
+ } while ( EFI_SUCCESS != Status );
+
+ //
+ // Attempt to create the socket for the web server
+ //
+ pWebServer->HttpListenPort = socket ( AF_INET,
+ SOCK_STREAM,
+ IPPROTO_TCP );
+ if ( -1 != pWebServer->HttpListenPort )
+ {
+ //
+ // Set the socket address
+ //
+ ZeroMem ( &WebServerAddress, sizeof ( WebServerAddress ));
+ HttpPort = PcdGet16 ( WebServer_HttpPort );
+ DEBUG (( DEBUG_HTTP_PORT,
+ "HTTP Port: %d\r\n",
+ HttpPort ));
+ WebServerAddress.sin_len = sizeof ( WebServerAddress );
+ WebServerAddress.sin_family = AF_INET;
+ WebServerAddress.sin_addr.s_addr = INADDR_ANY;
+ WebServerAddress.sin_port = htons ( HttpPort );
+
+ //
+ // Bind the socket to the HTTP port
+ //
+ SocketStatus = bind ( pWebServer->HttpListenPort,
+ (struct sockaddr *) &WebServerAddress,
+ WebServerAddress.sin_len );
+ if ( -1 != SocketStatus ) {
+ //
+ // Enable connections to the HTTP port
+ //
+ SocketStatus = listen ( pWebServer->HttpListenPort,
+ SOMAXCONN );
+ }
+
+ //
+ // Release the socket if necessary
+ //
+ if ( -1 == SocketStatus ) {
+ close ( pWebServer->HttpListenPort );
+ pWebServer->HttpListenPort = -1;
+ }
+ }
+
+ //
+ // Wait until the socket is open
+ //
+ }while ( -1 == pWebServer->HttpListenPort );
+
+ DEBUG (( DEBUG_SERVER_TIMER, "Exiting WebServerTimer\r\n" ));
+}
+
+
+/**
+ Start the web server port creation timer
+
+ @param [in] pWebServer The web server control structure address.
+
+ @retval EFI_SUCCESS The timer was successfully started.
+ @retval EFI_ALREADY_STARTED The timer is already running.
+ @retval Other The timer failed to start.
+
+**/
+EFI_STATUS
+WebServerTimerStart (
+ IN DT_WEB_SERVER * pWebServer
+ )
+{
+ EFI_STATUS Status;
+ UINT64 TriggerTime;
+
+ DBG_ENTER ( );
+
+ //
+ // Assume the timer is already running
+ //
+ Status = EFI_ALREADY_STARTED;
+ if ( !pWebServer->bTimerRunning ) {
+ //
+ // Compute the poll interval
+ //
+ TriggerTime = HTTP_PORT_POLL_DELAY * ( 1000 * 10 );
+ Status = gBS->SetTimer ( pWebServer->TimerEvent,
+ TimerPeriodic,
+ TriggerTime );
+ if ( !EFI_ERROR ( Status )) {
+ DEBUG (( DEBUG_HTTP_PORT, "HTTP port timer started\r\n" ));
+
+ //
+ // Mark the timer running
+ //
+ pWebServer->bTimerRunning = TRUE;
+ }
+ else {
+ DEBUG (( DEBUG_ERROR | DEBUG_HTTP_PORT,
+ "ERROR - Failed to start HTTP port timer, Status: %r\r\n",
+ Status ));
+ }
+ }
+
+ //
+ // Return the operation status
+ //
+ DBG_EXIT_STATUS ( Status );
+ return Status;
+}
+
+
+/**
+ Stop the web server port creation timer
+
+ @param [in] pWebServer The web server control structure address.
+
+ @retval EFI_SUCCESS The HTTP port timer is stopped
+ @retval Other Failed to stop the HTTP port timer
+
+**/
+EFI_STATUS
+WebServerTimerStop (
+ IN DT_WEB_SERVER * pWebServer
+ )
+{
+ EFI_STATUS Status;
+
+ DBG_ENTER ( );
+
+ //
+ // Assume the timer is stopped
+ //
+ Status = EFI_SUCCESS;
+ if ( pWebServer->bTimerRunning ) {
+ //
+ // Stop the port creation polling
+ //
+ Status = gBS->SetTimer ( pWebServer->TimerEvent,
+ TimerCancel,
+ 0 );
+ if ( !EFI_ERROR ( Status )) {
+ DEBUG (( DEBUG_HTTP_PORT, "HTTP port timer stopped\r\n" ));
+
+ //
+ // Mark the timer stopped
+ //
+ pWebServer->bTimerRunning = FALSE;
+ }
+ else {
+ DEBUG (( DEBUG_ERROR | DEBUG_HTTP_PORT,
+ "ERROR - Failed to stop HTTP port timer, Status: %r\r\n",
+ Status ));
+ }
+ }
+
+ //
+ // Return the operation status
+ //
+ DBG_EXIT_STATUS ( Status );
+ return Status;
+}
+
+/**
+ Entry point for the web server application.
+
+ @param [in] Argc The number of arguments
+ @param [in] Argv The argument value array
+
+ @retval 0 The application exited normally.
+ @retval Other An error occurred.
+**/
+int
+main (
+ IN int Argc,
+ IN char **Argv
+ )
+{
+ DT_WEB_SERVER * pWebServer;
+ EFI_STATUS Status;
+
+ //
+ // Create a timer event to start HTTP port
+ //
+ pWebServer = &mWebServer;
+ Status = gBS->CreateEvent ( EVT_TIMER,
+ TPL_WEB_SERVER,
+ NULL,
+ NULL,
+ &pWebServer->TimerEvent );
+ if ( !EFI_ERROR ( Status )) {
+ Status = WebServerTimerStart ( pWebServer );
+ if ( !EFI_ERROR ( Status )) {
+ //
+ // Run the web server forever
+ //
+ for ( ; ; ) {
+ //
+ // Poll the network layer to create the HTTP port
+ // for the web server. More than one attempt may
+ // be necessary since it may take some time to get
+ // the IP address and initialize the upper layers
+ // of the network stack.
+ //
+ WebServerTimer ( pWebServer );
+
+ //
+ // Add the HTTP port to the list of ports
+ //
+ Status = PortAdd ( pWebServer, pWebServer->HttpListenPort );
+ if ( !EFI_ERROR ( Status )) {
+ //
+ // Poll the sockets for activity
+ //
+ do {
+ SocketPoll ( pWebServer );
+ } while ( -1 != pWebServer->HttpListenPort );
+
+ //
+ // The HTTP port failed the accept and was closed
+ //
+ }
+
+ //
+ // Close the HTTP port if necessary
+ //
+ if ( -1 != pWebServer->HttpListenPort ) {
+ close ( pWebServer->HttpListenPort );
+ pWebServer->HttpListenPort = -1;
+ }
+//
+// TODO: Remove the following test code
+// Exit when the network connection is broken
+//
+break;
+ }
+
+ //
+ // Done with the timer event
+ //
+ WebServerTimerStop ( pWebServer );
+ Status = gBS->CloseEvent ( pWebServer->TimerEvent );
+ }
+ }
+
+ //
+ // Return the final status
+ //
+ DBG_EXIT_STATUS ( Status );
+ return Status;
+}