Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ability to compress and skip unserializable session attributes #10747

Merged
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import org.eclipse.jetty.http3.server.internal.HTTP3SessionServer;
import org.eclipse.jetty.quic.client.ClientQuicSession;
import org.eclipse.jetty.quic.common.QuicSession;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
Expand Down Expand Up @@ -365,6 +366,7 @@ public void onDataAvailable(Stream.Client stream)
}

@Test
@Tag("flaky")
public void testRequestHeadersTooLarge() throws Exception
{
start(new Session.Server.Listener()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,16 @@

package org.eclipse.jetty.session;

import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import org.eclipse.jetty.util.ClassLoadingObjectInputStream;
import org.eclipse.jetty.util.FuturePromise;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.annotation.ManagedObject;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.nio.file.FileVisitOption;
Expand All @@ -32,7 +33,6 @@
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;

import org.eclipse.jetty.util.ClassLoadingObjectInputStream;
import org.eclipse.jetty.util.ExceptionUtil;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
Expand All @@ -46,7 +46,7 @@
* A file-based store of session data.
*/
@ManagedObject
public class FileSessionDataStore extends AbstractSessionDataStore
public class FileSessionDataStore extends ObjectStreamSessionDataStore
{
private static final Logger LOG = LoggerFactory.getLogger(FileSessionDataStore.class);
protected File _storeDir;
Expand Down Expand Up @@ -464,7 +464,7 @@ public boolean doExists(String id) throws Exception
* @param id identity of the session
* @param data the info of the session
*/
protected void save(OutputStream os, String id, SessionData data) throws IOException
protected void save(OutputStream os, String id, SessionData data) throws Exception
{
DataOutputStream out = new DataOutputStream(os);
out.writeUTF(id);
Expand All @@ -478,8 +478,7 @@ protected void save(OutputStream os, String id, SessionData data) throws IOExcep
out.writeLong(data.getExpiry());
out.writeLong(data.getMaxInactiveMs());

ObjectOutputStream oos = new ObjectOutputStream(out);
SessionData.serializeAttributes(data, oos);
serializeAttributes(data, out);
}

/**
Expand Down Expand Up @@ -623,8 +622,7 @@ protected SessionData load(InputStream is, String expectedId)
data.setMaxInactiveMs(maxIdle);

// Attributes
ClassLoadingObjectInputStream ois = new ClassLoadingObjectInputStream(is);
SessionData.deserializeAttributes(data, ois);
deserializeAttributes(data, is);
return data;
}
catch (Exception e)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
Expand All @@ -26,7 +27,6 @@
import java.util.HashSet;
import java.util.Set;

import org.eclipse.jetty.util.ClassLoadingObjectInputStream;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.annotation.ManagedObject;
Expand All @@ -39,7 +39,7 @@
* Session data stored in database
*/
@ManagedObject
public class JDBCSessionDataStore extends AbstractSessionDataStore
public class JDBCSessionDataStore extends ObjectStreamSessionDataStore
{
private static final Logger LOG = LoggerFactory.getLogger(JDBCSessionDataStore.class);

Expand Down Expand Up @@ -662,10 +662,9 @@ public SessionData doLoad(String id) throws Exception
data.setContextPath(_context.getCanonicalContextPath());
data.setVhost(_context.getVhost());

try (InputStream is = _dbAdaptor.getBlobInputStream(result, _sessionTableSchema.getMapColumn());
ClassLoadingObjectInputStream ois = new ClassLoadingObjectInputStream(is))
try (InputStream is = _dbAdaptor.getBlobInputStream(result, _sessionTableSchema.getMapColumn()))
{
SessionData.deserializeAttributes(data, ois);
deserializeAttributes(data, is);
}
catch (Exception e)
{
Expand Down Expand Up @@ -741,10 +740,10 @@ protected void doInsert(String id, SessionData data)
statement.setLong(10, data.getExpiry());
statement.setLong(11, data.getMaxInactiveMs());

try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos))
try (ByteArrayOutputStream baos = new ByteArrayOutputStream();)
{
SessionData.serializeAttributes(data, oos);
serializeAttributes(data, baos);

byte[] bytes = baos.toByteArray();
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
statement.setBinaryStream(12, bais, bytes.length); //attribute map as blob
Expand Down Expand Up @@ -772,10 +771,10 @@ protected void doUpdate(String id, SessionData data)
statement.setLong(5, data.getExpiry());
statement.setLong(6, data.getMaxInactiveMs());

try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos))
try (ByteArrayOutputStream baos = new ByteArrayOutputStream();)
{
SessionData.serializeAttributes(data, oos);
serializeAttributes(data, baos);

byte[] bytes = baos.toByteArray();
try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes))
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//

package org.eclipse.jetty.session;

import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.util.Objects;

import org.eclipse.jetty.util.ClassLoadingObjectInputStream;

public abstract class ObjectStreamSessionDataStore extends AbstractSessionDataStore
{
/**
* Get an ObjectOutputStream suitable to serialize SessionData objects
* into the provided OutputStream.
* <br/>
* By default, an ObjectObjectStream is returned.
* <br/>
* Override this method to provide a custom ObjectOutputStream, and/or to
* chain other OutputStreams to perform such tasks as compressing the serialized
* data, for example:
* <br/>
* <code>
* GZIPOutputStream gos = new GZIPOutputStream(os);
* return new ObjectOutputStream(gos);
* </code>
* @param os an output stream to which to serialize the session data
* @return an ObjectOutputStream wrapping the OutputStream
* @throws IOException if the stream cannot be created
*/
public ObjectOutputStream newObjectOutputStream(OutputStream os) throws IOException
{
return new ObjectOutputStream(os);
}

/**
* Get an ObjectInputStream that is capable of deserializing the session data
* present in the provided InputStream.
* <br/>
* By default, a Classloader-aware ObjectInputStream is used, however, you
* can return your own specialized ObjectInputStream, or chain other InputStreams
* together to perform such tasks as data decompression, for example:
* <br/>
* <code>
* GZIPInputStream gis = new GZIPInputStream(is);
* return new ClassLoadingObjectInputStream(is)
* </code>
* @param is an input stream for accessing the session data to be deserialized
* @return an ObjectInputStream that can deserialize the session data
* @throws IOException if the stream cannot be created
*/
protected ObjectInputStream newObjectInputStream(InputStream is) throws IOException
{
return new ClassLoadingObjectInputStream(is);
}

/**
* Serialize the attribute map of the SessionData into the OutputStream provided.
* @param data the SessionData whose attributes are to be serialized
* @param os the OutputStream to receive the serialized attributes
* @throws Exception if the attributes cannot be serialized
*/
protected void serializeAttributes(SessionData data, OutputStream os) throws Exception
{
Objects.requireNonNull(data);
Objects.requireNonNull(os);
try (ObjectOutputStream oos = newObjectOutputStream(os))
{
SessionData.serializeAttributes(data, oos);
}
}

/**
* Deserialize the attribute map from the InputStream provided and store into the SessionData.
* @param data the SessionData into which to deserialize the attributes
* @param is the InputStream for reading the serialized attributes
* @throws Exception if the attributes cannot be deserialized
*/
protected void deserializeAttributes(SessionData data, InputStream is) throws Exception
{
Objects.requireNonNull(data);
Objects.requireNonNull(is);
try (ObjectInputStream ois = newObjectInputStream(is))
{
SessionData.deserializeAttributes(data, ois);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,8 @@ public static void deserializeAttributes(SessionData data, java.io.ObjectInputSt
if (LOG.isDebugEnabled())
LOG.debug("Deserialize {} isServerLoader={} serverLoader={} tccl={}", name, isServerClassLoader, serverLoader, contextLoader);
Object value = ((ClassLoadingObjectInputStream)in).readObject(isServerClassLoader ? serverLoader : contextLoader);
data._attributes.put(name, value);
if (value != null)
data._attributes.put(name, value);
}
}
else
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -456,10 +456,9 @@ public void testEmptyLoadSession() throws Exception
long now = System.currentTimeMillis();
SessionData data = store.newSessionData("aaa6", 100, now, now - 1, -1);
data.setLastNode(_sessionIdManager.getWorkerName());
//persistSession(data);
store.store("aaa6", data);
_server.stop();
_server.start(); //reindex files
_server.start();
store = _sessionManager.getSessionCache().getSessionDataStore();

//test that we can retrieve it
Expand Down
Loading