Compare commits
19 commits
Author | SHA1 | Date | |
---|---|---|---|
|
b77c6d120e | ||
|
dfeb611532 | ||
|
bbb842887b | ||
|
c2eed8b2fe | ||
|
d75e736ba3 | ||
|
f47551fc6f | ||
|
9cedf6898f | ||
|
cdafc9cbaf | ||
|
12f2561921 | ||
|
2c135c2e26 | ||
|
f2ecb3a1eb | ||
|
012978d759 | ||
|
a79876b71c | ||
|
7e4cc9f425 | ||
|
8a6b744519 | ||
|
9193e0d9d3 | ||
|
9f11825f97 | ||
|
c06cc46787 | ||
|
2cf4363e25 |
22 changed files with 990 additions and 258 deletions
Binary file not shown.
|
@ -2,7 +2,7 @@ package dev.peerat.framework;
|
||||||
|
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
|
|
||||||
import org.jose4j.jwt.consumer.InvalidJwtException;
|
import dev.peerat.framework.auth.AuthException;
|
||||||
|
|
||||||
public class Client<U extends User> extends Thread{
|
public class Client<U extends User> extends Thread{
|
||||||
|
|
||||||
|
@ -28,38 +28,35 @@ 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(InvalidJwtException e){
|
}catch(AuthException e){
|
||||||
this.router.getExceptionLogger().setValue(e);
|
this.router.getExceptionLogger().pushValue(e);
|
||||||
}catch(Throwable e){
|
}catch(Throwable e){
|
||||||
this.router.getExceptionLogger().setValue(e);
|
this.router.getExceptionLogger().pushValue(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().setValue(ex);
|
this.router.getExceptionLogger().pushValue(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(context != null) router.getLogger().setValue(context);
|
if(context != null) router.getLogger().pushValue(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
private User isLogin(RequestType type, HttpReader reader) throws InvalidJwtException{
|
private User isLogin(RequestType type, HttpReader reader) throws AuthException{
|
||||||
String auth = reader.getHeader("Authorization");
|
|
||||||
if(auth == null) return null;
|
|
||||||
auth = auth.substring(7);
|
|
||||||
try{
|
try{
|
||||||
return this.router.getUser(auth);
|
return this.router.getUser(type, reader);
|
||||||
}catch(InvalidJwtException e){
|
}catch(Exception 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().setValue(ex);
|
this.router.getExceptionLogger().pushValue(ex);
|
||||||
}
|
}
|
||||||
throw e;
|
throw new AuthException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,21 +1,26 @@
|
||||||
package dev.peerat.framework;
|
package dev.peerat.framework;
|
||||||
|
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Executable;
|
||||||
|
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 List<BiFunction<Constructor<?>, Parameter, Object>> builders;
|
private Map<Class<?>, Supplier<?>> suppliers;
|
||||||
|
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<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,14 +29,20 @@ public class DependencyInjector{
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DependencyInjector of(BiFunction<Constructor<?>, Parameter, Object> builder){
|
public DependencyInjector of(BiFunction<Injection, Class<?>, 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];
|
||||||
|
@ -41,30 +52,56 @@ public class DependencyInjector{
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Object applyDependency(Constructor<?> constructor, Parameter parameter, Map<Class<?>, Object> cache){
|
public Object[] apply(Executable executable) throws Exception{
|
||||||
Injection annotation = parameter.getAnnotation(Injection.class);
|
Parameter[] parameters = executable.getParameters();
|
||||||
|
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) throw new IllegalArgumentException("No depdency named "+annotation.value()+" ("+this.map.keySet()+") in constructor "+constructor);
|
if(result == null){
|
||||||
|
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<Constructor<?>, Parameter, Object> function : builders){
|
for(BiFunction<Injection, Class<?>, Object> function : builders){
|
||||||
Object result = function.apply(constructor, parameter);
|
Object result = function.apply(annotation, type);
|
||||||
if(result != null) return result;
|
if(result != null) return result;
|
||||||
}
|
}
|
||||||
if(this.injections == null) return null;
|
if(this.injections == null){
|
||||||
Class<?> type = parameter.getType();
|
Supplier<?> supplier = this.suppliers.get(type);
|
||||||
Object result = null;
|
if(supplier == null) throw new IllegalArgumentException("No dependency for type "+type);
|
||||||
|
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)){
|
if(cache.containsKey(type)) throw new IllegalArgumentException("Double dependency for type "+type);
|
||||||
throw new IllegalArgumentException("Double dependency for type "+type+", can be in constructor "+constructor);
|
|
||||||
}else{
|
|
||||||
cache.put(type, injection);
|
cache.put(type, injection);
|
||||||
result = 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+")";
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,9 +1,7 @@
|
||||||
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;
|
||||||
|
@ -23,14 +21,14 @@ public class HttpReader{
|
||||||
|
|
||||||
private Socket socket;
|
private Socket socket;
|
||||||
private InputStream in;
|
private InputStream in;
|
||||||
private BufferedReader reader;
|
private StrictReader 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 BufferedReader(new InputStreamReader(in));
|
this.reader = new StrictReader(this.in);
|
||||||
this.headers = new HashMap<>();
|
this.headers = new HashMap<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,7 +64,7 @@ public class HttpReader{
|
||||||
}
|
}
|
||||||
|
|
||||||
public int read(char[] buffer) throws IOException{
|
public int read(char[] buffer) throws IOException{
|
||||||
return this.reader.read(buffer);
|
return this.reader.read(buffer, 0, buffer.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String readLine() throws IOException{
|
public String readLine() throws IOException{
|
||||||
|
@ -133,4 +131,39 @@ 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;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,9 +1,8 @@
|
||||||
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;
|
||||||
|
@ -13,7 +12,7 @@ public class Locker<V>{
|
||||||
private Map<Key, BlockingQueue<V>> map;
|
private Map<Key, BlockingQueue<V>> map;
|
||||||
|
|
||||||
public Locker(){
|
public Locker(){
|
||||||
this.map = new HashMap<>();
|
this.map = new ConcurrentHashMap<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void init(Key key){
|
public void init(Key key){
|
||||||
|
@ -24,25 +23,20 @@ public class Locker<V>{
|
||||||
this.map.remove(key);
|
this.map.remove(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
private BlockingQueue<V> get(Key key){
|
public void pushValue(V value){
|
||||||
return this.map.get(key);
|
for(BlockingQueue<V> queue : this.map.values()){
|
||||||
}
|
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){
|
public V getValue(Key key) throws InterruptedException{
|
||||||
BlockingQueue<V> queue = get(key);
|
BlockingQueue<V> queue = this.map.get(key);
|
||||||
if(queue.isEmpty()) return null;
|
lock(queue);
|
||||||
return queue.poll();
|
return queue.poll();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void lock(Key key) throws InterruptedException{
|
private void lock(BlockingQueue<V> queue) throws InterruptedException{
|
||||||
BlockingQueue<V> queue = get(key);
|
|
||||||
if(queue.isEmpty()){
|
if(queue.isEmpty()){
|
||||||
synchronized(queue){
|
synchronized(queue){
|
||||||
queue.wait();
|
queue.wait();
|
||||||
|
@ -50,8 +44,7 @@ public class Locker<V>{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void unlock(Key key){
|
private void unlock(BlockingQueue<V> queue){
|
||||||
BlockingQueue<V> queue = get(key);
|
|
||||||
synchronized(queue){
|
synchronized(queue){
|
||||||
queue.notify();
|
queue.notify();
|
||||||
}
|
}
|
||||||
|
@ -61,46 +54,40 @@ public class Locker<V>{
|
||||||
Key key = new Key();
|
Key key = new Key();
|
||||||
init(key);
|
init(key);
|
||||||
try {
|
try {
|
||||||
while(true){
|
while(true) action.accept(getValue(key));
|
||||||
lock(key);
|
|
||||||
V value = getValue(key);
|
|
||||||
action.accept(value);
|
|
||||||
}
|
|
||||||
}catch(Exception e){}
|
}catch(Exception e){}
|
||||||
remove(key);
|
remove(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void listen(Supplier<Boolean> condition,Consumer<V> action){
|
public void listen(Consumer<V> action, Consumer<Exception> onClose){
|
||||||
Key key = new Key();
|
Key key = new Key();
|
||||||
init(key);
|
init(key);
|
||||||
try {
|
try {
|
||||||
while(condition.get()){
|
while(true) action.accept(getValue(key));
|
||||||
lock(key);
|
}catch(Exception e){
|
||||||
V value = getValue(key);
|
onClose.accept(e);
|
||||||
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 Thread listenAsync(Consumer<V> action){
|
public void listen(Supplier<Boolean> condition, Consumer<V> action, Consumer<Exception> onClose){
|
||||||
Thread thread = new Thread(new Runnable(){
|
Key key = new Key();
|
||||||
public void run(){
|
init(key);
|
||||||
listen(action);
|
try {
|
||||||
|
while(condition.get()) action.accept(getValue(key));
|
||||||
|
}catch(Exception e){
|
||||||
|
onClose.accept(e);
|
||||||
}
|
}
|
||||||
});
|
remove(key);
|
||||||
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(){} }
|
||||||
|
|
7
src/dev/peerat/framework/ResponseMapper.java
Normal file
7
src/dev/peerat/framework/ResponseMapper.java
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
package dev.peerat.framework;
|
||||||
|
|
||||||
|
public interface ResponseMapper{
|
||||||
|
|
||||||
|
void map(Context context, HttpReader reader, HttpWriter writer, Object result) throws Exception;
|
||||||
|
|
||||||
|
}
|
72
src/dev/peerat/framework/RouteBinder.java
Normal file
72
src/dev/peerat/framework/RouteBinder.java
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
61
src/dev/peerat/framework/RouteCachable.java
Normal file
61
src/dev/peerat/framework/RouteCachable.java
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
10
src/dev/peerat/framework/RouteFilter.java
Normal file
10
src/dev/peerat/framework/RouteFilter.java
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
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);
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
boolean intercept(Matcher matcher, Context context, HttpReader reader, HttpWriter writer, Method method, Object[] params);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ 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;
|
||||||
|
|
||||||
|
@ -12,73 +11,93 @@ public class RouteMapper<U extends User>{
|
||||||
|
|
||||||
private Router<U> router;
|
private Router<U> router;
|
||||||
|
|
||||||
private Response[] responses;
|
private RouteState[] states;
|
||||||
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.responses = new Response[0];
|
this.states = new RouteState[0];
|
||||||
this.methods = new Method[0];
|
|
||||||
this.routes = new Route[0];
|
|
||||||
this.patterns = new Pattern[0];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void register(Response response, Method method, Route route, Pattern pattern){
|
public RouteState register(Object instance, Method method, Route route){
|
||||||
int length = responses.length+1;
|
int length = states.length+1;
|
||||||
this.responses = addElement(responses, new Response[length], response);
|
method.setAccessible(true);
|
||||||
this.methods = addElement(methods, new Method[length], method);
|
RouteState state = new RouteState(instance, method, route);
|
||||||
this.routes = addElement(routes, new Route[length], route);
|
this.states = addElement(states, new RouteState[length], state);
|
||||||
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[responses.length];
|
this.calls = new int[states.length];
|
||||||
this.dif = 2;
|
this.dif = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean exec(Context context, HttpReader reader, HttpWriter writer, List<RouteInterceptor> interceptors) throws Exception{
|
public boolean exec(Context context, HttpReader reader, HttpWriter writer) throws Exception{
|
||||||
Response response = null;
|
RouteState target = null;
|
||||||
Method method = null;
|
|
||||||
Matcher matcher = null;
|
Matcher matcher = null;
|
||||||
|
|
||||||
String path = context.getPath();
|
String path = context.getPath();
|
||||||
|
|
||||||
synchronized(responses){
|
synchronized(states){
|
||||||
for(int i = 0; i < responses.length; i++){
|
for(int i = 0; i < states.length; i++){
|
||||||
Route route = routes[i];
|
RouteState state = states[i];
|
||||||
Pattern pattern = patterns[i];
|
matcher = state.match(path);
|
||||||
matcher = pattern.matcher(path);
|
if(matcher != null){
|
||||||
if(matcher.matches()){
|
if(state.isWebSocket()){
|
||||||
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(route.needLogin()){
|
if(state.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()) && route.needLogin()){
|
if((!context.isLogged()) && state.needLogin()){
|
||||||
writer.response(401, router.getDefaultHeaders(context.getType()));
|
writer.response(401, router.getDefaultHeaders(context.getType()));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
response = responses[i];
|
target = state;
|
||||||
method = methods[i];
|
|
||||||
order(i);
|
order(i);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(response != null){
|
if(target != null){
|
||||||
for(RouteInterceptor interceptor : interceptors){
|
Object[] params = new Object[target.getBinder().getParameterCount()];
|
||||||
if(!interceptor.intercept(matcher, context, reader, writer, method)) return true;
|
List<RouteInterceptor> localInterceptors = target.getInterceptors();
|
||||||
|
if(localInterceptors != null){
|
||||||
|
for(RouteInterceptor interceptor : localInterceptors){
|
||||||
|
if(!interceptor.intercept(matcher, context, reader, writer, target.getMethod(), params)) return true;
|
||||||
}
|
}
|
||||||
response.exec(matcher, context, reader, writer);
|
}
|
||||||
|
Object[] inv = target.getBinder().bindMethod(target.getMethod(), matcher, context, reader, writer, params);
|
||||||
|
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;
|
||||||
|
@ -94,10 +113,7 @@ 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(responses, index, index-1);
|
switchElement(states, 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;
|
||||||
|
|
78
src/dev/peerat/framework/RouteState.java
Normal file
78
src/dev/peerat/framework/RouteState.java
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -5,49 +5,26 @@ 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 org.jose4j.jwa.AlgorithmConstraints.ConstraintType;
|
import dev.peerat.framework.auth.Authenticator;
|
||||||
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) {
|
public static void main(String[] args) throws Exception{}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
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 List<RouteInterceptor> interceptors;
|
private RouteCachable cachable;
|
||||||
private Response noFileFound;
|
private Response noFileFound;
|
||||||
private RsaJsonWebKey rsaJsonWebKey;
|
private Authenticator<U> auth;
|
||||||
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(){
|
||||||
|
@ -55,41 +32,16 @@ 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> configureJwt(Consumer<JwtConsumerBuilder> consumer, Consumer<JwtClaims> claims, Function<JwtClaims, U> userCreator) throws Exception{
|
public Router<U> setAuthenticator(Authenticator<U> auth){
|
||||||
this.rsaJsonWebKey = RsaJwkGenerator.generateJwk(2048);
|
this.auth = auth;
|
||||||
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];
|
||||||
|
@ -99,25 +51,57 @@ 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);
|
||||||
|
|
||||||
this.mappers[route.type().ordinal()].register(response, method, route, Pattern.compile(route.path()));
|
RouteState state = this.mappers[route.type().ordinal()].register(response, method, route);
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
@ -131,19 +115,13 @@ 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.lastIndexOf('.')));
|
Class<?> clazz = Class.forName(name+"."+line.substring(0, line.length()-6));
|
||||||
if(Response.class.isAssignableFrom(clazz)){
|
registerClass(clazz, injector);
|
||||||
if(clazz.equals(Response.class)) continue;
|
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){
|
||||||
|
@ -153,36 +131,30 @@ 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.interceptors.add(interceptor);
|
this.cachable.addInterceptor(mappers, 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;
|
||||||
|
@ -192,8 +164,16 @@ 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, this.interceptors)) return;
|
if(this.mappers[context.getType().ordinal()].exec(context, reader, writer)) return;
|
||||||
if(noFileFound != null) noFileFound.exec(null, context, reader, writer);
|
if(noFileFound != null) noFileFound.exec(null, context, reader, writer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,25 +183,16 @@ public class Router<U extends User>{
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public U getUser(String token) throws InvalidJwtException{
|
public U getUser(RequestType type, HttpReader reader) throws Exception{
|
||||||
return this.userCreator.apply(this.jwtConsumer.processToClaims(token));
|
return this.auth != null ? this.auth.getUser(type, reader) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String createAuthUser(U user) throws JoseException{
|
public U getUser(String data) throws Exception{
|
||||||
JwtClaims claims = new JwtClaims();
|
return this.auth != null ? this.auth.getUser(data) : null;
|
||||||
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);
|
public String createAuthUser(U user) throws Exception{
|
||||||
|
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(){
|
||||||
|
@ -232,7 +203,13 @@ 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();
|
||||||
|
|
321
src/dev/peerat/framework/StrictReader.java
Normal file
321
src/dev/peerat/framework/StrictReader.java
Normal file
|
@ -0,0 +1,321 @@
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,9 +1,3 @@
|
||||||
package dev.peerat.framework;
|
package dev.peerat.framework;
|
||||||
|
|
||||||
import org.jose4j.jwt.JwtClaims;
|
public abstract class User{}
|
||||||
|
|
||||||
public abstract class User{
|
|
||||||
|
|
||||||
public abstract void write(JwtClaims claims);
|
|
||||||
|
|
||||||
}
|
|
17
src/dev/peerat/framework/auth/AuthException.java
Normal file
17
src/dev/peerat/framework/auth/AuthException.java
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
15
src/dev/peerat/framework/auth/Authenticator.java
Normal file
15
src/dev/peerat/framework/auth/Authenticator.java
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
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;
|
||||||
|
|
||||||
|
}
|
94
src/dev/peerat/framework/auth/JwtAuthenticator.java
Normal file
94
src/dev/peerat/framework/auth/JwtAuthenticator.java
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -3,16 +3,25 @@ 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 List<Object> list;
|
private Collection<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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,11 @@ 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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,17 @@
|
||||||
package dev.peerat.framework.utils.json;
|
package dev.peerat.framework.utils.json;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import static dev.peerat.parser.TokenType.*;
|
||||||
|
|
||||||
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 be.jeffcheasey88.peeratcode.parser.Parser;
|
import dev.peerat.parser.Parser;
|
||||||
import be.jeffcheasey88.peeratcode.parser.Token;
|
import dev.peerat.parser.Token;
|
||||||
import be.jeffcheasey88.peeratcode.parser.TokenType;
|
import dev.peerat.parser.state.RedirectStateTree;
|
||||||
import be.jeffcheasey88.peeratcode.parser.state.RedirectStateTree;
|
import dev.peerat.parser.state.StateTree;
|
||||||
import be.jeffcheasey88.peeratcode.parser.state.StateTree;
|
|
||||||
|
|
||||||
public class JsonParser extends Parser<Json>{
|
public class JsonParser extends Parser<Json>{
|
||||||
|
|
||||||
|
@ -25,7 +26,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 ArrayList<>();
|
list = new LinkedList<>();
|
||||||
global.set(list);
|
global.set(list);
|
||||||
}
|
}
|
||||||
list.add(local.get());
|
list.add(local.get());
|
||||||
|
@ -43,16 +44,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(TokenType.STRING), (bag, token) -> bag.set(token))).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.CHAR), (bag, token) -> bag.set(token))).end();
|
content.then((validator) -> validator.validate((token) -> token.getType().equals(CHAR), (bag, token) -> bag.set(token))).end();
|
||||||
StateTree<Json> number = content.then((validator) -> validator.validate((token) -> token.getType().equals(TokenType.NAME), (bag, token) -> bag.set(token)));
|
StateTree<Json> number = content.then((validator) -> validator.validate((token) -> token.getType().equals(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(TokenType.NAME), (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))))
|
||||||
.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(TokenType.STRING), (bag, token) -> {
|
StateTree<Json> mapper_key = mapper.then((validator) -> validator.validate((token) -> token.getType().equals(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<>();
|
||||||
|
@ -105,20 +106,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(TokenType.STRING)){
|
if(token.getType().equals(STRING)){
|
||||||
return content;
|
return content;
|
||||||
}else if(token.getType().equals(TokenType.CHAR)){
|
}else if(token.getType().equals(CHAR)){
|
||||||
return content.charAt(0);
|
return content.charAt(0);
|
||||||
}else{
|
}else{
|
||||||
try{
|
try{
|
||||||
return Long.parseLong(content);
|
return Long.parseLong(content);
|
||||||
}catch(Exception _){
|
}catch(Exception e){
|
||||||
try {
|
try {
|
||||||
return Double.parseDouble(content);
|
return Double.parseDouble(content);
|
||||||
}catch(Exception __){
|
}catch(Exception ex){
|
||||||
try{
|
try{
|
||||||
return Boolean.parseBoolean(content);
|
return Boolean.parseBoolean(content);
|
||||||
}catch(Exception ___){}
|
}catch(Exception exc){}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
package dev.peerat.framework.utils.json;
|
package dev.peerat.framework.utils.json;
|
||||||
|
|
||||||
import be.jeffcheasey88.peeratcode.parser.Token;
|
import dev.peerat.parser.Token;
|
||||||
import be.jeffcheasey88.peeratcode.parser.TokenType;
|
import dev.peerat.parser.TokenType;
|
||||||
import be.jeffcheasey88.peeratcode.parser.Tokenizer;
|
import dev.peerat.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);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue