blob: 94f89f5665846049c0aafbf471d18463d186d84e [file] [log] [blame]
/** @file
Miscellaneous routines for HttpDxe driver.
Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR>
(C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>
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;
}
//
// 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 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->TlsSb != NULL && HttpInstance->TlsChildHandle != NULL) {
//
// Destroy the TLS instance.
//
HttpInstance->TlsSb->DestroyChild (HttpInstance->TlsSb, HttpInstance->TlsChildHandle);
}
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 *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 ((EFI_D_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 ((EFI_D_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 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);
*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 ((EFI_D_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 ((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;
}
}
}