package com.firefly.server.http2.router.handler.session;
import com.firefly.codec.http2.model.Cookie;
import com.firefly.server.http2.router.RoutingContext;
import com.firefly.server.http2.router.spi.HTTPSessionHandlerSPI;
import com.firefly.utils.StringUtils;
import com.firefly.utils.concurrent.Scheduler;
import com.firefly.utils.time.Millisecond100Clock;
import javax.servlet.http.HttpSession;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
/**
* @author Pengtao Qiu
*/
public class HTTPSessionHandlerSPIImpl implements HTTPSessionHandlerSPI {
private final HTTPSessionConfiguration configuration;
private final SessionStore sessionStore;
private final RoutingContext routingContext;
private final Scheduler scheduler;
private boolean requestedSessionIdFromURL;
private boolean requestedSessionIdFromCookie;
private String requestedSessionId;
private HTTPSessionImpl httpSession;
public HTTPSessionHandlerSPIImpl(SessionStore sessionStore,
RoutingContext routingContext,
Scheduler scheduler,
HTTPSessionConfiguration configuration) {
this.sessionStore = sessionStore;
this.routingContext = routingContext;
this.configuration = configuration;
this.scheduler = scheduler;
init();
}
private void init() {
if (getHttpSessionFromCookie() == null) {
getHttpSessionFromURL();
}
}
private String getHttpSessionFromURL() {
if (requestedSessionId != null) {
return requestedSessionId;
}
String param = routingContext.getURI().getParam();
if (StringUtils.hasText(param)) {
String prefix = configuration.getSessionIdParameterName() + "=";
if (param.length() > prefix.length()) {
int s = param.indexOf(prefix);
if (s >= 0) {
s += prefix.length();
requestedSessionId = param.substring(s);
requestedSessionIdFromCookie = false;
requestedSessionIdFromURL = true;
requestHttpSession();
return requestedSessionId;
}
}
}
return null;
}
private String getHttpSessionFromCookie() {
if (requestedSessionId != null) {
return requestedSessionId;
}
List<Cookie> cookies = routingContext.getCookies();
if (cookies != null && !cookies.isEmpty()) {
Optional<Cookie> optional = cookies.stream()
.filter(c -> configuration.getSessionIdParameterName().equalsIgnoreCase(c.getName()))
.findFirst();
if (optional.isPresent()) {
requestedSessionIdFromCookie = true;
requestedSessionIdFromURL = false;
requestedSessionId = optional.get().getValue();
requestHttpSession();
return requestedSessionId;
}
}
return null;
}
private void requestHttpSession() {
httpSession = (HTTPSessionImpl) sessionStore.get(requestedSessionId);
if (httpSession != null) {
if (httpSession.check()) {
httpSession.setLastAccessedTime(Millisecond100Clock.currentTimeMillis());
httpSession.setNewSession(false);
scheduleCheck(httpSession, httpSession.getRemainInactiveInterval());
} else {
httpSession = null;
sessionStore.remove(requestedSessionId);
}
}
}
@Override
public HttpSession getSession() {
return getSession(false);
}
@Override
public HttpSession getSession(boolean create) {
if (create) {
if (httpSession == null) {
String id = UUID.randomUUID().toString();
httpSession = new HTTPSessionImpl(id);
httpSession.setMaxInactiveInterval(configuration.getDefaultMaxInactiveInterval());
routingContext.addCookie(new Cookie(configuration.getSessionIdParameterName(), id));
sessionStore.put(id, httpSession);
scheduleCheck(httpSession, httpSession.getMaxInactiveInterval());
return httpSession;
} else {
return httpSession;
}
} else {
return httpSession;
}
}
private void scheduleCheck(final HTTPSessionImpl session, final long remainInactiveInterval) {
scheduler.schedule(() -> {
if (!session.check()) {
sessionStore.remove(session.getId());
} else {
scheduleCheck(session, session.getRemainInactiveInterval());
}
}, remainInactiveInterval, TimeUnit.MILLISECONDS);
}
@Override
public boolean isRequestedSessionIdFromURL() {
return requestedSessionIdFromURL;
}
@Override
public boolean isRequestedSessionIdFromCookie() {
return requestedSessionIdFromCookie;
}
@Override
public boolean isRequestedSessionIdValid() {
return false;
}
@Override
public String getRequestedSessionId() {
return requestedSessionId;
}
@Override
public int getSessionSize() {
return sessionStore.size();
}
}