public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
From: "Abner Chang" <abner.chang@hpe.com>
To: devel@edk2.groups.io
Cc: Maciej Rabeda <maciej.rabeda@linux.intel.com>,
	Jiaxin Wu <jiaxin.wu@intel.com>, Siyuan Fu <siyuan.fu@intel.com>,
	Nickle Wang <nickle.wang@hpe.com>
Subject: [DxeHttpIoLib PATCH V2 1/3] NetworkPkg/Library: Implementation of Http IO Helper Library
Date: Tue, 20 Oct 2020 10:38:46 +0800	[thread overview]
Message-ID: <20201020023848.3015-2-abner.chang@hpe.com> (raw)
In-Reply-To: <20201020023848.3015-1-abner.chang@hpe.com>

Add HTTP IO helper library which could be sued by HTTP applications
such as HTTP Boot, Redfish HTTP REST EX driver instance and etc.

Signed-off-by: Abner Chang <abner.chang@hpe.com>

Cc: Maciej Rabeda <maciej.rabeda@linux.intel.com>
Cc: Jiaxin Wu <jiaxin.wu@intel.com>
Cc: Siyuan Fu <siyuan.fu@intel.com>
Cc: Nickle Wang <nickle.wang@hpe.com>
---
 NetworkPkg/Include/Library/HttpIoLib.h        | 328 +++++++
 .../Library/DxeHttpIoLib/DxeHttpIoLib.c       | 809 ++++++++++++++++++
 .../Library/DxeHttpIoLib/DxeHttpIoLib.inf     |  43 +
 .../Library/DxeHttpIoLib/DxeHttpIoLib.uni     |  13 +
 4 files changed, 1193 insertions(+)
 create mode 100644 NetworkPkg/Include/Library/HttpIoLib.h
 create mode 100644 NetworkPkg/Library/DxeHttpIoLib/DxeHttpIoLib.c
 create mode 100644 NetworkPkg/Library/DxeHttpIoLib/DxeHttpIoLib.inf
 create mode 100644 NetworkPkg/Library/DxeHttpIoLib/DxeHttpIoLib.uni

diff --git a/NetworkPkg/Include/Library/HttpIoLib.h b/NetworkPkg/Include/Library/HttpIoLib.h
new file mode 100644
index 0000000000..8f3804ca42
--- /dev/null
+++ b/NetworkPkg/Include/Library/HttpIoLib.h
@@ -0,0 +1,328 @@
+/** @file
+  HttpIoLib.h.
+
+(C) Copyright 2020 Hewlett-Packard Development Company, L.P.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef HTTP_IO_LIB_H_
+#define HTTP_IO_LIB_H_
+
+#include <IndustryStandard/Http11.h>
+
+#include <Library/DpcLib.h>
+#include <Library/HttpLib.h>
+#include <Library/NetLib.h>
+
+#define HTTP_IO_MAX_SEND_PAYLOAD                    1024
+#define HTTP_IO_CHUNK_SIZE_STRING_LEN               50
+#define HTTP_IO_CHUNKED_TRANSFER_CODING_DATA_LENGTH 256
+
+///
+/// HTTP_IO_CALLBACK_EVENT
+///
+typedef enum {
+  HttpIoRequest,
+  HttpIoResponse
+} HTTP_IO_CALLBACK_EVENT;
+
+/**
+  HttpIo Callback function which will be invoked when specified HTTP_IO_CALLBACK_EVENT happened.
+
+  @param[in]    EventType      Indicate the Event type that occurs in the current callback.
+  @param[in]    Message        HTTP message which will be send to, or just received from HTTP server.
+  @param[in]    Context        The Callback Context pointer.
+
+  @retval EFI_SUCCESS          Tells the HttpIo to continue the HTTP process.
+  @retval Others               Tells the HttpIo to abort the current HTTP process.
+**/
+typedef
+EFI_STATUS
+(EFIAPI * HTTP_IO_CALLBACK) (
+  IN  HTTP_IO_CALLBACK_EVENT    EventType,
+  IN  EFI_HTTP_MESSAGE          *Message,
+  IN  VOID                      *Context
+  );
+
+///
+/// A wrapper structure to hold the received HTTP response data.
+///
+typedef struct {
+  EFI_HTTP_RESPONSE_DATA      Response;
+  UINTN                       HeaderCount;
+  EFI_HTTP_HEADER             *Headers;
+  UINTN                       BodyLength;
+  CHAR8                       *Body;
+  EFI_STATUS                  Status;
+} HTTP_IO_RESPONSE_DATA;
+
+///
+/// HTTP_IO configuration data for IPv4
+///
+typedef struct {
+  EFI_HTTP_VERSION          HttpVersion;
+  UINT32                    RequestTimeOut;  ///< In milliseconds.
+  UINT32                    ResponseTimeOut; ///< In milliseconds.
+  BOOLEAN                   UseDefaultAddress;
+  EFI_IPv4_ADDRESS          LocalIp;
+  EFI_IPv4_ADDRESS          SubnetMask;
+  UINT16                    LocalPort;
+} HTTP4_IO_CONFIG_DATA;
+
+///
+/// HTTP_IO configuration data for IPv6
+///
+typedef struct {
+  EFI_HTTP_VERSION          HttpVersion;
+  UINT32                    RequestTimeOut;  ///< In milliseconds.
+  BOOLEAN                   UseDefaultAddress;
+  EFI_IPv6_ADDRESS          LocalIp;
+  UINT16                    LocalPort;
+} HTTP6_IO_CONFIG_DATA;
+
+///
+/// HTTP_IO configuration
+///
+typedef union {
+  HTTP4_IO_CONFIG_DATA       Config4;
+  HTTP6_IO_CONFIG_DATA       Config6;
+} HTTP_IO_CONFIG_DATA;
+
+///
+/// HTTP_IO wrapper of the EFI HTTP service.
+///
+typedef struct {
+  UINT8                     IpVersion;
+  EFI_HANDLE                Image;
+  EFI_HANDLE                Controller;
+  EFI_HANDLE                Handle;
+
+  EFI_HTTP_PROTOCOL         *Http;
+
+  HTTP_IO_CALLBACK          Callback;
+  VOID                      *Context;
+
+  EFI_HTTP_TOKEN            ReqToken;
+  EFI_HTTP_MESSAGE          ReqMessage;
+  EFI_HTTP_TOKEN            RspToken;
+  EFI_HTTP_MESSAGE          RspMessage;
+
+  BOOLEAN                   IsTxDone;
+  BOOLEAN                   IsRxDone;
+
+  EFI_EVENT                 TimeoutEvent;
+  UINT32                    Timeout;
+} HTTP_IO;
+
+///
+/// Process code of HTTP chunk transfer.
+///
+typedef enum  {
+  HttpIoSendChunkNone = 0,
+  HttpIoSendChunkHeaderZeroContent,
+  HttpIoSendChunkContent,
+  HttpIoSendChunkEndChunk,
+  HttpIoSendChunkFinish
+} HTTP_IO_SEND_CHUNK_PROCESS;
+
+///
+/// Process code of HTTP non chunk transfer.
+///
+typedef enum  {
+  HttpIoSendNonChunkNone = 0,
+  HttpIoSendNonChunkHeaderZeroContent,
+  HttpIoSendNonChunkContent,
+  HttpIoSendNonChunkFinish
+} HTTP_IO_SEND_NON_CHUNK_PROCESS;
+
+///
+/// Chunk links for HTTP chunked transfer coding.
+///
+typedef struct {
+  LIST_ENTRY  NextChunk;
+  UINTN       Length;
+  CHAR8       *Data;
+} HTTP_IO_CHUNKS;
+
+/**
+  Notify the callback function when an event is triggered.
+
+  @param[in]  Context         The opaque parameter to the function.
+
+**/
+VOID
+EFIAPI
+HttpIoNotifyDpc (
+  IN VOID                *Context
+  );
+
+/**
+  Request HttpIoNotifyDpc as a DPC at TPL_CALLBACK.
+
+  @param[in]  Event                 The event signaled.
+  @param[in]  Context               The opaque parameter to the function.
+
+**/
+VOID
+EFIAPI
+HttpIoNotify (
+  IN EFI_EVENT              Event,
+  IN VOID                   *Context
+  );
+
+/**
+  Destroy the HTTP_IO and release the resources.
+
+  @param[in]  HttpIo          The HTTP_IO which wraps the HTTP service to be destroyed.
+
+**/
+VOID
+HttpIoDestroyIo (
+  IN HTTP_IO                *HttpIo
+  );
+
+/**
+  Create a HTTP_IO to access the HTTP service. It will create and configure
+  a HTTP child handle.
+
+  @param[in]  Image          The handle of the driver image.
+  @param[in]  Controller     The handle of the controller.
+  @param[in]  IpVersion      IP_VERSION_4 or IP_VERSION_6.
+  @param[in]  ConfigData     The HTTP_IO configuration data.
+  @param[in]  Callback       Callback function which will be invoked when specified
+                             HTTP_IO_CALLBACK_EVENT happened.
+  @param[in]  Context        The Context data which will be passed to the Callback function.
+  @param[out] HttpIo         The HTTP_IO.
+
+  @retval EFI_SUCCESS            The HTTP_IO is created and configured.
+  @retval EFI_INVALID_PARAMETER  One or more parameters are invalid.
+  @retval EFI_UNSUPPORTED        One or more of the control options are not
+                                 supported in the implementation.
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory.
+  @retval Others                 Failed to create the HTTP_IO or configure it.
+
+**/
+EFI_STATUS
+HttpIoCreateIo (
+  IN EFI_HANDLE             Image,
+  IN EFI_HANDLE             Controller,
+  IN UINT8                  IpVersion,
+  IN HTTP_IO_CONFIG_DATA    *ConfigData,
+  IN HTTP_IO_CALLBACK       Callback,
+  IN VOID                   *Context,
+  OUT HTTP_IO               *HttpIo
+  );
+
+/**
+  Synchronously send a HTTP REQUEST message to the server.
+
+  @param[in]   HttpIo           The HttpIo wrapping the HTTP service.
+  @param[in]   Request          A pointer to storage such data as URL and HTTP method.
+  @param[in]   HeaderCount      Number of HTTP header structures in Headers list.
+  @param[in]   Headers          Array containing list of HTTP headers.
+  @param[in]   BodyLength       Length in bytes of the HTTP body.
+  @param[in]   Body             Body associated with the HTTP request.
+
+  @retval EFI_SUCCESS            The HTTP request is transmitted.
+  @retval EFI_INVALID_PARAMETER  One or more parameters are invalid.
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory.
+  @retval EFI_DEVICE_ERROR       An unexpected network or system error occurred.
+  @retval Others                 Other errors as indicated.
+
+**/
+EFI_STATUS
+HttpIoSendRequest (
+  IN  HTTP_IO                *HttpIo,
+  IN  EFI_HTTP_REQUEST_DATA  *Request,      OPTIONAL
+  IN  UINTN                  HeaderCount,
+  IN  EFI_HTTP_HEADER        *Headers,      OPTIONAL
+  IN  UINTN                  BodyLength,
+  IN  VOID                   *Body          OPTIONAL
+  );
+
+/**
+  Synchronously receive a HTTP RESPONSE message from the server.
+
+  @param[in]   HttpIo           The HttpIo wrapping the HTTP service.
+  @param[in]   RecvMsgHeader    TRUE to receive a new HTTP response (from message header).
+                                FALSE to continue receive the previous response message.
+  @param[out]  ResponseData     Point to a wrapper of the received response data.
+
+  @retval EFI_SUCCESS            The HTTP response is received.
+  @retval EFI_INVALID_PARAMETER  One or more parameters are invalid.
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory.
+  @retval EFI_DEVICE_ERROR       An unexpected network or system error occurred.
+  @retval Others                 Other errors as indicated.
+
+**/
+EFI_STATUS
+HttpIoRecvResponse (
+  IN      HTTP_IO                  *HttpIo,
+  IN      BOOLEAN                  RecvMsgHeader,
+  OUT     HTTP_IO_RESPONSE_DATA    *ResponseData
+  );
+
+/**
+  Get the value of the content length if there is a "Content-Length" header.
+
+  @param[in]    HeaderCount        Number of HTTP header structures in Headers.
+  @param[in]    Headers            Array containing list of HTTP headers.
+  @param[out]   ContentLength      Pointer to save the value of the content length.
+
+  @retval EFI_SUCCESS              Successfully get the content length.
+  @retval EFI_NOT_FOUND            No "Content-Length" header in the Headers.
+
+**/
+EFI_STATUS
+HttpIoGetContentLength (
+  IN     UINTN                HeaderCount,
+  IN     EFI_HTTP_HEADER      *Headers,
+  OUT    UINTN                *ContentLength
+  );
+
+/**
+  Synchronously receive a HTTP RESPONSE message from the server.
+
+  @param[in]   HttpIo           The HttpIo wrapping the HTTP service.
+  @param[in]   HeaderCount      Number of headers in Headers.
+  @param[in]   Headers          Array containing list of HTTP headers.
+  @param[out]  ChunkListHead    A pointer to receivce list head of chunked data.
+                                Caller has to release memory of ChunkListHead
+                                and all list entries.
+  @param[out]  ContentLength    Total content length
+
+  @retval EFI_SUCCESS            The HTTP chunked transfer is received.
+  @retval EFI_NOT_FOUND          No chunked transfer coding header found.
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory.
+  @retval EFI_INVALID_PARAMETER  Improper parameters.
+  @retval Others                 Other errors as indicated.
+
+**/
+EFI_STATUS
+HttpIoGetChunkedTransferContent (
+  IN     HTTP_IO              *HttpIo,
+  IN     UINTN                HeaderCount,
+  IN     EFI_HTTP_HEADER      *Headers,
+  OUT    LIST_ENTRY           **ChunkListHead,
+  OUT    UINTN                *ContentLength
+  );
+
+/**
+  Send HTTP request in chunks.
+
+  @param[in]   HttpIo             The HttpIo wrapping the HTTP service.
+  @param[in]   SendChunkProcess   Pointer to current chunk process status.
+  @param[out]  RequestMessage     Request to send.
+
+  @retval EFI_SUCCESS             Successfully to send chunk data according to SendChunkProcess.
+  @retval Other                   Other errors.
+
+**/
+EFI_STATUS
+HttpIoSendChunkedTransfer (
+  IN  HTTP_IO                    *HttpIo,
+  IN  HTTP_IO_SEND_CHUNK_PROCESS *SendChunkProcess,
+  IN  EFI_HTTP_MESSAGE           *RequestMessage
+);
+#endif
diff --git a/NetworkPkg/Library/DxeHttpIoLib/DxeHttpIoLib.c b/NetworkPkg/Library/DxeHttpIoLib/DxeHttpIoLib.c
new file mode 100644
index 0000000000..fbce86ea5c
--- /dev/null
+++ b/NetworkPkg/Library/DxeHttpIoLib/DxeHttpIoLib.c
@@ -0,0 +1,809 @@
+/** @file
+  Http IO Helper Library.
+
+  (C) Copyright 2020 Hewlett-Packard Development Company, L.P.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Uefi.h>
+
+#include <Protocol/Http.h>
+
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/HttpIoLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/PrintLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+
+/**
+  Notify the callback function when an event is triggered.
+
+  @param[in]  Context         The opaque parameter to the function.
+
+**/
+VOID
+EFIAPI
+HttpIoNotifyDpc (
+  IN VOID                *Context
+  )
+{
+  *((BOOLEAN *) Context) = TRUE;
+}
+
+/**
+  Request HttpIoNotifyDpc as a DPC at TPL_CALLBACK.
+
+  @param[in]  Event                 The event signaled.
+  @param[in]  Context               The opaque parameter to the function.
+
+**/
+VOID
+EFIAPI
+HttpIoNotify (
+  IN EFI_EVENT              Event,
+  IN VOID                   *Context
+  )
+{
+  //
+  // Request HttpIoNotifyDpc as a DPC at TPL_CALLBACK
+  //
+  QueueDpc (TPL_CALLBACK, HttpIoNotifyDpc, Context);
+}
+
+/**
+  Destroy the HTTP_IO and release the resources.
+
+  @param[in]  HttpIo          The HTTP_IO which wraps the HTTP service to be destroyed.
+
+**/
+VOID
+HttpIoDestroyIo (
+  IN HTTP_IO                *HttpIo
+  )
+{
+  EFI_HTTP_PROTOCOL         *Http;
+  EFI_EVENT                 Event;
+
+  if (HttpIo == NULL) {
+    return;
+  }
+
+  Event = HttpIo->ReqToken.Event;
+  if (Event != NULL) {
+    gBS->CloseEvent (Event);
+  }
+
+  Event = HttpIo->RspToken.Event;
+  if (Event != NULL) {
+    gBS->CloseEvent (Event);
+  }
+
+  Event = HttpIo->TimeoutEvent;
+  if (Event != NULL) {
+    gBS->CloseEvent (Event);
+  }
+
+  Http = HttpIo->Http;
+  if (Http != NULL) {
+    Http->Configure (Http, NULL);
+    gBS->CloseProtocol (
+           HttpIo->Handle,
+           &gEfiHttpProtocolGuid,
+           HttpIo->Image,
+           HttpIo->Controller
+           );
+  }
+
+  NetLibDestroyServiceChild (
+    HttpIo->Controller,
+    HttpIo->Image,
+    &gEfiHttpServiceBindingProtocolGuid,
+    HttpIo->Handle
+    );
+}
+
+/**
+  Create a HTTP_IO to access the HTTP service. It will create and configure
+  a HTTP child handle.
+
+  @param[in]  Image          The handle of the driver image.
+  @param[in]  Controller     The handle of the controller.
+  @param[in]  IpVersion      IP_VERSION_4 or IP_VERSION_6.
+  @param[in]  ConfigData     The HTTP_IO configuration data ,
+                             NULL means not to configure the HTTP child.
+  @param[in]  Callback       Callback function which will be invoked when specified
+                             HTTP_IO_CALLBACK_EVENT happened.
+  @param[in]  Context        The Context data which will be passed to the Callback function.
+  @param[out] HttpIo         The HTTP_IO.
+
+  @retval EFI_SUCCESS            The HTTP_IO is created and configured.
+  @retval EFI_INVALID_PARAMETER  One or more parameters are invalid.
+  @retval EFI_UNSUPPORTED        One or more of the control options are not
+                                 supported in the implementation.
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory.
+  @retval Others                 Failed to create the HTTP_IO or configure it.
+
+**/
+EFI_STATUS
+HttpIoCreateIo (
+  IN EFI_HANDLE             Image,
+  IN EFI_HANDLE             Controller,
+  IN UINT8                  IpVersion,
+  IN HTTP_IO_CONFIG_DATA    *ConfigData, OPTIONAL
+  IN HTTP_IO_CALLBACK       Callback,
+  IN VOID                   *Context,
+  OUT HTTP_IO               *HttpIo
+  )
+{
+  EFI_STATUS                Status;
+  EFI_HTTP_CONFIG_DATA      HttpConfigData;
+  EFI_HTTPv4_ACCESS_POINT   Http4AccessPoint;
+  EFI_HTTPv6_ACCESS_POINT   Http6AccessPoint;
+  EFI_HTTP_PROTOCOL         *Http;
+  EFI_EVENT                 Event;
+
+  if ((Image == NULL) || (Controller == NULL) || (HttpIo == NULL)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (IpVersion != IP_VERSION_4 && IpVersion != IP_VERSION_6) {
+    return EFI_UNSUPPORTED;
+  }
+
+  ZeroMem (HttpIo, sizeof (HTTP_IO));
+  ZeroMem (&HttpConfigData, sizeof (EFI_HTTP_CONFIG_DATA));
+
+  //
+  // Create the HTTP child instance and get the HTTP protocol.
+  //
+  Status = NetLibCreateServiceChild (
+             Controller,
+             Image,
+             &gEfiHttpServiceBindingProtocolGuid,
+             &HttpIo->Handle
+             );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = gBS->OpenProtocol (
+                  HttpIo->Handle,
+                  &gEfiHttpProtocolGuid,
+                  (VOID **) &Http,
+                  Image,
+                  Controller,
+                  EFI_OPEN_PROTOCOL_BY_DRIVER
+                  );
+  if (EFI_ERROR (Status) || (Http == NULL)) {
+    goto ON_ERROR;
+  }
+
+  //
+  // Init the configuration data and configure the HTTP child.
+  //
+  HttpIo->Image       = Image;
+  HttpIo->Controller  = Controller;
+  HttpIo->IpVersion   = IpVersion;
+  HttpIo->Http        = Http;
+  HttpIo->Callback    = Callback;
+  HttpIo->Context     = Context;
+
+  if (ConfigData != NULL) {
+    if (HttpIo->IpVersion == IP_VERSION_4) {
+      HttpConfigData.LocalAddressIsIPv6  = FALSE;
+      HttpConfigData.HttpVersion         = ConfigData->Config4.HttpVersion;
+      HttpConfigData.TimeOutMillisec     = ConfigData->Config4.RequestTimeOut;
+
+      Http4AccessPoint.UseDefaultAddress = ConfigData->Config4.UseDefaultAddress;
+      Http4AccessPoint.LocalPort         = ConfigData->Config4.LocalPort;
+      IP4_COPY_ADDRESS (&Http4AccessPoint.LocalAddress, &ConfigData->Config4.LocalIp);
+      IP4_COPY_ADDRESS (&Http4AccessPoint.LocalSubnet, &ConfigData->Config4.SubnetMask);
+      HttpConfigData.AccessPoint.IPv4Node = &Http4AccessPoint;
+    } else {
+      HttpConfigData.LocalAddressIsIPv6 = TRUE;
+      HttpConfigData.HttpVersion         = ConfigData->Config6.HttpVersion;
+      HttpConfigData.TimeOutMillisec     = ConfigData->Config6.RequestTimeOut;
+
+      Http6AccessPoint.LocalPort         = ConfigData->Config6.LocalPort;
+      IP6_COPY_ADDRESS (&Http6AccessPoint.LocalAddress, &ConfigData->Config6.LocalIp);
+      HttpConfigData.AccessPoint.IPv6Node = &Http6AccessPoint;
+    }
+
+    Status = Http->Configure (Http, &HttpConfigData);
+    if (EFI_ERROR (Status)) {
+      goto ON_ERROR;
+    }
+  }
+
+  //
+  // Create events for variuos asynchronous operations.
+  //
+  Status = gBS->CreateEvent (
+                  EVT_NOTIFY_SIGNAL,
+                  TPL_NOTIFY,
+                  HttpIoNotify,
+                  &HttpIo->IsTxDone,
+                  &Event
+                  );
+  if (EFI_ERROR (Status)) {
+    goto ON_ERROR;
+  }
+  HttpIo->ReqToken.Event = Event;
+  HttpIo->ReqToken.Message = &HttpIo->ReqMessage;
+
+  Status = gBS->CreateEvent (
+                  EVT_NOTIFY_SIGNAL,
+                  TPL_NOTIFY,
+                  HttpIoNotify,
+                  &HttpIo->IsRxDone,
+                  &Event
+                  );
+  if (EFI_ERROR (Status)) {
+    goto ON_ERROR;
+  }
+  HttpIo->RspToken.Event = Event;
+  HttpIo->RspToken.Message = &HttpIo->RspMessage;
+
+  //
+  // Create TimeoutEvent for response
+  //
+  Status = gBS->CreateEvent (
+                  EVT_TIMER,
+                  TPL_CALLBACK,
+                  NULL,
+                  NULL,
+                  &Event
+                  );
+  if (EFI_ERROR (Status)) {
+    goto ON_ERROR;
+  }
+  HttpIo->TimeoutEvent = Event;
+  return EFI_SUCCESS;
+
+ON_ERROR:
+  HttpIoDestroyIo (HttpIo);
+
+  return Status;
+}
+
+/**
+  Synchronously send a HTTP REQUEST message to the server.
+
+  @param[in]   HttpIo           The HttpIo wrapping the HTTP service.
+  @param[in]   Request          A pointer to storage such data as URL and HTTP method.
+  @param[in]   HeaderCount      Number of HTTP header structures in Headers list.
+  @param[in]   Headers          Array containing list of HTTP headers.
+  @param[in]   BodyLength       Length in bytes of the HTTP body.
+  @param[in]   Body             Body associated with the HTTP request.
+
+  @retval EFI_SUCCESS            The HTTP request is trasmitted.
+  @retval EFI_INVALID_PARAMETER  One or more parameters are invalid.
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory.
+  @retval EFI_DEVICE_ERROR       An unexpected network or system error occurred.
+  @retval Others                 Other errors as indicated.
+
+**/
+EFI_STATUS
+HttpIoSendRequest (
+  IN  HTTP_IO                *HttpIo,
+  IN  EFI_HTTP_REQUEST_DATA  *Request,
+  IN  UINTN                  HeaderCount,
+  IN  EFI_HTTP_HEADER        *Headers,
+  IN  UINTN                  BodyLength,
+  IN  VOID                   *Body
+  )
+{
+  EFI_STATUS                 Status;
+  EFI_HTTP_PROTOCOL          *Http;
+
+  if (HttpIo == NULL || HttpIo->Http == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  HttpIo->ReqToken.Status  = EFI_NOT_READY;
+  HttpIo->ReqToken.Message->Data.Request = Request;
+  HttpIo->ReqToken.Message->HeaderCount  = HeaderCount;
+  HttpIo->ReqToken.Message->Headers      = Headers;
+  HttpIo->ReqToken.Message->BodyLength   = BodyLength;
+  HttpIo->ReqToken.Message->Body         = Body;
+
+  if (HttpIo->Callback != NULL) {
+    Status = HttpIo->Callback (
+               HttpIoRequest,
+               HttpIo->ReqToken.Message,
+               HttpIo->Context
+               );
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+  }
+
+  //
+  // Queue the request token to HTTP instances.
+  //
+  Http = HttpIo->Http;
+  HttpIo->IsTxDone = FALSE;
+  Status = Http->Request (
+                   Http,
+                   &HttpIo->ReqToken
+                   );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Poll the network until transmit finish.
+  //
+  while (!HttpIo->IsTxDone) {
+    Http->Poll (Http);
+  }
+
+  return HttpIo->ReqToken.Status;
+}
+
+/**
+  Synchronously receive a HTTP RESPONSE message from the server.
+
+  @param[in]   HttpIo           The HttpIo wrapping the HTTP service.
+  @param[in]   RecvMsgHeader    TRUE to receive a new HTTP response (from message header).
+                                FALSE to continue receive the previous response message.
+  @param[out]  ResponseData     Point to a wrapper of the received response data.
+
+  @retval EFI_SUCCESS            The HTTP response is received.
+  @retval EFI_INVALID_PARAMETER  One or more parameters are invalid.
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory.
+  @retval EFI_DEVICE_ERROR       An unexpected network or system error occurred.
+  @retval Others                 Other errors as indicated.
+
+**/
+EFI_STATUS
+HttpIoRecvResponse (
+  IN      HTTP_IO                  *HttpIo,
+  IN      BOOLEAN                  RecvMsgHeader,
+  OUT     HTTP_IO_RESPONSE_DATA    *ResponseData
+  )
+{
+  EFI_STATUS                 Status;
+  EFI_HTTP_PROTOCOL          *Http;
+
+  if (HttpIo == NULL || HttpIo->Http == NULL || ResponseData == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // Create new TimeoutEvent for response
+  //
+  if (HttpIo->TimeoutEvent != NULL) {
+    gBS->CloseEvent (HttpIo->TimeoutEvent);
+    HttpIo->TimeoutEvent = NULL;
+  }
+
+  Status = gBS->CreateEvent (
+                  EVT_TIMER,
+                  TPL_CALLBACK,
+                  NULL,
+                  NULL,
+                  &HttpIo->TimeoutEvent
+                  );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Start the timer, and wait Timeout seconds to receive the header packet.
+  //
+  Status = gBS->SetTimer (HttpIo->TimeoutEvent, TimerRelative, HttpIo->Timeout * TICKS_PER_MS);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Queue the response token to HTTP instances.
+  //
+  HttpIo->RspToken.Status  = EFI_NOT_READY;
+  if (RecvMsgHeader) {
+    HttpIo->RspToken.Message->Data.Response = &ResponseData->Response;
+  } else {
+    HttpIo->RspToken.Message->Data.Response = NULL;
+  }
+  HttpIo->RspToken.Message->HeaderCount   = 0;
+  HttpIo->RspToken.Message->Headers       = NULL;
+  HttpIo->RspToken.Message->BodyLength    = ResponseData->BodyLength;
+  HttpIo->RspToken.Message->Body          = ResponseData->Body;
+
+  Http = HttpIo->Http;
+  HttpIo->IsRxDone = FALSE;
+  Status = Http->Response (
+                   Http,
+                   &HttpIo->RspToken
+                   );
+
+  if (EFI_ERROR (Status)) {
+    gBS->SetTimer (HttpIo->TimeoutEvent, TimerCancel, 0);
+    return Status;
+  }
+
+  //
+  // Poll the network until receive finish.
+  //
+  while (!HttpIo->IsRxDone && ((HttpIo->TimeoutEvent == NULL) || EFI_ERROR (gBS->CheckEvent (HttpIo->TimeoutEvent)))) {
+    Http->Poll (Http);
+  }
+
+  gBS->SetTimer (HttpIo->TimeoutEvent, TimerCancel, 0);
+
+  if (!HttpIo->IsRxDone) {
+    //
+    // Timeout occurs, cancel the response token.
+    //
+    Http->Cancel (Http, &HttpIo->RspToken);
+
+    Status = EFI_TIMEOUT;
+
+    return Status;
+  } else {
+    HttpIo->IsRxDone = FALSE;
+  }
+
+  if ((HttpIo->Callback != NULL) &&
+      (HttpIo->RspToken.Status == EFI_SUCCESS || HttpIo->RspToken.Status == EFI_HTTP_ERROR)) {
+    Status = HttpIo->Callback (
+               HttpIoResponse,
+               HttpIo->RspToken.Message,
+               HttpIo->Context
+               );
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+  }
+
+  //
+  // Store the received data into the wrapper.
+  //
+  ResponseData->Status = HttpIo->RspToken.Status;
+  ResponseData->HeaderCount = HttpIo->RspToken.Message->HeaderCount;
+  ResponseData->Headers     = HttpIo->RspToken.Message->Headers;
+  ResponseData->BodyLength  = HttpIo->RspToken.Message->BodyLength;
+
+  return Status;
+}
+
+/**
+  Get the value of the content length if there is a "Content-Length" header.
+
+  @param[in]    HeaderCount        Number of HTTP header structures in Headers.
+  @param[in]    Headers            Array containing list of HTTP headers.
+  @param[out]   ContentLength      Pointer to save the value of the content length.
+
+  @retval EFI_SUCCESS              Successfully get the content length.
+  @retval EFI_NOT_FOUND            No "Content-Length" header in the Headers.
+
+**/
+EFI_STATUS
+HttpIoGetContentLength (
+  IN     UINTN                HeaderCount,
+  IN     EFI_HTTP_HEADER      *Headers,
+  OUT    UINTN                *ContentLength
+  )
+{
+  EFI_HTTP_HEADER       *Header;
+
+  Header = HttpFindHeader (HeaderCount, Headers, HTTP_HEADER_CONTENT_LENGTH);
+  if (Header == NULL) {
+    return EFI_NOT_FOUND;
+  }
+
+  return AsciiStrDecimalToUintnS (Header->FieldValue, (CHAR8 **) NULL, ContentLength);
+}
+/**
+  Send HTTP request in chunks.
+
+  @param[in]   HttpIo             The HttpIo wrapping the HTTP service.
+  @param[in]   SendChunkProcess   Pointer to current chunk process status.
+  @param[in]   RequestMessage     Request to send.
+
+  @retval EFI_SUCCESS             Successfully to send chunk data according to SendChunkProcess.
+  @retval Other                   Other errors.
+
+**/
+EFI_STATUS
+HttpIoSendChunkedTransfer (
+  IN  HTTP_IO                    *HttpIo,
+  IN  HTTP_IO_SEND_CHUNK_PROCESS *SendChunkProcess,
+  IN  EFI_HTTP_MESSAGE           *RequestMessage
+)
+{
+  EFI_STATUS Status;
+  EFI_HTTP_HEADER *NewHeaders;
+  EFI_HTTP_HEADER *ContentLengthHeader;
+  UINTN AddNewHeader;
+  UINTN HeaderCount;
+  CHAR8 *MessageBody;
+  UINTN MessageBodyLength;
+  CHAR8 ChunkLengthStr [HTTP_IO_CHUNK_SIZE_STRING_LEN];
+  EFI_HTTP_REQUEST_DATA *SentRequestData;
+
+  AddNewHeader = 0;
+  NewHeaders = NULL;
+  MessageBody = NULL;
+  ContentLengthHeader = NULL;
+  MessageBodyLength = 0;
+
+  switch (*SendChunkProcess) {
+  case HttpIoSendChunkHeaderZeroContent:
+      ContentLengthHeader = HttpFindHeader(RequestMessage->HeaderCount, RequestMessage->Headers, HTTP_HEADER_CONTENT_LENGTH);
+      if (ContentLengthHeader == NULL) {
+        AddNewHeader = 1;
+      }
+
+      NewHeaders = AllocateZeroPool((RequestMessage->HeaderCount + AddNewHeader) * sizeof(EFI_HTTP_HEADER));
+      CopyMem ((VOID*)NewHeaders, (VOID *)RequestMessage->Headers, RequestMessage->HeaderCount * sizeof (EFI_HTTP_HEADER));
+      if (AddNewHeader == 0) {
+        //
+        // Override content-length to Transfer-Encoding.
+        //
+        ContentLengthHeader = HttpFindHeader (RequestMessage->HeaderCount, NewHeaders, HTTP_HEADER_CONTENT_LENGTH);
+        ContentLengthHeader->FieldName = NULL;
+        ContentLengthHeader->FieldValue = NULL;
+      } else {
+        ContentLengthHeader = NewHeaders + RequestMessage->HeaderCount;
+      }
+      HttpSetFieldNameAndValue(ContentLengthHeader, HTTP_HEADER_TRANSFER_ENCODING, HTTP_HEADER_TRANSFER_ENCODING_CHUNKED);
+      HeaderCount = RequestMessage->HeaderCount + AddNewHeader;
+      MessageBodyLength = 0;
+      MessageBody = NULL;
+      SentRequestData = RequestMessage->Data.Request;
+      break;
+
+  case HttpIoSendChunkContent:
+      HeaderCount = 0;
+      NewHeaders = NULL;
+      SentRequestData = NULL;
+      if (RequestMessage->BodyLength > HTTP_IO_MAX_SEND_PAYLOAD) {
+        MessageBodyLength = HTTP_IO_MAX_SEND_PAYLOAD;
+      } else {
+        MessageBodyLength = RequestMessage->BodyLength;
+      }
+      AsciiSPrint (ChunkLengthStr, HTTP_IO_CHUNK_SIZE_STRING_LEN, "%x%c%c", MessageBodyLength, CHUNKED_TRNASFER_CODING_CR, CHUNKED_TRNASFER_CODING_LF);
+      MessageBody = AllocatePool (AsciiStrLen (ChunkLengthStr) + MessageBodyLength + 2);
+      if (MessageBody == NULL) {
+        DEBUG((DEBUG_ERROR, "Not enough memory for chunk transfer\n"));
+        return EFI_OUT_OF_RESOURCES;
+      }
+      CopyMem (MessageBody, ChunkLengthStr, AsciiStrLen (ChunkLengthStr));
+      CopyMem (MessageBody + AsciiStrLen (ChunkLengthStr), RequestMessage->Body, MessageBodyLength);
+      *(MessageBody + AsciiStrLen (ChunkLengthStr) + MessageBodyLength) = CHUNKED_TRNASFER_CODING_CR;
+      *(MessageBody + AsciiStrLen (ChunkLengthStr) + MessageBodyLength + 1) = CHUNKED_TRNASFER_CODING_LF;
+      RequestMessage->BodyLength -= MessageBodyLength;
+      RequestMessage->Body = (VOID *)((CHAR8 *)RequestMessage->Body + MessageBodyLength);
+      MessageBodyLength += (AsciiStrLen (ChunkLengthStr) + 2);
+      if (RequestMessage->BodyLength == 0) {
+        *SendChunkProcess = HttpIoSendChunkEndChunk;
+      }
+      break;
+
+  case HttpIoSendChunkEndChunk:
+      HeaderCount = 0;
+      NewHeaders = NULL;
+      SentRequestData = NULL;
+      AsciiSPrint (ChunkLengthStr, HTTP_IO_CHUNK_SIZE_STRING_LEN, "0%c%c%c%c",
+                      CHUNKED_TRNASFER_CODING_CR, CHUNKED_TRNASFER_CODING_LF,
+                      CHUNKED_TRNASFER_CODING_CR, CHUNKED_TRNASFER_CODING_LF
+                      );
+      MessageBody = AllocatePool (AsciiStrLen(ChunkLengthStr));
+      if (MessageBody == NULL) {
+        DEBUG((DEBUG_ERROR, "Not enough memory for the end chunk transfer\n"));
+        return EFI_OUT_OF_RESOURCES;
+      }
+      CopyMem (MessageBody, ChunkLengthStr, AsciiStrLen (ChunkLengthStr));
+      MessageBodyLength = AsciiStrLen(ChunkLengthStr);
+      *SendChunkProcess = HttpIoSendChunkFinish;
+      break;
+
+  default:
+      return EFI_INVALID_PARAMETER;
+  }
+
+  Status = HttpIoSendRequest(
+               HttpIo,
+               SentRequestData,
+               HeaderCount,
+               NewHeaders,
+               MessageBodyLength,
+               MessageBody
+               );
+  if (ContentLengthHeader != NULL) {
+    if (ContentLengthHeader->FieldName != NULL) {
+      FreePool(ContentLengthHeader->FieldName);
+    }
+    if (ContentLengthHeader->FieldValue != NULL) {
+      FreePool(ContentLengthHeader->FieldValue);
+    }
+    ContentLengthHeader = NULL;
+  }
+  if (NewHeaders != NULL) {
+    FreePool(NewHeaders);
+    NewHeaders = NULL;
+  }
+  if (MessageBody != NULL) {
+    FreePool(MessageBody);
+    MessageBody = NULL;
+  }
+  return Status;
+}
+
+/**
+  Synchronously receive a HTTP RESPONSE message from the server.
+
+  @param[in]   HttpIo           The HttpIo wrapping the HTTP service.
+  @param[in]   HeaderCount      Number of headers in Headers.
+  @param[in]   Headers          Array containing list of HTTP headers.
+  @param[out]  ChunkListHead    A pointer to receivce list head of chunked data.
+                                Caller has to release memory of ChunkListHead
+                                and all list entries.
+  @param[out]  ContentLength    Total content length
+
+  @retval EFI_SUCCESS            The HTTP chunked transfer is received.
+  @retval EFI_NOT_FOUND          No chunked transfer coding header found.
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory.
+  @retval EFI_INVALID_PARAMETER  Improper parameters.
+  @retval Others                 Other errors as indicated.
+
+**/
+EFI_STATUS
+HttpIoGetChunkedTransferContent (
+  IN     HTTP_IO              *HttpIo,
+  IN     UINTN                HeaderCount,
+  IN     EFI_HTTP_HEADER      *Headers,
+  OUT    LIST_ENTRY           **ChunkListHead,
+  OUT    UINTN                *ContentLength
+  )
+{
+  EFI_HTTP_HEADER       *Header;
+  CHAR8                 ChunkSizeAscii[256];
+  EFI_STATUS            Status;
+  UINTN                 Index;
+  HTTP_IO_RESPONSE_DATA ResponseData;
+  UINTN                 TotalLength;
+  LIST_ENTRY            *HttpChunks;
+  HTTP_IO_CHUNKS       *ThisChunk;
+  LIST_ENTRY            *ThisListEntry;
+
+  if (ChunkListHead == NULL || ContentLength == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  *ContentLength = 0;
+  Header = HttpFindHeader (HeaderCount, Headers, HTTP_HEADER_TRANSFER_ENCODING);
+  if (Header == NULL) {
+    return EFI_NOT_FOUND;
+  }
+  if (AsciiStrCmp (Header->FieldValue, HTTP_HEADER_TRANSFER_ENCODING_CHUNKED) == 0) {
+    //
+    // Loop to get all chunks.
+    //
+    TotalLength = 0;
+    HttpChunks = (LIST_ENTRY *)AllocateZeroPool (sizeof (LIST_ENTRY));
+    if (HttpChunks == NULL) {
+      Status = EFI_OUT_OF_RESOURCES;
+      goto DeleterChunks;
+    }
+    InitializeListHead (HttpChunks);
+    DEBUG ((DEBUG_INFO, "     Chunked transfer\n"));
+    while (TRUE) {
+      ZeroMem((VOID *)&ResponseData, sizeof(HTTP_IO_RESPONSE_DATA));
+      ResponseData.BodyLength = HTTP_IO_CHUNKED_TRANSFER_CODING_DATA_LENGTH;
+      ResponseData.Body = ChunkSizeAscii;
+      Status = HttpIoRecvResponse (
+                 HttpIo,
+                 FALSE,
+                 &ResponseData
+                 );
+      if (EFI_ERROR (Status)) {
+        return Status;
+      }
+      //
+      // Decoding Chunked Transfer Coding.
+      // Only decode chunk-size and last chunk.
+      //
+      DEBUG ((DEBUG_INFO, "     Chunk HTTP Response StatusCode - %d\n", ResponseData.Response.StatusCode));
+      //
+      // Break if this is last chunk.
+      //
+      if (ChunkSizeAscii [0] == CHUNKED_TRNASFER_CODING_LAST_CHUNK) {
+        Status = EFI_SUCCESS;
+        DEBUG ((DEBUG_INFO, "     Last chunk\n"));
+        ThisChunk = (HTTP_IO_CHUNKS *)AllocateZeroPool (sizeof (HTTP_IO_CHUNKS));
+        if (ThisChunk == NULL) {
+          Status = EFI_OUT_OF_RESOURCES;
+          goto DeleterChunks;
+        }
+        InitializeListHead (&ThisChunk->NextChunk);
+        ThisChunk->Length = ResponseData.BodyLength - 1 - 2; // Minus sizeof '0' and CRLF.
+        ThisChunk->Data = (CHAR8 *)AllocatePool (ThisChunk->Length);
+        if (ThisChunk->Data == NULL) {
+          FreePool ((UINT8 *)ThisChunk);
+          Status = EFI_OUT_OF_RESOURCES;
+          goto DeleterChunks;
+        }
+        CopyMem ((UINT8 *)ThisChunk->Data, (UINT8 *)ResponseData.Body + 1, ThisChunk->Length);
+        TotalLength += ThisChunk->Length;
+        InsertTailList (HttpChunks, &ThisChunk->NextChunk);
+        break;
+      }
+
+      //
+      // Get the chunk length
+      //
+      Index = 0;
+      while ((ChunkSizeAscii [Index] != CHUNKED_TRNASFER_CODING_EXTENSION_SAPERATOR) &&
+             (ChunkSizeAscii [Index] != (CHAR8)CHUNKED_TRNASFER_CODING_CR) &&
+             (Index != HTTP_IO_CHUNKED_TRANSFER_CODING_DATA_LENGTH)) {
+        Index ++;
+      };
+      if (Index == HTTP_IO_CHUNKED_TRANSFER_CODING_DATA_LENGTH) {
+        return EFI_NOT_FOUND;
+      }
+      ChunkSizeAscii[Index] = 0;
+      AsciiStrHexToUintnS (ChunkSizeAscii, NULL, ContentLength);
+      DEBUG ((DEBUG_INFO, "     Length of this chunk %d\n", *ContentLength));
+      //
+      // Receive the data;
+      //
+      ThisChunk = (HTTP_IO_CHUNKS *)AllocateZeroPool (sizeof (HTTP_IO_CHUNKS));
+      if (ThisChunk == NULL) {
+        Status = EFI_OUT_OF_RESOURCES;
+        goto DeleterChunks;
+      }
+      ResponseData.BodyLength = *ContentLength;
+      ResponseData.Body = (CHAR8 *)AllocatePool (*ContentLength);
+      if (ResponseData.Body == NULL) {
+        FreePool (ThisChunk);
+        Status = EFI_OUT_OF_RESOURCES;
+        goto DeleterChunks;
+      }
+      InitializeListHead (&ThisChunk->NextChunk);
+      ThisChunk->Length = *ContentLength;
+      ThisChunk->Data = ResponseData.Body;
+      InsertTailList (HttpChunks, &ThisChunk->NextChunk);
+      Status = HttpIoRecvResponse (
+                 HttpIo,
+                 FALSE,
+                 &ResponseData
+                 );
+      if (EFI_ERROR (Status)) {
+        goto DeleterChunks;
+      }
+      //
+      // Read CRLF
+      //
+      ZeroMem((VOID *)&ResponseData, sizeof(HTTP_IO_RESPONSE_DATA));
+      ResponseData.BodyLength = 2;
+      ResponseData.Body = ChunkSizeAscii;
+      Status = HttpIoRecvResponse (
+                 HttpIo,
+                 FALSE,
+                 &ResponseData
+                 );
+      if (EFI_ERROR (Status)) {
+        goto DeleterChunks;
+      }
+
+      TotalLength += *ContentLength;
+    };
+    *ContentLength = TotalLength;
+    *ChunkListHead = HttpChunks;
+    DEBUG ((DEBUG_INFO, "     Total of lengh of chunks :%d\n", TotalLength));
+    return EFI_SUCCESS;
+  } else {
+    return EFI_NOT_FOUND;
+  }
+DeleterChunks:;
+  while (!IsListEmpty (HttpChunks)) {
+    ThisListEntry = GetFirstNode (HttpChunks);
+    RemoveEntryList (ThisListEntry);
+  };
+  return Status;
+}
diff --git a/NetworkPkg/Library/DxeHttpIoLib/DxeHttpIoLib.inf b/NetworkPkg/Library/DxeHttpIoLib/DxeHttpIoLib.inf
new file mode 100644
index 0000000000..a02b409547
--- /dev/null
+++ b/NetworkPkg/Library/DxeHttpIoLib/DxeHttpIoLib.inf
@@ -0,0 +1,43 @@
+## @file
+#  This library instance provides HTTP IO helper functions.
+#
+#  (C) Copyright 2020 Hewlett Packard Enterprise Development LP<BR>
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x0001001b
+  BASE_NAME                      = DxeHttpIoLib
+  MODULE_UNI_FILE                = DxeHttpIoLib.uni
+  FILE_GUID                      = 50B198F8-7986-4F51-A857-CFE4643D59F3
+  MODULE_TYPE                    = DXE_DRIVER
+  VERSION_STRING                 = 1.0
+  LIBRARY_CLASS                  = HttpIoLib| DXE_CORE DXE_DRIVER DXE_RUNTIME_DRIVER DXE_SMM_DRIVER UEFI_APPLICATION UEFI_DRIVER
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+#  VALID_ARCHITECTURES           = IA32 X64 EBC ARM AARM64 RISCV64
+#
+
+[Sources]
+  DxeHttpIoLib.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+  NetworkPkg/NetworkPkg.dec
+
+
+[LibraryClasses]
+  BaseLib
+  BaseMemoryLib
+  DebugLib
+  DpcLib
+  MemoryAllocationLib
+  PrintLib
+  UefiBootServicesTableLib
+
+[Protocols]
+  gEfiHttpProtocolGuid             ## SOMETIMES_CONSUMES
+
diff --git a/NetworkPkg/Library/DxeHttpIoLib/DxeHttpIoLib.uni b/NetworkPkg/Library/DxeHttpIoLib/DxeHttpIoLib.uni
new file mode 100644
index 0000000000..d419b1a49d
--- /dev/null
+++ b/NetworkPkg/Library/DxeHttpIoLib/DxeHttpIoLib.uni
@@ -0,0 +1,13 @@
+// /** @file
+// The library instance provides HTTP IO helper functions.
+//
+// (C) Copyright 2020 Hewlett Packard Enterprise Development LP<BR>
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT             #language en-US "HTTP IO Helper Library"
+
+#string STR_MODULE_DESCRIPTION          #language en-US "The library instance provides HTTP IO helper functions."
+
-- 
2.17.1


  reply	other threads:[~2020-10-20  3:23 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-10-20  2:38 [DxeHttpIoLib PATCH V2 0/3] Add Http IO Helper Library Abner Chang
2020-10-20  2:38 ` Abner Chang [this message]
2020-10-23 16:24   ` [edk2-devel] [DxeHttpIoLib PATCH V2 1/3] NetworkPkg/Library: Implementation of " Maciej Rabeda
2020-10-26  6:53     ` Abner Chang
2020-10-28 18:27       ` Maciej Rabeda
2020-10-29  6:11         ` Abner Chang
2020-11-02 12:50           ` Maciej Rabeda
2020-11-02 14:14             ` Abner Chang
2020-11-02 14:49               ` Maciej Rabeda
2020-11-03  6:15                 ` Abner Chang
2020-10-20  2:38 ` [DxeHttpIoLib PATCH V2 2/3] NetworkPkg: Add Http IO Helper Library to NetworkPkg Abner Chang
2020-10-20  2:38 ` [DxeHttpIoLib PATCH V2 3/3] NetworkPkg/HttpBootDxe: Utilize HttpIoLib Abner Chang

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-list from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20201020023848.3015-2-abner.chang@hpe.com \
    --to=devel@edk2.groups.io \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox