Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 7 additions & 24 deletions src/main/java/org/prebid/server/cookie/UidsCookieService.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,6 @@ public class UidsCookieService {
private static final int MIN_NUMBER_OF_UID_COOKIES = 1;
private static final int MAX_NUMBER_OF_UID_COOKIES = 30;

// {"tempUIDs":{},"optout":false}
private static final int TEMP_UIDS_BASE64_BYTES = "eyJ0ZW1wVUlEcyI6e30sIm9wdG91dCI6ZmFsc2V9".length();
// "":{"uid":"","expires":"1970-01-01T00:00:00.000000000Z"},
private static final int UID_BASE64_BYTES = ("IiI6eyJ1aWQiOiIiLCJleHBpcmVzI"
+ "joiMTk3MC0wMS0wMVQwMDowMDowMC4wMDAwMDAwMDBaIn0s").length();

private final String optOutCookieName;
private final String optOutCookieValue;
private final String hostCookieFamily;
Expand Down Expand Up @@ -287,30 +281,27 @@ public List<Cookie> splitUidsIntoCookies(UidsCookie uidsCookie) {
final Iterator<String> cookieFamilies = cookieFamilyNamesByDescPriorityAndExpiration(uidsCookie);
final List<Cookie> splitCookies = new ArrayList<>();

final int staticCookieDataBytes = makeCookie(COOKIE_NAME, StringUtils.EMPTY, ttlSeconds).encode().length();

final int cookieSchemaSize = UidsCookieSize.schemaSize(makeCookie(COOKIE_NAME, StringUtils.EMPTY, ttlSeconds));
String nextCookieFamily = null;
for (int i = 0; i < numberOfUidCookies; i++) {
final int digits = i < 10 ? Integer.signum(i) : 2;
final UidsCookieSize uidsCookieSize = new UidsCookieSize(cookieSchemaSize + digits, maxCookieSizeBytes);

for (int uidsIndex = 0; uidsIndex < numberOfUidCookies; uidsIndex++) {
int actualCookieSize = staticCookieDataBytes + TEMP_UIDS_BASE64_BYTES;
final Map<String, UidWithExpiry> tempUids = new HashMap<>();

while (nextCookieFamily != null || cookieFamilies.hasNext()) {
nextCookieFamily = nextCookieFamily == null ? cookieFamilies.next() : nextCookieFamily;

final UidWithExpiry uidWithExpiry = uids.get(nextCookieFamily);
actualCookieSize += UID_BASE64_BYTES
+ calculateCookieSize(uidsIndex, nextCookieFamily, uidWithExpiry.getUid());

if (maxCookieSizeBytes > 0 && actualCookieSize > maxCookieSizeBytes) {
uidsCookieSize.addUid(nextCookieFamily, uidWithExpiry.getUid());
if (!uidsCookieSize.isValid()) {
break;
}

tempUids.put(nextCookieFamily, uidWithExpiry);
nextCookieFamily = null;
}

final String uidsName = uidsIndex == 0 ? COOKIE_NAME : COOKIE_NAME_FORMAT.formatted(uidsIndex + 1);
final String uidsName = i == 0 ? COOKIE_NAME : COOKIE_NAME_FORMAT.formatted(i + 1);

if (tempUids.isEmpty()) {
splitCookies.add(expiredCookie(uidsName));
Expand All @@ -330,14 +321,6 @@ public List<Cookie> splitUidsIntoCookies(UidsCookie uidsCookie) {
return splitCookies;
}

private static int calculateCookieSize(int uidsIndex, String cookieFamily, String uid) {
final int approximateBase64CookieFamilySize = (int) Math.ceil(cookieFamily.length() * 1.33);
final int approximateBase64UidSize = (int) Math.ceil(uid.length() * 1.33);
final int uidsIndexSize = uidsIndex == 0 ? 0 : 2;

return uidsIndexSize + approximateBase64CookieFamilySize + approximateBase64UidSize;
}

private Iterator<String> cookieFamilyNamesByDescPriorityAndExpiration(UidsCookie uidsCookie) {
return uidsCookie.getCookieUids().getUids().entrySet().stream()
.sorted(this::compareCookieFamilyNames)
Expand Down
73 changes: 73 additions & 0 deletions src/main/java/org/prebid/server/cookie/UidsCookieSize.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package org.prebid.server.cookie;

import com.fasterxml.jackson.core.JsonProcessingException;
import io.vertx.core.http.Cookie;
import org.apache.commons.lang3.StringUtils;
import org.prebid.server.json.ObjectMapperProvider;

import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;

public class UidsCookieSize {

// {"tempUIDs":{},"optout":false}
private static final int TEMP_UIDS_BASE64_BYTES = "eyJ0ZW1wVUlEcyI6e30sIm9wdG91dCI6ZmFsc2V9".length();
private static final int UID_TEMPLATE_BYTES;

static {
try {
UID_TEMPLATE_BYTES = "\"\":{\"uid\":\"\",\"expires\":\"%s\"},"
.formatted(ObjectMapperProvider.mapper().writeValueAsString(
ZonedDateTime.ofInstant(Instant.ofEpochSecond(0, 1), ZoneId.of("UTC"))))
.length();
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}

private final int cookieSchemaSize;
private final int maxSize;
private int encodedUidsSize;

public UidsCookieSize(int cookieSchemaSize, int maxSize) {
this.cookieSchemaSize = cookieSchemaSize;
this.maxSize = maxSize;

encodedUidsSize = 0;
}

public static int schemaSize(Cookie cookieSchema) {
return cookieSchema.setValue(StringUtils.EMPTY).encode().length();
}

public boolean isValid() {
return maxSize <= 0 || totalSize() <= maxSize;
}

public int totalSize() {
return cookieSchemaSize
+ TEMP_UIDS_BASE64_BYTES
+ Base64Size.base64Size(encodedUidsSize);
}

public void addUid(String cookieFamily, String uid) {
final int uidSize = UID_TEMPLATE_BYTES + cookieFamily.length() + uid.length();
encodedUidsSize = Base64Size.encodeSize(Base64Size.decodeSize(encodedUidsSize) + uidSize);
}

private static class Base64Size {

public static int encodeSize(int size) {
return size / 3 * 4 + size % 3;
}

public static int decodeSize(int encodedSize) {
return encodedSize / 4 * 3 + encodedSize % 4;
}

private static int base64Size(int encodedSize) {
return (encodedSize & -4) + 4 * Integer.signum(encodedSize % 4);
}
}
}