From 098f8621634f1cbdd1253c9957eed09a505223f5 Mon Sep 17 00:00:00 2001 From: Guo Mang Date: Thu, 27 Apr 2017 11:16:34 +0800 Subject: NetWorkPkg: Move to new location Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Guo Mang --- Core/NetworkPkg/HttpDxe/HttpProto.c | 2128 +++++++++++++++++++++++++++++++++++ 1 file changed, 2128 insertions(+) create mode 100644 Core/NetworkPkg/HttpDxe/HttpProto.c (limited to 'Core/NetworkPkg/HttpDxe/HttpProto.c') diff --git a/Core/NetworkPkg/HttpDxe/HttpProto.c b/Core/NetworkPkg/HttpDxe/HttpProto.c new file mode 100644 index 0000000000..3d61ba2ae1 --- /dev/null +++ b/Core/NetworkPkg/HttpDxe/HttpProto.c @@ -0,0 +1,2128 @@ +/** @file + Miscellaneous routines for HttpDxe driver. + +Copyright (c) 2015 - 2017, Intel Corporation. All rights reserved.
+(C) Copyright 2016 Hewlett Packard Enterprise Development LP
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "HttpDriver.h" + +/** + The common notify function used in HTTP driver. + + @param[in] Event The event signaled. + @param[in] Context The context. + +**/ +VOID +EFIAPI +HttpCommonNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + if ((Event == NULL) || (Context == NULL)) { + return ; + } + + *((BOOLEAN *) Context) = TRUE; +} + +/** + The notify function associated with Tx4Token for Tcp4->Transmit() or Tx6Token for Tcp6->Transmit(). + + @param[in] Context The context. + +**/ +VOID +EFIAPI +HttpTcpTransmitNotifyDpc ( + IN VOID *Context + ) +{ + HTTP_TOKEN_WRAP *Wrap; + HTTP_PROTOCOL *HttpInstance; + + if (Context == NULL) { + return ; + } + + Wrap = (HTTP_TOKEN_WRAP *) Context; + HttpInstance = Wrap->HttpInstance; + + if (!HttpInstance->LocalAddressIsIPv6) { + Wrap->HttpToken->Status = Wrap->TcpWrap.Tx4Token.CompletionToken.Status; + gBS->SignalEvent (Wrap->HttpToken->Event); + + // + // Free resources. + // + if (Wrap->TcpWrap.Tx4Token.Packet.TxData->FragmentTable[0].FragmentBuffer != NULL) { + FreePool (Wrap->TcpWrap.Tx4Token.Packet.TxData->FragmentTable[0].FragmentBuffer); + } + + if (Wrap->TcpWrap.Tx4Token.CompletionToken.Event != NULL) { + gBS->CloseEvent (Wrap->TcpWrap.Tx4Token.CompletionToken.Event); + } + + } else { + Wrap->HttpToken->Status = Wrap->TcpWrap.Tx6Token.CompletionToken.Status; + gBS->SignalEvent (Wrap->HttpToken->Event); + + // + // Free resources. + // + if (Wrap->TcpWrap.Tx6Token.Packet.TxData->FragmentTable[0].FragmentBuffer != NULL) { + FreePool (Wrap->TcpWrap.Tx6Token.Packet.TxData->FragmentTable[0].FragmentBuffer); + } + + if (Wrap->TcpWrap.Tx6Token.CompletionToken.Event != NULL) { + gBS->CloseEvent (Wrap->TcpWrap.Tx6Token.CompletionToken.Event); + } + } + + + Wrap->TcpWrap.IsTxDone = TRUE; + + // + // Check pending TxTokens and sent out. + // + NetMapIterate (&Wrap->HttpInstance->TxTokens, HttpTcpTransmit, NULL); + +} + +/** + Request HttpTcpTransmitNotifyDpc as a DPC at TPL_CALLBACK. + + @param Event The receive event delivered to TCP for transmit. + @param Context Context for the callback. + +**/ +VOID +EFIAPI +HttpTcpTransmitNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + // + // Request HttpTcpTransmitNotifyDpc as a DPC at TPL_CALLBACK + // + QueueDpc (TPL_CALLBACK, HttpTcpTransmitNotifyDpc, Context); +} + +/** + The notify function associated with Rx4Token for Tcp4->Receive () or Rx6Token for Tcp6->Receive(). + + @param[in] Context The context. + +**/ +VOID +EFIAPI +HttpTcpReceiveNotifyDpc ( + IN VOID *Context + ) +{ + HTTP_TOKEN_WRAP *Wrap; + NET_MAP_ITEM *Item; + UINTN Length; + EFI_STATUS Status; + HTTP_PROTOCOL *HttpInstance; + BOOLEAN UsingIpv6; + + if (Context == NULL) { + return ; + } + + Wrap = (HTTP_TOKEN_WRAP *) Context; + HttpInstance = Wrap->HttpInstance; + UsingIpv6 = HttpInstance->LocalAddressIsIPv6; + + if (UsingIpv6) { + gBS->CloseEvent (Wrap->TcpWrap.Rx6Token.CompletionToken.Event); + Wrap->TcpWrap.Rx6Token.CompletionToken.Event = NULL; + + if (EFI_ERROR (Wrap->TcpWrap.Rx6Token.CompletionToken.Status)) { + DEBUG ((EFI_D_ERROR, "HttpTcpReceiveNotifyDpc: %r!\n", Wrap->TcpWrap.Rx6Token.CompletionToken.Status)); + Wrap->HttpToken->Status = Wrap->TcpWrap.Rx6Token.CompletionToken.Status; + gBS->SignalEvent (Wrap->HttpToken->Event); + + Item = NetMapFindKey (&HttpInstance->RxTokens, Wrap->HttpToken); + if (Item != NULL) { + NetMapRemoveItem (&HttpInstance->RxTokens, Item, NULL); + } + + FreePool (Wrap); + Wrap = NULL; + + return ; + } + + } else { + gBS->CloseEvent (Wrap->TcpWrap.Rx4Token.CompletionToken.Event); + Wrap->TcpWrap.Rx4Token.CompletionToken.Event = NULL; + + if (EFI_ERROR (Wrap->TcpWrap.Rx4Token.CompletionToken.Status)) { + DEBUG ((EFI_D_ERROR, "HttpTcpReceiveNotifyDpc: %r!\n", Wrap->TcpWrap.Rx4Token.CompletionToken.Status)); + Wrap->HttpToken->Status = Wrap->TcpWrap.Rx4Token.CompletionToken.Status; + gBS->SignalEvent (Wrap->HttpToken->Event); + + Item = NetMapFindKey (&HttpInstance->RxTokens, Wrap->HttpToken); + if (Item != NULL) { + NetMapRemoveItem (&HttpInstance->RxTokens, Item, NULL); + } + + FreePool (Wrap); + Wrap = NULL; + + return ; + } + } + + // + // Check whether we receive a complete HTTP message. + // + ASSERT (HttpInstance->MsgParser != NULL); + if (UsingIpv6) { + Length = (UINTN) Wrap->TcpWrap.Rx6Data.FragmentTable[0].FragmentLength; + } else { + Length = (UINTN) Wrap->TcpWrap.Rx4Data.FragmentTable[0].FragmentLength; + } + + Status = HttpParseMessageBody ( + HttpInstance->MsgParser, + Length, + Wrap->HttpToken->Message->Body + ); + if (EFI_ERROR (Status)) { + return ; + } + + if (HttpIsMessageComplete (HttpInstance->MsgParser)) { + // + // Free the MsgParse since we already have a full HTTP message. + // + HttpFreeMsgParser (HttpInstance->MsgParser); + HttpInstance->MsgParser = NULL; + } + + Wrap->HttpToken->Message->BodyLength = Length; + ASSERT (HttpInstance->CacheBody == NULL); + // + // We receive part of header of next HTTP msg. + // + if (HttpInstance->NextMsg != NULL) { + Wrap->HttpToken->Message->BodyLength = HttpInstance->NextMsg - + (CHAR8 *) Wrap->HttpToken->Message->Body; + HttpInstance->CacheLen = Length - Wrap->HttpToken->Message->BodyLength; + if (HttpInstance->CacheLen != 0) { + HttpInstance->CacheBody = AllocateZeroPool (HttpInstance->CacheLen); + if (HttpInstance->CacheBody == NULL) { + return ; + } + CopyMem (HttpInstance->CacheBody, HttpInstance->NextMsg, HttpInstance->CacheLen); + HttpInstance->NextMsg = HttpInstance->CacheBody; + HttpInstance->CacheOffset = 0; + } + } + + Item = NetMapFindKey (&Wrap->HttpInstance->RxTokens, Wrap->HttpToken); + if (Item != NULL) { + NetMapRemoveItem (&Wrap->HttpInstance->RxTokens, Item, NULL); + } + + + Wrap->TcpWrap.IsRxDone = TRUE; + if (UsingIpv6) { + Wrap->HttpToken->Status = Wrap->TcpWrap.Rx6Token.CompletionToken.Status; + } else { + Wrap->HttpToken->Status = Wrap->TcpWrap.Rx4Token.CompletionToken.Status; + } + + + gBS->SignalEvent (Wrap->HttpToken->Event); + + // + // Check pending RxTokens and receive the HTTP message. + // + NetMapIterate (&Wrap->HttpInstance->RxTokens, HttpTcpReceive, NULL); + + FreePool (Wrap); + Wrap = NULL; +} + +/** + Request HttpTcpReceiveNotifyDpc as a DPC at TPL_CALLBACK. + + @param Event The receive event delivered to TCP for receive. + @param Context Context for the callback. + +**/ +VOID +EFIAPI +HttpTcpReceiveNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + // + // Request HttpTcpTransmitNotifyDpc as a DPC at TPL_CALLBACK + // + QueueDpc (TPL_CALLBACK, HttpTcpReceiveNotifyDpc, Context); +} + +/** + Create events for the TCP connection token and TCP close token. + + @param[in] HttpInstance Pointer to HTTP_PROTOCOL structure. + + @retval EFI_SUCCESS The events are created successfully. + @retval others Other error as indicated. + +**/ +EFI_STATUS +HttpCreateTcpConnCloseEvent ( + IN HTTP_PROTOCOL *HttpInstance + ) +{ + EFI_STATUS Status; + + if (!HttpInstance->LocalAddressIsIPv6) { + // + // Create events for variuos asynchronous operations. + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpCommonNotify, + &HttpInstance->IsTcp4ConnDone, + &HttpInstance->Tcp4ConnToken.CompletionToken.Event + ); + if (EFI_ERROR (Status)) { + goto ERROR; + } + + // + // Initialize Tcp4CloseToken + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpCommonNotify, + &HttpInstance->IsTcp4CloseDone, + &HttpInstance->Tcp4CloseToken.CompletionToken.Event + ); + if (EFI_ERROR (Status)) { + goto ERROR; + } + + } else { + // + // Create events for variuos asynchronous operations. + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpCommonNotify, + &HttpInstance->IsTcp6ConnDone, + &HttpInstance->Tcp6ConnToken.CompletionToken.Event + ); + if (EFI_ERROR (Status)) { + goto ERROR; + } + + // + // Initialize Tcp6CloseToken + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpCommonNotify, + &HttpInstance->IsTcp6CloseDone, + &HttpInstance->Tcp6CloseToken.CompletionToken.Event + ); + if (EFI_ERROR (Status)) { + goto ERROR; + } + } + + return EFI_SUCCESS; + +ERROR: + // + // Error handling + // + HttpCloseTcpConnCloseEvent (HttpInstance); + + return Status; +} + + +/** + Close events in the TCP connection token and TCP close token. + + @param[in] HttpInstance Pointer to HTTP_PROTOCOL structure. + +**/ +VOID +HttpCloseTcpConnCloseEvent ( + IN HTTP_PROTOCOL *HttpInstance + ) +{ + ASSERT (HttpInstance != NULL); + + if (HttpInstance->LocalAddressIsIPv6) { + if (NULL != HttpInstance->Tcp6ConnToken.CompletionToken.Event) { + gBS->CloseEvent (HttpInstance->Tcp6ConnToken.CompletionToken.Event); + HttpInstance->Tcp6ConnToken.CompletionToken.Event = NULL; + } + + if (NULL != HttpInstance->Tcp6CloseToken.CompletionToken.Event) { + gBS->CloseEvent(HttpInstance->Tcp6CloseToken.CompletionToken.Event); + HttpInstance->Tcp6CloseToken.CompletionToken.Event = NULL; + } + + } else { + if (NULL != HttpInstance->Tcp4ConnToken.CompletionToken.Event) { + gBS->CloseEvent (HttpInstance->Tcp4ConnToken.CompletionToken.Event); + HttpInstance->Tcp4ConnToken.CompletionToken.Event = NULL; + } + + if (NULL != HttpInstance->Tcp4CloseToken.CompletionToken.Event) { + gBS->CloseEvent(HttpInstance->Tcp4CloseToken.CompletionToken.Event); + HttpInstance->Tcp4CloseToken.CompletionToken.Event = NULL; + } + } + +} + +/** + Create event for the TCP transmit token. + + @param[in] Wrap Point to HTTP token's wrap data. + + @retval EFI_SUCCESS The events is created successfully. + @retval others Other error as indicated. + +**/ +EFI_STATUS +HttpCreateTcpTxEvent ( + IN HTTP_TOKEN_WRAP *Wrap + ) +{ + EFI_STATUS Status; + HTTP_PROTOCOL *HttpInstance; + HTTP_TCP_TOKEN_WRAP *TcpWrap; + + HttpInstance = Wrap->HttpInstance; + TcpWrap = &Wrap->TcpWrap; + + if (!HttpInstance->LocalAddressIsIPv6) { + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpTcpTransmitNotify, + Wrap, + &TcpWrap->Tx4Token.CompletionToken.Event + ); + if (EFI_ERROR (Status)) { + return Status; + } + + TcpWrap->Tx4Data.Push = TRUE; + TcpWrap->Tx4Data.Urgent = FALSE; + TcpWrap->Tx4Data.FragmentCount = 1; + TcpWrap->Tx4Token.Packet.TxData = &Wrap->TcpWrap.Tx4Data; + TcpWrap->Tx4Token.CompletionToken.Status = EFI_NOT_READY; + + } else { + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpTcpTransmitNotify, + Wrap, + &TcpWrap->Tx6Token.CompletionToken.Event + ); + if (EFI_ERROR (Status)) { + return Status; + } + + TcpWrap->Tx6Data.Push = TRUE; + TcpWrap->Tx6Data.Urgent = FALSE; + TcpWrap->Tx6Data.FragmentCount = 1; + TcpWrap->Tx6Token.Packet.TxData = &Wrap->TcpWrap.Tx6Data; + TcpWrap->Tx6Token.CompletionToken.Status =EFI_NOT_READY; + + } + + return EFI_SUCCESS; +} + +/** + Create event for the TCP receive token which is used to receive HTTP header. + + @param[in] HttpInstance Pointer to HTTP_PROTOCOL structure. + + @retval EFI_SUCCESS The events is created successfully. + @retval others Other error as indicated. + +**/ +EFI_STATUS +HttpCreateTcpRxEventForHeader ( + IN HTTP_PROTOCOL *HttpInstance + ) +{ + EFI_STATUS Status; + + if (!HttpInstance->LocalAddressIsIPv6) { + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpCommonNotify, + &HttpInstance->IsRxDone, + &HttpInstance->Rx4Token.CompletionToken.Event + ); + if (EFI_ERROR (Status)) { + return Status; + } + + HttpInstance->Rx4Data.FragmentCount = 1; + HttpInstance->Rx4Token.Packet.RxData = &HttpInstance->Rx4Data; + HttpInstance->Rx4Token.CompletionToken.Status = EFI_NOT_READY; + + } else { + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpCommonNotify, + &HttpInstance->IsRxDone, + &HttpInstance->Rx6Token.CompletionToken.Event + ); + if (EFI_ERROR (Status)) { + return Status; + } + + HttpInstance->Rx6Data.FragmentCount =1; + HttpInstance->Rx6Token.Packet.RxData = &HttpInstance->Rx6Data; + HttpInstance->Rx6Token.CompletionToken.Status = EFI_NOT_READY; + + } + + + return EFI_SUCCESS; +} + +/** + Create event for the TCP receive token which is used to receive HTTP body. + + @param[in] Wrap Point to HTTP token's wrap data. + + @retval EFI_SUCCESS The events is created successfully. + @retval others Other error as indicated. + +**/ +EFI_STATUS +HttpCreateTcpRxEvent ( + IN HTTP_TOKEN_WRAP *Wrap + ) +{ + EFI_STATUS Status; + HTTP_PROTOCOL *HttpInstance; + HTTP_TCP_TOKEN_WRAP *TcpWrap; + + HttpInstance = Wrap->HttpInstance; + TcpWrap = &Wrap->TcpWrap; + if (!HttpInstance->LocalAddressIsIPv6) { + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpTcpReceiveNotify, + Wrap, + &TcpWrap->Rx4Token.CompletionToken.Event + ); + if (EFI_ERROR (Status)) { + return Status; + } + + TcpWrap->Rx4Data.FragmentCount = 1; + TcpWrap->Rx4Token.Packet.RxData = &Wrap->TcpWrap.Rx4Data; + TcpWrap->Rx4Token.CompletionToken.Status = EFI_NOT_READY; + + } else { + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpTcpReceiveNotify, + Wrap, + &TcpWrap->Rx6Token.CompletionToken.Event + ); + if (EFI_ERROR (Status)) { + return Status; + } + + TcpWrap->Rx6Data.FragmentCount = 1; + TcpWrap->Rx6Token.Packet.RxData = &Wrap->TcpWrap.Rx6Data; + TcpWrap->Rx6Token.CompletionToken.Status = EFI_NOT_READY; + } + + return EFI_SUCCESS; +} + +/** + Close Events for Tcp Receive Tokens for HTTP body and HTTP header. + + @param[in] Wrap Pointer to HTTP token's wrap data. + +**/ +VOID +HttpCloseTcpRxEvent ( + IN HTTP_TOKEN_WRAP *Wrap + ) +{ + HTTP_PROTOCOL *HttpInstance; + + ASSERT (Wrap != NULL); + HttpInstance = Wrap->HttpInstance; + + if (HttpInstance->LocalAddressIsIPv6) { + if (Wrap->TcpWrap.Rx6Token.CompletionToken.Event != NULL) { + gBS->CloseEvent (Wrap->TcpWrap.Rx6Token.CompletionToken.Event); + } + + if (HttpInstance->Rx6Token.CompletionToken.Event != NULL) { + gBS->CloseEvent (HttpInstance->Rx6Token.CompletionToken.Event); + HttpInstance->Rx6Token.CompletionToken.Event = NULL; + } + } else { + if (Wrap->TcpWrap.Rx4Token.CompletionToken.Event != NULL) { + gBS->CloseEvent (Wrap->TcpWrap.Rx4Token.CompletionToken.Event); + } + + if (HttpInstance->Rx4Token.CompletionToken.Event != NULL) { + gBS->CloseEvent (HttpInstance->Rx4Token.CompletionToken.Event); + HttpInstance->Rx4Token.CompletionToken.Event = NULL; + } + } +} + +/** + Intiialize the HTTP_PROTOCOL structure to the unconfigured state. + + @param[in, out] HttpInstance Pointer to HTTP_PROTOCOL structure. + @param[in] IpVersion Indicate us TCP4 protocol or TCP6 protocol. + + @retval EFI_SUCCESS HTTP_PROTOCOL structure is initialized successfully. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpInitProtocol ( + IN OUT HTTP_PROTOCOL *HttpInstance, + IN BOOLEAN IpVersion + ) +{ + EFI_STATUS Status; + VOID *Interface; + BOOLEAN UsingIpv6; + + ASSERT (HttpInstance != NULL); + UsingIpv6 = IpVersion; + + if (!UsingIpv6) { + // + // Create TCP4 child. + // + Status = NetLibCreateServiceChild ( + HttpInstance->Service->ControllerHandle, + HttpInstance->Service->Ip4DriverBindingHandle, + &gEfiTcp4ServiceBindingProtocolGuid, + &HttpInstance->Tcp4ChildHandle + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + HttpInstance->Tcp4ChildHandle, + &gEfiTcp4ProtocolGuid, + (VOID **) &Interface, + HttpInstance->Service->Ip4DriverBindingHandle, + HttpInstance->Service->ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + HttpInstance->Tcp4ChildHandle, + &gEfiTcp4ProtocolGuid, + (VOID **) &HttpInstance->Tcp4, + HttpInstance->Service->Ip4DriverBindingHandle, + HttpInstance->Handle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + if (EFI_ERROR(Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + HttpInstance->Service->Tcp4ChildHandle, + &gEfiTcp4ProtocolGuid, + (VOID **) &Interface, + HttpInstance->Service->Ip4DriverBindingHandle, + HttpInstance->Handle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + if (EFI_ERROR(Status)) { + goto ON_ERROR; + } + } else { + // + // Create TCP6 Child. + // + Status = NetLibCreateServiceChild ( + HttpInstance->Service->ControllerHandle, + HttpInstance->Service->Ip6DriverBindingHandle, + &gEfiTcp6ServiceBindingProtocolGuid, + &HttpInstance->Tcp6ChildHandle + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + HttpInstance->Tcp6ChildHandle, + &gEfiTcp6ProtocolGuid, + (VOID **) &Interface, + HttpInstance->Service->Ip6DriverBindingHandle, + HttpInstance->Service->ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + HttpInstance->Tcp6ChildHandle, + &gEfiTcp6ProtocolGuid, + (VOID **) &HttpInstance->Tcp6, + HttpInstance->Service->Ip6DriverBindingHandle, + HttpInstance->Handle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + + if (EFI_ERROR(Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + HttpInstance->Service->Tcp6ChildHandle, + &gEfiTcp6ProtocolGuid, + (VOID **) &Interface, + HttpInstance->Service->Ip6DriverBindingHandle, + HttpInstance->Handle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + + if (EFI_ERROR(Status)) { + goto ON_ERROR; + } + } + + HttpInstance->Url = AllocateZeroPool (HTTP_URL_BUFFER_LEN); + if (HttpInstance->Url == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + + return EFI_SUCCESS; + +ON_ERROR: + + if (HttpInstance->Tcp4ChildHandle != NULL) { + gBS->CloseProtocol ( + HttpInstance->Tcp4ChildHandle, + &gEfiTcp4ProtocolGuid, + HttpInstance->Service->Ip4DriverBindingHandle, + HttpInstance->Service->ControllerHandle + ); + + gBS->CloseProtocol ( + HttpInstance->Tcp4ChildHandle, + &gEfiTcp4ProtocolGuid, + HttpInstance->Service->Ip4DriverBindingHandle, + HttpInstance->Handle + ); + + NetLibDestroyServiceChild ( + HttpInstance->Service->ControllerHandle, + HttpInstance->Service->Ip4DriverBindingHandle, + &gEfiTcp4ServiceBindingProtocolGuid, + HttpInstance->Tcp4ChildHandle + ); + } + + if (HttpInstance->Service->Tcp4ChildHandle != NULL) { + gBS->CloseProtocol ( + HttpInstance->Service->Tcp4ChildHandle, + &gEfiTcp4ProtocolGuid, + HttpInstance->Service->Ip4DriverBindingHandle, + HttpInstance->Handle + ); + } + + if (HttpInstance->Tcp6ChildHandle != NULL) { + gBS->CloseProtocol ( + HttpInstance->Tcp6ChildHandle, + &gEfiTcp6ProtocolGuid, + HttpInstance->Service->Ip6DriverBindingHandle, + HttpInstance->Service->ControllerHandle + ); + + gBS->CloseProtocol ( + HttpInstance->Tcp6ChildHandle, + &gEfiTcp6ProtocolGuid, + HttpInstance->Service->Ip6DriverBindingHandle, + HttpInstance->Handle + ); + + NetLibDestroyServiceChild ( + HttpInstance->Service->ControllerHandle, + HttpInstance->Service->Ip6DriverBindingHandle, + &gEfiTcp6ServiceBindingProtocolGuid, + HttpInstance->Tcp6ChildHandle + ); + } + + if (HttpInstance->Service->Tcp6ChildHandle != NULL) { + gBS->CloseProtocol ( + HttpInstance->Service->Tcp6ChildHandle, + &gEfiTcp6ProtocolGuid, + HttpInstance->Service->Ip6DriverBindingHandle, + HttpInstance->Handle + ); + } + + return EFI_UNSUPPORTED; + +} + +/** + Clean up the HTTP child, release all the resources used by it. + + @param[in] HttpInstance The HTTP child to clean up. + +**/ +VOID +HttpCleanProtocol ( + IN HTTP_PROTOCOL *HttpInstance + ) +{ + HttpCloseConnection (HttpInstance); + + HttpCloseTcpConnCloseEvent (HttpInstance); + + if (HttpInstance->TimeoutEvent != NULL) { + gBS->CloseEvent (HttpInstance->TimeoutEvent); + HttpInstance->TimeoutEvent = NULL; + } + + if (HttpInstance->CacheBody != NULL) { + FreePool (HttpInstance->CacheBody); + HttpInstance->CacheBody = NULL; + HttpInstance->NextMsg = NULL; + } + + if (HttpInstance->RemoteHost != NULL) { + FreePool (HttpInstance->RemoteHost); + HttpInstance->RemoteHost = NULL; + } + + if (HttpInstance->MsgParser != NULL) { + HttpFreeMsgParser (HttpInstance->MsgParser); + HttpInstance->MsgParser = NULL; + } + + if (HttpInstance->Url != NULL) { + FreePool (HttpInstance->Url); + HttpInstance->Url = NULL; + } + + NetMapClean (&HttpInstance->TxTokens); + NetMapClean (&HttpInstance->RxTokens); + + if (HttpInstance->Tcp4ChildHandle != NULL) { + gBS->CloseProtocol ( + HttpInstance->Tcp4ChildHandle, + &gEfiTcp4ProtocolGuid, + HttpInstance->Service->Ip4DriverBindingHandle, + HttpInstance->Service->ControllerHandle + ); + + gBS->CloseProtocol ( + HttpInstance->Tcp4ChildHandle, + &gEfiTcp4ProtocolGuid, + HttpInstance->Service->Ip4DriverBindingHandle, + HttpInstance->Handle + ); + + NetLibDestroyServiceChild ( + HttpInstance->Service->ControllerHandle, + HttpInstance->Service->Ip4DriverBindingHandle, + &gEfiTcp4ServiceBindingProtocolGuid, + HttpInstance->Tcp4ChildHandle + ); + } + + if (HttpInstance->Service->Tcp4ChildHandle != NULL) { + gBS->CloseProtocol ( + HttpInstance->Service->Tcp4ChildHandle, + &gEfiTcp4ProtocolGuid, + HttpInstance->Service->Ip4DriverBindingHandle, + HttpInstance->Handle + ); + } + + if (HttpInstance->Tcp6ChildHandle != NULL) { + gBS->CloseProtocol ( + HttpInstance->Tcp6ChildHandle, + &gEfiTcp6ProtocolGuid, + HttpInstance->Service->Ip6DriverBindingHandle, + HttpInstance->Service->ControllerHandle + ); + + gBS->CloseProtocol ( + HttpInstance->Tcp6ChildHandle, + &gEfiTcp6ProtocolGuid, + HttpInstance->Service->Ip6DriverBindingHandle, + HttpInstance->Handle + ); + + NetLibDestroyServiceChild ( + HttpInstance->Service->ControllerHandle, + HttpInstance->Service->Ip6DriverBindingHandle, + &gEfiTcp6ServiceBindingProtocolGuid, + HttpInstance->Tcp6ChildHandle + ); + } + + if (HttpInstance->Service->Tcp6ChildHandle != NULL) { + gBS->CloseProtocol ( + HttpInstance->Service->Tcp6ChildHandle, + &gEfiTcp6ProtocolGuid, + HttpInstance->Service->Ip6DriverBindingHandle, + HttpInstance->Handle + ); + } + + TlsCloseTxRxEvent (HttpInstance); +} + +/** + Establish TCP connection with HTTP server. + + @param[in] HttpInstance The HTTP instance private data. + + @retval EFI_SUCCESS The TCP connection is established. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpCreateConnection ( + IN HTTP_PROTOCOL *HttpInstance + ) +{ + EFI_STATUS Status; + + // + // Connect to Http server + // + if (!HttpInstance->LocalAddressIsIPv6) { + HttpInstance->IsTcp4ConnDone = FALSE; + HttpInstance->Tcp4ConnToken.CompletionToken.Status = EFI_NOT_READY; + Status = HttpInstance->Tcp4->Connect (HttpInstance->Tcp4, &HttpInstance->Tcp4ConnToken); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "HttpCreateConnection: Tcp4->Connect() = %r\n", Status)); + return Status; + } + + while (!HttpInstance->IsTcp4ConnDone) { + HttpInstance->Tcp4->Poll (HttpInstance->Tcp4); + } + + Status = HttpInstance->Tcp4ConnToken.CompletionToken.Status; + + } else { + HttpInstance->IsTcp6ConnDone = FALSE; + HttpInstance->Tcp6ConnToken.CompletionToken.Status = EFI_NOT_READY; + Status = HttpInstance->Tcp6->Connect (HttpInstance->Tcp6, &HttpInstance->Tcp6ConnToken); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "HttpCreateConnection: Tcp6->Connect() = %r\n", Status)); + return Status; + } + + while(!HttpInstance->IsTcp6ConnDone) { + HttpInstance->Tcp6->Poll (HttpInstance->Tcp6); + } + + Status = HttpInstance->Tcp6ConnToken.CompletionToken.Status; + } + + if (!EFI_ERROR (Status)) { + HttpInstance->State = HTTP_STATE_TCP_CONNECTED; + } + + return Status; +} + +/** + Close existing TCP connection. + + @param[in] HttpInstance The HTTP instance private data. + + @retval EFI_SUCCESS The TCP connection is closed. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpCloseConnection ( + IN HTTP_PROTOCOL *HttpInstance + ) +{ + EFI_STATUS Status; + + if (HttpInstance->State == HTTP_STATE_TCP_CONNECTED) { + + if (HttpInstance->LocalAddressIsIPv6) { + HttpInstance->Tcp6CloseToken.AbortOnClose = TRUE; + HttpInstance->IsTcp6CloseDone = FALSE; + Status = HttpInstance->Tcp6->Close (HttpInstance->Tcp6, &HttpInstance->Tcp6CloseToken); + if (EFI_ERROR (Status)) { + return Status; + } + + while (!HttpInstance->IsTcp6CloseDone) { + HttpInstance->Tcp6->Poll (HttpInstance->Tcp6); + } + + } else { + HttpInstance->Tcp4CloseToken.AbortOnClose = TRUE; + HttpInstance->IsTcp4CloseDone = FALSE; + Status = HttpInstance->Tcp4->Close (HttpInstance->Tcp4, &HttpInstance->Tcp4CloseToken); + if (EFI_ERROR (Status)) { + return Status; + } + + while (!HttpInstance->IsTcp4CloseDone) { + HttpInstance->Tcp4->Poll (HttpInstance->Tcp4); + } + } + + } + + HttpInstance->State = HTTP_STATE_TCP_CLOSED; + return EFI_SUCCESS; +} + +/** + Configure TCP4 protocol child. + + @param[in] HttpInstance The HTTP instance private data. + @param[in] Wrap The HTTP token's wrap data. + + @retval EFI_SUCCESS The TCP4 protocol child is configured. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpConfigureTcp4 ( + IN HTTP_PROTOCOL *HttpInstance, + IN HTTP_TOKEN_WRAP *Wrap + ) +{ + EFI_STATUS Status; + EFI_TCP4_CONFIG_DATA *Tcp4CfgData; + EFI_TCP4_ACCESS_POINT *Tcp4AP; + EFI_TCP4_OPTION *Tcp4Option; + + ASSERT (HttpInstance != NULL); + + + Tcp4CfgData = &HttpInstance->Tcp4CfgData; + ZeroMem (Tcp4CfgData, sizeof (EFI_TCP4_CONFIG_DATA)); + + Tcp4CfgData->TypeOfService = HTTP_TOS_DEAULT; + Tcp4CfgData->TimeToLive = HTTP_TTL_DEAULT; + Tcp4CfgData->ControlOption = &HttpInstance->Tcp4Option; + + Tcp4AP = &Tcp4CfgData->AccessPoint; + Tcp4AP->UseDefaultAddress = HttpInstance->IPv4Node.UseDefaultAddress; + if (!Tcp4AP->UseDefaultAddress) { + IP4_COPY_ADDRESS (&Tcp4AP->StationAddress, &HttpInstance->IPv4Node.LocalAddress); + IP4_COPY_ADDRESS (&Tcp4AP->SubnetMask, &HttpInstance->IPv4Node.LocalSubnet); + } + + Tcp4AP->StationPort = HttpInstance->IPv4Node.LocalPort; + Tcp4AP->RemotePort = HttpInstance->RemotePort; + Tcp4AP->ActiveFlag = TRUE; + IP4_COPY_ADDRESS (&Tcp4AP->RemoteAddress, &HttpInstance->RemoteAddr); + + Tcp4Option = Tcp4CfgData->ControlOption; + Tcp4Option->ReceiveBufferSize = HTTP_BUFFER_SIZE_DEAULT; + Tcp4Option->SendBufferSize = HTTP_BUFFER_SIZE_DEAULT; + Tcp4Option->MaxSynBackLog = HTTP_MAX_SYN_BACK_LOG; + Tcp4Option->ConnectionTimeout = HTTP_CONNECTION_TIMEOUT; + Tcp4Option->DataRetries = HTTP_DATA_RETRIES; + Tcp4Option->FinTimeout = HTTP_FIN_TIMEOUT; + Tcp4Option->KeepAliveProbes = HTTP_KEEP_ALIVE_PROBES; + Tcp4Option->KeepAliveTime = HTTP_KEEP_ALIVE_TIME; + Tcp4Option->KeepAliveInterval = HTTP_KEEP_ALIVE_INTERVAL; + Tcp4Option->EnableNagle = TRUE; + Tcp4CfgData->ControlOption = Tcp4Option; + + Status = HttpInstance->Tcp4->Configure (HttpInstance->Tcp4, Tcp4CfgData); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "HttpConfigureTcp4 - %r\n", Status)); + return Status; + } + + Status = HttpCreateTcpConnCloseEvent (HttpInstance); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = HttpCreateTcpTxEvent (Wrap); + if (EFI_ERROR (Status)) { + return Status; + } + + HttpInstance->State = HTTP_STATE_TCP_CONFIGED; + + return EFI_SUCCESS; +} + +/** + Configure TCP6 protocol child. + + @param[in] HttpInstance The HTTP instance private data. + @param[in] Wrap The HTTP token's wrap data. + + @retval EFI_SUCCESS The TCP6 protocol child is configured. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpConfigureTcp6 ( + IN HTTP_PROTOCOL *HttpInstance, + IN HTTP_TOKEN_WRAP *Wrap + ) +{ + EFI_STATUS Status; + EFI_TCP6_CONFIG_DATA *Tcp6CfgData; + EFI_TCP6_ACCESS_POINT *Tcp6Ap; + EFI_TCP6_OPTION *Tcp6Option; + + ASSERT (HttpInstance != NULL); + + Tcp6CfgData = &HttpInstance->Tcp6CfgData; + ZeroMem (Tcp6CfgData, sizeof (EFI_TCP6_CONFIG_DATA)); + + Tcp6CfgData->TrafficClass = 0; + Tcp6CfgData->HopLimit = 255; + Tcp6CfgData->ControlOption = &HttpInstance->Tcp6Option; + + Tcp6Ap = &Tcp6CfgData->AccessPoint; + Tcp6Ap->ActiveFlag = TRUE; + Tcp6Ap->StationPort = HttpInstance->Ipv6Node.LocalPort; + Tcp6Ap->RemotePort = HttpInstance->RemotePort; + IP6_COPY_ADDRESS (&Tcp6Ap->StationAddress, &HttpInstance->Ipv6Node.LocalAddress); + IP6_COPY_ADDRESS (&Tcp6Ap->RemoteAddress , &HttpInstance->RemoteIpv6Addr); + + Tcp6Option = Tcp6CfgData->ControlOption; + Tcp6Option->ReceiveBufferSize = HTTP_BUFFER_SIZE_DEAULT; + Tcp6Option->SendBufferSize = HTTP_BUFFER_SIZE_DEAULT; + Tcp6Option->MaxSynBackLog = HTTP_MAX_SYN_BACK_LOG; + Tcp6Option->ConnectionTimeout = HTTP_CONNECTION_TIMEOUT; + Tcp6Option->DataRetries = HTTP_DATA_RETRIES; + Tcp6Option->FinTimeout = HTTP_FIN_TIMEOUT; + Tcp6Option->KeepAliveProbes = HTTP_KEEP_ALIVE_PROBES; + Tcp6Option->KeepAliveTime = HTTP_KEEP_ALIVE_TIME; + Tcp6Option->KeepAliveInterval = HTTP_KEEP_ALIVE_INTERVAL; + Tcp6Option->EnableNagle = TRUE; + + Status = HttpInstance->Tcp6->Configure (HttpInstance->Tcp6, Tcp6CfgData); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "HttpConfigureTcp6 - %r\n", Status)); + return Status; + } + + Status = HttpCreateTcpConnCloseEvent (HttpInstance); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = HttpCreateTcpTxEvent (Wrap); + if (EFI_ERROR (Status)) { + return Status; + } + + HttpInstance->State = HTTP_STATE_TCP_CONFIGED; + + return EFI_SUCCESS; + +} + +/** + Check existing TCP connection, if in error state, recover TCP4 connection. Then, + connect one TLS session if required. + + @param[in] HttpInstance The HTTP instance private data. + + @retval EFI_SUCCESS The TCP connection is established. + @retval EFI_NOT_READY TCP4 protocol child is not created or configured. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpConnectTcp4 ( + IN HTTP_PROTOCOL *HttpInstance + ) +{ + EFI_STATUS Status; + EFI_TCP4_CONNECTION_STATE Tcp4State; + + + if (HttpInstance->State < HTTP_STATE_TCP_CONFIGED || HttpInstance->Tcp4 == NULL) { + return EFI_NOT_READY; + } + + Status = HttpInstance->Tcp4->GetModeData( + HttpInstance->Tcp4, + &Tcp4State, + NULL, + NULL, + NULL, + NULL + ); + if (EFI_ERROR(Status)){ + DEBUG ((EFI_D_ERROR, "Tcp4 GetModeData fail - %x\n", Status)); + return Status; + } + + if (Tcp4State == Tcp4StateEstablished) { + return EFI_SUCCESS; + } else if (Tcp4State > Tcp4StateEstablished ) { + HttpCloseConnection(HttpInstance); + } + + Status = HttpCreateConnection (HttpInstance); + if (EFI_ERROR(Status)){ + DEBUG ((EFI_D_ERROR, "Tcp4 Connection fail - %x\n", Status)); + return Status; + } + + // + // Tls session connection. + // + if (HttpInstance->UseHttps) { + if (HttpInstance->TimeoutEvent == NULL) { + // + // Create TimeoutEvent for TLS connection. + // + Status = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &HttpInstance->TimeoutEvent + ); + if (EFI_ERROR (Status)) { + TlsCloseTxRxEvent (HttpInstance); + return Status; + } + } + + // + // Start the timer, and wait Timeout seconds for connection. + // + Status = gBS->SetTimer (HttpInstance->TimeoutEvent, TimerRelative, HTTP_CONNECTION_TIMEOUT * TICKS_PER_SECOND); + if (EFI_ERROR (Status)) { + TlsCloseTxRxEvent (HttpInstance); + return Status; + } + + Status = TlsConnectSession (HttpInstance, HttpInstance->TimeoutEvent); + + gBS->SetTimer (HttpInstance->TimeoutEvent, TimerCancel, 0); + + if (EFI_ERROR (Status)) { + TlsCloseTxRxEvent (HttpInstance); + return Status; + } + } + + return Status; +} + +/** + Check existing TCP connection, if in error state, recover TCP6 connection. Then, + connect one TLS session if required. + + @param[in] HttpInstance The HTTP instance private data. + + @retval EFI_SUCCESS The TCP connection is established. + @retval EFI_NOT_READY TCP6 protocol child is not created or configured. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpConnectTcp6 ( + IN HTTP_PROTOCOL *HttpInstance + ) +{ + EFI_STATUS Status; + EFI_TCP6_CONNECTION_STATE Tcp6State; + + if (HttpInstance->State < HTTP_STATE_TCP_CONFIGED || HttpInstance->Tcp6 == NULL) { + return EFI_NOT_READY; + } + + Status = HttpInstance->Tcp6->GetModeData ( + HttpInstance->Tcp6, + &Tcp6State, + NULL, + NULL, + NULL, + NULL + ); + + if (EFI_ERROR(Status)){ + DEBUG ((EFI_D_ERROR, "Tcp6 GetModeData fail - %x\n", Status)); + return Status; + } + + if (Tcp6State == Tcp6StateEstablished) { + return EFI_SUCCESS; + } else if (Tcp6State > Tcp6StateEstablished ) { + HttpCloseConnection(HttpInstance); + } + + Status = HttpCreateConnection (HttpInstance); + if (EFI_ERROR(Status)){ + DEBUG ((EFI_D_ERROR, "Tcp6 Connection fail - %x\n", Status)); + return Status; + } + + // + // Tls session connection. + // + if (HttpInstance->UseHttps) { + if (HttpInstance->TimeoutEvent == NULL) { + // + // Create TimeoutEvent for TLS connection. + // + Status = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &HttpInstance->TimeoutEvent + ); + if (EFI_ERROR (Status)) { + TlsCloseTxRxEvent (HttpInstance); + return Status; + } + } + + // + // Start the timer, and wait Timeout seconds for connection. + // + Status = gBS->SetTimer (HttpInstance->TimeoutEvent, TimerRelative, HTTP_CONNECTION_TIMEOUT * TICKS_PER_SECOND); + if (EFI_ERROR (Status)) { + TlsCloseTxRxEvent (HttpInstance); + return Status; + } + + Status = TlsConnectSession (HttpInstance, HttpInstance->TimeoutEvent); + + gBS->SetTimer (HttpInstance->TimeoutEvent, TimerCancel, 0); + + if (EFI_ERROR (Status)) { + TlsCloseTxRxEvent (HttpInstance); + return Status; + } + } + + return Status; +} + +/** + Initialize Http session. + + @param[in] HttpInstance The HTTP instance private data. + @param[in] Wrap The HTTP token's wrap data. + @param[in] Configure The Flag indicates whether need to initialize session. + @param[in] TlsConfigure The Flag indicates whether it's the new Tls session. + + @retval EFI_SUCCESS The initialization of session is done. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpInitSession ( + IN HTTP_PROTOCOL *HttpInstance, + IN HTTP_TOKEN_WRAP *Wrap, + IN BOOLEAN Configure, + IN BOOLEAN TlsConfigure + ) +{ + EFI_STATUS Status; + ASSERT (HttpInstance != NULL); + + // + // Configure Tls session. + // + if (TlsConfigure) { + Status = TlsConfigureSession (HttpInstance); + if (EFI_ERROR (Status)) { + return Status; + } + } + + if (!HttpInstance->LocalAddressIsIPv6) { + // + // Configure TCP instance. + // + if (Configure) { + Status = HttpConfigureTcp4 (HttpInstance, Wrap); + if (EFI_ERROR (Status)) { + return Status; + } + } + + // + // Connect TCP. + // + Status = HttpConnectTcp4 (HttpInstance); + if (EFI_ERROR (Status)) { + return Status; + } + } else { + // + // Configure TCP instance. + // + if (Configure) { + Status = HttpConfigureTcp6 (HttpInstance, Wrap); + if (EFI_ERROR (Status)) { + return Status; + } + } + + // + // Connect TCP. + // + Status = HttpConnectTcp6 (HttpInstance); + if (EFI_ERROR (Status)) { + return Status; + } + } + + return EFI_SUCCESS; + +} + +/** + Send the HTTP or HTTPS message through TCP4 or TCP6. + + @param[in] HttpInstance The HTTP instance private data. + @param[in] Wrap The HTTP token's wrap data. + @param[in] TxString Buffer containing the HTTP message string. + @param[in] TxStringLen Length of the HTTP message string in bytes. + + @retval EFI_SUCCESS The HTTP message is queued into TCP transmit queue. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpTransmitTcp ( + IN HTTP_PROTOCOL *HttpInstance, + IN HTTP_TOKEN_WRAP *Wrap, + IN UINT8 *TxString, + IN UINTN TxStringLen + ) +{ + EFI_STATUS Status; + EFI_TCP4_IO_TOKEN *Tx4Token; + EFI_TCP4_PROTOCOL *Tcp4; + EFI_TCP6_IO_TOKEN *Tx6Token; + EFI_TCP6_PROTOCOL *Tcp6; + UINT8 *Buffer; + UINTN BufferSize; + NET_FRAGMENT TempFragment; + + Status = EFI_SUCCESS; + Buffer = NULL; + TempFragment.Len = 0; + TempFragment.Bulk = NULL; + + // + // Need to encrypt data. + // + if (HttpInstance->UseHttps) { + // + // Build BufferOut data + // + BufferSize = sizeof (TLS_RECORD_HEADER) + TxStringLen; + Buffer = AllocateZeroPool (BufferSize); + if (Buffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + return Status; + } + ((TLS_RECORD_HEADER *) Buffer)->ContentType = TlsContentTypeApplicationData; + ((TLS_RECORD_HEADER *) Buffer)->Version.Major = HttpInstance->TlsConfigData.Version.Major; + ((TLS_RECORD_HEADER *) Buffer)->Version.Minor = HttpInstance->TlsConfigData.Version.Minor; + ((TLS_RECORD_HEADER *) Buffer)->Length = (UINT16) (TxStringLen); + CopyMem (Buffer + sizeof (TLS_RECORD_HEADER), TxString, TxStringLen); + + // + // Encrypt Packet. + // + Status = TlsProcessMessage ( + HttpInstance, + Buffer, + BufferSize, + EfiTlsEncrypt, + &TempFragment + ); + + FreePool (Buffer); + + if (EFI_ERROR (Status)) { + return Status; + } + } + + if (!HttpInstance->LocalAddressIsIPv6) { + Tcp4 = HttpInstance->Tcp4; + Tx4Token = &Wrap->TcpWrap.Tx4Token; + + if (HttpInstance->UseHttps) { + Tx4Token->Packet.TxData->DataLength = TempFragment.Len; + Tx4Token->Packet.TxData->FragmentTable[0].FragmentLength = TempFragment.Len; + Tx4Token->Packet.TxData->FragmentTable[0].FragmentBuffer = (VOID *) TempFragment.Bulk; + } else { + Tx4Token->Packet.TxData->DataLength = (UINT32) TxStringLen; + Tx4Token->Packet.TxData->FragmentTable[0].FragmentLength = (UINT32) TxStringLen; + Tx4Token->Packet.TxData->FragmentTable[0].FragmentBuffer = (VOID *) TxString; + } + + Tx4Token->CompletionToken.Status = EFI_NOT_READY; + + Wrap->TcpWrap.IsTxDone = FALSE; + Status = Tcp4->Transmit (Tcp4, Tx4Token); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Transmit failed: %r\n", Status)); + return Status; + } + + } else { + Tcp6 = HttpInstance->Tcp6; + Tx6Token = &Wrap->TcpWrap.Tx6Token; + + if (HttpInstance->UseHttps) { + Tx6Token->Packet.TxData->DataLength = TempFragment.Len; + Tx6Token->Packet.TxData->FragmentTable[0].FragmentLength = TempFragment.Len; + Tx6Token->Packet.TxData->FragmentTable[0].FragmentBuffer = (VOID *) TempFragment.Bulk; + } else { + Tx6Token->Packet.TxData->DataLength = (UINT32) TxStringLen; + Tx6Token->Packet.TxData->FragmentTable[0].FragmentLength = (UINT32) TxStringLen; + Tx6Token->Packet.TxData->FragmentTable[0].FragmentBuffer = (VOID *) TxString; + } + + Tx6Token->CompletionToken.Status = EFI_NOT_READY; + + Wrap->TcpWrap.IsTxDone = FALSE; + Status = Tcp6->Transmit (Tcp6, Tx6Token); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Transmit failed: %r\n", Status)); + return Status; + } + } + + return Status; +} + +/** + Check whether the user's token or event has already + been enqueue on HTTP Tx or Rx Token list. + + @param[in] Map The container of either user's transmit or receive + token. + @param[in] Item Current item to check against. + @param[in] Context The Token to check againist. + + @retval EFI_ACCESS_DENIED The token or event has already been enqueued in IP + @retval EFI_SUCCESS The current item isn't the same token/event as the + context. + +**/ +EFI_STATUS +EFIAPI +HttpTokenExist ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Context + ) +{ + EFI_HTTP_TOKEN *Token; + EFI_HTTP_TOKEN *TokenInItem; + + Token = (EFI_HTTP_TOKEN *) Context; + TokenInItem = (EFI_HTTP_TOKEN *) Item->Key; + + if (Token == TokenInItem || Token->Event == TokenInItem->Event) { + return EFI_ACCESS_DENIED; + } + + return EFI_SUCCESS; +} + +/** + Check whether the HTTP message associated with Tx4Token or Tx6Token is already sent out. + + @param[in] Map The container of Tx4Token or Tx6Token. + @param[in] Item Current item to check against. + @param[in] Context The Token to check againist. + + @retval EFI_NOT_READY The HTTP message is still queued in the list. + @retval EFI_SUCCESS The HTTP message has been sent out. + +**/ +EFI_STATUS +EFIAPI +HttpTcpNotReady ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Context + ) +{ + HTTP_TOKEN_WRAP *ValueInItem; + + ValueInItem = (HTTP_TOKEN_WRAP *) Item->Value; + + if (!ValueInItem->TcpWrap.IsTxDone) { + return EFI_NOT_READY; + } + + return EFI_SUCCESS; +} + +/** + Transmit the HTTP or HTTPS mssage by processing the associated HTTP token. + + @param[in] Map The container of Tx4Token or Tx6Token. + @param[in] Item Current item to check against. + @param[in] Context The Token to check againist. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources. + @retval EFI_SUCCESS The HTTP message is queued into TCP transmit + queue. + +**/ +EFI_STATUS +EFIAPI +HttpTcpTransmit ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Context + ) +{ + HTTP_TOKEN_WRAP *ValueInItem; + EFI_STATUS Status; + CHAR8 *RequestMsg; + CHAR8 *Url; + UINTN UrlSize; + UINTN RequestMsgSize; + + RequestMsg = NULL; + + ValueInItem = (HTTP_TOKEN_WRAP *) Item->Value; + if (ValueInItem->TcpWrap.IsTxDone) { + return EFI_SUCCESS; + } + + // + // Parse the URI of the remote host. + // + UrlSize = StrLen (ValueInItem->HttpToken->Message->Data.Request->Url) + 1; + Url = AllocatePool (UrlSize); + if (Url == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + UnicodeStrToAsciiStrS (ValueInItem->HttpToken->Message->Data.Request->Url, Url, UrlSize); + + // + // Create request message. + // + Status = HttpGenRequestMessage ( + ValueInItem->HttpToken->Message, + Url, + &RequestMsg, + &RequestMsgSize + ); + FreePool (Url); + + if (EFI_ERROR (Status) || NULL == RequestMsg){ + return Status; + } + + ASSERT (RequestMsg != NULL); + + // + // Transmit the request message. + // + Status = HttpTransmitTcp ( + ValueInItem->HttpInstance, + ValueInItem, + (UINT8*) RequestMsg, + RequestMsgSize + ); + FreePool (RequestMsg); + return Status; +} + +/** + Receive the HTTP response by processing the associated HTTP token. + + @param[in] Map The container of Rx4Token or Rx6Token. + @param[in] Item Current item to check against. + @param[in] Context The Token to check againist. + + @retval EFI_SUCCESS The HTTP response is queued into TCP receive + queue. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +EFIAPI +HttpTcpReceive ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Context + ) +{ + // + // Process the queued HTTP response. + // + return HttpResponseWorker ((HTTP_TOKEN_WRAP *) Item->Value); +} + +/** + Receive the HTTP header by processing the associated HTTP token. + + @param[in] HttpInstance The HTTP instance private data. + @param[in, out] SizeofHeaders The HTTP header length. + @param[in, out] BufferSize The size of buffer to cacahe the header message. + @param[in] Timeout The time to wait for receiving the header packet. + + @retval EFI_SUCCESS The HTTP header is received. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +HttpTcpReceiveHeader ( + IN HTTP_PROTOCOL *HttpInstance, + IN OUT UINTN *SizeofHeaders, + IN OUT UINTN *BufferSize, + IN EFI_EVENT Timeout + ) +{ + EFI_STATUS Status; + EFI_TCP4_IO_TOKEN *Rx4Token; + EFI_TCP4_PROTOCOL *Tcp4; + EFI_TCP6_IO_TOKEN *Rx6Token; + EFI_TCP6_PROTOCOL *Tcp6; + CHAR8 **EndofHeader; + CHAR8 **HttpHeaders; + CHAR8 *Buffer; + NET_FRAGMENT Fragment; + + ASSERT (HttpInstance != NULL); + + EndofHeader = HttpInstance->EndofHeader; + HttpHeaders = HttpInstance->HttpHeaders; + Tcp4 = HttpInstance->Tcp4; + Tcp6 = HttpInstance->Tcp6; + Buffer = NULL; + Rx4Token = NULL; + Rx6Token = NULL; + Fragment.Len = 0; + Fragment.Bulk = NULL; + + if (HttpInstance->LocalAddressIsIPv6) { + ASSERT (Tcp6 != NULL); + } else { + ASSERT (Tcp4 != NULL); + } + + if (!HttpInstance->UseHttps) { + Status = HttpCreateTcpRxEventForHeader (HttpInstance); + if (EFI_ERROR (Status)) { + return Status; + } + } + + if (!HttpInstance->LocalAddressIsIPv6) { + if (!HttpInstance->UseHttps) { + Rx4Token = &HttpInstance->Rx4Token; + Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer = AllocateZeroPool (DEF_BUF_LEN); + if (Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + return Status; + } + } + + // + // Receive the HTTP headers only when EFI_HTTP_RESPONSE_DATA is not NULL. + // + while (*EndofHeader == NULL) { + if (!HttpInstance->UseHttps) { + HttpInstance->IsRxDone = FALSE; + Rx4Token->Packet.RxData->DataLength = DEF_BUF_LEN; + Rx4Token->Packet.RxData->FragmentTable[0].FragmentLength = DEF_BUF_LEN; + Status = Tcp4->Receive (Tcp4, Rx4Token); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Tcp4 receive failed: %r\n", Status)); + return Status; + } + + while (!HttpInstance->IsRxDone && ((Timeout == NULL) || EFI_ERROR (gBS->CheckEvent (Timeout)))) { + Tcp4->Poll (Tcp4); + } + + if (!HttpInstance->IsRxDone) { + // + // Cancle the Token before close its Event. + // + Tcp4->Cancel (HttpInstance->Tcp4, &Rx4Token->CompletionToken); + gBS->CloseEvent (Rx4Token->CompletionToken.Event); + Rx4Token->CompletionToken.Status = EFI_TIMEOUT; + } + + Status = Rx4Token->CompletionToken.Status; + if (EFI_ERROR (Status)) { + return Status; + } + + Fragment.Len = Rx4Token->Packet.RxData->FragmentTable[0].FragmentLength; + Fragment.Bulk = (UINT8 *) Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer; + } else { + if (Fragment.Bulk != NULL) { + FreePool (Fragment.Bulk); + Fragment.Bulk = NULL; + } + + Status = HttpsReceive (HttpInstance, &Fragment, Timeout); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Tcp4 receive failed: %r\n", Status)); + return Status; + } + } + + // + // Append the response string. + // + *BufferSize = *SizeofHeaders + Fragment.Len; + Buffer = AllocateZeroPool (*BufferSize); + if (Buffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + return Status; + } + + if (*HttpHeaders != NULL) { + CopyMem (Buffer, *HttpHeaders, *SizeofHeaders); + FreePool (*HttpHeaders); + } + + CopyMem ( + Buffer + *SizeofHeaders, + Fragment.Bulk, + Fragment.Len + ); + *HttpHeaders = Buffer; + *SizeofHeaders = *BufferSize; + + // + // Check whether we received end of HTTP headers. + // + *EndofHeader = AsciiStrStr (*HttpHeaders, HTTP_END_OF_HDR_STR); + }; + + // + // Free the buffer. + // + if (Rx4Token != NULL && Rx4Token->Packet.RxData != NULL && Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer != NULL) { + FreePool (Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer); + Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer = NULL; + Fragment.Bulk = NULL; + } + + if (Fragment.Bulk != NULL) { + FreePool (Fragment.Bulk); + Fragment.Bulk = NULL; + } + } else { + if (!HttpInstance->UseHttps) { + Rx6Token = &HttpInstance->Rx6Token; + Rx6Token->Packet.RxData->FragmentTable[0].FragmentBuffer = AllocateZeroPool (DEF_BUF_LEN); + if (Rx6Token->Packet.RxData->FragmentTable[0].FragmentBuffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + return Status; + } + } + + // + // Receive the HTTP headers only when EFI_HTTP_RESPONSE_DATA is not NULL. + // + while (*EndofHeader == NULL) { + if (!HttpInstance->UseHttps) { + HttpInstance->IsRxDone = FALSE; + Rx6Token->Packet.RxData->DataLength = DEF_BUF_LEN; + Rx6Token->Packet.RxData->FragmentTable[0].FragmentLength = DEF_BUF_LEN; + Status = Tcp6->Receive (Tcp6, Rx6Token); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Tcp6 receive failed: %r\n", Status)); + return Status; + } + + while (!HttpInstance->IsRxDone && ((Timeout == NULL) || EFI_ERROR (gBS->CheckEvent (Timeout)))) { + Tcp6->Poll (Tcp6); + } + + if (!HttpInstance->IsRxDone) { + // + // Cancle the Token before close its Event. + // + Tcp6->Cancel (HttpInstance->Tcp6, &Rx6Token->CompletionToken); + gBS->CloseEvent (Rx6Token->CompletionToken.Event); + Rx6Token->CompletionToken.Status = EFI_TIMEOUT; + } + + Status = Rx6Token->CompletionToken.Status; + if (EFI_ERROR (Status)) { + return Status; + } + + Fragment.Len = Rx6Token->Packet.RxData->FragmentTable[0].FragmentLength; + Fragment.Bulk = (UINT8 *) Rx6Token->Packet.RxData->FragmentTable[0].FragmentBuffer; + } else { + if (Fragment.Bulk != NULL) { + FreePool (Fragment.Bulk); + Fragment.Bulk = NULL; + } + + Status = HttpsReceive (HttpInstance, &Fragment, Timeout); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Tcp6 receive failed: %r\n", Status)); + return Status; + } + } + + // + // Append the response string. + // + *BufferSize = *SizeofHeaders + Fragment.Len; + Buffer = AllocateZeroPool (*BufferSize); + if (Buffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + return Status; + } + + if (*HttpHeaders != NULL) { + CopyMem (Buffer, *HttpHeaders, *SizeofHeaders); + FreePool (*HttpHeaders); + } + + CopyMem ( + Buffer + *SizeofHeaders, + Fragment.Bulk, + Fragment.Len + ); + *HttpHeaders = Buffer; + *SizeofHeaders = *BufferSize; + + // + // Check whether we received end of HTTP headers. + // + *EndofHeader = AsciiStrStr (*HttpHeaders, HTTP_END_OF_HDR_STR); + }; + + // + // Free the buffer. + // + if (Rx6Token != NULL && Rx6Token->Packet.RxData != NULL && Rx6Token->Packet.RxData->FragmentTable[0].FragmentBuffer != NULL) { + FreePool (Rx6Token->Packet.RxData->FragmentTable[0].FragmentBuffer); + Rx6Token->Packet.RxData->FragmentTable[0].FragmentBuffer = NULL; + Fragment.Bulk = NULL; + } + + if (Fragment.Bulk != NULL) { + FreePool (Fragment.Bulk); + Fragment.Bulk = NULL; + } + } + + // + // Skip the CRLF after the HTTP headers. + // + *EndofHeader = *EndofHeader + AsciiStrLen (HTTP_END_OF_HDR_STR); + + return EFI_SUCCESS; +} + +/** + Receive the HTTP body by processing the associated HTTP token. + + @param[in] Wrap The HTTP token's wrap data. + @param[in] HttpMsg The HTTP message data. + + @retval EFI_SUCCESS The HTTP body is received. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpTcpReceiveBody ( + IN HTTP_TOKEN_WRAP *Wrap, + IN EFI_HTTP_MESSAGE *HttpMsg + ) +{ + EFI_STATUS Status; + HTTP_PROTOCOL *HttpInstance; + EFI_TCP6_PROTOCOL *Tcp6; + EFI_TCP6_IO_TOKEN *Rx6Token; + EFI_TCP4_PROTOCOL *Tcp4; + EFI_TCP4_IO_TOKEN *Rx4Token; + + HttpInstance = Wrap->HttpInstance; + Tcp4 = HttpInstance->Tcp4; + Tcp6 = HttpInstance->Tcp6; + Rx4Token = NULL; + Rx6Token = NULL; + + if (HttpInstance->LocalAddressIsIPv6) { + ASSERT (Tcp6 != NULL); + } else { + ASSERT (Tcp4 != NULL); + } + + if (HttpInstance->LocalAddressIsIPv6) { + Rx6Token = &Wrap->TcpWrap.Rx6Token; + Rx6Token ->Packet.RxData->DataLength = (UINT32) HttpMsg->BodyLength; + Rx6Token ->Packet.RxData->FragmentTable[0].FragmentLength = (UINT32) HttpMsg->BodyLength; + Rx6Token ->Packet.RxData->FragmentTable[0].FragmentBuffer = (VOID *) HttpMsg->Body; + Rx6Token->CompletionToken.Status = EFI_NOT_READY; + + Status = Tcp6->Receive (Tcp6, Rx6Token); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Tcp6 receive failed: %r\n", Status)); + return Status; + } + } else { + Rx4Token = &Wrap->TcpWrap.Rx4Token; + Rx4Token->Packet.RxData->DataLength = (UINT32) HttpMsg->BodyLength; + Rx4Token->Packet.RxData->FragmentTable[0].FragmentLength = (UINT32) HttpMsg->BodyLength; + Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer = (VOID *) HttpMsg->Body; + + Rx4Token->CompletionToken.Status = EFI_NOT_READY; + Status = Tcp4->Receive (Tcp4, Rx4Token); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Tcp4 receive failed: %r\n", Status)); + return Status; + } + } + + return EFI_SUCCESS; + +} + +/** + Clean up Tcp Tokens while the Tcp transmission error occurs. + + @param[in] Wrap Pointer to HTTP token's wrap data. + +**/ +VOID +HttpTcpTokenCleanup ( + IN HTTP_TOKEN_WRAP *Wrap + ) +{ + HTTP_PROTOCOL *HttpInstance; + EFI_TCP4_IO_TOKEN *Rx4Token; + EFI_TCP6_IO_TOKEN *Rx6Token; + + ASSERT (Wrap != NULL); + HttpInstance = Wrap->HttpInstance; + Rx4Token = NULL; + Rx6Token = NULL; + + if (HttpInstance->LocalAddressIsIPv6) { + Rx6Token = &Wrap->TcpWrap.Rx6Token; + + if (Rx6Token->CompletionToken.Event != NULL) { + gBS->CloseEvent (Rx6Token->CompletionToken.Event); + Rx6Token->CompletionToken.Event = NULL; + } + + FreePool (Wrap); + + Rx6Token = &HttpInstance->Rx6Token; + + if (Rx6Token->CompletionToken.Event != NULL) { + gBS->CloseEvent (Rx6Token->CompletionToken.Event); + Rx6Token->CompletionToken.Event = NULL; + } + + if (Rx6Token->Packet.RxData->FragmentTable[0].FragmentBuffer != NULL) { + FreePool (Rx6Token->Packet.RxData->FragmentTable[0].FragmentBuffer); + Rx6Token->Packet.RxData->FragmentTable[0].FragmentBuffer = NULL; + } + + } else { + Rx4Token = &Wrap->TcpWrap.Rx4Token; + + if (Rx4Token->CompletionToken.Event != NULL) { + gBS->CloseEvent (Rx4Token->CompletionToken.Event); + Rx4Token->CompletionToken.Event = NULL; + } + + FreePool (Wrap); + + Rx4Token = &HttpInstance->Rx4Token; + + if (Rx4Token->CompletionToken.Event != NULL) { + gBS->CloseEvent (Rx4Token->CompletionToken.Event); + Rx4Token->CompletionToken.Event = NULL; + } + + + if (Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer != NULL) { + FreePool (Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer); + Rx4Token->Packet.RxData->FragmentTable[0].FragmentBuffer = NULL; + } + } + +} -- cgit v1.2.3