diff --git a/docs/config-app.md b/docs/config-app.md index a661f5a74a2..569e4c09b15 100644 --- a/docs/config-app.md +++ b/docs/config-app.md @@ -1,22 +1,31 @@ - # Full list of application configuration options This document describes all configuration properties available for Prebid Server. ## Spring -- `spring.main.banner-mode` - determine if the banner has to be printed on System.out (console), using the configured logger (log) or not at all (off). -This section can be extended against standard [Spring configuration](https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-spring-application.html) options. +- `spring.main.banner-mode` - determine if the banner has to be printed on System.out (console), using the configured + logger (log) or not at all (off). + +This section can be extended against +standard [Spring configuration](https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-spring-application.html) +options. ## Vert.x + - `vertx.worker-pool-size` - set the maximum number of worker threads to be used by the Vert.x instance. -- `vertx.uploads-dir` - directory that Vert.x [BodyHandler](http://vertx.io/docs/apidocs/io/vertx/ext/web/handler/BodyHandler.html) will use to store multi-part file uploads. -This parameter exists to allow to change the location of the directory Vert.x will create because it will and there is no way to make it not. -- `vertx.init-timeout-ms` - time to wait for asynchronous initialization steps completion before considering them stuck. When exceeded - exception is thrown and Prebid Server stops. +- `vertx.uploads-dir` - directory that + Vert.x [BodyHandler](http://vertx.io/docs/apidocs/io/vertx/ext/web/handler/BodyHandler.html) will use to store + multi-part file uploads. + This parameter exists to allow to change the location of the directory Vert.x will create because it will and there is + no way to make it not. +- `vertx.init-timeout-ms` - time to wait for asynchronous initialization steps completion before considering them stuck. + When exceeded - exception is thrown and Prebid Server stops. - `vertx.enable-per-client-endpoint-metrics` - enables HTTP client metrics per destination endpoint (`host:port`) - `vertx.round-robin-inet-address` - enables round-robin inet address selection of the ip address to use ## Server + - `server.max-headers-size` - set the maximum length of all headers. - `server.ssl` - enable SSL/TLS support. - `server.jks-path` - path to the java keystore (if ssl is enabled). @@ -24,6 +33,7 @@ This parameter exists to allow to change the location of the directory Vert.x wi - `server.cpu-load-monitoring.measurement-interval-ms` - the CPU load monitoring interval (milliseconds) ## HTTP Server + - `server.max-headers-size` - set the maximum length of all headers, deprecated(use server.max-headers-size instead). - `server.ssl` - enable SSL/TLS support, deprecated(use server.ssl instead). - `server.jks-path` - path to the java keystore (if ssl is enabled), deprecated(use server.jks-path instead). @@ -33,215 +43,291 @@ This parameter exists to allow to change the location of the directory Vert.x wi - `server.enable-quickack` - enables the TCP_QUICKACK option - only with linux native transport. - `server.enable-reuseport` - set the value of reuse port - `server.http.server-instances` - how many http server instances should be created. - This parameter affects how many CPU cores will be utilized by the application. Rough assumption - one http server instance will keep 1 CPU core busy. + This parameter affects how many CPU cores will be utilized by the application. Rough assumption - one http server + instance will keep 1 CPU core busy. - `server.http.enabled` - if set to `true` enables http server - `server.http.port` - the port to listen on. ## Unix Domain Socket Server + - `server.unix-socket.server-instances` - how many http server instances should be created. - `server.unix-socket.enabled` - if set to `true` enables unix socket server - `server.unix-socket.path` - the path to unix socket to listen on. ## HTTP Client + - `http-client.max-pool-size` - set the maximum pool size for outgoing connections (per host). - `http-client.idle-timeout-ms` - set the maximum time idle connections could exist before being reaped - `http-client.pool-cleaner-period-ms` - set how often idle connections will be closed removed from pool - `http-client.connect-timeout-ms` - set the connect timeout. -- `http-client.circuit-breaker.enabled` - if equals to `true` circuit breaker will be used to make http client more robust. +- `http-client.circuit-breaker.enabled` - if equals to `true` circuit breaker will be used to make http client more + robust. - `http-client.circuit-breaker.opening-threshold` - the number of failures before opening the circuit. -- `http-client.circuit-breaker.opening-interval-ms` - time interval for opening the circuit breaker if failures count reached. +- `http-client.circuit-breaker.opening-interval-ms` - time interval for opening the circuit breaker if failures count + reached (must be a multiple of 1000). - `http-client.circuit-breaker.closing-interval-ms` - time spent in open state before attempting to re-try. - `http-client.circuit-breaker.idle-expire-hours` - idle time to clean the circuit breaker up. -- `http-client.use-compression` - if equals to `true` httpclient compression is enabled for requests (see [also](https://vertx.io/docs/apidocs/io/vertx/core/http/HttpClientOptions.html#setTryUseCompression-boolean-)) -- `http-client.max-redirects` - set the maximum amount of HTTP redirections to follow. A value of 0 (the default) prevents redirections from being followed. +- `http-client.use-compression` - if equals to `true` httpclient compression is enabled for requests ( + see [also](https://vertx.io/docs/apidocs/io/vertx/core/http/HttpClientOptions.html#setTryUseCompression-boolean-)) +- `http-client.max-redirects` - set the maximum amount of HTTP redirections to follow. A value of 0 (the default) + prevents redirections from being followed. - `http-client.ssl` - enable SSL/TLS support. - `http-client.jks-path` - path to the java keystore (if ssl is enabled). - `http-client.jks-password` - password for the keystore (if ssl is enabled). ## Remote-file-syncer + Remote File Syncer can be related to particular entity like geolocation maxmind service etc. Removes and downloads file again if depending service cant process probably corrupted file in the first start. - `.remote-file-syncer.download-url` - url to database file to download. -- `.remote-file-syncer.save-filepath` - full path to the usable file, which will be consumed by internal service. +- `.remote-file-syncer.save-filepath` - full path to the usable file, which will be consumed by internal + service. - `.remote-file-syncer.tmp-filepath` - full path to the temporary file. - `.remote-file-syncer.retry-count` - how many times try to download. - `.remote-file-syncer.retry-interval-ms` - how long to wait between failed retries. - `.remote-file-syncer.retry.delay-millis` - initial time of how long to wait between failed retries. - `.remote-file-syncer.retry.max-delay-millis` - maximum allowed value for `delay-millis`. -- `.remote-file-syncer.retry.factor` - factor for the `delay-millis` value, that will be applied after each failed retry to modify `delay-millis` value. +- `.remote-file-syncer.retry.factor` - factor for the `delay-millis` value, that will be applied after each + failed retry to modify `delay-millis` value. - `.remote-file-syncer.retry.jitter` - jitter (multiplicative) for `delay-millis` parameter. - `.remote-file-syncer.timeout-ms` - default operation timeout for obtaining database file. - `.remote-file-syncer.update-interval-ms` - time interval between updates of the usable file. - `.remote-file-syncer.http-client.connect-timeout-ms` - set the connect timeout. -- `.remote-file-syncer.http-client.max-redirects` - set the maximum amount of HTTP redirections to follow. A value of 0 (the default) prevents redirections from being followed. +- `.remote-file-syncer.http-client.max-redirects` - set the maximum amount of HTTP redirections to follow. A + value of 0 (the default) prevents redirections from being followed. ## General settings -- `host-id` - the ID of node where prebid server deployed. -- `external-url` - the setting stands for external URL prebid server is reachable by, for example address of the load-balancer e.g. http://prebid.host.com. + +- `host-id` - the ID of node where prebid server deployed. +- `external-url` - the setting stands for external URL prebid server is reachable by, for example address of the + load-balancer e.g. http://prebid.host.com. - `admin.port` - the port to listen on administration requests. ## Default bid request + - `default-request.file.path` - path to a JSON file containing the default request ## Auction (OpenRTB) + - `auction.blocklisted-accounts` - comma separated list of blocklisted account IDs. -- `auction.blocklisted-apps` - comma separated list of blocklisted applications IDs, requests from which should not be processed. +- `auction.blocklisted-apps` - comma separated list of blocklisted applications IDs, requests from which should not be + processed. - `auction.biddertmax.min` - minimum operation timeout for OpenRTB Auction requests. - `auction.biddertmax.max` - maximum operation timeout for OpenRTB Auction requests. - `auction.biddertmax.percent` - adjustment factor for `request.tmax` for bidders. - `auction.tmax-upstream-response-time` - the amount of time that PBS needs to respond to the original caller. - `auction.max-request-size` - set the maximum size in bytes of OpenRTB Auction request. - `auction.stored-requests-timeout-ms` - timeout for stored requests fetching. -- `auction.ad-server-currency` - default currency for auction, if its value was not specified in request. Important note: PBS uses ISO-4217 codes for the representation of currencies. +- `auction.ad-server-currency` - default currency for auction, if its value was not specified in request. Important + note: PBS uses ISO-4217 codes for the representation of currencies. - `auction.cache.expected-request-time-ms` - approximate value in milliseconds for Cache Service interacting. -- `auction.cache.only-winning-bids` - if equals to `true` only the winning bids would be cached. Has lower priority than request-specific flags. +- `auction.cache.only-winning-bids` - if equals to `true` only the winning bids would be cached. Has lower priority than + request-specific flags. - `auction.generate-bid-id` - whether to generate seatbid[].bid[].ext.prebid.bidid in the OpenRTB response. -- `auction.enforce-random-bid-id` - whether to enforce generating a robust random seatbid[].bid[].id in the OpenRTB response if the initial value is less than 17 characters. -- `auction.validations.banner-creative-max-size` - enables creative max size validation for banners. Possible values: `skip`, `enforce`, `warn`. Default is `skip`. -- `auction.validations.secure-markup` - enables secure markup validation. Possible values: `skip`, `enforce`, `warn`. Default is `skip`. -- `auction.host-schain-node` - defines global schain node that will be appended to `request.source.ext.schain.nodes` passed to bidders +- `auction.enforce-random-bid-id` - whether to enforce generating a robust random seatbid[].bid[].id in the OpenRTB + response if the initial value is less than 17 characters. +- `auction.validations.banner-creative-max-size` - enables creative max size validation for banners. Possible values: + `skip`, `enforce`, `warn`. Default is `skip`. +- `auction.validations.secure-markup` - enables secure markup validation. Possible values: `skip`, `enforce`, `warn`. + Default is `skip`. +- `auction.host-schain-node` - defines global schain node that will be appended to `request.source.ext.schain.nodes` + passed to bidders - `auction.category-mapping-enabled` - if equals to `true` the category mapping feature will be active while auction. -- `auction.strict-app-site-dooh` - if set to `true`, it will reject requests that contain more than one of app/site/dooh. Defaults to `false`. +- `auction.strict-app-site-dooh` - if set to `true`, it will reject requests that contain more than one of + app/site/dooh. Defaults to `false`. ## Event + - `event.default-timeout-ms` - timeout for event notifications ## Timeout notification + - `auction.timeout-notification.timeout-ms` - HTTP timeout to use when sending notifications about bidder timeouts - `auction.timeout-notification.log-result` - causes bidder timeout notification result to be logged - `auction.timeout-notification.log-failure-only` - causes only bidder timeout notification failures to be logged -- `auction.timeout-notification.log-sampling-rate` - instructs apply sampling when logging bidder timeout notification results +- `auction.timeout-notification.log-sampling-rate` - instructs apply sampling when logging bidder timeout notification + results ## Video + - `video.stored-request-required` - flag forces to merge with stored request - `video.stored-requests-timeout-ms` - timeout for stored requests fetching. - `auction.blocklisted-accounts` - comma separated list of blocklisted account IDs. - `auction.video.escape-log-cache-regex` - regex to remove from cache debug log xml. -- `auction.ad-server-currency` - default currency for video auction, if its value was not specified in request. Important note: PBS uses ISO-4217 codes for the representation of currencies. +- `auction.ad-server-currency` - default currency for video auction, if its value was not specified in request. + Important note: PBS uses ISO-4217 codes for the representation of currencies. ## Setuid + - `setuid.default-timeout-ms` - default operation timeout for requests to `/setuid` endpoint. -- `setuid.number-of-uid-cookies` - specifies the maximum number of UID cookies that can be returned in the `/setuid` endpoint response. If it's not specified `1` will be taken as the default value. +- `setuid.number-of-uid-cookies` - specifies the maximum number of UID cookies that can be returned in the `/setuid` + endpoint response. If it's not specified `1` will be taken as the default value. ## Cookie Sync + - `cookie-sync.default-timeout-ms` - default operation timeout for requests to `/cookie_sync` endpoint. - `cookie-sync.coop-sync.default` - default value for coopSync when it missing in requests to `/cookie_sync` endpoint. - `cookie-sync.pri` - lists of bidders prioritised in groups. -- `cookie-sync.default-limit` - default bidder limit, that is applied when limit parameter is not sent in the request and absent in account config -- `cookie-sync.max-limit` - default maximum possible limit value for the limit parameter, that is applied when maximum limit parameter is absent in account config +- `cookie-sync.default-limit` - default bidder limit, that is applied when limit parameter is not sent in the request + and absent in account config +- `cookie-sync.max-limit` - default maximum possible limit value for the limit parameter, that is applied when maximum + limit parameter is absent in account config ## Vtrack -- `vtrack.allow-unknown-bidder` - flag that allows servicing requests with bidders who were not configured in Prebid Server. -- `vtrack.modify-vast-for-unknown-bidder` - flag that allows modifying the VAST value and adding the impression tag to it, for bidders who were not configured in Prebid Server. + +- `vtrack.allow-unknown-bidder` - flag that allows servicing requests with bidders who were not configured in Prebid + Server. +- `vtrack.modify-vast-for-unknown-bidder` - flag that allows modifying the VAST value and adding the impression tag to + it, for bidders who were not configured in Prebid Server. - `vtrack.default-timeout-ms` - a default timeout in ms for the vtrack request ## Adapters + - `adapters.*` - the section for bidder specific configuration options. There are several typical keys: -- `adapters..enabled` - indicates the bidder should be active and ready for auction. By default all bidders are disabled. + +- `adapters..enabled` - indicates the bidder should be active and ready for auction. By default all bidders + are disabled. - `adapters..endpoint` - the url for submitting bids. -- `adapters..pbs-enforces-ccpa` - indicates if PBS server provides CCPA support for bidder or bidder will handle it itself. -- `adapters..modifying-vast-xml-allowed` - indicates if PBS server is allowed to modify VAST creatives received from this bidder. +- `adapters..pbs-enforces-ccpa` - indicates if PBS server provides CCPA support for bidder or bidder will + handle it itself. +- `adapters..modifying-vast-xml-allowed` - indicates if PBS server is allowed to modify VAST creatives + received from this bidder. - `adapters..deprecated-names` - comma separated deprecated names of bidder. -- `adapters..meta-info.maintainer-email` - specifies maintainer e-mail address that will be shown in bidder info endpoint response. -- `adapters..meta-info.app-media-types` - specifies media types supported for app requests that will be shown in bidder info endpoint response. -- `adapters..meta-info.site-media-types` - specifies media types supported for site requests that will be shown in bidder info endpoint response. +- `adapters..meta-info.maintainer-email` - specifies maintainer e-mail address that will be shown in bidder + info endpoint response. +- `adapters..meta-info.app-media-types` - specifies media types supported for app requests that will be + shown in bidder info endpoint response. +- `adapters..meta-info.site-media-types` - specifies media types supported for site requests that will be + shown in bidder info endpoint response. - `adapters..meta-info.supported-vendors` - specifies viewability vendors supported by the bidder. - `adapters..meta-info.vendor-id` - specifies TCF vendor ID. - `adapters..usersync.url` - the url for synchronizing UIDs cookie. - `adapters..usersync.redirect-url` - the redirect part of url for synchronizing UIDs cookie. -- `adapters..usersync.cookie-family-name` - the family name by which user ids within adapter's realm are stored in uidsCookie. +- `adapters..usersync.cookie-family-name` - the family name by which user ids within adapter's realm are + stored in uidsCookie. - `adapters..usersync.type` - usersync type (i.e. redirect, iframe). - `adapters..usersync.support-cors` - flag signals if CORS supported by usersync. -- `adapters..debug.allow` - enables debug output in the auction response for the given bidder. Default `true`. -- `adapters..tmax-deduction-ms` - adjusts the tmax sent to the bidder by deducting the provided value (ms). Default `0 ms` - no deduction. - -In addition, each bidder could have arbitrary aliases configured that will look and act very much the same as the bidder itself. -Aliases are configured by adding child configuration object at `adapters..aliases..`, aliases -support the same configuration options that their bidder counterparts support except `aliases` (i.e. it's not possible -to declare alias of an alias). Another restriction of aliases configuration is that they cannot declare support for media types -not supported by their bidders (however aliases could narrow down media types they support). For example: if the bidder -is written to not support native site requests, then an alias cannot magically decide to change that; however, if a bidder -supports native site requests, and the alias does not want to for some reason, it has the ability to remove that support. +- `adapters..debug.allow` - enables debug output in the auction response for the given bidder. Default + `true`. +- `adapters..tmax-deduction-ms` - adjusts the tmax sent to the bidder by deducting the provided value (ms). + Default `0 ms` - no deduction. + +In addition, each bidder could have arbitrary aliases configured that will look and act very much the same as the bidder +itself. +Aliases are configured by adding child configuration object at `adapters..aliases..`, aliases +support the same configuration options that their bidder counterparts support except `aliases` (i.e. it's not possible +to declare alias of an alias). Another restriction of aliases configuration is that they cannot declare support for +media types +not supported by their bidders (however aliases could narrow down media types they support). For example: if the bidder +is written to not support native site requests, then an alias cannot magically decide to change that; however, if a +bidder +supports native site requests, and the alias does not want to for some reason, it has the ability to remove that +support. Also, each bidder could have its own bidder-specific options. ## Logging + - `logging.http-interaction.max-limit` - maximum value for the number of interactions to log in one take. - `logging.change-level.max-duration-ms` - maximum duration (in milliseconds) for which logging level could be changed. - `logging.sampling-rate` - a percentage of messages that are logged ## Currency Converter -- `currency-converter.external-rates.enabled` - if equals to `true` the currency conversion service will be enabled to fetch updated rates and convert bid currencies from external source. Also enables `/currency-rates` endpoint on admin port. -- `currency-converter.external-rates.url` - the url for Prebid.org’s currency file. [More details](http://prebid.org/dev-docs/modules/currency.html) + +- `currency-converter.external-rates.enabled` - if equals to `true` the currency conversion service will be enabled to + fetch updated rates and convert bid currencies from external source. Also enables `/currency-rates` endpoint on admin + port. +- `currency-converter.external-rates.url` - the url for Prebid.org’s currency + file. [More details](http://prebid.org/dev-docs/modules/currency.html) - `currency-converter.external-rates.default-timeout-ms` - default operation timeout for fetching currency rates. - `currency-converter.external-rates.refresh-period-ms` - default refresh period for currency rates updates. - `currency-converter.external-rates.stale-after-ms` - how old currency rates should be to become considered stale. -- `currency-converter.external-rates.stale-period-ms` - stale period after which the latest external currency rates get discarded. +- `currency-converter.external-rates.stale-period-ms` - stale period after which the latest external currency rates get + discarded. ## Admin Endpoints + - `admin-endpoints.version.enabled` - if equals to `true` the endpoint will be available. - `admin-endpoints.version.path` - the server context path where the endpoint will be accessible. - `admin-endpoints.version.on-application-port` - when equals to `false` endpoint will be bound to `admin.port`. -- `admin-endpoints.version.protected` - when equals to `true` endpoint will be protected by basic authentication configured in `admin-endpoints.credentials` +- `admin-endpoints.version.protected` - when equals to `true` endpoint will be protected by basic authentication + configured in `admin-endpoints.credentials` - `admin-endpoints.currency-rates.enabled` - if equals to `true` the endpoint will be available. - `admin-endpoints.currency-rates.path` - the server context path where the endpoint will be accessible. - `admin-endpoints.currency-rates.on-application-port` - when equals to `false` endpoint will be bound to `admin.port`. -- `admin-endpoints.currency-rates.protected` - when equals to `true` endpoint will be protected by basic authentication configured in `admin-endpoints.credentials` +- `admin-endpoints.currency-rates.protected` - when equals to `true` endpoint will be protected by basic authentication + configured in `admin-endpoints.credentials` - `admin-endpoints.storedrequest.enabled` - if equals to `true` the endpoint will be available. - `admin-endpoints.storedrequest.path` - the server context path where the endpoint will be accessible. - `admin-endpoints.storedrequest.on-application-port` - when equals to `false` endpoint will be bound to `admin.port`. -- `admin-endpoints.storedrequest.protected` - when equals to `true` endpoint will be protected by basic authentication configured in `admin-endpoints.credentials` +- `admin-endpoints.storedrequest.protected` - when equals to `true` endpoint will be protected by basic authentication + configured in `admin-endpoints.credentials` - `admin-endpoints.storedrequest-amp.enabled` - if equals to `true` the endpoint will be available. - `admin-endpoints.storedrequest-amp.path` - the server context path where the endpoint will be accessible. -- `admin-endpoints.storedrequest-amp.on-application-port` - when equals to `false` endpoint will be bound to `admin.port`. -- `admin-endpoints.storedrequest-amp.protected` - when equals to `true` endpoint will be protected by basic authentication configured in `admin-endpoints.credentials` +- `admin-endpoints.storedrequest-amp.on-application-port` - when equals to `false` endpoint will be bound to + `admin.port`. +- `admin-endpoints.storedrequest-amp.protected` - when equals to `true` endpoint will be protected by basic + authentication configured in `admin-endpoints.credentials` - `admin-endpoints.cache-invalidation.enabled` - if equals to `true` the endpoint will be available. - `admin-endpoints.cache-invalidation.path` - the server context path where the endpoint will be accessible. -- `admin-endpoints.cache-invalidation.on-application-port` - when equals to `false` endpoint will be bound to `admin.port`. -- `admin-endpoints.cache-invalidation.protected` - when equals to `true` endpoint will be protected by basic authentication configured in `admin-endpoints.credentials` +- `admin-endpoints.cache-invalidation.on-application-port` - when equals to `false` endpoint will be bound to + `admin.port`. +- `admin-endpoints.cache-invalidation.protected` - when equals to `true` endpoint will be protected by basic + authentication configured in `admin-endpoints.credentials` - `admin-endpoints.logging-httpinteraction.enabled` - if equals to `true` the endpoint will be available. - `admin-endpoints.logging-httpinteraction.path` - the server context path where the endpoint will be accessible. -- `admin-endpoints.logging-httpinteraction.on-application-port` - when equals to `false` endpoint will be bound to `admin.port`. -- `admin-endpoints.logging-httpinteraction.protected` - when equals to `true` endpoint will be protected by basic authentication configured in `admin-endpoints.credentials` +- `admin-endpoints.logging-httpinteraction.on-application-port` - when equals to `false` endpoint will be bound to + `admin.port`. +- `admin-endpoints.logging-httpinteraction.protected` - when equals to `true` endpoint will be protected by basic + authentication configured in `admin-endpoints.credentials` - `admin-endpoints.tracelog.enabled` - if equals to `true` the endpoint will be available. - `admin-endpoints.tracelog.path` - the server context path where the endpoint will be accessible. - `admin-endpoints.tracelog.on-application-port` - when equals to `false` endpoint will be bound to `admin.port`. -- `admin-endpoints.tracelog.protected` - when equals to `true` endpoint will be protected by basic authentication configured in `admin-endpoints.credentials` +- `admin-endpoints.tracelog.protected` - when equals to `true` endpoint will be protected by basic authentication + configured in `admin-endpoints.credentials` - `admin-endpoints.collected-metrics.enabled` - if equals to `true` the endpoint will be available. - `admin-endpoints.collected-metrics.path` - the server context path where the endpoint will be accessible. -- `admin-endpoints.collected-metrics.on-application-port` - when equals to `false` endpoint will be bound to `admin.port`. -- `admin-endpoints.collected-metrics.protected` - when equals to `true` endpoint will be protected by basic authentication configured in `admin-endpoints.credentials` +- `admin-endpoints.collected-metrics.on-application-port` - when equals to `false` endpoint will be bound to + `admin.port`. +- `admin-endpoints.collected-metrics.protected` - when equals to `true` endpoint will be protected by basic + authentication configured in `admin-endpoints.credentials` - `admin-endpoints.logging-changelevel.enabled` - if equals to `true` the endpoint will be available. - `admin-endpoints.logging-changelevel.path` - the server context path where the endpoint will be accessible -- `admin-endpoints.logging-changelevel.on-application-port` - when equals to `false` endpoint will be bound to `admin.port`. -- `admin-endpoints.logging-changelevel.protected` - when equals to `true` endpoint will be protected by basic authentication configured in `admin-endpoints.credentials` +- `admin-endpoints.logging-changelevel.on-application-port` - when equals to `false` endpoint will be bound to + `admin.port`. +- `admin-endpoints.logging-changelevel.protected` - when equals to `true` endpoint will be protected by basic + authentication configured in `admin-endpoints.credentials` -- `admin-endpoints.credentials` - user and password for access to admin endpoints if `admin-endpoints.[NAME].protected` is true`. +- `admin-endpoints.credentials` - user and password for access to admin endpoints if `admin-endpoints.[NAME].protected` + is true`. ## Metrics -- `metrics.metricType` - set the type of metric counter for [Dropwizard Metrics](http://metrics.dropwizard.io). Can be `flushingCounter` (default), `counter` or `meter`. -So far metrics cannot be submitted simultaneously to many backends. Currently we support `graphite` and `influxdb`. +- `metrics.metricType` - set the type of metric counter for [Dropwizard Metrics](http://metrics.dropwizard.io). Can be + `flushingCounter` (default), `counter` or `meter`. + +So far metrics cannot be submitted simultaneously to many backends. Currently we support `graphite` and `influxdb`. Also, for debug purposes you can use `console` as metrics backend. For `logback` backend type available next options: + - `metrics.logback.enabled` - if equals to `true` then logback reporter will be started. - `metrics.logback.name` - name of logger element in the logback configuration file. - `metrics.logback.interval` - interval in seconds between successive sending metrics. - For `graphite` backend type available next options: + - `metrics.graphite.enabled` - if equals to `true` then `graphite` will be used to submit metrics. - `metrics.graphite.prefix` - the prefix of all metric names. - `metrics.graphite.host` - the graphite host for sending statistics. @@ -249,6 +335,7 @@ For `graphite` backend type available next options: - `metrics.graphite.interval` - interval in seconds between successive sending metrics. For `influxdb` backend type available next options: + - `metrics.influxdb.enabled` - if equals to `true` then `influxdb` will be used to submit metrics. - `metrics.influxdb.prefix` - the prefix of all metric names. - `metrics.influxdb.protocol` - external service destination protocol. @@ -262,48 +349,63 @@ For `influxdb` backend type available next options: - `metrics.influxdb.tags` - the influxDb tags, optional key-value metrics metadata. For `console` backend type available next options: + - `metrics.console.enabled` - if equals to `true` then `console` will be used to submit metrics. - `metrics.console.interval` - interval in seconds between successive sending metrics. For `prometheus` backend type available next options: + - `metrics.prometheus.enabled` - if equals to `true` then prometheus reporter will be started - `metrics.prometheus.port` - prometheus reporter port - `metrics.prometheus.namespace` - optional namespace prefix for metrics - `metrics.prometheus.subsystem` - optional subsystem prefix for metrics -- `metrics.prometheus.custom-labels-enabled` - If set to `true` it enables tags/labels for prometheus metrics instead of including them in the metrics path +- `metrics.prometheus.custom-labels-enabled` - If set to `true` it enables tags/labels for prometheus metrics instead of + including them in the metrics path It is possible to define how many account-level metrics will be submitted on per-account basis. See [metrics documentation](metrics.md) for complete list of metrics submitted at each verbosity level. -- `metrics.accounts.default-verbosity` - verbosity for accounts not specified in next sections. Allowed values: `none, basic, detailed`. Default is `none`. + +- `metrics.accounts.default-verbosity` - verbosity for accounts not specified in next sections. Allowed values: + `none, basic, detailed`. Default is `none`. - `metrics.accounts.basic-verbosity` - a list of accounts for which only basic metrics will be submitted. -- `metrics.accounts.detailed-verbosity` - a list of accounts for which all metrics will be submitted. +- `metrics.accounts.detailed-verbosity` - a list of accounts for which all metrics will be submitted. For `JVM` metrics + - `metrics.jmx.enabled` - if equals to `true` then `jvm.gc` and `jvm.memory` metrics will be submitted ## Cache + - `cache.scheme` - set the external Cache Service protocol: `http`, `https`, etc. - `cache.host` - set the external Cache Service destination in format `host:port`. - `cache.path` - set the external Cache Service path, for example `/cache`. -- `cache.internal.scheme` - set the internal Cache Service protocol: `http`, `https`, etc., the internal scheme get priority over the external one when provided. -- `cache.internal.host` - set the internal Cache Service destination in format `host:port`, the internal port get priority over the external one when provided. -- `cache.internal.path` - set the internal Cache Service path, for example `/cache`, the internal path get priority over the external one when provided. +- `cache.internal.scheme` - set the internal Cache Service protocol: `http`, `https`, etc., the internal scheme get + priority over the external one when provided. +- `cache.internal.host` - set the internal Cache Service destination in format `host:port`, the internal port get + priority over the external one when provided. +- `cache.internal.path` - set the internal Cache Service path, for example `/cache`, the internal path get priority over + the external one when provided. - `storage.pbc.enabled` - If set to true, this will allow storing modules’ data in third-party storage. - `storage.pbc.path` - set the external Cache Service path for module caching, for example `/pbc-storage`. -- `cache.api-key-secured` - if set to `true`, will cause Prebid Server to add a special API key header to Prebid Cache requests. +- `cache.api-key-secured` - if set to `true`, will cause Prebid Server to add a special API key header to Prebid Cache + requests. - `pbc.api.key` - set the external Cache Service api key for secured calls. - `cache.query` - appends to the cache path as query string params (used for legacy Auction requests). - `cache.banner-ttl-seconds` - how long (in seconds) banner will be available via the external Cache Service. - `cache.video-ttl-seconds` - how long (in seconds) video creative will be available via the external Cache Service. -- `cache.account..banner-ttl-seconds` - how long (in seconds) banner will be available in Cache Service -for particular publisher account. Overrides `cache.banner-ttl-seconds` property. -- `cache.account..video-ttl-seconds` - how long (in seconds) video creative will be available in Cache Service -for particular publisher account. Overrides `cache.video-ttl-seconds` property. -- `cache.default-ttl-seconds.{banner, video, audio, native}` - a default value how long (in seconds) a creative of the specific type will be available in Cache Service -- `cache.append-trace-info-to-cache-id` - if set to `true`, causes the addition account ID and datacenter to cache UUID: _ACCOUNT-DATACENTER-remainderOfUUID_. Implies that cache UUID will be generated by the Prebid Server. +- `cache.account..banner-ttl-seconds` - how long (in seconds) banner will be available in Cache Service + for particular publisher account. Overrides `cache.banner-ttl-seconds` property. +- `cache.account..video-ttl-seconds` - how long (in seconds) video creative will be available in Cache Service + for particular publisher account. Overrides `cache.video-ttl-seconds` property. +- `cache.default-ttl-seconds.{banner, video, audio, native}` - a default value how long (in seconds) a creative of the + specific type will be available in Cache Service +- `cache.append-trace-info-to-cache-id` - if set to `true`, causes the addition account ID and datacenter to cache UUID: + _ACCOUNT-DATACENTER-remainderOfUUID_. Implies that cache UUID will be generated by the Prebid Server. ## Application settings (account configuration, stored ad unit configurations, stored requests) -Preconfigured application settings can be obtained from multiple data sources consequently: + +Preconfigured application settings can be obtained from multiple data sources consequently: + 1. Try to fetch from filesystem data source (if configured). 2. Try to fetch from database data source (if configured). 3. Try to fetch from http data source (if configured). @@ -311,10 +413,12 @@ Preconfigured application settings can be obtained from multiple data sources co Warning! Application will not start in case of no one data source is defined and you'll get an exception in logs. For requests validation mode available next options: + - `settings.fail-on-unknown-bidders` - fail with validation error or just make warning for unknown bidders. - `settings.fail-on-disabled-bidders` - fail with validation error or just make warning for disabled bidders. For filesystem data source available next options: + - `settings.filesystem.settings-filename` - location of file settings. - `settings.filesystem.stored-requests-dir` - directory with stored requests. - `settings.filesystem.stored-imps-dir` - directory with stored imps. @@ -322,6 +426,7 @@ For filesystem data source available next options: - `settings.filesystem.categories-dir` - directory with categories. For database data source available next options: + - `settings.database.type` - type of database to be used: `mysql` or `postgres`. - `settings.database.host` - database destination host. - `settings.database.port` - database destination port. @@ -329,72 +434,96 @@ For database data source available next options: - `settings.database.user` - database user. - `settings.database.password` - database password. - `settings.database.pool-size` - set the initial/min/max pool size of database connections. -- `settings.database.idle-connection-timeout` - Set the idle timeout, time unit is seconds. Zero means don't timeout. This determines if a connection will timeout and be closed and get back to the pool if no data is received nor sent within the timeout. -- `settings.database.enable-prepared-statement-caching` - Enable caching of the prepared statements so that they can be reused. Defaults to `false`. Please be vary of the DB server limitations as cache instances is per-database-connection. -- `settings.database.max-prepared-statement-cache-size` - Set the maximum size of the prepared statement cache. Defaults to `256`. Has any effect only when `settings.database.enable-prepared-statement-caching` is set to `true`. Please note that the cache size is multiplied by `settings.database.pool-size`. +- `settings.database.idle-connection-timeout` - Set the idle timeout, time unit is seconds. Zero means don't timeout. + This determines if a connection will timeout and be closed and get back to the pool if no data is received nor sent + within the timeout. +- `settings.database.enable-prepared-statement-caching` - Enable caching of the prepared statements so that they can be + reused. Defaults to `false`. Please be vary of the DB server limitations as cache instances is + per-database-connection. +- `settings.database.max-prepared-statement-cache-size` - Set the maximum size of the prepared statement cache. Defaults + to `256`. Has any effect only when `settings.database.enable-prepared-statement-caching` is set to `true`. Please note + that the cache size is multiplied by `settings.database.pool-size`. - `settings.database.account-query` - the SQL query to fetch account. - `settings.database.stored-requests-query` - the SQL query to fetch stored requests. - `settings.database.amp-stored-requests-query` - the SQL query to fetch AMP stored requests. - `settings.database.stored-responses-query` - the SQL query to fetch stored responses. -- `settings.database.circuit-breaker.enabled` - if equals to `true` circuit breaker will be used to make database client more robust. +- `settings.database.circuit-breaker.enabled` - if equals to `true` circuit breaker will be used to make database client + more robust. - `settings.database.circuit-breaker.opening-threshold` - the number of failures before opening the circuit. -- `settings.database.circuit-breaker.opening-interval-ms` - time interval for opening the circuit breaker if failures count reached. +- `settings.database.circuit-breaker.opening-interval-ms` - time interval for opening the circuit breaker if failures + count reached (must be a multiple of 1000). - `settings.database.circuit-breaker.closing-interval-ms` - time spent in open state before attempting to re-try. For HTTP data source available next options: + - `settings.http.endpoint` - the url to fetch stored requests. - `settings.http.amp-endpoint` - the url to fetch AMP stored requests. - `settings.http.video-endpoint` - the url to fetch video stored requests. - `settings.http.category-endpoint` - the url to fetch categories for long form video. -- `settings.http.rfc3986-compatible` - if equals to `true` the url will be build according to RFC 3986, `false` by default +- `settings.http.rfc3986-compatible` - if equals to `true` the url will be build according to RFC 3986, `false` by + default For account processing rules available next options: + - `settings.enforce-valid-account` - if equals to `true` then request without account id will be rejection with 401. -- `settings.generate-storedrequest-bidrequest-id` - overrides `bidrequest.id` in amp or app stored request with generated UUID if true. Default value is false. This flag can be overridden by setting `bidrequest.id` as `{{UUID}}` placeholder directly in stored request. +- `settings.generate-storedrequest-bidrequest-id` - overrides `bidrequest.id` in amp or app stored request with + generated UUID if true. Default value is false. This flag can be overridden by setting `bidrequest.id` as `{{UUID}}` + placeholder directly in stored request. -It is possible to specify default account configuration values that will be assumed if account config have them +It is possible to specify default account configuration values that will be assumed if account config have them unspecified or missing at all. Example: + ```yaml -settings: - default-account-config: > - { - "auction": { - "default-integration": "pbjs" - "events": { - "enabled": true +settings: + default-account-config: > + { + "auction": { + "default-integration": "pbjs" + "events": { + "enabled": true + } + }, + "privacy": { + "gdpr": { + "enabled": true + } + } } - }, - "privacy": { - "gdpr": { - "enabled": true - } - } - } ``` + See [application settings](application-settings.md) for full reference of available configuration parameters. For caching available next options: + - `settings.in-memory-cache.ttl-seconds` - how long (in seconds) data will be available in LRU cache. - `settings.in-memory-cache.cache-size` - the size of LRU cache. - `settings.in-memory-cache.jitter-seconds` - jitter (in seconds) for `settings.in-memory-cache.ttl-seconds` parameter. - `settings.in-memory-cache.notification-endpoints-enabled` - if equals to `true` two additional endpoints will be -available: [/storedrequests/openrtb2](endpoints/storedrequests/openrtb2.md) and [/storedrequests/amp](endpoints/storedrequests/amp.md). -- `settings.in-memory-cache.account-invalidation-enabled` - if equals to `true` additional admin protected endpoints will be -available: `/cache/invalidate?account={accountId}` which remove account from the cache. + available: [/storedrequests/openrtb2](endpoints/storedrequests/openrtb2.md) + and [/storedrequests/amp](endpoints/storedrequests/amp.md). +- `settings.in-memory-cache.account-invalidation-enabled` - if equals to `true` additional admin protected endpoints + will be + available: `/cache/invalidate?account={accountId}` which remove account from the cache. - `settings.in-memory-cache.http-update.endpoint` - the url to fetch stored request updates. - `settings.in-memory-cache.http-update.amp-endpoint` - the url to fetch AMP stored request updates. - `settings.in-memory-cache.http-update.refresh-rate` - refresh period in ms for stored request updates. - `settings.in-memory-cache.http-update.timeout` - timeout for obtaining stored request updates. - `settings.in-memory-cache.database-update.init-query` - initial query for fetching all stored requests at the startup. -- `settings.in-memory-cache.database-update.update-query` - a query for periodical update of stored requests, that should -contain 'WHERE last_updated > ?' for MySQL and 'WHERE last_updated > $1' for Postgresql to fetch only the records that were updated since previous check. -- `settings.in-memory-cache.database-update.amp-init-query` - initial query for fetching all AMP stored requests at the startup. -- `settings.in-memory-cache.database-update.amp-update-query` - a query for periodical update of AMP stored requests, that should -contain 'WHERE last_updated > ?' for MySQL and 'WHERE last_updated > $1' for Postgresql to fetch only the records that were updated since previous check. +- `settings.in-memory-cache.database-update.update-query` - a query for periodical update of stored requests, that + should + contain 'WHERE last_updated > ?' for MySQL and 'WHERE last_updated > $1' for Postgresql to fetch only the records that + were updated since previous check. +- `settings.in-memory-cache.database-update.amp-init-query` - initial query for fetching all AMP stored requests at the + startup. +- `settings.in-memory-cache.database-update.amp-update-query` - a query for periodical update of AMP stored requests, + that should + contain 'WHERE last_updated > ?' for MySQL and 'WHERE last_updated > $1' for Postgresql to fetch only the records that + were updated since previous check. - `settings.in-memory-cache.database-update.refresh-rate` - refresh period in ms for stored request updates. - `settings.in-memory-cache.database-update.timeout` - timeout for obtaining stored request updates. For S3 storage configuration + - `settings.in-memory-cache.s3-update.refresh-rate` - refresh period in ms for stored request updates in S3 - `settings.s3.access-key-id` - an access key (optional) - `settings.s3.secret-access-key` - a secret access key (optional) @@ -407,19 +536,23 @@ For S3 storage configuration - `settings.s3.stored-requests-dir` - a directory with stored requests - `settings.s3.stored-responses-dir` - a directory with stored responses -If `settings.s3.access-key-id` and `settings.s3.secret-access-key` are not specified in the Prebid Server configuration then AWS credentials will be looked up in this order: +If `settings.s3.access-key-id` and `settings.s3.secret-access-key` are not specified in the Prebid Server configuration +then AWS credentials will be looked up in this order: + - Java System Properties - `aws.accessKeyId` and `aws.secretAccessKey` - Environment Variables - `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` -- Web Identity Token credentials from system properties or environment variables +- Web Identity Token credentials from system properties or environment variables - Credential profiles file at the default location (`~/.aws/credentials`) shared by all AWS SDKs and the AWS CLI -- Credentials delivered through the Amazon EC2 container service if "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI" environment variable is set and security manager has permission to access the variable, +- Credentials delivered through the Amazon EC2 container service if "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI" environment + variable is set and security manager has permission to access the variable, - Instance profile credentials delivered through the Amazon EC2 metadata service - For targeting available next options: + - `settings.targeting.truncate-attr-chars` - set the max length for names of targeting keywords (0 means no truncation). ## Host Cookie + - `host-cookie.optout-cookie.name` - set the cookie name for optout checking. - `host-cookie.optout-cookie.value` - set the cookie value for optout checking. - `host-cookie.opt-out-url` - set the url for user redirect in case of opt out. @@ -431,53 +564,72 @@ For targeting available next options: - `host-cookie.max-cookie-size-bytes` - a size limit for UIDs Cookie. Valid values are `0` (disabled) and `>500`. ## Google Recaptcha + - `recaptcha-url` - the url for Google Recaptcha service to submit user verification. - `recaptcha-secret` - Google Recaptcha secret string given to certain domain account. ## Server status + - `status-response` - message returned by ApplicationChecker in /status endpoint when server is ready to serve requests. -If not defined in config all other Health Checkers would be disabled and endpoint will respond with 'No Content' (204) status with empty body. + If not defined in config all other Health Checkers would be disabled and endpoint will respond with 'No Content' (204) + status with empty body. ## Health Check -- `health-check.database.enabled` - if equals to `true` the database health check will be enabled to periodically check database status. + +- `health-check.database.enabled` - if equals to `true` the database health check will be enabled to periodically check + database status. - `health-check.database.refresh-period-ms` - the refresh period for database status updates. -- `health-check.geolocation.enabled` - if equals to `true` the geolocation service health check will be enabled to periodically check the status. +- `health-check.geolocation.enabled` - if equals to `true` the geolocation service health check will be enabled to + periodically check the status. - `health-check.geolocation.refresh-period-ms` - the refresh period for geolocation service status updates. ## GDPR + - `gdpr.eea-countries` - comma separated list of countries in European Economic Area (EEA). - `gdpr.default-value` - determines GDPR in scope default value (if no information in request and no geolocation data). - `gdpr.host-vendor-id` - the organization running a cluster of Prebid Servers. - `datacenter-region` - the datacenter region of a cluster of Prebid Servers - `gdpr.enabled` - gdpr feature switch. Default `true`. - `gdpr.purposes.pN.enforce-purpose` - define type of enforcement confirmation: `no`/`basic`/`full`. Default `full` -- `gdpr.purposes.pN.enforce-vendors` - if equals to `true`, user must give consent to use vendors. Purposes will be omitted. Default `true` +- `gdpr.purposes.pN.enforce-vendors` - if equals to `true`, user must give consent to use vendors. Purposes will be + omitted. Default `true` - `gdpr.purposes.pN.vendor-exceptions[]` - bidder names that will be treated opposite to `pN.enforce-vendors` value. -- `gdpr.special-features.sfN.enforce` - if equals to `true`, special feature will be enforced for purpose. Default `true` +- `gdpr.special-features.sfN.enforce` - if equals to `true`, special feature will be enforced for purpose. Default + `true` - `gdpr.special-features.sfN.vendor-exceptions[]` - bidder names that will be treated opposite to `sfN.enforce` value. - `gdpr.purpose-one-treatment-interpretation` - option that allows to skip the Purpose one enforcement workflow. - `gdpr.vendorlist.default-timeout-ms` - default operation timeout for obtaining new vendor list. - `gdpr.vendorlist.v2.http-endpoint-template` - template string for vendor list url version 2. -- `gdpr.vendorlist.v2.refresh-missing-list-period-ms` - time to wait between attempts to fetch vendor list version that previously was reported to be missing by origin. Default `3600000` (one hour). -- `gdpr.vendorlist.v2.fallback-vendor-list-path` - location on the file system of the fallback vendor list that will be used in place of missing vendor list versions. Optional. +- `gdpr.vendorlist.v2.refresh-missing-list-period-ms` - time to wait between attempts to fetch vendor list version that + previously was reported to be missing by origin. Default `3600000` (one hour). +- `gdpr.vendorlist.v2.fallback-vendor-list-path` - location on the file system of the fallback vendor list that will be + used in place of missing vendor list versions. Optional. - `gdpr.vendorlist.v2.deprecated` - Flag to show is this vendor list is deprecated or not. -- `gdpr.vendorlist.v2.cache-dir` - directory for local storage cache for vendor list. Should be with `WRITE` permissions for user application run from. +- `gdpr.vendorlist.v2.cache-dir` - directory for local storage cache for vendor list. Should be with `WRITE` permissions + for user application run from. ## CCPA + - `ccpa.enforce` - if equals to `true` enforces to check ccpa policy, otherwise ignore ccpa verification. ## LMT + - `lmt.enforce` - if equals to `true` enforces to check lmt policy, otherwise ignore lmt verification. ## Geo Location -- `geolocation.enabled` - if equals to `true` the geo location service will be used to determine the country for client request. -- `geolocation.circuit-breaker.enabled` - if equals to `true` circuit breaker will be used to make geo location client more robust. + +- `geolocation.enabled` - if equals to `true` the geo location service will be used to determine the country for client + request. +- `geolocation.circuit-breaker.enabled` - if equals to `true` circuit breaker will be used to make geo location client + more robust. - `geolocation.circuit-breaker.opening-threshold` - the number of failures before opening the circuit. -- `geolocation.circuit-breaker.opening-interval-ms` - time interval for opening the circuit breaker if failures count reached. +- `geolocation.circuit-breaker.opening-interval-ms` - time interval for opening the circuit breaker if failures count + reached (must be a multiple of 1000). - `geolocation.circuit-breaker.closing-interval-ms` - time spent in open state before attempting to re-try. - `geolocation.type` - set the geo location service provider, can be `maxmind` or custom provided by hosting company. - `geolocation.maxmind` - section for [MaxMind](https://www.maxmind.com) configuration as geo location service provider. -- `geolocation.maxmind.remote-file-syncer` - use RemoteFileSyncer component for downloading/updating MaxMind database file. See [RemoteFileSyncer](#remote-file-syncer) section for its configuration. +- `geolocation.maxmind.remote-file-syncer` - use RemoteFileSyncer component for downloading/updating MaxMind database + file. See [RemoteFileSyncer](#remote-file-syncer) section for its configuration. - `geolocation.configurations[]` - a list of geo-lookup configurations for the `configuration` `geolocation.type` - `geolocation.configurations[].address-pattern` - an address pattern for matching an IP to look up - `geolocation.configurations[].geo-info.continent` - a continent to return on the `configuration` geo-lookup @@ -488,31 +640,39 @@ If not defined in config all other Health Checkers would be disabled and endpoin - `geolocation.configurations[].geo-info.metro-google` - a metro Google to return on the `configuration` geo-lookup - `geolocation.configurations[].geo-info.metro-nielsen` - a metro Nielsen to return on the `configuration` geo-lookup - `geolocation.configurations[].geo-info.zip` - a zip to return on the `configuration` geo-lookup -- `geolocation.configurations[].geo-info.connection-speed` - a connection-speed to return on the `configuration` geo-lookup +- `geolocation.configurations[].geo-info.connection-speed` - a connection-speed to return on the `configuration` + geo-lookup - `geolocation.configurations[].geo-info.lat` - a lat to return on the `configuration` geo-lookup - `geolocation.configurations[].geo-info.lon` - a lon to return on the `configuration` geo-lookup - `geolocation.configurations[].geo-info.time-zone` - a time zone to return on the `configuration` geo-lookup ## IPv6 + - `ipv6.always-mask-right` - a bit mask for masking an IPv6 address of the device - `ipv6.anon-left-mask-bits` - a bit mask for anonymizing an IPv6 address of the device - `ipv6.private-networks` - a list of known private/local networks to skip masking of an IP address of the device ## Analytics -- `analytics.global.adapters` - Names of analytics adapters that will work for each request, except those disabled at the account level. + +- `analytics.global.adapters` - Names of analytics adapters that will work for each request, except those disabled at + the account level. For the `pubstack` analytics adapter -- `analytics.pubstack.enabled` - if equals to `true` the Pubstack analytics module will be enabled. Default value is `false`. -- `analytics.pubstack.endpoint` - url for reporting events and fetching configuration. + +- `analytics.pubstack.enabled` - if equals to `true` the Pubstack analytics module will be enabled. Default value is + `false`. +- `analytics.pubstack.endpoint` - url for reporting events and fetching configuration. - `analytics.pubstack.scopeid` - defined the scope provided by the Pubstack Support Team. - `analytics.pubstack.configuration-refresh-delay-ms` - delay in milliseconds between remote config updates. - `analytics.pubstack.timeout-ms` - timeout in milliseconds for report and fetch config requests. -- `analytics.pubstack.buffers.size-bytes` - threshold in bytes for buffer to send events. +- `analytics.pubstack.buffers.size-bytes` - threshold in bytes for buffer to send events. - `analytics.pubstack.buffers.count` - threshold in events count for buffer to send events - `analytics.pubstack.buffers.report-ttl-ms` - max period between two reports. For the `greenbids` analytics adapter -- `analytics.greenbids.enabled` - if equals to `true` the Greenbids analytics module will be enabled. Default value is `false`. + +- `analytics.greenbids.enabled` - if equals to `true` the Greenbids analytics module will be enabled. Default value is + `false`. - `analytics.greenbids.analytics-server-version` - a server version to add to the event - `analytics.greenbids.analytics-server` - url for reporting events - `analytics.greenbids.timeout-ms` - timeout in milliseconds for report requests. @@ -520,10 +680,12 @@ For the `greenbids` analytics adapter - `analytics.greenbids.default-sampling-rate` - a default sampling rate for report requests For the `agma` analytics adapter + - `analytics.agma.enabled` - if equals to `true` the Agma analytics module will be enabled. Default value is `false`. - `analytics.agma.endpoint.url` - url for reporting events - `analytics.agma.endpoint.timeout-ms` - timeout in milliseconds for report requests. -- `analytics.agma.endpoint.gzip` - if equals to `true` the Agma analytics module enables gzip encoding. Default value is `false`. +- `analytics.agma.endpoint.gzip` - if equals to `true` the Agma analytics module enables gzip encoding. Default value is + `false`. - `analytics.agma.buffers.size-bytes` - threshold in bytes for buffer to send events. - `analytics.agma.buffers.count` - threshold in events count for buffer to send events. - `analytics.agma.buffers.timeout-ms` - max period between two reports. @@ -532,27 +694,36 @@ For the `agma` analytics adapter - `analytics.agma.accounts[].site-app-id` - a site or app id to match an event to send ## Modules -- `hooks.admin.module-execution` - a key-value map, where a key is a module name and a value is a boolean, that defines whether modules hooks should/should not be always executed; if the module is not specified it is executed by default when it's present in the execution plan + +- `hooks.admin.module-execution` - a key-value map, where a key is a module name and a value is a boolean, that defines + whether modules hooks should/should not be always executed; if the module is not specified it is executed by default + when it's present in the execution plan - `settings.modules.require-config-to-invoke` - when enabled it requires a runtime config to exist for a module. ## Debugging -- `debug.override-token` - special string token for overriding Prebid Server account and/or adapter debug information presence in the auction response. + +- `debug.override-token` - special string token for overriding Prebid Server account and/or adapter debug information + presence in the auction response. To override (force enable) account and/or bidder adapter debug setting, a client must include `x-pbs-debug-override` HTTP header in the auction call containing same token as in the `debug.override-token` property. This will make Prebid Server ignore account `auction.debug-allow` and/or `adapters..debug.allow` properties. ## Privacy Sandbox + - `auction.privacysandbox.topicsdomain` - the list of Sec-Browsing-Topics for the Privacy Sandbox ## AMP + - `amp.custom-targeting` - a list of bidders that support custom targeting ## Hooks + - `hooks.host-execution-plan` - a host execution plan for modules - `hooks.default-account-execution-plan` - a default account execution plan ## Price Floors Debug + - `price-floors.enabled` - enables price floors for account if true. Defaults to true. - `price-floors.min-max-age-sec` - a price floors fetch data time to live in cache. - `price-floors.min-period-sec` - a refresh period for fetching price floors data. diff --git a/extra/modules/confiant-ad-quality/src/main/java/org/prebid/server/hooks/modules/com/confiant/adquality/core/BidsScanner.java b/extra/modules/confiant-ad-quality/src/main/java/org/prebid/server/hooks/modules/com/confiant/adquality/core/BidsScanner.java index 1b3afe3092d..0fbe25832a2 100644 --- a/extra/modules/confiant-ad-quality/src/main/java/org/prebid/server/hooks/modules/com/confiant/adquality/core/BidsScanner.java +++ b/extra/modules/confiant-ad-quality/src/main/java/org/prebid/server/hooks/modules/com/confiant/adquality/core/BidsScanner.java @@ -55,83 +55,50 @@ public void disableScan() { } public Future submitBids(RedisBidsData bids) { - final Promise scanResult = Promise.promise(); - final RedisAPI readRedisNodeAPI = this.readRedisNode.getRedisAPI(); - final boolean shouldSubmit = !isScanDisabled - && readRedisNodeAPI != null && !bids.getBresps().isEmpty(); - - if (shouldSubmit) { - readRedisNodeAPI.get("function_submit_bids", submitHash -> { - final Object submitHashResult = submitHash.result(); - if (submitHashResult != null) { - final List readArgs = List.of( - submitHashResult.toString(), - "0", - toBidsAsJson(bids), - apiKey, - "true"); - - readRedisNodeAPI.evalsha(readArgs, response -> { - if (response.result() != null) { - final BidsScanResult parserResult = redisParser - .parseBidsScanResult(response.result().toString()); - final boolean isAnyRoSkipped = parserResult.getBidScanResults() - .stream().anyMatch(BidScanResult::isRoSkipped); - - if (isAnyRoSkipped) { - reSubmitBidsToWriteNode(readArgs, scanResult); - } else { - scanResult.complete(parserResult); - } - } else { - scanResult.complete(getEmptyScanResult()); - } - }); - } else { - scanResult.complete(getEmptyScanResult()); - } - }); - - return scanResult.future(); + if (isScanDisabled || readRedisNodeAPI == null || bids.getBresps().isEmpty()) { + return Future.succeededFuture(getEmptyScanResult()); } - return Future.succeededFuture(getEmptyScanResult()); + return readRedisNodeAPI.get("function_submit_bids") + .map(Response::toString) + .map(response -> List.of(response, "0", toBidsAsJson(bids), apiKey, "true")) + .compose(args -> scanBids(args, readRedisNodeAPI)) + .otherwise(ignored -> getEmptyScanResult()); + } + + private Future scanBids(List args, RedisAPI redisAPI) { + return redisAPI.evalsha(args) + .map(Response::toString) + .map(redisParser::parseBidsScanResult) + .compose(parsedResult -> parsedResult.getBidScanResults() + .stream().anyMatch(BidScanResult::isRoSkipped) + ? reSubmitBidsToWriteNode(args) + : Future.succeededFuture(parsedResult)); } - private void reSubmitBidsToWriteNode(List readArgs, Promise scanResult) { + private Future reSubmitBidsToWriteNode(List readArgs) { final RedisAPI writeRedisAPI = this.writeRedisNode.getRedisAPI(); - if (writeRedisAPI != null) { - final List writeArgs = readArgs.stream().limit(4).toList(); - writeRedisAPI.evalsha(writeArgs, response -> { - if (response.result() != null) { - final BidsScanResult parserResult = redisParser - .parseBidsScanResult(response.result().toString()); - - scanResult.complete(parserResult); - } else { - scanResult.complete(getEmptyScanResult()); - } - }); - } else { - scanResult.complete(getEmptyScanResult()); + if (writeRedisAPI == null) { + return Future.succeededFuture(getEmptyScanResult()); } + + final List writeArgs = readArgs.stream().limit(4).toList(); + return writeRedisAPI.evalsha(writeArgs) + .map(Response::toString) + .map(redisParser::parseBidsScanResult); } public Future isScanDisabledFlag() { final RedisAPI redisAPI = this.readRedisNode.getRedisAPI(); - final Promise isDisabled = Promise.promise(); - - if (redisAPI != null) { - redisAPI.get("scan-disabled", scanDisabledValue -> { - final Response scanDisabled = scanDisabledValue.result(); - isDisabled.complete(scanDisabled != null && "true".equals(scanDisabled.toString())); - }); - - return isDisabled.future(); + if (redisAPI == null) { + return Future.succeededFuture(true); } - return Future.succeededFuture(true); + return redisAPI.get("scan-disabled") + .map(Response::toString) + .map(Boolean::parseBoolean) + .otherwise(false); } private String toBidsAsJson(RedisBidsData bids) { diff --git a/extra/modules/confiant-ad-quality/src/main/java/org/prebid/server/hooks/modules/com/confiant/adquality/core/RedisClient.java b/extra/modules/confiant-ad-quality/src/main/java/org/prebid/server/hooks/modules/com/confiant/adquality/core/RedisClient.java index d1b424f314e..60b910c632a 100644 --- a/extra/modules/confiant-ad-quality/src/main/java/org/prebid/server/hooks/modules/com/confiant/adquality/core/RedisClient.java +++ b/extra/modules/confiant-ad-quality/src/main/java/org/prebid/server/hooks/modules/com/confiant/adquality/core/RedisClient.java @@ -61,7 +61,8 @@ public RedisAPI getRedisAPI() { */ private void createRedisClient(Handler> handler, boolean isReconnect) { Redis.createClient(vertx, options) - .connect(onConnect -> { + .connect() + .onComplete(onConnect -> { if (onConnect.succeeded()) { connection = onConnect.result(); connection.exceptionHandler(e -> { diff --git a/extra/modules/optable-targeting/src/main/java/org/prebid/server/hooks/modules/optable/targeting/v1/net/APIClientImpl.java b/extra/modules/optable-targeting/src/main/java/org/prebid/server/hooks/modules/optable/targeting/v1/net/APIClientImpl.java index d31929f111f..e6e5510c40f 100644 --- a/extra/modules/optable-targeting/src/main/java/org/prebid/server/hooks/modules/optable/targeting/v1/net/APIClientImpl.java +++ b/extra/modules/optable-targeting/src/main/java/org/prebid/server/hooks/modules/optable/targeting/v1/net/APIClientImpl.java @@ -3,7 +3,7 @@ import io.netty.handler.codec.http.HttpResponseStatus; import io.vertx.core.Future; import io.vertx.core.MultiMap; -import io.vertx.core.http.impl.headers.HeadersMultiMap; +import io.vertx.core.http.HttpHeaders; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.prebid.server.execution.timeout.Timeout; @@ -69,7 +69,7 @@ private String resolveEndpoint(String tenant, String origin) { } private static MultiMap headers(OptableTargetingProperties properties, List ips, String userAgent) { - final MultiMap headers = HeadersMultiMap.headers() + final MultiMap headers = HttpHeaders.headers() .add(HttpUtil.ACCEPT_HEADER, "application/json"); if (userAgent != null) { diff --git a/extra/modules/optable-targeting/src/test/java/org/prebid/server/hooks/modules/optable/targeting/v1/BaseOptableTest.java b/extra/modules/optable-targeting/src/test/java/org/prebid/server/hooks/modules/optable/targeting/v1/BaseOptableTest.java index 99f24ea4bc5..2ba8789c5c9 100644 --- a/extra/modules/optable-targeting/src/test/java/org/prebid/server/hooks/modules/optable/targeting/v1/BaseOptableTest.java +++ b/extra/modules/optable-targeting/src/test/java/org/prebid/server/hooks/modules/optable/targeting/v1/BaseOptableTest.java @@ -16,7 +16,7 @@ import com.iab.openrtb.response.BidResponse; import com.iab.openrtb.response.SeatBid; import io.vertx.core.MultiMap; -import io.vertx.core.http.impl.headers.HeadersMultiMap; +import io.vertx.core.http.HttpHeaders; import org.apache.commons.io.IOUtils; import org.apache.http.HttpStatus; import org.prebid.server.activity.infrastructure.ActivityInfrastructure; @@ -193,7 +193,7 @@ protected Device givenDevice() { } protected HttpClientResponse givenSuccessHttpResponse(String fileName) { - final MultiMap headers = HeadersMultiMap.headers().add("Content-Type", "application/json"); + final MultiMap headers = HttpHeaders.headers().add("Content-Type", "application/json"); return HttpClientResponse.of(HttpStatus.SC_OK, headers, givenBodyFromFile(fileName)); } diff --git a/extra/pom.xml b/extra/pom.xml index 1a43c1b8ace..57e33aac8b3 100644 --- a/extra/pom.xml +++ b/extra/pom.xml @@ -34,7 +34,7 @@ 3.5.10 - 4.5.20 + 5.1.0 2.0.1.Final 4.4 1.27.1 diff --git a/src/main/java/org/prebid/server/analytics/reporter/pubstack/PubstackAnalyticsReporter.java b/src/main/java/org/prebid/server/analytics/reporter/pubstack/PubstackAnalyticsReporter.java index 0bddcbe05ca..1fd2ab7749b 100644 --- a/src/main/java/org/prebid/server/analytics/reporter/pubstack/PubstackAnalyticsReporter.java +++ b/src/main/java/org/prebid/server/analytics/reporter/pubstack/PubstackAnalyticsReporter.java @@ -1,6 +1,5 @@ package org.prebid.server.analytics.reporter.pubstack; -import io.vertx.core.AsyncResult; import io.vertx.core.Future; import io.vertx.core.Promise; import io.vertx.core.Vertx; @@ -119,19 +118,20 @@ public String name() { @Override public void initialize(Promise initializePromise) { vertx.setPeriodic(configurationRefreshDelay, id -> fetchRemoteConfig()); - fetchRemoteConfig(); - initializePromise.tryComplete(); + fetchRemoteConfig().onComplete(initializePromise); } void shutdown() { eventHandlers.values().forEach(PubstackEventHandler::reportEvents); } - private void fetchRemoteConfig() { + private Future fetchRemoteConfig() { logger.info("[pubstack] Updating config: {}", pubstackConfig); - httpClient.get(makeEventEndpointUrl(pubstackConfig.getEndpoint(), pubstackConfig.getScopeId()), timeout) + return httpClient.get(makeEventEndpointUrl(pubstackConfig.getEndpoint(), pubstackConfig.getScopeId()), timeout) .map(this::processRemoteConfigurationResponse) - .onComplete(this::updateConfigsOnChange); + .map(this::updateConfigsOnChange) + .onFailure(PubstackAnalyticsReporter::logError) + .mapEmpty(); } private PubstackConfig processRemoteConfigurationResponse(HttpClientResponse response) { @@ -148,15 +148,14 @@ private PubstackConfig processRemoteConfigurationResponse(HttpClientResponse res } } - private void updateConfigsOnChange(AsyncResult asyncConfigResult) { - if (asyncConfigResult.failed()) { - logger.error("[pubstask] Fail to fetch remote configuration: {}", asyncConfigResult.cause().getMessage()); - } else if (!Objects.equals(pubstackConfig, asyncConfigResult.result())) { - final PubstackConfig pubstackConfig = asyncConfigResult.result(); + private Void updateConfigsOnChange(PubstackConfig config) { + if (!Objects.equals(pubstackConfig, config)) { eventHandlers.values().forEach(PubstackEventHandler::reportEvents); - this.pubstackConfig = pubstackConfig; + this.pubstackConfig = config; updateHandlers(pubstackConfig); } + + return null; } private void updateHandlers(PubstackConfig pubstackConfig) { @@ -187,4 +186,8 @@ private String makeEventHandlerEndpoint(String endpoint, EventType eventType) { throw new PreBidException(message); } } + + private static void logError(Throwable throwable) { + logger.error("[pubstask] Fail to fetch remote configuration: {}", throwable.getCause().getMessage()); + } } diff --git a/src/main/java/org/prebid/server/floors/PriceFloorFetcher.java b/src/main/java/org/prebid/server/floors/PriceFloorFetcher.java index b1cc8c257b2..bbaeec77a4c 100644 --- a/src/main/java/org/prebid/server/floors/PriceFloorFetcher.java +++ b/src/main/java/org/prebid/server/floors/PriceFloorFetcher.java @@ -5,7 +5,6 @@ import io.vertx.core.Future; import io.vertx.core.Vertx; import io.vertx.core.http.HttpHeaders; -import io.vertx.core.impl.ConcurrentHashSet; import lombok.Value; import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.ObjectUtils; @@ -37,6 +36,7 @@ import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.regex.Matcher; @@ -79,7 +79,7 @@ public PriceFloorFetcher(ApplicationSettings applicationSettings, this.debugProperties = debugProperties; this.mapper = Objects.requireNonNull(mapper); - fetchInProgress = new ConcurrentHashSet<>(); + fetchInProgress = ConcurrentHashMap.newKeySet(); fetchedData = Caffeine.newBuilder() .maximumSize(MAXIMUM_CACHE_SIZE) .build() diff --git a/src/main/java/org/prebid/server/geolocation/CircuitBreakerSecuredGeoLocationService.java b/src/main/java/org/prebid/server/geolocation/CircuitBreakerSecuredGeoLocationService.java index f0fc0be3574..57d01997b96 100755 --- a/src/main/java/org/prebid/server/geolocation/CircuitBreakerSecuredGeoLocationService.java +++ b/src/main/java/org/prebid/server/geolocation/CircuitBreakerSecuredGeoLocationService.java @@ -1,18 +1,17 @@ package org.prebid.server.geolocation; +import io.vertx.circuitbreaker.CircuitBreaker; +import io.vertx.circuitbreaker.CircuitBreakerOptions; +import io.vertx.circuitbreaker.CircuitBreakerState; import io.vertx.core.Future; import io.vertx.core.Vertx; import org.prebid.server.execution.timeout.Timeout; import org.prebid.server.geolocation.model.GeoInfo; -import org.prebid.server.log.ConditionalLogger; import org.prebid.server.log.Logger; import org.prebid.server.log.LoggerFactory; import org.prebid.server.metric.Metrics; -import org.prebid.server.vertx.CircuitBreaker; -import java.time.Clock; import java.util.Objects; -import java.util.concurrent.TimeUnit; /** * Wrapper for geolocation service with circuit breaker. @@ -20,8 +19,6 @@ public class CircuitBreakerSecuredGeoLocationService implements GeoLocationService { private static final Logger logger = LoggerFactory.getLogger(CircuitBreakerSecuredGeoLocationService.class); - private static final ConditionalLogger conditionalLogger = new ConditionalLogger(logger); - private static final int LOG_PERIOD_SECONDS = 5; private final GeoLocationService geoLocationService; private final CircuitBreaker breaker; @@ -31,39 +28,26 @@ public CircuitBreakerSecuredGeoLocationService(Vertx vertx, Metrics metrics, int openingThreshold, long openingIntervalMs, - long closingIntervalMs, - Clock clock) { + long closingIntervalMs) { this.geoLocationService = Objects.requireNonNull(geoLocationService); - breaker = new CircuitBreaker("geo_cb", Objects.requireNonNull(vertx), - openingThreshold, openingIntervalMs, closingIntervalMs, Objects.requireNonNull(clock)) - .openHandler(ignored -> circuitOpened()) - .halfOpenHandler(ignored -> circuitHalfOpened()) - .closeHandler(ignored -> circuitClosed()); + breaker = CircuitBreaker.create( + "geo_cb", + Objects.requireNonNull(vertx), + new CircuitBreakerOptions() + .setNotificationPeriod(0) + .setMaxFailures(openingThreshold) + .setFailuresRollingWindow(openingIntervalMs) + .setResetTimeout(closingIntervalMs)); - metrics.createGeoLocationCircuitBreakerGauge(breaker::isOpen); + metrics.createGeoLocationCircuitBreakerGauge(() -> breaker.state() != CircuitBreakerState.CLOSED); logger.info("Initialized GeoLocation service with Circuit Breaker"); } @Override public Future lookup(String ip, Timeout timeout) { - return breaker.execute(promise -> geoLocationService.lookup(ip, timeout).onComplete(promise)); - } - - private void circuitOpened() { - conditionalLogger.warn( - "GeoLocation service is unavailable, circuit opened.", - LOG_PERIOD_SECONDS, - TimeUnit.SECONDS); - } - - private void circuitHalfOpened() { - logger.warn("GeoLocation service is ready to try again, circuit half-opened."); - } - - private void circuitClosed() { - logger.warn("GeoLocation service becomes working, circuit closed."); + return breaker.execute(() -> geoLocationService.lookup(ip, timeout)); } } diff --git a/src/main/java/org/prebid/server/handler/SetuidHandler.java b/src/main/java/org/prebid/server/handler/SetuidHandler.java index bce568db2d7..fb2e2bb581e 100644 --- a/src/main/java/org/prebid/server/handler/SetuidHandler.java +++ b/src/main/java/org/prebid/server/handler/SetuidHandler.java @@ -4,7 +4,6 @@ import io.vertx.core.AsyncResult; import io.vertx.core.CompositeFuture; import io.vertx.core.Future; -import io.vertx.core.http.Cookie; import io.vertx.core.http.HttpHeaders; import io.vertx.core.http.HttpMethod; import io.vertx.core.http.HttpServerRequest; @@ -333,7 +332,7 @@ private void respondWithCookie(SetuidContext setuidContext) { setuidContext.getUidsCookie(), bidder, uid); uidsCookieService.splitUidsIntoCookies(uidsCookieUpdateResult.getValue()) - .forEach(cookie -> addCookie(routingContext, cookie)); + .forEach(routingContext.response()::addCookie); if (uidsCookieUpdateResult.isUpdated()) { metrics.updateUserSyncSetsMetric(bidder); @@ -412,8 +411,4 @@ private void handleErrors(Throwable error, RoutingContext routingContext, TcfCon analyticsDelegator.processEvent(setuidEvent, tcfContext); } } - - private void addCookie(RoutingContext routingContext, Cookie cookie) { - routingContext.response().headers().add(HttpUtil.SET_COOKIE_HEADER, cookie.encode()); - } } diff --git a/src/main/java/org/prebid/server/json/JacksonMapper.java b/src/main/java/org/prebid/server/json/JacksonMapper.java index ec6d7f94d88..a56160e7aa8 100644 --- a/src/main/java/org/prebid/server/json/JacksonMapper.java +++ b/src/main/java/org/prebid/server/json/JacksonMapper.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import io.netty.buffer.ByteBufInputStream; import io.vertx.core.buffer.Buffer; +import io.vertx.core.internal.buffer.BufferInternal; import org.prebid.server.proto.openrtb.ext.FlexibleExtension; import java.io.IOException; @@ -66,7 +67,7 @@ public T decodeValue(String str, TypeReference type) throws DecodeExcepti public T decodeValue(Buffer buf, Class clazz) throws DecodeException { try { - return mapper.readValue((InputStream) new ByteBufInputStream(buf.getByteBuf()), clazz); + return mapper.readValue((InputStream) new ByteBufInputStream(((BufferInternal) buf).getByteBuf()), clazz); } catch (IOException e) { throw new DecodeException(FAILED_TO_DECODE.formatted(e.getMessage()), e); } @@ -74,7 +75,7 @@ public T decodeValue(Buffer buf, Class clazz) throws DecodeException { public T decodeValue(Buffer buf, TypeReference type) throws DecodeException { try { - return mapper.readValue(new ByteBufInputStream(buf.getByteBuf()), type); + return mapper.readValue(new ByteBufInputStream(((BufferInternal) buf).getByteBuf()), type); } catch (IOException e) { throw new DecodeException(FAILED_TO_DECODE.formatted(e.getMessage()), e); } diff --git a/src/main/java/org/prebid/server/log/CriteriaLogManager.java b/src/main/java/org/prebid/server/log/CriteriaLogManager.java index 75ca28f5f81..58e1f377ce9 100644 --- a/src/main/java/org/prebid/server/log/CriteriaLogManager.java +++ b/src/main/java/org/prebid/server/log/CriteriaLogManager.java @@ -2,18 +2,18 @@ import com.iab.openrtb.request.BidRequest; import com.iab.openrtb.response.BidResponse; -import io.vertx.core.impl.ConcurrentHashSet; import org.prebid.server.json.EncodeException; import org.prebid.server.json.JacksonMapper; import java.util.Objects; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; public class CriteriaLogManager { private static final Logger logger = LoggerFactory.getLogger(CriteriaLogManager.class); - private final Set criterias = new ConcurrentHashSet<>(); + private final Set criterias = ConcurrentHashMap.newKeySet(); private final JacksonMapper mapper; diff --git a/src/main/java/org/prebid/server/privacy/gdpr/vendorlist/VendorListService.java b/src/main/java/org/prebid/server/privacy/gdpr/vendorlist/VendorListService.java index 51c9b3e9252..2468828f9e9 100644 --- a/src/main/java/org/prebid/server/privacy/gdpr/vendorlist/VendorListService.java +++ b/src/main/java/org/prebid/server/privacy/gdpr/vendorlist/VendorListService.java @@ -3,7 +3,6 @@ import com.github.benmanes.caffeine.cache.Caffeine; import io.netty.handler.codec.http.HttpResponseStatus; import io.vertx.core.Future; -import io.vertx.core.Promise; import io.vertx.core.Vertx; import io.vertx.core.buffer.Buffer; import io.vertx.core.file.FileProps; @@ -317,23 +316,15 @@ private VendorListResult processResponse(HttpClientResponse response * Saves given vendor list on file system. */ private Future> saveToFile(VendorListResult vendorListResult) { - final Promise> promise = Promise.promise(); final int version = vendorListResult.getVersion(); final String filepath = new File(cacheDir, version + JSON_SUFFIX).getPath(); - fileSystem.writeFile(filepath, Buffer.buffer(vendorListResult.getVendorListAsString()), result -> { - if (result.succeeded()) { - promise.complete(vendorListResult); - } else { - conditionalLogger.error( + return fileSystem.writeFile(filepath, Buffer.buffer(vendorListResult.getVendorListAsString())) + .map(vendorListResult) + .onFailure(error -> conditionalLogger.error( "Could not create new vendor list for version %s.%s, file: %s, trace: %s".formatted( - generationVersion, version, filepath, ExceptionUtils.getStackTrace(result.cause())), - logSamplingRate); - promise.fail(result.cause()); - } - }); - - return promise.future(); + generationVersion, version, filepath, ExceptionUtils.getStackTrace(error.getCause())), + logSamplingRate)); } private Void updateCache(VendorListResult vendorListResult) { diff --git a/src/main/java/org/prebid/server/spring/config/GeoLocationConfiguration.java b/src/main/java/org/prebid/server/spring/config/GeoLocationConfiguration.java index bfb56b8c0c1..7058f06e903 100644 --- a/src/main/java/org/prebid/server/spring/config/GeoLocationConfiguration.java +++ b/src/main/java/org/prebid/server/spring/config/GeoLocationConfiguration.java @@ -30,7 +30,6 @@ import java.io.InputStreamReader; import java.io.Reader; import java.nio.charset.StandardCharsets; -import java.time.Clock; import java.time.ZoneId; import java.util.ArrayList; import java.util.List; @@ -70,13 +69,15 @@ CircuitBreakerSecuredGeoLocationService circuitBreakerSecuredGeoLocationService( Vertx vertx, Metrics metrics, FileSyncerProperties fileSyncerProperties, - @Qualifier("maxMindCircuitBreakerProperties") CircuitBreakerProperties circuitBreakerProperties, - Clock clock) { - - return new CircuitBreakerSecuredGeoLocationService(vertx, - createGeoLocationService(fileSyncerProperties, vertx), metrics, - circuitBreakerProperties.getOpeningThreshold(), circuitBreakerProperties.getOpeningIntervalMs(), - circuitBreakerProperties.getClosingIntervalMs(), clock); + @Qualifier("maxMindCircuitBreakerProperties") CircuitBreakerProperties circuitBreakerProperties) { + + return new CircuitBreakerSecuredGeoLocationService( + vertx, + createGeoLocationService(fileSyncerProperties, vertx), + metrics, + circuitBreakerProperties.getOpeningThreshold(), + circuitBreakerProperties.getOpeningIntervalMs(), + circuitBreakerProperties.getClosingIntervalMs()); } private GeoLocationService createGeoLocationService(FileSyncerProperties properties, Vertx vertx) { diff --git a/src/main/java/org/prebid/server/spring/config/ServiceConfiguration.java b/src/main/java/org/prebid/server/spring/config/ServiceConfiguration.java index 64a8dc7614a..597feb1f1ac 100644 --- a/src/main/java/org/prebid/server/spring/config/ServiceConfiguration.java +++ b/src/main/java/org/prebid/server/spring/config/ServiceConfiguration.java @@ -5,6 +5,7 @@ import io.vertx.core.Vertx; import io.vertx.core.file.FileSystem; import io.vertx.core.http.HttpClientOptions; +import io.vertx.core.http.PoolOptions; import io.vertx.core.net.JksOptions; import lombok.Data; import org.apache.commons.lang3.ObjectUtils; @@ -659,8 +660,7 @@ CircuitBreakerSecuredHttpClient circuitBreakerSecuredHttpClient( Metrics metrics, HttpClientProperties httpClientProperties, @Qualifier("httpClientCircuitBreakerProperties") - HttpClientCircuitBreakerProperties circuitBreakerProperties, - Clock clock) { + HttpClientCircuitBreakerProperties circuitBreakerProperties) { final HttpClient httpClient = createBasicHttpClient(vertx, httpClientProperties); @@ -671,16 +671,17 @@ CircuitBreakerSecuredHttpClient circuitBreakerSecuredHttpClient( circuitBreakerProperties.getOpeningThreshold(), circuitBreakerProperties.getOpeningIntervalMs(), circuitBreakerProperties.getClosingIntervalMs(), - circuitBreakerProperties.getIdleExpireHours(), - clock); + circuitBreakerProperties.getIdleExpireHours()); } private static BasicHttpClient createBasicHttpClient(Vertx vertx, HttpClientProperties httpClientProperties) { + final PoolOptions poolOptions = new PoolOptions() + .setHttp1MaxSize(httpClientProperties.getMaxPoolSize()) + .setCleanerPeriod(httpClientProperties.getPoolCleanerPeriodMs()); + final HttpClientOptions options = new HttpClientOptions() - .setMaxPoolSize(httpClientProperties.getMaxPoolSize()) .setIdleTimeoutUnit(TimeUnit.MILLISECONDS) .setIdleTimeout(httpClientProperties.getIdleTimeoutMs()) - .setPoolCleanerPeriod(httpClientProperties.getPoolCleanerPeriodMs()) .setDecompressionSupported(httpClientProperties.getUseCompression()) .setConnectTimeout(httpClientProperties.getConnectTimeoutMs()) // Vert.x's HttpClientRequest needs this value to be 2 for redirections to be followed once, @@ -697,7 +698,7 @@ private static BasicHttpClient createBasicHttpClient(Vertx vertx, HttpClientProp .setKeyCertOptions(jksOptions); } - return new BasicHttpClient(vertx, vertx.createHttpClient(options)); + return new BasicHttpClient(vertx, vertx.createHttpClient(options, poolOptions)); } @Bean diff --git a/src/main/java/org/prebid/server/spring/config/VerticleStarter.java b/src/main/java/org/prebid/server/spring/config/VerticleStarter.java index cb519b88477..ae6500ba766 100644 --- a/src/main/java/org/prebid/server/spring/config/VerticleStarter.java +++ b/src/main/java/org/prebid/server/spring/config/VerticleStarter.java @@ -30,11 +30,10 @@ public void start() { continue; } - contextRunner.runBlocking(promise -> + contextRunner.runBlocking(() -> vertx.deployVerticle( definition.getFactory(), - new DeploymentOptions().setInstances(definition.getAmount()), - promise)); + new DeploymentOptions().setInstances(definition.getAmount()))); } } } diff --git a/src/main/java/org/prebid/server/spring/config/database/DatabaseConfiguration.java b/src/main/java/org/prebid/server/spring/config/database/DatabaseConfiguration.java index 6a1a1531f1c..ded875b91a7 100644 --- a/src/main/java/org/prebid/server/spring/config/database/DatabaseConfiguration.java +++ b/src/main/java/org/prebid/server/spring/config/database/DatabaseConfiguration.java @@ -1,6 +1,7 @@ package org.prebid.server.spring.config.database; import io.vertx.core.Vertx; +import io.vertx.core.net.NetClientOptions; import io.vertx.mysqlclient.MySQLBuilder; import io.vertx.mysqlclient.MySQLConnectOptions; import io.vertx.pgclient.PgBuilder; @@ -84,19 +85,20 @@ Pool mysqlConnectionPool(Vertx vertx, .setDatabase(databaseAddress.getDatabaseName()) .setUser(connectionPoolSettings.getUser()) .setPassword(connectionPoolSettings.getPassword()) - .setSsl(false) - .setTcpKeepAlive(true) .setCachePreparedStatements(connectionPoolSettings.getEnablePreparedStatementCaching()) - .setPreparedStatementCacheMaxSize(connectionPoolSettings.getMaxPreparedStatementCacheSize()) - .setIdleTimeout(connectionPoolSettings.getIdleTimeout()) - .setIdleTimeoutUnit(TimeUnit.SECONDS); + .setPreparedStatementCacheMaxSize(connectionPoolSettings.getMaxPreparedStatementCacheSize()); + + final NetClientOptions netClientOptions = new NetClientOptions() + .setTcpKeepAlive(true); final PoolOptions poolOptions = new PoolOptions() - .setMaxSize(connectionPoolSettings.getPoolSize()); + .setMaxSize(connectionPoolSettings.getPoolSize()) + .setIdleTimeout(connectionPoolSettings.getIdleTimeout()) + .setIdleTimeoutUnit(TimeUnit.SECONDS); - return MySQLBuilder - .pool() + return MySQLBuilder.pool() .with(poolOptions) + .with(netClientOptions) .connectingTo(sqlConnectOptions) .using(vertx) .build(); @@ -114,19 +116,20 @@ Pool postgresConnectionPool(Vertx vertx, .setDatabase(databaseAddress.getDatabaseName()) .setUser(connectionPoolSettings.getUser()) .setPassword(connectionPoolSettings.getPassword()) - .setSsl(false) - .setTcpKeepAlive(true) .setCachePreparedStatements(connectionPoolSettings.getEnablePreparedStatementCaching()) - .setPreparedStatementCacheMaxSize(connectionPoolSettings.getMaxPreparedStatementCacheSize()) - .setIdleTimeout(connectionPoolSettings.getIdleTimeout()) - .setIdleTimeoutUnit(TimeUnit.SECONDS); + .setPreparedStatementCacheMaxSize(connectionPoolSettings.getMaxPreparedStatementCacheSize()); + + final NetClientOptions netClientOptions = new NetClientOptions() + .setTcpKeepAlive(true); final PoolOptions poolOptions = new PoolOptions() - .setMaxSize(connectionPoolSettings.getPoolSize()); + .setMaxSize(connectionPoolSettings.getPoolSize()) + .setIdleTimeout(connectionPoolSettings.getIdleTimeout()) + .setIdleTimeoutUnit(TimeUnit.SECONDS); - return PgBuilder - .pool() + return PgBuilder.pool() .with(poolOptions) + .with(netClientOptions) .connectingTo(sqlConnectOptions) .using(vertx) .build(); @@ -164,8 +167,7 @@ CircuitBreakerSecuredDatabaseClient circuitBreakerSecuredAsyncDatabaseClient( metrics, circuitBreakerProperties.getOpeningThreshold(), circuitBreakerProperties.getOpeningIntervalMs(), - circuitBreakerProperties.getClosingIntervalMs(), - clock); + circuitBreakerProperties.getClosingIntervalMs()); } private static BasicDatabaseClient createBasicDatabaseClient(Pool pool, @@ -175,7 +177,7 @@ private static BasicDatabaseClient createBasicDatabaseClient(Pool pool, final BasicDatabaseClient basicDatabaseClient = new BasicDatabaseClient(pool, metrics, clock); - contextRunner.runBlocking(promise -> basicDatabaseClient.initialize().onComplete(promise)); + contextRunner.runBlocking(basicDatabaseClient::initialize); return basicDatabaseClient; } diff --git a/src/main/java/org/prebid/server/spring/config/server/admin/AdminServerAuthProvider.java b/src/main/java/org/prebid/server/spring/config/server/admin/AdminServerAuthProvider.java index f95980d1a60..6796f7e13f6 100644 --- a/src/main/java/org/prebid/server/spring/config/server/admin/AdminServerAuthProvider.java +++ b/src/main/java/org/prebid/server/spring/config/server/admin/AdminServerAuthProvider.java @@ -1,17 +1,16 @@ package org.prebid.server.spring.config.server.admin; -import io.vertx.core.AsyncResult; import io.vertx.core.Future; -import io.vertx.core.Handler; import io.vertx.core.json.JsonObject; -import io.vertx.ext.auth.AuthProvider; import io.vertx.ext.auth.User; +import io.vertx.ext.auth.authentication.AuthenticationProvider; +import io.vertx.ext.auth.authentication.Credentials; import org.apache.commons.collections4.MapUtils; import org.apache.commons.lang3.StringUtils; import java.util.Map; -public class AdminServerAuthProvider implements AuthProvider { +public class AdminServerAuthProvider implements AuthenticationProvider { private final Map credentials; @@ -20,20 +19,18 @@ public AdminServerAuthProvider(Map credentials) { } @Override - public void authenticate(JsonObject authInfo, Handler> resultHandler) { + public Future authenticate(Credentials userCredentials) { if (MapUtils.isEmpty(credentials)) { - resultHandler.handle(Future.failedFuture("Credentials not set in configuration.")); - return; + return Future.failedFuture("Credentials not set in configuration."); } - final String requestUsername = authInfo.getString("username"); - final String requestPassword = StringUtils.chomp(authInfo.getString("password")); - + final JsonObject principal = userCredentials.toJson(); + final String requestUsername = principal.getString("username"); + final String requestPassword = StringUtils.chomp(principal.getString("password")); final String storedPassword = credentials.get(requestUsername); - if (StringUtils.isNotBlank(requestPassword) && StringUtils.equals(storedPassword, requestPassword)) { - resultHandler.handle(Future.succeededFuture()); - } else { - resultHandler.handle(Future.failedFuture("No such user, or password incorrect.")); - } + + return StringUtils.isNotBlank(requestPassword) && StringUtils.equals(storedPassword, requestPassword) + ? Future.succeededFuture() + : Future.failedFuture("Password does not match."); } } diff --git a/src/main/java/org/prebid/server/spring/config/server/application/ApplicationServerConfiguration.java b/src/main/java/org/prebid/server/spring/config/server/application/ApplicationServerConfiguration.java index c6ae167ae94..a6b56622c42 100644 --- a/src/main/java/org/prebid/server/spring/config/server/application/ApplicationServerConfiguration.java +++ b/src/main/java/org/prebid/server/spring/config/server/application/ApplicationServerConfiguration.java @@ -197,7 +197,7 @@ NoCacheHandler noCacheHandler() { @Bean CorsHandler corsHandler() { return CorsHandler.create() - .addRelativeOrigin(".*") + .addOriginWithRegex(".*") .allowCredentials(true) .allowedHeaders(new HashSet<>(Arrays.asList( HttpUtil.ORIGIN_HEADER.toString(), diff --git a/src/main/java/org/prebid/server/util/HttpUtil.java b/src/main/java/org/prebid/server/util/HttpUtil.java index e08a276c6fa..95b686abf9b 100644 --- a/src/main/java/org/prebid/server/util/HttpUtil.java +++ b/src/main/java/org/prebid/server/util/HttpUtil.java @@ -181,13 +181,12 @@ public static Map cookiesAsMap(HttpRequestContext httpRequest) { } public static Map cookiesAsMap(RoutingContext routingContext) { - return routingContext.cookieMap().entrySet().stream() - .collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().getValue())); + return routingContext.request().cookies().stream() + .collect(Collectors.toMap(Cookie::getName, Cookie::getValue)); } public static String createCookiesHeader(RoutingContext routingContext) { - return routingContext.cookieMap().entrySet().stream() - .map(entry -> Cookie.cookie(entry.getKey(), entry.getValue().getValue())) + return routingContext.request().cookies().stream() .map(Cookie::encode) .collect(Collectors.joining("; ")); } diff --git a/src/main/java/org/prebid/server/vertx/CircuitBreaker.java b/src/main/java/org/prebid/server/vertx/CircuitBreaker.java deleted file mode 100644 index 104545470f5..00000000000 --- a/src/main/java/org/prebid/server/vertx/CircuitBreaker.java +++ /dev/null @@ -1,144 +0,0 @@ -package org.prebid.server.vertx; - -import io.vertx.circuitbreaker.CircuitBreakerOptions; -import io.vertx.circuitbreaker.CircuitBreakerState; -import io.vertx.core.Future; -import io.vertx.core.Handler; -import io.vertx.core.Promise; -import io.vertx.core.Vertx; -import org.prebid.server.log.Logger; -import org.prebid.server.log.LoggerFactory; - -import java.time.Clock; -import java.util.Objects; - -/** - * Wrapper over Vert.x {@link io.vertx.circuitbreaker.CircuitBreaker} with functionality - * to reset failure counter to adjust open-circuit time frame. - */ -public class CircuitBreaker { - - private static final Logger logger = LoggerFactory.getLogger(CircuitBreaker.class); - - private final io.vertx.circuitbreaker.CircuitBreaker breaker; - private final Vertx vertx; - private final long openingIntervalMs; - private final Clock clock; - - private volatile long lastFailureTime; - - public CircuitBreaker(String name, - Vertx vertx, - int openingThreshold, - long openingIntervalMs, - long closingIntervalMs, - Clock clock) { - - breaker = io.vertx.circuitbreaker.CircuitBreaker.create( - Objects.requireNonNull(name), - Objects.requireNonNull(vertx), - new CircuitBreakerOptions() - .setNotificationPeriod(0) - .setMaxFailures(openingThreshold) - .setResetTimeout(closingIntervalMs)); - - this.vertx = vertx; - this.openingIntervalMs = openingIntervalMs; - this.clock = Objects.requireNonNull(clock); - } - - /** - * Executes the given operation with the circuit breaker control. - */ - public Future execute(Handler> command) { - return breaker.execute(promise -> execute(command, promise)); - } - - /** - * Executes operation and handle result of it on given {@link Promise}. - */ - private void execute(Handler> command, Promise promise) { - final Promise passedPromise = Promise.promise(); - command.handle(passedPromise); - - passedPromise.future() - .compose(response -> succeedBreaker(response, promise)) - .recover(exception -> failBreaker(exception, promise)); - } - - /** - * Succeeds given {@link Promise} and returns corresponding {@link Future}. - */ - private static Future succeedBreaker(T result, Promise promise) { - promise.complete(result); - return promise.future(); - } - - /** - * Fails given {@link Promise} and returns corresponding {@link Future}. - */ - private Future failBreaker(Throwable exception, Promise promise) { - final Promise ensureStatePromise = Promise.promise(); - vertx.executeBlocking(this::ensureState, false, ensureStatePromise); - - return ensureStatePromise.future() - .recover(throwable -> { - logger.warn("Resetting circuit breaker state failed", throwable); - promise.fail(throwable); - return promise.future(); - }) - .compose(ignored -> { // ensuring state succeeded, propagate real error - promise.fail(exception); - return promise.future(); - }); - } - - /** - * Resets failure counter to adjust open-circuit time frame. - *

