3030using System . Threading ;
3131using System . Threading . Tasks ;
3232
33+ #nullable enable
34+
3335namespace OpenQA . Selenium . Remote
3436{
3537 /// <summary>
@@ -47,7 +49,8 @@ public class HttpCommandExecutor : ICommandExecutor
4749 private readonly TimeSpan serverResponseTimeout ;
4850 private bool isDisposed ;
4951 private CommandInfoRepository commandInfoRepository = new W3CWireProtocolCommandInfoRepository ( ) ;
50- private HttpClient client ;
52+ private HttpClient ? client ;
53+ private readonly object _createClientLock = new ( ) ;
5154
5255 private static readonly ILogger _logger = Log . GetLogger < HttpCommandExecutor > ( ) ;
5356
@@ -56,6 +59,7 @@ public class HttpCommandExecutor : ICommandExecutor
5659 /// </summary>
5760 /// <param name="addressOfRemoteServer">Address of the WebDriver Server</param>
5861 /// <param name="timeout">The timeout within which the server must respond.</param>
62+ /// <exception cref="ArgumentNullException">If <paramref name="addressOfRemoteServer"/> is <see langword="null"/>.</exception>
5963 public HttpCommandExecutor ( Uri addressOfRemoteServer , TimeSpan timeout )
6064 : this ( addressOfRemoteServer , timeout , true )
6165 {
@@ -91,14 +95,14 @@ public HttpCommandExecutor(Uri addressOfRemoteServer, TimeSpan timeout, bool ena
9195 /// Occurs when the <see cref="HttpCommandExecutor"/> is sending an HTTP
9296 /// request to the remote end WebDriver implementation.
9397 /// </summary>
94- public event EventHandler < SendingRemoteHttpRequestEventArgs > SendingRemoteHttpRequest ;
98+ public event EventHandler < SendingRemoteHttpRequestEventArgs > ? SendingRemoteHttpRequest ;
9599
96100 /// <summary>
97101 /// Gets or sets an <see cref="IWebProxy"/> object to be used to proxy requests
98102 /// between this <see cref="HttpCommandExecutor"/> and the remote end WebDriver
99103 /// implementation.
100104 /// </summary>
101- public IWebProxy Proxy { get ; set ; }
105+ public IWebProxy ? Proxy { get ; set ; }
102106
103107 /// <summary>
104108 /// Gets or sets a value indicating whether keep-alive is enabled for HTTP
@@ -167,17 +171,12 @@ public virtual async Task<Response> ExecuteAsync(Command commandToExecute)
167171 _logger . Debug ( $ "Executing command: { commandToExecute } ") ;
168172 }
169173
170- HttpCommandInfo info = this . commandInfoRepository . GetCommandInfo < HttpCommandInfo > ( commandToExecute . Name ) ;
174+ HttpCommandInfo ? info = this . commandInfoRepository . GetCommandInfo < HttpCommandInfo > ( commandToExecute . Name ) ;
171175 if ( info == null )
172176 {
173177 throw new NotImplementedException ( string . Format ( "The command you are attempting to execute, {0}, does not exist in the protocol dialect used by the remote end." , commandToExecute . Name ) ) ;
174178 }
175179
176- if ( this . client == null )
177- {
178- this . CreateHttpClient ( ) ;
179- }
180-
181180 HttpRequestInfo requestInfo = new HttpRequestInfo ( this . remoteServerUri , commandToExecute , info ) ;
182181 HttpResponseInfo responseInfo ;
183182 try
@@ -216,42 +215,55 @@ protected virtual void OnSendingRemoteHttpRequest(SendingRemoteHttpRequestEventA
216215 throw new ArgumentNullException ( nameof ( eventArgs ) , "eventArgs must not be null" ) ;
217216 }
218217
219- if ( this . SendingRemoteHttpRequest != null )
220- {
221- this . SendingRemoteHttpRequest ( this , eventArgs ) ;
222- }
218+ this . SendingRemoteHttpRequest ? . Invoke ( this , eventArgs ) ;
223219 }
224220
225- private void CreateHttpClient ( )
221+ private HttpClient Client
226222 {
227- HttpClientHandler httpClientHandler = new HttpClientHandler ( ) ;
228- string userInfo = this . remoteServerUri . UserInfo ;
229- if ( ! string . IsNullOrEmpty ( userInfo ) && userInfo . Contains ( ":" ) )
230- {
231- string [ ] userInfoComponents = this . remoteServerUri . UserInfo . Split ( new char [ ] { ':' } , 2 ) ;
232- httpClientHandler . Credentials = new NetworkCredential ( userInfoComponents [ 0 ] , userInfoComponents [ 1 ] ) ;
233- httpClientHandler . PreAuthenticate = true ;
234- }
235-
236- httpClientHandler . Proxy = this . Proxy ;
237-
238- HttpMessageHandler handler = httpClientHandler ;
239-
240- if ( _logger . IsEnabled ( LogEventLevel . Trace ) )
223+ get
241224 {
242- handler = new DiagnosticsHttpHandler ( httpClientHandler , _logger ) ;
243- }
225+ if ( this . client is null )
226+ {
227+ lock ( _createClientLock )
228+ {
229+ if ( this . client is null )
230+ {
231+ HttpClientHandler httpClientHandler = new HttpClientHandler ( ) ;
232+ string userInfo = this . remoteServerUri . UserInfo ;
233+ if ( ! string . IsNullOrEmpty ( userInfo ) && userInfo . Contains ( ":" ) )
234+ {
235+ string [ ] userInfoComponents = this . remoteServerUri . UserInfo . Split ( new char [ ] { ':' } , 2 ) ;
236+ httpClientHandler . Credentials = new NetworkCredential ( userInfoComponents [ 0 ] , userInfoComponents [ 1 ] ) ;
237+ httpClientHandler . PreAuthenticate = true ;
238+ }
239+
240+ httpClientHandler . Proxy = this . Proxy ;
241+
242+ HttpMessageHandler handler = httpClientHandler ;
243+
244+ if ( _logger . IsEnabled ( LogEventLevel . Trace ) )
245+ {
246+ handler = new DiagnosticsHttpHandler ( httpClientHandler , _logger ) ;
247+ }
248+
249+ var client = new HttpClient ( handler ) ;
250+ client . DefaultRequestHeaders . UserAgent . ParseAdd ( this . UserAgent ) ;
251+ client . DefaultRequestHeaders . Accept . ParseAdd ( RequestAcceptHeader ) ;
252+ client . DefaultRequestHeaders . ExpectContinue = false ;
253+ if ( ! this . IsKeepAliveEnabled )
254+ {
255+ client . DefaultRequestHeaders . Connection . ParseAdd ( "close" ) ;
256+ }
257+
258+ client . Timeout = this . serverResponseTimeout ;
259+
260+ this . client = client ;
261+ }
262+ }
263+ }
244264
245- this . client = new HttpClient ( handler ) ;
246- this . client . DefaultRequestHeaders . UserAgent . ParseAdd ( this . UserAgent ) ;
247- this . client . DefaultRequestHeaders . Accept . ParseAdd ( RequestAcceptHeader ) ;
248- this . client . DefaultRequestHeaders . ExpectContinue = false ;
249- if ( ! this . IsKeepAliveEnabled )
250- {
251- this . client . DefaultRequestHeaders . Connection . ParseAdd ( "close" ) ;
265+ return this . client ;
252266 }
253-
254- this . client . Timeout = this . serverResponseTimeout ;
255267 }
256268
257269 private async Task < HttpResponseInfo > MakeHttpRequest ( HttpRequestInfo requestInfo )
@@ -288,7 +300,7 @@ private async Task<HttpResponseInfo> MakeHttpRequest(HttpRequestInfo requestInfo
288300 requestMessage . Content . Headers . ContentType = contentTypeHeader ;
289301 }
290302
291- using ( HttpResponseMessage responseMessage = await this . client . SendAsync ( requestMessage ) . ConfigureAwait ( false ) )
303+ using ( HttpResponseMessage responseMessage = await this . Client . SendAsync ( requestMessage ) . ConfigureAwait ( false ) )
292304 {
293305 var responseBody = await responseMessage . Content . ReadAsStringAsync ( ) . ConfigureAwait ( false ) ;
294306 var responseContentType = responseMessage . Content . Headers . ContentType ? . ToString ( ) ;
@@ -331,8 +343,6 @@ private Response CreateResponse(HttpResponseInfo responseInfo)
331343 return response ;
332344 }
333345
334- #nullable enable
335-
336346 /// <summary>
337347 /// Releases all resources used by the <see cref="HttpCommandExecutor"/>.
338348 /// </summary>
0 commit comments