diff --git a/java/google/registry/flows/EppController.java b/java/google/registry/flows/EppController.java index 38594124f..98782fd05 100644 --- a/java/google/registry/flows/EppController.java +++ b/java/google/registry/flows/EppController.java @@ -14,11 +14,16 @@ package google.registry.flows; +import static com.google.common.base.Strings.nullToEmpty; +import static com.google.common.io.BaseEncoding.base64; import static google.registry.flows.EppXmlTransformer.unmarshal; +import static java.nio.charset.StandardCharsets.UTF_8; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Joiner; import com.google.common.base.Optional; +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableMap; import google.registry.flows.FlowModule.EppExceptionInProviderException; import google.registry.model.eppcommon.Trid; import google.registry.model.eppinput.EppInput; @@ -30,6 +35,7 @@ import google.registry.monitoring.whitebox.BigQueryMetricsEnqueuer; import google.registry.monitoring.whitebox.EppMetric; import google.registry.util.FormattingLogger; import javax.inject.Inject; +import org.json.simple.JSONValue; /** * An implementation of the EPP command/response protocol. @@ -46,7 +52,7 @@ public final class EppController { @Inject BigQueryMetricsEnqueuer bigQueryMetricsEnqueuer; @Inject EppController() {} - /** Read EPP XML, execute the matching flow, and return an {@link EppOutput}. */ + /** Reads EPP XML, executes the matching flow, and returns an {@link EppOutput}. */ public EppOutput handleEppCommand( SessionMetadata sessionMetadata, TransportCredentials credentials, @@ -61,7 +67,21 @@ public final class EppController { try { eppInput = unmarshal(EppInput.class, inputXmlBytes); } catch (EppException e) { - // Send the client an error message, with no clTRID since we couldn't unmarshal it. + // Log the unmarshalling error, with the raw bytes (in base64) to help with debugging. + logger.infofmt( + e, + "EPP request XML unmarshalling failed - \"%s\":\n%s\n%s\n%s\n%s", + e.getMessage(), + JSONValue.toJSONString( + ImmutableMap.of( + "clientId", nullToEmpty(sessionMetadata.getClientId()), + "resultCode", e.getResult().getCode().code, + "resultMessage", e.getResult().getCode().msg, + "xmlBytes", base64().encode(inputXmlBytes))), + Strings.repeat("=", 40), + new String(inputXmlBytes, UTF_8).trim(), // Charset decoding failures are swallowed. + Strings.repeat("=", 40)); + // Return early by sending an error message, with no clTRID since we couldn't unmarshal it. metricBuilder.setStatus(e.getResult().getCode()); return getErrorResponse(e.getResult(), Trid.create(null)); } @@ -92,7 +112,7 @@ public final class EppController { } } - /** Run an EPP flow and convert known exceptions into EPP error responses. */ + /** Runs an EPP flow and converts known exceptions into EPP error responses. */ private EppOutput runFlowConvertEppErrors(FlowComponent flowComponent) { try { return flowComponent.flowRunner().run(); @@ -107,7 +127,7 @@ public final class EppController { } } - /** Create a response indicating an EPP failure. */ + /** Creates a response indicating an EPP failure. */ @VisibleForTesting static EppOutput getErrorResponse(Result result, Trid trid) { return EppOutput.create(new EppResponse.Builder()