44package io .clientcore .core .http .pipeline ;
55
66import io .clientcore .core .http .client .HttpClient ;
7+ import io .clientcore .core .instrumentation .logging .ClientLogger ;
78import io .clientcore .core .util .configuration .Configuration ;
89
910import java .util .ArrayList ;
10- import java .util .Arrays ;
11+ import java .util .LinkedList ;
1112import java .util .List ;
13+ import java .util .Objects ;
1214
1315/**
1416 * This class provides a fluent builder API to help aid the configuration and instantiation of the {@link HttpPipeline},
2830 * <!-- end io.clientcore.core.http.HttpPipelineBuilder.noConfiguration -->
2931 *
3032 * <p>Create a pipeline using the default HTTP client and a retry policy</p>
31- *
33+ *
3234 * <!-- src_embed io.clientcore.core.http.HttpPipelineBuilder.defaultHttpClientWithRetryPolicy -->
3335 * <pre>
3436 * HttpPipeline pipeline = new HttpPipelineBuilder()
4143 * @see HttpPipeline
4244 */
4345public class HttpPipelineBuilder {
46+ private static final ClientLogger LOGGER = new ClientLogger (HttpPipelineBuilder .class );
47+
4448 private HttpClient httpClient ;
45- private List <HttpPipelinePolicy > pipelinePolicies ;
49+
50+ private final LinkedList <HttpPipelinePolicy > beforeRedirect = new LinkedList <>();
51+ private HttpRedirectPolicy redirectPolicy ;
52+ private final LinkedList <HttpPipelinePolicy > betweenRedirectAndRetry = new LinkedList <>();
53+ private HttpRetryPolicy retryPolicy ;
54+ private final LinkedList <HttpPipelinePolicy > betweenRetryAndAuthentication = new LinkedList <>();
55+ private HttpCredentialPolicy credentialPolicy ;
56+ private final LinkedList <HttpPipelinePolicy > betweenAuthenticationAndInstrumentation = new LinkedList <>();
57+ private HttpInstrumentationPolicy instrumentationPolicy ;
58+ private final LinkedList <HttpPipelinePolicy > afterInstrumentation = new LinkedList <>();
4659
4760 /**
4861 * Creates a new instance of HttpPipelineBuilder that can configure options for the {@link HttpPipeline} before
@@ -60,7 +73,32 @@ public HttpPipelineBuilder() {
6073 * @return A HttpPipeline with the options set from the builder.
6174 */
6275 public HttpPipeline build () {
63- List <HttpPipelinePolicy > policies = (pipelinePolicies == null ) ? new ArrayList <>() : pipelinePolicies ;
76+ List <HttpPipelinePolicy > policies = new ArrayList <>(beforeRedirect );
77+
78+ if (redirectPolicy != null ) {
79+ policies .add (redirectPolicy );
80+ }
81+
82+ policies .addAll (betweenRedirectAndRetry );
83+
84+ if (retryPolicy != null ) {
85+ policies .add (retryPolicy );
86+ }
87+
88+ policies .addAll (betweenRetryAndAuthentication );
89+
90+ if (credentialPolicy != null ) {
91+ policies .add (credentialPolicy );
92+ }
93+
94+ policies .addAll (betweenAuthenticationAndInstrumentation );
95+
96+ if (instrumentationPolicy != null ) {
97+ policies .add (instrumentationPolicy );
98+ }
99+
100+ policies .addAll (afterInstrumentation );
101+
64102 HttpClient client ;
65103
66104 if (httpClient != null ) {
@@ -80,7 +118,6 @@ public HttpPipeline build() {
80118 * Sets the HttpClient that the pipeline will use to send requests.
81119 *
82120 * @param httpClient The HttpClient the pipeline will use when sending requests.
83- *
84121 * @return The updated HttpPipelineBuilder object.
85122 */
86123 public HttpPipelineBuilder httpClient (HttpClient httpClient ) {
@@ -90,20 +127,82 @@ public HttpPipelineBuilder httpClient(HttpClient httpClient) {
90127 }
91128
92129 /**
93- * Adds {@link HttpPipelinePolicy policies} to the set of policies that the pipeline will use when sending
94- * requests.
95- *
96- * @param policies Policies to add to the policy set.
130+ * Adds an {@link HttpPipelinePolicy} to the builder.
131+ * <p>
132+ * The {@code policy} passed will be positioned based on {@link HttpPipelinePolicy#getOrder()}. If the
133+ * {@link HttpPipelineOrder} is null an {@link IllegalArgumentException} will be thrown.
134+ * <p>
135+ * If the {@code policy} is one of the pillar policies ({@link HttpRedirectPolicy}, {@link HttpRetryPolicy},
136+ * {@link HttpCredentialPolicy}, or {@link HttpInstrumentationPolicy}) the {@link HttpPipelineOrder} will be ignored
137+ * as those policies are positioned in a specific location within the pipeline. If a duplicate pillar policy is
138+ * added (for example two {@link HttpRetryPolicy}) the last one added will be used and a message will be logged.
97139 *
140+ * @param policy The policy to add to the pipeline.
98141 * @return The updated HttpPipelineBuilder object.
99142 */
100- public HttpPipelineBuilder policies (HttpPipelinePolicy ... policies ) {
101- if (pipelinePolicies == null ) {
102- pipelinePolicies = new ArrayList <>();
143+ public HttpPipelineBuilder addPolicy (HttpPipelinePolicy policy ) {
144+ Objects .requireNonNull (policy , "'policy' cannot be null." );
145+
146+ if (tryAddPillar (policy )) {
147+ return this ;
103148 }
104149
105- this .pipelinePolicies .addAll (Arrays .asList (policies ));
150+ HttpPipelineOrder order = policy .getOrder ();
151+ if (order == null ) {
152+ throw LOGGER .atError ()
153+ .addKeyValue ("policyType" , policy .getClass ())
154+ .log ("Policy order cannot be null." , new IllegalArgumentException ("Policy order cannot be null." ));
155+ }
156+
157+ if (order == HttpPipelineOrder .BEFORE_REDIRECT ) {
158+ beforeRedirect .add (policy );
159+ } else if (order == HttpPipelineOrder .BETWEEN_REDIRECT_AND_RETRY ) {
160+ betweenRedirectAndRetry .add (policy );
161+ } else if (order == HttpPipelineOrder .BETWEEN_RETRY_AND_AUTHENTICATION ) {
162+ betweenRetryAndAuthentication .add (policy );
163+ } else if (order == HttpPipelineOrder .BETWEEN_AUTHENTICATION_AND_INSTRUMENTATION ) {
164+ betweenAuthenticationAndInstrumentation .add (policy );
165+ } else if (order == HttpPipelineOrder .AFTER_INSTRUMENTATION ) {
166+ afterInstrumentation .add (policy );
167+ } else {
168+ throw LOGGER .atError ()
169+ .addKeyValue ("policyType" , policy .getClass ())
170+ .addKeyValue ("order" , order )
171+ .log ("Unknown policy order." , new IllegalArgumentException ("Unknown policy order." ));
172+ }
106173
107174 return this ;
108175 }
176+
177+ private boolean tryAddPillar (HttpPipelinePolicy policy ) {
178+ HttpPipelinePolicy previous = null ;
179+ boolean added = false ;
180+
181+ HttpPipelineOrder order = policy .getOrder ();
182+ if (order == HttpPipelineOrder .REDIRECT ) {
183+ previous = redirectPolicy ;
184+ redirectPolicy = (HttpRedirectPolicy ) policy ;
185+ added = true ;
186+ } else if (order == HttpPipelineOrder .RETRY ) {
187+ previous = retryPolicy ;
188+ retryPolicy = (HttpRetryPolicy ) policy ;
189+ added = true ;
190+ } else if (order == HttpPipelineOrder .AUTHENTICATION ) {
191+ previous = credentialPolicy ;
192+ credentialPolicy = (HttpCredentialPolicy ) policy ;
193+ added = true ;
194+ } else if (order == HttpPipelineOrder .INSTRUMENTATION ) {
195+ previous = instrumentationPolicy ;
196+ instrumentationPolicy = (HttpInstrumentationPolicy ) policy ;
197+ added = true ;
198+ }
199+
200+ if (previous != null ) {
201+ LOGGER .atWarning ()
202+ .addKeyValue ("policyType" , previous .getClass ().getSimpleName ())
203+ .log ("A pillar policy was replaced in the pipeline." );
204+ }
205+
206+ return added ;
207+ }
109208}
0 commit comments