]> git.proxmox.com Git - mirror_edk2.git/blame - AppPkg/Applications/Sockets/WebServer/WebServer.c
Update the license dates
[mirror_edk2.git] / AppPkg / Applications / Sockets / WebServer / WebServer.c
CommitLineData
9f7f5161 1/**
2 @file
3 Web server application
4684b66f 4
9f7f5161 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
4684b66f 10
9f7f5161 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.
4684b66f 13
14**/
15
16#include <WebServer.h>
17
18DT_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**/
32EFI_STATUS
33PortAdd (
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++ ) {
59bc0593 115 pFdListNew[ Index ].fd = -1;
116 pFdListNew[ Index ].events = 0;
117 pFdListNew[ Index ].revents = 0;
4684b66f 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++ ) {
59bc0593 147 ppPortListNew[ Index ] = NULL;
4684b66f 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 //
59bc0593 191 pFdList[ pWebServer->Entries ].fd = SocketFD;
192 pFdList[ pWebServer->Entries ].events = POLLRDNORM
4684b66f 193 | POLLHUP;
59bc0593 194 pFdList[ pWebServer->Entries ].revents = 0;
4684b66f 195
196 //
197 // Add the port to the port list
198 //
59bc0593 199 pWebServer->ppPortList[ pWebServer->Entries ] = pPort;
4684b66f 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**/
231VOID
232PortRemove (
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 //
59bc0593 254 if ( SocketFD == pFdList[ Index ].fd ) {
4684b66f 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 //
59bc0593 270 gBS->FreePool ( ppPortList[ Index ]);
4684b66f 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++ ) {
59bc0593 278 pFdList[ Index ] = pFdList[ Index + 1 ];
279 ppPortList[ Index ] = ppPortList[ Index + 1 ];
4684b66f 280 }
59bc0593 281 pFdList[ Index ].fd = -1;
282 pFdList[ Index ].events = 0;
283 pFdList[ Index ].revents = 0;
284 ppPortList[ Index ] = NULL;
4684b66f 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**/
316EFI_STATUS
317PortWork (
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;
f6e5cdd5 328 struct sockaddr_in6 RemoteAddress;
4684b66f 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 //
f6e5cdd5 346 if (( SocketFD == pWebServer->HttpListenPort )
347 || ( SocketFD == pWebServer->HttpListenPort6 )) {
4684b66f 348 //
349 // Handle connection attempts
350 // Accepts arrive as read events
351 //
352 RemoteAddressLength = sizeof ( RemoteAddress );
353 NewSocket = accept ( SocketFD,
f6e5cdd5 354 (struct sockaddr *)&RemoteAddress,
4684b66f 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 }
59bc0593 431 else {
4684b66f 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**/
486VOID
487SocketPoll (
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/**
f6e5cdd5 553 Create an HTTP port for the web server
4684b66f 554
f6e5cdd5 555 This routine polls the network layer to create an HTTP port for the
4684b66f 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
f6e5cdd5 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
4684b66f 569
570**/
571VOID
f6e5cdd5 572WebServerListen (
573 IN DT_WEB_SERVER * pWebServer,
574 IN sa_family_t AddressFamily,
575 IN int Protocol,
576 IN UINT16 HttpPort,
577 OUT int * pPort
4684b66f 578 )
579{
f6e5cdd5 580 union {
581 struct sockaddr_in v4;
582 struct sockaddr_in6 v6;
583 } WebServerAddress;
4684b66f 584 int SocketStatus;
585 EFI_STATUS Status;
586
f6e5cdd5 587 DEBUG (( DEBUG_SERVER_LISTEN, "Entering WebServerListen\r\n" ));
4684b66f 588
589 //
f6e5cdd5 590 // Attempt to create the socket for the web server
4684b66f 591 //
f6e5cdd5 592 * pPort = socket ( AddressFamily, SOCK_STREAM, Protocol );
593 if ( -1 != *pPort ) {
4684b66f 594 //
f6e5cdd5 595 // Build the socket address
4684b66f 596 //
f6e5cdd5 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 }
4684b66f 609
f6e5cdd5 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 ) {
4684b66f 617 //
f6e5cdd5 618 // Enable connections to the HTTP port
4684b66f 619 //
f6e5cdd5 620 SocketStatus = listen ( *pPort, SOMAXCONN );
4684b66f 621 if ( -1 != SocketStatus ) {
622 //
f6e5cdd5 623 // Add the HTTP port to the list of ports to poll
4684b66f 624 //
f6e5cdd5 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 }
4684b66f 635 }
636 }
637
638 //
f6e5cdd5 639 // Release the socket if necessary
4684b66f 640 //
f6e5cdd5 641 if ( -1 == SocketStatus ) {
642 close ( *pPort );
643 *pPort = -1;
4684b66f 644 }
645 }
646
f6e5cdd5 647 DEBUG (( DEBUG_SERVER_LISTEN, "Exiting WebServerListen\r\n" ));
4684b66f 648}
649
650
4684b66f 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**/
660int
661main (
662 IN int Argc,
663 IN char **Argv
664 )
665{
f6e5cdd5 666 UINT16 HttpPort;
667 UINTN Index;
4684b66f 668 DT_WEB_SERVER * pWebServer;
669 EFI_STATUS Status;
f6e5cdd5 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 ));
4684b66f 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 )) {
f6e5cdd5 690 TriggerTime = HTTP_PORT_POLL_DELAY * ( 1000 * 10 );
691 Status = gBS->SetTimer ( pWebServer->TimerEvent,
692 TimerPeriodic,
693 TriggerTime );
4684b66f 694 if ( !EFI_ERROR ( Status )) {
695 //
696 // Run the web server forever
697 //
f6e5cdd5 698 pWebServer->HttpListenPort = -1;
699 pWebServer->HttpListenPort6 = -1;
700 pWebServer->bRunning = TRUE;
701 do {
4684b66f 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 //
f6e5cdd5 709 if (( -1 == pWebServer->HttpListenPort )
710 || ( -1 == pWebServer->HttpListenPort6 )) {
4684b66f 711 do {
f6e5cdd5 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 }
4684b66f 721
f6e5cdd5 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 );
4684b66f 745 }
746
747 //
f6e5cdd5 748 // Poll the sockets for activity while both network
749 // connections are connected
4684b66f 750 //
f6e5cdd5 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 );
4684b66f 762
763 //
f6e5cdd5 764 // Stop the timer
4684b66f 765 //
f6e5cdd5 766 gBS->SetTimer ( pWebServer->TimerEvent,
767 TimerCancel,
768 0 );
4684b66f 769 }
f6e5cdd5 770
771 //
772 // Done with the timer event
773 //
774 gBS->CloseEvent ( pWebServer->TimerEvent );
4684b66f 775 }
776
777 //
778 // Return the final status
779 //
780 DBG_EXIT_STATUS ( Status );
781 return Status;
782}