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
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.
16 #include <WebServer.h>
18 DT_WEB_SERVER mWebServer
; ///< Web server's control structure
22 Add a port to the list of ports to be polled.
24 @param [in] pWebServer The web server control structure address.
26 @param [in] SocketFD The socket's file descriptor to add to the list.
28 @retval EFI_SUCCESS The port was successfully added
29 @retval EFI_NO_RESOURCES Insufficient memory to add the port
34 IN DT_WEB_SERVER
* pWebServer
,
42 struct pollfd
* pFdList
;
43 struct pollfd
* pFdListNew
;
44 WSDT_PORT
** ppPortListNew
;
51 // Use for/break instead of goto
60 // Create a new list if necessary
62 pFdList
= pWebServer
->pFdList
;
63 MaxEntries
= pWebServer
->MaxEntries
;
64 if ( pWebServer
->Entries
>= MaxEntries
) {
65 MaxEntriesNew
= 16 + MaxEntries
;
68 // The current FD list is full
69 // Allocate a new FD list
71 LengthInBytes
= sizeof ( *pFdList
) * MaxEntriesNew
;
72 Status
= gBS
->AllocatePool ( EfiRuntimeServicesData
,
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",
83 // Allocate a new port list
85 LengthInBytes
= sizeof ( *ppPortListNew
) * MaxEntriesNew
;
86 Status
= gBS
->AllocatePool ( EfiRuntimeServicesData
,
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",
95 // Free the new FD list
97 gBS
->FreePool ( pFdListNew
);
102 // Duplicate the FD list
105 if ( NULL
!= pFdList
) {
106 CopyMem ( pFdListNew
,
108 Index
* sizeof ( *pFdList
));
112 // Initialize the new entries in the FD list
114 for ( ; MaxEntriesNew
> Index
; Index
++ ) {
115 pFdListNew
[ Index
].fd
= -1;
116 pFdListNew
[ Index
].events
= 0;
117 pFdListNew
[ Index
].revents
= 0;
121 // Free the old FD list
123 if ( NULL
!= pFdList
) {
124 gBS
->FreePool ( pFdList
);
128 // Switch to the new FD list
130 pWebServer
->pFdList
= pFdListNew
;
131 pFdList
= pWebServer
->pFdList
;
134 // Duplicate the port list
137 if ( NULL
!= pWebServer
->ppPortList
) {
138 CopyMem ( ppPortListNew
,
139 pWebServer
->ppPortList
,
140 Index
* sizeof ( *ppPortListNew
));
144 // Initialize the new entries in the port list
146 for ( ; MaxEntriesNew
> Index
; Index
++ ) {
147 ppPortListNew
[ Index
] = NULL
;
151 // Free the old port list
153 if ( NULL
!= pWebServer
->ppPortList
) {
154 gBS
->FreePool ( pWebServer
->ppPortList
);
158 // Switch to the new port list
160 pWebServer
->ppPortList
= ppPortListNew
;
163 // Update the list size
165 pWebServer
->MaxEntries
= MaxEntriesNew
;
169 // Allocate a new port
171 LengthInBytes
= sizeof ( *pPort
);
172 Status
= gBS
->AllocatePool ( EfiRuntimeServicesData
,
175 if ( EFI_ERROR ( Status
)) {
176 DEBUG (( DEBUG_ERROR
| DEBUG_POOL
,
177 "ERROR - Failed to allocate the port, Status: %r\r\n",
183 // Initialize the port
185 pPort
->RequestLength
= 0;
189 // Add the socket to the FD list
191 pFdList
[ pWebServer
->Entries
].fd
= SocketFD
;
192 pFdList
[ pWebServer
->Entries
].events
= POLLRDNORM
194 pFdList
[ pWebServer
->Entries
].revents
= 0;
197 // Add the port to the port list
199 pWebServer
->ppPortList
[ pWebServer
->Entries
] = pPort
;
202 // Account for the new entry
204 pWebServer
->Entries
+= 1;
205 DEBUG (( DEBUG_PORT_WORK
| DEBUG_INFO
,
206 "WebServer handling %d ports\r\n",
207 pWebServer
->Entries
));
216 // Return the operation status
218 DBG_EXIT_STATUS ( Status
);
224 Remove a port from the list of ports to be polled.
226 @param [in] pWebServer The web server control structure address.
228 @param [in] SocketFD The socket's file descriptor to add to the list.
233 IN DT_WEB_SERVER
* pWebServer
,
239 struct pollfd
* pFdList
;
240 WSDT_PORT
** ppPortList
;
245 // Attempt to remove the entry from the list
247 Entries
= pWebServer
->Entries
;
248 pFdList
= pWebServer
->pFdList
;
249 ppPortList
= pWebServer
->ppPortList
;
250 for ( Index
= 0; Entries
> Index
; Index
++ ) {
252 // Locate the specified socket file descriptor
254 if ( SocketFD
== pFdList
[ Index
].fd
) {
256 // Determine if this is the listen port
258 if ( SocketFD
== pWebServer
->HttpListenPort
) {
259 pWebServer
->HttpListenPort
= -1;
268 // Free the port structure
270 gBS
->FreePool ( ppPortList
[ Index
]);
273 // Remove this port from the list by copying
274 // the rest of the list down one entry
277 for ( ; Entries
> Index
; Index
++ ) {
278 pFdList
[ Index
] = pFdList
[ Index
+ 1 ];
279 ppPortList
[ Index
] = ppPortList
[ Index
+ 1 ];
281 pFdList
[ Index
].fd
= -1;
282 pFdList
[ Index
].events
= 0;
283 pFdList
[ Index
].revents
= 0;
284 ppPortList
[ Index
] = NULL
;
287 // Update the number of entries in the list
289 pWebServer
->Entries
= Entries
;
290 DEBUG (( DEBUG_PORT_WORK
| DEBUG_INFO
,
291 "WebServer handling %d ports\r\n",
292 pWebServer
->Entries
));
302 Process the work for the sockets.
304 @param [in] pWebServer The web server control structure address.
306 @param [in] SocketFD The socket's file descriptor to add to the list.
308 @param [in] events everts is a bitmask of the work to be done
310 @param [in] pPort The address of a WSDT_PORT structure
312 @retval EFI_SUCCESS The operation was successful
313 @retval EFI_DEVICE_ERROR Error, close the port
318 IN DT_WEB_SERVER
* pWebServer
,
325 size_t LengthInBytes
;
328 struct sockaddr_in6 RemoteAddress
;
329 socklen_t RemoteAddressLength
;
332 DEBUG (( DEBUG_PORT_WORK
, "Entering PortWork\r\n" ));
337 OpStatus
= EFI_SUCCESS
;
340 // Handle input events
342 if ( 0 != ( events
& POLLRDNORM
)) {
344 // Determine if this is a connection attempt
346 if (( SocketFD
== pWebServer
->HttpListenPort
)
347 || ( SocketFD
== pWebServer
->HttpListenPort6
)) {
349 // Handle connection attempts
350 // Accepts arrive as read events
352 RemoteAddressLength
= sizeof ( RemoteAddress
);
353 NewSocket
= accept ( SocketFD
,
354 (struct sockaddr
*)&RemoteAddress
,
355 &RemoteAddressLength
);
356 if ( -1 != NewSocket
) {
357 if ( 0 != NewSocket
) {
359 // Add this port to the list monitored by the web server
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",
369 // Done with the new socket
375 DEBUG (( DEBUG_ERROR
,
376 "ERROR - Socket not available!\r\n" ));
380 // Leave the listen port open
386 // Close the listen port by returning error status
388 OpStatus
= EFI_DEVICE_ERROR
;
389 DEBUG (( DEBUG_ERROR
,
390 "ERROR - Failed to accept new connection, errno: 0x%08x\r\n",
396 // Handle the data received event
398 if ( 0 == pPort
->RequestLength
) {
400 // Receive the page request
402 pPort
->RequestLength
= recv ( SocketFD
,
404 DIM ( pPort
->Request
),
406 if ( -1 == pPort
->RequestLength
) {
408 // Receive error detected
411 OpStatus
= EFI_DEVICE_ERROR
;
414 DEBUG (( DEBUG_REQUEST
,
415 "0x%08x: Socket - Received %d bytes of HTTP request\r\n",
417 pPort
->RequestLength
));
420 // Process the request
422 OpStatus
= HttpRequest ( SocketFD
, pPort
, &bDone
);
425 // Notify the upper layer to close the socket
427 OpStatus
= EFI_DEVICE_ERROR
;
433 // Receive the file data
435 LengthInBytes
= recv ( SocketFD
,
437 DIM ( pPort
->RxBuffer
),
439 if ( -1 == LengthInBytes
) {
441 // Receive error detected
444 OpStatus
= EFI_DEVICE_ERROR
;
447 DEBUG (( DEBUG_REQUEST
,
448 "0x%08x: Socket - Received %d bytes of file data\r\n",
453 // TODO: Process the file data
461 // Handle the close event
463 if ( 0 != ( events
& POLLHUP
)) {
467 OpStatus
= EFI_DEVICE_ERROR
;
471 // Return the operation status
473 DEBUG (( DEBUG_PORT_WORK
,
474 "Exiting PortWork, Status: %r\r\n",
481 Scan the list of sockets and process any pending work
483 @param [in] pWebServer The web server control structure address.
488 IN DT_WEB_SERVER
* pWebServer
492 struct pollfd
* pPoll
;
496 DEBUG (( DEBUG_SOCKET_POLL
, "Entering SocketPoll\r\n" ));
499 // Determine if any ports are active
501 FDCount
= poll ( pWebServer
->pFdList
,
504 if ( -1 == FDCount
) {
505 DEBUG (( DEBUG_ERROR
| DEBUG_SOCKET_POLL
,
506 "ERROR - errno: %d\r\n",
510 pPoll
= pWebServer
->pFdList
;
511 ppPort
= pWebServer
->ppPortList
;
512 while ( 0 < FDCount
) {
514 // Walk the list of ports to determine what work needs to be done
516 if ( 0 != pPoll
->revents
) {
520 Status
= PortWork ( pWebServer
,
527 // Close the port if necessary
529 if ( EFI_ERROR ( Status
)) {
530 PortRemove ( pWebServer
, pPoll
->fd
);
536 // Account for this file descriptor
548 DEBUG (( DEBUG_SOCKET_POLL
, "Exiting SocketPoll\r\n" ));
553 Create an HTTP port for the web server
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
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.
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
573 IN DT_WEB_SERVER
* pWebServer
,
574 IN sa_family_t AddressFamily
,
581 struct sockaddr_in v4
;
582 struct sockaddr_in6 v6
;
587 DEBUG (( DEBUG_SERVER_LISTEN
, "Entering WebServerListen\r\n" ));
590 // Attempt to create the socket for the web server
592 * pPort
= socket ( AddressFamily
, SOCK_STREAM
, Protocol
);
593 if ( -1 != *pPort
) {
595 // Build the socket address
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
);
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
;
611 // Bind the socket to the HTTP port
613 SocketStatus
= bind ( *pPort
,
614 (struct sockaddr
*) &WebServerAddress
,
615 WebServerAddress
.v4
.sin_len
);
616 if ( -1 != SocketStatus
) {
618 // Enable connections to the HTTP port
620 SocketStatus
= listen ( *pPort
, SOMAXCONN
);
621 if ( -1 != SocketStatus
) {
623 // Add the HTTP port to the list of ports to poll
625 Status
= PortAdd ( pWebServer
, *pPort
);
626 if ( EFI_ERROR ( Status
)) {
630 DEBUG (( DEBUG_PORT_WORK
,
631 "Listening on Tcp%d:%d\r\n",
632 ( AF_INET
== AddressFamily
) ? 4 : 6,
639 // Release the socket if necessary
641 if ( -1 == SocketStatus
) {
647 DEBUG (( DEBUG_SERVER_LISTEN
, "Exiting WebServerListen\r\n" ));
652 Entry point for the web server application.
654 @param [in] Argc The number of arguments
655 @param [in] Argv The argument value array
657 @retval 0 The application exited normally.
658 @retval Other An error occurred.
668 DT_WEB_SERVER
* pWebServer
;
675 HttpPort
= PcdGet16 ( WebServer_HttpPort
);
676 DEBUG (( DEBUG_HTTP_PORT
,
681 // Create a timer event to start HTTP port
683 pWebServer
= &mWebServer
;
684 Status
= gBS
->CreateEvent ( EVT_TIMER
,
688 &pWebServer
->TimerEvent
);
689 if ( !EFI_ERROR ( Status
)) {
690 TriggerTime
= HTTP_PORT_POLL_DELAY
* ( 1000 * 10 );
691 Status
= gBS
->SetTimer ( pWebServer
->TimerEvent
,
694 if ( !EFI_ERROR ( Status
)) {
696 // Run the web server forever
698 pWebServer
->HttpListenPort
= -1;
699 pWebServer
->HttpListenPort6
= -1;
700 pWebServer
->bRunning
= TRUE
;
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.
709 if (( -1 == pWebServer
->HttpListenPort
)
710 || ( -1 == pWebServer
->HttpListenPort6
)) {
713 // Wait a while before polling for a connection
715 if ( EFI_SUCCESS
!= gBS
->CheckEvent ( pWebServer
->TimerEvent
)) {
716 if ( 0 != pWebServer
->Entries
) {
719 gBS
->WaitForEvent ( 1, &pWebServer
->TimerEvent
, &Index
);
723 // Poll for a network connection
725 if ( -1 == pWebServer
->HttpListenPort
) {
726 WebServerListen ( pWebServer
,
730 &pWebServer
->HttpListenPort
);
732 if ( -1 == pWebServer
->HttpListenPort6
) {
733 WebServerListen ( pWebServer
,
737 &pWebServer
->HttpListenPort6
);
741 // Continue polling while both network connections are
744 } while ( 0 == pWebServer
->Entries
);
748 // Poll the sockets for activity while both network
749 // connections are connected
752 SocketPoll ( pWebServer
);
753 } while ( pWebServer
->bRunning
754 && ( -1 != pWebServer
->HttpListenPort
)
755 && ( -1 != pWebServer
->HttpListenPort6
));
758 // Continue polling the network connections until both
759 // TCP4 and TCP6 are connected
761 } while ( pWebServer
->bRunning
);
766 gBS
->SetTimer ( pWebServer
->TimerEvent
,
772 // Done with the timer event
774 gBS
->CloseEvent ( pWebServer
->TimerEvent
);
778 // Return the final status
780 DBG_EXIT_STATUS ( Status
);