From: "Saloni Kasbekar" <saloni.kasbekar@intel.com>
To: devel@edk2.groups.io
Cc: Saloni Kasbekar <saloni.kasbekar@intel.com>,
Maciej Rabeda <maciej.rabeda@linux.intel.com>,
Wu Jiaxin <jiaxin.wu@intel.com>, Siyuan Fu <siyuan.fu@intel.com>
Subject: [edk2-staging/HttpProxy PATCH v3 5/7] NetworkPkg: Add support for HTTP CONNECT Method
Date: Fri, 2 Dec 2022 11:12:24 -0800 [thread overview]
Message-ID: <99951a1bb8751c1e9b001a047575be45413ef616.1670008048.git.saloni.kasbekar@intel.com> (raw)
In-Reply-To: <cover.1670008048.git.saloni.kasbekar@intel.com>
REF: https://bugzilla.tianocore.org/show_bug.cgi?id=3951
Add support for the HTTP CONNECT method to request the Proxy Server
to open a tunnel to the EndPoint Server
Cc: Maciej Rabeda <maciej.rabeda@linux.intel.com>
Cc: Wu Jiaxin <jiaxin.wu@intel.com>
Cc: Siyuan Fu <siyuan.fu@intel.com>
Signed-off-by: Saloni Kasbekar <saloni.kasbekar@intel.com>
---
NetworkPkg/HttpBootDxe/HttpBootClient.c | 176 +++++++++++++++++++++
NetworkPkg/HttpBootDxe/HttpBootClient.h | 15 ++
NetworkPkg/HttpBootDxe/HttpBootImpl.c | 16 +-
NetworkPkg/HttpBootDxe/HttpBootImpl.h | 1 +
NetworkPkg/HttpDxe/HttpDriver.h | 2 +
NetworkPkg/HttpDxe/HttpDxe.inf | 1 +
NetworkPkg/HttpDxe/HttpImpl.c | 151 ++++++++++++++----
NetworkPkg/Library/DxeHttpLib/DxeHttpLib.c | 5 +
8 files changed, 335 insertions(+), 32 deletions(-)
diff --git a/NetworkPkg/HttpBootDxe/HttpBootClient.c b/NetworkPkg/HttpBootDxe/HttpBootClient.c
index b13155b576..b4d02eaff2 100644
--- a/NetworkPkg/HttpBootDxe/HttpBootClient.c
+++ b/NetworkPkg/HttpBootDxe/HttpBootClient.c
@@ -905,6 +905,182 @@ HttpBootGetBootFileCallback (
return EFI_SUCCESS;
}
+/**
+ This function establishes a connection through a proxy server
+
+ @param[in] Private The pointer to the driver's private data.
+
+ @retval EFI_SUCCESS Connection successful.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources
+ @retval Others Unexpected error happened.
+
+**/
+EFI_STATUS
+HttpBootConnectProxy (
+ IN HTTP_BOOT_PRIVATE_DATA *Private
+ )
+{
+ EFI_STATUS Status;
+ EFI_HTTP_STATUS_CODE StatusCode;
+ CHAR8 *HostName;
+ EFI_HTTP_REQUEST_DATA *RequestData;
+ HTTP_IO_RESPONSE_DATA *ResponseData;
+ HTTP_IO *HttpIo;
+ HTTP_IO_HEADER *HttpIoHeader;
+ CHAR16 *Url;
+ CHAR16 *ProxyUrl;
+ UINTN UrlSize;
+
+ Url = NULL;
+ ProxyUrl = NULL;
+ RequestData = NULL;
+ ResponseData = NULL;
+ HttpIoHeader = NULL;
+
+ UrlSize = AsciiStrSize (Private->BootFileUri);
+ Url = AllocatePool (UrlSize * sizeof (CHAR16));
+ if (Url == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ AsciiStrToUnicodeStrS (Private->BootFileUri, Url, UrlSize);
+
+ UrlSize = AsciiStrSize (Private->ProxyUri);
+ ProxyUrl = AllocatePool (UrlSize * (sizeof (CHAR16)));
+ if (ProxyUrl == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto EXIT;
+ }
+
+ AsciiStrToUnicodeStrS (Private->ProxyUri, ProxyUrl, UrlSize);
+
+ //
+ // Send HTTP request message.
+ //
+
+ //
+ // Build HTTP header for the request, 2 headers are needed to send a CONNECT method:
+ // Host
+ // User
+ //
+ HttpIoHeader = HttpIoCreateHeader (2);
+ if (HttpIoHeader == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto EXIT;
+ }
+
+ //
+ // Add HTTP header field 1: Host (EndPoint URI)
+ //
+ HostName = NULL;
+ Status = HttpUrlGetHostName (
+ Private->BootFileUri,
+ Private->BootFileUriParser,
+ &HostName
+ );
+ if (EFI_ERROR (Status)) {
+ goto EXIT;
+ }
+
+ Status = HttpIoSetHeader (
+ HttpIoHeader,
+ HTTP_HEADER_HOST,
+ HostName
+ );
+ if (EFI_ERROR (Status)) {
+ goto EXIT;
+ }
+
+ //
+ // Add HTTP header field 2: User-Agent
+ //
+ Status = HttpIoSetHeader (
+ HttpIoHeader,
+ HTTP_HEADER_USER_AGENT,
+ HTTP_USER_AGENT_EFI_HTTP_BOOT
+ );
+ if (EFI_ERROR (Status)) {
+ goto EXIT;
+ }
+
+ //
+ // Build the rest of HTTP request info.
+ //
+ RequestData = AllocatePool (sizeof (EFI_HTTP_REQUEST_DATA));
+ if (RequestData == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto EXIT;
+ }
+
+ RequestData->Method = HttpMethodConnect;
+ RequestData->ProxyUrl = ProxyUrl;
+ RequestData->Url = Url;
+
+ //
+ // Send out the request to HTTP server.
+ //
+ HttpIo = &Private->HttpIo;
+ Status = HttpIoSendRequest (
+ HttpIo,
+ RequestData,
+ HttpIoHeader->HeaderCount,
+ HttpIoHeader->Headers,
+ 0,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ goto EXIT;
+ }
+
+ //
+ // Receive HTTP response message.
+ //
+
+ //
+ // Use zero BodyLength to only receive the response headers.
+ //
+ ResponseData = AllocateZeroPool (sizeof (HTTP_IO_RESPONSE_DATA));
+ if (ResponseData == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto EXIT;
+ }
+
+ Status = HttpIoRecvResponse (
+ &Private->HttpIo,
+ TRUE,
+ ResponseData
+ );
+
+ if (EFI_ERROR (Status) || EFI_ERROR (ResponseData->Status)) {
+ if (EFI_ERROR (ResponseData->Status)) {
+ StatusCode = HttpIo->RspToken.Message->Data.Response->StatusCode;
+ HttpBootPrintErrorMessage (StatusCode);
+ Status = ResponseData->Status;
+ }
+ }
+
+EXIT:
+ if (ResponseData != NULL) {
+ FreePool (ResponseData);
+ }
+
+ if (RequestData != NULL) {
+ FreePool (RequestData);
+ }
+
+ HttpIoFreeHeader (HttpIoHeader);
+
+ if (ProxyUrl != NULL) {
+ FreePool (ProxyUrl);
+ }
+
+ if (Url != NULL) {
+ FreePool (Url);
+ }
+
+ return Status;
+}
+
/**
This function download the boot file by using UEFI HTTP protocol.
diff --git a/NetworkPkg/HttpBootDxe/HttpBootClient.h b/NetworkPkg/HttpBootDxe/HttpBootClient.h
index 2fba713679..fcd624f536 100644
--- a/NetworkPkg/HttpBootDxe/HttpBootClient.h
+++ b/NetworkPkg/HttpBootDxe/HttpBootClient.h
@@ -86,6 +86,21 @@ HttpBootCreateHttpIo (
IN HTTP_BOOT_PRIVATE_DATA *Private
);
+/**
+ This function establishes a connection through a proxy server
+
+ @param[in] Private The pointer to the driver's private data.
+
+ @retval EFI_SUCCESS Connection successful.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources
+ @retval Others Unexpected error happened.
+
+**/
+EFI_STATUS
+HttpBootConnectProxy (
+ IN HTTP_BOOT_PRIVATE_DATA *Private
+ );
+
/**
This function download the boot file by using UEFI HTTP protocol.
diff --git a/NetworkPkg/HttpBootDxe/HttpBootImpl.c b/NetworkPkg/HttpBootDxe/HttpBootImpl.c
index 4748de0603..d4a7c8385a 100644
--- a/NetworkPkg/HttpBootDxe/HttpBootImpl.c
+++ b/NetworkPkg/HttpBootDxe/HttpBootImpl.c
@@ -313,7 +313,11 @@ HttpBootGetBootFileCaller (
EFI_STATUS Status;
if (Private->BootFileSize == 0) {
- State = GetBootFileHead;
+ if (Private->ProxyUri != NULL) {
+ State = ConnectToProxy;
+ } else {
+ State = GetBootFileHead;
+ }
} else {
State = LoadBootFile;
}
@@ -366,6 +370,16 @@ HttpBootGetBootFileCaller (
break;
+ case ConnectToProxy:
+ Status = HttpBootConnectProxy (Private);
+ if (Status == EFI_SUCCESS) {
+ State = GetBootFileHead;
+ } else {
+ State = GetBootFileError;
+ }
+
+ break;
+
case LoadBootFile:
if (*BufferSize < Private->BootFileSize) {
*BufferSize = Private->BootFileSize;
diff --git a/NetworkPkg/HttpBootDxe/HttpBootImpl.h b/NetworkPkg/HttpBootDxe/HttpBootImpl.h
index 33da4fec51..e4ffc3ed48 100644
--- a/NetworkPkg/HttpBootDxe/HttpBootImpl.h
+++ b/NetworkPkg/HttpBootDxe/HttpBootImpl.h
@@ -14,6 +14,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
typedef enum {
GetBootFileHead,
GetBootFileGet,
+ ConnectToProxy,
LoadBootFile,
GetBootFileError
} HTTP_GET_BOOT_FILE_STATE;
diff --git a/NetworkPkg/HttpDxe/HttpDriver.h b/NetworkPkg/HttpDxe/HttpDriver.h
index 01a6bb7f4b..e0917f431e 100644
--- a/NetworkPkg/HttpDxe/HttpDriver.h
+++ b/NetworkPkg/HttpDxe/HttpDriver.h
@@ -26,6 +26,7 @@
#include <Library/NetLib.h>
#include <Library/HttpLib.h>
#include <Library/DpcLib.h>
+#include <Library/PrintLib.h>
//
// UEFI Driver Model Protocols
@@ -64,6 +65,7 @@
// Driver Version
//
#define HTTP_DRIVER_VERSION 0xa
+#define URI_STR_MAX_SIZE 255
//
// Protocol instances
diff --git a/NetworkPkg/HttpDxe/HttpDxe.inf b/NetworkPkg/HttpDxe/HttpDxe.inf
index c9502d0bb6..30b7de1951 100644
--- a/NetworkPkg/HttpDxe/HttpDxe.inf
+++ b/NetworkPkg/HttpDxe/HttpDxe.inf
@@ -47,6 +47,7 @@
NetLib
HttpLib
DpcLib
+ PrintLib
[Protocols]
gEfiHttpServiceBindingProtocolGuid ## BY_START
diff --git a/NetworkPkg/HttpDxe/HttpImpl.c b/NetworkPkg/HttpDxe/HttpImpl.c
index a761ce3d5d..2a305e0864 100644
--- a/NetworkPkg/HttpDxe/HttpImpl.c
+++ b/NetworkPkg/HttpDxe/HttpImpl.c
@@ -234,6 +234,7 @@ EfiHttpRequest (
EFI_HTTP_MESSAGE *HttpMsg;
EFI_HTTP_REQUEST_DATA *Request;
VOID *UrlParser;
+ VOID *EndPointUrlParser;
EFI_STATUS Status;
CHAR8 *HostName;
UINTN HostNameSize;
@@ -247,25 +248,31 @@ EfiHttpRequest (
UINTN UrlLen;
CHAR8 *ProxyUrl;
UINTN ProxyUrlLen;
+ CHAR8 *ParseUrl;
CHAR16 *HostNameStr;
HTTP_TOKEN_WRAP *Wrap;
CHAR8 *FileUrl;
UINTN RequestMsgSize;
EFI_HANDLE ImageHandle;
+ UINT16 EndPointRemotePort;
+ CHAR8 *EndPointUrlMsg;
//
// Initializations
//
- Url = NULL;
- ProxyUrl = NULL;
- UrlParser = NULL;
- RemotePort = 0;
- HostName = NULL;
- RequestMsg = NULL;
- HostNameStr = NULL;
- Wrap = NULL;
- FileUrl = NULL;
- TlsConfigure = FALSE;
+ Url = NULL;
+ ProxyUrl = NULL;
+ UrlParser = NULL;
+ EndPointUrlParser = NULL;
+ RemotePort = 0;
+ HostName = NULL;
+ RequestMsg = NULL;
+ HostNameStr = NULL;
+ Wrap = NULL;
+ FileUrl = NULL;
+ TlsConfigure = FALSE;
+ EndPointUrlMsg = NULL;
+ EndPointRemotePort = 0;
if ((This == NULL) || (Token == NULL)) {
return EFI_INVALID_PARAMETER;
@@ -279,7 +286,7 @@ EfiHttpRequest (
Request = HttpMsg->Data.Request;
//
- // Only support GET, HEAD, DELETE, PATCH, PUT and POST method in current implementation.
+ // Only support GET, HEAD, DELETE, CONNECT, PATCH, PUT and POST method in current implementation.
//
if (Request != NULL) {
switch (Request->Method) {
@@ -289,6 +296,12 @@ EfiHttpRequest (
case HttpMethodPut:
case HttpMethodPost:
case HttpMethodPatch:
+ break;
+ case HttpMethodConnect:
+ if (Request->ProxyUrl == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
break;
default:
return EFI_UNSUPPORTED;
@@ -391,10 +404,14 @@ EfiHttpRequest (
}
//
- // From the information in Url, the HTTP instance will
+ // From the information in the Urls, the HTTP instance will
// be able to determine whether to use http or https.
//
- HttpInstance->UseHttps = IsHttpsUrl (Url);
+ if (Request->Method == HttpMethodConnect) {
+ HttpInstance->UseHttps = IsHttpsUrl (ProxyUrl);
+ } else {
+ HttpInstance->UseHttps = IsHttpsUrl (Url);
+ }
//
// HTTP is disabled, return directly if the URI is not HTTPS.
@@ -431,13 +448,26 @@ EfiHttpRequest (
TlsConfigure = TRUE;
}
- UrlParser = NULL;
- Status = HttpParseUrl (Url, (UINT32)AsciiStrLen (Url), FALSE, &UrlParser);
+ //
+ // Setup RemoteAddress and RemotePort of HttpInstance.
+ //
+ if (Request->Method == HttpMethodConnect) {
+ // Case 1: HTTP Connect request
+ ParseUrl = ProxyUrl;
+ } else if (HttpInstance->ProxyConnected == TRUE) {
+ // Case 2: Other HTTP request (proxy connected)
+ ParseUrl = HttpInstance->ProxyUrl;
+ } else {
+ // Case 3: Other HTTP request (proxy not connected)
+ ParseUrl = Url;
+ }
+
+ Status = HttpParseUrl (ParseUrl, (UINT32)AsciiStrLen (ParseUrl), FALSE, &UrlParser);
if (EFI_ERROR (Status)) {
goto Error1;
}
- Status = HttpUrlGetHostName (Url, UrlParser, &HostName);
+ Status = HttpUrlGetHostName (ParseUrl, UrlParser, &HostName);
if (EFI_ERROR (Status)) {
goto Error1;
}
@@ -455,7 +485,7 @@ EfiHttpRequest (
}
}
- Status = HttpUrlGetPort (Url, UrlParser, &RemotePort);
+ Status = HttpUrlGetPort (ParseUrl, UrlParser, &RemotePort);
if (EFI_ERROR (Status)) {
if (HttpInstance->UseHttps) {
RemotePort = HTTPS_DEFAULT_PORT;
@@ -551,7 +581,7 @@ EfiHttpRequest (
if (!HttpInstance->LocalAddressIsIPv6) {
Status = NetLibAsciiStrToIp4 (HostName, &HttpInstance->RemoteAddr);
} else {
- Status = HttpUrlGetIp6 (Url, UrlParser, &HttpInstance->RemoteIpv6Addr);
+ Status = HttpUrlGetIp6 (ParseUrl, UrlParser, &HttpInstance->RemoteIpv6Addr);
}
if (EFI_ERROR (Status)) {
@@ -649,27 +679,74 @@ EfiHttpRequest (
//
// Create request message.
//
- FileUrl = Url;
- if ((Url != NULL) && (*FileUrl != '/')) {
+ if (Request->Method == HttpMethodConnect) {
//
- // Convert the absolute-URI to the absolute-path
+ // HTTP Connect shall contain EndPoint host name in URI
//
- while (*FileUrl != ':') {
- FileUrl++;
+ Status = HttpParseUrl (Url, (UINT32)AsciiStrLen (Url), FALSE, &EndPointUrlParser);
+ if (EFI_ERROR (Status)) {
+ goto Error3;
}
- if ((*(FileUrl+1) == '/') && (*(FileUrl+2) == '/')) {
- FileUrl += 3;
- while (*FileUrl != '/') {
- FileUrl++;
+ Status = HttpUrlGetHostName (
+ Url,
+ EndPointUrlParser,
+ &HttpInstance->EndPointHostName
+ );
+ if (EFI_ERROR (Status)) {
+ goto Error3;
+ }
+
+ Status = HttpUrlGetPort (Url, EndPointUrlParser, &EndPointRemotePort);
+ if (EFI_ERROR (Status)) {
+ if (IsHttpsUrl (Url)) {
+ EndPointRemotePort = HTTPS_DEFAULT_PORT;
+ } else {
+ EndPointRemotePort = HTTP_DEFAULT_PORT;
}
- } else {
- Status = EFI_INVALID_PARAMETER;
+ }
+
+ EndPointUrlMsg = AllocateZeroPool (URI_STR_MAX_SIZE);
+ if (EndPointUrlMsg == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
goto Error3;
}
- }
- Status = HttpGenRequestMessage (HttpMsg, FileUrl, &RequestMsg, &RequestMsgSize);
+ AsciiSPrint (
+ EndPointUrlMsg,
+ URI_STR_MAX_SIZE,
+ "%a:%d",
+ HttpInstance->EndPointHostName,
+ EndPointRemotePort
+ );
+
+ Status = HttpGenRequestMessage (HttpMsg, EndPointUrlMsg, &RequestMsg, &RequestMsgSize);
+
+ FreePool (EndPointUrlMsg);
+ HttpUrlFreeParser (EndPointUrlParser);
+ } else {
+ FileUrl = Url;
+ if ((Url != NULL) && (*FileUrl != '/')) {
+ //
+ // Convert the absolute-URI to the absolute-path
+ //
+ while (*FileUrl != ':') {
+ FileUrl++;
+ }
+
+ if ((*(FileUrl+1) == '/') && (*(FileUrl+2) == '/')) {
+ FileUrl += 3;
+ while (*FileUrl != '/') {
+ FileUrl++;
+ }
+ } else {
+ Status = EFI_INVALID_PARAMETER;
+ goto Error3;
+ }
+ }
+
+ Status = HttpGenRequestMessage (HttpMsg, FileUrl, &RequestMsg, &RequestMsgSize);
+ }
if (EFI_ERROR (Status) || (NULL == RequestMsg)) {
goto Error3;
@@ -705,6 +782,10 @@ EfiHttpRequest (
DispatchDpc ();
+ if (HttpInstance->Method == HttpMethodConnect) {
+ HttpInstance->ProxyConnected = TRUE;
+ }
+
if (HostName != NULL) {
FreePool (HostName);
}
@@ -750,6 +831,14 @@ Error2:
}
Error1:
+ if (EndPointUrlMsg != NULL) {
+ FreePool (EndPointUrlMsg);
+ }
+
+ if (EndPointUrlParser != NULL) {
+ HttpUrlFreeParser (EndPointUrlParser);
+ }
+
if (HostName != NULL) {
FreePool (HostName);
}
diff --git a/NetworkPkg/Library/DxeHttpLib/DxeHttpLib.c b/NetworkPkg/Library/DxeHttpLib/DxeHttpLib.c
index 6a5d78629b..45087a1935 100644
--- a/NetworkPkg/Library/DxeHttpLib/DxeHttpLib.c
+++ b/NetworkPkg/Library/DxeHttpLib/DxeHttpLib.c
@@ -1927,6 +1927,11 @@ HttpGenRequestMessage (
CopyMem (RequestPtr, HTTP_METHOD_DELETE, StrLength);
RequestPtr += StrLength;
break;
+ case HttpMethodConnect:
+ StrLength = sizeof (HTTP_METHOD_CONNECT) - 1;
+ CopyMem (RequestPtr, HTTP_METHOD_CONNECT, StrLength);
+ RequestPtr += StrLength;
+ break;
default:
ASSERT (FALSE);
Status = EFI_INVALID_PARAMETER;
--
2.36.1.windows.1
next prev parent reply other threads:[~2022-12-02 19:12 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-12-02 19:12 [edk2-staging/HttpProxy PATCH v3 0/7] Support HTTPS Proxy Server for HTTP Boot Saloni Kasbekar
2022-12-02 19:12 ` [edk2-staging/HttpProxy PATCH v3 1/7] MdeModulePkg/Library: Support multi-URI HTTP Boot device path Saloni Kasbekar
2022-12-02 19:12 ` [edk2-staging/HttpProxy PATCH v3 2/7] MdePkg/Include: Add Proxy Server URL in EFI_HTTP_REQUEST_DATA Saloni Kasbekar
2022-12-02 19:12 ` [edk2-staging/HttpProxy PATCH v3 3/7] NetworkPkg/HttpBootDxe: Update HTTP Boot Driver with parsed Proxy URL Saloni Kasbekar
2022-12-02 19:12 ` [edk2-staging/HttpProxy PATCH v3 4/7] NetworkPkg: Add Proxy Support to HTTP_PROTOCOL Saloni Kasbekar
2022-12-02 19:12 ` Saloni Kasbekar [this message]
2022-12-02 19:12 ` [edk2-staging/HttpProxy PATCH v3 6/7] NetworkPkg/HttpDxe: Support HTTPS EndPoint server with Proxy Saloni Kasbekar
2022-12-02 19:12 ` [edk2-staging/HttpProxy PATCH v3 7/7] NetworkPkg/HttpBootDxe: Add Proxy URI input in setup menu Saloni Kasbekar
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=99951a1bb8751c1e9b001a047575be45413ef616.1670008048.git.saloni.kasbekar@intel.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