/** @file | |
Miscellaneous routines for HttpDxe driver. | |
Copyright (c) 2015 - 2021, Intel Corporation. All rights reserved.<BR> | |
(C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#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 ((DEBUG_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 ((DEBUG_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; | |
} | |
// | |
// Record the CallbackData data. | |
// | |
HttpInstance->CallbackData.Wrap = (VOID *)Wrap; | |
HttpInstance->CallbackData.ParseData = Wrap->HttpToken->Message->Body; | |
HttpInstance->CallbackData.ParseDataLength = Length; | |
// | |
// Parse Body with CallbackData data. | |
// | |
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 various 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 various 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; | |
} | |
} | |
} | |
/** | |
Initialize 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->TlsSb != NULL) && (HttpInstance->TlsChildHandle != NULL)) { | |
// | |
// Destroy the TLS instance. | |
// | |
HttpInstance->TlsSb->DestroyChild (HttpInstance->TlsSb, HttpInstance->TlsChildHandle); | |
HttpInstance->TlsChildHandle = NULL; | |
} | |
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); | |
HttpNotify (HttpEventConnectTcp, Status); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_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); | |
HttpNotify (HttpEventConnectTcp, Status); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_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; | |
if ((HttpInstance->State == HTTP_STATE_TCP_CONNECTED) || | |
(HttpInstance->State == HTTP_STATE_TCP_CLOSED)) | |
{ | |
Status = HttpInstance->Tcp4->Configure (HttpInstance->Tcp4, NULL); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "HttpConfigureTcp4(NULL) - %r\n", Status)); | |
return Status; | |
} | |
HttpInstance->State = HTTP_STATE_TCP_UNCONFIGED; | |
} | |
Status = HttpInstance->Tcp4->Configure (HttpInstance->Tcp4, Tcp4CfgData); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_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; | |
if ((HttpInstance->State == HTTP_STATE_TCP_CONNECTED) || | |
(HttpInstance->State == HTTP_STATE_TCP_CLOSED)) | |
{ | |
Status = HttpInstance->Tcp6->Configure (HttpInstance->Tcp6, NULL); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "HttpConfigureTcp6(NULL) - %r\n", Status)); | |
return Status; | |
} | |
HttpInstance->State = HTTP_STATE_TCP_UNCONFIGED; | |
} | |
Status = HttpInstance->Tcp6->Configure (HttpInstance->Tcp6, Tcp6CfgData); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_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 ((DEBUG_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 ((DEBUG_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); | |
HttpNotify (HttpEventTlsConnectSession, Status); | |
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 ((DEBUG_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 ((DEBUG_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); | |
HttpNotify (HttpEventTlsConnectSession, Status); | |
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 *TlsRecord; | |
UINT16 PayloadSize; | |
NET_FRAGMENT TempFragment; | |
NET_FRAGMENT Fragment; | |
UINTN RecordCount; | |
UINTN RemainingLen; | |
Status = EFI_SUCCESS; | |
TlsRecord = NULL; | |
PayloadSize = 0; | |
TempFragment.Len = 0; | |
TempFragment.Bulk = NULL; | |
Fragment.Len = 0; | |
Fragment.Bulk = NULL; | |
RecordCount = 0; | |
RemainingLen = 0; | |
// | |
// Need to encrypt data. | |
// | |
if (HttpInstance->UseHttps) { | |
// | |
// Allocate enough buffer for each TLS plaintext records. | |
// | |
TlsRecord = AllocateZeroPool (TLS_RECORD_HEADER_LENGTH + TLS_PLAINTEXT_RECORD_MAX_PAYLOAD_LENGTH); | |
if (TlsRecord == NULL) { | |
Status = EFI_OUT_OF_RESOURCES; | |
return Status; | |
} | |
// | |
// Allocate enough buffer for all TLS ciphertext records. | |
// | |
RecordCount = TxStringLen / TLS_PLAINTEXT_RECORD_MAX_PAYLOAD_LENGTH + 1; | |
Fragment.Bulk = AllocateZeroPool (RecordCount * (TLS_RECORD_HEADER_LENGTH + TLS_CIPHERTEXT_RECORD_MAX_PAYLOAD_LENGTH)); | |
if (Fragment.Bulk == NULL) { | |
Status = EFI_OUT_OF_RESOURCES; | |
goto ON_ERROR; | |
} | |
// | |
// Encrypt each TLS plaintext records. | |
// | |
RemainingLen = TxStringLen; | |
while (RemainingLen != 0) { | |
PayloadSize = (UINT16)MIN (TLS_PLAINTEXT_RECORD_MAX_PAYLOAD_LENGTH, RemainingLen); | |
((TLS_RECORD_HEADER *)TlsRecord)->ContentType = TlsContentTypeApplicationData; | |
((TLS_RECORD_HEADER *)TlsRecord)->Version.Major = HttpInstance->TlsConfigData.Version.Major; | |
((TLS_RECORD_HEADER *)TlsRecord)->Version.Minor = HttpInstance->TlsConfigData.Version.Minor; | |
((TLS_RECORD_HEADER *)TlsRecord)->Length = PayloadSize; | |
CopyMem (TlsRecord + TLS_RECORD_HEADER_LENGTH, TxString + (TxStringLen - RemainingLen), PayloadSize); | |
Status = TlsProcessMessage ( | |
HttpInstance, | |
TlsRecord, | |
TLS_RECORD_HEADER_LENGTH + PayloadSize, | |
EfiTlsEncrypt, | |
&TempFragment | |
); | |
if (EFI_ERROR (Status)) { | |
goto ON_ERROR; | |
} | |
// | |
// Record the processed/encrypted Packet. | |
// | |
CopyMem (Fragment.Bulk + Fragment.Len, TempFragment.Bulk, TempFragment.Len); | |
Fragment.Len += TempFragment.Len; | |
FreePool (TempFragment.Bulk); | |
TempFragment.Len = 0; | |
TempFragment.Bulk = NULL; | |
RemainingLen -= (UINTN)PayloadSize; | |
ZeroMem (TlsRecord, TLS_RECORD_HEADER_LENGTH + TLS_PLAINTEXT_RECORD_MAX_PAYLOAD_LENGTH); | |
} | |
FreePool (TlsRecord); | |
TlsRecord = NULL; | |
} | |
if (!HttpInstance->LocalAddressIsIPv6) { | |
Tcp4 = HttpInstance->Tcp4; | |
Tx4Token = &Wrap->TcpWrap.Tx4Token; | |
if (HttpInstance->UseHttps) { | |
Tx4Token->Packet.TxData->DataLength = Fragment.Len; | |
Tx4Token->Packet.TxData->FragmentTable[0].FragmentLength = Fragment.Len; | |
Tx4Token->Packet.TxData->FragmentTable[0].FragmentBuffer = (VOID *)Fragment.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 ((DEBUG_ERROR, "Transmit failed: %r\n", Status)); | |
goto ON_ERROR; | |
} | |
} else { | |
Tcp6 = HttpInstance->Tcp6; | |
Tx6Token = &Wrap->TcpWrap.Tx6Token; | |
if (HttpInstance->UseHttps) { | |
Tx6Token->Packet.TxData->DataLength = Fragment.Len; | |
Tx6Token->Packet.TxData->FragmentTable[0].FragmentLength = Fragment.Len; | |
Tx6Token->Packet.TxData->FragmentTable[0].FragmentBuffer = (VOID *)Fragment.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 ((DEBUG_ERROR, "Transmit failed: %r\n", Status)); | |
goto ON_ERROR; | |
} | |
} | |
return Status; | |
ON_ERROR: | |
if (HttpInstance->UseHttps) { | |
if (TlsRecord != NULL) { | |
FreePool (TlsRecord); | |
TlsRecord = NULL; | |
} | |
if (Fragment.Bulk != NULL) { | |
FreePool (Fragment.Bulk); | |
Fragment.Bulk = NULL; | |
} | |
} | |
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 against. | |
@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 against. | |
@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 message 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 against. | |
@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 against. | |
@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 cache 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 ((DEBUG_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) { | |
// | |
// Cancel 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 ((DEBUG_ERROR, "Tcp4 receive failed: %r\n", Status)); | |
return Status; | |
} | |
} | |
// | |
// Append the response string along with a Null-terminator. | |
// | |
*BufferSize = *SizeofHeaders + Fragment.Len; | |
Buffer = AllocatePool (*BufferSize + 1); | |
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 | |
); | |
*(Buffer + *BufferSize) = '\0'; | |
*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 ((DEBUG_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) { | |
// | |
// Cancel 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 ((DEBUG_ERROR, "Tcp6 receive failed: %r\n", Status)); | |
return Status; | |
} | |
} | |
// | |
// Append the response string along with a Null-terminator. | |
// | |
*BufferSize = *SizeofHeaders + Fragment.Len; | |
Buffer = AllocatePool (*BufferSize + 1); | |
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 | |
); | |
*(Buffer + *BufferSize) = '\0'; | |
*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); | |
*SizeofHeaders = *EndofHeader - *HttpHeaders; | |
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)MIN (MAX_UINT32, HttpMsg->BodyLength); | |
Rx6Token->Packet.RxData->FragmentTable[0].FragmentLength = (UINT32)MIN (MAX_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 ((DEBUG_ERROR, "Tcp6 receive failed: %r\n", Status)); | |
return Status; | |
} | |
} else { | |
Rx4Token = &Wrap->TcpWrap.Rx4Token; | |
Rx4Token->Packet.RxData->DataLength = (UINT32)MIN (MAX_UINT32, HttpMsg->BodyLength); | |
Rx4Token->Packet.RxData->FragmentTable[0].FragmentLength = (UINT32)MIN (MAX_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 ((DEBUG_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; | |
} | |
} | |
} | |
/** | |
Send Events via EDKII_HTTP_CALLBACK_PROTOCOL. | |
@param[in] Event The event that occurs in the current state. | |
@param[in] EventStatus The Status of Event, EFI_SUCCESS or other errors. | |
**/ | |
VOID | |
HttpNotify ( | |
IN EDKII_HTTP_CALLBACK_EVENT Event, | |
IN EFI_STATUS EventStatus | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_HANDLE *Handles; | |
UINTN Index; | |
UINTN HandleCount; | |
EFI_HANDLE Handle; | |
EDKII_HTTP_CALLBACK_PROTOCOL *HttpCallback; | |
DEBUG ((DEBUG_INFO, "HttpNotify: Event - %d, EventStatus - %r\n", Event, EventStatus)); | |
Handles = NULL; | |
HandleCount = 0; | |
Status = gBS->LocateHandleBuffer ( | |
ByProtocol, | |
&gEdkiiHttpCallbackProtocolGuid, | |
NULL, | |
&HandleCount, | |
&Handles | |
); | |
if (Status == EFI_SUCCESS) { | |
for (Index = 0; Index < HandleCount; Index++) { | |
Handle = Handles[Index]; | |
Status = gBS->HandleProtocol ( | |
Handle, | |
&gEdkiiHttpCallbackProtocolGuid, | |
(VOID **)&HttpCallback | |
); | |
if (Status == EFI_SUCCESS) { | |
DEBUG ((DEBUG_INFO, "HttpNotify: Notifying %p\n", HttpCallback)); | |
HttpCallback->Callback ( | |
HttpCallback, | |
Event, | |
EventStatus | |
); | |
} | |
} | |
FreePool (Handles); | |
} | |
} |