- * Note: the operations {@link io.vertx.circuitbreaker.CircuitBreaker#state()} - * and {@link io.vertx.circuitbreaker.CircuitBreaker#reset()} can take a while, - * so it is better to perform them on a worker thread. - */ - private void ensureState(Promise executeBlockingPromise) { - final long currentTime = clock.millis(); - if (breaker.state() == CircuitBreakerState.CLOSED && lastFailureTime > 0 - && currentTime - lastFailureTime > openingIntervalMs) { - breaker.reset(); - } - - lastFailureTime = currentTime; - executeBlockingPromise.complete(); - } - - /** - * Sets a {@link Handler} invoked when the circuit breaker state switches to open. - */ - public CircuitBreaker openHandler(Handler handler) { - breaker.openHandler(handler); - return this; - } - - /** - * Sets a {@link Handler} invoked when the circuit breaker state switches to half-open. - */ - public CircuitBreaker halfOpenHandler(Handler handler) { - breaker.halfOpenHandler(handler); - return this; - } - - /** - * Sets a {@link Handler} invoked when the circuit breaker state switches to close. - */ - public CircuitBreaker closeHandler(Handler handler) { - breaker.closeHandler(handler); - return this; - } - - public boolean isOpen() { - return switch (breaker.state()) { - case OPEN, HALF_OPEN -> true; - case CLOSED -> false; - }; - } -} diff --git a/src/main/java/org/prebid/server/vertx/CloseableAdapter.java b/src/main/java/org/prebid/server/vertx/CloseableAdapter.java index 708796c63c7..6f714f8f0ae 100644 --- a/src/main/java/org/prebid/server/vertx/CloseableAdapter.java +++ b/src/main/java/org/prebid/server/vertx/CloseableAdapter.java @@ -1,7 +1,6 @@ package org.prebid.server.vertx; -import io.vertx.core.Future; -import io.vertx.core.Promise; +import io.vertx.core.Completable; import java.io.Closeable; import java.io.IOException; @@ -20,12 +19,12 @@ public CloseableAdapter(Closeable closeable) { } @Override - public void close(Promise promise) { + public void close(Completable completable) { try { closeable.close(); - promise.handle(Future.succeededFuture()); + completable.succeed(); } catch (IOException e) { - promise.handle(Future.failedFuture(e)); + completable.fail(e); } } } diff --git a/src/main/java/org/prebid/server/vertx/ContextRunner.java b/src/main/java/org/prebid/server/vertx/ContextRunner.java index 43dc35c3683..1e59fc394e9 100644 --- a/src/main/java/org/prebid/server/vertx/ContextRunner.java +++ b/src/main/java/org/prebid/server/vertx/ContextRunner.java @@ -7,6 +7,7 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; public class ContextRunner { @@ -18,6 +19,10 @@ public ContextRunner(Vertx vertx, long timeoutMs) { this.timeoutMs = timeoutMs; } + public void runBlocking(Supplier> action) { + runBlocking(promise -> action.get().onComplete(promise)); + } + public void runBlocking(Handler> action) { final CountDownLatch completionLatch = new CountDownLatch(1); final Promise promise = Promise.promise(); diff --git a/src/main/java/org/prebid/server/vertx/database/CircuitBreakerSecuredDatabaseClient.java b/src/main/java/org/prebid/server/vertx/database/CircuitBreakerSecuredDatabaseClient.java index ea59c9f9670..867f2e8c67d 100644 --- a/src/main/java/org/prebid/server/vertx/database/CircuitBreakerSecuredDatabaseClient.java +++ b/src/main/java/org/prebid/server/vertx/database/CircuitBreakerSecuredDatabaseClient.java @@ -1,30 +1,27 @@ package org.prebid.server.vertx.database; +import io.vertx.circuitbreaker.CircuitBreaker; +import io.vertx.circuitbreaker.CircuitBreakerOptions; +import io.vertx.circuitbreaker.CircuitBreakerState; import io.vertx.core.Future; import io.vertx.core.Vertx; import io.vertx.sqlclient.Row; import io.vertx.sqlclient.RowSet; import org.prebid.server.execution.timeout.Timeout; -import org.prebid.server.log.ConditionalLogger; import org.prebid.server.log.Logger; import org.prebid.server.log.LoggerFactory; import org.prebid.server.metric.Metrics; -import org.prebid.server.vertx.CircuitBreaker; -import java.time.Clock; import java.util.List; import java.util.Objects; -import java.util.concurrent.TimeUnit; import java.util.function.Function; /** - * Database Client wrapped by {@link CircuitBreaker} to achieve robust operating. + * Database Client wrapped by CircuitBreaker to achieve robust operating. */ public class CircuitBreakerSecuredDatabaseClient implements DatabaseClient { private static final Logger logger = LoggerFactory.getLogger(CircuitBreakerSecuredDatabaseClient.class); - private static final ConditionalLogger conditionalLogger = new ConditionalLogger(logger); - private static final int LOG_PERIOD_SECONDS = 5; private final DatabaseClient databaseClient; private final CircuitBreaker breaker; @@ -34,23 +31,20 @@ public CircuitBreakerSecuredDatabaseClient(Vertx vertx, Metrics metrics, int openingThreshold, long openingIntervalMs, - long closingIntervalMs, - Clock clock) { + long closingIntervalMs) { this.databaseClient = Objects.requireNonNull(databaseClient); - breaker = new CircuitBreaker( + breaker = CircuitBreaker.create( "db_cb", Objects.requireNonNull(vertx), - openingThreshold, - openingIntervalMs, - closingIntervalMs, - Objects.requireNonNull(clock)) - .openHandler(ignored -> circuitOpened()) - .halfOpenHandler(ignored -> circuitHalfOpened()) - .closeHandler(ignored -> circuitClosed()); + new CircuitBreakerOptions() + .setNotificationPeriod(0) + .setMaxFailures(openingThreshold) + .setFailuresRollingWindow(openingIntervalMs) + .setResetTimeout(closingIntervalMs)); - metrics.createDatabaseCircuitBreakerGauge(breaker::isOpen); + metrics.createDatabaseCircuitBreakerGauge(() -> breaker.state() != CircuitBreakerState.CLOSED); logger.info("Initialized database client with Circuit Breaker"); } @@ -61,19 +55,6 @@ public Future executeQuery(String query, Function, T> mapper, Timeout timeout) { - return breaker.execute( - promise -> databaseClient.executeQuery(query, params, mapper, timeout).onComplete(promise)); - } - - private void circuitOpened() { - conditionalLogger.warn("Database is unavailable, circuit opened.", LOG_PERIOD_SECONDS, TimeUnit.SECONDS); - } - - private void circuitHalfOpened() { - logger.warn("Database is ready to try again, circuit half-opened."); - } - - private void circuitClosed() { - logger.warn("Database becomes working, circuit closed."); + return breaker.execute(() -> databaseClient.executeQuery(query, params, mapper, timeout)); } } diff --git a/src/main/java/org/prebid/server/vertx/httpclient/CircuitBreakerSecuredHttpClient.java b/src/main/java/org/prebid/server/vertx/httpclient/CircuitBreakerSecuredHttpClient.java index 0843a04de12..7097be2de56 100644 --- a/src/main/java/org/prebid/server/vertx/httpclient/CircuitBreakerSecuredHttpClient.java +++ b/src/main/java/org/prebid/server/vertx/httpclient/CircuitBreakerSecuredHttpClient.java @@ -1,6 +1,9 @@ package org.prebid.server.vertx.httpclient; import com.github.benmanes.caffeine.cache.Caffeine; +import io.vertx.circuitbreaker.CircuitBreaker; +import io.vertx.circuitbreaker.CircuitBreakerOptions; +import io.vertx.circuitbreaker.CircuitBreakerState; import io.vertx.core.Future; import io.vertx.core.MultiMap; import io.vertx.core.Vertx; @@ -10,12 +13,10 @@ import org.prebid.server.log.Logger; import org.prebid.server.log.LoggerFactory; import org.prebid.server.metric.Metrics; -import org.prebid.server.vertx.CircuitBreaker; import org.prebid.server.vertx.httpclient.model.HttpClientResponse; import java.net.MalformedURLException; import java.net.URL; -import java.time.Clock; import java.util.Map; import java.util.Objects; import java.util.concurrent.TimeUnit; @@ -41,13 +42,12 @@ public CircuitBreakerSecuredHttpClient(Vertx vertx, int openingThreshold, long openingIntervalMs, long closingIntervalMs, - int idleExpireHours, - Clock clock) { + int idleExpireHours) { this.httpClient = Objects.requireNonNull(httpClient); circuitBreakerCreator = name -> createCircuitBreaker( - name, vertx, openingThreshold, openingIntervalMs, closingIntervalMs, clock, metrics); + name, vertx, openingThreshold, openingIntervalMs, closingIntervalMs, metrics); circuitBreakerByName = Caffeine.newBuilder() .expireAfterAccess(idleExpireHours, TimeUnit.HOURS) @@ -69,9 +69,7 @@ public Future request(HttpMethod method, long maxResponseSize) { return circuitBreakerByName.computeIfAbsent(nameFrom(url), circuitBreakerCreator) - .execute(promise -> - httpClient.request(method, url, headers, body, timeoutMs, maxResponseSize) - .onComplete(promise)); + .execute(() -> httpClient.request(method, url, headers, body, timeoutMs, maxResponseSize)); } @Override @@ -82,9 +80,7 @@ public Future request(HttpMethod method, long timeoutMs, long maxResponseSize) { return circuitBreakerByName.computeIfAbsent(nameFrom(url), circuitBreakerCreator) - .execute(promise -> - httpClient.request(method, url, headers, body, timeoutMs, maxResponseSize) - .onComplete(promise)); + .execute(() -> httpClient.request(method, url, headers, body, timeoutMs, maxResponseSize)); } private CircuitBreaker createCircuitBreaker(String name, @@ -92,16 +88,16 @@ private CircuitBreaker createCircuitBreaker(String name, int openingThreshold, long openingIntervalMs, long closingIntervalMs, - Clock clock, Metrics metrics) { - final CircuitBreaker circuitBreaker = new CircuitBreaker( - "http_cb_" + name, - Objects.requireNonNull(vertx), - openingThreshold, - openingIntervalMs, - closingIntervalMs, - Objects.requireNonNull(clock)) + final CircuitBreakerOptions options = new CircuitBreakerOptions() + .setNotificationPeriod(0) + .setMaxFailures(openingThreshold) + .setFailuresRollingWindow(openingIntervalMs) + .setResetTimeout(closingIntervalMs); + + final CircuitBreaker circuitBreaker = CircuitBreaker.create( + "http_cb_" + name, Objects.requireNonNull(vertx), options) .openHandler(ignored -> circuitOpened(name)) .halfOpenHandler(ignored -> circuitHalfOpened(name)) .closeHandler(ignored -> circuitClosed(name)); @@ -112,7 +108,8 @@ private CircuitBreaker createCircuitBreaker(String name, } private void createCircuitBreakerGauge(String name, CircuitBreaker circuitBreaker, Metrics metrics) { - metrics.createHttpClientCircuitBreakerGauge(idFrom(name), circuitBreaker::isOpen); + metrics.createHttpClientCircuitBreakerGauge( + idFrom(name), () -> circuitBreaker.state() != CircuitBreakerState.CLOSED); } private void removeCircuitBreakerGauge(String name, Metrics metrics) { diff --git a/src/main/java/org/prebid/server/vertx/verticles/server/ServerVerticle.java b/src/main/java/org/prebid/server/vertx/verticles/server/ServerVerticle.java index 147de2210c4..94046bfc0d5 100644 --- a/src/main/java/org/prebid/server/vertx/verticles/server/ServerVerticle.java +++ b/src/main/java/org/prebid/server/vertx/verticles/server/ServerVerticle.java @@ -1,7 +1,6 @@ package org.prebid.server.vertx.verticles.server; import io.vertx.core.AbstractVerticle; -import io.vertx.core.AsyncResult; import io.vertx.core.Promise; import io.vertx.core.http.HttpServer; import io.vertx.core.http.HttpServerOptions; @@ -55,19 +54,12 @@ public void start(Promise startPromise) { server.exceptionHandler(exceptionHandler); } - server.listen(address, result -> onServerStarted(result, startPromise)); - } - - private void onServerStarted(AsyncResult result, Promise startPromise) { - if (result.succeeded()) { - startPromise.tryComplete(); - logger.info( - "Successfully started {} instance on address: {}, thread: {}", - name, - address, - Thread.currentThread().getName()); - } else { - startPromise.tryFail(result.cause()); - } + server.listen(address) + .onComplete(result -> startPromise.complete(null, result.cause())) + .onSuccess(ignored -> logger.info( + "Successfully started {} instance on address: {}, thread: {}", + name, + address, + Thread.currentThread().getName())); } } diff --git a/src/test/groovy/org/prebid/server/functional/tests/SetUidSpec.groovy b/src/test/groovy/org/prebid/server/functional/tests/SetUidSpec.groovy index 7e9eff9ebd3..a64bbc21086 100644 --- a/src/test/groovy/org/prebid/server/functional/tests/SetUidSpec.groovy +++ b/src/test/groovy/org/prebid/server/functional/tests/SetUidSpec.groovy @@ -514,7 +514,7 @@ class SetUidSpec extends BaseSpec { } List getSetUidsHeaders(SetuidResponse response, boolean includeEmpty = false) { - response.headers.get("Set-Cookie").findAll { cookie -> + response.headers.get("set-cookie").findAll { cookie -> includeEmpty || !(cookie =~ /\buids\d*=\s*;/) } } diff --git a/src/test/java/org/prebid/server/analytics/reporter/pubstack/PubstackAnalyticsReporterTest.java b/src/test/java/org/prebid/server/analytics/reporter/pubstack/PubstackAnalyticsReporterTest.java index ef1e488e81e..e971498c598 100644 --- a/src/test/java/org/prebid/server/analytics/reporter/pubstack/PubstackAnalyticsReporterTest.java +++ b/src/test/java/org/prebid/server/analytics/reporter/pubstack/PubstackAnalyticsReporterTest.java @@ -26,6 +26,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.TimeUnit; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.any; @@ -106,14 +107,19 @@ public void initializeShouldFetchConfigAndSetPeriodicTimerForConfigUpdate() thro @Test public void initializeShouldFailUpdateSendBuffersAndSetTimerWhenEndpointFromRemoteConfigIsNotValid() throws JsonProcessingException { + // given + final Promise promise = Promise.promise(); final PubstackConfig pubstackConfig = PubstackConfig.of("newScopeId", "invalid", Collections.singletonMap(EventType.auction, true)); given(httpClient.get(anyString(), anyLong())).willReturn( Future.succeededFuture(HttpClientResponse.of(200, null, mapper.writeValueAsString(pubstackConfig)))); - // when and then - assertThatThrownBy(() -> pubstackAnalyticsReporter.initialize(Promise.promise())) + // when + pubstackAnalyticsReporter.initialize(promise); + + // then + assertThatThrownBy(() -> promise.future().await(5, TimeUnit.SECONDS)) .hasMessage("[pubstack] Failed to create event report url for endpoint: invalid") .isInstanceOf(PreBidException.class); verify(auctionHandler).reportEvents(); diff --git a/src/test/java/org/prebid/server/bidder/criteo/CriteoBidderTest.java b/src/test/java/org/prebid/server/bidder/criteo/CriteoBidderTest.java index 291dd35d415..dc2df0ee0bd 100644 --- a/src/test/java/org/prebid/server/bidder/criteo/CriteoBidderTest.java +++ b/src/test/java/org/prebid/server/bidder/criteo/CriteoBidderTest.java @@ -71,7 +71,10 @@ public void makeHttpRequestsShouldEncodePassedBidRequest() { .body(jacksonMapper.encodeToBytes(bidRequest)) .payload(bidRequest) .build()); - assertThat(result.getValue()).usingRecursiveComparison().isEqualTo(expectedResult.getValue()); + + assertThat(result.getValue()).usingRecursiveComparison() + .withComparatorForType((a, b) -> a.entries().equals(b.entries()) ? 0 : 1, MultiMap.class) + .isEqualTo(expectedResult.getValue()); assertThat(result.getErrors()).isEmpty(); } diff --git a/src/test/java/org/prebid/server/bidder/gothamads/GothamAdsBidderTest.java b/src/test/java/org/prebid/server/bidder/gothamads/GothamAdsBidderTest.java index 41c38db12ad..324a43a69bd 100644 --- a/src/test/java/org/prebid/server/bidder/gothamads/GothamAdsBidderTest.java +++ b/src/test/java/org/prebid/server/bidder/gothamads/GothamAdsBidderTest.java @@ -103,7 +103,10 @@ public void makeHttpRequestsShouldMakeCorrectRequest() { .build() ); - assertThat(result.getValue()).usingRecursiveComparison().isEqualTo(expectedResult.getValue()); + assertThat(result.getValue()) + .usingRecursiveComparison() + .withComparatorForType((a, b) -> a.entries().equals(b.entries()) ? 0 : 1, MultiMap.class) + .isEqualTo(expectedResult.getValue()); assertThat(result.getErrors()).isEmpty(); } diff --git a/src/test/java/org/prebid/server/bidder/pwbid/PwbidBidderTest.java b/src/test/java/org/prebid/server/bidder/pwbid/PwbidBidderTest.java index b790ad9033f..ec6b07fd06e 100644 --- a/src/test/java/org/prebid/server/bidder/pwbid/PwbidBidderTest.java +++ b/src/test/java/org/prebid/server/bidder/pwbid/PwbidBidderTest.java @@ -72,7 +72,10 @@ public void makeHttpRequestsShouldReturnExpectedHttpRequest() { .payload(bidRequest) .build()); - assertThat(result.getValue()).usingRecursiveComparison().isEqualTo(expectedResults.getValue()); + assertThat(result.getValue()) + .usingRecursiveComparison() + .withComparatorForType((a, b) -> a.entries().equals(b.entries()) ? 0 : 1, MultiMap.class) + .isEqualTo(expectedResults.getValue()); assertThat(result.getErrors()).isEmpty(); } diff --git a/src/test/java/org/prebid/server/cookie/CookieDeprecationServiceTest.java b/src/test/java/org/prebid/server/cookie/CookieDeprecationServiceTest.java index 111d4f441f5..da03b34ec0e 100644 --- a/src/test/java/org/prebid/server/cookie/CookieDeprecationServiceTest.java +++ b/src/test/java/org/prebid/server/cookie/CookieDeprecationServiceTest.java @@ -5,6 +5,7 @@ import com.iab.openrtb.request.Device; import io.vertx.core.http.Cookie; import io.vertx.core.http.CookieSameSite; +import io.vertx.core.http.HttpServerRequest; import io.vertx.ext.web.RoutingContext; import org.apache.commons.lang3.RandomStringUtils; import org.junit.jupiter.api.BeforeEach; @@ -26,6 +27,7 @@ import org.prebid.server.settings.model.AccountPrivacySandboxCookieDeprecationConfig; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.function.UnaryOperator; @@ -41,18 +43,22 @@ public class CookieDeprecationServiceTest extends VertxTest { @Mock(strictness = LENIENT) private RoutingContext routingContext; + @Mock(strictness = LENIENT) + private HttpServerRequest request; + private final CookieDeprecationService target = new CookieDeprecationService(); @BeforeEach public void before() { - given(routingContext.cookieMap()).willReturn(Map.of()); + given(routingContext.request()).willReturn(request); + given(request.cookies()).willReturn(Collections.emptySet()); } @Test public void makeCookieShouldReturnNullWhenRequestContainsDeprecationCookie() { // given - given(routingContext.cookieMap()) - .willReturn(Map.of("receive-cookie-deprecation", Cookie.cookie("receive-cookie-deprecation", "1"))); + given(routingContext.request().cookies()) + .willReturn(Collections.singleton(Cookie.cookie("receive-cookie-deprecation", "1"))); // when final PartitionedCookie actualCookie = target.makeCookie( @@ -66,8 +72,8 @@ public void makeCookieShouldReturnNullWhenRequestContainsDeprecationCookie() { @Test public void makeCookieShouldReturnNullWhenRequestContainsDeprecationCookieAndAccountIsEmpty() { // given - given(routingContext.cookieMap()) - .willReturn(Map.of("receive-cookie-deprecation", Cookie.cookie("receive-cookie-deprecation", "1"))); + given(request.cookies()).willReturn( + Collections.singleton(Cookie.cookie("receive-cookie-deprecation", "1"))); // when final PartitionedCookie actualCookie = target.makeCookie(Account.builder().build(), routingContext); diff --git a/src/test/java/org/prebid/server/cookie/UidsCookieServiceTest.java b/src/test/java/org/prebid/server/cookie/UidsCookieServiceTest.java index c73ee163935..2f930fdf0de 100644 --- a/src/test/java/org/prebid/server/cookie/UidsCookieServiceTest.java +++ b/src/test/java/org/prebid/server/cookie/UidsCookieServiceTest.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import io.vertx.core.http.Cookie; import io.vertx.core.http.CookieSameSite; +import io.vertx.core.http.HttpServerRequest; import io.vertx.ext.web.RoutingContext; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeEach; @@ -25,15 +26,19 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; import static java.util.Collections.emptyMap; +import static java.util.Collections.emptySet; +import static java.util.Collections.singleton; import static java.util.Collections.singletonMap; import static java.util.function.Function.identity; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.within; import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; +import static org.mockito.Mock.Strictness.LENIENT; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; @@ -49,8 +54,10 @@ public class UidsCookieServiceTest extends VertxTest { // Zero means size checking is disabled private static final int MAX_COOKIE_SIZE_BYTES = 0; - @Mock + @Mock(strictness = LENIENT) private RoutingContext routingContext; + @Mock(strictness = LENIENT) + private HttpServerRequest request; @Mock private PrioritizedCoopSyncProvider prioritizedCoopSyncProvider; @Mock @@ -72,6 +79,8 @@ public void setUp() { prioritizedCoopSyncProvider, metrics, jacksonMapper); + + given(routingContext.request()).willReturn(request); } @Test @@ -145,12 +154,13 @@ public void shouldReturnNonEmptyUidsCookie() { // this uids cookie value stands for { "tempUIDs":{ "rubicon":{ "uid": "J5VLCWQP-26-CWFT", // "expires": "2023-12-05T19:00:05.103329-03:00" }, "adnxs":{ "uid": "12345", // "expires": "2023-12-05T19:00:05.103329-03:00" } } } - given(routingContext.cookieMap()).willReturn(singletonMap("uids", Cookie.cookie( - "tempUIDs", - "eyAidGVtcFVJRHMiOnsgInJ1Ymljb24iOnsgInVpZCI6ICJKNVZMQ1dRUC0yNi1DV0ZUIiwg" - + "ImV4cGlyZXMiOiAiMjAyMy0xMi0wNVQxOTowMDowNS4xMDMzMjktMDM6MDAiIH0sICJhZG5" - + "4cyI6eyAidWlkIjogIjEyMzQ1IiwgImV4cGlyZXMiOiAiMjAyMy0xMi0wNVQxOTowMDowNS" - + "4xMDMzMjktMDM6MDAiIH0gfSB9"))); + given(request.cookies()).willReturn(singleton( + Cookie.cookie( + "uids", + "eyAidGVtcFVJRHMiOnsgInJ1Ymljb24iOnsgInVpZCI6ICJKNVZMQ1dRUC0yNi1DV0ZUIiwg" + + "ImV4cGlyZXMiOiAiMjAyMy0xMi0wNVQxOTowMDowNS4xMDMzMjktMDM6MDAiIH0sICJhZG5" + + "4cyI6eyAidWlkIjogIjEyMzQ1IiwgImV4cGlyZXMiOiAiMjAyMy0xMi0wNVQxOTowMDowNS" + + "4xMDMzMjktMDM6MDAiIH0gfSB9"))); // when final UidsCookie uidsCookie = target.parseFromRequest(routingContext); @@ -173,7 +183,7 @@ public void shouldReturnNonNullUidsCookieIfUidsCookieIsMissing() { @Test public void shouldReturnNonNullUidsCookieIfUidsCookieIsNonBase64() { // given - given(routingContext.cookieMap()).willReturn(singletonMap("uids", Cookie.cookie("uids", "abcde"))); + given(request.cookies()).willReturn(singleton(Cookie.cookie("uids", "abcde"))); // when final UidsCookie uidsCookie = target.parseFromRequest(routingContext); @@ -186,7 +196,7 @@ public void shouldReturnNonNullUidsCookieIfUidsCookieIsNonBase64() { public void shouldReturnNonNullUidsCookieIfUidsCookieIsNonJson() { // given // this uids cookie value stands for "abcde" - given(routingContext.cookieMap()).willReturn(singletonMap("uids", Cookie.cookie("tempUIDs", "bm9uLWpzb24="))); + given(request.cookies()).willReturn(singleton(Cookie.cookie("uids", "bm9uLWpzb24="))); // when final UidsCookie uidsCookie = target.parseFromRequest(routingContext); @@ -198,8 +208,8 @@ public void shouldReturnNonNullUidsCookieIfUidsCookieIsNonJson() { @Test public void shouldReturnUidsCookieWithOptoutTrueIfUidsCookieIsMissingAndOptoutCookieHasExpectedValue() { // given - given(routingContext.cookieMap()).willReturn( - singletonMap(OPT_OUT_COOKIE_NAME, Cookie.cookie(OPT_OUT_COOKIE_NAME, OPT_OUT_COOKIE_VALUE))); + given(request.cookies()).willReturn( + singleton(Cookie.cookie(OPT_OUT_COOKIE_NAME, OPT_OUT_COOKIE_VALUE))); // when final UidsCookie uidsCookie = target.parseFromRequest(routingContext); @@ -211,19 +221,18 @@ public void shouldReturnUidsCookieWithOptoutTrueIfUidsCookieIsMissingAndOptoutCo @Test public void shouldReturnUidsCookieWithOptoutTrueIfUidsCookieIsPresentAndOptoutCookieHasExpectedValue() { // given - final Map cookies = new HashMap<>(); - // this uids cookie value stands for { "tempUIDs":{ "rubicon":{ "uid": "J5VLCWQP-26-CWFT", - // "expires": "2023-12-05T19:00:05.103329-03:00" }, "adnxs":{ "uid": "12345", - // "expires": "2023-12-05T19:00:05.103329-03:00" } } } - cookies.put("uids", + final Set cookies = Set.of( + // this uids cookie value stands for { "tempUIDs":{ "rubicon":{ "uid": "J5VLCWQP-26-CWFT", + // "expires": "2023-12-05T19:00:05.103329-03:00" }, "adnxs":{ "uid": "12345", + // "expires": "2023-12-05T19:00:05.103329-03:00" } } } Cookie.cookie("uids", "eyAidGVtcFVJRHMiOnsgInJ1Ymljb24iOnsgInVpZCI6ICJKNVZMQ1dRUC0yNi1DV0" + "ZUIiwgImV4cGlyZXMiOiAiMjAyMy0xMi0wNVQxOTowMDowNS4xMDMzMjktMDM6MDAiIH0sICJhZG5" + "4cyI6eyAidWlkIjogIjEyMzQ1IiwgImV4cGlyZXMiOiAiMjAyMy0xMi0wNVQxOTowMDowNS" - + "4xMDMzMjktMDM6MDAiIH0gfSB9")); + + "4xMDMzMjktMDM6MDAiIH0gfSB9"), - cookies.put(OPT_OUT_COOKIE_NAME, Cookie.cookie(OPT_OUT_COOKIE_NAME, OPT_OUT_COOKIE_VALUE)); + Cookie.cookie(OPT_OUT_COOKIE_NAME, OPT_OUT_COOKIE_VALUE)); - given(routingContext.cookieMap()).willReturn(cookies); + given(request.cookies()).willReturn(cookies); // when final UidsCookie uidsCookie = target.parseFromRequest(routingContext); @@ -285,19 +294,20 @@ public void aliveCookieShouldSetPath() { @Test public void shouldReturnUidsCookieWithOptoutFalseIfOptoutCookieHasNotExpectedValue() { // given - final Map cookies = new HashMap<>(); - // this uids cookie value stands for { "tempUIDs":{ "rubicon":{ "uid": "J5VLCWQP-26-CWFT", - // "expires": "2023-12-05T19:00:05.103329-03:00" }, "adnxs":{ "uid": "12345", - // "expires": "2023-12-05T19:00:05.103329-03:00" } } } - cookies.put("uids", Cookie.cookie( - "tempUIDs", - "eyAidGVtcFVJRHMiOnsgInJ1Ymljb24iOnsgInVpZCI6ICJKNVZMQ1dRUC0yNi1DV0ZUIiwg" - + "ImV4cGlyZXMiOiAiMjAyMy0xMi0wNVQxOTowMDowNS4xMDMzMjktMDM6MDAiIH0sICJhZG5" - + "4cyI6eyAidWlkIjogIjEyMzQ1IiwgImV4cGlyZXMiOiAiMjAyMy0xMi0wNVQxOTowMDowNS" - + "4xMDMzMjktMDM6MDAiIH0gfSB9")); - cookies.put(OPT_OUT_COOKIE_NAME, Cookie.cookie(OPT_OUT_COOKIE_NAME, "dummy")); + final Set cookies = Set.of( + // this uids cookie value stands for { "tempUIDs":{ "rubicon":{ "uid": "J5VLCWQP-26-CWFT", + // "expires": "2023-12-05T19:00:05.103329-03:00" }, "adnxs":{ "uid": "12345", + // "expires": "2023-12-05T19:00:05.103329-03:00" } } } + Cookie.cookie( + "uids", + "eyAidGVtcFVJRHMiOnsgInJ1Ymljb24iOnsgInVpZCI6ICJKNVZMQ1dRUC0yNi1DV0ZUIiwg" + + "ImV4cGlyZXMiOiAiMjAyMy0xMi0wNVQxOTowMDowNS4xMDMzMjktMDM6MDAiIH0sICJhZG5" + + "4cyI6eyAidWlkIjogIjEyMzQ1IiwgImV4cGlyZXMiOiAiMjAyMy0xMi0wNVQxOTowMDowNS" + + "4xMDMzMjktMDM6MDAiIH0gfSB9"), - given(routingContext.cookieMap()).willReturn(cookies); + Cookie.cookie(OPT_OUT_COOKIE_NAME, "dummy")); + + given(request.cookies()).willReturn(cookies); // when final UidsCookie uidsCookie = target.parseFromRequest(routingContext); @@ -323,8 +333,9 @@ public void shouldReturnUidsCookieWithOptoutFalseIfOptoutCookieNameNotSpecified( prioritizedCoopSyncProvider, metrics, jacksonMapper); - given(routingContext.cookieMap()).willReturn( - singletonMap(OPT_OUT_COOKIE_NAME, Cookie.cookie("trp_optout", "true"))); + + given(request.cookies()).willReturn( + singleton(Cookie.cookie(OPT_OUT_COOKIE_NAME, "true"))); // when final UidsCookie uidsCookie = target.parseFromRequest(routingContext); @@ -348,8 +359,9 @@ public void shouldReturnUidsCookieWithOptoutFalseIfOptoutCookieValueNotSpecified prioritizedCoopSyncProvider, metrics, jacksonMapper); - given(routingContext.cookieMap()).willReturn( - singletonMap(OPT_OUT_COOKIE_NAME, Cookie.cookie("trp_optout", "true"))); + + given(request.cookies()).willReturn( + singleton(Cookie.cookie(OPT_OUT_COOKIE_NAME, "true"))); // when final UidsCookie uidsCookie = target.parseFromRequest(routingContext); @@ -373,7 +385,8 @@ public void shouldReturnRubiconCookieValueFromHostCookieWhenUidValueIsAbsent() { prioritizedCoopSyncProvider, metrics, jacksonMapper); - given(routingContext.cookieMap()).willReturn(singletonMap("khaos", Cookie.cookie("khaos", "abc123"))); + + given(request.cookies()).willReturn(singleton(Cookie.cookie("khaos", "abc123"))); // when final UidsCookie uidsCookie = target.parseFromRequest(routingContext); @@ -398,18 +411,18 @@ public void shouldReturnRubiconCookieValueFromHostCookieWhenUidValueIsPresentBut metrics, jacksonMapper); - final Map cookies = new HashMap<>(); - // this uids cookie value stands for { "tempUIDs":{ "rubicon":{ "uid": "J5VLCWQP-26-CWFT", - // "expires": "2023-12-05T19:00:05.103329-03:00" }, "adnxs":{ "uid": "12345", - // "expires": "2023-12-05T19:00:05.103329-03:00" } } } - cookies.put("uids", + final Set cookies = Set.of( + // this uids cookie value stands for { "tempUIDs":{ "rubicon":{ "uid": "J5VLCWQP-26-CWFT", + // "expires": "2023-12-05T19:00:05.103329-03:00" }, "adnxs":{ "uid": "12345", + // "expires": "2023-12-05T19:00:05.103329-03:00" } } } Cookie.cookie("uids", "eyAidGVtcFVJRHMiOnsgInJ1Ymljb24iOnsgInVpZCI6ICJKNVZMQ1dRUC0yNi1DV0" + "ZUIiwgImV4cGlyZXMiOiAiMjAyMy0xMi0wNVQxOTowMDowNS4xMDMzMjktMDM6MDAiIH0sICJhZG5" + "4cyI6eyAidWlkIjogIjEyMzQ1IiwgImV4cGlyZXMiOiAiMjAyMy0xMi0wNVQxOTowMDowNS" - + "4xMDMzMjktMDM6MDAiIH0gfSB9")); - cookies.put("khaos", Cookie.cookie("khaos", "abc123")); + + "4xMDMzMjktMDM6MDAiIH0gfSB9"), + + Cookie.cookie("khaos", "abc123")); - given(routingContext.cookieMap()).willReturn(cookies); + given(request.cookies()).willReturn(cookies); // when final UidsCookie uidsCookie = target.parseFromRequest(routingContext); @@ -427,7 +440,7 @@ public void shouldSkipFacebookSentinelFromUidsCookie() throws JsonProcessingExce final Uids uids = Uids.builder().uids(uidsWithExpiry).build(); final String encodedUids = encodeUids(uids); - given(routingContext.cookieMap()).willReturn(singletonMap("uids", Cookie.cookie("uids", encodedUids))); + given(request.cookies()).willReturn(singleton(Cookie.cookie("uids", encodedUids))); // when final UidsCookie uidsCookie = target.parseFromRequest(routingContext); @@ -594,11 +607,11 @@ public void hostCookieUidToSyncShouldReturnHostCookieUidWhenHostCookieUidIsAbsen jacksonMapper); final String uidsCookieBase64 = Base64.getUrlEncoder().encodeToString(uidsCookie.toJson().getBytes()); - final Map cookieMap = Map.of( - "khaos", Cookie.cookie("khaos", "hostCookieUid"), - "uids", Cookie.cookie("uids", uidsCookieBase64)); + final Set cookies = Set.of( + Cookie.cookie("khaos", "hostCookieUid"), + Cookie.cookie("uids", uidsCookieBase64)); - given(routingContext.cookieMap()).willReturn(cookieMap); + given(request.cookies()).willReturn(cookies); // when final String result = target.hostCookieUidToSync(routingContext, RUBICON); @@ -623,7 +636,7 @@ public void hostCookieUidToSyncShouldReturnNullWhenUidsCookieHasNoUidForHostCook metrics, jacksonMapper); - given(routingContext.cookieMap()).willReturn(emptyMap()); + given(request.cookies()).willReturn(emptySet()); // when final String result = target.hostCookieUidToSync(routingContext, RUBICON); @@ -653,11 +666,11 @@ public void hostCookieUidToSyncShouldReturnNullWhenUidInUidsCookieSameAsUidInHos jacksonMapper); final String uidsCookieBase64 = Base64.getUrlEncoder().encodeToString(uidsCookie.toJson().getBytes()); - final Map cookieMap = Map.of( - "khaos", Cookie.cookie("khaos", "hostCookieUid"), - "uids", Cookie.cookie("uids", uidsCookieBase64)); + final Set cookies = Set.of( + Cookie.cookie("khaos", "hostCookieUid"), + Cookie.cookie("uids", uidsCookieBase64)); - given(routingContext.cookieMap()).willReturn(cookieMap); + given(request.cookies()).willReturn(cookies); // when final String result = target.hostCookieUidToSync(routingContext, RUBICON); diff --git a/src/test/java/org/prebid/server/execution/file/syncer/RemoteFileSyncerTest.java b/src/test/java/org/prebid/server/execution/file/syncer/RemoteFileSyncerTest.java index 2da614c4e50..33bef039bab 100644 --- a/src/test/java/org/prebid/server/execution/file/syncer/RemoteFileSyncerTest.java +++ b/src/test/java/org/prebid/server/execution/file/syncer/RemoteFileSyncerTest.java @@ -207,7 +207,7 @@ public void syncForFilepathShouldNotUpdateWhenHeadRequestReturnInvalidHead() { verify(fileSystem, times(2)).exists(eq(FILE_PATH)); verify(httpClient).request(any()); verify(fileProcessor).setDataPath(any()); - verify(fileSystem, never()).move(eq(TMP_FILE_PATH), eq(FILE_PATH), any(), any()); + verify(fileSystem, never()).move(eq(TMP_FILE_PATH), eq(FILE_PATH), any()); verify(vertx).setPeriodic(eq(UPDATE_INTERVAL), any()); verifyNoMoreInteractions(httpClient); } @@ -376,9 +376,7 @@ public void syncForFilepathShouldRetryWhenFileOpeningFailed() { given(vertx.setTimer(eq(RETRY_INTERVAL), any())) .willAnswer(withReturnObjectAndPassObjectToHandler(0L, 10L, 1)); - given(fileSystem.delete(any(), any())) - .willAnswer(withSelfAndPassObjectToHandler(Future.succeededFuture())) - .willAnswer(withSelfAndPassObjectToHandler(Future.failedFuture(new RuntimeException()))); + given(fileSystem.delete(any())).willReturn(Future.failedFuture(new RuntimeException())); given(fileProcessor.setDataPath(anyString())) .willReturn(Future.succeededFuture()); @@ -528,7 +526,7 @@ public void syncShouldNotUpdateFileWhenServerRespondsWithNonOkStatusCode() { verify(fileSystem, times(1)).exists(eq(FILE_PATH)); verify(fileSystem, never()).open(any(), any()); verify(fileSystem, never()).delete(any()); - verify(fileSystem, never()).move(any(), any(), any(), any()); + verify(fileSystem, never()).move(any(), any(), any()); verify(asyncFile, never()).close(); verify(httpClient, times(1)).request(any()); verify(httpClientResponse).statusCode(); diff --git a/src/test/java/org/prebid/server/geolocation/CircuitBreakerSecuredGeoLocationServiceTest.java b/src/test/java/org/prebid/server/geolocation/CircuitBreakerSecuredGeoLocationServiceTest.java index 084d64d1bcf..6afe645cc4d 100644 --- a/src/test/java/org/prebid/server/geolocation/CircuitBreakerSecuredGeoLocationServiceTest.java +++ b/src/test/java/org/prebid/server/geolocation/CircuitBreakerSecuredGeoLocationServiceTest.java @@ -16,9 +16,7 @@ import org.prebid.server.geolocation.model.GeoInfo; import org.prebid.server.metric.Metrics; -import java.time.Clock; -import java.time.Instant; -import java.time.ZoneId; +import java.util.concurrent.TimeUnit; import java.util.function.BooleanSupplier; import static org.assertj.core.api.Assertions.assertThat; @@ -33,7 +31,6 @@ public class CircuitBreakerSecuredGeoLocationServiceTest { private Vertx vertx; - private Clock clock; @Mock private GeoLocationService wrappedGeoLocationService; @Mock @@ -44,14 +41,14 @@ public class CircuitBreakerSecuredGeoLocationServiceTest { @BeforeEach public void setUp() { vertx = Vertx.vertx(); - clock = Clock.fixed(Instant.now(), ZoneId.systemDefault()); geoLocationService = new CircuitBreakerSecuredGeoLocationService(vertx, wrappedGeoLocationService, metrics, 1, - 100L, 200L, clock); + 1000L, 200L); } @AfterEach - public void tearDown(VertxTestContext context) { - vertx.close(context.succeedingThenComplete()); + public void tearDown(VertxTestContext context) throws InterruptedException { + vertx.close().onComplete(context.succeedingThenComplete()); + context.awaitCompletion(1000, TimeUnit.MILLISECONDS); } @Test @@ -143,7 +140,7 @@ public void lookupShouldSucceedsIfCircuitIsHalfOpenedAndWrappedGeoLocationSuccee public void lookupShouldFailsWithOriginalExceptionIfOpeningIntervalExceeds() { // given geoLocationService = new CircuitBreakerSecuredGeoLocationService(vertx, wrappedGeoLocationService, metrics, 2, - 100L, 200L, clock); + 100L, 200L); givenWrappedGeoLocationReturning( Future.failedFuture(new RuntimeException("exception1")), diff --git a/src/test/java/org/prebid/server/handler/SetuidHandlerTest.java b/src/test/java/org/prebid/server/handler/SetuidHandlerTest.java index ed88f7f6fdd..8cb36af2a28 100644 --- a/src/test/java/org/prebid/server/handler/SetuidHandlerTest.java +++ b/src/test/java/org/prebid/server/handler/SetuidHandlerTest.java @@ -1,5 +1,6 @@ package org.prebid.server.handler; +import com.fasterxml.jackson.core.JsonProcessingException; import io.vertx.core.Future; import io.vertx.core.MultiMap; import io.vertx.core.http.Cookie; @@ -55,7 +56,6 @@ import java.util.Map; import java.util.Optional; import java.util.Set; -import java.util.stream.Collectors; import static java.util.Collections.emptyMap; import static java.util.Collections.singleton; @@ -64,6 +64,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; import static org.mockito.Mock.Strictness.LENIENT; @@ -344,7 +345,7 @@ public void shouldRespondWithoutCookieIfGdprProcessingPreventsCookieSetting() { setuidHandler.handle(routingContext); // then - verify(routingContext, never()).addCookie(any(Cookie.class)); + verify(httpResponse, never()).addCookie(any(Cookie.class)); verify(httpResponse).setStatusCode(eq(451)); verify(httpResponse).end(eq("The gdpr_consent param prevents cookies from being saved")); verify(metrics).updateUserSyncTcfBlockedMetric(RUBICON); @@ -368,7 +369,7 @@ public void shouldRespondWithBadRequestStatusIfGdprProcessingFailsWithInvalidReq setuidHandler.handle(routingContext); // then - verify(routingContext, never()).addCookie(any(Cookie.class)); + verify(httpResponse, never()).addCookie(any(Cookie.class)); verify(httpResponse).setStatusCode(eq(400)); verify(httpResponse).end(eq("Invalid request format: gdpr exception")); @@ -391,8 +392,8 @@ public void shouldRespondWithInternalServerErrorStatusIfGdprProcessingFailsWithU setuidHandler.handle(routingContext); // then - verify(httpResponse, never()).sendFile(any()); - verify(routingContext, never()).addCookie(any(Cookie.class)); + verify(httpResponse, never()).sendFile(anyString()); + verify(httpResponse, never()).addCookie(any(Cookie.class)); verify(httpResponse).setStatusCode(eq(500)); verify(httpResponse).end(eq("Unexpected setuid processing error: unexpected error TCF")); } @@ -452,11 +453,13 @@ public void shouldPassAccountToPrivacyEnforcementServiceWhenAccountIsNotFound() public void shouldRespondWithCookieFromRequestParam() throws IOException { // given final UidsCookie uidsCookie = emptyUidsCookie(); + final UidsCookie updatedUidsCookie = uidsCookie.updateUid(RUBICON, "J5VLCWQP-26-CWFT"); + given(uidsCookieService.parseFromRequest(any(RoutingContext.class))) .willReturn(uidsCookie); given(uidsCookieService.updateUidsCookie(uidsCookie, RUBICON, "J5VLCWQP-26-CWFT")) - .willReturn(updated(uidsCookie.updateUid(RUBICON, "J5VLCWQP-26-CWFT"))); + .willReturn(updated(updatedUidsCookie)); given(httpRequest.getParam("bidder")).willReturn(RUBICON); given(httpRequest.getParam("uid")).willReturn("J5VLCWQP-26-CWFT"); @@ -465,21 +468,19 @@ public void shouldRespondWithCookieFromRequestParam() throws IOException { setuidHandler.handle(routingContext); // then - verify(routingContext, never()).addCookie(any(Cookie.class)); - final String encodedUidsCookie = getUidsCookie(); - final Uids decodedUids = decodeUids(encodedUidsCookie); - assertThat(decodedUids.getUids()).hasSize(1); - assertThat(decodedUids.getUids().get(RUBICON).getUid()).isEqualTo("J5VLCWQP-26-CWFT"); + verify(httpResponse).addCookie(equalToUidsCookie(updatedUidsCookie)); } @Test public void shouldRespondWithCookieFromRequestParamWhenBidderAndCookieFamilyAreDifferent() throws IOException { // given final UidsCookie uidsCookie = emptyUidsCookie(); + final UidsCookie updatedUidsCookie = uidsCookie.updateUid(ADNXS, "J5VLCWQP-26-CWFT"); + given(uidsCookieService.parseFromRequest(any(RoutingContext.class))) .willReturn(uidsCookie); given(uidsCookieService.updateUidsCookie(uidsCookie, ADNXS, "J5VLCWQP-26-CWFT")) - .willReturn(updated(uidsCookie.updateUid(ADNXS, "J5VLCWQP-26-CWFT"))); + .willReturn(updated(updatedUidsCookie)); given(httpRequest.getParam("bidder")).willReturn(ADNXS); given(httpRequest.getParam("uid")).willReturn("J5VLCWQP-26-CWFT"); @@ -488,21 +489,18 @@ public void shouldRespondWithCookieFromRequestParamWhenBidderAndCookieFamilyAreD setuidHandler.handle(routingContext); // then - verify(routingContext, never()).addCookie(any(Cookie.class)); - final String encodedUidsCookie = getUidsCookie(); - final Uids decodedUids = decodeUids(encodedUidsCookie); - assertThat(decodedUids.getUids()).hasSize(1); - assertThat(decodedUids.getUids().get(ADNXS).getUid()).isEqualTo("J5VLCWQP-26-CWFT"); + verify(httpResponse).addCookie(equalToUidsCookie(updatedUidsCookie)); } @Test - public void shouldSendPixelWhenFParamIsEqualToIWhenTypeIsIframe() { + public void shouldSendPixelWhenFParamIsEqualToIWhenTypeIsIframe() throws JsonProcessingException { // given - given(uidsCookieService.parseFromRequest(any(RoutingContext.class))) - .willReturn(new UidsCookie(Uids.builder().uids(emptyMap()).build(), jacksonMapper)); + final UidsCookie uidsCookie = emptyUidsCookie(); + given(uidsCookieService.parseFromRequest(any(RoutingContext.class))) + .willReturn(uidsCookie); given(uidsCookieService.updateUidsCookie(any(), any(), any())) - .willReturn(updated(emptyUidsCookie())); + .willReturn(updated(uidsCookie)); given(httpRequest.getParam("bidder")).willReturn(RUBICON); given(httpRequest.getParam("f")).willReturn("i"); @@ -512,12 +510,12 @@ public void shouldSendPixelWhenFParamIsEqualToIWhenTypeIsIframe() { setuidHandler.handle(routingContext); // then - verify(routingContext, never()).addCookie(any(Cookie.class)); - verify(httpResponse).sendFile(any()); + verify(httpResponse).sendFile(anyString()); + verify(httpResponse).addCookie(equalToUidsCookie(uidsCookie)); } @Test - public void shouldSendEmptyResponseWhenFParamIsEqualToBWhenTypeIsRedirect() { + public void shouldSendEmptyResponseWhenFParamIsEqualToBWhenTypeIsRedirect() throws JsonProcessingException { // given given(tcfDefinerService.getGdprHostVendorId()).willReturn(null); @@ -551,14 +549,14 @@ public void shouldSendEmptyResponseWhenFParamIsEqualToBWhenTypeIsRedirect() { setuidHandler.handle(routingContext); // then - verify(routingContext, never()).addCookie(any(Cookie.class)); - verify(httpResponse, never()).sendFile(any()); + verify(httpResponse, never()).sendFile(anyString()); + verify(httpResponse).addCookie(equalToUidsCookie(uidsCookie)); verify(httpResponse).putHeader(eq(HttpHeaders.CONTENT_LENGTH), eq("0")); verify(httpResponse).putHeader(eq(HttpHeaders.CONTENT_TYPE), eq(HttpHeaders.TEXT_HTML)); } @Test - public void shouldSendEmptyResponseWhenFParamNotDefinedAndTypeIsIframe() { + public void shouldSendEmptyResponseWhenFParamNotDefinedAndTypeIsIframe() throws JsonProcessingException { // given given(tcfDefinerService.getGdprHostVendorId()).willReturn(null); @@ -591,14 +589,14 @@ public void shouldSendEmptyResponseWhenFParamNotDefinedAndTypeIsIframe() { setuidHandler.handle(routingContext); // then - verify(routingContext, never()).addCookie(any(Cookie.class)); - verify(httpResponse, never()).sendFile(any()); + verify(httpResponse, never()).sendFile(anyString()); + verify(httpResponse).addCookie(equalToUidsCookie(uidsCookie)); verify(httpResponse).putHeader(eq(HttpHeaders.CONTENT_LENGTH), eq("0")); verify(httpResponse).putHeader(eq(HttpHeaders.CONTENT_TYPE), eq(HttpHeaders.TEXT_HTML)); } @Test - public void shouldSendPixelWhenFParamNotDefinedAndTypeIsRedirect() { + public void shouldSendPixelWhenFParamNotDefinedAndTypeIsRedirect() throws JsonProcessingException { // given given(tcfDefinerService.getGdprHostVendorId()).willReturn(null); @@ -631,8 +629,8 @@ public void shouldSendPixelWhenFParamNotDefinedAndTypeIsRedirect() { setuidHandler.handle(routingContext); // then - verify(routingContext, never()).addCookie(any(Cookie.class)); - verify(httpResponse).sendFile(any()); + verify(httpResponse).sendFile(anyString()); + verify(httpResponse).addCookie(equalToUidsCookie(uidsCookie)); } @Test @@ -642,11 +640,12 @@ public void shouldInCookieWithRequestValue() throws IOException { RUBICON, UidWithExpiry.live("J5VLCWQP-26-CWFT"), ADNXS, UidWithExpiry.live("12345")); final UidsCookie uidsCookie = new UidsCookie(Uids.builder().uids(uids).build(), jacksonMapper); + final UidsCookie updatedUidsCookie = uidsCookie.updateUid(RUBICON, "updatedUid"); given(uidsCookieService.parseFromRequest(any(RoutingContext.class))) .willReturn(uidsCookie); given(uidsCookieService.updateUidsCookie(uidsCookie, RUBICON, "updatedUid")) - .willReturn(updated(uidsCookie.updateUid(RUBICON, "updatedUid"))); + .willReturn(updated(updatedUidsCookie)); given(httpRequest.getParam("bidder")).willReturn(RUBICON); given(httpRequest.getParam("uid")).willReturn("updatedUid"); @@ -655,18 +654,12 @@ public void shouldInCookieWithRequestValue() throws IOException { setuidHandler.handle(routingContext); // then - verify(httpResponse).sendFile(any()); - verify(routingContext, never()).addCookie(any(Cookie.class)); - - final String encodedUidsCookie = getUidsCookie(); - final Uids decodedUids = decodeUids(encodedUidsCookie); - assertThat(decodedUids.getUids()).hasSize(2); - assertThat(decodedUids.getUids().get(RUBICON).getUid()).isEqualTo("updatedUid"); - assertThat(decodedUids.getUids().get(ADNXS).getUid()).isEqualTo("12345"); + verify(httpResponse).sendFile(anyString()); + verify(httpResponse).addCookie(equalToUidsCookie(updatedUidsCookie)); } @Test - public void shouldReturnMultipleCookies() throws IOException { + public void shouldReturnMultipleCookies() { // given final Map uids = Map.of( RUBICON, UidWithExpiry.live("J5VLCWQP-26-CWFT"), @@ -694,23 +687,11 @@ public void shouldReturnMultipleCookies() throws IOException { setuidHandler.handle(routingContext); // then - verify(httpResponse).sendFile(any()); - verify(routingContext, never()).addCookie(any(Cookie.class)); - - final Map encodedUidsCookie = httpResponse.headers().getAll("Set-Cookie").stream() - .collect(Collectors.toMap(value -> value.split("=")[0], value -> value.split("=")[1])); - - assertThat(encodedUidsCookie).hasSize(2); - final Uids decodedUids1 = mapper.readValue(Base64.getUrlDecoder() - .decode(encodedUidsCookie.get("uids")), Uids.class); - final Uids decodedUids2 = mapper.readValue(Base64.getUrlDecoder() - .decode(encodedUidsCookie.get("uids2")), Uids.class); - - assertThat(decodedUids1.getUids()).hasSize(1); - assertThat(decodedUids1.getUids().get(RUBICON).getUid()).isEqualTo("updatedUid"); - - assertThat(decodedUids2.getUids()).hasSize(1); - assertThat(decodedUids2.getUids().get(ADNXS).getUid()).isEqualTo("12345"); + verify(httpResponse).sendFile(anyString()); + verify(httpResponse).addCookie( + cookieEqualTo("uids", "eyJ0ZW1wVUlEcyI6eyJydWJpY29uIjp7InVpZCI6InVwZGF0ZWRVaWQifX19")); + verify(httpResponse).addCookie( + cookieEqualTo("uids2", "eyJ0ZW1wVUlEcyI6eyJhZG54cyI6eyJ1aWQiOiIxMjM0NSJ9fX0")); } @Test @@ -720,10 +701,12 @@ public void shouldRespondWithCookieIfUserIsNotInGdprScope() throws IOException { .willReturn(Future.succeededFuture(TcfResponse.of(false, emptyMap(), null))); final UidsCookie uidsCookie = emptyUidsCookie(); + final UidsCookie updatedUidsCookie = uidsCookie.updateUid(RUBICON, "J5VLCWQP-26-CWFT"); + given(uidsCookieService.parseFromRequest(any(RoutingContext.class))) .willReturn(uidsCookie); given(uidsCookieService.updateUidsCookie(uidsCookie, RUBICON, "J5VLCWQP-26-CWFT")) - .willReturn(updated(uidsCookie.updateUid(RUBICON, "J5VLCWQP-26-CWFT"))); + .willReturn(updated(updatedUidsCookie)); given(httpRequest.getParam("bidder")).willReturn(RUBICON); given(httpRequest.getParam("uid")).willReturn("J5VLCWQP-26-CWFT"); @@ -732,13 +715,8 @@ public void shouldRespondWithCookieIfUserIsNotInGdprScope() throws IOException { setuidHandler.handle(routingContext); // then - verify(routingContext, never()).addCookie(any(Cookie.class)); - verify(httpResponse).sendFile(any()); - - final String encodedUidsCookie = getUidsCookie(); - final Uids decodedUids = decodeUids(encodedUidsCookie); - assertThat(decodedUids.getUids()).hasSize(1); - assertThat(decodedUids.getUids().get(RUBICON).getUid()).isEqualTo("J5VLCWQP-26-CWFT"); + verify(httpResponse).addCookie(equalToUidsCookie(updatedUidsCookie)); + verify(httpResponse).sendFile(anyString()); } @Test @@ -761,10 +739,12 @@ public void shouldSkipTcfChecksAndRespondWithCookieIfHostVendorIdNotDefined() th given(tcfDefinerService.getGdprHostVendorId()).willReturn(null); final UidsCookie uidsCookie = emptyUidsCookie(); + final UidsCookie updatedUidsCookie = uidsCookie.updateUid(RUBICON, "J5VLCWQP-26-CWFT"); + given(uidsCookieService.parseFromRequest(any(RoutingContext.class))) .willReturn(uidsCookie); given(uidsCookieService.updateUidsCookie(uidsCookie, RUBICON, "J5VLCWQP-26-CWFT")) - .willReturn(updated(uidsCookie.updateUid(RUBICON, "J5VLCWQP-26-CWFT"))); + .willReturn(updated(updatedUidsCookie)); given(httpRequest.getParam("bidder")).willReturn(RUBICON); given(httpRequest.getParam("uid")).willReturn("J5VLCWQP-26-CWFT"); @@ -774,13 +754,8 @@ public void shouldSkipTcfChecksAndRespondWithCookieIfHostVendorIdNotDefined() th // then verify(tcfDefinerService, never()).resultForVendorIds(anySet(), any()); - verify(routingContext, never()).addCookie(any(Cookie.class)); - verify(httpResponse).sendFile(any()); - - final String encodedUidsCookie = getUidsCookie(); - final Uids decodedUids = decodeUids(encodedUidsCookie); - assertThat(decodedUids.getUids()).hasSize(1); - assertThat(decodedUids.getUids().get(RUBICON).getUid()).isEqualTo("J5VLCWQP-26-CWFT"); + verify(httpResponse).sendFile(anyString()); + verify(httpResponse).addCookie(equalToUidsCookie(updatedUidsCookie)); } @Test @@ -877,13 +852,15 @@ public void shouldThrowExceptionInCaseOfBaseBidderCookieFamilyNameDuplicates() { assertThat(values).containsExactlyInAnyOrder("audienceNetwork", "rubicon"); } - private String getUidsCookie() { - return httpResponse.headers().get("Set-Cookie"); + private static Cookie equalToUidsCookie(UidsCookie uidsCookie) throws JsonProcessingException { + final String value = Base64.getUrlEncoder() + .encodeToString(mapper.writeValueAsBytes(uidsCookie.getCookieUids())); + + return cookieEqualTo("uids", value); } - private static Uids decodeUids(String value) throws IOException { - final String uids = value.substring(5).split(";")[0]; - return mapper.readValue(Base64.getUrlDecoder().decode(uids), Uids.class); + private static Cookie cookieEqualTo(String name, String value) { + return argThat(cookie -> cookie.getName().equals(name) && cookie.getValue().equals(value)); } private SetuidEvent captureSetuidEvent() { diff --git a/src/test/java/org/prebid/server/hooks/execution/HookStageExecutorTest.java b/src/test/java/org/prebid/server/hooks/execution/HookStageExecutorTest.java index 24210ae6b72..6e760120fbf 100644 --- a/src/test/java/org/prebid/server/hooks/execution/HookStageExecutorTest.java +++ b/src/test/java/org/prebid/server/hooks/execution/HookStageExecutorTest.java @@ -98,6 +98,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.TimeUnit; import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.Predicate; @@ -149,8 +150,9 @@ public void setUp() { } @AfterEach - public void tearDown(VertxTestContext context) { - vertx.close(context.succeedingThenComplete()); + public void tearDown(VertxTestContext context) throws InterruptedException { + vertx.close().onComplete(context.succeedingThenComplete()); + context.awaitCompletion(1000, TimeUnit.MILLISECONDS); } @Test diff --git a/src/test/java/org/prebid/server/log/ConditionalLoggerTest.java b/src/test/java/org/prebid/server/log/ConditionalLoggerTest.java index c1e6c56cf13..8fd380d9df1 100644 --- a/src/test/java/org/prebid/server/log/ConditionalLoggerTest.java +++ b/src/test/java/org/prebid/server/log/ConditionalLoggerTest.java @@ -36,8 +36,9 @@ public void setUp() { } @AfterEach - public void tearDown(VertxTestContext context) { - vertx.close(context.succeedingThenComplete()); + public void tearDown(VertxTestContext context) throws InterruptedException { + vertx.close().onComplete(context.succeedingThenComplete()); + context.awaitCompletion(1000, TimeUnit.MILLISECONDS); } @Test diff --git a/src/test/java/org/prebid/server/privacy/gdpr/vendorlist/VendorListServiceTest.java b/src/test/java/org/prebid/server/privacy/gdpr/vendorlist/VendorListServiceTest.java index 82ef85987d2..6476487c340 100644 --- a/src/test/java/org/prebid/server/privacy/gdpr/vendorlist/VendorListServiceTest.java +++ b/src/test/java/org/prebid/server/privacy/gdpr/vendorlist/VendorListServiceTest.java @@ -2,7 +2,6 @@ import com.fasterxml.jackson.core.JsonProcessingException; import io.vertx.core.Future; -import io.vertx.core.Handler; import io.vertx.core.Vertx; import io.vertx.core.buffer.Buffer; import io.vertx.core.file.FileSystem; @@ -12,7 +11,6 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import org.mockito.stubbing.Answer; import org.prebid.server.VertxTest; import org.prebid.server.bidder.BidderCatalog; import org.prebid.server.exception.PreBidException; @@ -293,7 +291,7 @@ public void shouldNotPerformHttpRequestIfVendorListNotFoundAndFetchNotAllowed() // then verify(httpClient, never()).get(anyString(), anyLong()); - verify(fileSystem, never()).writeFile(any(), any(), any()); + verify(fileSystem, never()).writeFile(any(), any()); } @Test @@ -306,7 +304,7 @@ public void shouldNotAskToSaveFileIfReadingHttpResponseFails() { // then verify(httpClient).get(anyString(), anyLong()); - verify(fileSystem, never()).writeFile(any(), any(), any()); + verify(fileSystem, never()).writeFile(any(), any()); } @Test @@ -319,7 +317,7 @@ public void shouldNotAskToSaveFileIfResponseCodeIsNot200() { // then verify(httpClient).get(anyString(), anyLong()); - verify(fileSystem, never()).writeFile(any(), any(), any()); + verify(fileSystem, never()).writeFile(any(), any()); } @Test @@ -332,7 +330,7 @@ public void shouldNotAskToSaveFileIfResponseBodyCouldNotBeParsed() { // then verify(httpClient).get(anyString(), anyLong()); - verify(fileSystem, never()).writeFile(any(), any(), any()); + verify(fileSystem, never()).writeFile(any(), any()); } @Test @@ -346,7 +344,7 @@ public void shouldNotAskToSaveFileIfFetchedVendorListHasInvalidVendorListVersion // then verify(httpClient).get(anyString(), anyLong()); - verify(fileSystem, never()).writeFile(any(), any(), any()); + verify(fileSystem, never()).writeFile(any(), any()); } @Test @@ -360,7 +358,7 @@ public void shouldNotAskToSaveFileIfFetchedVendorListHasInvalidLastUpdated() thr // then verify(httpClient).get(anyString(), anyLong()); - verify(fileSystem, never()).writeFile(any(), any(), any()); + verify(fileSystem, never()).writeFile(any(), any()); } @Test @@ -374,7 +372,7 @@ public void shouldNotAskToSaveFileIfFetchedVendorListHasNoVendors() throws JsonP // then verify(httpClient).get(anyString(), anyLong()); - verify(fileSystem, never()).writeFile(any(), any(), any()); + verify(fileSystem, never()).writeFile(any(), any()); } @Test @@ -388,7 +386,7 @@ public void shouldNotAskToSaveFileIfFetchedVendorListHasEmptyVendors() throws Js // then verify(httpClient).get(anyString(), anyLong()); - verify(fileSystem, never()).writeFile(any(), any(), any()); + verify(fileSystem, never()).writeFile(any(), any()); } @Test @@ -402,7 +400,7 @@ public void shouldNotAskToSaveFileIfFetchedVendorListHasAtLeastOneInvalidVendor( // then verify(httpClient).get(anyString(), anyLong()); - verify(fileSystem, never()).writeFile(any(), any(), any()); + verify(fileSystem, never()).writeFile(any(), any()); } // File system related tests @@ -419,7 +417,7 @@ public void shouldSaveFileWithExpectedPathAndContentIfVendorListNotFound() throw target.forVersion(1); // then - verify(fileSystem).writeFile(eq(filePath), eq(Buffer.buffer(vendorListAsString)), any()); + verify(fileSystem).writeFile(eq(filePath), eq(Buffer.buffer(vendorListAsString))); } // In-memory cache related tests @@ -456,8 +454,7 @@ public void shouldReturnVendorListFromCache() throws JsonProcessingException { // given givenHttpClientReturnsResponse(200, mapper.writeValueAsString(givenVendorList())); - given(fileSystem.writeFile(anyString(), any(), any())) - .willAnswer(withSelfAndPassObjectToHandler(Future.succeededFuture())); + given(fileSystem.writeFile(anyString(), any())).willReturn(Future.succeededFuture()); // when target.forVersion(1); // populate cache @@ -504,8 +501,8 @@ public void shouldKeepPurposesForAllVendors() throws JsonProcessingException { final VendorList vendorList = VendorList.of(1, new Date(), idToVendor); givenHttpClientReturnsResponse(200, mapper.writeValueAsString(vendorList)); - given(fileSystem.writeFile(anyString(), any(), any())) - .willAnswer(withSelfAndPassObjectToHandler(Future.succeededFuture())); + given(fileSystem.writeFile(anyString(), any())) + .willReturn(Future.succeededFuture()); // when target.forVersion(1); // populate cache @@ -572,8 +569,7 @@ public void shouldIncrementVendorListErrorMetricWhenFileIsNotSaved() throws Json // given givenHttpClientReturnsResponse(200, mapper.writeValueAsString(givenVendorList())); - given(fileSystem.writeFile(anyString(), any(), any())) - .willAnswer(withSelfAndPassObjectToHandler(Future.failedFuture("error"))); + given(fileSystem.writeFile(anyString(), any())).willReturn(Future.failedFuture("error")); // when target.forVersion(1); @@ -587,8 +583,7 @@ public void shouldIncrementVendorListOkMetric() throws JsonProcessingException { // given givenHttpClientReturnsResponse(200, mapper.writeValueAsString(givenVendorList())); - given(fileSystem.writeFile(anyString(), any(), any())) - .willAnswer(withSelfAndPassObjectToHandler(Future.succeededFuture())); + given(fileSystem.writeFile(anyString(), any())).willReturn(Future.succeededFuture()); // when target.forVersion(1); @@ -635,13 +630,4 @@ private void givenHttpClientProducesException(Throwable throwable) { given(httpClient.get(anyString(), anyLong())) .willReturn(Future.failedFuture(throwable)); } - - @SuppressWarnings("unchecked") - private static Answer withSelfAndPassObjectToHandler(T obj) { - return inv -> { - // invoking handler right away passing mock to it - ((Handler) inv.getArgument(2)).handle(obj); - return inv.getMock(); - }; - } } diff --git a/src/test/java/org/prebid/server/settings/S3ApplicationSettingsTest.java b/src/test/java/org/prebid/server/settings/S3ApplicationSettingsTest.java index 13fda67839e..1ffc9052637 100644 --- a/src/test/java/org/prebid/server/settings/S3ApplicationSettingsTest.java +++ b/src/test/java/org/prebid/server/settings/S3ApplicationSettingsTest.java @@ -27,6 +27,7 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import static java.util.Collections.emptySet; @@ -73,8 +74,9 @@ public void setUp() { } @AfterEach - public void tearDown(VertxTestContext context) { - vertx.close(context.succeedingThenComplete()); + public void tearDown(VertxTestContext context) throws InterruptedException { + vertx.close().onComplete(context.succeedingThenComplete()); + context.awaitCompletion(1000, TimeUnit.MILLISECONDS); } @Test diff --git a/src/test/java/org/prebid/server/settings/service/S3PeriodicRefreshServiceTest.java b/src/test/java/org/prebid/server/settings/service/S3PeriodicRefreshServiceTest.java index b46587f01a8..b340b7f0ca4 100644 --- a/src/test/java/org/prebid/server/settings/service/S3PeriodicRefreshServiceTest.java +++ b/src/test/java/org/prebid/server/settings/service/S3PeriodicRefreshServiceTest.java @@ -26,6 +26,7 @@ import java.time.Clock; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; import static java.util.Collections.singletonList; import static java.util.Collections.singletonMap; @@ -95,8 +96,9 @@ public void setUp() { } @AfterEach - public void tearDown(VertxTestContext context) { - vertx.close(context.succeedingThenComplete()); + public void tearDown(VertxTestContext context) throws InterruptedException { + vertx.close().onComplete(context.succeedingThenComplete()); + context.awaitCompletion(1000, TimeUnit.MILLISECONDS); } @Test diff --git a/src/test/java/org/prebid/server/util/HttpUtilTest.java b/src/test/java/org/prebid/server/util/HttpUtilTest.java index 7926e05ed46..e02a8bf6577 100644 --- a/src/test/java/org/prebid/server/util/HttpUtilTest.java +++ b/src/test/java/org/prebid/server/util/HttpUtilTest.java @@ -3,6 +3,7 @@ import io.vertx.core.MultiMap; import io.vertx.core.http.Cookie; import io.vertx.core.http.HttpHeaders; +import io.vertx.core.http.HttpServerRequest; import io.vertx.core.http.HttpServerResponse; import io.vertx.ext.web.RoutingContext; import org.junit.jupiter.api.BeforeEach; @@ -16,7 +17,7 @@ import java.util.Map; import java.util.function.Consumer; -import static java.util.Collections.singletonMap; +import static java.util.Collections.singleton; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; import static org.assertj.core.api.Assertions.entry; @@ -35,11 +36,14 @@ public class HttpUtilTest { @Mock(strictness = LENIENT) private RoutingContext routingContext; + @Mock(strictness = LENIENT) + private HttpServerRequest httpRequest; @Mock private HttpServerResponse httpResponse; @BeforeEach public void setUp() { + given(routingContext.request()).willReturn(httpRequest); given(routingContext.response()).willReturn(httpResponse); } @@ -131,7 +135,7 @@ public void getHostFromUrlShouldReturnNullIfUrlIsMalformed() { @Test public void cookiesAsMapShouldReturnExpectedResult() { // given - given(routingContext.cookieMap()).willReturn(singletonMap("name", Cookie.cookie("name", "value"))); + given(httpRequest.cookies()).willReturn(singleton(Cookie.cookie("name", "value"))); // when final Map cookies = HttpUtil.cookiesAsMap(routingContext); diff --git a/src/test/java/org/prebid/server/vertx/CircuitBreakerTest.java b/src/test/java/org/prebid/server/vertx/CircuitBreakerTest.java deleted file mode 100644 index 12984132eae..00000000000 --- a/src/test/java/org/prebid/server/vertx/CircuitBreakerTest.java +++ /dev/null @@ -1,164 +0,0 @@ -package org.prebid.server.vertx; - -import io.vertx.core.Future; -import io.vertx.core.Handler; -import io.vertx.core.Promise; -import io.vertx.core.Vertx; -import io.vertx.junit5.VertxExtension; -import io.vertx.junit5.VertxTestContext; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.junit.jupiter.MockitoExtension; - -import java.time.Clock; -import java.time.Instant; -import java.time.ZoneId; - -import static org.assertj.core.api.Assertions.assertThat; - -@ExtendWith(MockitoExtension.class) -@ExtendWith(VertxExtension.class) -public class CircuitBreakerTest { - - private Vertx vertx; - - private Clock clock; - - private CircuitBreaker circuitBreaker; - - @BeforeEach - public void setUp() { - vertx = Vertx.vertx(); - clock = Clock.fixed(Instant.now(), ZoneId.systemDefault()); - circuitBreaker = new CircuitBreaker("name", vertx, 1, 100L, 200L, clock); - } - - @AfterEach - public void tearDown(VertxTestContext context) { - vertx.close(context.succeedingThenComplete()); - } - - @Test - public void executeShouldSucceedsIfOperationSucceeds() { - // when - final Future future = executeWithSuccess("value"); - - // then - assertThat(future.succeeded()).isTrue(); - assertThat(future.result()).isEqualTo("value"); - } - - @Test - public void executeShouldFailsIfCircuitIsClosedAndOperationFails() { - // when - final Future future = executeWithFail("exception"); - - // then - assertThat(future.failed()).isTrue(); - assertThat(future.cause()).isInstanceOf(RuntimeException.class).hasMessage("exception"); - } - - @Test - public void executeShouldFailsIfCircuitIsHalfOpenedAndOperationFailsAndClosingTimeIsNotPassedBy() { - // when - final Future future1 = executeWithFail("exception1"); - final Future future2 = executeWithFail(null); - - // then - assertThat(future1.failed()).isTrue(); - assertThat(future1.cause()).isInstanceOf(RuntimeException.class).hasMessage("exception1"); - - assertThat(future2.failed()).isTrue(); - assertThat(future2.cause()).isInstanceOf(RuntimeException.class).hasMessage("open circuit"); - } - - @Test - public void executeShouldFailsIfCircuitIsHalfOpenedAndOperationFails() { - // when - final Future future1 = executeWithFail("exception1"); - final Future future2 = executeWithFail(null); - waitForClosingInterval(); - final Future future3 = executeWithFail("exception3"); - - // then - assertThat(future1.failed()).isTrue(); - assertThat(future1.cause()).isInstanceOf(RuntimeException.class).hasMessage("exception1"); - - assertThat(future2.failed()).isTrue(); - assertThat(future2.cause()).isInstanceOf(RuntimeException.class).hasMessage("open circuit"); - - assertThat(future3.failed()).isTrue(); - assertThat(future3.cause()).isInstanceOf(RuntimeException.class).hasMessage("exception3"); - } - - @Test - public void executeShouldSucceedsIfCircuitIsHalfOpenedAndOperationSucceeds() { - // when - final Future future1 = executeWithFail("exception1"); - final Future future2 = executeWithFail("exception2"); - waitForClosingInterval(); - final Future future3 = executeWithSuccess("value after half-open"); - - // then - assertThat(future1.failed()).isTrue(); - assertThat(future1.cause()).isInstanceOf(RuntimeException.class).hasMessage("exception1"); - - assertThat(future2.failed()).isTrue(); - assertThat(future2.cause()).isInstanceOf(RuntimeException.class).hasMessage("open circuit"); - - assertThat(future3.succeeded()).isTrue(); - assertThat(future3.result()).isEqualTo("value after half-open"); - } - - @Test - public void executeShouldFailsWithOriginalExceptionIfOpeningIntervalExceeds() { - // given - circuitBreaker = new CircuitBreaker("name", vertx, 2, 100L, 200L, clock); - - // when - final Future future1 = executeWithFail("exception1"); - waitForOpeningInterval(); - final Future future2 = executeWithFail("exception2"); - - // then - assertThat(future1.failed()).isTrue(); - assertThat(future1.cause()).isInstanceOf(RuntimeException.class).hasMessage("exception1"); - - assertThat(future2.failed()).isTrue(); - assertThat(future2.cause()).isInstanceOf(RuntimeException.class).hasMessage("exception2"); - } - - private Future executeWithSuccess(String result) { - return execute(operationPromise -> operationPromise.complete(result)); - } - - private Future executeWithFail(String errorMessage) { - return execute(operationPromise -> operationPromise.fail(new RuntimeException(errorMessage))); - } - - private Future execute(Handler> handler) { - final Future future = circuitBreaker.execute(handler); - - final Promise promise = Promise.promise(); - future.onComplete(ar -> promise.complete()); - promise.future().toCompletionStage().toCompletableFuture().join(); - - return future; - } - - private void waitForOpeningInterval() { - waitForInterval(150L); - } - - private void waitForClosingInterval() { - waitForInterval(250L); - } - - private void waitForInterval(long timeout) { - final Promise promise = Promise.promise(); - vertx.setTimer(timeout, id -> promise.complete()); - promise.future().toCompletionStage().toCompletableFuture().join(); - } -} diff --git a/src/test/java/org/prebid/server/vertx/CloseableAdapterTest.java b/src/test/java/org/prebid/server/vertx/CloseableAdapterTest.java index 5b8cafd5251..dc3284b4cf0 100644 --- a/src/test/java/org/prebid/server/vertx/CloseableAdapterTest.java +++ b/src/test/java/org/prebid/server/vertx/CloseableAdapterTest.java @@ -1,6 +1,5 @@ package org.prebid.server.vertx; -import io.vertx.core.Future; import io.vertx.core.Promise; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -11,8 +10,6 @@ import java.io.IOException; import static org.assertj.core.api.Assertions.assertThatNullPointerException; -import static org.mockito.ArgumentMatchers.argThat; -import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.willThrow; import static org.mockito.Mockito.verify; @@ -35,7 +32,7 @@ public void closeShouldInvokeHandlerWithSuccededFuture() { new CloseableAdapter(closeable).close(completionPromise); // then - verify(completionPromise).handle(eq(Future.succeededFuture())); + verify(completionPromise).succeed(); } @Test @@ -48,6 +45,6 @@ public void closeShouldInvokeHandlerWithFailedFutureIfIOExceptionThrown() throws new CloseableAdapter(closeable).close(completionPromise); // then - verify(completionPromise).handle(argThat(future -> future.failed() && future.cause() == exception)); + verify(completionPromise).fail(exception); } } diff --git a/src/test/java/org/prebid/server/vertx/database/CircuitBreakerSecuredDatabaseClientTest.java b/src/test/java/org/prebid/server/vertx/database/CircuitBreakerSecuredDatabaseClientTest.java index abc2d8af479..f91edaa1db3 100644 --- a/src/test/java/org/prebid/server/vertx/database/CircuitBreakerSecuredDatabaseClientTest.java +++ b/src/test/java/org/prebid/server/vertx/database/CircuitBreakerSecuredDatabaseClientTest.java @@ -22,6 +22,7 @@ import java.time.Instant; import java.time.ZoneId; import java.util.List; +import java.util.concurrent.TimeUnit; import java.util.function.BooleanSupplier; import static java.util.Arrays.asList; @@ -40,7 +41,6 @@ public class CircuitBreakerSecuredDatabaseClientTest { private Vertx vertx; - private Clock clock; @Mock private DatabaseClient wrappedDatabaseClient; @Mock @@ -53,15 +53,15 @@ public class CircuitBreakerSecuredDatabaseClientTest { @BeforeEach public void setUp() { vertx = Vertx.vertx(); - clock = Clock.fixed(Instant.now(), ZoneId.systemDefault()); - timeout = new TimeoutFactory(clock).create(500L); + timeout = new TimeoutFactory(Clock.fixed(Instant.now(), ZoneId.systemDefault())).create(500L); - target = new CircuitBreakerSecuredDatabaseClient(vertx, wrappedDatabaseClient, metrics, 1, 100L, 200L, clock); + target = new CircuitBreakerSecuredDatabaseClient(vertx, wrappedDatabaseClient, metrics, 1, 1000L, 200L); } @AfterEach - public void tearDown(VertxTestContext context) { - vertx.close(context.succeedingThenComplete()); + public void tearDown(VertxTestContext context) throws InterruptedException { + vertx.close().onComplete(context.succeedingThenComplete()); + context.awaitCompletion(1000, TimeUnit.MILLISECONDS); } @Test @@ -184,7 +184,7 @@ public void executeQueryShouldReturnResultIfCircuitIsHalfOpenedAndQuerySucceeded @Test public void executeQueryShouldFailsWithOriginalExceptionIfOpeningIntervalExceeds(VertxTestContext context) { // given - target = new CircuitBreakerSecuredDatabaseClient(vertx, wrappedDatabaseClient, metrics, 2, 100L, 200L, clock); + target = new CircuitBreakerSecuredDatabaseClient(vertx, wrappedDatabaseClient, metrics, 2, 100L, 200L); givenExecuteQueryReturning(asList( Future.failedFuture(new RuntimeException("exception1")), diff --git a/src/test/java/org/prebid/server/vertx/httpclient/BasicHttpClientTest.java b/src/test/java/org/prebid/server/vertx/httpclient/BasicHttpClientTest.java index 61618b362ea..3c3ab67bf39 100644 --- a/src/test/java/org/prebid/server/vertx/httpclient/BasicHttpClientTest.java +++ b/src/test/java/org/prebid/server/vertx/httpclient/BasicHttpClientTest.java @@ -40,6 +40,8 @@ @ExtendWith(VertxExtension.class) public class BasicHttpClientTest { + private static final String CRLF = "\r\n"; + @Mock private Vertx vertx; @Mock(strictness = LENIENT) @@ -203,12 +205,12 @@ private static void startServer(int port, long entireResponseDelay, long bodyRes } out.write("HTTP/1.1 200 OK"); - out.newLine(); + out.write(CRLF); out.write("Content-Length: 6"); // set body size greater then length of "start" word - out.newLine(); + out.write(CRLF); - out.newLine(); + out.write(CRLF); out.write("start"); out.flush(); diff --git a/src/test/java/org/prebid/server/vertx/httpclient/CircuitBreakerSecuredHttpClientTest.java b/src/test/java/org/prebid/server/vertx/httpclient/CircuitBreakerSecuredHttpClientTest.java index 75f2b8c6a52..629446d6a4a 100644 --- a/src/test/java/org/prebid/server/vertx/httpclient/CircuitBreakerSecuredHttpClientTest.java +++ b/src/test/java/org/prebid/server/vertx/httpclient/CircuitBreakerSecuredHttpClientTest.java @@ -18,9 +18,7 @@ import org.prebid.server.metric.Metrics; import org.prebid.server.vertx.httpclient.model.HttpClientResponse; -import java.time.Clock; -import java.time.Instant; -import java.time.ZoneId; +import java.util.concurrent.TimeUnit; import java.util.function.BooleanSupplier; import java.util.function.LongSupplier; @@ -40,7 +38,6 @@ public class CircuitBreakerSecuredHttpClientTest { private Vertx vertx; - private Clock clock; @Mock private HttpClient wrappedHttpClient; @Mock @@ -51,13 +48,13 @@ public class CircuitBreakerSecuredHttpClientTest { @BeforeEach public void setUp() { vertx = Vertx.vertx(); - clock = Clock.fixed(Instant.now(), ZoneId.systemDefault()); - httpClient = new CircuitBreakerSecuredHttpClient(vertx, wrappedHttpClient, metrics, 1, 100L, 200L, 24, clock); + httpClient = new CircuitBreakerSecuredHttpClient(vertx, wrappedHttpClient, metrics, 1, 1000L, 200L, 24); } @AfterEach - public void tearDown(VertxTestContext context) { - vertx.close(context.succeedingThenComplete()); + public void tearDown(VertxTestContext context) throws InterruptedException { + vertx.close().onComplete(context.succeedingThenComplete()); + context.awaitCompletion(1000, TimeUnit.MILLISECONDS); } @Test @@ -169,7 +166,7 @@ public void requestShouldSucceedIfCircuitIsHalfOpenedAndWrappedHttpClientSucceed @Test public void requestShouldFailWithOriginalExceptionIfOpeningIntervalExceeds() { // given - httpClient = new CircuitBreakerSecuredHttpClient(vertx, wrappedHttpClient, metrics, 2, 100L, 200L, 24, clock); + httpClient = new CircuitBreakerSecuredHttpClient(vertx, wrappedHttpClient, metrics, 2, 100L, 200L, 24); givenHttpClientReturning(new RuntimeException("exception1"), new RuntimeException("exception2")); @@ -192,6 +189,8 @@ public void requestShouldFailWithOriginalExceptionIfOpeningIntervalExceeds() { @Test public void circuitBreakerNumberGaugeShouldReportActualNumber() { // when + givenHttpClientReturning(new RuntimeException("exception")); + doRequest(); // then