Compare commits

..

No commits in common. "dev" and "main" have entirely different histories.
dev ... main

22 changed files with 258 additions and 990 deletions

Binary file not shown.

View file

@ -2,7 +2,7 @@ package dev.peerat.framework;
import java.net.Socket; import java.net.Socket;
import dev.peerat.framework.auth.AuthException; import org.jose4j.jwt.consumer.InvalidJwtException;
public class Client<U extends User> extends Thread{ public class Client<U extends User> extends Thread{
@ -28,35 +28,38 @@ public class Client<U extends User> extends Thread{
router.exec(context, reader, writer); router.exec(context, reader, writer);
writer.flush(); writer.flush();
writer.close(); writer.close();
}catch(AuthException e){ }catch(InvalidJwtException e){
this.router.getExceptionLogger().pushValue(e); this.router.getExceptionLogger().setValue(e);
}catch(Throwable e){ }catch(Throwable e){
this.router.getExceptionLogger().pushValue(e); this.router.getExceptionLogger().setValue(e);
if(context != null && context.getResponseCode() == 0){ if(context != null && context.getResponseCode() == 0){
try{ try{
context.response(500); context.response(500);
writer.flush(); writer.flush();
writer.close(); writer.close();
}catch(Exception ex){ }catch(Exception ex){
this.router.getExceptionLogger().pushValue(ex); this.router.getExceptionLogger().setValue(ex);
} }
} }
} }
if(context != null) router.getLogger().pushValue(context); if(context != null) router.getLogger().setValue(context);
} }
private User isLogin(RequestType type, HttpReader reader) throws AuthException{ private User isLogin(RequestType type, HttpReader reader) throws InvalidJwtException{
String auth = reader.getHeader("Authorization");
if(auth == null) return null;
auth = auth.substring(7);
try{ try{
return this.router.getUser(type, reader); return this.router.getUser(auth);
}catch(Exception e){ }catch(InvalidJwtException e){
try{ try{
writer.response(401, this.router.getDefaultHeaders(type)); writer.response(401, this.router.getDefaultHeaders(type));
writer.flush(); writer.flush();
writer.close(); writer.close();
}catch(Exception ex){ }catch(Exception ex){
this.router.getExceptionLogger().pushValue(ex); this.router.getExceptionLogger().setValue(ex);
} }
throw new AuthException(e); throw e;
} }
} }
} }

View file

@ -1,26 +1,21 @@
package dev.peerat.framework; package dev.peerat.framework;
import java.lang.reflect.Executable; import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Parameter; import java.lang.reflect.Parameter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.BiFunction; import java.util.function.BiFunction;
import java.util.function.Supplier;
public class DependencyInjector{ public class DependencyInjector{
private Map<String, Object> map; private Map<String, Object> map;
private Map<Class<?>, Supplier<?>> suppliers; private List<BiFunction<Constructor<?>, Parameter, Object>> builders;
private List<BiFunction<Injection, Class<?>, Object>> builders;
private Object[] injections; private Object[] injections;
private Map<Class<?>, Object> cache;
public DependencyInjector(){ public DependencyInjector(){
this.map = new HashMap<>(); this.map = new HashMap<>();
this.suppliers = new HashMap<>();
this.builders = new ArrayList<>(); this.builders = new ArrayList<>();
} }
@ -29,20 +24,14 @@ public class DependencyInjector{
return this; return this;
} }
public DependencyInjector of(BiFunction<Injection, Class<?>, Object> builder){ public DependencyInjector of(BiFunction<Constructor<?>, Parameter, Object> builder){
this.builders.add(builder); this.builders.add(builder);
return this; return this;
} }
public DependencyInjector of(Class<?> type, Supplier<?> supplier){
this.suppliers.put(type, supplier);
return this;
}
public DependencyInjector of(Object... injections){ public DependencyInjector of(Object... injections){
if(this.injections == null){ if(this.injections == null){
this.injections = injections; this.injections = injections;
this.cache = new HashMap<>();
return this; return this;
} }
Object[] copy = new Object[this.injections.length+injections.length]; Object[] copy = new Object[this.injections.length+injections.length];
@ -52,56 +41,30 @@ public class DependencyInjector{
return this; return this;
} }
public Object[] apply(Executable executable) throws Exception{ Object applyDependency(Constructor<?> constructor, Parameter parameter, Map<Class<?>, Object> cache){
Parameter[] parameters = executable.getParameters(); Injection annotation = parameter.getAnnotation(Injection.class);
Object[] injections = new Object[parameters.length];
for(int i = 0; i < parameters.length; i++) injections[i] = find(parameters[i].getAnnotation(Injection.class), parameters[i].getType());
return injections;
}
public Object apply(Field field) throws Exception{
return find(field.getAnnotation(Injection.class), field.getType());
}
public Object find(Injection annotation, Class<?> type){
if(annotation != null){ if(annotation != null){
Object result = this.map.get(annotation.value()); Object result = this.map.get(annotation.value());
if(result == null){ if(result == null) throw new IllegalArgumentException("No depdency named "+annotation.value()+" ("+this.map.keySet()+") in constructor "+constructor);
Supplier<?> supplier = this.suppliers.get(type);
if(supplier == null) throw new IllegalArgumentException("No dependency named "+annotation.value()+" ("+this.map.keySet()+")");
result = supplier.get();
this.map.put(annotation.value(), result);
}
return result; return result;
} }
for(BiFunction<Injection, Class<?>, Object> function : builders){ for(BiFunction<Constructor<?>, Parameter, Object> function : builders){
Object result = function.apply(annotation, type); Object result = function.apply(constructor, parameter);
if(result != null) return result; if(result != null) return result;
} }
if(this.injections == null){ if(this.injections == null) return null;
Supplier<?> supplier = this.suppliers.get(type); Class<?> type = parameter.getType();
if(supplier == null) throw new IllegalArgumentException("No dependency for type "+type); Object result = null;
return supplier.get();
}
Object result = this.cache.get(type);
if(result != null) return result;
for(Object injection : injections){ for(Object injection : injections){
if(type.isAssignableFrom(injection.getClass())){ if(type.isAssignableFrom(injection.getClass())){
if(cache.containsKey(type)) throw new IllegalArgumentException("Double dependency for type "+type); if(cache.containsKey(type)){
cache.put(type, injection); throw new IllegalArgumentException("Double dependency for type "+type+", can be in constructor "+constructor);
result = injection; }else{
cache.put(type, injection);
result = injection;
}
} }
} }
if(result == null){
Supplier<?> supplier = this.suppliers.get(type);
if(supplier == null) throw new IllegalArgumentException("No dependency for type "+type);
return supplier.get();
}
return result; return result;
} }
@Override
public String toString(){
return "([DependencyInjector] map="+map+",buildersCount="+builders.size()+",injections="+injections+",cache="+cache+")";
}
} }

