88using System . Globalization ;
99using System . Linq ;
1010using System . Net ;
11+ using System . Text . Json ;
1112using System . Threading ;
1213using System . Threading . Tasks ;
1314using Newtonsoft . Json . Linq ;
15+ using NuGet . Common ;
1416using NuGet . Protocol . Core . Types ;
17+ using NuGet . Protocol . Model ;
18+ using NuGet . Protocol . Utility ;
19+ using NuGet . Shared ;
1520using NuGet . Versioning ;
1621
1722namespace NuGet . Protocol
1823{
1924 public class AutoCompleteResourceV3 : AutoCompleteResource
2025 {
21- private readonly RegistrationResourceV3 _regResource ;
22- private readonly ServiceIndexResourceV3 _serviceIndex ;
23- private readonly HttpSource _client ;
26+ internal readonly RegistrationResourceV3 _regResource ;
27+ internal readonly ServiceIndexResourceV3 _serviceIndex ;
28+ internal readonly HttpSource _client ;
29+ private readonly IEnvironmentVariableReader _environmentVariableReader ;
2430
2531 public AutoCompleteResourceV3 ( HttpSource client , ServiceIndexResourceV3 serviceIndex , RegistrationResourceV3 regResource )
32+ : this ( client , serviceIndex , regResource , null )
33+ {
34+ }
35+
36+ internal AutoCompleteResourceV3 ( HttpSource client , ServiceIndexResourceV3 serviceIndex , RegistrationResourceV3 regResource , IEnvironmentVariableReader environmentVariableReader )
2637 : base ( )
2738 {
2839 _regResource = regResource ;
2940 _serviceIndex = serviceIndex ;
3041 _client = client ;
42+ _environmentVariableReader = environmentVariableReader ;
3143 }
3244
3345 public override async Task < IEnumerable < string > > IdStartsWith (
@@ -36,25 +48,54 @@ public override async Task<IEnumerable<string>> IdStartsWith(
3648 Common . ILogger log ,
3749 CancellationToken token )
3850 {
39- var searchUrl = _serviceIndex . GetServiceEntryUri ( ServiceTypes . SearchAutocompleteService ) ;
40-
41- if ( searchUrl == null )
51+ if ( NuGetFeatureFlags . UseSystemTextJsonDeserializationFeatureSwitch || NuGetFeatureFlags . IsSystemTextJsonDeserializationEnabledByEnvironment ( _environmentVariableReader ) )
4252 {
43- throw new FatalProtocolException ( Strings . Protocol_MissingSearchService ) ;
53+ return await IdStartsWithStjAsync ( packageIdPrefix , includePrerelease , log , token ) ;
4454 }
55+ else
56+ {
57+ return await IdStartsWithNsjAsync ( packageIdPrefix , includePrerelease , log , token ) ;
58+ }
59+ }
4560
46- // Construct the query
47- var queryUrl = new UriBuilder ( searchUrl . AbsoluteUri ) ;
48- var queryString =
49- "q=" + WebUtility . UrlEncode ( packageIdPrefix ) +
50- "&prerelease=" + includePrerelease . ToString ( CultureInfo . CurrentCulture ) . ToLowerInvariant ( ) +
51- "&semVerLevel=2.0.0" ;
61+ private async Task < IEnumerable < string > > IdStartsWithStjAsync (
62+ string packageIdPrefix ,
63+ bool includePrerelease ,
64+ Common . ILogger log ,
65+ CancellationToken token )
66+ {
67+ var queryUri = BuildQueryUri ( packageIdPrefix , includePrerelease ) ;
68+ Common . ILogger logger = log ?? Common . NullLogger . Instance ;
69+
70+ AutoCompleteModel results = await _client . ProcessStreamAsync (
71+ new HttpSourceRequest ( queryUri , logger ) ,
72+ async stream =>
73+ {
74+ if ( stream == null )
75+ {
76+ return null ;
77+ }
5278
53- queryUrl . Query = queryString ;
79+ return await JsonSerializer . DeserializeAsync ( stream , JsonContext . Default . AutoCompleteModel , token ) ;
80+ } ,
81+ logger ,
82+ token ) ;
5483
84+ token . ThrowIfCancellationRequested ( ) ;
85+
86+ return results ? . Data ? . Where ( item => item != null && item . StartsWith ( packageIdPrefix , StringComparison . OrdinalIgnoreCase ) )
87+ ?? [ ] ;
88+ }
89+
90+ private async Task < IEnumerable < string > > IdStartsWithNsjAsync (
91+ string packageIdPrefix ,
92+ bool includePrerelease ,
93+ Common . ILogger log ,
94+ CancellationToken token )
95+ {
96+ var queryUri = BuildQueryUri ( packageIdPrefix , includePrerelease ) ;
5597 Common . ILogger logger = log ?? Common . NullLogger . Instance ;
5698
57- var queryUri = queryUrl . Uri ;
5899 var results = await _client . GetJObjectAsync (
59100 new HttpSourceRequest ( queryUri , logger ) ,
60101 logger ,
@@ -83,6 +124,25 @@ public override async Task<IEnumerable<string>> IdStartsWith(
83124 return outputs . Where ( item => item . StartsWith ( packageIdPrefix , StringComparison . OrdinalIgnoreCase ) ) ;
84125 }
85126
127+ private Uri BuildQueryUri ( string packageIdPrefix , bool includePrerelease )
128+ {
129+ var searchUrl = _serviceIndex . GetServiceEntryUri ( ServiceTypes . SearchAutocompleteService ) ;
130+
131+ if ( searchUrl == null )
132+ {
133+ throw new FatalProtocolException ( Strings . Protocol_MissingSearchService ) ;
134+ }
135+
136+ // Construct the query
137+ var queryUrl = new UriBuilder ( searchUrl . AbsoluteUri ) ;
138+ queryUrl . Query =
139+ "q=" + WebUtility . UrlEncode ( packageIdPrefix ) +
140+ "&prerelease=" + includePrerelease . ToString ( CultureInfo . CurrentCulture ) . ToLowerInvariant ( ) +
141+ "&semVerLevel=2.0.0" ;
142+
143+ return queryUrl . Uri ;
144+ }
145+
86146 public override async Task < IEnumerable < NuGetVersion > > VersionStartsWith (
87147 string packageId ,
88148 string versionPrefix ,
0 commit comments