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 org.jose4j.jwt.consumer.InvalidJwtException;
|
||||
import dev.peerat.framework.auth.AuthException;
|
||||
|
||||
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);
|
||||
writer.flush();
|
||||
writer.close();
|
||||
}catch(InvalidJwtException e){
|
||||
this.router.getExceptionLogger().setValue(e);
|
||||
}catch(AuthException e){
|
||||
this.router.getExceptionLogger().pushValue(e);
|
||||
}catch(Throwable e){
|
||||
this.router.getExceptionLogger().setValue(e);
|
||||
this.router.getExceptionLogger().pushValue(e);
|
||||
if(context != null && context.getResponseCode() == 0){
|
||||
try{
|
||||
context.response(500);
|
||||
writer.flush();
|
||||
writer.close();
|
||||
}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{
|
||||
String auth = reader.getHeader("Authorization");
|
||||
if(auth == null) return null;
|
||||
auth = auth.substring(7);
|
||||
private User isLogin(RequestType type, HttpReader reader) throws AuthException{
|
||||
try{
|
||||
return this.router.getUser(auth);
|
||||
}catch(InvalidJwtException e){
|
||||
return this.router.getUser(type, reader);
|
||||
}catch(Exception e){
|
||||
try{
|
||||
writer.response(401, this.router.getDefaultHeaders(type));
|
||||
writer.flush();
|
||||
writer.close();
|
||||
}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;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Executable;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Parameter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class DependencyInjector{
|
||||
|
||||
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 Map<Class<?>, Object> cache;
|
||||
|
||||
public DependencyInjector(){
|
||||
this.map = new HashMap<>();
|
||||
this.suppliers = new HashMap<>();
|
||||
this.builders = new ArrayList<>();
|
||||
}
|
||||
|
||||
|
@ -24,14 +29,20 @@ public class DependencyInjector{
|
|||
return this;
|
||||
}
|
||||
|
||||
public DependencyInjector of(BiFunction<Constructor<?>, Parameter, Object> builder){
|
||||
public DependencyInjector of(BiFunction<Injection, Class<?>, Object> builder){
|
||||
this.builders.add(builder);
|
||||
return this;
|
||||
}
|
||||
|
||||
public DependencyInjector of(Class<?> type, Supplier<?> supplier){
|
||||
this.suppliers.put(type, supplier);
|
||||
return this;
|
||||
}
|
||||
|
||||
public DependencyInjector of(Object... injections){
|
||||
if(this.injections == null){
|
||||
this.injections = injections;
|
||||
this.cache = new HashMap<>();
|
||||
return this;
|
||||
}
|
||||
Object[] copy = new Object[this.injections.length+injections.length];
|
||||
|
@ -40,31 +51,57 @@ public class DependencyInjector{
|
|||
this.injections = copy;
|
||||
return this;
|
||||
}
|
||||
|
||||
Object applyDependency(Constructor<?> constructor, Parameter parameter, Map<Class<?>, Object> cache){
|
||||
Injection annotation = parameter.getAnnotation(Injection.class);
|
||||
|
||||
public Object[] apply(Executable executable) throws Exception{
|
||||
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){
|
||||
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;
|
||||
}
|
||||
for(BiFunction<Constructor<?>, Parameter, Object> function : builders){
|
||||
Object result = function.apply(constructor, parameter);
|
||||
for(BiFunction<Injection, Class<?>, Object> function : builders){
|
||||
Object result = function.apply(annotation, type);
|
||||
if(result != null) return result;
|
||||
}
|
||||
if(this.injections == null) return null;
|
||||
Class<?> type = parameter.getType();
|
||||
Object result = null;
|
||||
if(this.injections == null){
|
||||
Supplier<?> supplier = this.suppliers.get(type);
|
||||
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){
|
||||
if(type.isAssignableFrom(injection.getClass())){
|
||||
if(cache.containsKey(type)){
|
||||
throw new IllegalArgumentException("Double dependency for type "+type+", can be in constructor "+constructor);
|
||||
}else{
|
||||
cache.put(type, injection);
|
||||
result = injection;
|
||||
}
|
||||
if(cache.containsKey(type)) throw new IllegalArgumentException("Double dependency for type "+type);
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
return "([DependencyInjector] map="+map+",buildersCount="+builders.size()+",injections="+injections+",cache="+cache+")";
|
||||
}
|
||||
}
|
|
@ -1,9 +1,7 @@
|
|||
package dev.peerat.framework;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.Socket;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
@ -23,14 +21,14 @@ public class HttpReader{
|
|||
|
||||
private Socket socket;
|
||||
private InputStream in;
|
||||
private BufferedReader reader;
|
||||
private StrictReader reader;
|
||||
|
||||
private Map<String, String> headers;
|
||||
|
||||
public HttpReader(Socket socket) throws Exception{
|
||||
this.socket = socket;
|
||||
this.in = socket.getInputStream();
|
||||
this.reader = new BufferedReader(new InputStreamReader(in));
|
||||
this.reader = new StrictReader(this.in);
|
||||
this.headers = new HashMap<>();
|
||||
}
|
||||
|
||||
|
@ -66,7 +64,7 @@ public class HttpReader{
|
|||
}
|
||||
|
||||
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{
|
||||
|
@ -133,4 +131,39 @@ Content-Type: text/javascript
|
|||
|
||||
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;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
@ -13,7 +12,7 @@ public class Locker<V>{
|
|||
private Map<Key, BlockingQueue<V>> map;
|
||||
|
||||
public Locker(){
|
||||
this.map = new HashMap<>();
|
||||
this.map = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
public void init(Key key){
|
||||
|
@ -24,25 +23,20 @@ public class Locker<V>{
|
|||
this.map.remove(key);
|
||||
}
|
||||
|
||||
private BlockingQueue<V> get(Key key){
|
||||
return this.map.get(key);
|
||||
}
|
||||
|
||||
public void setValue(V value){
|
||||
for(Entry<Key, BlockingQueue<V>> entry : this.map.entrySet()){
|
||||
entry.getValue().add(value);
|
||||
this.unlock(entry.getKey());
|
||||
public void pushValue(V value){
|
||||
for(BlockingQueue<V> queue : this.map.values()){
|
||||
queue.add(value);
|
||||
unlock(queue);
|
||||
}
|
||||
}
|
||||
|
||||
public V getValue(Key key){
|
||||
BlockingQueue<V> queue = get(key);
|
||||
if(queue.isEmpty()) return null;
|
||||
public V getValue(Key key) throws InterruptedException{
|
||||
BlockingQueue<V> queue = this.map.get(key);
|
||||
lock(queue);
|
||||
return queue.poll();
|
||||
}
|
||||
|
||||
public void lock(Key key) throws InterruptedException{
|
||||
BlockingQueue<V> queue = get(key);
|
||||
private void lock(BlockingQueue<V> queue) throws InterruptedException{
|
||||
if(queue.isEmpty()){
|
||||
synchronized(queue){
|
||||
queue.wait();
|
||||
|
@ -50,8 +44,7 @@ public class Locker<V>{
|
|||
}
|
||||
}
|
||||
|
||||
public void unlock(Key key){
|
||||
BlockingQueue<V> queue = get(key);
|
||||
private void unlock(BlockingQueue<V> queue){
|
||||
synchronized(queue){
|
||||
queue.notify();
|
||||
}
|
||||
|
@ -61,46 +54,40 @@ public class Locker<V>{
|
|||
Key key = new Key();
|
||||
init(key);
|
||||
try {
|
||||
while(true){
|
||||
lock(key);
|
||||
V value = getValue(key);
|
||||
action.accept(value);
|
||||
}
|
||||
while(true) action.accept(getValue(key));
|
||||
}catch(Exception e){}
|
||||
remove(key);
|
||||
}
|
||||
|
||||
public void listen(Supplier<Boolean> condition,Consumer<V> action){
|
||||
public void listen(Consumer<V> action, Consumer<Exception> onClose){
|
||||
Key key = new Key();
|
||||
init(key);
|
||||
try {
|
||||
while(condition.get()){
|
||||
lock(key);
|
||||
V value = getValue(key);
|
||||
action.accept(value);
|
||||
}
|
||||
while(true) action.accept(getValue(key));
|
||||
}catch(Exception e){
|
||||
onClose.accept(e);
|
||||
}
|
||||
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){}
|
||||
remove(key);
|
||||
}
|
||||
|
||||
public Thread listenAsync(Consumer<V> action){
|
||||
Thread thread = new Thread(new Runnable(){
|
||||
public void run(){
|
||||
listen(action);
|
||||
}
|
||||
});
|
||||
thread.start();
|
||||
return thread;
|
||||
}
|
||||
|
||||
public Thread listenAsync(Supplier<Boolean> condition,Consumer<V> action){
|
||||
Thread thread = new Thread(new Runnable(){
|
||||
public void run(){
|
||||
listen(condition, action);
|
||||
}
|
||||
});
|
||||
thread.start();
|
||||
return thread;
|
||||
public void listen(Supplier<Boolean> condition, Consumer<V> action, Consumer<Exception> onClose){
|
||||
Key key = new Key();
|
||||
init(key);
|
||||
try {
|
||||
while(condition.get()) action.accept(getValue(key));
|
||||
}catch(Exception e){
|
||||
onClose.accept(e);
|
||||
}
|
||||
remove(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{
|
||||
|
||||
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.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import dev.peerat.framework.utils.json.JsonMap;
|
||||
|
||||
|
@ -12,73 +11,93 @@ public class RouteMapper<U extends User>{
|
|||
|
||||
private Router<U> router;
|
||||
|
||||
private Response[] responses;
|
||||
private Method[] methods;
|
||||
private Route[] routes;
|
||||
private Pattern[] patterns;
|
||||
private RouteState[] states;
|
||||
private int[] calls;
|
||||
private int dif;
|
||||
|
||||
public RouteMapper(Router<U> router){
|
||||
this.router = router;
|
||||
this.responses = new Response[0];
|
||||
this.methods = new Method[0];
|
||||
this.routes = new Route[0];
|
||||
this.patterns = new Pattern[0];
|
||||
this.states = new RouteState[0];
|
||||
}
|
||||
|
||||
public void register(Response response, Method method, Route route, Pattern pattern){
|
||||
int length = responses.length+1;
|
||||
this.responses = addElement(responses, new Response[length], response);
|
||||
this.methods = addElement(methods, new Method[length], method);
|
||||
this.routes = addElement(routes, new Route[length], route);
|
||||
this.patterns = addElement(patterns, new Pattern[length], pattern);
|
||||
public RouteState register(Object instance, Method method, Route route){
|
||||
int length = states.length+1;
|
||||
method.setAccessible(true);
|
||||
RouteState state = new RouteState(instance, method, route);
|
||||
this.states = addElement(states, new RouteState[length], state);
|
||||
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(){
|
||||
this.calls = new int[responses.length];
|
||||
this.calls = new int[states.length];
|
||||
this.dif = 2;
|
||||
}
|
||||
|
||||
public boolean exec(Context context, HttpReader reader, HttpWriter writer, List<RouteInterceptor> interceptors) throws Exception{
|
||||
Response response = null;
|
||||
Method method = null;
|
||||
public boolean exec(Context context, HttpReader reader, HttpWriter writer) throws Exception{
|
||||
RouteState target = null;
|
||||
Matcher matcher = null;
|
||||
|
||||
String path = context.getPath();
|
||||
|
||||
synchronized(responses){
|
||||
for(int i = 0; i < responses.length; i++){
|
||||
Route route = routes[i];
|
||||
Pattern pattern = patterns[i];
|
||||
matcher = pattern.matcher(path);
|
||||
if(matcher.matches()){
|
||||
if(route.websocket()){
|
||||
synchronized(states){
|
||||
for(int i = 0; i < states.length; i++){
|
||||
RouteState state = states[i];
|
||||
matcher = state.match(path);
|
||||
if(matcher != null){
|
||||
if(state.isWebSocket()){
|
||||
switchToWebSocket(reader, writer);
|
||||
reader = new WebSocketReader(reader);
|
||||
writer = new WebSocketWriter(writer);
|
||||
if(route.needLogin()){
|
||||
if(state.needLogin()){
|
||||
String token = reader.<JsonMap>readJson().get("token");
|
||||
context.setUser(this.router.getUser(token));
|
||||
}
|
||||
}
|
||||
if((!context.isLogged()) && route.needLogin()){
|
||||
if((!context.isLogged()) && state.needLogin()){
|
||||
writer.response(401, router.getDefaultHeaders(context.getType()));
|
||||
return true;
|
||||
}
|
||||
response = responses[i];
|
||||
method = methods[i];
|
||||
target = state;
|
||||
order(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(response != null){
|
||||
for(RouteInterceptor interceptor : interceptors){
|
||||
if(!interceptor.intercept(matcher, context, reader, writer, method)) return true;
|
||||
if(target != null){
|
||||
Object[] params = new Object[target.getBinder().getParameterCount()];
|
||||
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 false;
|
||||
|
@ -94,10 +113,7 @@ public class RouteMapper<U extends User>{
|
|||
}
|
||||
if(index < 1) return;
|
||||
if(call > this.calls[index-1]){
|
||||
switchElement(responses, index, index-1);
|
||||
switchElement(methods, index, index-1);
|
||||
switchElement(routes, index, index-1);
|
||||
switchElement(patterns, index, index-1);
|
||||
switchElement(states, index, index-1);
|
||||
|
||||
this.calls[index] = this.calls[index-1];
|
||||
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.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Parameter;
|
||||
import java.net.ServerSocket;
|
||||
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.SSLServerSocketFactory;
|
||||
|
||||
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;
|
||||
import dev.peerat.framework.auth.Authenticator;
|
||||
|
||||
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<Throwable> exceptions;
|
||||
private RouteMapper<U>[] mappers;
|
||||
private List<RouteInterceptor> interceptors;
|
||||
private RouteCachable cachable;
|
||||
private Response noFileFound;
|
||||
private RsaJsonWebKey rsaJsonWebKey;
|
||||
private JwtConsumer jwtConsumer;
|
||||
private Consumer<JwtClaims> claims;
|
||||
private Function<JwtClaims, U> userCreator;
|
||||
private Authenticator<U> auth;
|
||||
private String[][] headers;
|
||||
private ResponseMapper responseMapper;
|
||||
private ServerSocket serverSocket;
|
||||
|
||||
public Router(){
|
||||
|
@ -55,41 +32,16 @@ public class Router<U extends User>{
|
|||
this.exceptions = new Locker<>();
|
||||
int types = RequestType.values().length;
|
||||
this.mappers = new RouteMapper[types];
|
||||
this.interceptors = new ArrayList<>();
|
||||
for(RequestType type : RequestType.values()) this.mappers[type.ordinal()] = new RouteMapper<>(this);
|
||||
this.cachable = new RouteCachable();
|
||||
this.headers = new String[types][0];
|
||||
}
|
||||
|
||||
public Router<U> configureJwt(Consumer<JwtConsumerBuilder> consumer, Consumer<JwtClaims> claims, Function<JwtClaims, U> userCreator) throws Exception{
|
||||
this.rsaJsonWebKey = RsaJwkGenerator.generateJwk(2048);
|
||||
configureJwtWithKey(consumer, claims, userCreator, this.rsaJsonWebKey.getKey());
|
||||
public Router<U> setAuthenticator(Authenticator<U> auth){
|
||||
this.auth = auth;
|
||||
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){
|
||||
String[] origin = this.headers[type.ordinal()];
|
||||
String[] copy = new String[origin.length+headers.length];
|
||||
|
@ -99,25 +51,57 @@ public class Router<U extends User>{
|
|||
return this;
|
||||
}
|
||||
|
||||
public Router<U> setMapper(ResponseMapper mapper){
|
||||
this.responseMapper = mapper;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Router<U> register(Response response){
|
||||
try{
|
||||
Method method = response.getClass().getDeclaredMethod("exec",
|
||||
Response.class.getDeclaredMethods()[0].getParameterTypes());
|
||||
Route route = method.getAnnotation(Route.class);
|
||||
|
||||
this.mappers[route.type().ordinal()].register(response, 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){
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
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){
|
||||
return registerPackages(new DependencyInjector().of(injections));
|
||||
}
|
||||
|
||||
public Router<U> registerPackages(DependencyInjector injector){
|
||||
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('.'));
|
||||
return registerPackages(pack, injector);
|
||||
}
|
||||
|
@ -131,19 +115,13 @@ public class Router<U extends User>{
|
|||
try{
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
|
||||
String line;
|
||||
Map<Class<?>, Object> cache = new HashMap<>();
|
||||
while((line = reader.readLine()) != null){
|
||||
if(line.endsWith(".class")){
|
||||
Class<?> clazz = Class.forName(name+"."+line.substring(0, line.lastIndexOf('.')));
|
||||
if(Response.class.isAssignableFrom(clazz)){
|
||||
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);
|
||||
Class<?> clazz = Class.forName(name+"."+line.substring(0, line.length()-6));
|
||||
registerClass(clazz, injector);
|
||||
continue;
|
||||
}
|
||||
if(!line.contains(".")) registerPackages(name+"."+line, injector);
|
||||
}
|
||||
reader.close();
|
||||
}catch(Exception e){
|
||||
|
@ -152,26 +130,6 @@ public class Router<U extends User>{
|
|||
}
|
||||
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){
|
||||
this.noFileFound = response;
|
||||
|
@ -179,10 +137,24 @@ public class Router<U extends User>{
|
|||
}
|
||||
|
||||
public Router<U> addInterceptor(RouteInterceptor interceptor){
|
||||
this.interceptors.add(interceptor);
|
||||
this.cachable.addInterceptor(mappers, interceptor);
|
||||
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(){
|
||||
for(RouteMapper<?> mapper : this.mappers) mapper.activeReOrdering();
|
||||
return this;
|
||||
|
@ -191,9 +163,17 @@ public class Router<U extends User>{
|
|||
String[] getDefaultHeaders(RequestType type){
|
||||
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{
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -203,25 +183,16 @@ public class Router<U extends User>{
|
|||
return this;
|
||||
}
|
||||
|
||||
public U getUser(String token) throws InvalidJwtException{
|
||||
return this.userCreator.apply(this.jwtConsumer.processToClaims(token));
|
||||
public U getUser(RequestType type, HttpReader reader) throws Exception{
|
||||
return this.auth != null ? this.auth.getUser(type, reader) : null;
|
||||
}
|
||||
|
||||
public U getUser(String data) throws Exception{
|
||||
return this.auth != null ? this.auth.getUser(data) : null;
|
||||
}
|
||||
|
||||
public String createAuthUser(U user) throws JoseException{
|
||||
JwtClaims claims = new JwtClaims();
|
||||
claims.setGeneratedJwtId(); // a unique identifier for the token
|
||||
claims.setIssuedAtToNow(); // when the token was issued/created (now)
|
||||
claims.setNotBeforeMinutesInThePast(2); // time before which the token is not yet valid (2 minutes ago)
|
||||
this.claims.accept(claims);
|
||||
|
||||
user.write(claims);
|
||||
|
||||
JsonWebSignature jws = new JsonWebSignature();
|
||||
jws.setPayload(claims.toJson());
|
||||
jws.setKey(rsaJsonWebKey.getPrivateKey());
|
||||
jws.setKeyIdHeaderValue(rsaJsonWebKey.getKeyId());
|
||||
jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256);
|
||||
return jws.getCompactSerialization();
|
||||
public String createAuthUser(U user) throws Exception{
|
||||
return this.auth.createAuthUser(user);
|
||||
}
|
||||
|
||||
public Locker<Context> getLogger(){
|
||||
|
@ -232,7 +203,13 @@ public class Router<U extends User>{
|
|||
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{
|
||||
checkStart();
|
||||
if (ssl) { // Not needed with the use of a proxy
|
||||
try {
|
||||
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;
|
||||
|
||||
import org.jose4j.jwt.JwtClaims;
|
||||
|
||||
public abstract class User{
|
||||
|
||||
public abstract void write(JwtClaims claims);
|
||||
|
||||
}
|
||||
public abstract class User{}
|
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.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
public class JsonArray extends Json{
|
||||
|
||||
private List<Object> list;
|
||||
private Collection<Object> list;
|
||||
|
||||
public JsonArray(){
|
||||
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(){
|
||||
return (Collection<E>) this.list;
|
||||
}
|
||||
|
|
|
@ -14,6 +14,11 @@ public class JsonMap extends Json{
|
|||
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(){
|
||||
return this.map.entrySet();
|
||||
}
|
||||
|
|
|
@ -1,16 +1,17 @@
|
|||
package dev.peerat.framework.utils.json;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import static dev.peerat.parser.TokenType.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import be.jeffcheasey88.peeratcode.parser.Parser;
|
||||
import be.jeffcheasey88.peeratcode.parser.Token;
|
||||
import be.jeffcheasey88.peeratcode.parser.TokenType;
|
||||
import be.jeffcheasey88.peeratcode.parser.state.RedirectStateTree;
|
||||
import be.jeffcheasey88.peeratcode.parser.state.StateTree;
|
||||
import dev.peerat.parser.Parser;
|
||||
import dev.peerat.parser.Token;
|
||||
import dev.peerat.parser.state.RedirectStateTree;
|
||||
import dev.peerat.parser.state.StateTree;
|
||||
|
||||
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) ->{
|
||||
List<Object> list = global.get();
|
||||
if(list == null){
|
||||
list = new ArrayList<>();
|
||||
list = new LinkedList<>();
|
||||
global.set(list);
|
||||
}
|
||||
list.add(local.get());
|
||||
|
@ -43,16 +44,16 @@ public class JsonParser extends Parser<Json>{
|
|||
.then(content_array_element);
|
||||
|
||||
content.then(new RedirectStateTree<>(base, (global, local) -> global.set(local.get()))).end();
|
||||
content.then((validator) -> validator.validate((token) -> token.getType().equals(TokenType.STRING), (bag, token) -> bag.set(token))).end();
|
||||
content.then((validator) -> validator.validate((token) -> token.getType().equals(TokenType.CHAR), (bag, token) -> bag.set(token))).end();
|
||||
StateTree<Json> number = content.then((validator) -> validator.validate((token) -> token.getType().equals(TokenType.NAME), (bag, token) -> bag.set(token)));
|
||||
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(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)));
|
||||
number.end();
|
||||
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();
|
||||
|
||||
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");
|
||||
if(map == null){
|
||||
map = new HashMap<>();
|
||||
|
@ -105,20 +106,20 @@ public class JsonParser extends Parser<Json>{
|
|||
if(value instanceof Token){
|
||||
Token token = (Token) value;
|
||||
String content = token.getValue();
|
||||
if(token.getType().equals(TokenType.STRING)){
|
||||
if(token.getType().equals(STRING)){
|
||||
return content;
|
||||
}else if(token.getType().equals(TokenType.CHAR)){
|
||||
}else if(token.getType().equals(CHAR)){
|
||||
return content.charAt(0);
|
||||
}else{
|
||||
try{
|
||||
return Long.parseLong(content);
|
||||
}catch(Exception _){
|
||||
}catch(Exception e){
|
||||
try {
|
||||
return Double.parseDouble(content);
|
||||
}catch(Exception __){
|
||||
}catch(Exception ex){
|
||||
try{
|
||||
return Boolean.parseBoolean(content);
|
||||
}catch(Exception ___){}
|
||||
}catch(Exception exc){}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
package dev.peerat.framework.utils.json;
|
||||
|
||||
import be.jeffcheasey88.peeratcode.parser.Token;
|
||||
import be.jeffcheasey88.peeratcode.parser.TokenType;
|
||||
import be.jeffcheasey88.peeratcode.parser.Tokenizer;
|
||||
import dev.peerat.parser.Token;
|
||||
import dev.peerat.parser.TokenType;
|
||||
import dev.peerat.parser.Tokenizer;
|
||||
|
||||
public class JsonTokenizer extends Tokenizer{
|
||||
|
||||
@Override
|
||||
public void parse(String line){
|
||||
long time = System.currentTimeMillis();
|
||||
for(int i = 0; i < line.length(); i++){
|
||||
char c = line.charAt(i);
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue