Skip to content

Commit e1c65df

Browse files
committed
Add cookie-session middleware
1 parent 01ca90f commit e1c65df

File tree

8 files changed

+267
-1
lines changed

8 files changed

+267
-1
lines changed

src/express/Express.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
11
package express;
22

3-
import com.sun.net.httpserver.HttpContext;
43
import com.sun.net.httpserver.HttpServer;
54
import express.events.Action;
65
import express.events.HttpRequest;
76
import express.expressfilter.ExpressFilter;
87
import express.expressfilter.ExpressFilterChain;
98
import express.http.Request;
109
import express.http.Response;
10+
import express.middleware.ExpressWorker;
1111

1212
import java.io.IOException;
1313
import java.net.InetSocketAddress;
14+
import java.util.ArrayList;
15+
import java.util.Collections;
16+
import java.util.List;
1417

1518
/**
1619
* @author Simon Reinisch
@@ -20,6 +23,7 @@
2023
*/
2124
public class Express {
2225

26+
private final List<ExpressWorker> WORKERS = Collections.synchronizedList(new ArrayList<>());
2327
private final ExpressFilterChain FILTER_CHAIN = new ExpressFilterChain();
2428

2529
private HttpServer httpServer;
@@ -125,6 +129,12 @@ public void patch(String context, HttpRequest request) {
125129
private void addFilter(boolean middleware, String requestMethod, String context, HttpRequest request) {
126130
ExpressFilter handler = new ExpressFilter(requestMethod, context, request);
127131

132+
if (request instanceof ExpressWorker){
133+
((ExpressWorker) request).start();
134+
WORKERS.add((ExpressWorker) request);
135+
}
136+
137+
128138
// Middleware needs an seperated list because it will ALWAYS fired before each request handler
129139
if (middleware)
130140
FILTER_CHAIN.addMiddleware(handler);
@@ -138,6 +148,7 @@ private void addFilter(boolean middleware, String requestMethod, String context,
138148
*
139149
* @throws IOException - If an IO-Error occurs, eg. the port is already in use.
140150
*/
151+
141152
public void listen() throws IOException {
142153
listen(null, 80);
143154
}

src/express/http/Request.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.sun.net.httpserver.Headers;
44
import com.sun.net.httpserver.HttpExchange;
55
import express.http.cookie.Cookie;
6+
import express.middleware.ExpressMiddleware;
67

78
import java.io.IOException;
89
import java.io.InputStream;
@@ -27,6 +28,8 @@ public class Request {
2728
private final Headers HEADER;
2829
private final String CONTENT_TYPE;
2930

31+
private final HashMap<String, Object> MIDDLEWARE;
32+
3033
private final HashMap<String, Cookie> COOKIES;
3134
private final HashMap<String, String> QUERYS;
3235
private HashMap<String, String> params;
@@ -38,6 +41,7 @@ public Request(HttpExchange exchange) {
3841
this.BODY = exchange.getRequestBody();
3942
this.CONTENT_TYPE = HEADER.get("Content-Type") == null ? "" : HEADER.get("Content-Type").get(0);
4043

44+
this.MIDDLEWARE = new HashMap<>();
4145
this.params = new HashMap<>();
4246

4347
this.QUERYS = parseRawQuery(exchange.getRequestURI());
@@ -85,6 +89,14 @@ public HashMap<String, Cookie> getCookies() {
8589
return COOKIES;
8690
}
8791

92+
public void addMiddlewareContent(ExpressMiddleware middleware, Object middlewareData) {
93+
MIDDLEWARE.put(middleware.getName(), middlewareData);
94+
}
95+
96+
public Object getMiddlewareContent(String name) {
97+
return MIDDLEWARE.get(name);
98+
}
99+
88100
/**
89101
* @return The request user-agent.
90102
*/
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package express.middleware;
2+
3+
import express.events.HttpRequest;
4+
import express.http.Request;
5+
import express.http.Response;
6+
import express.http.cookie.Cookie;
7+
8+
import java.math.BigInteger;
9+
import java.security.SecureRandom;
10+
import java.util.concurrent.ConcurrentHashMap;
11+
12+
public class CookieSession extends ExpressWorker implements HttpRequest, ExpressMiddleware {
13+
14+
private final static String MIDDLEWARE_NAME = "SessionCookie";
15+
16+
private final ConcurrentHashMap<String, SessionCookie> COOKIES = new ConcurrentHashMap<>();
17+
private final String COOKIE_NAME;
18+
private final long MAX_AGE;
19+
20+
public CookieSession(String cookieName, long maxAge) {
21+
this.COOKIE_NAME = cookieName;
22+
this.MAX_AGE = maxAge;
23+
}
24+
25+
@Override
26+
public String getName() {
27+
return MIDDLEWARE_NAME;
28+
}
29+
30+
@Override
31+
public void handle(Request req, Response res) {
32+
Cookie cookie = req.getCookie(COOKIE_NAME);
33+
34+
if (cookie != null && COOKIES.containsKey(cookie.getValue())) {
35+
36+
req.addMiddlewareContent(this, COOKIES.get(cookie.getValue()));
37+
} else {
38+
String token = generateSecureToken(32);
39+
40+
cookie = new Cookie(COOKIE_NAME, token).setMaxAge(MAX_AGE);
41+
res.setCookie(cookie);
42+
43+
SessionCookie sessionCookie = new SessionCookie(MAX_AGE);
44+
COOKIES.put(token, sessionCookie);
45+
46+
req.addMiddlewareContent(this, sessionCookie);
47+
}
48+
}
49+
50+
@Override
51+
long getDelay() {
52+
return 15000; // 1min
53+
}
54+
55+
@Override
56+
void update() {
57+
long current = System.currentTimeMillis();
58+
59+
COOKIES.forEach((s, o) -> {
60+
if (current > o.getCreated() + o.getMaxAge())
61+
COOKIES.remove(s);
62+
});
63+
}
64+
65+
private static String generateSecureToken(int byteLength) {
66+
SecureRandom secureRandom = new SecureRandom();
67+
byte[] token = new byte[byteLength];
68+
secureRandom.nextBytes(token);
69+
return new BigInteger(1, token).toString(16); //hex encoding
70+
}
71+
72+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package express.middleware;
2+
3+
public interface ExpressMiddleware {
4+
String getName();
5+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package express.middleware;
2+
3+
4+
import express.utils.ExtendetTimer;
5+
6+
public abstract class ExpressWorker {
7+
8+
private final ExtendetTimer TIMER = new ExtendetTimer();
9+
10+
public void start() {
11+
if (!TIMER.isActive()) {
12+
TIMER.scheduleAtFixedRate(this::update, 0, getDelay());
13+
}
14+
}
15+
16+
public void stop() {
17+
TIMER.cancel();
18+
}
19+
20+
abstract long getDelay();
21+
22+
abstract void update();
23+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package express.middleware;
2+
3+
public class SessionCookie {
4+
5+
private final long MAX_AGE;
6+
private final long CREATED;
7+
8+
private Object data;
9+
10+
public SessionCookie(long maxAge) {
11+
this.MAX_AGE = maxAge;
12+
this.CREATED = System.currentTimeMillis();
13+
}
14+
15+
public long getMaxAge() {
16+
return MAX_AGE;
17+
}
18+
19+
public long getCreated() {
20+
return CREATED;
21+
}
22+
23+
public Object getData() {
24+
return data;
25+
}
26+
27+
public Object setData(Object data) {
28+
return this.data = data;
29+
}
30+
31+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package express.utils;
2+
3+
import java.util.Date;
4+
import java.util.Timer;
5+
import java.util.TimerTask;
6+
7+
public class ExtendetTimer extends Timer {
8+
9+
private ExtendetTimerTask extendetTimerTask;
10+
private LambdaTimerTask lambdaTimerTask;
11+
private boolean isActive;
12+
13+
public void schedule(LambdaTimerTask task, long delay) {
14+
init(task);
15+
super.schedule(extendetTimerTask, delay);
16+
}
17+
18+
public void schedule(LambdaTimerTask task, Date time) {
19+
init(task);
20+
super.schedule(extendetTimerTask, time);
21+
}
22+
23+
public void schedule(LambdaTimerTask task, long delay, long period) {
24+
init(task);
25+
super.schedule(extendetTimerTask, delay, period);
26+
}
27+
28+
public void schedule(LambdaTimerTask task, Date firstTime, long period) {
29+
init(task);
30+
super.schedule(extendetTimerTask, firstTime, period);
31+
}
32+
33+
public void scheduleAtFixedRate(LambdaTimerTask task, long delay, long period) {
34+
init(task);
35+
super.scheduleAtFixedRate(extendetTimerTask, delay, period);
36+
}
37+
38+
public void scheduleAtFixedRate(LambdaTimerTask task, Date firstTime, long period) {
39+
init(task);
40+
super.scheduleAtFixedRate(extendetTimerTask, firstTime, period);
41+
}
42+
43+
private void init(LambdaTimerTask task){
44+
extendetTimerTask = new ExtendetTimerTask();
45+
isActive = true;
46+
this.lambdaTimerTask = task;
47+
}
48+
49+
public void cancel() {
50+
isActive = false;
51+
super.cancel();
52+
}
53+
54+
public boolean isActive() {
55+
return isActive;
56+
}
57+
58+
private class ExtendetTimerTask extends TimerTask {
59+
@Override
60+
public void run() {
61+
lambdaTimerTask.run();
62+
}
63+
}
64+
65+
@FunctionalInterface
66+
public interface LambdaTimerTask {
67+
void run();
68+
}
69+
70+
}

src/test/Use.java

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
package test;
22

33
import express.Express;
4+
import express.middleware.CookieSession;
5+
import express.middleware.SessionCookie;
46
import express.middleware.Static;
7+
58
import java.io.IOException;
69

710
public class Use {
@@ -12,6 +15,45 @@ public static void main(String[] args) throws IOException {
1215
// Test for static file service
1316
app.use(new Static("examplepath\\test_statics"));
1417

18+
// Test for cookie session
19+
app.use(new CookieSession("ses", 9000));
20+
21+
// Cookie session example
22+
app.get("/session", (req, res) -> {
23+
24+
/**
25+
* Get the middleware object
26+
* Every middleware who extend the request is saved in an hashmap
27+
* with the name as key, so every middleware need to implement this interface
28+
* to extend the request.
29+
*
30+
* You can access your middlewares data over getMiddlewareContent("MIDDLEWARE NAME")
31+
*/
32+
Object middlewareObject = req.getMiddlewareContent("SessionCookie");
33+
34+
// Check if the middleware has set some data
35+
if (middlewareObject != null) {
36+
37+
// Okay, now cast the object to an session cookie
38+
SessionCookie sessionCookie = (SessionCookie) middlewareObject;
39+
int count;
40+
41+
// Check if the data is null, we want to implement an simple counter
42+
if (sessionCookie.getData() == null) {
43+
// Set the default data to 1 (first request with this session cookie)
44+
count = (Integer) sessionCookie.setData(1);
45+
} else {
46+
// Now we know that the cookie has an integer as data property, increase this
47+
count = (Integer) sessionCookie.setData((Integer) sessionCookie.getData() + 1);
48+
}
49+
50+
// Send an happy message :D
51+
res.send("You take use of your session cookie " + count + " times :)");
52+
} else {
53+
res.send("No session cookie middleware used.");
54+
}
55+
});
56+
1557

1658
app.listen(() -> System.out.println("Express is listening!"));
1759
}

0 commit comments

Comments
 (0)