-
Notifications
You must be signed in to change notification settings - Fork 224
Greenbids fix geolookup: fetch from official MaxMind URL + mock dbReader UT #3626
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 32 commits
0f41d3f
12e072b
4a8d2a3
2eb7a4c
00e7255
6217ac4
5e685d0
972cfd4
587d1ba
8e39877
b2f3c8a
e56b002
37c7be7
af3c123
98bf147
2cc7d99
62068e5
1055606
2d5ba29
b4ae0d7
3972151
2f991c3
a694373
2a7878b
760cbc6
0c372f9
3bbc02d
599d332
40113b4
cc4d6a6
8cdeae9
806e48c
9794643
a7cbc33
ea1b65c
829b83a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,52 +1,120 @@ | ||
| package org.prebid.server.hooks.modules.greenbids.real.time.data.config; | ||
|
|
||
| import io.netty.handler.codec.http.HttpResponseStatus; | ||
| import io.vertx.core.file.FileSystem; | ||
| import io.vertx.core.http.HttpClientRequest; | ||
| import io.vertx.core.http.HttpClientResponse; | ||
| import com.maxmind.db.Reader; | ||
| import io.vertx.core.Future; | ||
| import io.vertx.core.Promise; | ||
| import io.vertx.core.Vertx; | ||
| import com.maxmind.geoip2.DatabaseReader; | ||
| import io.vertx.core.file.OpenOptions; | ||
| import io.vertx.core.http.HttpMethod; | ||
| import io.vertx.core.http.RequestOptions; | ||
| import org.apache.commons.compress.archivers.tar.TarArchiveEntry; | ||
| import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; | ||
| import org.prebid.server.exception.PreBidException; | ||
| import org.prebid.server.log.Logger; | ||
| import org.prebid.server.log.LoggerFactory; | ||
| import org.prebid.server.vertx.Initializable; | ||
|
|
||
| import java.io.FileOutputStream; | ||
| import java.io.IOException; | ||
| import java.io.InputStream; | ||
| import java.net.URL; | ||
| import java.nio.file.Files; | ||
| import java.nio.file.Path; | ||
| import java.util.concurrent.atomic.AtomicReference; | ||
| import java.util.zip.GZIPInputStream; | ||
|
|
||
| public class DatabaseReaderFactory implements Initializable { | ||
|
|
||
| private final String geoLiteCountryUrl; | ||
| private static final Logger logger = LoggerFactory.getLogger(DatabaseReaderFactory.class); | ||
|
|
||
| private final GreenbidsRealTimeDataProperties properties; | ||
|
|
||
| private final Vertx vertx; | ||
|
|
||
| private final AtomicReference<DatabaseReader> databaseReaderRef = new AtomicReference<>(); | ||
|
|
||
| public DatabaseReaderFactory(String geoLitCountryUrl, Vertx vertx) { | ||
| this.geoLiteCountryUrl = geoLitCountryUrl; | ||
| private final FileSystem fileSystem; | ||
|
|
||
| public DatabaseReaderFactory( | ||
EvgeniiMunin marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| GreenbidsRealTimeDataProperties properties, Vertx vertx) { | ||
| this.properties = properties; | ||
| this.vertx = vertx; | ||
| this.fileSystem = vertx.fileSystem(); | ||
| } | ||
|
|
||
| @Override | ||
| public void initialize(Promise<Void> initializePromise) { | ||
| vertx.executeBlocking(() -> downloadAndExtract().onSuccess(databaseReaderRef::set)) | ||
EvgeniiMunin marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| .<Void>mapEmpty() | ||
| .onComplete(initializePromise); | ||
| } | ||
|
|
||
| private Future<DatabaseReader> downloadAndExtract() { | ||
| final String downloadUrl = properties.getGeoLiteCountryPath(); | ||
| final String tmpPath = properties.getTmpPath(); | ||
| return downloadFile(downloadUrl, tmpPath) | ||
| .map(unused -> extractMMDB(tmpPath)) | ||
EvgeniiMunin marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| .onComplete(ar -> removeFile(tmpPath)); | ||
| } | ||
|
|
||
| private Future<Void> downloadFile(String downloadUrl, String tmpPath) { | ||
| return fileSystem.open(tmpPath, new OpenOptions()) | ||
| .compose(tmpFile -> sendHttpRequest(downloadUrl) | ||
| .compose(response -> response.pipeTo(tmpFile)) | ||
|
||
| .onFailure(error -> logger.error( | ||
| "Failed to download file from {} to {}.", downloadUrl, tmpPath, error))); | ||
| } | ||
|
|
||
| private Future<HttpClientResponse> sendHttpRequest(String url) { | ||
| final RequestOptions options = new RequestOptions() | ||
| .setFollowRedirects(true) | ||
| .setMethod(HttpMethod.GET) | ||
| .setTimeout(properties.getTimeoutMs()) | ||
| .setAbsoluteURI(url); | ||
|
|
||
| vertx.executeBlocking(() -> { | ||
| try { | ||
| final URL url = new URL(geoLiteCountryUrl); | ||
| final Path databasePath = Files.createTempFile("GeoLite2-Country", ".mmdb"); | ||
| return vertx.createHttpClient().request(options) | ||
EvgeniiMunin marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| .compose(HttpClientRequest::send) | ||
| .map(this::validateResponse); | ||
| } | ||
|
|
||
| private HttpClientResponse validateResponse(HttpClientResponse response) { | ||
| final int statusCode = response.statusCode(); | ||
| if (statusCode != HttpResponseStatus.OK.code()) { | ||
| throw new PreBidException("Got unexpected response from server with status code %s and message %s" | ||
| .formatted(statusCode, response.statusMessage())); | ||
| } | ||
| return response; | ||
| } | ||
|
|
||
| private DatabaseReader extractMMDB(String tarGzPath) { | ||
| try (GZIPInputStream gis = new GZIPInputStream(Files.newInputStream(Path.of(tarGzPath))); | ||
| TarArchiveInputStream tarInput = new TarArchiveInputStream(gis)) { | ||
|
|
||
| try (InputStream inputStream = url.openStream(); | ||
| FileOutputStream outputStream = new FileOutputStream(databasePath.toFile())) { | ||
| inputStream.transferTo(outputStream); | ||
| TarArchiveEntry currentEntry; | ||
| boolean hasDatabaseFile = false; | ||
| while ((currentEntry = tarInput.getNextTarEntry()) != null) { | ||
| if (currentEntry.getName().contains("GeoLite2-Country.mmdb")) { | ||
| hasDatabaseFile = true; | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| databaseReaderRef.set(new DatabaseReader.Builder(databasePath.toFile()).build()); | ||
| } catch (IOException e) { | ||
| throw new PreBidException("Failed to initialize DatabaseReader from URL", e); | ||
| if (!hasDatabaseFile) { | ||
| throw new PreBidException("GeoLite2-Country.mmdb not found in the archive"); | ||
| } | ||
| return null; | ||
| }).<Void>mapEmpty() | ||
| .onComplete(initializePromise); | ||
|
|
||
| return new DatabaseReader.Builder(tarInput) | ||
| .fileMode(Reader.FileMode.MEMORY).build(); | ||
| } catch (IOException e) { | ||
| throw new PreBidException("Failed to extract MMDB file", e); | ||
| } | ||
| } | ||
|
|
||
| private void removeFile(String filePath) { | ||
| fileSystem.delete(filePath) | ||
EvgeniiMunin marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| .onFailure(err -> logger.error("Failed to remove file {}", filePath, err)); | ||
| } | ||
|
|
||
| public DatabaseReader getDatabaseReader() { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about to use
RemoteFileSyncerV2instead of this class? Just configureupdate-interval-ms: 0. You can see example of using it withMaxMindGeoLocationService.Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As I remember from our discussion with @AntoxaAntoxic , we have discussed whether to reuse or not RemoteFileSyncerV2 and the public get() method of
RemoteFileSuppliercalled in syncer and decided to not do that and to proceed with defining methodsvalidateResponse,sendHttpRequest,downloadFileon the side of RTD module