Fixed http server session recreated every connection

This commit is contained in:
Ziver Koc 2017-04-19 17:55:34 +02:00
parent 914d1d83df
commit 178f881d57
3 changed files with 82 additions and 62 deletions

View file

@ -70,4 +70,11 @@ public class Timer {
public boolean hasTimedOut(){ public boolean hasTimedOut(){
return timestamp + period < System.currentTimeMillis(); return timestamp + period < System.currentTimeMillis();
} }
public String toString(){
if (hasTimedOut())
return "Timed out";
else
return "Timeout in "+StringUtil.formatTimeToString((timestamp+period)-System.currentTimeMillis());
}
} }

View file

@ -25,6 +25,7 @@
package zutil.net.http; package zutil.net.http;
import zutil.StringUtil; import zutil.StringUtil;
import zutil.Timer;
import zutil.log.LogUtil; import zutil.log.LogUtil;
import zutil.net.threaded.ThreadedTCPNetworkServer; import zutil.net.threaded.ThreadedTCPNetworkServer;
import zutil.net.threaded.ThreadedTCPNetworkServerThread; import zutil.net.threaded.ThreadedTCPNetworkServerThread;
@ -33,8 +34,11 @@ import java.io.BufferedInputStream;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.net.Socket; import java.net.Socket;
import java.util.*; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -48,15 +52,15 @@ import java.util.logging.Logger;
public class HttpServer extends ThreadedTCPNetworkServer{ public class HttpServer extends ThreadedTCPNetworkServer{
private static final Logger logger = LogUtil.getLogger(); private static final Logger logger = LogUtil.getLogger();
public static final String SESSION_ID_KEY = "session_id"; public static final String SESSION_KEY_ID = "session_id";
public static final String SESSION_TTL_KEY = "session_ttl"; public static final String SESSION_KEY_TTL = "session_ttl";
public static final String SERVER_VERSION = "Zutil HttpServer"; public static final String SERVER_NAME = "Zutil HttpServer";
public static final int SESSION_TTL = 10*60*1000; // in milliseconds public static final int SESSION_TTL = 10*60*1000; // in milliseconds
private Map<String,HttpPage> pages; private Map<String,HttpPage> pages;
private HttpPage defaultPage; private HttpPage defaultPage;
private Map<Integer,Map<String,Object>> sessions; private Map<String,Map<String,Object>> sessions;
private int nextSessionId; private int nextSessionId;
/** /**
@ -83,8 +87,8 @@ public class HttpServer extends ThreadedTCPNetworkServer{
sessions = new ConcurrentHashMap<>(); sessions = new ConcurrentHashMap<>();
nextSessionId = 0; nextSessionId = 0;
Timer timer = new Timer(); ScheduledExecutorService exec = Executors.newSingleThreadScheduledExecutor();
timer.schedule(new SessionGarbageCollector(), 10000, SESSION_TTL / 2); exec.scheduleWithFixedDelay(new SessionGarbageCollector(), 10000, SESSION_TTL / 2, TimeUnit.MILLISECONDS);
logger.info("HTTP"+(keyStore==null?"":"S")+" Server ready!"); logger.info("HTTP"+(keyStore==null?"":"S")+" Server ready!");
} }
@ -92,10 +96,8 @@ public class HttpServer extends ThreadedTCPNetworkServer{
/** /**
* This class acts as an garbage collector that * This class acts as an garbage collector that
* removes old sessions from the session HashMap * removes old sessions from the session HashMap
*
* @author Ziver
*/ */
private class SessionGarbageCollector extends TimerTask { private class SessionGarbageCollector implements Runnable {
public void run(){ public void run(){
Object[] keys = sessions.keySet().toArray(); Object[] keys = sessions.keySet().toArray();
int count = 0; int count = 0;
@ -103,7 +105,7 @@ public class HttpServer extends ThreadedTCPNetworkServer{
Map<String,Object> session = sessions.get(key); Map<String,Object> session = sessions.get(key);
// Check if session is still valid // Check if session is still valid
if((Long)session.get(SESSION_TTL_KEY) < System.currentTimeMillis()){ if(((Timer) session.get(SESSION_KEY_TTL)).hasTimedOut()){
sessions.remove(key); sessions.remove(key);
++count; ++count;
} }
@ -190,28 +192,28 @@ public class HttpServer extends ThreadedTCPNetworkServer{
//**************************** HANDLE REQUEST ********************************* //**************************** HANDLE REQUEST *********************************
// Get the client session or create one // Get the client session or create one
long ttlTime = System.currentTimeMillis() + SESSION_TTL; String sessionCookie = header.getCookie(SESSION_KEY_ID);
String sessionCookie = header.getCookie(SESSION_ID_KEY);
if (sessionCookie != null && sessions.containsKey(sessionCookie) && if (sessionCookie != null && sessions.containsKey(sessionCookie) &&
(Long) sessions.get(sessionCookie).get(SESSION_TTL_KEY) < System.currentTimeMillis()) { // Check if session is still valid !((Timer) sessions.get(sessionCookie).get(SESSION_KEY_TTL)).hasTimedOut()) { // Check if session is still valid
session = sessions.get(sessionCookie); session = sessions.get(sessionCookie);
// renew the session TTL ((Timer) sessions.get(sessionCookie).get(SESSION_KEY_TTL)).start(); // renew the session TTL
session.put(SESSION_TTL_KEY, ttlTime);
} else { } else {
session = Collections.synchronizedMap(new HashMap<String, Object>()); synchronized (sessions) {
session.put(SESSION_ID_KEY, nextSessionId); session = new ConcurrentHashMap<>();
session.put(SESSION_TTL_KEY, ttlTime); session.put(SESSION_KEY_ID, ""+nextSessionId);
sessions.put(nextSessionId, session); session.put(SESSION_KEY_TTL, new Timer(SESSION_TTL).start());
sessions.put(""+nextSessionId, session);
out.setCookie(SESSION_KEY_ID, ""+nextSessionId);
++nextSessionId; ++nextSessionId;
} }
}
//**************************** RESPONSE ************************************ //**************************** RESPONSE ************************************
out.setHttpVersion("1.0"); out.setHttpVersion("1.0");
out.setStatusCode(200); out.setStatusCode(200);
out.setHeader("Server", SERVER_VERSION); out.setHeader("Server", SERVER_NAME);
out.setHeader("Content-Type", "text/html"); out.setHeader("Content-Type", "text/html");
out.setCookie(SESSION_ID_KEY, "" + session.get(SESSION_ID_KEY));
if (header.getRequestURL() != null && pages.containsKey(header.getRequestURL())) { if (header.getRequestURL() != null && pages.containsKey(header.getRequestURL())) {
HttpPage page = pages.get(header.getRequestURL()); HttpPage page = pages.get(header.getRequestURL());

View file

@ -24,6 +24,8 @@
package zutil.net.http.page; package zutil.net.http.page;
import zutil.log.CompactLogFormatter;
import zutil.log.LogUtil;
import zutil.net.http.HttpHeader; import zutil.net.http.HttpHeader;
import zutil.net.http.HttpPage; import zutil.net.http.HttpPage;
import zutil.net.http.HttpPrintStream; import zutil.net.http.HttpPrintStream;
@ -31,11 +33,23 @@ import zutil.net.http.HttpServer;
import java.io.IOException; import java.io.IOException;
import java.util.Map; import java.util.Map;
import java.util.logging.Level;
import static zutil.net.http.HttpServer.SESSION_KEY_ID;
public class HttpGuessTheNumber implements HttpPage { public class HttpGuessTheNumber implements HttpPage {
private static final String SESSION_KEY_NUMBER = "random_number";
private static final String REQUEST_KEY_GUESS = "guess";
private static final String COOKIE_KEY_LOW = "low";
private static final String COOKIE_KEY_HIGH = "high";
public static void main(String[] args) throws IOException{ public static void main(String[] args) throws IOException{
LogUtil.setGlobalLevel(Level.ALL);
LogUtil.setGlobalFormatter(new CompactLogFormatter());
//HttpServer server = new HttpServer("localhost", 443, FileFinder.find("keySSL"), "rootroot");//SSL //HttpServer server = new HttpServer("localhost", 443, FileFinder.find("keySSL"), "rootroot");//SSL
HttpServer server = new HttpServer(8080); HttpServer server = new HttpServer(8080);
server.setDefaultPage(new HttpGuessTheNumber()); server.setDefaultPage(new HttpGuessTheNumber());
@ -52,55 +66,52 @@ public class HttpGuessTheNumber implements HttpPage {
out.println("<html>"); out.println("<html>");
out.println("<H2>Welcome To The Number Guess Game!</H2>"); out.println("<H2>Welcome To The Number Guess Game!</H2>");
if(session.containsKey("random_nummber") && request.containsKey("guess") && !request.get("guess").isEmpty()){ String low = cookie.get(COOKIE_KEY_LOW);
int guess = Integer.parseInt(request.get("guess")); String high = cookie.get(COOKIE_KEY_HIGH);
int nummber = (Integer)session.get("random_nummber");
try { if(session.containsKey(SESSION_KEY_NUMBER)){
if(guess == nummber){ if (request.containsKey(REQUEST_KEY_GUESS)) {
session.remove("random_nummber"); int guess = Integer.parseInt(request.get(REQUEST_KEY_GUESS));
int number = (Integer) session.get(SESSION_KEY_NUMBER);
if (guess == number) {
session.remove(SESSION_KEY_NUMBER);
out.println("You Guessed Right! Congrats!"); out.println("You Guessed Right! Congrats!");
out.println("</html>"); out.println("</html>");
return; return;
} } else if (guess > number) {
else if(guess > nummber){
out.println("<b>To High</b><br>"); out.println("<b>To High</b><br>");
if(Integer.parseInt(cookie.get("high")) > guess){ if (Integer.parseInt(high) > guess) {
out.setCookie("high", ""+guess); high = String.valueOf(guess);
cookie.put("high", ""+guess); out.setCookie(COOKIE_KEY_HIGH, high);
} }
} } else {
else{
out.println("<b>To Low</b><br>"); out.println("<b>To Low</b><br>");
if(Integer.parseInt(cookie.get("low")) < guess){ if (Integer.parseInt(low) < guess) {
out.setCookie("low", ""+guess); low = String.valueOf(guess);
cookie.put("low", ""+guess); out.setCookie(COOKIE_KEY_LOW, low);
} }
} }
} catch (Exception e) {
e.printStackTrace();
} }
} }
else{ else{
session.put("random_nummber", (int)(Math.random()*99+1)); session.put(SESSION_KEY_NUMBER, (int)(Math.random()*99+1));
try { low = "0";
out.setCookie("low", "0"); high = "100";
out.setCookie("high", "100"); out.setCookie(COOKIE_KEY_LOW, low);
cookie.put("low", "0"); out.setCookie(COOKIE_KEY_HIGH, high);
cookie.put("high", "100");
} catch (Exception e) {
e.printStackTrace();
}
} }
out.println("<form method='post'>"); out.println("<form method='post'>");
out.println(cookie.get("low")+" < X < "+cookie.get("high")+"<br>"); out.println(low+" < X < "+high+"<br>");
out.println("Guess a number between 0 and 100:<br>"); out.println("Guess a number between 0 and 100:<br>");
out.println("<input type='text' name='guess'>"); out.println("<input type='text' name='guess'>");
out.println("<input type='hidden' name='test' value='test'>"); out.println("<input type='hidden' name='test' value='test'>");
out.println("<input type='submit' value='Guess'>"); out.println("<input type='submit' value='Guess'>");
out.println("</form>"); out.println("</form>");
out.println("<script>document.all.guess.focus();</script>"); out.println("<script>document.all.guess.focus();</script>");
out.println("<b>DEBUG: nummber="+session.get("random_nummber")+"</b><br>"); out.println("<b>DEBUG: session_id="+session.get(SESSION_KEY_ID)+"</b><br>");
out.println("<b>DEBUG: number="+session.get(SESSION_KEY_NUMBER)+"</b><br>");
out.println("</html>"); out.println("</html>");
} }