Summary

Class:E_CBAM
Assembly:CBAM.HTTP.Implementation
File(s):/repo-dir/contents/Source/Code/CBAM.HTTP.Implementation/Connection.cs
/repo-dir/contents/Source/Code/CBAM.HTTP.Implementation/ConnectionConfiguration.cs
/repo-dir/contents/Source/Code/CBAM.HTTP.Implementation/ConnectionFactory.cs
Covered lines:122
Uncovered lines:27
Coverable lines:149
Total lines:870
Line coverage:81.8%
Branch coverage:82.2%

Coverage History

Metrics

MethodCyclomatic complexity NPath complexity Sequence coverage Branch coverage
.cctor()101%0%
WriteHTTPString(...)200.889%0.5%
SendRequest()1900.818%1%
ReceiveResponse()1800.783%1%
CanHaveMessageContent(...)801%0.625%
TrimBeginAndEnd(...)1000.857%0.9%
CreateNetworkCreationInfo(...)601%0.5%
CreatePoolAndReceiveTextualResponseAsync(...)101%0%

File(s)

/repo-dir/contents/Source/Code/CBAM.HTTP.Implementation/Connection.cs

#LineLine coverage
 1/*
 2 * Copyright 2017 Stanislav Muhametsin. All rights Reserved.
 3 *
 4 * Licensed  under the  Apache License,  Version 2.0  (the "License");
 5 * you may not use  this file  except in  compliance with the License.
 6 * You may obtain a copy of the License at
 7 *
 8 *   http://www.apache.org/licenses/LICENSE-2.0
 9 *
 10 * Unless required by applicable law or agreed to in writing, software
 11 * distributed  under the  License is distributed on an "AS IS" BASIS,
 12 * WITHOUT  WARRANTIES OR CONDITIONS  OF ANY KIND, either  express  or
 13 * implied.
 14 *
 15 * See the License for the specific language governing permissions and
 16 * limitations under the License.
 17 */
 18using CBAM.Abstractions.Implementation;
 19using CBAM.HTTP;
 20using CBAM.HTTP.Implementation;
 21using System;
 22using System.Collections.Generic;
 23using System.IO;
 24using System.Linq;
 25using System.Text;
 26using System.Threading;
 27using System.Threading.Tasks;
 28using UtilPack;
 29
 30namespace CBAM.HTTP.Implementation
 31{
 32   internal sealed class HTTPConnectionImpl<TRequestMetaData> : ConnectionImpl<HTTPStatement<TRequestMetaData>, HTTPStat
 33   {
 34      public HTTPConnectionImpl(
 35         HTTPConnectionFunctionalityImpl<TRequestMetaData> functionality
 36         ) : base( functionality )
 37      {
 38      }
 39
 40      public String ProtocolVersion => HTTPFactory.VERSION_HTTP1_1;
 41   }
 42
 43   internal sealed class HTTPConnectionFunctionalityImpl<TRequestMetaData> : ConnectionFunctionalitySU<HTTPStatement<TRe
 44   {
 45      private readonly ClientProtocolIOState _state;
 46
 47      public HTTPConnectionFunctionalityImpl(
 48         HTTPConnectionVendorImpl<TRequestMetaData> vendor,
 49         ClientProtocolIOState state
 50         ) : base( vendor, AsyncEnumeration.Implementation.Provider.DefaultAsyncProvider.Instance )
 51      {
 52         this._state = ArgumentValidator.ValidateNotNull( nameof( state ), state );
 53      }
 54
 55
 56      public Stream Stream => this._state.Stream;
 57
 58      protected override ReservedForStatement CreateReservationObject( HTTPStatementInformation<TRequestMetaData> stmt )
 59      {
 60         return new ReservedForStatement(
 61#if DEBUG
 62            stmt
 63#endif
 64            );
 65      }
 66
 67      protected override HTTPStatementInformation<TRequestMetaData> GetInformationFromStatement( HTTPStatement<TRequestM
 68      {
 69         return statement?.Information;
 70      }
 71
 72      protected override Task PerformDisposeStatementAsync( ReservedForStatement reservationObject )
 73      {
 74         // Nothing to do as HTTP is stateless protocol
 75         return TaskUtils.CompletedTask;
 76      }
 77
 78      protected override void ValidateStatementOrThrow( HTTPStatementInformation<TRequestMetaData> statement )
 79      {
 80         ArgumentValidator.ValidateNotNull( nameof( statement ), statement );
 81      }
 82
 83      protected override async ValueTask<(HTTPResponseInfo<TRequestMetaData>, Boolean, Func<ValueTask<(Boolean, HTTPResp
 84         HTTPStatementInformation<TRequestMetaData> stmt,
 85         ReservedForStatement reservationObject
 86         )
 87      {
 88         var stmtImpl = (HTTPStatementInformationImpl<TRequestMetaData>) stmt;
 89         var generator = stmtImpl.NextRequestGenerator;
 90         var currentMD = stmtImpl.InitialRequestMetaData;
 91         HTTPResponse currentResponse = default;
 92         async ValueTask<(Boolean, HTTPResponseInfo<TRequestMetaData>)> ReadNextResponse()
 93         {
 94            var requestInfo = await ( generator?.Invoke( new HTTPResponseInfo<TRequestMetaData>( currentResponse, curren
 95            currentMD = requestInfo.RequestMetaData;
 96            // Call this always, as it will take care of reading the previous response content till the end.
 97            currentResponse = await this.SendAndReceive( currentResponse, requestInfo.Request );
 98            return (currentResponse != default, currentResponse == default ? default : new HTTPResponseInfo<TRequestMeta
 99         }
 100
 101         // Send request
 102         return (
 103            new HTTPResponseInfo<TRequestMetaData>( currentResponse = await this.SendAndReceive( default, stmtImpl.Initi
 104            true,
 105            ReadNextResponse
 106            );
 107      }
 108
 109      private async Task<HTTPResponse> SendAndReceive(
 110         HTTPResponse prevResponse,
 111         HTTPRequest request
 112         )
 113      {
 114         var state = this._state;
 115         var buffer = state.ReadState.Buffer;
 116
 117         HTTPResponseContent prevResponseContent;
 118         if ( ( prevResponseContent = prevResponse?.Content ) != null )
 119         {
 120            while ( ( await prevResponseContent.ReadToBuffer( buffer.Array, 0, buffer.CurrentMaxCapacity ) ) > 0 )
 121               ;
 122         }
 123
 124         HTTPResponse retVal;
 125         if ( request != null )
 126         {
 127            var requestMethod = await this._state.SendRequest(
 128               request,
 129               this.CurrentCancellationToken
 130               );
 131            retVal = await this._state.ReceiveResponse(
 132               requestMethod,
 133               this.CurrentCancellationToken
 134               );
 135         }
 136         else
 137         {
 138            retVal = null;
 139         }
 140
 141         return retVal;
 142      }
 143
 144   }
 145
 146   internal abstract class AbstractIOState
 147   {
 148
 149      public AbstractIOState()
 150      {
 151         //this.Lock = new AsyncLock();
 152         this.Buffer = new ResizableArray<Byte>( 0x100 );
 153      }
 154
 155      public ResizableArray<Byte> Buffer { get; }
 156
 157      //public AsyncLock Lock { get; }
 158   }
 159
 160   internal sealed class WriteState : AbstractIOState
 161   {
 162      public WriteState(
 163         ) : base()
 164      {
 165      }
 166   }
 167
 168   internal sealed class ReadState : AbstractIOState
 169   {
 170      public ReadState(
 171         ) : base()
 172      {
 173         this.BufferAdvanceState = new BufferAdvanceState();
 174      }
 175
 176      public BufferAdvanceState BufferAdvanceState { get; }
 177   }
 178
 179   internal sealed class ClientProtocolIOState
 180   {
 181
 182      public ClientProtocolIOState(
 183         Stream stream,
 184         BinaryStringPool stringPool,
 185         IEncodingInfo encoding,
 186         WriteState writeState,
 187         ReadState readState
 188         )
 189      {
 190         this.Stream = ArgumentValidator.ValidateNotNull( nameof( stream ), stream );
 191         this.StringPool = stringPool ?? BinaryStringPoolFactory.NewNotConcurrentBinaryStringPool( encoding.Encoding );
 192         this.Encoding = ArgumentValidator.ValidateNotNull( nameof( encoding ), encoding );
 193         this.WriteState = writeState ?? new WriteState();
 194         this.ReadState = readState ?? new ReadState();
 195      }
 196
 197      public WriteState WriteState { get; }
 198
 199      public ReadState ReadState { get; }
 200
 201      public Stream Stream { get; }
 202
 203      public BinaryStringPool StringPool { get; }
 204
 205      public IEncodingInfo Encoding { get; }
 206   }
 207
 208   internal sealed class HTTPConnectionVendorImpl<TRequestMetaData> : HTTPConnectionVendorFunctionality<TRequestMetaData
 209   {
 210      internal static HTTPConnectionVendorImpl<TRequestMetaData> Instance { get; } = new HTTPConnectionVendorImpl<TReque
 211
 212      private HTTPConnectionVendorImpl()
 213      {
 214
 215      }
 216
 217      public HTTPStatement<TRequestMetaData> CreateStatementBuilder( HTTPRequestInfo<TRequestMetaData> creationArgs )
 218      {
 219         return new HTTPStatementImpl<TRequestMetaData>( creationArgs );
 220      }
 221   }
 222
 223   internal sealed class HTTPWriterImpl : HTTPWriter
 224   {
 225      private readonly ResizableArray<Byte> _buffer;
 226      private readonly Stream _stream;
 227      private readonly CancellationToken _token;
 228
 229      public HTTPWriterImpl( ResizableArray<Byte> buffer, Stream stream, CancellationToken token )
 230      {
 231         this._buffer = ArgumentValidator.ValidateNotNull( nameof( buffer ), buffer );
 232         this._stream = ArgumentValidator.ValidateNotNull( nameof( stream ), stream );
 233         this._token = token;
 234      }
 235
 236      public Byte[] Buffer => this._buffer.Array;
 237
 238      public async ValueTask<Int64> FlushBufferContents( Int32 offset, Int32 count )
 239      {
 240         await this._stream.WriteAsync( this.Buffer, offset, count, this._token );
 241         return count - offset;
 242      }
 243   }
 244
 245}
 246
 247/// <summary>
 248/// This class contains extension methods for types defined in this assembly.
 249/// </summary>
 250public static partial class E_CBAM
 251{
 252   private const Byte CR = 0x0D; // \r
 253   private const Byte LF = 0x0A; // \n
 254   private const Byte SPACE = 0x20;
 255   private const Byte COLON = 0x3A;
 256
 257   private const String CRLF = "\r\n";
 1258   private static readonly Byte[] CRLF_BYTES = new[] { (Byte) '\r', (Byte) '\n' };
 259   private const String SPACE_STR = " ";
 260   private const String COLON_STR = ":";
 261
 262   internal static Task WriteHTTPString( this Stream stream, ResizableArray<Byte> buffer, Encoding encoding, String str,
 263   {
 264      Task retVal;
 12265      if ( !String.IsNullOrEmpty( str ) )
 266      {
 12267         var strByteCount = encoding.GetByteCount( str );
 12268         var count = bufferIndex + strByteCount;
 12269         var array = buffer.SetCapacityAndReturnArray( count );
 12270         encoding.GetBytes( str, 0, str.Length, array, bufferIndex );
 12271         retVal = stream.WriteAsync( array, 0, count, token );
 12272      }
 273      else
 274      {
 0275         retVal = TaskUtils.CompletedTask;
 276      }
 277
 12278      return retVal;
 279   }
 280
 281   internal static async Task<String> SendRequest(
 282      this ClientProtocolIOState state,
 283      HTTPRequest request,
 284      CancellationToken token
 285      )
 286   {
 287      // null as "separator" treats "str" as URI path and query
 288      // Empty string as "separator" prevents all escaping
 289      String EscapeHTTPComponentString( String str, String separator )
 290      {
 10291         if ( !String.IsNullOrEmpty( str ) )
 292         {
 10293            if ( separator == null && str != "*" && !Uri.IsWellFormedUriString( str, UriKind.RelativeOrAbsolute ) )
 294            {
 0295               str = new Uri( "dummy://dummy:1" + ( str[0] == '/' ? "" : "/" ) + str ).PathAndQuery;
 0296            }
 10297            else if ( !String.IsNullOrEmpty( separator ) && str.IndexOf( separator ) >= 0 )
 298            {
 299               // TODO extremely ineffective, but hopefully we won't be going here very often
 0300               str = str.Replace( separator, new String( separator.ToCharArray().SelectMany( s => new[] { '\\', s } ).To
 301            }
 302         }
 10303         return str;
 304      }
 305
 2306      String method = null;
 307
 2308      var stream = state.Stream;
 2309      var wState = state.WriteState;
 2310      var buffer = wState.Buffer;
 2311      var encoding = state.Encoding.Encoding;
 312
 313      // First line - method, path, version
 2314      await stream.WriteHTTPString( buffer, encoding, method = EscapeHTTPComponentString( request.Method, SPACE_STR ), t
 2315      buffer.Array[0] = SPACE;
 2316      var path = request.Path;
 2317      if ( String.IsNullOrEmpty( path ) )
 318      {
 2319         path = "/";
 320      }
 2321      await stream.WriteHTTPString( buffer, encoding, EscapeHTTPComponentString( path, null ), token, 1 );
 2322      await stream.WriteHTTPString( buffer, encoding, EscapeHTTPComponentString( request.Version, CRLF ), token, 1 );
 323      // CRLF will be sent as part of sending headers
 324
 325      // Headers
 8326      foreach ( var hdr in request.Headers )
 327      {
 8328         foreach ( var hdrValue in hdr.Value )
 329         {
 2330            buffer.Array[0] = CR;
 2331            buffer.Array[1] = LF;
 2332            await stream.WriteHTTPString( buffer, encoding, EscapeHTTPComponentString( hdr.Key.Trim(), CRLF ), token, 2 
 2333            buffer.Array[0] = COLON;
 2334            await stream.WriteHTTPString( buffer, encoding, EscapeHTTPComponentString( hdrValue, CRLF ), token, 1 );
 2335         }
 2336      }
 337
 2338      await stream.WriteHTTPString( buffer, encoding, CRLF + CRLF, token );
 2339      await stream.FlushAsync( default );
 340
 341      // Body
 2342      var body = request.Content;
 2343      if ( body != null )
 344      {
 0345         var bodySize = body.ByteCount;
 0346         if ( ( bodySize ?? -1 ) != 0 )
 347         {
 0348            if ( bodySize.HasValue )
 349            {
 0350               buffer.CurrentMaxCapacity = buffer.MaximumSize < 0 ? (Int32) bodySize.Value : (Int32) Math.Min( bodySize.
 351            }
 352
 0353            await body.WriteToStream( new HTTPWriterImpl( buffer, stream, token ), bodySize );
 354
 0355            await stream.FlushAsync( default );
 356         }
 357      }
 358
 2359      return method;
 2360   }
 361
 362   internal static async ValueTask<HTTPResponse> ReceiveResponse(
 363      this ClientProtocolIOState state,
 364      String requestMethod,
 365      CancellationToken token
 366      )
 367   {
 368      String UnescapeHTTPComponentString( String str ) //, String separator )
 369      {
 370         //if ( !String.IsNullOrEmpty( str ) )
 371         //{
 372         //   if ( separator == null && str != "*" )
 373         //   {
 374         //      //str = new Uri( str, UriKind.RelativeOrAbsolute ).PathAndQuery;
 375         //   }
 376         //   else if ( !String.IsNullOrEmpty( separator ) &&  )
 377         //      str = new Uri( "dummy://dummy:1" + ( str[0] == '/' ? "" : "/" ) + str ).PathAndQuery;
 378         //}
 38379         return str;
 380      }
 381
 2382      var streamReadCount = 0x1000;
 2383      var stream = state.Stream;
 2384      var rState = state.ReadState;
 2385      var buffer = rState.Buffer;
 2386      var aState = rState.BufferAdvanceState;
 2387      var strings = state.StringPool;
 388
 389      // Read first line
 2390      if ( aState.BufferTotal > 0 )
 391      {
 0392         HTTPUtils.EraseReadData( aState, buffer, true );
 393      }
 2394      await stream.ReadUntilMaybeAsync( buffer, aState, CRLF_BYTES, streamReadCount );
 2395      var array = buffer.Array;
 2396      var idx = Array.IndexOf( array, SPACE );
 2397      var version = UnescapeHTTPComponentString( strings.GetString( array, 0, idx ) );
 398
 2399      var start = idx + 1;
 2400      idx = Array.IndexOf( array, SPACE, start );
 2401      var statusCode = UnescapeHTTPComponentString( strings.GetString( array, start, idx - start ) );
 2402      Int32.TryParse( statusCode, out var statusCodeInt );
 403
 404      // The rest is message
 2405      var statusMessage = UnescapeHTTPComponentString( strings.GetString( array, idx + 1, aState.BufferOffset - idx - 1 
 406      // Read headers - one line at a time
 407      // TODO max header count limit (how many fieldname:fieldvalue lines)
 2408      var headers = HTTPFactory.CreateHeadersDictionary();
 2409      HTTPUtils.EraseReadData( aState, buffer );
 410      Int32 bufferOffset;
 411      do
 412      {
 18413         await stream.ReadUntilMaybeAsync( buffer, aState, CRLF_BYTES, streamReadCount );
 18414         bufferOffset = aState.BufferOffset;
 18415         if ( bufferOffset > 0 )
 416         {
 16417            array = buffer.Array;
 16418            idx = Array.IndexOf( array, COLON );
 16419            if ( idx > 0 )
 420            {
 16421               start = 0;
 422               // In this block, "idx" = count
 16423               TrimBeginAndEnd( array, ref start, ref idx, false );
 16424               if ( start < idx )
 425               {
 16426                  var headerName = UnescapeHTTPComponentString( strings.GetString( array, start, idx ) );
 16427                  start = idx + 1;
 16428                  idx = bufferOffset - start;
 16429                  TrimBeginAndEnd( array, ref start, ref idx, true );
 430                  String headerValue;
 16431                  if ( idx > 0 )
 432                  {
 16433                     headerValue = UnescapeHTTPComponentString( strings.GetString( array, start, idx ) );
 16434                  }
 435                  else
 436                  {
 0437                     headerValue = String.Empty;
 438                  }
 16439                  headers
 32440                     .GetOrAdd_NotThreadSafe( headerName, hn => new List<String>( 1 ) )
 16441                     .Add( headerValue );
 442               }
 443            }
 444         }
 445
 18446         HTTPUtils.EraseReadData( aState, buffer );
 18447      } while ( bufferOffset > 0 );
 448
 449      // Now we can set the content, if it is present
 450      // https://tools.ietf.org/html/rfc7230#section-3.3
 2451      var hasContent = CanHaveMessageContent( requestMethod, statusCodeInt );
 452      HTTPResponseContent responseContent;
 2453      if ( hasContent )
 454      {
 455
 2456         if ( headers.TryGetValue( "Content-Length", out var headerValues )
 2457            && headerValues.Count > 0
 2458            && Int64.TryParse( headerValues[0], out var contentLengthInt )
 2459            )
 460         {
 2461            responseContent = HTTPFactory.CreateResponseContentWithKnownByteCount(
 2462               stream,
 2463               buffer.Array,
 2464               aState,
 2465               contentLengthInt,
 2466               token
 2467               );
 2468         }
 0469         else if ( headers.TryGetValue( "Transfer-Encoding", out headerValues )
 0470          && headerValues.Count > 0
 0471          && headerValues.Any( xferEncoding => String.Equals( xferEncoding, "chunked", StringComparison.OrdinalIgnoreCas
 0472          )
 473         {
 0474            responseContent = await HTTPFactory.CreateResponseContentWithChunkedEncoding(
 0475               stream,
 0476               buffer,
 0477               aState,
 0478               streamReadCount,
 0479               token
 0480               );
 0481         }
 482         else
 483         {
 0484            throw new InvalidOperationException( "Response did not have content length nor recognizable transfer encodin
 485         }
 486      }
 487      else
 488      {
 0489         responseContent = EmptyHTTPResponseContent.Instance;
 490      }
 491
 2492      return HTTPFactory.CreateResponse( version, statusCodeInt, statusMessage, headers, responseContent );
 2493   }
 494
 495
 496   private static Boolean CanHaveMessageContent( String requestMethod, Int32 statusCode )
 497   {
 498      // Request: HEAD method never has content
 499      // Response: 1XX, 204, and 304 never have content
 2500      return !( String.Equals( requestMethod, "HEAD" ) || ( statusCode >= 100 && statusCode < 200 ) || statusCode == 204
 501   }
 502
 503   private static void TrimBeginAndEnd( Byte[] array, ref Int32 start, ref Int32 count, Boolean trimEnd )
 504   {
 505      // Trim begin
 48506      while ( count > 0 && Char.IsWhiteSpace( (Char) array[start] ) )
 507      {
 16508         ++start;
 16509         --count;
 510      }
 32511      if ( trimEnd )
 512      {
 513         // Trim end
 16514         while ( count > 0 && Char.IsWhiteSpace( (Char) array[start + count - 1] ) )
 515         {
 0516            --count;
 517         }
 518      }
 32519   }
 520}
 521

/repo-dir/contents/Source/Code/CBAM.HTTP.Implementation/ConnectionConfiguration.cs

#LineLine coverage
 1/*
 2 * Copyright 2017 Stanislav Muhametsin. All rights Reserved.
 3 *
 4 * Licensed  under the  Apache License,  Version 2.0  (the "License");
 5 * you may not use  this file  except in  compliance with the License.
 6 * You may obtain a copy of the License at
 7 *
 8 *   http://www.apache.org/licenses/LICENSE-2.0
 9 *
 10 * Unless required by applicable law or agreed to in writing, software
 11 * distributed  under the  License is distributed on an "AS IS" BASIS,
 12 * WITHOUT  WARRANTIES OR CONDITIONS  OF ANY KIND, either  express  or
 13 * implied.
 14 *
 15 * See the License for the specific language governing permissions and
 16 * limitations under the License.
 17 */
 18using CBAM.HTTP;
 19using IOUtils.Network.Configuration;
 20using System;
 21using System.Collections.Generic;
 22using System.Net;
 23using System.Text;
 24using UtilPack;
 25
 26namespace CBAM.HTTP
 27{
 28   /// <summary>
 29   /// This class extends <see cref="NetworkConnectionCreationInfo{TCreationData, TConnectionConfiguration, TInitializat
 30   /// </summary>
 31   public sealed class HTTPNetworkCreationInfo : NetworkConnectionCreationInfo<HTTPNetworkCreationInfoData, HTTPConnecti
 32   {
 33      /// <summary>
 34      /// Creates new instance of <see cref="HTTPNetworkCreationInfo"/> with given <see cref="HTTPNetworkCreationInfoDat
 35      /// </summary>
 36      /// <param name="data">The <see cref="HTTPNetworkCreationInfoData"/>.</param>
 37      /// <exception cref="ArgumentNullException">If <paramref name="data"/> is <c>null</c>.</exception>
 38      public HTTPNetworkCreationInfo(
 39         HTTPNetworkCreationInfoData data
 40         ) : base( data )
 41      {
 42      }
 43
 44   }
 45
 46   /// <summary>
 47   /// This class extends <see cref="NetworkConnectionCreationInfoData{TConnectionConfiguration, TInitializationConfigur
 48   /// </summary>
 49   public sealed class HTTPNetworkCreationInfoData : NetworkConnectionCreationInfoData<HTTPConnectionConfiguration, HTTP
 50   {
 51
 52   }
 53
 54   /// <summary>
 55   /// This class extends <see cref="NetworkConnectionConfiguration"/> to provide detailed and highly customizable confi
 56   /// </summary>
 57   public sealed class HTTPConnectionConfiguration : NetworkConnectionConfiguration
 58   {
 59
 60   }
 61
 62   /// <summary>
 63   /// This class extends <see cref="NetworkInitializationConfiguration{TProtocolConfiguration, TPoolingConfiguration}"/
 64   /// </summary>
 65   public sealed class HTTPInitializationConfiguration : NetworkInitializationConfiguration<HTTPProtocolConfiguration, H
 66   {
 67
 68   }
 69
 70   /// <summary>
 71   /// This class contains information about HTTP protocol initialization.
 72   /// </summary>
 73   public sealed class HTTPProtocolConfiguration
 74   {
 75   }
 76
 77   /// <summary>
 78   /// This class extends <see cref="NetworkPoolingConfiguration"/>.
 79   /// </summary>
 80   public sealed class HTTPPoolingConfiguration : NetworkPoolingConfiguration
 81   {
 82
 83   }
 84
 85   /// <summary>
 86   /// This class contains properties for simplistic HTTP configuration.
 87   /// This class may also be used when (de)serializing configuration.
 88   /// </summary>
 89   /// <seealso cref="Implementation.HTTPSimpleConfigurationPoolProvider{TRequestMetaData}"/>
 90   public class SimpleHTTPConfiguration
 91   {
 92      /// <summary>
 93      /// Gets or sets the host name for the remote endpoint.
 94      /// </summary>
 95      /// <value>The host name for the remote endpoint.</value>
 96      /// <remarks>
 97      /// This may be either stringified <see cref="IPAddress"/> or actual hostname (which will result in DNS resolve).
 98      /// </remarks>
 99      public String Host { get; set; }
 100
 101      /// <summary>
 102      /// Gets or sets the port number for the remote endpoint.
 103      /// </summary>
 104      /// <value>The port number for the remote endpoint.</value>
 105      public Int32 Port { get; set; }
 106
 107      /// <summary>
 108      /// Gets or sets the value indicating whether the connection is secured by SSL.
 109      /// </summary>
 110      /// <value>The value indicating whether the connection is secured by SSL.</value>
 111      public Boolean IsSecure { get; set; }
 112   }
 113
 114
 115}
 116
 117public static partial class E_CBAM
 118{
 119   /// <summary>
 120   /// This is helper method to create a new <see cref="HTTPNetworkCreationInfo"/> from this <see cref="SimpleHTTPConfig
 121   /// </summary>
 122   /// <param name="simpleConfig">This <see cref="SimpleHTTPConfiguration"/>.</param>
 123   /// <returns>A new instance of <see cref="HTTPNetworkCreationInfo"/> which is configured as this <see cref="SimpleHTT
 124   /// <exception cref="NullReferenceException">If this <see cref="SimpleHTTPConfiguration"/> is <c>null</c>.</exception
 125   public static HTTPNetworkCreationInfo CreateNetworkCreationInfo( this SimpleHTTPConfiguration simpleConfig )
 126   {
 2127      var isSecure = simpleConfig.IsSecure;
 2128      var port = simpleConfig.Port;
 2129      return new HTTPNetworkCreationInfo( new HTTPNetworkCreationInfoData()
 2130      {
 2131         Connection = new HTTPConnectionConfiguration()
 2132         {
 2133            ConnectionSSLMode = isSecure ? ConnectionSSLMode.Required : ConnectionSSLMode.NotRequired,
 2134            Host = simpleConfig.Host,
 2135            Port = port <= 0 ? ( isSecure ? 443 : 80 ) : port
 2136         },
 2137
 2138      } );
 139   }
 140}

/repo-dir/contents/Source/Code/CBAM.HTTP.Implementation/ConnectionFactory.cs

#LineLine coverage
 1/*
 2 * Copyright 2018 Stanislav Muhametsin. All rights Reserved.
 3 *
 4 * Licensed  under the  Apache License,  Version 2.0  (the "License");
 5 * you may not use  this file  except in  compliance with the License.
 6 * You may obtain a copy of the License at
 7 *
 8 *   http://www.apache.org/licenses/LICENSE-2.0
 9 *
 10 * Unless required by applicable law or agreed to in writing, software
 11 * distributed  under the  License is distributed on an "AS IS" BASIS,
 12 * WITHOUT  WARRANTIES OR CONDITIONS  OF ANY KIND, either  express  or
 13 * implied.
 14 *
 15 * See the License for the specific language governing permissions and
 16 * limitations under the License.
 17 */
 18using CBAM.Abstractions.Implementation;
 19using CBAM.Abstractions.Implementation.NetworkStream;
 20using CBAM.HTTP;
 21using CBAM.HTTP.Implementation;
 22using IOUtils.Network.Configuration;
 23using ResourcePooling.Async.Abstractions;
 24using System;
 25using System.Collections.Generic;
 26using System.IO;
 27using System.Net;
 28using System.Text;
 29using System.Threading;
 30using System.Threading.Tasks;
 31using UtilPack;
 32
 33namespace CBAM.HTTP.Implementation
 34{
 35   /// <summary>
 36   /// This class provides static <see cref="Factory"/> property to create connection pools that provide instances of <s
 37   /// This class also can be used dynamically by some other code, and then use <see cref="AsyncResourceFactoryProvider.
 38   /// The configuration type for this factory class is <see cref="HTTPNetworkCreationInfo"/>.
 39   /// For simpler configuration support, see <see cref="HTTPSimpleConfigurationPoolProvider{TRequestMetaData}"/>
 40   /// </summary>
 41   /// <typeparam name="TRequestMetaData">The type of metadata associated with each request, used in identifying the req
 42   /// <seealso cref="HTTPSimpleConfigurationPoolProvider{TRequestMetaData}"/>
 43   public sealed class HTTPNetworkConnectionPoolProvider<TRequestMetaData> : AbstractAsyncResourceFactoryProvider<HTTPCo
 44   {
 45
 46      /// <summary>
 47      /// Gets the <see cref="AsyncResourceFactory{TResource, TParams}"/> which can create pools that provide instances 
 48      /// </summary>
 49      /// <value>The <see cref="AsyncResourceFactory{TResource, TParams}"/> which can create <see cref="HTTPConnection{T
 50      /// <remarks>
 51      /// By invoking <see cref="AsyncResourceFactory{TResource, TParams}.BindCreationParameters"/>, one gets the bound 
 52      /// Instead of directly using <see cref="AsyncResourceFactory{TResource}.CreateAcquireResourceContext"/>, typical 
 53      /// </remarks>
 54      public static AsyncResourceFactory<HTTPConnection<TRequestMetaData>, HTTPNetworkCreationInfo> Factory { get; } = n
 55            .NewFactoryParametrizer<HTTPNetworkCreationInfo, HTTPNetworkCreationInfoData, HTTPConnectionConfiguration, H
 56            .BindPublicConnectionType<HTTPConnection<TRequestMetaData>>()
 57            .CreateStatelessDelegatingConnectionFactory(
 58               Encoding.ASCII.CreateDefaultEncodingInfo(),
 59               ( parameters, encodingInfo, stringPool, stringPoolIsDedicated ) =>
 60               {
 61                  return TaskUtils.TaskFromBoolean( ( parameters.CreationData?.Connection?.ConnectionSSLMode ?? Connecti
 62               },
 63               null,
 64               null,
 65               null,
 66               null,
 67               null,
 68               ( parameters, encodingInfo, stringPool, stringPoolIsDedicated, stream, socketOrNull, token ) =>
 69               {
 70                  return new ValueTask<HTTPConnectionFunctionalityImpl<TRequestMetaData>>(
 71                     new HTTPConnectionFunctionalityImpl<TRequestMetaData>(
 72                        HTTPConnectionVendorImpl<TRequestMetaData>.Instance,
 73                        new ClientProtocolIOState(
 74                           stream,
 75                           stringPool,
 76                           encodingInfo,
 77                           new WriteState(),
 78                           new ReadState()
 79                           ) )
 80                        );
 81               },
 82               functionality => new ValueTask<HTTPConnectionImpl<TRequestMetaData>>( new HTTPConnectionImpl<TRequestMeta
 83               ( functionality, connection ) => new StatelessConnectionAcquireInfo<HTTPConnectionImpl<TRequestMetaData>,
 84               ( functionality, connection, token, error ) => functionality?.Stream
 85               ) );
 86
 87      /// <summary>
 88      /// Creates a new instance of <see cref="HTTPNetworkConnectionPoolProvider{TRequestMetaData}"/>.
 89      /// </summary>
 90      /// <remarks>
 91      /// This constructor is not intended to be used directly, but a generic scenarios like MSBuild task dynamically lo
 92      /// </remarks>
 93      public HTTPNetworkConnectionPoolProvider()
 94         : base( typeof( HTTPNetworkCreationInfoData ) )
 95      {
 96      }
 97
 98      /// <inheritdoc />
 99      protected override HTTPNetworkCreationInfo TransformFactoryParameters( Object creationParameters )
 100      {
 101         ArgumentValidator.ValidateNotNull( nameof( creationParameters ), creationParameters );
 102
 103         HTTPNetworkCreationInfo retVal;
 104         switch ( creationParameters )
 105         {
 106            case HTTPNetworkCreationInfoData creationData:
 107               retVal = new HTTPNetworkCreationInfo( creationData );
 108               break;
 109            case HTTPNetworkCreationInfo creationInfo:
 110               retVal = creationInfo;
 111               break;
 112            case SimpleHTTPConfiguration simpleConfig:
 113               retVal = simpleConfig.CreateNetworkCreationInfo();
 114               break;
 115            default:
 116               throw new ArgumentException( $"The {nameof( creationParameters )} must be instance of {typeof( HTTPNetwor
 117         }
 118         return retVal;
 119      }
 120
 121      /// <summary>
 122      /// This method implements <see cref="AbstractAsyncResourceFactoryProvider{TFactoryResource, TCreationParameters}.
 123      /// </summary>
 124      /// <returns>The value of <see cref="Factory"/> static property.</returns>
 125      protected override AsyncResourceFactory<HTTPConnection<TRequestMetaData>, HTTPNetworkCreationInfo> CreateFactory()
 126         => Factory;
 127   }
 128
 129   /// <summary>
 130   /// This class provides static <see cref="Factory"/> property to create connection pools that provide instances of <s
 131   /// This class also can be used dynamically by some other code, and then use <see cref="AsyncResourceFactoryProvider.
 132   /// The configuration type for this factory class is <see cref="SimpleHTTPConfiguration"/>.
 133   /// For more advanced and customizable configuration support, see <see cref="HTTPNetworkConnectionPoolProvider{TReque
 134   /// </summary>
 135   /// <typeparam name="TRequestMetaData">The type of metadata associated with each request, used in identifying the req
 136   /// <seealso cref="HTTPNetworkConnectionPoolProvider{TRequestMetaData}"/>
 137   public sealed class HTTPSimpleConfigurationPoolProvider<TRequestMetaData> : AbstractAsyncResourceFactoryProvider<HTTP
 138   {
 139      /// <summary>
 140      /// Gets the <see cref="AsyncResourceFactory{TResource, TParams}"/> which can create pools that provide instances 
 141      /// </summary>
 142      /// <value>The <see cref="AsyncResourceFactory{TResource, TParams}"/> which can create <see cref="HTTPConnection{T
 143      /// <remarks>
 144      /// By invoking <see cref="AsyncResourceFactory{TResource, TParams}.BindCreationParameters"/>, one gets the bound 
 145      /// Instead of directly using <see cref="AsyncResourceFactory{TResource}.CreateAcquireResourceContext"/>, typical 
 146      /// </remarks>
 147      public static AsyncResourceFactory<HTTPConnection<TRequestMetaData>, SimpleHTTPConfiguration> Factory { get; } = n
 148
 149      /// <summary>
 150      /// Creates a new instance of <see cref="HTTPSimpleConfigurationPoolProvider{TRequestMetaData}"/>.
 151      /// </summary>
 152      /// <remarks>
 153      /// This constructor is not intended to be used directly, but a generic scenarios like MSBuild task dynamically lo
 154      /// </remarks>
 155      public HTTPSimpleConfigurationPoolProvider()
 156         : base( typeof( SimpleHTTPConfiguration ) )
 157      {
 158      }
 159
 160      /// <inheritdoc />
 161      protected override SimpleHTTPConfiguration TransformFactoryParameters( Object creationParameters )
 162      {
 163         ArgumentValidator.ValidateNotNull( nameof( creationParameters ), creationParameters );
 164
 165         SimpleHTTPConfiguration retVal;
 166         switch ( creationParameters )
 167         {
 168            case SimpleHTTPConfiguration simpleConfig:
 169               retVal = simpleConfig;
 170               break;
 171            default:
 172               throw new ArgumentException( $"The {nameof( creationParameters )} must be instance of { typeof( SimpleHTT
 173         }
 174         return retVal;
 175      }
 176
 177      /// <summary>
 178      /// This method implements <see cref="AbstractAsyncResourceFactoryProvider{TFactoryResource, TCreationParameters}.
 179      /// </summary>
 180      /// <returns>The value of <see cref="Factory"/> static property.</returns>
 181      protected override AsyncResourceFactory<HTTPConnection<TRequestMetaData>, SimpleHTTPConfiguration> CreateFactory()
 182         => Factory;
 183   }
 184
 185
 186}
 187
 188public static partial class E_CBAM
 189{
 190   /// <summary>
 191   /// This is ease-of-life method to asynchronously create <see cref="HTTPConnection{TRequestMetaData}"/> pool, send on
 192   /// </summary>
 193   /// <param name="config">This <see cref="SimpleHTTPConfiguration"/>.</param>
 194   /// <param name="request">The request to send.</param>
 195   /// <param name="defaultEncoding">The default encoding to use when stringifying the response contents. If not specifi
 196   /// <returns>Asynchronously returns the <see cref="HTTPTextualResponseInfo"/> of the first response that server sends
 197   /// <exception cref="NullReferenceException">If this <see cref="SimpleHTTPConfiguration"/> is <c>null</c>.</exception
 198   /// <exception cref="ArgumentNullException">If <paramref name="request"/> is <c>null</c>.</exception>
 199   public static Task<HTTPTextualResponseInfo> CreatePoolAndReceiveTextualResponseAsync( this SimpleHTTPConfiguration co
 200   {
 2201      ArgumentValidator.ValidateNotNullReference( config );
 2202      ArgumentValidator.ValidateNotNull( nameof( request ), request );
 2203      return HTTPSimpleConfigurationPoolProvider<Int64>
 2204         .Factory
 2205         .BindCreationParameters( config )
 2206         .CreateOneTimeUseResourcePool()
 6207         .UseResourceAsync( async conn => { return await conn.ReceiveOneTextualResponseAsync( request, defaultEncoding: 
 208   }
 209}