11// Copyright (c) Just Eat, 2017. All rights reserved.
22// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information.
33
4+ using System . Collections ;
5+ using System . Diagnostics . CodeAnalysis ;
46using System . Net ;
57
68namespace JustEat . HttpClientInterception ;
@@ -426,10 +428,7 @@ public HttpRequestInterceptionBuilder WithContentHeader(string name, IEnumerable
426428 throw new ArgumentNullException ( nameof ( values ) ) ;
427429 }
428430
429- if ( _contentHeaders is null )
430- {
431- _contentHeaders = new Dictionary < string , ICollection < string > > ( StringComparer . OrdinalIgnoreCase ) ;
432- }
431+ _contentHeaders ??= new Dictionary < string , ICollection < string > > ( StringComparer . OrdinalIgnoreCase ) ;
433432
434433 if ( ! _contentHeaders . TryGetValue ( name , out var current ) )
435434 {
@@ -500,6 +499,29 @@ public HttpRequestInterceptionBuilder WithContentHeaders(IDictionary<string, str
500499 return this ;
501500 }
502501
502+ /// <summary>
503+ /// Sets a delegate to a method that generates any custom HTTP content headers to use.
504+ /// </summary>
505+ /// <param name="headerFactory">Any delegate that creates any custom HTTP content headers to use.</param>
506+ /// <returns>
507+ /// The current <see cref="HttpRequestInterceptionBuilder"/>.
508+ /// </returns>
509+ /// <exception cref="ArgumentNullException">
510+ /// <paramref name="headerFactory"/> is <see langword="null"/>.
511+ /// </exception>
512+ public HttpRequestInterceptionBuilder WithContentHeaders (
513+ Func < IEnumerable < KeyValuePair < string , ICollection < string > > > > headerFactory )
514+ {
515+ if ( headerFactory is null )
516+ {
517+ throw new ArgumentNullException ( nameof ( headerFactory ) ) ;
518+ }
519+
520+ _contentHeaders = new DynamicDictionary ( headerFactory ) ;
521+ IncrementRevision ( ) ;
522+ return this ;
523+ }
524+
503525 /// <summary>
504526 /// Sets a custom HTTP response header to use with a single value.
505527 /// </summary>
@@ -551,10 +573,7 @@ public HttpRequestInterceptionBuilder WithResponseHeader(string name, IEnumerabl
551573 throw new ArgumentNullException ( nameof ( values ) ) ;
552574 }
553575
554- if ( _responseHeaders is null )
555- {
556- _responseHeaders = new Dictionary < string , ICollection < string > > ( StringComparer . OrdinalIgnoreCase ) ;
557- }
576+ _responseHeaders ??= new Dictionary < string , ICollection < string > > ( StringComparer . OrdinalIgnoreCase ) ;
558577
559578 if ( ! _responseHeaders . TryGetValue ( name , out ICollection < string > ? current ) )
560579 {
@@ -617,6 +636,29 @@ public HttpRequestInterceptionBuilder WithResponseHeaders(IDictionary<string, IC
617636 return this ;
618637 }
619638
639+ /// <summary>
640+ /// Sets a delegate to a method that generates any custom HTTP response headers to use.
641+ /// </summary>
642+ /// <param name="headerFactory">Any delegate that creates any custom HTTP response headers to use.</param>
643+ /// <returns>
644+ /// The current <see cref="HttpRequestInterceptionBuilder"/>.
645+ /// </returns>
646+ /// <exception cref="ArgumentNullException">
647+ /// <paramref name="headerFactory"/> is <see langword="null"/>.
648+ /// </exception>
649+ public HttpRequestInterceptionBuilder WithResponseHeaders (
650+ Func < IEnumerable < KeyValuePair < string , ICollection < string > > > > headerFactory )
651+ {
652+ if ( headerFactory is null )
653+ {
654+ throw new ArgumentNullException ( nameof ( headerFactory ) ) ;
655+ }
656+
657+ _responseHeaders = new DynamicDictionary ( headerFactory ) ;
658+ IncrementRevision ( ) ;
659+ return this ;
660+ }
661+
620662 /// <summary>
621663 /// Sets media type for the response body content.
622664 /// </summary>
@@ -869,10 +911,7 @@ public HttpRequestInterceptionBuilder ForRequestHeader(string name, IEnumerable<
869911 throw new ArgumentNullException ( nameof ( values ) ) ;
870912 }
871913
872- if ( _requestHeaders is null )
873- {
874- _requestHeaders = new Dictionary < string , ICollection < string > > ( StringComparer . OrdinalIgnoreCase ) ;
875- }
914+ _requestHeaders ??= new Dictionary < string , ICollection < string > > ( StringComparer . OrdinalIgnoreCase ) ;
876915
877916 if ( ! _requestHeaders . TryGetValue ( name , out ICollection < string > ? current ) )
878917 {
@@ -931,16 +970,50 @@ public HttpRequestInterceptionBuilder ForRequestHeaders(IDictionary<string, stri
931970 /// <returns>
932971 /// The current <see cref="HttpRequestInterceptionBuilder"/>.
933972 /// </returns>
973+ /// <exception cref="ArgumentNullException">
974+ /// <paramref name="headers"/> is <see langword="null"/>.
975+ /// </exception>
934976 /// <remarks>
935977 /// HTTP request headers are only tested for interception if the URI requested was registered for interception.
936978 /// </remarks>
937979 public HttpRequestInterceptionBuilder ForRequestHeaders ( IDictionary < string , ICollection < string > > headers )
938980 {
981+ if ( headers is null )
982+ {
983+ throw new ArgumentNullException ( nameof ( headers ) ) ;
984+ }
985+
939986 _requestHeaders = new Dictionary < string , ICollection < string > > ( headers , StringComparer . OrdinalIgnoreCase ) ;
940987 IncrementRevision ( ) ;
941988 return this ;
942989 }
943990
991+ /// <summary>
992+ /// Sets a delegate to a method that generates the HTTP request headers to intercept.
993+ /// </summary>
994+ /// <param name="headerFactory">Any delegate that returns the HTTP request headers to intercept..</param>
995+ /// <returns>
996+ /// The current <see cref="HttpRequestInterceptionBuilder"/>.
997+ /// </returns>
998+ /// <exception cref="ArgumentNullException">
999+ /// <paramref name="headerFactory"/> is <see langword="null"/>.
1000+ /// </exception>
1001+ /// <remarks>
1002+ /// HTTP request headers are only tested for interception if the URI requested was registered for interception.
1003+ /// </remarks>
1004+ public HttpRequestInterceptionBuilder ForRequestHeaders (
1005+ Func < IEnumerable < KeyValuePair < string , ICollection < string > > > > headerFactory )
1006+ {
1007+ if ( headerFactory is null )
1008+ {
1009+ throw new ArgumentNullException ( nameof ( headerFactory ) ) ;
1010+ }
1011+
1012+ _requestHeaders = new DynamicDictionary ( headerFactory ) ;
1013+ IncrementRevision ( ) ;
1014+ return this ;
1015+ }
1016+
9441017 /// <summary>
9451018 /// Configures the builder to match any request whose HTTP content meets the criteria defined by the specified predicate.
9461019 /// </summary>
@@ -983,40 +1056,61 @@ internal HttpInterceptionResponse Build()
9831056 Version = _version ,
9841057 } ;
9851058
986- if ( _requestHeaders ? . Count > 0 )
1059+ if ( _requestHeaders is not null )
9871060 {
988- var headers = new Dictionary < string , IEnumerable < string > > ( _requestHeaders . Count ) ;
989-
990- foreach ( var pair in _requestHeaders )
1061+ if ( _requestHeaders is DynamicDictionary factory )
9911062 {
992- headers [ pair . Key ] = pair . Value ;
1063+ response . RequestHeaders = factory ;
9931064 }
1065+ else if ( _requestHeaders . Count > 0 )
1066+ {
1067+ var headers = new Dictionary < string , IEnumerable < string > > ( _requestHeaders . Count ) ;
9941068
995- response . RequestHeaders = headers ;
1069+ foreach ( var pair in _requestHeaders )
1070+ {
1071+ headers [ pair . Key ] = pair . Value ;
1072+ }
1073+
1074+ response . RequestHeaders = headers ;
1075+ }
9961076 }
9971077
998- if ( _responseHeaders ? . Count > 0 )
1078+ if ( _responseHeaders is not null )
9991079 {
1000- var headers = new Dictionary < string , IEnumerable < string > > ( _responseHeaders . Count ) ;
1001-
1002- foreach ( var pair in _responseHeaders )
1080+ if ( _responseHeaders is DynamicDictionary factory )
10031081 {
1004- headers [ pair . Key ] = pair . Value ;
1082+ response . ResponseHeaders = factory ;
10051083 }
1084+ else if ( _responseHeaders . Count > 0 )
1085+ {
1086+ var headers = new Dictionary < string , IEnumerable < string > > ( _responseHeaders . Count ) ;
1087+
1088+ foreach ( var pair in _responseHeaders )
1089+ {
1090+ headers [ pair . Key ] = pair . Value ;
1091+ }
10061092
1007- response . ResponseHeaders = headers ;
1093+ response . ResponseHeaders = headers ;
1094+ }
10081095 }
10091096
1010- if ( _contentHeaders ? . Count > 0 )
1097+ if ( _contentHeaders is not null )
10111098 {
1012- var headers = new Dictionary < string , IEnumerable < string > > ( _contentHeaders . Count ) ;
1013-
1014- foreach ( var pair in _contentHeaders )
1099+ if ( _contentHeaders is DynamicDictionary factory )
10151100 {
1016- headers [ pair . Key ] = pair . Value ;
1101+ response . ContentHeaders = factory ;
10171102 }
1103+ else if ( _contentHeaders . Count > 0 )
1104+ {
1105+ var headers = new Dictionary < string , IEnumerable < string > > ( _contentHeaders . Count ) ;
1106+
1107+ foreach ( var pair in _contentHeaders )
1108+ {
1109+ headers [ pair . Key ] = pair . Value ;
1110+ }
10181111
1019- response . ContentHeaders = headers ;
1112+ response . ContentHeaders = headers ;
1113+ }
10201114 }
10211115
10221116 return response ;
@@ -1036,4 +1130,105 @@ private void IncrementRevision()
10361130 _revision ++ ;
10371131 }
10381132 }
1133+
1134+ private sealed class DynamicDictionary :
1135+ IDictionary < string , ICollection < string > > ,
1136+ IEnumerable < KeyValuePair < string , IEnumerable < string > > >
1137+ {
1138+ private readonly Func < IEnumerable < KeyValuePair < string , ICollection < string > > > > _generator ;
1139+
1140+ internal DynamicDictionary ( Func < IEnumerable < KeyValuePair < string , ICollection < string > > > > generator )
1141+ {
1142+ _generator = generator ;
1143+ }
1144+
1145+ [ ExcludeFromCodeCoverage ]
1146+ public ICollection < string > Keys => throw new NotSupportedException ( ) ;
1147+
1148+ [ ExcludeFromCodeCoverage ]
1149+ public ICollection < ICollection < string > > Values => throw new NotSupportedException ( ) ;
1150+
1151+ [ ExcludeFromCodeCoverage ]
1152+ public int Count => throw new NotSupportedException ( ) ;
1153+
1154+ [ ExcludeFromCodeCoverage ]
1155+ public bool IsReadOnly => true ;
1156+
1157+ [ ExcludeFromCodeCoverage ]
1158+ public ICollection < string > this [ string key ]
1159+ {
1160+ get => throw new NotSupportedException ( ) ;
1161+ set => throw new NotSupportedException ( ) ;
1162+ }
1163+
1164+ public IEnumerator < KeyValuePair < string , ICollection < string > > > GetEnumerator ( )
1165+ => _generator ( ) . GetEnumerator ( ) ;
1166+
1167+ IEnumerator IEnumerable . GetEnumerator ( )
1168+ => GetEnumerator ( ) ;
1169+
1170+ IEnumerator < KeyValuePair < string , IEnumerable < string > > > IEnumerable < KeyValuePair < string , IEnumerable < string > > > . GetEnumerator ( )
1171+ => new EnumeratorAdapter ( GetEnumerator ( ) ) ;
1172+
1173+ [ ExcludeFromCodeCoverage ]
1174+ public void Add ( string key , ICollection < string > value )
1175+ => throw new NotSupportedException ( ) ;
1176+
1177+ [ ExcludeFromCodeCoverage ]
1178+ public void Add ( KeyValuePair < string , ICollection < string > > item )
1179+ => throw new NotSupportedException ( ) ;
1180+
1181+ [ ExcludeFromCodeCoverage ]
1182+ public void Clear ( )
1183+ => throw new NotSupportedException ( ) ;
1184+
1185+ [ ExcludeFromCodeCoverage ]
1186+ public bool Contains ( KeyValuePair < string , ICollection < string > > item )
1187+ => throw new NotSupportedException ( ) ;
1188+
1189+ [ ExcludeFromCodeCoverage ]
1190+ public bool ContainsKey ( string key )
1191+ => throw new NotSupportedException ( ) ;
1192+
1193+ [ ExcludeFromCodeCoverage ]
1194+ public void CopyTo ( KeyValuePair < string , ICollection < string > > [ ] array , int arrayIndex )
1195+ => throw new NotSupportedException ( ) ;
1196+
1197+ [ ExcludeFromCodeCoverage ]
1198+ public bool Remove ( string key )
1199+ => throw new NotSupportedException ( ) ;
1200+
1201+ [ ExcludeFromCodeCoverage ]
1202+ public bool Remove ( KeyValuePair < string , ICollection < string > > item )
1203+ => throw new NotSupportedException ( ) ;
1204+
1205+ [ ExcludeFromCodeCoverage ]
1206+ public bool TryGetValue ( string key , out ICollection < string > value )
1207+ => throw new NotSupportedException ( ) ;
1208+
1209+ private sealed class EnumeratorAdapter : IEnumerator < KeyValuePair < string , IEnumerable < string > > >
1210+ {
1211+ private readonly IEnumerator < KeyValuePair < string , ICollection < string > > > _enumerator ;
1212+
1213+ internal EnumeratorAdapter ( IEnumerator < KeyValuePair < string , ICollection < string > > > enumerator )
1214+ {
1215+ _enumerator = enumerator ;
1216+ }
1217+
1218+ public KeyValuePair < string , IEnumerable < string > > Current
1219+ => new ( _enumerator . Current . Key , _enumerator . Current . Value ) ;
1220+
1221+ object IEnumerator . Current
1222+ => _enumerator . Current ;
1223+
1224+ public void Dispose ( )
1225+ => _enumerator . Dispose ( ) ;
1226+
1227+ public bool MoveNext ( )
1228+ => _enumerator . MoveNext ( ) ;
1229+
1230+ public void Reset ( )
1231+ => _enumerator . Reset ( ) ;
1232+ }
1233+ }
10391234}
0 commit comments