From 47f51a064fbb3d3226304380674b2bec3cb33ba1 Mon Sep 17 00:00:00 2001 From: Ye Ting Date: Tue, 7 Jul 2015 09:09:21 +0000 Subject: [PATCH] NetworkPkg: Add HTTP Driver Add HTTP driver to support HTTP protocols defined in UEFI 2.5 specification. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Ye Ting Reviewed-by: Samer El-Haj-Mahmoud Reviewed-by: Fu Siyuan git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@17855 6f19259b-4bc3-4df7-8a09-765794883524 --- NetworkPkg/HttpDxe/ComponentName.c | 138 +++ NetworkPkg/HttpDxe/ComponentName.h | 98 ++ NetworkPkg/HttpDxe/HttpDns.c | 218 +++++ NetworkPkg/HttpDxe/HttpDns.h | 38 + NetworkPkg/HttpDxe/HttpDriver.c | 669 ++++++++++++++ NetworkPkg/HttpDxe/HttpDriver.h | 260 ++++++ NetworkPkg/HttpDxe/HttpDxe.inf | 64 ++ NetworkPkg/HttpDxe/HttpDxe.uni | Bin 0 -> 1822 bytes NetworkPkg/HttpDxe/HttpDxeExtra.uni | Bin 0 -> 1310 bytes NetworkPkg/HttpDxe/HttpImpl.c | 1309 +++++++++++++++++++++++++++ NetworkPkg/HttpDxe/HttpImpl.h | 239 +++++ NetworkPkg/HttpDxe/HttpProto.c | 1165 ++++++++++++++++++++++++ NetworkPkg/HttpDxe/HttpProto.h | 457 ++++++++++ NetworkPkg/HttpDxe/HttpUtilities.c | 622 +++++++++++++ NetworkPkg/HttpDxe/HttpUtilities.h | 82 ++ 15 files changed, 5359 insertions(+) create mode 100644 NetworkPkg/HttpDxe/ComponentName.c create mode 100644 NetworkPkg/HttpDxe/ComponentName.h create mode 100644 NetworkPkg/HttpDxe/HttpDns.c create mode 100644 NetworkPkg/HttpDxe/HttpDns.h create mode 100644 NetworkPkg/HttpDxe/HttpDriver.c create mode 100644 NetworkPkg/HttpDxe/HttpDriver.h create mode 100644 NetworkPkg/HttpDxe/HttpDxe.inf create mode 100644 NetworkPkg/HttpDxe/HttpDxe.uni create mode 100644 NetworkPkg/HttpDxe/HttpDxeExtra.uni create mode 100644 NetworkPkg/HttpDxe/HttpImpl.c create mode 100644 NetworkPkg/HttpDxe/HttpImpl.h create mode 100644 NetworkPkg/HttpDxe/HttpProto.c create mode 100644 NetworkPkg/HttpDxe/HttpProto.h create mode 100644 NetworkPkg/HttpDxe/HttpUtilities.c create mode 100644 NetworkPkg/HttpDxe/HttpUtilities.h diff --git a/NetworkPkg/HttpDxe/ComponentName.c b/NetworkPkg/HttpDxe/ComponentName.c new file mode 100644 index 0000000000..fdd2e7f344 --- /dev/null +++ b/NetworkPkg/HttpDxe/ComponentName.c @@ -0,0 +1,138 @@ +/** @file + Implementation of EFI_COMPONENT_NAME_PROTOCOL and + EFI_COMPONENT_NAME2_PROTOCOL protocol. + + Copyright (c) 2015, Intel Corporation. All rights reserved.
+ + 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" + +/// +/// Component Name Protocol instance +/// +GLOBAL_REMOVE_IF_UNREFERENCED +EFI_COMPONENT_NAME_PROTOCOL gHttpDxeComponentName = { + (EFI_COMPONENT_NAME_GET_DRIVER_NAME) HttpDxeComponentNameGetDriverName, + (EFI_COMPONENT_NAME_GET_CONTROLLER_NAME) HttpDxeComponentNameGetControllerName, + "eng" +}; + +/// +/// Component Name 2 Protocol instance +/// +GLOBAL_REMOVE_IF_UNREFERENCED +EFI_COMPONENT_NAME2_PROTOCOL gHttpDxeComponentName2 = { + HttpDxeComponentNameGetDriverName, + HttpDxeComponentNameGetControllerName, + "en" +}; + +/// +/// Table of driver names +/// +GLOBAL_REMOVE_IF_UNREFERENCED +EFI_UNICODE_STRING_TABLE mHttpDxeDriverNameTable[] = { + { "eng;en", (CHAR16 *) L"HttpDxe" }, + { NULL, NULL } +}; + +/** + Retrieves a Unicode string that is the user-readable name of the EFI Driver. + + @param This A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance. + @param Language A pointer to a three-character ISO 639-2 language identifier. + This is the language of the driver name that that the caller + is requesting, and it must match one of the languages specified + in SupportedLanguages. The number of languages supported by a + driver is up to the driver writer. + @param DriverName A pointer to the Unicode string to return. This Unicode string + is the name of the driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by This + and the language specified by Language was returned + in DriverName. + @retval EFI_INVALID_PARAMETER Language is NULL. + @retval EFI_INVALID_PARAMETER DriverName is NULL. + @retval EFI_UNSUPPORTED The driver specified by This does not support the + language specified by Language. + +**/ +EFI_STATUS +EFIAPI +HttpDxeComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME2_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mHttpDxeDriverNameTable, + DriverName, + (BOOLEAN)(This != &gHttpDxeComponentName2) + ); +} + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by an EFI Driver. + + @param This A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance. + @param ControllerHandle The handle of a controller that the driver specified by + This is managing. This handle specifies the controller + whose name is to be returned. + @param ChildHandle The handle of the child controller to retrieve the name + of. This is an optional parameter that may be NULL. It + will be NULL for device drivers. It will also be NULL + for a bus drivers that wish to retrieve the name of the + bus controller. It will not be NULL for a bus driver + that wishes to retrieve the name of a child controller. + @param Language A pointer to a three character ISO 639-2 language + identifier. This is the language of the controller name + that the caller is requesting, and it must match one + of the languages specified in SupportedLanguages. The + number of languages supported by a driver is up to the + driver writer. + @param ControllerName A pointer to the Unicode string to return. This Unicode + string is the name of the controller specified by + ControllerHandle and ChildHandle in the language specified + by Language, from the point of view of the driver specified + by This. + + @retval EFI_SUCCESS The Unicode string for the user-readable name in the + language specified by Language for the driver + specified by This was returned in DriverName. + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid EFI_HANDLE. + @retval EFI_INVALID_PARAMETER Language is NULL. + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + @retval EFI_UNSUPPORTED The driver specified by This is not currently managing + the controller specified by ControllerHandle and + ChildHandle. + @retval EFI_UNSUPPORTED The driver specified by This does not support the + language specified by Language. + +**/ +EFI_STATUS +EFIAPI +HttpDxeComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME2_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + return EFI_UNSUPPORTED; +} diff --git a/NetworkPkg/HttpDxe/ComponentName.h b/NetworkPkg/HttpDxe/ComponentName.h new file mode 100644 index 0000000000..29a91f2ec1 --- /dev/null +++ b/NetworkPkg/HttpDxe/ComponentName.h @@ -0,0 +1,98 @@ +/** @file + Header file for implementation of UEFI Component Name(2) protocol. + +Copyright (c) 2015, Intel Corporation. All rights reserved.
+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. + +**/ + +#ifndef __EFI_HTTP_COMPONENT_NAME_H__ +#define __EFI_HTTP_COMPONENT_NAME_H__ + +/** + Retrieves a Unicode string that is the user-readable name of the EFI Driver. + + @param This A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance. + @param Language A pointer to a three-character ISO 639-2 language identifier. + This is the language of the driver name that that the caller + is requesting, and it must match one of the languages specified + in SupportedLanguages. The number of languages supported by a + driver is up to the driver writer. + @param DriverName A pointer to the Unicode string to return. This Unicode string + is the name of the driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by This + and the language specified by Language was returned + in DriverName. + @retval EFI_INVALID_PARAMETER Language is NULL. + @retval EFI_INVALID_PARAMETER DriverName is NULL. + @retval EFI_UNSUPPORTED The driver specified by This does not support the + language specified by Language. + +**/ +EFI_STATUS +EFIAPI +HttpDxeComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME2_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by an EFI Driver. + + @param This A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance. + @param ControllerHandle The handle of a controller that the driver specified by + This is managing. This handle specifies the controller + whose name is to be returned. + @param ChildHandle The handle of the child controller to retrieve the name + of. This is an optional parameter that may be NULL. It + will be NULL for device drivers. It will also be NULL + for a bus drivers that wish to retrieve the name of the + bus controller. It will not be NULL for a bus driver + that wishes to retrieve the name of a child controller. + @param Language A pointer to a three character ISO 639-2 language + identifier. This is the language of the controller name + that the caller is requesting, and it must match one + of the languages specified in SupportedLanguages. The + number of languages supported by a driver is up to the + driver writer. + @param ControllerName A pointer to the Unicode string to return. This Unicode + string is the name of the controller specified by + ControllerHandle and ChildHandle in the language specified + by Language, from the point of view of the driver specified + by This. + + @retval EFI_SUCCESS The Unicode string for the user-readable name in the + language specified by Language for the driver + specified by This was returned in DriverName. + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid EFI_HANDLE. + @retval EFI_INVALID_PARAMETER Language is NULL. + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + @retval EFI_UNSUPPORTED The driver specified by This is not currently managing + the controller specified by ControllerHandle and + ChildHandle. + @retval EFI_UNSUPPORTED The driver specified by This does not support the + language specified by Language. + +**/ +EFI_STATUS +EFIAPI +HttpDxeComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME2_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + +#endif diff --git a/NetworkPkg/HttpDxe/HttpDns.c b/NetworkPkg/HttpDxe/HttpDns.c new file mode 100644 index 0000000000..daebc173b5 --- /dev/null +++ b/NetworkPkg/HttpDxe/HttpDns.c @@ -0,0 +1,218 @@ +/** @file + Routines for HttpDxe driver to perform DNS resolution based on UEFI DNS protocols. + +Copyright (c) 2015, Intel Corporation. All rights reserved.
+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" + +/** + Retrieve the host address using the EFI_DNS4_PROTOCOL. + + @param[in] HttpInstance Pointer to HTTP_PROTOCOL instance. + @param[in] HostName Pointer to buffer containing hostname. + @param[out] IpAddress On output, pointer to buffer containing IPv4 address. + + @retval EFI_SUCCESS Operation succeeded. + @retval EFI_OUT_OF_RESOURCES Failed to allocate needed resources. + @retval EFI_DEVICE_ERROR An unexpected network error occurred. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +HttpDns4 ( + IN HTTP_PROTOCOL *HttpInstance, + IN CHAR16 *HostName, + OUT EFI_IPv4_ADDRESS *IpAddress + ) +{ + EFI_STATUS Status; + EFI_DNS4_PROTOCOL *Dns4; + EFI_DNS4_CONFIG_DATA Dns4CfgData; + EFI_DNS4_COMPLETION_TOKEN Token; + BOOLEAN IsDone; + HTTP_SERVICE *Service; + EFI_HANDLE Dns4Handle; + EFI_IP4_CONFIG2_PROTOCOL *Ip4Config2; + UINTN DnsServerListCount; + EFI_IPv4_ADDRESS *DnsServerList; + UINTN DataSize; + + + Service = HttpInstance->Service; + ASSERT (Service != NULL); + + DnsServerList = NULL; + DnsServerListCount = 0; + ZeroMem (&Token, sizeof (EFI_DNS4_COMPLETION_TOKEN)); + + // + // Get DNS server list from EFI IPv4 Configuration II protocol. + // + Status = gBS->HandleProtocol (Service->ControllerHandle, &gEfiIp4Config2ProtocolGuid, (VOID **) &Ip4Config2); + if (!EFI_ERROR (Status)) { + // + // Get the required size. + // + DataSize = 0; + Status = Ip4Config2->GetData (Ip4Config2, Ip4Config2DataTypeDnsServer, &DataSize, NULL); + if (Status == EFI_BUFFER_TOO_SMALL) { + DnsServerList = AllocatePool (DataSize); + if (DnsServerList == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = Ip4Config2->GetData (Ip4Config2, Ip4Config2DataTypeDnsServer, &DataSize, DnsServerList); + if (EFI_ERROR (Status)) { + FreePool (DnsServerList); + DnsServerList = NULL; + } else { + DnsServerListCount = DataSize / sizeof (EFI_IPv4_ADDRESS); + } + } + } + + Dns4Handle = NULL; + Dns4 = NULL; + + // + // Create a DNS child instance and get the protocol. + // + Status = NetLibCreateServiceChild ( + Service->ControllerHandle, + Service->ImageHandle, + &gEfiDns4ServiceBindingProtocolGuid, + &Dns4Handle + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + Status = gBS->OpenProtocol ( + Dns4Handle, + &gEfiDns4ProtocolGuid, + (VOID **) &Dns4, + Service->ImageHandle, + Service->ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + // + // Configure DNS4 instance for the DNS server address and protocol. + // + ZeroMem (&Dns4CfgData, sizeof (Dns4CfgData)); + Dns4CfgData.DnsServerListCount = DnsServerListCount; + Dns4CfgData.DnsServerList = DnsServerList; + Dns4CfgData.UseDefaultSetting = HttpInstance->IPv4Node.UseDefaultAddress; + if (!Dns4CfgData.UseDefaultSetting) { + IP4_COPY_ADDRESS (&Dns4CfgData.StationIp, &HttpInstance->IPv4Node.LocalAddress); + IP4_COPY_ADDRESS (&Dns4CfgData.SubnetMask, &HttpInstance->IPv4Node.LocalSubnet); + } + Dns4CfgData.EnableDnsCache = TRUE; + Dns4CfgData.Protocol = EFI_IP_PROTO_UDP; + Status = Dns4->Configure ( + Dns4, + &Dns4CfgData + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + // + // Create event to set the is done flag when name resolution is finished. + // + ZeroMem (&Token, sizeof (Token)); + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpCommonNotify, + &IsDone, + &Token.Event + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + // + // Start asynchronous name resolution. + // + Token.Status = EFI_NOT_READY; + IsDone = FALSE; + Status = Dns4->HostNameToIp (Dns4, HostName, &Token); + if (EFI_ERROR (Status)) { + goto Exit; + } + + while (!IsDone) { + Dns4->Poll (Dns4); + } + + // + // Name resolution is done, check result. + // + Status = Token.Status; + if (!EFI_ERROR (Status)) { + if (Token.RspData.H2AData == NULL) { + Status = EFI_DEVICE_ERROR; + goto Exit; + } + if (Token.RspData.H2AData->IpCount == 0 || Token.RspData.H2AData->IpList == NULL) { + Status = EFI_DEVICE_ERROR; + goto Exit; + } + // + // We just return the first IP address from DNS protocol. + // + IP4_COPY_ADDRESS (IpAddress, Token.RspData.H2AData->IpList); + Status = EFI_SUCCESS; + } + +Exit: + + if (Token.Event != NULL) { + gBS->CloseEvent (Token.Event); + } + if (Token.RspData.H2AData != NULL) { + if (Token.RspData.H2AData->IpList != NULL) { + FreePool (Token.RspData.H2AData->IpList); + } + FreePool (Token.RspData.H2AData); + } + + if (Dns4 != NULL) { + Dns4->Configure (Dns4, NULL); + + gBS->CloseProtocol ( + Dns4Handle, + &gEfiDns4ProtocolGuid, + Service->ImageHandle, + Service->ControllerHandle + ); + } + + if (Dns4Handle != NULL) { + NetLibDestroyServiceChild ( + Service->ControllerHandle, + Service->ImageHandle, + &gEfiDns4ServiceBindingProtocolGuid, + Dns4Handle + ); + } + + if (DnsServerList != NULL) { + FreePool (DnsServerList); + } + + return Status; +} diff --git a/NetworkPkg/HttpDxe/HttpDns.h b/NetworkPkg/HttpDxe/HttpDns.h new file mode 100644 index 0000000000..0fb418635c --- /dev/null +++ b/NetworkPkg/HttpDxe/HttpDns.h @@ -0,0 +1,38 @@ +/** @file + The header file of routines for HttpDxe driver to perform DNS resolution based on UEFI DNS protocols. + +Copyright (c) 2015, Intel Corporation. All rights reserved.
+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. + +**/ + +#ifndef __EFI_HTTP_DNS_H__ +#define __EFI_HTTP_DNS_H__ + +/** + Retrieve the host address using the EFI_DNS4_PROTOCOL. + + @param[in] HttpInstance Pointer to HTTP_PROTOCOL instance. + @param[in] HostName Pointer to buffer containing hostname. + @param[out] IpAddress On output, pointer to buffer containing IPv4 address. + + @retval EFI_SUCCESS Operation succeeded. + @retval EFI_OUT_OF_RESOURCES Failed to allocate needed resources. + @retval EFI_DEVICE_ERROR An unexpected network error occurred. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +HttpDns4 ( + IN HTTP_PROTOCOL *HttpInstance, + IN CHAR16 *HostName, + OUT EFI_IPv4_ADDRESS *IpAddress + ); + +#endif \ No newline at end of file diff --git a/NetworkPkg/HttpDxe/HttpDriver.c b/NetworkPkg/HttpDxe/HttpDriver.c new file mode 100644 index 0000000000..b65607a4cb --- /dev/null +++ b/NetworkPkg/HttpDxe/HttpDriver.c @@ -0,0 +1,669 @@ +/** @file + The driver binding and service binding protocol for HttpDxe driver. + + Copyright (c) 2015, Intel Corporation. All rights reserved.
+ + 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" + +/// +/// Driver Binding Protocol instance +/// +EFI_DRIVER_BINDING_PROTOCOL gHttpDxeDriverBinding = { + HttpDxeDriverBindingSupported, + HttpDxeDriverBindingStart, + HttpDxeDriverBindingStop, + HTTP_DRIVER_VERSION, + NULL, + NULL +}; + +/** + Create a HTTP driver service binding private instance. + + @param[in] Controller The controller that has TCP4 service binding + installed. + @param[in] ImageHandle The HTTP driver's image handle. + @param[out] ServiceData Point to HTTP driver private instance. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate some resources. + @retval EFI_SUCCESS A new HTTP driver private instance is created. + +**/ +EFI_STATUS +HttpCreateService ( + IN EFI_HANDLE Controller, + IN EFI_HANDLE ImageHandle, + OUT HTTP_SERVICE **ServiceData + ) +{ + HTTP_SERVICE *HttpService; + + ASSERT (ServiceData != NULL); + *ServiceData = NULL; + + HttpService = AllocateZeroPool (sizeof (HTTP_SERVICE)); + if (HttpService == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + HttpService->Signature = HTTP_SERVICE_SIGNATURE; + HttpService->ServiceBinding.CreateChild = HttpServiceBindingCreateChild; + HttpService->ServiceBinding.DestroyChild = HttpServiceBindingDestroyChild; + HttpService->ImageHandle = ImageHandle; + HttpService->ControllerHandle = Controller; + HttpService->ChildrenNumber = 0; + InitializeListHead (&HttpService->ChildrenList); + + *ServiceData = HttpService; + return EFI_SUCCESS; +} + +/** + Release all the resource used the HTTP service binding instance. + + @param HttpService The HTTP private instance. + +**/ +VOID +HttpCleanService ( + IN HTTP_SERVICE *HttpService + ) +{ + if (HttpService != NULL) { + return ; + } + + if (HttpService->TcpChildHandle != NULL) { + gBS->CloseProtocol ( + HttpService->TcpChildHandle, + &gEfiTcp4ProtocolGuid, + HttpService->ImageHandle, + HttpService->ControllerHandle + ); + + NetLibDestroyServiceChild ( + HttpService->ControllerHandle, + HttpService->ImageHandle, + &gEfiTcp4ServiceBindingProtocolGuid, + HttpService->TcpChildHandle + ); + } +} + +/** + This is the declaration of an EFI image entry point. This entry point is + the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers including + both device drivers and bus drivers. + + @param ImageHandle The firmware allocated handle for the UEFI image. + @param SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The operation completed successfully. + @retval Others An unexpected error occurred. + +**/ +EFI_STATUS +EFIAPI +HttpDxeDriverEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + // + // Install UEFI Driver Model protocol(s). + // + return EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gHttpDxeDriverBinding, + ImageHandle, + &gHttpDxeComponentName, + &gHttpDxeComponentName2 + ); +} + +/** + Callback function which provided by user to remove one node in NetDestroyLinkList process. + + @param[in] Entry The entry to be removed. + @param[in] Context Pointer to the callback context corresponds to the Context in NetDestroyLinkList. + + @retval EFI_INVALID_PARAMETER Any input parameter is NULL. + @retval EFI_SUCCESS The entry has been removed successfully. + @retval Others Fail to remove the entry. + +**/ +EFI_STATUS +EFIAPI +HttpDestroyChildEntryInHandleBuffer ( + IN LIST_ENTRY *Entry, + IN VOID *Context + ) +{ + HTTP_PROTOCOL *HttpInstance; + EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; + UINTN NumberOfChildren; + EFI_HANDLE *ChildHandleBuffer; + + if (Entry == NULL || Context == NULL) { + return EFI_INVALID_PARAMETER; + } + + HttpInstance = NET_LIST_USER_STRUCT_S (Entry, HTTP_PROTOCOL, Link, HTTP_PROTOCOL_SIGNATURE); + ServiceBinding = ((HTTP_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT *) Context)->ServiceBinding; + NumberOfChildren = ((HTTP_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT *) Context)->NumberOfChildren; + ChildHandleBuffer = ((HTTP_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT *) Context)->ChildHandleBuffer; + + if (!NetIsInHandleBuffer (HttpInstance->Handle, NumberOfChildren, ChildHandleBuffer)) { + return EFI_SUCCESS; + } + + return ServiceBinding->DestroyChild (ServiceBinding, HttpInstance->Handle); +} + +/** + Tests to see if this driver supports a given controller. If a child device is provided, + it further tests to see if this driver supports creating a handle for the specified child device. + + This function checks to see if the driver specified by This supports the device specified by + ControllerHandle. Drivers will typically use the device path attached to + ControllerHandle and/or the services from the bus I/O abstraction attached to + ControllerHandle to determine if the driver supports ControllerHandle. This function + may be called many times during platform initialization. In order to reduce boot times, the tests + performed by this function must be very small, and take as little time as possible to execute. This + function must not change the state of any hardware devices, and this function must be aware that the + device specified by ControllerHandle may already be managed by the same driver or a + different driver. This function must match its calls to AllocatePages() with FreePages(), + AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol(). + Because ControllerHandle may have been previously started by the same driver, if a protocol is + already in the opened state, then it must not be closed with CloseProtocol(). This is required + to guarantee the state of ControllerHandle is not modified by this function. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to test. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For bus drivers, if this parameter is not NULL, then + the bus driver must determine if the bus controller specified + by ControllerHandle and the child controller specified + by RemainingDevicePath are both supported by this + bus driver. + + @retval EFI_SUCCESS The device specified by ControllerHandle and + RemainingDevicePath is supported by the driver specified by This. + @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by the driver + specified by This. + @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by a different + driver or an application that requires exclusive access. + Currently not implemented. + @retval EFI_UNSUPPORTED The device specified by ControllerHandle and + RemainingDevicePath is not supported by the driver specified by This. +**/ +EFI_STATUS +EFIAPI +HttpDxeDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + EFI_STATUS Status; + + // + // Test for the HttpServiceBinding protocol. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiHttpServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + if (!EFI_ERROR (Status)) { + return EFI_ALREADY_STARTED; + } + + // + // Test for the Tcp4 Protocol + // + return gBS->OpenProtocol ( + ControllerHandle, + &gEfiTcp4ServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + +} + +/** + Starts a device controller or a bus controller. + + The Start() function is designed to be invoked from the EFI boot service ConnectController(). + As a result, much of the error checking on the parameters to Start() has been moved into this + common boot service. It is legal to call Start() from other locations, + but the following calling restrictions must be followed, or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE. + 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned + EFI_DEVICE_PATH_PROTOCOL. + 3. Prior to calling Start(), the Supported() function for the driver specified by This must + have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to start. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For a bus driver, if this parameter is NULL, then handles + for all the children of Controller are created by this driver. + If this parameter is not NULL and the first Device Path Node is + not the End of Device Path Node, then only the handle for the + child device specified by the first Device Path Node of + RemainingDevicePath is created by this driver. + If the first Device Path Node of RemainingDevicePath is + the End of Device Path Node, no child handle is created by this + driver. + + @retval EFI_SUCCESS The device was started. + @retval EFI_ALREADY_STARTED This device is already running on ControllerHandle. + @retval EFI_DEVICE_ERROR The device could not be started due to a device error.Currently not implemented. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval Others The driver failded to start the device. + +**/ +EFI_STATUS +EFIAPI +HttpDxeDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + EFI_STATUS Status; + HTTP_SERVICE *HttpService; + VOID *Interface; + + // + // Test for the Http service binding protocol + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiHttpServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + + if (Status == EFI_SUCCESS) { + return EFI_ALREADY_STARTED; + } + + Status = HttpCreateService (ControllerHandle, This->DriverBindingHandle, &HttpService); + if (EFI_ERROR (Status)) { + return Status; + } + + ASSERT (HttpService != NULL); + + // + // Create a TCP child instance, but do not configure it. This will establish the parent-child relationship. + // + Status = NetLibCreateServiceChild ( + ControllerHandle, + This->DriverBindingHandle, + &gEfiTcp4ServiceBindingProtocolGuid, + &HttpService->TcpChildHandle + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + HttpService->TcpChildHandle, + &gEfiTcp4ProtocolGuid, + &Interface, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Install the HttpServiceBinding Protocol onto Controller + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &ControllerHandle, + &gEfiHttpServiceBindingProtocolGuid, + &HttpService->ServiceBinding, + NULL + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + return EFI_SUCCESS; + +ON_ERROR: + + if (HttpService != NULL) { + HttpCleanService (HttpService); + FreePool (HttpService); + } + + return Status; +} + +/** + Stops a device controller or a bus controller. + + The Stop() function is designed to be invoked from the EFI boot service DisconnectController(). + As a result, much of the error checking on the parameters to Stop() has been moved + into this common boot service. It is legal to call Stop() from other locations, + but the following calling restrictions must be followed, or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this + same driver's Start() function. + 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid + EFI_HANDLE. In addition, all of these handles must have been created in this driver's + Start() function, and the Start() function must have called OpenProtocol() on + ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle A handle to the device being stopped. The handle must + support a bus specific I/O protocol for the driver + to use to stop the device. + @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer. + @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL + if NumberOfChildren is 0. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. + +**/ +EFI_STATUS +EFIAPI +HttpDxeDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer OPTIONAL + ) +{ + EFI_HANDLE NicHandle; + EFI_STATUS Status; + EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; + HTTP_SERVICE *HttpService; + LIST_ENTRY *List; + HTTP_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT Context; + + // + // HTTP driver opens TCP child, So, Controller is a TCP + // child handle. Locate the Nic handle first. Then get the + // HTTP private data back. + // + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiTcp4ProtocolGuid); + if (NicHandle == NULL) { + return EFI_SUCCESS; + } + + Status = gBS->OpenProtocol ( + NicHandle, + &gEfiHttpServiceBindingProtocolGuid, + (VOID **) &ServiceBinding, + This->DriverBindingHandle, + NicHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + HttpService = HTTP_SERVICE_FROM_PROTOCOL (ServiceBinding); + + if (!IsListEmpty (&HttpService->ChildrenList)) { + // + // Destroy the HTTP child instance in ChildHandleBuffer. + // + List = &HttpService->ChildrenList; + Context.ServiceBinding = ServiceBinding; + Context.NumberOfChildren = NumberOfChildren; + Context.ChildHandleBuffer = ChildHandleBuffer; + Status = NetDestroyLinkList ( + List, + HttpDestroyChildEntryInHandleBuffer, + &Context, + NULL + ); + } + + if (NumberOfChildren == 0 && IsListEmpty (&HttpService->ChildrenList)) { + gBS->UninstallProtocolInterface ( + NicHandle, + &gEfiHttpServiceBindingProtocolGuid, + ServiceBinding + ); + + HttpCleanService (HttpService); + + FreePool (HttpService); + + Status = EFI_SUCCESS; + } + + return Status; +} + +/** + Creates a child handle and installs a protocol. + + The CreateChild() function installs a protocol on ChildHandle. + If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle. + If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle. + + @param This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param ChildHandle Pointer to the handle of the child to create. If it is NULL, + then a new handle is created. If it is a pointer to an existing UEFI handle, + then the protocol is added to the existing UEFI handle. + + @retval EFI_SUCCES The protocol was added to ChildHandle. + @retval EFI_INVALID_PARAMETER This is NULL, or ChildHandle is NULL. + @retval EFI_OUT_OF_RESOURCES There are not enough resources availabe to create + the child. + @retval other The child handle was not created. + +**/ +EFI_STATUS +EFIAPI +HttpServiceBindingCreateChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN OUT EFI_HANDLE *ChildHandle + ) +{ + HTTP_SERVICE *HttpService; + HTTP_PROTOCOL *HttpInstance; + EFI_STATUS Status; + VOID *Interface; + EFI_TPL OldTpl; + + if ((This == NULL) || (ChildHandle == NULL)) { + return EFI_INVALID_PARAMETER; + } + + HttpService = HTTP_SERVICE_FROM_PROTOCOL (This); + HttpInstance = AllocateZeroPool (sizeof (HTTP_PROTOCOL)); + if (HttpInstance == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Install HTTP protocol onto ChildHandle + // + Status = gBS->InstallMultipleProtocolInterfaces ( + ChildHandle, + &gEfiHttpProtocolGuid, + &HttpInstance->Http, + NULL + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + HttpInstance->Handle = *ChildHandle; + + Status = HttpInitProtocol (HttpService, HttpInstance); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Open the default Tcp4 protocol by child. + // + Status = gBS->OpenProtocol ( + HttpService->TcpChildHandle, + &gEfiTcp4ProtocolGuid, + (VOID **) &Interface, + gHttpDxeDriverBinding.DriverBindingHandle, + HttpInstance->Handle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Add it to the HTTP service's child list. + // + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + InsertTailList (&HttpService->ChildrenList, &HttpInstance->Link); + HttpService->ChildrenNumber++; + + gBS->RestoreTPL (OldTpl); + + return EFI_SUCCESS; + +ON_ERROR: + + HttpCleanProtocol (HttpInstance); + FreePool (HttpInstance); + + return Status; +} + +/** + Destroys a child handle with a protocol installed on it. + + The DestroyChild() function does the opposite of CreateChild(). It removes a protocol + that was installed by CreateChild() from ChildHandle. If the removed protocol is the + last protocol on ChildHandle, then ChildHandle is destroyed. + + @param This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param ChildHandle Handle of the child to destroy + + @retval EFI_SUCCES The protocol was removed from ChildHandle. + @retval EFI_UNSUPPORTED ChildHandle does not support the protocol that is being removed. + @retval EFI_INVALID_PARAMETER Child handle is NULL. + @retval other The child handle was not destroyed + +**/ +EFI_STATUS +EFIAPI +HttpServiceBindingDestroyChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE ChildHandle + ) +{ + HTTP_SERVICE *HttpService; + HTTP_PROTOCOL *HttpInstance; + EFI_HTTP_PROTOCOL *Http; + EFI_STATUS Status; + EFI_TPL OldTpl; + + if ((This == NULL) || (ChildHandle == NULL)) { + return EFI_INVALID_PARAMETER; + } + + HttpService = HTTP_SERVICE_FROM_PROTOCOL (This); + Status = gBS->OpenProtocol ( + ChildHandle, + &gEfiHttpProtocolGuid, + (VOID **) &Http, + gHttpDxeDriverBinding.DriverBindingHandle, + ChildHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (Http); + if (HttpInstance->Service != HttpService) { + return EFI_INVALID_PARAMETER; + } + + if (HttpInstance->InDestroy) { + return EFI_SUCCESS; + } + + // + // Close the Tcp4 protocol. + // + gBS->CloseProtocol ( + HttpService->TcpChildHandle, + &gEfiTcp4ProtocolGuid, + gHttpDxeDriverBinding.DriverBindingHandle, + ChildHandle + ); + + HttpInstance->InDestroy = TRUE; + + // + // Uninstall the HTTP protocol. + // + Status = gBS->UninstallProtocolInterface ( + ChildHandle, + &gEfiHttpProtocolGuid, + Http + ); + + if (EFI_ERROR (Status)) { + HttpInstance->InDestroy = FALSE; + return Status; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + HttpCleanProtocol (HttpInstance); + + RemoveEntryList (&HttpInstance->Link); + HttpService->ChildrenNumber--; + + gBS->RestoreTPL (OldTpl); + + FreePool (HttpInstance); + return EFI_SUCCESS; +} diff --git a/NetworkPkg/HttpDxe/HttpDriver.h b/NetworkPkg/HttpDxe/HttpDriver.h new file mode 100644 index 0000000000..5bad7059f3 --- /dev/null +++ b/NetworkPkg/HttpDxe/HttpDriver.h @@ -0,0 +1,260 @@ +/** @file + The header files of the driver binding and service binding protocol for HttpDxe driver. + + Copyright (c) 2015, Intel Corporation. All rights reserved.
+ + 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. + +**/ + +#ifndef __EFI_HTTP_DRIVER_H__ +#define __EFI_HTTP_DRIVER_H__ + +#include + +// +// Libraries +// +#include +#include +#include +#include +#include +#include +#include +#include + +// +// UEFI Driver Model Protocols +// +#include +#include +#include +#include + +// +// Consumed Protocols +// +#include +#include +#include + +// +// Produced Protocols +// +#include + +// +// Driver Version +// +#define HTTP_DRIVER_VERSION 0xa + +// +// Protocol instances +// +extern EFI_DRIVER_BINDING_PROTOCOL gHttpDxeDriverBinding; +extern EFI_COMPONENT_NAME2_PROTOCOL gHttpDxeComponentName2; +extern EFI_COMPONENT_NAME_PROTOCOL gHttpDxeComponentName; + +// +// Include files with function prototypes +// +#include "ComponentName.h" +#include "HttpImpl.h" +#include "HttpProto.h" +#include "HttpDns.h" +#include "HttpUtilities.h" + +typedef struct { + EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; + UINTN NumberOfChildren; + EFI_HANDLE *ChildHandleBuffer; +} HTTP_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT; + +/** + Tests to see if this driver supports a given controller. If a child device is provided, + it further tests to see if this driver supports creating a handle for the specified child device. + + This function checks to see if the driver specified by This supports the device specified by + ControllerHandle. Drivers will typically use the device path attached to + ControllerHandle and/or the services from the bus I/O abstraction attached to + ControllerHandle to determine if the driver supports ControllerHandle. This function + may be called many times during platform initialization. In order to reduce boot times, the tests + performed by this function must be very small, and take as little time as possible to execute. This + function must not change the state of any hardware devices, and this function must be aware that the + device specified by ControllerHandle may already be managed by the same driver or a + different driver. This function must match its calls to AllocatePages() with FreePages(), + AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol(). + Because ControllerHandle may have been previously started by the same driver, if a protocol is + already in the opened state, then it must not be closed with CloseProtocol(). This is required + to guarantee the state of ControllerHandle is not modified by this function. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to test. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For bus drivers, if this parameter is not NULL, then + the bus driver must determine if the bus controller specified + by ControllerHandle and the child controller specified + by RemainingDevicePath are both supported by this + bus driver. + + @retval EFI_SUCCESS The device specified by ControllerHandle and + RemainingDevicePath is supported by the driver specified by This. + @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by the driver + specified by This. + @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by a different + driver or an application that requires exclusive access. + Currently not implemented. + @retval EFI_UNSUPPORTED The device specified by ControllerHandle and + RemainingDevicePath is not supported by the driver specified by This. +**/ +EFI_STATUS +EFIAPI +HttpDxeDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Starts a device controller or a bus controller. + + The Start() function is designed to be invoked from the EFI boot service ConnectController(). + As a result, much of the error checking on the parameters to Start() has been moved into this + common boot service. It is legal to call Start() from other locations, + but the following calling restrictions must be followed, or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE. + 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned + EFI_DEVICE_PATH_PROTOCOL. + 3. Prior to calling Start(), the Supported() function for the driver specified by This must + have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to start. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For a bus driver, if this parameter is NULL, then handles + for all the children of Controller are created by this driver. + If this parameter is not NULL and the first Device Path Node is + not the End of Device Path Node, then only the handle for the + child device specified by the first Device Path Node of + RemainingDevicePath is created by this driver. + If the first Device Path Node of RemainingDevicePath is + the End of Device Path Node, no child handle is created by this + driver. + + @retval EFI_SUCCESS The device was started. + @retval EFI_DEVICE_ERROR The device could not be started due to a device error.Currently not implemented. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval Others The driver failded to start the device. + +**/ +EFI_STATUS +EFIAPI +HttpDxeDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Stops a device controller or a bus controller. + + The Stop() function is designed to be invoked from the EFI boot service DisconnectController(). + As a result, much of the error checking on the parameters to Stop() has been moved + into this common boot service. It is legal to call Stop() from other locations, + but the following calling restrictions must be followed, or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this + same driver's Start() function. + 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid + EFI_HANDLE. In addition, all of these handles must have been created in this driver's + Start() function, and the Start() function must have called OpenProtocol() on + ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle A handle to the device being stopped. The handle must + support a bus specific I/O protocol for the driver + to use to stop the device. + @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer. + @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL + if NumberOfChildren is 0. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. + +**/ +EFI_STATUS +EFIAPI +HttpDxeDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer OPTIONAL + ); + +/** + Creates a child handle and installs a protocol. + + The CreateChild() function installs a protocol on ChildHandle. + If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle. + If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle. + + @param This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param ChildHandle Pointer to the handle of the child to create. If it is NULL, + then a new handle is created. If it is a pointer to an existing UEFI handle, + then the protocol is added to the existing UEFI handle. + + @retval EFI_SUCCES The protocol was added to ChildHandle. + @retval EFI_INVALID_PARAMETER This is NULL, or ChildHandle is NULL. + @retval EFI_OUT_OF_RESOURCES There are not enough resources availabe to create + the child. + @retval other The child handle was not created. + +**/ +EFI_STATUS +EFIAPI +HttpServiceBindingCreateChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN OUT EFI_HANDLE *ChildHandle + ); + +/** + Destroys a child handle with a protocol installed on it. + + The DestroyChild() function does the opposite of CreateChild(). It removes a protocol + that was installed by CreateChild() from ChildHandle. If the removed protocol is the + last protocol on ChildHandle, then ChildHandle is destroyed. + + @param This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param ChildHandle Handle of the child to destroy + + @retval EFI_SUCCES The protocol was removed from ChildHandle. + @retval EFI_UNSUPPORTED ChildHandle does not support the protocol that is being removed. + @retval EFI_INVALID_PARAMETER Child handle is NULL. + @retval other The child handle was not destroyed + +**/ +EFI_STATUS +EFIAPI +HttpServiceBindingDestroyChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE ChildHandle + ); + + +extern EFI_HTTP_PROTOCOL mEfiHttpProtocolTemplete; + +#endif diff --git a/NetworkPkg/HttpDxe/HttpDxe.inf b/NetworkPkg/HttpDxe/HttpDxe.inf new file mode 100644 index 0000000000..4632934720 --- /dev/null +++ b/NetworkPkg/HttpDxe/HttpDxe.inf @@ -0,0 +1,64 @@ +## @file +# Implementation of EFI HTTP protocol interfaces. +# +# Copyright (c) 2015, Intel Corporation. All rights reserved.
+# +# 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. +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = HttpDxe + FILE_GUID = 2366c20f-e15a-11e3-8bf1-e4115b28bc50 + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = HttpDxeDriverEntryPoint + UNLOAD_IMAGE = NetLibDefaultUnload + MODULE_UNI_FILE = HttpDxe.uni + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[Sources] + ComponentName.h + ComponentName.c + HttpDns.h + HttpDns.c + HttpDriver.h + HttpDriver.c + HttpImpl.h + HttpImpl.c + HttpProto.h + HttpProto.c + HttpUtilities.h + HttpUtilities.c + +[LibraryClasses] + UefiDriverEntryPoint + UefiBootServicesTableLib + MemoryAllocationLib + BaseLib + UefiLib + DebugLib + NetLib + HttpLib + +[Protocols] + gEfiHttpServiceBindingProtocolGuid ## BY_START + gEfiHttpProtocolGuid ## BY_START + gEfiTcp4ServiceBindingProtocolGuid ## TO_START + gEfiTcp4ProtocolGuid ## TO_START + gEfiDns4ServiceBindingProtocolGuid ## SOMETIMES_CONSUMES + gEfiDns4ProtocolGuid ## SOMETIMES_CONSUMES + gEfiIp4Config2ProtocolGuid ## SOMETIMES_CONSUMES + +[UserExtensions.TianoCore."ExtraFiles"] + HttpDxeExtra.uni \ No newline at end of file diff --git a/NetworkPkg/HttpDxe/HttpDxe.uni b/NetworkPkg/HttpDxe/HttpDxe.uni new file mode 100644 index 0000000000000000000000000000000000000000..f9c2ac812e573faa9e5ff1e6d2ecd630be2ff395 GIT binary patch literal 1822 zcmbuAU2hUW6o${WiT`0kZ`8D)jouhzZ0*)=s)ZCt^{RnFVbhj23#Ibs)#p7^7#1)! zF=RgWe7^IZGsEv+HLF>|c*QTVPj+d!U0dIt+cTunwJo(n%h(-Ri(iA?fqh4+wZMLg zePPvwPxNis5d9d--jZcvcYNLAmm}}HK4#e^(z{Z&U_HU(f^m&~6@6wCW=Zam_1HhK zFYL;9Ioct+j*XZzP*3eGyD{<(URO(alMTo}6YI`qj(R{O<$8hNOV(X`ZEwK1?U6_5 z`XES5J_TNyJ4kbOBgeXjUKV02L=z{4pIzLE!5)?$Y~N1p;|4sD$-u_IbKDR`C)DY6 z-oAfdI4d_Gk#0uL5m^(vaa78evlEZ8QXNN=AkFda<1uzjF}}Q2ElH*f^-0IN%#z(Z z3dN2{#7jX|opp{<^%7pyC-5WfEqxdL_N_^0CHA$1C1QQ>uDW=vZ~3%2ITcZC%sWr( z#82G?=RB`^NURF|rT363B+NgVS22}&30%&w>OT6b3qu`L`G{4WGocdi{Whtm0=Kd+ zoaw6f%K&{j#~4W$DUCU%C^sUtf< zo)J|s>Yn|SFJYHVIa}DuMp=W^A-2l?gY_7Vkn1j{k6FSgQr@U}oESZWiw2#goCiw` z#)|1BtT}ecG`1G~6Yf#q@0>_=Vzh~t+Bt~CmzdpytrmFx_S^ECqgeOci-C8Lx?+b| zLRw*6e{*#o^-_fUSy>?bH*~6P6I`l$W5MSpgioW3Z_!h)`uZsvoZ*%mF@MYB%dl?S ZES*@rrI$=^N2tSUEZyk;|5u32{sE|K6_Wq} literal 0 HcmV?d00001 diff --git a/NetworkPkg/HttpDxe/HttpDxeExtra.uni b/NetworkPkg/HttpDxe/HttpDxeExtra.uni new file mode 100644 index 0000000000000000000000000000000000000000..1a06619eeecf0c5c4af10e517db44c1a205e0e06 GIT binary patch literal 1310 zcmZvc-EPxB5QXO&iFdHl8$hiCNZcTVNQes-Z5*YJfUC+$+SF1KM@}dW4^R7@UH`NJ zS>BzUojG&n?ArH#yVkXU_Z3fIpX}OlTiTU9w`X{@_tkbh8ynai{=m+xu$g5<<@N{9 z9Nz^ICF`{slK(TYYxb_(SYy_+-{08Y@&W^F;3T0Vg zmhdUa%E-uFjgXxloD?A@9!*?Is&9EqyLD_s`(S+=+sCHImQMPT3goY zdJ50S=OR_b{u;kOA#uJV=8)4Rz`6?oKjkrgNePQg`IqMir160+V;*Gg!6RgN7 zJ+UEkgg?ON5!zFT`C1|(E*x@#r5mlW!z^5(c9{Eat<`bEEwJzG3pH^SweC?#%!c@5 zP=%4W>SI>oE}v?qL@7p9ebgzj%KXXvjU6%9ml#!5d>4`ZXmDEe7%fKJE7ctQS3mu2 zx{kXvJ!)=_{vZ0ANW05Q-4Q6`rh`kc6Hq7MN5l+ad5#r0v?I + + 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" + +EFI_HTTP_PROTOCOL mEfiHttpTemplate = { + EfiHttpGetModeData, + EfiHttpConfigure, + EfiHttpRequest, + EfiHttpCancel, + EfiHttpResponse, + EfiHttpPoll +}; + +/** + Returns the operational parameters for the current HTTP child instance. + + The GetModeData() function is used to read the current mode data (operational + parameters) for this HTTP protocol instance. + + @param[in] This Pointer to EFI_HTTP_PROTOCOL instance. + @param[out] HttpConfigData Point to buffer for operational parameters of this + HTTP instance. + + @retval EFI_SUCCESS Operation succeeded. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + This is NULL. + HttpConfigData is NULL. + HttpConfigData->AccessPoint is NULL. + @retval EFI_NOT_STARTED The HTTP instance is not configured. + +**/ +EFI_STATUS +EFIAPI +EfiHttpGetModeData ( + IN EFI_HTTP_PROTOCOL *This, + OUT EFI_HTTP_CONFIG_DATA *HttpConfigData + ) +{ + HTTP_PROTOCOL *HttpInstance; + + if ((This == NULL) || (HttpConfigData == NULL)) { + return EFI_INVALID_PARAMETER; + } + + HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This); + ASSERT (HttpInstance != NULL); + + if (HttpInstance->State < HTTP_STATE_HTTP_CONFIGED) { + return EFI_NOT_STARTED; + } + + if (HttpConfigData->AccessPoint.IPv4Node == NULL) { + return EFI_INVALID_PARAMETER; + } + + HttpConfigData->HttpVersion = HttpInstance->HttpVersion; + HttpConfigData->TimeOutMillisec = HttpInstance->TimeOutMillisec; + HttpConfigData->LocalAddressIsIPv6 = HttpInstance->LocalAddressIsIPv6; + + CopyMem ( + HttpConfigData->AccessPoint.IPv4Node, + &HttpInstance->IPv4Node, + sizeof (HttpInstance->IPv4Node) + ); + + return EFI_SUCCESS; +} + +/** + Initialize or brutally reset the operational parameters for this EFI HTTP instance. + + The Configure() function does the following: + When HttpConfigData is not NULL Initialize this EFI HTTP instance by configuring + timeout, local address, port, etc. + When HttpConfigData is NULL, reset this EFI HTTP instance by closing all active + connections with remote hosts, canceling all asynchronous tokens, and flush request + and response buffers without informing the appropriate hosts. + + Except for GetModeData() and Configure(), No other EFI HTTP function can be executed + by this instance until the Configure() function is executed and returns successfully. + + @param[in] This Pointer to EFI_HTTP_PROTOCOL instance. + @param[in] HttpConfigData Pointer to the configure data to configure the instance. + + @retval EFI_SUCCESS Operation succeeded. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + This is NULL. + HttpConfigData->LocalAddressIsIPv6 is FALSE and + HttpConfigData->IPv4Node is NULL. + HttpConfigData->LocalAddressIsIPv6 is TRUE and + HttpConfigData->IPv6Node is NULL. + @retval EFI_ALREADY_STARTED Reinitialize this HTTP instance without calling + Configure() with NULL to reset it. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough system resources when + executing Configure(). + @retval EFI_UNSUPPORTED One or more options in HttpConfigData are not supported + in the implementation. +**/ +EFI_STATUS +EFIAPI +EfiHttpConfigure ( + IN EFI_HTTP_PROTOCOL *This, + IN EFI_HTTP_CONFIG_DATA *HttpConfigData + ) +{ + HTTP_PROTOCOL *HttpInstance; + EFI_STATUS Status; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This); + ASSERT (HttpInstance != NULL && HttpInstance->Service != NULL); + + if (HttpConfigData != NULL) { + // + // Check input parameters. + // + if (HttpConfigData->LocalAddressIsIPv6) { + if (HttpConfigData->AccessPoint.IPv6Node == NULL) { + return EFI_INVALID_PARAMETER; + } + } else { + if (HttpConfigData->AccessPoint.IPv4Node == NULL) { + return EFI_INVALID_PARAMETER; + } + } + // + // Now configure this HTTP instance. + // + if (HttpInstance->State != HTTP_STATE_UNCONFIGED) { + return EFI_ALREADY_STARTED; + } + + HttpInstance->HttpVersion = HttpConfigData->HttpVersion; + HttpInstance->TimeOutMillisec = HttpConfigData->TimeOutMillisec; + HttpInstance->LocalAddressIsIPv6 = HttpConfigData->LocalAddressIsIPv6; + + if (HttpConfigData->LocalAddressIsIPv6) { + return EFI_UNSUPPORTED; + } else { + CopyMem ( + &HttpInstance->IPv4Node, + HttpConfigData->AccessPoint.IPv4Node, + sizeof (HttpInstance->IPv4Node) + ); + + HttpInstance->State = HTTP_STATE_HTTP_CONFIGED; + return EFI_SUCCESS; + } + + } else { + if (HttpInstance->LocalAddressIsIPv6) { + return EFI_UNSUPPORTED; + } else { + HttpCleanProtocol (HttpInstance); + Status = HttpInitProtocol (HttpInstance->Service, HttpInstance); + if (EFI_ERROR (Status)) { + return Status; + } + + HttpInstance->State = HTTP_STATE_UNCONFIGED; + return EFI_SUCCESS; + } + } +} + + +/** + The Request() function queues an HTTP request to this HTTP instance. + + Similar to Transmit() function in the EFI TCP driver. When the HTTP request is sent + successfully, or if there is an error, Status in token will be updated and Event will + be signaled. + + @param[in] This Pointer to EFI_HTTP_PROTOCOL instance. + @param[in] Token Pointer to storage containing HTTP request token. + + @retval EFI_SUCCESS Outgoing data was processed. + @retval EFI_NOT_STARTED This EFI HTTP Protocol instance has not been started. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_TIMEOUT Data was dropped out of the transmit or receive queue. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough system resources. + @retval EFI_UNSUPPORTED The HTTP method is not supported in current + implementation. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + This is NULL. + Token->Message is NULL. + Token->Message->Body is not NULL, + Token->Message->BodyLength is non-zero, and + Token->Message->Data is NULL, but a previous call to + Request()has not been completed successfully. +**/ +EFI_STATUS +EFIAPI +EfiHttpRequest ( + IN EFI_HTTP_PROTOCOL *This, + IN EFI_HTTP_TOKEN *Token + ) +{ + EFI_HTTP_MESSAGE *HttpMsg; + EFI_HTTP_REQUEST_DATA *Request; + VOID *UrlParser; + EFI_STATUS Status; + CHAR8 *HostName; + UINT16 RemotePort; + HTTP_PROTOCOL *HttpInstance; + BOOLEAN Configure; + BOOLEAN ReConfigure; + CHAR8 *RequestStr; + CHAR8 *Url; + CHAR16 *HostNameStr; + HTTP_TOKEN_WRAP *Wrap; + HTTP_TCP_TOKEN_WRAP *TcpWrap; + + if ((This == NULL) || (Token == NULL)) { + return EFI_INVALID_PARAMETER; + } + + HttpMsg = Token->Message; + if ((HttpMsg == NULL) || (HttpMsg->Headers == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // + // Current implementation does not support POST/PUT method. + // If future version supports these two methods, Request could be NULL for a special case that to send large amounts + // of data. For this case, the implementation need check whether previous call to Request() has been completed or not. + // + // + Request = HttpMsg->Data.Request; + if ((Request == NULL) || (Request->Url == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // + // Only support GET and HEAD method in current implementation. + // + if ((Request->Method != HttpMethodGet) && (Request->Method != HttpMethodHead)) { + return EFI_UNSUPPORTED; + } + + HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This); + ASSERT (HttpInstance != NULL); + + if (HttpInstance->State < HTTP_STATE_HTTP_CONFIGED) { + return EFI_NOT_STARTED; + } + + if (HttpInstance->LocalAddressIsIPv6) { + return EFI_UNSUPPORTED; + } + + // + // Check whether the token already existed. + // + if (EFI_ERROR (NetMapIterate (&HttpInstance->TxTokens, HttpTokenExist, Token))) { + return EFI_ACCESS_DENIED; + } + + Url = NULL; + HostName = NULL; + Wrap = NULL; + HostNameStr = NULL; + TcpWrap = NULL; + + // + // Parse the URI of the remote host. + // + Url = AllocatePool (StrLen (Request->Url) + 1); + if (Url == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + UnicodeStrToAsciiStr (Request->Url, Url); + UrlParser = NULL; + Status = HttpParseUrl (Url, (UINT32) AsciiStrLen (Url), FALSE, &UrlParser); + if (EFI_ERROR (Status)) { + goto Error1; + } + + RequestStr = NULL; + HostName = NULL; + Status = HttpUrlGetHostName (Url, UrlParser, &HostName); + if (EFI_ERROR (Status)) { + goto Error1; + } + + Status = HttpUrlGetPort (Url, UrlParser, &RemotePort); + if (EFI_ERROR (Status)) { + RemotePort = HTTP_DEFAULT_PORT; + } + + Configure = TRUE; + ReConfigure = TRUE; + + if (HttpInstance->RemoteHost == NULL && HttpInstance->RemotePort == 0) { + // + // Request() is called the first time. + // + ReConfigure = FALSE; + } else { + if ((HttpInstance->RemotePort == RemotePort) && + (AsciiStrCmp (HttpInstance->RemoteHost, HostName) == 0)) { + // + // Host Name and port number of the request URL are the same with previous call to Request(). + // Check whether previous TCP packet sent out. + // + if (EFI_ERROR (NetMapIterate (&HttpInstance->TxTokens, HttpTcpNotReady, NULL))) { + // + // Wrap the HTTP token in HTTP_TOKEN_WRAP + // + Wrap = AllocateZeroPool (sizeof (HTTP_TOKEN_WRAP)); + if (Wrap == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error1; + } + + Wrap->HttpToken = Token; + Wrap->HttpInstance = HttpInstance; + + Status = HttpCreateTcp4TxEvent (Wrap); + if (EFI_ERROR (Status)) { + goto Error1; + } + + Status = NetMapInsertTail (&HttpInstance->TxTokens, Token, Wrap); + if (EFI_ERROR (Status)) { + goto Error1; + } + + Wrap->TcpWrap.Method = Request->Method; + + FreePool (Url); + FreePool (HostName); + + // + // Queue the HTTP token and return. + // + return EFI_SUCCESS; + } else { + // + // Use existing TCP instance to transmit the packet. + // + Configure = FALSE; + ReConfigure = FALSE; + } + } else { + // + // Need close existing TCP instance and create a new TCP instance for data transmit. + // + if (HttpInstance->RemoteHost != NULL) { + FreePool (HttpInstance->RemoteHost); + HttpInstance->RemoteHost = NULL; + } + } + } + + if (Configure) { + // + // Parse Url for IPv4 address, if failed, perform DNS resolution. + // + Status = NetLibAsciiStrToIp4 (HostName, &HttpInstance->RemoteAddr); + if (EFI_ERROR (Status)) { + HostNameStr = AllocateZeroPool ((AsciiStrLen (HostName) + 1) * sizeof (UINT16)); + if (HostNameStr == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error1; + } + + AsciiStrToUnicodeStr (HostName, HostNameStr); + Status = HttpDns4 (HttpInstance, HostNameStr, &HttpInstance->RemoteAddr); + FreePool (HostNameStr); + if (EFI_ERROR (Status)) { + goto Error1; + } + } + + // + // Save the RemotePort and RemoteHost. + // + ASSERT (HttpInstance->RemoteHost == NULL); + HttpInstance->RemotePort = RemotePort; + HttpInstance->RemoteHost = HostName; + HostName = NULL; + } + + if (ReConfigure) { + // + // The request URL is different from previous calls to Request(), close existing TCP instance. + // + ASSERT (HttpInstance->Tcp4 != NULL); + HttpCloseConnection (HttpInstance); + EfiHttpCancel (This, NULL); + } + + // + // Wrap the HTTP token in HTTP_TOKEN_WRAP + // + Wrap = AllocateZeroPool (sizeof (HTTP_TOKEN_WRAP)); + if (Wrap == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error1; + } + + Wrap->HttpToken = Token; + Wrap->HttpInstance = HttpInstance; + Wrap->TcpWrap.Method = Request->Method; + + if (Configure) { + // + // Configure TCP instance. + // + Status = HttpConfigureTcp4 (HttpInstance, Wrap); + if (EFI_ERROR (Status)) { + goto Error1; + } + // + // Connect TCP. + // + Status = HttpConnectTcp4 (HttpInstance); + if (EFI_ERROR (Status)) { + goto Error2; + } + } else { + // + // For the new HTTP token, create TX TCP token events. + // + Status = HttpCreateTcp4TxEvent (Wrap); + if (EFI_ERROR (Status)) { + goto Error1; + } + } + + // + // Create request message. + // + RequestStr = HttpGenRequestString (HttpInstance, HttpMsg, Url); + if (RequestStr == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error3; + } + + Status = NetMapInsertTail (&HttpInstance->TxTokens, Token, Wrap); + if (EFI_ERROR (Status)) { + goto Error4; + } + + FreePool (Url); + if (HostName != NULL) { + FreePool (HostName); + } + + // + // Transmit the request message. + // + Status = HttpTransmitTcp4 ( + HttpInstance, + Wrap, + (UINT8*) RequestStr, + AsciiStrLen (RequestStr) + ); + if (EFI_ERROR (Status)) { + goto Error5; + } + + return EFI_SUCCESS; + +Error5: + NetMapRemoveTail (&HttpInstance->TxTokens, NULL); + +Error4: + if (RequestStr != NULL) { + FreePool (RequestStr); + } + +Error3: + HttpCloseConnection (HttpInstance); + + +Error2: + HttpCloseTcp4ConnCloseEvent (HttpInstance); + if (NULL != Wrap->TcpWrap.TxToken.CompletionToken.Event) { + gBS->CloseEvent (Wrap->TcpWrap.TxToken.CompletionToken.Event); + } + +Error1: + if (Url != NULL) { + FreePool (Url); + } + if (HostName != NULL) { + FreePool (HostName); + } + if (Wrap != NULL) { + FreePool (Wrap); + } + if (UrlParser!= NULL) { + HttpUrlFreeParser (UrlParser); + } + + return Status; + +} + +/** + Cancel a TxToken or RxToken. + + @param[in] Map The HTTP instance's token queue. + @param[in] Item Object container for one HTTP token and token's wrap. + @param[in] Context The user's token to cancel. + + @retval EFI_SUCCESS Continue to check the next Item. + @retval EFI_ABORTED The user's Token (Token != NULL) is cancelled. + +**/ +EFI_STATUS +EFIAPI +HttpCancelTokens ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Context + ) +{ + + EFI_HTTP_TOKEN *Token; + HTTP_TOKEN_WRAP *Wrap; + + Token = (EFI_HTTP_TOKEN *) Context; + + // + // Return EFI_SUCCESS to check the next item in the map if + // this one doesn't match. + // + if ((Token != NULL) && (Token != Item->Key)) { + return EFI_SUCCESS; + } + + Wrap = (HTTP_TOKEN_WRAP *) Item->Value; + ASSERT (Wrap != NULL); + + // + // Free resources. + // + NetMapRemoveItem (Map, Item, NULL); + + if (Wrap->TcpWrap.TxToken.CompletionToken.Event != NULL) { + gBS->CloseEvent (Wrap->TcpWrap.TxToken.CompletionToken.Event); + } + + if (Wrap->TcpWrap.RxToken.CompletionToken.Event != NULL) { + gBS->CloseEvent (Wrap->TcpWrap.RxToken.CompletionToken.Event); + } + + if (Wrap->TcpWrap.RxToken.Packet.RxData->FragmentTable[0].FragmentBuffer != NULL) { + FreePool (Wrap->TcpWrap.RxToken.Packet.RxData->FragmentTable[0].FragmentBuffer); + } + + FreePool (Wrap); + + // + // If only one item is to be cancel, return EFI_ABORTED to stop + // iterating the map any more. + // + if (Token != NULL) { + return EFI_ABORTED; + } + + return EFI_SUCCESS; +} + +/** + Cancel the user's receive/transmit request. It is the worker function of + EfiHttpCancel API. If a matching token is found, it will call HttpCancelTokens to cancel the + token. + + @param[in] HttpInstance Pointer to HTTP_PROTOCOL structure. + @param[in] Token The token to cancel. If NULL, all token will be + cancelled. + + @retval EFI_SUCCESS The token is cancelled. + @retval EFI_NOT_FOUND The asynchronous request or response token is not found. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpCancel ( + IN HTTP_PROTOCOL *HttpInstance, + IN EFI_HTTP_TOKEN *Token + ) +{ + EFI_STATUS Status; + + // + // First check the tokens queued by EfiHttpRequest(). + // + Status = NetMapIterate (&HttpInstance->TxTokens, HttpCancelTokens, Token); + if (EFI_ERROR (Status)) { + if (Token != NULL) { + if (Status == EFI_ABORTED) { + return EFI_SUCCESS; + } + } else { + return Status; + } + } + + // + // Then check the tokens queued by EfiHttpResponse(). + // + Status = NetMapIterate (&HttpInstance->RxTokens, HttpCancelTokens, Token); + if (EFI_ERROR (Status)) { + if (Token != NULL) { + if (Status == EFI_ABORTED) { + return EFI_SUCCESS; + } else { + return EFI_NOT_FOUND; + } + } else { + return Status; + } + } + + return EFI_SUCCESS; +} + + +/** + Abort an asynchronous HTTP request or response token. + + The Cancel() function aborts a pending HTTP request or response transaction. If + Token is not NULL and the token is in transmit or receive queues when it is being + cancelled, its Token->Status will be set to EFI_ABORTED and then Token->Event will + be signaled. If the token is not in one of the queues, which usually means that the + asynchronous operation has completed, EFI_NOT_FOUND is returned. If Token is NULL, + all asynchronous tokens issued by Request() or Response() will be aborted. + + @param[in] This Pointer to EFI_HTTP_PROTOCOL instance. + @param[in] Token Point to storage containing HTTP request or response + token. + + @retval EFI_SUCCESS Request and Response queues are successfully flushed. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_NOT_STARTED This instance hasn't been configured. + @retval EFI_NO_MAPPING When using the default address, configuration (DHCP, + BOOTP, RARP, etc.) hasn't finished yet. + @retval EFI_NOT_FOUND The asynchronous request or response token is not + found. + @retval EFI_UNSUPPORTED The implementation does not support this function. + +**/ +EFI_STATUS +EFIAPI +EfiHttpCancel ( + IN EFI_HTTP_PROTOCOL *This, + IN EFI_HTTP_TOKEN *Token + ) +{ + HTTP_PROTOCOL *HttpInstance; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This); + ASSERT (HttpInstance != NULL); + + if (HttpInstance->State != HTTP_STATE_TCP_CONNECTED) { + return EFI_NOT_STARTED; + } + + return HttpCancel (HttpInstance, Token); + +} + +/** + A callback function to intercept events during message parser. + + This function will be invoked during HttpParseMessageBody() with various events type. An error + return status of the callback function will cause the HttpParseMessageBody() aborted. + + @param[in] EventType Event type of this callback call. + @param[in] Data A pointer to data buffer. + @param[in] Length Length in bytes of the Data. + @param[in] Context Callback context set by HttpInitMsgParser(). + + @retval EFI_SUCCESS Continue to parser the message body. + +**/ +EFI_STATUS +EFIAPI +HttpBodyParserCallback ( + IN HTTP_BODY_PARSE_EVENT EventType, + IN CHAR8 *Data, + IN UINTN Length, + IN VOID *Context + ) +{ + HTTP_TOKEN_WRAP *Wrap; + + if (EventType != BodyParseEventOnComplete) { + return EFI_SUCCESS; + } + + if (Data == NULL || Length != 0 || Context == NULL) { + return EFI_SUCCESS; + } + + Wrap = (HTTP_TOKEN_WRAP *) Context; + Wrap->HttpInstance->NextMsg = Data; + + // + // Free TxToken since already received corrsponding HTTP response. + // + FreePool (Wrap); + + return EFI_SUCCESS; +} + +/** + The work function of EfiHttpResponse(). + + @param[in] Wrap Pointer to HTTP token's wrap data. + + @retval EFI_SUCCESS Allocation succeeded. + @retval EFI_OUT_OF_RESOURCES Failed to complete the opration due to lack of resources. + @retval EFI_NOT_READY Can't find a corresponding TxToken. + +**/ +EFI_STATUS +HttpResponseWorker ( + IN HTTP_TOKEN_WRAP *Wrap + ) +{ + EFI_STATUS Status; + EFI_HTTP_MESSAGE *HttpMsg; + EFI_TCP4_IO_TOKEN *RxToken; + EFI_TCP4_PROTOCOL *Tcp4; + CHAR8 *EndofHeader; + CHAR8 *HttpHeaders; + UINTN SizeofHeaders; + CHAR8 *Buffer; + UINTN BufferSize; + UINTN StatusCode; + CHAR8 *Tmp; + CHAR8 *HeaderTmp; + CHAR8 *StatusCodeStr; + UINTN BodyLen; + HTTP_PROTOCOL *HttpInstance; + EFI_HTTP_TOKEN *Token; + NET_MAP_ITEM *Item; + HTTP_TOKEN_WRAP *ValueInItem; + UINTN HdrLen; + + HttpInstance = Wrap->HttpInstance; + Token = Wrap->HttpToken; + + HttpMsg = Token->Message; + + Tcp4 = HttpInstance->Tcp4; + ASSERT (Tcp4 != NULL); + HttpMsg->Headers = NULL; + HttpHeaders = NULL; + SizeofHeaders = 0; + Buffer = NULL; + BufferSize = 0; + EndofHeader = NULL; + + if (HttpMsg->Data.Response != NULL) { + // + // Need receive the HTTP headers, prepare buffer. + // + Status = HttpCreateTcp4RxEventForHeader (HttpInstance); + if (EFI_ERROR (Status)) { + goto Error; + } + + // + // Check whether we have cached header from previous call. + // + if ((HttpInstance->CacheBody != NULL) && (HttpInstance->NextMsg != NULL)) { + // + // The data is stored at [NextMsg, CacheBody + CacheLen]. + // + HdrLen = HttpInstance->CacheBody + HttpInstance->CacheLen - HttpInstance->NextMsg; + HttpHeaders = AllocateZeroPool (HdrLen); + if (HttpHeaders == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + + CopyMem (HttpHeaders, HttpInstance->NextMsg, HdrLen); + FreePool (HttpInstance->CacheBody); + HttpInstance->CacheBody = NULL; + HttpInstance->NextMsg = NULL; + HttpInstance->CacheOffset = 0; + SizeofHeaders = HdrLen; + BufferSize = HttpInstance->CacheLen; + + // + // Check whether we cached the whole HTTP headers. + // + EndofHeader = AsciiStrStr (HttpHeaders, HTTP_END_OF_HDR_STR); + } + + RxToken = &HttpInstance->RxToken; + RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer = AllocateZeroPool (DEF_BUF_LEN); + if (RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + + // + // Receive the HTTP headers only when EFI_HTTP_RESPONSE_DATA is not NULL. + // + while (EndofHeader == NULL) { + HttpInstance->IsRxDone = FALSE; + RxToken->Packet.RxData->DataLength = DEF_BUF_LEN; + RxToken->Packet.RxData->FragmentTable[0].FragmentLength = DEF_BUF_LEN; + Status = Tcp4->Receive (Tcp4, RxToken); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Tcp4 receive failed: %r\n", Status)); + goto Error; + } + + while (!HttpInstance->IsRxDone) { + Tcp4->Poll (Tcp4); + } + + Status = RxToken->CompletionToken.Status; + if (EFI_ERROR (Status)) { + goto Error; + } + + // + // Append the response string. + // + BufferSize = SizeofHeaders + RxToken->Packet.RxData->FragmentTable[0].FragmentLength; + Buffer = AllocateZeroPool (BufferSize); + if (Buffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + + if (HttpHeaders != NULL) { + CopyMem (Buffer, HttpHeaders, SizeofHeaders); + FreePool (HttpHeaders); + } + + CopyMem ( + Buffer + SizeofHeaders, + RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer, + RxToken->Packet.RxData->FragmentTable[0].FragmentLength + ); + HttpHeaders = Buffer; + SizeofHeaders = BufferSize; + + // + // Check whether we received end of HTTP headers. + // + EndofHeader = AsciiStrStr (HttpHeaders, HTTP_END_OF_HDR_STR); + }; + + // + // Skip the CRLF after the HTTP headers. + // + EndofHeader = EndofHeader + AsciiStrLen (HTTP_END_OF_HDR_STR); + + // + // Cache the part of body. + // + BodyLen = BufferSize - (EndofHeader - HttpHeaders); + if (BodyLen > 0) { + if (HttpInstance->CacheBody != NULL) { + FreePool (HttpInstance->CacheBody); + } + + HttpInstance->CacheBody = AllocateZeroPool (BodyLen); + if (HttpInstance->CacheBody == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + + CopyMem (HttpInstance->CacheBody, EndofHeader, BodyLen); + HttpInstance->CacheLen = BodyLen; + } + + FreePool (RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer); + RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer = NULL; + + // + // Search for Status Code. + // + StatusCodeStr = HttpHeaders + AsciiStrLen (HTTP_VERSION_STR) + 1; + if (StatusCodeStr == NULL) { + goto Error; + } + + StatusCode = AsciiStrDecimalToUintn (StatusCodeStr); + + // + // Remove the first line of HTTP message, e.g. "HTTP/1.1 200 OK\r\n". + // + Tmp = AsciiStrStr (HttpHeaders, HTTP_CRLF_STR); + if (Tmp == NULL) { + goto Error; + } + + Tmp = Tmp + AsciiStrLen (HTTP_CRLF_STR); + SizeofHeaders = SizeofHeaders - (Tmp - HttpHeaders); + HeaderTmp = AllocateZeroPool (SizeofHeaders); + if (HeaderTmp == NULL) { + goto Error; + } + + CopyMem (HeaderTmp, Tmp, SizeofHeaders); + FreePool (HttpHeaders); + HttpHeaders = HeaderTmp; + // + // Parse the HTTP header into array of key/value pairs. + // + Status = HttpUtilitiesParse (HttpHeaders, SizeofHeaders, &HttpMsg->Headers, &HttpMsg->HeaderCount); + if (EFI_ERROR (Status)) { + goto Error; + } + + FreePool (HttpHeaders); + HttpHeaders = NULL; + + HttpMsg->Data.Response->StatusCode = HttpMappingToStatusCode (StatusCode); + + // + // Init message-body parser by header information. + // + Status = EFI_NOT_READY; + ValueInItem = NULL; + NetMapRemoveHead (&HttpInstance->TxTokens, (VOID**) &ValueInItem); + if (ValueInItem == NULL) { + goto Error; + } + + // + // The first TxToken not transmitted yet, insert back and return error. + // + if (!ValueInItem->TcpWrap.IsTxDone) { + goto Error2; + } + + Status = HttpInitMsgParser ( + ValueInItem->TcpWrap.Method, + HttpMsg->Data.Response->StatusCode, + HttpMsg->HeaderCount, + HttpMsg->Headers, + HttpBodyParserCallback, + (VOID *) ValueInItem, + &HttpInstance->MsgParser + ); + if (EFI_ERROR (Status)) { + goto Error2; + } + + // + // Check whether we received a complete HTTP message. + // + if (HttpInstance->CacheBody != NULL) { + Status = HttpParseMessageBody (HttpInstance->MsgParser, HttpInstance->CacheLen, HttpInstance->CacheBody); + if (EFI_ERROR (Status)) { + goto Error2; + } + + if (HttpIsMessageComplete (HttpInstance->MsgParser)) { + // + // Free the MsgParse since we already have a full HTTP message. + // + HttpFreeMsgParser (HttpInstance->MsgParser); + HttpInstance->MsgParser = NULL; + } + } + + if ((HttpMsg->Body == NULL) || (HttpMsg->BodyLength == 0)) { + Status = EFI_SUCCESS; + goto Exit; + } + } + + // + // Receive the response body. + // + BodyLen = 0; + + // + // First check whether we cached some data. + // + if (HttpInstance->CacheBody != NULL) { + // + // Calculate the length of the cached data. + // + if (HttpInstance->NextMsg != NULL) { + // + // We have a cached HTTP message which includes a part of HTTP header of next message. + // + BodyLen = HttpInstance->NextMsg - (HttpInstance->CacheBody + HttpInstance->CacheOffset); + } else { + BodyLen = HttpInstance->CacheLen - HttpInstance->CacheOffset; + } + + if (BodyLen > 0) { + // + // We have some cached data. Just copy the data and return. + // + if (HttpMsg->BodyLength < BodyLen) { + CopyMem (HttpMsg->Body, HttpInstance->CacheBody + HttpInstance->CacheOffset, HttpMsg->BodyLength); + HttpInstance->CacheOffset = HttpInstance->CacheOffset + HttpMsg->BodyLength; + } else { + // + // Copy all cached data out. + // + CopyMem (HttpMsg->Body, HttpInstance->CacheBody + HttpInstance->CacheOffset, BodyLen); + HttpInstance->CacheOffset = BodyLen + HttpInstance->CacheOffset; + HttpMsg->BodyLength = BodyLen; + + if (HttpInstance->NextMsg == NULL) { + // + // There is no HTTP header of next message. Just free the cache buffer. + // + FreePool (HttpInstance->CacheBody); + HttpInstance->CacheBody = NULL; + HttpInstance->NextMsg = NULL; + HttpInstance->CacheOffset = 0; + } + } + // + // Return since we aready received required data. + // + Status = EFI_SUCCESS; + goto Exit; + } + + if (BodyLen == 0 && HttpInstance->MsgParser == NULL) { + // + // We received a complete HTTP message, and we don't have more data to return to caller. + // + HttpMsg->BodyLength = 0; + Status = EFI_SUCCESS; + goto Exit; + } + } + + ASSERT (HttpInstance->MsgParser != NULL); + + // + // We still need receive more data when there is no cache data and MsgParser is not NULL; + // + RxToken = &Wrap->TcpWrap.RxToken; + + RxToken->Packet.RxData->DataLength = (UINT32) HttpMsg->BodyLength; + RxToken->Packet.RxData->FragmentTable[0].FragmentLength = (UINT32) HttpMsg->BodyLength; + RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer = (VOID *) HttpMsg->Body; + + RxToken->CompletionToken.Status = EFI_NOT_READY; + Status = Tcp4->Receive (Tcp4, RxToken); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Tcp4 receive failed: %r\n", Status)); + goto Error; + } + + return Status; + +Exit: + Item = NetMapFindKey (&Wrap->HttpInstance->RxTokens, Wrap->HttpToken); + if (Item != NULL) { + NetMapRemoveItem (&Wrap->HttpInstance->RxTokens, Item, NULL); + } + Token->Status = Status; + gBS->SignalEvent (Token->Event); + FreePool (Wrap); + return Status; + +Error2: + NetMapInsertHead (&HttpInstance->TxTokens, ValueInItem->HttpToken, ValueInItem); + +Error: + if (Wrap != NULL) { + if (Wrap->TcpWrap.RxToken.CompletionToken.Event != NULL) { + gBS->CloseEvent (Wrap->TcpWrap.RxToken.CompletionToken.Event); + } + RxToken = &Wrap->TcpWrap.RxToken; + if (RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer != NULL) { + FreePool (RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer); + RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer = NULL; + } + FreePool (Wrap); + } + + if (HttpInstance->RxToken.CompletionToken.Event != NULL) { + gBS->CloseEvent (HttpInstance->RxToken.CompletionToken.Event); + HttpInstance->RxToken.CompletionToken.Event = NULL; + } + + RxToken = &HttpInstance->RxToken; + if (RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer != NULL) { + FreePool (RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer); + RxToken->Packet.RxData->FragmentTable[0].FragmentBuffer = NULL; + } + + if (HttpHeaders != NULL) { + FreePool (HttpHeaders); + } + + if (HttpMsg->Headers != NULL) { + FreePool (HttpMsg->Headers); + } + + if (HttpInstance->CacheBody != NULL) { + FreePool (HttpInstance->CacheBody); + HttpInstance->CacheBody = NULL; + } + + Token->Status = Status; + gBS->SignalEvent (Token->Event); + + return Status; + +} + + +/** + The Response() function queues an HTTP response to this HTTP instance, similar to + Receive() function in the EFI TCP driver. When the HTTP request is sent successfully, + or if there is an error, Status in token will be updated and Event will be signaled. + + The HTTP driver will queue a receive token to the underlying TCP instance. When data + is received in the underlying TCP instance, the data will be parsed and Token will + be populated with the response data. If the data received from the remote host + contains an incomplete or invalid HTTP header, the HTTP driver will continue waiting + (asynchronously) for more data to be sent from the remote host before signaling + Event in Token. + + It is the responsibility of the caller to allocate a buffer for Body and specify the + size in BodyLength. If the remote host provides a response that contains a content + body, up to BodyLength bytes will be copied from the receive buffer into Body and + BodyLength will be updated with the amount of bytes received and copied to Body. This + allows the client to download a large file in chunks instead of into one contiguous + block of memory. Similar to HTTP request, if Body is not NULL and BodyLength is + non-zero and all other fields are NULL or 0, the HTTP driver will queue a receive + token to underlying TCP instance. If data arrives in the receive buffer, up to + BodyLength bytes of data will be copied to Body. The HTTP driver will then update + BodyLength with the amount of bytes received and copied to Body. + + If the HTTP driver does not have an open underlying TCP connection with the host + specified in the response URL, Request() will return EFI_ACCESS_DENIED. This is + consistent with RFC 2616 recommendation that HTTP clients should attempt to maintain + an open TCP connection between client and host. + + @param[in] This Pointer to EFI_HTTP_PROTOCOL instance. + @param[in] Token Pointer to storage containing HTTP response token. + + @retval EFI_SUCCESS Allocation succeeded. + @retval EFI_NOT_STARTED This EFI HTTP Protocol instance has not been + initialized. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + This is NULL. + Token is NULL. + Token->Message->Headers is NULL. + Token->Message is NULL. + Token->Message->Body is not NULL, + Token->Message->BodyLength is non-zero, and + Token->Message->Data is NULL, but a previous call to + Response() has not been completed successfully. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough system resources. + @retval EFI_ACCESS_DENIED An open TCP connection is not present with the host + specified by response URL. +**/ +EFI_STATUS +EFIAPI +EfiHttpResponse ( + IN EFI_HTTP_PROTOCOL *This, + IN EFI_HTTP_TOKEN *Token + ) +{ + EFI_STATUS Status; + EFI_HTTP_MESSAGE *HttpMsg; + HTTP_PROTOCOL *HttpInstance; + HTTP_TOKEN_WRAP *Wrap; + + if ((This == NULL) || (Token == NULL)) { + return EFI_INVALID_PARAMETER; + } + + HttpMsg = Token->Message; + if (HttpMsg == NULL) { + return EFI_INVALID_PARAMETER; + } + + HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This); + ASSERT (HttpInstance != NULL); + + if (HttpInstance->State != HTTP_STATE_TCP_CONNECTED) { + return EFI_NOT_STARTED; + } + + if (HttpInstance->LocalAddressIsIPv6) { + return EFI_UNSUPPORTED; + } + + // + // Check whether the token already existed. + // + if (EFI_ERROR (NetMapIterate (&HttpInstance->RxTokens, HttpTokenExist, Token))) { + return EFI_ACCESS_DENIED; + } + + Wrap = AllocateZeroPool (sizeof (HTTP_TOKEN_WRAP)); + if (Wrap == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Wrap->HttpInstance = HttpInstance; + Wrap->HttpToken = Token; + + Status = HttpCreateTcp4RxEvent (Wrap); + if (EFI_ERROR (Status)) { + goto Error; + } + + Status = NetMapInsertTail (&HttpInstance->RxTokens, Token, Wrap); + if (EFI_ERROR (Status)) { + goto Error; + } + + // + // If already have pending RxTokens, return directly. + // + if (NetMapGetCount (&HttpInstance->RxTokens) > 1) { + return EFI_SUCCESS; + } + + return HttpResponseWorker (Wrap); + +Error: + if (Wrap != NULL) { + if (Wrap->TcpWrap.RxToken.CompletionToken.Event != NULL) { + gBS->CloseEvent (Wrap->TcpWrap.RxToken.CompletionToken.Event); + } + FreePool (Wrap); + } + + return Status; +} + +/** + The Poll() function can be used by network drivers and applications to increase the + rate that data packets are moved between the communication devices and the transmit + and receive queues. + + In some systems, the periodic timer event in the managed network driver may not poll + the underlying communications device fast enough to transmit and/or receive all data + packets without missing incoming packets or dropping outgoing packets. Drivers and + applications that are experiencing packet loss should try calling the Poll() function + more often. + + @param[in] This Pointer to EFI_HTTP_PROTOCOL instance. + + @retval EFI_SUCCESS Incoming or outgoing data was processed. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_NOT_READY No incoming or outgoing data is processed. + @retval EFI_NOT_STARTED This EFI HTTP Protocol instance has not been started. + +**/ +EFI_STATUS +EFIAPI +EfiHttpPoll ( + IN EFI_HTTP_PROTOCOL *This + ) +{ + HTTP_PROTOCOL *HttpInstance; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + HttpInstance = HTTP_INSTANCE_FROM_PROTOCOL (This); + ASSERT (HttpInstance != NULL); + + if (HttpInstance->LocalAddressIsIPv6) { + return EFI_UNSUPPORTED; + } + + if (HttpInstance->Tcp4 == NULL || HttpInstance->State != HTTP_STATE_TCP_CONNECTED) { + return EFI_NOT_STARTED; + } + + return HttpInstance->Tcp4->Poll (HttpInstance->Tcp4); +} diff --git a/NetworkPkg/HttpDxe/HttpImpl.h b/NetworkPkg/HttpDxe/HttpImpl.h new file mode 100644 index 0000000000..49c8af1b21 --- /dev/null +++ b/NetworkPkg/HttpDxe/HttpImpl.h @@ -0,0 +1,239 @@ +/** @file + The header files of implementation of EFI_HTTP_PROTOCOL protocol interfaces. + + Copyright (c) 2015, Intel Corporation. All rights reserved.
+ + 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. + +**/ + +#ifndef __EFI_HTTP_IMPL_H__ +#define __EFI_HTTP_IMPL_H__ + +#define HTTP_DEFAULT_PORT 80 +#define HTTP_END_OF_HDR_STR "\r\n\r\n" +#define HTTP_CRLF_STR "\r\n" +#define HTTP_VERSION_STR "HTTP/1.1" +#define HTTP_VERSION_CRLF_STR " HTTP/1.1\r\n" +#define HTTP_GET_STR "GET " +#define HTTP_HEAD_STR "HEAD " +// +// Connect method has maximum length according to EFI_HTTP_METHOD defined in +// UEFI2.5 spec so use this. +// +#define HTTP_MAXIMUM_METHOD_LEN sizeof ("CONNECT") + +/** + Returns the operational parameters for the current HTTP child instance. + + The GetModeData() function is used to read the current mode data (operational + parameters) for this HTTP protocol instance. + + @param[in] This Pointer to EFI_HTTP_PROTOCOL instance. + @param[out] HttpConfigData Point to buffer for operational parameters of this + HTTP instance. + + @retval EFI_SUCCESS Operation succeeded. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + This is NULL. + HttpConfigData is NULL. + HttpConfigData->AccessPoint is NULL. + @retval EFI_NOT_STARTED The HTTP instance is not configured. + +**/ +EFI_STATUS +EFIAPI +EfiHttpGetModeData ( + IN EFI_HTTP_PROTOCOL *This, + OUT EFI_HTTP_CONFIG_DATA *HttpConfigData + ); + +/** + Initialize or brutally reset the operational parameters for this EFI HTTP instance. + + The Configure() function does the following: + When HttpConfigData is not NULL Initialize this EFI HTTP instance by configuring + timeout, local address, port, etc. + When HttpConfigData is NULL, reset this EFI HTTP instance by closing all active + connections with remote hosts, canceling all asynchronous tokens, and flush request + and response buffers without informing the appropriate hosts. + + Except for GetModeData() and Configure(), No other EFI HTTP function can be executed + by this instance until the Configure() function is executed and returns successfully. + + @param[in] This Pointer to EFI_HTTP_PROTOCOL instance. + @param[in] HttpConfigData Pointer to the configure data to configure the instance. + + @retval EFI_SUCCESS Operation succeeded. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + This is NULL. + HttpConfigData->LocalAddressIsIPv6 is FALSE and + HttpConfigData->IPv4Node is NULL. + HttpConfigData->LocalAddressIsIPv6 is TRUE and + HttpConfigData->IPv6Node is NULL. + @retval EFI_ALREADY_STARTED Reinitialize this HTTP instance without calling + Configure() with NULL to reset it. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough system resources when + executing Configure(). + @retval EFI_UNSUPPORTED One or more options in ConfigData are not supported + in the implementation. +**/ +EFI_STATUS +EFIAPI +EfiHttpConfigure ( + IN EFI_HTTP_PROTOCOL *This, + IN EFI_HTTP_CONFIG_DATA *HttpConfigData + ); + +/** + The Request() function queues an HTTP request to this HTTP instance. + + Similar to Transmit() function in the EFI TCP driver. When the HTTP request is sent + successfully, or if there is an error, Status in token will be updated and Event will + be signaled. + + @param[in] This Pointer to EFI_HTTP_PROTOCOL instance. + @param[in] Token Pointer to storage containing HTTP request token. + + @retval EFI_SUCCESS Outgoing data was processed. + @retval EFI_NOT_STARTED This EFI HTTP Protocol instance has not been started. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_TIMEOUT Data was dropped out of the transmit or receive queue. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough system resources. + @retval EFI_UNSUPPORTED The HTTP method is not supported in current + implementation. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + This is NULL. + Token->Message is NULL. + Token->Message->Body is not NULL, + Token->Message->BodyLength is non-zero, and + Token->Message->Data is NULL, but a previous call to + Request()has not been completed successfully. +**/ +EFI_STATUS +EFIAPI +EfiHttpRequest ( + IN EFI_HTTP_PROTOCOL *This, + IN EFI_HTTP_TOKEN *Token + ); + +/** + Abort an asynchronous HTTP request or response token. + + The Cancel() function aborts a pending HTTP request or response transaction. If + Token is not NULL and the token is in transmit or receive queues when it is being + cancelled, its Token->Status will be set to EFI_ABORTED and then Token->Event will + be signaled. If the token is not in one of the queues, which usually means that the + asynchronous operation has completed, EFI_NOT_FOUND is returned. If Token is NULL, + all asynchronous tokens issued by Request() or Response() will be aborted. + + @param[in] This Pointer to EFI_HTTP_PROTOCOL instance. + @param[in] Token Point to storage containing HTTP request or response + token. + + @retval EFI_SUCCESS Request and Response queues are successfully flushed. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_NOT_STARTED This instance hasn't been configured. + @retval EFI_NO_MAPPING When using the default address, configuration (DHCP, + BOOTP, RARP, etc.) hasn't finished yet. + @retval EFI_NOT_FOUND The asynchronous request or response token is not + found. + @retval EFI_UNSUPPORTED The implementation does not support this function. +**/ +EFI_STATUS +EFIAPI +EfiHttpCancel ( + IN EFI_HTTP_PROTOCOL *This, + IN EFI_HTTP_TOKEN *Token + ); + +/** + The Response() function queues an HTTP response to this HTTP instance, similar to + Receive() function in the EFI TCP driver. When the HTTP request is sent successfully, + or if there is an error, Status in token will be updated and Event will be signaled. + + The HTTP driver will queue a receive token to the underlying TCP instance. When data + is received in the underlying TCP instance, the data will be parsed and Token will + be populated with the response data. If the data received from the remote host + contains an incomplete or invalid HTTP header, the HTTP driver will continue waiting + (asynchronously) for more data to be sent from the remote host before signaling + Event in Token. + + It is the responsibility of the caller to allocate a buffer for Body and specify the + size in BodyLength. If the remote host provides a response that contains a content + body, up to BodyLength bytes will be copied from the receive buffer into Body and + BodyLength will be updated with the amount of bytes received and copied to Body. This + allows the client to download a large file in chunks instead of into one contiguous + block of memory. Similar to HTTP request, if Body is not NULL and BodyLength is + non-zero and all other fields are NULL or 0, the HTTP driver will queue a receive + token to underlying TCP instance. If data arrives in the receive buffer, up to + BodyLength bytes of data will be copied to Body. The HTTP driver will then update + BodyLength with the amount of bytes received and copied to Body. + + If the HTTP driver does not have an open underlying TCP connection with the host + specified in the response URL, Request() will return EFI_ACCESS_DENIED. This is + consistent with RFC 2616 recommendation that HTTP clients should attempt to maintain + an open TCP connection between client and host. + + @param[in] This Pointer to EFI_HTTP_PROTOCOL instance. + @param[in] Token Pointer to storage containing HTTP response token. + + @retval EFI_SUCCESS Allocation succeeded. + @retval EFI_NOT_STARTED This EFI HTTP Protocol instance has not been + initialized. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + This is NULL. + Token is NULL. + Token->Message->Headers is NULL. + Token->Message is NULL. + Token->Message->Body is not NULL, + Token->Message->BodyLength is non-zero, and + Token->Message->Data is NULL, but a previous call to + Response() has not been completed successfully. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough system resources. + @retval EFI_ACCESS_DENIED An open TCP connection is not present with the host + specified by response URL. +**/ +EFI_STATUS +EFIAPI +EfiHttpResponse ( + IN EFI_HTTP_PROTOCOL *This, + IN EFI_HTTP_TOKEN *Token + ); + +/** + The Poll() function can be used by network drivers and applications to increase the + rate that data packets are moved between the communication devices and the transmit + and receive queues. + + In some systems, the periodic timer event in the managed network driver may not poll + the underlying communications device fast enough to transmit and/or receive all data + packets without missing incoming packets or dropping outgoing packets. Drivers and + applications that are experiencing packet loss should try calling the Poll() function + more often. + + @param[in] This Pointer to EFI_HTTP_PROTOCOL instance. + + @retval EFI_SUCCESS Incoming or outgoing data was processed. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_NOT_READY No incoming or outgoing data is processed. + @retval EFI_NOT_STARTED This EFI HTTP Protocol instance has not been started. + +**/ +EFI_STATUS +EFIAPI +EfiHttpPoll ( + IN EFI_HTTP_PROTOCOL *This + ); + +extern EFI_HTTP_PROTOCOL mEfiHttpTemplate; + +#endif diff --git a/NetworkPkg/HttpDxe/HttpProto.c b/NetworkPkg/HttpDxe/HttpProto.c new file mode 100644 index 0000000000..50ade4c230 --- /dev/null +++ b/NetworkPkg/HttpDxe/HttpProto.c @@ -0,0 +1,1165 @@ +/** @file + Miscellaneous routines for HttpDxe driver. + +Copyright (c) 2015, Intel Corporation. All rights reserved.
+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 TxToken for Tcp4->Transmit(). + + @param[in] Event The event signaled. + @param[in] Context The context. + +**/ +VOID +EFIAPI +HttpTcpTransmitNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + HTTP_TOKEN_WRAP *Wrap; + + if ((Event == NULL) || (Context == NULL)) { + return ; + } + + Wrap = (HTTP_TOKEN_WRAP *) Context; + Wrap->HttpToken->Status = Wrap->TcpWrap.TxToken.CompletionToken.Status; + gBS->SignalEvent (Wrap->HttpToken->Event); + + // + // Free resources. + // + if (Wrap->TcpWrap.TxToken.Packet.TxData->FragmentTable[0].FragmentBuffer != NULL) { + FreePool (Wrap->TcpWrap.TxToken.Packet.TxData->FragmentTable[0].FragmentBuffer); + } + + if (Wrap->TcpWrap.TxToken.CompletionToken.Event != NULL) { + gBS->CloseEvent (Wrap->TcpWrap.TxToken.CompletionToken.Event); + } + + Wrap->TcpWrap.IsTxDone = TRUE; + + // + // Check pending TxTokens and sent out. + // + NetMapIterate (&Wrap->HttpInstance->TxTokens, HttpTcpTransmit, NULL); + +} + +/** + The notify function associated with RxToken for Tcp4->Receive (). + + @param[in] Event The event signaled. + @param[in] Context The context. + +**/ +VOID +EFIAPI +HttpTcpReceiveNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + HTTP_TOKEN_WRAP *Wrap; + NET_MAP_ITEM *Item; + UINTN Length; + EFI_STATUS Status; + HTTP_PROTOCOL *HttpInstance; + + if ((Event == NULL) || (Context == NULL)) { + return ; + } + + Wrap = (HTTP_TOKEN_WRAP *) Context; + if (EFI_ERROR (Wrap->TcpWrap.RxToken.CompletionToken.Status)) { + return ; + } + + HttpInstance = Wrap->HttpInstance; + + // + // Check whether we receive a complete HTTP message. + // + ASSERT (HttpInstance->MsgParser != NULL); + + Length = (UINTN) Wrap->TcpWrap.RxData.FragmentTable[0].FragmentLength; + Status = HttpParseMessageBody ( + HttpInstance->MsgParser, + Length, + Wrap->HttpToken->Message->Body + ); + if (EFI_ERROR (Status)) { + return ; + } + + if (HttpIsMessageComplete (HttpInstance->MsgParser)) { + // + // Free the MsgParse since we already have a full HTTP message. + // + HttpFreeMsgParser (HttpInstance->MsgParser); + HttpInstance->MsgParser = NULL; + } + + Wrap->HttpToken->Message->BodyLength = Length; + ASSERT (HttpInstance->CacheBody == NULL); + // + // We receive part of header of next HTTP msg. + // + if (HttpInstance->NextMsg != NULL) { + Wrap->HttpToken->Message->BodyLength = HttpInstance->NextMsg - + (CHAR8 *) Wrap->HttpToken->Message->Body; + HttpInstance->CacheLen = Length - Wrap->HttpToken->Message->BodyLength; + if (HttpInstance->CacheLen != 0) { + HttpInstance->CacheBody = AllocateZeroPool (HttpInstance->CacheLen); + if (HttpInstance->CacheBody == NULL) { + return ; + } + CopyMem (HttpInstance->CacheBody, HttpInstance->NextMsg, HttpInstance->CacheLen); + HttpInstance->NextMsg = HttpInstance->CacheBody; + HttpInstance->CacheOffset = 0; + } + } + + Item = NetMapFindKey (&Wrap->HttpInstance->RxTokens, Wrap->HttpToken); + if (Item != NULL) { + NetMapRemoveItem (&Wrap->HttpInstance->RxTokens, Item, NULL); + } + + + Wrap->TcpWrap.IsRxDone = TRUE; + Wrap->HttpToken->Status = Wrap->TcpWrap.RxToken.CompletionToken.Status; + + gBS->SignalEvent (Wrap->HttpToken->Event); + + // + // Check pending RxTokens and receive the HTTP message. + // + NetMapIterate (&Wrap->HttpInstance->RxTokens, HttpTcpReceive, NULL); + + FreePool (Wrap); +} + +/** + Create events for the TCP4 connection token and TCP4 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 +HttpCreateTcp4ConnCloseEvent ( + IN HTTP_PROTOCOL *HttpInstance + ) +{ + EFI_STATUS Status; + // + // Create events for variuos asynchronous operations. + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpCommonNotify, + &HttpInstance->IsConnDone, + &HttpInstance->ConnToken.CompletionToken.Event + ); + if (EFI_ERROR (Status)) { + goto ERROR; + } + + // + // Initialize CloseToken + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpCommonNotify, + &HttpInstance->IsCloseDone, + &HttpInstance->CloseToken.CompletionToken.Event + ); + if (EFI_ERROR (Status)) { + goto ERROR; + } + + + return EFI_SUCCESS; + +ERROR: + // + // Error handling + // + HttpCloseTcp4ConnCloseEvent (HttpInstance); + + return Status; +} + + +/** + Close events in the TCP4 connection token and TCP4 close token. + + @param[in] HttpInstance Pointer to HTTP_PROTOCOL structure. + +**/ +VOID +HttpCloseTcp4ConnCloseEvent ( + IN HTTP_PROTOCOL *HttpInstance + ) +{ + ASSERT (HttpInstance != NULL); + + if (NULL != HttpInstance->ConnToken.CompletionToken.Event) { + gBS->CloseEvent (HttpInstance->ConnToken.CompletionToken.Event); + } + + if (NULL != HttpInstance->CloseToken.CompletionToken.Event) { + gBS->CloseEvent(HttpInstance->CloseToken.CompletionToken.Event); + } +} + +/** + Create event for the TCP4 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 +HttpCreateTcp4TxEvent ( + IN HTTP_TOKEN_WRAP *Wrap + ) +{ + EFI_STATUS Status; + HTTP_PROTOCOL *HttpInstance; + HTTP_TCP_TOKEN_WRAP *TcpWrap; + + HttpInstance = Wrap->HttpInstance; + TcpWrap = &Wrap->TcpWrap; + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpTcpTransmitNotify, + Wrap, + &TcpWrap->TxToken.CompletionToken.Event + ); + if (EFI_ERROR (Status)) { + return Status; + } + + TcpWrap->TxData.Push = TRUE; + TcpWrap->TxData.Urgent = FALSE; + TcpWrap->TxData.FragmentCount = 1; + TcpWrap->TxToken.Packet.TxData = &Wrap->TcpWrap.TxData; + TcpWrap->TxToken.CompletionToken.Status = EFI_NOT_READY; + + return EFI_SUCCESS; +} + +/** + Create event for the TCP4 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 +HttpCreateTcp4RxEventForHeader ( + IN HTTP_PROTOCOL *HttpInstance + ) +{ + EFI_STATUS Status; + + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpCommonNotify, + &HttpInstance->IsRxDone, + &HttpInstance->RxToken.CompletionToken.Event + ); + if (EFI_ERROR (Status)) { + return Status; + } + + HttpInstance->RxData.FragmentCount = 1; + HttpInstance->RxToken.Packet.RxData = &HttpInstance->RxData; + HttpInstance->RxToken.CompletionToken.Status = EFI_NOT_READY; + + return EFI_SUCCESS; +} + +/** + Create event for the TCP4 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 +HttpCreateTcp4RxEvent ( + IN HTTP_TOKEN_WRAP *Wrap + ) +{ + EFI_STATUS Status; + HTTP_PROTOCOL *HttpInstance; + HTTP_TCP_TOKEN_WRAP *TcpWrap; + + HttpInstance = Wrap->HttpInstance; + TcpWrap = &Wrap->TcpWrap; + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + HttpTcpReceiveNotify, + Wrap, + &TcpWrap->RxToken.CompletionToken.Event + ); + if (EFI_ERROR (Status)) { + return Status; + } + + TcpWrap->RxData.FragmentCount = 1; + TcpWrap->RxToken.Packet.RxData = &Wrap->TcpWrap.RxData; + TcpWrap->RxToken.CompletionToken.Status = EFI_NOT_READY; + + return EFI_SUCCESS; +} + +/** + Intiialize the HTTP_PROTOCOL structure to the unconfigured state. + + @param[in] HttpSb The HTTP service private instance. + @param[in, out] HttpInstance Pointer to HTTP_PROTOCOL structure. + + @retval EFI_SUCCESS HTTP_PROTOCOL structure is initialized successfully. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpInitProtocol ( + IN HTTP_SERVICE *HttpSb, + IN OUT HTTP_PROTOCOL *HttpInstance + ) +{ + EFI_STATUS Status; + VOID *Interface; + + ASSERT ((HttpSb != NULL) && (HttpInstance != NULL)); + + HttpInstance->Signature = HTTP_PROTOCOL_SIGNATURE; + CopyMem (&HttpInstance->Http, &mEfiHttpTemplate, sizeof (HttpInstance->Http)); + HttpInstance->Service = HttpSb; + + // + // Create TCP child. + // + Status = NetLibCreateServiceChild ( + HttpInstance->Service->ControllerHandle, + HttpInstance->Service->ImageHandle, + &gEfiTcp4ServiceBindingProtocolGuid, + &HttpInstance->TcpChildHandle + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + HttpInstance->TcpChildHandle, + &gEfiTcp4ProtocolGuid, + (VOID **) &Interface, + HttpInstance->Service->ImageHandle, + HttpInstance->Service->ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + HttpInstance->TcpChildHandle, + &gEfiTcp4ProtocolGuid, + (VOID **) &HttpInstance->Tcp4, + HttpInstance->Service->ImageHandle, + HttpInstance->Handle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + if (EFI_ERROR(Status)) { + goto ON_ERROR; + } + + NetMapInit (&HttpInstance->TxTokens); + NetMapInit (&HttpInstance->RxTokens); + + return EFI_SUCCESS; + +ON_ERROR: + + if (HttpInstance->TcpChildHandle != NULL) { + gBS->CloseProtocol ( + HttpInstance->TcpChildHandle, + &gEfiTcp4ProtocolGuid, + HttpInstance->Service->ImageHandle, + HttpInstance->Service->ControllerHandle + ); + + gBS->CloseProtocol ( + HttpInstance->TcpChildHandle, + &gEfiTcp4ProtocolGuid, + HttpInstance->Service->ImageHandle, + HttpInstance->Handle + ); + + NetLibDestroyServiceChild ( + HttpInstance->Service->ControllerHandle, + HttpInstance->Service->ImageHandle, + &gEfiTcp4ServiceBindingProtocolGuid, + HttpInstance->TcpChildHandle + ); + } + + return Status; + +} + +/** + 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); + + HttpCloseTcp4ConnCloseEvent (HttpInstance); + + 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; + } + + NetMapClean (&HttpInstance->TxTokens); + NetMapClean (&HttpInstance->RxTokens); + + if (HttpInstance->TcpChildHandle != NULL) { + gBS->CloseProtocol ( + HttpInstance->TcpChildHandle, + &gEfiTcp4ProtocolGuid, + HttpInstance->Service->ImageHandle, + HttpInstance->Service->ControllerHandle + ); + + gBS->CloseProtocol ( + HttpInstance->TcpChildHandle, + &gEfiTcp4ProtocolGuid, + HttpInstance->Service->ImageHandle, + HttpInstance->Handle + ); + + NetLibDestroyServiceChild ( + HttpInstance->Service->ControllerHandle, + HttpInstance->Service->ImageHandle, + &gEfiTcp4ServiceBindingProtocolGuid, + HttpInstance->TcpChildHandle + ); + } +} + +/** + 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; + + // + // Create events for variuos asynchronous operations. + // + HttpInstance->IsConnDone = FALSE; + + // + // Connect to Http server + // + HttpInstance->ConnToken.CompletionToken.Status = EFI_NOT_READY; + Status = HttpInstance->Tcp4->Connect (HttpInstance->Tcp4, &HttpInstance->ConnToken); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "HttpCreateConnection: Tcp4->Connect() = %r\n", Status)); + return Status; + } + + while (!HttpInstance->IsConnDone) { + HttpInstance->Tcp4->Poll (HttpInstance->Tcp4); + } + + Status = HttpInstance->ConnToken.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; + + HttpInstance->CloseToken.AbortOnClose = TRUE; + HttpInstance->IsCloseDone = FALSE; + + + Status = HttpInstance->Tcp4->Close (HttpInstance->Tcp4, &HttpInstance->CloseToken); + if (EFI_ERROR (Status)) { + return Status; + } + + while (!HttpInstance->IsCloseDone) { + HttpInstance->Tcp4->Poll (HttpInstance->Tcp4); + } + + HttpInstance->State = HTTP_STATE_TCP_CLOSED; + return Status; +} + +/** + 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; + HTTP_TCP_TOKEN_WRAP *TcpWrap; + + ASSERT (HttpInstance != NULL); + TcpWrap = &Wrap->TcpWrap; + + + 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 = HttpCreateTcp4ConnCloseEvent (HttpInstance); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = HttpCreateTcp4TxEvent (Wrap); + if (EFI_ERROR (Status)) { + return Status; + } + + HttpInstance->State = HTTP_STATE_TCP_CONFIGED; + + return EFI_SUCCESS; +} + +/** + Check existing TCP connection, if in error state, receover TCP4 connection. + + @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) { + HttpCloseConnection(HttpInstance); + } + + return HttpCreateConnection (HttpInstance); +} + +/** + Send the HTTP message through TCP4. + + @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 +HttpTransmitTcp4 ( + IN HTTP_PROTOCOL *HttpInstance, + IN HTTP_TOKEN_WRAP *Wrap, + IN UINT8 *TxString, + IN UINTN TxStringLen + ) +{ + EFI_STATUS Status; + EFI_TCP4_IO_TOKEN *TxToken; + EFI_TCP4_PROTOCOL *Tcp4; + + Tcp4 = HttpInstance->Tcp4; + TxToken = &Wrap->TcpWrap.TxToken; + + TxToken->Packet.TxData->DataLength = (UINT32) TxStringLen; + TxToken->Packet.TxData->FragmentTable[0].FragmentLength = (UINT32) TxStringLen; + TxToken->Packet.TxData->FragmentTable[0].FragmentBuffer = (VOID *) TxString; + TxToken->CompletionToken.Status = EFI_NOT_READY; + + Wrap->TcpWrap.IsTxDone = FALSE; + Status = Tcp4->Transmit (Tcp4, TxToken); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Transmit failed: %r\n", Status)); + return Status; + } + + return Status; +} + +/** + Translate the status code in HTTP message to EFI_HTTP_STATUS_CODE defined + in UEFI 2.5 specification. + + @param[in] StatusCode The status code value in HTTP message. + + @return Value defined in EFI_HTTP_STATUS_CODE . + +**/ +EFI_HTTP_STATUS_CODE +HttpMappingToStatusCode ( + IN UINTN StatusCode + ) +{ + switch (StatusCode) { + case 100: + return HTTP_STATUS_100_CONTINUE; + case 101: + return HTTP_STATUS_101_SWITCHING_PROTOCOLS; + case 200: + return HTTP_STATUS_200_OK; + case 201: + return HTTP_STATUS_201_CREATED; + case 202: + return HTTP_STATUS_202_ACCEPTED; + case 203: + return HTTP_STATUS_203_NON_AUTHORITATIVE_INFORMATION; + case 204: + return HTTP_STATUS_204_NO_CONTENT; + case 205: + return HTTP_STATUS_205_RESET_CONTENT; + case 206: + return HTTP_STATUS_206_PARTIAL_CONTENT; + case 300: + return HTTP_STATUS_300_MULTIPLE_CHIOCES; + case 301: + return HTTP_STATUS_301_MOVED_PERMANENTLY; + case 302: + return HTTP_STATUS_302_FOUND; + case 303: + return HTTP_STATUS_303_SEE_OTHER; + case 304: + return HTTP_STATUS_304_NOT_MODIFIED; + case 305: + return HTTP_STATUS_305_USE_PROXY; + case 307: + return HTTP_STATUS_307_TEMPORARY_REDIRECT; + case 400: + return HTTP_STATUS_400_BAD_REQUEST; + case 401: + return HTTP_STATUS_401_UNAUTHORIZED; + case 402: + return HTTP_STATUS_402_PAYMENT_REQUIRED; + case 403: + return HTTP_STATUS_403_FORBIDDEN; + case 404: + return HTTP_STATUS_404_NOT_FOUND; + case 405: + return HTTP_STATUS_405_METHOD_NOT_ALLOWED; + case 406: + return HTTP_STATUS_406_NOT_ACCEPTABLE; + case 407: + return HTTP_STATUS_407_PROXY_AUTHENTICATION_REQUIRED; + case 408: + return HTTP_STATUS_408_REQUEST_TIME_OUT; + case 409: + return HTTP_STATUS_409_CONFLICT; + case 410: + return HTTP_STATUS_410_GONE; + case 411: + return HTTP_STATUS_411_LENGTH_REQUIRED; + case 412: + return HTTP_STATUS_412_PRECONDITION_FAILED; + case 413: + return HTTP_STATUS_413_REQUEST_ENTITY_TOO_LARGE; + case 414: + return HTTP_STATUS_414_REQUEST_URI_TOO_LARGE; + case 415: + return HTTP_STATUS_415_UNSUPPORETD_MEDIA_TYPE; + case 416: + return HTTP_STATUS_416_REQUESTED_RANGE_NOT_SATISFIED; + case 417: + return HTTP_STATUS_417_EXPECTATION_FAILED; + case 500: + return HTTP_STATUS_500_INTERNAL_SERVER_ERROR; + case 501: + return HTTP_STATUS_501_NOT_IMIPLEMENTED; + case 502: + return HTTP_STATUS_502_BAD_GATEWAY; + case 503: + return HTTP_STATUS_503_SERVICE_UNAVAILABLE; + case 504: + return HTTP_STATUS_504_GATEWAY_TIME_OUT; + case 505: + return HTTP_STATUS_505_HTTP_VERSION_NOT_SUPPORTED; + + default: + return HTTP_STATUS_UNSUPPORTED_STATUS; + } +} + +/** + Check whether the user's token or event has already + been enqueue on HTTP TxToken or RxToken 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 TxToken is already sent out. + + @param[in] Map The container of TxToken. + @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 mssage by processing the associated HTTP token. + + @param[in] Map The container of TxToken. + @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 *RequestStr; + CHAR8 *Url; + + ValueInItem = (HTTP_TOKEN_WRAP *) Item->Value; + if (ValueInItem->TcpWrap.IsTxDone) { + return EFI_SUCCESS; + } + + // + // Parse the URI of the remote host. + // + Url = AllocatePool (StrLen (ValueInItem->HttpToken->Message->Data.Request->Url) + 1); + if (Url == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + UnicodeStrToAsciiStr (ValueInItem->HttpToken->Message->Data.Request->Url, Url); + + // + // Create request message. + // + RequestStr = HttpGenRequestString ( + ValueInItem->HttpInstance, + ValueInItem->HttpToken->Message, + Url + ); + FreePool (Url); + if (RequestStr == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Transmit the request message. + // + Status = HttpTransmitTcp4 ( + ValueInItem->HttpInstance, + ValueInItem, + (UINT8*) RequestStr, + AsciiStrLen (RequestStr) + ); + FreePool (RequestStr); + return Status; +} + +/** + Receive the HTTP response by processing the associated HTTP token. + + @param[in] Map The container of RxToken. + @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); +} + +/** + Generate HTTP request string. + + @param[in] HttpInstance Pointer to HTTP_PROTOCOL structure. + @param[in] Message Pointer to storage containing HTTP message data. + @param[in] Url The URL of a remote host. + + @return Pointer to the created HTTP request string. + @return NULL if any error occured. + +**/ +CHAR8 * +HttpGenRequestString ( + IN HTTP_PROTOCOL *HttpInstance, + IN EFI_HTTP_MESSAGE *Message, + IN CHAR8 *Url + ) +{ + EFI_STATUS Status; + UINTN StrLength; + UINT8 *Request; + UINT8 *RequestPtr; + UINTN HttpHdrSize; + UINTN MsgSize; + BOOLEAN Success; + VOID *HttpHdr; + EFI_HTTP_HEADER **AppendList; + UINTN Index; + + ASSERT (HttpInstance != NULL); + ASSERT (Message != NULL); + + DEBUG ((EFI_D_ERROR, "HttpMethod - %x\n", Message->Data.Request->Method)); + + Request = NULL; + Success = FALSE; + HttpHdr = NULL; + AppendList = NULL; + + // + // Build AppendList + // + AppendList = AllocateZeroPool (sizeof (EFI_HTTP_HEADER *) * (Message->HeaderCount)); + if (AppendList == NULL) { + return NULL; + } + + for(Index = 0; Index < Message->HeaderCount; Index++){ + AppendList[Index] = &Message->Headers[Index]; + } + + // + // Build raw unformatted HTTP headers. + // + Status = HttpUtilitiesBuild ( + 0, + NULL, + 0, + NULL, + Message->HeaderCount, + AppendList, + &HttpHdrSize, + &HttpHdr + ); + FreePool (AppendList); + if (EFI_ERROR (Status) || HttpHdr == NULL) { + return NULL; + } + + // + // Calculate HTTP message length. + // + MsgSize = Message->BodyLength + HTTP_MAXIMUM_METHOD_LEN + AsciiStrLen (Url) + + AsciiStrLen (HTTP_VERSION_CRLF_STR) + HttpHdrSize; + Request = AllocateZeroPool (MsgSize); + if (Request == NULL) { + goto Exit; + } + + RequestPtr = Request; + // + // Construct header request + // + switch (Message->Data.Request->Method) { + case HttpMethodGet: + StrLength = sizeof (HTTP_GET_STR) - 1; + CopyMem (RequestPtr, HTTP_GET_STR, StrLength); + RequestPtr += StrLength; + break; + case HttpMethodHead: + StrLength = sizeof (HTTP_HEAD_STR) - 1; + CopyMem (RequestPtr, HTTP_HEAD_STR, StrLength); + RequestPtr += StrLength; + break; + default: + ASSERT (FALSE); + goto Exit; + } + + StrLength = AsciiStrLen (Url); + CopyMem (RequestPtr, Url, StrLength); + RequestPtr += StrLength; + + StrLength = sizeof (HTTP_VERSION_CRLF_STR) - 1; + CopyMem (RequestPtr, HTTP_VERSION_CRLF_STR, StrLength); + RequestPtr += StrLength; + + // + // Construct header + // + CopyMem (RequestPtr, HttpHdr, HttpHdrSize); + RequestPtr += HttpHdrSize; + + // + // Construct body + // + if (Message->Body != NULL) { + CopyMem (RequestPtr, Message->Body, Message->BodyLength); + RequestPtr += Message->BodyLength; + } + + // + // Done + // + *RequestPtr = 0; + Success = TRUE; + +Exit: + + if (!Success) { + if (Request != NULL) { + FreePool (Request); + } + + Request = NULL; + } + + if (HttpHdr != NULL) { + FreePool (HttpHdr); + } + + return (CHAR8*) Request; +} diff --git a/NetworkPkg/HttpDxe/HttpProto.h b/NetworkPkg/HttpDxe/HttpProto.h new file mode 100644 index 0000000000..ca4b7b6035 --- /dev/null +++ b/NetworkPkg/HttpDxe/HttpProto.h @@ -0,0 +1,457 @@ +/** @file + The header files of miscellaneous routines for HttpDxe driver. + +Copyright (c) 2015, Intel Corporation. All rights reserved.
+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. + +**/ + +#ifndef __EFI_HTTP_PROTO_H__ +#define __EFI_HTTP_PROTO_H__ + +#define DEF_BUF_LEN 2048 + +#define HTTP_SERVICE_SIGNATURE SIGNATURE_32('H', 't', 't', 'S') + +#define HTTP_SERVICE_FROM_PROTOCOL(a) \ + CR ( \ + (a), \ + HTTP_SERVICE, \ + ServiceBinding, \ + HTTP_SERVICE_SIGNATURE \ + ) + +// +// The state of HTTP protocol. It starts from UNCONFIGED. +// +#define HTTP_STATE_UNCONFIGED 0 +#define HTTP_STATE_HTTP_CONFIGED 1 +#define HTTP_STATE_TCP_CONFIGED 2 +#define HTTP_STATE_TCP_UNCONFIGED 3 +#define HTTP_STATE_TCP_CONNECTED 4 +#define HTTP_STATE_TCP_CLOSED 5 + +// +// TCP configured data. +// +#define HTTP_TOS_DEAULT 8 +#define HTTP_TTL_DEAULT 255 +#define HTTP_BUFFER_SIZE_DEAULT 65535 +#define HTTP_MAX_SYN_BACK_LOG 5 +#define HTTP_CONNECTION_TIMEOUT 60 +#define HTTP_DATA_RETRIES 12 +#define HTTP_FIN_TIMEOUT 2 +#define HTTP_KEEP_ALIVE_PROBES 6 +#define HTTP_KEEP_ALIVE_TIME 7200 +#define HTTP_KEEP_ALIVE_INTERVAL 30 + +typedef struct _HTTP_SERVICE { + UINT32 Signature; + EFI_SERVICE_BINDING_PROTOCOL ServiceBinding; + EFI_HANDLE ImageHandle; + EFI_HANDLE ControllerHandle; + LIST_ENTRY ChildrenList; + UINTN ChildrenNumber; + EFI_HANDLE TcpChildHandle; + INTN State; +} HTTP_SERVICE; + +typedef struct { + EFI_TCP4_IO_TOKEN TxToken; + EFI_TCP4_TRANSMIT_DATA TxData; + BOOLEAN IsTxDone; + EFI_TCP4_IO_TOKEN RxToken; + EFI_TCP4_RECEIVE_DATA RxData; + BOOLEAN IsRxDone; + UINTN BodyLen; + EFI_HTTP_METHOD Method; +} HTTP_TCP_TOKEN_WRAP; + +typedef struct _HTTP_PROTOCOL { + UINT32 Signature; + EFI_HTTP_PROTOCOL Http; + EFI_HANDLE Handle; + HTTP_SERVICE *Service; + LIST_ENTRY Link; // Link to all HTTP instance from the service. + BOOLEAN InDestroy; + INTN State; + + EFI_HANDLE TcpChildHandle; + EFI_TCP4_PROTOCOL *Tcp4; + EFI_TCP4_CONFIG_DATA Tcp4CfgData; + EFI_TCP4_OPTION Tcp4Option; + + EFI_TCP4_CONNECTION_TOKEN ConnToken; + BOOLEAN IsConnDone; + EFI_TCP4_CLOSE_TOKEN CloseToken; + BOOLEAN IsCloseDone; + + CHAR8 *RemoteHost; + UINT16 RemotePort; + EFI_IPv4_ADDRESS RemoteAddr; + // + // RxToken used for receiving HTTP header. + // + EFI_TCP4_IO_TOKEN RxToken; + EFI_TCP4_RECEIVE_DATA RxData; + BOOLEAN IsRxDone; + + CHAR8 *CacheBody; + CHAR8 *NextMsg; + UINTN CacheLen; + UINTN CacheOffset; + + // + // HTTP message-body parser. + // + VOID *MsgParser; + + EFI_HTTP_VERSION HttpVersion; + UINT32 TimeOutMillisec; + BOOLEAN LocalAddressIsIPv6; + + EFI_HTTPv4_ACCESS_POINT IPv4Node; + + NET_MAP TxTokens; + NET_MAP RxTokens; +} HTTP_PROTOCOL; + +typedef struct { + EFI_HTTP_TOKEN *HttpToken; + HTTP_PROTOCOL *HttpInstance; + HTTP_TCP_TOKEN_WRAP TcpWrap; +} HTTP_TOKEN_WRAP; + + +#define HTTP_PROTOCOL_SIGNATURE SIGNATURE_32('H', 't', 't', 'P') + +#define HTTP_INSTANCE_FROM_PROTOCOL(a) \ + CR ( \ + (a), \ + HTTP_PROTOCOL, \ + Http, \ + HTTP_PROTOCOL_SIGNATURE \ + ) + +/** + 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 + ); + +/** + Create events for the TCP4 connection token and TCP4 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 +HttpCreateTcp4ConnCloseEvent ( + IN HTTP_PROTOCOL *HttpInstance + ); + +/** + Close events in the TCP4 connection token and TCP4 close token. + + @param[in] HttpInstance Pointer to HTTP_PROTOCOL structure. + +**/ +VOID +HttpCloseTcp4ConnCloseEvent ( + IN HTTP_PROTOCOL *HttpInstance + ); + +/** + Create event for the TCP4 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 +HttpCreateTcp4TxEvent ( + IN HTTP_TOKEN_WRAP *Wrap + ); + +/** + Create event for the TCP4 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 +HttpCreateTcp4RxEventForHeader ( + IN HTTP_PROTOCOL *HttpInstance + ); + +/** + Create event for the TCP4 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 +HttpCreateTcp4RxEvent ( + IN HTTP_TOKEN_WRAP *Wrap + ); + +/** + Intiialize the HTTP_PROTOCOL structure to the unconfigured state. + + @param[in] HttpSb The HTTP service private instance. + @param[in, out] HttpInstance Pointer to HTTP_PROTOCOL structure. + + @retval EFI_SUCCESS HTTP_PROTOCOL structure is initialized successfully. + @retval Others Other error as indicated. + +**/ +EFI_STATUS +HttpInitProtocol ( + IN HTTP_SERVICE *HttpSb, + IN OUT HTTP_PROTOCOL *HttpInstance + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + Check existing TCP connection, if in error state, receover TCP4 connection. + + @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 + ); + +/** + Send the HTTP message through TCP4. + + @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 +HttpTransmitTcp4 ( + IN HTTP_PROTOCOL *HttpInstance, + IN HTTP_TOKEN_WRAP *Wrap, + IN UINT8 *TxString, + IN UINTN TxStringLen + ); + +/** + Translate the status code in HTTP message to EFI_HTTP_STATUS_CODE defined + in UEFI 2.5 specification. + + @param[in] StatusCode The status code value in HTTP message. + + @return Value defined in EFI_HTTP_STATUS_CODE . + +**/ +EFI_HTTP_STATUS_CODE +HttpMappingToStatusCode ( + IN UINTN StatusCode + ); + +/** + Check whether the user's token or event has already + been enqueue on HTTP TxToken or RxToken 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 + ); + +/** + Check whether the HTTP message associated with TxToken is already sent out. + + @param[in] Map The container of TxToken. + @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 + ); + +/** + Transmit the HTTP mssage by processing the associated HTTP token. + + @param[in] Map The container of TxToken. + @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 + ); + +/** + Receive the HTTP response by processing the associated HTTP token. + + @param[in] Map The container of RxToken. + @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 + ); + +/** + Generate HTTP request string. + + @param[in] HttpInstance Pointer to HTTP_PROTOCOL structure. + @param[in] Message Pointer to storage containing HTTP message data. + @param[in] Url The URL of a remote host. + + @return Pointer to the created HTTP request string. + @return NULL if any error occured. + +**/ +CHAR8 * +HttpGenRequestString ( + IN HTTP_PROTOCOL *HttpInstance, + IN EFI_HTTP_MESSAGE *Message, + IN CHAR8 *Url + ); + +/** + The work function of EfiHttpResponse(). + + @param[in] Wrap Pointer to HTTP token's wrap data. + + @retval EFI_SUCCESS Allocation succeeded. + @retval EFI_OUT_OF_RESOURCES Failed to complete the opration due to lack of resources. + @retval EFI_NOT_READY Can't find a corresponding TxToken. + +**/ +EFI_STATUS +HttpResponseWorker ( + IN HTTP_TOKEN_WRAP *Wrap + ); + +#endif diff --git a/NetworkPkg/HttpDxe/HttpUtilities.c b/NetworkPkg/HttpDxe/HttpUtilities.c new file mode 100644 index 0000000000..c2a99a40bf --- /dev/null +++ b/NetworkPkg/HttpDxe/HttpUtilities.c @@ -0,0 +1,622 @@ +/** @file + +Implementation of help functions to parse HTTP message header. + +Copyright (c) 2015, Intel Corporation. All rights reserved.
+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" + +/** + Get the next string, which is distinguished by specified seperator. + + @param[in] String Pointer to the string. + @param[in] Seperator Specified seperator used to distinguish where is the beginning + of next string. + + @return Pointer to the next string. + @return NULL if not find or String is NULL. + +**/ +CHAR8 * +AsciiStrGetNextToken ( + IN CONST CHAR8 *String, + IN CHAR8 Seperator + ) +{ + CONST CHAR8 *Token; + + Token = String; + while (TRUE) { + if (*Token == 0) { + return NULL; + } + if (*Token == Seperator) { + return (CHAR8 *) (Token + 1); + } + Token++; + } +} + +/** + Free existing HeaderFields. + + @param[in] HeaderFields Pointer to array of key/value header pairs waitting for free. + @param[in] FieldCount The number of header pairs in HeaderFields. + +**/ +VOID +FreeHeaderFields ( + IN EFI_HTTP_HEADER *HeaderFields, + IN UINTN FieldCount + ) +{ + UINTN Index; + + if (HeaderFields != NULL) { + for (Index = 0; Index < FieldCount; Index++) { + if(HeaderFields[Index].FieldName != NULL) { + FreePool (HeaderFields[Index].FieldName); + } + if(HeaderFields[Index].FieldValue != NULL) { + FreePool (HeaderFields[Index].FieldValue); + } + } + + FreePool (HeaderFields); + } +} + +/** + Find required header field in HeaderFields. + + @param[in] HeaderFields Pointer to array of key/value header pairs. + @param[in] FieldCount The number of header pairs. + @param[in] FieldName Pointer to header field's name. + + @return Pointer to the queried header field. + @return NULL if not find this required header field. + +**/ +EFI_HTTP_HEADER * +FindHttpHeader ( + IN EFI_HTTP_HEADER *HeaderFields, + IN UINTN FieldCount, + IN CHAR8 *FieldName + ) +{ + UINTN Index; + + for (Index = 0; Index < FieldCount; Index++) { + if (AsciiStrCmp (FieldName, HeaderFields[Index].FieldName) == 0) { + // + // Find the required header field. + // + return &HeaderFields[Index]; + } + } + return NULL; +} + +/** + Check whether header field called FieldName is in DeleteList. + + @param[in] DeleteList Pointer to array of key/value header pairs. + @param[in] DeleteCount The number of header pairs. + @param[in] FieldName Pointer to header field's name. + + @return TRUE if FieldName is not in DeleteList, that means this header field is valid. + @return FALSE if FieldName is in DeleteList, that means this header field is invalid. + +**/ +BOOLEAN +IsValidHttpHeader ( + IN CHAR8 *DeleteList[], + IN UINTN DeleteCount, + IN CHAR8 *FieldName + ) +{ + UINTN Index; + + for (Index = 0; Index < DeleteCount; Index++) { + if (AsciiStrCmp (FieldName, DeleteList[Index]) == 0) { + return FALSE; + } + } + + return TRUE; +} + +/** + Set FieldName and FieldValue into specified HttpHeader. + + @param[in] HttpHeader Specified HttpHeader. + @param[in] FieldName FieldName of this HttpHeader. + @param[in] FieldValue FieldValue of this HttpHeader. + + + @retval EFI_SUCCESS The FieldName and FieldValue are set into HttpHeader successfully. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources. + +**/ +EFI_STATUS +SetFieldNameAndValue ( + IN EFI_HTTP_HEADER *HttpHeader, + IN CHAR8 *FieldName, + IN CHAR8 *FieldValue + ) +{ + UINTN FieldNameSize; + UINTN FieldValueSize; + + if (HttpHeader->FieldName != NULL) { + FreePool (HttpHeader->FieldName); + } + if (HttpHeader->FieldValue != NULL) { + FreePool (HttpHeader->FieldValue); + } + + FieldNameSize = AsciiStrSize (FieldName); + HttpHeader->FieldName = AllocateZeroPool (FieldNameSize); + if (HttpHeader->FieldName == NULL) { + return EFI_OUT_OF_RESOURCES; + } + CopyMem (HttpHeader->FieldName, FieldName, FieldNameSize); + HttpHeader->FieldName[FieldNameSize - 1] = 0; + + FieldValueSize = AsciiStrSize (FieldValue); + HttpHeader->FieldValue = AllocateZeroPool (FieldValueSize); + if (HttpHeader->FieldValue == NULL) { + FreePool (HttpHeader->FieldName); + return EFI_OUT_OF_RESOURCES; + } + CopyMem (HttpHeader->FieldValue, FieldValue, FieldValueSize); + HttpHeader->FieldValue[FieldValueSize - 1] = 0; + + return EFI_SUCCESS; +} + +/** + Get one key/value header pair from the raw string. + + @param[in] String Pointer to the raw string. + @param[out] FieldName Pointer to header field's name. + @param[out] FieldValue Pointer to header field's value. + + @return Pointer to the next raw string. + @return NULL if no key/value header pair from this raw string. + +**/ +CHAR8 * +GetFieldNameAndValue ( + IN CHAR8 *String, + OUT CHAR8 **FieldName, + OUT CHAR8 **FieldValue + ) +{ + CHAR8 *FieldNameStr; + CHAR8 *FieldValueStr; + CHAR8 *StrPtr; + + if (String == NULL || FieldName == NULL || FieldValue == NULL) { + return NULL; + } + + *FieldName = NULL; + *FieldValue = NULL; + FieldNameStr = NULL; + FieldValueStr = NULL; + StrPtr = NULL; + + // + // Each header field consists of a name followed by a colon (":") and the field value. + // + FieldNameStr = String; + FieldValueStr = AsciiStrGetNextToken (FieldNameStr, ':'); + if (FieldValueStr == NULL) { + return NULL; + } + + *(FieldValueStr - 1) = 0; /// Replace ':' with 0 + + // + // The field value MAY be preceded by any amount of LWS, though a single SP is preferred. + // + while (TRUE) { + if(*FieldValueStr == ' ' || *FieldValueStr == '\t') { + FieldValueStr ++; + } else if (*FieldValueStr == '\r' && *(FieldValueStr + 1) == '\n' && + (*(FieldValueStr + 2) == ' ' || *(FieldValueStr + 2) == '\t')) { + FieldValueStr = FieldValueStr + 3; + } else { + break; + } + } + + // + // Header fields can be extended over multiple lines by preceding each extra + // line with at least one SP or HT. + // + StrPtr = FieldValueStr; + do { + StrPtr = AsciiStrGetNextToken (StrPtr, '\r'); + if (StrPtr == NULL || *StrPtr != '\n') { + return NULL; + } + + StrPtr++; + } while (*StrPtr == ' ' || *StrPtr == '\t'); + + // + // Replace '\r' with 0. + // + *(StrPtr - 2) = 0; + + // + // Get FieldName and FieldValue. + // + *FieldName = FieldNameStr; + *FieldValue = FieldValueStr; + + return StrPtr; +} + +/** + This function is used to manage the headers portion of an HTTP message by providing + the ability to add, remove, or replace HTTP headers. + + @param[in] SeedMessageSize Size in bytes of the initial HTTP header. This can be zero. + @param[in] SeedMessage Initial raw unformatted HTTP header to be used as a base for + building a new unformatted HTTP header. If NULL, SeedMessageSize + is ignored. The buffer containing this message will be allocated + and released by the caller. + @param[in] DeleteCount Number of null-terminated HTTP header field names in DeleteList. + @param[in] DeleteList List of null-terminated HTTP header field names to remove from SeedMessage. + Only the field names are in this list because the field values are irrelevant + to this operation. If NULL, DeleteCount is ignored. The buffer containing the + list will be allocated and released by the caller. + @param[in] AppendCount Number of header fields in AppendList. + @param[in] AppendList List of HTTP headers to populate NewMessage with. If SeedMessage is not NULL, + AppendList will be appended to the existing list from SeedMessage in NewMessage. + @param[out] NewMessageSize Pointer to the size in bytes of the new unformatted HTTP header in NewMessage. + @param[out] NewMessage Pointer to a new unformatted HTTP header. The storage for this NewMessage is + allocated by the driver publishing this protocol, and must be freed by the caller. + + @retval EFI_SUCCESS Add, remove, and replace operations succeeded. + @retval EFI_OUT_OF_RESOURCES Could not allocate memory for NewMessage. + +**/ +EFI_STATUS +HttpUtilitiesBuild( + IN UINTN SeedMessageSize, + IN VOID *SeedMessage, OPTIONAL + IN UINTN DeleteCount, + IN CHAR8 *DeleteList[], OPTIONAL + IN UINTN AppendCount, + IN EFI_HTTP_HEADER *AppendList[], OPTIONAL + OUT UINTN *NewMessageSize, + OUT VOID **NewMessage + ) +{ + EFI_STATUS Status; + EFI_HTTP_HEADER *SeedHeaderFields; + UINTN SeedFieldCount; + UINTN Index; + EFI_HTTP_HEADER *TempHeaderFields; + UINTN TempFieldCount; + EFI_HTTP_HEADER *NewHeaderFields; + UINTN NewFieldCount; + EFI_HTTP_HEADER *HttpHeader; + UINTN StrLength; + UINT8 *NewMessagePtr; + + SeedHeaderFields = NULL; + SeedFieldCount = 0; + TempHeaderFields = NULL; + TempFieldCount = 0; + NewHeaderFields = NULL; + NewFieldCount = 0; + + HttpHeader = NULL; + StrLength = 0; + NewMessagePtr = NULL; + *NewMessageSize = 0; + Status = EFI_SUCCESS; + + if (SeedMessage != NULL) { + Status = HttpUtilitiesParse ( + SeedMessage, + SeedMessageSize, + &SeedHeaderFields, + &SeedFieldCount + ); + if (EFI_ERROR (Status)){ + goto ON_EXIT; + } + } + + // + // Handle DeleteList + // + if(SeedFieldCount != 0 && DeleteCount != 0) { + TempHeaderFields = AllocateZeroPool (SeedFieldCount * sizeof(EFI_HTTP_HEADER)); + if (TempHeaderFields == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + for (Index = 0, TempFieldCount = 0; Index < SeedFieldCount; Index++) { + // + // Check whether each SeedHeaderFields member is in DeleteList + // + if (IsValidHttpHeader(DeleteList, DeleteCount, SeedHeaderFields[Index].FieldName)) { + Status = SetFieldNameAndValue( + &TempHeaderFields[TempFieldCount], + SeedHeaderFields[Index].FieldName, + SeedHeaderFields[Index].FieldValue + ); + if (EFI_ERROR (Status)){ + goto ON_EXIT; + } + TempFieldCount++; + } + } + } else { + TempHeaderFields = SeedHeaderFields; + TempFieldCount = SeedFieldCount; + } + + // + // Handle AppendList + // + NewHeaderFields = AllocateZeroPool ((TempFieldCount + AppendCount) * sizeof(EFI_HTTP_HEADER)); + if (NewHeaderFields == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + for (Index = 0; Index < TempFieldCount; Index++) { + Status = SetFieldNameAndValue( + &NewHeaderFields[Index], + TempHeaderFields[Index].FieldName, + TempHeaderFields[Index].FieldValue + ); + if (EFI_ERROR (Status)){ + goto ON_EXIT; + } + } + + NewFieldCount = TempFieldCount; + + for (Index = 0; Index < AppendCount; Index++) { + HttpHeader = FindHttpHeader(NewHeaderFields, NewFieldCount, AppendList[Index]->FieldName); + if(HttpHeader != NULL) { + Status = SetFieldNameAndValue( + HttpHeader, + AppendList[Index]->FieldName, + AppendList[Index]->FieldValue + ); + if (EFI_ERROR (Status)){ + goto ON_EXIT; + } + } else { + Status = SetFieldNameAndValue + (&NewHeaderFields[NewFieldCount], + AppendList[Index]->FieldName, + AppendList[Index]->FieldValue + ); + if (EFI_ERROR (Status)){ + goto ON_EXIT; + } + NewFieldCount++; + } + } + + // + // Calculate NewMessageSize, then build NewMessage + // + for (Index = 0; Index < NewFieldCount; Index++) { + HttpHeader = &NewHeaderFields[Index]; + + StrLength = AsciiStrLen (HttpHeader->FieldName); + *NewMessageSize += StrLength; + + StrLength = sizeof(": ") - 1; + *NewMessageSize += StrLength; + + StrLength = AsciiStrLen (HttpHeader->FieldValue); + *NewMessageSize += StrLength; + + StrLength = sizeof(HTTP_CRLF_STR) - 1; + *NewMessageSize += StrLength; + } + StrLength = sizeof(HTTP_CRLF_STR) - 1; + *NewMessageSize += StrLength; + // + // Final 0 for end flag. + // + *NewMessageSize += 1; + + *NewMessage = AllocateZeroPool (*NewMessageSize); + if (*NewMessage == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + NewMessagePtr = (UINT8 *)(*NewMessage); + + for (Index = 0; Index < NewFieldCount; Index++) { + HttpHeader = &NewHeaderFields[Index]; + + StrLength = AsciiStrLen (HttpHeader->FieldName); + CopyMem (NewMessagePtr, HttpHeader->FieldName, StrLength); + NewMessagePtr += StrLength; + + StrLength = sizeof(": ") - 1; + CopyMem (NewMessagePtr, ": ", StrLength); + NewMessagePtr += StrLength; + + StrLength = AsciiStrLen (HttpHeader->FieldValue); + CopyMem (NewMessagePtr, HttpHeader->FieldValue, StrLength); + NewMessagePtr += StrLength; + + StrLength = sizeof(HTTP_CRLF_STR) - 1; + CopyMem (NewMessagePtr, HTTP_CRLF_STR, StrLength); + NewMessagePtr += StrLength; + } + StrLength = sizeof(HTTP_CRLF_STR) - 1; + CopyMem (NewMessagePtr, HTTP_CRLF_STR, StrLength); + NewMessagePtr += StrLength; + + *NewMessagePtr = 0; + + ASSERT (*NewMessageSize == (UINTN) NewMessagePtr - (UINTN) (*NewMessage) + 1); + + // + // Free allocated buffer + // +ON_EXIT: + if(SeedHeaderFields != NULL) { + FreeHeaderFields(SeedHeaderFields, SeedFieldCount); + } + + if(TempHeaderFields != NULL) { + FreeHeaderFields(TempHeaderFields, TempFieldCount); + } + + if(NewHeaderFields != NULL) { + FreeHeaderFields(NewHeaderFields, NewFieldCount); + } + + return Status; +} + +/** + This function is used to transform data stored in HttpMessage into a list of fields + paired with their corresponding values. + + @param[in] HttpMessage Contains raw unformatted HTTP header string. The buffer for this string will + be allocated and released by the caller. + @param[in] HttpMessageSize Size in bytes of raw unformatted HTTP header. + @param[out] HeaderFields Array of key/value header pairs. The storage for all header pairs is allocated + by the driver publishing this protocol, and must be freed by the caller. + @param[out] FieldCount Number of headers in HeaderFields. + + @retval EFI_SUCCESS Parse HTTP header into array of key/value pairs succeeded. + @retval EFI_OUT_OF_RESOURCES Could not allocate memory for NewMessage. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + HttpMessage is NULL. + HeaderFields is NULL. + FieldCount is NULL. + +**/ +EFI_STATUS +HttpUtilitiesParse( + IN CHAR8 *HttpMessage, + IN UINTN HttpMessageSize, + OUT EFI_HTTP_HEADER **HeaderFields, + OUT UINTN *FieldCount + ) +{ + EFI_STATUS Status; + CHAR8 *TempHttpMessage; + CHAR8 *Token; + CHAR8 *NextToken; + CHAR8 *FieldName; + CHAR8 *FieldValue; + UINTN Index; + + if (HttpMessage == NULL || HeaderFields == NULL || FieldCount == NULL) { + return EFI_INVALID_PARAMETER; + } + + Status = EFI_SUCCESS; + TempHttpMessage = NULL; + *FieldCount = 0; + Token = NULL; + NextToken = NULL; + FieldName = NULL; + FieldValue = NULL; + Index = 0; + + TempHttpMessage = AllocateZeroPool (HttpMessageSize); + if (TempHttpMessage == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + CopyMem (TempHttpMessage, HttpMessage, HttpMessageSize); + + // + // Get header number + // + Token = TempHttpMessage; + while (TRUE) { + FieldName = NULL; + FieldValue = NULL; + NextToken = GetFieldNameAndValue (Token, &FieldName, &FieldValue); + Token = NextToken; + if (FieldName == NULL || FieldValue == NULL) { + break; + } + + (*FieldCount)++; + } + + if(*FieldCount == 0) { + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + + // + // Allocate buffer for header + // + *HeaderFields = AllocateZeroPool ((*FieldCount) * sizeof(EFI_HTTP_HEADER)); + if (*HeaderFields == NULL) { + *FieldCount = 0; + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + CopyMem (TempHttpMessage, HttpMessage, HttpMessageSize); + + // + // Set Field and Value to each header + // + Token = TempHttpMessage; + while (Index < *FieldCount) { + FieldName = NULL; + FieldValue = NULL; + NextToken = GetFieldNameAndValue (Token, &FieldName, &FieldValue); + Token = NextToken; + if (FieldName == NULL || FieldValue == NULL) { + break; + } + + Status = SetFieldNameAndValue(&(*HeaderFields)[Index], FieldName, FieldValue); + if(EFI_ERROR(Status)){ + *FieldCount = 0; + FreeHeaderFields (*HeaderFields, Index); + goto ON_EXIT; + } + + Index++; + } + + // + // Free allocated buffer + // +ON_EXIT: + if (TempHttpMessage != NULL) { + FreePool(TempHttpMessage); + } + + return Status; +} diff --git a/NetworkPkg/HttpDxe/HttpUtilities.h b/NetworkPkg/HttpDxe/HttpUtilities.h new file mode 100644 index 0000000000..bd4ef0b110 --- /dev/null +++ b/NetworkPkg/HttpDxe/HttpUtilities.h @@ -0,0 +1,82 @@ +/** @file + The header files of HTTP helper functions for HttpDxe driver. + +Copyright (c) 2015, Intel Corporation. All rights reserved.
+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. + +**/ + +#ifndef __EFI_HTTP_UTILITIES_H__ +#define __EFI_HTTP_UTILITIES_H__ + +/** + This function is used to manage the headers portion of an HTTP message by providing + the ability to add, remove, or replace HTTP headers. + + @param[in] SeedMessageSize Size in bytes of the initial HTTP header. This can be zero. + @param[in] SeedMessage Initial raw unformatted HTTP header to be used as a base for + building a new unformatted HTTP header. If NULL, SeedMessageSize + is ignored. The buffer containing this message will be allocated + and released by the caller. + @param[in] DeleteCount Number of null-terminated HTTP header field names in DeleteList. + @param[in] DeleteList List of null-terminated HTTP header field names to remove from SeedMessage. + Only the field names are in this list because the field values are irrelevant + to this operation. If NULL, DeleteCount is ignored. The buffer containing the + list will be allocated and released by the caller. + @param[in] AppendCount Number of header fields in AppendList. + @param[in] AppendList List of HTTP headers to populate NewMessage with. If SeedMessage is not NULL, + AppendList will be appended to the existing list from SeedMessage in NewMessage. + @param[out] NewMessageSize Pointer to the size in bytes of the new unformatted HTTP header in NewMessage. + @param[out] NewMessage Pointer to a new unformatted HTTP header. The storage for this NewMessage is + allocated by the driver publishing this protocol, and must be freed by the caller. + + @retval EFI_SUCCESS Add, remove, and replace operations succeeded. + @retval EFI_OUT_OF_RESOURCES Could not allocate memory for NewMessage. + +**/ +EFI_STATUS +HttpUtilitiesBuild( + IN UINTN SeedMessageSize, + IN VOID *SeedMessage, OPTIONAL + IN UINTN DeleteCount, + IN CHAR8 *DeleteList[], OPTIONAL + IN UINTN AppendCount, + IN EFI_HTTP_HEADER *AppendList[], OPTIONAL + OUT UINTN *NewMessageSize, + OUT VOID **NewMessage + ); + +/** + This function is used to transform data stored in HttpMessage into a list of fields + paired with their corresponding values. + + @param[in] HttpMessage Contains raw unformatted HTTP header string. The buffer for this string will + be allocated and released by the caller. + @param[in] HttpMessageSize Size in bytes of raw unformatted HTTP header. + @param[out] HeaderFields Array of key/value header pairs. The storage for all header pairs is allocated + by the driver publishing this protocol, and must be freed by the caller. + @param[out] FieldCount Number of headers in HeaderFields. + + @retval EFI_SUCCESS Parse HTTP header into array of key/value pairs succeeded. + @retval EFI_OUT_OF_RESOURCES Could not allocate memory for NewMessage. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + HttpMessage is NULL. + HeaderFields is NULL. + FieldCount is NULL. + +**/ +EFI_STATUS +HttpUtilitiesParse( + IN CHAR8 *HttpMessage, + IN UINTN HttpMessageSize, + OUT EFI_HTTP_HEADER **HeaderFields, + OUT UINTN *FieldCount + ); + +#endif -- 2.39.2