From 6749010f49b5d3fa02c030feff526fb3286340e8 Mon Sep 17 00:00:00 2001 From: "arun.suri" Date: Thu, 30 Oct 2025 07:51:25 +0530 Subject: [PATCH 1/6] initial commit --- runtime/service/build.gradle.kts | 1 + .../service/http/ServerHeaderFilter.java | 57 +++++++++++++++++++ .../service/http/ServerHeaderFilterTest.java | 55 ++++++++++++++++++ 3 files changed, 113 insertions(+) create mode 100644 runtime/service/src/main/java/org/apache/polaris/service/http/ServerHeaderFilter.java create mode 100644 runtime/service/src/test/java/org/apache/polaris/service/http/ServerHeaderFilterTest.java diff --git a/runtime/service/build.gradle.kts b/runtime/service/build.gradle.kts index 7b41c3d10e..d5cb9494d7 100644 --- a/runtime/service/build.gradle.kts +++ b/runtime/service/build.gradle.kts @@ -30,6 +30,7 @@ dependencies { implementation(project(":polaris-api-management-service")) implementation(project(":polaris-api-iceberg-service")) implementation(project(":polaris-api-catalog-service")) + implementation(project(":polaris-version")) runtimeOnly(project(":polaris-relational-jdbc")) diff --git a/runtime/service/src/main/java/org/apache/polaris/service/http/ServerHeaderFilter.java b/runtime/service/src/main/java/org/apache/polaris/service/http/ServerHeaderFilter.java new file mode 100644 index 0000000000..168167d072 --- /dev/null +++ b/runtime/service/src/main/java/org/apache/polaris/service/http/ServerHeaderFilter.java @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.polaris.service.http; + +import jakarta.annotation.Priority; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import jakarta.ws.rs.Priorities; +import jakarta.ws.rs.container.ContainerRequestContext; +import jakarta.ws.rs.container.ContainerResponseContext; +import jakarta.ws.rs.container.ContainerResponseFilter; +import jakarta.ws.rs.ext.Provider; +import org.apache.http.HttpHeaders; +import org.apache.polaris.version.PolarisVersion; +import org.eclipse.microprofile.config.inject.ConfigProperty; + +/** Adds the standard HTTP {@code Server} header to outgoing responses. */ +@ApplicationScoped +@Provider +@Priority(Priorities.HEADER_DECORATOR) +public class ServerHeaderFilter implements ContainerResponseFilter { + + static final String SERVER_HEADER_VALUE = "Polaris/" + PolarisVersion.polarisVersionString(); + + private final boolean headerEnabled; + + @Inject + public ServerHeaderFilter( + @ConfigProperty(name = "polaris.http.version-header.enabled", defaultValue = "false") + boolean headerEnabled) { + this.headerEnabled = headerEnabled; + } + + @Override + public void filter( + ContainerRequestContext requestContext, ContainerResponseContext responseContext) { + if (headerEnabled) { + responseContext.getHeaders().putSingle(HttpHeaders.SERVER, SERVER_HEADER_VALUE); + } + } +} diff --git a/runtime/service/src/test/java/org/apache/polaris/service/http/ServerHeaderFilterTest.java b/runtime/service/src/test/java/org/apache/polaris/service/http/ServerHeaderFilterTest.java new file mode 100644 index 0000000000..e4988a8030 --- /dev/null +++ b/runtime/service/src/test/java/org/apache/polaris/service/http/ServerHeaderFilterTest.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.polaris.service.http; + +import static org.assertj.core.api.Assertions.assertThat; + +import jakarta.ws.rs.container.ContainerResponseContext; +import jakarta.ws.rs.core.MultivaluedHashMap; +import org.apache.http.HttpHeaders; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +public class ServerHeaderFilterTest { + + @Test + public void setsServerHeader() { + ServerHeaderFilter filter = new ServerHeaderFilter(true); + MultivaluedHashMap headers = new MultivaluedHashMap<>(); + ContainerResponseContext responseContext = Mockito.mock(ContainerResponseContext.class); + Mockito.when(responseContext.getHeaders()).thenReturn(headers); + + filter.filter(null, responseContext); + + assertThat(headers.getFirst(HttpHeaders.SERVER)) + .isEqualTo(ServerHeaderFilter.SERVER_HEADER_VALUE); + } + + @Test + public void doesNotSetServerHeaderWhenDisabled() { + ServerHeaderFilter filter = new ServerHeaderFilter(false); + MultivaluedHashMap headers = new MultivaluedHashMap<>(); + ContainerResponseContext responseContext = Mockito.mock(ContainerResponseContext.class); + Mockito.when(responseContext.getHeaders()).thenReturn(headers); + + filter.filter(null, responseContext); + + assertThat(headers.getFirst(HttpHeaders.SERVER)).isNull(); + } +} From 981a43cb61a34831067ec16f8d46045cfe244781 Mon Sep 17 00:00:00 2001 From: "arun.suri" Date: Fri, 31 Oct 2025 05:06:59 +0530 Subject: [PATCH 2/6] handle comments --- .../src/main/resources/application.properties | 8 +++ runtime/service/build.gradle.kts | 1 - .../service/http/ServerHeaderFilter.java | 57 ------------------- .../service/http/ServerHeaderFilterTest.java | 55 ------------------ .../configuring-polaris-for-production.md | 18 ++++++ 5 files changed, 26 insertions(+), 113 deletions(-) delete mode 100644 runtime/service/src/main/java/org/apache/polaris/service/http/ServerHeaderFilter.java delete mode 100644 runtime/service/src/test/java/org/apache/polaris/service/http/ServerHeaderFilterTest.java diff --git a/runtime/defaults/src/main/resources/application.properties b/runtime/defaults/src/main/resources/application.properties index 0358eb4b50..dc340368ab 100644 --- a/runtime/defaults/src/main/resources/application.properties +++ b/runtime/defaults/src/main/resources/application.properties @@ -52,6 +52,14 @@ quarkus.http.cors.exposed-headers=* quarkus.http.cors.access-control-max-age=PT10M quarkus.http.cors.access-control-allow-credentials=true +# --- Polaris Server header (Quarkus-managed) --- +# Keep .value present (required by Quarkus), but make the path never match so it's effectively OFF by default. +quarkus.http.header."Server".value=Polaris/${quarkus.application.version} +quarkus.http.header."Server".path=/__disabled__ + +# To enable in production (or any chosen profile), override the path back to normal: +#%prod.quarkus.http.header."Server".path=/* + quarkus.http.port=8181 quarkus.http.test-port=0 diff --git a/runtime/service/build.gradle.kts b/runtime/service/build.gradle.kts index d5cb9494d7..7b41c3d10e 100644 --- a/runtime/service/build.gradle.kts +++ b/runtime/service/build.gradle.kts @@ -30,7 +30,6 @@ dependencies { implementation(project(":polaris-api-management-service")) implementation(project(":polaris-api-iceberg-service")) implementation(project(":polaris-api-catalog-service")) - implementation(project(":polaris-version")) runtimeOnly(project(":polaris-relational-jdbc")) diff --git a/runtime/service/src/main/java/org/apache/polaris/service/http/ServerHeaderFilter.java b/runtime/service/src/main/java/org/apache/polaris/service/http/ServerHeaderFilter.java deleted file mode 100644 index 168167d072..0000000000 --- a/runtime/service/src/main/java/org/apache/polaris/service/http/ServerHeaderFilter.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.polaris.service.http; - -import jakarta.annotation.Priority; -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.inject.Inject; -import jakarta.ws.rs.Priorities; -import jakarta.ws.rs.container.ContainerRequestContext; -import jakarta.ws.rs.container.ContainerResponseContext; -import jakarta.ws.rs.container.ContainerResponseFilter; -import jakarta.ws.rs.ext.Provider; -import org.apache.http.HttpHeaders; -import org.apache.polaris.version.PolarisVersion; -import org.eclipse.microprofile.config.inject.ConfigProperty; - -/** Adds the standard HTTP {@code Server} header to outgoing responses. */ -@ApplicationScoped -@Provider -@Priority(Priorities.HEADER_DECORATOR) -public class ServerHeaderFilter implements ContainerResponseFilter { - - static final String SERVER_HEADER_VALUE = "Polaris/" + PolarisVersion.polarisVersionString(); - - private final boolean headerEnabled; - - @Inject - public ServerHeaderFilter( - @ConfigProperty(name = "polaris.http.version-header.enabled", defaultValue = "false") - boolean headerEnabled) { - this.headerEnabled = headerEnabled; - } - - @Override - public void filter( - ContainerRequestContext requestContext, ContainerResponseContext responseContext) { - if (headerEnabled) { - responseContext.getHeaders().putSingle(HttpHeaders.SERVER, SERVER_HEADER_VALUE); - } - } -} diff --git a/runtime/service/src/test/java/org/apache/polaris/service/http/ServerHeaderFilterTest.java b/runtime/service/src/test/java/org/apache/polaris/service/http/ServerHeaderFilterTest.java deleted file mode 100644 index e4988a8030..0000000000 --- a/runtime/service/src/test/java/org/apache/polaris/service/http/ServerHeaderFilterTest.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.polaris.service.http; - -import static org.assertj.core.api.Assertions.assertThat; - -import jakarta.ws.rs.container.ContainerResponseContext; -import jakarta.ws.rs.core.MultivaluedHashMap; -import org.apache.http.HttpHeaders; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; - -public class ServerHeaderFilterTest { - - @Test - public void setsServerHeader() { - ServerHeaderFilter filter = new ServerHeaderFilter(true); - MultivaluedHashMap headers = new MultivaluedHashMap<>(); - ContainerResponseContext responseContext = Mockito.mock(ContainerResponseContext.class); - Mockito.when(responseContext.getHeaders()).thenReturn(headers); - - filter.filter(null, responseContext); - - assertThat(headers.getFirst(HttpHeaders.SERVER)) - .isEqualTo(ServerHeaderFilter.SERVER_HEADER_VALUE); - } - - @Test - public void doesNotSetServerHeaderWhenDisabled() { - ServerHeaderFilter filter = new ServerHeaderFilter(false); - MultivaluedHashMap headers = new MultivaluedHashMap<>(); - ContainerResponseContext responseContext = Mockito.mock(ContainerResponseContext.class); - Mockito.when(responseContext.getHeaders()).thenReturn(headers); - - filter.filter(null, responseContext); - - assertThat(headers.getFirst(HttpHeaders.SERVER)).isNull(); - } -} diff --git a/site/content/in-dev/unreleased/configuring-polaris-for-production.md b/site/content/in-dev/unreleased/configuring-polaris-for-production.md index 928d8115f1..9f59e2f402 100644 --- a/site/content/in-dev/unreleased/configuring-polaris-for-production.md +++ b/site/content/in-dev/unreleased/configuring-polaris-for-production.md @@ -125,6 +125,24 @@ polaris.realm-context.require-header=true This will cause Polaris to also return a `404 Not Found` response if the realm header is not present in the request. +### Polaris Server Header + +Polaris can emit an informational `Server` HTTP response header using Quarkus' built-in header +configuration. The default configuration keeps the header disabled by pointing it at a path that does +not match any request. + +```properties +quarkus.http.header."Server".value=Polaris/${quarkus.application.version} +quarkus.http.header."Server".path=/__disabled__ +``` + +To enable the header for production traffic, override the header path for the desired profile (for +example, `%prod`). + +```properties +%prod.quarkus.http.header."Server".path=/* +``` + ### Metastore Configuration A metastore should be configured with an implementation that durably persists Polaris entities. By From 481ae89fd450ada0292229151f144fac8ecff557 Mon Sep 17 00:00:00 2001 From: "arun.suri" Date: Mon, 3 Nov 2025 10:17:59 +0530 Subject: [PATCH 3/6] handle comments --- .../src/main/resources/application.properties | 9 --------- .../configuring-polaris-for-production.md | 13 ++++--------- 2 files changed, 4 insertions(+), 18 deletions(-) diff --git a/runtime/defaults/src/main/resources/application.properties b/runtime/defaults/src/main/resources/application.properties index dc340368ab..ab98d623f0 100644 --- a/runtime/defaults/src/main/resources/application.properties +++ b/runtime/defaults/src/main/resources/application.properties @@ -51,15 +51,6 @@ quarkus.http.cors.headers=* quarkus.http.cors.exposed-headers=* quarkus.http.cors.access-control-max-age=PT10M quarkus.http.cors.access-control-allow-credentials=true - -# --- Polaris Server header (Quarkus-managed) --- -# Keep .value present (required by Quarkus), but make the path never match so it's effectively OFF by default. -quarkus.http.header."Server".value=Polaris/${quarkus.application.version} -quarkus.http.header."Server".path=/__disabled__ - -# To enable in production (or any chosen profile), override the path back to normal: -#%prod.quarkus.http.header."Server".path=/* - quarkus.http.port=8181 quarkus.http.test-port=0 diff --git a/site/content/in-dev/unreleased/configuring-polaris-for-production.md b/site/content/in-dev/unreleased/configuring-polaris-for-production.md index 9f59e2f402..6fe34893f8 100644 --- a/site/content/in-dev/unreleased/configuring-polaris-for-production.md +++ b/site/content/in-dev/unreleased/configuring-polaris-for-production.md @@ -128,20 +128,15 @@ in the request. ### Polaris Server Header Polaris can emit an informational `Server` HTTP response header using Quarkus' built-in header -configuration. The default configuration keeps the header disabled by pointing it at a path that does -not match any request. +configuration. Add the following property to one of the supported configuration sources (for example, +`application.properties`) to enable it with the Polaris version string: ```properties quarkus.http.header."Server".value=Polaris/${quarkus.application.version} -quarkus.http.header."Server".path=/__disabled__ ``` -To enable the header for production traffic, override the header path for the desired profile (for -example, `%prod`). - -```properties -%prod.quarkus.http.header."Server".path=/* -``` +If you prefer to scope the header to specific environments, only set the property for the desired +profile (for example, `%prod`). ### Metastore Configuration From 3727652e65d2f73c15ce62d5f2b5f66e3c292f1b Mon Sep 17 00:00:00 2001 From: "arun.suri" Date: Mon, 3 Nov 2025 10:42:15 +0530 Subject: [PATCH 4/6] handle comments --- runtime/defaults/src/main/resources/application.properties | 1 + 1 file changed, 1 insertion(+) diff --git a/runtime/defaults/src/main/resources/application.properties b/runtime/defaults/src/main/resources/application.properties index ab98d623f0..0358eb4b50 100644 --- a/runtime/defaults/src/main/resources/application.properties +++ b/runtime/defaults/src/main/resources/application.properties @@ -51,6 +51,7 @@ quarkus.http.cors.headers=* quarkus.http.cors.exposed-headers=* quarkus.http.cors.access-control-max-age=PT10M quarkus.http.cors.access-control-allow-credentials=true + quarkus.http.port=8181 quarkus.http.test-port=0 From 00ed2e626cbfa881ccf7e0fd9552de9e9cda329e Mon Sep 17 00:00:00 2001 From: "arun.suri" Date: Tue, 4 Nov 2025 08:43:42 +0530 Subject: [PATCH 5/6] handle comments --- .../configuring-polaris-for-production.md | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/site/content/in-dev/unreleased/configuring-polaris-for-production.md b/site/content/in-dev/unreleased/configuring-polaris-for-production.md index 6fe34893f8..2a91dd35a8 100644 --- a/site/content/in-dev/unreleased/configuring-polaris-for-production.md +++ b/site/content/in-dev/unreleased/configuring-polaris-for-production.md @@ -124,20 +124,6 @@ polaris.realm-context.require-header=true This will cause Polaris to also return a `404 Not Found` response if the realm header is not present in the request. - -### Polaris Server Header - -Polaris can emit an informational `Server` HTTP response header using Quarkus' built-in header -configuration. Add the following property to one of the supported configuration sources (for example, -`application.properties`) to enable it with the Polaris version string: - -```properties -quarkus.http.header."Server".value=Polaris/${quarkus.application.version} -``` - -If you prefer to scope the header to specific environments, only set the property for the desired -profile (for example, `%prod`). - ### Metastore Configuration A metastore should be configured with an implementation that durably persists Polaris entities. By @@ -230,6 +216,19 @@ polaris.features."SUPPORTED_CATALOG_STORAGE_TYPES" = [ "S3", "Azure" ] ``` Leave out `FILE` to prevent its use. Only include the storage types your setup needs. +### Polaris Server Header + +Polaris can emit an informational `Server` HTTP response header using Quarkus' built-in header +configuration. Add the following property to one of the supported configuration sources (for example, +`application.properties`) to enable it with the Polaris version string: + +```properties +quarkus.http.header."Server".value=Polaris/${quarkus.application.version} +``` + +If you prefer to scope the header to specific environments, only set the property for the desired +profile (for example, `%prod`). + ### Upgrade Considerations The [Polaris Evolution](../evolution) page discusses backward compatibility and From 6d62c9fd47906887ae1d4350af1079ae72e8633b Mon Sep 17 00:00:00 2001 From: "arun.suri" Date: Tue, 4 Nov 2025 08:44:29 +0530 Subject: [PATCH 6/6] handle comments --- .../in-dev/unreleased/configuring-polaris-for-production.md | 1 + 1 file changed, 1 insertion(+) diff --git a/site/content/in-dev/unreleased/configuring-polaris-for-production.md b/site/content/in-dev/unreleased/configuring-polaris-for-production.md index 2a91dd35a8..2a1efe36bb 100644 --- a/site/content/in-dev/unreleased/configuring-polaris-for-production.md +++ b/site/content/in-dev/unreleased/configuring-polaris-for-production.md @@ -124,6 +124,7 @@ polaris.realm-context.require-header=true This will cause Polaris to also return a `404 Not Found` response if the realm header is not present in the request. + ### Metastore Configuration A metastore should be configured with an implementation that durably persists Polaris entities. By