1
0
mirror of https://github.com/google/nomulus synced 2026-06-09 08:22:59 +00:00

Use built-in Java URL connections instead of UrlFetchService (#1535)

- Use the standard HttpsURLConnection to write/read data
- Rewrite RdeReporter, Nordn*Action, and Marksdb classes and related
  tests to conform to the new format
- Remove FakeURLFetchService and ForwardingUrlFetchService as they weren't used
- Refactor UrlFetchException to UrlConnectionException
- Refactor UrlFetchUtils to UrlConnectionUtils

I will need to test this on Alpha. Fortunately the connections that
don't require auth (e.g. TMDB downloading) should be testable.
This commit is contained in:
gbrodman
2022-03-04 14:16:22 -05:00
committed by GitHub
parent b146301495
commit f1bbdc5a0b
26 changed files with 507 additions and 584 deletions

View File

@@ -43,7 +43,7 @@ import google.registry.rde.JSchModule;
import google.registry.request.Modules.DatastoreServiceModule;
import google.registry.request.Modules.Jackson2Module;
import google.registry.request.Modules.NetHttpTransportModule;
import google.registry.request.Modules.URLFetchServiceModule;
import google.registry.request.Modules.UrlConnectionServiceModule;
import google.registry.request.Modules.UrlFetchTransportModule;
import google.registry.request.Modules.UserServiceModule;
import google.registry.request.auth.AuthModule;
@@ -80,7 +80,7 @@ import javax.inject.Singleton;
ServerTridProviderModule.class,
SheetsServiceModule.class,
StackdriverModule.class,
URLFetchServiceModule.class,
UrlConnectionServiceModule.class,
UrlFetchTransportModule.class,
UserServiceModule.class,
VoidDnsWriterModule.class,

View File

@@ -34,7 +34,6 @@ import google.registry.monitoring.whitebox.StackdriverModule;
import google.registry.privileges.secretmanager.SecretManagerModule;
import google.registry.request.Modules.Jackson2Module;
import google.registry.request.Modules.NetHttpTransportModule;
import google.registry.request.Modules.UrlFetchTransportModule;
import google.registry.request.Modules.UserServiceModule;
import google.registry.request.auth.AuthModule;
import google.registry.ui.ConsoleDebug.ConsoleConfigModule;
@@ -65,7 +64,6 @@ import javax.inject.Singleton;
SecretManagerModule.class,
ServerTridProviderModule.class,
StackdriverModule.class,
UrlFetchTransportModule.class,
UserServiceModule.class,
UtilsModule.class
})

View File

@@ -34,7 +34,6 @@ import google.registry.persistence.PersistenceModule;
import google.registry.privileges.secretmanager.SecretManagerModule;
import google.registry.request.Modules.Jackson2Module;
import google.registry.request.Modules.NetHttpTransportModule;
import google.registry.request.Modules.UrlFetchTransportModule;
import google.registry.request.Modules.UserServiceModule;
import google.registry.request.auth.AuthModule;
import google.registry.util.UtilsModule;
@@ -62,7 +61,6 @@ import javax.inject.Singleton;
SecretManagerModule.class,
ServerTridProviderModule.class,
StackdriverModule.class,
UrlFetchTransportModule.class,
UserServiceModule.class,
UtilsModule.class
})

View File

@@ -36,7 +36,6 @@ import google.registry.privileges.secretmanager.SecretManagerModule;
import google.registry.request.Modules.DatastoreServiceModule;
import google.registry.request.Modules.Jackson2Module;
import google.registry.request.Modules.NetHttpTransportModule;
import google.registry.request.Modules.UrlFetchTransportModule;
import google.registry.request.Modules.UserServiceModule;
import google.registry.request.auth.AuthModule;
import google.registry.util.UtilsModule;
@@ -66,7 +65,6 @@ import javax.inject.Singleton;
ServerTridProviderModule.class,
StackdriverModule.class,
ToolsRequestComponentModule.class,
UrlFetchTransportModule.class,
UserServiceModule.class,
UtilsModule.class
})

View File

@@ -14,26 +14,24 @@
package google.registry.rde;
import static com.google.appengine.api.urlfetch.FetchOptions.Builder.validateCertificate;
import static com.google.appengine.api.urlfetch.HTTPMethod.PUT;
import static com.google.common.io.BaseEncoding.base64;
import static com.google.common.net.HttpHeaders.AUTHORIZATION;
import static com.google.common.net.HttpHeaders.CONTENT_TYPE;
import static google.registry.request.UrlConnectionUtils.getResponseBytes;
import static google.registry.request.UrlConnectionUtils.setBasicAuth;
import static google.registry.request.UrlConnectionUtils.setPayload;
import static google.registry.util.DomainNameUtils.canonicalizeDomainName;
import static java.nio.charset.StandardCharsets.UTF_8;
import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
import static javax.servlet.http.HttpServletResponse.SC_OK;
import com.google.appengine.api.urlfetch.HTTPHeader;
import com.google.appengine.api.urlfetch.HTTPRequest;
import com.google.api.client.http.HttpMethods;
import com.google.appengine.api.urlfetch.HTTPResponse;
import com.google.appengine.api.urlfetch.URLFetchService;
import com.google.common.flogger.FluentLogger;
import com.google.common.net.MediaType;
import google.registry.config.RegistryConfig.Config;
import google.registry.keyring.api.KeyModule.Key;
import google.registry.request.HttpException.InternalServerErrorException;
import google.registry.request.UrlConnectionService;
import google.registry.util.Retrier;
import google.registry.util.UrlFetchException;
import google.registry.util.UrlConnectionException;
import google.registry.xjc.XjcXmlTransformer;
import google.registry.xjc.iirdea.XjcIirdeaResponseElement;
import google.registry.xjc.iirdea.XjcIirdeaResult;
@@ -41,6 +39,7 @@ import google.registry.xjc.rdeheader.XjcRdeHeader;
import google.registry.xjc.rdereport.XjcRdeReportReport;
import google.registry.xml.XmlException;
import java.io.ByteArrayInputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.SocketTimeoutException;
import java.net.URL;
@@ -55,12 +54,15 @@ public class RdeReporter {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
/** @see <a href="http://tools.ietf.org/html/draft-lozano-icann-registry-interfaces-05#section-4">
* ICANN Registry Interfaces - Interface details</a>*/
private static final String REPORT_MIME = "text/xml";
/**
* @see <a href="http://tools.ietf.org/html/draft-lozano-icann-registry-interfaces-05#section-4">
* ICANN Registry Interfaces - Interface details</a>
*/
private static final MediaType MEDIA_TYPE = MediaType.XML_UTF_8;
@Inject Retrier retrier;
@Inject URLFetchService urlFetchService;
@Inject UrlConnectionService urlConnectionService;
@Inject @Config("rdeReportUrlPrefix") String reportUrlPrefix;
@Inject @Key("icannReportingPassword") String password;
@Inject RdeReporter() {}
@@ -74,29 +76,24 @@ public class RdeReporter {
// Send a PUT request to ICANN's HTTPS server.
URL url = makeReportUrl(header.getTld(), report.getId());
String username = header.getTld() + "_ry";
String token = base64().encode(String.format("%s:%s", username, password).getBytes(UTF_8));
final HTTPRequest req = new HTTPRequest(url, PUT, validateCertificate().setDeadline(60d));
req.addHeader(new HTTPHeader(CONTENT_TYPE, REPORT_MIME));
req.addHeader(new HTTPHeader(AUTHORIZATION, "Basic " + token));
req.setPayload(reportBytes);
logger.atInfo().log("Sending report:\n%s", new String(reportBytes, UTF_8));
HTTPResponse rsp =
byte[] responseBytes =
retrier.callWithRetry(
() -> {
HTTPResponse rsp1 = urlFetchService.fetch(req);
switch (rsp1.getResponseCode()) {
case SC_OK:
case SC_BAD_REQUEST:
break;
default:
throw new UrlFetchException("PUT failed", req, rsp1);
HttpURLConnection connection = urlConnectionService.createConnection(url);
connection.setRequestMethod(HttpMethods.PUT);
setBasicAuth(connection, username, password);
setPayload(connection, reportBytes, MEDIA_TYPE.toString());
int responseCode = connection.getResponseCode();
if (responseCode == SC_OK || responseCode == SC_BAD_REQUEST) {
return getResponseBytes(connection);
}
return rsp1;
throw new UrlConnectionException("PUT failed", connection);
},
SocketTimeoutException.class);
// Ensure the XML response is valid.
XjcIirdeaResult result = parseResult(rsp);
XjcIirdeaResult result = parseResult(responseBytes);
if (result.getCode().getValue() != 1000) {
logger.atWarning().log(
"PUT rejected: %d %s\n%s",
@@ -108,11 +105,11 @@ public class RdeReporter {
/**
* Unmarshals IIRDEA XML result object from {@link HTTPResponse} payload.
*
* @see <a href="http://tools.ietf.org/html/draft-lozano-icann-registry-interfaces-05#section-4.1">
* @see <a
* href="http://tools.ietf.org/html/draft-lozano-icann-registry-interfaces-05#section-4.1">
* ICANN Registry Interfaces - IIRDEA Result Object</a>
*/
private XjcIirdeaResult parseResult(HTTPResponse rsp) throws XmlException {
byte[] responseBytes = rsp.getContent();
private XjcIirdeaResult parseResult(byte[] responseBytes) throws XmlException {
logger.atInfo().log("Received response:\n%s", new String(responseBytes, UTF_8));
XjcIirdeaResponseElement response = XjcXmlTransformer.unmarshal(
XjcIirdeaResponseElement.class, new ByteArrayInputStream(responseBytes));

View File

@@ -23,12 +23,11 @@ import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.urlfetch.URLFetchService;
import com.google.appengine.api.urlfetch.URLFetchServiceFactory;
import com.google.appengine.api.users.UserService;
import com.google.appengine.api.users.UserServiceFactory;
import dagger.Module;
import dagger.Provides;
import java.net.HttpURLConnection;
import javax.inject.Singleton;
/** Dagger modules for App Engine services and other vendor classes. */
@@ -45,14 +44,12 @@ public final class Modules {
}
}
/** Dagger module for {@link URLFetchService}. */
/** Dagger module for {@link UrlConnectionService}. */
@Module
public static final class URLFetchServiceModule {
private static final URLFetchService fetchService = URLFetchServiceFactory.getURLFetchService();
public static final class UrlConnectionServiceModule {
@Provides
static URLFetchService provideURLFetchService() {
return fetchService;
static UrlConnectionService provideUrlConnectionService() {
return url -> (HttpURLConnection) url.openConnection();
}
}

View File

@@ -0,0 +1,25 @@
// Copyright 2022 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.request;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
/** Functional interface for opening a connection from a URL, injectable for testing. */
public interface UrlConnectionService {
HttpURLConnection createConnection(URL url) throws IOException;
}

View File

@@ -0,0 +1,104 @@
// Copyright 2022 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.request;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.io.BaseEncoding.base64;
import static com.google.common.net.HttpHeaders.AUTHORIZATION;
import static com.google.common.net.HttpHeaders.CONTENT_DISPOSITION;
import static com.google.common.net.HttpHeaders.CONTENT_LENGTH;
import static com.google.common.net.HttpHeaders.CONTENT_TYPE;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.base.Strings;
import com.google.common.io.ByteStreams;
import com.google.common.net.MediaType;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.URLConnection;
import java.util.Random;
/** Utilities for common functionality relating to {@link java.net.URLConnection}s. */
public class UrlConnectionUtils {
/** Retrieves the response from the given connection as a byte array. */
public static byte[] getResponseBytes(URLConnection connection) throws IOException {
return ByteStreams.toByteArray(connection.getInputStream());
}
/** Sets auth on the given connection with the given username/password. */
public static void setBasicAuth(URLConnection connection, String username, String password) {
setBasicAuth(connection, String.format("%s:%s", username, password));
}
/** Sets auth on the given connection with the given string, formatted "username:password". */
public static void setBasicAuth(URLConnection connection, String usernameAndPassword) {
String token = base64().encode(usernameAndPassword.getBytes(UTF_8));
connection.setRequestProperty(AUTHORIZATION, "Basic " + token);
}
/** Sets the given byte[] payload on the given connection with a particular content type. */
public static void setPayload(URLConnection connection, byte[] bytes, String contentType)
throws IOException {
connection.setRequestProperty(CONTENT_TYPE, contentType);
connection.setDoOutput(true);
try (DataOutputStream dataStream = new DataOutputStream(connection.getOutputStream())) {
dataStream.write(bytes);
}
}
/**
* Sets payload on request as a {@code multipart/form-data} request.
*
* <p>This is equivalent to running the command: {@code curl -F fieldName=@payload.txt URL}
*
* @see <a href="http://www.ietf.org/rfc/rfc2388.txt">RFC2388 - Returning Values from Forms</a>
*/
public static void setPayloadMultipart(
URLConnection connection,
String name,
String filename,
MediaType contentType,
String data,
Random random)
throws IOException {
String boundary = createMultipartBoundary(random);
checkState(
!data.contains(boundary), "Multipart data contains autogenerated boundary: %s", boundary);
String multipart =
String.format("--%s\r\n", boundary)
+ String.format(
"%s: form-data; name=\"%s\"; filename=\"%s\"\r\n",
CONTENT_DISPOSITION, name, filename)
+ String.format("%s: %s\r\n", CONTENT_TYPE, contentType)
+ "\r\n"
+ data
+ "\r\n"
+ String.format("--%s--\r\n", boundary);
byte[] payload = multipart.getBytes(UTF_8);
connection.setRequestProperty(CONTENT_LENGTH, Integer.toString(payload.length));
setPayload(
connection, payload, String.format("multipart/form-data;" + " boundary=\"%s\"", boundary));
}
private static String createMultipartBoundary(Random random) {
// Generate 192 random bits (24 bytes) to produce 192/log_2(64) = 192/6 = 32 base64 digits.
byte[] rand = new byte[24];
random.nextBytes(rand);
// Boundary strings can be up to 70 characters long, so use 30 hyphens plus 32 random digits.
// See https://tools.ietf.org/html/rfc2046#section-5.1.1
return Strings.repeat("-", 30) + base64().encode(rand);
}
}

View File

@@ -15,12 +15,12 @@
package google.registry.tmch;
import static com.google.common.base.Verify.verifyNotNull;
import static google.registry.util.UrlFetchUtils.setAuthorizationHeader;
import com.google.appengine.api.urlfetch.HTTPRequest;
import com.google.common.flogger.FluentLogger;
import google.registry.keyring.api.KeyModule.Key;
import google.registry.model.tld.Registry;
import google.registry.request.UrlConnectionUtils;
import java.net.HttpURLConnection;
import java.util.Optional;
import javax.inject.Inject;
@@ -37,8 +37,9 @@ final class LordnRequestInitializer {
}
/** Initializes a URL fetch request for talking to the MarksDB server. */
void initialize(HTTPRequest request, String tld) {
setAuthorizationHeader(request, getMarksDbLordnCredentials(tld));
void initialize(HttpURLConnection connection, String tld) {
getMarksDbLordnCredentials(tld)
.ifPresent(login -> UrlConnectionUtils.setBasicAuth(connection, login));
}
/** Returns the username and password for the current TLD to login to the MarksDB server. */

View File

@@ -14,25 +14,23 @@
package google.registry.tmch;
import static com.google.appengine.api.urlfetch.FetchOptions.Builder.validateCertificate;
import static com.google.appengine.api.urlfetch.HTTPMethod.GET;
import static com.google.common.base.Preconditions.checkArgument;
import static google.registry.request.UrlConnectionUtils.getResponseBytes;
import static google.registry.request.UrlConnectionUtils.setBasicAuth;
import static google.registry.util.HexDumper.dumpHex;
import static google.registry.util.UrlFetchUtils.setAuthorizationHeader;
import static java.nio.charset.StandardCharsets.US_ASCII;
import static javax.servlet.http.HttpServletResponse.SC_OK;
import com.google.appengine.api.urlfetch.HTTPRequest;
import com.google.appengine.api.urlfetch.HTTPResponse;
import com.google.appengine.api.urlfetch.URLFetchService;
import com.google.common.collect.ImmutableList;
import com.google.common.flogger.FluentLogger;
import com.google.common.io.ByteSource;
import google.registry.config.RegistryConfig.Config;
import google.registry.keyring.api.KeyModule.Key;
import google.registry.util.UrlFetchException;
import google.registry.request.UrlConnectionService;
import google.registry.util.UrlConnectionException;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.Security;
import java.security.SignatureException;
@@ -57,7 +55,8 @@ public final class Marksdb {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private static final int MAX_DNL_LOGGING_LENGTH = 500;
@Inject URLFetchService fetchService;
@Inject UrlConnectionService urlConnectionService;
@Inject @Config("tmchMarksdbUrl") String tmchMarksdbUrl;
@Inject @Key("marksdbPublicKey") PGPPublicKey marksdbPublicKey;
@Inject Marksdb() {}
@@ -112,19 +111,16 @@ public final class Marksdb {
}
byte[] fetch(URL url, Optional<String> loginAndPassword) throws IOException {
HTTPRequest req = new HTTPRequest(url, GET, validateCertificate().setDeadline(60d));
setAuthorizationHeader(req, loginAndPassword);
HTTPResponse rsp;
HttpURLConnection connection = urlConnectionService.createConnection(url);
loginAndPassword.ifPresent(auth -> setBasicAuth(connection, auth));
try {
rsp = fetchService.fetch(req);
if (connection.getResponseCode() != SC_OK) {
throw new UrlConnectionException("Failed to fetch from MarksDB", connection);
}
return getResponseBytes(connection);
} catch (IOException e) {
throw new IOException(
String.format("Error connecting to MarksDB at URL %s", url), e);
throw new IOException(String.format("Error connecting to MarksDB at URL %s", url), e);
}
if (rsp.getResponseCode() != SC_OK) {
throw new UrlFetchException("Failed to fetch from MarksDB", req, rsp);
}
return rsp.getContent();
}
List<String> fetchSignedCsv(Optional<String> loginAndPassword, String csvPath, String sigPath)

View File

@@ -16,28 +16,23 @@ package google.registry.tmch;
import static com.google.appengine.api.taskqueue.QueueFactory.getQueue;
import static com.google.appengine.api.taskqueue.TaskOptions.Builder.withUrl;
import static com.google.appengine.api.urlfetch.FetchOptions.Builder.validateCertificate;
import static com.google.appengine.api.urlfetch.HTTPMethod.POST;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.net.HttpHeaders.LOCATION;
import static com.google.common.net.MediaType.CSV_UTF_8;
import static google.registry.request.UrlConnectionUtils.getResponseBytes;
import static google.registry.tmch.LordnTaskUtils.COLUMNS_CLAIMS;
import static google.registry.tmch.LordnTaskUtils.COLUMNS_SUNRISE;
import static google.registry.util.UrlFetchUtils.getHeaderFirst;
import static google.registry.util.UrlFetchUtils.setPayloadMultipart;
import static java.nio.charset.StandardCharsets.US_ASCII;
import static java.nio.charset.StandardCharsets.UTF_8;
import static javax.servlet.http.HttpServletResponse.SC_ACCEPTED;
import com.google.api.client.http.HttpMethods;
import com.google.appengine.api.taskqueue.LeaseOptions;
import com.google.appengine.api.taskqueue.Queue;
import com.google.appengine.api.taskqueue.TaskHandle;
import com.google.appengine.api.taskqueue.TaskOptions;
import com.google.appengine.api.taskqueue.TransientFailureException;
import com.google.appengine.api.urlfetch.HTTPRequest;
import com.google.appengine.api.urlfetch.HTTPResponse;
import com.google.appengine.api.urlfetch.URLFetchService;
import com.google.apphosting.api.DeadlineExceededException;
import com.google.common.base.Joiner;
import com.google.common.base.Strings;
@@ -49,16 +44,18 @@ import google.registry.config.RegistryConfig.Config;
import google.registry.request.Action;
import google.registry.request.Parameter;
import google.registry.request.RequestParameters;
import google.registry.request.UrlConnectionService;
import google.registry.request.UrlConnectionUtils;
import google.registry.request.auth.Auth;
import google.registry.util.Clock;
import google.registry.util.Retrier;
import google.registry.util.TaskQueueUtils;
import google.registry.util.UrlFetchException;
import google.registry.util.UrlConnectionException;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.SecureRandom;
import java.util.List;
import java.util.Optional;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
@@ -97,7 +94,8 @@ public final class NordnUploadAction implements Runnable {
@Inject Retrier retrier;
@Inject SecureRandom random;
@Inject LordnRequestInitializer lordnRequestInitializer;
@Inject URLFetchService fetchService;
@Inject UrlConnectionService urlConnectionService;
@Inject @Config("tmchMarksdbUrl") String tmchMarksdbUrl;
@Inject @Parameter(LORDN_PHASE_PARAM) String phase;
@Inject @Parameter(RequestParameters.PARAM_TLD) String tld;
@@ -193,47 +191,48 @@ public final class NordnUploadAction implements Runnable {
* <p>Idempotency: If the exact same LORDN report is uploaded twice, the MarksDB server will
* return the same confirmation number.
*
* @see <a href="http://tools.ietf.org/html/draft-lozano-tmch-func-spec-08#section-6.3">
* TMCH functional specifications - LORDN File</a>
* @see <a href="http://tools.ietf.org/html/draft-lozano-tmch-func-spec-08#section-6.3">TMCH
* functional specifications - LORDN File</a>
*/
private void uploadCsvToLordn(String urlPath, String csvData) throws IOException {
String url = tmchMarksdbUrl + urlPath;
logger.atInfo().log(
"LORDN upload task %s: Sending to URL: %s ; data: %s", actionLogId, url, csvData);
HTTPRequest req = new HTTPRequest(new URL(url), POST, validateCertificate().setDeadline(60d));
lordnRequestInitializer.initialize(req, tld);
setPayloadMultipart(req, "file", "claims.csv", CSV_UTF_8, csvData, random);
HTTPResponse rsp;
HttpURLConnection connection = urlConnectionService.createConnection(new URL(url));
connection.setRequestMethod(HttpMethods.POST);
lordnRequestInitializer.initialize(connection, tld);
UrlConnectionUtils.setPayloadMultipart(
connection, "file", "claims.csv", CSV_UTF_8, csvData, random);
try {
rsp = fetchService.fetch(req);
int responseCode = connection.getResponseCode();
if (logger.atInfo().isEnabled()) {
String responseContent = new String(getResponseBytes(connection), US_ASCII);
if (responseContent.isEmpty()) {
responseContent = "(null)";
}
logger.atInfo().log(
"LORDN upload task %s response: HTTP response code %d, response data: %s",
actionLogId, responseCode, responseContent);
}
if (responseCode != SC_ACCEPTED) {
throw new UrlConnectionException(
String.format(
"LORDN upload task %s error: Failed to upload LORDN claims to MarksDB",
actionLogId),
connection);
}
String location = connection.getHeaderField(LOCATION);
if (location == null) {
throw new UrlConnectionException(
String.format(
"LORDN upload task %s error: MarksDB failed to provide a Location header",
actionLogId),
connection);
}
getQueue(NordnVerifyAction.QUEUE).add(makeVerifyTask(new URL(location)));
} catch (IOException e) {
throw new IOException(
String.format("Error connecting to MarksDB at URL %s", url), e);
throw new IOException(String.format("Error connecting to MarksDB at URL %s", url), e);
}
if (logger.atInfo().isEnabled()) {
String response =
(rsp.getContent() == null) ? "(null)" : new String(rsp.getContent(), US_ASCII);
logger.atInfo().log(
"LORDN upload task %s response: HTTP response code %d, response data: %s",
actionLogId, rsp.getResponseCode(), response);
}
if (rsp.getResponseCode() != SC_ACCEPTED) {
throw new UrlFetchException(
String.format(
"LORDN upload task %s error: Failed to upload LORDN claims to MarksDB", actionLogId),
req,
rsp);
}
Optional<String> location = getHeaderFirst(rsp, LOCATION);
if (!location.isPresent()) {
throw new UrlFetchException(
String.format(
"LORDN upload task %s error: MarksDB failed to provide a Location header",
actionLogId),
req,
rsp);
}
getQueue(NordnVerifyAction.QUEUE).add(makeVerifyTask(new URL(location.get())));
}
private TaskOptions makeVerifyTask(URL url) {

View File

@@ -14,15 +14,11 @@
package google.registry.tmch;
import static com.google.appengine.api.urlfetch.FetchOptions.Builder.validateCertificate;
import static com.google.appengine.api.urlfetch.HTTPMethod.GET;
import static google.registry.request.UrlConnectionUtils.getResponseBytes;
import static java.nio.charset.StandardCharsets.UTF_8;
import static javax.servlet.http.HttpServletResponse.SC_NO_CONTENT;
import static javax.servlet.http.HttpServletResponse.SC_OK;
import com.google.appengine.api.urlfetch.HTTPRequest;
import com.google.appengine.api.urlfetch.HTTPResponse;
import com.google.appengine.api.urlfetch.URLFetchService;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.flogger.FluentLogger;
import com.google.common.io.ByteSource;
@@ -32,9 +28,11 @@ import google.registry.request.HttpException.ConflictException;
import google.registry.request.Parameter;
import google.registry.request.RequestParameters;
import google.registry.request.Response;
import google.registry.request.UrlConnectionService;
import google.registry.request.auth.Auth;
import google.registry.util.UrlFetchException;
import google.registry.util.UrlConnectionException;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Map.Entry;
import javax.inject.Inject;
@@ -68,7 +66,8 @@ public final class NordnVerifyAction implements Runnable {
@Inject LordnRequestInitializer lordnRequestInitializer;
@Inject Response response;
@Inject URLFetchService fetchService;
@Inject UrlConnectionService urlConnectionService;
@Inject @Header(URL_HEADER) URL url;
@Inject @Header(HEADER_ACTION_LOG_ID) String actionLogId;
@Inject @Parameter(RequestParameters.PARAM_TLD) String tld;
@@ -96,51 +95,49 @@ public final class NordnVerifyAction implements Runnable {
@VisibleForTesting
LordnLog verify() throws IOException {
logger.atInfo().log("LORDN verify task %s: Sending request to URL %s", actionLogId, url);
HTTPRequest req = new HTTPRequest(url, GET, validateCertificate().setDeadline(60d));
lordnRequestInitializer.initialize(req, tld);
HTTPResponse rsp;
HttpURLConnection connection = urlConnectionService.createConnection(url);
lordnRequestInitializer.initialize(connection, tld);
try {
rsp = fetchService.fetch(req);
} catch (IOException e) {
throw new IOException(
String.format("Error connecting to MarksDB at URL %s", url), e);
}
logger.atInfo().log(
"LORDN verify task %s response: HTTP response code %d, response data: %s",
actionLogId, rsp.getResponseCode(), rsp.getContent());
if (rsp.getResponseCode() == SC_NO_CONTENT) {
// Send a 400+ status code so App Engine will retry the task.
throw new ConflictException("Not ready");
}
if (rsp.getResponseCode() != SC_OK) {
throw new UrlFetchException(
String.format("LORDN verify task %s: Failed to verify LORDN upload to MarksDB.",
actionLogId),
req, rsp);
}
LordnLog log =
LordnLog.parse(ByteSource.wrap(rsp.getContent()).asCharSource(UTF_8).readLines());
if (log.getStatus() == LordnLog.Status.ACCEPTED) {
logger.atInfo().log("LORDN verify task %s: Upload accepted.", actionLogId);
} else {
logger.atSevere().log(
"LORDN verify task %s: Upload rejected with reason: %s", actionLogId, log);
}
for (Entry<String, LordnLog.Result> result : log) {
switch (result.getValue().getOutcome()) {
case OK:
break;
case WARNING:
// fall through
case ERROR:
logger.atWarning().log(result.toString());
break;
default:
logger.atWarning().log(
"LORDN verify task %s: Unexpected outcome: %s", actionLogId, result);
break;
int responseCode = connection.getResponseCode();
logger.atInfo().log(
"LORDN verify task %s response: HTTP response code %d", actionLogId, responseCode);
if (responseCode == SC_NO_CONTENT) {
// Send a 400+ status code so App Engine will retry the task.
throw new ConflictException("Not ready");
}
if (responseCode != SC_OK) {
throw new UrlConnectionException(
String.format(
"LORDN verify task %s: Failed to verify LORDN upload to MarksDB.", actionLogId),
connection);
}
LordnLog log =
LordnLog.parse(
ByteSource.wrap(getResponseBytes(connection)).asCharSource(UTF_8).readLines());
if (log.getStatus() == LordnLog.Status.ACCEPTED) {
logger.atInfo().log("LORDN verify task %s: Upload accepted.", actionLogId);
} else {
logger.atSevere().log(
"LORDN verify task %s: Upload rejected with reason: %s", actionLogId, log);
}
for (Entry<String, LordnLog.Result> result : log) {
switch (result.getValue().getOutcome()) {
case OK:
break;
case WARNING:
// fall through
case ERROR:
logger.atWarning().log(result.toString());
break;
default:
logger.atWarning().log(
"LORDN verify task %s: Unexpected outcome: %s", actionLogId, result);
break;
}
}
return log;
} catch (IOException e) {
throw new IOException(String.format("Error connecting to MarksDB at URL %s", url), e);
}
return log;
}
}

View File

@@ -38,8 +38,7 @@ import google.registry.privileges.secretmanager.SecretManagerModule;
import google.registry.rde.RdeModule;
import google.registry.request.Modules.DatastoreServiceModule;
import google.registry.request.Modules.Jackson2Module;
import google.registry.request.Modules.URLFetchServiceModule;
import google.registry.request.Modules.UrlFetchTransportModule;
import google.registry.request.Modules.UrlConnectionServiceModule;
import google.registry.request.Modules.UserServiceModule;
import google.registry.tools.AuthModule.LocalCredentialModule;
import google.registry.tools.javascrap.CompareEscrowDepositsCommand;
@@ -79,8 +78,7 @@ import javax.inject.Singleton;
RegistryToolDataflowModule.class,
RequestFactoryModule.class,
SecretManagerModule.class,
URLFetchServiceModule.class,
UrlFetchTransportModule.class,
UrlConnectionServiceModule.class,
UserServiceModule.class,
UtilsModule.class,
VoidDnsWriterModule.class,