From 50b0efdd5091ee72e8d9973a4b8aef98980fd448 Mon Sep 17 00:00:00 2001 From: "igor.petrenko" Date: Sun, 8 Feb 2026 13:25:28 +0200 Subject: [PATCH 01/39] CE-135 okhttp -> jetty http client --- .../oap/application/remote/RemoteTest.java | 2 + .../remote/RemoteInvocationHandler.java | 95 ++--- .../main/java/oap/http/test/HttpAsserts.java | 354 ++++++------------ oap-http/oap-http/pom.xml | 22 +- .../java/oap/http/InputStreamRequestBody.java | 35 -- .../oap/message/client/MessageSender.java | 75 ++-- pom.xml | 14 +- 7 files changed, 232 insertions(+), 365 deletions(-) delete mode 100644 oap-http/oap-http/src/main/java/oap/http/InputStreamRequestBody.java diff --git a/oap-application/oap-application-test/src/test/java/oap/application/remote/RemoteTest.java b/oap-application/oap-application-test/src/test/java/oap/application/remote/RemoteTest.java index 1682510bd..bda37bacc 100644 --- a/oap-application/oap-application-test/src/test/java/oap/application/remote/RemoteTest.java +++ b/oap-application/oap-application-test/src/test/java/oap/application/remote/RemoteTest.java @@ -31,6 +31,7 @@ import oap.testng.Fixtures; import oap.testng.Ports; import oap.util.Dates; +import org.assertj.core.api.Assertions; import org.testng.annotations.Test; import java.net.URL; @@ -49,6 +50,7 @@ public class RemoteTest extends Fixtures { @Test public void invoke() { + Assertions.setMaxStackTraceElementsDisplayed( 1024 ); int port = Ports.getFreePort( getClass() ); List modules = Module.CONFIGURATION.urlsFromClassPath(); diff --git a/oap-application/oap-application/src/main/java/oap/application/remote/RemoteInvocationHandler.java b/oap-application/oap-application/src/main/java/oap/application/remote/RemoteInvocationHandler.java index 4b357d3f9..be8801442 100644 --- a/oap-application/oap-application/src/main/java/oap/application/remote/RemoteInvocationHandler.java +++ b/oap-application/oap-application/src/main/java/oap/application/remote/RemoteInvocationHandler.java @@ -24,7 +24,6 @@ package oap.application.remote; import com.google.common.base.Preconditions; -import com.google.common.util.concurrent.SimpleTimeLimiter; import io.micrometer.core.instrument.Counter; import io.micrometer.core.instrument.Metrics; import io.micrometer.core.instrument.Tags; @@ -35,20 +34,18 @@ import oap.util.Result; import oap.util.Stream; import oap.util.function.Try; -import okhttp3.Call; -import okhttp3.Callback; -import okhttp3.Dispatcher; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.RequestBody; -import okhttp3.Response; -import org.jetbrains.annotations.NotNull; - +import org.eclipse.jetty.client.BytesRequestContent; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.InputStreamResponseListener; +import org.eclipse.jetty.client.Request; +import org.eclipse.jetty.client.Response; +import org.eclipse.jetty.http.HttpMethod; + +import javax.annotation.Nonnull; import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; -import java.io.IOException; import java.io.InputStream; import java.lang.invoke.MethodHandles; import java.lang.reflect.InvocationHandler; @@ -57,31 +54,38 @@ import java.lang.reflect.Proxy; import java.net.URI; import java.net.http.HttpTimeoutException; -import java.time.Duration; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; +import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import static java.net.HttpURLConnection.HTTP_OK; @Slf4j public final class RemoteInvocationHandler implements InvocationHandler { - public static final ExecutorService NEW_SINGLE_THREAD_EXECUTOR = Executors.newSingleThreadExecutor(); - private static final OkHttpClient globalClient; - private static final SimpleTimeLimiter SIMPLE_TIME_LIMITER = SimpleTimeLimiter.create( NEW_SINGLE_THREAD_EXECUTOR ); + private static final HttpClient globalClient; + + private static ExecutorService executor; static { - OkHttpClient.Builder builder = new OkHttpClient.Builder(); - Dispatcher dispatcher = new Dispatcher(); - dispatcher.setMaxRequests( 1024 ); - dispatcher.setMaxRequestsPerHost( 1024 ); - builder.dispatcher( dispatcher ); - globalClient = builder.build(); + try { + ThreadFactory threadFactory = Thread.ofVirtual().name( "RemoteInvocationHandler-", 0 ).factory(); + + globalClient = new HttpClient(); + executor = Executors.newThreadPerTaskExecutor( threadFactory ); + globalClient.setExecutor( executor ); + globalClient.start(); + } catch( Exception e ) { + throw new RuntimeException( e ); + } } private final Counter timeoutMetrics; @@ -120,7 +124,7 @@ private static Object proxy( String source, URI uri, String service, Class cl new RemoteInvocationHandler( source, uri, service, timeout ) ); } - @NotNull + @Nonnull private static CompletionStage> retException( Throwable e, boolean async ) { if( async ) { return CompletableFuture.failedStage( e ); @@ -167,39 +171,40 @@ private Result invoke( Method method, Object[] args ) { boolean async = CompletableFuture.class.isAssignableFrom( method.getReturnType() ); try { - OkHttpClient client = globalClient.newBuilder().callTimeout( Duration.ofMillis( timeout ) ).build(); - Request request = new Request.Builder() - .url( uri.toURL() ) - .post( RequestBody.create( invocationB ) ) - .build(); - Call call = client.newCall( request ); + Request request = globalClient + .newRequest( uri ) + .method( HttpMethod.POST ) + .body( new BytesRequestContent( invocationB ) ) + .timeout( timeout, TimeUnit.MILLISECONDS ); + + CompletableFuture responseAsync; - CompletableFuture responseAsync = new CompletableFuture<>(); + InputStreamResponseListener inputStreamResponseListener = new InputStreamResponseListener(); + request.send( inputStreamResponseListener ); if( async ) { - call.enqueue( new Callback() { - @Override - public void onFailure( @NotNull Call call, @NotNull IOException e ) { - responseAsync.completeExceptionally( e ); - } - @Override - public void onResponse( @NotNull Call call, @NotNull Response response ) { - responseAsync.complete( response ); + responseAsync = CompletableFuture.supplyAsync( () -> { + try { + return inputStreamResponseListener.get( timeout + 10, TimeUnit.MILLISECONDS ); + } catch( InterruptedException | TimeoutException | ExecutionException e ) { + throw new RuntimeException( e ); } - } ); + }, executor ); + } else { + responseAsync = new CompletableFuture<>(); try { - responseAsync.complete( call.execute() ); - } catch( IOException e ) { + responseAsync.complete( inputStreamResponseListener.get( timeout + 10, TimeUnit.MILLISECONDS ) ); + } catch( Exception e ) { responseAsync.completeExceptionally( e ); } } CompletableFuture> ret = responseAsync.thenCompose( response -> { try { - if( response.code() == HTTP_OK ) { - InputStream inputStream = response.body().byteStream(); + if( response.getStatus() == HTTP_OK ) { + InputStream inputStream = inputStreamResponseListener.getInputStream(); BufferedInputStream bis = new BufferedInputStream( inputStream ); DataInputStream dis = new DataInputStream( bis ); boolean success = dis.readBoolean(); @@ -244,9 +249,9 @@ public void onResponse( @NotNull Call call, @NotNull Response response ) { } } else { RemoteInvocationException ex = new RemoteInvocationException( "invocation failed " + this + "#" + service + "@" + method.getName() - + " code " + response.code() - + " body '" + response.body().string() + "'" - + " message '" + response.message() + "'" ); + + " code " + response.getStatus() + + " body '" + new String( inputStreamResponseListener.getInputStream().readAllBytes(), StandardCharsets.UTF_8 ) + "'" + + " message '" + response.getReason() + "'" ); return retException( ex, async ); } diff --git a/oap-http/oap-http-test/src/main/java/oap/http/test/HttpAsserts.java b/oap-http/oap-http-test/src/main/java/oap/http/test/HttpAsserts.java index 3ba39e0ff..ab19f572c 100644 --- a/oap-http/oap-http-test/src/main/java/oap/http/test/HttpAsserts.java +++ b/oap-http/oap-http-test/src/main/java/oap/http/test/HttpAsserts.java @@ -23,15 +23,12 @@ */ package oap.http.test; -import com.google.common.base.Preconditions; import lombok.EqualsAndHashCode; import lombok.SneakyThrows; import lombok.ToString; import lombok.extern.slf4j.Slf4j; import oap.http.Client; import oap.http.Cookie; -import oap.http.InputStreamRequestBody; -import oap.http.Uri; import oap.json.JsonException; import oap.json.testng.JsonAsserts; import oap.testng.Asserts; @@ -40,38 +37,33 @@ import oap.util.Maps; import oap.util.Pair; import oap.util.Stream; -import okhttp3.Dispatcher; -import okhttp3.Headers; -import okhttp3.HttpUrl; -import okhttp3.MediaType; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.RequestBody; -import okhttp3.Response; -import okhttp3.ResponseBody; -import okhttp3.java.net.cookiejar.JavaNetCookieJar; -import org.apache.commons.lang3.StringUtils; import org.assertj.core.api.AbstractIntegerAssert; import org.assertj.core.api.Assertions; +import org.eclipse.jetty.client.BytesRequestContent; +import org.eclipse.jetty.client.ContentResponse; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.InputStreamRequestContent; +import org.eclipse.jetty.client.StringRequestContent; +import org.eclipse.jetty.http.HttpFields; +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.http.HttpMethod; import org.joda.time.DateTime; import org.testng.internal.collections.Ints; import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.io.ByteArrayInputStream; -import java.io.IOException; import java.io.InputStream; import java.io.UncheckedIOException; -import java.lang.reflect.Field; import java.net.CookieManager; import java.net.CookiePolicy; import java.net.HttpCookie; import java.util.ArrayList; -import java.util.Date; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; import java.util.function.Consumer; import java.util.regex.Pattern; @@ -83,34 +75,28 @@ import static oap.io.content.ContentReader.ofString; import static oap.testng.Asserts.assertString; import static oap.testng.Asserts.contentOfTestResource; +import static oap.util.Pair.__; import static org.assertj.core.api.Assertions.assertThat; @Slf4j @SuppressWarnings( "unused" ) public class HttpAsserts { - public static final OkHttpClient OK_HTTP_CLIENT; + public static final HttpClient HTTP_CLIENT; - private static final JavaNetCookieJar cookieJar; private static final CookieManager cookieManager; - private static Field whenCreatedField; - static { cookieManager = new CookieManager(); cookieManager.setCookiePolicy( CookiePolicy.ACCEPT_ALL ); - cookieJar = new JavaNetCookieJar( cookieManager ); - OK_HTTP_CLIENT = new OkHttpClient.Builder() - .cookieJar( cookieJar ) - .dispatcher( new Dispatcher( Executors.newVirtualThreadPerTaskExecutor() ) ) - .followRedirects( false ) - .followSslRedirects( false ) - .build(); - try { - whenCreatedField = HttpCookie.class.getDeclaredField( "whenCreated" ); - whenCreatedField.setAccessible( true ); - } catch( NoSuchFieldException e ) { + ThreadFactory threadFactory = Thread.ofVirtual().name( "HttpAsserts-", 0 ).factory(); + + HTTP_CLIENT = new HttpClient(); + HTTP_CLIENT.setExecutor( Executors.newThreadPerTaskExecutor( threadFactory ) ); + HTTP_CLIENT.setFollowRedirects( false ); + HTTP_CLIENT.start(); + } catch( Exception e ) { throw new RuntimeException( e ); } } @@ -123,8 +109,11 @@ public static String httpUrl( int port, String suffix ) { return httpPrefix( port ) + ( suffix.startsWith( "/" ) ? suffix : "/" + suffix ); } + @SneakyThrows public static void reset() { - OK_HTTP_CLIENT.connectionPool().evictAll(); + HTTP_CLIENT.stop(); + HTTP_CLIENT.start(); + cookieManager.getCookieStore().removeAll(); } @@ -134,65 +123,22 @@ public static HttpAssertion assertGet( String uri, Pair... param } public static HttpAssertion assertGet( String uri, Map params, Map headers ) throws UncheckedIOException { - try { - Request.Builder builder = new Request.Builder(); - - setHeaders( uri, headers, builder ); - - Request request = builder - .url( Uri.uri( uri, params ).toURL() ) - .get() - .build(); - - return getResponseAsHttpAssertion( request ); - } catch( IOException e ) { - throw new UncheckedIOException( e ); - } - } - - private static void setHeaders( String uri, Map headers, Request.Builder builder ) { - headers.forEach( ( k, v ) -> { - if( "Cookie".equalsIgnoreCase( k ) ) { - HttpUrl httpUrl = HttpUrl.parse( uri ); - Preconditions.checkNotNull( httpUrl ); - Preconditions.checkNotNull( v ); - - ArrayList cookies = new ArrayList<>(); - - List list = cookieJar.loadForRequest( httpUrl ); - cookies.addAll( list ); - - String[] setCookies = StringUtils.split( v.toString(), ";" ); - for( String setCookie : setCookies ) { - okhttp3.Cookie cookie = okhttp3.Cookie.parse( httpUrl, setCookie ); - Preconditions.checkNotNull( cookie ); - cookies.add( cookie ); - } - - cookieJar.saveFromResponse( httpUrl, cookies ); - } else { - builder.header( k, v == null ? "" : v.toString() ); - } - } ); + return getResponseAsHttpAssertion( HTTP_CLIENT + .newRequest( uri ) + .method( HttpMethod.GET ) + .headers( h -> { + headers.forEach( ( k, v ) -> h.add( k, v == null ? "" : v.toString() ) ); + } ) ); } public static HttpAssertion assertPost( String uri, InputStream content, @Nullable String contentType, Map headers ) { - try { - Request.Builder builder = new Request.Builder(); - - setHeaders( uri, headers, builder ); - - RequestBody requestBody = new InputStreamRequestBody( contentType != null ? MediaType.get( contentType ) : null, content ); - - Request request = builder - .url( uri ) - .post( requestBody ) - .build(); - - return getResponseAsHttpAssertion( request ); - } catch( IOException e ) { - throw new UncheckedIOException( e ); - } + return getResponseAsHttpAssertion( HTTP_CLIENT + .newRequest( uri ) + .method( HttpMethod.POST ) + .headers( h -> { + headers.forEach( ( k, v ) -> h.add( k, v == null ? "" : v.toString() ) ); + } ) + .body( new InputStreamRequestContent( contentType, content, null ) ) ); } public static HttpAssertion assertPost( String uri, InputStream content, @Nullable String contentType ) { @@ -200,22 +146,13 @@ public static HttpAssertion assertPost( String uri, InputStream content, @Nullab } public static HttpAssertion assertPost( String uri, String content, @Nullable String contentType, Map headers ) { - try { - Request.Builder builder = new Request.Builder(); - - setHeaders( uri, headers, builder ); - - RequestBody requestBody = RequestBody.create( content, contentType != null ? MediaType.parse( contentType ) : null ); - - Request request = builder - .url( uri ) - .post( requestBody ) - .build(); - - return getResponseAsHttpAssertion( request ); - } catch( IOException e ) { - throw new UncheckedIOException( e ); - } + return getResponseAsHttpAssertion( HTTP_CLIENT + .newRequest( uri ) + .method( HttpMethod.POST ) + .headers( h -> { + headers.forEach( ( k, v ) -> h.add( k, v == null ? "" : v.toString() ) ); + } ) + .body( new StringRequestContent( contentType, content ) ) ); } public static HttpAssertion assertPost( String uri, String content ) { @@ -230,19 +167,22 @@ public static HttpAssertion assertPost( String uri, String content, String conte return assertPost( uri, content, contentType, Maps.of() ); } - private static @Nonnull HttpAssertion getResponseAsHttpAssertion( Request request ) throws IOException { - try( Response response = OK_HTTP_CLIENT.newCall( request ).execute(); - ResponseBody body = response.body() ) { + @SneakyThrows + private static @Nonnull HttpAssertion getResponseAsHttpAssertion( org.eclipse.jetty.client.Request request ) { + ContentResponse contentResponse = request.send(); - Headers responseHeaders = response.headers(); - ArrayList> headers = new ArrayList<>(); - responseHeaders.toMultimap().forEach( ( k, vs ) -> vs.forEach( v -> headers.add( Pair.__( k, v ) ) ) ); - byte[] bytes = body.bytes(); - MediaType mediaType = body.contentType(); - return new HttpAssertion( new Client.Response( - response.request().url().toString(), - response.code(), response.message(), headers, mediaType != null ? mediaType.toString() : APPLICATION_OCTET_STREAM, new ByteArrayInputStream( bytes ) ) ); - } + String mediaType = contentResponse.getMediaType(); + + ArrayList> headers = new ArrayList<>(); + HttpFields responseHeaders = contentResponse.getHeaders(); + responseHeaders.forEach( field -> { + HttpHeader header = field.getHeader(); + headers.add( __( header.name(), header.asString() ) ); + } ); + + return new HttpAssertion( new Client.Response( + request.getURI().toString(), + contentResponse.getStatus(), contentResponse.getReason(), headers, mediaType != null ? mediaType : APPLICATION_OCTET_STREAM, new ByteArrayInputStream( contentResponse.getContent() ) ) ); } public static HttpAssertion assertPut( String uri, String content, String contentType ) { @@ -250,22 +190,14 @@ public static HttpAssertion assertPut( String uri, String content, String conten } public static HttpAssertion assertPut( String uri, String content, String contentType, Map headers ) { - try { - Request.Builder builder = new Request.Builder(); - - setHeaders( uri, headers, builder ); - - RequestBody requestBody = RequestBody.create( content, contentType != null ? MediaType.parse( contentType ) : null ); - - Request request = builder - .url( uri ) - .put( requestBody ) - .build(); - - return getResponseAsHttpAssertion( request ); - } catch( IOException e ) { - throw new UncheckedIOException( e ); - } + return getResponseAsHttpAssertion( HTTP_CLIENT + .newRequest( uri ) + .method( HttpMethod.PUT ) + .headers( h -> { + headers.forEach( ( k, v ) -> h.add( k, v == null ? "" : v.toString() ) ); + } ) + .body( new StringRequestContent( contentType, content ) ) + ); } public static HttpAssertion assertPut( String uri, byte[] content, String contentType ) { @@ -273,22 +205,14 @@ public static HttpAssertion assertPut( String uri, byte[] content, String conten } public static HttpAssertion assertPut( String uri, byte[] content, String contentType, Map headers ) { - try { - Request.Builder builder = new Request.Builder(); - - setHeaders( uri, headers, builder ); - - RequestBody requestBody = RequestBody.create( content, contentType != null ? MediaType.parse( contentType ) : null ); - - Request request = builder - .url( uri ) - .put( requestBody ) - .build(); - - return getResponseAsHttpAssertion( request ); - } catch( IOException e ) { - throw new UncheckedIOException( e ); - } + return getResponseAsHttpAssertion( HTTP_CLIENT + .newRequest( uri ) + .method( HttpMethod.PUT ) + .headers( h -> { + headers.forEach( ( k, v ) -> h.add( k, v == null ? "" : v.toString() ) ); + } ) + .body( new BytesRequestContent( contentType, content ) ) + ); } public static HttpAssertion assertPut( String uri, InputStream is, String contentType ) { @@ -296,22 +220,14 @@ public static HttpAssertion assertPut( String uri, InputStream is, String conten } public static HttpAssertion assertPut( String uri, InputStream is, String contentType, Map headers ) { - try { - Request.Builder builder = new Request.Builder(); - - setHeaders( uri, headers, builder ); - - InputStreamRequestBody requestBody = new InputStreamRequestBody( contentType != null ? MediaType.parse( contentType ) : null, is ); - - Request request = builder - .url( uri ) - .put( requestBody ) - .build(); - - return getResponseAsHttpAssertion( request ); - } catch( IOException e ) { - throw new UncheckedIOException( e ); - } + return getResponseAsHttpAssertion( HTTP_CLIENT + .newRequest( uri ) + .method( HttpMethod.PUT ) + .headers( h -> { + headers.forEach( ( k, v ) -> h.add( k, v == null ? "" : v.toString() ) ); + } ) + .body( new InputStreamRequestContent( contentType, is, null ) ) + ); } public static HttpAssertion assertPatch( String uri, byte[] content, String contentType ) { @@ -319,22 +235,14 @@ public static HttpAssertion assertPatch( String uri, byte[] content, String cont } public static HttpAssertion assertPatch( String uri, byte[] content, String contentType, Map headers ) { - try { - Request.Builder builder = new Request.Builder(); - - setHeaders( uri, headers, builder ); - - RequestBody requestBody = RequestBody.create( content, contentType != null ? MediaType.parse( contentType ) : null ); - - Request request = builder - .url( uri ) - .patch( requestBody ) - .build(); - - return getResponseAsHttpAssertion( request ); - } catch( IOException e ) { - throw new UncheckedIOException( e ); - } + return getResponseAsHttpAssertion( HTTP_CLIENT + .newRequest( uri ) + .method( HttpMethod.PATCH ) + .headers( h -> { + headers.forEach( ( k, v ) -> h.add( k, v == null ? "" : v.toString() ) ); + } ) + .body( new BytesRequestContent( contentType, content ) ) + ); } public static HttpAssertion assertPatch( String uri, String content, String contentType ) { @@ -342,22 +250,14 @@ public static HttpAssertion assertPatch( String uri, String content, String cont } public static HttpAssertion assertPatch( String uri, String content, String contentType, Map headers ) { - try { - Request.Builder builder = new Request.Builder(); - - setHeaders( uri, headers, builder ); - - RequestBody requestBody = RequestBody.create( content, contentType != null ? MediaType.parse( contentType ) : null ); - - Request request = builder - .url( uri ) - .patch( requestBody ) - .build(); - - return getResponseAsHttpAssertion( request ); - } catch( IOException e ) { - throw new UncheckedIOException( e ); - } + return getResponseAsHttpAssertion( HTTP_CLIENT + .newRequest( uri ) + .method( HttpMethod.PATCH ) + .headers( h -> { + headers.forEach( ( k, v ) -> h.add( k, v == null ? "" : v.toString() ) ); + } ) + .body( new StringRequestContent( contentType, content ) ) + ); } public static HttpAssertion assertPatch( String uri, InputStream is, String contentType ) { @@ -366,50 +266,30 @@ public static HttpAssertion assertPatch( String uri, InputStream is, String cont public static HttpAssertion assertPatch( String uri, InputStream is, String contentType, Map headers ) { - try { - Request.Builder builder = new Request.Builder(); - - setHeaders( uri, headers, builder ); - - InputStreamRequestBody requestBody = new InputStreamRequestBody( contentType != null ? MediaType.parse( contentType ) : null, is ); - - Request request = builder - .url( uri ) - .patch( requestBody ) - .build(); - - return getResponseAsHttpAssertion( request ); - } catch( IOException e ) { - throw new UncheckedIOException( e ); - } + return getResponseAsHttpAssertion( HTTP_CLIENT + .newRequest( uri ) + .method( HttpMethod.PATCH ) + .headers( h -> { + headers.forEach( ( k, v ) -> h.add( k, v == null ? "" : v.toString() ) ); + } ) + .body( new InputStreamRequestContent( contentType, is, null ) ) + ); } public static HttpAssertion assertDelete( String uri, Map headers ) { - try { - Request.Builder builder = new Request.Builder(); - - setHeaders( uri, headers, builder ); - - Request request = builder - .url( uri ) - .delete() - .build(); - - return getResponseAsHttpAssertion( request ); - } catch( IOException e ) { - throw new UncheckedIOException( e ); - } + return getResponseAsHttpAssertion( HTTP_CLIENT + .newRequest( uri ) + .method( HttpMethod.DELETE ) + .headers( h -> { + headers.forEach( ( k, v ) -> h.add( k, v == null ? "" : v.toString() ) ); + } ) + ); } public static HttpAssertion assertDelete( String uri ) { return assertDelete( uri, Map.of() ); } - @SneakyThrows - private static long whenCreatedFieldGet( HttpCookie cookie ) { - return ( long ) whenCreatedField.get( cookie ); - } - @EqualsAndHashCode @ToString public static final class HttpAssertion { @@ -526,11 +406,11 @@ public HttpAssertion containsCookie( String cookie ) { } public HttpAssertion cookies( Consumer cons ) { - HttpUrl httpUrl = HttpUrl.parse( response.url ); - assertThat( httpUrl ).isNotNull(); - List cookies = cookieManager.getCookieStore().get( httpUrl.uri() ); +// HttpUrl httpUrl = HttpUrl.parse( response.url ); +// assertThat( httpUrl ).isNotNull(); +// List cookies = cookieManager.getCookieStore().get( httpUrl.uri() ); - cons.accept( new CookiesHttpAssertion( cookies ) ); +// cons.accept( new CookiesHttpAssertion( cookies ) ); return this; } @@ -609,7 +489,7 @@ public CookiesHttpAssertion( List cookies ) { .withPath( c.getPath() ) .withDomain( c.getDomain() ) .withMaxAge( c.getMaxAge() < 0 ? null : ( int ) c.getMaxAge() ) - .withExpires( c.getMaxAge() <= 0 ? null : new Date( ( whenCreatedFieldGet( c ) + c.getMaxAge() ) * 1000L ) ) +// .withExpires( c.getMaxAge() <= 0 ? null : new Date( ( whenCreatedFieldGet( c ) + c.getMaxAge() ) * 1000L ) ) .withDiscard( c.getDiscard() ) .withSecure( c.getSecure() ) .withHttpOnly( c.isHttpOnly() ) diff --git a/oap-http/oap-http/pom.xml b/oap-http/oap-http/pom.xml index d5eb23fe5..f01abdbc1 100644 --- a/oap-http/oap-http/pom.xml +++ b/oap-http/oap-http/pom.xml @@ -78,14 +78,28 @@ + + + org.eclipse.jetty + jetty-client + ${oap.deps.jetty-client.version} + - com.squareup.okhttp3 - okhttp-jvm + org.eclipse.jetty.compression + jetty-compression-gzip + ${oap.deps.jetty-client.version} - com.squareup.okhttp3 - okhttp-java-net-cookiejar + org.eclipse.jetty.compression + jetty-compression-brotli + ${oap.deps.jetty-client.version} + + org.eclipse.jetty.compression + jetty-compression-zstandard + ${oap.deps.jetty-client.version} + + org.projectlombok lombok diff --git a/oap-http/oap-http/src/main/java/oap/http/InputStreamRequestBody.java b/oap-http/oap-http/src/main/java/oap/http/InputStreamRequestBody.java deleted file mode 100644 index 110f2530b..000000000 --- a/oap-http/oap-http/src/main/java/oap/http/InputStreamRequestBody.java +++ /dev/null @@ -1,35 +0,0 @@ -package oap.http; - -import okhttp3.MediaType; -import okhttp3.RequestBody; -import okio.BufferedSink; -import okio.Okio; -import okio.Source; - -import java.io.IOException; -import java.io.InputStream; - -public class InputStreamRequestBody extends RequestBody { - private final MediaType mediaType; - private final InputStream inputStream; - - public InputStreamRequestBody( MediaType mediaType, InputStream inputStream ) { - this.mediaType = mediaType; - this.inputStream = inputStream; - } - - @Override - public MediaType contentType() { - return mediaType; - } - - // You can override contentLength() if you know the length in advance for better performance/features (e.g., S3 uploads). - // If you don't override it, OkHttp will use chunked transfer encoding for large bodies. - - @Override - public void writeTo( BufferedSink sink ) throws IOException { - try( Source source = Okio.source( inputStream ) ) { - sink.writeAll( source ); - } - } -} diff --git a/oap-message/oap-message-client/src/main/java/oap/message/client/MessageSender.java b/oap-message/oap-message-client/src/main/java/oap/message/client/MessageSender.java index 4c6e647de..fab50c02b 100644 --- a/oap-message/oap-message-client/src/main/java/oap/message/client/MessageSender.java +++ b/oap-message/oap-message-client/src/main/java/oap/message/client/MessageSender.java @@ -33,6 +33,7 @@ import oap.LogConsolidated; import oap.concurrent.scheduler.Scheduled; import oap.concurrent.scheduler.Scheduler; +import oap.http.Http; import oap.io.Closeables; import oap.io.Files; import oap.io.content.ContentReader; @@ -46,16 +47,15 @@ import oap.util.FastByteArrayOutputStream; import oap.util.Pair; import oap.util.Throwables; -import okhttp3.Dispatcher; -import okhttp3.MediaType; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.RequestBody; -import okhttp3.Response; import org.apache.commons.codec.DecoderException; import org.apache.commons.codec.binary.Hex; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.io.FilenameUtils; +import org.eclipse.jetty.client.BytesRequestContent; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.InputStreamResponseListener; +import org.eclipse.jetty.client.Response; +import org.eclipse.jetty.http.HttpMethod; import org.joda.time.DateTimeUtils; import org.slf4j.event.Level; @@ -69,20 +69,20 @@ import java.nio.file.DirectoryStream; import java.nio.file.Path; import java.nio.file.Paths; -import java.time.Duration; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import java.util.concurrent.locks.ReentrantLock; @Slf4j @ToString public class MessageSender implements Closeable, AutoCloseable { - public static final MediaType MEDIA_TYPE = MediaType.parse( "application/octet-stream" ); private static final Pair STATUS_OK = Pair.__( MessageStatus.OK, MessageProtocol.STATUS_OK ); private final Object syncDiskLock = new Object(); private final String host; @@ -94,6 +94,7 @@ public class MessageSender implements Closeable, AutoCloseable { private final ConcurrentMap> lastStatus = new ConcurrentHashMap<>(); private final String messageUrl; private final long memorySyncPeriod; + private final ReentrantLock lock = new ReentrantLock(); public String uniqueName = Cuid.UNIQUE.next(); public long storageLockExpiration = Dates.h( 1 ); public int poolSize = 4; @@ -106,7 +107,7 @@ public class MessageSender implements Closeable, AutoCloseable { private volatile boolean closed = false; private Scheduled diskSyncScheduler; private boolean networkAvailable = true; - private OkHttpClient httpClient; + private HttpClient httpClient; private ExecutorService executor; private long ioExceptionStartRetryTimeout = -1; @@ -145,6 +146,7 @@ public final long getClientId() { return clientId; } + @SneakyThrows public void start() { log.info( "[{}] message server messageUrl {} storage {} storageLockExpiration {}", uniqueName, messageUrl, directory, Dates.durationToString( storageLockExpiration ) ); @@ -157,18 +159,13 @@ public void start() { executor = Executors.newVirtualThreadPerTaskExecutor(); - OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder() - .connectTimeout( Duration.ofMillis( connectionTimeout ) ) - .callTimeout( Duration.ofMillis( timeout ) ); - - if( poolSize > 0 ) { - Dispatcher dispatcher = new Dispatcher(); - dispatcher.setMaxRequests( poolSize ); - dispatcher.setMaxRequestsPerHost( poolSize ); - clientBuilder.dispatcher( dispatcher ); - } + httpClient = new HttpClient(); + httpClient.setConnectTimeout( connectionTimeout ); + httpClient.setMaxConnectionsPerDestination( poolSize ); - httpClient = clientBuilder.build(); + ThreadFactory threadFactory = Thread.ofVirtual().name( "messages-", 0 ).factory(); + httpClient.setExecutor( Executors.newThreadPerTaskExecutor( threadFactory ) ); + httpClient.start(); if( diskSyncPeriod > 0 ) diskSyncScheduler = Scheduler.scheduleWithFixedDelay( diskSyncPeriod, TimeUnit.MILLISECONDS, this::syncDisk ); @@ -203,12 +200,18 @@ public MessageSender send( byte messageType, short version, byte[] data, int off } + @SneakyThrows @Override public void close() { - synchronized( syncDiskLock ) { + httpClient.stop(); + + lock.lock(); + try { closed = true; Closeables.close( diskSyncScheduler ); + } finally { + lock.unlock(); } int count = 0; @@ -283,17 +286,20 @@ public CompletableFuture send( Messages.MessageInfo messag out.writeInt( message.data.length ); out.write( message.data ); - Request request = new Request.Builder() - .url( messageUrl ) - .post( RequestBody.create( buf.array, MEDIA_TYPE, 0, buf.length ) ) - .build(); + InputStreamResponseListener inputStreamResponseListener = new InputStreamResponseListener(); + httpClient + .newRequest( messageUrl ) + .method( HttpMethod.POST ) + .timeout( timeout, TimeUnit.MILLISECONDS ) + .body( new BytesRequestContent( Http.ContentType.APPLICATION_OCTET_STREAM, buf.array ) ) + .send( inputStreamResponseListener ); - Response response = httpClient.newCall( request ).execute(); + Response response = inputStreamResponseListener.get( timeout, TimeUnit.MILLISECONDS ); - if( response.code() >= 300 || response.code() < 200 ) { - throw new IOException( "Not OK (" + response.code() + ") response code returned for url: " + messageUrl ); + if( response.getStatus() >= 300 || response.getStatus() < 200 ) { + throw new IOException( "Not OK (" + response.getStatus() + ") response code returned for url: " + messageUrl ); } - return onOkRespone( messageInfo, response, now ); + return onOkRespone( messageInfo, inputStreamResponseListener, now ); } catch( UnknownHostException e ) { processException( messageInfo, now, message, e, true ); @@ -317,10 +323,10 @@ private void processException( Messages.MessageInfo messageInfo, long now, Messa messages.retry( messageInfo, now + retryTimeout ); } - private Messages.MessageInfo onOkRespone( Messages.MessageInfo messageInfo, Response response, long now ) { + private Messages.MessageInfo onOkRespone( Messages.MessageInfo messageInfo, InputStreamResponseListener inputStreamResponseListener, long now ) { Message message = messageInfo.message; - InputStream body = response.body().byteStream(); + InputStream body = inputStreamResponseListener.getInputStream(); try( DataInputStream in = new DataInputStream( body ) ) { byte version = in.readByte(); @@ -407,7 +413,7 @@ public void syncMemory( long timeoutMs ) { if( messageInfo != null ) { log.trace( "[{}] message {}...", uniqueName, messageInfo.message.md5 ); CompletableFuture future = send( messageInfo, now ); - future.handle( ( mi, e ) -> { + future.handle( ( mi, _ ) -> { messages.removeInProgress( mi ); log.trace( "[{}] message {}... done", uniqueName, mi.message.md5 ); return null; @@ -500,7 +506,8 @@ public MessageSender syncDisk() { Path lockFile; - synchronized( syncDiskLock ) { + lock.lock(); + try { if( closed ) return this; if( ( lockFile = lock( uniqueName, messagePath, storageLockExpiration ) ) != null ) { @@ -521,6 +528,8 @@ public MessageSender syncDisk() { Files.delete( lockFile ); } } + } finally { + lock.unlock(); } } } diff --git a/pom.xml b/pom.xml index e27d0008e..16641e502 100644 --- a/pom.xml +++ b/pom.xml @@ -23,14 +23,6 @@ pom import - - - com.squareup.okhttp3 - okhttp-bom - ${oap.deps.okhttp.version} - pom - import - @@ -65,7 +57,7 @@ - 25.3.3 + 25.4.0 25.0.1 25.0.0 @@ -74,8 +66,6 @@ 5.18.0 1.17.6 - 5.3.2 - 4.4.16 4.5.14 4.1.5 @@ -122,6 +112,8 @@ 2.3 4.9.3 + 12.1.6 + 4.9.8 From b0fdd5dceba1d29a81c6a58770ac41e35d6edb79 Mon Sep 17 00:00:00 2001 From: "igor.petrenko" Date: Sun, 8 Feb 2026 14:06:45 +0200 Subject: [PATCH 02/39] CE-135 okhttp -> jetty http client --- .../prometheus/PrometheusExporterTest.java | 1 - .../main/java/oap/http/test/HttpAsserts.java | 73 +- .../src/test/java/oap/http/ClientTest.java | 2 +- .../java/oap/http/file/HttpFileSyncTest.java | 1 - .../http/server/nio/NioHttpServerTest.java | 6 +- .../KeepaliveRequestsHandlerTest.java | 1 - .../src/main/java/oap/http/Client.java | 872 ------------------ .../src/main/java/oap/http/Response.java | 161 ++++ .../http/client/JettyRequestExtensions.java | 19 + .../main/java/oap/http/file/HttpFileSync.java | 48 - .../java/oap/http/pniov3/PnioServerTest.java | 4 +- .../java/oap/io/FileSystemFileSyncTest.java | 77 -- .../main/java/oap/io/AbstractFileSync.java | 127 --- .../main/java/oap/io/FileSystemFileSync.java | 63 -- .../META-INF/services/oap.io.AbstractFileSync | 2 - .../validate/testng/ValidationAssertion.java | 6 +- .../java/oap/ws/WebServicesSessionTest.java | 7 +- .../src/test/java/oap/ws/WebServicesTest.java | 15 +- 18 files changed, 221 insertions(+), 1264 deletions(-) delete mode 100644 oap-http/oap-http/src/main/java/oap/http/Client.java create mode 100644 oap-http/oap-http/src/main/java/oap/http/Response.java create mode 100644 oap-http/oap-http/src/main/java/oap/http/client/JettyRequestExtensions.java delete mode 100644 oap-http/oap-http/src/main/java/oap/http/file/HttpFileSync.java delete mode 100644 oap-stdlib-test/src/test/java/oap/io/FileSystemFileSyncTest.java delete mode 100644 oap-stdlib/src/main/java/oap/io/AbstractFileSync.java delete mode 100644 oap-stdlib/src/main/java/oap/io/FileSystemFileSync.java delete mode 100644 oap-stdlib/src/main/resources/META-INF/services/oap.io.AbstractFileSync diff --git a/oap-http/oap-http-prometheus/src/test/java/oap/http/prometheus/PrometheusExporterTest.java b/oap-http/oap-http-prometheus/src/test/java/oap/http/prometheus/PrometheusExporterTest.java index f79dad4c1..816af3e8e 100644 --- a/oap-http/oap-http-prometheus/src/test/java/oap/http/prometheus/PrometheusExporterTest.java +++ b/oap-http/oap-http-prometheus/src/test/java/oap/http/prometheus/PrometheusExporterTest.java @@ -27,7 +27,6 @@ import io.micrometer.core.instrument.Meter; import io.micrometer.core.instrument.Metrics; import io.micrometer.core.instrument.search.Search; -import oap.http.Client; import oap.http.server.nio.NioHttpServer; import oap.testng.Fixtures; import oap.testng.Ports; diff --git a/oap-http/oap-http-test/src/main/java/oap/http/test/HttpAsserts.java b/oap-http/oap-http-test/src/main/java/oap/http/test/HttpAsserts.java index ab19f572c..da42b823e 100644 --- a/oap-http/oap-http-test/src/main/java/oap/http/test/HttpAsserts.java +++ b/oap-http/oap-http-test/src/main/java/oap/http/test/HttpAsserts.java @@ -26,9 +26,11 @@ import lombok.EqualsAndHashCode; import lombok.SneakyThrows; import lombok.ToString; +import lombok.experimental.ExtensionMethod; import lombok.extern.slf4j.Slf4j; -import oap.http.Client; import oap.http.Cookie; +import oap.http.Response; +import oap.http.client.JettyRequestExtensions; import oap.json.JsonException; import oap.json.testng.JsonAsserts; import oap.testng.Asserts; @@ -55,8 +57,6 @@ import java.io.ByteArrayInputStream; import java.io.InputStream; import java.io.UncheckedIOException; -import java.net.CookieManager; -import java.net.CookiePolicy; import java.net.HttpCookie; import java.util.ArrayList; import java.util.List; @@ -78,17 +78,13 @@ import static oap.util.Pair.__; import static org.assertj.core.api.Assertions.assertThat; +@ExtensionMethod( JettyRequestExtensions.class ) @Slf4j @SuppressWarnings( "unused" ) public class HttpAsserts { public static final HttpClient HTTP_CLIENT; - private static final CookieManager cookieManager; - static { - cookieManager = new CookieManager(); - cookieManager.setCookiePolicy( CookiePolicy.ACCEPT_ALL ); - try { ThreadFactory threadFactory = Thread.ofVirtual().name( "HttpAsserts-", 0 ).factory(); @@ -113,8 +109,6 @@ public static String httpUrl( int port, String suffix ) { public static void reset() { HTTP_CLIENT.stop(); HTTP_CLIENT.start(); - - cookieManager.getCookieStore().removeAll(); } @SafeVarargs @@ -126,18 +120,15 @@ public static HttpAssertion assertGet( String uri, Map params, M return getResponseAsHttpAssertion( HTTP_CLIENT .newRequest( uri ) .method( HttpMethod.GET ) - .headers( h -> { - headers.forEach( ( k, v ) -> h.add( k, v == null ? "" : v.toString() ) ); - } ) ); + .addParams( params ) + .addHeaders( headers ) ); } public static HttpAssertion assertPost( String uri, InputStream content, @Nullable String contentType, Map headers ) { return getResponseAsHttpAssertion( HTTP_CLIENT .newRequest( uri ) .method( HttpMethod.POST ) - .headers( h -> { - headers.forEach( ( k, v ) -> h.add( k, v == null ? "" : v.toString() ) ); - } ) + .addHeaders( headers ) .body( new InputStreamRequestContent( contentType, content, null ) ) ); } @@ -149,9 +140,7 @@ public static HttpAssertion assertPost( String uri, String content, @Nullable St return getResponseAsHttpAssertion( HTTP_CLIENT .newRequest( uri ) .method( HttpMethod.POST ) - .headers( h -> { - headers.forEach( ( k, v ) -> h.add( k, v == null ? "" : v.toString() ) ); - } ) + .addHeaders( headers ) .body( new StringRequestContent( contentType, content ) ) ); } @@ -180,7 +169,7 @@ public static HttpAssertion assertPost( String uri, String content, String conte headers.add( __( header.name(), header.asString() ) ); } ); - return new HttpAssertion( new Client.Response( + return new HttpAssertion( new Response( request.getURI().toString(), contentResponse.getStatus(), contentResponse.getReason(), headers, mediaType != null ? mediaType : APPLICATION_OCTET_STREAM, new ByteArrayInputStream( contentResponse.getContent() ) ) ); } @@ -193,9 +182,7 @@ public static HttpAssertion assertPut( String uri, String content, String conten return getResponseAsHttpAssertion( HTTP_CLIENT .newRequest( uri ) .method( HttpMethod.PUT ) - .headers( h -> { - headers.forEach( ( k, v ) -> h.add( k, v == null ? "" : v.toString() ) ); - } ) + .addHeaders( headers ) .body( new StringRequestContent( contentType, content ) ) ); } @@ -208,9 +195,7 @@ public static HttpAssertion assertPut( String uri, byte[] content, String conten return getResponseAsHttpAssertion( HTTP_CLIENT .newRequest( uri ) .method( HttpMethod.PUT ) - .headers( h -> { - headers.forEach( ( k, v ) -> h.add( k, v == null ? "" : v.toString() ) ); - } ) + .addHeaders( headers ) .body( new BytesRequestContent( contentType, content ) ) ); } @@ -223,9 +208,7 @@ public static HttpAssertion assertPut( String uri, InputStream is, String conten return getResponseAsHttpAssertion( HTTP_CLIENT .newRequest( uri ) .method( HttpMethod.PUT ) - .headers( h -> { - headers.forEach( ( k, v ) -> h.add( k, v == null ? "" : v.toString() ) ); - } ) + .addHeaders( headers ) .body( new InputStreamRequestContent( contentType, is, null ) ) ); } @@ -238,9 +221,7 @@ public static HttpAssertion assertPatch( String uri, byte[] content, String cont return getResponseAsHttpAssertion( HTTP_CLIENT .newRequest( uri ) .method( HttpMethod.PATCH ) - .headers( h -> { - headers.forEach( ( k, v ) -> h.add( k, v == null ? "" : v.toString() ) ); - } ) + .addHeaders( headers ) .body( new BytesRequestContent( contentType, content ) ) ); } @@ -253,9 +234,7 @@ public static HttpAssertion assertPatch( String uri, String content, String cont return getResponseAsHttpAssertion( HTTP_CLIENT .newRequest( uri ) .method( HttpMethod.PATCH ) - .headers( h -> { - headers.forEach( ( k, v ) -> h.add( k, v == null ? "" : v.toString() ) ); - } ) + .addHeaders( headers ) .body( new StringRequestContent( contentType, content ) ) ); } @@ -269,9 +248,7 @@ public static HttpAssertion assertPatch( String uri, InputStream is, String cont return getResponseAsHttpAssertion( HTTP_CLIENT .newRequest( uri ) .method( HttpMethod.PATCH ) - .headers( h -> { - headers.forEach( ( k, v ) -> h.add( k, v == null ? "" : v.toString() ) ); - } ) + .addHeaders( headers ) .body( new InputStreamRequestContent( contentType, is, null ) ) ); } @@ -280,9 +257,7 @@ public static HttpAssertion assertDelete( String uri, Map header return getResponseAsHttpAssertion( HTTP_CLIENT .newRequest( uri ) .method( HttpMethod.DELETE ) - .headers( h -> { - headers.forEach( ( k, v ) -> h.add( k, v == null ? "" : v.toString() ) ); - } ) + .addHeaders( headers ) ); } @@ -293,13 +268,13 @@ public static HttpAssertion assertDelete( String uri ) { @EqualsAndHashCode @ToString public static final class HttpAssertion { - private final Client.Response response; + private final Response response; - private HttpAssertion( Client.Response response ) { + private HttpAssertion( Response response ) { this.response = response; } - public static HttpAssertion assertHttpResponse( Client.Response response ) { + public static HttpAssertion assertHttpResponse( Response response ) { return new HttpAssertion( response ); } @@ -421,7 +396,7 @@ private List getCookies() { .toList(); } - public HttpAssertion is( Consumer condition ) { + public HttpAssertion is( Consumer condition ) { condition.accept( response ); return this; } @@ -460,7 +435,7 @@ public HttpAssertion respondedJson( Class contextClass, String resource, Map< return respondedJson( contentOfTestResource( contextClass, resource, ofString() ), substitutions ); } - public HttpAssertion satisfies( Consumer assertion ) { + public HttpAssertion satisfies( Consumer assertion ) { assertion.accept( response ); return this; } @@ -582,13 +557,13 @@ public CookieHttpAssertion isNotHttpOnly() { } public static final class JsonHttpAssertion { - private final Client.Response response; + private final Response response; - private JsonHttpAssertion( Client.Response response ) { + private JsonHttpAssertion( Response response ) { this.response = response; } - public static JsonHttpAssertion assertJsonResponse( Client.Response response ) { + public static JsonHttpAssertion assertJsonResponse( Response response ) { return new JsonHttpAssertion( response ); } diff --git a/oap-http/oap-http-test/src/test/java/oap/http/ClientTest.java b/oap-http/oap-http-test/src/test/java/oap/http/ClientTest.java index c655f999c..723d12936 100644 --- a/oap-http/oap-http-test/src/test/java/oap/http/ClientTest.java +++ b/oap-http/oap-http-test/src/test/java/oap/http/ClientTest.java @@ -49,7 +49,7 @@ public class ClientTest extends Fixtures { private final TestDirectoryFixture testDirectoryFixture; private int port; private ClientAndServer mockServer; - private Client.Response response; + private Response response; public ClientTest() { testDirectoryFixture = fixture( new TestDirectoryFixture() ); diff --git a/oap-http/oap-http-test/src/test/java/oap/http/file/HttpFileSyncTest.java b/oap-http/oap-http-test/src/test/java/oap/http/file/HttpFileSyncTest.java index 0591829c7..de0b05eca 100644 --- a/oap-http/oap-http-test/src/test/java/oap/http/file/HttpFileSyncTest.java +++ b/oap-http/oap-http-test/src/test/java/oap/http/file/HttpFileSyncTest.java @@ -24,7 +24,6 @@ package oap.http.file; -import oap.io.AbstractFileSync; import oap.testng.Fixtures; import oap.testng.Ports; import oap.testng.SystemTimerFixture; diff --git a/oap-http/oap-http-test/src/test/java/oap/http/server/nio/NioHttpServerTest.java b/oap-http/oap-http-test/src/test/java/oap/http/server/nio/NioHttpServerTest.java index 6d3c8de6a..11f9da895 100644 --- a/oap-http/oap-http-test/src/test/java/oap/http/server/nio/NioHttpServerTest.java +++ b/oap-http/oap-http-test/src/test/java/oap/http/server/nio/NioHttpServerTest.java @@ -24,8 +24,8 @@ package oap.http.server.nio; -import oap.http.Client; import oap.http.Http; +import oap.http.Response; import oap.http.server.nio.handlers.BlockingReadTimeoutHandler; import oap.http.server.nio.handlers.CompressionNioHandler; import oap.http.server.nio.handlers.KeepaliveRequestsHandler; @@ -51,7 +51,7 @@ public void testResponseHeaders() throws IOException { try( NioHttpServer httpServer = new NioHttpServer( new NioHttpServer.DefaultPort( port ) ) ) { httpServer.start(); - Client.Response response = Client.DEFAULT.get( "http://localhost:" + port + "/" ); + Response response = Client.DEFAULT.get( "http://localhost:" + port + "/" ); assertThat( response.getHeaders() ) .hasSize( 3 ) @@ -64,7 +64,7 @@ public void testResponseHeaders() throws IOException { httpServer.alwaysSetKeepAlive = false; httpServer.start(); - Client.Response response = Client.DEFAULT.get( "http://localhost:" + port + "/" ); + Response response = Client.DEFAULT.get( "http://localhost:" + port + "/" ); assertThat( response.getHeaders() ) .hasSize( 1 ) diff --git a/oap-http/oap-http-test/src/test/java/oap/http/server/nio/handlers/KeepaliveRequestsHandlerTest.java b/oap-http/oap-http-test/src/test/java/oap/http/server/nio/handlers/KeepaliveRequestsHandlerTest.java index fecf877f3..f998a8e3e 100644 --- a/oap-http/oap-http-test/src/test/java/oap/http/server/nio/handlers/KeepaliveRequestsHandlerTest.java +++ b/oap-http/oap-http-test/src/test/java/oap/http/server/nio/handlers/KeepaliveRequestsHandlerTest.java @@ -1,6 +1,5 @@ package oap.http.server.nio.handlers; -import oap.http.Client; import oap.http.Http; import oap.http.server.nio.NioHttpServer; import oap.testng.Fixtures; diff --git a/oap-http/oap-http/src/main/java/oap/http/Client.java b/oap-http/oap-http/src/main/java/oap/http/Client.java deleted file mode 100644 index d6b85e0d1..000000000 --- a/oap-http/oap-http/src/main/java/oap/http/Client.java +++ /dev/null @@ -1,872 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) Open Application Platform Authors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package oap.http; - -import com.fasterxml.jackson.databind.MappingIterator; -import com.google.common.base.Preconditions; -import com.google.common.io.ByteStreams; -import lombok.SneakyThrows; -import lombok.ToString; -import lombok.extern.slf4j.Slf4j; -import oap.concurrent.AsyncCallbacks; -import oap.http.client.HttpClient; -import oap.io.Closeables; -import oap.io.Files; -import oap.io.IoStreams; -import oap.json.Binder; -import oap.reflect.TypeRef; -import oap.util.BiStream; -import oap.util.Dates; -import oap.util.Maps; -import oap.util.Pair; -import oap.util.Result; -import oap.util.Stream; -import oap.util.Throwables; -import oap.util.function.Try; -import oap.util.function.Try.ThrowingRunnable; -import org.apache.commons.lang3.StringUtils; -import org.apache.http.Header; -import org.apache.http.HttpResponse; -import org.apache.http.NameValuePair; -import org.apache.http.client.CookieStore; -import org.apache.http.client.config.CookieSpecs; -import org.apache.http.client.config.RequestConfig; -import org.apache.http.client.entity.UrlEncodedFormEntity; -import org.apache.http.client.methods.HttpDelete; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpPatch; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.client.methods.HttpPut; -import org.apache.http.client.methods.HttpRequestBase; -import org.apache.http.client.methods.HttpUriRequest; -import org.apache.http.client.utils.DateUtils; -import org.apache.http.concurrent.FutureCallback; -import org.apache.http.config.RegistryBuilder; -import org.apache.http.conn.ssl.DefaultHostnameVerifier; -import org.apache.http.conn.util.PublicSuffixMatcherLoader; -import org.apache.http.cookie.Cookie; -import org.apache.http.entity.ByteArrayEntity; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.InputStreamEntity; -import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.BasicCookieStore; -import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy; -import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; -import org.apache.http.impl.nio.client.HttpAsyncClientBuilder; -import org.apache.http.impl.nio.client.HttpAsyncClients; -import org.apache.http.impl.nio.conn.PoolingNHttpClientConnectionManager; -import org.apache.http.impl.nio.reactor.DefaultConnectingIOReactor; -import org.apache.http.impl.nio.reactor.IOReactorConfig; -import org.apache.http.message.BasicNameValuePair; -import org.apache.http.nio.conn.NoopIOSessionStrategy; -import org.apache.http.nio.conn.SchemeIOSessionStrategy; -import org.apache.http.nio.conn.ssl.SSLIOSessionStrategy; -import org.apache.http.nio.reactor.IOReactorException; -import org.apache.http.ssl.SSLContexts; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import java.io.BufferedInputStream; -import java.io.Closeable; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.PipedInputStream; -import java.io.PipedOutputStream; -import java.io.UncheckedIOException; -import java.io.UnsupportedEncodingException; -import java.net.URI; -import java.nio.file.Path; -import java.util.Date; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; -import java.util.concurrent.TimeoutException; -import java.util.function.Consumer; -import java.util.stream.Collectors; - -import static java.net.HttpURLConnection.HTTP_MOVED_TEMP; -import static java.net.HttpURLConnection.HTTP_NOT_MODIFIED; -import static java.net.HttpURLConnection.HTTP_OK; -import static java.nio.charset.StandardCharsets.UTF_8; -import static java.util.concurrent.TimeUnit.MILLISECONDS; -import static oap.http.Http.ContentType.APPLICATION_OCTET_STREAM; -import static oap.io.IoStreams.Encoding.PLAIN; -import static oap.io.ProgressInputStream.progress; - -@Deprecated( since = "24.2.4" ) -/** - * replaced by okhttp - */ -@Slf4j -public final class Client implements Closeable, AutoCloseable { - public static final Client DEFAULT = custom() - .onError( ( c, e ) -> log.error( e.getMessage(), e ) ) - .onTimeout( c -> log.error( "timeout" ) ) - .build(); - public static final String NO_RESPONSE = "no response"; - private static final FutureCallback FUTURE_CALLBACK = new FutureCallback<>() { - @Override - public void completed( org.apache.http.HttpResponse result ) { - } - - @Override - public void failed( Exception e ) { - log.error( "Error appears", e ); - } - - @Override - public void cancelled() { - } - }; - private final CookieStore cookieStore; - private final ClientBuilder builder; - private CloseableHttpAsyncClient client; - - private Client( CookieStore cookieStore, ClientBuilder builder ) { - this.client = builder.client(); - - this.cookieStore = cookieStore; - this.builder = builder; - } - - public static ClientBuilder custom( Path certificateLocation, String certificatePassword, int connectTimeout, int readTimeout ) { - return new ClientBuilder( certificateLocation, certificatePassword, connectTimeout, readTimeout ); - } - - public static ClientBuilder custom() { - return new ClientBuilder( null, null, Dates.m( 1 ), Dates.m( 5 ) ); - } - - private static List> headers( org.apache.http.HttpResponse response ) { - return Stream.of( response.getAllHeaders() ) - .map( h -> Pair.__( h.getName(), h.getValue() ) ) - .toList(); - } - - private static String[] split( final String s ) { - if( StringUtils.isBlank( s ) ) { - return null; - } - return s.split( " *, *" ); - } - - public Response get( String uri ) { - return get( uri, Map.of(), Map.of() ); - } - - public Response get( URI uri ) { - return get( uri, Map.of() ); - } - - @SafeVarargs - public final Response get( String uri, Pair... params ) { - return get( uri, Maps.of( params ) ); - } - - public Response get( String uri, Map params ) { - return get( uri, params, Map.of() ); - } - - public Response get( String uri, Map params, Map headers ) { - return get( uri, params, headers, builder.timeout ) - .orElseThrow( Throwables::propagate ); - } - - public Response get( URI uri, Map headers ) { - return get( uri, headers, builder.timeout ) - .orElseThrow( Throwables::propagate ); - } - - public Result get( String uri, Map params, long timeout ) { - return get( uri, params, Map.of(), timeout ); - } - - public Result get( String uri, Map params, Map headers, long timeout ) { - return get( Uri.uri( uri, params ), headers, timeout ); - } - - public Result get( URI uri, Map headers, long timeout ) { - var request = new HttpGet( uri ); - return getResponse( request, timeout, execute( request, headers ) ); - } - - public Response post( String uri, Map params ) { - return post( uri, params, Map.of() ); - } - - public Response post( String uri, Map params, Map headers ) { - return post( uri, params, headers, builder.timeout ) - .orElseThrow( Throwables::propagate ); - } - - public Result post( String uri, Map params, long timeout ) { - return post( uri, params, Map.of(), timeout ); - } - - public Result post( String uri, Map params, Map headers, long timeout ) { - try { - var request = new HttpPost( uri ); - request.setEntity( new UrlEncodedFormEntity( Stream.of( params.entrySet() ) - .map( e -> new BasicNameValuePair( e.getKey(), - e.getValue() == null ? "" : e.getValue().toString() ) ) - .toList() - ) ); - return getResponse( request, Math.max( builder.timeout, timeout ), execute( request, headers ) ); - } catch( UnsupportedEncodingException e ) { - throw new UncheckedIOException( e ); - } - } - - public Response post( String uri, String content, String contentType ) { - return post( uri, content, contentType, Maps.of() ); - } - - public Response post( String uri, String content, String contentType, Map headers ) { - return post( uri, content, contentType, headers, builder.timeout ) - .orElseThrow( Throwables::propagate ); - } - - public Result post( String uri, String content, String contentType, long timeout ) { - return post( uri, content, contentType, Map.of(), timeout ); - } - - public Result post( String uri, String content, String contentType, Map headers, long timeout ) { - var request = new HttpPost( uri ); - request.setEntity( new StringEntity( content, ContentType.create( contentType ) ) ); - return getResponse( request, timeout, execute( request, headers ) ); - } - - public Result post( String uri, byte[] content, long timeout ) { - var request = new HttpPost( uri ); - request.setEntity( new ByteArrayEntity( content, ContentType.APPLICATION_OCTET_STREAM ) ); - return getResponse( request, timeout, execute( request, Map.of() ) ); - } - - public Result post( String uri, byte[] content, int off, int length, long timeout ) { - var request = new HttpPost( uri ); - request.setEntity( new ByteArrayEntity( content, off, length, ContentType.APPLICATION_OCTET_STREAM ) ); - return getResponse( request, timeout, execute( request, Map.of() ) ); - } - - @SneakyThrows - public OutputStreamWithResponse post( String uri, ContentType contentType ) throws UncheckedIOException { - var request = new HttpPost( uri ); - - return post( contentType, request ); - } - - @SneakyThrows - public OutputStreamWithResponse post( URI uri, ContentType contentType ) throws UncheckedIOException { - var request = new HttpPost( uri ); - - return post( contentType, request ); - } - - private OutputStreamWithResponse post( ContentType contentType, HttpPost request ) throws UncheckedIOException { - try { - var pos = new PipedOutputStream(); - var pis = new PipedInputStream( pos ); - request.setEntity( new InputStreamEntity( pis, contentType ) ); - - return new OutputStreamWithResponse( pos, execute( request, Map.of() ), request, builder.timeout ); - } catch( IOException e ) { - throw new UncheckedIOException( e ); - } - } - - public Response post( String uri, InputStream content, String contentType ) { - var request = new HttpPost( uri ); - request.setEntity( new InputStreamEntity( content, ContentType.create( contentType ) ) ); - return getResponse( request, builder.timeout, execute( request, Map.of() ) ) - .orElseThrow( Throwables::propagate ); - } - - public Response post( String uri, InputStream content, String contentType, Map headers ) { - var request = new HttpPost( uri ); - request.setEntity( new InputStreamEntity( content, ContentType.create( contentType ) ) ); - return getResponse( request, builder.timeout, execute( request, headers ) ) - .orElseThrow( Throwables::propagate ); - } - - public Response post( String uri, byte[] content, String contentType, Map headers ) { - var request = new HttpPost( uri ); - request.setEntity( new ByteArrayEntity( content, ContentType.create( contentType ) ) ); - return getResponse( request, builder.timeout, execute( request, headers ) ) - .orElseThrow( Throwables::propagate ); - } - - private Result getResponse( HttpRequestBase request, long timeout, CompletableFuture future ) { - try { - return Result.success( timeout == 0 ? future.get() : future.get( timeout, MILLISECONDS ) ); - } catch( ExecutionException e ) { - var newEx = new UncheckedIOException( request.getURI().toString(), new IOException( e.getCause().getMessage(), e.getCause() ) ); - builder.onError.accept( this, newEx ); - return Result.failure( e.getCause() ); - } catch( TimeoutException e ) { - this.builder.onTimeout.accept( this ); - return Result.failure( e ); - } catch( InterruptedException e ) { - Thread.currentThread().interrupt(); - this.builder.onError.accept( this, e ); - return Result.failure( e ); - } - } - - public Response put( String uri, String content, String contentType, Map headers ) { - var request = new HttpPut( uri ); - request.setEntity( new StringEntity( content, ContentType.create( contentType ) ) ); - return getResponse( request, builder.timeout, execute( request, headers ) ) - .orElseThrow( Throwables::propagate ); - } - - public Response put( String uri, String content, String contentType ) { - return put( uri, content, contentType, Map.of() ); - } - - public Response put( String uri, byte[] content, String contentType, Map headers ) { - var request = new HttpPut( uri ); - request.setEntity( new ByteArrayEntity( content, ContentType.parse( contentType ) ) ); - return getResponse( request, builder.timeout, execute( request, headers ) ) - .orElseThrow( Throwables::propagate ); - } - - public Response put( String uri, byte[] content, String contentType ) { - return put( uri, content, contentType, Map.of() ); - } - - public Response put( String uri, InputStream is, String contentType, Map headers ) { - var request = new HttpPut( uri ); - request.setEntity( new InputStreamEntity( is, ContentType.parse( contentType ) ) ); - return getResponse( request, builder.timeout, execute( request, headers ) ) - .orElseThrow( Throwables::propagate ); - } - - public Response put( String uri, InputStream is, String contentType ) { - return put( uri, is, contentType, Map.of() ); - } - - public Response patch( String uri, String content, String contentType, Map headers ) { - var request = new HttpPatch( uri ); - request.setEntity( new StringEntity( content, ContentType.create( contentType ) ) ); - return getResponse( request, builder.timeout, execute( request, headers ) ) - .orElseThrow( Throwables::propagate ); - } - - public Response patch( String uri, String content, String contentType ) { - return patch( uri, content, contentType, Map.of() ); - } - - public Response patch( String uri, byte[] content, String contentType, Map headers ) { - var request = new HttpPatch( uri ); - request.setEntity( new ByteArrayEntity( content, ContentType.parse( contentType ) ) ); - return getResponse( request, builder.timeout, execute( request, headers ) ) - .orElseThrow( Throwables::propagate ); - } - - public Response patch( String uri, byte[] content, String contentType ) { - return patch( uri, content, contentType, Map.of() ); - } - - public Response patch( String uri, InputStream is, String contentType, Map headers ) { - var request = new HttpPatch( uri ); - request.setEntity( new InputStreamEntity( is, ContentType.parse( contentType ) ) ); - return getResponse( request, builder.timeout, execute( request, headers ) ) - .orElseThrow( Throwables::propagate ); - } - - public Response patch( String uri, InputStream is, String contentType ) { - return patch( uri, is, contentType, Map.of() ); - } - - public Response delete( String uri ) { - return delete( uri, builder.timeout ); - } - - public Response delete( String uri, long timeout ) { - return delete( uri, Map.of(), timeout ); - } - - public Response delete( String uri, Map headers ) { - return delete( uri, headers, builder.timeout ); - } - - public Response delete( String uri, Map headers, long timeout ) { - var request = new HttpDelete( uri ); - return getResponse( request, Math.max( builder.timeout, timeout ), execute( request, headers ) ) - .orElseThrow( Throwables::propagate ); - } - - public List getCookies() { - return cookieStore.getCookies(); - } - - public void clearCookies() { - cookieStore.clear(); - } - - private CompletableFuture execute( HttpUriRequest request, Map headers ) { - return execute( request, headers, () -> {} ); - } - - @SneakyThrows - private CompletableFuture execute( HttpUriRequest request, Map headers, - ThrowingRunnable asyncRunnable ) { - headers.forEach( ( name, value ) -> request.setHeader( name, value == null ? "" : value.toString() ) ); - - CompletableFuture completableFuture = new CompletableFuture(); - - client.execute( request, new FutureCallback<>() { - @Override - public void completed( HttpResponse response ) { - try { - List> responseHeaders = headers( response ); - Response result; - if( response.getEntity() != null ) { - var entity = response.getEntity(); - result = new Response( - request.getURI().toString(), - response.getStatusLine().getStatusCode(), - response.getStatusLine().getReasonPhrase(), - responseHeaders, - entity.getContentType() != null - ? entity.getContentType().getValue() - : APPLICATION_OCTET_STREAM, - entity.getContent() - ); - } else result = new Response( - request.getURI().toString(), - response.getStatusLine().getStatusCode(), - response.getStatusLine().getReasonPhrase(), - responseHeaders - ); - builder.onSuccess.accept( Client.this ); - - completableFuture.complete( result ); - } catch( IOException e ) { - completableFuture.completeExceptionally( e ); - } - } - - @Override - public void failed( Exception ex ) { - completableFuture.completeExceptionally( ex ); - } - - @Override - public void cancelled() { - completableFuture.cancel( false ); - } - } ); - - asyncRunnable.run(); - - return completableFuture; - } - - @SneakyThrows - public Optional download( String url, Optional modificationTime, Optional file, Consumer progress ) { - try { - var response = resolve( url, modificationTime ).orElse( null ); - if( response == null ) return Optional.empty(); - - var entity = response.getEntity(); - - final Path path = file.orElseGet( Try.supply( () -> { - final IoStreams.Encoding encoding = IoStreams.Encoding.from( url ); - - final File tempFile = File.createTempFile( "file", "down" + encoding.extension ); - tempFile.deleteOnExit(); - return tempFile.toPath(); - } ) ); - - try( InputStream in = new BufferedInputStream( entity.getContent() ) ) { - IoStreams.write( path, PLAIN, in, false, file.isPresent(), progress( entity.getContentLength(), progress ) ); - } - - final Header lastModified = response.getLastHeader( "Last-Modified" ); - if( lastModified != null ) { - final Date date = DateUtils.parseDate( lastModified.getValue() ); - - Files.setLastModifiedTime( path, date.getTime() ); - } - - builder.onSuccess.accept( this ); - - return Optional.of( path ); - } catch( ExecutionException | IOException e ) { - builder.onError.accept( this, e ); - throw e; - } catch( InterruptedException e ) { - Thread.currentThread().interrupt(); - builder.onTimeout.accept( this ); - return Optional.empty(); - } - } - - private Optional resolve( String url, Optional ifModifiedSince ) throws InterruptedException, ExecutionException, IOException { - HttpGet request = new HttpGet( url ); - ifModifiedSince.ifPresent( ims -> request.addHeader( "If-Modified-Since", DateUtils.formatDate( new Date( ims ) ) ) ); - Future future = client.execute( request, FUTURE_CALLBACK ); - HttpResponse response = future.get(); - if( response.getStatusLine().getStatusCode() == HTTP_OK && response.getEntity() != null ) - return Optional.of( response ); - else if( response.getStatusLine().getStatusCode() == HTTP_MOVED_TEMP ) { - Header location = response.getFirstHeader( "Location" ); - if( location == null ) throw new IOException( "redirect w/o location!" ); - log.debug( "following {}", location.getValue() ); - return resolve( location.getValue(), Optional.empty() ); - } else if( response.getStatusLine().getStatusCode() == HTTP_NOT_MODIFIED ) { - return Optional.empty(); - } else - throw new IOException( response.getStatusLine().toString() ); - } - - public void reset() { - Closeables.close( client ); - client = builder.client(); - clearCookies(); - } - - @Override - public void close() { - Closeables.close( client ); - } - - @ToString( exclude = { "inputStream", "content" }, doNotUseGetters = true ) - public static class Response implements Closeable, AutoCloseable { - public final String url; - public final int code; - public final String reasonPhrase; - public final String contentType; - public final List> headers; - private InputStream inputStream; - private volatile byte[] content = null; - - public Response( String url, int code, String reasonPhrase, List> headers, @Nonnull String contentType, InputStream inputStream ) { - this.url = url; - this.code = code; - this.reasonPhrase = reasonPhrase; - this.headers = headers; - this.contentType = Objects.requireNonNull( contentType ); - this.inputStream = inputStream; - } - - public Response( String url, int code, String reasonPhrase, List> headers ) { - this( url, code, reasonPhrase, headers, BiStream.of( headers ) - .filter( ( name, value ) -> "Content-type".equalsIgnoreCase( name ) ) - .mapToObj( ( name, value ) -> value ) - .findAny() - .orElse( APPLICATION_OCTET_STREAM ), null ); - } - - public Optional header( @Nonnull String headerName ) { - return BiStream.of( headers ) - .filter( ( name, value ) -> headerName.equalsIgnoreCase( name ) ) - .mapToObj( ( name, value ) -> value ) - .findAny(); - } - - @Nullable - @SneakyThrows - public byte[] content() { - if( content == null && inputStream == null ) return null; - if( content == null ) synchronized( this ) { - if( content == null ) { - content = ByteStreams.toByteArray( inputStream ); - close(); - } - } - return content; - } - - public InputStream getInputStream() { - return inputStream; - } - - public String contentString() { - var text = content(); - - if( text == null ) return null; - - return new String( text, UTF_8 ); - } - - public Optional unmarshal( Class clazz ) { - if( inputStream != null ) synchronized( this ) { - if( inputStream != null ) - return Optional.of( Binder.json.unmarshal( clazz, inputStream ) ); - } - - var contentString = contentString(); - if( contentString == null ) return Optional.empty(); - - return Optional.of( Binder.json.unmarshal( clazz, contentString ) ); - } - - public Optional unmarshal( TypeRef ref ) { - if( inputStream != null ) synchronized( this ) { - if( inputStream != null ) - return Optional.of( Binder.json.unmarshal( ref, inputStream ) ); - } - - var contentString = contentString(); - if( contentString == null ) return Optional.empty(); - - return Optional.of( Binder.json.unmarshal( ref, contentString ) ); - } - - @SneakyThrows - public Stream unmarshalStream( TypeRef ref ) { - MappingIterator objectMappingIterator = null; - - if( inputStream != null ) { - synchronized( this ) { - if( inputStream != null ) { - objectMappingIterator = Binder.json.readerFor( ref ).readValues( inputStream ); - } - } - } - - if( objectMappingIterator == null ) { - var contentString = contentString(); - if( contentString == null ) - return Stream.empty(); - - objectMappingIterator = Binder.json.readerFor( ref ).readValues( contentString ); - } - - var finalObjectMappingIterator = objectMappingIterator; - - var it = new Iterator() { - @Override - public boolean hasNext() { - return finalObjectMappingIterator.hasNext(); - } - - @Override - @SuppressWarnings( "unchecked" ) - public T next() { - return ( T ) finalObjectMappingIterator.next(); - } - }; - - var stream = Stream.of( it ); - if( inputStream != null ) stream = stream.onClose( Try.run( () -> inputStream.close() ) ); - return stream; - } - - @Override - public void close() { - Closeables.close( inputStream ); - inputStream = null; - } - - public Map getHeaders() { - return headers.stream().collect( Collectors.toMap( p -> p._1, p -> p._2 ) ); - } - } - - public static class ClientBuilder extends AsyncCallbacks { - - private final Path certificateLocation; - private final String certificatePassword; - private final long timeout; - private CookieStore cookieStore; - private long connectTimeout; - private int maxConnTotal = 10000; - private int maxConnPerRoute = 1000; - private boolean redirectsEnabled = false; - private String cookieSpec = CookieSpecs.STANDARD; - - public ClientBuilder( Path certificateLocation, String certificatePassword, long connectTimeout, long timeout ) { - cookieStore = new BasicCookieStore(); - - this.certificateLocation = certificateLocation; - this.certificatePassword = certificatePassword; - this.connectTimeout = connectTimeout; - this.timeout = timeout; - } - - public ClientBuilder withCookieStore( CookieStore cookieStore ) { - this.cookieStore = cookieStore; - - return this; - } - - private HttpAsyncClientBuilder initialize() { - try { - final PoolingNHttpClientConnectionManager connManager = new PoolingNHttpClientConnectionManager( - new DefaultConnectingIOReactor( IOReactorConfig.custom() - .setConnectTimeout( ( int ) connectTimeout ) - .setSoTimeout( ( int ) timeout ) - .build() ), - RegistryBuilder.create() - .register( "http", NoopIOSessionStrategy.INSTANCE ) - .register( "https", - new SSLIOSessionStrategy( certificateLocation != null - ? HttpClient.createSSLContext( certificateLocation, certificatePassword ) - : SSLContexts.createDefault(), - split( System.getProperty( "https.protocols" ) ), - split( System.getProperty( "https.cipherSuites" ) ), - new DefaultHostnameVerifier( PublicSuffixMatcherLoader.getDefault() ) ) ) - .build() ); - - connManager.setMaxTotal( maxConnTotal ); - connManager.setDefaultMaxPerRoute( maxConnPerRoute ); - - return ( certificateLocation != null - ? HttpAsyncClients.custom() - .setSSLContext( HttpClient.createSSLContext( certificateLocation, certificatePassword ) ) - : HttpAsyncClients.custom() ) - .setMaxConnPerRoute( maxConnPerRoute ) - .setConnectionManager( connManager ) - .setMaxConnTotal( maxConnTotal ) - .setKeepAliveStrategy( DefaultConnectionKeepAliveStrategy.INSTANCE ) - .setDefaultRequestConfig( RequestConfig - .custom() - .setRedirectsEnabled( redirectsEnabled ) - .setCookieSpec( cookieSpec ) - .build() ) - .setDefaultCookieStore( cookieStore ); - } catch( IOReactorException e ) { - throw new UncheckedIOException( e ); - } - } - - public ClientBuilder setConnectTimeout( long connectTimeout ) { - this.connectTimeout = connectTimeout; - - return this; - } - - public ClientBuilder setMaxConnTotal( int maxConnTotal ) { - this.maxConnTotal = maxConnTotal; - - return this; - } - - public ClientBuilder setMaxConnPerRoute( int maxConnPerRoute ) { - this.maxConnPerRoute = maxConnPerRoute; - - return this; - } - - public ClientBuilder setRedirectsEnabled( boolean redirectsEnabled ) { - this.redirectsEnabled = redirectsEnabled; - - return this; - } - - public ClientBuilder setCookieSpec( String cookieSpec ) { - this.cookieSpec = cookieSpec; - - return this; - } - - private CloseableHttpAsyncClient client() { - final CloseableHttpAsyncClient build = initialize().build(); - build.start(); - return build; - } - - public Client build() { - return new Client( cookieStore, this ); - } - } - - public class OutputStreamWithResponse extends OutputStream implements Closeable, AutoCloseable { - private final CompletableFuture completableFuture; - private final HttpRequestBase request; - private final long timeout; - private PipedOutputStream pos; - private Response response; - - public OutputStreamWithResponse( PipedOutputStream pos, CompletableFuture completableFuture, HttpRequestBase request, long timeout ) { - this.pos = pos; - this.completableFuture = completableFuture; - this.request = request; - this.timeout = timeout; - } - - @Override - public void write( int b ) throws IOException { - pos.write( b ); - } - - @Override - public void write( @Nonnull byte[] b ) throws IOException { - pos.write( b ); - } - - @Override - public void write( @Nonnull byte[] b, int off, int len ) throws IOException { - pos.write( b, off, len ); - } - - @Override - public void flush() throws IOException { - pos.flush(); - } - - public Response waitAndGetResponse() { - Preconditions.checkState( response == null ); - try { - pos.flush(); - pos.close(); - Response result = getResponse( request, timeout, completableFuture ) - .orElseThrow( Throwables::propagate ); - response = result; - return result; - } catch( IOException e ) { - throw Throwables.propagate( e ); - } finally { - try { - pos.close(); - } catch( IOException e ) { - log.error( "Cannot close output", e ); - } finally { - pos = null; - } - } - } - - @Override - public void close() { - try { - if( response == null ) { - waitAndGetResponse(); - } - } finally { - response.close(); - } - } - } -} diff --git a/oap-http/oap-http/src/main/java/oap/http/Response.java b/oap-http/oap-http/src/main/java/oap/http/Response.java new file mode 100644 index 000000000..fc514af68 --- /dev/null +++ b/oap-http/oap-http/src/main/java/oap/http/Response.java @@ -0,0 +1,161 @@ +package oap.http; + +import com.fasterxml.jackson.databind.MappingIterator; +import com.google.common.io.ByteStreams; +import lombok.SneakyThrows; +import lombok.ToString; +import oap.io.Closeables; +import oap.json.Binder; +import oap.reflect.TypeRef; +import oap.util.BiStream; +import oap.util.Pair; +import oap.util.Stream; +import oap.util.function.Try; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.io.Closeable; +import java.io.InputStream; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static oap.http.Http.ContentType.APPLICATION_OCTET_STREAM; + +@ToString( exclude = { "inputStream", "content" }, doNotUseGetters = true ) +public class Response implements Closeable, AutoCloseable { + public final String url; + public final int code; + public final String reasonPhrase; + public final String contentType; + public final List> headers; + private InputStream inputStream; + private volatile byte[] content = null; + + public Response( String url, int code, String reasonPhrase, List> headers, @Nonnull String contentType, InputStream inputStream ) { + this.url = url; + this.code = code; + this.reasonPhrase = reasonPhrase; + this.headers = headers; + this.contentType = Objects.requireNonNull( contentType ); + this.inputStream = inputStream; + } + + public Response( String url, int code, String reasonPhrase, List> headers ) { + this( url, code, reasonPhrase, headers, BiStream.of( headers ) + .filter( ( name, value ) -> "Content-type".equalsIgnoreCase( name ) ) + .mapToObj( ( name, value ) -> value ) + .findAny() + .orElse( APPLICATION_OCTET_STREAM ), null ); + } + + public Optional header( @Nonnull String headerName ) { + return BiStream.of( headers ) + .filter( ( name, value ) -> headerName.equalsIgnoreCase( name ) ) + .mapToObj( ( name, value ) -> value ) + .findAny(); + } + + @Nullable + @SneakyThrows + public byte[] content() { + if( content == null && inputStream == null ) return null; + if( content == null ) synchronized( this ) { + if( content == null ) { + content = ByteStreams.toByteArray( inputStream ); + close(); + } + } + return content; + } + + public InputStream getInputStream() { + return inputStream; + } + + public String contentString() { + var text = content(); + + if( text == null ) return null; + + return new String( text, UTF_8 ); + } + + public Optional unmarshal( Class clazz ) { + if( inputStream != null ) synchronized( this ) { + if( inputStream != null ) + return Optional.of( Binder.json.unmarshal( clazz, inputStream ) ); + } + + var contentString = contentString(); + if( contentString == null ) return Optional.empty(); + + return Optional.of( Binder.json.unmarshal( clazz, contentString ) ); + } + + public Optional unmarshal( TypeRef ref ) { + if( inputStream != null ) synchronized( this ) { + if( inputStream != null ) + return Optional.of( Binder.json.unmarshal( ref, inputStream ) ); + } + + var contentString = contentString(); + if( contentString == null ) return Optional.empty(); + + return Optional.of( Binder.json.unmarshal( ref, contentString ) ); + } + + @SneakyThrows + public Stream unmarshalStream( TypeRef ref ) { + MappingIterator objectMappingIterator = null; + + if( inputStream != null ) { + synchronized( this ) { + if( inputStream != null ) { + objectMappingIterator = Binder.json.readerFor( ref ).readValues( inputStream ); + } + } + } + + if( objectMappingIterator == null ) { + var contentString = contentString(); + if( contentString == null ) + return Stream.empty(); + + objectMappingIterator = Binder.json.readerFor( ref ).readValues( contentString ); + } + + var finalObjectMappingIterator = objectMappingIterator; + + var it = new Iterator() { + @Override + public boolean hasNext() { + return finalObjectMappingIterator.hasNext(); + } + + @Override + @SuppressWarnings( "unchecked" ) + public T next() { + return ( T ) finalObjectMappingIterator.next(); + } + }; + + var stream = Stream.of( it ); + if( inputStream != null ) stream = stream.onClose( Try.run( () -> inputStream.close() ) ); + return stream; + } + + @Override + public void close() { + Closeables.close( inputStream ); + inputStream = null; + } + + public Map getHeaders() { + return headers.stream().collect( Collectors.toMap( p -> p._1, p -> p._2 ) ); + } +} diff --git a/oap-http/oap-http/src/main/java/oap/http/client/JettyRequestExtensions.java b/oap-http/oap-http/src/main/java/oap/http/client/JettyRequestExtensions.java new file mode 100644 index 000000000..2d25560f2 --- /dev/null +++ b/oap-http/oap-http/src/main/java/oap/http/client/JettyRequestExtensions.java @@ -0,0 +1,19 @@ +package oap.http.client; + +import org.eclipse.jetty.client.Request; + +import java.util.Map; + +public class JettyRequestExtensions { + public static Request addParams( Request request, Map params ) { + params.forEach( ( k, v ) -> request.param( k, v.toString() ) ); + + return request; + } + + public static Request addHeaders( Request request, Map headers ) { + return request.headers( h -> { + headers.forEach( ( k, v ) -> h.add( k, v == null ? "" : v.toString() ) ); + } ); + } +} diff --git a/oap-http/oap-http/src/main/java/oap/http/file/HttpFileSync.java b/oap-http/oap-http/src/main/java/oap/http/file/HttpFileSync.java deleted file mode 100644 index e4f3bc9f4..000000000 --- a/oap-http/oap-http/src/main/java/oap/http/file/HttpFileSync.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) Open Application Platform Authors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package oap.http.file; - -import lombok.SneakyThrows; -import lombok.extern.slf4j.Slf4j; -import oap.http.Client; -import oap.io.AbstractFileSync; - -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Optional; - -@Slf4j -public class HttpFileSync extends AbstractFileSync { - public HttpFileSync() { - super( "http", "https" ); - } - - @SneakyThrows - protected Optional download() { - Optional modificationTime = Files.exists( localFile ) - ? Optional.of( Files.getLastModifiedTime( localFile ).toMillis() ) : Optional.empty(); - return Client.DEFAULT.download( uri.toString(), modificationTime, Optional.of( localFile ), i -> {} ); - } -} diff --git a/oap-http/oap-pnio-v3/src/test/java/oap/http/pniov3/PnioServerTest.java b/oap-http/oap-pnio-v3/src/test/java/oap/http/pniov3/PnioServerTest.java index 727e3ba76..89dad4522 100644 --- a/oap-http/oap-pnio-v3/src/test/java/oap/http/pniov3/PnioServerTest.java +++ b/oap-http/oap-pnio-v3/src/test/java/oap/http/pniov3/PnioServerTest.java @@ -2,7 +2,7 @@ import oap.concurrent.Executors; import oap.concurrent.ThreadPoolExecutor; -import oap.http.Client; +import oap.http.Response; import oap.http.server.nio.NioHttpServer; import oap.testng.Fixtures; import oap.util.Dates; @@ -43,7 +43,7 @@ public void testRequestUndertow() throws InterruptedException, IOException { for( int i = 0; i < 20; i++ ) { threadPoolExecutor.submit( () -> { try { - Client.Response response = client.get( "http://localhost:" + port + "/pnio?trace=true" ); + Response response = client.get( "http://localhost:" + port + "/pnio?trace=true" ); okCount.incrementAndGet(); } catch( Exception e ) { errorCount.incrementAndGet(); diff --git a/oap-stdlib-test/src/test/java/oap/io/FileSystemFileSyncTest.java b/oap-stdlib-test/src/test/java/oap/io/FileSystemFileSyncTest.java deleted file mode 100644 index d51063e0b..000000000 --- a/oap-stdlib-test/src/test/java/oap/io/FileSystemFileSyncTest.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) Open Application Platform Authors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package oap.io; - -import lombok.SneakyThrows; -import oap.testng.Fixtures; -import oap.testng.TestDirectoryFixture; -import org.testng.annotations.Test; - -import java.nio.file.Paths; - -import static oap.io.content.ContentWriter.ofString; -import static org.assertj.core.api.Assertions.assertThat; - -public class FileSystemFileSyncTest extends Fixtures { - private final TestDirectoryFixture testDirectoryFixture; - - public FileSystemFileSyncTest() { - testDirectoryFixture = fixture( new TestDirectoryFixture() ); - } - - @Test - @SneakyThrows - public void sync() { - StringBuilder b = new StringBuilder(); - - var remoteFile = testDirectoryFixture.testPath( "rtest.file" ).toUri(); - var localFile = testDirectoryFixture.testPath( "ltest.file" ); - - Files.write( Paths.get( remoteFile ), "test", ofString() ); - - Files.setLastModifiedTime( Paths.get( remoteFile ), 10 ); - - final AbstractFileSync fileSync = AbstractFileSync.create( remoteFile, localFile ); - fileSync.addListener( path -> b.append( "f" ) ); - fileSync.run(); - - assertThat( localFile ).hasContent( "test" ); - assertThat( java.nio.file.Files.getLastModifiedTime( localFile ).toMillis() ).isEqualTo( 10L ); - assertThat( b ).contains( "f" ); - - Files.write( Paths.get( remoteFile ), "test2", ofString() ); - Files.setLastModifiedTime( Paths.get( remoteFile ), 10 ); - - fileSync.run(); - assertThat( localFile ).hasContent( "test" ); - assertThat( b ).contains( "f" ); - - Files.setLastModifiedTime( Paths.get( remoteFile ), 20L ); - fileSync.run(); - assertThat( localFile ).hasContent( "test2" ); - assertThat( b ).contains( "ff" ); - } - -} diff --git a/oap-stdlib/src/main/java/oap/io/AbstractFileSync.java b/oap-stdlib/src/main/java/oap/io/AbstractFileSync.java deleted file mode 100644 index 64bcd9b9f..000000000 --- a/oap-stdlib/src/main/java/oap/io/AbstractFileSync.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) Open Application Platform Authors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package oap.io; - -import lombok.SneakyThrows; - -import java.io.IOException; -import java.net.URI; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.Optional; -import java.util.ServiceLoader; -import java.util.Set; - -import static java.util.Arrays.asList; - -public abstract class AbstractFileSync implements Runnable { - private final HashSet protocols; - private final ArrayList listeners = new ArrayList<>(); - protected URI uri; - protected Path localFile; - - protected AbstractFileSync( String... protocols ) { - this.protocols = new HashSet<>( asList( protocols ) ); - } - - @SneakyThrows - public static AbstractFileSync create( String url, Path localFile ) { - return create( new URI( url ), localFile ); - } - - @SneakyThrows - public static AbstractFileSync create( URI uri, Path localFile ) { - var protocol = uri.getScheme(); - - final ServiceLoader load = ServiceLoader.load( AbstractFileSync.class ); - for( AbstractFileSync fs : load ) { - if( fs.accept( protocol ) ) { - fs.init( uri, localFile ); - return fs; - } - } - - throw new IOException( "unknown protocol: " + protocol ); - } - - public Set getProtocols() { - return protocols; - } - - protected boolean accept( String protocol ) { - return protocols.contains( protocol ); - } - - void init( URI uri, Path localFile ) { - this.uri = uri; - this.localFile = localFile; - } - - public URI getUri() { - return uri; - } - - public Path getLocalFile() { - return localFile; - } - - protected void fireDownloaded( Path path ) { - this.listeners.forEach( l -> l.downloaded( path ) ); - } - - protected void fireNotModified() { - this.listeners.forEach( FileDownloaderListener::notModified ); - } - - public void addListener( FileDownloaderListener listener ) { - this.listeners.add( listener ); - } - - public void removeListener( FileDownloaderListener listener ) { - this.listeners.remove( listener ); - } - - @Override - public synchronized void run() { - final Optional downloaded = download(); - - if( downloaded.isPresent() ) { - final Path path = downloaded.get(); - - fireDownloaded( path ); - } else - fireNotModified(); - } - - protected abstract Optional download(); - - public interface FileDownloaderListener { - void downloaded( Path path ); - - default void notModified() { - } - } -} diff --git a/oap-stdlib/src/main/java/oap/io/FileSystemFileSync.java b/oap-stdlib/src/main/java/oap/io/FileSystemFileSync.java deleted file mode 100644 index 0520f5a74..000000000 --- a/oap-stdlib/src/main/java/oap/io/FileSystemFileSync.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) Open Application Platform Authors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package oap.io; - -import lombok.SneakyThrows; -import lombok.extern.slf4j.Slf4j; - -import java.io.InputStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Optional; - -@Slf4j -public class FileSystemFileSync extends AbstractFileSync { - public FileSystemFileSync() { - super( "file" ); - } - - @Override - @SneakyThrows - protected Optional download() { - var remoteFile = Paths.get( uri ); - var localFile = this.localFile.toAbsolutePath(); - - var remoteFileLastModifiedTime = java.nio.file.Files.getLastModifiedTime( remoteFile ).toMillis(); - var localFileLastModifiedTime = Files.exists( localFile ) - ? java.nio.file.Files.getLastModifiedTime( localFile ).toMillis() - : Long.MIN_VALUE; - - if( remoteFileLastModifiedTime > localFileLastModifiedTime ) { - try( InputStream in = IoStreams.in( uri.toURL().openStream(), IoStreams.Encoding.PLAIN ) ) { - IoStreams.write( localFile, IoStreams.Encoding.PLAIN, in, false, true ); - } - - oap.io.Files.setLastModifiedTime( localFile, remoteFileLastModifiedTime ); - - return Optional.of( localFile ); - } else return Optional.empty(); - } -} diff --git a/oap-stdlib/src/main/resources/META-INF/services/oap.io.AbstractFileSync b/oap-stdlib/src/main/resources/META-INF/services/oap.io.AbstractFileSync deleted file mode 100644 index ab6f2cad5..000000000 --- a/oap-stdlib/src/main/resources/META-INF/services/oap.io.AbstractFileSync +++ /dev/null @@ -1,2 +0,0 @@ -oap.io.FileSystemFileSync -oap.http.file.HttpFileSync diff --git a/oap-ws/oap-ws-test/src/main/java/oap/ws/validate/testng/ValidationAssertion.java b/oap-ws/oap-ws-test/src/main/java/oap/ws/validate/testng/ValidationAssertion.java index b684158c4..cfd016fd3 100644 --- a/oap-ws/oap-ws-test/src/main/java/oap/ws/validate/testng/ValidationAssertion.java +++ b/oap-ws/oap-ws-test/src/main/java/oap/ws/validate/testng/ValidationAssertion.java @@ -24,7 +24,7 @@ package oap.ws.validate.testng; -import oap.http.Client; +import oap.http.Response; import oap.json.Binder; import oap.ws.validate.ValidationErrors; @@ -34,11 +34,11 @@ public final class ValidationAssertion { private final ValidationErrors errors; - private ValidationAssertion( Client.Response response ) { + private ValidationAssertion( Response response ) { errors = Binder.json.unmarshal( ValidationErrors.class, response.contentString() ); } - public static ValidationAssertion assertValidation( Client.Response response ) { + public static ValidationAssertion assertValidation( Response response ) { assertJsonResponse( response ); return new ValidationAssertion( response ); } diff --git a/oap-ws/oap-ws-test/src/test/java/oap/ws/WebServicesSessionTest.java b/oap-ws/oap-ws-test/src/test/java/oap/ws/WebServicesSessionTest.java index 368b2bf58..b1432f491 100644 --- a/oap-ws/oap-ws-test/src/test/java/oap/ws/WebServicesSessionTest.java +++ b/oap-ws/oap-ws-test/src/test/java/oap/ws/WebServicesSessionTest.java @@ -26,7 +26,6 @@ import oap.application.testng.KernelFixture; import oap.http.Http; -import oap.http.test.HttpAsserts; import oap.testng.Fixtures; import oap.testng.TestDirectoryFixture; import org.testng.annotations.Test; @@ -50,7 +49,7 @@ public WebServicesSessionTest() { public void sessionViaResponse() { assertGet( kernel.httpUrl( "/session/put" ), Map.of( "value", "vvv" ), Map.of() ) .hasCode( Http.StatusCode.NO_CONTENT ); - HttpAsserts.assertGet( kernel.httpUrl( "/session/get" ) ) + assertGet( kernel.httpUrl( "/session/get" ) ) .isOk() .hasBody( "vvv" ); } @@ -59,7 +58,7 @@ public void sessionViaResponse() { public void sessionDirectly() { assertGet( kernel.httpUrl( "/session/putDirectly" ), Map.of( "value", "vvv" ), Map.of() ) .hasCode( Http.StatusCode.NO_CONTENT ); - HttpAsserts.assertGet( kernel.httpUrl( "/session/get" ) ) + assertGet( kernel.httpUrl( "/session/get" ) ) .isOk() .hasBody( "vvv" ); } @@ -68,7 +67,7 @@ public void sessionDirectly() { public void respondHtmlContentType() { assertGet( kernel.httpUrl( "/session/putDirectly" ), Map.of( "value", "vvv" ), Map.of() ) .hasCode( Http.StatusCode.NO_CONTENT ); - HttpAsserts.assertGet( kernel.httpUrl( "/session/html" ) ) + assertGet( kernel.httpUrl( "/session/html" ) ) .isOk() .hasBody( "vvv" ) .hasContentType( Http.ContentType.TEXT_HTML ); diff --git a/oap-ws/oap-ws-test/src/test/java/oap/ws/WebServicesTest.java b/oap-ws/oap-ws-test/src/test/java/oap/ws/WebServicesTest.java index caef53d5a..e3cb7113b 100644 --- a/oap-ws/oap-ws-test/src/test/java/oap/ws/WebServicesTest.java +++ b/oap-ws/oap-ws-test/src/test/java/oap/ws/WebServicesTest.java @@ -25,7 +25,6 @@ import lombok.extern.slf4j.Slf4j; import oap.application.testng.KernelFixture; -import oap.http.Client; import oap.http.Http; import oap.http.server.nio.HttpHandler; import oap.http.server.nio.HttpServerExchange; @@ -51,6 +50,7 @@ import static oap.http.Http.StatusCode.OK; import static oap.http.server.nio.HttpServerExchange.HttpMethod.GET; import static oap.http.test.HttpAsserts.assertGet; +import static oap.http.test.HttpAsserts.assertPost; import static oap.io.Resources.urlOrThrow; import static oap.util.Pair.__; import static oap.ws.WsParam.From.BODY; @@ -201,15 +201,10 @@ public void shouldVerifyGZIPRequestProcessing() throws Exception { gzip.write( "{\"i\":1,\"s\":\"sss\"}".getBytes( StandardCharsets.UTF_8 ) ); gzip.close(); - Client.Response response = Client - .custom() - .build() - .post( kernel.httpUrl( "/x/v/math/json" ), - new ByteArrayInputStream( byteArrayOutputStream.toByteArray() ), - APPLICATION_JSON, Map.of( "Content-Encoding", "gzip" ) ); - - assertThat( response.code ).isEqualTo( OK ); - assertThat( response.contentString() ).isEqualTo( "{\"i\":1,\"s\":\"sss\"}" ); + assertPost( kernel.httpUrl( "/x/v/math/json" ), new ByteArrayInputStream( byteArrayOutputStream.toByteArray() ), APPLICATION_JSON, Map.of( "Content-Encoding", "gzip" ) ) + .isOk() + .respondedJson( """ + { "i":1, "s":"sss" }""" ); } /** From 61c831e8f936ff4b692d159a2145ed9c275cc8d8 Mon Sep 17 00:00:00 2001 From: "igor.petrenko" Date: Sun, 8 Feb 2026 18:49:41 +0200 Subject: [PATCH 03/39] CE-135 okhttp -> jetty http client --- oap-http/oap-http-prometheus/pom.xml | 6 + .../prometheus/PrometheusExporterTest.java | 32 ++-- .../main/java/oap/http/test/HttpAsserts.java | 31 +++- .../src/test/java/oap/http/ClientTest.java | 140 ------------------ .../src/test/java/oap/http/GzipHttpTest.java | 40 +++-- .../java/oap/http/file/HttpFileSyncTest.java | 126 ---------------- .../http/server/nio/NioHttpServerTest.java | 49 +++--- .../KeepaliveRequestsHandlerTest.java | 13 +- 8 files changed, 105 insertions(+), 332 deletions(-) delete mode 100644 oap-http/oap-http-test/src/test/java/oap/http/ClientTest.java delete mode 100644 oap-http/oap-http-test/src/test/java/oap/http/file/HttpFileSyncTest.java diff --git a/oap-http/oap-http-prometheus/pom.xml b/oap-http/oap-http-prometheus/pom.xml index ad63401a9..debf9dbfb 100644 --- a/oap-http/oap-http-prometheus/pom.xml +++ b/oap-http/oap-http-prometheus/pom.xml @@ -40,6 +40,12 @@ ${project.version} test + + oap + oap-http-test + ${project.version} + test + org.projectlombok diff --git a/oap-http/oap-http-prometheus/src/test/java/oap/http/prometheus/PrometheusExporterTest.java b/oap-http/oap-http-prometheus/src/test/java/oap/http/prometheus/PrometheusExporterTest.java index 816af3e8e..da4038b85 100644 --- a/oap-http/oap-http-prometheus/src/test/java/oap/http/prometheus/PrometheusExporterTest.java +++ b/oap-http/oap-http-prometheus/src/test/java/oap/http/prometheus/PrometheusExporterTest.java @@ -24,8 +24,10 @@ package oap.http.prometheus; +import io.micrometer.core.instrument.Counter; import io.micrometer.core.instrument.Meter; import io.micrometer.core.instrument.Metrics; +import io.micrometer.core.instrument.Timer; import io.micrometer.core.instrument.search.Search; import oap.http.server.nio.NioHttpServer; import oap.testng.Fixtures; @@ -33,10 +35,9 @@ import org.testng.annotations.AfterMethod; import org.testng.annotations.Test; -import java.io.IOException; import java.util.concurrent.TimeUnit; -import static org.assertj.core.api.Assertions.assertThat; +import static oap.http.test.HttpAsserts.assertGet; public class PrometheusExporterTest extends Fixtures { private static void clear() { @@ -46,28 +47,29 @@ private static void clear() { } @Test - public void server() throws IOException { - var port = Ports.getFreePort( getClass() ); + public void server() throws Exception { + int port = Ports.getFreePort( getClass() ); try( var server = new NioHttpServer( new NioHttpServer.DefaultPort( port ) ) ) { var exporter = new PrometheusExporter( server ); - var metric1 = Metrics.counter( "test1" ); - var metric2 = Metrics.timer( "test2" ); + Counter metric1 = Metrics.counter( "test1" ); + Timer metric2 = Metrics.timer( "test2" ); server.start(); metric1.increment( 2 ); metric2.record( 2, TimeUnit.SECONDS ); - var response = Client.DEFAULT.get( "http://localhost:" + port + "/metrics" ).contentString(); - assertThat( response ).contains( """ - # HELP test1_total \s - # TYPE test1_total counter - test1_total 2.0 - """ ); - assertThat( response ).contains( "test2_seconds_count 1" ); - assertThat( response ).contains( "test2_seconds_max 2.0" ); - assertThat( response ).contains( "system_metrics 5" ); + assertGet( "http://localhost:" + port + "/metrics" ) + .body() + .contains( """ + # HELP test1_total \s + # TYPE test1_total counter + test1_total 2.0 + """ ) + .contains( "test2_seconds_count 1" ) + .contains( "test2_seconds_max 2.0" ) + .contains( "system_metrics 5" ); } } diff --git a/oap-http/oap-http-test/src/main/java/oap/http/test/HttpAsserts.java b/oap-http/oap-http-test/src/main/java/oap/http/test/HttpAsserts.java index da42b823e..2ee83dcab 100644 --- a/oap-http/oap-http-test/src/main/java/oap/http/test/HttpAsserts.java +++ b/oap-http/oap-http-test/src/main/java/oap/http/test/HttpAsserts.java @@ -117,7 +117,11 @@ public static HttpAssertion assertGet( String uri, Pair... param } public static HttpAssertion assertGet( String uri, Map params, Map headers ) throws UncheckedIOException { - return getResponseAsHttpAssertion( HTTP_CLIENT + return assertGet( HTTP_CLIENT, uri, params, headers ); + } + + public static HttpAssertion assertGet( HttpClient client, String uri, Map params, Map headers ) throws UncheckedIOException { + return getResponseAsHttpAssertion( client .newRequest( uri ) .method( HttpMethod.GET ) .addParams( params ) @@ -136,6 +140,14 @@ public static HttpAssertion assertPost( String uri, InputStream content, @Nullab return assertPost( uri, content, contentType, Maps.of() ); } + public static HttpAssertion assertPost( String uri, byte[] content, @Nullable String contentType, Map headers ) { + return getResponseAsHttpAssertion( HTTP_CLIENT + .newRequest( uri ) + .method( HttpMethod.POST ) + .addHeaders( headers ) + .body( new BytesRequestContent( contentType, content ) ) ); + } + public static HttpAssertion assertPost( String uri, String content, @Nullable String contentType, Map headers ) { return getResponseAsHttpAssertion( HTTP_CLIENT .newRequest( uri ) @@ -356,11 +368,28 @@ public HttpAssertion bodyContainsPattern( String pattern ) { return this; } + public HttpAssertion hasHeadersSize( int size ) { + assertThat( response.headers ).hasSize( size ); + + return this; + } + public HttpAssertion containsHeader( String name, String value ) { + containsHeader( name ); assertString( response.header( name ).orElse( null ) ).isEqualTo( value ); return this; } + public HttpAssertion containsHeader( String name ) { + assertThat( response.getHeaders() ).containsKey( name ); + return this; + } + + public HttpAssertion doesNotContainHeader( String name ) { + assertThat( response.getHeaders() ).doesNotContainKey( name ); + return this; + } + public HttpAssertion containsCookie( String name, Consumer assertion ) { Optional cookie = Stream.of( getCookies() ).filter( c -> c.getName().equalsIgnoreCase( name ) ).findAny(); Assertions.assertThat( cookie ) diff --git a/oap-http/oap-http-test/src/test/java/oap/http/ClientTest.java b/oap-http/oap-http-test/src/test/java/oap/http/ClientTest.java deleted file mode 100644 index 723d12936..000000000 --- a/oap-http/oap-http-test/src/test/java/oap/http/ClientTest.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) Open Application Platform Authors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package oap.http; - -import lombok.extern.slf4j.Slf4j; -import oap.testng.Fixtures; -import oap.testng.TestDirectoryFixture; -import org.apache.http.entity.ContentType; -import org.mockserver.integration.ClientAndServer; -import org.mockserver.matchers.Times; -import org.mockserver.model.HttpRequest; -import org.mockserver.model.HttpResponse; -import org.testng.annotations.AfterMethod; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; - -import java.io.IOException; -import java.util.Optional; -import java.util.concurrent.atomic.AtomicInteger; - -import static java.net.HttpURLConnection.HTTP_OK; -import static oap.testng.Asserts.assertFile; -import static org.assertj.core.api.Assertions.assertThat; - -@Slf4j -public class ClientTest extends Fixtures { - private final TestDirectoryFixture testDirectoryFixture; - private int port; - private ClientAndServer mockServer; - private Response response; - - public ClientTest() { - testDirectoryFixture = fixture( new TestDirectoryFixture() ); - } - - @BeforeMethod - public void start() { - mockServer = ClientAndServer.startClientAndServer( 0 ); - port = mockServer.getLocalPort(); - response = null; - } - - @AfterMethod - public void stop() { - mockServer.stop( true ); - } - - @Test - public void download() { - mockServer - .when( - HttpRequest.request() - .withMethod( "GET" ) - .withPath( "/file" ), - Times.once() - ) - .respond( - org.mockserver.model.HttpResponse.response() - .withStatusCode( HTTP_OK ) - .withBody( "test1" ) - ); - - var path = testDirectoryFixture.testPath( "new.file" ); - var progress = new AtomicInteger(); - var download = Client.DEFAULT.download( "http://localhost:" + port + "/file", - Optional.empty(), Optional.of( path ), progress::set ); - - assertThat( download ).contains( path ); - assertThat( download ).isPresent(); - assertFile( path ).exists().hasSize( 5 ); - assertThat( progress.get() ).isEqualTo( 100 ); - } - - @Test - public void downloadTempFile() { - mockServer - .when( - HttpRequest.request() - .withMethod( "GET" ) - .withPath( "/file.gz" ), - Times.once() - ) - .respond( - org.mockserver.model.HttpResponse.response() - .withStatusCode( HTTP_OK ) - .withBody( "test1" ) - ); - - var progress = new AtomicInteger(); - var download = Client.DEFAULT.download( "http://localhost:" + port + "/file.gz", - Optional.empty(), Optional.empty(), progress::set ); - assertThat( download ).isPresent(); - assertFile( download.get() ).exists().hasSize( 5 ); - assertFile( download.get() ).hasExtension( "gz" ); - assertThat( progress.get() ).isEqualTo( 100 ); - } - - @Test - public void postOutputStream() throws IOException { - mockServer.when( HttpRequest.request() - .withMethod( "POST" ) - .withPath( "/test" ) - .withBody( "test\ntest1" ), - Times.once() - ).respond( HttpResponse.response().withStatusCode( HTTP_OK ).withBody( "ok" ) ); - - try( var os = Client.DEFAULT.post( "http://localhost:" + port + "/test", ContentType.TEXT_PLAIN ) ) { - os.write( "test".getBytes() ); - os.write( '\n' ); - os.write( "test1".getBytes() ); - - response = os.waitAndGetResponse(); - - assertThat( response ).isNotNull(); - assertThat( response.contentString() ).isEqualTo( "ok" ); - } - } -} diff --git a/oap-http/oap-http-test/src/test/java/oap/http/GzipHttpTest.java b/oap-http/oap-http-test/src/test/java/oap/http/GzipHttpTest.java index a795d75e5..c8943debd 100644 --- a/oap-http/oap-http-test/src/test/java/oap/http/GzipHttpTest.java +++ b/oap-http/oap-http-test/src/test/java/oap/http/GzipHttpTest.java @@ -25,7 +25,6 @@ package oap.http; import oap.http.server.nio.NioHttpServer; -import oap.io.IoStreams; import oap.io.content.ContentWriter; import oap.testng.Fixtures; import oap.testng.Ports; @@ -35,14 +34,12 @@ import java.util.Map; -import static java.net.HttpURLConnection.HTTP_OK; import static oap.compression.Compression.ContentWriter.ofGzip; import static oap.http.Http.ContentType.TEXT_PLAIN; import static oap.http.Http.Headers.ACCEPT_ENCODING; import static oap.http.Http.Headers.CONTENT_ENCODING; -import static oap.io.IoStreams.Encoding.GZIP; -import static oap.io.IoStreams.Encoding.PLAIN; -import static org.assertj.core.api.Assertions.assertThat; +import static oap.http.test.HttpAsserts.assertGet; +import static oap.http.test.HttpAsserts.assertPost; public class GzipHttpTest extends Fixtures { private NioHttpServer server; @@ -64,18 +61,18 @@ public void gzipOutput() { ); server.start(); - var responseHtml = Client.DEFAULT.get( "http://localhost:" + server.defaultPort.httpPort + "/test" ); + assertGet( "http://localhost:" + server.defaultPort.httpPort + "/test" ) + .isOk() + .hasContentType( TEXT_PLAIN ) + .body() + .isEqualTo( "test" ); - assertThat( responseHtml.code ).isEqualTo( HTTP_OK ); - assertThat( responseHtml.contentType ).isEqualTo( TEXT_PLAIN ); - assertThat( responseHtml.contentString() ).isEqualTo( "test" ); - - var responseGzip = Client.DEFAULT.get( "http://localhost:" + server.defaultPort.httpPort + "/test", - Map.of(), Map.of( ACCEPT_ENCODING, "gzip,deflate" ) ); - - assertThat( responseGzip.code ).isEqualTo( HTTP_OK ); - assertThat( responseGzip.contentType ).isEqualTo( TEXT_PLAIN ); - assertThat( IoStreams.asString( responseGzip.getInputStream(), GZIP ) ).isEqualTo( "test" ); + assertGet( "http://localhost:" + server.defaultPort.httpPort + "/test", Map.of(), Map.of( ACCEPT_ENCODING, "gzip,deflate" ) ) + .isOk() + .hasContentType( TEXT_PLAIN ) + .containsHeader( "Accept-Encoding", "gzip,deflate" ) + .body() + .isEqualTo( "test" ); } @Test @@ -85,11 +82,10 @@ public void gzipInput() { ); server.start(); - var response = Client.DEFAULT.post( "http://localhost:" + server.defaultPort.httpPort + "/test", - ContentWriter.write( "test2", ofGzip() ), TEXT_PLAIN, Map.of( CONTENT_ENCODING, "gzip" ) ); - - assertThat( response.code ).isEqualTo( HTTP_OK ); - assertThat( response.contentType ).isEqualTo( TEXT_PLAIN ); - assertThat( IoStreams.asString( response.getInputStream(), PLAIN ) ).isEqualTo( "test2" ); + assertPost( "http://localhost:" + server.defaultPort.httpPort + "/test", + ContentWriter.write( "test2", ofGzip() ), TEXT_PLAIN, Map.of( CONTENT_ENCODING, "gzip" ) ) + .hasContentType( TEXT_PLAIN ) + .body() + .isEqualTo( "test2" ); } } diff --git a/oap-http/oap-http-test/src/test/java/oap/http/file/HttpFileSyncTest.java b/oap-http/oap-http-test/src/test/java/oap/http/file/HttpFileSyncTest.java deleted file mode 100644 index de0b05eca..000000000 --- a/oap-http/oap-http-test/src/test/java/oap/http/file/HttpFileSyncTest.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) Open Application Platform Authors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package oap.http.file; - -import oap.testng.Fixtures; -import oap.testng.Ports; -import oap.testng.SystemTimerFixture; -import oap.testng.TestDirectoryFixture; -import org.apache.http.client.utils.DateUtils; -import org.joda.time.DateTimeUtils; -import org.mockserver.integration.ClientAndServer; -import org.mockserver.matchers.Times; -import org.mockserver.model.Header; -import org.mockserver.model.HttpRequest; -import org.mockserver.model.HttpResponse; -import org.testng.annotations.AfterMethod; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; - -import java.util.Date; - -import static java.net.HttpURLConnection.HTTP_NOT_MODIFIED; -import static java.net.HttpURLConnection.HTTP_OK; -import static org.assertj.core.api.Assertions.assertThat; - -public class HttpFileSyncTest extends Fixtures { - private final TestDirectoryFixture testDirectoryFixture; - private ClientAndServer mockServer; - - public HttpFileSyncTest() { - fixture( new SystemTimerFixture( true ) ); - testDirectoryFixture = fixture( new TestDirectoryFixture() ); - } - - @BeforeMethod - public void start() { - mockServer = ClientAndServer.startClientAndServer( Ports.getFreePort( getClass() ) ); - } - - @AfterMethod - public void stop() { - mockServer.stop( true ); - } - - @Test - public void sync() throws Exception { - var b = new StringBuilder(); - DateTimeUtils.setCurrentMillisFixed( 10 * 1000 ); - - var date10 = new Date( 10 * 1000 ); - var date20 = new Date( 20 * 1000 ); - - var localFile = testDirectoryFixture.testPath( "ltest.file" ); - - var fileSync = AbstractFileSync.create( "http://localhost:" + mockServer.getLocalPort() + "/file", localFile ); - fileSync.addListener( path -> b.append( "f" ) ); - - mockServer - .reset() - .when( HttpRequest.request().withMethod( "GET" ).withPath( "/file" ), Times.once() ) - .respond( - HttpResponse.response() - .withStatusCode( HTTP_OK ) - .withBody( "test" ) - .withHeader( Header.header( "Last-Modified", DateUtils.formatDate( date10 ) ) ) - .withHeader( Header.header( "Content-Disposition", "inline; filename=\"test.file\"" ) ) - ); - - fileSync.run(); - assertThat( localFile ).hasContent( "test" ); - assertThat( java.nio.file.Files.getLastModifiedTime( localFile ).toMillis() ).isEqualTo( 10L * 1000 ); - assertThat( b ).contains( "f" ); - - mockServer - .reset() - .when( HttpRequest.request().withMethod( "GET" ).withPath( "/file" ) - .withHeader( Header.header( "If-Modified-Since", DateUtils.formatDate( date10 ) ) ), - Times.once() ) - .respond( - HttpResponse.response() - .withStatusCode( HTTP_NOT_MODIFIED ) - .withHeader( Header.header( "Last-Modified", DateUtils.formatDate( date10 ) ) ) - ); - fileSync.run(); - assertThat( localFile ).hasContent( "test" ); - assertThat( b ).contains( "f" ); - - mockServer - .reset() - .when( HttpRequest.request().withMethod( "GET" ).withPath( "/file" ) - .withHeader( Header.header( "If-Modified-Since", DateUtils.formatDate( date10 ) ) ), - Times.once() ) - .respond( - HttpResponse.response() - .withStatusCode( HTTP_OK ) - .withBody( "test2" ) - .withHeader( Header.header( "Last-Modified", DateUtils.formatDate( date20 ) ) ) - .withHeader( Header.header( "Content-Disposition", "inline; filename=\"test.file\"" ) ) - ); - fileSync.run(); - assertThat( localFile ).hasContent( "test2" ); - assertThat( b ).contains( "ff" ); - } -} diff --git a/oap-http/oap-http-test/src/test/java/oap/http/server/nio/NioHttpServerTest.java b/oap-http/oap-http-test/src/test/java/oap/http/server/nio/NioHttpServerTest.java index 11f9da895..c56bb681c 100644 --- a/oap-http/oap-http-test/src/test/java/oap/http/server/nio/NioHttpServerTest.java +++ b/oap-http/oap-http-test/src/test/java/oap/http/server/nio/NioHttpServerTest.java @@ -25,7 +25,6 @@ package oap.http.server.nio; import oap.http.Http; -import oap.http.Response; import oap.http.server.nio.handlers.BlockingReadTimeoutHandler; import oap.http.server.nio.handlers.CompressionNioHandler; import oap.http.server.nio.handlers.KeepaliveRequestsHandler; @@ -34,9 +33,14 @@ import oap.testng.Fixtures; import oap.testng.Ports; import oap.util.Dates; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.transport.HttpClientTransportDynamic; +import org.eclipse.jetty.io.ClientConnector; +import org.eclipse.jetty.util.ssl.SslContextFactory; import org.testng.annotations.Test; import java.io.IOException; +import java.util.Map; import static oap.http.Http.Headers.CONNECTION; import static oap.http.Http.Headers.DATE; @@ -51,12 +55,10 @@ public void testResponseHeaders() throws IOException { try( NioHttpServer httpServer = new NioHttpServer( new NioHttpServer.DefaultPort( port ) ) ) { httpServer.start(); - Response response = Client.DEFAULT.get( "http://localhost:" + port + "/" ); - - assertThat( response.getHeaders() ) - .hasSize( 3 ) - .containsKey( DATE ) - .containsKey( CONNECTION ); + assertGet( "http://localhost:" + port + "/" ) + .hasHeadersSize( 3 ) + .containsHeader( DATE ) + .containsHeader( CONNECTION ); } try( NioHttpServer httpServer = new NioHttpServer( new NioHttpServer.DefaultPort( port ) ) ) { @@ -64,12 +66,10 @@ public void testResponseHeaders() throws IOException { httpServer.alwaysSetKeepAlive = false; httpServer.start(); - Response response = Client.DEFAULT.get( "http://localhost:" + port + "/" ); - - assertThat( response.getHeaders() ) - .hasSize( 1 ) - .doesNotContainKey( DATE ) - .doesNotContainKey( CONNECTION ); + assertGet( "http://localhost:" + port + "/" ) + .hasHeadersSize( 1 ) + .doesNotContainHeader( DATE ) + .doesNotContainHeader( CONNECTION ); } } @@ -93,9 +93,9 @@ public void testBindToSpecificPort() throws IOException { httpServer.start(); httpServer.bind( "/test3", exchange -> exchange.responseOk( "test3", Http.ContentType.TEXT_PLAIN ), "test2" ); - assertThat( Client.DEFAULT.get( "http://localhost:" + testPort + "/test" ).contentString() ).isEqualTo( "test" ); - assertThat( Client.DEFAULT.get( "http://localhost:" + testPort2 + "/test2" ).contentString() ).isEqualTo( "test2" ); - assertThat( Client.DEFAULT.get( "http://localhost:" + testPort2 + "/test3" ).contentString() ).isEqualTo( "test3" ); + assertGet( "http://localhost:" + testPort + "/test" ).body().isEqualTo( "test" ); + assertGet( "http://localhost:" + testPort2 + "/test2" ).body().isEqualTo( "test2" ); + assertGet( "http://localhost:" + testPort2 + "/test3" ).body().isEqualTo( "test3" ); } } @@ -107,21 +107,22 @@ public void testHttps() throws IOException { int httpPort = Ports.getFreePort( getClass() ); int httpsPort = Ports.getFreePort( getClass() ); - try( NioHttpServer httpServer = new NioHttpServer( new NioHttpServer.DefaultPort( httpPort, httpsPort, Resources.urlOrThrow( getClass(), "/oap/http/test_https.jks" ), "1234567" ) ); - Client client = Client - .custom( Resources.filePath( getClass(), "/oap/http/test_https.jks" ).get(), "1234567", 10000, 10000 ) - .build() ) { + try( NioHttpServer httpServer = new NioHttpServer( new NioHttpServer.DefaultPort( httpPort, httpsPort, Resources.urlOrThrow( getClass(), "/oap/http/test_https.jks" ), "1234567" ) ) ) { + ClientConnector connector = new ClientConnector(); + + SslContextFactory.Client sslContextFactory = new SslContextFactory.Client(); + sslContextFactory.setKeyStorePath( Resources.filePath( getClass(), "/oap/http/test_https.jks" ).get() ); + sslContextFactory.setKeyStorePassword( "1234567" ); + connector.setSslContextFactory( sslContextFactory ); + HttpClient httpClient = new HttpClient( new HttpClientTransportDynamic( connector ) ); new TestHttpHandler( httpServer, "/test", "default-https" ); new HealthHttpHandler( httpServer, "/healtz", "default-http" ).start(); httpServer.start(); - assertThat( client.get( "https://localhost:" + httpsPort + "/test" ) - .contentString() ).isEqualTo( "ok" ); - + assertGet( httpClient, "https://localhost:" + httpsPort + "/test", Map.of(), Map.of() ).body().isEqualTo( "ok" ); assertGet( "http://localhost:" + httpPort + "/healtz" ).hasCode( Http.StatusCode.NO_CONTENT ); - } } diff --git a/oap-http/oap-http-test/src/test/java/oap/http/server/nio/handlers/KeepaliveRequestsHandlerTest.java b/oap-http/oap-http-test/src/test/java/oap/http/server/nio/handlers/KeepaliveRequestsHandlerTest.java index f998a8e3e..049f60b04 100644 --- a/oap-http/oap-http-test/src/test/java/oap/http/server/nio/handlers/KeepaliveRequestsHandlerTest.java +++ b/oap-http/oap-http-test/src/test/java/oap/http/server/nio/handlers/KeepaliveRequestsHandlerTest.java @@ -4,11 +4,14 @@ import oap.http.server.nio.NioHttpServer; import oap.testng.Fixtures; import oap.testng.Ports; +import org.eclipse.jetty.client.HttpClient; import org.testng.annotations.Test; import java.io.IOException; import java.util.LinkedHashSet; +import java.util.Map; +import static oap.http.test.HttpAsserts.assertGet; import static oap.testng.Asserts.assertEventually; import static org.assertj.core.api.Assertions.assertThat; @@ -36,10 +39,11 @@ public void testCloseConnectionBlocking() throws IOException { exchange.responseOk( "ok", Http.ContentType.TEXT_PLAIN ); } ); - Client client = Client.custom().setMaxConnTotal( 10 ).setMaxConnPerRoute( 10 ).build(); + HttpClient client = new HttpClient(); + client.setMaxConnectionsPerDestination( 10 ); for( int i = 0; i < 101; i++ ) { - assertThat( client.get( "http://localhost:" + testHttpPort + "/test" ).contentString() ).isEqualTo( "ok" ); + assertGet( client, "http://localhost:" + testHttpPort + "/test", Map.of(), Map.of() ).body().isEqualTo( "ok" ); } assertThat( ids ).hasSize( 51 ); @@ -65,10 +69,11 @@ public void testCloseConnectionAsync() throws IOException { exchange.responseOk( "ok", Http.ContentType.TEXT_PLAIN ); }, true ); - Client client = Client.custom().setMaxConnTotal( 10 ).setMaxConnPerRoute( 10 ).build(); + HttpClient client = new HttpClient(); + client.setMaxConnectionsPerDestination( 10 ); for( int i = 0; i < 101; i++ ) { - assertThat( client.get( "http://localhost:" + testHttpPort + "/test" ).contentString() ).isEqualTo( "ok" ); + assertGet( client, "http://localhost:" + testHttpPort + "/test", Map.of(), Map.of() ).body().isEqualTo( "ok" ); } assertThat( ids ).hasSize( 51 ); From c0c60cff6b6669fb94b90afe7784d77204f11ad6 Mon Sep 17 00:00:00 2001 From: "igor.petrenko" Date: Sun, 8 Feb 2026 19:05:20 +0200 Subject: [PATCH 04/39] CE-135 okhttp -> jetty http client --- .../java/oap/http/pniov3/PnioServerTest.java | 73 ++++++++++--------- 1 file changed, 37 insertions(+), 36 deletions(-) diff --git a/oap-http/oap-pnio-v3/src/test/java/oap/http/pniov3/PnioServerTest.java b/oap-http/oap-pnio-v3/src/test/java/oap/http/pniov3/PnioServerTest.java index 89dad4522..8e8d4a516 100644 --- a/oap-http/oap-pnio-v3/src/test/java/oap/http/pniov3/PnioServerTest.java +++ b/oap-http/oap-pnio-v3/src/test/java/oap/http/pniov3/PnioServerTest.java @@ -1,17 +1,18 @@ package oap.http.pniov3; -import oap.concurrent.Executors; -import oap.concurrent.ThreadPoolExecutor; -import oap.http.Response; import oap.http.server.nio.NioHttpServer; import oap.testng.Fixtures; import oap.util.Dates; +import org.eclipse.jetty.client.HttpClient; import org.testng.annotations.Test; -import java.io.IOException; -import java.util.concurrent.TimeUnit; +import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicInteger; +import static oap.http.test.HttpAsserts.assertGet; + public class PnioServerTest extends Fixtures { private final PortFixture fixture; @@ -20,41 +21,41 @@ public PnioServerTest() { } @Test - public void testRequestUndertow() throws InterruptedException, IOException { + public void testRequestUndertow() throws Exception { int port = fixture.definePort( "test" ); - ThreadPoolExecutor threadPoolExecutor = Executors.newFixedBlockingThreadPool( 1024 ); - - Client client = Client.custom().setMaxConnPerRoute( 20000 ).setMaxConnTotal( 20000 ).build(); - - AtomicInteger errorCount = new AtomicInteger(); - AtomicInteger okCount = new AtomicInteger(); - - try( NioHttpServer pnioServer = new NioHttpServer( new NioHttpServer.DefaultPort( port ) ) ) { - pnioServer.ioThreads = Runtime.getRuntime().availableProcessors(); - pnioServer.bind( "/pnio", exchange -> { - exchange.setStatusCode( 204 ); - exchange.endExchange(); - } ); - pnioServer.start(); - - long start = System.currentTimeMillis(); - - for( int i = 0; i < 20; i++ ) { - threadPoolExecutor.submit( () -> { - try { - Response response = client.get( "http://localhost:" + port + "/pnio?trace=true" ); - okCount.incrementAndGet(); - } catch( Exception e ) { - errorCount.incrementAndGet(); + try( ExecutorService threadPoolExecutor = Executors.newVirtualThreadPerTaskExecutor() ) { + try( HttpClient httpClient = new HttpClient() ) { + httpClient.setMaxConnectionsPerDestination( 20000 ); + httpClient.start(); + + AtomicInteger errorCount = new AtomicInteger(); + AtomicInteger okCount = new AtomicInteger(); + + try( NioHttpServer pnioServer = new NioHttpServer( new NioHttpServer.DefaultPort( port ) ) ) { + pnioServer.ioThreads = Runtime.getRuntime().availableProcessors(); + pnioServer.bind( "/pnio", exchange -> { + exchange.setStatusCode( 204 ); + exchange.endExchange(); + } ); + pnioServer.start(); + + long start = System.currentTimeMillis(); + + for( int i = 0; i < 20; i++ ) { + threadPoolExecutor.execute( () -> { + try { + assertGet( httpClient, "http://localhost:" + port + "/pnio?trace=true", Map.of(), Map.of() ); + okCount.incrementAndGet(); + } catch( Exception e ) { + errorCount.incrementAndGet(); + } + } ); } - } ); - } - System.out.println( "ok " + okCount.get() + " error " + errorCount.get() + " duration " + Dates.durationToString( System.currentTimeMillis() - start ) ); - - threadPoolExecutor.shutdown(); - threadPoolExecutor.awaitTermination( 10, TimeUnit.SECONDS ); + System.out.println( "ok " + okCount.get() + " error " + errorCount.get() + " duration " + Dates.durationToString( System.currentTimeMillis() - start ) ); + } + } } } } From 92391958c4f799ec5eccfde61c271a32ca853d28 Mon Sep 17 00:00:00 2001 From: "igor.petrenko" Date: Sun, 8 Feb 2026 19:22:50 +0200 Subject: [PATCH 05/39] CE-135 okhttp -> jetty http client --- .../main/java/oap/http/test/HttpAsserts.java | 2 +- .../oap/http/server/nio/NioHttpServerTest.java | 18 ++++++++++-------- .../java/oap/message/client/MessageSender.java | 4 ++-- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/oap-http/oap-http-test/src/main/java/oap/http/test/HttpAsserts.java b/oap-http/oap-http-test/src/main/java/oap/http/test/HttpAsserts.java index 2ee83dcab..83856fd6b 100644 --- a/oap-http/oap-http-test/src/main/java/oap/http/test/HttpAsserts.java +++ b/oap-http/oap-http-test/src/main/java/oap/http/test/HttpAsserts.java @@ -178,7 +178,7 @@ public static HttpAssertion assertPost( String uri, String content, String conte HttpFields responseHeaders = contentResponse.getHeaders(); responseHeaders.forEach( field -> { HttpHeader header = field.getHeader(); - headers.add( __( header.name(), header.asString() ) ); + headers.add( __( field.getName(), field.getValue() ) ); } ); return new HttpAssertion( new Response( diff --git a/oap-http/oap-http-test/src/test/java/oap/http/server/nio/NioHttpServerTest.java b/oap-http/oap-http-test/src/test/java/oap/http/server/nio/NioHttpServerTest.java index c56bb681c..f66c753da 100644 --- a/oap-http/oap-http-test/src/test/java/oap/http/server/nio/NioHttpServerTest.java +++ b/oap-http/oap-http-test/src/test/java/oap/http/server/nio/NioHttpServerTest.java @@ -103,18 +103,20 @@ public void testBindToSpecificPort() throws IOException { * keytool -genkey -alias ssl -keyalg RSA -keysize 2048 -dname "CN=localhost,OU=IT" -keystore master.jks -storepass 1234567 -keypass 1234567 */ @Test - public void testHttps() throws IOException { + public void testHttps() throws Exception { int httpPort = Ports.getFreePort( getClass() ); int httpsPort = Ports.getFreePort( getClass() ); - try( NioHttpServer httpServer = new NioHttpServer( new NioHttpServer.DefaultPort( httpPort, httpsPort, Resources.urlOrThrow( getClass(), "/oap/http/test_https.jks" ), "1234567" ) ) ) { - ClientConnector connector = new ClientConnector(); + ClientConnector connector = new ClientConnector(); - SslContextFactory.Client sslContextFactory = new SslContextFactory.Client(); - sslContextFactory.setKeyStorePath( Resources.filePath( getClass(), "/oap/http/test_https.jks" ).get() ); - sslContextFactory.setKeyStorePassword( "1234567" ); - connector.setSslContextFactory( sslContextFactory ); - HttpClient httpClient = new HttpClient( new HttpClientTransportDynamic( connector ) ); + SslContextFactory.Client sslContextFactory = new SslContextFactory.Client(); + sslContextFactory.setKeyStorePath( Resources.filePath( getClass(), "/oap/http/test_https.jks" ).get() ); + sslContextFactory.setKeyStorePassword( "1234567" ); + connector.setSslContextFactory( sslContextFactory ); + + try( NioHttpServer httpServer = new NioHttpServer( new NioHttpServer.DefaultPort( httpPort, httpsPort, Resources.urlOrThrow( getClass(), "/oap/http/test_https.jks" ), "1234567" ) ); + HttpClient httpClient = new HttpClient( new HttpClientTransportDynamic( connector ) ) ) { + httpClient.start(); new TestHttpHandler( httpServer, "/test", "default-https" ); new HealthHttpHandler( httpServer, "/healtz", "default-http" ).start(); diff --git a/oap-message/oap-message-client/src/main/java/oap/message/client/MessageSender.java b/oap-message/oap-message-client/src/main/java/oap/message/client/MessageSender.java index fab50c02b..6da6841dc 100644 --- a/oap-message/oap-message-client/src/main/java/oap/message/client/MessageSender.java +++ b/oap-message/oap-message-client/src/main/java/oap/message/client/MessageSender.java @@ -203,8 +203,6 @@ public MessageSender send( byte messageType, short version, byte[] data, int off @SneakyThrows @Override public void close() { - httpClient.stop(); - lock.lock(); try { closed = true; @@ -225,6 +223,8 @@ public void close() { } } + Closeables.close( httpClient ); + saveMessagesToDirectory( directory ); } From 61a2d688b486d3f6d5e64d910d4538ef4eb3a14b Mon Sep 17 00:00:00 2001 From: "igor.petrenko" Date: Sun, 8 Feb 2026 19:28:26 +0200 Subject: [PATCH 06/39] CE-135 okhttp -> jetty http client --- .../KeepaliveRequestsHandlerTest.java | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/oap-http/oap-http-test/src/test/java/oap/http/server/nio/handlers/KeepaliveRequestsHandlerTest.java b/oap-http/oap-http-test/src/test/java/oap/http/server/nio/handlers/KeepaliveRequestsHandlerTest.java index 049f60b04..c50f9fdef 100644 --- a/oap-http/oap-http-test/src/test/java/oap/http/server/nio/handlers/KeepaliveRequestsHandlerTest.java +++ b/oap-http/oap-http-test/src/test/java/oap/http/server/nio/handlers/KeepaliveRequestsHandlerTest.java @@ -7,7 +7,6 @@ import org.eclipse.jetty.client.HttpClient; import org.testng.annotations.Test; -import java.io.IOException; import java.util.LinkedHashSet; import java.util.Map; @@ -24,9 +23,12 @@ public KeepaliveRequestsHandlerTest() { } @Test - public void testCloseConnectionBlocking() throws IOException { + public void testCloseConnectionBlocking() throws Exception { LinkedHashSet ids = new LinkedHashSet<>(); - try( NioHttpServer httpServer = new NioHttpServer( new NioHttpServer.DefaultPort( testHttpPort ) ) ) { + try( NioHttpServer httpServer = new NioHttpServer( new NioHttpServer.DefaultPort( testHttpPort ) ); + HttpClient client = new HttpClient() ) { + client.setMaxConnectionsPerDestination( 10 ); + client.start(); KeepaliveRequestsHandler keepaliveRequestsHandler = new KeepaliveRequestsHandler( 2 ); httpServer.handlers.add( keepaliveRequestsHandler ); @@ -39,8 +41,6 @@ public void testCloseConnectionBlocking() throws IOException { exchange.responseOk( "ok", Http.ContentType.TEXT_PLAIN ); } ); - HttpClient client = new HttpClient(); - client.setMaxConnectionsPerDestination( 10 ); for( int i = 0; i < 101; i++ ) { assertGet( client, "http://localhost:" + testHttpPort + "/test", Map.of(), Map.of() ).body().isEqualTo( "ok" ); @@ -54,9 +54,13 @@ public void testCloseConnectionBlocking() throws IOException { } @Test - public void testCloseConnectionAsync() throws IOException { + public void testCloseConnectionAsync() throws Exception { LinkedHashSet ids = new LinkedHashSet<>(); - try( NioHttpServer httpServer = new NioHttpServer( new NioHttpServer.DefaultPort( testHttpPort ) ) ) { + try( NioHttpServer httpServer = new NioHttpServer( new NioHttpServer.DefaultPort( testHttpPort ) ); + HttpClient client = new HttpClient() ) { + + client.setMaxConnectionsPerDestination( 10 ); + client.start(); KeepaliveRequestsHandler keepaliveRequestsHandler = new KeepaliveRequestsHandler( 2 ); httpServer.handlers.add( keepaliveRequestsHandler ); @@ -69,9 +73,6 @@ public void testCloseConnectionAsync() throws IOException { exchange.responseOk( "ok", Http.ContentType.TEXT_PLAIN ); }, true ); - HttpClient client = new HttpClient(); - client.setMaxConnectionsPerDestination( 10 ); - for( int i = 0; i < 101; i++ ) { assertGet( client, "http://localhost:" + testHttpPort + "/test", Map.of(), Map.of() ).body().isEqualTo( "ok" ); } From c391c0bcbc56b03359165a730c35324fb3c66a16 Mon Sep 17 00:00:00 2001 From: "igor.petrenko" Date: Sun, 8 Feb 2026 21:12:27 +0200 Subject: [PATCH 07/39] CE-135 okhttp -> jetty http client --- .../src/main/java/oap/http/test/HttpAsserts.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/oap-http/oap-http-test/src/main/java/oap/http/test/HttpAsserts.java b/oap-http/oap-http-test/src/main/java/oap/http/test/HttpAsserts.java index 83856fd6b..39b69bb12 100644 --- a/oap-http/oap-http-test/src/main/java/oap/http/test/HttpAsserts.java +++ b/oap-http/oap-http-test/src/main/java/oap/http/test/HttpAsserts.java @@ -64,6 +64,7 @@ import java.util.Optional; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import java.util.regex.Pattern; @@ -170,7 +171,9 @@ public static HttpAssertion assertPost( String uri, String content, String conte @SneakyThrows private static @Nonnull HttpAssertion getResponseAsHttpAssertion( org.eclipse.jetty.client.Request request ) { - ContentResponse contentResponse = request.send(); + ContentResponse contentResponse = request + .timeout( 10, TimeUnit.SECONDS ) + .send(); String mediaType = contentResponse.getMediaType(); From 2ea0e51ad37f157da436b958c6b0cee927a2af98 Mon Sep 17 00:00:00 2001 From: "igor.petrenko" Date: Mon, 9 Feb 2026 07:58:21 +0200 Subject: [PATCH 08/39] CE-135 okhttp -> jetty http client --- .../oap/application/remote/RemoteTest.java | 6 +- .../remote/RemoteInvocationHandler.java | 21 ++++-- .../main/java/oap/http/test/HttpAsserts.java | 5 ++ .../src/test/java/oap/http/GzipHttpTest.java | 24 ++++-- .../oap/compression/CompressionUtils.java | 73 ------------------- .../src/test/java/oap/ws/WebServicesTest.java | 2 +- 6 files changed, 43 insertions(+), 88 deletions(-) delete mode 100644 oap-stdlib/src/main/java/oap/compression/CompressionUtils.java diff --git a/oap-application/oap-application-test/src/test/java/oap/application/remote/RemoteTest.java b/oap-application/oap-application-test/src/test/java/oap/application/remote/RemoteTest.java index bda37bacc..2074368a5 100644 --- a/oap-application/oap-application-test/src/test/java/oap/application/remote/RemoteTest.java +++ b/oap-application/oap-application-test/src/test/java/oap/application/remote/RemoteTest.java @@ -34,6 +34,7 @@ import org.assertj.core.api.Assertions; import org.testng.annotations.Test; +import java.net.ConnectException; import java.net.URL; import java.time.Duration; import java.util.List; @@ -80,7 +81,10 @@ public void invoke() { assertThat( kernel.service( "*.remote-client-unreachable" ) ) .isPresent() .get() - .satisfies( remote -> assertThatThrownBy( remote::accessible ).isInstanceOf( IllegalArgumentException.class ) ); + .satisfies( remote -> assertThatThrownBy( remote::accessible ) + .isInstanceOf( RuntimeException.class ) + .hasCauseInstanceOf( ConnectException.class ) + ); } } diff --git a/oap-application/oap-application/src/main/java/oap/application/remote/RemoteInvocationHandler.java b/oap-application/oap-application/src/main/java/oap/application/remote/RemoteInvocationHandler.java index be8801442..72cb0cca1 100644 --- a/oap-application/oap-application/src/main/java/oap/application/remote/RemoteInvocationHandler.java +++ b/oap-application/oap-application/src/main/java/oap/application/remote/RemoteInvocationHandler.java @@ -59,6 +59,7 @@ import java.util.Iterator; import java.util.List; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; import java.util.concurrent.CompletionStage; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; @@ -120,8 +121,7 @@ public static Object proxy( String source, RemoteLocation remote, Class clazz } private static Object proxy( String source, URI uri, String service, Class clazz, long timeout ) { - return Proxy.newProxyInstance( clazz.getClassLoader(), new Class[] { clazz }, - new RemoteInvocationHandler( source, uri, service, timeout ) ); + return Proxy.newProxyInstance( clazz.getClassLoader(), new Class[] { clazz }, new RemoteInvocationHandler( source, uri, service, timeout ) ); } @Nonnull @@ -153,7 +153,11 @@ public Object invoke( Object proxy, Method method, Object[] args ) throws Throwa if( result.isSuccess() ) { return result.successValue; } else { - throw result.failureValue; + if( result.failureValue instanceof RuntimeException ) { + throw result.failureValue; + } else { + throw new RuntimeException( result.failureValue ); + } } } @@ -183,7 +187,6 @@ private Result invoke( Method method, Object[] args ) { request.send( inputStreamResponseListener ); if( async ) { - responseAsync = CompletableFuture.supplyAsync( () -> { try { return inputStreamResponseListener.get( timeout + 10, TimeUnit.MILLISECONDS ); @@ -196,6 +199,8 @@ private Result invoke( Method method, Object[] args ) { responseAsync = new CompletableFuture<>(); try { responseAsync.complete( inputStreamResponseListener.get( timeout + 10, TimeUnit.MILLISECONDS ) ); + } catch( ExecutionException e ) { + responseAsync.completeExceptionally( e.getCause() ); } catch( Exception e ) { responseAsync.completeExceptionally( e ); } @@ -220,7 +225,7 @@ private Result invoke( Method method, Object[] args ) { } errorMetrics.increment(); - return async ? CompletableFuture.>failedStage( throwable ) : CompletableFuture.completedStage( Result.failure( throwable ) ); + return async ? CompletableFuture.failedStage( throwable ) : CompletableFuture.completedStage( Result.failure( throwable ) ); } finally { dis.close(); } @@ -269,7 +274,11 @@ private Result invoke( Method method, Object[] args ) { if( async ) { return Result.success( ret.thenApply( r -> r.successValue ) ); } else { - return ret.get(); + try { + return ret.join(); + } catch( CompletionException e ) { + throw ( Exception ) e.getCause(); + } } } catch( Exception e ) { if( async ) { diff --git a/oap-http/oap-http-test/src/main/java/oap/http/test/HttpAsserts.java b/oap-http/oap-http-test/src/main/java/oap/http/test/HttpAsserts.java index 39b69bb12..ffe7de280 100644 --- a/oap-http/oap-http-test/src/main/java/oap/http/test/HttpAsserts.java +++ b/oap-http/oap-http-test/src/main/java/oap/http/test/HttpAsserts.java @@ -66,6 +66,7 @@ import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; +import java.util.function.Function; import java.util.regex.Pattern; import static java.net.HttpURLConnection.HTTP_OK; @@ -485,6 +486,10 @@ public T unmarshal( Class clazz ) { public Asserts.StringAssertion body() { return assertString( response.contentString() ); } + + public Asserts.StringAssertion body( Function conv ) { + return assertString( conv.apply( response.content() ) ); + } } public static final class CookiesHttpAssertion { diff --git a/oap-http/oap-http-test/src/test/java/oap/http/GzipHttpTest.java b/oap-http/oap-http-test/src/test/java/oap/http/GzipHttpTest.java index c8943debd..25c983d6f 100644 --- a/oap-http/oap-http-test/src/test/java/oap/http/GzipHttpTest.java +++ b/oap-http/oap-http-test/src/test/java/oap/http/GzipHttpTest.java @@ -25,13 +25,16 @@ package oap.http; import oap.http.server.nio.NioHttpServer; +import oap.io.IoStreams; import oap.io.content.ContentWriter; import oap.testng.Fixtures; import oap.testng.Ports; +import org.eclipse.jetty.client.HttpClient; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; +import java.io.ByteArrayInputStream; import java.util.Map; import static oap.compression.Compression.ContentWriter.ofGzip; @@ -55,7 +58,7 @@ public void afterMethod() { } @Test - public void gzipOutput() { + public void gzipOutput() throws Exception { server.bind( "test", exchange -> exchange.responseOk( "test", true, TEXT_PLAIN ) ); @@ -67,12 +70,19 @@ public void gzipOutput() { .body() .isEqualTo( "test" ); - assertGet( "http://localhost:" + server.defaultPort.httpPort + "/test", Map.of(), Map.of( ACCEPT_ENCODING, "gzip,deflate" ) ) - .isOk() - .hasContentType( TEXT_PLAIN ) - .containsHeader( "Accept-Encoding", "gzip,deflate" ) - .body() - .isEqualTo( "test" ); + try( HttpClient httpClient = new HttpClient() ) { + httpClient.start(); + + // auto-decompression + httpClient.getContentDecoderFactories().clear(); + + assertGet( httpClient, "http://localhost:" + server.defaultPort.httpPort + "/test", Map.of(), Map.of( ACCEPT_ENCODING, "gzip,deflate" ) ) + .isOk() + .hasContentType( TEXT_PLAIN ) + .containsHeader( "Content-Encoding", "gzip" ) + .body( bytes -> IoStreams.asString( new ByteArrayInputStream( bytes ), IoStreams.Encoding.GZIP ) ) + .isEqualTo( "test" ); + } } @Test diff --git a/oap-stdlib/src/main/java/oap/compression/CompressionUtils.java b/oap-stdlib/src/main/java/oap/compression/CompressionUtils.java deleted file mode 100644 index 0c13e68b1..000000000 --- a/oap-stdlib/src/main/java/oap/compression/CompressionUtils.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) Open Application Platform Authors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package oap.compression; - -import org.apache.commons.io.IOUtils; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.zip.GZIPInputStream; -import java.util.zip.GZIPOutputStream; - -import static java.nio.charset.StandardCharsets.UTF_8; - -/** - * @see Compression - */ -@Deprecated -public class CompressionUtils { - private CompressionUtils() { - } - - public static byte[] gzip( String content ) throws IOException { - var baos = new ByteArrayOutputStream(); - try( var gos = new GZIPOutputStream( baos ) ) { - gos.write( content.getBytes() ); - } - - return baos.toByteArray(); - } - - public static String ungzip( byte[] content ) throws IOException { - var bais = new ByteArrayInputStream( content ); - var baos = new ByteArrayOutputStream(); - try( var gos = new GZIPInputStream( bais ) ) { - IOUtils.copy( gos, baos ); - } - - return baos.toString( UTF_8 ); - } - - public static String ungzip( byte[] data, int offset, int dataLength ) throws IOException { - var bais = new ByteArrayInputStream( data, offset, dataLength ); - var baos = new ByteArrayOutputStream(); - try( var gos = new GZIPInputStream( bais ) ) { - IOUtils.copy( gos, baos ); - } - - return baos.toString( UTF_8 ); - } -} diff --git a/oap-ws/oap-ws-test/src/test/java/oap/ws/WebServicesTest.java b/oap-ws/oap-ws-test/src/test/java/oap/ws/WebServicesTest.java index e3cb7113b..ec095e8eb 100644 --- a/oap-ws/oap-ws-test/src/test/java/oap/ws/WebServicesTest.java +++ b/oap-ws/oap-ws-test/src/test/java/oap/ws/WebServicesTest.java @@ -143,7 +143,7 @@ public void invocationString() { .responded( OK, "OK", APPLICATION_JSON, "\"1234\"" ); HttpAsserts.assertPost( kernel.httpUrl( "/x/v/math/string" ), "1234", Http.ContentType.APPLICATION_OCTET_STREAM ) .satisfies( response -> assertThat( response.headers ) - .contains( __( "content-type", "application/json" ) ) ); + .contains( __( "Content-Type", "application/json" ) ) ); } @Test From 1864b99943eb37b3157146af0b03cd2194feb778 Mon Sep 17 00:00:00 2001 From: "igor.petrenko" Date: Mon, 9 Feb 2026 08:32:58 +0200 Subject: [PATCH 09/39] CE-135 okhttp -> jetty http client --- .../src/test/java/oap/http/pniov3/PnioServerTest.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/oap-http/oap-pnio-v3/src/test/java/oap/http/pniov3/PnioServerTest.java b/oap-http/oap-pnio-v3/src/test/java/oap/http/pniov3/PnioServerTest.java index 8e8d4a516..aa8936dde 100644 --- a/oap-http/oap-pnio-v3/src/test/java/oap/http/pniov3/PnioServerTest.java +++ b/oap-http/oap-pnio-v3/src/test/java/oap/http/pniov3/PnioServerTest.java @@ -1,5 +1,6 @@ package oap.http.pniov3; +import oap.http.Http; import oap.http.server.nio.NioHttpServer; import oap.testng.Fixtures; import oap.util.Dates; @@ -9,6 +10,7 @@ import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import static oap.http.test.HttpAsserts.assertGet; @@ -26,6 +28,7 @@ public void testRequestUndertow() throws Exception { try( ExecutorService threadPoolExecutor = Executors.newVirtualThreadPerTaskExecutor() ) { try( HttpClient httpClient = new HttpClient() ) { + httpClient.setExecutor( threadPoolExecutor ); httpClient.setMaxConnectionsPerDestination( 20000 ); httpClient.start(); @@ -45,7 +48,8 @@ public void testRequestUndertow() throws Exception { for( int i = 0; i < 20; i++ ) { threadPoolExecutor.execute( () -> { try { - assertGet( httpClient, "http://localhost:" + port + "/pnio?trace=true", Map.of(), Map.of() ); + assertGet( httpClient, "http://localhost:" + port + "/pnio?trace=true", Map.of(), Map.of() ) + .hasCode( Http.StatusCode.NO_CONTENT ); okCount.incrementAndGet(); } catch( Exception e ) { errorCount.incrementAndGet(); @@ -56,6 +60,8 @@ public void testRequestUndertow() throws Exception { System.out.println( "ok " + okCount.get() + " error " + errorCount.get() + " duration " + Dates.durationToString( System.currentTimeMillis() - start ) ); } } + threadPoolExecutor.shutdown(); + threadPoolExecutor.awaitTermination( 20, TimeUnit.SECONDS ); } } } From 9dc4197d8b239d4268d274d19d7c5cd7a192ca9a Mon Sep 17 00:00:00 2001 From: "igor.petrenko" Date: Mon, 9 Feb 2026 08:35:24 +0200 Subject: [PATCH 10/39] CE-135 okhttp -> jetty http client --- .../src/test/java/oap/http/GzipHttpTest.java | 2 + .../KeepaliveRequestsHandlerTest.java | 4 +- .../main/java/oap/http/client/HttpClient.java | 93 ------------------- .../oap/message/client/MessageSender.java | 6 +- 4 files changed, 8 insertions(+), 97 deletions(-) delete mode 100644 oap-http/oap-http/src/main/java/oap/http/client/HttpClient.java diff --git a/oap-http/oap-http-test/src/test/java/oap/http/GzipHttpTest.java b/oap-http/oap-http-test/src/test/java/oap/http/GzipHttpTest.java index 25c983d6f..eb4d68208 100644 --- a/oap-http/oap-http-test/src/test/java/oap/http/GzipHttpTest.java +++ b/oap-http/oap-http-test/src/test/java/oap/http/GzipHttpTest.java @@ -36,6 +36,7 @@ import java.io.ByteArrayInputStream; import java.util.Map; +import java.util.concurrent.Executors; import static oap.compression.Compression.ContentWriter.ofGzip; import static oap.http.Http.ContentType.TEXT_PLAIN; @@ -71,6 +72,7 @@ public void gzipOutput() throws Exception { .isEqualTo( "test" ); try( HttpClient httpClient = new HttpClient() ) { + httpClient.setExecutor( Executors.newVirtualThreadPerTaskExecutor() ); httpClient.start(); // auto-decompression diff --git a/oap-http/oap-http-test/src/test/java/oap/http/server/nio/handlers/KeepaliveRequestsHandlerTest.java b/oap-http/oap-http-test/src/test/java/oap/http/server/nio/handlers/KeepaliveRequestsHandlerTest.java index c50f9fdef..583a317e0 100644 --- a/oap-http/oap-http-test/src/test/java/oap/http/server/nio/handlers/KeepaliveRequestsHandlerTest.java +++ b/oap-http/oap-http-test/src/test/java/oap/http/server/nio/handlers/KeepaliveRequestsHandlerTest.java @@ -9,6 +9,7 @@ import java.util.LinkedHashSet; import java.util.Map; +import java.util.concurrent.Executors; import static oap.http.test.HttpAsserts.assertGet; import static oap.testng.Asserts.assertEventually; @@ -27,6 +28,7 @@ public void testCloseConnectionBlocking() throws Exception { LinkedHashSet ids = new LinkedHashSet<>(); try( NioHttpServer httpServer = new NioHttpServer( new NioHttpServer.DefaultPort( testHttpPort ) ); HttpClient client = new HttpClient() ) { + client.setExecutor( Executors.newVirtualThreadPerTaskExecutor() ); client.setMaxConnectionsPerDestination( 10 ); client.start(); @@ -58,7 +60,7 @@ public void testCloseConnectionAsync() throws Exception { LinkedHashSet ids = new LinkedHashSet<>(); try( NioHttpServer httpServer = new NioHttpServer( new NioHttpServer.DefaultPort( testHttpPort ) ); HttpClient client = new HttpClient() ) { - + client.setExecutor( Executors.newVirtualThreadPerTaskExecutor() ); client.setMaxConnectionsPerDestination( 10 ); client.start(); diff --git a/oap-http/oap-http/src/main/java/oap/http/client/HttpClient.java b/oap-http/oap-http/src/main/java/oap/http/client/HttpClient.java deleted file mode 100644 index b4d2e222d..000000000 --- a/oap-http/oap-http/src/main/java/oap/http/client/HttpClient.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) Open Application Platform Authors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package oap.http.client; - -import lombok.SneakyThrows; -import lombok.extern.slf4j.Slf4j; -import oap.io.IoStreams; - -import javax.net.ssl.SSLContext; -import javax.net.ssl.TrustManager; -import javax.net.ssl.TrustManagerFactory; -import javax.net.ssl.X509TrustManager; -import java.nio.file.Path; -import java.security.KeyManagementException; -import java.security.KeyStore; -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; -import java.security.cert.X509Certificate; - -import static oap.io.IoStreams.Encoding.PLAIN; - -@Slf4j -public final class HttpClient { - private java.net.http.HttpClient impl; - - private HttpClient() { - try { - SSLContext sslContext = SSLContext.getInstance( "TLS" ); - sslContext.init( null, new TrustManager[] { ACCEPTING_TRUST_MANAGER }, new SecureRandom() ); - impl = java.net.http.HttpClient.newBuilder() - .version( java.net.http.HttpClient.Version.HTTP_2 ) - .followRedirects( java.net.http.HttpClient.Redirect.ALWAYS ) - .sslContext( sslContext ) - .build(); - } catch( NoSuchAlgorithmException | KeyManagementException e ) { - log.error( e.getMessage(), e ); - } - } - - public static final HttpClient DEFAULT = new HttpClient(); - public static final X509TrustManager ACCEPTING_TRUST_MANAGER = new X509TrustManager() { - @Override - public void checkClientTrusted( X509Certificate[] x509Certificates, String s ) { - } - - @Override - public void checkServerTrusted( X509Certificate[] x509Certificates, String s ) { - } - - @Override - public X509Certificate[] getAcceptedIssuers() { - return new X509Certificate[0]; - } - }; - - @SneakyThrows - public static SSLContext createSSLContext( Path certificateLocation, String certificatePassword ) { - try( var inputStream = IoStreams.in( certificateLocation, PLAIN ) ) { - KeyStore keyStore = KeyStore.getInstance( "JKS" ); - keyStore.load( inputStream, certificatePassword.toCharArray() ); - - TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance( TrustManagerFactory.getDefaultAlgorithm() ); - trustManagerFactory.init( keyStore ); - - SSLContext sslContext = SSLContext.getInstance( "TLS" ); - sslContext.init( null, trustManagerFactory.getTrustManagers(), null ); - - return sslContext; - } - } -} diff --git a/oap-message/oap-message-client/src/main/java/oap/message/client/MessageSender.java b/oap-message/oap-message-client/src/main/java/oap/message/client/MessageSender.java index 6da6841dc..a8bc4c5cf 100644 --- a/oap-message/oap-message-client/src/main/java/oap/message/client/MessageSender.java +++ b/oap-message/oap-message-client/src/main/java/oap/message/client/MessageSender.java @@ -157,14 +157,14 @@ public void start() { uniqueName, Dates.durationToString( retryTimeout ), Dates.durationToString( diskSyncPeriod ), Dates.durationToString( memorySyncPeriod ) ); log.info( "custom status = {}", MessageProtocol.printMapping() ); - executor = Executors.newVirtualThreadPerTaskExecutor(); + ThreadFactory threadFactory = Thread.ofVirtual().name( "messages-", 0 ).factory(); + executor = Executors.newThreadPerTaskExecutor( threadFactory ); httpClient = new HttpClient(); httpClient.setConnectTimeout( connectionTimeout ); httpClient.setMaxConnectionsPerDestination( poolSize ); - ThreadFactory threadFactory = Thread.ofVirtual().name( "messages-", 0 ).factory(); - httpClient.setExecutor( Executors.newThreadPerTaskExecutor( threadFactory ) ); + httpClient.setExecutor( executor ); httpClient.start(); if( diskSyncPeriod > 0 ) From 3840d511a7b470f5fa53222a5941415083ae43a7 Mon Sep 17 00:00:00 2001 From: "igor.petrenko" Date: Mon, 9 Feb 2026 08:51:56 +0200 Subject: [PATCH 11/39] CE-135 okhttp -> jetty http client --- .../src/test/java/oap/http/pniov3/PnioServerTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/oap-http/oap-pnio-v3/src/test/java/oap/http/pniov3/PnioServerTest.java b/oap-http/oap-pnio-v3/src/test/java/oap/http/pniov3/PnioServerTest.java index aa8936dde..34b739493 100644 --- a/oap-http/oap-pnio-v3/src/test/java/oap/http/pniov3/PnioServerTest.java +++ b/oap-http/oap-pnio-v3/src/test/java/oap/http/pniov3/PnioServerTest.java @@ -28,8 +28,8 @@ public void testRequestUndertow() throws Exception { try( ExecutorService threadPoolExecutor = Executors.newVirtualThreadPerTaskExecutor() ) { try( HttpClient httpClient = new HttpClient() ) { - httpClient.setExecutor( threadPoolExecutor ); - httpClient.setMaxConnectionsPerDestination( 20000 ); + httpClient.setExecutor( Executors.newVirtualThreadPerTaskExecutor() ); + httpClient.setMaxConnectionsPerDestination( 2000 ); httpClient.start(); AtomicInteger errorCount = new AtomicInteger(); From 9af3119c64fbd06d2b82532143c4f2f0817f6e77 Mon Sep 17 00:00:00 2001 From: "igor.petrenko" Date: Mon, 9 Feb 2026 09:21:58 +0200 Subject: [PATCH 12/39] CE-135 okhttp -> jetty http client --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 16641e502..58102dbab 100644 --- a/pom.xml +++ b/pom.xml @@ -99,7 +99,7 @@ 4.13.0 9.8 - 2.3.19.Final + 2.3.23.Final 3.23.3 3.3.4 From 7b41708c78adcc56c82eaa88e7192cb19df16274 Mon Sep 17 00:00:00 2001 From: "igor.petrenko" Date: Mon, 9 Feb 2026 09:36:23 +0200 Subject: [PATCH 13/39] CE-135 okhttp -> jetty http client --- .../main/java/oap/http/server/nio/HttpServerExchange.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/oap-http/oap-http/src/main/java/oap/http/server/nio/HttpServerExchange.java b/oap-http/oap-http/src/main/java/oap/http/server/nio/HttpServerExchange.java index 5c7f7251c..8cb032d81 100644 --- a/oap-http/oap-http/src/main/java/oap/http/server/nio/HttpServerExchange.java +++ b/oap-http/oap-http/src/main/java/oap/http/server/nio/HttpServerExchange.java @@ -319,16 +319,20 @@ public HttpServerExchange setResponseCookie( oap.http.Cookie cookie ) { } private Cookie convert( oap.http.Cookie cookie ) { - return new CookieImpl( cookie.getName(), cookie.getValue() ) + CookieImpl newCookie = new CookieImpl( cookie.getName(), cookie.getValue() ); + newCookie .setPath( cookie.getPath() ) .setDomain( cookie.getDomain() ) - .setMaxAge( cookie.getMaxAge() ) .setExpires( cookie.getExpires() ) .setDiscard( cookie.isDiscard() ) .setSecure( cookie.isSecure() ) .setHttpOnly( cookie.isHttpOnly() ) .setVersion( cookie.getVersion() ) .setComment( cookie.getComment() ); + if( cookie.getMaxAge() != null ) { + newCookie.setMaxAge( cookie.getMaxAge() ); + } + return newCookie; } public Iterable responseCookies() { From d6d82c96c49f60f5015ff820b6fc79b70f28156c Mon Sep 17 00:00:00 2001 From: "igor.petrenko" Date: Mon, 9 Feb 2026 11:05:52 +0200 Subject: [PATCH 14/39] CE-135 okhttp -> jetty http client --- .../remote/RemoteInvocationHandler.java | 16 ++++++---------- .../main/java/oap/http/test/HttpAsserts.java | 12 +++++++----- .../src/test/java/oap/http/GzipHttpTest.java | 7 +++++-- .../KeepaliveRequestsHandlerTest.java | 11 ++++++++--- .../java/oap/http/pniov3/PnioServerTest.java | 19 ++++++++++++++++++- .../oap/message/client/MessageSender.java | 15 ++++++--------- 6 files changed, 50 insertions(+), 30 deletions(-) diff --git a/oap-application/oap-application/src/main/java/oap/application/remote/RemoteInvocationHandler.java b/oap-application/oap-application/src/main/java/oap/application/remote/RemoteInvocationHandler.java index 72cb0cca1..079a42fce 100644 --- a/oap-application/oap-application/src/main/java/oap/application/remote/RemoteInvocationHandler.java +++ b/oap-application/oap-application/src/main/java/oap/application/remote/RemoteInvocationHandler.java @@ -40,6 +40,8 @@ import org.eclipse.jetty.client.Request; import org.eclipse.jetty.client.Response; import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.util.thread.QueuedThreadPool; +import org.eclipse.jetty.util.thread.VirtualThreadPool; import javax.annotation.Nonnull; import java.io.BufferedInputStream; @@ -62,9 +64,6 @@ import java.util.concurrent.CompletionException; import java.util.concurrent.CompletionStage; import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; @@ -74,15 +73,12 @@ public final class RemoteInvocationHandler implements InvocationHandler { private static final HttpClient globalClient; - private static ExecutorService executor; - static { try { - ThreadFactory threadFactory = Thread.ofVirtual().name( "RemoteInvocationHandler-", 0 ).factory(); - globalClient = new HttpClient(); - executor = Executors.newThreadPerTaskExecutor( threadFactory ); - globalClient.setExecutor( executor ); + QueuedThreadPool qtp = new QueuedThreadPool(); + qtp.setVirtualThreadsExecutor( new VirtualThreadPool() ); + globalClient.setExecutor( qtp ); globalClient.start(); } catch( Exception e ) { throw new RuntimeException( e ); @@ -193,7 +189,7 @@ private Result invoke( Method method, Object[] args ) { } catch( InterruptedException | TimeoutException | ExecutionException e ) { throw new RuntimeException( e ); } - }, executor ); + }, ( ( QueuedThreadPool ) globalClient.getExecutor() ).getVirtualThreadsExecutor() ); } else { responseAsync = new CompletableFuture<>(); diff --git a/oap-http/oap-http-test/src/main/java/oap/http/test/HttpAsserts.java b/oap-http/oap-http-test/src/main/java/oap/http/test/HttpAsserts.java index ffe7de280..b1cb75955 100644 --- a/oap-http/oap-http-test/src/main/java/oap/http/test/HttpAsserts.java +++ b/oap-http/oap-http-test/src/main/java/oap/http/test/HttpAsserts.java @@ -35,6 +35,7 @@ import oap.json.testng.JsonAsserts; import oap.testng.Asserts; import oap.util.BiStream; +import oap.util.Dates; import oap.util.Lists; import oap.util.Maps; import oap.util.Pair; @@ -49,6 +50,8 @@ import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.util.thread.QueuedThreadPool; +import org.eclipse.jetty.util.thread.VirtualThreadPool; import org.joda.time.DateTime; import org.testng.internal.collections.Ints; @@ -62,8 +65,6 @@ import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.concurrent.Executors; -import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import java.util.function.Function; @@ -88,10 +89,11 @@ public class HttpAsserts { static { try { - ThreadFactory threadFactory = Thread.ofVirtual().name( "HttpAsserts-", 0 ).factory(); - HTTP_CLIENT = new HttpClient(); - HTTP_CLIENT.setExecutor( Executors.newThreadPerTaskExecutor( threadFactory ) ); + HTTP_CLIENT.setConnectTimeout( Dates.s( 10 ) ); + QueuedThreadPool qtp = new QueuedThreadPool(); + qtp.setVirtualThreadsExecutor( new VirtualThreadPool() ); + HTTP_CLIENT.setExecutor( qtp ); HTTP_CLIENT.setFollowRedirects( false ); HTTP_CLIENT.start(); } catch( Exception e ) { diff --git a/oap-http/oap-http-test/src/test/java/oap/http/GzipHttpTest.java b/oap-http/oap-http-test/src/test/java/oap/http/GzipHttpTest.java index eb4d68208..dd6f07c12 100644 --- a/oap-http/oap-http-test/src/test/java/oap/http/GzipHttpTest.java +++ b/oap-http/oap-http-test/src/test/java/oap/http/GzipHttpTest.java @@ -30,13 +30,14 @@ import oap.testng.Fixtures; import oap.testng.Ports; import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.util.thread.QueuedThreadPool; +import org.eclipse.jetty.util.thread.VirtualThreadPool; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.io.ByteArrayInputStream; import java.util.Map; -import java.util.concurrent.Executors; import static oap.compression.Compression.ContentWriter.ofGzip; import static oap.http.Http.ContentType.TEXT_PLAIN; @@ -72,7 +73,9 @@ public void gzipOutput() throws Exception { .isEqualTo( "test" ); try( HttpClient httpClient = new HttpClient() ) { - httpClient.setExecutor( Executors.newVirtualThreadPerTaskExecutor() ); + QueuedThreadPool qtp = new QueuedThreadPool(); + qtp.setVirtualThreadsExecutor( new VirtualThreadPool() ); + httpClient.setExecutor( qtp ); httpClient.start(); // auto-decompression diff --git a/oap-http/oap-http-test/src/test/java/oap/http/server/nio/handlers/KeepaliveRequestsHandlerTest.java b/oap-http/oap-http-test/src/test/java/oap/http/server/nio/handlers/KeepaliveRequestsHandlerTest.java index 583a317e0..17c6f4035 100644 --- a/oap-http/oap-http-test/src/test/java/oap/http/server/nio/handlers/KeepaliveRequestsHandlerTest.java +++ b/oap-http/oap-http-test/src/test/java/oap/http/server/nio/handlers/KeepaliveRequestsHandlerTest.java @@ -5,11 +5,12 @@ import oap.testng.Fixtures; import oap.testng.Ports; import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.util.thread.QueuedThreadPool; +import org.eclipse.jetty.util.thread.VirtualThreadPool; import org.testng.annotations.Test; import java.util.LinkedHashSet; import java.util.Map; -import java.util.concurrent.Executors; import static oap.http.test.HttpAsserts.assertGet; import static oap.testng.Asserts.assertEventually; @@ -28,7 +29,9 @@ public void testCloseConnectionBlocking() throws Exception { LinkedHashSet ids = new LinkedHashSet<>(); try( NioHttpServer httpServer = new NioHttpServer( new NioHttpServer.DefaultPort( testHttpPort ) ); HttpClient client = new HttpClient() ) { - client.setExecutor( Executors.newVirtualThreadPerTaskExecutor() ); + QueuedThreadPool qtp = new QueuedThreadPool(); + qtp.setVirtualThreadsExecutor( new VirtualThreadPool() ); + client.setExecutor( qtp ); client.setMaxConnectionsPerDestination( 10 ); client.start(); @@ -60,7 +63,9 @@ public void testCloseConnectionAsync() throws Exception { LinkedHashSet ids = new LinkedHashSet<>(); try( NioHttpServer httpServer = new NioHttpServer( new NioHttpServer.DefaultPort( testHttpPort ) ); HttpClient client = new HttpClient() ) { - client.setExecutor( Executors.newVirtualThreadPerTaskExecutor() ); + QueuedThreadPool qtp = new QueuedThreadPool(); + qtp.setVirtualThreadsExecutor( new VirtualThreadPool() ); + client.setExecutor( qtp ); client.setMaxConnectionsPerDestination( 10 ); client.start(); diff --git a/oap-http/oap-pnio-v3/src/test/java/oap/http/pniov3/PnioServerTest.java b/oap-http/oap-pnio-v3/src/test/java/oap/http/pniov3/PnioServerTest.java index 34b739493..a9108bc4a 100644 --- a/oap-http/oap-pnio-v3/src/test/java/oap/http/pniov3/PnioServerTest.java +++ b/oap-http/oap-pnio-v3/src/test/java/oap/http/pniov3/PnioServerTest.java @@ -5,6 +5,8 @@ import oap.testng.Fixtures; import oap.util.Dates; import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.util.thread.QueuedThreadPool; +import org.eclipse.jetty.util.thread.VirtualThreadPool; import org.testng.annotations.Test; import java.util.Map; @@ -28,8 +30,11 @@ public void testRequestUndertow() throws Exception { try( ExecutorService threadPoolExecutor = Executors.newVirtualThreadPerTaskExecutor() ) { try( HttpClient httpClient = new HttpClient() ) { - httpClient.setExecutor( Executors.newVirtualThreadPerTaskExecutor() ); + QueuedThreadPool qtp = new QueuedThreadPool(); + qtp.setVirtualThreadsExecutor( new VirtualThreadPool() ); + httpClient.setExecutor( qtp ); httpClient.setMaxConnectionsPerDestination( 2000 ); + httpClient.setConnectTimeout( Dates.s( 10 ) ); httpClient.start(); AtomicInteger errorCount = new AtomicInteger(); @@ -59,6 +64,18 @@ public void testRequestUndertow() throws Exception { System.out.println( "ok " + okCount.get() + " error " + errorCount.get() + " duration " + Dates.durationToString( System.currentTimeMillis() - start ) ); } + + for( int i = 0; i < 20; i++ ) { +// threadPoolExecutor.execute( () -> { + try { + assertGet( httpClient, "http://localhost:" + port + "/pnio?trace=true", Map.of(), Map.of() ) + .hasCode( Http.StatusCode.NO_CONTENT ); + okCount.incrementAndGet(); + } catch( Exception e ) { + errorCount.incrementAndGet(); + } +// } ); + } } threadPoolExecutor.shutdown(); threadPoolExecutor.awaitTermination( 20, TimeUnit.SECONDS ); diff --git a/oap-message/oap-message-client/src/main/java/oap/message/client/MessageSender.java b/oap-message/oap-message-client/src/main/java/oap/message/client/MessageSender.java index a8bc4c5cf..049a2aa2d 100644 --- a/oap-message/oap-message-client/src/main/java/oap/message/client/MessageSender.java +++ b/oap-message/oap-message-client/src/main/java/oap/message/client/MessageSender.java @@ -56,6 +56,8 @@ import org.eclipse.jetty.client.InputStreamResponseListener; import org.eclipse.jetty.client.Response; import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.util.thread.QueuedThreadPool; +import org.eclipse.jetty.util.thread.VirtualThreadPool; import org.joda.time.DateTimeUtils; import org.slf4j.event.Level; @@ -73,9 +75,6 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.locks.ReentrantLock; @@ -108,7 +107,6 @@ public class MessageSender implements Closeable, AutoCloseable { private Scheduled diskSyncScheduler; private boolean networkAvailable = true; private HttpClient httpClient; - private ExecutorService executor; private long ioExceptionStartRetryTimeout = -1; public MessageSender( String host, int port, String httpPrefix, Path persistenceDirectory, long memorySyncPeriod ) { @@ -157,14 +155,13 @@ public void start() { uniqueName, Dates.durationToString( retryTimeout ), Dates.durationToString( diskSyncPeriod ), Dates.durationToString( memorySyncPeriod ) ); log.info( "custom status = {}", MessageProtocol.printMapping() ); - ThreadFactory threadFactory = Thread.ofVirtual().name( "messages-", 0 ).factory(); - executor = Executors.newThreadPerTaskExecutor( threadFactory ); - httpClient = new HttpClient(); httpClient.setConnectTimeout( connectionTimeout ); httpClient.setMaxConnectionsPerDestination( poolSize ); - httpClient.setExecutor( executor ); + QueuedThreadPool qtp = new QueuedThreadPool(); + qtp.setVirtualThreadsExecutor( new VirtualThreadPool() ); + httpClient.setExecutor( qtp ); httpClient.start(); if( diskSyncPeriod > 0 ) @@ -312,7 +309,7 @@ public CompletableFuture send( Messages.MessageInfo messag throw Throwables.propagate( e ); } - }, executor ); + }, ( ( QueuedThreadPool ) httpClient.getExecutor() ).getVirtualThreadsExecutor() ); } private void processException( Messages.MessageInfo messageInfo, long now, Message message, Throwable e, boolean globalRetryTimeout ) { From 42b8f57f847070dae7a8513b6748e2f0699b8bc0 Mon Sep 17 00:00:00 2001 From: "igor.petrenko" Date: Mon, 9 Feb 2026 11:26:07 +0200 Subject: [PATCH 15/39] CE-135 okhttp -> jetty http client --- .../remote/RemoteInvocationHandler.java | 22 ++------- .../main/java/oap/http/test/HttpAsserts.java | 48 +++++-------------- .../src/test/java/oap/http/GzipHttpTest.java | 10 +--- .../http/server/nio/NioHttpServerTest.java | 3 +- .../KeepaliveRequestsHandlerTest.java | 15 ++---- .../src/main/java/oap/http/client/Client.java | 36 ++++++++++++++ .../java/oap/http/pniov3/PnioServerTest.java | 10 +--- .../oap/message/client/MessageSender.java | 25 ++-------- .../main/resources/META-INF/oap-module.oap | 2 - .../java/oap/message/MessageServerTest.java | 7 --- 10 files changed, 65 insertions(+), 113 deletions(-) create mode 100644 oap-http/oap-http/src/main/java/oap/http/client/Client.java diff --git a/oap-application/oap-application/src/main/java/oap/application/remote/RemoteInvocationHandler.java b/oap-application/oap-application/src/main/java/oap/application/remote/RemoteInvocationHandler.java index 079a42fce..2cf2ab8cc 100644 --- a/oap-application/oap-application/src/main/java/oap/application/remote/RemoteInvocationHandler.java +++ b/oap-application/oap-application/src/main/java/oap/application/remote/RemoteInvocationHandler.java @@ -31,17 +31,15 @@ import lombok.extern.slf4j.Slf4j; import oap.application.ServiceKernelCommand; import oap.application.module.Reference; +import oap.http.client.Client; import oap.util.Result; import oap.util.Stream; import oap.util.function.Try; import org.eclipse.jetty.client.BytesRequestContent; -import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.InputStreamResponseListener; import org.eclipse.jetty.client.Request; import org.eclipse.jetty.client.Response; import org.eclipse.jetty.http.HttpMethod; -import org.eclipse.jetty.util.thread.QueuedThreadPool; -import org.eclipse.jetty.util.thread.VirtualThreadPool; import javax.annotation.Nonnull; import java.io.BufferedInputStream; @@ -71,20 +69,6 @@ @Slf4j public final class RemoteInvocationHandler implements InvocationHandler { - private static final HttpClient globalClient; - - static { - try { - globalClient = new HttpClient(); - QueuedThreadPool qtp = new QueuedThreadPool(); - qtp.setVirtualThreadsExecutor( new VirtualThreadPool() ); - globalClient.setExecutor( qtp ); - globalClient.start(); - } catch( Exception e ) { - throw new RuntimeException( e ); - } - } - private final Counter timeoutMetrics; private final Counter errorMetrics; private final Counter successMetrics; @@ -171,7 +155,7 @@ private Result invoke( Method method, Object[] args ) { boolean async = CompletableFuture.class.isAssignableFrom( method.getReturnType() ); try { - Request request = globalClient + Request request = Client.DEFAULT_HTTP_CLIENT .newRequest( uri ) .method( HttpMethod.POST ) .body( new BytesRequestContent( invocationB ) ) @@ -189,7 +173,7 @@ private Result invoke( Method method, Object[] args ) { } catch( InterruptedException | TimeoutException | ExecutionException e ) { throw new RuntimeException( e ); } - }, ( ( QueuedThreadPool ) globalClient.getExecutor() ).getVirtualThreadsExecutor() ); + }, Client.DEFAULT_VIRTUAL_THREAD_EXECUTOR ); } else { responseAsync = new CompletableFuture<>(); diff --git a/oap-http/oap-http-test/src/main/java/oap/http/test/HttpAsserts.java b/oap-http/oap-http-test/src/main/java/oap/http/test/HttpAsserts.java index b1cb75955..0b6539710 100644 --- a/oap-http/oap-http-test/src/main/java/oap/http/test/HttpAsserts.java +++ b/oap-http/oap-http-test/src/main/java/oap/http/test/HttpAsserts.java @@ -30,12 +30,12 @@ import lombok.extern.slf4j.Slf4j; import oap.http.Cookie; import oap.http.Response; +import oap.http.client.Client; import oap.http.client.JettyRequestExtensions; import oap.json.JsonException; import oap.json.testng.JsonAsserts; import oap.testng.Asserts; import oap.util.BiStream; -import oap.util.Dates; import oap.util.Lists; import oap.util.Maps; import oap.util.Pair; @@ -50,8 +50,6 @@ import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpMethod; -import org.eclipse.jetty.util.thread.QueuedThreadPool; -import org.eclipse.jetty.util.thread.VirtualThreadPool; import org.joda.time.DateTime; import org.testng.internal.collections.Ints; @@ -85,22 +83,6 @@ @Slf4j @SuppressWarnings( "unused" ) public class HttpAsserts { - public static final HttpClient HTTP_CLIENT; - - static { - try { - HTTP_CLIENT = new HttpClient(); - HTTP_CLIENT.setConnectTimeout( Dates.s( 10 ) ); - QueuedThreadPool qtp = new QueuedThreadPool(); - qtp.setVirtualThreadsExecutor( new VirtualThreadPool() ); - HTTP_CLIENT.setExecutor( qtp ); - HTTP_CLIENT.setFollowRedirects( false ); - HTTP_CLIENT.start(); - } catch( Exception e ) { - throw new RuntimeException( e ); - } - } - public static String httpPrefix( int port ) { return "http://localhost:" + port; } @@ -109,19 +91,13 @@ public static String httpUrl( int port, String suffix ) { return httpPrefix( port ) + ( suffix.startsWith( "/" ) ? suffix : "/" + suffix ); } - @SneakyThrows - public static void reset() { - HTTP_CLIENT.stop(); - HTTP_CLIENT.start(); - } - @SafeVarargs public static HttpAssertion assertGet( String uri, Pair... params ) throws UncheckedIOException { return assertGet( uri, Maps.of( params ), Map.of() ); } public static HttpAssertion assertGet( String uri, Map params, Map headers ) throws UncheckedIOException { - return assertGet( HTTP_CLIENT, uri, params, headers ); + return assertGet( Client.DEFAULT_HTTP_CLIENT, uri, params, headers ); } public static HttpAssertion assertGet( HttpClient client, String uri, Map params, Map headers ) throws UncheckedIOException { @@ -133,7 +109,7 @@ public static HttpAssertion assertGet( HttpClient client, String uri, Map headers ) { - return getResponseAsHttpAssertion( HTTP_CLIENT + return getResponseAsHttpAssertion( Client.DEFAULT_HTTP_CLIENT .newRequest( uri ) .method( HttpMethod.POST ) .addHeaders( headers ) @@ -145,7 +121,7 @@ public static HttpAssertion assertPost( String uri, InputStream content, @Nullab } public static HttpAssertion assertPost( String uri, byte[] content, @Nullable String contentType, Map headers ) { - return getResponseAsHttpAssertion( HTTP_CLIENT + return getResponseAsHttpAssertion( Client.DEFAULT_HTTP_CLIENT .newRequest( uri ) .method( HttpMethod.POST ) .addHeaders( headers ) @@ -153,7 +129,7 @@ public static HttpAssertion assertPost( String uri, byte[] content, @Nullable St } public static HttpAssertion assertPost( String uri, String content, @Nullable String contentType, Map headers ) { - return getResponseAsHttpAssertion( HTTP_CLIENT + return getResponseAsHttpAssertion( Client.DEFAULT_HTTP_CLIENT .newRequest( uri ) .method( HttpMethod.POST ) .addHeaders( headers ) @@ -197,7 +173,7 @@ public static HttpAssertion assertPut( String uri, String content, String conten } public static HttpAssertion assertPut( String uri, String content, String contentType, Map headers ) { - return getResponseAsHttpAssertion( HTTP_CLIENT + return getResponseAsHttpAssertion( Client.DEFAULT_HTTP_CLIENT .newRequest( uri ) .method( HttpMethod.PUT ) .addHeaders( headers ) @@ -210,7 +186,7 @@ public static HttpAssertion assertPut( String uri, byte[] content, String conten } public static HttpAssertion assertPut( String uri, byte[] content, String contentType, Map headers ) { - return getResponseAsHttpAssertion( HTTP_CLIENT + return getResponseAsHttpAssertion( Client.DEFAULT_HTTP_CLIENT .newRequest( uri ) .method( HttpMethod.PUT ) .addHeaders( headers ) @@ -223,7 +199,7 @@ public static HttpAssertion assertPut( String uri, InputStream is, String conten } public static HttpAssertion assertPut( String uri, InputStream is, String contentType, Map headers ) { - return getResponseAsHttpAssertion( HTTP_CLIENT + return getResponseAsHttpAssertion( Client.DEFAULT_HTTP_CLIENT .newRequest( uri ) .method( HttpMethod.PUT ) .addHeaders( headers ) @@ -236,7 +212,7 @@ public static HttpAssertion assertPatch( String uri, byte[] content, String cont } public static HttpAssertion assertPatch( String uri, byte[] content, String contentType, Map headers ) { - return getResponseAsHttpAssertion( HTTP_CLIENT + return getResponseAsHttpAssertion( Client.DEFAULT_HTTP_CLIENT .newRequest( uri ) .method( HttpMethod.PATCH ) .addHeaders( headers ) @@ -249,7 +225,7 @@ public static HttpAssertion assertPatch( String uri, String content, String cont } public static HttpAssertion assertPatch( String uri, String content, String contentType, Map headers ) { - return getResponseAsHttpAssertion( HTTP_CLIENT + return getResponseAsHttpAssertion( Client.DEFAULT_HTTP_CLIENT .newRequest( uri ) .method( HttpMethod.PATCH ) .addHeaders( headers ) @@ -263,7 +239,7 @@ public static HttpAssertion assertPatch( String uri, InputStream is, String cont public static HttpAssertion assertPatch( String uri, InputStream is, String contentType, Map headers ) { - return getResponseAsHttpAssertion( HTTP_CLIENT + return getResponseAsHttpAssertion( Client.DEFAULT_HTTP_CLIENT .newRequest( uri ) .method( HttpMethod.PATCH ) .addHeaders( headers ) @@ -272,7 +248,7 @@ public static HttpAssertion assertPatch( String uri, InputStream is, String cont } public static HttpAssertion assertDelete( String uri, Map headers ) { - return getResponseAsHttpAssertion( HTTP_CLIENT + return getResponseAsHttpAssertion( Client.DEFAULT_HTTP_CLIENT .newRequest( uri ) .method( HttpMethod.DELETE ) .addHeaders( headers ) diff --git a/oap-http/oap-http-test/src/test/java/oap/http/GzipHttpTest.java b/oap-http/oap-http-test/src/test/java/oap/http/GzipHttpTest.java index dd6f07c12..4e0d0b466 100644 --- a/oap-http/oap-http-test/src/test/java/oap/http/GzipHttpTest.java +++ b/oap-http/oap-http-test/src/test/java/oap/http/GzipHttpTest.java @@ -24,14 +24,13 @@ package oap.http; +import oap.http.client.Client; import oap.http.server.nio.NioHttpServer; import oap.io.IoStreams; import oap.io.content.ContentWriter; import oap.testng.Fixtures; import oap.testng.Ports; import org.eclipse.jetty.client.HttpClient; -import org.eclipse.jetty.util.thread.QueuedThreadPool; -import org.eclipse.jetty.util.thread.VirtualThreadPool; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; @@ -72,12 +71,7 @@ public void gzipOutput() throws Exception { .body() .isEqualTo( "test" ); - try( HttpClient httpClient = new HttpClient() ) { - QueuedThreadPool qtp = new QueuedThreadPool(); - qtp.setVirtualThreadsExecutor( new VirtualThreadPool() ); - httpClient.setExecutor( qtp ); - httpClient.start(); - + try( HttpClient httpClient = Client.customHttpClient() ) { // auto-decompression httpClient.getContentDecoderFactories().clear(); diff --git a/oap-http/oap-http-test/src/test/java/oap/http/server/nio/NioHttpServerTest.java b/oap-http/oap-http-test/src/test/java/oap/http/server/nio/NioHttpServerTest.java index f66c753da..39a59fff0 100644 --- a/oap-http/oap-http-test/src/test/java/oap/http/server/nio/NioHttpServerTest.java +++ b/oap-http/oap-http-test/src/test/java/oap/http/server/nio/NioHttpServerTest.java @@ -25,6 +25,7 @@ package oap.http.server.nio; import oap.http.Http; +import oap.http.client.Client; import oap.http.server.nio.handlers.BlockingReadTimeoutHandler; import oap.http.server.nio.handlers.CompressionNioHandler; import oap.http.server.nio.handlers.KeepaliveRequestsHandler; @@ -115,7 +116,7 @@ public void testHttps() throws Exception { connector.setSslContextFactory( sslContextFactory ); try( NioHttpServer httpServer = new NioHttpServer( new NioHttpServer.DefaultPort( httpPort, httpsPort, Resources.urlOrThrow( getClass(), "/oap/http/test_https.jks" ), "1234567" ) ); - HttpClient httpClient = new HttpClient( new HttpClientTransportDynamic( connector ) ) ) { + HttpClient httpClient = Client.customHttpClient( new HttpClientTransportDynamic( connector ) ) ) { httpClient.start(); new TestHttpHandler( httpServer, "/test", "default-https" ); diff --git a/oap-http/oap-http-test/src/test/java/oap/http/server/nio/handlers/KeepaliveRequestsHandlerTest.java b/oap-http/oap-http-test/src/test/java/oap/http/server/nio/handlers/KeepaliveRequestsHandlerTest.java index 17c6f4035..7f0993c59 100644 --- a/oap-http/oap-http-test/src/test/java/oap/http/server/nio/handlers/KeepaliveRequestsHandlerTest.java +++ b/oap-http/oap-http-test/src/test/java/oap/http/server/nio/handlers/KeepaliveRequestsHandlerTest.java @@ -1,12 +1,11 @@ package oap.http.server.nio.handlers; import oap.http.Http; +import oap.http.client.Client; import oap.http.server.nio.NioHttpServer; import oap.testng.Fixtures; import oap.testng.Ports; import org.eclipse.jetty.client.HttpClient; -import org.eclipse.jetty.util.thread.QueuedThreadPool; -import org.eclipse.jetty.util.thread.VirtualThreadPool; import org.testng.annotations.Test; import java.util.LinkedHashSet; @@ -28,12 +27,8 @@ public KeepaliveRequestsHandlerTest() { public void testCloseConnectionBlocking() throws Exception { LinkedHashSet ids = new LinkedHashSet<>(); try( NioHttpServer httpServer = new NioHttpServer( new NioHttpServer.DefaultPort( testHttpPort ) ); - HttpClient client = new HttpClient() ) { - QueuedThreadPool qtp = new QueuedThreadPool(); - qtp.setVirtualThreadsExecutor( new VirtualThreadPool() ); - client.setExecutor( qtp ); + HttpClient client = Client.customHttpClient() ) { client.setMaxConnectionsPerDestination( 10 ); - client.start(); KeepaliveRequestsHandler keepaliveRequestsHandler = new KeepaliveRequestsHandler( 2 ); httpServer.handlers.add( keepaliveRequestsHandler ); @@ -62,12 +57,8 @@ public void testCloseConnectionBlocking() throws Exception { public void testCloseConnectionAsync() throws Exception { LinkedHashSet ids = new LinkedHashSet<>(); try( NioHttpServer httpServer = new NioHttpServer( new NioHttpServer.DefaultPort( testHttpPort ) ); - HttpClient client = new HttpClient() ) { - QueuedThreadPool qtp = new QueuedThreadPool(); - qtp.setVirtualThreadsExecutor( new VirtualThreadPool() ); - client.setExecutor( qtp ); + HttpClient client = Client.customHttpClient() ) { client.setMaxConnectionsPerDestination( 10 ); - client.start(); KeepaliveRequestsHandler keepaliveRequestsHandler = new KeepaliveRequestsHandler( 2 ); httpServer.handlers.add( keepaliveRequestsHandler ); diff --git a/oap-http/oap-http/src/main/java/oap/http/client/Client.java b/oap-http/oap-http/src/main/java/oap/http/client/Client.java new file mode 100644 index 000000000..f4a7fe32b --- /dev/null +++ b/oap-http/oap-http/src/main/java/oap/http/client/Client.java @@ -0,0 +1,36 @@ +package oap.http.client; + +import lombok.SneakyThrows; +import oap.util.Dates; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.HttpClientTransport; +import org.eclipse.jetty.util.thread.QueuedThreadPool; +import org.eclipse.jetty.util.thread.VirtualThreadPool; + +import javax.annotation.Nonnull; +import java.util.concurrent.Executor; + +public class Client { + public static final HttpClient DEFAULT_HTTP_CLIENT = customHttpClient(); + public static final Executor DEFAULT_VIRTUAL_THREAD_EXECUTOR = ( ( QueuedThreadPool ) DEFAULT_HTTP_CLIENT.getExecutor() ).getVirtualThreadsExecutor(); + + public static HttpClient customHttpClient() { + return defaultInit( new HttpClient() ); + } + + public static HttpClient customHttpClient( HttpClientTransport transport ) { + return defaultInit( new HttpClient( transport ) ); + } + + @SneakyThrows + private static @Nonnull HttpClient defaultInit( HttpClient client ) { + client.setConnectTimeout( Dates.s( 10 ) ); + QueuedThreadPool qtp = new QueuedThreadPool(); + qtp.setVirtualThreadsExecutor( new VirtualThreadPool() ); + client.setExecutor( qtp ); + client.setFollowRedirects( false ); + client.start(); + + return client; + } +} diff --git a/oap-http/oap-pnio-v3/src/test/java/oap/http/pniov3/PnioServerTest.java b/oap-http/oap-pnio-v3/src/test/java/oap/http/pniov3/PnioServerTest.java index a9108bc4a..c77de74cb 100644 --- a/oap-http/oap-pnio-v3/src/test/java/oap/http/pniov3/PnioServerTest.java +++ b/oap-http/oap-pnio-v3/src/test/java/oap/http/pniov3/PnioServerTest.java @@ -1,12 +1,11 @@ package oap.http.pniov3; import oap.http.Http; +import oap.http.client.Client; import oap.http.server.nio.NioHttpServer; import oap.testng.Fixtures; import oap.util.Dates; import org.eclipse.jetty.client.HttpClient; -import org.eclipse.jetty.util.thread.QueuedThreadPool; -import org.eclipse.jetty.util.thread.VirtualThreadPool; import org.testng.annotations.Test; import java.util.Map; @@ -29,13 +28,8 @@ public void testRequestUndertow() throws Exception { int port = fixture.definePort( "test" ); try( ExecutorService threadPoolExecutor = Executors.newVirtualThreadPerTaskExecutor() ) { - try( HttpClient httpClient = new HttpClient() ) { - QueuedThreadPool qtp = new QueuedThreadPool(); - qtp.setVirtualThreadsExecutor( new VirtualThreadPool() ); - httpClient.setExecutor( qtp ); + try( HttpClient httpClient = Client.customHttpClient() ) { httpClient.setMaxConnectionsPerDestination( 2000 ); - httpClient.setConnectTimeout( Dates.s( 10 ) ); - httpClient.start(); AtomicInteger errorCount = new AtomicInteger(); AtomicInteger okCount = new AtomicInteger(); diff --git a/oap-message/oap-message-client/src/main/java/oap/message/client/MessageSender.java b/oap-message/oap-message-client/src/main/java/oap/message/client/MessageSender.java index 049a2aa2d..03083e2a2 100644 --- a/oap-message/oap-message-client/src/main/java/oap/message/client/MessageSender.java +++ b/oap-message/oap-message-client/src/main/java/oap/message/client/MessageSender.java @@ -34,6 +34,7 @@ import oap.concurrent.scheduler.Scheduled; import oap.concurrent.scheduler.Scheduler; import oap.http.Http; +import oap.http.client.Client; import oap.io.Closeables; import oap.io.Files; import oap.io.content.ContentReader; @@ -52,12 +53,9 @@ import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.io.FilenameUtils; import org.eclipse.jetty.client.BytesRequestContent; -import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.InputStreamResponseListener; import org.eclipse.jetty.client.Response; import org.eclipse.jetty.http.HttpMethod; -import org.eclipse.jetty.util.thread.QueuedThreadPool; -import org.eclipse.jetty.util.thread.VirtualThreadPool; import org.joda.time.DateTimeUtils; import org.slf4j.event.Level; @@ -96,7 +94,6 @@ public class MessageSender implements Closeable, AutoCloseable { private final ReentrantLock lock = new ReentrantLock(); public String uniqueName = Cuid.UNIQUE.next(); public long storageLockExpiration = Dates.h( 1 ); - public int poolSize = 4; public long diskSyncPeriod = Dates.m( 1 ); public long globalIoRetryTimeout = Dates.s( 1 ); public long retryTimeout = Dates.s( 1 ); @@ -106,7 +103,6 @@ public class MessageSender implements Closeable, AutoCloseable { private volatile boolean closed = false; private Scheduled diskSyncScheduler; private boolean networkAvailable = true; - private HttpClient httpClient; private long ioExceptionStartRetryTimeout = -1; public MessageSender( String host, int port, String httpPrefix, Path persistenceDirectory, long memorySyncPeriod ) { @@ -148,22 +144,13 @@ public final long getClientId() { public void start() { log.info( "[{}] message server messageUrl {} storage {} storageLockExpiration {}", uniqueName, messageUrl, directory, Dates.durationToString( storageLockExpiration ) ); - log.info( "[{}] connection timeout {} rw timeout {} pool size {} keepAliveDuration {}", - uniqueName, Dates.durationToString( connectionTimeout ), Dates.durationToString( timeout ), poolSize, + log.info( "[{}] connection timeout {} rw timeout {} keepAliveDuration {}", + uniqueName, Dates.durationToString( connectionTimeout ), Dates.durationToString( timeout ), Dates.durationToString( keepAliveDuration ) ); log.info( "[{}] retry timeout {} disk sync period '{}' memory sync period '{}'", uniqueName, Dates.durationToString( retryTimeout ), Dates.durationToString( diskSyncPeriod ), Dates.durationToString( memorySyncPeriod ) ); log.info( "custom status = {}", MessageProtocol.printMapping() ); - httpClient = new HttpClient(); - httpClient.setConnectTimeout( connectionTimeout ); - httpClient.setMaxConnectionsPerDestination( poolSize ); - - QueuedThreadPool qtp = new QueuedThreadPool(); - qtp.setVirtualThreadsExecutor( new VirtualThreadPool() ); - httpClient.setExecutor( qtp ); - httpClient.start(); - if( diskSyncPeriod > 0 ) diskSyncScheduler = Scheduler.scheduleWithFixedDelay( diskSyncPeriod, TimeUnit.MILLISECONDS, this::syncDisk ); @@ -220,8 +207,6 @@ public void close() { } } - Closeables.close( httpClient ); - saveMessagesToDirectory( directory ); } @@ -284,7 +269,7 @@ public CompletableFuture send( Messages.MessageInfo messag out.write( message.data ); InputStreamResponseListener inputStreamResponseListener = new InputStreamResponseListener(); - httpClient + Client.DEFAULT_HTTP_CLIENT .newRequest( messageUrl ) .method( HttpMethod.POST ) .timeout( timeout, TimeUnit.MILLISECONDS ) @@ -309,7 +294,7 @@ public CompletableFuture send( Messages.MessageInfo messag throw Throwables.propagate( e ); } - }, ( ( QueuedThreadPool ) httpClient.getExecutor() ).getVirtualThreadsExecutor() ); + }, Client.DEFAULT_VIRTUAL_THREAD_EXECUTOR ); } private void processException( Messages.MessageInfo messageInfo, long now, Message message, Throwable e, boolean globalRetryTimeout ) { diff --git a/oap-message/oap-message-client/src/main/resources/META-INF/oap-module.oap b/oap-message/oap-message-client/src/main/resources/META-INF/oap-module.oap index bc0707b92..420a949c4 100644 --- a/oap-message/oap-message-client/src/main/resources/META-INF/oap-module.oap +++ b/oap-message/oap-message-client/src/main/resources/META-INF/oap-module.oap @@ -4,14 +4,12 @@ services { oap-http-message-sender { implementation = oap.message.client.MessageSender parameters { - connectionTimeout = 30s timeout = 5s retryTimeout = 1s globalIoRetryTimeout = 1s diskSyncPeriod = 1m memorySyncPeriod = 100 keepAliveDuration = 30d - poolSize = -1 storageLockExpiration = 1h port = 8081 diff --git a/oap-message/oap-message-test/src/test/java/oap/message/MessageServerTest.java b/oap-message/oap-message-test/src/test/java/oap/message/MessageServerTest.java index 97c54d57e..55f219310 100644 --- a/oap-message/oap-message-test/src/test/java/oap/message/MessageServerTest.java +++ b/oap-message/oap-message-test/src/test/java/oap/message/MessageServerTest.java @@ -94,9 +94,6 @@ public void rejectedException() throws IOException { try( MessageSender client1 = new MessageSender( "localhost", port, "/messages", testDirectoryFixture.testPath( "tmp" ), -1 ); MessageSender client2 = new MessageSender( "localhost", port, "/messages", testDirectoryFixture.testPath( "tmp" ), -1 ) ) { - client1.poolSize = 1; - client2.poolSize = 1; - client1.start(); client2.start(); messageHttpHandler.preStart(); @@ -111,8 +108,6 @@ public void rejectedException() throws IOException { } try( MessageSender client = new MessageSender( "localhost", port, "/messages", testDirectoryFixture.testPath( "tmp" ), -1 ) ) { - client.poolSize = 1; - client.start(); client.send( MessageListenerMock.MESSAGE_TYPE, ( short ) 1, "rejectedException 2", ofString() ).syncDisk() @@ -201,7 +196,6 @@ public void sendAndReceiveJsonOneThread() throws IOException { try( NioHttpServer server = new NioHttpServer( new NioHttpServer.DefaultPort( port ) ); MessageHttpHandler messageHttpHandler = new MessageHttpHandler( server, "/messages", controlStatePath, List.of( listener1 ), -1 ); MessageSender client = new MessageSender( "localhost", port, "/messages", testDirectoryFixture.testPath( "tmp" ), -1 ) ) { - client.poolSize = 1; server.bind( "/messages", messageHttpHandler ); client.start(); @@ -509,7 +503,6 @@ public void clientPersistenceLockExpiration() throws IOException { Path msgDirectory = testDirectoryFixture.testPath( "tmp" ); try( MessageSender client = new MessageSender( "localhost", port, "/messages", msgDirectory, -1 ) ) { client.retryTimeout = 100; - client.poolSize = 2; client.start(); listener1.throwUnknownError = 2; From 0362b4de57778620b578a2f008bcc58012ab320f Mon Sep 17 00:00:00 2001 From: "igor.petrenko" Date: Mon, 9 Feb 2026 11:47:09 +0200 Subject: [PATCH 16/39] CE-135 okhttp -> jetty http client --- .../main/java/oap/http/test/HttpAsserts.java | 60 +++++++++++++++---- 1 file changed, 50 insertions(+), 10 deletions(-) diff --git a/oap-http/oap-http-test/src/main/java/oap/http/test/HttpAsserts.java b/oap-http/oap-http-test/src/main/java/oap/http/test/HttpAsserts.java index 0b6539710..6f9ac776c 100644 --- a/oap-http/oap-http-test/src/main/java/oap/http/test/HttpAsserts.java +++ b/oap-http/oap-http-test/src/main/java/oap/http/test/HttpAsserts.java @@ -109,7 +109,11 @@ public static HttpAssertion assertGet( HttpClient client, String uri, Map headers ) { - return getResponseAsHttpAssertion( Client.DEFAULT_HTTP_CLIENT + return assertPost( Client.DEFAULT_HTTP_CLIENT, uri, content, contentType, headers ); + } + + public static HttpAssertion assertPost( HttpClient httpClient, String uri, InputStream content, @Nullable String contentType, Map headers ) { + return getResponseAsHttpAssertion( httpClient .newRequest( uri ) .method( HttpMethod.POST ) .addHeaders( headers ) @@ -121,7 +125,11 @@ public static HttpAssertion assertPost( String uri, InputStream content, @Nullab } public static HttpAssertion assertPost( String uri, byte[] content, @Nullable String contentType, Map headers ) { - return getResponseAsHttpAssertion( Client.DEFAULT_HTTP_CLIENT + return assertPost( Client.DEFAULT_HTTP_CLIENT, uri, content, contentType, headers ); + } + + public static HttpAssertion assertPost( HttpClient httpClient, String uri, byte[] content, @Nullable String contentType, Map headers ) { + return getResponseAsHttpAssertion( httpClient .newRequest( uri ) .method( HttpMethod.POST ) .addHeaders( headers ) @@ -129,7 +137,11 @@ public static HttpAssertion assertPost( String uri, byte[] content, @Nullable St } public static HttpAssertion assertPost( String uri, String content, @Nullable String contentType, Map headers ) { - return getResponseAsHttpAssertion( Client.DEFAULT_HTTP_CLIENT + return assertPost( Client.DEFAULT_HTTP_CLIENT, uri, content, contentType, headers ); + } + + public static HttpAssertion assertPost( HttpClient httpClient, String uri, String content, @Nullable String contentType, Map headers ) { + return getResponseAsHttpAssertion( httpClient .newRequest( uri ) .method( HttpMethod.POST ) .addHeaders( headers ) @@ -173,7 +185,11 @@ public static HttpAssertion assertPut( String uri, String content, String conten } public static HttpAssertion assertPut( String uri, String content, String contentType, Map headers ) { - return getResponseAsHttpAssertion( Client.DEFAULT_HTTP_CLIENT + return assertPut( Client.DEFAULT_HTTP_CLIENT, uri, content, contentType, headers ); + } + + public static HttpAssertion assertPut( HttpClient httpClient, String uri, String content, String contentType, Map headers ) { + return getResponseAsHttpAssertion( httpClient .newRequest( uri ) .method( HttpMethod.PUT ) .addHeaders( headers ) @@ -186,7 +202,11 @@ public static HttpAssertion assertPut( String uri, byte[] content, String conten } public static HttpAssertion assertPut( String uri, byte[] content, String contentType, Map headers ) { - return getResponseAsHttpAssertion( Client.DEFAULT_HTTP_CLIENT + return assertPut( Client.DEFAULT_HTTP_CLIENT, uri, content, contentType, headers ); + } + + public static HttpAssertion assertPut( HttpClient httpClient, String uri, byte[] content, String contentType, Map headers ) { + return getResponseAsHttpAssertion( httpClient .newRequest( uri ) .method( HttpMethod.PUT ) .addHeaders( headers ) @@ -199,7 +219,11 @@ public static HttpAssertion assertPut( String uri, InputStream is, String conten } public static HttpAssertion assertPut( String uri, InputStream is, String contentType, Map headers ) { - return getResponseAsHttpAssertion( Client.DEFAULT_HTTP_CLIENT + return assertPut( Client.DEFAULT_HTTP_CLIENT, uri, is, contentType, headers ); + } + + public static HttpAssertion assertPut( HttpClient httpClient, String uri, InputStream is, String contentType, Map headers ) { + return getResponseAsHttpAssertion( httpClient .newRequest( uri ) .method( HttpMethod.PUT ) .addHeaders( headers ) @@ -212,7 +236,11 @@ public static HttpAssertion assertPatch( String uri, byte[] content, String cont } public static HttpAssertion assertPatch( String uri, byte[] content, String contentType, Map headers ) { - return getResponseAsHttpAssertion( Client.DEFAULT_HTTP_CLIENT + return assertPatch( Client.DEFAULT_HTTP_CLIENT, uri, content, contentType, headers ); + } + + public static HttpAssertion assertPatch( HttpClient httpClient, String uri, byte[] content, String contentType, Map headers ) { + return getResponseAsHttpAssertion( httpClient .newRequest( uri ) .method( HttpMethod.PATCH ) .addHeaders( headers ) @@ -225,7 +253,11 @@ public static HttpAssertion assertPatch( String uri, String content, String cont } public static HttpAssertion assertPatch( String uri, String content, String contentType, Map headers ) { - return getResponseAsHttpAssertion( Client.DEFAULT_HTTP_CLIENT + return assertPatch( Client.DEFAULT_HTTP_CLIENT, uri, content, contentType, headers ); + } + + public static HttpAssertion assertPatch( HttpClient httpClient, String uri, String content, String contentType, Map headers ) { + return getResponseAsHttpAssertion( httpClient .newRequest( uri ) .method( HttpMethod.PATCH ) .addHeaders( headers ) @@ -239,7 +271,11 @@ public static HttpAssertion assertPatch( String uri, InputStream is, String cont public static HttpAssertion assertPatch( String uri, InputStream is, String contentType, Map headers ) { - return getResponseAsHttpAssertion( Client.DEFAULT_HTTP_CLIENT + return assertPatch( Client.DEFAULT_HTTP_CLIENT, uri, is, contentType, headers ); + } + + public static HttpAssertion assertPatch( HttpClient httpClient, String uri, InputStream is, String contentType, Map headers ) { + return getResponseAsHttpAssertion( httpClient .newRequest( uri ) .method( HttpMethod.PATCH ) .addHeaders( headers ) @@ -248,7 +284,11 @@ public static HttpAssertion assertPatch( String uri, InputStream is, String cont } public static HttpAssertion assertDelete( String uri, Map headers ) { - return getResponseAsHttpAssertion( Client.DEFAULT_HTTP_CLIENT + return assertDelete( Client.DEFAULT_HTTP_CLIENT, uri, headers ); + } + + public static HttpAssertion assertDelete( HttpClient httpClient, String uri, Map headers ) { + return getResponseAsHttpAssertion( httpClient .newRequest( uri ) .method( HttpMethod.DELETE ) .addHeaders( headers ) From ba2373a60a70bbea34a8e18d7a5319e0524865d8 Mon Sep 17 00:00:00 2001 From: "igor.petrenko" Date: Mon, 9 Feb 2026 11:50:44 +0200 Subject: [PATCH 17/39] CE-135 okhttp -> jetty http client --- .../oap-http/src/main/java/oap/http/client/Client.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/oap-http/oap-http/src/main/java/oap/http/client/Client.java b/oap-http/oap-http/src/main/java/oap/http/client/Client.java index f4a7fe32b..721a5bd69 100644 --- a/oap-http/oap-http/src/main/java/oap/http/client/Client.java +++ b/oap-http/oap-http/src/main/java/oap/http/client/Client.java @@ -33,4 +33,12 @@ public static HttpClient customHttpClient( HttpClientTransport transport ) { return client; } + + public static void resetCookies() { + resetCookies( DEFAULT_HTTP_CLIENT ); + } + + public static void resetCookies( HttpClient httpClient ) { + httpClient.getHttpCookieStore().clear(); + } } From 3b0b5d4e446b6a4c6b4eefb640cee66f4046971a Mon Sep 17 00:00:00 2001 From: "igor.petrenko" Date: Mon, 9 Feb 2026 15:28:43 +0200 Subject: [PATCH 18/39] CE-135 okhttp -> jetty http client --- oap-http/oap-http/src/main/java/oap/http/client/Client.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/oap-http/oap-http/src/main/java/oap/http/client/Client.java b/oap-http/oap-http/src/main/java/oap/http/client/Client.java index 721a5bd69..5bee6e7cb 100644 --- a/oap-http/oap-http/src/main/java/oap/http/client/Client.java +++ b/oap-http/oap-http/src/main/java/oap/http/client/Client.java @@ -4,6 +4,7 @@ import oap.util.Dates; import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.HttpClientTransport; +import org.eclipse.jetty.client.WWWAuthenticationProtocolHandler; import org.eclipse.jetty.util.thread.QueuedThreadPool; import org.eclipse.jetty.util.thread.VirtualThreadPool; @@ -29,6 +30,7 @@ public static HttpClient customHttpClient( HttpClientTransport transport ) { qtp.setVirtualThreadsExecutor( new VirtualThreadPool() ); client.setExecutor( qtp ); client.setFollowRedirects( false ); + client.getProtocolHandlers().remove( WWWAuthenticationProtocolHandler.NAME ); client.start(); return client; From dc8de49fa605b65926213e449fc5d7b8f6cb6af7 Mon Sep 17 00:00:00 2001 From: "igor.petrenko" Date: Mon, 9 Feb 2026 15:36:50 +0200 Subject: [PATCH 19/39] CE-135 okhttp -> jetty http client --- oap-http/oap-http/src/main/java/oap/http/client/Client.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/oap-http/oap-http/src/main/java/oap/http/client/Client.java b/oap-http/oap-http/src/main/java/oap/http/client/Client.java index 5bee6e7cb..ad2727d91 100644 --- a/oap-http/oap-http/src/main/java/oap/http/client/Client.java +++ b/oap-http/oap-http/src/main/java/oap/http/client/Client.java @@ -30,9 +30,10 @@ public static HttpClient customHttpClient( HttpClientTransport transport ) { qtp.setVirtualThreadsExecutor( new VirtualThreadPool() ); client.setExecutor( qtp ); client.setFollowRedirects( false ); - client.getProtocolHandlers().remove( WWWAuthenticationProtocolHandler.NAME ); client.start(); + client.getProtocolHandlers().remove( WWWAuthenticationProtocolHandler.NAME ); + return client; } From 5d1f54c98fe8a85dbc52b433e0ffa95a05184556 Mon Sep 17 00:00:00 2001 From: "igor.petrenko" Date: Wed, 11 Feb 2026 11:20:01 +0200 Subject: [PATCH 20/39] CE-135 okhttp -> jetty http client --- .../remote/RemoteInvocationHandler.java | 25 +-- .../main/java/oap/http/test/HttpAsserts.java | 47 +++-- .../src/test/java/oap/http/GzipHttpTest.java | 4 +- .../http/server/nio/NioHttpServerTest.java | 5 +- .../KeepaliveRequestsHandlerTest.java | 10 +- oap-http/oap-http/pom.xml | 5 + .../src/main/java/oap/http/client/Client.java | 47 ----- .../client/InputStreamResponseListener.java | 27 +++ .../java/oap/http/client/OapHttpClient.java | 162 ++++++++++++++++++ .../java/oap/http/pniov3/PnioServerTest.java | 6 +- .../oap/message/client/MessageSender.java | 91 ++++------ .../java/oap/message/MessageServerTest.java | 42 +++-- pom.xml | 1 + 13 files changed, 295 insertions(+), 177 deletions(-) delete mode 100644 oap-http/oap-http/src/main/java/oap/http/client/Client.java create mode 100644 oap-http/oap-http/src/main/java/oap/http/client/InputStreamResponseListener.java create mode 100644 oap-http/oap-http/src/main/java/oap/http/client/OapHttpClient.java diff --git a/oap-application/oap-application/src/main/java/oap/application/remote/RemoteInvocationHandler.java b/oap-application/oap-application/src/main/java/oap/application/remote/RemoteInvocationHandler.java index 2cf2ab8cc..ae3153769 100644 --- a/oap-application/oap-application/src/main/java/oap/application/remote/RemoteInvocationHandler.java +++ b/oap-application/oap-application/src/main/java/oap/application/remote/RemoteInvocationHandler.java @@ -31,12 +31,13 @@ import lombok.extern.slf4j.Slf4j; import oap.application.ServiceKernelCommand; import oap.application.module.Reference; -import oap.http.client.Client; +import oap.http.client.InputStreamResponseListener; +import oap.http.client.OapHttpClient; +import oap.io.Closeables; import oap.util.Result; import oap.util.Stream; import oap.util.function.Try; import org.eclipse.jetty.client.BytesRequestContent; -import org.eclipse.jetty.client.InputStreamResponseListener; import org.eclipse.jetty.client.Request; import org.eclipse.jetty.client.Response; import org.eclipse.jetty.http.HttpMethod; @@ -155,33 +156,25 @@ private Result invoke( Method method, Object[] args ) { boolean async = CompletableFuture.class.isAssignableFrom( method.getReturnType() ); try { - Request request = Client.DEFAULT_HTTP_CLIENT + Request request = OapHttpClient.DEFAULT_HTTP_CLIENT .newRequest( uri ) .method( HttpMethod.POST ) .body( new BytesRequestContent( invocationB ) ) .timeout( timeout, TimeUnit.MILLISECONDS ); - CompletableFuture responseAsync; InputStreamResponseListener inputStreamResponseListener = new InputStreamResponseListener(); request.send( inputStreamResponseListener ); - if( async ) { - responseAsync = CompletableFuture.supplyAsync( () -> { - try { - return inputStreamResponseListener.get( timeout + 10, TimeUnit.MILLISECONDS ); - } catch( InterruptedException | TimeoutException | ExecutionException e ) { - throw new RuntimeException( e ); - } - }, Client.DEFAULT_VIRTUAL_THREAD_EXECUTOR ); - - } else { - responseAsync = new CompletableFuture<>(); + CompletableFuture responseAsync = inputStreamResponseListener.getResponseAsync(); + if( !async ) { try { - responseAsync.complete( inputStreamResponseListener.get( timeout + 10, TimeUnit.MILLISECONDS ) ); + inputStreamResponseListener.get( timeout + 10, TimeUnit.MILLISECONDS ); } catch( ExecutionException e ) { + Closeables.close( inputStreamResponseListener ); responseAsync.completeExceptionally( e.getCause() ); } catch( Exception e ) { + Closeables.close( inputStreamResponseListener ); responseAsync.completeExceptionally( e ); } } diff --git a/oap-http/oap-http-test/src/main/java/oap/http/test/HttpAsserts.java b/oap-http/oap-http-test/src/main/java/oap/http/test/HttpAsserts.java index 6f9ac776c..a6b2b499f 100644 --- a/oap-http/oap-http-test/src/main/java/oap/http/test/HttpAsserts.java +++ b/oap-http/oap-http-test/src/main/java/oap/http/test/HttpAsserts.java @@ -30,7 +30,7 @@ import lombok.extern.slf4j.Slf4j; import oap.http.Cookie; import oap.http.Response; -import oap.http.client.Client; +import oap.http.client.OapHttpClient; import oap.http.client.JettyRequestExtensions; import oap.json.JsonException; import oap.json.testng.JsonAsserts; @@ -44,7 +44,6 @@ import org.assertj.core.api.Assertions; import org.eclipse.jetty.client.BytesRequestContent; import org.eclipse.jetty.client.ContentResponse; -import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.InputStreamRequestContent; import org.eclipse.jetty.client.StringRequestContent; import org.eclipse.jetty.http.HttpFields; @@ -97,10 +96,10 @@ public static HttpAssertion assertGet( String uri, Pair... param } public static HttpAssertion assertGet( String uri, Map params, Map headers ) throws UncheckedIOException { - return assertGet( Client.DEFAULT_HTTP_CLIENT, uri, params, headers ); + return assertGet( OapHttpClient.DEFAULT_HTTP_CLIENT, uri, params, headers ); } - public static HttpAssertion assertGet( HttpClient client, String uri, Map params, Map headers ) throws UncheckedIOException { + public static HttpAssertion assertGet( org.eclipse.jetty.client.HttpClient client, String uri, Map params, Map headers ) throws UncheckedIOException { return getResponseAsHttpAssertion( client .newRequest( uri ) .method( HttpMethod.GET ) @@ -109,10 +108,10 @@ public static HttpAssertion assertGet( HttpClient client, String uri, Map headers ) { - return assertPost( Client.DEFAULT_HTTP_CLIENT, uri, content, contentType, headers ); + return assertPost( OapHttpClient.DEFAULT_HTTP_CLIENT, uri, content, contentType, headers ); } - public static HttpAssertion assertPost( HttpClient httpClient, String uri, InputStream content, @Nullable String contentType, Map headers ) { + public static HttpAssertion assertPost( org.eclipse.jetty.client.HttpClient httpClient, String uri, InputStream content, @Nullable String contentType, Map headers ) { return getResponseAsHttpAssertion( httpClient .newRequest( uri ) .method( HttpMethod.POST ) @@ -125,10 +124,10 @@ public static HttpAssertion assertPost( String uri, InputStream content, @Nullab } public static HttpAssertion assertPost( String uri, byte[] content, @Nullable String contentType, Map headers ) { - return assertPost( Client.DEFAULT_HTTP_CLIENT, uri, content, contentType, headers ); + return assertPost( OapHttpClient.DEFAULT_HTTP_CLIENT, uri, content, contentType, headers ); } - public static HttpAssertion assertPost( HttpClient httpClient, String uri, byte[] content, @Nullable String contentType, Map headers ) { + public static HttpAssertion assertPost( org.eclipse.jetty.client.HttpClient httpClient, String uri, byte[] content, @Nullable String contentType, Map headers ) { return getResponseAsHttpAssertion( httpClient .newRequest( uri ) .method( HttpMethod.POST ) @@ -137,10 +136,10 @@ public static HttpAssertion assertPost( HttpClient httpClient, String uri, byte[ } public static HttpAssertion assertPost( String uri, String content, @Nullable String contentType, Map headers ) { - return assertPost( Client.DEFAULT_HTTP_CLIENT, uri, content, contentType, headers ); + return assertPost( OapHttpClient.DEFAULT_HTTP_CLIENT, uri, content, contentType, headers ); } - public static HttpAssertion assertPost( HttpClient httpClient, String uri, String content, @Nullable String contentType, Map headers ) { + public static HttpAssertion assertPost( org.eclipse.jetty.client.HttpClient httpClient, String uri, String content, @Nullable String contentType, Map headers ) { return getResponseAsHttpAssertion( httpClient .newRequest( uri ) .method( HttpMethod.POST ) @@ -185,10 +184,10 @@ public static HttpAssertion assertPut( String uri, String content, String conten } public static HttpAssertion assertPut( String uri, String content, String contentType, Map headers ) { - return assertPut( Client.DEFAULT_HTTP_CLIENT, uri, content, contentType, headers ); + return assertPut( OapHttpClient.DEFAULT_HTTP_CLIENT, uri, content, contentType, headers ); } - public static HttpAssertion assertPut( HttpClient httpClient, String uri, String content, String contentType, Map headers ) { + public static HttpAssertion assertPut( org.eclipse.jetty.client.HttpClient httpClient, String uri, String content, String contentType, Map headers ) { return getResponseAsHttpAssertion( httpClient .newRequest( uri ) .method( HttpMethod.PUT ) @@ -202,10 +201,10 @@ public static HttpAssertion assertPut( String uri, byte[] content, String conten } public static HttpAssertion assertPut( String uri, byte[] content, String contentType, Map headers ) { - return assertPut( Client.DEFAULT_HTTP_CLIENT, uri, content, contentType, headers ); + return assertPut( OapHttpClient.DEFAULT_HTTP_CLIENT, uri, content, contentType, headers ); } - public static HttpAssertion assertPut( HttpClient httpClient, String uri, byte[] content, String contentType, Map headers ) { + public static HttpAssertion assertPut( org.eclipse.jetty.client.HttpClient httpClient, String uri, byte[] content, String contentType, Map headers ) { return getResponseAsHttpAssertion( httpClient .newRequest( uri ) .method( HttpMethod.PUT ) @@ -219,10 +218,10 @@ public static HttpAssertion assertPut( String uri, InputStream is, String conten } public static HttpAssertion assertPut( String uri, InputStream is, String contentType, Map headers ) { - return assertPut( Client.DEFAULT_HTTP_CLIENT, uri, is, contentType, headers ); + return assertPut( OapHttpClient.DEFAULT_HTTP_CLIENT, uri, is, contentType, headers ); } - public static HttpAssertion assertPut( HttpClient httpClient, String uri, InputStream is, String contentType, Map headers ) { + public static HttpAssertion assertPut( org.eclipse.jetty.client.HttpClient httpClient, String uri, InputStream is, String contentType, Map headers ) { return getResponseAsHttpAssertion( httpClient .newRequest( uri ) .method( HttpMethod.PUT ) @@ -236,10 +235,10 @@ public static HttpAssertion assertPatch( String uri, byte[] content, String cont } public static HttpAssertion assertPatch( String uri, byte[] content, String contentType, Map headers ) { - return assertPatch( Client.DEFAULT_HTTP_CLIENT, uri, content, contentType, headers ); + return assertPatch( OapHttpClient.DEFAULT_HTTP_CLIENT, uri, content, contentType, headers ); } - public static HttpAssertion assertPatch( HttpClient httpClient, String uri, byte[] content, String contentType, Map headers ) { + public static HttpAssertion assertPatch( org.eclipse.jetty.client.HttpClient httpClient, String uri, byte[] content, String contentType, Map headers ) { return getResponseAsHttpAssertion( httpClient .newRequest( uri ) .method( HttpMethod.PATCH ) @@ -253,10 +252,10 @@ public static HttpAssertion assertPatch( String uri, String content, String cont } public static HttpAssertion assertPatch( String uri, String content, String contentType, Map headers ) { - return assertPatch( Client.DEFAULT_HTTP_CLIENT, uri, content, contentType, headers ); + return assertPatch( OapHttpClient.DEFAULT_HTTP_CLIENT, uri, content, contentType, headers ); } - public static HttpAssertion assertPatch( HttpClient httpClient, String uri, String content, String contentType, Map headers ) { + public static HttpAssertion assertPatch( org.eclipse.jetty.client.HttpClient httpClient, String uri, String content, String contentType, Map headers ) { return getResponseAsHttpAssertion( httpClient .newRequest( uri ) .method( HttpMethod.PATCH ) @@ -271,10 +270,10 @@ public static HttpAssertion assertPatch( String uri, InputStream is, String cont public static HttpAssertion assertPatch( String uri, InputStream is, String contentType, Map headers ) { - return assertPatch( Client.DEFAULT_HTTP_CLIENT, uri, is, contentType, headers ); + return assertPatch( OapHttpClient.DEFAULT_HTTP_CLIENT, uri, is, contentType, headers ); } - public static HttpAssertion assertPatch( HttpClient httpClient, String uri, InputStream is, String contentType, Map headers ) { + public static HttpAssertion assertPatch( org.eclipse.jetty.client.HttpClient httpClient, String uri, InputStream is, String contentType, Map headers ) { return getResponseAsHttpAssertion( httpClient .newRequest( uri ) .method( HttpMethod.PATCH ) @@ -284,10 +283,10 @@ public static HttpAssertion assertPatch( HttpClient httpClient, String uri, Inpu } public static HttpAssertion assertDelete( String uri, Map headers ) { - return assertDelete( Client.DEFAULT_HTTP_CLIENT, uri, headers ); + return assertDelete( OapHttpClient.DEFAULT_HTTP_CLIENT, uri, headers ); } - public static HttpAssertion assertDelete( HttpClient httpClient, String uri, Map headers ) { + public static HttpAssertion assertDelete( org.eclipse.jetty.client.HttpClient httpClient, String uri, Map headers ) { return getResponseAsHttpAssertion( httpClient .newRequest( uri ) .method( HttpMethod.DELETE ) diff --git a/oap-http/oap-http-test/src/test/java/oap/http/GzipHttpTest.java b/oap-http/oap-http-test/src/test/java/oap/http/GzipHttpTest.java index 4e0d0b466..4b779dd35 100644 --- a/oap-http/oap-http-test/src/test/java/oap/http/GzipHttpTest.java +++ b/oap-http/oap-http-test/src/test/java/oap/http/GzipHttpTest.java @@ -24,7 +24,7 @@ package oap.http; -import oap.http.client.Client; +import oap.http.client.OapHttpClient; import oap.http.server.nio.NioHttpServer; import oap.io.IoStreams; import oap.io.content.ContentWriter; @@ -71,7 +71,7 @@ public void gzipOutput() throws Exception { .body() .isEqualTo( "test" ); - try( HttpClient httpClient = Client.customHttpClient() ) { + try( HttpClient httpClient = OapHttpClient.customHttpClient().build() ) { // auto-decompression httpClient.getContentDecoderFactories().clear(); diff --git a/oap-http/oap-http-test/src/test/java/oap/http/server/nio/NioHttpServerTest.java b/oap-http/oap-http-test/src/test/java/oap/http/server/nio/NioHttpServerTest.java index 39a59fff0..904abcf1d 100644 --- a/oap-http/oap-http-test/src/test/java/oap/http/server/nio/NioHttpServerTest.java +++ b/oap-http/oap-http-test/src/test/java/oap/http/server/nio/NioHttpServerTest.java @@ -25,7 +25,7 @@ package oap.http.server.nio; import oap.http.Http; -import oap.http.client.Client; +import oap.http.client.OapHttpClient; import oap.http.server.nio.handlers.BlockingReadTimeoutHandler; import oap.http.server.nio.handlers.CompressionNioHandler; import oap.http.server.nio.handlers.KeepaliveRequestsHandler; @@ -34,7 +34,6 @@ import oap.testng.Fixtures; import oap.testng.Ports; import oap.util.Dates; -import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.transport.HttpClientTransportDynamic; import org.eclipse.jetty.io.ClientConnector; import org.eclipse.jetty.util.ssl.SslContextFactory; @@ -116,7 +115,7 @@ public void testHttps() throws Exception { connector.setSslContextFactory( sslContextFactory ); try( NioHttpServer httpServer = new NioHttpServer( new NioHttpServer.DefaultPort( httpPort, httpsPort, Resources.urlOrThrow( getClass(), "/oap/http/test_https.jks" ), "1234567" ) ); - HttpClient httpClient = Client.customHttpClient( new HttpClientTransportDynamic( connector ) ) ) { + org.eclipse.jetty.client.HttpClient httpClient = OapHttpClient.customHttpClient().transport( new HttpClientTransportDynamic( connector ) ).build() ) { httpClient.start(); new TestHttpHandler( httpServer, "/test", "default-https" ); diff --git a/oap-http/oap-http-test/src/test/java/oap/http/server/nio/handlers/KeepaliveRequestsHandlerTest.java b/oap-http/oap-http-test/src/test/java/oap/http/server/nio/handlers/KeepaliveRequestsHandlerTest.java index 7f0993c59..b0662df39 100644 --- a/oap-http/oap-http-test/src/test/java/oap/http/server/nio/handlers/KeepaliveRequestsHandlerTest.java +++ b/oap-http/oap-http-test/src/test/java/oap/http/server/nio/handlers/KeepaliveRequestsHandlerTest.java @@ -1,7 +1,7 @@ package oap.http.server.nio.handlers; import oap.http.Http; -import oap.http.client.Client; +import oap.http.client.OapHttpClient; import oap.http.server.nio.NioHttpServer; import oap.testng.Fixtures; import oap.testng.Ports; @@ -27,8 +27,8 @@ public KeepaliveRequestsHandlerTest() { public void testCloseConnectionBlocking() throws Exception { LinkedHashSet ids = new LinkedHashSet<>(); try( NioHttpServer httpServer = new NioHttpServer( new NioHttpServer.DefaultPort( testHttpPort ) ); - HttpClient client = Client.customHttpClient() ) { - client.setMaxConnectionsPerDestination( 10 ); + HttpClient client = OapHttpClient.customHttpClient().maxConnectionsPerDestination( 10 ).build() ) { + client.start(); KeepaliveRequestsHandler keepaliveRequestsHandler = new KeepaliveRequestsHandler( 2 ); httpServer.handlers.add( keepaliveRequestsHandler ); @@ -57,8 +57,8 @@ public void testCloseConnectionBlocking() throws Exception { public void testCloseConnectionAsync() throws Exception { LinkedHashSet ids = new LinkedHashSet<>(); try( NioHttpServer httpServer = new NioHttpServer( new NioHttpServer.DefaultPort( testHttpPort ) ); - HttpClient client = Client.customHttpClient() ) { - client.setMaxConnectionsPerDestination( 10 ); + HttpClient client = OapHttpClient.customHttpClient().maxConnectionsPerDestination( 10 ).build() ) { + client.start(); KeepaliveRequestsHandler keepaliveRequestsHandler = new KeepaliveRequestsHandler( 2 ); httpServer.handlers.add( keepaliveRequestsHandler ); diff --git a/oap-http/oap-http/pom.xml b/oap-http/oap-http/pom.xml index f01abdbc1..b2abe8d56 100644 --- a/oap-http/oap-http/pom.xml +++ b/oap-http/oap-http/pom.xml @@ -99,6 +99,11 @@ jetty-compression-zstandard ${oap.deps.jetty-client.version} + + dnsjava + dnsjava + ${oap.deps.dnsjava.version} + org.projectlombok diff --git a/oap-http/oap-http/src/main/java/oap/http/client/Client.java b/oap-http/oap-http/src/main/java/oap/http/client/Client.java deleted file mode 100644 index ad2727d91..000000000 --- a/oap-http/oap-http/src/main/java/oap/http/client/Client.java +++ /dev/null @@ -1,47 +0,0 @@ -package oap.http.client; - -import lombok.SneakyThrows; -import oap.util.Dates; -import org.eclipse.jetty.client.HttpClient; -import org.eclipse.jetty.client.HttpClientTransport; -import org.eclipse.jetty.client.WWWAuthenticationProtocolHandler; -import org.eclipse.jetty.util.thread.QueuedThreadPool; -import org.eclipse.jetty.util.thread.VirtualThreadPool; - -import javax.annotation.Nonnull; -import java.util.concurrent.Executor; - -public class Client { - public static final HttpClient DEFAULT_HTTP_CLIENT = customHttpClient(); - public static final Executor DEFAULT_VIRTUAL_THREAD_EXECUTOR = ( ( QueuedThreadPool ) DEFAULT_HTTP_CLIENT.getExecutor() ).getVirtualThreadsExecutor(); - - public static HttpClient customHttpClient() { - return defaultInit( new HttpClient() ); - } - - public static HttpClient customHttpClient( HttpClientTransport transport ) { - return defaultInit( new HttpClient( transport ) ); - } - - @SneakyThrows - private static @Nonnull HttpClient defaultInit( HttpClient client ) { - client.setConnectTimeout( Dates.s( 10 ) ); - QueuedThreadPool qtp = new QueuedThreadPool(); - qtp.setVirtualThreadsExecutor( new VirtualThreadPool() ); - client.setExecutor( qtp ); - client.setFollowRedirects( false ); - client.start(); - - client.getProtocolHandlers().remove( WWWAuthenticationProtocolHandler.NAME ); - - return client; - } - - public static void resetCookies() { - resetCookies( DEFAULT_HTTP_CLIENT ); - } - - public static void resetCookies( HttpClient httpClient ) { - httpClient.getHttpCookieStore().clear(); - } -} diff --git a/oap-http/oap-http/src/main/java/oap/http/client/InputStreamResponseListener.java b/oap-http/oap-http/src/main/java/oap/http/client/InputStreamResponseListener.java new file mode 100644 index 000000000..b1c3f6444 --- /dev/null +++ b/oap-http/oap-http/src/main/java/oap/http/client/InputStreamResponseListener.java @@ -0,0 +1,27 @@ +package oap.http.client; + +import org.eclipse.jetty.client.Response; + +import java.util.concurrent.CompletableFuture; + +public class InputStreamResponseListener extends org.eclipse.jetty.client.InputStreamResponseListener { + public final CompletableFuture responseAsync = new CompletableFuture<>(); + + @Override + public void onHeaders( Response response ) { + super.onHeaders( response ); + + responseAsync.complete( response ); + } + + @Override + public void onFailure( Response response, Throwable failure ) { + super.onFailure( response, failure ); + + responseAsync.completeExceptionally( failure ); + } + + public CompletableFuture getResponseAsync() { + return responseAsync; + } +} diff --git a/oap-http/oap-http/src/main/java/oap/http/client/OapHttpClient.java b/oap-http/oap-http/src/main/java/oap/http/client/OapHttpClient.java new file mode 100644 index 000000000..e98860ad7 --- /dev/null +++ b/oap-http/oap-http/src/main/java/oap/http/client/OapHttpClient.java @@ -0,0 +1,162 @@ +package oap.http.client; + +import io.micrometer.core.instrument.Gauge; +import io.micrometer.core.instrument.Metrics; +import io.micrometer.core.instrument.binder.BaseUnits; +import lombok.SneakyThrows; +import oap.util.Dates; +import oap.util.Lists; +import org.eclipse.jetty.client.AbstractConnectionPool; +import org.eclipse.jetty.client.AbstractConnectorHttpClientTransport; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.WWWAuthenticationProtocolHandler; +import org.eclipse.jetty.io.ClientConnector; +import org.eclipse.jetty.util.thread.QueuedThreadPool; +import org.eclipse.jetty.util.thread.VirtualThreadPool; +import org.xbill.DNS.Address; + +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.net.UnknownHostException; +import java.nio.channels.SocketChannel; +import java.util.List; +import java.util.concurrent.atomic.LongAdder; + +public class OapHttpClient { + public static final HttpClient DEFAULT_HTTP_CLIENT = customHttpClient().build(); + + public static OapHttpClientBuilder customHttpClient() { + return new OapHttpClientBuilder(); + } + + public static void resetCookies() { + resetCookies( DEFAULT_HTTP_CLIENT ); + } + + public static void resetCookies( HttpClient httpClient ) { + httpClient.getHttpCookieStore().clear(); + } + + public static class OapHttpClientBuilder { + public AbstractConnectorHttpClientTransport httpClientTransport; + public long connectionTimeout = Dates.s( 10 ); + public boolean followRedirects = false; + public int maxConnectionsPerDestination = 64; + public boolean dnsjava = false; + private String metrics; + + public OapHttpClientBuilder transport( AbstractConnectorHttpClientTransport httpClientTransport ) { + this.httpClientTransport = httpClientTransport; + + return this; + } + + public OapHttpClientBuilder connectionTimeout( long connectionTimeout ) { + this.connectionTimeout = connectionTimeout; + + return this; + } + + public OapHttpClientBuilder followRedirects( boolean followRedirects ) { + this.followRedirects = followRedirects; + + return this; + } + + public OapHttpClientBuilder maxConnectionsPerDestination( int maxConnectionsPerDestination ) { + this.maxConnectionsPerDestination = maxConnectionsPerDestination; + + return this; + } + + public OapHttpClientBuilder dnsjava( boolean enabled ) { + this.dnsjava = enabled; + + return this; + } + + public OapHttpClientBuilder metrics( String name ) { + this.metrics = name; + + return this; + } + + @SneakyThrows + public HttpClient build() { + HttpClient httpClient = httpClientTransport != null ? new HttpClient( httpClientTransport ) : new HttpClient(); + httpClient.setConnectTimeout( connectionTimeout ); + QueuedThreadPool qtp = new QueuedThreadPool(); + qtp.setVirtualThreadsExecutor( new VirtualThreadPool() ); + httpClient.setExecutor( qtp ); + httpClient.setFollowRedirects( followRedirects ); + httpClient.setMaxConnectionsPerDestination( maxConnectionsPerDestination ); + + if( dnsjava ) { + httpClient.setSocketAddressResolver( ( host, port, context, promise ) -> { + try { + if( "localhost".equals( host ) ) { + promise.succeeded( List.of( new InetSocketAddress( InetAddress.getLocalHost(), port ) ) ); + return; + } + + InetAddress[] inetAddresses = Address.getAllByName( host ); + + promise.succeeded( Lists.map( inetAddresses, ia -> new InetSocketAddress( ia, port ) ) ); + } catch( UnknownHostException e ) { + promise.failed( e ); + } + } ); + } + + if( metrics != null ) { + ( ( AbstractConnectorHttpClientTransport ) httpClient.getHttpClientTransport() ).getClientConnector().addEventListener( new ClientConnectorConnectListener( metrics ) ); + + Gauge.builder( "dsp_tunneling_connections", httpClient, cs -> cs.getDestinations().stream().mapToInt( d -> ( ( AbstractConnectionPool ) d.getConnectionPool() ).getMaxConnectionCount() ).sum() ) + .baseUnit( BaseUnits.CONNECTIONS ) + .tags( "event", "max", "client", metrics ) + .register( Metrics.globalRegistry ); + Gauge.builder( "dsp_tunneling_connections", httpClient, cs -> cs.getDestinations().stream().mapToInt( d -> ( ( AbstractConnectionPool ) d.getConnectionPool() ).getConnectionCount() ).sum() ) + .baseUnit( BaseUnits.CONNECTIONS ) + .tags( "event", "total", "client", metrics ) + .register( Metrics.globalRegistry ); + Gauge.builder( "dsp_tunneling_connections", httpClient, cs -> cs.getDestinations().stream().mapToInt( d -> ( ( AbstractConnectionPool ) d.getConnectionPool() ).getActiveConnectionCount() ).sum() ) + .baseUnit( BaseUnits.CONNECTIONS ) + .tags( "event", "active", "client", metrics ) + .register( Metrics.globalRegistry ); + } + + httpClient.start(); + + httpClient.getProtocolHandlers().remove( WWWAuthenticationProtocolHandler.NAME ); + + return httpClient; + } + + public static class ClientConnectorConnectListener implements ClientConnector.ConnectListener { + public final LongAdder connectSuccessCounter = new LongAdder(); + public final LongAdder connectFailedCounter = new LongAdder(); + + public ClientConnectorConnectListener( String name ) { + Gauge.builder( "dsp_tunneling_connections", this, cs -> cs.connectSuccessCounter.doubleValue() ) + .baseUnit( BaseUnits.CONNECTIONS ) + .tags( "event", "success", "client", name ) + .register( Metrics.globalRegistry ); + Gauge.builder( "dsp_tunneling_connections", this, cs -> cs.connectFailedCounter.doubleValue() ) + .baseUnit( BaseUnits.CONNECTIONS ) + .tags( "event", "failed", "client", name ) + .register( Metrics.globalRegistry ); + } + + @Override + public void onConnectSuccess( SocketChannel socketChannel ) { + connectSuccessCounter.increment(); + } + + @Override + public void onConnectFailure( SocketChannel socketChannel, SocketAddress socketAddress, Throwable failure ) { + connectFailedCounter.increment(); + } + } + } +} diff --git a/oap-http/oap-pnio-v3/src/test/java/oap/http/pniov3/PnioServerTest.java b/oap-http/oap-pnio-v3/src/test/java/oap/http/pniov3/PnioServerTest.java index c77de74cb..3578ff9c2 100644 --- a/oap-http/oap-pnio-v3/src/test/java/oap/http/pniov3/PnioServerTest.java +++ b/oap-http/oap-pnio-v3/src/test/java/oap/http/pniov3/PnioServerTest.java @@ -1,7 +1,7 @@ package oap.http.pniov3; import oap.http.Http; -import oap.http.client.Client; +import oap.http.client.OapHttpClient; import oap.http.server.nio.NioHttpServer; import oap.testng.Fixtures; import oap.util.Dates; @@ -28,8 +28,8 @@ public void testRequestUndertow() throws Exception { int port = fixture.definePort( "test" ); try( ExecutorService threadPoolExecutor = Executors.newVirtualThreadPerTaskExecutor() ) { - try( HttpClient httpClient = Client.customHttpClient() ) { - httpClient.setMaxConnectionsPerDestination( 2000 ); + try( HttpClient httpClient = OapHttpClient.customHttpClient().maxConnectionsPerDestination( 2000 ).build() ) { + httpClient.start(); AtomicInteger errorCount = new AtomicInteger(); AtomicInteger okCount = new AtomicInteger(); diff --git a/oap-message/oap-message-client/src/main/java/oap/message/client/MessageSender.java b/oap-message/oap-message-client/src/main/java/oap/message/client/MessageSender.java index 03083e2a2..42a9199da 100644 --- a/oap-message/oap-message-client/src/main/java/oap/message/client/MessageSender.java +++ b/oap-message/oap-message-client/src/main/java/oap/message/client/MessageSender.java @@ -34,7 +34,7 @@ import oap.concurrent.scheduler.Scheduled; import oap.concurrent.scheduler.Scheduler; import oap.http.Http; -import oap.http.client.Client; +import oap.http.client.OapHttpClient; import oap.io.Closeables; import oap.io.Files; import oap.io.content.ContentReader; @@ -69,12 +69,9 @@ import java.nio.file.DirectoryStream; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; import java.util.concurrent.locks.ReentrantLock; @Slf4j @@ -248,53 +245,51 @@ public MessageAvailabilityReport availabilityReport( byte messageType ) { } @SuppressWarnings( "checkstyle:OverloadMethodsDeclarationOrder" ) - public CompletableFuture send( Messages.MessageInfo messageInfo, long now ) { + public Messages.MessageInfo send( Messages.MessageInfo messageInfo, long now ) { Message message = messageInfo.message; log.debug( "[{}] sending data [type = {}] to server...", uniqueName, MessageProtocol.messageTypeToString( message.messageType ) ); - return CompletableFuture.supplyAsync( () -> { - Metrics.counter( "oap.messages", "type", MessageProtocol.messageTypeToString( message.messageType ), "status", "trysend" ).increment(); + Metrics.counter( "oap.messages", "type", MessageProtocol.messageTypeToString( message.messageType ), "status", "trysend" ).increment(); - try( FastByteArrayOutputStream buf = new FastByteArrayOutputStream(); - DataOutputStream out = new DataOutputStream( buf ) ) { - out.writeByte( message.messageType ); - out.writeShort( message.version ); - out.writeLong( message.clientId ); + try( FastByteArrayOutputStream buf = new FastByteArrayOutputStream(); + DataOutputStream out = new DataOutputStream( buf ) ) { + out.writeByte( message.messageType ); + out.writeShort( message.version ); + out.writeLong( message.clientId ); - out.write( message.md5.bytes ); + out.write( message.md5.bytes ); - out.write( MessageProtocol.RESERVED, 0, MessageProtocol.RESERVED_LENGTH ); - out.writeInt( message.data.length ); - out.write( message.data ); + out.write( MessageProtocol.RESERVED, 0, MessageProtocol.RESERVED_LENGTH ); + out.writeInt( message.data.length ); + out.write( message.data ); - InputStreamResponseListener inputStreamResponseListener = new InputStreamResponseListener(); - Client.DEFAULT_HTTP_CLIENT - .newRequest( messageUrl ) - .method( HttpMethod.POST ) - .timeout( timeout, TimeUnit.MILLISECONDS ) - .body( new BytesRequestContent( Http.ContentType.APPLICATION_OCTET_STREAM, buf.array ) ) - .send( inputStreamResponseListener ); + InputStreamResponseListener inputStreamResponseListener = new InputStreamResponseListener(); + OapHttpClient.DEFAULT_HTTP_CLIENT + .newRequest( messageUrl ) + .method( HttpMethod.POST ) + .timeout( timeout, TimeUnit.MILLISECONDS ) + .body( new BytesRequestContent( Http.ContentType.APPLICATION_OCTET_STREAM, buf.array ) ) + .send( inputStreamResponseListener ); - Response response = inputStreamResponseListener.get( timeout, TimeUnit.MILLISECONDS ); + Response response = inputStreamResponseListener.get( timeout, TimeUnit.MILLISECONDS ); - if( response.getStatus() >= 300 || response.getStatus() < 200 ) { - throw new IOException( "Not OK (" + response.getStatus() + ") response code returned for url: " + messageUrl ); - } - return onOkRespone( messageInfo, inputStreamResponseListener, now ); + if( response.getStatus() >= 300 || response.getStatus() < 200 ) { + throw new IOException( "Not OK (" + response.getStatus() + ") response code returned for url: " + messageUrl ); + } + return onOkRespone( messageInfo, inputStreamResponseListener, now ); - } catch( UnknownHostException e ) { - processException( messageInfo, now, message, e, true ); + } catch( UnknownHostException e ) { + processException( messageInfo, now, message, e, true ); - ioExceptionStartRetryTimeout = now; + ioExceptionStartRetryTimeout = now; - throw Throwables.propagate( e ); - } catch( Throwable e ) { - processException( messageInfo, now, message, e, false ); + throw Throwables.propagate( e ); + } catch( Throwable e ) { + processException( messageInfo, now, message, e, false ); - throw Throwables.propagate( e ); - } - }, Client.DEFAULT_VIRTUAL_THREAD_EXECUTOR ); + throw Throwables.propagate( e ); + } } private void processException( Messages.MessageInfo messageInfo, long now, Message message, Throwable e, boolean globalRetryTimeout ) { @@ -371,10 +366,6 @@ private Messages.MessageInfo onOkRespone( Messages.MessageInfo messageInfo, Inpu } public void syncMemory() { - syncMemory( -1 ); - } - - public void syncMemory( long timeoutMs ) { if( getReadyMessages() + getRetryMessages() + getInProgressMessages() > 0 ) log.trace( "[{}] sync ready {} retry {} inprogress {} ...", uniqueName, getReadyMessages(), getRetryMessages(), getInProgressMessages() ); @@ -394,20 +385,10 @@ public void syncMemory( long timeoutMs ) { if( messageInfo != null ) { log.trace( "[{}] message {}...", uniqueName, messageInfo.message.md5 ); - CompletableFuture future = send( messageInfo, now ); - future.handle( ( mi, _ ) -> { - messages.removeInProgress( mi ); - log.trace( "[{}] message {}... done", uniqueName, mi.message.md5 ); - return null; - } ); - - if( timeoutMs >= 0 ) { - try { - future.get( timeoutMs, TimeUnit.MILLISECONDS ); - } catch( InterruptedException | TimeoutException | ExecutionException e ) { - log.error( e.getMessage(), e ); - } - } + Messages.MessageInfo mi = send( messageInfo, now ); + + messages.removeInProgress( mi ); + log.trace( "[{}] message {}... done", uniqueName, mi.message.md5 ); } if( isGlobalIoRetryTimeout( now ) ) { diff --git a/oap-message/oap-message-test/src/test/java/oap/message/MessageServerTest.java b/oap-message/oap-message-test/src/test/java/oap/message/MessageServerTest.java index 55f219310..1752fdf68 100644 --- a/oap-message/oap-message-test/src/test/java/oap/message/MessageServerTest.java +++ b/oap-message/oap-message-test/src/test/java/oap/message/MessageServerTest.java @@ -99,8 +99,7 @@ public void rejectedException() throws IOException { messageHttpHandler.preStart(); server.start(); - client1.send( MessageListenerMock.MESSAGE_TYPE, ( short ) 1, "rejectedException", ofString() ) - .syncMemory( Dates.s( 10 ) ); + client1.send( MessageListenerMock.MESSAGE_TYPE, ( short ) 1, "rejectedException", ofString() ).syncMemory(); assertThat( listener1.getMessages() ).containsOnly( new TestMessage( 1, "rejectedException" ) ); @@ -110,8 +109,7 @@ public void rejectedException() throws IOException { try( MessageSender client = new MessageSender( "localhost", port, "/messages", testDirectoryFixture.testPath( "tmp" ), -1 ) ) { client.start(); - client.send( MessageListenerMock.MESSAGE_TYPE, ( short ) 1, "rejectedException 2", ofString() ).syncDisk() - .syncMemory( Dates.s( 10 ) ); + client.send( MessageListenerMock.MESSAGE_TYPE, ( short ) 1, "rejectedException 2", ofString() ).syncDisk().syncMemory(); assertThat( listener1.getMessages() ).containsOnly( new TestMessage( 1, "rejectedException" ), new TestMessage( 1, "rejectedException 2" ) ); assertThat( client.getReadyMessages() ).isEqualTo( 0L ); @@ -143,7 +141,7 @@ public void sendAndReceive() throws IOException { .send( MessageListenerMock.MESSAGE_TYPE, ( short ) 1, "sendAndReceive 2", ofString() ) .send( MessageListenerMock.MESSAGE_TYPE, ( short ) 1, "sendAndReceive 1", ofString() ) .send( MessageListenerMock.MESSAGE_TYPE2, ( short ) 1, "sendAndReceive 3", ofString() ) - .syncMemory( Dates.s( 10 ) ); + .syncMemory(); assertThat( listener1.getMessages() ).containsOnly( new TestMessage( 1, "sendAndReceive 1" ), new TestMessage( 1, "sendAndReceive 2" ) ); @@ -177,7 +175,7 @@ public void sendAndReceiveJson() throws IOException { .send( MessageListenerMock.MESSAGE_TYPE, ( short ) 1, "sendAndReceiveJson 2", ofJson() ) .send( MessageListenerMock.MESSAGE_TYPE, ( short ) 1, "sendAndReceiveJson 2", ofJson() ) .send( MessageListenerMock.MESSAGE_TYPE, ( short ) 1, "sendAndReceiveJson 1", ofJson() ) - .syncMemory( Dates.s( 10 ) ); + .syncMemory(); assertThat( listener1.messages ).containsOnly( new TestMessage( 1, Hex.encodeHexString( DigestUtils.getMd5Digest().digest( "\"sendAndReceiveJson 1\"".getBytes( UTF_8 ) ) ), "sendAndReceiveJson 1" ), @@ -207,7 +205,7 @@ public void sendAndReceiveJsonOneThread() throws IOException { .send( MessageListenerMock.MESSAGE_TYPE, ( short ) 1, "sendAndReceiveJsonOneThread 2", ofJson() ) .send( MessageListenerMock.MESSAGE_TYPE, ( short ) 1, "sendAndReceiveJsonOneThread 2", ofJson() ) .send( MessageListenerMock.MESSAGE_TYPE, ( short ) 1, "sendAndReceiveJsonOneThread 1", ofJson() ) - .syncMemory( Dates.s( 10 ) ); + .syncMemory(); assertThat( listener1.messages ).containsOnly( new TestMessage( 1, Hex.encodeHexString( DigestUtils.getMd5Digest().digest( "\"sendAndReceiveJsonOneThread 1\"".getBytes( UTF_8 ) ) ), "sendAndReceiveJsonOneThread 1" ), @@ -234,7 +232,7 @@ public void unknownErrorNoRetry() throws IOException { listener1.throwUnknownError( Integer.MAX_VALUE, true ); client.send( MessageListenerMock.MESSAGE_TYPE, ( short ) 1, "unknownErrorNoRetry", ofString() ) - .syncMemory( Dates.s( 10 ) ); + .syncMemory(); assertThat( client.getReadyMessages() ).isEqualTo( 0L ); assertThat( client.getRetryMessages() ).isEqualTo( 0L ); @@ -267,7 +265,7 @@ public void unknownError() throws IOException { client.send( MessageListenerMock.MESSAGE_TYPE, ( short ) 1, "unknownError", ofString() ); for( int i = 0; i < 5; i++ ) { - client.syncMemory( Dates.s( 10 ) ); + client.syncMemory(); Dates.incFixed( 100 + 1 ); } assertThat( listener1.throwUnknownError ).isLessThanOrEqualTo( 0 ); @@ -300,7 +298,7 @@ public void statusError() throws IOException { listener1.setStatus( 567 ); client.send( MessageListenerMock.MESSAGE_TYPE, ( short ) 1, "statusError", ofString() ) - .syncMemory( Dates.s( 10 ) ); + .syncMemory(); assertThat( client.getRetryMessages() ).isEqualTo( 1 ); assertThat( listener1.getMessages() ).isEmpty(); @@ -309,7 +307,7 @@ public void statusError() throws IOException { Dates.incFixed( 100 + 1 ); - client.syncMemory( Dates.s( 10 ) ); + client.syncMemory(); assertThat( listener1.getMessages() ).containsOnly( new TestMessage( 1, "statusError" ) ); } @@ -341,7 +339,7 @@ public void ttl() throws IOException { .send( MessageListenerMock.MESSAGE_TYPE, ( short ) 1, "ttl", ofString() ) .send( MessageListenerMock.MESSAGE_TYPE, ( short ) 1, "ttl", ofString() ) .send( MessageListenerMock.MESSAGE_TYPE, ( short ) 1, "ttl", ofString() ) - .syncMemory( Dates.s( 10 ) ); + .syncMemory(); assertThat( listener1.getMessages() ).containsOnly( new TestMessage( 1, "ttl" ) ); @@ -352,7 +350,7 @@ public void ttl() throws IOException { .send( MessageListenerMock.MESSAGE_TYPE, ( short ) 1, "ttl", ofString() ) .send( MessageListenerMock.MESSAGE_TYPE, ( short ) 1, "ttl", ofString() ) .send( MessageListenerMock.MESSAGE_TYPE, ( short ) 1, "ttl", ofString() ) - .syncMemory( Dates.s( 10 ) ); + .syncMemory(); assertThat( listener1.getMessages() ).containsExactly( new TestMessage( 1, "ttl" ), @@ -386,7 +384,7 @@ public void persistence() throws IOException { .send( MessageListenerMock.MESSAGE_TYPE, ( short ) 1, "persistence", ofString() ) .send( MessageListenerMock.MESSAGE_TYPE, ( short ) 1, "persistence", ofString() ) .send( MessageListenerMock.MESSAGE_TYPE, ( short ) 1, "persistence", ofString() ) - .syncMemory( Dates.s( 10 ) ); + .syncMemory(); assertThat( client.getReadyMessages() ).isEqualTo( 0L ); assertThat( client.getRetryMessages() ).isEqualTo( 0L ); @@ -408,7 +406,7 @@ public void persistence() throws IOException { .send( MessageListenerMock.MESSAGE_TYPE, ( short ) 1, "persistence", ofString() ) .send( MessageListenerMock.MESSAGE_TYPE, ( short ) 1, "persistence", ofString() ) .send( MessageListenerMock.MESSAGE_TYPE, ( short ) 1, "persistence", ofString() ) - .syncMemory( Dates.s( 10 ) ); + .syncMemory(); assertThat( client.getReadyMessages() ).isEqualTo( 0L ); assertThat( client.getRetryMessages() ).isEqualTo( 0L ); @@ -442,7 +440,7 @@ public void clientPersistence() throws IOException { client.start(); client.send( MessageListenerMock.MESSAGE_TYPE, ( short ) 2, "clientPersistence 1", ofString() ) - .syncMemory( Dates.s( 10 ) ); + .syncMemory(); client.send( MessageListenerMock.MESSAGE_TYPE2, ( short ) 2, "clientPersistence 2", ofString() ); assertThat( listener1.getMessages() ).isEmpty(); @@ -472,7 +470,7 @@ public void clientPersistence() throws IOException { assertThat( listener1.getMessages() ).isEmpty(); client.syncDisk(); - client.syncMemory( Dates.s( 10 ) ); + client.syncMemory(); assertThat( persistenceDirectory ).isEmptyDirectory(); @@ -509,7 +507,7 @@ public void clientPersistenceLockExpiration() throws IOException { client .send( MessageListenerMock.MESSAGE_TYPE, ( short ) 1, "clientPersistenceLockExpiration 1", ofString() ) .send( MessageListenerMock.MESSAGE_TYPE, ( short ) 1, "clientPersistenceLockExpiration 2", ofString() ) - .syncMemory( Dates.s( 10 ) ); + .syncMemory(); } assertThat( Files.wildcard( msgDirectory, "**/*.bin" ) ).hasSize( 2 ); @@ -534,7 +532,7 @@ public void clientPersistenceLockExpiration() throws IOException { client .syncDisk() - .syncMemory( Dates.s( 10 ) ); + .syncMemory(); assertThat( listener1.getMessages() ).containsExactly( new TestMessage( 1, "clientPersistenceLockExpiration 2" ) ); assertThat( client.getReadyMessages() ).isEqualTo( 0L ); @@ -565,14 +563,14 @@ public void availabilityReport() throws IOException { listener1.setStatus( 300 ); client.send( MessageListenerMock.MESSAGE_TYPE, ( short ) 1, "availabilityReport", ofString() ) - .syncMemory( Dates.s( 20 ) ); + .syncMemory(); assertThat( client.availabilityReport( MessageListenerMock.MESSAGE_TYPE ).state ).isEqualTo( State.FAILED ); assertThat( client.availabilityReport( MessageListenerMock.MESSAGE_TYPE2 ).state ).isEqualTo( State.OPERATIONAL ); listener1.setStatus( MessageProtocol.STATUS_OK ); - client.syncMemory( Dates.s( 10 ) ); + client.syncMemory(); assertThat( client.availabilityReport( MessageListenerMock.MESSAGE_TYPE ).state ).isEqualTo( State.OPERATIONAL ); assertThat( client.availabilityReport( MessageListenerMock.MESSAGE_TYPE2 ).state ).isEqualTo( State.OPERATIONAL ); @@ -591,7 +589,7 @@ public void testKernel() { kernelFixture.service( "oap-message-client", MessageSender.class ) .send( ( byte ) 12, ( short ) 1, "testKernel", ofString() ) - .syncMemory( Dates.s( 10 ) ); + .syncMemory(); assertThat( kernelFixture.service( "oap-message-test", MessageListenerMock.class ).getMessages() ) .containsExactly( new TestMessage( 1, "testKernel" ) ); diff --git a/pom.xml b/pom.xml index 58102dbab..dce352c45 100644 --- a/pom.xml +++ b/pom.xml @@ -112,6 +112,7 @@ 2.3 4.9.3 + 3.6.4 12.1.6 From 69a35482e70da46a3266287a129e0dd869334e71 Mon Sep 17 00:00:00 2001 From: "igor.petrenko" Date: Thu, 12 Feb 2026 11:53:38 +0200 Subject: [PATCH 21/39] CE-135 okhttp -> jetty http client --- .../oap/application/remote/RemoteTest.java | 29 ---- .../remote/RemoteInvocationHandler.java | 163 ++++++------------ .../client/InputStreamResponseListener.java | 27 --- 3 files changed, 55 insertions(+), 164 deletions(-) delete mode 100644 oap-http/oap-http/src/main/java/oap/http/client/InputStreamResponseListener.java diff --git a/oap-application/oap-application-test/src/test/java/oap/application/remote/RemoteTest.java b/oap-application/oap-application-test/src/test/java/oap/application/remote/RemoteTest.java index 2074368a5..c41daf386 100644 --- a/oap-application/oap-application-test/src/test/java/oap/application/remote/RemoteTest.java +++ b/oap-application/oap-application-test/src/test/java/oap/application/remote/RemoteTest.java @@ -30,17 +30,14 @@ import oap.application.module.Module; import oap.testng.Fixtures; import oap.testng.Ports; -import oap.util.Dates; import org.assertj.core.api.Assertions; import org.testng.annotations.Test; import java.net.ConnectException; import java.net.URL; -import java.time.Duration; import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.concurrent.CompletableFuture; import static oap.testng.Asserts.urlOfTestResource; import static org.assertj.core.api.Assertions.assertThat; @@ -88,32 +85,6 @@ public void invoke() { } } - @Test - public void testAsync() { - int port = Ports.getFreePort( getClass() ); - - List modules = Module.CONFIGURATION.urlsFromClassPath(); - modules.add( urlOfTestResource( getClass(), "module.oap" ) ); - try( Kernel kernel = new Kernel( modules ) ) { - kernel.start( ApplicationConfiguration.load( urlOfTestResource( RemoteTest.class, "application-remote.conf" ), - List.of(), - Map.of( "HTTP_PORT", port ) ) ); - - Optional service = kernel.service( "*.remote-client" ); - assertThat( service ).isPresent(); - assertThat( service ) - .get() - .satisfies( remote -> { - CompletableFuture actual = remote.accessibleAsync(); - long timeStart = System.currentTimeMillis(); - assertThat( actual ).succeedsWithin( Duration.ofSeconds( 10 ) ).isEqualTo( true ); - long timeEnd = System.currentTimeMillis(); - - assertThat( timeEnd - timeStart ).isGreaterThanOrEqualTo( Dates.s( 2 ) ); - } ); - } - } - @Test public void testStream() { List modules = Module.CONFIGURATION.urlsFromClassPath(); diff --git a/oap-application/oap-application/src/main/java/oap/application/remote/RemoteInvocationHandler.java b/oap-application/oap-application/src/main/java/oap/application/remote/RemoteInvocationHandler.java index ae3153769..9abd163a2 100644 --- a/oap-application/oap-application/src/main/java/oap/application/remote/RemoteInvocationHandler.java +++ b/oap-application/oap-application/src/main/java/oap/application/remote/RemoteInvocationHandler.java @@ -31,18 +31,16 @@ import lombok.extern.slf4j.Slf4j; import oap.application.ServiceKernelCommand; import oap.application.module.Reference; -import oap.http.client.InputStreamResponseListener; import oap.http.client.OapHttpClient; -import oap.io.Closeables; import oap.util.Result; import oap.util.Stream; import oap.util.function.Try; import org.eclipse.jetty.client.BytesRequestContent; +import org.eclipse.jetty.client.InputStreamResponseListener; import org.eclipse.jetty.client.Request; import org.eclipse.jetty.client.Response; import org.eclipse.jetty.http.HttpMethod; -import javax.annotation.Nonnull; import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; @@ -54,14 +52,10 @@ import java.lang.reflect.Parameter; import java.lang.reflect.Proxy; import java.net.URI; -import java.net.http.HttpTimeoutException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Iterator; import java.util.List; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionException; -import java.util.concurrent.CompletionStage; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; @@ -105,15 +99,6 @@ private static Object proxy( String source, URI uri, String service, Class cl return Proxy.newProxyInstance( clazz.getClassLoader(), new Class[] { clazz }, new RemoteInvocationHandler( source, uri, service, timeout ) ); } - @Nonnull - private static CompletionStage> retException( Throwable e, boolean async ) { - if( async ) { - return CompletableFuture.failedStage( e ); - } else { - return CompletableFuture.completedStage( Result.failure( e ) ); - } - } - @Override public Object invoke( Object proxy, Method method, Object[] args ) throws Throwable { if( uri == null ) { @@ -153,9 +138,9 @@ private Result invoke( Method method, Object[] args ) { byte[] invocationB = getInvocation( method, arguments ); - boolean async = CompletableFuture.class.isAssignableFrom( method.getReturnType() ); - try { + InputStreamResponseListener inputStreamResponseListener = new InputStreamResponseListener(); + Request request = OapHttpClient.DEFAULT_HTTP_CLIENT .newRequest( uri ) .method( HttpMethod.POST ) @@ -163,116 +148,78 @@ private Result invoke( Method method, Object[] args ) { .timeout( timeout, TimeUnit.MILLISECONDS ); - InputStreamResponseListener inputStreamResponseListener = new InputStreamResponseListener(); request.send( inputStreamResponseListener ); - CompletableFuture responseAsync = inputStreamResponseListener.getResponseAsync(); - if( !async ) { - try { - inputStreamResponseListener.get( timeout + 10, TimeUnit.MILLISECONDS ); - } catch( ExecutionException e ) { - Closeables.close( inputStreamResponseListener ); - responseAsync.completeExceptionally( e.getCause() ); - } catch( Exception e ) { - Closeables.close( inputStreamResponseListener ); - responseAsync.completeExceptionally( e ); - } - } - - CompletableFuture> ret = responseAsync.thenCompose( response -> { - try { - if( response.getStatus() == HTTP_OK ) { - InputStream inputStream = inputStreamResponseListener.getInputStream(); - BufferedInputStream bis = new BufferedInputStream( inputStream ); - DataInputStream dis = new DataInputStream( bis ); - boolean success = dis.readBoolean(); + try { + Response response = inputStreamResponseListener.get( timeout, TimeUnit.MILLISECONDS ); - try { - if( !success ) { - try { - Throwable throwable = FstConsts.readObjectWithSize( dis ); + if( response.getStatus() == HTTP_OK ) { + InputStream inputStream = inputStreamResponseListener.getInputStream(); + BufferedInputStream bis = new BufferedInputStream( inputStream ); + DataInputStream dis = new DataInputStream( bis ); + boolean success = dis.readBoolean(); - if( throwable instanceof RemoteInvocationException riex ) { - errorMetrics.increment(); - return retException( riex, async ); - } + try { + if( !success ) { + try { + Throwable throwable = FstConsts.readObjectWithSize( dis ); + if( throwable instanceof RemoteInvocationException riex ) { errorMetrics.increment(); - return async ? CompletableFuture.failedStage( throwable ) : CompletableFuture.completedStage( Result.failure( throwable ) ); - } finally { - dis.close(); + return Result.failure( riex ); } + + errorMetrics.increment(); + return Result.failure( throwable ); + } finally { + dis.close(); + } + } else { + boolean stream = dis.readBoolean(); + if( stream ) { + ChainIterator it = new ChainIterator( dis ); + + return Result.success( Stream.of( it ).onClose( Try.run( () -> { + dis.close(); + successMetrics.increment(); + } ) ) ); } else { - boolean stream = dis.readBoolean(); - if( stream ) { - ChainIterator it = new ChainIterator( dis ); - - return CompletableFuture.completedStage( Result.success( Stream.of( it ).onClose( Try.run( () -> { - dis.close(); - successMetrics.increment(); - } ) ) ) ); - } else { - try { - Result r = Result.success( FstConsts.readObjectWithSize( dis ) ); - successMetrics.increment(); - return CompletableFuture.completedStage( r ); - } finally { - dis.close(); - } + try { + Result r = Result.success( FstConsts.readObjectWithSize( dis ) ); + successMetrics.increment(); + return r; + } finally { + dis.close(); } } - } catch( Exception e ) { - dis.close(); - return retException( e, async ); } - } else { - RemoteInvocationException ex = new RemoteInvocationException( "invocation failed " + this + "#" + service + "@" + method.getName() - + " code " + response.getStatus() - + " body '" + new String( inputStreamResponseListener.getInputStream().readAllBytes(), StandardCharsets.UTF_8 ) + "'" - + " message '" + response.getReason() + "'" ); - - return retException( ex, async ); + } catch( Exception e ) { + dis.close(); + return Result.failure( e ); } - } catch( Throwable e ) { - return retException( e, async ); - } - } ); - - ret.whenComplete( ( _, ex ) -> { - if( ex != null ) { - checkException( ex ); - } - } ); + } else { + RemoteInvocationException ex = new RemoteInvocationException( "invocation failed " + this + "#" + service + "@" + method.getName() + + " code " + response.getStatus() + + " body '" + new String( inputStreamResponseListener.getInputStream().readAllBytes(), StandardCharsets.UTF_8 ) + "'" + + " message '" + response.getReason() + "'" ); - if( async ) { - return Result.success( ret.thenApply( r -> r.successValue ) ); - } else { - try { - return ret.join(); - } catch( CompletionException e ) { - throw ( Exception ) e.getCause(); + return Result.failure( ex ); } - } - } catch( Exception e ) { - if( async ) { - return Result.success( CompletableFuture.failedFuture( e ) ); + } catch( InterruptedException | TimeoutException e ) { + timeoutMetrics.increment(); + + return Result.failure( e ); + } catch( ExecutionException e ) { + return Result.failure( e.getCause() ); + } catch( Throwable e ) { + return Result.failure( e ); } + } catch( Exception e ) { return Result.failure( e ); } } - private void checkException( Throwable ex ) { - if( ex instanceof RemoteInvocationException riex ) { - checkException( riex.getCause() ); - return; - } - - if( ex instanceof HttpTimeoutException || ex instanceof TimeoutException ) { - timeoutMetrics.increment(); - } - } - @SneakyThrows private byte[] getInvocation( Method method, List arguments ) { diff --git a/oap-http/oap-http/src/main/java/oap/http/client/InputStreamResponseListener.java b/oap-http/oap-http/src/main/java/oap/http/client/InputStreamResponseListener.java deleted file mode 100644 index b1c3f6444..000000000 --- a/oap-http/oap-http/src/main/java/oap/http/client/InputStreamResponseListener.java +++ /dev/null @@ -1,27 +0,0 @@ -package oap.http.client; - -import org.eclipse.jetty.client.Response; - -import java.util.concurrent.CompletableFuture; - -public class InputStreamResponseListener extends org.eclipse.jetty.client.InputStreamResponseListener { - public final CompletableFuture responseAsync = new CompletableFuture<>(); - - @Override - public void onHeaders( Response response ) { - super.onHeaders( response ); - - responseAsync.complete( response ); - } - - @Override - public void onFailure( Response response, Throwable failure ) { - super.onFailure( response, failure ); - - responseAsync.completeExceptionally( failure ); - } - - public CompletableFuture getResponseAsync() { - return responseAsync; - } -} From db3c0d27a705a6bdb8815b63e3f3544582492e06 Mon Sep 17 00:00:00 2001 From: "igor.petrenko" Date: Thu, 12 Feb 2026 19:13:28 +0200 Subject: [PATCH 22/39] CE-135 okhttp -> jetty http client --- oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/SSO.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/SSO.java b/oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/SSO.java index 55f3b314c..43b6577e0 100644 --- a/oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/SSO.java +++ b/oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/SSO.java @@ -109,19 +109,19 @@ public static Response logoutResponse( String cookieDomain ) { .withCookie( Cookie.builder( AUTHENTICATION_KEY, "" ) .withDomain( cookieDomain ) .withPath( "/" ) - .withExpires( new DateTime( 1970, 1, 1, 1, 1, UTC ) ) + .withMaxAge( 0 ) .build() ) .withCookie( Cookie.builder( REFRESH_TOKEN_KEY, "" ) .withDomain( cookieDomain ) .withPath( "/" ) - .withExpires( new DateTime( 1970, 1, 1, 1, 1, UTC ) ) + .withMaxAge( 0 ) .build() ) .withCookie( Cookie.builder( SessionManager.COOKIE_ID, "" ) .withDomain( cookieDomain ) .withPath( "/" ) - .withExpires( new DateTime( 1970, 1, 1, 1, 1, UTC ) ) + .withMaxAge( 0 ) .build() ); } @@ -131,7 +131,7 @@ public static Response notAuthenticatedResponse( int code, String reasonPhrase, .withCookie( Cookie.builder( AUTHENTICATION_KEY, "" ) .withDomain( cookieDomain ) .withPath( "/" ) - .withExpires( new DateTime( 1970, 1, 1, 1, 1, UTC ) ) + .withMaxAge( 0 ) .build() ); } From 069f20aa396be4d224049dae9a5e59255a9468eb Mon Sep 17 00:00:00 2001 From: "igor.petrenko" Date: Thu, 12 Feb 2026 19:29:11 +0200 Subject: [PATCH 23/39] CE-135 okhttp -> jetty http client --- oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/SSO.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/SSO.java b/oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/SSO.java index 43b6577e0..894868694 100644 --- a/oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/SSO.java +++ b/oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/SSO.java @@ -109,19 +109,19 @@ public static Response logoutResponse( String cookieDomain ) { .withCookie( Cookie.builder( AUTHENTICATION_KEY, "" ) .withDomain( cookieDomain ) .withPath( "/" ) - .withMaxAge( 0 ) + .withMaxAge( 99999 ) .build() ) .withCookie( Cookie.builder( REFRESH_TOKEN_KEY, "" ) .withDomain( cookieDomain ) .withPath( "/" ) - .withMaxAge( 0 ) + .withMaxAge( 99999 ) .build() ) .withCookie( Cookie.builder( SessionManager.COOKIE_ID, "" ) .withDomain( cookieDomain ) .withPath( "/" ) - .withMaxAge( 0 ) + .withMaxAge( 99999 ) .build() ); } @@ -131,7 +131,7 @@ public static Response notAuthenticatedResponse( int code, String reasonPhrase, .withCookie( Cookie.builder( AUTHENTICATION_KEY, "" ) .withDomain( cookieDomain ) .withPath( "/" ) - .withMaxAge( 0 ) + .withMaxAge( 99999 ) .build() ); } From 014ed3408b3b2ab5ce352cdaa51e94ed109f6939 Mon Sep 17 00:00:00 2001 From: "igor.petrenko" Date: Fri, 13 Feb 2026 07:48:53 +0200 Subject: [PATCH 24/39] CE-135 okhttp -> jetty http client --- oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/SSO.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/SSO.java b/oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/SSO.java index 894868694..4b52f83b9 100644 --- a/oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/SSO.java +++ b/oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/SSO.java @@ -103,19 +103,24 @@ private static DateTime getExpirationTimeCookie( Date expirationInToken, Date co return expirationInToken != null ? new DateTime( expirationInToken ) : new DateTime( cookieExpiration ); } - public static Response logoutResponse( String cookieDomain ) { + public static Response logoutResponse( String cookieDomain, boolean cookieSecure ) { return Response .noContent() + .withHeader( AUTHENTICATION_KEY, "" ) .withCookie( Cookie.builder( AUTHENTICATION_KEY, "" ) .withDomain( cookieDomain ) .withPath( "/" ) .withMaxAge( 99999 ) + .withHttpOnly( true ) + .withSecure( cookieSecure ) .build() ) .withCookie( Cookie.builder( REFRESH_TOKEN_KEY, "" ) .withDomain( cookieDomain ) .withPath( "/" ) .withMaxAge( 99999 ) + .withHttpOnly( true ) + .withSecure( cookieSecure ) .build() ) .withCookie( Cookie.builder( SessionManager.COOKIE_ID, "" ) From aed8883287af9e2a761d04d604567818e03efc10 Mon Sep 17 00:00:00 2001 From: "igor.petrenko" Date: Fri, 13 Feb 2026 08:09:31 +0200 Subject: [PATCH 25/39] CE-135 okhttp -> jetty http client --- oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/SSO.java | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/SSO.java b/oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/SSO.java index 4b52f83b9..894868694 100644 --- a/oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/SSO.java +++ b/oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/SSO.java @@ -103,24 +103,19 @@ private static DateTime getExpirationTimeCookie( Date expirationInToken, Date co return expirationInToken != null ? new DateTime( expirationInToken ) : new DateTime( cookieExpiration ); } - public static Response logoutResponse( String cookieDomain, boolean cookieSecure ) { + public static Response logoutResponse( String cookieDomain ) { return Response .noContent() - .withHeader( AUTHENTICATION_KEY, "" ) .withCookie( Cookie.builder( AUTHENTICATION_KEY, "" ) .withDomain( cookieDomain ) .withPath( "/" ) .withMaxAge( 99999 ) - .withHttpOnly( true ) - .withSecure( cookieSecure ) .build() ) .withCookie( Cookie.builder( REFRESH_TOKEN_KEY, "" ) .withDomain( cookieDomain ) .withPath( "/" ) .withMaxAge( 99999 ) - .withHttpOnly( true ) - .withSecure( cookieSecure ) .build() ) .withCookie( Cookie.builder( SessionManager.COOKIE_ID, "" ) From 7e361241d5b5750f6e377a21956dbe1883f1dcb4 Mon Sep 17 00:00:00 2001 From: "igor.petrenko" Date: Fri, 13 Feb 2026 08:15:14 +0200 Subject: [PATCH 26/39] CE-135 okhttp -> jetty http client --- .../src/main/java/oap/ws/sso/SSO.java | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/SSO.java b/oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/SSO.java index 894868694..034a5147b 100644 --- a/oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/SSO.java +++ b/oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/SSO.java @@ -31,7 +31,6 @@ import oap.ws.Response; import oap.ws.SessionManager; import org.joda.time.DateTime; -import org.joda.time.Duration; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -66,16 +65,13 @@ public static Optional getRefreshAuthentication( HttpServerExchange exch } public static Response authenticatedResponse( Authentication authentication, String cookieDomain, Boolean cookieSecure ) { - long accessTokenMaxAge = new Duration( new DateTime( UTC ), new DateTime( authentication.accessToken.expires, UTC ) ).getStandardSeconds(); - long refreshTokenMaxAge = new Duration( new DateTime( UTC ), new DateTime( authentication.refreshToken.expires, UTC ) ).getStandardSeconds(); - return Response .jsonOk() .withHeader( AUTHENTICATION_KEY, authentication.accessToken.jwt ) .withCookie( Cookie.builder( AUTHENTICATION_KEY, authentication.accessToken.jwt ) .withDomain( cookieDomain ) .withPath( "/" ) - .withMaxAge( ( int ) accessTokenMaxAge ) + .withExpires( new DateTime( authentication.accessToken.expires, UTC ) ) .withHttpOnly( true ) .withSecure( cookieSecure ) .build() @@ -83,7 +79,7 @@ public static Response authenticatedResponse( Authentication authentication, Str .withCookie( Cookie.builder( REFRESH_TOKEN_KEY, authentication.refreshToken.jwt ) .withDomain( cookieDomain ) .withPath( "/" ) - .withMaxAge( ( int ) refreshTokenMaxAge ) + .withExpires( new DateTime( authentication.refreshToken.expires, UTC ) ) .withHttpOnly( true ) .withSecure( cookieSecure ) .build() @@ -109,19 +105,19 @@ public static Response logoutResponse( String cookieDomain ) { .withCookie( Cookie.builder( AUTHENTICATION_KEY, "" ) .withDomain( cookieDomain ) .withPath( "/" ) - .withMaxAge( 99999 ) + .withExpires( new DateTime( 1970, 1, 1, 1, 1, UTC ) ) .build() ) .withCookie( Cookie.builder( REFRESH_TOKEN_KEY, "" ) .withDomain( cookieDomain ) .withPath( "/" ) - .withMaxAge( 99999 ) + .withExpires( new DateTime( 1970, 1, 1, 1, 1, UTC ) ) .build() ) .withCookie( Cookie.builder( SessionManager.COOKIE_ID, "" ) .withDomain( cookieDomain ) .withPath( "/" ) - .withMaxAge( 99999 ) + .withExpires( new DateTime( 1970, 1, 1, 1, 1, UTC ) ) .build() ); } @@ -131,7 +127,7 @@ public static Response notAuthenticatedResponse( int code, String reasonPhrase, .withCookie( Cookie.builder( AUTHENTICATION_KEY, "" ) .withDomain( cookieDomain ) .withPath( "/" ) - .withMaxAge( 99999 ) + .withExpires( new DateTime( 1970, 1, 1, 1, 1, UTC ) ) .build() ); } From a11f518b24786e1e7bb5d24cb0ec84eedc51b6cb Mon Sep 17 00:00:00 2001 From: "igor.petrenko" Date: Fri, 13 Feb 2026 08:49:23 +0200 Subject: [PATCH 27/39] CE-135 okhttp -> jetty http client --- .../main/java/oap/http/test/HttpAsserts.java | 28 +-- .../java/oap/http/test/MockCookieStore.java | 164 ------------------ .../http/test/cookies/MockClientCookie.java | 19 -- .../oap/http/test/cookies/MockCookie.java | 59 ++++--- .../http/test/cookies/MockCookieFactory.java | 10 -- .../test/cookies/MockHttpCookieStorage.java | 44 +++++ .../java/oap/http/client/OapHttpClient.java | 12 ++ 7 files changed, 107 insertions(+), 229 deletions(-) delete mode 100644 oap-http/oap-http-test/src/main/java/oap/http/test/MockCookieStore.java delete mode 100644 oap-http/oap-http-test/src/main/java/oap/http/test/cookies/MockClientCookie.java delete mode 100644 oap-http/oap-http-test/src/main/java/oap/http/test/cookies/MockCookieFactory.java create mode 100644 oap-http/oap-http-test/src/main/java/oap/http/test/cookies/MockHttpCookieStorage.java diff --git a/oap-http/oap-http-test/src/main/java/oap/http/test/HttpAsserts.java b/oap-http/oap-http-test/src/main/java/oap/http/test/HttpAsserts.java index a6b2b499f..93bde2268 100644 --- a/oap-http/oap-http-test/src/main/java/oap/http/test/HttpAsserts.java +++ b/oap-http/oap-http-test/src/main/java/oap/http/test/HttpAsserts.java @@ -30,8 +30,9 @@ import lombok.extern.slf4j.Slf4j; import oap.http.Cookie; import oap.http.Response; -import oap.http.client.OapHttpClient; import oap.http.client.JettyRequestExtensions; +import oap.http.client.OapHttpClient; +import oap.http.test.cookies.MockHttpCookieStorage; import oap.json.JsonException; import oap.json.testng.JsonAsserts; import oap.testng.Asserts; @@ -44,6 +45,7 @@ import org.assertj.core.api.Assertions; import org.eclipse.jetty.client.BytesRequestContent; import org.eclipse.jetty.client.ContentResponse; +import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.InputStreamRequestContent; import org.eclipse.jetty.client.StringRequestContent; import org.eclipse.jetty.http.HttpFields; @@ -82,6 +84,8 @@ @Slf4j @SuppressWarnings( "unused" ) public class HttpAsserts { + public static final HttpClient TEST_HTTP_CLIENT = OapHttpClient.customHttpClient().cookieStore( new MockHttpCookieStorage() ).build(); + public static String httpPrefix( int port ) { return "http://localhost:" + port; } @@ -96,7 +100,7 @@ public static HttpAssertion assertGet( String uri, Pair... param } public static HttpAssertion assertGet( String uri, Map params, Map headers ) throws UncheckedIOException { - return assertGet( OapHttpClient.DEFAULT_HTTP_CLIENT, uri, params, headers ); + return assertGet( TEST_HTTP_CLIENT, uri, params, headers ); } public static HttpAssertion assertGet( org.eclipse.jetty.client.HttpClient client, String uri, Map params, Map headers ) throws UncheckedIOException { @@ -108,7 +112,7 @@ public static HttpAssertion assertGet( org.eclipse.jetty.client.HttpClient clien } public static HttpAssertion assertPost( String uri, InputStream content, @Nullable String contentType, Map headers ) { - return assertPost( OapHttpClient.DEFAULT_HTTP_CLIENT, uri, content, contentType, headers ); + return assertPost( TEST_HTTP_CLIENT, uri, content, contentType, headers ); } public static HttpAssertion assertPost( org.eclipse.jetty.client.HttpClient httpClient, String uri, InputStream content, @Nullable String contentType, Map headers ) { @@ -124,7 +128,7 @@ public static HttpAssertion assertPost( String uri, InputStream content, @Nullab } public static HttpAssertion assertPost( String uri, byte[] content, @Nullable String contentType, Map headers ) { - return assertPost( OapHttpClient.DEFAULT_HTTP_CLIENT, uri, content, contentType, headers ); + return assertPost( TEST_HTTP_CLIENT, uri, content, contentType, headers ); } public static HttpAssertion assertPost( org.eclipse.jetty.client.HttpClient httpClient, String uri, byte[] content, @Nullable String contentType, Map headers ) { @@ -136,7 +140,7 @@ public static HttpAssertion assertPost( org.eclipse.jetty.client.HttpClient http } public static HttpAssertion assertPost( String uri, String content, @Nullable String contentType, Map headers ) { - return assertPost( OapHttpClient.DEFAULT_HTTP_CLIENT, uri, content, contentType, headers ); + return assertPost( TEST_HTTP_CLIENT, uri, content, contentType, headers ); } public static HttpAssertion assertPost( org.eclipse.jetty.client.HttpClient httpClient, String uri, String content, @Nullable String contentType, Map headers ) { @@ -184,7 +188,7 @@ public static HttpAssertion assertPut( String uri, String content, String conten } public static HttpAssertion assertPut( String uri, String content, String contentType, Map headers ) { - return assertPut( OapHttpClient.DEFAULT_HTTP_CLIENT, uri, content, contentType, headers ); + return assertPut( TEST_HTTP_CLIENT, uri, content, contentType, headers ); } public static HttpAssertion assertPut( org.eclipse.jetty.client.HttpClient httpClient, String uri, String content, String contentType, Map headers ) { @@ -201,7 +205,7 @@ public static HttpAssertion assertPut( String uri, byte[] content, String conten } public static HttpAssertion assertPut( String uri, byte[] content, String contentType, Map headers ) { - return assertPut( OapHttpClient.DEFAULT_HTTP_CLIENT, uri, content, contentType, headers ); + return assertPut( TEST_HTTP_CLIENT, uri, content, contentType, headers ); } public static HttpAssertion assertPut( org.eclipse.jetty.client.HttpClient httpClient, String uri, byte[] content, String contentType, Map headers ) { @@ -218,7 +222,7 @@ public static HttpAssertion assertPut( String uri, InputStream is, String conten } public static HttpAssertion assertPut( String uri, InputStream is, String contentType, Map headers ) { - return assertPut( OapHttpClient.DEFAULT_HTTP_CLIENT, uri, is, contentType, headers ); + return assertPut( TEST_HTTP_CLIENT, uri, is, contentType, headers ); } public static HttpAssertion assertPut( org.eclipse.jetty.client.HttpClient httpClient, String uri, InputStream is, String contentType, Map headers ) { @@ -235,7 +239,7 @@ public static HttpAssertion assertPatch( String uri, byte[] content, String cont } public static HttpAssertion assertPatch( String uri, byte[] content, String contentType, Map headers ) { - return assertPatch( OapHttpClient.DEFAULT_HTTP_CLIENT, uri, content, contentType, headers ); + return assertPatch( TEST_HTTP_CLIENT, uri, content, contentType, headers ); } public static HttpAssertion assertPatch( org.eclipse.jetty.client.HttpClient httpClient, String uri, byte[] content, String contentType, Map headers ) { @@ -252,7 +256,7 @@ public static HttpAssertion assertPatch( String uri, String content, String cont } public static HttpAssertion assertPatch( String uri, String content, String contentType, Map headers ) { - return assertPatch( OapHttpClient.DEFAULT_HTTP_CLIENT, uri, content, contentType, headers ); + return assertPatch( TEST_HTTP_CLIENT, uri, content, contentType, headers ); } public static HttpAssertion assertPatch( org.eclipse.jetty.client.HttpClient httpClient, String uri, String content, String contentType, Map headers ) { @@ -270,7 +274,7 @@ public static HttpAssertion assertPatch( String uri, InputStream is, String cont public static HttpAssertion assertPatch( String uri, InputStream is, String contentType, Map headers ) { - return assertPatch( OapHttpClient.DEFAULT_HTTP_CLIENT, uri, is, contentType, headers ); + return assertPatch( TEST_HTTP_CLIENT, uri, is, contentType, headers ); } public static HttpAssertion assertPatch( org.eclipse.jetty.client.HttpClient httpClient, String uri, InputStream is, String contentType, Map headers ) { @@ -283,7 +287,7 @@ public static HttpAssertion assertPatch( org.eclipse.jetty.client.HttpClient htt } public static HttpAssertion assertDelete( String uri, Map headers ) { - return assertDelete( OapHttpClient.DEFAULT_HTTP_CLIENT, uri, headers ); + return assertDelete( TEST_HTTP_CLIENT, uri, headers ); } public static HttpAssertion assertDelete( org.eclipse.jetty.client.HttpClient httpClient, String uri, Map headers ) { diff --git a/oap-http/oap-http-test/src/main/java/oap/http/test/MockCookieStore.java b/oap-http/oap-http-test/src/main/java/oap/http/test/MockCookieStore.java deleted file mode 100644 index 3f6746781..000000000 --- a/oap-http/oap-http-test/src/main/java/oap/http/test/MockCookieStore.java +++ /dev/null @@ -1,164 +0,0 @@ -package oap.http.test; - -import oap.http.test.cookies.MockCookieFactory; -import org.apache.http.annotation.Contract; -import org.apache.http.annotation.ThreadingBehavior; -import org.apache.http.client.CookieStore; -import org.apache.http.cookie.Cookie; -import org.apache.http.cookie.CookieIdentityComparator; -import org.joda.time.DateTime; - -import javax.annotation.Nullable; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.util.ArrayList; -import java.util.Date; -import java.util.Iterator; -import java.util.List; -import java.util.TreeSet; -import java.util.concurrent.locks.ReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock; - -import static org.joda.time.DateTimeZone.UTC; - -@Contract( threading = ThreadingBehavior.SAFE ) -public class MockCookieStore implements CookieStore { - - - private final TreeSet cookies; - private transient ReadWriteLock lock; - - public MockCookieStore() { - super(); - this.cookies = new TreeSet<>( new CookieIdentityComparator() ); - this.lock = new ReentrantReadWriteLock(); - } - - private void readObject( final ObjectInputStream stream ) throws IOException, ClassNotFoundException { - stream.defaultReadObject(); - - /* Reinstantiate transient fields. */ - this.lock = new ReentrantReadWriteLock(); - } - - /** - * Adds an {@link Cookie HTTP cookie}, replacing any existing equivalent cookies. - * If the given cookie has already expired it will not be added, but existing - * values will still be removed. - * - * @param cookie the {@link Cookie cookie} to be added - * @see #addCookies(Cookie[]) - */ - @Override - public void addCookie( final Cookie cookie ) { - if( cookie != null ) { - lock.writeLock().lock(); - try { - // first remove any old cookie that is equivalent - cookies.remove( cookie ); - if( !cookie.isExpired( new DateTime( UTC ).toDate() ) ) { - cookies.add( MockCookieFactory.wrap( cookie ) ); - } - } finally { - lock.writeLock().unlock(); - } - } - } - - /** - * Adds an array of {@link Cookie HTTP cookies}. Cookies are added individually and - * in the given array order. If any of the given cookies has already expired it will - * not be added, but existing values will still be removed. - * - * @param cookies the {@link Cookie cookies} to be added - * @see #addCookie(Cookie) - */ - public void addCookies( final Cookie[] cookies ) { - if( cookies != null ) { - for( final Cookie cookie : cookies ) { - this.addCookie( cookie ); - } - } - } - - /** - * Returns an immutable array of {@link Cookie cookies} that this HTTP - * state currently contains. - * - * @return an array of {@link Cookie cookies}. - */ - @Override - public List getCookies() { - lock.readLock().lock(); - try { - //create defensive copy so it won't be concurrently modified - return new ArrayList( cookies ); - } finally { - lock.readLock().unlock(); - } - } - - /** - * Removes all of {@link Cookie cookies} in this HTTP state - * that have expired by the specified {@link java.util.Date date}. - * - * @return true if any cookies were purged. - * @see Cookie#isExpired(Date) - */ - @Override - public boolean clearExpired( final Date ignored ) { - if( ignored == null ) { - return false; - } - - Date date = DateTime.now( UTC ).toDate(); - - lock.writeLock().lock(); - try { - boolean removed = false; - for( final Iterator it = cookies.iterator(); it.hasNext(); ) { - if( it.next().isExpired( date ) ) { - it.remove(); - removed = true; - } - } - return removed; - } finally { - lock.writeLock().unlock(); - } - } - - /** - * Clears all cookies. - */ - @Override - public void clear() { - lock.writeLock().lock(); - try { - cookies.clear(); - } finally { - lock.writeLock().unlock(); - } - } - - @Override - public String toString() { - lock.readLock().lock(); - try { - return cookies.toString(); - } finally { - lock.readLock().unlock(); - } - } - - @Nullable - public Cookie getCookie( String name ) { - for( Cookie cookie : getCookies() ) { - if( name.equals( cookie.getName() ) ) { - return cookie; - } - } - - return null; - } -} diff --git a/oap-http/oap-http-test/src/main/java/oap/http/test/cookies/MockClientCookie.java b/oap-http/oap-http-test/src/main/java/oap/http/test/cookies/MockClientCookie.java deleted file mode 100644 index b90e480e7..000000000 --- a/oap-http/oap-http-test/src/main/java/oap/http/test/cookies/MockClientCookie.java +++ /dev/null @@ -1,19 +0,0 @@ -package oap.http.test.cookies; - -import org.apache.http.cookie.ClientCookie; - -public class MockClientCookie extends MockCookie implements ClientCookie { - MockClientCookie( T cookie ) { - super( cookie ); - } - - @Override - public String getAttribute( String name ) { - return cookie.getAttribute( name ); - } - - @Override - public boolean containsAttribute( String name ) { - return cookie.containsAttribute( name ); - } -} diff --git a/oap-http/oap-http-test/src/main/java/oap/http/test/cookies/MockCookie.java b/oap-http/oap-http-test/src/main/java/oap/http/test/cookies/MockCookie.java index 05f6ccf45..c2bb88806 100644 --- a/oap-http/oap-http-test/src/main/java/oap/http/test/cookies/MockCookie.java +++ b/oap-http/oap-http-test/src/main/java/oap/http/test/cookies/MockCookie.java @@ -1,16 +1,17 @@ package oap.http.test.cookies; -import org.apache.http.cookie.Cookie; +import org.eclipse.jetty.http.HttpCookie; import org.joda.time.DateTime; -import java.util.Date; +import java.time.Instant; +import java.util.Map; import static org.joda.time.DateTimeZone.UTC; -public class MockCookie implements Cookie { - protected final T cookie; +public class MockCookie implements HttpCookie { + protected final HttpCookie cookie; - MockCookie( T cookie ) { + MockCookie( HttpCookie cookie ) { this.cookie = cookie; } @@ -30,49 +31,59 @@ public String getComment() { } @Override - public String getCommentURL() { - return cookie.getCommentURL(); + public String getDomain() { + return cookie.getDomain(); } @Override - public Date getExpiryDate() { - return cookie.getExpiryDate(); + public String getPath() { + return cookie.getPath(); } @Override - public boolean isPersistent() { - return cookie.isPersistent(); + public boolean isSecure() { + return cookie.isSecure(); } @Override - public String getDomain() { - return cookie.getDomain(); + public SameSite getSameSite() { + return cookie.getSameSite(); } @Override - public String getPath() { - return cookie.getPath(); + public boolean isHttpOnly() { + return cookie.isHttpOnly(); } @Override - public int[] getPorts() { - return cookie.getPorts(); + public boolean isPartitioned() { + return cookie.isPartitioned(); } @Override - public boolean isSecure() { - return cookie.isSecure(); + public int getVersion() { + return cookie.getVersion(); } @Override - public int getVersion() { - return cookie.getVersion(); + public Map getAttributes() { + return cookie.getAttributes(); + } + + @Override + public Instant getExpires() { + return cookie.getExpires(); + } + + @Override + public long getMaxAge() { + return cookie.getMaxAge(); } @Override - public boolean isExpired( Date ignored ) { - Date date = DateTime.now( UTC ).toDate(); + public boolean isExpired() { + DateTime date = new DateTime( UTC ); - return cookie.isExpired( date ); + return date.isAfter( cookie.getExpires().toEpochMilli() ); } } diff --git a/oap-http/oap-http-test/src/main/java/oap/http/test/cookies/MockCookieFactory.java b/oap-http/oap-http-test/src/main/java/oap/http/test/cookies/MockCookieFactory.java deleted file mode 100644 index 11f6fcbf8..000000000 --- a/oap-http/oap-http-test/src/main/java/oap/http/test/cookies/MockCookieFactory.java +++ /dev/null @@ -1,10 +0,0 @@ -package oap.http.test.cookies; - -import org.apache.http.cookie.ClientCookie; -import org.apache.http.cookie.Cookie; - -public class MockCookieFactory { - public static Cookie wrap( Cookie cookie ) { - return cookie instanceof ClientCookie ? new MockClientCookie<>( ( ClientCookie ) cookie ) : new MockCookie<>( cookie ); - } -} diff --git a/oap-http/oap-http-test/src/main/java/oap/http/test/cookies/MockHttpCookieStorage.java b/oap-http/oap-http-test/src/main/java/oap/http/test/cookies/MockHttpCookieStorage.java new file mode 100644 index 000000000..0b780dc07 --- /dev/null +++ b/oap-http/oap-http-test/src/main/java/oap/http/test/cookies/MockHttpCookieStorage.java @@ -0,0 +1,44 @@ +package oap.http.test.cookies; + +import org.eclipse.jetty.http.HttpCookie; +import org.eclipse.jetty.http.HttpCookieStore; + +import java.net.URI; +import java.util.List; + +public class MockHttpCookieStorage implements HttpCookieStore { + private final HttpCookieStore cookieStore; + + public MockHttpCookieStorage( HttpCookieStore cookieStore ) { + this.cookieStore = cookieStore; + } + + public MockHttpCookieStorage() { + this( new Default() ); + } + + @Override + public boolean add( URI uri, HttpCookie httpCookie ) { + return cookieStore.add( uri, new MockCookie( httpCookie ) ); + } + + @Override + public List all() { + return cookieStore.all(); + } + + @Override + public List match( URI uri ) { + return cookieStore.match( uri ); + } + + @Override + public boolean remove( URI uri, HttpCookie httpCookie ) { + return cookieStore.remove( uri, httpCookie ); + } + + @Override + public boolean clear() { + return cookieStore.clear(); + } +} diff --git a/oap-http/oap-http/src/main/java/oap/http/client/OapHttpClient.java b/oap-http/oap-http/src/main/java/oap/http/client/OapHttpClient.java index e98860ad7..41e8b995d 100644 --- a/oap-http/oap-http/src/main/java/oap/http/client/OapHttpClient.java +++ b/oap-http/oap-http/src/main/java/oap/http/client/OapHttpClient.java @@ -10,6 +10,7 @@ import org.eclipse.jetty.client.AbstractConnectorHttpClientTransport; import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.WWWAuthenticationProtocolHandler; +import org.eclipse.jetty.http.HttpCookieStore; import org.eclipse.jetty.io.ClientConnector; import org.eclipse.jetty.util.thread.QueuedThreadPool; import org.eclipse.jetty.util.thread.VirtualThreadPool; @@ -45,6 +46,7 @@ public static class OapHttpClientBuilder { public int maxConnectionsPerDestination = 64; public boolean dnsjava = false; private String metrics; + private HttpCookieStore cookieStore; public OapHttpClientBuilder transport( AbstractConnectorHttpClientTransport httpClientTransport ) { this.httpClientTransport = httpClientTransport; @@ -82,6 +84,12 @@ public OapHttpClientBuilder metrics( String name ) { return this; } + public OapHttpClientBuilder cookieStore( HttpCookieStore cookieStore ) { + this.cookieStore = cookieStore; + + return this; + } + @SneakyThrows public HttpClient build() { HttpClient httpClient = httpClientTransport != null ? new HttpClient( httpClientTransport ) : new HttpClient(); @@ -92,6 +100,10 @@ public HttpClient build() { httpClient.setFollowRedirects( followRedirects ); httpClient.setMaxConnectionsPerDestination( maxConnectionsPerDestination ); + if( cookieStore != null ) { + httpClient.setHttpCookieStore( cookieStore ); + } + if( dnsjava ) { httpClient.setSocketAddressResolver( ( host, port, context, promise ) -> { try { From 4b4d942b8b23fddd3012d01136514970f41eb7d2 Mon Sep 17 00:00:00 2001 From: "igor.petrenko" Date: Fri, 13 Feb 2026 09:57:33 +0200 Subject: [PATCH 28/39] CE-135 okhttp -> jetty http client --- .../main/java/oap/http/test/HttpAsserts.java | 3 +- .../oap/http/test/cookies/MockCookie.java | 89 ------------------- .../test/cookies/MockHttpCookieStorage.java | 44 --------- .../http/server/nio/HttpServerExchange.java | 4 +- .../main/java/oap/ws/sso/Authentication.java | 4 +- 5 files changed, 6 insertions(+), 138 deletions(-) delete mode 100644 oap-http/oap-http-test/src/main/java/oap/http/test/cookies/MockCookie.java delete mode 100644 oap-http/oap-http-test/src/main/java/oap/http/test/cookies/MockHttpCookieStorage.java diff --git a/oap-http/oap-http-test/src/main/java/oap/http/test/HttpAsserts.java b/oap-http/oap-http-test/src/main/java/oap/http/test/HttpAsserts.java index 93bde2268..8d08359dc 100644 --- a/oap-http/oap-http-test/src/main/java/oap/http/test/HttpAsserts.java +++ b/oap-http/oap-http-test/src/main/java/oap/http/test/HttpAsserts.java @@ -32,7 +32,6 @@ import oap.http.Response; import oap.http.client.JettyRequestExtensions; import oap.http.client.OapHttpClient; -import oap.http.test.cookies.MockHttpCookieStorage; import oap.json.JsonException; import oap.json.testng.JsonAsserts; import oap.testng.Asserts; @@ -84,7 +83,7 @@ @Slf4j @SuppressWarnings( "unused" ) public class HttpAsserts { - public static final HttpClient TEST_HTTP_CLIENT = OapHttpClient.customHttpClient().cookieStore( new MockHttpCookieStorage() ).build(); + public static final HttpClient TEST_HTTP_CLIENT = OapHttpClient.customHttpClient().build(); public static String httpPrefix( int port ) { return "http://localhost:" + port; diff --git a/oap-http/oap-http-test/src/main/java/oap/http/test/cookies/MockCookie.java b/oap-http/oap-http-test/src/main/java/oap/http/test/cookies/MockCookie.java deleted file mode 100644 index c2bb88806..000000000 --- a/oap-http/oap-http-test/src/main/java/oap/http/test/cookies/MockCookie.java +++ /dev/null @@ -1,89 +0,0 @@ -package oap.http.test.cookies; - -import org.eclipse.jetty.http.HttpCookie; -import org.joda.time.DateTime; - -import java.time.Instant; -import java.util.Map; - -import static org.joda.time.DateTimeZone.UTC; - -public class MockCookie implements HttpCookie { - protected final HttpCookie cookie; - - MockCookie( HttpCookie cookie ) { - this.cookie = cookie; - } - - @Override - public String getName() { - return cookie.getName(); - } - - @Override - public String getValue() { - return cookie.getValue(); - } - - @Override - public String getComment() { - return cookie.getComment(); - } - - @Override - public String getDomain() { - return cookie.getDomain(); - } - - @Override - public String getPath() { - return cookie.getPath(); - } - - @Override - public boolean isSecure() { - return cookie.isSecure(); - } - - @Override - public SameSite getSameSite() { - return cookie.getSameSite(); - } - - @Override - public boolean isHttpOnly() { - return cookie.isHttpOnly(); - } - - @Override - public boolean isPartitioned() { - return cookie.isPartitioned(); - } - - @Override - public int getVersion() { - return cookie.getVersion(); - } - - @Override - public Map getAttributes() { - return cookie.getAttributes(); - } - - @Override - public Instant getExpires() { - return cookie.getExpires(); - } - - @Override - public long getMaxAge() { - return cookie.getMaxAge(); - } - - @Override - public boolean isExpired() { - DateTime date = new DateTime( UTC ); - - return date.isAfter( cookie.getExpires().toEpochMilli() ); - } -} diff --git a/oap-http/oap-http-test/src/main/java/oap/http/test/cookies/MockHttpCookieStorage.java b/oap-http/oap-http-test/src/main/java/oap/http/test/cookies/MockHttpCookieStorage.java deleted file mode 100644 index 0b780dc07..000000000 --- a/oap-http/oap-http-test/src/main/java/oap/http/test/cookies/MockHttpCookieStorage.java +++ /dev/null @@ -1,44 +0,0 @@ -package oap.http.test.cookies; - -import org.eclipse.jetty.http.HttpCookie; -import org.eclipse.jetty.http.HttpCookieStore; - -import java.net.URI; -import java.util.List; - -public class MockHttpCookieStorage implements HttpCookieStore { - private final HttpCookieStore cookieStore; - - public MockHttpCookieStorage( HttpCookieStore cookieStore ) { - this.cookieStore = cookieStore; - } - - public MockHttpCookieStorage() { - this( new Default() ); - } - - @Override - public boolean add( URI uri, HttpCookie httpCookie ) { - return cookieStore.add( uri, new MockCookie( httpCookie ) ); - } - - @Override - public List all() { - return cookieStore.all(); - } - - @Override - public List match( URI uri ) { - return cookieStore.match( uri ); - } - - @Override - public boolean remove( URI uri, HttpCookie httpCookie ) { - return cookieStore.remove( uri, httpCookie ); - } - - @Override - public boolean clear() { - return cookieStore.clear(); - } -} diff --git a/oap-http/oap-http/src/main/java/oap/http/server/nio/HttpServerExchange.java b/oap-http/oap-http/src/main/java/oap/http/server/nio/HttpServerExchange.java index 8cb032d81..90f44f921 100644 --- a/oap-http/oap-http/src/main/java/oap/http/server/nio/HttpServerExchange.java +++ b/oap-http/oap-http/src/main/java/oap/http/server/nio/HttpServerExchange.java @@ -273,7 +273,7 @@ public void responseOk( Object content, boolean raw, String contentType ) { } public byte[] readBody() throws IOException { - var ret = new ByteArrayOutputStream(); + ByteArrayOutputStream ret = new ByteArrayOutputStream(); IOUtils.copy( getInputStream(), ret ); return ret.toByteArray(); @@ -283,7 +283,7 @@ public void responseStream( Stream content, boolean raw, String contentType ) setStatusCode( HTTP_OK ); setResponseHeader( Headers.CONTENT_TYPE, contentType ); - var out = exchange.getOutputStream(); + OutputStream out = exchange.getOutputStream(); content .map( v -> contentToString( raw, v, contentType ) ) diff --git a/oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/Authentication.java b/oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/Authentication.java index 8e31fa33e..6051ffdc0 100644 --- a/oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/Authentication.java +++ b/oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/Authentication.java @@ -33,6 +33,8 @@ import java.io.Serializable; import java.util.Date; +import static org.joda.time.DateTimeZone.UTC; + @ToString @EqualsAndHashCode public class Authentication implements Serializable { @@ -47,7 +49,7 @@ public Authentication( Token accessToken, Token refreshToken, UserView user ) { this.accessToken = accessToken; this.refreshToken = refreshToken; this.user = user; - this.created = new DateTime(); + this.created = new DateTime( System.currentTimeMillis(), UTC ); } @ToString From e8887d5f91e5c0c4152813a61263a4bd265a55c2 Mon Sep 17 00:00:00 2001 From: "igor.petrenko" Date: Fri, 13 Feb 2026 10:06:42 +0200 Subject: [PATCH 29/39] CE-135 okhttp -> jetty http client --- .../src/main/java/oap/ws/sso/JwtTokenGenerator.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/JwtTokenGenerator.java b/oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/JwtTokenGenerator.java index d89b9eb7b..384bd7e26 100644 --- a/oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/JwtTokenGenerator.java +++ b/oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/JwtTokenGenerator.java @@ -27,7 +27,6 @@ import com.auth0.jwt.JWT; import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.exceptions.JWTCreationException; -import org.joda.time.DateTimeUtils; import java.util.Date; @@ -51,7 +50,7 @@ public JwtTokenGenerator( String accessSecret, String refreshSecret, String issu public Authentication.Token generateAccessToken( User user ) throws JWTCreationException { Algorithm algorithm = Algorithm.HMAC256( accessSecret ); - Date expiresAt = new org.joda.time.DateTime( DateTimeUtils.currentTimeMillis() + accessSecretExpiration, UTC ).toDate(); + Date expiresAt = new org.joda.time.DateTime( System.currentTimeMillis() + accessSecretExpiration, UTC ).toDate(); return new Authentication.Token( expiresAt, JWT.create() .withClaim( "id", user.getId() ) .withClaim( "user", user.getEmail() ) @@ -64,7 +63,7 @@ public Authentication.Token generateAccessToken( User user ) throws JWTCreationE public Authentication.Token generateAccessTokenWithActiveOrgId( User user, String activeOrganization ) throws JWTCreationException { Algorithm algorithm = Algorithm.HMAC256( accessSecret ); - Date expiresAt = new org.joda.time.DateTime( DateTimeUtils.currentTimeMillis() + accessSecretExpiration, UTC ).toDate(); + Date expiresAt = new org.joda.time.DateTime( System.currentTimeMillis() + accessSecretExpiration, UTC ).toDate(); return new Authentication.Token( expiresAt, JWT.create() .withClaim( "id", user.getId() ) .withClaim( "user", user.getEmail() ) @@ -78,7 +77,7 @@ public Authentication.Token generateAccessTokenWithActiveOrgId( User user, Strin public Authentication.Token generateRefreshToken( User user ) throws JWTCreationException { Algorithm algorithm = Algorithm.HMAC256( refreshSecret ); - Date expiresAt = new org.joda.time.DateTime( DateTimeUtils.currentTimeMillis() + refreshSecretExpiration, UTC ).toDate(); + Date expiresAt = new org.joda.time.DateTime( System.currentTimeMillis() + refreshSecretExpiration, UTC ).toDate(); return new Authentication.Token( expiresAt, JWT.create() .withClaim( "id", user.getId() ) .withClaim( "user", user.getEmail() ) From 79d4032f9be91170b2d7c7aac3203d6548f50e0a Mon Sep 17 00:00:00 2001 From: "igor.petrenko" Date: Fri, 13 Feb 2026 10:11:26 +0200 Subject: [PATCH 30/39] CE-135 okhttp -> jetty http client --- .../java/oap/ws/sso/JwtTokenGeneratorExtractorTest.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/oap-ws/oap-ws-sso-api/src/test/java/oap/ws/sso/JwtTokenGeneratorExtractorTest.java b/oap-ws/oap-ws-sso-api/src/test/java/oap/ws/sso/JwtTokenGeneratorExtractorTest.java index 750cedb3a..5f8c0f499 100644 --- a/oap-ws/oap-ws-sso-api/src/test/java/oap/ws/sso/JwtTokenGeneratorExtractorTest.java +++ b/oap-ws/oap-ws-sso-api/src/test/java/oap/ws/sso/JwtTokenGeneratorExtractorTest.java @@ -30,7 +30,6 @@ import oap.ws.sso.AbstractUserTest.TestSecurityRolesProvider; import oap.ws.sso.AbstractUserTest.TestUser; import org.joda.time.DateTime; -import org.joda.time.DateTimeUtils; import org.testng.annotations.Test; import static oap.ws.sso.JWTExtractor.TokenStatus.VALID; @@ -49,12 +48,12 @@ public JwtTokenGeneratorExtractorTest() { @Test public void generateAndExtractToken() { - DateTimeUtils.setCurrentMillisFixed( DateTimeUtils.currentTimeMillis() ); - Authentication.Token token = jwtTokenGenerator.generateAccessToken( new TestUser( "email@email.com", "password", Pair.of( "org1", "ADMIN" ) ) ); assertNotNull( token.expires ); assertThat( token.jwt ).isNotEmpty(); - assertThat( token.expires ).isEqualTo( new DateTime( UTC ).plusMinutes( 15 ).toDate() ); + assertThat( token.expires ) + .isBeforeOrEqualTo( new DateTime( UTC ).plusMinutes( 15 ).toDate() ) + .isAfterOrEqualTo( new DateTime( UTC ).plusMinutes( 14 ).toDate() ); assertThat( jwtExtractor.verifyToken( token.jwt ) ).isEqualTo( VALID ); JwtToken jwtToken = jwtExtractor.decodeJWT( token.jwt ); From 6f9e7437021e128d977abc8995962e90117012a0 Mon Sep 17 00:00:00 2001 From: "igor.petrenko" Date: Fri, 13 Feb 2026 10:26:33 +0200 Subject: [PATCH 31/39] CE-135 okhttp -> jetty http client --- oap-ws/oap-ws/src/main/java/oap/ws/WebService.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/oap-ws/oap-ws/src/main/java/oap/ws/WebService.java b/oap-ws/oap-ws/src/main/java/oap/ws/WebService.java index 2dce5f731..cec737393 100644 --- a/oap-ws/oap-ws/src/main/java/oap/ws/WebService.java +++ b/oap-ws/oap-ws/src/main/java/oap/ws/WebService.java @@ -46,6 +46,8 @@ import java.util.Optional; import java.util.concurrent.CompletableFuture; +import static org.joda.time.DateTimeZone.UTC; + @Slf4j public class WebService implements HttpHandler { public final boolean compressionSupport; @@ -166,7 +168,7 @@ private void handleInternal( InvocationContext context ) { if( context.session != null && !containsSessionCookie( context.exchange.responseCookies() ) ) { oap.http.Cookie cookie = oap.http.Cookie.builder( SessionManager.COOKIE_ID, context.session.id ) .withPath( sessionManager.cookiePath ) - .withExpires( DateTime.now().plus( sessionManager.cookieExpiration ) ) + .withExpires( new DateTime( System.currentTimeMillis(), UTC ).plus( sessionManager.cookieExpiration ) ) .withDomain( sessionManager.cookieDomain ) .withSecure( sessionManager.cookieSecure ) .withHttpOnly( true ) From 73692ceee3398f54d54e9e66138a23085e1a8f8a Mon Sep 17 00:00:00 2001 From: "igor.petrenko" Date: Fri, 13 Feb 2026 10:51:10 +0200 Subject: [PATCH 32/39] CE-135 okhttp -> jetty http client --- .../http/server/nio/HttpServerExchange.java | 6 ++-- .../src/main/java/oap/ws/WebService.java | 32 ++++++++----------- .../java/oap/ws/interceptor/Interceptors.java | 8 ++--- 3 files changed, 20 insertions(+), 26 deletions(-) diff --git a/oap-http/oap-http/src/main/java/oap/http/server/nio/HttpServerExchange.java b/oap-http/oap-http/src/main/java/oap/http/server/nio/HttpServerExchange.java index 90f44f921..b252706e3 100644 --- a/oap-http/oap-http/src/main/java/oap/http/server/nio/HttpServerExchange.java +++ b/oap-http/oap-http/src/main/java/oap/http/server/nio/HttpServerExchange.java @@ -328,10 +328,8 @@ private Cookie convert( oap.http.Cookie cookie ) { .setSecure( cookie.isSecure() ) .setHttpOnly( cookie.isHttpOnly() ) .setVersion( cookie.getVersion() ) - .setComment( cookie.getComment() ); - if( cookie.getMaxAge() != null ) { - newCookie.setMaxAge( cookie.getMaxAge() ); - } + .setComment( cookie.getComment() ) + .setMaxAge( cookie.getMaxAge() ); return newCookie; } diff --git a/oap-ws/oap-ws/src/main/java/oap/ws/WebService.java b/oap-ws/oap-ws/src/main/java/oap/ws/WebService.java index cec737393..980c66738 100644 --- a/oap-ws/oap-ws/src/main/java/oap/ws/WebService.java +++ b/oap-ws/oap-ws/src/main/java/oap/ws/WebService.java @@ -44,7 +44,6 @@ import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.concurrent.CompletableFuture; import static org.joda.time.DateTimeZone.UTC; @@ -177,11 +176,10 @@ private void handleInternal( InvocationContext context ) { context.exchange.setResponseCookie( cookie ); } - CompletableFuture responseFuture = produceResultResponse( context.method, wsMethod, context.method.invoke( instance, paramValues ) ); - responseFuture.thenAccept( response -> { - Interceptors.after( interceptors, response, context ); - response.send( context.exchange ); - } ); + Response response = produceResultResponse( context.method, wsMethod, context.method.invoke( instance, paramValues ) ); + + Interceptors.after( interceptors, response, context ); + response.send( context.exchange ); } ); } @@ -192,27 +190,25 @@ private boolean containsSessionCookie( Iterable cookies ) { return false; } - private CompletableFuture produceResultResponse( Reflection.Method method, Optional wsMethod, Object result ) { + private Response produceResultResponse( Reflection.Method method, Optional wsMethod, Object result ) { boolean isRaw = wsMethod.map( WsMethod::raw ).orElse( false ); String produces = wsMethod.map( WsMethod::produces ) .orElse( Http.ContentType.APPLICATION_JSON ); if( method.isVoid() ) { - return CompletableFuture.completedFuture( Response.noContent() ); - } else if( result instanceof Response response ) return CompletableFuture.completedFuture( response ); - else if( result instanceof Optional optResult ) return CompletableFuture.completedFuture( optResult.isEmpty() + return Response.noContent(); + } else if( result instanceof Response response ) return response; + else if( result instanceof Optional optResult ) return optResult.isEmpty() ? Response.notFound() - : Response.ok().withBody( optResult.get(), isRaw ).withContentType( produces ) ); + : Response.ok().withBody( optResult.get(), isRaw ).withContentType( produces ); else if( result instanceof Result resultResult ) if( resultResult.isSuccess() ) - return CompletableFuture.completedFuture( Response.ok().withBody( resultResult.successValue, isRaw ).withContentType( produces ) ); - else return CompletableFuture.completedFuture( new Response( Http.StatusCode.INTERNAL_SERVER_ERROR, "" ) + return Response.ok().withBody( resultResult.successValue, isRaw ).withContentType( produces ); + else return new Response( Http.StatusCode.INTERNAL_SERVER_ERROR, "" ) .withBody( resultResult.failureValue, false ) - .withContentType( Http.ContentType.APPLICATION_JSON ) ); + .withContentType( Http.ContentType.APPLICATION_JSON ); else if( result instanceof java.util.stream.Stream stream ) { - return CompletableFuture.completedFuture( Response.ok().withBody( stream, isRaw ).withContentType( produces ) ); - } else if( result instanceof CompletableFuture future ) { - return future.thenCompose( fResult -> produceResultResponse( method, wsMethod, fResult ) ); - } else return CompletableFuture.completedFuture( Response.ok().withBody( result, isRaw ).withContentType( produces ) ); + return Response.ok().withBody( stream, isRaw ).withContentType( produces ); + } else return Response.ok().withBody( result, isRaw ).withContentType( produces ); } @Override diff --git a/oap-ws/oap-ws/src/main/java/oap/ws/interceptor/Interceptors.java b/oap-ws/oap-ws/src/main/java/oap/ws/interceptor/Interceptors.java index a61742799..bdf754db9 100644 --- a/oap-ws/oap-ws/src/main/java/oap/ws/interceptor/Interceptors.java +++ b/oap-ws/oap-ws/src/main/java/oap/ws/interceptor/Interceptors.java @@ -34,17 +34,17 @@ @Slf4j public class Interceptors { public static Optional before( List interceptors, InvocationContext context ) { - for( var interceptor : interceptors ) { + for( Interceptor interceptor : interceptors ) { log.trace( "running before call {}", interceptor.getClass().getSimpleName() ); - var response = interceptor.before( context ); + Optional response = interceptor.before( context ); if( response.isPresent() ) return response; } return Optional.empty(); } public static void after( List interceptors, Response response, InvocationContext context ) { - for( var i = interceptors.size() - 1; i >= 0; i-- ) { - var interceptor = interceptors.get( i ); + for( int i = interceptors.size() - 1; i >= 0; i-- ) { + Interceptor interceptor = interceptors.get( i ); log.trace( "running after call {}", interceptor.getClass().getSimpleName() ); interceptor.after( response, context ); } From 63c6701f47d019d42710ca31770dcfc6c99a7eb9 Mon Sep 17 00:00:00 2001 From: "igor.petrenko" Date: Fri, 13 Feb 2026 10:56:07 +0200 Subject: [PATCH 33/39] CE-135 okhttp -> jetty http client --- .../main/java/oap/http/server/nio/HttpServerExchange.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/oap-http/oap-http/src/main/java/oap/http/server/nio/HttpServerExchange.java b/oap-http/oap-http/src/main/java/oap/http/server/nio/HttpServerExchange.java index b252706e3..90f44f921 100644 --- a/oap-http/oap-http/src/main/java/oap/http/server/nio/HttpServerExchange.java +++ b/oap-http/oap-http/src/main/java/oap/http/server/nio/HttpServerExchange.java @@ -328,8 +328,10 @@ private Cookie convert( oap.http.Cookie cookie ) { .setSecure( cookie.isSecure() ) .setHttpOnly( cookie.isHttpOnly() ) .setVersion( cookie.getVersion() ) - .setComment( cookie.getComment() ) - .setMaxAge( cookie.getMaxAge() ); + .setComment( cookie.getComment() ); + if( cookie.getMaxAge() != null ) { + newCookie.setMaxAge( cookie.getMaxAge() ); + } return newCookie; } From 5ba467e7260289a6c0024b766675538c20c3efc4 Mon Sep 17 00:00:00 2001 From: "igor.petrenko" Date: Fri, 13 Feb 2026 11:07:19 +0200 Subject: [PATCH 34/39] CE-135 okhttp -> jetty http client --- oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/SSO.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/SSO.java b/oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/SSO.java index 034a5147b..e9995af2b 100644 --- a/oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/SSO.java +++ b/oap-ws/oap-ws-sso-api/src/main/java/oap/ws/sso/SSO.java @@ -102,19 +102,19 @@ private static DateTime getExpirationTimeCookie( Date expirationInToken, Date co public static Response logoutResponse( String cookieDomain ) { return Response .noContent() - .withCookie( Cookie.builder( AUTHENTICATION_KEY, "" ) + .withCookie( Cookie.builder( AUTHENTICATION_KEY, "" ) .withDomain( cookieDomain ) .withPath( "/" ) .withExpires( new DateTime( 1970, 1, 1, 1, 1, UTC ) ) .build() ) - .withCookie( Cookie.builder( REFRESH_TOKEN_KEY, "" ) + .withCookie( Cookie.builder( REFRESH_TOKEN_KEY, "" ) .withDomain( cookieDomain ) .withPath( "/" ) .withExpires( new DateTime( 1970, 1, 1, 1, 1, UTC ) ) .build() ) - .withCookie( Cookie.builder( SessionManager.COOKIE_ID, "" ) + .withCookie( Cookie.builder( SessionManager.COOKIE_ID, "" ) .withDomain( cookieDomain ) .withPath( "/" ) .withExpires( new DateTime( 1970, 1, 1, 1, 1, UTC ) ) @@ -124,7 +124,7 @@ public static Response logoutResponse( String cookieDomain ) { public static Response notAuthenticatedResponse( int code, String reasonPhrase, String cookieDomain ) { return new Response( code, reasonPhrase ) - .withCookie( Cookie.builder( AUTHENTICATION_KEY, "" ) + .withCookie( Cookie.builder( AUTHENTICATION_KEY, "" ) .withDomain( cookieDomain ) .withPath( "/" ) .withExpires( new DateTime( 1970, 1, 1, 1, 1, UTC ) ) From 2f829c1743d9cdc1f110ffd1376d7ac8a206b939 Mon Sep 17 00:00:00 2001 From: "igor.petrenko" Date: Fri, 13 Feb 2026 11:32:04 +0200 Subject: [PATCH 35/39] CE-135 okhttp -> jetty http client --- oap-ws/oap-ws/src/main/java/oap/ws/WebService.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/oap-ws/oap-ws/src/main/java/oap/ws/WebService.java b/oap-ws/oap-ws/src/main/java/oap/ws/WebService.java index 980c66738..289b1b417 100644 --- a/oap-ws/oap-ws/src/main/java/oap/ws/WebService.java +++ b/oap-ws/oap-ws/src/main/java/oap/ws/WebService.java @@ -23,7 +23,6 @@ */ package oap.ws; -import io.undertow.server.handlers.Cookie; import lombok.extern.slf4j.Slf4j; import oap.http.Http; import oap.http.server.nio.HttpHandler; @@ -164,7 +163,9 @@ private void handleInternal( InvocationContext context ) { return; } - if( context.session != null && !containsSessionCookie( context.exchange.responseCookies() ) ) { + Response response = produceResultResponse( context.method, wsMethod, context.method.invoke( instance, paramValues ) ); + + if( context.session != null && !containsSessionCookie( response.cookies ) ) { oap.http.Cookie cookie = oap.http.Cookie.builder( SessionManager.COOKIE_ID, context.session.id ) .withPath( sessionManager.cookiePath ) .withExpires( new DateTime( System.currentTimeMillis(), UTC ).plus( sessionManager.cookieExpiration ) ) @@ -173,18 +174,16 @@ private void handleInternal( InvocationContext context ) { .withHttpOnly( true ) .build(); - context.exchange.setResponseCookie( cookie ); + response.cookies.add( cookie ); } - Response response = produceResultResponse( context.method, wsMethod, context.method.invoke( instance, paramValues ) ); - Interceptors.after( interceptors, response, context ); response.send( context.exchange ); } ); } - private boolean containsSessionCookie( Iterable cookies ) { - for( Cookie p : cookies ) { + private boolean containsSessionCookie( Iterable cookies ) { + for( oap.http.Cookie p : cookies ) { if( SessionManager.COOKIE_ID.equals( p.getName() ) ) return true; } return false; From db3b15c236c2790880f97e15eca4267f7c84b146 Mon Sep 17 00:00:00 2001 From: "igor.petrenko" Date: Fri, 13 Feb 2026 11:45:08 +0200 Subject: [PATCH 36/39] CE-135 okhttp -> jetty http client --- .../oap-http/src/main/java/oap/http/Response.java | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/oap-http/oap-http/src/main/java/oap/http/Response.java b/oap-http/oap-http/src/main/java/oap/http/Response.java index fc514af68..b90c3c4c4 100644 --- a/oap-http/oap-http/src/main/java/oap/http/Response.java +++ b/oap-http/oap-http/src/main/java/oap/http/Response.java @@ -16,12 +16,14 @@ import javax.annotation.Nullable; import java.io.Closeable; import java.io.InputStream; +import java.util.ArrayDeque; +import java.util.Deque; import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; -import java.util.stream.Collectors; import static java.nio.charset.StandardCharsets.UTF_8; import static oap.http.Http.ContentType.APPLICATION_OCTET_STREAM; @@ -155,7 +157,12 @@ public void close() { inputStream = null; } - public Map getHeaders() { - return headers.stream().collect( Collectors.toMap( p -> p._1, p -> p._2 ) ); + public Map> getHeaders() { + LinkedHashMap> ret = new LinkedHashMap<>(); + + for( Pair header : headers ) { + ret.computeIfAbsent( header._1, _ -> new ArrayDeque<>() ).add( header._2 ); + } + return ret; } } From 33ca72350667ab8ba7dc32d64b3fe68f30a794f4 Mon Sep 17 00:00:00 2001 From: "igor.petrenko" Date: Fri, 13 Feb 2026 15:46:07 +0200 Subject: [PATCH 37/39] CE-135 okhttp -> jetty http client --- .../java/oap/compression/Compression.java | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/oap-stdlib/src/main/java/oap/compression/Compression.java b/oap-stdlib/src/main/java/oap/compression/Compression.java index fe6de2669..db72f0aab 100644 --- a/oap-stdlib/src/main/java/oap/compression/Compression.java +++ b/oap-stdlib/src/main/java/oap/compression/Compression.java @@ -29,6 +29,7 @@ import org.apache.commons.io.IOUtils; import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.function.BiFunction; @@ -58,6 +59,22 @@ public static OutputStream gzip( OutputStream os ) { return gzip( os, DEFAULT_BUFFER_SIZE ); } + public static void gzip( OutputStream out, byte[] bytes, int offset, int length ) throws IOException { + try( OutputStream gos = gzip( out ) ) { + gos.write( bytes, offset, length ); + } + } + + public static byte[] gzip( byte[] bytes, int offset, int length ) throws IOException { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + gzip( byteArrayOutputStream, bytes, offset, length ); + return byteArrayOutputStream.toByteArray(); + } + + public static byte[] gzip( byte[] bytes ) throws IOException { + return gzip( bytes, 0, bytes.length ); + } + public static OutputStream gzip( OutputStream os, int bufferSize ) { return gzipOutputStreamSupplier.apply( os, bufferSize ); } @@ -72,7 +89,7 @@ public static oap.io.content.ContentWriter ofGzip() { @Override @SneakyThrows public void write( OutputStream os, String object ) { - try( var gos = gzip( os ) ) { + try( OutputStream gos = gzip( os ) ) { gos.write( object.getBytes( UTF_8 ) ); } } @@ -86,8 +103,8 @@ public static oap.io.content.ContentReader ofBytes() { @Override @SneakyThrows public byte[] read( InputStream is ) { - var baos = new ByteArrayOutputStream(); - try( var gos = ungzip( is ) ) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try( InputStream gos = ungzip( is ) ) { IOUtils.copy( gos, baos ); } return baos.toByteArray(); From 5a133b4797e1fe560ba4f1374e0572b48fd41fbd Mon Sep 17 00:00:00 2001 From: "igor.petrenko" Date: Fri, 13 Feb 2026 15:52:31 +0200 Subject: [PATCH 38/39] CE-135 okhttp -> jetty http client --- .../java/oap/compression/Compression.java | 43 ++++++------------- 1 file changed, 13 insertions(+), 30 deletions(-) diff --git a/oap-stdlib/src/main/java/oap/compression/Compression.java b/oap-stdlib/src/main/java/oap/compression/Compression.java index db72f0aab..877270d03 100644 --- a/oap-stdlib/src/main/java/oap/compression/Compression.java +++ b/oap-stdlib/src/main/java/oap/compression/Compression.java @@ -24,43 +24,21 @@ package oap.compression; import lombok.SneakyThrows; -import oap.util.function.Try; -import org.apache.commons.compress.compressors.CompressorStreamFactory; import org.apache.commons.io.IOUtils; +import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.util.function.BiFunction; -import java.util.function.Function; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; import static java.nio.charset.StandardCharsets.UTF_8; public class Compression { - public static final int DEFAULT_BUFFER_SIZE = 512; - private static final Function gzipInputStreamSupplyer; - private static final BiFunction gzipOutputStreamSupplier; - - static { - if( "apache".equals( System.getProperty( "oap.io.gzip", "apache" ) ) ) { - CompressorStreamFactory factory = new CompressorStreamFactory( true ); - gzipInputStreamSupplyer = Try.map( is -> factory.createCompressorInputStream( CompressorStreamFactory.GZIP, is ) ); - gzipOutputStreamSupplier = Try.biMap( ( os, bufferSize ) -> factory.createCompressorOutputStream( CompressorStreamFactory.GZIP, os ) ); - } else { - gzipInputStreamSupplyer = Try.map( GZIPInputStream::new ); - gzipOutputStreamSupplier = Try.biMap( GZIPOutputStream::new ); - } - } - - public static OutputStream gzip( OutputStream os ) { - return gzip( os, DEFAULT_BUFFER_SIZE ); - } - public static void gzip( OutputStream out, byte[] bytes, int offset, int length ) throws IOException { - try( OutputStream gos = gzip( out ) ) { + try( OutputStream gos = new GZIPOutputStream( out ) ) { gos.write( bytes, offset, length ); } } @@ -75,12 +53,17 @@ public static byte[] gzip( byte[] bytes ) throws IOException { return gzip( bytes, 0, bytes.length ); } - public static OutputStream gzip( OutputStream os, int bufferSize ) { - return gzipOutputStreamSupplier.apply( os, bufferSize ); + public static byte[] ungzip( byte[] bytes ) throws IOException { + return ungzip( bytes, 0, bytes.length ); + } + + public static byte[] ungzip( byte[] bytes, int offset, int length ) throws IOException { + return new GZIPInputStream( new ByteArrayInputStream( bytes, offset, length ) ).readAllBytes(); } - public static InputStream ungzip( InputStream is ) { - return gzipInputStreamSupplyer.apply( is ); + public static void ungzip( byte[] bytes, int offset, int length, OutputStream out ) throws IOException { + GZIPInputStream gzipInputStream = new GZIPInputStream( new ByteArrayInputStream( bytes, offset, length ) ); + IOUtils.copy( gzipInputStream, out ); } public static class ContentWriter { @@ -89,7 +72,7 @@ public static oap.io.content.ContentWriter ofGzip() { @Override @SneakyThrows public void write( OutputStream os, String object ) { - try( OutputStream gos = gzip( os ) ) { + try( GZIPOutputStream gos = new GZIPOutputStream( os ) ) { gos.write( object.getBytes( UTF_8 ) ); } } @@ -104,7 +87,7 @@ public static oap.io.content.ContentReader ofBytes() { @SneakyThrows public byte[] read( InputStream is ) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); - try( InputStream gos = ungzip( is ) ) { + try( GZIPInputStream gos = new GZIPInputStream( is ) ) { IOUtils.copy( gos, baos ); } return baos.toByteArray(); From 4e5c5f604293b870d84d1f448128f6cd9d3b8cb2 Mon Sep 17 00:00:00 2001 From: "igor.petrenko" Date: Mon, 16 Feb 2026 17:32:00 +0200 Subject: [PATCH 39/39] CE-135 okhttp -> jetty http client --- oap-stdlib/src/main/java/oap/json/Binder.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/oap-stdlib/src/main/java/oap/json/Binder.java b/oap-stdlib/src/main/java/oap/json/Binder.java index b56bcac87..427409917 100644 --- a/oap-stdlib/src/main/java/oap/json/Binder.java +++ b/oap-stdlib/src/main/java/oap/json/Binder.java @@ -328,13 +328,13 @@ public String marshal( Object value, boolean prettyPrinter ) { } public void marshal( Object value, StringBuilder sb ) { - try( var writer = new StringBuilderWriter( sb ) ) { + try( StringBuilderWriter writer = new StringBuilderWriter( sb ) ) { marshal( value, writer, false ); } } public void marshal( Object value, StringBuilder sb, boolean prettyPrinter ) { - try( var writer = new StringBuilderWriter( sb ) ) { + try( StringBuilderWriter writer = new StringBuilderWriter( sb ) ) { marshal( value, writer, prettyPrinter ); } } @@ -345,9 +345,13 @@ public void marshal( Object value, Writer writer ) { public void marshal( Object value, Writer writer, boolean prettyPrinter ) { try { - if( prettyPrinter ) mapper.writerWithDefaultPrettyPrinter().writeValue( writer, value ); - else + if( prettyPrinter ) { + DefaultPrettyPrinter defaultPrettyPrinter = new DefaultPrettyPrinter(); + defaultPrettyPrinter.indentArraysWith( DefaultIndenter.SYSTEM_LINEFEED_INSTANCE ); + mapper.writer( defaultPrettyPrinter ).writeValue( writer, value ); + } else { mapper.writeValue( writer, value ); + } } catch( IOException e ) { throw new JsonException( e ); }