Repository: microsoft/terminal (language: cpp)

Issue:
Azure Cloud Shell Could not connect to Azure behind Corp Proxy
<!-- 
🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨

I ACKNOWLEDGE THE FOLLOWING BEFORE PROCEEDING:
1. If I delete this entire template and go my own path, the core team may close my issue without further explanation or engagement.
2. If I list multiple bugs/concerns in this one issue, the core team may close my issue without further explanation or engagement.
3. If I write an issue that has many duplicates, the core team may close my issue without further explanation or engagement (and without necessarily spending time to find the exact duplicate ID number).
4. If I leave the title incomplete when filing the issue, the core team may close my issue without further explanation or engagement.
5. If I file something completely blank in the body, the core team may close my issue without further explanation or engagement.

All good? Then proceed!
-->

<!--
This bug tracker is monitored by Windows Terminal development team and other technical folks.

**Important: When reporting BSODs or security issues, DO NOT attach memory dumps, logs, or traces to Github issues**.
Instead, send dumps/traces to secure@microsoft.com, referencing this GitHub issue.

If this is an application crash, please also provide a Feedback Hub submission link so we can find your diagnostic data on the backend. Use the category "Apps > Windows Terminal (Preview)" and choose "Share My Feedback" after submission to get the link.

Please use this form and describe your issue, concisely but precisely, with as much detail as possible.

-->

# Environment

```none
Windows build number: 10.0.18363.0
Windows Terminal version (if applicable): v0.9.433.0

Any other software?
```

# Steps to reproduce

Add a new "Azure Cloud Shell" tab to your terminal instance.

# Expected behavior

A prompt should appear asking to authenticate with Azure.

# Actual behavior

The message "Could not connect to Azure. You may not have internet or the server might be down." appears immediately in the new tab. Fiddler only shows a single outgoing SSL initiation request to login.microsoftonline.com with a 200 response and no followup communication. 

I recently had to update the cacert bundle for the azure cli located in C:\Program Files (x86)\Microsoft SDKs\Azure\CLI2\Lib\site-packages\certifi\cacert.pem to include our corp proxy certificate which restored its functionality. Is there something similar here or should the terminal be using the windows store?



Discussion / hints:
Terminal should be using the Windows certificate store; is your corp proxy cert part of the Trusted Roots (at either User or System level?)

I believe the HTTP library we're using _does_ use the WinHTTP APIs.
My corp cert is definitely in both user and machine Trusted Root stores (confirmed just now). Does cloud shell function when running from source? Perhaps I could get some additional context on the connection error that way?
Pulled source to attempt debugging my specific version (v0.9.433.0), followed the instructions in the readme to restore git submodules via `git submodule update --init --recursive`, opened OpenConsole.sln in VS2019 Community V16.4.4, installed the required desktop development components, re-opened the solution and when right-clicking on CascadiaPackage -> Properties I am met with the error: 

"An error occurred trying to load the project properties window.  Close the window and try again.
The project configuration "Debug|x86" was not found in the project manifest."

Same problem in master branch. Tried searching issues for an existing bug report of this scenario and found nothing. Should I open one or am I missing something obvious here?
@Ugenx I just bumped into it also. I changed the platform in the top menu from x86 to x64 and then I successfully opened the "CascadiaPackage" properties. You should try it also.

It's happening because the x86 platform isn't defined in the project configuration and I think the main reason is that the "CascadiaPackage" debug project configuration for the x86 platform isn't configured deployable.

Fix patch:
diff --git a/.github/actions/spelling/allow/apis.txt b/.github/actions/spelling/allow/apis.txt
index e0cc7550095..ce36d8d2a2b 100644
--- a/.github/actions/spelling/allow/apis.txt
+++ b/.github/actions/spelling/allow/apis.txt
@@ -55,6 +55,8 @@ GETMOUSEHOVERTIME
 Hashtable
 HIGHCONTRASTON
 HIGHCONTRASTW
+hinternet
+HINTERNET
 hotkeys
 href
 hrgn
@@ -214,6 +216,8 @@ Viewbox
 virtualalloc
 wcsstr
 wcstoui
+WDJ
+winhttp
 winmain
 winsta
 winstamin
@@ -221,6 +225,7 @@ wmemcmp
 wpc
 WSF
 wsregex
+WWH
 wwinmain
 xchg
 XDocument
diff --git a/.github/actions/spelling/expect/expect.txt b/.github/actions/spelling/expect/expect.txt
index 9c7ef11a7d7..0c46903dbe8 100644
--- a/.github/actions/spelling/expect/expect.txt
+++ b/.github/actions/spelling/expect/expect.txt
@@ -311,7 +311,6 @@ CPLINFO
 cplusplus
 CPPCORECHECK
 cppcorecheckrules
-cpprest
 cpprestsdk
 cppwinrt
 CProc
@@ -1437,7 +1436,6 @@ PPEB
 ppf
 ppguid
 ppidl
-pplx
 PPROC
 PPROCESS
 ppropvar
@@ -2114,7 +2112,6 @@ WDDMCONSOLECONTEXT
 wdm
 webpage
 websites
-websockets
 wekyb
 wex
 wextest
diff --git a/build/config/ESRPSigning_Terminal.json b/build/config/ESRPSigning_Terminal.json
index 715848a42c4..01780c2df3c 100644
--- a/build/config/ESRPSigning_Terminal.json
+++ b/build/config/ESRPSigning_Terminal.json
@@ -67,51 +67,6 @@
                     }
                 ]
             }
-        },
-        {
-            // THIRD PARTY SOFTWARE
-            "MatchedPath": [
-                "cpprest*.dll"
-            ],
-            "SigningInfo": {
-                "Operations": [
-                    {
-                        "KeyCode": "CP-231522",
-                        "OperationSetCode": "SigntoolSign",
-                        "Parameters": [
-                            {
-                                "parameterName": "OpusName",
-                                "parameterValue": "Microsoft"
-                            },
-                            {
-                                "parameterName": "OpusInfo",
-                                "parameterValue": "http://www.microsoft.com"
-                            },
-                            {
-                                "parameterName": "FileDigest",
-                                "parameterValue": "/fd \"SHA256\""
-                            },
-                            {
-                                "parameterName": "PageHash",
-                                "parameterValue": "/NPH"
-                            },
-                            {
-                                "parameterName": "TimeStamp",
-                                "parameterValue": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256"
-                            }
-                        ],
-                        "ToolName": "sign",
-                        "ToolVersion": "1.0"
-                    },
-                    {
-                        "KeyCode": "CP-231522",
-                        "OperationSetCode": "SigntoolVerify",
-                        "Parameters": [],
-                        "ToolName": "sign",
-                        "ToolVersion": "1.0"
-                    }
-                ]
-            }
         }
     ]
 }
diff --git a/build/pipelines/release.yml b/build/pipelines/release.yml
index 2f3c6992c5e..2fd0f8757db 100644
--- a/build/pipelines/release.yml
+++ b/build/pipelines/release.yml
@@ -64,7 +64,7 @@ parameters:
 
 variables:
   MakeAppxPath: 'C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x86\MakeAppx.exe'
-  TerminalInternalPackageVersion: "0.0.7"
+  TerminalInternalPackageVersion: "0.0.8"
   # If we are building a branch called "release-*", change the NuGet suffix
   # to "preview". If we don't do that, XES will set the suffix to "release1"
   # because it truncates the value after the first period.
