@@ -17,8 +17,11 @@ namespace GVFS.Common.Http
1717{
1818 public abstract class HttpRequestor : IDisposable
1919 {
20+ private const int MinConnectionLimit = 64 ;
21+
2022 private static long requestCount = 0 ;
2123 private static SemaphoreSlim availableConnections ;
24+ private static int connectionLimitConfigured = 0 ;
2225
2326 private readonly ProductInfoHeaderValue userAgentHeader ;
2427
@@ -34,8 +37,13 @@ static HttpRequestor()
3437 using ( var machineConfigLock = GetMachineConfigLock ( ) )
3538 {
3639 ServicePointManager . SecurityProtocol = ServicePointManager . SecurityProtocol | SecurityProtocolType . Tls12 ;
37- ServicePointManager . DefaultConnectionLimit = Environment . ProcessorCount ;
38- availableConnections = new SemaphoreSlim ( ServicePointManager . DefaultConnectionLimit ) ;
40+
41+ // HTTP downloads are I/O-bound, not CPU-bound, so ProcessorCount is too low
42+ // for burst workloads (e.g., checkout triggering many object downloads).
43+ // This default can be overridden via git config gvfs.max-http-connections.
44+ int connectionLimit = Math . Max ( Environment . ProcessorCount , MinConnectionLimit ) ;
45+ ServicePointManager . DefaultConnectionLimit = connectionLimit ;
46+ availableConnections = new SemaphoreSlim ( connectionLimit ) ;
3947 }
4048 }
4149
@@ -47,6 +55,13 @@ protected HttpRequestor(ITracer tracer, RetryConfig retryConfig, Enlistment enli
4755
4856 this . Tracer = tracer ;
4957
58+ // On first instantiation, check git config for a custom connection limit.
59+ // This runs before any requests are made (during mount initialization).
60+ if ( Interlocked . CompareExchange ( ref connectionLimitConfigured , 1 , 0 ) == 0 )
61+ {
62+ TryApplyConnectionLimitFromConfig ( tracer , enlistment ) ;
63+ }
64+
5065 HttpClientHandler httpClientHandler = new HttpClientHandler ( ) { UseDefaultCredentials = true } ;
5166
5267 this . authentication . ConfigureHttpClientHandlerSslIfNeeded ( this . Tracer , httpClientHandler , enlistment . CreateGitProcess ( ) ) ;
@@ -337,6 +352,31 @@ private static bool TryGetResponseMessageFromHttpRequestException(HttpRequestExc
337352
338353 }
339354
355+ private static void TryApplyConnectionLimitFromConfig ( ITracer tracer , Enlistment enlistment )
356+ {
357+ try
358+ {
359+ GitProcess . ConfigResult result = enlistment . CreateGitProcess ( ) . GetFromConfig ( GVFSConstants . GitConfig . MaxHttpConnectionsConfig ) ;
360+ string error ;
361+ int configuredLimit ;
362+ if ( result . TryParseAsInt ( 0 , MinConnectionLimit , out configuredLimit , out error ) && configuredLimit > 0 )
363+ {
364+ ServicePointManager . DefaultConnectionLimit = configuredLimit ;
365+ availableConnections = new SemaphoreSlim ( configuredLimit ) ;
366+
367+ EventMetadata metadata = new EventMetadata ( ) ;
368+ metadata . Add ( "configuredLimit" , configuredLimit ) ;
369+ tracer . RelatedEvent ( EventLevel . Informational , "HttpRequestor_ConnectionLimitConfigured" , metadata ) ;
370+ }
371+ }
372+ catch ( Exception e )
373+ {
374+ EventMetadata metadata = new EventMetadata ( ) ;
375+ metadata . Add ( "Exception" , e . ToString ( ) ) ;
376+ tracer . RelatedWarning ( metadata , "HttpRequestor: Failed to read gvfs.max-http-connections config, using default" ) ;
377+ }
378+ }
379+
340380 private static FileStream GetMachineConfigLock ( )
341381 {
342382 var machineConfigLocation = RuntimeEnvironment . SystemConfigurationFile ;
0 commit comments