@@ -93,6 +93,11 @@ public abstract class WebRequestPSCmdlet : PSCmdlet, IDisposable
93
93
{
94
94
#region Fields
95
95
96
+ //github.com/ <summary>
97
+ //github.com/ Used to prefix the headers in debug and verbose messaging.
98
+ //github.com/ </summary>
99
+ internal const string DebugHeaderPrefix = "--- " ;
100
+
96
101
//github.com/ <summary>
97
102
//github.com/ Cancellation token source.
98
103
//github.com/ </summary>
@@ -1280,40 +1285,27 @@ internal virtual HttpResponseMessage GetResponse(HttpClient client, HttpRequestM
1280
1285
_cancelToken = new CancellationTokenSource ( ) ;
1281
1286
try
1282
1287
{
1283
- long requestContentLength = request . Content is null ? 0 : request . Content . Headers . ContentLength . Value ;
1284
-
1285
- string reqVerboseMsg = string . Format (
1286
- CultureInfo . CurrentCulture ,
1287
- WebCmdletStrings . WebMethodInvocationVerboseMsg ,
1288
- request . Version ,
1289
- request . Method ,
1290
- requestContentLength ) ;
1291
-
1292
- WriteVerbose ( reqVerboseMsg ) ;
1293
-
1294
- string reqDebugMsg = string . Format (
1295
- CultureInfo . CurrentCulture ,
1296
- WebCmdletStrings . WebRequestDebugMsg ,
1297
- request . ToString ( ) ) ;
1288
+ if ( IsWriteVerboseEnabled ( ) )
1289
+ {
1290
+ WriteWebRequestVerboseInfo ( request ) ;
1291
+ }
1298
1292
1299
- WriteDebug ( reqDebugMsg ) ;
1293
+ if ( IsWriteDebugEnabled ( ) )
1294
+ {
1295
+ WriteWebRequestDebugInfo ( request ) ;
1296
+ }
1300
1297
1301
1298
response = client . SendAsync ( currentRequest , HttpCompletionOption . ResponseHeadersRead , _cancelToken . Token ) . GetAwaiter ( ) . GetResult ( ) ;
1302
1299
1303
- string contentType = ContentHelper . GetContentType ( response ) ;
1304
- long ? contentLength = response . Content . Headers . ContentLength ;
1305
- string respVerboseMsg = contentLength is null
1306
- ? string . Format ( CultureInfo . CurrentCulture , WebCmdletStrings . WebResponseNoSizeVerboseMsg , response . Version , contentType )
1307
- : string . Format ( CultureInfo . CurrentCulture , WebCmdletStrings . WebResponseVerboseMsg , response . Version , contentLength , contentType ) ;
1308
-
1309
- WriteVerbose ( respVerboseMsg ) ;
1310
-
1311
- string resDebugMsg = string . Format (
1312
- CultureInfo . CurrentCulture ,
1313
- WebCmdletStrings . WebResponseDebugMsg ,
1314
- response . ToString ( ) ) ;
1300
+ if ( IsWriteVerboseEnabled ( ) )
1301
+ {
1302
+ WriteWebResponseVerboseInfo ( response ) ;
1303
+ }
1315
1304
1316
- WriteDebug ( resDebugMsg ) ;
1305
+ if ( IsWriteDebugEnabled ( ) )
1306
+ {
1307
+ WriteWebResponseDebugInfo ( response ) ;
1308
+ }
1317
1309
}
1318
1310
catch ( TaskCanceledException ex )
1319
1311
{
@@ -1426,7 +1418,8 @@ internal virtual HttpResponseMessage GetResponse(HttpClient client, HttpRequestM
1426
1418
FillRequestStream ( currentRequest ) ;
1427
1419
}
1428
1420
1429
- totalRequests -- ;
1421
+ // We know the message will usually be at least a certain size, so this reduces allocations.
1422
+ StringBuilder verboseBuilder = new ( 128 ) ;
1430
1423
}
1431
1424
while ( totalRequests > 0 && ! response . IsSuccessStatusCode ) ;
1432
1425
@@ -1437,13 +1430,181 @@ internal virtual void UpdateSession(HttpResponseMessage response)
1437
1430
{
1438
1431
ArgumentNullException . ThrowIfNull ( response ) ;
1439
1432
}
1440
-
1441
1433
#endregion Virtual Methods
1442
1434
1443
1435
#region Helper Methods
1444
1436
1445
1437
internal static TimeSpan ConvertTimeoutSecondsToTimeSpan ( int timeout ) => timeout > 0 ? TimeSpan . FromSeconds ( timeout ) : Timeout . InfiniteTimeSpan ;
1446
1438
1439
+ private void WriteWebRequestVerboseInfo ( HttpRequestMessage request )
1440
+ {
1441
+ try
1442
+ {
1443
+ // We know the message will usually be at least a certain size, so this reduces allocations.
1444
+ StringBuilder verboseBuilder = new ( 128 ) ;
1445
+
1446
+ // "Redact" the query string from verbose output, the details will be visible in Debug output
1447
+ string uriWithoutQuery = request . RequestUri . GetLeftPart ( UriPartial . Path ) ;
1448
+ verboseBuilder . Append ( $ "WebRequest: v{ request . Version } { request . Method } { uriWithoutQuery } ") ;
1449
+
1450
+ string requestContentType = ContentHelper . GetContentType ( request ) ;
1451
+ if ( requestContentType is not null )
1452
+ {
1453
+ verboseBuilder . Append ( $ " with { requestContentType } payload") ;
1454
+ }
1455
+
1456
+ long ? requestContentLength = request . Content ? . Headers ? . ContentLength ;
1457
+ if ( requestContentLength is not null )
1458
+ {
1459
+ verboseBuilder . Append ( $ " ({ ContentHelper . GetFriendlyContentLength ( requestContentLength ) } )") ;
1460
+ }
1461
+
1462
+ WriteVerbose ( verboseBuilder . ToString ( ) . Trim ( ) ) ;
1463
+ }
1464
+ catch ( Exception ex )
1465
+ {
1466
+ // Just in case there are any edge cases we missed, we don't break workflows with an exception
1467
+ WriteVerbose ( $ "Failed to Write WebRequest Verbose Info: { ex } { ex . StackTrace } ") ;
1468
+ }
1469
+ }
1470
+
1471
+ private void WriteWebRequestDebugInfo ( HttpRequestMessage request )
1472
+ {
1473
+ try
1474
+ {
1475
+ // We know the message will usually be at least a certain size, so this reduces allocations.
1476
+ StringBuilder debugBuilder = new ( "WebRequest Detail" + Environment . NewLine , 512 ) ;
1477
+
1478
+ if ( ! string . IsNullOrEmpty ( request . RequestUri . Query ) )
1479
+ {
1480
+ debugBuilder . Append ( DebugHeaderPrefix ) . AppendLine ( "QUERY" ) ;
1481
+ string [ ] queryParams = request . RequestUri . Query . TrimStart ( '?' ) . Split ( '&' ) ;
1482
+ foreach ( string param in queryParams )
1483
+ {
1484
+ debugBuilder . AppendLine ( param ) ;
1485
+ }
1486
+ }
1487
+
1488
+ debugBuilder . Append ( DebugHeaderPrefix ) . AppendLine ( "HEADERS" ) ;
1489
+ List < KeyValuePair < string , IEnumerable < string > > > allHeaders = new ( ) ;
1490
+
1491
+ foreach ( var headerSet in new HttpHeaders [ ] { request . Headers , request . Content ? . Headers } )
1492
+ {
1493
+ foreach ( var header in headerSet )
1494
+ {
1495
+ debugBuilder
1496
+ . Append ( $ "{ header . Key } : ")
1497
+ . AppendJoin ( ", " , header . Value )
1498
+ . AppendLine ( ) ;
1499
+ }
1500
+ }
1501
+
1502
+ if ( request . Content is not null )
1503
+ {
1504
+ debugBuilder
1505
+ . AppendLine ( DebugHeaderPrefix + "BODY" )
1506
+ . AppendLine ( request . Content switch
1507
+ {
1508
+ StringContent stringContent => stringContent
1509
+ . ReadAsStringAsync ( _cancelToken . Token )
1510
+ . GetAwaiter ( ) . GetResult ( ) ,
1511
+ MultipartFormDataContent multipartContent => "=> Multipart Form Content"
1512
+ + Environment . NewLine
1513
+ + multipartContent . ReadAsStringAsync ( _cancelToken . Token )
1514
+ . GetAwaiter ( ) . GetResult ( ) ,
1515
+ ByteArrayContent byteContent => InFile is not null
1516
+ ? "[Binary content: "
1517
+ + ContentHelper . GetFriendlyContentLength ( byteContent . Headers . ContentLength )
1518
+ + "]"
1519
+ : byteContent . ReadAsStringAsync ( _cancelToken . Token ) . GetAwaiter ( ) . GetResult ( ) ,
1520
+ StreamContent streamContent =>
1521
+ "[Stream content: " + ContentHelper . GetFriendlyContentLength ( streamContent . Headers . ContentLength ) + "]" ,
1522
+ _ => "[Unknown content type]" ,
1523
+ } ) ;
1524
+ }
1525
+
1526
+ WriteDebug ( debugBuilder . ToString ( ) . Trim ( ) ) ;
1527
+ }
1528
+ catch ( Exception ex )
1529
+ {
1530
+ // Just in case there are any edge cases we missed, we don't break workflows with an exception
1531
+ WriteVerbose ( $ "Failed to Write WebRequest Debug Info: { ex } { ex . StackTrace } ") ;
1532
+ }
1533
+ }
1534
+
1535
+ private void WriteWebResponseVerboseInfo ( HttpResponseMessage response )
1536
+ {
1537
+ try
1538
+ {
1539
+ // We know the message will usually be at least a certain size, so this reduces allocations.
1540
+ StringBuilder verboseBuilder = new ( 128 ) ;
1541
+ verboseBuilder . Append ( $ "WebResponse: { ( int ) response . StatusCode } { response . ReasonPhrase ?? response . StatusCode . ToString ( ) } ") ;
1542
+
1543
+ string responseContentType = ContentHelper . GetContentType ( response ) ;
1544
+ if ( responseContentType is not null )
1545
+ {
1546
+ verboseBuilder . Append ( $ " with { responseContentType } payload") ;
1547
+ }
1548
+
1549
+ long ? responseContentLength = response . Content ? . Headers ? . ContentLength ;
1550
+ if ( responseContentLength is not null )
1551
+ {
1552
+ verboseBuilder . Append ( $ " ({ ContentHelper . GetFriendlyContentLength ( responseContentLength ) } )") ;
1553
+ }
1554
+
1555
+ WriteVerbose ( verboseBuilder . ToString ( ) . Trim ( ) ) ;
1556
+ }
1557
+ catch ( Exception ex )
1558
+ {
1559
+ // Just in case there are any edge cases we missed, we don't break workflows with an exception
1560
+ WriteVerbose ( $ "Failed to Write WebResponse Verbose Info: { ex } { ex . StackTrace } ") ;
1561
+ }
1562
+ }
1563
+
1564
+ private void WriteWebResponseDebugInfo ( HttpResponseMessage response )
1565
+ {
1566
+ try
1567
+ {
1568
+ // We know the message will usually be at least a certain size, so this reduces allocations.
1569
+ StringBuilder debugBuilder = new ( "WebResponse Detail" + Environment . NewLine , 512 ) ;
1570
+
1571
+ debugBuilder . AppendLine ( DebugHeaderPrefix + "HEADERS" ) ;
1572
+
1573
+ foreach ( var headerSet in new HttpHeaders [ ] { response . Headers , response . Content ? . Headers } )
1574
+ {
1575
+ foreach ( var header in headerSet )
1576
+ {
1577
+ debugBuilder . AppendLine ( $ "{ header . Key } : { string . Join ( ", " , header . Value ) } ") ;
1578
+ }
1579
+ }
1580
+
1581
+ if ( response . Content is not null )
1582
+ {
1583
+ debugBuilder . AppendLine ( DebugHeaderPrefix + "BODY" ) ;
1584
+
1585
+ if ( ContentHelper . IsTextBasedContentType ( ContentHelper . GetContentType ( response ) ) )
1586
+ {
1587
+ debugBuilder . AppendLine (
1588
+ response . Content . ReadAsStringAsync ( _cancelToken . Token )
1589
+ . GetAwaiter ( ) . GetResult ( ) ) ;
1590
+ }
1591
+ else
1592
+ {
1593
+ string friendlyContentLength = ContentHelper . GetFriendlyContentLength (
1594
+ response . Content ? . Headers ? . ContentLength ) ;
1595
+ debugBuilder . AppendLine ( $ "[Binary content: { friendlyContentLength } ]") ;
1596
+ }
1597
+ }
1598
+
1599
+ WriteDebug ( debugBuilder . ToString ( ) . Trim ( ) ) ;
1600
+ }
1601
+ catch ( Exception ex )
1602
+ {
1603
+ // Just in case there are any edge cases we missed, we don't break workflows with an exception
1604
+ WriteVerbose ( $ "Failed to Write WebResponse Debug Info: { ex } { ex . StackTrace } ") ;
1605
+ }
1606
+ }
1607
+
1447
1608
private Uri PrepareUri ( Uri uri )
1448
1609
{
1449
1610
uri = CheckProtocol ( uri ) ;
0 commit comments