11// Copyright (c) Microsoft Corporation. All rights reserved.
22// Licensed under the MIT License.
33
4+ using System ;
5+ using System . Collections . Generic ;
46using System . Threading ;
7+ using Azure . Core ;
8+ using Azure . Core . Pipeline ;
59
610namespace Azure
711{
812 /// <summary>
913 /// Options that can be used to control the behavior of a request sent by a client.
1014 /// </summary>
11- public class RequestContext : RequestOptions
15+ public class RequestContext
1216 {
17+ private bool _frozen ;
18+
19+ private ( int Status , bool IsError ) [ ] ? _statusCodes ;
20+ internal ( int Status , bool IsError ) [ ] ? StatusCodes => _statusCodes ;
21+
22+ private ResponseClassificationHandler [ ] ? _handlers ;
23+ internal ResponseClassificationHandler [ ] ? Handlers => _handlers ;
24+
25+ internal List < ( HttpPipelinePosition Position , HttpPipelinePolicy Policy ) > ? Policies { get ; private set ; }
26+
1327 /// <summary>
14- /// Initializes a new instance of the <see cref="RequestContext"/> class .
28+ /// Controls under what conditions the operation raises an exception if the underlying response indicates a failure .
1529 /// </summary>
16- public RequestContext ( ) : base ( )
17- {
18- }
30+ public ErrorOptions ErrorOptions { get ; set ; } = ErrorOptions . Default ;
31+
32+ /// <summary>
33+ /// The token to check for cancellation.
34+ /// </summary>
35+ public CancellationToken CancellationToken { get ; set ; } = CancellationToken . None ;
1936
2037 /// <summary>
21- /// Initializes a new instance of the <see cref="RequestContext"/> class from
22- /// a <see cref="RequestOptions"/> instance.
38+ /// Initializes a new instance of the <see cref="RequestContext"/> class.
2339 /// </summary>
24- public RequestContext ( RequestOptions options ) : base ( options )
40+ public RequestContext ( )
2541 {
2642 }
2743
@@ -32,8 +48,103 @@ public RequestContext(RequestOptions options) : base(options)
3248 public static implicit operator RequestContext ( ErrorOptions options ) => new RequestContext { ErrorOptions = options } ;
3349
3450 /// <summary>
35- /// The token to check for cancellation.
51+ /// Adds an <see cref="HttpPipelinePolicy"/> into the pipeline for the duration of this request.
52+ /// The position of policy in the pipeline is controlled by <paramref name="position"/> parameter.
53+ /// If you want the policy to execute once per client request use <see cref="HttpPipelinePosition.PerCall"/>
54+ /// otherwise use <see cref="HttpPipelinePosition.PerRetry"/> to run the policy for every retry.
3655 /// </summary>
37- public CancellationToken CancellationToken { get ; set ; } = CancellationToken . None ;
56+ /// <param name="policy">The <see cref="HttpPipelinePolicy"/> instance to be added to the pipeline.</param>
57+ /// <param name="position">The position of the policy in the pipeline.</param>
58+ public void AddPolicy ( HttpPipelinePolicy policy , HttpPipelinePosition position )
59+ {
60+ Policies ??= new ( ) ;
61+ Policies . Add ( ( position , policy ) ) ;
62+ }
63+
64+ /// <summary>
65+ /// Customizes the <see cref="ResponseClassifier"/> for this operation to change
66+ /// the default <see cref="Response"/> classification behavior so that it considers
67+ /// the passed-in status code to be an error or not, as specified.
68+ /// Status code classifiers are applied after all <see cref="ResponseClassificationHandler"/> classifiers.
69+ /// This is useful for cases where you'd like to prevent specific response status codes from being treated as errors by
70+ /// logging and distributed tracing policies -- that is, if a response is not classified as an error, it will not appear as an error in
71+ /// logs or distributed traces.
72+ /// </summary>
73+ /// <param name="statusCode">The status code to customize classification for.</param>
74+ /// <param name="isError">Whether the passed-in status code should be classified as an error.</param>
75+ /// <exception cref="ArgumentOutOfRangeException">statusCode is not between 100 and 599 (inclusive).</exception>
76+ /// <exception cref="InvalidOperationException">If this method is called after the <see cref="RequestContext"/> has been
77+ /// used in a method call.</exception>
78+ public void AddClassifier ( int statusCode , bool isError )
79+ {
80+ Argument . AssertInRange ( statusCode , 100 , 599 , nameof ( statusCode ) ) ;
81+
82+ if ( _frozen )
83+ {
84+ throw new InvalidOperationException ( "Cannot modify classifiers after this type has been used in a method call." ) ;
85+ }
86+
87+ int length = _statusCodes == null ? 0 : _statusCodes . Length ;
88+ Array . Resize ( ref _statusCodes , length + 1 ) ;
89+ Array . Copy ( _statusCodes , 0 , _statusCodes , 1 , length ) ;
90+ _statusCodes [ 0 ] = ( statusCode , isError ) ;
91+ }
92+
93+ /// <summary>
94+ /// Customizes the <see cref="ResponseClassifier"/> for this operation.
95+ /// Adding a <see cref="ResponseClassificationHandler"/> changes the classification
96+ /// behavior so that it first tries to classify a response via the handler, and if
97+ /// the handler doesn't have an opinion, it instead uses the default classifier.
98+ /// Handlers are applied in order so the most recently added takes precedence.
99+ /// This is useful for cases where you'd like to prevent specific response status codes from being treated as errors by
100+ /// logging and distributed tracing policies -- that is, if a response is not classified as an error, it will not appear as an error in
101+ /// logs or distributed traces.
102+ /// </summary>
103+ /// <param name="classifier">The custom classifier.</param>
104+ /// <exception cref="InvalidOperationException">If this method is called after the <see cref="RequestContext"/> has been
105+ /// used in a method call.</exception>
106+ public void AddClassifier ( ResponseClassificationHandler classifier )
107+ {
108+ if ( _frozen )
109+ {
110+ throw new InvalidOperationException ( "Cannot modify classifiers after this type has been used in a method call." ) ;
111+ }
112+
113+ int length = _handlers == null ? 0 : _handlers . Length ;
114+ Array . Resize ( ref _handlers , length + 1 ) ;
115+ Array . Copy ( _handlers , 0 , _handlers , 1 , length ) ;
116+ _handlers [ 0 ] = classifier ;
117+ }
118+
119+ internal void Freeze ( )
120+ {
121+ _frozen = true ;
122+ }
123+
124+ internal ResponseClassifier Apply ( ResponseClassifier classifier )
125+ {
126+ if ( _statusCodes == null && _handlers == null )
127+ {
128+ return classifier ;
129+ }
130+
131+ if ( classifier is CoreResponseClassifier coreClassifier )
132+ {
133+ CoreResponseClassifier clone = coreClassifier . Clone ( ) ;
134+ clone . Handlers = _handlers ;
135+
136+ if ( _statusCodes != null )
137+ {
138+ foreach ( var classification in _statusCodes )
139+ {
140+ clone . AddClassifier ( classification . Status , classification . IsError ) ;
141+ }
142+ }
143+
144+ return clone ;
145+ }
146+
147+ return new ChainingClassifier ( _statusCodes , _handlers , classifier ) ;
148+ }
38149 }
39150}
0 commit comments