Skip to content

Commit b89ddf6

Browse files
author
ayazychyan
committed
gh-362: internal http server api
1 parent 7fbdef3 commit b89ddf6

4 files changed

Lines changed: 189 additions & 23 deletions

File tree

http/http-server-common/src/main/java/ru/tinkoff/kora/http/server/common/HttpServerModule.java

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import io.micrometer.core.instrument.MeterRegistry;
44
import io.opentelemetry.api.trace.Tracer;
5+
import java.util.Optional;
56
import org.jspecify.annotations.Nullable;
67
import ru.tinkoff.kora.application.graph.All;
78
import ru.tinkoff.kora.application.graph.PromiseOf;
@@ -13,6 +14,7 @@
1314
import ru.tinkoff.kora.config.common.Config;
1415
import ru.tinkoff.kora.config.common.extractor.ConfigValueExtractionException;
1516
import ru.tinkoff.kora.config.common.extractor.ConfigValueExtractor;
17+
import ru.tinkoff.kora.http.server.common.annotation.InternalApi;
1618
import ru.tinkoff.kora.http.server.common.annotation.PrivateApi;
1719
import ru.tinkoff.kora.http.server.common.handler.HttpServerRequestHandler;
1820
import ru.tinkoff.kora.http.server.common.privateapi.LivenessHandler;
@@ -23,8 +25,6 @@
2325
import ru.tinkoff.kora.http.server.common.telemetry.impl.DefaultHttpServerTelemetryFactory;
2426
import ru.tinkoff.kora.telemetry.common.MetricsScraper;
2527

