CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-eclipse-jetty--jetty-server

Core server component of Eclipse Jetty web server providing HTTP server functionality, request handling, and connection management

Pending
Overview
Eval results
Files

session-management.mddocs/

Session Management

Session management provides HTTP session support with creation, invalidation, attribute storage, and timeout management for maintaining user state across requests.

Session Interface

The core session interface for managing HTTP session state.

public interface Session extends Attributes {
    // Session identification
    String getId();
    Context getContext();
    
    // Timing information
    long getCreationTime();
    long getLastAccessedTime();
    
    // Session lifecycle
    void setMaxInactiveInterval(int interval);
    int getMaxInactiveInterval();
    void invalidate();
    boolean isNew();
    boolean isValid();
    
    // Attribute management (from Attributes interface)
    Object getAttribute(String name);
    void setAttribute(String name, Object value);
    void removeAttribute(String name);
    Set<String> getAttributeNameSet();
    void clearAttributes();
}

Basic Session Usage

public class SessionDemoHandler extends Handler.Abstract {
    
    @Override
    public boolean handle(Request request, Response response, Callback callback) 
            throws Exception {
        
        // Get existing session or create new one
        Session session = request.getSession(true);
        
        // Check if this is a new session
        if (session.isNew()) {
            System.out.println("New session created: " + session.getId());
            session.setAttribute("createdAt", Instant.now());
        }
        
        // Update visit count
        Integer visitCount = (Integer) session.getAttribute("visitCount");
        if (visitCount == null) {
            visitCount = 0;
        }
        visitCount++;
        session.setAttribute("visitCount", visitCount);
        
        // Update last visit time
        session.setAttribute("lastVisit", Instant.now());
        
        // Set session timeout (30 minutes)
        session.setMaxInactiveInterval(30 * 60);
        
        // Generate response with session info
        response.setStatus(200);
        response.getHeaders().put("Content-Type", "application/json");
        
        String json = createSessionInfoJson(session);
        response.write(true, ByteBuffer.wrap(json.getBytes()), callback);
        
        return true;
    }
    
    private String createSessionInfoJson(Session session) {
        Map<String, Object> sessionInfo = new HashMap<>();
        sessionInfo.put("sessionId", session.getId());
        sessionInfo.put("isNew", session.isNew());
        sessionInfo.put("creationTime", session.getCreationTime());
        sessionInfo.put("lastAccessedTime", session.getLastAccessedTime());
        sessionInfo.put("maxInactiveInterval", session.getMaxInactiveInterval());
        
        // Add session attributes
        Map<String, Object> attributes = new HashMap<>();
        for (String name : session.getAttributeNameSet()) {
            Object value = session.getAttribute(name);
            if (value instanceof Serializable) {
                attributes.put(name, value.toString());
            }
        }
        sessionInfo.put("attributes", attributes);
        
        // Convert to JSON (simplified)
        return toJson(sessionInfo);
    }
    
    private String toJson(Map<String, Object> map) {
        // Simplified JSON conversion
        StringBuilder json = new StringBuilder("{");
        boolean first = true;
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            if (!first) json.append(",");
            json.append("\"").append(entry.getKey()).append("\":");
            json.append("\"").append(entry.getValue()).append("\"");
            first = false;
        }
        json.append("}");
        return json.toString();
    }
}

Session Configuration

SessionHandler

Handler that provides session support to child handlers.

public class SessionHandler extends Handler.Wrapper {
    // Session configuration
    public SessionCache getSessionCache();
    public void setSessionCache(SessionCache sessionCache);
    public SessionDataStore getSessionDataStore();
    public void setSessionDataStore(SessionDataStore sessionDataStore);
    
    // Session ID configuration
    public SessionIdManager getSessionIdManager();
    public void setSessionIdManager(SessionIdManager sessionIdManager);
    
    // Cookie configuration
    public SessionCookieConfig getSessionCookieConfig();
    public void setSessionCookieConfig(SessionCookieConfig config);
    
    // Timeout configuration
    public int getMaxInactiveInterval();
    public void setMaxInactiveInterval(int maxInactiveInterval);
    
    // Session tracking modes
    public Set<SessionTrackingMode> getEffectiveSessionTrackingModes();
    public void setSessionTrackingModes(Set<SessionTrackingMode> sessionTrackingModes);
    
    // Session lifecycle
    public Session newSession(Request request);
    public void complete(Session session);
}

Basic Session Configuration

