diff --git a/java/google/registry/rdap/BUILD b/java/google/registry/rdap/BUILD index 5abbf22e2..0b69e4198 100644 --- a/java/google/registry/rdap/BUILD +++ b/java/google/registry/rdap/BUILD @@ -21,6 +21,7 @@ java_library( "@com_google_code_findbugs_jsr305", "@com_google_dagger", "@com_google_guava", + "@com_google_http_client_jackson2", "@com_google_re2j", "@com_googlecode_json_simple", "@javax_servlet_api", diff --git a/java/google/registry/rdap/RdapActionBase.java b/java/google/registry/rdap/RdapActionBase.java index 68abe65a8..b69a48969 100644 --- a/java/google/registry/rdap/RdapActionBase.java +++ b/java/google/registry/rdap/RdapActionBase.java @@ -25,6 +25,7 @@ import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST; import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR; import static javax.servlet.http.HttpServletResponse.SC_OK; +import com.google.api.client.json.jackson2.JacksonFactory; import com.google.common.collect.ImmutableMap; import com.google.common.net.MediaType; import com.google.re2j.Pattern; @@ -48,6 +49,7 @@ import google.registry.request.auth.AuthResult; import google.registry.request.auth.UserAuthInfo; import google.registry.ui.server.registrar.SessionUtils; import google.registry.util.FormattingLogger; +import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; @@ -89,6 +91,7 @@ public abstract class RdapActionBase implements Runnable { @Inject RdapJsonFormatter rdapJsonFormatter; @Inject @Parameter("registrar") Optional registrarParam; @Inject @Parameter("includeDeleted") Optional includeDeletedParam; + @Inject @Parameter("formatOutput") Optional formatOutputParam; @Inject @Config("rdapWhoisServer") @Nullable String rdapWhoisServer; @Inject @Config("rdapResultSetMaxSize") int rdapResultSetMaxSize; @Inject RdapMetrics rdapMetrics; @@ -143,9 +146,7 @@ public abstract class RdapActionBase implements Runnable { pathProper.substring(getActionPath().length()), requestMethod == Action.Method.HEAD); response.setStatus(SC_OK); response.setContentType(RESPONSE_MEDIA_TYPE); - if (requestMethod != Action.Method.HEAD) { - response.setPayload(JSONValue.toJSONString(rdapJson)); - } + setPayload(rdapJson); metricInformationBuilder.setStatusCode(SC_OK); } catch (HttpException e) { setError(e.getResponseCode(), e.getResponseCodeString(), e.getMessage()); @@ -163,15 +164,25 @@ public abstract class RdapActionBase implements Runnable { response.setStatus(status); response.setContentType(RESPONSE_MEDIA_TYPE); try { - if (requestMethod != Action.Method.HEAD) { - response.setPayload( - JSONValue.toJSONString(rdapJsonFormatter.makeError(status, title, description))); - } + setPayload(rdapJsonFormatter.makeError(status, title, description)); } catch (Exception ex) { - if (requestMethod != Action.Method.HEAD) { - response.setPayload(""); + response.setPayload(""); + } + } + + void setPayload(ImmutableMap rdapJson) { + if (requestMethod == Action.Method.HEAD) { + return; + } + if (formatOutputParam.orElse(false)) { + try { + response.setPayload(new JacksonFactory().toPrettyString(rdapJson)); + return; + } catch (IOException e) { + // On exception, fall back to unformatted output } } + response.setPayload(JSONValue.toJSONString(rdapJson)); } RdapAuthorization getAuthorization() { diff --git a/java/google/registry/rdap/RdapModule.java b/java/google/registry/rdap/RdapModule.java index 5e8e0acf3..4df649667 100644 --- a/java/google/registry/rdap/RdapModule.java +++ b/java/google/registry/rdap/RdapModule.java @@ -72,4 +72,10 @@ public final class RdapModule { static Optional provideIncludeDeleted(HttpServletRequest req) { return RequestParameters.extractOptionalBooleanParameter(req, "includeDeleted"); } + + @Provides + @Parameter("formatOutput") + static Optional provideFormatOutput(HttpServletRequest req) { + return RequestParameters.extractOptionalBooleanParameter(req, "formatOutput"); + } } diff --git a/javatests/google/registry/rdap/RdapActionBaseTest.java b/javatests/google/registry/rdap/RdapActionBaseTest.java index 89d20905d..30b472537 100644 --- a/javatests/google/registry/rdap/RdapActionBaseTest.java +++ b/javatests/google/registry/rdap/RdapActionBaseTest.java @@ -123,6 +123,7 @@ public class RdapActionBaseTest { action.authResult = AuthResult.create(AuthLevel.USER, userAuthInfo); action.includeDeletedParam = Optional.empty(); action.registrarParam = Optional.empty(); + action.formatOutputParam = Optional.empty(); action.response = response; action.rdapJsonFormatter = RdapTestHelper.getTestRdapJsonFormatter(); action.rdapMetrics = rdapMetrics; @@ -231,4 +232,25 @@ public class RdapActionBaseTest { .setIncompletenessWarningType(IncompletenessWarningType.COMPLETE) .build()); } + + @Test + public void testUnformatted() throws Exception { + action.requestPath = RdapTestAction.PATH + "no.thing"; + action.fullServletPath = "http://myserver.example.com" + RdapTestAction.PATH; + action.requestMethod = GET; + action.run(); + assertThat(response.getPayload() + '\n').isEqualTo( + loadFileWithSubstitutions(this.getClass(), "rdap_unformatted_output.json", null)); + } + + @Test + public void testFormatted() throws Exception { + action.requestPath = RdapTestAction.PATH + "no.thing?formatOutput=true"; + action.fullServletPath = "http://myserver.example.com" + RdapTestAction.PATH; + action.requestMethod = GET; + action.formatOutputParam = Optional.of(true); + action.run(); + assertThat(response.getPayload() + '\n').isEqualTo( + loadFileWithSubstitutions(this.getClass(), "rdap_formatted_output.json", null)); + } } diff --git a/javatests/google/registry/rdap/RdapDomainActionTest.java b/javatests/google/registry/rdap/RdapDomainActionTest.java index 538b48ccd..d5d164012 100644 --- a/javatests/google/registry/rdap/RdapDomainActionTest.java +++ b/javatests/google/registry/rdap/RdapDomainActionTest.java @@ -268,6 +268,7 @@ public class RdapDomainActionTest { action.response = response; action.registrarParam = Optional.empty(); action.includeDeletedParam = Optional.empty(); + action.formatOutputParam = Optional.empty(); action.rdapJsonFormatter = RdapTestHelper.getTestRdapJsonFormatter(); action.rdapWhoisServer = null; action.sessionUtils = sessionUtils; diff --git a/javatests/google/registry/rdap/RdapDomainSearchActionTest.java b/javatests/google/registry/rdap/RdapDomainSearchActionTest.java index 6c80aa104..e1e09aeaa 100644 --- a/javatests/google/registry/rdap/RdapDomainSearchActionTest.java +++ b/javatests/google/registry/rdap/RdapDomainSearchActionTest.java @@ -375,6 +375,7 @@ public class RdapDomainSearchActionTest extends RdapSearchActionTestCase { action.response = response; action.registrarParam = Optional.empty(); action.includeDeletedParam = Optional.empty(); + action.formatOutputParam = Optional.empty(); action.rdapJsonFormatter = RdapTestHelper.getTestRdapJsonFormatter(); action.rdapWhoisServer = null; action.sessionUtils = sessionUtils; diff --git a/javatests/google/registry/rdap/RdapEntityActionTest.java b/javatests/google/registry/rdap/RdapEntityActionTest.java index a8e7450cd..0a9fcb21d 100644 --- a/javatests/google/registry/rdap/RdapEntityActionTest.java +++ b/javatests/google/registry/rdap/RdapEntityActionTest.java @@ -169,6 +169,7 @@ public class RdapEntityActionTest { action.response = response; action.registrarParam = Optional.empty(); action.includeDeletedParam = Optional.empty(); + action.formatOutputParam = Optional.empty(); action.rdapJsonFormatter = RdapTestHelper.getTestRdapJsonFormatter(); action.rdapWhoisServer = null; action.sessionUtils = sessionUtils; diff --git a/javatests/google/registry/rdap/RdapEntitySearchActionTest.java b/javatests/google/registry/rdap/RdapEntitySearchActionTest.java index 2dac475c5..61bc7f22a 100644 --- a/javatests/google/registry/rdap/RdapEntitySearchActionTest.java +++ b/javatests/google/registry/rdap/RdapEntitySearchActionTest.java @@ -160,6 +160,7 @@ public class RdapEntitySearchActionTest extends RdapSearchActionTestCase { action.handleParam = Optional.empty(); action.registrarParam = Optional.empty(); action.includeDeletedParam = Optional.empty(); + action.formatOutputParam = Optional.empty(); action.sessionUtils = sessionUtils; action.authResult = AuthResult.create(AuthLevel.USER, userAuthInfo); action.rdapMetrics = rdapMetrics; diff --git a/javatests/google/registry/rdap/RdapHelpActionTest.java b/javatests/google/registry/rdap/RdapHelpActionTest.java index 45d5547b5..27d021d62 100644 --- a/javatests/google/registry/rdap/RdapHelpActionTest.java +++ b/javatests/google/registry/rdap/RdapHelpActionTest.java @@ -71,6 +71,7 @@ public class RdapHelpActionTest { action.authResult = AuthResult.create(AuthLevel.USER, userAuthInfo); action.includeDeletedParam = Optional.empty(); action.registrarParam = Optional.empty(); + action.formatOutputParam = Optional.empty(); action.response = response; action.rdapJsonFormatter = RdapTestHelper.getTestRdapJsonFormatter(); action.rdapWhoisServer = null; diff --git a/javatests/google/registry/rdap/RdapNameserverActionTest.java b/javatests/google/registry/rdap/RdapNameserverActionTest.java index d0c1d9694..2c0391071 100644 --- a/javatests/google/registry/rdap/RdapNameserverActionTest.java +++ b/javatests/google/registry/rdap/RdapNameserverActionTest.java @@ -124,6 +124,7 @@ public class RdapNameserverActionTest { action.requestPath = RdapNameserverAction.PATH.concat(input); action.registrarParam = desiredRegistrar; action.includeDeletedParam = includeDeleted; + action.formatOutputParam = Optional.empty(); action.rdapJsonFormatter = RdapTestHelper.getTestRdapJsonFormatter(); action.rdapWhoisServer = null; action.authResult = authResult; diff --git a/javatests/google/registry/rdap/RdapNameserverSearchActionTest.java b/javatests/google/registry/rdap/RdapNameserverSearchActionTest.java index 82566f287..c9a794b6a 100644 --- a/javatests/google/registry/rdap/RdapNameserverSearchActionTest.java +++ b/javatests/google/registry/rdap/RdapNameserverSearchActionTest.java @@ -164,6 +164,7 @@ public class RdapNameserverSearchActionTest extends RdapSearchActionTestCase { action.nameParam = Optional.empty(); action.registrarParam = Optional.empty(); action.includeDeletedParam = Optional.empty(); + action.formatOutputParam = Optional.empty(); action.authResult = AuthResult.create(AuthLevel.USER, userAuthInfo); action.sessionUtils = sessionUtils; action.rdapMetrics = rdapMetrics; diff --git a/javatests/google/registry/rdap/testdata/rdap_formatted_output.json b/javatests/google/registry/rdap/testdata/rdap_formatted_output.json new file mode 100644 index 000000000..0b9112702 --- /dev/null +++ b/javatests/google/registry/rdap/testdata/rdap_formatted_output.json @@ -0,0 +1,14 @@ +{ + "key" : "value", + "rdapConformance" : [ "rdap_level_0" ], + "notices" : [ { + "title" : "RDAP Terms of Service", + "description" : [ "By querying our Domain Database, you are agreeing to comply with these terms so please read them carefully.", "Any information provided is 'as is' without any guarantee of accuracy.", "Please do not misuse the Domain Database. It is intended solely for query-based access.", "Don't use the Domain Database to allow, enable, or otherwise support the transmission of mass unsolicited, commercial advertising or solicitations.", "Don't access our Domain Database through the use of high volume, automated electronic processes that send queries or data to the systems of any ICANN-accredited registrar.", "You may only use the information contained in the Domain Database for lawful purposes.", "Do not compile, repackage, disseminate, or otherwise use the information contained in the Domain Database in its entirety, or in any substantial portion, without our prior written permission.", "We may retain certain details about queries to our Domain Database for the purposes of detecting and preventing misuse.", "We reserve the right to restrict or deny your access to the database if we suspect that you have failed to comply with these terms.", "We reserve the right to modify this agreement at any time." ], + "links" : [ { + "value" : "http://myserver.example.com/help/tos", + "rel" : "alternate", + "href" : "https://www.registry.tld/about/rdap/tos.html", + "type" : "text/html" + } ] + } ] +} diff --git a/javatests/google/registry/rdap/testdata/rdap_unformatted_output.json b/javatests/google/registry/rdap/testdata/rdap_unformatted_output.json new file mode 100644 index 000000000..af3097c69 --- /dev/null +++ b/javatests/google/registry/rdap/testdata/rdap_unformatted_output.json @@ -0,0 +1 @@ +{"key":"value","rdapConformance":["rdap_level_0"],"notices":[{"title":"RDAP Terms of Service","description":["By querying our Domain Database, you are agreeing to comply with these terms so please read them carefully.","Any information provided is 'as is' without any guarantee of accuracy.","Please do not misuse the Domain Database. It is intended solely for query-based access.","Don't use the Domain Database to allow, enable, or otherwise support the transmission of mass unsolicited, commercial advertising or solicitations.","Don't access our Domain Database through the use of high volume, automated electronic processes that send queries or data to the systems of any ICANN-accredited registrar.","You may only use the information contained in the Domain Database for lawful purposes.","Do not compile, repackage, disseminate, or otherwise use the information contained in the Domain Database in its entirety, or in any substantial portion, without our prior written permission.","We may retain certain details about queries to our Domain Database for the purposes of detecting and preventing misuse.","We reserve the right to restrict or deny your access to the database if we suspect that you have failed to comply with these terms.","We reserve the right to modify this agreement at any time."],"links":[{"value":"http:\/\/myserver.example.com\/help\/tos","rel":"alternate","href":"https:\/\/www.registry.tld\/about\/rdap\/tos.html","type":"text\/html"}]}]}