Compare commits
18 commits
28e9da1536
...
8ea733c637
Author | SHA1 | Date | |
---|---|---|---|
|
8ea733c637 | ||
|
f713e3941d | ||
|
ec7877fe22 | ||
|
bda04a188b | ||
|
4999115ca9 | ||
|
f176f118a3 | ||
|
b58934cff8 | ||
|
06ac691747 | ||
|
c8796e320f | ||
|
879b12571b | ||
|
eed1646a32 | ||
|
b06ed20afd | ||
|
d9d602bb83 | ||
|
896a2be120 | ||
|
d77fe329b8 | ||
|
ab33fd465e | ||
|
d8e5948fa2 | ||
|
f735a7d6bf |
24 changed files with 924 additions and 317 deletions
|
@ -3,8 +3,8 @@
|
||||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
|
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
|
||||||
<classpathentry kind="src" path="src"/>
|
<classpathentry kind="src" path="src"/>
|
||||||
<classpathentry exported="true" kind="lib" path="jose4j-0.9.3.jar"/>
|
<classpathentry exported="true" kind="lib" path="jose4j-0.9.3.jar"/>
|
||||||
<classpathentry exported="true" kind="lib" path="json-simple-1.1.1.jar"/>
|
|
||||||
<classpathentry exported="true" kind="lib" path="password4j-1.6.3.jar"/>
|
<classpathentry exported="true" kind="lib" path="password4j-1.6.3.jar"/>
|
||||||
<classpathentry exported="true" kind="lib" path="slf4j-api-2.0.6.jar"/>
|
<classpathentry exported="true" kind="lib" path="slf4j-api-2.0.6.jar"/>
|
||||||
|
<classpathentry exported="true" kind="lib" path="C:/Users/jeffc/eclipse-workspace/peer-at-code-framework/PeerAtCodeParser.jar"/>
|
||||||
<classpathentry kind="output" path="bin"/>
|
<classpathentry kind="output" path="bin"/>
|
||||||
</classpath>
|
</classpath>
|
||||||
|
|
BIN
PeerAtCodeParser.jar
Normal file
BIN
PeerAtCodeParser.jar
Normal file
Binary file not shown.
Binary file not shown.
|
@ -1,46 +0,0 @@
|
||||||
package be.jeffcheasey88.peeratcode.framework;
|
|
||||||
|
|
||||||
import java.net.Socket;
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
public class Client<U extends User> extends Thread{
|
|
||||||
|
|
||||||
private HttpReader reader;
|
|
||||||
private HttpWriter writer;
|
|
||||||
private Router<U> router;
|
|
||||||
|
|
||||||
public Client(Socket socket, Router<U> 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{
|
|
||||||
return this.router.getUser(auth);
|
|
||||||
}catch(Exception e){
|
|
||||||
writer.response(401, "Access-Control-Allow-Origin: *");
|
|
||||||
writer.flush();
|
|
||||||
writer.close();
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
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;
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,229 +0,0 @@
|
||||||
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.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 org.jose4j.jwa.AlgorithmConstraints.ConstraintType;
|
|
||||||
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.jwt.consumer.JwtConsumer;
|
|
||||||
import org.jose4j.jwt.consumer.JwtConsumerBuilder;
|
|
||||||
import org.jose4j.lang.JoseException;
|
|
||||||
|
|
||||||
public class Router<U extends User>{
|
|
||||||
|
|
||||||
private Map<RequestType, Map<Response, Route>> responses;
|
|
||||||
private Map<Response, Pattern> patterns;
|
|
||||||
private Response noFileFound;
|
|
||||||
private RsaJsonWebKey rsaJsonWebKey;
|
|
||||||
private JwtConsumer jwtConsumer;
|
|
||||||
private Consumer<JwtClaims> claims;
|
|
||||||
private Function<JwtClaims, U> userCreator;
|
|
||||||
|
|
||||||
public Router() throws Exception{
|
|
||||||
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 Router<U> configureJwt(Consumer<JwtConsumerBuilder> consumer, Consumer<JwtClaims> claims, Function<JwtClaims, U> userCreator){
|
|
||||||
JwtConsumerBuilder builder = new JwtConsumerBuilder()
|
|
||||||
.setRequireExpirationTime()
|
|
||||||
.setAllowedClockSkewInSeconds(30)
|
|
||||||
.setVerificationKey(rsaJsonWebKey.getKey())
|
|
||||||
.setJwsAlgorithmConstraints(ConstraintType.PERMIT, AlgorithmIdentifiers.RSA_USING_SHA256);
|
|
||||||
|
|
||||||
consumer.accept(builder);
|
|
||||||
|
|
||||||
this.jwtConsumer = builder.build();
|
|
||||||
this.claims = claims;
|
|
||||||
this.userCreator = userCreator;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
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<U> 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<U> client = new Client<>(socket, this);
|
|
||||||
client.start();
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Router<U> 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);
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Router<U> setDefault(Response response){
|
|
||||||
this.noFileFound = response;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
void exec(RequestType type, String path, User user, HttpReader reader, HttpWriter writer) throws Exception{
|
|
||||||
if(type == null) return;
|
|
||||||
for(Entry<Response, Route> 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 Router<U> configureSSL(String keyStore, String keyStorePassword){
|
|
||||||
System.setProperty("javax.net.ssl.keyStore", keyStore);
|
|
||||||
System.setProperty("javax.net.ssl.keyStorePassword", keyStorePassword);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public U getUser(String token) throws Exception{
|
|
||||||
return this.userCreator.apply(this.jwtConsumer.processToClaims(token));
|
|
||||||
}
|
|
||||||
|
|
||||||
public String createAuthUser(U user) throws JoseException{
|
|
||||||
JwtClaims claims = new JwtClaims();
|
|
||||||
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)
|
|
||||||
this.claims.accept(claims);
|
|
||||||
|
|
||||||
user.write(claims);
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
62
src/dev/peerat/framework/Client.java
Normal file
62
src/dev/peerat/framework/Client.java
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
package dev.peerat.framework;
|
||||||
|
|
||||||
|
import java.net.Socket;
|
||||||
|
|
||||||
|
import org.jose4j.jwt.consumer.InvalidJwtException;
|
||||||
|
|
||||||
|
public class Client<U extends User> extends Thread{
|
||||||
|
|
||||||
|
private HttpReader reader;
|
||||||
|
private HttpWriter writer;
|
||||||
|
private Router<U> router;
|
||||||
|
private Locker<Context> logger;
|
||||||
|
|
||||||
|
public Client(Socket socket, Router<U> router) throws Exception{
|
||||||
|
this.reader = new HttpReader(socket);
|
||||||
|
this.writer = new HttpWriter(socket);
|
||||||
|
this.router = router;
|
||||||
|
this.logger = router.getLogger();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run(){
|
||||||
|
Context context = null;
|
||||||
|
try{
|
||||||
|
String[] headers = reader.readLine().split("\\s");
|
||||||
|
reader.readHeaders();
|
||||||
|
|
||||||
|
RequestType type = RequestType.valueOf(headers[0]);
|
||||||
|
context = new Context(type, headers[1], isLogin(router, type, reader), writer, router.getDefaultHeaders(type));
|
||||||
|
router.exec(context, reader, writer);
|
||||||
|
writer.flush();
|
||||||
|
writer.close();
|
||||||
|
}catch(InvalidJwtException e){
|
||||||
|
}catch(Exception e){
|
||||||
|
e.printStackTrace();
|
||||||
|
if(context != null && context.getResponseCode() == 0){
|
||||||
|
try{
|
||||||
|
context.response(500);
|
||||||
|
writer.flush();
|
||||||
|
writer.close();
|
||||||
|
}catch(Exception ex){}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(context != null) logger.setValue(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
private User isLogin(Router<U> router, RequestType type, HttpReader reader) throws InvalidJwtException{
|
||||||
|
String auth = reader.getHeader("Authorization");
|
||||||
|
if(auth == null) return null;
|
||||||
|
auth = auth.substring(7);
|
||||||
|
try{
|
||||||
|
return this.router.getUser(auth);
|
||||||
|
}catch(InvalidJwtException e){
|
||||||
|
try{
|
||||||
|
writer.response(401, router.getDefaultHeaders(type));
|
||||||
|
writer.flush();
|
||||||
|
writer.close();
|
||||||
|
}catch(Exception ex){}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
59
src/dev/peerat/framework/Context.java
Normal file
59
src/dev/peerat/framework/Context.java
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
package dev.peerat.framework;
|
||||||
|
|
||||||
|
public class Context{
|
||||||
|
|
||||||
|
private RequestType type;
|
||||||
|
private String path;
|
||||||
|
private User user;
|
||||||
|
private int responseCode;
|
||||||
|
private HttpWriter writer;
|
||||||
|
private String[] headers;
|
||||||
|
|
||||||
|
public Context(RequestType type, String path, User user, HttpWriter writer, String... headers){
|
||||||
|
this.type = type;
|
||||||
|
this.path = path;
|
||||||
|
this.user = user;
|
||||||
|
this.writer = writer;
|
||||||
|
this.headers = headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RequestType getType(){
|
||||||
|
return this.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPath(){
|
||||||
|
return this.path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isLogged(){
|
||||||
|
return user != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setUser(User user){
|
||||||
|
this.user = user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <U extends User> U getUser(){
|
||||||
|
return (U) user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void response(int code, String... headers) throws Exception{
|
||||||
|
if(headers != null && headers.length > 0){
|
||||||
|
if(this.headers.length > 0){
|
||||||
|
String[] copy = new String[this.headers.length+headers.length];
|
||||||
|
System.arraycopy(this.headers, 0, copy, 0, this.headers.length);
|
||||||
|
System.arraycopy(headers, 0, copy, this.headers.length, headers.length);
|
||||||
|
this.headers = copy;
|
||||||
|
}else{
|
||||||
|
this.headers = headers;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.responseCode = code;
|
||||||
|
this.writer.response(code, this.headers);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getResponseCode(){
|
||||||
|
return this.responseCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package be.jeffcheasey88.peeratcode.framework;
|
package dev.peerat.framework;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -12,11 +12,13 @@ import java.util.Map;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import org.json.simple.parser.JSONParser;
|
import dev.peerat.framework.utils.json.Json;
|
||||||
|
import dev.peerat.framework.utils.json.JsonParser;
|
||||||
|
|
||||||
public class HttpReader{
|
public class HttpReader{
|
||||||
|
|
||||||
private static Pattern HEADER_PATTERN = Pattern.compile("^([^:]*):\\s+(.*)$");
|
private static Pattern HEADER_PATTERN = Pattern.compile("^([^:]*):\\s+(.*)$");
|
||||||
|
static JsonParser JSON_PARSER = new JsonParser();
|
||||||
|
|
||||||
private Socket socket;
|
private Socket socket;
|
||||||
private InputStream in;
|
private InputStream in;
|
||||||
|
@ -41,7 +43,7 @@ public class HttpReader{
|
||||||
return this.socket.isClosed();
|
return this.socket.isClosed();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void readHeaders() throws Exception{
|
void readHeaders() throws Exception{
|
||||||
String line;
|
String line;
|
||||||
while(((line = reader.readLine()) != null) && (line.length() > 0)){
|
while(((line = reader.readLine()) != null) && (line.length() > 0)){
|
||||||
Matcher matcher = HEADER_PATTERN.matcher(line);
|
Matcher matcher = HEADER_PATTERN.matcher(line);
|
||||||
|
@ -79,22 +81,12 @@ public class HttpReader{
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T> T readJson() throws Exception{
|
public <J extends Json> J readJson() throws Exception{
|
||||||
String line = "";
|
int length = Integer.parseInt(this.headers.get("content-length"));
|
||||||
while (ready()){
|
//to limit
|
||||||
char[] c = new char[1];
|
char[] content = new char[length];
|
||||||
read(c);
|
read(content);
|
||||||
line += c[0];
|
return JSON_PARSER.parse(new String(content));
|
||||||
if (c[0] == '}'){
|
|
||||||
Object parse;
|
|
||||||
try {
|
|
||||||
parse = new JSONParser().parse(line);
|
|
||||||
if (parse != null)
|
|
||||||
return (T) parse;
|
|
||||||
}catch(Exception e){}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -128,9 +120,10 @@ Content-Type: text/javascript
|
||||||
while (((line = readLine()) != null) && (line.length() > 0));
|
while (((line = readLine()) != null) && (line.length() > 0));
|
||||||
String buffer = "";
|
String buffer = "";
|
||||||
while (((line = readLine()) != null) && (!line.startsWith("--"+boundary))){
|
while (((line = readLine()) != null) && (!line.startsWith("--"+boundary))){
|
||||||
buffer += line;
|
buffer += line+"\n";
|
||||||
}
|
}
|
||||||
list.add(buffer);
|
if(buffer.length() < 1) buffer = "\n";
|
||||||
|
list.add(buffer.substring(0, buffer.length()-1));
|
||||||
}
|
}
|
||||||
|
|
||||||
return list;
|
return list;
|
|
@ -1,4 +1,4 @@
|
||||||
package be.jeffcheasey88.peeratcode.framework;
|
package dev.peerat.framework;
|
||||||
|
|
||||||
import java.io.BufferedWriter;
|
import java.io.BufferedWriter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -39,13 +39,11 @@ public class HttpWriter{
|
||||||
this.writer.close();
|
this.writer.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void response(int code, String... headers) throws Exception{
|
void response(int code, String... headers) throws Exception{
|
||||||
write("HTTP/1.1 "+code+codeMessage(code)+"\n");
|
write("HTTP/1.1 "+code+codeMessage(code)+"\n");
|
||||||
for(String header : headers) write(header+"\n");
|
for(String header : headers) write(header+"\n");
|
||||||
write("\n");
|
write("\n");
|
||||||
flush();
|
flush();
|
||||||
StackTraceElement[] e = Thread.currentThread().getStackTrace();
|
|
||||||
System.out.println(e[2]+" -> response "+code);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String[] HEIGHTBITS = new String[9];
|
private static String[] HEIGHTBITS = new String[9];
|
|
@ -1,10 +1,14 @@
|
||||||
package be.jeffcheasey88.peeratcode.framework;
|
package dev.peerat.framework;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.concurrent.BlockingQueue;
|
import java.util.concurrent.BlockingQueue;
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import dev.peerat.framework.Locker.Key;
|
||||||
|
|
||||||
public class Locker<V>{
|
public class Locker<V>{
|
||||||
|
|
||||||
|
@ -55,5 +59,51 @@ public class Locker<V>{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void listen(Consumer<V> action){
|
||||||
|
Key key = new Key();
|
||||||
|
init(key);
|
||||||
|
try {
|
||||||
|
while(true){
|
||||||
|
lock(key);
|
||||||
|
V value = getValue(key);
|
||||||
|
action.accept(value);
|
||||||
|
}
|
||||||
|
}catch(Exception e){}
|
||||||
|
remove(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void listen(Supplier<Boolean> condition,Consumer<V> action){
|
||||||
|
Key key = new Key();
|
||||||
|
init(key);
|
||||||
|
try {
|
||||||
|
while(condition.get()){
|
||||||
|
lock(key);
|
||||||
|
V value = getValue(key);
|
||||||
|
action.accept(value);
|
||||||
|
}
|
||||||
|
}catch(Exception e){}
|
||||||
|
remove(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Thread listenAsync(Consumer<V> action){
|
||||||
|
Thread thread = new Thread(new Runnable(){
|
||||||
|
public void run(){
|
||||||
|
listen(action);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
thread.start();
|
||||||
|
return thread;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Thread listenAsync(Supplier<Boolean> condition,Consumer<V> action){
|
||||||
|
Thread thread = new Thread(new Runnable(){
|
||||||
|
public void run(){
|
||||||
|
listen(condition, action);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
thread.start();
|
||||||
|
return thread;
|
||||||
|
}
|
||||||
|
|
||||||
public static class Key{ public Key(){} }
|
public static class Key{ public Key(){} }
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package be.jeffcheasey88.peeratcode.framework;
|
package dev.peerat.framework;
|
||||||
|
|
||||||
public enum RequestType{
|
public enum RequestType{
|
||||||
|
|
9
src/dev/peerat/framework/Response.java
Normal file
9
src/dev/peerat/framework/Response.java
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
package dev.peerat.framework;
|
||||||
|
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
|
||||||
|
public interface Response{
|
||||||
|
|
||||||
|
void exec(Matcher matcher, Context context, HttpReader reader, HttpWriter writer) throws Exception;
|
||||||
|
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package be.jeffcheasey88.peeratcode.framework;
|
package dev.peerat.framework;
|
||||||
|
|
||||||
import java.lang.annotation.ElementType;
|
import java.lang.annotation.ElementType;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
180
src/dev/peerat/framework/RouteMapper.java
Normal file
180
src/dev/peerat/framework/RouteMapper.java
Normal file
|
@ -0,0 +1,180 @@
|
||||||
|
package dev.peerat.framework;
|
||||||
|
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import dev.peerat.framework.utils.json.JsonMap;
|
||||||
|
|
||||||
|
public class RouteMapper<U extends User>{
|
||||||
|
|
||||||
|
private Router<U> router;
|
||||||
|
|
||||||
|
private Response[] responses;
|
||||||
|
private Route[] routes;
|
||||||
|
private Pattern[] patterns;
|
||||||
|
private int[] calls;
|
||||||
|
private int dif;
|
||||||
|
|
||||||
|
public RouteMapper(Router<U> router){
|
||||||
|
this.router = router;
|
||||||
|
this.responses = new Response[0];
|
||||||
|
this.routes = new Route[0];
|
||||||
|
this.patterns = new Pattern[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void register(Response response, Route route, Pattern pattern){
|
||||||
|
int length = responses.length+1;
|
||||||
|
this.responses = addElement(responses, new Response[length], response);
|
||||||
|
this.routes = addElement(routes, new Route[length], route);
|
||||||
|
this.patterns = addElement(patterns, new Pattern[length], pattern);
|
||||||
|
if(this.calls != null) this.calls = new int[length];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void activeReOrdering(){
|
||||||
|
this.calls = new int[responses.length];
|
||||||
|
this.dif = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean exec(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, writer);
|
||||||
|
reader = new WebSocketReader(reader);
|
||||||
|
writer = new WebSocketWriter(writer);
|
||||||
|
if(route.needLogin()){
|
||||||
|
String token = reader.<JsonMap>readJson().get("token");
|
||||||
|
context.setUser(this.router.getUser(token));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if((!context.isLogged()) && route.needLogin()){
|
||||||
|
writer.response(401, router.getDefaultHeaders(context.getType()));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
order(i);
|
||||||
|
result = response;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(result != null){
|
||||||
|
result.exec(matcher, context, reader, writer);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void order(int index){
|
||||||
|
if(this.calls == null) return;
|
||||||
|
int call = this.calls[index]++;
|
||||||
|
if(call >= (Integer.MAX_VALUE-(dif+1))){
|
||||||
|
for(int i = 0; i < this.calls.length; i++) this.calls[i]/=dif;
|
||||||
|
dif++;
|
||||||
|
call = this.calls[index];
|
||||||
|
}
|
||||||
|
if(index < 1) return;
|
||||||
|
if(call > this.calls[index-1]){
|
||||||
|
switchElement(responses, index, index-1);
|
||||||
|
switchElement(routes, index, index-1);
|
||||||
|
switchElement(patterns, index, index-1);
|
||||||
|
|
||||||
|
this.calls[index] = this.calls[index-1];
|
||||||
|
this.calls[index-1] = call;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private <E> E[] addElement(E[] current, E[] growed, E element){
|
||||||
|
System.arraycopy(current, 0, growed, 0, current.length);
|
||||||
|
growed[current.length] = element;
|
||||||
|
return growed;
|
||||||
|
}
|
||||||
|
|
||||||
|
private <E> void switchElement(E[] array, int before, int after){
|
||||||
|
E tmp = array[before];
|
||||||
|
array[before] = array[after];
|
||||||
|
array[after] = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
178
src/dev/peerat/framework/Router.java
Normal file
178
src/dev/peerat/framework/Router.java
Normal file
|
@ -0,0 +1,178 @@
|
||||||
|
package dev.peerat.framework;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.net.ServerSocket;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import javax.net.ssl.SSLServerSocket;
|
||||||
|
import javax.net.ssl.SSLServerSocketFactory;
|
||||||
|
|
||||||
|
import org.jose4j.jwa.AlgorithmConstraints.ConstraintType;
|
||||||
|
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.jwt.consumer.InvalidJwtException;
|
||||||
|
import org.jose4j.jwt.consumer.JwtConsumer;
|
||||||
|
import org.jose4j.jwt.consumer.JwtConsumerBuilder;
|
||||||
|
import org.jose4j.lang.JoseException;
|
||||||
|
|
||||||
|
public class Router<U extends User>{
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private Locker<Context> logger;
|
||||||
|
private RouteMapper<U>[] mappers;
|
||||||
|
private Response noFileFound;
|
||||||
|
private RsaJsonWebKey rsaJsonWebKey;
|
||||||
|
private JwtConsumer jwtConsumer;
|
||||||
|
private Consumer<JwtClaims> claims;
|
||||||
|
private Function<JwtClaims, U> userCreator;
|
||||||
|
private String[][] headers;
|
||||||
|
private ServerSocket serverSocket;
|
||||||
|
|
||||||
|
public Router() throws Exception{
|
||||||
|
this.logger = new Locker<>();
|
||||||
|
int types = RequestType.values().length;
|
||||||
|
this.mappers = new RouteMapper[types];
|
||||||
|
for(RequestType type : RequestType.values()) this.mappers[type.ordinal()] = new RouteMapper<>(this);
|
||||||
|
this.rsaJsonWebKey = RsaJwkGenerator.generateJwk(2048);
|
||||||
|
this.headers = new String[types][0];
|
||||||
|
}
|
||||||
|
|
||||||
|
public Router<U> configureJwt(Consumer<JwtConsumerBuilder> consumer, Consumer<JwtClaims> claims, Function<JwtClaims, U> userCreator){
|
||||||
|
JwtConsumerBuilder builder = new JwtConsumerBuilder()
|
||||||
|
.setRequireExpirationTime()
|
||||||
|
.setAllowedClockSkewInSeconds(30)
|
||||||
|
.setVerificationKey(rsaJsonWebKey.getKey())
|
||||||
|
.setJwsAlgorithmConstraints(ConstraintType.PERMIT, AlgorithmIdentifiers.RSA_USING_SHA256);
|
||||||
|
|
||||||
|
consumer.accept(builder);
|
||||||
|
|
||||||
|
this.jwtConsumer = builder.build();
|
||||||
|
this.claims = claims;
|
||||||
|
this.userCreator = userCreator;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Router<U> addDefaultHeaders(RequestType type, String... headers){
|
||||||
|
String[] origin = this.headers[type.ordinal()];
|
||||||
|
String[] copy = new String[origin.length+headers.length];
|
||||||
|
System.arraycopy(origin, 0, copy, 0, origin.length);
|
||||||
|
System.arraycopy(headers, 0, copy, origin.length, headers.length);
|
||||||
|
this.headers[type.ordinal()] = copy;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Router<U> register(Response response){
|
||||||
|
try{
|
||||||
|
Method method = response.getClass().getDeclaredMethod("exec",
|
||||||
|
Response.class.getDeclaredMethods()[0].getParameterTypes());
|
||||||
|
Route route = method.getAnnotation(Route.class);
|
||||||
|
|
||||||
|
this.mappers[route.type().ordinal()].register(response, route, Pattern.compile(route.path()));
|
||||||
|
}catch(Exception e){
|
||||||
|
throw new IllegalArgumentException(e);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Router<U> setDefault(Response response){
|
||||||
|
this.noFileFound = response;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Router<U> activeReOrdering(){
|
||||||
|
for(RouteMapper<?> mapper : this.mappers) mapper.activeReOrdering();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] getDefaultHeaders(RequestType type){
|
||||||
|
return this.headers[type.ordinal()];
|
||||||
|
}
|
||||||
|
|
||||||
|
void exec(Context context, HttpReader reader, HttpWriter writer) throws Exception{
|
||||||
|
if(this.mappers[context.getType().ordinal()].exec(context, reader, writer)) return;
|
||||||
|
if(noFileFound != null) noFileFound.exec(null, context, reader, writer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Router<U> configureSSL(String keyStore, String keyStorePassword){
|
||||||
|
System.setProperty("javax.net.ssl.keyStore", keyStore);
|
||||||
|
System.setProperty("javax.net.ssl.keyStorePassword", keyStorePassword);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public U getUser(String token) throws InvalidJwtException{
|
||||||
|
return this.userCreator.apply(this.jwtConsumer.processToClaims(token));
|
||||||
|
}
|
||||||
|
|
||||||
|
public String createAuthUser(U user) throws JoseException{
|
||||||
|
JwtClaims claims = new JwtClaims();
|
||||||
|
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)
|
||||||
|
this.claims.accept(claims);
|
||||||
|
|
||||||
|
user.write(claims);
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Locker<Context> getLogger(){
|
||||||
|
return this.logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void listen(int port, boolean ssl) throws Exception{
|
||||||
|
if (ssl) { // Not needed with the use of a proxy
|
||||||
|
try {
|
||||||
|
SSLServerSocketFactory ssf = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
|
||||||
|
serverSocket = (SSLServerSocket) ssf.createServerSocket(port);
|
||||||
|
|
||||||
|
while (!serverSocket.isClosed()) {
|
||||||
|
Socket socket = serverSocket.accept();
|
||||||
|
Client<U> client = new Client<>(socket, this);
|
||||||
|
client.start();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} finally {
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try{
|
||||||
|
serverSocket = new ServerSocket(port);
|
||||||
|
while (!serverSocket.isClosed()) {
|
||||||
|
Socket socket = serverSocket.accept();
|
||||||
|
Client<U> client = new Client<>(socket, this);
|
||||||
|
client.start();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} finally {
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop(){
|
||||||
|
if(serverSocket == null) return;
|
||||||
|
try {
|
||||||
|
serverSocket.close();
|
||||||
|
serverSocket = null;
|
||||||
|
}catch(Exception e){
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package be.jeffcheasey88.peeratcode.framework;
|
package dev.peerat.framework;
|
||||||
|
|
||||||
import org.jose4j.jwt.JwtClaims;
|
import org.jose4j.jwt.JwtClaims;
|
||||||
|
|
|
@ -1,14 +1,18 @@
|
||||||
package be.jeffcheasey88.peeratcode.framework;
|
package dev.peerat.framework;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import dev.peerat.framework.utils.json.Json;
|
||||||
|
|
||||||
public class WebSocketReader extends HttpReader{
|
public class WebSocketReader extends HttpReader{
|
||||||
|
|
||||||
public WebSocketReader(HttpReader origin) throws Exception{
|
public WebSocketReader(HttpReader origin) throws Exception{
|
||||||
super(origin);
|
super(origin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public <J extends Json> J readJson() throws Exception{
|
||||||
|
return JSON_PARSER.parse(readLine());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String readLine() throws IOException{
|
public String readLine() throws IOException{
|
|
@ -1,4 +1,4 @@
|
||||||
package be.jeffcheasey88.peeratcode.framework;
|
package dev.peerat.framework;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
50
src/dev/peerat/framework/utils/json/Json.java
Normal file
50
src/dev/peerat/framework/utils/json/Json.java
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
package dev.peerat.framework.utils.json;
|
||||||
|
|
||||||
|
public class Json{
|
||||||
|
|
||||||
|
void buildValue(StringBuilder builder, Object value){
|
||||||
|
if(value == null) builder.append("null");
|
||||||
|
else if(value instanceof String){
|
||||||
|
builder.append('"');
|
||||||
|
for(char c : ((String)value).toCharArray()){
|
||||||
|
if(c == '"'){
|
||||||
|
builder.append("\\\"");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(c == '\\'){
|
||||||
|
builder.append("\\\\");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(c == '\n'){
|
||||||
|
builder.append("\\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(c == '\b'){
|
||||||
|
builder.append("\\b");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(c == '\f'){
|
||||||
|
builder.append("\\f");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(c == '\r'){
|
||||||
|
builder.append("\\r");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(c == '\t'){
|
||||||
|
builder.append("\\t");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
builder.append(c);
|
||||||
|
}
|
||||||
|
builder.append('"');
|
||||||
|
}else if(value instanceof Character){
|
||||||
|
builder.append('\'');
|
||||||
|
builder.append(value);
|
||||||
|
builder.append('\'');
|
||||||
|
}else{
|
||||||
|
builder.append(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
39
src/dev/peerat/framework/utils/json/JsonArray.java
Normal file
39
src/dev/peerat/framework/utils/json/JsonArray.java
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
package dev.peerat.framework.utils.json;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class JsonArray extends Json{
|
||||||
|
|
||||||
|
private List<Object> list;
|
||||||
|
|
||||||
|
public JsonArray(){
|
||||||
|
this.list = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public <E> Collection<E> toList(){
|
||||||
|
return (Collection<E>) this.list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(Object value){
|
||||||
|
this.list.add(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString(){
|
||||||
|
Iterator<Object> iterator = list.iterator();
|
||||||
|
if(!iterator.hasNext()) return "[]";
|
||||||
|
StringBuilder builder= new StringBuilder();
|
||||||
|
builder.append('[');
|
||||||
|
while(iterator.hasNext()){
|
||||||
|
buildValue(builder, iterator.next());
|
||||||
|
if(iterator.hasNext()) builder.append(',');
|
||||||
|
}
|
||||||
|
builder.append(']');
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
57
src/dev/peerat/framework/utils/json/JsonMap.java
Normal file
57
src/dev/peerat/framework/utils/json/JsonMap.java
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
package dev.peerat.framework.utils.json;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class JsonMap extends Json{
|
||||||
|
|
||||||
|
private Map<String, Object> map;
|
||||||
|
|
||||||
|
public JsonMap(){
|
||||||
|
this.map = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Entry<String, Object>> entries(){
|
||||||
|
return this.map.entrySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> keys(){
|
||||||
|
return this.map.keySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set(String key, Object value){
|
||||||
|
this.map.put(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean has(String key){
|
||||||
|
return map.containsKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <E> E get(String key){
|
||||||
|
return (E) map.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString(){
|
||||||
|
Iterator<Entry<String, Object>> iterator = map.entrySet().iterator();
|
||||||
|
if(!iterator.hasNext()) return "{}";
|
||||||
|
StringBuilder builder= new StringBuilder();
|
||||||
|
builder.append('{');
|
||||||
|
while(iterator.hasNext()){
|
||||||
|
Entry<String, Object> entry = iterator.next();
|
||||||
|
builder.append('"');
|
||||||
|
builder.append(entry.getKey());
|
||||||
|
builder.append("\":");
|
||||||
|
|
||||||
|
Object value = entry.getValue();
|
||||||
|
buildValue(builder, value);
|
||||||
|
|
||||||
|
if(iterator.hasNext()) builder.append(',');
|
||||||
|
}
|
||||||
|
builder.append('}');
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
}
|
147
src/dev/peerat/framework/utils/json/JsonParser.java
Normal file
147
src/dev/peerat/framework/utils/json/JsonParser.java
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
package dev.peerat.framework.utils.json;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
import be.jeffcheasey88.peeratcode.parser.Parser;
|
||||||
|
import be.jeffcheasey88.peeratcode.parser.Token;
|
||||||
|
import be.jeffcheasey88.peeratcode.parser.TokenType;
|
||||||
|
import be.jeffcheasey88.peeratcode.parser.state.RedirectStateTree;
|
||||||
|
import be.jeffcheasey88.peeratcode.parser.state.StateTree;
|
||||||
|
|
||||||
|
public class JsonParser extends Parser<Json>{
|
||||||
|
|
||||||
|
private static StateTree<Json> base;
|
||||||
|
|
||||||
|
static{
|
||||||
|
base = new StateTree<>();
|
||||||
|
|
||||||
|
StateTree<Json> content = new StateTree<>();
|
||||||
|
|
||||||
|
StateTree<Json> content_array = new StateTree<>();
|
||||||
|
StateTree<Json> content_array_element = content_array.then(new RedirectStateTree<>(content, (global, local) ->{
|
||||||
|
List<Object> list = global.get();
|
||||||
|
if(list == null){
|
||||||
|
list = new ArrayList<>();
|
||||||
|
global.set(list);
|
||||||
|
}
|
||||||
|
list.add(local.get());
|
||||||
|
}));
|
||||||
|
content_array_element.end((parent, bag) -> {
|
||||||
|
JsonArray json = (JsonArray)parent;
|
||||||
|
|
||||||
|
List<Object> list = bag.get();
|
||||||
|
for(Object value : list){
|
||||||
|
json.add(convert(value));
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
content_array_element.then((validator) -> validator.validate((token) -> token.getValue().equals(",")))
|
||||||
|
.then(content_array_element);
|
||||||
|
|
||||||
|
content.then(new RedirectStateTree<>(base, (global, local) -> global.set(local.get()))).end();
|
||||||
|
content.then((validator) -> validator.validate((token) -> token.getType().equals(TokenType.STRING), (bag, token) -> bag.set(token))).end();
|
||||||
|
content.then((validator) -> validator.validate((token) -> token.getType().equals(TokenType.CHAR), (bag, token) -> bag.set(token))).end();
|
||||||
|
content.then((validator) -> validator.validate((token) -> token.getType().equals(TokenType.NAME), (bag, token) -> bag.set(token))).end();
|
||||||
|
|
||||||
|
StateTree<Json> mapper = new StateTree<>();
|
||||||
|
StateTree<Json> mapper_key = mapper.then((validator) -> validator.validate((token) -> token.getType().equals(TokenType.STRING), (bag, token) -> {
|
||||||
|
Map<Token, Object> map = bag.get("map");
|
||||||
|
if(map == null){
|
||||||
|
map = new HashMap<>();
|
||||||
|
bag.set("map", map);
|
||||||
|
}
|
||||||
|
map.put(token, null);
|
||||||
|
bag.set("last", token);
|
||||||
|
}));
|
||||||
|
StateTree<Json> mapper_value = mapper_key.then((validator) -> validator.validate((token) -> token.getValue().equals(":")))
|
||||||
|
.then(new RedirectStateTree<>(content, (global, local) -> {
|
||||||
|
global.<Map<Token, Object>>get("map").put(global.<Token>get("last"), local.get());
|
||||||
|
}));
|
||||||
|
|
||||||
|
mapper_value.end((parent, bag) -> {
|
||||||
|
Map<Token, Object> map = bag.get("map");
|
||||||
|
JsonMap jMap = (JsonMap)parent;
|
||||||
|
for(Entry<Token, Object> entry : map.entrySet()){
|
||||||
|
jMap.set(entry.getKey().getValue(), convert(entry.getValue()));
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
mapper_value.then((validator) -> validator.validate((token) -> token.getValue().equals(",")))
|
||||||
|
.then(mapper_key);
|
||||||
|
|
||||||
|
|
||||||
|
base.then((validator) -> validator.validate((token) -> token.getValue().equals("{")))
|
||||||
|
.<Json>end((parent, bag) -> {
|
||||||
|
JsonMap result = new JsonMap();
|
||||||
|
bag.set(result);
|
||||||
|
if(parent instanceof JsonContainer) ((JsonContainer)parent).value = result;
|
||||||
|
return result;
|
||||||
|
})
|
||||||
|
.unique(mapper)
|
||||||
|
.unique((validator) -> validator.validate((token) -> token.getValue().equals("}")))
|
||||||
|
.end();
|
||||||
|
|
||||||
|
base.then((validator) -> validator.validate((token) -> token.getValue().equals("[")))
|
||||||
|
.<Json>end((parent, bag) -> {
|
||||||
|
JsonArray result = new JsonArray();
|
||||||
|
bag.set(result);
|
||||||
|
if(parent instanceof JsonContainer) ((JsonContainer)parent).value = result;
|
||||||
|
return result;
|
||||||
|
})
|
||||||
|
.unique(content_array)
|
||||||
|
.unique((validator) -> validator.validate((token) -> token.getValue().equals("]")))
|
||||||
|
.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Object convert(Object value){
|
||||||
|
if(value instanceof Token){
|
||||||
|
Token token = (Token) value;
|
||||||
|
String content = token.getValue();
|
||||||
|
if(token.getType().equals(TokenType.STRING)){
|
||||||
|
return content;
|
||||||
|
}else if(token.getType().equals(TokenType.CHAR)){
|
||||||
|
return content.charAt(0);
|
||||||
|
}else{
|
||||||
|
try{
|
||||||
|
return Long.parseLong(content);
|
||||||
|
}catch(Exception _){
|
||||||
|
try {
|
||||||
|
return Boolean.parseBoolean(content);
|
||||||
|
}catch(Exception __){
|
||||||
|
try{
|
||||||
|
return Double.parseDouble(content);
|
||||||
|
}catch(Exception ___){}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JsonParser(){
|
||||||
|
setTokenizer(new JsonTokenizer());
|
||||||
|
setStateTree(base);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <J extends Json> J parse(String content) throws Exception{
|
||||||
|
JsonContainer container = new JsonContainer();
|
||||||
|
parse(content, container);
|
||||||
|
return container.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class JsonContainer extends Json{
|
||||||
|
|
||||||
|
private Json value;
|
||||||
|
|
||||||
|
public JsonContainer(){}
|
||||||
|
|
||||||
|
public <J extends Json> J getValue(){
|
||||||
|
return (J) this.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
65
src/dev/peerat/framework/utils/json/JsonTokenizer.java
Normal file
65
src/dev/peerat/framework/utils/json/JsonTokenizer.java
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
package dev.peerat.framework.utils.json;
|
||||||
|
|
||||||
|
import be.jeffcheasey88.peeratcode.parser.Token;
|
||||||
|
import be.jeffcheasey88.peeratcode.parser.TokenType;
|
||||||
|
import be.jeffcheasey88.peeratcode.parser.Tokenizer;
|
||||||
|
|
||||||
|
public class JsonTokenizer extends Tokenizer{
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void parse(String line){
|
||||||
|
for(int i = 0; i < line.length(); i++){
|
||||||
|
char c = line.charAt(i);
|
||||||
|
|
||||||
|
if(isValidName(c)){
|
||||||
|
String buffer = ""+c;
|
||||||
|
int j = i+1;
|
||||||
|
for(; j < line.length(); j++){
|
||||||
|
c = line.charAt(j);
|
||||||
|
if(isValidName(c)) buffer+=c;
|
||||||
|
else break;
|
||||||
|
}
|
||||||
|
getTokens().add(new Token(1, i+1, buffer, TokenType.NAME));
|
||||||
|
i=j-1;
|
||||||
|
}else if(Character.isWhitespace(c)) continue;
|
||||||
|
else{
|
||||||
|
if(c == '"'){
|
||||||
|
String buffer = "";
|
||||||
|
int j = i+1;
|
||||||
|
for(; j < line.length(); j++){
|
||||||
|
c = line.charAt(j);
|
||||||
|
if(c == '\\'){
|
||||||
|
buffer+=c+line.charAt(++j);
|
||||||
|
}
|
||||||
|
if(c == '\"') break;
|
||||||
|
buffer+=c;
|
||||||
|
}
|
||||||
|
getTokens().add(new Token(1, i+1, buffer, TokenType.STRING));
|
||||||
|
i=j;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(c == '\''){
|
||||||
|
String buffer = "";
|
||||||
|
int j = i+1;
|
||||||
|
for(; j < line.length(); j++){
|
||||||
|
c = line.charAt(j);
|
||||||
|
if(c == '\\'){
|
||||||
|
buffer+=c+line.charAt(++j);
|
||||||
|
}
|
||||||
|
if(c == '\'') break;
|
||||||
|
buffer+=c;
|
||||||
|
}
|
||||||
|
getTokens().add(new Token(1, i+1, buffer, TokenType.CHAR));
|
||||||
|
i=j;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
getTokens().add(new Token(1, i+1, ""+c, TokenType.DELIMITER));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isValidName(char c){
|
||||||
|
return c == '_' || c == '-' || c == '$' || Character.isAlphabetic(c) || Character.isDigit(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue