|
@@ -14,6 +14,7 @@ import java.net.http.HttpResponse;
|
|
|
import java.nio.charset.StandardCharsets;
|
|
import java.nio.charset.StandardCharsets;
|
|
|
import java.util.ArrayList;
|
|
import java.util.ArrayList;
|
|
|
import java.util.List;
|
|
import java.util.List;
|
|
|
|
|
+import java.util.Optional;
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* 对应 Go internal/baidu/adx.go 的 ADXClient。
|
|
* 对应 Go internal/baidu/adx.go 的 ADXClient。
|
|
@@ -30,12 +31,16 @@ public class AdxClient {
|
|
|
private final HttpClient httpClient;
|
|
private final HttpClient httpClient;
|
|
|
private final AuctionPriceEncoder priceEncoder;
|
|
private final AuctionPriceEncoder priceEncoder;
|
|
|
private final ObjectMapper objectMapper;
|
|
private final ObjectMapper objectMapper;
|
|
|
|
|
+ private final int trackingMaxRedirects;
|
|
|
|
|
|
|
|
- public AdxClient(String endpoint, AuctionPriceEncoder priceEncoder, ObjectMapper objectMapper) {
|
|
|
|
|
|
|
+ public AdxClient(String endpoint, AuctionPriceEncoder priceEncoder, ObjectMapper objectMapper, int trackingMaxRedirects) {
|
|
|
this.endpoint = endpoint;
|
|
this.endpoint = endpoint;
|
|
|
this.priceEncoder = priceEncoder;
|
|
this.priceEncoder = priceEncoder;
|
|
|
- this.httpClient = HttpClient.newHttpClient();
|
|
|
|
|
|
|
+ this.httpClient = HttpClient.newBuilder()
|
|
|
|
|
+ .followRedirects(HttpClient.Redirect.NEVER)
|
|
|
|
|
+ .build();
|
|
|
this.objectMapper = objectMapper;
|
|
this.objectMapper = objectMapper;
|
|
|
|
|
+ this.trackingMaxRedirects = Math.max(0, trackingMaxRedirects);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -115,16 +120,57 @@ public class AdxClient {
|
|
|
throws IOException, InterruptedException {
|
|
throws IOException, InterruptedException {
|
|
|
List<TrackingResult> results = new ArrayList<>();
|
|
List<TrackingResult> results = new ArrayList<>();
|
|
|
for (String url : trackingUrls) {
|
|
for (String url : trackingUrls) {
|
|
|
|
|
+ results.add(reportTrackingUrl(url));
|
|
|
|
|
+ }
|
|
|
|
|
+ return results;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private TrackingResult reportTrackingUrl(String originalUrl)
|
|
|
|
|
+ throws IOException, InterruptedException {
|
|
|
|
|
+ String currentUrl = originalUrl;
|
|
|
|
|
+ int redirectCount = 0;
|
|
|
|
|
+ while (true) {
|
|
|
HttpRequest req = HttpRequest.newBuilder()
|
|
HttpRequest req = HttpRequest.newBuilder()
|
|
|
- .uri(URI.create(url))
|
|
|
|
|
|
|
+ .uri(URI.create(currentUrl))
|
|
|
.GET()
|
|
.GET()
|
|
|
.header("User-Agent", BAIDU_USER_AGENT)
|
|
.header("User-Agent", BAIDU_USER_AGENT)
|
|
|
.build();
|
|
.build();
|
|
|
HttpResponse<Void> resp = httpClient.send(req, HttpResponse.BodyHandlers.discarding());
|
|
HttpResponse<Void> resp = httpClient.send(req, HttpResponse.BodyHandlers.discarding());
|
|
|
int status = resp.statusCode();
|
|
int status = resp.statusCode();
|
|
|
- results.add(new TrackingResult(url, status, status >= 200 && status < 400));
|
|
|
|
|
|
|
+ if (status == 200) {
|
|
|
|
|
+ log.info("[百度监测][请求成功] originalUrl={} finalUrl={} status={} redirects={}",
|
|
|
|
|
+ originalUrl, currentUrl, status, redirectCount);
|
|
|
|
|
+ return new TrackingResult(originalUrl, status, true);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (status != 302) {
|
|
|
|
|
+ log.warn("[百度监测][请求失败] originalUrl={} finalUrl={} status={} redirects={}",
|
|
|
|
|
+ originalUrl, currentUrl, status, redirectCount);
|
|
|
|
|
+ return new TrackingResult(originalUrl, status, false);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ Optional<String> location = resp.headers().firstValue("location");
|
|
|
|
|
+ if (location.isEmpty() || location.get().isBlank()) {
|
|
|
|
|
+ log.warn("[百度监测][302跟随失败] originalUrl={} currentUrl={} status={} redirects={} reason=missing_location",
|
|
|
|
|
+ originalUrl, currentUrl, status, redirectCount);
|
|
|
|
|
+ return new TrackingResult(originalUrl, status, false);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (redirectCount >= trackingMaxRedirects) {
|
|
|
|
|
+ log.warn("[百度监测][302跟随失败] originalUrl={} currentUrl={} location={} status={} redirects={} maxRedirects={} reason=redirect_limit_exceeded",
|
|
|
|
|
+ originalUrl, currentUrl, location.get(), status, redirectCount, trackingMaxRedirects);
|
|
|
|
|
+ return new TrackingResult(originalUrl, status, false);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ String nextUrl = resolveRedirectUrl(currentUrl, location.get());
|
|
|
|
|
+ redirectCount++;
|
|
|
|
|
+ log.info("[百度监测][302跟随] originalUrl={} currentUrl={} location={} nextUrl={} status={} redirects={}/{}",
|
|
|
|
|
+ originalUrl, currentUrl, location.get(), nextUrl, status, redirectCount, trackingMaxRedirects);
|
|
|
|
|
+ currentUrl = nextUrl;
|
|
|
}
|
|
}
|
|
|
- return results;
|
|
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static String resolveRedirectUrl(String currentUrl, String location) {
|
|
|
|
|
+ URI current = URI.create(currentUrl);
|
|
|
|
|
+ return current.resolve(location).toString();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// ─── 默认价格编码(不加密,直接 URL encode 数字字符串)───────────────
|
|
// ─── 默认价格编码(不加密,直接 URL encode 数字字符串)───────────────
|