commit 8d5a6524329a8a3a06a9460275336423ab38e4e2 Author: jeffcheasey88 <66554203+jeffcheasey88@users.noreply.github.com> Date: Sat Mar 25 19:47:30 2023 +0100 first commit diff --git a/.classpath b/.classpath new file mode 100644 index 0000000..da9ddb9 --- /dev/null +++ b/.classpath @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..30263e5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +bin/ +.settings/ \ No newline at end of file diff --git a/.project b/.project new file mode 100644 index 0000000..710842b --- /dev/null +++ b/.project @@ -0,0 +1,17 @@ + + + Endg-2048-Backend + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/json-simple-1.1.1.jar b/json-simple-1.1.1.jar new file mode 100644 index 0000000..dfd5856 Binary files /dev/null and b/json-simple-1.1.1.jar differ diff --git a/src/be/jeffcheasey88/Main.java b/src/be/jeffcheasey88/Main.java new file mode 100644 index 0000000..082773a --- /dev/null +++ b/src/be/jeffcheasey88/Main.java @@ -0,0 +1,70 @@ +package be.jeffcheasey88; + +import java.net.ServerSocket; +import java.net.Socket; +import java.util.regex.Matcher; + +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; + +import be.jeffcheasey88.webserver.Client; +import be.jeffcheasey88.webserver.HttpReader; +import be.jeffcheasey88.webserver.HttpUtil; +import be.jeffcheasey88.webserver.HttpWriter; +import be.jeffcheasey88.webserver.Response; +import be.jeffcheasey88.webserver.Router; + +public class Main { + + public static void main(String[] args) throws Exception { + Router router = new Router(); + + router.setDefault(new Response(){ + @Override + public void exec(Matcher matcher, HttpReader reader, HttpWriter writer) throws Exception { + HttpUtil.switchToWebSocket(reader, writer); + + JSONObject welcome = new JSONObject(); + welcome.put("action", "state"); + welcome.put("game", "game1"); + HttpUtil.sendWebSocket(writer, welcome.toJSONString()); + + Player player = new Player(writer); + Party party = Party.pick(); + party.join(player); + while(!party.ready()) Thread.sleep(10); + Player opposite = party.getOpposite(player); + sendState(writer, opposite.getState()); + + while(!reader.isClosed()){ + JSONObject json = (JSONObject) new JSONParser().parse(HttpUtil.readWebSocket(reader)); + String action = (String)json.get("action"); + if(action.equals("restart")){ + player.setState(null); + sendState(writer, null); + }else if(action.equals("state")){ + player.setState((String)json.get("value")); + } + + sendState(opposite.getWriter(), player.getState()); + } + } + + private void sendState(HttpWriter writer, String state) throws Exception{ + JSONObject set = new JSONObject(); + set.put("action", "state"); + set.put("game", "game2"); + if(state != null) set.put("value", state); + HttpUtil.sendWebSocket(writer, set.toJSONString()); + } + }); + + ServerSocket server = new ServerSocket(80); + while(!server.isClosed()){ + Socket socket = server.accept(); + Client client = new Client(socket, router); + client.start(); + } + } + +} diff --git a/src/be/jeffcheasey88/Party.java b/src/be/jeffcheasey88/Party.java new file mode 100644 index 0000000..cc60f7f --- /dev/null +++ b/src/be/jeffcheasey88/Party.java @@ -0,0 +1,39 @@ +package be.jeffcheasey88; + +import java.util.HashSet; +import java.util.Set; + +public class Party { + + private static Set parties = new HashSet<>(); + + public static Party pick(){ + for(Party party : parties){ + if(!party.ready()) return party; + } + Party party = new Party(); + parties.add(party); + return party; + } + + private Player one; + private Player second; + + private Party() {} + + public void join(Player player){ + if(this.one == null){ + this.one = player; + }else{ + this.second = player; + } + } + + public boolean ready(){ + return one != null && second != null; + } + + public Player getOpposite(Player current){ + return current.equals(one) ? second : one; + } +} diff --git a/src/be/jeffcheasey88/Player.java b/src/be/jeffcheasey88/Player.java new file mode 100644 index 0000000..e7a5adc --- /dev/null +++ b/src/be/jeffcheasey88/Player.java @@ -0,0 +1,26 @@ +package be.jeffcheasey88; + +import be.jeffcheasey88.webserver.HttpWriter; + +public class Player { + + private String state; + private final HttpWriter writer; + + public Player(HttpWriter writer){ + this.writer = writer; + } + + public void setState(String state){ + this.state = state; + } + + public String getState(){ + return this.state; + } + + public HttpWriter getWriter(){ + return this.writer; + } + +} diff --git a/src/be/jeffcheasey88/webserver/Client.java b/src/be/jeffcheasey88/webserver/Client.java new file mode 100644 index 0000000..fd4f825 --- /dev/null +++ b/src/be/jeffcheasey88/webserver/Client.java @@ -0,0 +1,29 @@ +package be.jeffcheasey88.webserver; + +import java.net.Socket; +import java.util.Arrays; + +public class Client extends Thread{ + + private HttpReader reader; + private HttpWriter writer; + private Router router; + + public Client(Socket socket, Router router) throws Exception{ + this.reader = new HttpReader(socket); + this.writer = new HttpWriter(socket); + this.router = router; + } + + @Override + public void run(){ + try { + String[] headers = reader.readLine().split("\\s"); + System.out.println(Arrays.toString(headers)); + + router.exec(headers[0], headers[1], reader, writer); + writer.flush(); + writer.close(); + } catch (Exception e){} + } +} \ No newline at end of file diff --git a/src/be/jeffcheasey88/webserver/HttpReader.java b/src/be/jeffcheasey88/webserver/HttpReader.java new file mode 100644 index 0000000..7438e83 --- /dev/null +++ b/src/be/jeffcheasey88/webserver/HttpReader.java @@ -0,0 +1,40 @@ +package be.jeffcheasey88.webserver; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.Socket; + +public class HttpReader { + + private Socket socket; + private InputStream in; + private BufferedReader reader; + + public HttpReader(Socket socket) throws Exception{ + this.socket = socket; + this.in = socket.getInputStream(); + this.reader = new BufferedReader(new InputStreamReader(in)); + } + + public boolean isClosed(){ + return this.socket.isClosed(); + } + + public int read(byte[] buffer) throws IOException{ + return this.in.read(buffer); + } + + public int read(char[] buffer) throws IOException { + return this.reader.read(buffer); + } + + public String readLine() throws IOException{ + return this.reader.readLine(); + } + + public boolean ready() throws IOException{ + return this.reader.ready(); + } +} \ No newline at end of file diff --git a/src/be/jeffcheasey88/webserver/HttpUtil.java b/src/be/jeffcheasey88/webserver/HttpUtil.java new file mode 100644 index 0000000..c7175a4 --- /dev/null +++ b/src/be/jeffcheasey88/webserver/HttpUtil.java @@ -0,0 +1,334 @@ +package be.jeffcheasey88.webserver; + +import java.security.MessageDigest; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.json.simple.parser.JSONParser; + +public class HttpUtil { + + private HttpUtil(){} + + public static void responseHeaders(HttpWriter writer, int code, String... headers) throws Exception{ + writer.write("HTTP/1.1 "+code+" "+codeMessage(code)+"\n"); + for(String header : headers) writer.write(header+"\n"); + writer.write("\n"); + writer.flush(); + } + + public static void skipHeaders(HttpReader reader) throws Exception{ + String line; + while(((line = reader.readLine()) != null) && (line.length() > 0)); + } + + public static List readMultiPartData(HttpReader reader) throws Exception{ + List list = new ArrayList<>(); + + reader.readLine(); + + while(reader.ready()){ + String line; + while(((line = reader.readLine()) != null) && (line.length() > 0)){ + + } + String buffer = ""; + while(((line = reader.readLine()) != null) && (!line.startsWith("------WebKitFormBoundary"))){ + buffer+=line; + } + list.add(buffer); + } + + return list; + } + + public static void switchToWebSocket(HttpReader reader, HttpWriter writer) throws Exception{ + String key = readWebSocketKey(reader); + 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(); + } + + private static Pattern WEBSOCKET_KEY = Pattern.compile("Sec-WebSocket-Key: (.*)"); + + public static String readWebSocketKey(HttpReader reader) throws Exception { + String line; + String key = null; + while(((line = reader.readLine()) != null) && (line.length() > 0)){ + if(key != null){ + continue; + } + Matcher matcher = WEBSOCKET_KEY.matcher(line); + if(matcher.matches()) key = matcher.group(1); + } + return key; + } + + private static Pattern AUTORIZATION = Pattern.compile("Authorization: Bearer (.*)"); + + public static String readAuthorization(HttpReader reader) throws Exception { + String line; + String key = null; + while(((line = reader.readLine()) != null) && (line.length() > 0)){ + Matcher matcher = AUTORIZATION.matcher(line); + if(matcher.matches()){ + key = matcher.group(1); + break; + } + } + return key; + } + + public static Object readJson(HttpReader reader) throws Exception{ + String line = ""; + while(reader.ready()){ + char[] c = new char[1]; + reader.read(c); + line+=c[0]; + if(c[0] == '}'){ + Object parse; + try { + parse = new JSONParser().parse(line); + if(parse != null) return parse; + }catch(Exception e){} + } + } + return null; + } + + //I found this code on StackOverFlow !!!!! (and the write too) + public static String readWebSocket(HttpReader reader) throws Exception{ + int buffLenth = 1024; + int len = 0; + byte[] b = new byte[buffLenth]; + //rawIn is a Socket.getInputStream(); + while(true){ + len = reader.read(b); + if(len!=-1){ + byte rLength = 0; + int rMaskIndex = 2; + int rDataStart = 0; + //b[0] is always text in my case so no need to check; + byte data = b[1]; + byte op = (byte) 127; + rLength = (byte) (data & op); + + if(rLength==(byte)126) rMaskIndex=4; + if(rLength==(byte)127) rMaskIndex=10; + + byte[] masks = new byte[4]; + + int j=0; + int i=0; + for(i=rMaskIndex;i<(rMaskIndex+4);i++){ + masks[j] = b[i]; + j++; + } + + rDataStart = rMaskIndex + 4; + + int messLen = len - rDataStart; + + byte[] message = new byte[messLen]; + + for(i=rDataStart, j=0; i= 126 && rawData.length <= 65535){ + frame[1] = (byte) 126; + int len = rawData.length; + frame[2] = (byte)((len >> 8 ) & (byte)255); + frame[3] = (byte)(len & (byte)255); + frameCount = 4; + }else{ + frame[1] = (byte) 127; + int len = rawData.length; + frame[2] = (byte)((len >> 56 ) & (byte)255); + frame[3] = (byte)((len >> 48 ) & (byte)255); + frame[4] = (byte)((len >> 40 ) & (byte)255); + frame[5] = (byte)((len >> 32 ) & (byte)255); + frame[6] = (byte)((len >> 24 ) & (byte)255); + frame[7] = (byte)((len >> 16 ) & (byte)255); + frame[8] = (byte)((len >> 8 ) & (byte)255); + frame[9] = (byte)(len & (byte)255); + frameCount = 10; + } + + int bLength = frameCount + rawData.length; + + byte[] reply = new byte[bLength]; + + int bLim = 0; + for(int i=0; i= 3; j += 3) { + paramArrayOfchar[paramInt3++] = encode(paramArrayOfbyte[j] >> 2); + paramArrayOfchar[paramInt3++] = encode((paramArrayOfbyte[j] & 0x3) << 4 | paramArrayOfbyte[j + 1] >> 4 & 0xF); + paramArrayOfchar[paramInt3++] = encode((paramArrayOfbyte[j + 1] & 0xF) << 2 | paramArrayOfbyte[j + 2] >> 6 & 0x3); + paramArrayOfchar[paramInt3++] = encode(paramArrayOfbyte[j + 2] & 0x3F); + i -= 3; + } + if (i == 1) { + paramArrayOfchar[paramInt3++] = encode(paramArrayOfbyte[j] >> 2); + paramArrayOfchar[paramInt3++] = encode((paramArrayOfbyte[j] & 0x3) << 4); + paramArrayOfchar[paramInt3++] = '='; + paramArrayOfchar[paramInt3++] = '='; + } + if (i == 2) { + paramArrayOfchar[paramInt3++] = encode(paramArrayOfbyte[j] >> 2); + paramArrayOfchar[paramInt3++] = encode((paramArrayOfbyte[j] & 0x3) << 4 | paramArrayOfbyte[j + 1] >> 4 & 0xF); + paramArrayOfchar[paramInt3++] = encode((paramArrayOfbyte[j + 1] & 0xF) << 2); + paramArrayOfchar[paramInt3++] = '='; + } + return paramInt3; + } + + private static char encode(int paramInt) { + return encodeMap[paramInt & 0x3F]; + } + private static final char[] encodeMap = initEncodeMap(); + + private static char[] initEncodeMap() { + char[] arrayOfChar = new char[64]; + byte b; + for (b = 0; b < 26; b++) + arrayOfChar[b] = (char)(65 + b); + for (b = 26; b < 52; b++) + arrayOfChar[b] = (char)(97 + b - 26); + for (b = 52; b < 62; b++) + arrayOfChar[b] = (char)(48 + b - 52); + arrayOfChar[62] = '+'; + arrayOfChar[63] = '/'; + return arrayOfChar; + } +} \ No newline at end of file diff --git a/src/be/jeffcheasey88/webserver/HttpWriter.java b/src/be/jeffcheasey88/webserver/HttpWriter.java new file mode 100644 index 0000000..7fc8389 --- /dev/null +++ b/src/be/jeffcheasey88/webserver/HttpWriter.java @@ -0,0 +1,36 @@ +package be.jeffcheasey88.webserver; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.net.Socket; +import java.nio.charset.StandardCharsets; + +public class HttpWriter{ + + private OutputStream out; + private BufferedWriter writer; + + public HttpWriter(Socket socket) throws Exception{ + this.out = socket.getOutputStream(); + this.writer = new BufferedWriter(new OutputStreamWriter(out, StandardCharsets.UTF_8)); + } + + public void write(byte[] buffer) throws IOException{ + this.out.write(buffer); + this.out.flush(); + } + + public void write(String message) throws IOException{ + this.writer.write(message); + } + + public void flush() throws IOException{ + this.writer.flush(); + } + + public void close() throws IOException{ + this.writer.close(); + } +} \ No newline at end of file diff --git a/src/be/jeffcheasey88/webserver/Response.java b/src/be/jeffcheasey88/webserver/Response.java new file mode 100644 index 0000000..9c543ae --- /dev/null +++ b/src/be/jeffcheasey88/webserver/Response.java @@ -0,0 +1,9 @@ +package be.jeffcheasey88.webserver; + +import java.util.regex.Matcher; + +public interface Response{ + + void exec(Matcher matcher, HttpReader reader, HttpWriter writer) throws Exception ; + +} \ No newline at end of file diff --git a/src/be/jeffcheasey88/webserver/Route.java b/src/be/jeffcheasey88/webserver/Route.java new file mode 100644 index 0000000..26f5ebe --- /dev/null +++ b/src/be/jeffcheasey88/webserver/Route.java @@ -0,0 +1,15 @@ +package be.jeffcheasey88.webserver; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface Route { + + String path() default "^.*$"; + String type() default "GET"; + +} \ No newline at end of file diff --git a/src/be/jeffcheasey88/webserver/Router.java b/src/be/jeffcheasey88/webserver/Router.java new file mode 100644 index 0000000..f62c8bf --- /dev/null +++ b/src/be/jeffcheasey88/webserver/Router.java @@ -0,0 +1,49 @@ +package be.jeffcheasey88.webserver; + +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class Router{ + + private Map responses; + private Map patterns; + private Response noFileFound; + + public Router() throws Exception{ + this.responses = new HashMap<>(); + this.patterns = new HashMap<>(); + } + + public void register(Response response){ + try { + Method method = response.getClass().getDeclaredMethod("exec", Response.class.getDeclaredMethods()[0].getParameterTypes()); + Route route = method.getAnnotation(Route.class); + + this.responses.put(response, route); + this.patterns.put(response, Pattern.compile(route.path())); + } catch (Exception e){ + throw new IllegalArgumentException(e); + } + } + + public void setDefault(Response response){ + this.noFileFound = response; + } + + public void exec(String type, String path,HttpReader reader, HttpWriter writer) throws Exception { + for(Entry routes : this.responses.entrySet()){ + if(routes.getValue().type().equals(type)){ + Matcher matcher = this.patterns.get(routes.getKey()).matcher(path); + if(matcher.matches()){ + routes.getKey().exec(matcher, reader, writer); + return; + } + } + } + if(noFileFound != null) noFileFound.exec(null, reader, writer); + } +} \ No newline at end of file