26-
import java.util.Optional;
27-
2828
public interface HttpServerModule extends StringParameterReadersModule, HttpServerRequestMapperModule, HttpServerResponseMapperModule {
2929

3030
default HttpServerConfig httpServerConfig(Config config, ConfigValueExtractor<HttpServerConfig> configValueExtractor) {
@@ -77,4 +77,19 @@ default HttpServerHandler privateApiHandler(@Tag(PrivateApi.class) All<HttpServe
7777
return new HttpServerHandler(handlers, interceptors, config);
7878
}
7979

80+
@InternalApi
81+
default InternalHttpServerConfig internalApiHttpServerConfig(Config config, ConfigValueExtractor<InternalHttpServerConfig> configValueExtractor) {
82+
var value = config.get("internalHttpServer");
83+
var parsed = configValueExtractor.extract(value);
84+
if (parsed == null) {
85+
throw ConfigValueExtractionException.missingValueAfterParse(value);
86+
}
87+
return parsed;
88+
}
89+
90+
@InternalApi
91+
default HttpServerHandler internalApiHandler(@Tag(InternalApi.class) All<HttpServerRequestHandler> handlers, @Tag(InternalApi.class) All<HttpServerInterceptor> interceptors, HttpServerConfig config) {
92+
return new HttpServerHandler(handlers, interceptors, config);
93+
}
94+
8095
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package ru.tinkoff.kora.http.server.common;
2+
3+
import ru.tinkoff.kora.config.common.annotation.ConfigValueExtractor;
4+
5+
@ConfigValueExtractor
6+
public interface InternalHttpServerConfig extends HttpServerConfig {
7+
8+
@Override
9+
default int port() {
10+
return 8090;
11+
}
12+
}

http/http-server-common/src/testFixtures/java/ru/tinkoff/kora/http/server/common/HttpServerTestKit.java

Lines changed: 146 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,45 @@
11
package ru.tinkoff.kora.http.server.common;
22

3+
import static java.time.Instant.now;
4+
import static org.assertj.core.api.Assertions.assertThat;
5+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
6+
import static org.junit.jupiter.api.Assertions.fail;
7+
import static org.mockito.Mockito.never;
8+
import static org.mockito.Mockito.timeout;
9+
import static org.mockito.Mockito.verify;
10+
import static org.mockito.Mockito.when;
11+
import static ru.tinkoff.kora.http.common.HttpMethod.GET;
12+
import static ru.tinkoff.kora.http.common.HttpMethod.POST;
13+
314
import io.opentelemetry.api.trace.Span;
15+
import java.io.IOException;
16+
import java.io.OutputStream;
17+
import java.nio.ByteBuffer;
18+
import java.nio.charset.StandardCharsets;
19+
import java.time.Duration;
20+
import java.util.ArrayList;
21+
import java.util.List;
22+
import java.util.Optional;
23+
import java.util.concurrent.CompletableFuture;
24+
import java.util.concurrent.ConcurrentLinkedDeque;
25+
import java.util.concurrent.ExecutionException;
26+
import java.util.concurrent.Executors;
27+
import java.util.concurrent.ForkJoinPool;
28+
import java.util.concurrent.ThreadLocalRandom;
29+
import java.util.concurrent.TimeUnit;
30+
import java.util.function.Supplier;
431
import okhttp3.ConnectionPool;
532
import okhttp3.OkHttpClient;
633
import okhttp3.Request;
734
import okhttp3.RequestBody;
835
import okio.BufferedSink;
936
import org.jetbrains.annotations.NotNull;
1037
import org.jspecify.annotations.Nullable;
11-
import org.junit.jupiter.api.*;
38+
import org.junit.jupiter.api.AfterEach;
39+
import org.junit.jupiter.api.Assertions;
40+
import org.junit.jupiter.api.Nested;
41+
import org.junit.jupiter.api.Test;
42+
import org.junit.jupiter.api.TestInstance;
1243
import org.mockito.AdditionalAnswers;
1344
import org.mockito.ArgumentMatchers;
1445
import org.mockito.Mockito;
@@ -32,28 +63,15 @@
3263
import ru.tinkoff.kora.http.server.common.privateapi.MetricsHandler;
3364
import ru.tinkoff.kora.http.server.common.privateapi.ReadinessHandler;
3465
import ru.tinkoff.kora.http.server.common.router.HttpServerHandler;
35-
import ru.tinkoff.kora.http.server.common.telemetry.*;
66+
import ru.tinkoff.kora.http.server.common.telemetry.$HttpServerTelemetryConfig_ConfigValueExtractor;
67+
import ru.tinkoff.kora.http.server.common.telemetry.$HttpServerTelemetryConfig_HttpServerLoggingConfig_ConfigValueExtractor;
68+
import ru.tinkoff.kora.http.server.common.telemetry.$HttpServerTelemetryConfig_HttpServerMetricsConfig_ConfigValueExtractor;
69+
import ru.tinkoff.kora.http.server.common.telemetry.$HttpServerTelemetryConfig_HttpServerTracingConfig_ConfigValueExtractor;
70+
import ru.tinkoff.kora.http.server.common.telemetry.HttpServerObservation;
71+
import ru.tinkoff.kora.http.server.common.telemetry.HttpServerTelemetry;
72+
import ru.tinkoff.kora.http.server.common.telemetry.NoopHttpServerTelemetry;
3673
import ru.tinkoff.kora.telemetry.common.MetricsScraper;
3774

38-
import java.io.IOException;
39-
import java.io.OutputStream;
40-
import java.nio.ByteBuffer;
41-
import java.nio.charset.StandardCharsets;
42-
import java.time.Duration;
43-
import java.util.ArrayList;
44-
import java.util.List;
45-
import java.util.Optional;
46-
import java.util.concurrent.*;
47-
import java.util.function.Supplier;
48-
49-
import static java.time.Instant.now;
50-
import static org.assertj.core.api.Assertions.assertThat;
51-
import static org.assertj.core.api.Assertions.assertThatThrownBy;
52-
import static org.junit.jupiter.api.Assertions.fail;
53-
import static org.mockito.Mockito.*;
54-
import static ru.tinkoff.kora.http.common.HttpMethod.GET;
55-
import static ru.tinkoff.kora.http.common.HttpMethod.POST;
56-
5775
@TestInstance(TestInstance.Lifecycle.PER_METHOD)
5876
public abstract class HttpServerTestKit {
5977
protected static MetricsScraper registry = Mockito.mock(MetricsScraper.class);
@@ -74,6 +92,7 @@ public abstract class HttpServerTestKit {
7492

7593
private volatile HttpServer httpServer = null;
7694
private volatile HttpServer privateHttpServer = null;
95+
private volatile HttpServer internalHttpServer = null;
7796

7897
protected final OkHttpClient client = new OkHttpClient.Builder()
7998
.connectionPool(new ConnectionPool(0, 1, TimeUnit.MICROSECONDS))
@@ -190,6 +209,76 @@ void testReadinessFailureOnUninitializedProbe() throws IOException {
190209
}
191210

192211

212+
@Nested
213+
public class InternalApiTest {
214+
@Test
215+
void testInternalApiHelloWorld() throws IOException {
216+
var httpResponse = HttpServerResponse.of(200, HttpBody.plaintext("internal hello"));
217+
var handler = handler(GET, "/internal", (_) -> httpResponse);
218+
startInternalHttpServer(handler);
219+
220+
var request = internalApiRequest("/internal")
221+
.get()
222+
.build();
223+
224+
try (var response = client.newCall(request).execute()) {
225+
assertThat(response.code()).isEqualTo(200);
226+
assertThat(response.body().string()).isEqualTo("internal hello");
227+
}
228+
}
229+
230+
@Test
231+
void testInternalApiUnknownPath() throws IOException {
232+
var handler = handler(GET, "/internal", (_) -> HttpServerResponse.of(200));
233+
startInternalHttpServer(handler);
234+
235+
var request = internalApiRequest("/unknown")
236+
.get()
237+
.build();
238+
239+
try (var response = client.newCall(request).execute()) {
240+
assertThat(response.code()).isEqualTo(404);
241+
}
242+
}
243+
244+
@Test
245+
void testInternalApiWithInterceptor() throws IOException {
246+
var httpResponse = HttpServerResponse.of(200, HttpBody.plaintext("internal hello"));
247+
var handler = handler(GET, "/internal", (_) -> httpResponse);
248+
var interceptor = new HttpServerInterceptor() {
249+
@Override
250+
public HttpServerResponse intercept(HttpServerRequest request, InterceptChain chain) throws Exception {
251+
var header = request.headers().getFirst("x-internal-block");
252+
if (header != null) {
253+
request.body().close();
254+
return HttpServerResponse.of(403, HttpBody.plaintext("blocked"));
255+
}
256+
return chain.process(request);
257+
}
258+
};
259+
startInternalHttpServer(List.of(interceptor), handler);
260+
261+
var request = internalApiRequest("/internal")
262+
.get()
263+
.build();
264+
265+
try (var response = client.newCall(request).execute()) {
266+
assertThat(response.code()).isEqualTo(200);
267+
assertThat(response.body().string()).isEqualTo("internal hello");
268+
}
269+
270+
var blockedRequest = internalApiRequest("/internal")
271+
.header("x-internal-block", "true")
272+
.get()
273+
.build();
274+
275+
try (var response = client.newCall(blockedRequest).execute()) {
276+
assertThat(response.code()).isEqualTo(403);
277+
assertThat(response.body().string()).isEqualTo("blocked");
278+
}
279+
}
280+
}
281+
193282
@Nested
194283
public class PublicApiTest {
195284
@Test
@@ -990,6 +1079,34 @@ protected void startPrivateHttpServer() {
9901079
}
9911080
}
9921081

1082+
protected void startInternalHttpServer(HttpServerRequestHandler... handlers) {
1083+
startInternalHttpServer(List.of(), handlers);
1084+
}
1085+
1086+
protected void startInternalHttpServer(List<HttpServerInterceptor> interceptors, HttpServerRequestHandler... handlers) {
1087+
var config = new HttpServerConfig_Impl(
1088+
0,
1089+
false,
1090+
Duration.ofSeconds(1),
1091+
Duration.ofSeconds(1),
1092+
false,
1093+
Duration.ofMillis(1),
1094+
new $HttpServerTelemetryConfig_ConfigValueExtractor.HttpServerTelemetryConfig_Impl(
1095+
new $HttpServerTelemetryConfig_HttpServerLoggingConfig_ConfigValueExtractor.HttpServerLoggingConfig_Defaults(),
1096+
new $HttpServerTelemetryConfig_HttpServerMetricsConfig_ConfigValueExtractor.HttpServerMetricsConfig_Defaults(),
1097+
new $HttpServerTelemetryConfig_HttpServerTracingConfig_ConfigValueExtractor.HttpServerTracingConfig_Defaults()
1098+
),
1099+
Size.of(1, Size.Type.GiB)
1100+
);
1101+
var internalApiHandler = new HttpServerHandler(List.of(handlers), interceptors, config);
1102+
this.internalHttpServer = this.httpServer(valueOf(config), internalApiHandler, this.telemetry);
1103+
try {
1104+
this.internalHttpServer.init();
1105+
} catch (Exception e) {
1106+
throw new RuntimeException(e);
1107+
}
1108+
}
1109+
9931110
@AfterEach
9941111
void tearDown() throws Exception {
9951112
if (this.httpServer != null) {
@@ -1000,6 +1117,10 @@ void tearDown() throws Exception {
10001117
this.privateHttpServer.release();
10011118
this.privateHttpServer = null;
10021119
}
1120+
if (this.internalHttpServer != null) {
1121+
this.internalHttpServer.release();
1122+
this.internalHttpServer = null;
1123+
}
10031124
this.readinessProbePromise.setValue(readinessProbe);
10041125
this.livenessProbePromise.setValue(livenessProbe);
10051126
}
@@ -1021,6 +1142,10 @@ protected Request.Builder privateApiRequest(String path) {
10211142
return request(this.privateHttpServer.port(), path);
10221143
}
10231144

1145+
protected Request.Builder internalApiRequest(String path) {
1146+
return request(this.internalHttpServer.port(), path);
1147+
}
1148+
10241149
protected Request.Builder request(String path) {
10251150
return request(this.httpServer.port(), path);
10261151
}

http/http-server-undertow/src/main/java/ru/tinkoff/kora/http/server/undertow/UndertowModule.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,12 @@
1111
import ru.tinkoff.kora.config.common.extractor.ConfigValueExtractionException;
1212
import ru.tinkoff.kora.config.common.extractor.ConfigValueExtractor;
1313
import ru.tinkoff.kora.http.server.common.HttpServerModule;
14+
import ru.tinkoff.kora.http.server.common.InternalHttpServerConfig;
1415
import ru.tinkoff.kora.http.server.common.PrivateHttpServerConfig;
16+
import ru.tinkoff.kora.http.server.common.annotation.InternalApi;
1517
import ru.tinkoff.kora.http.server.common.annotation.PrivateApi;
1618
import ru.tinkoff.kora.http.server.common.router.HttpServerHandler;
19+
import ru.tinkoff.kora.http.server.common.telemetry.HttpServerTelemetryFactory;
1720
import ru.tinkoff.kora.http.server.common.telemetry.NoopHttpServerTelemetry;
1821

1922
public interface UndertowModule extends HttpServerModule {
@@ -38,4 +41,15 @@ default UndertowConfig undertowHttpServerConfig(Config config, ConfigValueExtrac
3841
}
3942
return parsed;
4043
}
44+
45+
@Root
46+
@InternalApi
47+
default UndertowHttpServer internalApiUndertowHttpServer(@InternalApi ValueOf<InternalHttpServerConfig> config,
48+
@InternalApi ValueOf<HttpServerHandler> handler,
49+
HttpServerTelemetryFactory telemetryFactory,
50+
XnioWorker worker,
51+
@Nullable @InternalApi Configurer<Undertow.Builder> configurer) {
52+
var telemetry = telemetryFactory.get(config.get().telemetry());
53+
return new UndertowHttpServer(config, handler, "kora-undertow-internal", telemetry, worker, configurer);
54+
}
4155
}

0 commit comments

Comments
 (0)