public class SessionConfiguration {
    
    public void configureSession(Server server) {
        // Create session handler
        SessionHandler sessionHandler = new SessionHandler();
        
        // Configure session timeout (30 minutes)
        sessionHandler.setMaxInactiveInterval(30 * 60);
        
        // Configure session cookie
        SessionCookieConfig cookieConfig = sessionHandler.getSessionCookieConfig();
        cookieConfig.setName("JSESSIONID");
        cookieConfig.setPath("/");
        cookieConfig.setHttpOnly(true);
        cookieConfig.setSecure(true);  // HTTPS only
        cookieConfig.setMaxAge(24 * 60 * 60); // 24 hours
        
        // Set session tracking mode
        Set<SessionTrackingMode> trackingModes = EnumSet.of(SessionTrackingMode.COOKIE);
        sessionHandler.setSessionTrackingModes(trackingModes);
        
        // Set application handler as child
        sessionHandler.setHandler(new ApplicationHandler());
        
        server.setHandler(sessionHandler);
    }
}

Advanced Session Management

Custom Session Store

public class DatabaseSessionDataStore extends AbstractSessionDataStore {
    private final DataSource dataSource;
    
    public DatabaseSessionDataStore(DataSource dataSource) {
        this.dataSource = dataSource;
    }
    
    @Override
    public SessionData doLoad(String id) throws Exception {
        try (Connection conn = dataSource.getConnection()) {
            String sql = "SELECT * FROM sessions WHERE session_id = ?";
            try (PreparedStatement stmt = conn.prepareStatement(sql)) {
                stmt.setString(1, id);
                try (ResultSet rs = stmt.executeQuery()) {
                    if (rs.next()) {
                        return deserializeSessionData(rs);
                    }
                }
            }
        }
        return null;
    }
    
    @Override
    public boolean doStore(String id, SessionData data, long lastSaveTime) throws Exception {
        try (Connection conn = dataSource.getConnection()) {
            String sql = "INSERT INTO sessions (session_id, creation_time, last_access_time, " +
                        "max_inactive_interval, attributes) VALUES (?, ?, ?, ?, ?) " +
                        "ON DUPLICATE KEY UPDATE last_access_time = ?, attributes = ?";
            
            try (PreparedStatement stmt = conn.prepareStatement(sql)) {
                stmt.setString(1, id);
                stmt.setLong(2, data.getCreated());
                stmt.setLong(3, data.getLastAccessed());
                stmt.setInt(4, data.getMaxInactiveInterval());
                stmt.setBytes(5, serializeAttributes(data.getAttributes()));
                stmt.setLong(6, data.getLastAccessed());
                stmt.setBytes(7, serializeAttributes(data.getAttributes()));
                
                return stmt.executeUpdate() > 0;
            }
        }
    }
    
    @Override
    public boolean doDelete(String id) throws Exception {
        try (Connection conn = dataSource.getConnection()) {
            String sql = "DELETE FROM sessions WHERE session_id = ?";
            try (PreparedStatement stmt = conn.prepareStatement(sql)) {
                stmt.setString(1, id);
                return stmt.executeUpdate() > 0;
            }
        }
    }
    
    @Override
    public Set<String> doCheckExpired(Set<String> candidates, long time) throws Exception {
        Set<String> expired = new HashSet<>();
        
        try (Connection conn = dataSource.getConnection()) {
            String sql = "SELECT session_id FROM sessions WHERE " +
                        "last_access_time + (max_inactive_interval * 1000) < ?";
            
            try (PreparedStatement stmt = conn.prepareStatement(sql)) {
                stmt.setLong(1, time);
                try (ResultSet rs = stmt.executeQuery()) {
                    while (rs.next()) {
                        expired.add(rs.getString("session_id"));
                    }
                }
            }
        }
        
        return expired;
    }
    
    @Override
    public Set<String> doGetExpired(long time) throws Exception {
        return doCheckExpired(Collections.emptySet(), time);
    }
    
    @Override
    public void doCleanOrphans(long time) throws Exception {
        try (Connection conn = dataSource.getConnection()) {
            String sql = "DELETE FROM sessions WHERE " +
                        "last_access_time + (max_inactive_interval * 1000) < ?";
            
            try (PreparedStatement stmt = conn.prepareStatement(sql)) {
                stmt.setLong(1, time);
                int deleted = stmt.executeUpdate();
                System.out.println("Cleaned " + deleted + " orphaned sessions");
            }
        }
    }
    