diff --git a/build/pipelines/templates/build-console-compliance-job.yml b/build/pipelines/templates/build-console-compliance-job.yml
index d5042cdb177..0f885cfcce7 100644
--- a/build/pipelines/templates/build-console-compliance-job.yml
+++ b/build/pipelines/templates/build-console-compliance-job.yml
@@ -144,7 +144,7 @@ jobs:
     inputs:
       TargetPattern: guardianGlob
       # See https://aka.ms/gdn-globs for how to do match patterns
-      AnalyzeTargetGlob: $(Build.SourcesDirectory)\bin\**\*.dll;$(Build.SourcesDirectory)\bin\**\*.exe;-:file|**\Microsoft.UI.Xaml.dll;-:file|**\Microsoft.Toolkit.Win32.UI.XamlHost.dll;-:file|**\vcruntime*.dll;-:file|**\vcomp*.dll;-:file|**\vccorlib*.dll;-:file|**\vcamp*.dll;-:file|**\msvcp*.dll;-:file|**\concrt*.dll;-:file|**\TerminalThemeHelpers*.dll;-:file|**\cpprest*.dll
+      AnalyzeTargetGlob: $(Build.SourcesDirectory)\bin\**\*.dll;$(Build.SourcesDirectory)\bin\**\*.exe;-:file|**\Microsoft.UI.Xaml.dll;-:file|**\Microsoft.Toolkit.Win32.UI.XamlHost.dll;-:file|**\vcruntime*.dll;-:file|**\vcomp*.dll;-:file|**\vccorlib*.dll;-:file|**\vcamp*.dll;-:file|**\msvcp*.dll;-:file|**\concrt*.dll;-:file|**\TerminalThemeHelpers*.dll
     continueOnError: true
 
   # Set XES_SERIALPOSTBUILDREADY to run Security and Compliance task once per build
diff --git a/build/scripts/Test-WindowsTerminalPackage.ps1 b/build/scripts/Test-WindowsTerminalPackage.ps1
index aa9cca6d107..a33329c26cd 100644
--- a/build/scripts/Test-WindowsTerminalPackage.ps1
+++ b/build/scripts/Test-WindowsTerminalPackage.ps1
@@ -96,11 +96,6 @@ Try {
         Throw "Failed to find App.xbf (TerminalApp project) in resources.pri"
     }
 
-    If (($null -eq (Get-Item "$AppxPackageRootPath\cpprest142_2_10.dll" -EA:Ignore)) -And
-        ($null -eq (Get-Item "$AppxPackageRootPath\cpprest142_2_10d.dll" -EA:Ignore))) {
-        Throw "Failed to find cpprest142_2_10.dll -- check the WAP packaging project"
-    }
-
     If (($null -eq (Get-Item "$AppxPackageRootPath\wtd.exe" -EA:Ignore)) -And
         ($null -eq (Get-Item "$AppxPackageRootPath\wt.exe" -EA:Ignore))) {
         Throw "Failed to find wt.exe/wtd.exe -- check the WAP packaging project"
diff --git a/dep/nuget/packages.config b/dep/nuget/packages.config
index 0a62482b487..935d004a72d 100644
--- a/dep/nuget/packages.config
+++ b/dep/nuget/packages.config
@@ -5,7 +5,6 @@
   <package id="Microsoft.Internal.PGO-Helpers.Cpp" version="0.2.34" targetFramework="native" />
   <package id="Microsoft.Taef" version="10.60.210621002" targetFramework="native" />
   <package id="Microsoft.Windows.CppWinRT" version="2.0.210825.3" targetFramework="native" />
-  <package id="vcpkg-cpprestsdk" version="2.10.14" targetFramework="native" />
   <package id="Microsoft.VCRTForwarders.140" version="1.0.4" targetFramework="native" />
   <package id="Microsoft.Internal.Windows.Terminal.ThemeHelpers" version="0.6.220404001" targetFramework="native" />
   <package id="Microsoft.VisualStudio.Setup.Configuration.Native" version="2.3.2262" targetFramework="native" developmentDependency="true" />
diff --git a/scratch/ScratchIslandApp/WindowExe/WindowExe.vcxproj b/scratch/ScratchIslandApp/WindowExe/WindowExe.vcxproj
index e17243bfc6e..de4c7a34351 100644
--- a/scratch/ScratchIslandApp/WindowExe/WindowExe.vcxproj
+++ b/scratch/ScratchIslandApp/WindowExe/WindowExe.vcxproj
@@ -197,26 +197,6 @@
     <!-- **END VC LIBS HACK** -->
 
 
-    <!-- **BEGIN TERMINAL CONNECTION HACK** -->
-    <!-- This is the same as the above VC libs hack, but for TerminalConnection.
-    TerminalConnection depends on cpprest*.dll, and if we don't include it in
-    the packaging output, we'll crash as soon as we try to load
-    TerminalConnection.dll.
-
-    The Sample sln needs to do this manually - the real exe has a
-    ProjectReference to TerminalConnection.vcxproj and can figure this out on
-    its own. -->
-    <ItemGroup>
-      <_TerminalConnectionDlls Include="$(OpenConsoleCommonOutDir)\TerminalConnection\*.dll" />
-
-      <PackagingOutputs Include="@(_TerminalConnectionDlls)">
-        <ProjectName>$(ProjectName)</ProjectName>
-        <OutputGroup>BuiltProjectOutputGroup</OutputGroup>
-        <TargetPath>%(Filename)%(Extension)</TargetPath>
-      </PackagingOutputs>
-    </ItemGroup>
-    <!-- **END TERMINAL CONNECTION HACK** -->
-
     <!-- Same thing again here, with WindowsTerminal.exe -->
     <ItemGroup>
       <_WindowsTerminalExe Include="$(OpenConsoleCommonOutDir)\WindowsTerminal\*.exe" />
diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp
index cf4f0140a27..94e9865e63a 100644
--- a/src/cascadia/TerminalApp/TerminalPage.cpp
+++ b/src/cascadia/TerminalApp/TerminalPage.cpp
@@ -1247,10 +1247,17 @@ namespace winrt::TerminalApp::implementation
         if (connectionType == TerminalConnection::AzureConnection::ConnectionType() &&
             TerminalConnection::AzureConnection::IsAzureConnectionAvailable())
         {
-            // TODO GH#4661: Replace this with directly using the AzCon when our VT is better
             std::filesystem::path azBridgePath{ wil::GetModuleFileNameW<std::wstring>(nullptr) };
             azBridgePath.replace_filename(L"TerminalAzBridge.exe");
-            connection = TerminalConnection::ConptyConnection();
+            if constexpr (Feature_AzureConnectionInProc::IsEnabled())
+            {
+                connection = TerminalConnection::AzureConnection{};
+            }
+            else
+            {
+                connection = TerminalConnection::ConptyConnection{};
+            }
+
             auto valueSet = TerminalConnection::ConptyConnection::CreateSettings(azBridgePath.wstring(),
                                                                                  L".",
                                                                                  L"Azure",
diff --git a/src/cascadia/TerminalConnection/AzureClient.h b/src/cascadia/TerminalConnection/AzureClient.h
index a0362125559..f8ca9c08235 100644
--- a/src/cascadia/TerminalConnection/AzureClient.h
+++ b/src/cascadia/TerminalConnection/AzureClient.h
@@ -3,8 +3,6 @@
 
 #pragma once
 
-#include "cpprest/json.h"
-
 namespace Microsoft::Terminal::Azure
 {
     class AzureException : public std::runtime_error
@@ -12,14 +10,24 @@ namespace Microsoft::Terminal::Azure
         std::wstring _code;
 
     public:
-        static bool IsErrorPayload(const web::json::value& errorObject)
+        static bool IsErrorPayload(const winrt::Windows::Data::Json::JsonObject& errorObject)
         {
-            return errorObject.has_string_field(L"error");
+            if (!errorObject.HasKey(L"error"))
+            {
+                return false;
+            }
+
+            if (errorObject.GetNamedValue(L"error").ValueType() != winrt::Windows::Data::Json::JsonValueType::String)
+            {
+                return false;
+            }
+
+            return true;
         }
 
-        AzureException(const web::json::value& errorObject) :
-            runtime_error(til::u16u8(errorObject.at(L"error_description").as_string())), // surface the human-readable description as .what()
-            _code(errorObject.at(L"error").as_string())
+        AzureException(const winrt::Windows::Data::Json::JsonObject& errorObject) :
+            runtime_error(til::u16u8(errorObject.GetNamedString(L"error_description"))), // surface the human-readable description as .what()
+            _code(errorObject.GetNamedString(L"error"))
         {
         }
 
diff --git a/src/cascadia/TerminalConnection/AzureConnection.cpp b/src/cascadia/TerminalConnection/AzureConnection.cpp
index f04e08bd74b..3844979b488 100644
--- a/src/cascadia/TerminalConnection/AzureConnection.cpp
+++ b/src/cascadia/TerminalConnection/AzureConnection.cpp
@@ -15,22 +15,24 @@
 #include "winrt/Windows.System.UserProfile.h"
 #include "../../types/inc/Utils.hpp"
 
+#include "winrt/Windows.Web.Http.Filters.h"
+
 using namespace ::Microsoft::Console;
 using namespace ::Microsoft::Terminal::Azure;
-
-using namespace web;
-using namespace web::http;
-using namespace web::http::client;
-using namespace web::websockets::client;
 using namespace winrt::Windows::Security::Credentials;
 
-static constexpr int CurrentCredentialVersion = 1;
-static constexpr auto PasswordVaultResourceName = L"Terminal";
-static constexpr auto HttpUserAgent = L"Terminal/0.0";
+static constexpr int CurrentCredentialVersion = 2;
+static constexpr std::wstring_view PasswordVaultResourceName = L"Terminal";
+static constexpr std::wstring_view HttpUserAgent = L"Mozilla/5.0 (Windows NT 10.0) Terminal/1.0";
 
 static constexpr int USER_INPUT_COLOR = 93; // yellow - the color of something the user can type
 static constexpr int USER_INFO_COLOR = 97; // white - the color of clarifying information
 
+using namespace winrt::Windows::Foundation;
+namespace WDJ = ::winrt::Windows::Data::Json;
+namespace WSS = ::winrt::Windows::Storage::Streams;
+namespace WWH = ::winrt::Windows::Web::Http;
+
 static constexpr winrt::guid AzureConnectionType = { 0xd9fcfdfa, 0xa479, 0x412c, { 0x83, 0xb7, 0xc5, 0x64, 0xe, 0x61, 0xcd, 0x62 } };
 
 static inline std::wstring _colorize(const unsigned int colorCode, const std::wstring_view text)
@@ -115,6 +117,8 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
     // - creates the output thread (where we will do the authentication and actually connect to Azure)
     void AzureConnection::Start()
     {
+        _httpClient = winrt::Windows::Web::Http::HttpClient{};
+        _httpClient.DefaultRequestHeaders().UserAgent().TryParseAdd(HttpUserAgent);
         // Create our own output handling thread
         // Each connection needs to make sure to drain the output from its backing host.
         _hOutputThread.reset(CreateThread(
@@ -126,7 +130,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
                 {
                     return pInstance->_OutputThread();
                 }
-                return gsl::narrow_cast<DWORD>(E_INVALIDARG);
+                return gsl::narrow<DWORD>(E_INVALIDARG);
             },
             this,
             0,
@@ -183,12 +187,8 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
 
         if (_state == AzureState::TermConnected)
         {
-            // If we're connected, we don't need to do any fun input shenanigans.
-            websocket_outgoing_message msg;
-            const auto str = winrt::to_string(data);
-            msg.set_utf8_message(str);
-
-            _cloudShellSocket.send(msg).get();
+            auto buff{ winrt::to_string(data) };
+            WinHttpWebSocketSend(_webSocket.get(), WINHTTP_WEB_SOCKET_UTF8_MESSAGE_BUFFER_TYPE, buff.data(), gsl::narrow<DWORD>(buff.size()));
             return;
         }
 
@@ -238,16 +238,17 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
         }
         else // We only transition to Connected when we've established the websocket.
         {
-            // Initialize client
-            http_client terminalClient(_cloudShellUri);
+            auto uri{ fmt::format(L"{}terminals/{}/size?cols={}&rows={}&version=2019-01-01", _cloudShellUri, _terminalID, columns, rows) };
 
-            // Initialize the request
-            http_request terminalRequest(L"POST");
-            terminalRequest.set_request_uri(fmt::format(L"terminals/{}/size?cols={}&rows={}&version=2019-01-01", _terminalID, columns, rows));
-            terminalRequest.set_body(json::value::null());
+            WWH::HttpStringContent content{
+                L"",
+                WSS::UnicodeEncoding::Utf8,
+                // LOAD-BEARING. the API returns "'content-type' should be 'application/json' or 'multipart/form-data'"
+                L"application/json"
+            };
 
             // Send the request (don't care about the response)
-            (void)_SendAuthenticatedRequestReturningJson(terminalClient, terminalRequest);
+            std::ignore = _SendRequestReturningJson(uri, content);
         }
     }
     CATCH_LOG();
@@ -264,7 +265,10 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
             if (_state == AzureState::TermConnected)
             {
                 // Close the websocket connection
-                _cloudShellSocket.close();
+                std::ignore = WinHttpWebSocketClose(_webSocket.get(), WINHTTP_WEB_SOCKET_SUCCESS_CLOSE_STATUS, nullptr, 0); // throw away the error
+                _webSocket.reset();
+                _socketConnectionHandle.reset();
+                _socketSessionHandle.reset();
             }
 
             if (_hOutputThread)
@@ -287,44 +291,46 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
     // - tenant - the unparsed tenant
     // Return value:
     // - a tuple containing the ID and display name of the tenant.
-    static Tenant _crackTenant(const json::value& jsonTenant)
+    static Tenant _crackTenant(const WDJ::IJsonValue& value)
     {
+        auto jsonTenant{ value.GetObjectW() };
+
         Tenant tenant{};
-        if (jsonTenant.has_string_field(L"tenantID"))
+        if (jsonTenant.HasKey(L"tenantID"))
         {
             // for compatibility with version 1 credentials
-            tenant.ID = jsonTenant.at(L"tenantID").as_string();
+            tenant.ID = jsonTenant.GetNamedString(L"tenantID");
         }
         else
         {
             // This one comes in off the wire
-            tenant.ID = jsonTenant.at(L"tenantId").as_string();
+            tenant.ID = jsonTenant.GetNamedString(L"tenantId");
         }
 
-        if (jsonTenant.has_string_field(L"displayName"))
+        if (jsonTenant.HasKey(L"displayName"))
         {
-            tenant.DisplayName = jsonTenant.at(L"displayName").as_string();
+            tenant.DisplayName = jsonTenant.GetNamedString(L"displayName");
         }
 
-        if (jsonTenant.has_string_field(L"defaultDomain"))
+        if (jsonTenant.HasKey(L"defaultDomain"))
         {
-            tenant.DefaultDomain = jsonTenant.at(L"defaultDomain").as_string();
+            tenant.DefaultDomain = jsonTenant.GetNamedString(L"defaultDomain");
         }
 
         return tenant;
     }
 
-    static void _packTenant(json::value& jsonTenant, const Tenant& tenant)
+    static void _packTenant(const WDJ::JsonObject& jsonTenant, const Tenant& tenant)
     {
-        jsonTenant[L"tenantId"] = json::value::string(tenant.ID);
+        jsonTenant.SetNamedValue(L"tenantId", WDJ::JsonValue::CreateStringValue(tenant.ID));
         if (tenant.DisplayName.has_value())
         {
-            jsonTenant[L"displayName"] = json::value::string(*tenant.DisplayName);
+            jsonTenant.SetNamedValue(L"displayName", WDJ::JsonValue::CreateStringValue(*tenant.DisplayName));
         }
 
         if (tenant.DefaultDomain.has_value())
         {
-            jsonTenant[L"defaultDomain"] = json::value::string(*tenant.DefaultDomain);
+            jsonTenant.SetNamedValue(L"defaultDomain", WDJ::JsonValue::CreateStringValue(*tenant.DefaultDomain));
         }
     }
 
@@ -383,35 +389,42 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
                 case AzureState::TermConnected:
                 {
                     _transitionToState(ConnectionState::Connected);
+
                     while (true)
                     {
-                        // Read from websocket
-                        pplx::task<websocket_incoming_message> msgT;
-                        try
+                        WINHTTP_WEB_SOCKET_BUFFER_TYPE bufferType{};
+                        DWORD read{};
+                        THROW_IF_WIN32_ERROR(WinHttpWebSocketReceive(_webSocket.get(), _buffer.data(), gsl::narrow<DWORD>(_buffer.size()), &read, &bufferType));
+
+                        switch (bufferType)
                         {
-                            msgT = _cloudShellSocket.receive();
-                            msgT.wait();
-                        }
-                        catch (...)
+                        case WINHTTP_WEB_SOCKET_UTF8_FRAGMENT_BUFFER_TYPE:
+                        case WINHTTP_WEB_SOCKET_UTF8_MESSAGE_BUFFER_TYPE:
                         {
-                            // Websocket has been closed; consider it a graceful exit?
-                            // This should result in our termination.
+                            const auto result{ til::u8u16(std::string_view{ _buffer.data(), read }, _u16Str, _u8State) };
+                            if (FAILED(result))
+                            {
+                                // EXIT POINT
+                                _transitionToState(ConnectionState::Failed);
+                                return gsl::narrow<DWORD>(result);
+                            }
+
+                            if (_u16Str.empty())
+                            {
+                                continue;
+                            }
+
+                            // Pass the output to our registered event handlers
+                            _TerminalOutputHandlers(_u16Str);
+                            break;
+                        }
+                        case WINHTTP_WEB_SOCKET_CLOSE_BUFFER_TYPE:
+                            // EXIT POINT
                             if (_transitionToState(ConnectionState::Closed))
                             {
-                                // End the output thread.
                                 return S_FALSE;
                             }
                         }
-
-                        auto msg = msgT.get();
-                        auto msgStringTask = msg.extract_string();
-                        auto msgString = msgStringTask.get();
-
-                        // Convert to hstring
-                        const auto hstr = winrt::to_hstring(msgString);
-
-                        // Pass the output to our registered event handlers
-                        _TerminalOutputHandlers(hstr);
                     }
                     return S_OK;
                 }
@@ -449,25 +462,29 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
         _tenantList.clear();
         for (const auto& entry : credList)
         {
-            auto nameJson = json::value::parse(entry.UserName().c_str());
-            std::optional<int> credentialVersion;
-            if (nameJson.has_integer_field(U("ver")))
+            try
             {
-                credentialVersion = nameJson.at(U("ver")).as_integer();
-            }
+                auto nameJson = WDJ::JsonObject::Parse(entry.UserName());
+                std::optional<int> credentialVersion;
+                if (nameJson.HasKey(L"ver"))
+                {
+                    credentialVersion = static_cast<int>(nameJson.GetNamedNumber(L"ver"));
+                }
 
-            if (!credentialVersion.has_value() || credentialVersion.value() != CurrentCredentialVersion)
-            {
-                // ignore credentials that aren't from the latest credential revision
-                vault.Remove(entry);
-                oldVersionEncountered = true;
-                continue;
-            }
+                if (!credentialVersion.has_value() || credentialVersion.value() != CurrentCredentialVersion)
+                {
+                    // ignore credentials that aren't from the latest credential revision
+                    vault.Remove(entry);
+                    oldVersionEncountered = true;
+                    continue;
+                }
 
-            auto newTenant{ _tenantList.emplace_back(_crackTenant(nameJson)) };
+                auto newTenant{ _tenantList.emplace_back(_crackTenant(nameJson)) };
 
-            _WriteStringWithNewline(_formatTenant(numTenants, newTenant));
-            numTenants++;
+                _WriteStringWithNewline(_formatTenant(numTenants, newTenant));
+                numTenants++;
+            }
+            CATCH_LOG();
         }
 
         if (!numTenants)
@@ -533,11 +550,11 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
         // User wants to login with one of the saved connection settings
         auto desiredCredential = credList.GetAt(selectedTenant);
         desiredCredential.RetrievePassword();
-        auto passWordJson = json::value::parse(desiredCredential.Password().c_str());
+        auto passWordJson = WDJ::JsonObject::Parse(desiredCredential.Password());
         _currentTenant = til::at(_tenantList, selectedTenant); // we already unpacked the name info, so we should just use it
-        _accessToken = passWordJson.at(L"accessToken").as_string();
-        _refreshToken = passWordJson.at(L"refreshToken").as_string();
-        _expiry = std::stoi(passWordJson.at(L"expiry").as_string());
+        _setAccessToken(passWordJson.GetNamedString(L"accessToken"));
+        _refreshToken = passWordJson.GetNamedString(L"refreshToken");
+        _expiry = std::stoi(winrt::to_string(passWordJson.GetNamedString(L"expiry")));
 
         const auto t1 = std::chrono::system_clock::now();
         const auto timeNow = std::chrono::duration_cast<std::chrono::seconds>(t1.time_since_epoch()).count();
@@ -577,17 +594,17 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
         const auto deviceCodeResponse = _GetDeviceCode();
 
         // Print the message and store the device code, polling interval and expiry
-        const auto message = winrt::to_hstring(deviceCodeResponse.at(L"message").as_string().c_str());
+        const auto message{ deviceCodeResponse.GetNamedString(L"message") };
         _WriteStringWithNewline(message);
         _WriteStringWithNewline(RS_(L"AzureCodeExpiry"));
-        const auto devCode = deviceCodeResponse.at(L"device_code").as_string();
-        const auto pollInterval = std::stoi(deviceCodeResponse.at(L"interval").as_string());
-        const auto expiresIn = std::stoi(deviceCodeResponse.at(L"expires_in").as_string());
+        const auto devCode = deviceCodeResponse.GetNamedString(L"device_code");
+        const auto pollInterval = std::stoi(winrt::to_string(deviceCodeResponse.GetNamedString(L"interval")));
+        const auto expiresIn = std::stoi(winrt::to_string(deviceCodeResponse.GetNamedString(L"expires_in")));
 
         // Wait for user authentication and obtain the access/refresh tokens
         auto authenticatedResponse = _WaitForUser(devCode, pollInterval, expiresIn);
-        _accessToken = authenticatedResponse.at(L"access_token").as_string();
-        _refreshToken = authenticatedResponse.at(L"refresh_token").as_string();
+        _setAccessToken(authenticatedResponse.GetNamedString(L"access_token"));
+        _refreshToken = authenticatedResponse.GetNamedString(L"refresh_token");
 
         // Get the tenants and the required tenant id
         _PopulateTenantList();
@@ -696,19 +713,18 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
     // - Helper function to parse the preferred shell type from user settings returned by cloud console API.
     // We need this function because the field might be missing in the settings
     // created with old versions of cloud console API.
-    std::optional<utility::string_t> AzureConnection::_ParsePreferredShellType(const web::json::value& settingsResponse)
+    winrt::hstring AzureConnection::_ParsePreferredShellType(const WDJ::JsonObject& settingsResponse)
     {
-        if (settingsResponse.has_object_field(L"properties"))
+        if (settingsResponse.HasKey(L"properties"))
         {
-            const auto userSettings = settingsResponse.at(L"properties");
-            if (userSettings.has_string_field(L"preferredShellType"))
+            const auto userSettings = settingsResponse.GetNamedObject(L"properties");
+            if (userSettings.HasKey(L"preferredShellType"))
             {
-                const auto preferredShellTypeValue = userSettings.at(L"preferredShellType");
-                return preferredShellTypeValue.as_string();
+                return userSettings.GetNamedString(L"preferredShellType");
             }
         }
 
-        return std::nullopt;
+        return L"pwsh";
     }
 
     // Method description:
@@ -717,7 +733,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
     {
         // Get user's cloud shell settings
         const auto settingsResponse = _GetCloudShellUserSettings();
-        if (settingsResponse.has_field(L"error"))
+        if (settingsResponse.HasKey(L"error"))
         {
             _WriteStringWithNewline(RS_(L"AzureNoCloudAccount"));
             _transitionToState(ConnectionState::Failed);
@@ -732,12 +748,36 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
         // Request for a terminal for said cloud shell
         const auto shellType = _ParsePreferredShellType(settingsResponse);
         _WriteStringWithNewline(RS_(L"AzureRequestingTerminal"));
-        const auto socketUri = _GetTerminal(shellType.value_or(L"pwsh"));
+        const auto socketUri = _GetTerminal(shellType);
         _TerminalOutputHandlers(L"\r\n");
 
-        // Step 8: connecting to said terminal
-        const auto connReqTask = _cloudShellSocket.connect(socketUri);
-        connReqTask.wait();
+        //// Step 8: connecting to said terminal
+        {
+            wil::unique_winhttp_hinternet sessionHandle, connectionHandle, requestHandle, socketHandle;
+            Uri parsedUri{ socketUri };
+            sessionHandle.reset(WinHttpOpen(HttpUserAgent.data(), WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY, nullptr, nullptr, 0));
+            THROW_LAST_ERROR_IF(!sessionHandle);
+
+            connectionHandle.reset(WinHttpConnect(sessionHandle.get(), parsedUri.Host().c_str(), INTERNET_DEFAULT_HTTPS_PORT, 0));
+            THROW_LAST_ERROR_IF(!connectionHandle);
+
+            requestHandle.reset(WinHttpOpenRequest(connectionHandle.get(), L"GET", parsedUri.Path().c_str(), nullptr, nullptr, nullptr, WINHTTP_FLAG_SECURE));
+            THROW_LAST_ERROR_IF(!requestHandle);
+
+            THROW_IF_WIN32_BOOL_FALSE(WinHttpSetOption(requestHandle.get(), WINHTTP_OPTION_UPGRADE_TO_WEB_SOCKET, nullptr, 0));
+#pragma warning(suppress : 26477) // WINHTTP_NO_ADDITIONAL_HEADERS expands to NULL rather than nullptr (who would have thought?)
+            THROW_IF_WIN32_BOOL_FALSE(WinHttpSendRequest(requestHandle.get(), WINHTTP_NO_ADDITIONAL_HEADERS, 0, nullptr, 0, 0, 0));
+            THROW_IF_WIN32_BOOL_FALSE(WinHttpReceiveResponse(requestHandle.get(), nullptr));
+
+            socketHandle.reset(WinHttpWebSocketCompleteUpgrade(requestHandle.get(), 0));
+            THROW_LAST_ERROR_IF(!socketHandle);
+
+            requestHandle.reset(); // We no longer need the request once we've upgraded it.
+            // We have to keep the socket session and connection handles.
+            _socketSessionHandle = std::move(sessionHandle);
+            _socketConnectionHandle = std::move(connectionHandle);
+            _webSocket = std::move(socketHandle);
+        }
 
         _state = AzureState::TermConnected;
 
@@ -752,58 +792,52 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
     // Method description:
     // - helper function to send requests with default headers and extract responses as json values
     // Arguments:
-    // - a http_client
-    // - a http_request for the client to send
+    // - the URI
+    // - optional body content
+    // - an optional HTTP method (defaults to POST if content is present, GET otherwise)
     // Return value:
     // - the response from the server as a json value
-    json::value AzureConnection::_SendRequestReturningJson(http_client& theClient, http_request theRequest)
+    WDJ::JsonObject AzureConnection::_SendRequestReturningJson(std::wstring_view uri, const WWH::IHttpContent& content, WWH::HttpMethod method)
     {
-        auto& headers{ theRequest.headers() };
-        headers.add(L"User-Agent", HttpUserAgent);
-        headers.add(L"Accept", L"application/json");
-
-        json::value jsonResult;
-        const auto responseTask = theClient.request(theRequest);
-        responseTask.wait();
-        const auto response = responseTask.get();
-        const auto responseJsonTask = response.extract_json();
-        responseJsonTask.wait();
-        jsonResult = responseJsonTask.get();
+        if (!method)
+        {
+            method = content == nullptr ? WWH::HttpMethod::Get() : WWH::HttpMethod::Post();
+        }
+
+        WWH::HttpRequestMessage request{ method, Uri{ uri } };
+        request.Content(content);
+
+        auto headers{ request.Headers() };
+        headers.Accept().TryParseAdd(L"application/json");
+
+        const auto response{ _httpClient.SendRequestAsync(request).get() };
+        const auto string{ response.Content().ReadAsStringAsync().get() };
+        const auto jsonResult{ WDJ::JsonObject::Parse(string) };
 
         THROW_IF_AZURE_ERROR(jsonResult);
         return jsonResult;
     }
 
-    // Method description:
-    // - helper function to send _authenticated_ requests with json bodies whose responses are expected
-    //   to be json. builds on _SendRequestReturningJson.
-    // Arguments:
-    // - the http_request
-    json::value AzureConnection::_SendAuthenticatedRequestReturningJson(http_client& theClient, http_request theRequest)
+    void AzureConnection::_setAccessToken(std::wstring_view accessToken)
     {
-        auto& headers{ theRequest.headers() };
-        headers.add(L"Authorization", L"Bearer " + _accessToken);
-
-        return _SendRequestReturningJson(theClient, std::move(theRequest));
+        _accessToken = accessToken;
+        _httpClient.DefaultRequestHeaders().Authorization(WWH::Headers::HttpCredentialsHeaderValue{ L"Bearer", _accessToken });
     }
 
     // Method description:
     // - helper function to start the device code flow
     // Return value:
     // - the response to the device code flow initiation
-    json::value AzureConnection::_GetDeviceCode()
+    WDJ::JsonObject AzureConnection::_GetDeviceCode()
     {
-        // Initialize the client
-        http_client loginClient(_loginUri);
-
-        // Initialize the request
-        http_request commonRequest(L"POST");
-        commonRequest.set_request_uri(L"common/oauth2/devicecode");
-        const auto body{ fmt::format(L"client_id={}&resource={}", AzureClientID, _wantedResource) };
-        commonRequest.set_body(body.c_str(), L"application/x-www-form-urlencoded");
-
-        // Send the request and receive the response as a json value
-        return _SendRequestReturningJson(loginClient, commonRequest);
+        auto uri{ fmt::format(L"{}common/oauth2/devicecode", _loginUri) };
+        WWH::HttpFormUrlEncodedContent content{
+            std::unordered_map<winrt::hstring, winrt::hstring>{
+                { winrt::hstring{ L"client_id" }, winrt::hstring{ AzureClientID } },
+                { winrt::hstring{ L"resource" }, winrt::hstring{ _wantedResource } },
+            }
+        };
+        return _SendRequestReturningJson(uri, content);
     }
 
     // Method description:
@@ -815,14 +849,17 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
     // Return value:
     // - if authentication is done successfully, then return the response from the server
     // - else, throw an exception
-    json::value AzureConnection::_WaitForUser(const utility::string_t deviceCode, int pollInterval, int expiresIn)
+    WDJ::JsonObject AzureConnection::_WaitForUser(const winrt::hstring& deviceCode, int pollInterval, int expiresIn)
     {
-        // Initialize the client
-        http_client pollingClient(_loginUri);
-
-        // Continuously send a poll request until the user authenticates
-        const auto body{ fmt::format(L"grant_type=device_code&resource={}&client_id={}&code={}", _wantedResource, AzureClientID, deviceCode) };
-        const auto requestUri = L"common/oauth2/token";
+        auto uri{ fmt::format(L"{}common/oauth2/token", _loginUri) };
+        WWH::HttpFormUrlEncodedContent content{
+            std::unordered_map<winrt::hstring, winrt::hstring>{
+                { winrt::hstring{ L"grant_type" }, winrt::hstring{ L"device_code" } },
+                { winrt::hstring{ L"client_id" }, winrt::hstring{ AzureClientID } },
+                { winrt::hstring{ L"resource" }, winrt::hstring{ _wantedResource } },
+                { winrt::hstring{ L"code" }, deviceCode },
+            }
+        };
 
         // use a steady clock here so it's not impacted by local time discontinuities
         const auto tokenExpiry{ std::chrono::steady_clock::now() + std::chrono::seconds(expiresIn) };
@@ -837,14 +874,11 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
                 break;
             }
 
-            http_request pollRequest(L"POST");
-            pollRequest.set_request_uri(requestUri);
-            pollRequest.set_body(body.c_str(), L"application/x-www-form-urlencoded");
-
             try
             {
-                auto response{ _SendRequestReturningJson(pollingClient, pollRequest) };
+                auto response = _SendRequestReturningJson(uri, content);
                 _WriteStringWithNewline(RS_(L"AzureSuccessfullyAuthenticated"));
+
                 // Got a valid response: we're done
                 return response;
             }
@@ -859,7 +893,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
             } // uncaught exceptions bubble up to the caller
         }
 
-        return json::value::null();
+        return nullptr;
     }
 
     // Method description:
@@ -868,16 +902,11 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
     // - the response which contains a list of the user's Azure tenants
     void AzureConnection::_PopulateTenantList()
     {
-        // Initialize the client
-        http_client tenantClient(_resourceUri);
-
-        // Initialize the request
-        http_request tenantRequest(L"GET");
-        tenantRequest.set_request_uri(L"tenants?api-version=2020-01-01");
+        auto uri{ fmt::format(L"{}tenants?api-version=2020-01-01", _resourceUri) };
 
         // Send the request and return the response as a json value
-        auto tenantResponse{ _SendAuthenticatedRequestReturningJson(tenantClient, tenantRequest) };
-        auto tenantList{ tenantResponse.at(L"value").as_array() };
+        auto tenantResponse{ _SendRequestReturningJson(uri, nullptr) };
+        auto tenantList{ tenantResponse.GetNamedArray(L"value") };
 
         _tenantList.clear();
         std::transform(tenantList.begin(), tenantList.end(), std::back_inserter(_tenantList), _crackTenant);
@@ -889,82 +918,73 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
     // - the response with the new tokens
     void AzureConnection::_RefreshTokens()
     {
-        // Initialize the client
-        http_client refreshClient(_loginUri);
-
-        // Initialize the request
-        http_request refreshRequest(L"POST");
-        refreshRequest.set_request_uri(_currentTenant->ID + L"/oauth2/token");
-        const auto body{ fmt::format(L"client_id={}&resource={}&grant_type=refresh_token&refresh_token={}", AzureClientID, _wantedResource, _refreshToken) };
-        refreshRequest.set_body(body.c_str(), L"application/x-www-form-urlencoded");
+        auto uri{ fmt::format(L"{}{}/oauth2/token", _loginUri, _currentTenant->ID) };
+        WWH::HttpFormUrlEncodedContent content{
+            std::unordered_map<winrt::hstring, winrt::hstring>{
+                { winrt::hstring{ L"grant_type" }, winrt::hstring{ L"refresh_token" } },
+                { winrt::hstring{ L"client_id" }, winrt::hstring{ AzureClientID } },
+                { winrt::hstring{ L"resource" }, winrt::hstring{ _wantedResource } },
+                { winrt::hstring{ L"refresh_token" }, winrt::hstring{ _refreshToken } },
+            }
+        };
 
         // Send the request and return the response as a json value
-        auto refreshResponse{ _SendRequestReturningJson(refreshClient, refreshRequest) };
-        _accessToken = refreshResponse.at(L"access_token").as_string();
-        _refreshToken = refreshResponse.at(L"refresh_token").as_string();
-        _expiry = std::stoi(refreshResponse.at(L"expires_on").as_string());
+        auto refreshResponse{ _SendRequestReturningJson(uri, content) };
+        _setAccessToken(refreshResponse.GetNamedString(L"access_token"));
+        _refreshToken = refreshResponse.GetNamedString(L"refresh_token");
+        _expiry = std::stoi(winrt::to_string(refreshResponse.GetNamedString(L"expires_on")));
     }
 
     // Method description:
     // - helper function to get the user's cloud shell settings
     // Return value:
     // - the user's cloud shell settings
-    json::value AzureConnection::_GetCloudShellUserSettings()
+    WDJ::JsonObject AzureConnection::_GetCloudShellUserSettings()
     {
-        // Initialize client
-        http_client settingsClient(_resourceUri);
-
-        // Initialize request
-        http_request settingsRequest(L"GET");
-        settingsRequest.set_request_uri(L"providers/Microsoft.Portal/userSettings/cloudconsole?api-version=2018-10-01");
-
-        return _SendAuthenticatedRequestReturningJson(settingsClient, settingsRequest);
+        auto uri{ fmt::format(L"{}providers/Microsoft.Portal/userSettings/cloudconsole?api-version=2020-04-01-preview", _resourceUri) };
+        return _SendRequestReturningJson(uri, nullptr);
     }
 
     // Method description:
     // - helper function to request for a cloud shell
     // Return value:
     // - the uri for the cloud shell
-    utility::string_t AzureConnection::_GetCloudShell()
+    winrt::hstring AzureConnection::_GetCloudShell()
     {
-        // Initialize client
-        http_client cloudShellClient(_resourceUri);
+        auto uri{ fmt::format(L"{}providers/Microsoft.Portal/consoles/default?api-version=2020-04-01-preview", _resourceUri) };
 
-        // Initialize request
-        http_request shellRequest(L"PUT");
-        shellRequest.set_request_uri(L"providers/Microsoft.Portal/consoles/default?api-version=2018-10-01");
-        // { "properties": { "osType": "linux" } }
-        auto body = json::value::object({ { U("properties"), json::value::object({ { U("osType"), json::value::string(U("linux")) } }) } });
-        shellRequest.set_body(body);
+        WWH::HttpStringContent content{
+            LR"-({"properties": {"osType": "linux"}})-",
+            WSS::UnicodeEncoding::Utf8,
+            L"application/json"
+        };
 
-        // Send the request and get the response as a json value
-        const auto cloudShell = _SendAuthenticatedRequestReturningJson(cloudShellClient, shellRequest);
+        const auto cloudShell = _SendRequestReturningJson(uri, content, WWH::HttpMethod::Put());
 
         // Return the uri
-        return cloudShell.at(L"properties").at(L"uri").as_string() + L"/";
+        return winrt::hstring{ std::wstring{ cloudShell.GetNamedObject(L"properties").GetNamedString(L"uri") } + L"/" };
     }
 
     // Method description:
     // - helper function to request for a terminal
     // Return value:
     // - the uri for the terminal
-    utility::string_t AzureConnection::_GetTerminal(utility::string_t shellType)
+    winrt::hstring AzureConnection::_GetTerminal(const winrt::hstring& shellType)
     {
-        // Initialize client
-        http_client terminalClient(_cloudShellUri);
+        auto uri{ fmt::format(L"{}terminals?cols={}&rows={}&version=2019-01-01&shell={}", _cloudShellUri, _initialCols, _initialRows, shellType) };
 
-        // Initialize the request
-        http_request terminalRequest(L"POST");
-        terminalRequest.set_request_uri(fmt::format(L"terminals?cols={}&rows={}&version=2019-01-01&shell={}", _initialCols, _initialRows, shellType));
-        // LOAD-BEARING. the API returns "'content-type' should be 'application/json' or 'multipart/form-data'"
-        terminalRequest.set_body(json::value::null());
+        WWH::HttpStringContent content{
+            L"",
+            WSS::UnicodeEncoding::Utf8,
+            // LOAD-BEARING. the API returns "'content-type' should be 'application/json' or 'multipart/form-data'"
+            L"application/json"
+        };
 
-        // Send the request and get the response as a json value
-        const auto terminalResponse = _SendAuthenticatedRequestReturningJson(terminalClient, terminalRequest);
-        _terminalID = terminalResponse.at(L"id").as_string();
+        const auto terminalResponse = _SendRequestReturningJson(uri, content);
+        _terminalID = terminalResponse.GetNamedString(L"id");
 
         // Return the uri
-        return terminalResponse.at(L"socketUri").as_string();
+        return terminalResponse.GetNamedString(L"socketUri");
     }
 
     // Method description:
@@ -972,16 +992,17 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
     // - we store the display name, tenant ID, access/refresh tokens, and token expiry
     void AzureConnection::_StoreCredential()
     {
-        json::value userName;
-        userName[U("ver")] = CurrentCredentialVersion;
+        WDJ::JsonObject userName;
+        userName.SetNamedValue(L"ver", WDJ::JsonValue::CreateNumberValue(CurrentCredentialVersion));
         _packTenant(userName, *_currentTenant);
-        json::value passWord;
-        passWord[U("accessToken")] = json::value::string(_accessToken);
-        passWord[U("refreshToken")] = json::value::string(_refreshToken);
-        passWord[U("expiry")] = json::value::string(std::to_wstring(_expiry));
+
+        WDJ::JsonObject passWord;
+        passWord.SetNamedValue(L"accessToken", WDJ::JsonValue::CreateStringValue(_accessToken));
+        passWord.SetNamedValue(L"refreshToken", WDJ::JsonValue::CreateStringValue(_refreshToken));
+        passWord.SetNamedValue(L"expiry", WDJ::JsonValue::CreateStringValue(std::to_wstring(_expiry)));
 
         PasswordVault vault;
-        PasswordCredential newCredential{ PasswordVaultResourceName, userName.serialize(), passWord.serialize() };
+        PasswordCredential newCredential{ PasswordVaultResourceName, userName.Stringify(), passWord.Stringify() };
         vault.Add(newCredential);
     }
 
diff --git a/src/cascadia/TerminalConnection/AzureConnection.h b/src/cascadia/TerminalConnection/AzureConnection.h
index 22c62699ca9..cedd76757af 100644
--- a/src/cascadia/TerminalConnection/AzureConnection.h
+++ b/src/cascadia/TerminalConnection/AzureConnection.h
@@ -5,9 +5,6 @@
 
 #include "AzureConnection.g.h"
 
-#include <cpprest/http_client.h>
-#include <cpprest/http_listener.h>
-#include <cpprest/ws_client.h>
 #include <mutex>
 #include <condition_variable>
 
@@ -56,30 +53,30 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
         void _RunStoreState();
         void _RunConnectState();
 
-        const utility::string_t _loginUri{ U("https://login.microsoftonline.com/") };
-        const utility::string_t _resourceUri{ U("https://management.azure.com/") };
-        const utility::string_t _wantedResource{ U("https://management.core.windows.net/") };
+        static constexpr std::wstring_view _loginUri{ L"https://login.microsoftonline.com/" };
+        static constexpr std::wstring_view _resourceUri{ L"https://management.azure.com/" };
+        static constexpr std::wstring_view _wantedResource{ L"https://management.core.windows.net/" };
         const int _expireLimit{ 2700 };
-        utility::string_t _accessToken;
-        utility::string_t _refreshToken;
+        winrt::hstring _accessToken;
+        winrt::hstring _refreshToken;
         int _expiry{ 0 };
-        utility::string_t _cloudShellUri;
-        utility::string_t _terminalID;
+        winrt::hstring _cloudShellUri;
+        winrt::hstring _terminalID;
 
         std::vector<::Microsoft::Terminal::Azure::Tenant> _tenantList;
         std::optional<::Microsoft::Terminal::Azure::Tenant> _currentTenant;
 
         void _WriteStringWithNewline(const std::wstring_view str);
         void _WriteCaughtExceptionRecord();
-        web::json::value _SendRequestReturningJson(web::http::client::http_client& theClient, web::http::http_request theRequest);
-        web::json::value _SendAuthenticatedRequestReturningJson(web::http::client::http_client& theClient, web::http::http_request theRequest);
-        web::json::value _GetDeviceCode();
-        web::json::value _WaitForUser(utility::string_t deviceCode, int pollInterval, int expiresIn);
+        winrt::Windows::Data::Json::JsonObject _SendRequestReturningJson(std::wstring_view uri, const winrt::Windows::Web::Http::IHttpContent& content = nullptr, winrt::Windows::Web::Http::HttpMethod method = nullptr);
+        void _setAccessToken(std::wstring_view accessToken);
+        winrt::Windows::Data::Json::JsonObject _GetDeviceCode();
+        winrt::Windows::Data::Json::JsonObject _WaitForUser(const winrt::hstring& deviceCode, int pollInterval, int expiresIn);
         void _PopulateTenantList();
         void _RefreshTokens();
-        web::json::value _GetCloudShellUserSettings();
-        utility::string_t _GetCloudShell();
-        utility::string_t _GetTerminal(utility::string_t shellType);
+        winrt::Windows::Data::Json::JsonObject _GetCloudShellUserSettings();
+        winrt::hstring _GetCloudShell();
+        winrt::hstring _GetTerminal(const winrt::hstring& shellType);
         void _StoreCredential();
         void _RemoveCredentials();
 
@@ -95,9 +92,16 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
 
         std::optional<std::wstring> _ReadUserInput(InputMode mode);
 
-        web::websockets::client::websocket_client _cloudShellSocket;
+        winrt::Windows::Web::Http::HttpClient _httpClient{ nullptr };
+        wil::unique_winhttp_hinternet _socketSessionHandle;
+        wil::unique_winhttp_hinternet _socketConnectionHandle;
+        wil::unique_winhttp_hinternet _webSocket;
 
-        static std::optional<utility::string_t> _ParsePreferredShellType(const web::json::value& settingsResponse);
+        til::u8state _u8State{};
+        std::wstring _u16Str;
+        std::array<char, 4096> _buffer{};
+
+        static winrt::hstring _ParsePreferredShellType(const winrt::Windows::Data::Json::JsonObject& settingsResponse);
     };
 }
 
