diff --git a/marklogic-client-api/src/main/java/com/marklogic/client/ClientCookie.java b/marklogic-client-api/src/main/java/com/marklogic/client/ClientCookie.java new file mode 100644 index 000000000..810b4bbb4 --- /dev/null +++ b/marklogic-client-api/src/main/java/com/marklogic/client/ClientCookie.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2010-2025 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved. + */ +package com.marklogic.client; + +import java.util.concurrent.TimeUnit; + +/** + * ClientCookie is a wrapper around the Cookie implementation so that the + * underlying implementation can be changed. + * + */ +public class ClientCookie { + + private final String name; + private final String value; + private final long expiresAt; + private final String domain; + private final String path; + private final boolean secure; + + public ClientCookie(String name, String value, long expiresAt, String domain, String path, boolean secure) { + this.name = name; + this.value = value; + this.expiresAt = expiresAt; + this.domain = domain; + this.path = path; + this.secure = secure; + } + + public boolean isSecure() { + return secure; + } + + public String getPath() { + return path; + } + + public String getDomain() { + return domain; + } + + public long expiresAt() { + return expiresAt; + } + + public String getName() { + return name; + } + + public int getMaxAge() { + return (int) TimeUnit.MILLISECONDS.toSeconds(expiresAt - System.currentTimeMillis()); + } + + public String getValue() { + return value; + } +} diff --git a/marklogic-client-api/src/main/java/com/marklogic/client/Transaction.java b/marklogic-client-api/src/main/java/com/marklogic/client/Transaction.java index 578743845..d98071bef 100644 --- a/marklogic-client-api/src/main/java/com/marklogic/client/Transaction.java +++ b/marklogic-client-api/src/main/java/com/marklogic/client/Transaction.java @@ -3,7 +3,6 @@ */ package com.marklogic.client; -import com.marklogic.client.impl.ClientCookie; import com.marklogic.client.io.marker.StructureReadHandle; import java.util.List; diff --git a/marklogic-client-api/src/main/java/com/marklogic/client/impl/ClientCookie.java b/marklogic-client-api/src/main/java/com/marklogic/client/impl/ClientCookie.java deleted file mode 100644 index 48735bd98..000000000 --- a/marklogic-client-api/src/main/java/com/marklogic/client/impl/ClientCookie.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (c) 2010-2025 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved. - */ -package com.marklogic.client.impl; - -import java.util.concurrent.TimeUnit; - -import okhttp3.Cookie; -import okhttp3.HttpUrl; - -/** - * ClientCookie is a wrapper around the Cookie implementation so that the - * underlying implementation can be changed. - * - */ -public class ClientCookie { - Cookie cookie; - - ClientCookie(String name, String value, long expiresAt, String domain, String path, - boolean secure) { - Cookie.Builder cookieBldr = new Cookie.Builder() - .domain(domain) - .path(path) - .name(name) - .value(value) - .expiresAt(expiresAt); - if ( secure == true ) cookieBldr = cookieBldr.secure(); - this.cookie = cookieBldr.build(); - } - - public ClientCookie(ClientCookie cookie) { - this(cookie.getName(), cookie.getValue(), cookie.expiresAt(), cookie.getDomain(), cookie.getPath(), - cookie.isSecure()); - } - - public boolean isSecure() { - return cookie.secure(); - } - - public String getPath() { - return cookie.path(); - } - - public String getDomain() { - return cookie.domain(); - } - - public long expiresAt() { - return cookie.expiresAt(); - } - - public String getName() { - return cookie.name(); - } - - public int getMaxAge() { - return (int) TimeUnit.MILLISECONDS.toSeconds(cookie.expiresAt() - System.currentTimeMillis()); - } - public String getValue() { - return cookie.value(); - } - - public static ClientCookie parse(HttpUrl url, String setCookie) { - Cookie cookie = Cookie.parse(url, setCookie); - if(cookie == null) throw new IllegalStateException(setCookie + "is not a well-formed cookie"); - return new ClientCookie(cookie.name(), cookie.value(), cookie.expiresAt(), cookie.domain(), cookie.path(), - cookie.secure()); - } - - public String toString() { - return cookie.toString(); - } -} diff --git a/marklogic-client-api/src/main/java/com/marklogic/client/impl/OkHttpServices.java b/marklogic-client-api/src/main/java/com/marklogic/client/impl/OkHttpServices.java index 29017c2c2..4c8ecf904 100644 --- a/marklogic-client-api/src/main/java/com/marklogic/client/impl/OkHttpServices.java +++ b/marklogic-client-api/src/main/java/com/marklogic/client/impl/OkHttpServices.java @@ -131,6 +131,13 @@ public OkHttpServices(ConnectionConfig connectionConfig) { this.okHttpClient = connect(connectionConfig); } + private static ClientCookie parseClientCookie(HttpUrl url, String setCookieHeaderValue) { + Cookie cookie = Cookie.parse(url, setCookieHeaderValue); + if(cookie == null) throw new IllegalStateException(setCookieHeaderValue + " is not a well-formed cookie"); + return new ClientCookie(cookie.name(), cookie.value(), cookie.expiresAt(), cookie.domain(), cookie.path(), + cookie.secure()); + } + private FailedRequest extractErrorFields(Response response) { if (response == null) return null; try { @@ -1501,7 +1508,7 @@ public Response apply(Request.Builder funcBuilder) { String location = response.headers().get("Location"); List cookies = new ArrayList<>(); for (String setCookie : response.headers(HEADER_SET_COOKIE)) { - ClientCookie cookie = ClientCookie.parse(requestBldr.build().url(), setCookie); + ClientCookie cookie = parseClientCookie(requestBldr.build().url(), setCookie); cookies.add(cookie); } closeResponse(response); @@ -5599,7 +5606,7 @@ private void executeRequest(CallResponseImpl responseImpl) { if (session != null) { List cookies = new ArrayList<>(); for (String setCookie : response.headers(HEADER_SET_COOKIE)) { - ClientCookie cookie = ClientCookie.parse(requestBldr.build().url(), setCookie); + ClientCookie cookie = parseClientCookie(requestBldr.build().url(), setCookie); cookies.add(cookie); } ((SessionStateImpl) session).setCookies(cookies); diff --git a/marklogic-client-api/src/main/java/com/marklogic/client/impl/SessionStateImpl.java b/marklogic-client-api/src/main/java/com/marklogic/client/impl/SessionStateImpl.java index 53d5defe9..1c27cccfb 100644 --- a/marklogic-client-api/src/main/java/com/marklogic/client/impl/SessionStateImpl.java +++ b/marklogic-client-api/src/main/java/com/marklogic/client/impl/SessionStateImpl.java @@ -3,6 +3,7 @@ */ package com.marklogic.client.impl; +import com.marklogic.client.ClientCookie; import com.marklogic.client.SessionState; import java.util.ArrayList; @@ -12,43 +13,42 @@ import java.util.concurrent.atomic.AtomicBoolean; public class SessionStateImpl implements SessionState { - private List cookies; - private String sessionId; - private AtomicBoolean setCreatedTimestamp; - private Calendar created; - - public SessionStateImpl() { - sessionId = Long.toUnsignedString(ThreadLocalRandom.current().nextLong(), 16); - cookies = new ArrayList<>(); - setCreatedTimestamp = new AtomicBoolean(false); - } - - @Override - public String getSessionId() { - return sessionId; - } - - List getCookies() { - return cookies; - } - - void setCookies(List cookies) { - if ( cookies != null ) { - if(setCreatedTimestamp.compareAndSet(false, true)) { - for (ClientCookie cookie : cookies) { - // Drop the SessionId cookie received from the server. We add it every - // time we make a request with a SessionState object passed - if(cookie.getName().equalsIgnoreCase("SessionId")) continue; - // make a clone to ensure we're not holding on to any resources - // related to an HTTP connection that need to be released - this.cookies.add(new ClientCookie(cookie)); - } - created = Calendar.getInstance(); - } - } - } - - Calendar getCreatedTimestamp() { - return created; - } + + private List cookies; + private String sessionId; + private AtomicBoolean setCreatedTimestamp; + private Calendar created; + + public SessionStateImpl() { + sessionId = Long.toUnsignedString(ThreadLocalRandom.current().nextLong(), 16); + cookies = new ArrayList<>(); + setCreatedTimestamp = new AtomicBoolean(false); + } + + @Override + public String getSessionId() { + return sessionId; + } + + List getCookies() { + return cookies; + } + + void setCookies(List cookies) { + if (cookies != null) { + if (setCreatedTimestamp.compareAndSet(false, true)) { + for (ClientCookie cookie : cookies) { + // Drop the SessionId cookie received from the server. We add it every + // time we make a request with a SessionState object passed + if (cookie.getName().equalsIgnoreCase("SessionId")) continue; + this.cookies.add(cookie); + } + created = Calendar.getInstance(); + } + } + } + + Calendar getCreatedTimestamp() { + return created; + } } diff --git a/marklogic-client-api/src/main/java/com/marklogic/client/impl/TransactionImpl.java b/marklogic-client-api/src/main/java/com/marklogic/client/impl/TransactionImpl.java index 60d25bd53..150313940 100644 --- a/marklogic-client-api/src/main/java/com/marklogic/client/impl/TransactionImpl.java +++ b/marklogic-client-api/src/main/java/com/marklogic/client/impl/TransactionImpl.java @@ -3,7 +3,7 @@ */ package com.marklogic.client.impl; -import com.marklogic.client.impl.ClientCookie; +import com.marklogic.client.ClientCookie; import com.marklogic.client.FailedRequestException; import com.marklogic.client.ForbiddenUserException; import com.marklogic.client.Transaction; @@ -19,7 +19,7 @@ class TransactionImpl implements Transaction { private RESTServices services; private String transactionId; private String hostId; - // we keep cookies scoped with each tranasaction to work with load balancers + // we keep cookies scoped with each transaction to work with load balancers // that need to keep requests for one transaction on a specific MarkLogic Server host private List cookies = new ArrayList<>(); private Calendar created; @@ -29,9 +29,7 @@ class TransactionImpl implements Transaction { this.transactionId = transactionId; if ( cookies != null ) { for (ClientCookie cookie : cookies) { - // make a clone to ensure we're not holding on to any resources - // related to an HTTP connection that need to be released - this.cookies.add(new ClientCookie(cookie)); + this.cookies.add(cookie); if ( "HostId".equalsIgnoreCase(cookie.getName()) ) { hostId = cookie.getValue(); } @@ -44,9 +42,6 @@ class TransactionImpl implements Transaction { public String getTransactionId() { return transactionId; } - public void setTransactionId(String transactionId) { - this.transactionId = transactionId; - } @Override public List getCookies() { @@ -57,9 +52,6 @@ public List getCookies() { public String getHostId() { return hostId; } - protected void setHostId(String hostId) { - this.hostId = hostId; - } public Calendar getCreatedTimestamp() { return created;