Set user-agent and cookie headers for 'copy as powershell' command

Updating the _generatePowerShellCommand function in NetworkLogView.ts to
generate a command that can successfully set the user-agent and cookie
headers.

According to the docs for the Invoke-WebRequest PowerShell command, the
-Headers parameter cannot be used to set these headers.

The fix is to create a WebRequestSession session object, populate it
with the user agent and cookie values, and then pass this object to
Invoke-WebRequest using the -WebSession parameter.

I've verified that the new command output works as expected and that a
test server will see the correct request headers.

Bug: 932971
Change-Id: Id5441137a311b223fcfab48bbd2075598a2879e2
Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/2910443
Commit-Queue: Brandon Walderman <[email protected]>
Reviewed-by: John Emau <[email protected]>
Reviewed-by: Sigurd Schneider <[email protected]>
Reviewed-by: Jose Leal <[email protected]>
diff --git a/front_end/panels/network/NetworkLogView.ts b/front_end/panels/network/NetworkLogView.ts
index 0b8b7dd..e4a2748 100644
--- a/front_end/panels/network/NetworkLogView.ts
+++ b/front_end/panels/network/NetworkLogView.ts
@@ -2069,20 +2069,61 @@
 
   async _generatePowerShellCommand(request: SDK.NetworkRequest.NetworkRequest): Promise<string> {
     const command = [];
-    const ignoredHeaders = new Set<string>(
-        ['host', 'connection', 'proxy-connection', 'content-length', 'expect', 'range', 'content-type']);
+    const ignoredHeaders = new Set<string>([
+      'host',
+      'connection',
+      'proxy-connection',
+      'content-length',
+      'expect',
+      'range',
+      'content-type',
+      'user-agent',
+      'cookie',
+    ]);
 
     function escapeString(str: string): string {
       return '"' +
           str.replace(/[`\$"]/g, '`$&').replace(/[^\x20-\x7E]/g, char => '$([char]' + char.charCodeAt(0) + ')') + '"';
     }
 
+    // Generate a WebRequestSession object with the UserAgent and Cookie header values.
+    // This is used to pass the user-agent and cookie headers to Invoke-WebRequest because the Invoke-WebRequest
+    // command does not allow setting these headers through the -Headers parameter. See docs at:
+    // https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/invoke-webrequest?view=powershell-7.1#parameters
+    function generatePowerShellSession(request: SDK.NetworkRequest.NetworkRequest): string|null {
+      const requestHeaders = request.requestHeaders();
+      const props = [];
+
+      const userAgentHeader = requestHeaders.find(({name}) => name.toLowerCase() === 'user-agent');
+      if (userAgentHeader) {
+        props.push(`$session.UserAgent = ${escapeString(userAgentHeader.value)}`);
+      }
+
+      for (const cookie of request.includedRequestCookies()) {
+        const name = escapeString(cookie.name());
+        const value = escapeString(cookie.value());
+        const domain = escapeString(cookie.domain());
+        props.push(`$session.Cookies.Add((New-Object System.Net.Cookie(${name}, ${value}, "/", ${domain})))`);
+      }
+
+      if (props.length) {
+        return '$session = New-Object Microsoft.PowerShell.Commands.WebRequestSession\n' + props.join('\n') + '\n';
+      }
+
+      return null;
+    }
+
     command.push('-Uri ' + escapeString(request.url()));
 
     if (request.requestMethod !== 'GET') {
       command.push('-Method ' + escapeString(request.requestMethod));
     }
 
+    const session = generatePowerShellSession(request);
+    if (session) {
+      command.push('-WebSession $session');
+    }
+
     const requestHeaders = request.requestHeaders();
     const headerNameValuePairs = [];
     for (const header of requestHeaders) {
@@ -2111,7 +2152,11 @@
       }
     }
 
-    return 'Invoke-WebRequest ' + command.join(command.length >= 3 ? ' `\n' : ' ');
+    // The -UseBasicParsing parameter prevents Invoke-WebRequest from using the IE engine for parsing. Basic
+    // parsing is the default behavior in PowerShell 6.0.0+ and the parameter is included here for backwards
+    // compatibility only.
+    const prelude = session || '';
+    return prelude + 'Invoke-WebRequest -UseBasicParsing ' + command.join(command.length >= 3 ? ' `\n' : ' ');
   }
 
   async _generateAllPowerShellCommand(requests: SDK.NetworkRequest.NetworkRequest[]): Promise<string> {