View file

@ -1,7 +1,9 @@
package dev.peerat.framework; package dev.peerat.framework;
import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket; import java.net.Socket;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
@ -21,14 +23,14 @@ public class HttpReader{
private Socket socket; private Socket socket;
private InputStream in; private InputStream in;
private StrictReader reader; private BufferedReader reader;
private Map<String, String> headers; private Map<String, String> headers;
public HttpReader(Socket socket) throws Exception{ public HttpReader(Socket socket) throws Exception{
this.socket = socket; this.socket = socket;
this.in = socket.getInputStream(); this.in = socket.getInputStream();
this.reader = new StrictReader(this.in); this.reader = new BufferedReader(new InputStreamReader(in));
this.headers = new HashMap<>(); this.headers = new HashMap<>();
} }
@ -64,7 +66,7 @@ public class HttpReader{
} }
public int read(char[] buffer) throws IOException{ public int read(char[] buffer) throws IOException{
return this.reader.read(buffer, 0, buffer.length); return this.reader.read(buffer);
} }
public String readLine() throws IOException{ public String readLine() throws IOException{
@ -131,39 +133,4 @@ Content-Type: text/javascript
return list; return list;
} }
public byte[] readMultipartFile() throws Exception{
int length = Integer.parseInt(getHeader("content-length"));
int headerLength = readLine().length()+2;
length-=headerLength;
for(int i = 0; i < 2; i++){
length-=(readLine().length()+2);
}
byte[] ptn = new byte[2];
read(ptn);
length-=2;
if(ptn[0] == 10){
read(new byte[1]);
length-=1;
}
byte[] file = new byte[0];
while(length > 0){
byte[] buffer = new byte[length];
int read = read(buffer);
if(read < 0) throw new IndexOutOfBoundsException("read -1 on HttpReader");
length-=read;
byte[] copy = new byte[file.length+read];
System.arraycopy(file, 0, copy, 0, file.length);
System.arraycopy(buffer, 0, copy, file.length, read);
file = copy;
}
byte[] cleaned = new byte[file.length-(headerLength+4)];
System.arraycopy(file, 0, cleaned, 0, cleaned.length);
return cleaned;
}
} }

View file

