diff --git a/core/src/main/java/google/registry/module/RequestComponent.java b/core/src/main/java/google/registry/module/RequestComponent.java index bef4e282c..2ccd703e1 100644 --- a/core/src/main/java/google/registry/module/RequestComponent.java +++ b/core/src/main/java/google/registry/module/RequestComponent.java @@ -62,6 +62,7 @@ import google.registry.monitoring.whitebox.WhiteboxModule; import google.registry.rdap.RdapAutnumAction; import google.registry.rdap.RdapDomainAction; import google.registry.rdap.RdapDomainSearchAction; +import google.registry.rdap.RdapEmptyAction; import google.registry.rdap.RdapEntityAction; import google.registry.rdap.RdapEntitySearchAction; import google.registry.rdap.RdapHelpAction; @@ -261,6 +262,8 @@ interface RequestComponent { RdapDomainSearchAction rdapDomainSearchAction(); + RdapEmptyAction rdapEmptyAction(); + RdapEntityAction rdapEntityAction(); RdapEntitySearchAction rdapEntitySearchAction(); diff --git a/core/src/main/java/google/registry/module/pubapi/PubApiRequestComponent.java b/core/src/main/java/google/registry/module/pubapi/PubApiRequestComponent.java index 30d1ab205..0a6eef6d6 100644 --- a/core/src/main/java/google/registry/module/pubapi/PubApiRequestComponent.java +++ b/core/src/main/java/google/registry/module/pubapi/PubApiRequestComponent.java @@ -24,6 +24,7 @@ import google.registry.monitoring.whitebox.WhiteboxModule; import google.registry.rdap.RdapAutnumAction; import google.registry.rdap.RdapDomainAction; import google.registry.rdap.RdapDomainSearchAction; +import google.registry.rdap.RdapEmptyAction; import google.registry.rdap.RdapEntityAction; import google.registry.rdap.RdapEntitySearchAction; import google.registry.rdap.RdapHelpAction; @@ -55,6 +56,9 @@ public interface PubApiRequestComponent { RdapAutnumAction rdapAutnumAction(); RdapDomainAction rdapDomainAction(); RdapDomainSearchAction rdapDomainSearchAction(); + + RdapEmptyAction rdapEmptyAction(); + RdapEntityAction rdapEntityAction(); RdapEntitySearchAction rdapEntitySearchAction(); RdapHelpAction rdapHelpAction(); diff --git a/core/src/main/java/google/registry/rdap/RdapEmptyAction.java b/core/src/main/java/google/registry/rdap/RdapEmptyAction.java new file mode 100644 index 000000000..59384e2c2 --- /dev/null +++ b/core/src/main/java/google/registry/rdap/RdapEmptyAction.java @@ -0,0 +1,54 @@ +// Copyright 2025 The Nomulus Authors. All Rights Reserved. +// +// Licensed 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 google.registry.rdap; + +import static google.registry.request.Action.Method.GET; +import static google.registry.request.Action.Method.HEAD; + +import google.registry.request.Action; +import google.registry.request.Response; +import google.registry.request.auth.Auth; +import jakarta.inject.Inject; +import java.io.IOException; + +/** + * RDAP action that serves the empty string, redirecting to the help page. + * + *
This isn't technically required, but if someone requests the base url it seems nice to give + * them the help response. + */ +@Action( + service = Action.GaeService.PUBAPI, + path = "/rdap/", + method = {GET, HEAD}, + auth = Auth.AUTH_PUBLIC) +public class RdapEmptyAction implements Runnable { + + private final Response response; + + @Inject + public RdapEmptyAction(Response response) { + this.response = response; + } + + @Override + public void run() { + try { + response.sendRedirect(RdapHelpAction.PATH); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/core/src/main/java/google/registry/rdap/RdapHelpAction.java b/core/src/main/java/google/registry/rdap/RdapHelpAction.java index b66d4216f..1d7849fe2 100644 --- a/core/src/main/java/google/registry/rdap/RdapHelpAction.java +++ b/core/src/main/java/google/registry/rdap/RdapHelpAction.java @@ -31,12 +31,14 @@ import java.util.Optional; /** RDAP (new WHOIS) action for help requests. */ @Action( service = GaeService.PUBAPI, - path = "/rdap/help", + path = RdapHelpAction.PATH, method = {GET, HEAD}, isPrefix = true, auth = Auth.AUTH_PUBLIC) public class RdapHelpAction extends RdapActionBase { + public static final String PATH = "/rdap/help"; + /** The help path for the RDAP terms of service. */ public static final String TOS_PATH = "/tos"; diff --git a/core/src/test/java/google/registry/rdap/RdapEmptyActionTest.java b/core/src/test/java/google/registry/rdap/RdapEmptyActionTest.java new file mode 100644 index 000000000..0e0116f58 --- /dev/null +++ b/core/src/test/java/google/registry/rdap/RdapEmptyActionTest.java @@ -0,0 +1,42 @@ +// Copyright 2025 The Nomulus Authors. All Rights Reserved. +// +// Licensed 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 google.registry.rdap; + +import static com.google.common.truth.Truth.assertThat; + +import google.registry.testing.FakeResponse; +import jakarta.servlet.http.HttpServletResponse; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** Tests for {@link RdapEmptyAction}. */ +public class RdapEmptyActionTest { + + private FakeResponse fakeResponse; + private RdapEmptyAction action; + + @BeforeEach + void beforeEach() { + fakeResponse = new FakeResponse(); + action = new RdapEmptyAction(fakeResponse); + } + + @Test + void testRedirect() { + action.run(); + assertThat(fakeResponse.getStatus()).isEqualTo(HttpServletResponse.SC_FOUND); + assertThat(fakeResponse.getPayload()).isEqualTo("Redirected to /rdap/help"); + } +} diff --git a/core/src/test/resources/google/registry/module/pubapi/pubapi_routing.txt b/core/src/test/resources/google/registry/module/pubapi/pubapi_routing.txt index 3a9ac8d1c..aa0b8f86d 100644 --- a/core/src/test/resources/google/registry/module/pubapi/pubapi_routing.txt +++ b/core/src/test/resources/google/registry/module/pubapi/pubapi_routing.txt @@ -1,6 +1,7 @@ SERVICE PATH CLASS METHODS OK MIN USER_POLICY PUBAPI /_dr/whois WhoisAction POST n APP ADMIN PUBAPI /check CheckApiAction GET n NONE PUBLIC +PUBAPI /rdap/ RdapEmptyAction GET,HEAD n NONE PUBLIC PUBAPI /rdap/autnum/(*) RdapAutnumAction GET,HEAD n NONE PUBLIC PUBAPI /rdap/domain/(*) RdapDomainAction GET,HEAD n NONE PUBLIC PUBAPI /rdap/domains RdapDomainSearchAction GET,HEAD n NONE PUBLIC diff --git a/core/src/test/resources/google/registry/module/routing.txt b/core/src/test/resources/google/registry/module/routing.txt index 4b8a6a77c..53c8ab5a7 100644 --- a/core/src/test/resources/google/registry/module/routing.txt +++ b/core/src/test/resources/google/registry/module/routing.txt @@ -56,6 +56,7 @@ BACKEND /_dr/task/uploadBsaUnavailableNames UploadBsaUnavailable BACKEND /_dr/task/wipeOutContactHistoryPii WipeOutContactHistoryPiiAction GET n APP ADMIN PUBAPI /_dr/whois WhoisAction POST n APP ADMIN PUBAPI /check CheckApiAction GET n NONE PUBLIC +PUBAPI /rdap/ RdapEmptyAction GET,HEAD n NONE PUBLIC PUBAPI /rdap/autnum/(*) RdapAutnumAction GET,HEAD n NONE PUBLIC PUBAPI /rdap/domain/(*) RdapDomainAction GET,HEAD n NONE PUBLIC PUBAPI /rdap/domains RdapDomainSearchAction GET,HEAD n NONE PUBLIC