diff --git a/src/cascadia/TerminalConnection/TerminalConnection.vcxproj b/src/cascadia/TerminalConnection/TerminalConnection.vcxproj
index 5668399a404..3f7ee847294 100644
--- a/src/cascadia/TerminalConnection/TerminalConnection.vcxproj
+++ b/src/cascadia/TerminalConnection/TerminalConnection.vcxproj
@@ -11,7 +11,6 @@
   </PropertyGroup>
   <PropertyGroup Label="NuGet Dependencies">
     <TerminalCppWinrt>true</TerminalCppWinrt>
-    <TerminalCppRestSDK>true</TerminalCppRestSDK>
   </PropertyGroup>
   <Import Project="..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
   <Import Project="$(OpenConsoleDir)src\common.nugetversions.props" />
@@ -99,39 +98,4 @@
 
   <!-- This -must- go after cppwinrt.build.post.props because that includes many VS-provided props including appcontainer.common.props, which stomps on what cppwinrt.targets did. -->
   <Import Project="$(OpenConsoleDir)src\common.nugetversions.targets" />
-
-  <!--
-  BODGY:
-
-  We depend on `cpprest142*.dll`, which comes from our vcpkg dependency. As a
-  part of the vcpkg dependency restore, msbuild will call the `deployBinary()`
-  function in
-  `packages\vcpkg-cpprestsdk.2.10.14\scripts\BuildSystems\msbuild\AppLocal.ps1`.
-  That function does the actual job of copying the file. It copies it outside of
-  MsBuild. MsBuild then, in the `_CopyFilesMarkedCopyLocal` target, determines
-  that it needs to copy `cpprest142*.dll`, because that dll is a member of
-  `@(ReferencesCopiedInThisBuild)`. However, the file's already been copied, so
-  MsBuild never copies it. But that also prevents MsBuild from setting
-  `WroteAtLeastOneFile`, which then means that MsBuild will never create the
-  .CopyComplete file for this project.
-
-  Because that file is missing, MsBuild will never think the project is up to
-  date, and the FastUpToDate check in VS will always force MsBuild to run a pass
-  on this project.
-
-  To mitigate this, we're adding this other target here, which runs after
-  _CopyFilesMarkedCopyLocal, and always creates the CopyUpToDateMarker. This
-  makes the FastUpToDate check succeed.
-  -->
-
-  <Target
-      Name="_Post_CopyFilesMarkedCopyLocal"
-      AfterTargets="_CopyFilesMarkedCopyLocal"
-      Condition="'@(ReferenceCopyLocalPaths)' != ''">
-
-    <Touch Files="@(CopyUpToDateMarker)"
-      AlwaysCreate="true" />
-  </Target>
-
-
 </Project>
