commit a1d35c31316c6cc0adef417aa654c1141aa7a65a Author: jeffcheasey88 <66554203+jeffcheasey88@users.noreply.github.com> Date: Wed Jul 5 14:34:25 2023 +0200 First Commit, FrameWork V1 diff --git a/.classpath b/.classpath new file mode 100644 index 0000000..77f334f --- /dev/null +++ b/.classpath @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..54ac269 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.settings/ +bin/ +.project diff --git a/jose4j-0.9.3.jar b/jose4j-0.9.3.jar new file mode 100644 index 0000000..e073555 Binary files /dev/null and b/jose4j-0.9.3.jar differ 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/password4j-1.6.3.jar b/password4j-1.6.3.jar new file mode 100644 index 0000000..4254563 Binary files /dev/null and b/password4j-1.6.3.jar differ diff --git a/slf4j-api-2.0.6.jar b/slf4j-api-2.0.6.jar new file mode 100644 index 0000000..a2cb802 Binary files /dev/null and b/slf4j-api-2.0.6.jar differ diff --git a/src/be/jeffcheasey88/peeratcode/framework/Client.java b/src/be/jeffcheasey88/peeratcode/framework/Client.java new file mode 100644 index 0000000..4c4da80 --- /dev/null +++ b/src/be/jeffcheasey88/peeratcode/framework/Client.java @@ -0,0 +1,60 @@ +package be.jeffcheasey88.peeratcode.framework; + +import java.net.Socket; +import java.util.Arrays; + +import org.jose4j.jwa.AlgorithmConstraints.ConstraintType; +import org.jose4j.jws.AlgorithmIdentifiers; +import org.jose4j.jwt.JwtClaims; +import org.jose4j.jwt.consumer.JwtConsumer; +import org.jose4j.jwt.consumer.JwtConsumerBuilder; + +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)); + reader.readHeaders(); + + router.exec(RequestType.valueOf(headers[0]), headers[1], isLogin(reader), reader, writer); + writer.flush(); + writer.close(); + }catch(Exception e){ + e.printStackTrace(); + } + } + + private User isLogin(HttpReader reader) throws Exception{ + String auth = reader.getHeader("Authorization"); + if(auth == null) return null; + auth = auth.substring(7); + try{ + JwtConsumer jwtConsumer = new JwtConsumerBuilder() + .setRequireExpirationTime() + .setAllowedClockSkewInSeconds(30) + .setExpectedIssuer(this.router.getTokenIssuer()) + .setVerificationKey(this.router.getWebKey().getKey()) + .setJwsAlgorithmConstraints(ConstraintType.PERMIT, AlgorithmIdentifiers.RSA_USING_SHA256).build(); + + JwtClaims jwtClaims = jwtConsumer.processToClaims(auth); + return new User(jwtClaims); + }catch(Exception e){ + writer.response(401, "Access-Control-Allow-Origin: *"); + writer.flush(); + writer.close(); + throw e; + } + } +} \ No newline at end of file diff --git a/src/be/jeffcheasey88/peeratcode/framework/HttpReader.java b/src/be/jeffcheasey88/peeratcode/framework/HttpReader.java new file mode 100644 index 0000000..7239e0e --- /dev/null +++ b/src/be/jeffcheasey88/peeratcode/framework/HttpReader.java @@ -0,0 +1,138 @@ +package be.jeffcheasey88.peeratcode.framework; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.Socket; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.json.simple.parser.JSONParser; + +public class HttpReader{ + + private static Pattern HEADER_PATTERN = Pattern.compile("^([^:]*):\\s+(.*)$"); + + private Socket socket; + private InputStream in; + private BufferedReader reader; + + private Map headers; + + public HttpReader(Socket socket) throws Exception{ + this.socket = socket; + this.in = socket.getInputStream(); + this.reader = new BufferedReader(new InputStreamReader(in)); + this.headers = new HashMap<>(); + } + + public HttpReader(HttpReader origin) throws Exception{ + this.socket = origin.socket; + this.in = origin.in; + this.reader = origin.reader; + } + + public boolean isClosed(){ + return this.socket.isClosed(); + } + + public void readHeaders() throws Exception{ + String line; + while(((line = reader.readLine()) != null) && (line.length() > 0)){ + Matcher matcher = HEADER_PATTERN.matcher(line); + matcher.matches(); + this.headers.put(matcher.group(1).toLowerCase(), matcher.group(2)); + } + } + + public String getHeader(String key){ + return this.headers.get(key.toLowerCase()); + } + + 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(); + } + + public int readInt() throws Exception{ + int result = 0; + result += this.in.read() << 24; + result += this.in.read() << 16; + result += this.in.read() << 8; + result += this.in.read(); + return result; + } + + public T readJson() throws Exception{ + String line = ""; + while (ready()){ + char[] c = new char[1]; + read(c); + line += c[0]; + if (c[0] == '}'){ + Object parse; + try { + parse = new JSONParser().parse(line); + if (parse != null) + return (T) parse; + }catch(Exception e){} + } + } + return null; + } + + /* + * +------WebKitFormBoundaryNUjiLBAMuX2dhxU7 +Content-Disposition: form-data; name="answer" + +12 +------WebKitFormBoundaryNUjiLBAMuX2dhxU7 +Content-Disposition: form-data; name="filename" + +webpack.js +------WebKitFormBoundaryNUjiLBAMuX2dhxU7 +Content-Disposition: form-data; name="code_file"; filename="webpack.js" +Content-Type: text/javascript + + +------WebKitFormBoundaryNUjiLBAMuX2dhxU7-- + * + */ + + public List readMultiPartData() throws Exception{ + List list = new ArrayList<>(); + String boundary = getHeader("content-type"); + if(boundary == null) return list; + boundary = boundary.split(";")[1].split("=")[1]; + readLine(); + + while(ready()){ + String line; + while (((line = readLine()) != null) && (line.length() > 0)); + String buffer = ""; + while (((line = readLine()) != null) && (!line.startsWith("--"+boundary))){ + buffer += line; + } + list.add(buffer); + } + + return list; + } +} \ No newline at end of file diff --git a/src/be/jeffcheasey88/peeratcode/framework/HttpWriter.java b/src/be/jeffcheasey88/peeratcode/framework/HttpWriter.java new file mode 100644 index 0000000..24356df --- /dev/null +++ b/src/be/jeffcheasey88/peeratcode/framework/HttpWriter.java @@ -0,0 +1,103 @@ +package be.jeffcheasey88.peeratcode.framework; + +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 HttpWriter(HttpWriter origin) throws Exception{ + this.out = origin.out; + this.writer = origin.writer; + } + + 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(); + } + + public void response(int code, String... headers) throws Exception{ + write("HTTP/1.1 "+code+codeMessage(code)+"\n"); + for(String header : headers) write(header+"\n"); + write("\n"); + flush(); + StackTraceElement[] e = Thread.currentThread().getStackTrace(); + System.out.println(e[2]+" -> response "+code); + } + + private static String[] HEIGHTBITS = new String[7]; + private static String[] NINEBITS = new String[206]; + + static { + HEIGHTBITS[0] = " OK"; + HEIGHTBITS[1] = " Created"; + HEIGHTBITS[2] = " Accepted"; + HEIGHTBITS[3] = " Non-Authoritative Information"; + HEIGHTBITS[4] = " No Content"; + HEIGHTBITS[5] = " Reset Content"; + HEIGHTBITS[6] = " Partial Content"; + + NINEBITS[0] = " Multiple Choices"; + NINEBITS[1] = " Moved Permanently"; + NINEBITS[2] = " Temporary Redirect"; + NINEBITS[3] = " See Other"; + NINEBITS[4] = " Not Modified"; + NINEBITS[5] = " Use Proxy"; + + NINEBITS[100] = " Bad Request"; + NINEBITS[101] = " Unauthorized"; + NINEBITS[102] = " Payment Required"; + NINEBITS[103] = " Forbidden"; + NINEBITS[104] = " Not Found"; + NINEBITS[105] = " Method Not Allowed"; + NINEBITS[106] = " Not Acceptable"; + NINEBITS[107] = " Proxy Authentication Required"; + NINEBITS[108] = " Request Time-Out"; + NINEBITS[109] = " Conflict"; + NINEBITS[110] = " Gone"; + NINEBITS[111] = " Length Required"; + NINEBITS[112] = " Precondition Failed"; + NINEBITS[113] = " Request Entity Too Large"; + NINEBITS[114] = " Request-URI Too Large"; + NINEBITS[115] = " Unsupported Media Type"; + NINEBITS[123] = " Locked"; + NINEBITS[125] = " Too Early"; + + NINEBITS[200] = " Internal Server Error"; + NINEBITS[201] = " Not Implemented"; + NINEBITS[202] = " Bad Gateway"; + NINEBITS[203] = " Service Unavailable"; + NINEBITS[204] = " Gateway Timeout"; + NINEBITS[205] = " HTTP Version Not Supported"; + } + + private static String codeMessage(int code){ + if(code == 100) return " Continue"; + if(code >> 8 == 0) return HEIGHTBITS[code-200]; + return NINEBITS[code-300]; + } + +} \ No newline at end of file diff --git a/src/be/jeffcheasey88/peeratcode/framework/Locker.java b/src/be/jeffcheasey88/peeratcode/framework/Locker.java new file mode 100644 index 0000000..8dc02be --- /dev/null +++ b/src/be/jeffcheasey88/peeratcode/framework/Locker.java @@ -0,0 +1,59 @@ +package be.jeffcheasey88.peeratcode.framework; + +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; + +public class Locker{ + + private Map> map; + + public Locker(){ + this.map = new HashMap<>(); + } + + public void init(Key key){ + this.map.put(key, new LinkedBlockingQueue<>()); + } + + public void remove(Key key){ + this.map.remove(key); + } + + private BlockingQueue get(Key key){ + return this.map.get(key); + } + + public void setValue(V value){ + for(Entry> entry : this.map.entrySet()){ + entry.getValue().add(value); + this.unlock(entry.getKey()); + } + } + + public V getValue(Key key){ + BlockingQueue queue = get(key); + if(queue.isEmpty()) return null; + return queue.poll(); + } + + public void lock(Key key) throws InterruptedException{ + BlockingQueue queue = get(key); + if(queue.isEmpty()){ + synchronized(queue){ + queue.wait(); + } + } + } + + public void unlock(Key key){ + BlockingQueue queue = get(key); + synchronized(queue){ + queue.notify(); + } + } + + public static class Key{ public Key(){} } +} diff --git a/src/be/jeffcheasey88/peeratcode/framework/RequestType.java b/src/be/jeffcheasey88/peeratcode/framework/RequestType.java new file mode 100644 index 0000000..f843ef6 --- /dev/null +++ b/src/be/jeffcheasey88/peeratcode/framework/RequestType.java @@ -0,0 +1,7 @@ +package be.jeffcheasey88.peeratcode.framework; + +public enum RequestType{ + + GET, POST, OPTIONS; + +} diff --git a/src/be/jeffcheasey88/peeratcode/framework/Response.java b/src/be/jeffcheasey88/peeratcode/framework/Response.java new file mode 100644 index 0000000..392c6f3 --- /dev/null +++ b/src/be/jeffcheasey88/peeratcode/framework/Response.java @@ -0,0 +1,9 @@ +package be.jeffcheasey88.peeratcode.framework; + +import java.util.regex.Matcher; + +public interface Response{ + + void exec(Matcher matcher, User user, HttpReader reader, HttpWriter writer) throws Exception; + +} \ No newline at end of file diff --git a/src/be/jeffcheasey88/peeratcode/framework/Route.java b/src/be/jeffcheasey88/peeratcode/framework/Route.java new file mode 100644 index 0000000..47ede2e --- /dev/null +++ b/src/be/jeffcheasey88/peeratcode/framework/Route.java @@ -0,0 +1,19 @@ +package be.jeffcheasey88.peeratcode.framework; + +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 "^.*$"; + + RequestType type() default RequestType.GET; + + boolean needLogin() default false; + + boolean websocket() default false; +} diff --git a/src/be/jeffcheasey88/peeratcode/framework/Router.java b/src/be/jeffcheasey88/peeratcode/framework/Router.java new file mode 100644 index 0000000..02e843e --- /dev/null +++ b/src/be/jeffcheasey88/peeratcode/framework/Router.java @@ -0,0 +1,212 @@ +package be.jeffcheasey88.peeratcode.framework; + +import java.lang.reflect.Method; +import java.net.ServerSocket; +import java.net.Socket; +import java.security.MessageDigest; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.net.ssl.SSLServerSocket; +import javax.net.ssl.SSLServerSocketFactory; + +import org.jose4j.jwk.RsaJsonWebKey; +import org.jose4j.jwk.RsaJwkGenerator; +import org.jose4j.jws.AlgorithmIdentifiers; +import org.jose4j.jws.JsonWebSignature; +import org.jose4j.jwt.JwtClaims; +import org.jose4j.lang.JoseException; + +public class Router{ + + private Map> responses; + private Map patterns; + private Response noFileFound; + private RsaJsonWebKey rsaJsonWebKey; + private String token_issuer; + private int token_expiration; + + public Router(String token_issuer, int token_expiration) throws Exception{ + this.token_issuer = token_issuer; + this.token_expiration = token_expiration; + this.responses = new HashMap<>(); + for(RequestType type : RequestType.values()) this.responses.put(type, new HashMap<>()); + this.patterns = new HashMap<>(); + this.rsaJsonWebKey = RsaJwkGenerator.generateJwk(2048); + } + + public void listen(int port, boolean ssl) throws Exception{ + if (ssl) { // Not needed with the use of a proxy + SSLServerSocket server = null; + try { + SSLServerSocketFactory ssf = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault(); + server = (SSLServerSocket) ssf.createServerSocket(port); + + while (!server.isClosed()) { + Socket socket = server.accept(); + Client client = new Client(socket, this); + client.start(); + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + if (server != null) { + server.close(); + } + } + } else { + try (ServerSocket server = new ServerSocket(port)) { + while (!server.isClosed()) { + Socket socket = server.accept(); + Client client = new Client(socket, this); + client.start(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + 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.get(route.type()).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(RequestType type, String path, User user, HttpReader reader, HttpWriter writer) throws Exception{ + if(type == null) return; + for(Entry routes : this.responses.get(type).entrySet()){ + Matcher matcher = this.patterns.get(routes.getKey()).matcher(path); + if(matcher.matches()){ + if(user == null && routes.getValue().needLogin()){ + writer.response(401, "Access-Control-Allow-Origin: *"); + return; + } + if(routes.getValue().websocket()){ + switchToWebSocket(reader, writer); + reader = new WebSocketReader(reader); + writer = new WebSocketWriter(writer); + } + routes.getKey().exec(matcher, user, reader, writer); + return; + } + } + if(noFileFound != null) noFileFound.exec(null, user, reader, writer); + } + + public RsaJsonWebKey getWebKey(){ + return this.rsaJsonWebKey; + } + + public String getTokenIssuer(){ + return this.token_issuer; + } + + public void configureSSL(String keyStore, String keyStorePassword){ + System.setProperty("javax.net.ssl.keyStore", keyStore); + System.setProperty("javax.net.ssl.keyStorePassword", keyStorePassword); + } + + public String createAuthUser(int id) throws JoseException{ + JwtClaims claims = new JwtClaims(); + claims.setIssuer(token_issuer); // who creates the token and signs it + claims.setExpirationTimeMinutesInTheFuture(token_expiration); + claims.setGeneratedJwtId(); // a unique identifier for the token + claims.setIssuedAtToNow(); // when the token was issued/created (now) + claims.setNotBeforeMinutesInThePast(2); // time before which the token is not yet valid (2 minutes ago) + + claims.setClaim("id", id); + + JsonWebSignature jws = new JsonWebSignature(); + jws.setPayload(claims.toJson()); + jws.setKey(rsaJsonWebKey.getPrivateKey()); + jws.setKeyIdHeaderValue(rsaJsonWebKey.getKeyId()); + jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256); + return jws.getCompactSerialization(); + } + + private void switchToWebSocket(HttpReader reader, HttpWriter writer) 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(); + } + + + // From javax.xml.bind.DatatypeConverter + private String printBase64Binary(byte[] array){ + char[] arrayOfChar = new char[(array.length + 2) / 3 * 4]; + int i = _printBase64Binary(array, 0, array.length, arrayOfChar, 0); + assert i == arrayOfChar.length; + return new String(arrayOfChar); + } + + private int _printBase64Binary(byte[] paramArrayOfbyte, int paramInt1, int paramInt2, + char[] paramArrayOfchar, int paramInt3){ + int i = paramInt2; + int j; + for (j = paramInt1; 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 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/peeratcode/framework/User.java b/src/be/jeffcheasey88/peeratcode/framework/User.java new file mode 100644 index 0000000..f2e107c --- /dev/null +++ b/src/be/jeffcheasey88/peeratcode/framework/User.java @@ -0,0 +1,27 @@ +package be.jeffcheasey88.peeratcode.framework; + +import org.jose4j.jwt.JwtClaims; + +import be.jeffcheasey88.peeratcode.framework.Locker.Key; + +public class User{ + + private int id; + private Key key; + + public User(JwtClaims jwtClaims){ + this.id = ((Long) jwtClaims.getClaimValue("id")).intValue(); + } + + public void setKey(Key key){ + this.key = key; + } + + public Key getKey(){ + return this.key; + } + + public int getId(){ + return this.id; + } +} \ No newline at end of file diff --git a/src/be/jeffcheasey88/peeratcode/framework/WebSocketReader.java b/src/be/jeffcheasey88/peeratcode/framework/WebSocketReader.java new file mode 100644 index 0000000..7007402 --- /dev/null +++ b/src/be/jeffcheasey88/peeratcode/framework/WebSocketReader.java @@ -0,0 +1,62 @@ +package be.jeffcheasey88.peeratcode.framework; + +import java.io.IOException; + +public class WebSocketReader extends HttpReader{ + + public WebSocketReader(HttpReader origin) throws Exception{ + super(origin); + } + + + + @Override + public String readLine() throws IOException{ + //read websocket found on StackOverFlow + int buffLenth = 1024; + int len = 0; + byte[] b = new byte[buffLenth]; + // rawIn is a Socket.getInputStream(); + while (true){ + len = 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 < len; i++, j++){ + message[j] = (byte) (b[i] ^ masks[j % 4]); + } + + return new String(message); + + } else + break; + } + return null; + } +} diff --git a/src/be/jeffcheasey88/peeratcode/framework/WebSocketWriter.java b/src/be/jeffcheasey88/peeratcode/framework/WebSocketWriter.java new file mode 100644 index 0000000..ac093f0 --- /dev/null +++ b/src/be/jeffcheasey88/peeratcode/framework/WebSocketWriter.java @@ -0,0 +1,61 @@ +package be.jeffcheasey88.peeratcode.framework; + +import java.io.IOException; + +public class WebSocketWriter extends HttpWriter{ + + public WebSocketWriter(HttpWriter origin) throws Exception{ + super(origin); + } + + @Override + public void write(String message) throws IOException{ + //write websocket found on StackOverFlow + byte[] rawData = message.getBytes(); + + int frameCount = 0; + byte[] frame = new byte[10]; + + frame[0] = (byte) 129; + + if (rawData.length <= 125){ + frame[1] = (byte) rawData.length; + frameCount = 2; + } else if (rawData.length >= 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 < frameCount; i++){ + reply[bLim] = frame[i]; + bLim++; + } + for (int i = 0; i < rawData.length; i++){ + reply[bLim] = rawData[i]; + bLim++; + } + + write(reply); + flush(); + } +}