    private SessionData deserializeSessionData(ResultSet rs) throws SQLException {
        SessionData data = new SessionData(
            rs.getString("session_id"),
            "/", // context path
            "localhost", // virtual host
            rs.getLong("creation_time"),
            rs.getLong("last_access_time"),
            rs.getLong("last_access_time"),
            rs.getInt("max_inactive_interval")
        );
        
        // Deserialize attributes
        byte[] attributeBytes = rs.getBytes("attributes");
        if (attributeBytes != null) {
            Map<String, Object> attributes = deserializeAttributes(attributeBytes);
            data.putAllAttributes(attributes);
        }
        
        return data;
    }
    
    private byte[] serializeAttributes(Map<String, Object> attributes) {
        // Implement serialization (e.g., using Java serialization or JSON)
        try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
             ObjectOutputStream oos = new ObjectOutputStream(baos)) {
            oos.writeObject(attributes);
            return baos.toByteArray();
        } catch (IOException e) {
            throw new RuntimeException("Failed to serialize session attributes", e);
        }
    }
    
    private Map<String, Object> deserializeAttributes(byte[] data) {
        // Implement deserialization
        try (ByteArrayInputStream bais = new ByteArrayInputStream(data);
             ObjectInputStream ois = new ObjectInputStream(bais)) {
            return (Map<String, Object>) ois.readObject();
        } catch (IOException | ClassNotFoundException e) {
            throw new RuntimeException("Failed to deserialize session attributes", e);
        }
    }
}

Session Cache Configuration

public class SessionCacheConfiguration {
    
    public SessionHandler createSessionHandler(DataSource dataSource) {
        SessionHandler sessionHandler = new SessionHandler();
        
        // Create session ID manager
        DefaultSessionIdManager sessionIdManager = new DefaultSessionIdManager(server);
        sessionIdManager.setWorkerName("node1");
        sessionHandler.setSessionIdManager(sessionIdManager);
        
        // Create session data store
        DatabaseSessionDataStore sessionDataStore = new DatabaseSessionDataStore(dataSource);
        sessionDataStore.setGracePeriodSec(3600); // 1 hour grace period
        sessionDataStore.setSavePeriodSec(0);     // Save on every change
        
        // Create session cache
        DefaultSessionCache sessionCache = new DefaultSessionCache(sessionHandler);
        sessionCache.setSessionDataStore(sessionDataStore);
        sessionCache.setEvictionPolicy(SessionCache.NEVER_EVICT);
        sessionCache.setSaveOnCreate(true);
        sessionCache.setSaveOnInactiveEviction(true);
        sessionCache.setRemoveUnloadableSessions(true);
        
        sessionHandler.setSessionCache(sessionCache);
        
        return sessionHandler;
    }
}

Session Security

Secure Session Configuration

public class SecureSessionHandler extends SessionHandler {
    
    @Override
    protected void doStart() throws Exception {
        super.doStart();
        
        // Configure secure session cookie
        SessionCookieConfig cookieConfig = getSessionCookieConfig();
        cookieConfig.setSecure(true);      // HTTPS only
        cookieConfig.setHttpOnly(true);    // No JavaScript access
        cookieConfig.setSameSite(SameSite.STRICT); // CSRF protection
        
        // Use random session ID generation
        SessionIdManager idManager = getSessionIdManager();
        if (idManager instanceof DefaultSessionIdManager) {
            DefaultSessionIdManager defaultIdManager = (DefaultSessionIdManager) idManager;
            defaultIdManager.setWorkerName(null); // Use random worker name
        }
    }
    
    @Override
    public Session newSession(Request request) {
        Session session = super.newSession(request);
        
        // Add security attributes
        session.setAttribute("remoteAddr", 
            request.getConnectionMetaData().getRemoteSocketAddress());
        session.setAttribute("userAgent", request.getHeaders().get("User-Agent"));
        
        return session;
    }
    
    @Override
    protected void sessionInactivityTimer(Session session) {
        // Log session timeout for security monitoring
        System.out.println("Session timeout: " + session.getId() + 
                          " from " + session.getAttribute("remoteAddr"));
        super.sessionInactivityTimer(session);
    }
}

Session Validation

public class SessionValidationHandler extends Handler.Wrapper {
    
    @Override
    public boolean handle(Request request, Response response, Callback callback) 
            throws Exception {
        
        Session session = request.getSession(false);
        if (session != null && !isValidSession(session, request)) {
            // Invalid session, invalidate and create new
            session.invalidate();
            session = request.getSession(true);
        }
        
        return super.handle(request, response, callback);
    }
    
    private boolean isValidSession(Session session, Request request) {
        // Check if session is from same IP address
        String sessionAddr = (String) session.getAttribute("remoteAddr");
        String currentAddr = request.getConnectionMetaData().getRemoteSocketAddress().toString();
        
        if (sessionAddr != null && !sessionAddr.equals(currentAddr)) {
            System.err.println("Session hijack attempt detected: " + session.getId());
            return false;
        }
        
        // Check user agent
        String sessionUserAgent = (String) session.getAttribute("userAgent");
        String currentUserAgent = request.getHeaders().get("User-Agent");
        
        if (sessionUserAgent != null && !sessionUserAgent.equals(currentUserAgent)) {
            System.err.println("User agent mismatch for session: " + session.getId());
            return false;
        }
        
        return true;
    }
}

Session Clustering

Distributed Session Configuration

public class ClusteredSessionConfiguration {
    
    public void configureClusteredSessions(Server server, String[] nodes) {
        // Create shared session ID manager
        DefaultSessionIdManager sessionIdManager = new DefaultSessionIdManager(server);
        sessionIdManager.setWorkerName(getNodeName());
        server.addBean(sessionIdManager);
        
        // Create clustered session data store
        ClusteredSessionDataStore dataStore = new ClusteredSessionDataStore(nodes);
        
        // Configure session handler
        SessionHandler sessionHandler = new SessionHandler();
        sessionHandler.setSessionIdManager(sessionIdManager);
        
        // Create session cache with clustering support
        DefaultSessionCache sessionCache = new DefaultSessionCache(sessionHandler);
        sessionCache.setSessionDataStore(dataStore);
        sessionCache.setEvictionPolicy(SessionCache.EVICT_ON_SESSION_EXIT);
        
        sessionHandler.setSessionCache(sessionCache);
        
        // Set application handler
        sessionHandler.setHandler(new ApplicationHandler());
        server.setHandler(sessionHandler);
    }
    
    private String getNodeName() {
        // Generate unique node identifier
        return "node-" + System.currentTimeMillis();
    }
}

Session Monitoring and Statistics

Session Statistics Handler

public class SessionStatisticsHandler extends Handler.Wrapper {
    private final AtomicLong sessionsCreated = new AtomicLong();
    private final AtomicLong sessionsDestroyed = new AtomicLong();
    private final AtomicLong sessionsTimeout = new AtomicLong();
    private final Map<String, Long> sessionStartTimes = new ConcurrentHashMap<>();
    
    @Override
    public boolean handle(Request request, Response response, Callback callback) 
            throws Exception {
        
        Session session = request.getSession(false);
        if (session != null) {
            if (session.isNew()) {
                sessionsCreated.incrementAndGet();
                sessionStartTimes.put(session.getId(), System.currentTimeMillis());
                
                // Add session listener
                session.setAttribute("statisticsHandler", this);
            }
        }
        
        return super.handle(request, response, callback);
    }
    
    public void onSessionDestroyed(String sessionId) {
        sessionsDestroyed.incrementAndGet();
        sessionStartTimes.remove(sessionId);
    }
    
    public void onSessionTimeout(String sessionId) {
        sessionsTimeout.incrementAndGet();
        sessionStartTimes.remove(sessionId);
    }
    
    public long getSessionsCreated() {
        return sessionsCreated.get();
    }
    
    public long getSessionsDestroyed() {
        return sessionsDestroyed.get();
    }
    
    public long getSessionsTimeout() {
        return sessionsTimeout.get();
    }
    
    public long getActiveSessions() {
        return sessionsCreated.get() - sessionsDestroyed.get();
    }
    
    public double getAverageSessionDuration() {
        long totalDuration = 0;
        long count = 0;
        
        for (Long startTime : sessionStartTimes.values()) {
            totalDuration += System.currentTimeMillis() - startTime;
            count++;
        }
        
        return count > 0 ? (double) totalDuration / count : 0.0;
    }
}

Install with Tessl CLI

npx tessl i tessl/maven-org-eclipse-jetty--jetty-server

docs

configuration.md

connection-management.md

context-resources.md

handlers.md

index.md

request-logging.md

request-response.md

security-ssl.md

server-core.md

session-management.md

utility-handlers.md

tile.json