diff --git a/src/cascadia/TerminalConnection/pch.h b/src/cascadia/TerminalConnection/pch.h
index 847f8971bca..124397ca3b7 100644
--- a/src/cascadia/TerminalConnection/pch.h
+++ b/src/cascadia/TerminalConnection/pch.h
@@ -24,8 +24,14 @@
 
 #include "winrt/Windows.Security.Credentials.h"
 #include "winrt/Windows.Foundation.Collections.h"
+#include "winrt/Windows.Web.Http.h"
+#include "winrt/Windows.Web.Http.Headers.h"
+#include "winrt/Windows.Data.Json.h"
 #include <Windows.h>
 
+#include <winhttp.h>
+#include <wil/resource.h>
+
 #include <TraceLoggingProvider.h>
 TRACELOGGING_DECLARE_PROVIDER(g_hTerminalConnectionProvider);
 #include <telemetry/ProjectTelemetry.h>
diff --git a/src/common.nugetversions.targets b/src/common.nugetversions.targets
index 78c37d86330..04cda21acbc 100644
--- a/src/common.nugetversions.targets
+++ b/src/common.nugetversions.targets
@@ -43,9 +43,6 @@
     <!-- TAEF -->
     <Import Project="$(MSBuildThisFileDirectory)..\packages\Microsoft.Taef.10.60.210621002\build\Microsoft.Taef.targets" Condition="'$(TerminalTAEF)' == 'true' and Exists('$(MSBuildThisFileDirectory)..\packages\Microsoft.Taef.10.60.210621002\build\Microsoft.Taef.targets')" />
     
-    <!-- CppRestSDK -->
-    <Import Project="$(MSBuildThisFileDirectory)..\packages\vcpkg-cpprestsdk.2.10.14\build\native\vcpkg-cpprestsdk.targets" Condition="'$(TerminalCppRestSDK)' == 'true' and Exists('$(MSBuildThisFileDirectory)..\packages\vcpkg-cpprestsdk.2.10.14\build\native\vcpkg-cpprestsdk.targets')" />
-
     <!-- VCRTForwarders -->
     <Import Project="$(MSBuildThisFileDirectory)..\packages\Microsoft.VCRTForwarders.140.1.0.4\build\native\Microsoft.VCRTForwarders.140.targets" Condition="'$(TerminalVCRTForwarders)' == 'true' and Exists('$(MSBuildThisFileDirectory)..\packages\Microsoft.VCRTForwarders.140.1.0.4\build\native\Microsoft.VCRTForwarders.140.targets')" />
 
@@ -79,9 +76,6 @@
     <!-- TAEF -->
     <Error Condition="'$(TerminalTAEF)' == 'true' AND !Exists('$(MSBuildThisFileDirectory)..\packages\Microsoft.Taef.10.60.210621002\build\Microsoft.Taef.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(MSBuildThisFileDirectory)..\packages\Microsoft.Taef.10.60.210621002\build\Microsoft.Taef.targets'))" />
 
-    <!-- CppRestSDK -->
-    <Error Condition="'$(TerminalCppRestSDK)' == 'true' AND !Exists('$(MSBuildThisFileDirectory)..\packages\vcpkg-cpprestsdk.2.10.14\build\native\vcpkg-cpprestsdk.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(MSBuildThisFileDirectory)..\packages\vcpkg-cpprestsdk.2.10.14\build\native\vcpkg-cpprestsdk.targets'))" />
-
     <!-- VCRTForwarders -->
     <Error Condition="'$(TerminalVCRTForwarders)' == 'true' AND !Exists('$(MSBuildThisFileDirectory)..\packages\Microsoft.VCRTForwarders.140.1.0.4\build\native\Microsoft.VCRTForwarders.140.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(MSBuildThisFileDirectory)..\packages\Microsoft.VCRTForwarders.140.1.0.4\build\native\Microsoft.VCRTForwarders.140.targets'))" />
 
diff --git a/src/features.xml b/src/features.xml
index e26c217d3bf..13925a66157 100644
--- a/src/features.xml
+++ b/src/features.xml
@@ -145,4 +145,14 @@
         <stage>AlwaysDisabled</stage>
     </feature>
 
+    <feature>
+        <name>Feature_AzureConnectionInProc</name>
+        <description>Host the AzureConnection inside Terminal rather than via TerminalAzBridge</description>
+        <id>4661</id>
+        <stage>AlwaysDisabled</stage>
+        <alwaysEnabledBrandingTokens>
+            <brandingToken>Dev</brandingToken>
+        </alwaysEnabledBrandingTokens>
+    </feature>
+
 </featureStaging>


Test patch: