diff --git a/.classpath b/.classpath index 49a1664..34db808 100644 --- a/.classpath +++ b/.classpath @@ -7,5 +7,11 @@ + + + + + + diff --git a/src/dev/peerat/framework/HttpReader.java b/src/dev/peerat/framework/HttpReader.java index fd9c81b..3c0b391 100644 --- a/src/dev/peerat/framework/HttpReader.java +++ b/src/dev/peerat/framework/HttpReader.java @@ -39,9 +39,9 @@ public class HttpReader{ this.reader = origin.reader; } - HttpReader(InputStream in, BufferedReader reader){ + HttpReader(InputStream in){ this.in = in; - this.reader = reader; + this.reader = new BufferedReader(new InputStreamReader(in)); } public boolean isClosed(){ @@ -87,7 +87,7 @@ public class HttpReader{ } public J readJson() throws Exception{ - int length = Integer.parseInt(this.headers.get("content-length")); + int length = Integer.parseInt(getHeader("content-length")); //to limit char[] content = new char[length]; read(content); diff --git a/src/dev/peerat/framework/HttpWriter.java b/src/dev/peerat/framework/HttpWriter.java index 08f999f..419a2c4 100644 --- a/src/dev/peerat/framework/HttpWriter.java +++ b/src/dev/peerat/framework/HttpWriter.java @@ -22,10 +22,7 @@ public class HttpWriter{ this.writer = origin.writer; } - public HttpWriter(OutputStream out, BufferedWriter writer){ - this.out = out; - this.writer = writer; - } + public HttpWriter(){} public void write(byte[] buffer) throws IOException{ this.out.write(buffer); @@ -119,6 +116,7 @@ public class HttpWriter{ private static String codeMessage(int code){ if(code == 100) return " Continue"; + if(code == 101) return " Switching Protocols"; if(code >> 8 == 0) return HEIGHTBITS[code-200]; return NINEBITS[code-300]; } diff --git a/src/dev/peerat/framework/RouteMapper.java b/src/dev/peerat/framework/RouteMapper.java index cf323ef..f733116 100644 --- a/src/dev/peerat/framework/RouteMapper.java +++ b/src/dev/peerat/framework/RouteMapper.java @@ -50,7 +50,7 @@ public class RouteMapper{ matcher = pattern.matcher(path); if(matcher.matches()){ if(route.websocket()){ - switchToWebSocket(reader, writer); + switchToWebSocket(reader, context); reader = new WebSocketReader(reader); writer = new WebSocketWriter(writer); if(route.needLogin()){ @@ -75,6 +75,54 @@ public class RouteMapper{ return false; } + public Response execTomcat(Context context, HttpReader reader, HttpWriter writer) throws Exception{ + Response result = null; + Matcher matcher = null; + + String path = context.getPath(); + + synchronized(responses){ + for(int i = 0; i < responses.length; i++){ + Response response = responses[i]; + Route route = routes[i]; + Pattern pattern = patterns[i]; + matcher = pattern.matcher(path); + if(matcher.matches()){ + if(route.websocket()){ + switchToWebSocket(reader, context); + result = response; + break; + } + if((!context.isLogged()) && route.needLogin()){ + writer.response(401, router.getDefaultHeaders(context.getType())); + return null; + } + order(i); + result = response; + break; + } + } + } + return result; + } + + Matcher matchTomcat(Context context){ + Matcher result = null; + String path = context.getPath(); + synchronized(responses){ + for(int i = 0; i < responses.length; i++){ + Pattern pattern = patterns[i]; + Matcher match = pattern.matcher(path); + if(match.matches()){ + result = match; + break; + } + } + } + + return result; + } + private void order(int index){ if(this.calls == null) return; int call = this.calls[index]++; @@ -106,17 +154,15 @@ public class RouteMapper{ array[after] = tmp; } - private void switchToWebSocket(HttpReader reader, HttpWriter writer) throws Exception{ + private void switchToWebSocket(HttpReader reader, Context context) throws Exception{ String key = reader.getHeader("Sec-WebSocket-Key"); if (key == null) throw new IllegalArgumentException(); - - writer.write("HTTP/1.1 101 Switching Protocols\n"); - writer.write("Connection: Upgrade\n"); - writer.write("Upgrade: websocket\n"); - writer.write("Sec-WebSocket-Accept: " + printBase64Binary(MessageDigest.getInstance("SHA-1") - .digest((key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").getBytes("UTF-8"))) + "\n"); - writer.write("\n"); - writer.flush(); + + context.response(101, + "Connection: Upgrade", + "Upgrade: websocket", + "Sec-WebSocket-Accept: "+printBase64Binary(MessageDigest.getInstance("SHA-1") + .digest((key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").getBytes("UTF-8")))); } diff --git a/src/dev/peerat/framework/Router.java b/src/dev/peerat/framework/Router.java index 5f27295..7329539 100644 --- a/src/dev/peerat/framework/Router.java +++ b/src/dev/peerat/framework/Router.java @@ -5,12 +5,11 @@ import java.net.ServerSocket; import java.net.Socket; import java.util.function.Consumer; import java.util.function.Function; +import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.net.ssl.SSLServerSocket; import javax.net.ssl.SSLServerSocketFactory; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import org.jose4j.jwa.AlgorithmConstraints.ConstraintType; import org.jose4j.jwk.RsaJsonWebKey; @@ -104,6 +103,15 @@ public class Router{ if(noFileFound != null) noFileFound.exec(null, context, reader, writer); } + Response execTomcat(Context context, HttpReader reader, HttpWriter writer) throws Exception{ + Response result = this.mappers[context.getType().ordinal()].execTomcat(context, reader, writer); + return result != null ? result : noFileFound; + } + + Matcher matchTomcat(Context context){ + return this.mappers[context.getType().ordinal()].matchTomcat(context); + } + public Router configureSSL(String keyStore, String keyStorePassword){ System.setProperty("javax.net.ssl.keyStore", keyStore); System.setProperty("javax.net.ssl.keyStorePassword", keyStorePassword); diff --git a/src/dev/peerat/framework/TomcatHttpReader.java b/src/dev/peerat/framework/TomcatHttpReader.java index fa661fb..da150f2 100644 --- a/src/dev/peerat/framework/TomcatHttpReader.java +++ b/src/dev/peerat/framework/TomcatHttpReader.java @@ -1,5 +1,8 @@ package dev.peerat.framework; +import java.util.HashMap; +import java.util.Map; + import javax.servlet.http.HttpServletRequest; public class TomcatHttpReader extends HttpReader{ @@ -7,7 +10,7 @@ public class TomcatHttpReader extends HttpReader{ private HttpServletRequest request; public TomcatHttpReader(HttpServletRequest request) throws Exception{ - super(request.getInputStream(), request.getReader()); + super(request.getInputStream()); this.request = request; } @@ -21,7 +24,30 @@ public class TomcatHttpReader extends HttpReader{ @Override public String getHeader(String key){ - return request.getHeader(key); + String result; + result = request.getHeader(key); + if(result != null) return result; + result = request.getHeader(key.toLowerCase()); + if(result != null) return result; + result = request.getHeader(key.toUpperCase()); + if(result != null) return result; + result = request.getHeader(firstUpper(key.toLowerCase())); + if(result != null) return result; + return null; } + private String firstUpper(String value){ + boolean beUpper = true; + String result = ""; + for(char c : value.toCharArray()){ + if(beUpper){ + result+=Character.toUpperCase(c); + beUpper = false; + }else if(c == '-'){ + beUpper = true; + result+='-'; + }else result+=c; + } + return result; + } } diff --git a/src/dev/peerat/framework/TomcatHttpWriter.java b/src/dev/peerat/framework/TomcatHttpWriter.java index c909ae4..7a30caa 100644 --- a/src/dev/peerat/framework/TomcatHttpWriter.java +++ b/src/dev/peerat/framework/TomcatHttpWriter.java @@ -1,16 +1,22 @@ package dev.peerat.framework; -import java.io.BufferedWriter; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.List; import javax.servlet.http.HttpServletResponse; public class TomcatHttpWriter extends HttpWriter{ private HttpServletResponse response; + private List senders; public TomcatHttpWriter(HttpServletResponse response) throws Exception{ - super(response.getOutputStream(), new BufferedWriter(response.getWriter())); + super(); this.response = response; + this.senders = new ArrayList<>(); } @Override @@ -21,4 +27,44 @@ public class TomcatHttpWriter extends HttpWriter{ response.setHeader(split[0], split[1]); } } + + @Override + public void write(String message) throws IOException { + this.senders.add((out) -> { + PrintWriter writer = new PrintWriter(out); + writer.write(message); + writer.flush(); + }); + } + + @Override + public void write(byte[] buffer) throws IOException{ + this.senders.add((out) -> { + out.write(buffer); + out.flush(); + }); + } + + @Override + public void flush() throws IOException{ + if(out == null) return; + flush(out); + } + + private OutputStream out; + + public void free(OutputStream out){ + this.out = out; + } + + public void flush(OutputStream out) throws IOException{ + for(Output sender : senders) sender.accept(out); + senders.clear(); + } + + public static interface Output{ + + void accept(OutputStream out) throws IOException; + + } } diff --git a/src/dev/peerat/framework/TomcatServlet.java b/src/dev/peerat/framework/TomcatServlet.java index b8c4f20..bbac453 100644 --- a/src/dev/peerat/framework/TomcatServlet.java +++ b/src/dev/peerat/framework/TomcatServlet.java @@ -1,6 +1,7 @@ package dev.peerat.framework; import java.io.IOException; +import java.io.OutputStream; import java.lang.reflect.Method; import javax.servlet.ServletConfig; @@ -14,11 +15,26 @@ import org.jose4j.jwt.consumer.InvalidJwtException; public class TomcatServlet extends HttpServlet{ private static Router router; + private static Locker sessions; static void setRouter(Router router){ TomcatServlet.router = router; } + static { + sessions = new Locker<>(); + sessions.listenAsync((container) -> { + try{ + Thread.sleep(1000); + WebSocketWriter writer = new WebSocketWriter(new TomcatHttpWriter(container.getResponse())); + writer.write("{\"test\":4}"); + writer.flush(); + }catch(Exception e){ + e.printStackTrace(); + } + }); + } + @Override public void init(ServletConfig config) throws ServletException{ try{ @@ -33,25 +49,78 @@ public class TomcatServlet extends HttpServlet{ } public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { + if(request.getHeader("Sec-WebSocket-Key") != null){ + Context context = null; + TomcatHttpReader reader = null; + TomcatHttpWriter writer = null; + try{ + reader = new TomcatHttpReader(request); + writer = new TomcatHttpWriter(response); + context = new Context(RequestType.GET, request.getPathInfo(), isLogin(router, RequestType.GET, reader, writer), writer, router.getDefaultHeaders(RequestType.GET)); + Response resp = router.execTomcat(context, reader, writer); + OutputStream output = response.getOutputStream(); + writer.flush(output); + writer.free(output); + WebSocketReader wreader = new WebSocketReader(reader); + sessions.setValue(new TomcatWebsocketContainer(wreader, response)); + }catch(InvalidJwtException e){ + }catch(Exception e){ + response.setHeader("exce", e.getMessage()); + if(context != null && context.getResponseCode() == 0){ + try{ + context.response(500); + }catch(Exception ex){} + } + throw new IOException(e); + } + if(context != null) router.getLogger().setValue(context); + return; + } + doIt(RequestType.GET, request, response); + } + + public void doDelete(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { + doIt(RequestType.DELETE, request, response); + } + + public void doHead(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { + doIt(RequestType.HEAD, request, response); + } + + public void doOptions(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { + doIt(RequestType.OPTIONS, request, response); + } + + public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { + doIt(RequestType.POST, request, response); + } + + public void doPut(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { + doIt(RequestType.PUT, request, response); + } + + public void doTrace(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { + doIt(RequestType.TRACE, request, response); + } + + private void doIt(RequestType type, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { Context context = null; - HttpReader reader = null; - HttpWriter writer = null; + TomcatHttpReader reader = null; + TomcatHttpWriter writer = null; try{ reader = new TomcatHttpReader(request); writer = new TomcatHttpWriter(response); - context = new Context(RequestType.GET, request.getPathInfo(), isLogin(router, RequestType.GET, reader, writer), writer); + context = new Context(type, request.getPathInfo(), isLogin(router, type, reader, writer), writer, router.getDefaultHeaders(type)); router.exec(context, reader, writer); - writer.flush(); - writer.close(); + writer.flush(response.getOutputStream()); }catch(InvalidJwtException e){ }catch(Exception e){ if(context != null && context.getResponseCode() == 0){ try{ context.response(500); - writer.flush(); - writer.close(); }catch(Exception ex){} } + throw new IOException(e); } if(context != null) router.getLogger().setValue(context); } @@ -72,4 +141,23 @@ public class TomcatServlet extends HttpServlet{ } } + private static class TomcatWebsocketContainer{ + + private WebSocketReader reader; + private HttpServletResponse response; + + public TomcatWebsocketContainer(WebSocketReader reader, HttpServletResponse response){ + this.reader = reader; + this.response = response; + } + + public WebSocketReader getReader(){ + return reader; + } + + public HttpServletResponse getResponse(){ + return response; + } + + } }