99
1010package org .elasticsearch .repositories .azure ;
1111
12- import com .sun .net .httpserver .Headers ;
1312import com .sun .net .httpserver .HttpExchange ;
1413import com .sun .net .httpserver .HttpHandler ;
1514
3433
3534import java .io .IOException ;
3635import java .nio .ByteBuffer ;
37- import java .nio .charset .StandardCharsets ;
3836import java .util .ArrayList ;
3937import java .util .List ;
4038import java .util .Map ;
4846import java .util .stream .IntStream ;
4947
5048import static org .elasticsearch .repositories .azure .AbstractAzureServerTestCase .randomBlobContent ;
49+ import static org .elasticsearch .repositories .azure .ResponseInjectingAzureHttpHandler .createFailNRequestsHandler ;
5150import static org .elasticsearch .repositories .blobstore .BlobStoreTestUtil .randomPurpose ;
5251import static org .hamcrest .Matchers .greaterThanOrEqualTo ;
5352import static org .hamcrest .Matchers .hasSize ;
@@ -61,7 +60,7 @@ public class AzureBlobStoreRepositoryMetricsTests extends AzureBlobStoreReposito
6160 );
6261 private static final int MAX_RETRIES = 3 ;
6362
64- private final Queue <RequestHandler > requestHandlers = new ConcurrentLinkedQueue <>();
63+ private final Queue <ResponseInjectingAzureHttpHandler . RequestHandler > requestHandlers = new ConcurrentLinkedQueue <>();
6564
6665 @ Override
6766 protected Map <String , HttpHandler > createHttpHandlers () {
@@ -106,7 +105,8 @@ public void testThrottleResponsesAreCountedInMetrics() throws IOException {
106105
107106 // Queue up some throttle responses
108107 final int numThrottles = randomIntBetween (1 , MAX_RETRIES );
109- IntStream .range (0 , numThrottles ).forEach (i -> requestHandlers .offer (new FixedRequestHandler (RestStatus .TOO_MANY_REQUESTS )));
108+ IntStream .range (0 , numThrottles )
109+ .forEach (i -> requestHandlers .offer (new ResponseInjectingAzureHttpHandler .FixedRequestHandler (RestStatus .TOO_MANY_REQUESTS )));
110110
111111 // Check that the blob exists
112112 blobContainer .blobExists (purpose , blobName );
@@ -131,7 +131,13 @@ public void testRangeNotSatisfiedAreCountedInMetrics() throws IOException {
131131 clearMetrics (dataNodeName );
132132
133133 // Queue up a range-not-satisfied error
134- requestHandlers .offer (new FixedRequestHandler (RestStatus .REQUESTED_RANGE_NOT_SATISFIED , null , GET_BLOB_REQUEST_PREDICATE ));
134+ requestHandlers .offer (
135+ new ResponseInjectingAzureHttpHandler .FixedRequestHandler (
136+ RestStatus .REQUESTED_RANGE_NOT_SATISFIED ,
137+ null ,
138+ GET_BLOB_REQUEST_PREDICATE
139+ )
140+ );
135141
136142 // Attempt to read the blob
137143 assertThrows (RequestedRangeNotSatisfiedException .class , () -> blobContainer .readBlob (purpose , blobName ));
@@ -163,7 +169,7 @@ public void testErrorResponsesAreCountedInMetrics() throws IOException {
163169 if (status == RestStatus .TOO_MANY_REQUESTS ) {
164170 throttles .incrementAndGet ();
165171 }
166- requestHandlers .offer (new FixedRequestHandler (status ));
172+ requestHandlers .offer (new ResponseInjectingAzureHttpHandler . FixedRequestHandler (status ));
167173 });
168174
169175 // Check that the blob exists
@@ -259,7 +265,7 @@ public void testBatchDeleteFailure() throws IOException {
259265 clearMetrics (dataNodeName );
260266
261267 // Handler will fail one or more of the batch requests
262- final RequestHandler failNRequestRequestHandler = createFailNRequestsHandler (failedBatches );
268+ final ResponseInjectingAzureHttpHandler . RequestHandler failNRequestRequestHandler = createFailNRequestsHandler (failedBatches );
263269
264270 // Exhaust the retries
265271 IntStream .range (0 , (numberOfBatches - failedBatches ) + (failedBatches * (MAX_RETRIES + 1 )))
@@ -287,35 +293,6 @@ private long getLongCounterTotal(String dataNodeName, String metricKey) {
287293 .reduce (0L , Long ::sum );
288294 }
289295
290- /**
291- * Creates a {@link RequestHandler} that will persistently fail the first <code>numberToFail</code> distinct requests
292- * it sees. Any other requests are passed through to the delegate.
293- *
294- * @param numberToFail The number of requests to fail
295- * @return the handler
296- */
297- private static RequestHandler createFailNRequestsHandler (int numberToFail ) {
298- final List <String > requestsToFail = new ArrayList <>(numberToFail );
299- return (exchange , delegate ) -> {
300- final Headers requestHeaders = exchange .getRequestHeaders ();
301- final String requestId = requestHeaders .get ("X-ms-client-request-id" ).get (0 );
302- boolean failRequest = false ;
303- synchronized (requestsToFail ) {
304- if (requestsToFail .contains (requestId )) {
305- failRequest = true ;
306- } else if (requestsToFail .size () < numberToFail ) {
307- requestsToFail .add (requestId );
308- failRequest = true ;
309- }
310- }
311- if (failRequest ) {
312- exchange .sendResponseHeaders (500 , -1 );
313- } else {
314- delegate .handle (exchange );
315- }
316- };
317- }
318-
319296 private void clearMetrics (String discoveryNode ) {
320297 internalCluster ().getInstance (PluginsService .class , discoveryNode )
321298 .filterPlugins (TestTelemetryPlugin .class )
@@ -480,80 +457,4 @@ private void assertMatchingMetricRecorded(MetricType metricType, String metricNa
480457 assertion .accept (measurement );
481458 }
482459 }
483-
484- @ SuppressForbidden (reason = "we use a HttpServer to emulate Azure" )
485- private static class ResponseInjectingAzureHttpHandler implements DelegatingHttpHandler {
486-
487- private final HttpHandler delegate ;
488- private final Queue <RequestHandler > requestHandlerQueue ;
489-
490- ResponseInjectingAzureHttpHandler (Queue <RequestHandler > requestHandlerQueue , HttpHandler delegate ) {
491- this .delegate = delegate ;
492- this .requestHandlerQueue = requestHandlerQueue ;
493- }
494-
495- @ Override
496- public void handle (HttpExchange exchange ) throws IOException {
497- RequestHandler nextHandler = requestHandlerQueue .peek ();
498- if (nextHandler != null && nextHandler .matchesRequest (exchange )) {
499- requestHandlerQueue .poll ().writeResponse (exchange , delegate );
500- } else {
501- delegate .handle (exchange );
502- }
503- }
504-
505- @ Override
506- public HttpHandler getDelegate () {
507- return delegate ;
508- }
509- }
510-
511- @ SuppressForbidden (reason = "we use a HttpServer to emulate Azure" )
512- @ FunctionalInterface
513- private interface RequestHandler {
514- void writeResponse (HttpExchange exchange , HttpHandler delegate ) throws IOException ;
515-
516- default boolean matchesRequest (HttpExchange exchange ) {
517- return true ;
518- }
519- }
520-
521- @ SuppressForbidden (reason = "we use a HttpServer to emulate Azure" )
522- private static class FixedRequestHandler implements RequestHandler {
523-
524- private final RestStatus status ;
525- private final String responseBody ;
526- private final Predicate <HttpExchange > requestMatcher ;
527-
528- FixedRequestHandler (RestStatus status ) {
529- this (status , null , req -> true );
530- }
531-
532- /**
533- * Create a handler that only gets executed for requests that match the supplied predicate. Note
534- * that because the errors are stored in a queue this will prevent any subsequently queued errors from
535- * being returned until after it returns.
536- */
537- FixedRequestHandler (RestStatus status , String responseBody , Predicate <HttpExchange > requestMatcher ) {
538- this .status = status ;
539- this .responseBody = responseBody ;
540- this .requestMatcher = requestMatcher ;
541- }
542-
543- @ Override
544- public boolean matchesRequest (HttpExchange exchange ) {
545- return requestMatcher .test (exchange );
546- }
547-
548- @ Override
549- public void writeResponse (HttpExchange exchange , HttpHandler delegateHandler ) throws IOException {
550- if (responseBody != null ) {
551- byte [] responseBytes = responseBody .getBytes (StandardCharsets .UTF_8 );
552- exchange .sendResponseHeaders (status .getStatus (), responseBytes .length );
553- exchange .getResponseBody ().write (responseBytes );
554- } else {
555- exchange .sendResponseHeaders (status .getStatus (), -1 );
556- }
557- }
558- }
559460}
0 commit comments