55using System . Linq ;
66using System . Net ;
77using System . Net . Http ;
8+ using System . Net . Sockets ;
89using System . Threading ;
910using System . Threading . Tasks ;
1011using Microsoft . Net . Http . Client ;
1112
12- #if ( NETSTANDARD1_6 || NETSTANDARD2_0 )
13- using System . Net . Sockets ;
14- #endif
15-
1613namespace Docker . DotNet
1714{
1815 public sealed class DockerClient : IDockerClient
1916 {
17+ internal readonly IEnumerable < ApiResponseErrorHandlingDelegate > NoErrorHandlers = Enumerable . Empty < ApiResponseErrorHandlingDelegate > ( ) ;
18+
2019 private const string UserAgent = "Docker.DotNet" ;
2120
22- private static readonly TimeSpan s_InfiniteTimeout = TimeSpan . FromMilliseconds ( Timeout . Infinite ) ;
21+ private static readonly TimeSpan SInfiniteTimeout = Timeout . InfiniteTimeSpan ;
2322
2423 private readonly HttpClient _client ;
2524
2625 private readonly Uri _endpointBaseUri ;
2726
28- internal readonly IEnumerable < ApiResponseErrorHandlingDelegate > NoErrorHandlers = Enumerable . Empty < ApiResponseErrorHandlingDelegate > ( ) ;
2927 private readonly Version _requestedApiVersion ;
3028
3129 internal DockerClient ( DockerClientConfiguration configuration , Version requestedApiVersion )
3230 {
33- Configuration = configuration ;
3431 _requestedApiVersion = requestedApiVersion ;
35- JsonSerializer = new JsonSerializer ( ) ;
3632
33+ Configuration = configuration ;
34+ DefaultTimeout = configuration . DefaultTimeout ;
35+
36+ JsonSerializer = new JsonSerializer ( ) ;
3737 Images = new ImageOperations ( this ) ;
3838 Containers = new ContainerOperations ( this ) ;
3939 System = new SystemOperations ( this ) ;
@@ -73,15 +73,13 @@ internal DockerClient(DockerClientConfiguration configuration, Version requested
7373 uri = new UriBuilder ( "http" , pipeName ) . Uri ;
7474 handler = new ManagedHandler ( async ( host , port , cancellationToken ) =>
7575 {
76- int timeout = ( int ) this . Configuration . NamedPipeConnectTimeout . TotalMilliseconds ;
76+ var timeout = ( int ) Configuration . NamedPipeConnectTimeout . TotalMilliseconds ;
7777 var stream = new NamedPipeClientStream ( serverName , pipeName , PipeDirection . InOut , PipeOptions . Asynchronous ) ;
7878 var dockerStream = new DockerPipeStream ( stream ) ;
7979
80- #if NET45
81- await Task . Run ( ( ) => stream . Connect ( timeout ) , cancellationToken ) ;
82- #else
83- await stream . ConnectAsync ( timeout , cancellationToken ) ;
84- #endif
80+ await stream . ConnectAsync ( timeout , cancellationToken )
81+ . ConfigureAwait ( false ) ;
82+
8583 return dockerStream ;
8684 } ) ;
8785
@@ -101,18 +99,19 @@ internal DockerClient(DockerClientConfiguration configuration, Version requested
10199 handler = new ManagedHandler ( ) ;
102100 break ;
103101
104- #if ( NETSTANDARD1_6 || NETSTANDARD2_0 )
105102 case "unix" :
106103 var pipeString = uri . LocalPath ;
107- handler = new ManagedHandler ( async ( string host , int port , CancellationToken cancellationToken ) =>
104+ handler = new ManagedHandler ( async ( host , port , cancellationToken ) =>
108105 {
109106 var sock = new Socket ( AddressFamily . Unix , SocketType . Stream , ProtocolType . Unspecified ) ;
110- await sock . ConnectAsync ( new UnixDomainSocketEndPoint ( pipeString ) ) ;
107+
108+ await sock . ConnectAsync ( new Microsoft . Net . Http . Client . UnixDomainSocketEndPoint ( pipeString ) )
109+ . ConfigureAwait ( false ) ;
110+
111111 return sock ;
112112 } ) ;
113113 uri = new UriBuilder ( "http" , uri . Segments . Last ( ) ) . Uri ;
114114 break ;
115- #endif
116115
117116 default :
118117 throw new Exception ( $ "Unknown URL scheme { configuration . EndpointBaseUri . Scheme } ") ;
@@ -121,8 +120,7 @@ internal DockerClient(DockerClientConfiguration configuration, Version requested
121120 _endpointBaseUri = uri ;
122121
123122 _client = new HttpClient ( Configuration . Credentials . GetHandler ( handler ) , true ) ;
124- DefaultTimeout = Configuration . DefaultTimeout ;
125- _client . Timeout = s_InfiniteTimeout ;
123+ _client . Timeout = SInfiniteTimeout ;
126124 }
127125
128126 public DockerClientConfiguration Configuration { get ; }
@@ -151,6 +149,11 @@ internal DockerClient(DockerClientConfiguration configuration, Version requested
151149
152150 internal JsonSerializer JsonSerializer { get ; }
153151
152+ public void Dispose ( )
153+ {
154+ Configuration . Dispose ( ) ;
155+ }
156+
154157 internal Task < DockerApiResponse > MakeRequestAsync (
155158 IEnumerable < ApiResponseErrorHandlingDelegate > errorHandlers ,
156159 HttpMethod method ,
@@ -190,7 +193,7 @@ internal Task<DockerApiResponse> MakeRequestAsync(
190193 IDictionary < string , string > headers ,
191194 CancellationToken token )
192195 {
193- return MakeRequestAsync ( errorHandlers , method , path , queryString , body , headers , this . DefaultTimeout , token ) ;
196+ return MakeRequestAsync ( errorHandlers , method , path , queryString , body , headers , DefaultTimeout , token ) ;
194197 }
195198
196199 internal async Task < DockerApiResponse > MakeRequestAsync (
@@ -203,12 +206,16 @@ internal async Task<DockerApiResponse> MakeRequestAsync(
203206 TimeSpan timeout ,
204207 CancellationToken token )
205208 {
206- var response = await PrivateMakeRequestAsync ( timeout , HttpCompletionOption . ResponseContentRead , method , path , queryString , headers , body , token ) . ConfigureAwait ( false ) ;
209+ var response = await PrivateMakeRequestAsync ( timeout , HttpCompletionOption . ResponseContentRead , method , path , queryString , headers , body , token )
210+ . ConfigureAwait ( false ) ;
211+
207212 using ( response )
208213 {
209- await HandleIfErrorResponseAsync ( response . StatusCode , response , errorHandlers ) ;
214+ await HandleIfErrorResponseAsync ( response . StatusCode , response , errorHandlers )
215+ . ConfigureAwait ( false ) ;
210216
211- var responseBody = await response . Content . ReadAsStringAsync ( ) . ConfigureAwait ( false ) ;
217+ var responseBody = await response . Content . ReadAsStringAsync ( )
218+ . ConfigureAwait ( false ) ;
212219
213220 return new DockerApiResponse ( response . StatusCode , responseBody ) ;
214221 }
@@ -253,7 +260,7 @@ internal Task<Stream> MakeRequestForStreamAsync(
253260 IDictionary < string , string > headers ,
254261 CancellationToken token )
255262 {
256- return MakeRequestForStreamAsync ( errorHandlers , method , path , queryString , body , headers , s_InfiniteTimeout , token ) ;
263+ return MakeRequestForStreamAsync ( errorHandlers , method , path , queryString , body , headers , SInfiniteTimeout , token ) ;
257264 }
258265
259266 internal async Task < Stream > MakeRequestForStreamAsync (
@@ -266,23 +273,25 @@ internal async Task<Stream> MakeRequestForStreamAsync(
266273 TimeSpan timeout ,
267274 CancellationToken token )
268275 {
269- var response = await PrivateMakeRequestAsync ( timeout , HttpCompletionOption . ResponseHeadersRead , method , path , queryString , headers , body , token ) . ConfigureAwait ( false ) ;
276+ var response = await PrivateMakeRequestAsync ( timeout , HttpCompletionOption . ResponseHeadersRead , method , path , queryString , headers , body , token )
277+ . ConfigureAwait ( false ) ;
270278
271- await HandleIfErrorResponseAsync ( response . StatusCode , response , errorHandlers ) ;
279+ await HandleIfErrorResponseAsync ( response . StatusCode , response , errorHandlers )
280+ . ConfigureAwait ( false ) ;
272281
273- return await response . Content . ReadAsStreamAsync ( ) . ConfigureAwait ( false ) ;
282+ return await response . Content . ReadAsStreamAsync ( )
283+ . ConfigureAwait ( false ) ;
274284 }
275285
276- internal async Task < HttpResponseMessage > MakeRequestForRawResponseAsync (
286+ internal Task < HttpResponseMessage > MakeRequestForRawResponseAsync (
277287 HttpMethod method ,
278288 string path ,
279289 IQueryString queryString ,
280290 IRequestContent body ,
281291 IDictionary < string , string > headers ,
282292 CancellationToken token )
283293 {
284- var response = await PrivateMakeRequestAsync ( s_InfiniteTimeout , HttpCompletionOption . ResponseHeadersRead , method , path , queryString , headers , body , token ) . ConfigureAwait ( false ) ;
285- return response ;
294+ return PrivateMakeRequestAsync ( SInfiniteTimeout , HttpCompletionOption . ResponseHeadersRead , method , path , queryString , headers , body , token ) ;
286295 }
287296
288297 internal async Task < DockerApiStreamedResponse > MakeRequestForStreamedResponseAsync (
@@ -292,11 +301,14 @@ internal async Task<DockerApiStreamedResponse> MakeRequestForStreamedResponseAsy
292301 IQueryString queryString ,
293302 CancellationToken cancellationToken )
294303 {
295- var response = await PrivateMakeRequestAsync ( s_InfiniteTimeout , HttpCompletionOption . ResponseHeadersRead , method , path , queryString , null , null , cancellationToken ) ;
304+ var response = await PrivateMakeRequestAsync ( SInfiniteTimeout , HttpCompletionOption . ResponseHeadersRead , method , path , queryString , null , null , cancellationToken )
305+ . ConfigureAwait ( false ) ;
296306
297- await HandleIfErrorResponseAsync ( response . StatusCode , response , errorHandlers ) ;
307+ await HandleIfErrorResponseAsync ( response . StatusCode , response , errorHandlers )
308+ . ConfigureAwait ( false ) ;
298309
299- var body = await response . Content . ReadAsStreamAsync ( ) ;
310+ var body = await response . Content . ReadAsStreamAsync ( )
311+ . ConfigureAwait ( false ) ;
300312
301313 return new DockerApiStreamedResponse ( response . StatusCode , body , response . Headers ) ;
302314 }
@@ -310,7 +322,7 @@ internal Task<WriteClosableStream> MakeRequestForHijackedStreamAsync(
310322 IDictionary < string , string > headers ,
311323 CancellationToken cancellationToken )
312324 {
313- return MakeRequestForHijackedStreamAsync ( errorHandlers , method , path , queryString , body , headers , s_InfiniteTimeout , cancellationToken ) ;
325+ return MakeRequestForHijackedStreamAsync ( errorHandlers , method , path , queryString , body , headers , SInfiniteTimeout , cancellationToken ) ;
314326 }
315327
316328 internal async Task < WriteClosableStream > MakeRequestForHijackedStreamAsync (
@@ -323,18 +335,20 @@ internal async Task<WriteClosableStream> MakeRequestForHijackedStreamAsync(
323335 TimeSpan timeout ,
324336 CancellationToken cancellationToken )
325337 {
326- var response = await PrivateMakeRequestAsync ( timeout , HttpCompletionOption . ResponseHeadersRead , method , path , queryString , headers , body , cancellationToken ) . ConfigureAwait ( false ) ;
338+ var response = await PrivateMakeRequestAsync ( timeout , HttpCompletionOption . ResponseHeadersRead , method , path , queryString , headers , body , cancellationToken )
339+ . ConfigureAwait ( false ) ;
327340
328- await HandleIfErrorResponseAsync ( response . StatusCode , response , errorHandlers ) ;
341+ await HandleIfErrorResponseAsync ( response . StatusCode , response , errorHandlers )
342+ . ConfigureAwait ( false ) ;
329343
330- var content = response . Content as HttpConnectionResponseContent ;
331- if ( content == null )
344+ if ( ! ( response . Content is HttpConnectionResponseContent content ) )
332345 {
333346 throw new NotSupportedException ( "message handler does not support hijacked streams" ) ;
334347 }
335348
336349 var stream = await content . ReadAsStreamAsync ( )
337350 . ConfigureAwait ( false ) ;
351+
338352 return ( WriteClosableStream ) stream ;
339353 }
340354
@@ -350,25 +364,55 @@ private async Task<HttpResponseMessage> PrivateMakeRequestAsync(
350364 {
351365 var request = PrepareRequest ( method , path , queryString , headers , data ) ;
352366
353- if ( timeout != s_InfiniteTimeout )
367+ if ( timeout != SInfiniteTimeout )
354368 {
355369 using ( var timeoutTokenSource = CancellationTokenSource . CreateLinkedTokenSource ( cancellationToken ) )
356370 {
357371 timeoutTokenSource . CancelAfter ( timeout ) ;
358- return await _client . SendAsync ( request , completionOption , timeoutTokenSource . Token ) . ConfigureAwait ( false ) ;
372+ return await _client . SendAsync ( request , completionOption , timeoutTokenSource . Token )
373+ . ConfigureAwait ( false ) ;
359374 }
360375 }
361376
362377 var tcs = new TaskCompletionSource < HttpResponseMessage > ( ) ;
363378 using ( cancellationToken . Register ( ( ) => tcs . SetCanceled ( ) ) )
364379 {
365- return await await Task . WhenAny ( tcs . Task , _client . SendAsync ( request , completionOption , cancellationToken ) ) . ConfigureAwait ( false ) ;
380+ return await await Task . WhenAny ( tcs . Task , _client . SendAsync ( request , completionOption , cancellationToken ) )
381+ . ConfigureAwait ( false ) ;
382+ }
383+ }
384+
385+ internal HttpRequestMessage PrepareRequest ( HttpMethod method , string path , IQueryString queryString , IDictionary < string , string > headers , IRequestContent data )
386+ {
387+ if ( string . IsNullOrEmpty ( path ) )
388+ {
389+ throw new ArgumentNullException ( nameof ( path ) ) ;
390+ }
391+
392+ var request = new HttpRequestMessage ( method , HttpUtility . BuildUri ( _endpointBaseUri , _requestedApiVersion , path , queryString ) ) ;
393+ request . Version = new Version ( 1 , 1 ) ;
394+ request . Headers . Add ( "User-Agent" , UserAgent ) ;
395+
396+ if ( headers != null )
397+ {
398+ foreach ( var header in headers )
399+ {
400+ request . Headers . Add ( header . Key , header . Value ) ;
401+ }
402+ }
403+
404+ if ( data != null )
405+ {
406+ var requestContent = data . GetContent ( ) ; // make the call only once.
407+ request . Content = requestContent ;
366408 }
409+
410+ return request ;
367411 }
368412
369413 private async Task HandleIfErrorResponseAsync ( HttpStatusCode statusCode , HttpResponseMessage response , IEnumerable < ApiResponseErrorHandlingDelegate > handlers )
370414 {
371- bool isErrorResponse = statusCode < HttpStatusCode . OK || statusCode >= HttpStatusCode . BadRequest ;
415+ var isErrorResponse = statusCode < HttpStatusCode . OK || statusCode >= HttpStatusCode . BadRequest ;
372416
373417 string responseBody = null ;
374418
@@ -377,7 +421,8 @@ private async Task HandleIfErrorResponseAsync(HttpStatusCode statusCode, HttpRes
377421 // If it is not an error response, we do not read the response body because the caller may wish to consume it.
378422 // If it is an error response, we do because there is nothing else going to be done with it anyway and
379423 // we want to report the response body in the error message as it contains potentially useful info.
380- responseBody = await response . Content . ReadAsStringAsync ( ) . ConfigureAwait ( false ) ;
424+ responseBody = await response . Content . ReadAsStringAsync ( )
425+ . ConfigureAwait ( false ) ;
381426 }
382427
383428 // If no customer handlers just default the response.
@@ -396,9 +441,9 @@ private async Task HandleIfErrorResponseAsync(HttpStatusCode statusCode, HttpRes
396441 }
397442 }
398443
399- public async Task HandleIfErrorResponseAsync ( HttpStatusCode statusCode , HttpResponseMessage response )
444+ private async Task HandleIfErrorResponseAsync ( HttpStatusCode statusCode , HttpResponseMessage response )
400445 {
401- bool isErrorResponse = statusCode < HttpStatusCode . OK || statusCode >= HttpStatusCode . BadRequest ;
446+ var isErrorResponse = statusCode < HttpStatusCode . OK || statusCode >= HttpStatusCode . BadRequest ;
402447
403448 string responseBody = null ;
404449
@@ -407,7 +452,8 @@ public async Task HandleIfErrorResponseAsync(HttpStatusCode statusCode, HttpResp
407452 // If it is not an error response, we do not read the response body because the caller may wish to consume it.
408453 // If it is an error response, we do because there is nothing else going to be done with it anyway and
409454 // we want to report the response body in the error message as it contains potentially useful info.
410- responseBody = await response . Content . ReadAsStringAsync ( ) . ConfigureAwait ( false ) ;
455+ responseBody = await response . Content . ReadAsStringAsync ( )
456+ . ConfigureAwait ( false ) ;
411457 }
412458
413459 // No custom handler was fired. Default the response for generic success/failures.
@@ -416,41 +462,6 @@ public async Task HandleIfErrorResponseAsync(HttpStatusCode statusCode, HttpResp
416462 throw new DockerApiException ( statusCode , responseBody ) ;
417463 }
418464 }
419-
420- internal HttpRequestMessage PrepareRequest ( HttpMethod method , string path , IQueryString queryString , IDictionary < string , string > headers , IRequestContent data )
421- {
422- if ( string . IsNullOrEmpty ( path ) )
423- {
424- throw new ArgumentNullException ( nameof ( path ) ) ;
425- }
426-
427- var request = new HttpRequestMessage ( method , HttpUtility . BuildUri ( _endpointBaseUri , this . _requestedApiVersion , path , queryString ) ) ;
428-
429- request . Version = new Version ( 1 , 1 ) ;
430-
431- request . Headers . Add ( "User-Agent" , UserAgent ) ;
432-
433- if ( headers != null )
434- {
435- foreach ( var header in headers )
436- {
437- request . Headers . Add ( header . Key , header . Value ) ;
438- }
439- }
440-
441- if ( data != null )
442- {
443- var requestContent = data . GetContent ( ) ; // make the call only once.
444- request . Content = requestContent ;
445- }
446-
447- return request ;
448- }
449-
450- public void Dispose ( )
451- {
452- Configuration . Dispose ( ) ;
453- }
454465 }
455466
456467 internal delegate void ApiResponseErrorHandlingDelegate ( HttpStatusCode statusCode , string responseBody ) ;
0 commit comments