@ -1,8 +1,9 @@
package dev.peerat.framework; package dev.peerat.framework;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.BlockingQueue; import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Supplier; import java.util.function.Supplier;
@ -12,7 +13,7 @@ public class Locker<V>{
private Map<Key, BlockingQueue<V>> map; private Map<Key, BlockingQueue<V>> map;
public Locker(){ public Locker(){
this.map = new ConcurrentHashMap<>(); this.map = new HashMap<>();
} }
public void init(Key key){ public void init(Key key){
@ -23,20 +24,25 @@ public class Locker<V>{
this.map.remove(key); this.map.remove(key);
} }
public void pushValue(V value){ private BlockingQueue<V> get(Key key){
for(BlockingQueue<V> queue : this.map.values()){ return this.map.get(key);
queue.add(value); }
unlock(queue);
public void setValue(V value){
for(Entry<Key, BlockingQueue<V>> entry : this.map.entrySet()){
entry.getValue().add(value);
this.unlock(entry.getKey());
} }
} }
public V getValue(Key key) throws InterruptedException{ public V getValue(Key key){
BlockingQueue<V> queue = this.map.get(key); BlockingQueue<V> queue = get(key);
lock(queue); if(queue.isEmpty()) return null;
return queue.poll(); return queue.poll();
} }
private void lock(BlockingQueue<V> queue) throws InterruptedException{ public void lock(Key key) throws InterruptedException{
BlockingQueue<V> queue = get(key);
if(queue.isEmpty()){ if(queue.isEmpty()){
synchronized(queue){ synchronized(queue){
queue.wait(); queue.wait();
@ -44,7 +50,8 @@ public class Locker<V>{
} }
} }
private void unlock(BlockingQueue<V> queue){ public void unlock(Key key){
BlockingQueue<V> queue = get(key);
synchronized(queue){ synchronized(queue){
queue.notify(); queue.notify();
} }
@ -54,40 +61,46 @@ public class Locker<V>{
Key key = new Key(); Key key = new Key();
init(key); init(key);
try { try {
while(true) action.accept(getValue(key)); while(true){
lock(key);
V value = getValue(key);
action.accept(value);
}
}catch(Exception e){} }catch(Exception e){}
remove(key); remove(key);
} }
public void listen(Consumer<V> action, Consumer<Exception> onClose){ public void listen(Supplier<Boolean> condition,Consumer<V> action){
Key key = new Key(); Key key = new Key();
init(key); init(key);
try { try {
while(true) action.accept(getValue(key)); while(condition.get()){
}catch(Exception e){ lock(key);
onClose.accept(e); V value = getValue(key);
} action.accept(value);
remove(key); }
}
public void listen(Supplier<Boolean> condition, Consumer<V> action){
Key key = new Key();
init(key);
try {
while(condition.get()) action.accept(getValue(key));
}catch(Exception e){} }catch(Exception e){}
remove(key); remove(key);
} }
public void listen(Supplier<Boolean> condition, Consumer<V> action, Consumer<Exception> onClose){ public Thread listenAsync(Consumer<V> action){
Key key = new Key(); Thread thread = new Thread(new Runnable(){
init(key); public void run(){
try { listen(action);
while(condition.get()) action.accept(getValue(key)); }
}catch(Exception e){ });
onClose.accept(e); thread.start();
} return thread;
remove(key); }
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(){} }

View file

@ -1,7 +0,0 @@
package dev.peerat.framework;
public interface ResponseMapper{
void map(Context context, HttpReader reader, HttpWriter writer, Object result) throws Exception;
}

View file

@ -1,72 +0,0 @@
package dev.peerat.framework;
import java.lang.reflect.Method;
import java.util.regex.Matcher;
public class RouteBinder{
//TODO interceptor -------> bind to a specific route, no all routes ? or both. Could be interesting for somes cases !
private Class<?>[] types;
private int[] bindMethod;
private int parameterCount;
public RouteBinder(){}
public void bind(Class<?>... types){
if(this.types == null){
this.types = types;
this.parameterCount = this.types.length;
return;
}
Class<?>[] copy = new Class[this.types.length+types.length];
System.arraycopy(this.types, 0, copy, 0, this.types.length);
System.arraycopy(types, 0, copy, this.types.length, types.length);
this.types = copy;
this.parameterCount = this.types.length;
}
public void bindIndexes(Method method){
Class<?>[] parameters = method.getParameterTypes();
this.bindMethod = new int[4+(types == null ? 0 : types.length)];
this.bindMethod[0] = findIndex(parameters, Matcher.class);
this.bindMethod[1] = findIndex(parameters, Context.class);
this.bindMethod[2] = findIndex(parameters, HttpReader.class);
this.bindMethod[3] = findIndex(parameters, HttpWriter.class);
if(this.types == null) return;
for(int i = 0; i < this.types.length; i++){
this.bindMethod[4+i] = findIndex(parameters, this.types[i]);
}
this.types = null;
}
public int getParameterCount(){
return this.parameterCount;
}
public Object[] bindMethod(Method method, Matcher matcher, Context context, HttpReader reader, HttpWriter writer, Object[] objects){
Object[] result = new Object[method.getParameterCount()];
setElement(result, bindMethod[0], matcher);
setElement(result, bindMethod[1], context);
setElement(result, bindMethod[2], reader);
setElement(result, bindMethod[3], writer);
for(int i = 0; i < objects.length; i++){
setElement(result, bindMethod[4+i], objects[i]);
}
return result;
}
private void setElement(Object[] array, int position, Object element){
if(position < 0) return;
array[position] = element;
}
private int findIndex(Class<?>[] array, Class<?> type){
for(int i = 0; i < array.length; i++){
Class<?> clazz = array[i];
if(type.isAssignableFrom(clazz)) return i;
}
return -1;
}
}

View file

@ -1,61 +0,0 @@
package dev.peerat.framework;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
public class RouteCachable{
private List<RouteInterceptor> interceptors;
private Map<RouteFilter, List<RouteInterceptor>> localInterceptors;
private Map<RouteFilter, Class<?>[]> types;
public RouteCachable(){
this.interceptors = new ArrayList<>();
this.localInterceptors = new HashMap<>();
this.types = new HashMap<>();
}
public void addInterceptor(RouteMapper<?>[] mappers, RouteInterceptor interceptor){
this.interceptors.add(interceptor);
for(RouteMapper<?> mapper : mappers) mapper.addInterceptor(interceptor);
}
public void addInterceptor(RouteMapper<?>[] mappers, RouteFilter filter, RouteInterceptor interceptor){
List<RouteInterceptor> list = this.localInterceptors.get(filter);
if(list == null){
list = new ArrayList<>();
this.localInterceptors.put(filter, list);
}
list.add(interceptor);
for(RouteMapper<?> mapper : mappers) mapper.addInterceptor(filter, interceptor);
}
public void bind(RouteMapper<?>[] mappers, RouteFilter filter, Class<?>... types){
Class<?>[] array = this.types.get(filter);
Class<?>[] copy;
if(array == null){
copy = types;
}else{
copy = new Class[array.length+types.length];
System.arraycopy(array, 0, copy, 0, array.length);
System.arraycopy(types, 0, copy, array.length, types.length);
}
this.types.put(filter, copy);
for(RouteMapper<?> mapper : mappers) mapper.bind(filter, types);
}
public void registerRoute(RouteState route){
if(!this.interceptors.isEmpty()) route.addInterceptors(this.interceptors);
for(Entry<RouteFilter, List<RouteInterceptor>> entry : this.localInterceptors.entrySet()){
if(entry.getKey().accept(route.getMethod(), route.getRoute(), route.getPattern())) route.addInterceptors(entry.getValue());
}
for(Entry<RouteFilter, Class<?>[]> entry : this.types.entrySet()){
if(entry.getKey().accept(route.getMethod(), route.getRoute(), route.getPattern())) route.getBinder().bind(entry.getValue());
}
}
}

View file

@ -1,10 +0,0 @@
package dev.peerat.framework;
import java.lang.reflect.Method;
import java.util.regex.Pattern;
public interface RouteFilter{
boolean accept(Method method, Route route, Pattern pattern);
}

View file

@ -5,6 +5,6 @@ import java.util.regex.Matcher;
public interface RouteInterceptor{ public interface RouteInterceptor{
boolean intercept(Matcher matcher, Context context, HttpReader reader, HttpWriter writer, Method method, Object[] params); boolean intercept(Matcher matcher, Context context, HttpReader reader, HttpWriter writer, Method method);
} }

View file

@ -4,6 +4,7 @@ import java.lang.reflect.Method;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.util.List; import java.util.List;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern;
import dev.peerat.framework.utils.json.JsonMap; import dev.peerat.framework.utils.json.JsonMap;
@ -11,93 +12,73 @@ public class RouteMapper<U extends User>{
private Router<U> router; private Router<U> router;
private RouteState[] states; private Response[] responses;
private Method[] methods;
private Route[] routes;
private Pattern[] patterns;
private int[] calls; private int[] calls;
private int dif; private int dif;
public RouteMapper(Router<U> router){ public RouteMapper(Router<U> router){
this.router = router; this.router = router;
this.states = new RouteState[0]; this.responses = new Response[0];
this.methods = new Method[0];
this.routes = new Route[0];
this.patterns = new Pattern[0];
} }
public RouteState register(Object instance, Method method, Route route){ public void register(Response response, Method method, Route route, Pattern pattern){
int length = states.length+1; int length = responses.length+1;
method.setAccessible(true); this.responses = addElement(responses, new Response[length], response);
RouteState state = new RouteState(instance, method, route); this.methods = addElement(methods, new Method[length], method);
this.states = addElement(states, new RouteState[length], state); 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]; if(this.calls != null) this.calls = new int[length];
return state;
}
public void addInterceptor(RouteInterceptor interceptor){
for(RouteState state : this.states){
state.addInterceptor(interceptor);
}
}
public void addInterceptor(RouteFilter filter, RouteInterceptor interceptor){
for(RouteState state : this.states){
if(filter.accept(state.getMethod(), state.getRoute(), state.getPattern())) state.addInterceptor(interceptor);
}
}
public void bind(RouteFilter filter, Class<?>... types){
for(RouteState state : this.states){
if(filter.accept(state.getMethod(), state.getRoute(), state.getPattern())) state.getBinder().bind(types);
}
}
public void bindAll(){
for(RouteState state : this.states) state.getBinder().bindIndexes(state.getMethod());
} }
public void activeReOrdering(){ public void activeReOrdering(){
this.calls = new int[states.length]; this.calls = new int[responses.length];
this.dif = 2; this.dif = 2;
} }
public boolean exec(Context context, HttpReader reader, HttpWriter writer) throws Exception{ public boolean exec(Context context, HttpReader reader, HttpWriter writer, List<RouteInterceptor> interceptors) throws Exception{
RouteState target = null; Response response = null;
Method method = null;
Matcher matcher = null; Matcher matcher = null;
String path = context.getPath(); String path = context.getPath();
synchronized(states){ synchronized(responses){
for(int i = 0; i < states.length; i++){ for(int i = 0; i < responses.length; i++){
RouteState state = states[i]; Route route = routes[i];
matcher = state.match(path); Pattern pattern = patterns[i];
if(matcher != null){ matcher = pattern.matcher(path);
if(state.isWebSocket()){ if(matcher.matches()){
if(route.websocket()){
switchToWebSocket(reader, writer); switchToWebSocket(reader, writer);
reader = new WebSocketReader(reader); reader = new WebSocketReader(reader);
writer = new WebSocketWriter(writer); writer = new WebSocketWriter(writer);
if(state.needLogin()){ if(route.needLogin()){
String token = reader.<JsonMap>readJson().get("token"); String token = reader.<JsonMap>readJson().get("token");
context.setUser(this.router.getUser(token)); context.setUser(this.router.getUser(token));
} }
} }
if((!context.isLogged()) && state.needLogin()){ if((!context.isLogged()) && route.needLogin()){
writer.response(401, router.getDefaultHeaders(context.getType())); writer.response(401, router.getDefaultHeaders(context.getType()));
return true; return true;
} }
target = state; response = responses[i];
method = methods[i];
order(i); order(i);
break; break;
} }
} }
} }
if(target != null){ if(response != null){
Object[] params = new Object[target.getBinder().getParameterCount()]; for(RouteInterceptor interceptor : interceptors){
List<RouteInterceptor> localInterceptors = target.getInterceptors(); if(!interceptor.intercept(matcher, context, reader, writer, method)) return true;
if(localInterceptors != null){
for(RouteInterceptor interceptor : localInterceptors){
if(!interceptor.intercept(matcher, context, reader, writer, target.getMethod(), params)) return true;
}
} }
Object[] inv = target.getBinder().bindMethod(target.getMethod(), matcher, context, reader, writer, params); response.exec(matcher, context, reader, writer);
Object result = target.getMethod().invoke(target.getInstance(), inv);
if(result == null) return true;
router.getMapper().map(context, reader, writer, result);
return true; return true;
} }
return false; return false;
@ -113,7 +94,10 @@ public class RouteMapper<U extends User>{
} }
if(index < 1) return; if(index < 1) return;
if(call > this.calls[index-1]){ if(call > this.calls[index-1]){
switchElement(states, index, index-1); switchElement(responses, index, index-1);
switchElement(methods, index, index-1);
switchElement(routes, index, index-1);
switchElement(patterns, index, index-1);
this.calls[index] = this.calls[index-1]; this.calls[index] = this.calls[index-1];
this.calls[index-1] = call; this.calls[index-1] = call;

View file

@ -1,78 +0,0 @@
package dev.peerat.framework;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RouteState {
private Object instance;
private Method method;
private Route route;
private Pattern pattern;
private RouteBinder binder;
private List<RouteInterceptor> interceptors; //TODO concat interceptors ??
public RouteState(Object instance, Method method, Route route){
this.instance = instance;
this.method = method;
this.route = route;
this.pattern = Pattern.compile(route.path());
this.binder = new RouteBinder();
}
public void bindIndexes(){
this.binder.bindIndexes(this.method);
}
public Matcher match(String path){
Matcher matcher = pattern.matcher(path);
return matcher.matches() ? matcher : null;
}
public void addInterceptor(RouteInterceptor interceptor){
if(this.interceptors == null) this.interceptors = new ArrayList<>();
this.interceptors.add(interceptor);
}
public void addInterceptors(Collection<RouteInterceptor> interceptors){
if(this.interceptors == null) this.interceptors = new ArrayList<>(interceptors);
else this.interceptors.addAll(interceptors);
}
public List<RouteInterceptor> getInterceptors(){
return this.interceptors;
}
public Route getRoute(){
return this.route;
}
public boolean isWebSocket(){
return this.route.websocket();
}
public boolean needLogin(){
return this.route.needLogin();
}
public Method getMethod(){
return this.method;
}
public Object getInstance(){
return this.instance;
}
public Pattern getPattern(){
return this.pattern;
}
public RouteBinder getBinder(){
return this.binder;
}
}

View file

@ -5,26 +5,49 @@ import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.net.ServerSocket; import java.net.ServerSocket;
import java.net.Socket; import java.net.Socket;
import java.security.Key;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.regex.Pattern;
import javax.net.ssl.SSLServerSocket; import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory; import javax.net.ssl.SSLServerSocketFactory;
import dev.peerat.framework.auth.Authenticator; import org.jose4j.jwa.AlgorithmConstraints.ConstraintType;
import org.jose4j.jwk.JsonWebKey.OutputControlLevel;
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 class Router<U extends User>{
public static void main(String[] args) throws Exception{} public static void main(String[] args) {
}
private Locker<Context> logger; private Locker<Context> logger;
private Locker<Throwable> exceptions; private Locker<Throwable> exceptions;
private RouteMapper<U>[] mappers; private RouteMapper<U>[] mappers;
private RouteCachable cachable; private List<RouteInterceptor> interceptors;
private Response noFileFound; private Response noFileFound;
private Authenticator<U> auth; private RsaJsonWebKey rsaJsonWebKey;
private JwtConsumer jwtConsumer;
private Consumer<JwtClaims> claims;
private Function<JwtClaims, U> userCreator;
private String[][] headers; private String[][] headers;
private ResponseMapper responseMapper;
private ServerSocket serverSocket; private ServerSocket serverSocket;
public Router(){ public Router(){
@ -32,16 +55,41 @@ public class Router<U extends User>{
this.exceptions = new Locker<>(); this.exceptions = new Locker<>();
int types = RequestType.values().length; int types = RequestType.values().length;
this.mappers = new RouteMapper[types]; this.mappers = new RouteMapper[types];
this.interceptors = new ArrayList<>();
for(RequestType type : RequestType.values()) this.mappers[type.ordinal()] = new RouteMapper<>(this); for(RequestType type : RequestType.values()) this.mappers[type.ordinal()] = new RouteMapper<>(this);
this.cachable = new RouteCachable();
this.headers = new String[types][0]; this.headers = new String[types][0];
} }
public Router<U> setAuthenticator(Authenticator<U> auth){ public Router<U> configureJwt(Consumer<JwtConsumerBuilder> consumer, Consumer<JwtClaims> claims, Function<JwtClaims, U> userCreator) throws Exception{
this.auth = auth; this.rsaJsonWebKey = RsaJwkGenerator.generateJwk(2048);
configureJwtWithKey(consumer, claims, userCreator, this.rsaJsonWebKey.getKey());
return this; return this;
} }
public Router<U> configureJwt(Consumer<JwtConsumerBuilder> consumer, Consumer<JwtClaims> claims, Function<JwtClaims, U> userCreator, Map<String, Object> keyParams) throws Exception{
this.rsaJsonWebKey = new RsaJsonWebKey(keyParams);
configureJwtWithKey(consumer, claims, userCreator, this.rsaJsonWebKey.getKey());
return this;
}
private void configureJwtWithKey(Consumer<JwtConsumerBuilder> consumer, Consumer<JwtClaims> claims, Function<JwtClaims, U> userCreator, Key key) throws Exception{
JwtConsumerBuilder builder = new JwtConsumerBuilder()
.setRequireExpirationTime()
.setAllowedClockSkewInSeconds(30)
.setVerificationKey(key)
.setJwsAlgorithmConstraints(ConstraintType.PERMIT, AlgorithmIdentifiers.RSA_USING_SHA256);
consumer.accept(builder);
this.jwtConsumer = builder.build();
this.claims = claims;
this.userCreator = userCreator;
}
public Map<String, Object> exportJwtKey(){
return this.rsaJsonWebKey.toParams(OutputControlLevel.INCLUDE_PRIVATE);
}
public Router<U> addDefaultHeaders(RequestType type, String... headers){ public Router<U> addDefaultHeaders(RequestType type, String... headers){
String[] origin = this.headers[type.ordinal()]; String[] origin = this.headers[type.ordinal()];
String[] copy = new String[origin.length+headers.length]; String[] copy = new String[origin.length+headers.length];
@ -51,57 +99,25 @@ public class Router<U extends User>{
return this; return this;
} }
public Router<U> setMapper(ResponseMapper mapper){
this.responseMapper = mapper;
return this;
}
public Router<U> register(Response response){ public Router<U> register(Response response){
try{ try{
Method method = response.getClass().getDeclaredMethod("exec", Method method = response.getClass().getDeclaredMethod("exec",
Response.class.getDeclaredMethods()[0].getParameterTypes()); Response.class.getDeclaredMethods()[0].getParameterTypes());
Route route = method.getAnnotation(Route.class); Route route = method.getAnnotation(Route.class);
RouteState state = this.mappers[route.type().ordinal()].register(response, method, route); this.mappers[route.type().ordinal()].register(response, method, route, Pattern.compile(route.path()));
this.cachable.registerRoute(state);
System.out.println("Registered route "+method+" ["+route.type()+" "+route.path()+"]");
}catch(Exception e){ }catch(Exception e){
throw new IllegalArgumentException(e); throw new IllegalArgumentException(e);
} }
return this; return this;
} }
public Router<U> registerClass(Class<?> clazz, Object... injections) throws Exception{
return registerClass(clazz, new DependencyInjector().of(injections));
}
public Router<U> registerClass(Class<?> clazz, DependencyInjector injector) throws Exception{
Object instance = null;
for(Method method : clazz.getDeclaredMethods()){
Route route = method.getAnnotation(Route.class);
if(route == null) continue;
if(instance == null){
Constructor<?>[] constructors = clazz.getDeclaredConstructors();
if(constructors.length != 1) throw new IllegalArgumentException("Class with multiple constructor. Not supported by this framework for the moment.");
instance = constructors[0].newInstance(injector.apply(constructors[0]));
}
RouteState state = this.mappers[route.type().ordinal()].register(instance, method, route);
this.cachable.registerRoute(state);
System.out.println("Registered route "+method+" ["+route.type()+" "+route.path()+"]");
}
return this;
}
public Router<U> registerPackages(Object... injections){ public Router<U> registerPackages(Object... injections){
return registerPackages(new DependencyInjector().of(injections)); return registerPackages(new DependencyInjector().of(injections));
} }
public Router<U> registerPackages(DependencyInjector injector){ public Router<U> registerPackages(DependencyInjector injector){
String clazz = Thread.currentThread().getStackTrace()[2].getClassName(); String clazz = Thread.currentThread().getStackTrace()[2].getClassName();
if(clazz.startsWith("dev.peerat.framework")) clazz = Thread.currentThread().getStackTrace()[3].getClassName();
String pack = clazz.substring(0, clazz.lastIndexOf('.')); String pack = clazz.substring(0, clazz.lastIndexOf('.'));
return registerPackages(pack, injector); return registerPackages(pack, injector);
} }
@ -115,13 +131,19 @@ public class Router<U extends User>{
try{ try{
BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
String line; String line;
Map<Class<?>, Object> cache = new HashMap<>();
while((line = reader.readLine()) != null){ while((line = reader.readLine()) != null){
if(line.endsWith(".class")){ if(line.endsWith(".class")){
Class<?> clazz = Class.forName(name+"."+line.substring(0, line.length()-6)); Class<?> clazz = Class.forName(name+"."+line.substring(0, line.lastIndexOf('.')));
registerClass(clazz, injector); if(Response.class.isAssignableFrom(clazz)){
continue; if(clazz.equals(Response.class)) continue;
Constructor<?>[] constructors = clazz.getDeclaredConstructors();
if(constructors.length != 1) continue;
injectDependencies(constructors[0], cache, injector);
}
}else{
registerPackages(name+"."+line, injector);
} }
if(!line.contains(".")) registerPackages(name+"."+line, injector);
} }
reader.close(); reader.close();
}catch(Exception e){ }catch(Exception e){
@ -131,30 +153,36 @@ public class Router<U extends User>{
return this; return this;
} }
private void injectDependencies(Constructor<?> constructor, Map<Class<?>, Object> cache, DependencyInjector injector){
Parameter[] parameters = constructor.getParameters();
Object[] injections = new Object[parameters.length];
for(int i = 0; i < parameters.length; i++){
Parameter parameter = parameters[i];
Class<?> type = parameter.getType();
Object dependency = cache.get(type);
if(dependency != null) injections[i] = dependency;
else if((injections[i] = injector.applyDependency(constructor, parameter, cache)) == null) throw new IllegalArgumentException("No dependency for type "+type+" in constructor "+constructor);
}
try {
Response response = (Response) constructor.newInstance(injections);
register(response);
System.out.println("registerd "+response.getClass());
} catch (Exception e){
e.printStackTrace();
}
}
public Router<U> setDefault(Response response){ public Router<U> setDefault(Response response){
this.noFileFound = response; this.noFileFound = response;
return this; return this;
} }
public Router<U> addInterceptor(RouteInterceptor interceptor){ public Router<U> addInterceptor(RouteInterceptor interceptor){
this.cachable.addInterceptor(mappers, interceptor); this.interceptors.add(interceptor);
return this; return this;
} }
public Router<U> addInterceptor(RouteFilter filter, RouteInterceptor interceptor){
this.cachable.addInterceptor(mappers, filter, interceptor);
return this;
}
public Router<U> bind(RouteFilter filter, Class<?>... types){
this.cachable.bind(mappers, filter, types);
return this;
}
private void bindAll(){
for(RouteMapper<U> mapper : this.mappers) mapper.bindAll();
}
public Router<U> activeReOrdering(){ public Router<U> activeReOrdering(){
for(RouteMapper<?> mapper : this.mappers) mapper.activeReOrdering(); for(RouteMapper<?> mapper : this.mappers) mapper.activeReOrdering();
return this; return this;
@ -164,16 +192,8 @@ public class Router<U extends User>{
return this.headers[type.ordinal()]; return this.headers[type.ordinal()];
} }
public ResponseMapper getMapper(){
return this.responseMapper;
}
public Authenticator<U> getAuthenticator(){
return this.auth;
}
void exec(Context context, HttpReader reader, HttpWriter writer) throws Exception{ void exec(Context context, HttpReader reader, HttpWriter writer) throws Exception{
if(this.mappers[context.getType().ordinal()].exec(context, reader, writer)) return; if(this.mappers[context.getType().ordinal()].exec(context, reader, writer, this.interceptors)) return;
if(noFileFound != null) noFileFound.exec(null, context, reader, writer); if(noFileFound != null) noFileFound.exec(null, context, reader, writer);
} }
@ -183,16 +203,25 @@ public class Router<U extends User>{
return this; return this;
} }
public U getUser(RequestType type, HttpReader reader) throws Exception{ public U getUser(String token) throws InvalidJwtException{
return this.auth != null ? this.auth.getUser(type, reader) : null; return this.userCreator.apply(this.jwtConsumer.processToClaims(token));
} }
public U getUser(String data) throws Exception{ public String createAuthUser(U user) throws JoseException{
return this.auth != null ? this.auth.getUser(data) : null; 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);
public String createAuthUser(U user) throws Exception{ user.write(claims);
return this.auth.createAuthUser(user);
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(){ public Locker<Context> getLogger(){
@ -203,13 +232,7 @@ public class Router<U extends User>{
return this.exceptions; return this.exceptions;
} }
private void checkStart(){
bindAll();
if(this.responseMapper == null) this.responseMapper = (c,r,w,o) -> {};
}
public void listen(int port, boolean ssl) throws Exception{ public void listen(int port, boolean ssl) throws Exception{
checkStart();
if (ssl) { // Not needed with the use of a proxy if (ssl) { // Not needed with the use of a proxy
try { try {
SSLServerSocketFactory ssf = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault(); SSLServerSocketFactory ssf = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();

View file

@ -1,321 +0,0 @@
package dev.peerat.framework;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CoderResult;
public class StrictReader {
private boolean skipLF = false;
private int nextChar;
private int nChars;
private char[] cb;
private int markedChar = -1;
private int readAheadLimit = 0;
private InputStream in;
private boolean haveLeftoverChar = false;
private char leftoverChar;
private ByteBuffer bb;
private CharsetDecoder decoder;
StrictReader(InputStream in) {
this.in = in;
this.decoder = Charset.defaultCharset().newDecoder();
this.cb = new char[1];
this.nextChar = this.nChars = 0;
this.bb = ByteBuffer.allocate(1);
this.bb.flip();
}
public boolean ready() throws IOException {
synchronized (this.in) {
ensureOpen();
if (this.skipLF) {
if (this.nextChar >= this.nChars && sdReady())
fill();
if (this.nextChar < this.nChars) {
if (this.cb[this.nextChar] == '\n')
this.nextChar++;
this.skipLF = false;
}
}
return (this.nextChar < this.nChars || sdReady());
}
}
public boolean sdReady() throws IOException {
synchronized (this.in) {
ensureOpen();
return (this.haveLeftoverChar || implReady());
}
}
public int read(char[] paramArrayOfchar, int paramInt1, int paramInt2) throws IOException {
if (paramInt1 < 0 || paramInt1 > paramArrayOfchar.length || paramInt2 < 0
|| paramInt1 + paramInt2 > paramArrayOfchar.length || paramInt1 + paramInt2 < 0)
throw new IndexOutOfBoundsException();
synchronized (this.in) {
ensureOpen();
if (paramInt2 == 0)
return 0;
int i = read1(paramArrayOfchar, paramInt1, paramInt2);
if (i <= 0)
return i;
while (i < paramInt2 && sdReady()) {
int j = read1(paramArrayOfchar, paramInt1 + i, paramInt2 - i);
if (j <= 0)
break;
i += j;
}
return i;
}
}
private int read1(char[] paramArrayOfchar, int paramInt1, int paramInt2) throws IOException {
if (this.nextChar >= this.nChars) {
if (paramInt2 >= this.cb.length && this.markedChar <= -1 && !this.skipLF)
return sdRead(paramArrayOfchar, paramInt1, paramInt2);
fill();
}
if (this.nextChar >= this.nChars)
return -1;
if (this.skipLF) {
this.skipLF = false;
if (this.cb[this.nextChar] == '\n') {
this.nextChar++;
if (this.nextChar >= this.nChars)
fill();
if (this.nextChar >= this.nChars)
return -1;
}
}
int i = Math.min(paramInt2, this.nChars - this.nextChar);
System.arraycopy(this.cb, this.nextChar, paramArrayOfchar, paramInt1, i);
this.nextChar += i;
return i;
}
private void ensureOpen() throws IOException {
if (this.in == null)
throw new IOException("Stream closed");
}
public String readLines() throws IOException{
StringBuilder builder = new StringBuilder();
synchronized (this.in){
ensureOpen();
}
return builder.toString();
}
public String readLine() throws IOException {
StringBuilder stringBuilder = null;
synchronized (this.in) {
ensureOpen();
boolean bool = this.skipLF;
while (true) {
if (this.nextChar >= this.nChars)
fill();
if (this.nextChar >= this.nChars) {
if (stringBuilder != null && stringBuilder.length() > 0)
return stringBuilder.toString();
return null;
}
boolean bool1 = false;
char c = Character.MIN_VALUE;
if (bool && this.cb[this.nextChar] == '\n')
this.nextChar++;
this.skipLF = false;
bool = false;
int j;
for (j = this.nextChar; j < this.nChars; j++) {
c = this.cb[j];
if (c == '\n' || c == '\r') {
bool1 = true;
break;
}
}
int i = this.nextChar;
this.nextChar = j;
if (bool1) {
String str;
if (stringBuilder == null) {
str = new String(this.cb, i, j - i);
} else {
stringBuilder.append(this.cb, i, j - i);
str = stringBuilder.toString();
}
this.nextChar++;
if (c == '\r')
this.skipLF = true;
return str;
}
if (stringBuilder == null)
stringBuilder = new StringBuilder();
stringBuilder.append(this.cb, i, j - i);
}
}
}
private void fill() throws IOException {
int i;
if (this.markedChar <= -1) {
i = 0;
} else {
int j = this.nextChar - this.markedChar;
if (j >= this.readAheadLimit) {
this.markedChar = -2;
this.readAheadLimit = 0;
i = 0;
} else {
if (this.readAheadLimit <= this.cb.length) {
System.arraycopy(this.cb, this.markedChar, this.cb, 0, j);
this.markedChar = 0;
i = j;
} else {
char[] arrayOfChar = new char[this.readAheadLimit];
System.arraycopy(this.cb, this.markedChar, arrayOfChar, 0, j);
this.cb = arrayOfChar;
this.markedChar = 0;
i = j;
}
this.nextChar = this.nChars = j;
}
}
while (true) {
int j = sdRead(this.cb, i, this.cb.length - i);
if (j != 0) {
if (j > 0) {
this.nChars = i + j;
this.nextChar = i;
}
return;
}
}
}
// StreamDecoder
private int sdRead(char[] paramArrayOfchar, int paramInt1, int paramInt2) throws IOException {
int i = paramInt1;
int j = paramInt2;
if (i < 0 || i > paramArrayOfchar.length || j < 0 || i + j > paramArrayOfchar.length || i + j < 0)
throw new IndexOutOfBoundsException();
synchronized (this.in) {
ensureOpen();
if (j == 0)
return 0;
byte b = 0;
if (this.haveLeftoverChar) {
paramArrayOfchar[i] = this.leftoverChar;
i++;
j--;
this.haveLeftoverChar = false;
b = 1;
if (j == 0 || !implReady())
return b;
}
if (j == 1) {
int k = read0();
if (k == -1)
return b == 0 ? -1 : b;
paramArrayOfchar[i] = (char) k;
return b + 1;
}
return b + implRead(paramArrayOfchar, i, i + j);
}
}
boolean implReady() {
return (this.bb.hasRemaining() || inReady());
}
private boolean inReady() {
try {
return (this.in != null && this.in.available() > 0);
} catch (IOException iOException) {
return false;
}
}
int implRead(char[] paramArrayOfchar, int paramInt1, int paramInt2) throws IOException {
CharBuffer charBuffer = CharBuffer.wrap(paramArrayOfchar, paramInt1, paramInt2 - paramInt1);
if (charBuffer.position() != 0)
charBuffer = charBuffer.slice();
boolean bool = false;
while (true) {
CoderResult coderResult = this.decoder.decode(this.bb, charBuffer, bool);
if (coderResult.isUnderflow()) {
if (bool || !charBuffer.hasRemaining() || (charBuffer.position() > 0 && !inReady()))
break;
int i = readBytes();
if (i < 0) {
bool = true;
if (charBuffer.position() == 0 && !this.bb.hasRemaining())
break;
this.decoder.reset();
}
continue;
}
if (coderResult.isOverflow()) {
break;
}
coderResult.throwException();
}
if (bool)
this.decoder.reset();
if (charBuffer.position() == 0) {
if (bool)
return -1;
}
return charBuffer.position();
}
private int read0() throws IOException {
synchronized (this.in) {
if (this.haveLeftoverChar) {
this.haveLeftoverChar = false;
return this.leftoverChar;
}
char[] arrayOfChar = new char[2];
int i = sdRead(arrayOfChar, 0, 2);
switch (i) {
case -1:
return -1;
case 2:
this.leftoverChar = arrayOfChar[1];
this.haveLeftoverChar = true;
case 1:
return arrayOfChar[0];
}
return -1;
}
}
private int readBytes() throws IOException {
this.bb.compact();
try {
int j = this.bb.limit();
int k = this.bb.position();
int b = (k <= j) ? (j - k) : 0;
int m = this.in.read(this.bb.array(), this.bb.arrayOffset() + k, b);
if (m < 0)
return m;
if (m == 0)
throw new IOException("Underlying input stream returned zero bytes");
this.bb.position(k + m);
} finally {
this.bb.flip();
}
int i = this.bb.remaining();
return i;
}
}

View file

@ -1,3 +1,9 @@
package dev.peerat.framework; package dev.peerat.framework;
public abstract class User{} import org.jose4j.jwt.JwtClaims;
public abstract class User{
public abstract void write(JwtClaims claims);
}

View file

@ -1,17 +0,0 @@
package dev.peerat.framework.auth;
public class AuthException extends Exception{
public AuthException(String message){
super(message);
}
public AuthException(Throwable throwable){
super(throwable);
}
public AuthException(String message, Throwable throwable){
super(message, throwable);
}
}

View file

@ -1,15 +0,0 @@
package dev.peerat.framework.auth;
import dev.peerat.framework.HttpReader;
import dev.peerat.framework.RequestType;
import dev.peerat.framework.User;
public interface Authenticator<U extends User>{
String createAuthUser(U user) throws Exception;
U getUser(RequestType type, HttpReader reader) throws Exception;
U getUser(String data) throws Exception;
}

View file

@ -1,94 +0,0 @@
package dev.peerat.framework.auth;
import java.security.Key;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import org.jose4j.jwa.AlgorithmConstraints.ConstraintType;
import org.jose4j.jwk.JsonWebKey.OutputControlLevel;
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 dev.peerat.framework.HttpReader;
import dev.peerat.framework.RequestType;
import dev.peerat.framework.User;
public class JwtAuthenticator<U extends User> implements Authenticator<U>{
private RsaJsonWebKey rsaJsonWebKey;
private JwtConsumer jwtConsumer;
private BiConsumer<JwtClaims, U> claims;
private Function<JwtClaims, U> userCreator;
public JwtAuthenticator(){}
public JwtAuthenticator<U> configure(Consumer<JwtConsumerBuilder> consumer, BiConsumer<JwtClaims, U> claims, Function<JwtClaims, U> userCreator) throws Exception{
this.rsaJsonWebKey = RsaJwkGenerator.generateJwk(2048);
configureWithKey(consumer, claims, userCreator, this.rsaJsonWebKey.getKey());
return this;
}
public JwtAuthenticator<U> configure(Consumer<JwtConsumerBuilder> consumer, BiConsumer<JwtClaims, U> claims, Function<JwtClaims, U> userCreator, Map<String, Object> keyParams) throws Exception{
this.rsaJsonWebKey = new RsaJsonWebKey(keyParams);
configureWithKey(consumer, claims, userCreator, this.rsaJsonWebKey.getKey());
return this;
}
private JwtAuthenticator<U> configureWithKey(Consumer<JwtConsumerBuilder> consumer, BiConsumer<JwtClaims, U> claims, Function<JwtClaims, U> userCreator, Key key) throws Exception{
JwtConsumerBuilder builder = new JwtConsumerBuilder()
.setRequireExpirationTime()
.setAllowedClockSkewInSeconds(30)
.setVerificationKey(key)
.setJwsAlgorithmConstraints(ConstraintType.PERMIT, AlgorithmIdentifiers.RSA_USING_SHA256);
consumer.accept(builder);
this.jwtConsumer = builder.build();
this.claims = claims;
this.userCreator = userCreator;
return this;
}
public Map<String, Object> exportKey(){
return this.rsaJsonWebKey.toParams(OutputControlLevel.INCLUDE_PRIVATE);
}
@Override
public String createAuthUser(U user) throws Exception{
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);
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();
}
@Override
public U getUser(RequestType type, HttpReader reader) throws Exception{
String auth = reader.getHeader("Authorization");
if(auth == null) return null;
auth = auth.substring(7);
return this.userCreator.apply(this.jwtConsumer.processToClaims(auth));
}
@Override
public U getUser(String data) throws Exception {
if(data == null) return null;
return this.userCreator.apply(this.jwtConsumer.processToClaims(data));
}
}

View file

@ -3,25 +3,16 @@ package dev.peerat.framework.utils.json;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Iterator; import java.util.Iterator;
import java.util.List;
public class JsonArray extends Json{ public class JsonArray extends Json{
private Collection<Object> list; private List<Object> list;
public JsonArray(){ public JsonArray(){
this.list = new ArrayList<>(); this.list = new ArrayList<>();
} }
public JsonArray(Collection<Object> collection){
this();
this.list.addAll(collection);
}
public JsonArray(Iterator<Object> iterator){
this();
while(iterator.hasNext()) this.list.add(iterator.next());
}
public <E> Collection<E> toList(){ public <E> Collection<E> toList(){
return (Collection<E>) this.list; return (Collection<E>) this.list;
} }

View file

@ -14,11 +14,6 @@ public class JsonMap extends Json{
this.map = new HashMap<>(); this.map = new HashMap<>();
} }
public JsonMap(Map<String, Object> map){
this();
for(Entry<String, Object> entry : map.entrySet()) this.map.put(entry.getKey(), entry.getValue());
}
public Set<Entry<String, Object>> entries(){ public Set<Entry<String, Object>> entries(){
return this.map.entrySet(); return this.map.entrySet();
} }

View file

@ -1,17 +1,16 @@
package dev.peerat.framework.utils.json; package dev.peerat.framework.utils.json;
import static dev.peerat.parser.TokenType.*; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import dev.peerat.parser.Parser; import be.jeffcheasey88.peeratcode.parser.Parser;
import dev.peerat.parser.Token; import be.jeffcheasey88.peeratcode.parser.Token;
import dev.peerat.parser.state.RedirectStateTree; import be.jeffcheasey88.peeratcode.parser.TokenType;
import dev.peerat.parser.state.StateTree; import be.jeffcheasey88.peeratcode.parser.state.RedirectStateTree;
import be.jeffcheasey88.peeratcode.parser.state.StateTree;
public class JsonParser extends Parser<Json>{ public class JsonParser extends Parser<Json>{
@ -26,7 +25,7 @@ public class JsonParser extends Parser<Json>{
StateTree<Json> content_array_element = content_array.then(new RedirectStateTree<>(content, (global, local) ->{ StateTree<Json> content_array_element = content_array.then(new RedirectStateTree<>(content, (global, local) ->{
List<Object> list = global.get(); List<Object> list = global.get();
if(list == null){ if(list == null){
list = new LinkedList<>(); list = new ArrayList<>();
global.set(list); global.set(list);
} }
list.add(local.get()); list.add(local.get());
@ -44,16 +43,16 @@ public class JsonParser extends Parser<Json>{
.then(content_array_element); .then(content_array_element);
content.then(new RedirectStateTree<>(base, (global, local) -> global.set(local.get()))).end(); content.then(new RedirectStateTree<>(base, (global, local) -> global.set(local.get()))).end();
content.then((validator) -> validator.validate((token) -> token.getType().equals(STRING), (bag, token) -> bag.set(token))).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(CHAR), (bag, token) -> bag.set(token))).end(); content.then((validator) -> validator.validate((token) -> token.getType().equals(TokenType.CHAR), (bag, token) -> bag.set(token))).end();
StateTree<Json> number = content.then((validator) -> validator.validate((token) -> token.getType().equals(NAME), (bag, token) -> bag.set(token))); StateTree<Json> number = content.then((validator) -> validator.validate((token) -> token.getType().equals(TokenType.NAME), (bag, token) -> bag.set(token)));
number.end(); number.end();
number.then((validator) -> validator.validate((token) -> token.getValue().equals("."), (bag, token) -> bag.set(bag.<Token>get().concat(token)))) number.then((validator) -> validator.validate((token) -> token.getValue().equals("."), (bag, token) -> bag.set(bag.<Token>get().concat(token))))
.then((validator) -> validator.validate((token) -> token.getType().equals(NAME), (bag, token) -> bag.set(bag.<Token>get().concat(token)))) .then((validator) -> validator.validate((token) -> token.getType().equals(TokenType.NAME), (bag, token) -> bag.set(bag.<Token>get().concat(token))))
.end(); .end();
StateTree<Json> mapper = new StateTree<>(); StateTree<Json> mapper = new StateTree<>();
StateTree<Json> mapper_key = mapper.then((validator) -> validator.validate((token) -> token.getType().equals(STRING), (bag, token) -> { StateTree<Json> mapper_key = mapper.then((validator) -> validator.validate((token) -> token.getType().equals(TokenType.STRING), (bag, token) -> {
Map<Token, Object> map = bag.get("map"); Map<Token, Object> map = bag.get("map");
if(map == null){ if(map == null){
map = new HashMap<>(); map = new HashMap<>();
@ -106,20 +105,20 @@ public class JsonParser extends Parser<Json>{
if(value instanceof Token){ if(value instanceof Token){
Token token = (Token) value; Token token = (Token) value;
String content = token.getValue(); String content = token.getValue();
if(token.getType().equals(STRING)){ if(token.getType().equals(TokenType.STRING)){
return content; return content;
}else if(token.getType().equals(CHAR)){ }else if(token.getType().equals(TokenType.CHAR)){
return content.charAt(0); return content.charAt(0);
}else{ }else{
try{ try{
return Long.parseLong(content); return Long.parseLong(content);
}catch(Exception e){ }catch(Exception _){
try { try {
return Double.parseDouble(content); return Double.parseDouble(content);
}catch(Exception ex){ }catch(Exception __){
try{ try{
return Boolean.parseBoolean(content); return Boolean.parseBoolean(content);
}catch(Exception exc){} }catch(Exception ___){}
} }
} }
} }

View file

@ -1,14 +1,13 @@
package dev.peerat.framework.utils.json; package dev.peerat.framework.utils.json;
import dev.peerat.parser.Token; import be.jeffcheasey88.peeratcode.parser.Token;
import dev.peerat.parser.TokenType; import be.jeffcheasey88.peeratcode.parser.TokenType;
import dev.peerat.parser.Tokenizer; import be.jeffcheasey88.peeratcode.parser.Tokenizer;
public class JsonTokenizer extends Tokenizer{ public class JsonTokenizer extends Tokenizer{
@Override @Override
public void parse(String line){ public void parse(String line){
long time = System.currentTimeMillis();
for(int i = 0; i < line.length(); i++){ for(int i = 0; i < line.length(); i++){
char c = line.charAt(i); char c = line.charAt(i);