Optimise route finding (remove maps to use array and add optional re-ordering routes by number of call)
This commit is contained in:
parent
ab33fd465e
commit
d77fe329b8
2 changed files with 212 additions and 139 deletions
162
src/dev/peerat/framework/RouteMapper.java
Normal file
162
src/dev/peerat/framework/RouteMapper.java
Normal file
|
@ -0,0 +1,162 @@
|
||||||
|
package dev.peerat.framework;
|
||||||
|
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public class RouteMapper{
|
||||||
|
|
||||||
|
private Response[] responses;
|
||||||
|
private Route[] routes;
|
||||||
|
private Pattern[] patterns;
|
||||||
|
private int[] calls;
|
||||||
|
private int dif;
|
||||||
|
|
||||||
|
public RouteMapper(){
|
||||||
|
this.responses = new Response[0];
|
||||||
|
this.routes = new Route[0];
|
||||||
|
this.patterns = new Pattern[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void register(Response response, Route route, Pattern pattern){
|
||||||
|
int length = responses.length+1;
|
||||||
|
this.responses = addElement(responses, new Response[length], response);
|
||||||
|
this.routes = addElement(routes, new Route[length], route);
|
||||||
|
this.patterns = addElement(patterns, new Pattern[length], pattern);
|
||||||
|
if(this.calls != null) this.calls = new int[length];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void activeReOrdering(){
|
||||||
|
this.calls = new int[responses.length];
|
||||||
|
this.dif = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean exec(Context context, String path, User user, HttpReader reader, HttpWriter writer) throws Exception{
|
||||||
|
synchronized(responses){
|
||||||
|
for(int i = 0; i < responses.length; i++){
|
||||||
|
Response response = responses[i];
|
||||||
|
Route route = routes[i];
|
||||||
|
Pattern pattern = patterns[i];
|
||||||
|
Matcher matcher = pattern.matcher(path);
|
||||||
|
if(matcher.matches()){
|
||||||
|
if(user == null && route.needLogin()){
|
||||||
|
writer.response(401, "Access-Control-Allow-Origin: *");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if(route.websocket()){
|
||||||
|
switchToWebSocket(reader, writer);
|
||||||
|
reader = new WebSocketReader(reader);
|
||||||
|
writer = new WebSocketWriter(writer);
|
||||||
|
}
|
||||||
|
response.exec(matcher, context, reader, writer);
|
||||||
|
order(i);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void order(int index){
|
||||||
|
if(this.calls == null) return;
|
||||||
|
int call = this.calls[index]++;
|
||||||
|
if(call >= (Integer.MAX_VALUE-(dif+1))){
|
||||||
|
for(int i = 0; i < this.calls.length; i++) this.calls[i]/=dif;
|
||||||
|
dif++;
|
||||||
|
call = this.calls[index];
|
||||||
|
}
|
||||||
|
if(index < 1) return;
|
||||||
|
if(call > this.calls[index-1]){
|
||||||
|
switchElement(responses, index, index-1);
|
||||||
|
switchElement(routes, index, index-1);
|
||||||
|
switchElement(patterns, index, index-1);
|
||||||
|
|
||||||
|
this.calls[index] = this.calls[index-1];
|
||||||
|
this.calls[index-1] = call;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private <E> E[] addElement(E[] current, E[] growed, E element){
|
||||||
|
System.arraycopy(current, 0, growed, 0, current.length);
|
||||||
|
growed[current.length] = element;
|
||||||
|
return growed;
|
||||||
|
}
|
||||||
|
|
||||||
|
private <E> void switchElement(E[] array, int before, int after){
|
||||||
|
E tmp = array[before];
|
||||||
|
array[before] = array[after];
|
||||||
|
array[after] = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void switchToWebSocket(HttpReader reader, HttpWriter writer) throws Exception{
|
||||||
|
String key = reader.getHeader("Sec-WebSocket-Key");
|
||||||
|
if (key == null) throw new IllegalArgumentException();
|
||||||
|
|
||||||
|
writer.write("HTTP/1.1 101 Switching Protocols\n");
|
||||||
|
writer.write("Connection: Upgrade\n");
|
||||||
|
writer.write("Upgrade: websocket\n");
|
||||||
|
writer.write("Sec-WebSocket-Accept: " + printBase64Binary(MessageDigest.getInstance("SHA-1")
|
||||||
|
.digest((key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").getBytes("UTF-8"))) + "\n");
|
||||||
|
writer.write("\n");
|
||||||
|
writer.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// From javax.xml.bind.DatatypeConverter
|
||||||
|
private String printBase64Binary(byte[] array){
|
||||||
|
char[] arrayOfChar = new char[(array.length + 2) / 3 * 4];
|
||||||
|
int i = _printBase64Binary(array, 0, array.length, arrayOfChar, 0);
|
||||||
|
assert i == arrayOfChar.length;
|
||||||
|
return new String(arrayOfChar);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int _printBase64Binary(byte[] paramArrayOfbyte, int paramInt1, int paramInt2,
|
||||||
|
char[] paramArrayOfchar, int paramInt3){
|
||||||
|
int i = paramInt2;
|
||||||
|
int j;
|
||||||
|
for (j = paramInt1; i >= 3; j += 3){
|
||||||
|
paramArrayOfchar[paramInt3++] = encode(paramArrayOfbyte[j] >> 2);
|
||||||
|
paramArrayOfchar[paramInt3++] = encode(
|
||||||
|
(paramArrayOfbyte[j] & 0x3) << 4 | paramArrayOfbyte[j + 1] >> 4 & 0xF);
|
||||||
|
paramArrayOfchar[paramInt3++] = encode(
|
||||||
|
(paramArrayOfbyte[j + 1] & 0xF) << 2 | paramArrayOfbyte[j + 2] >> 6 & 0x3);
|
||||||
|
paramArrayOfchar[paramInt3++] = encode(paramArrayOfbyte[j + 2] & 0x3F);
|
||||||
|
i -= 3;
|
||||||
|
}
|
||||||
|
if (i == 1){
|
||||||
|
paramArrayOfchar[paramInt3++] = encode(paramArrayOfbyte[j] >> 2);
|
||||||
|
paramArrayOfchar[paramInt3++] = encode((paramArrayOfbyte[j] & 0x3) << 4);
|
||||||
|
paramArrayOfchar[paramInt3++] = '=';
|
||||||
|
paramArrayOfchar[paramInt3++] = '=';
|
||||||
|
}
|
||||||
|
if (i == 2){
|
||||||
|
paramArrayOfchar[paramInt3++] = encode(paramArrayOfbyte[j] >> 2);
|
||||||
|
paramArrayOfchar[paramInt3++] = encode(
|
||||||
|
(paramArrayOfbyte[j] & 0x3) << 4 | paramArrayOfbyte[j + 1] >> 4 & 0xF);
|
||||||
|
paramArrayOfchar[paramInt3++] = encode((paramArrayOfbyte[j + 1] & 0xF) << 2);
|
||||||
|
paramArrayOfchar[paramInt3++] = '=';
|
||||||
|
}
|
||||||
|
return paramInt3;
|
||||||
|
}
|
||||||
|
|
||||||
|
private char encode(int paramInt){
|
||||||
|
return encodeMap[paramInt & 0x3F];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final char[] encodeMap = initEncodeMap();
|
||||||
|
|
||||||
|
private static char[] initEncodeMap(){
|
||||||
|
char[] arrayOfChar = new char[64];
|
||||||
|
byte b;
|
||||||
|
for (b = 0; b < 26; b++)
|
||||||
|
arrayOfChar[b] = (char) (65 + b);
|
||||||
|
for (b = 26; b < 52; b++)
|
||||||
|
arrayOfChar[b] = (char) (97 + b - 26);
|
||||||
|
for (b = 52; b < 62; b++)
|
||||||
|
arrayOfChar[b] = (char) (48 + b - 52);
|
||||||
|
arrayOfChar[62] = '+';
|
||||||
|
arrayOfChar[63] = '/';
|
||||||
|
return arrayOfChar;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -3,13 +3,8 @@ package dev.peerat.framework;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.net.ServerSocket;
|
import java.net.ServerSocket;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.security.MessageDigest;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Map.Entry;
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import javax.net.ssl.SSLServerSocket;
|
import javax.net.ssl.SSLServerSocket;
|
||||||
|
@ -32,8 +27,7 @@ public class Router<U extends User>{
|
||||||
}
|
}
|
||||||
|
|
||||||
private Locker<Context> logger;
|
private Locker<Context> logger;
|
||||||
private Map<Response, Route>[] responses;
|
private RouteMapper[] mappers;
|
||||||
private Map<Response, Pattern> patterns;
|
|
||||||
private Response noFileFound;
|
private Response noFileFound;
|
||||||
private RsaJsonWebKey rsaJsonWebKey;
|
private RsaJsonWebKey rsaJsonWebKey;
|
||||||
private JwtConsumer jwtConsumer;
|
private JwtConsumer jwtConsumer;
|
||||||
|
@ -45,9 +39,8 @@ public class Router<U extends User>{
|
||||||
public Router() throws Exception{
|
public Router() throws Exception{
|
||||||
this.logger = new Locker<>();
|
this.logger = new Locker<>();
|
||||||
int types = RequestType.values().length;
|
int types = RequestType.values().length;
|
||||||
this.responses = new HashMap[types];
|
this.mappers = new RouteMapper[types];
|
||||||
for(RequestType type : RequestType.values()) this.responses[type.ordinal()] = new HashMap<>();
|
for(RequestType type : RequestType.values()) this.mappers[type.ordinal()] = new RouteMapper();
|
||||||
this.patterns = new HashMap<>();
|
|
||||||
this.rsaJsonWebKey = RsaJwkGenerator.generateJwk(2048);
|
this.rsaJsonWebKey = RsaJwkGenerator.generateJwk(2048);
|
||||||
this.headers = new String[types][0];
|
this.headers = new String[types][0];
|
||||||
}
|
}
|
||||||
|
@ -67,56 +60,13 @@ public class Router<U extends User>{
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void 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];
|
||||||
System.arraycopy(origin, 0, copy, 0, origin.length);
|
System.arraycopy(origin, 0, copy, 0, origin.length);
|
||||||
System.arraycopy(headers, 0, copy, origin.length, headers.length);
|
System.arraycopy(headers, 0, copy, origin.length, headers.length);
|
||||||
this.headers[type.ordinal()] = copy;
|
this.headers[type.ordinal()] = copy;
|
||||||
}
|
return this;
|
||||||
|
|
||||||
public void listen(int port, boolean ssl) throws Exception{
|
|
||||||
if (ssl) { // Not needed with the use of a proxy
|
|
||||||
try {
|
|
||||||
SSLServerSocketFactory ssf = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
|
|
||||||
serverSocket = (SSLServerSocket) ssf.createServerSocket(port);
|
|
||||||
|
|
||||||
while (!serverSocket.isClosed()) {
|
|
||||||
Socket socket = serverSocket.accept();
|
|
||||||
Client<U> client = new Client<>(socket, this);
|
|
||||||
client.start();
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
} finally {
|
|
||||||
stop();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
try{
|
|
||||||
serverSocket = new ServerSocket(port);
|
|
||||||
while (!serverSocket.isClosed()) {
|
|
||||||
Socket socket = serverSocket.accept();
|
|
||||||
Client<U> client = new Client<>(socket, this);
|
|
||||||
client.start();
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
} finally {
|
|
||||||
stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public void stop(){
|
|
||||||
if(serverSocket == null) return;
|
|
||||||
try {
|
|
||||||
serverSocket.close();
|
|
||||||
serverSocket = null;
|
|
||||||
}catch(Exception e){
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Router<U> register(Response response){
|
public Router<U> register(Response response){
|
||||||
|
@ -125,8 +75,7 @@ public class Router<U extends User>{
|
||||||
Response.class.getDeclaredMethods()[0].getParameterTypes());
|
Response.class.getDeclaredMethods()[0].getParameterTypes());
|
||||||
Route route = method.getAnnotation(Route.class);
|
Route route = method.getAnnotation(Route.class);
|
||||||
|
|
||||||
this.responses[route.type().ordinal()].put(response, route);
|
this.mappers[route.type().ordinal()].register(response, route, Pattern.compile(route.path()));
|
||||||
this.patterns.put(response, Pattern.compile(route.path()));
|
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
throw new IllegalArgumentException(e);
|
throw new IllegalArgumentException(e);
|
||||||
}
|
}
|
||||||
|
@ -138,25 +87,15 @@ public class Router<U extends User>{
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Router<U> activeReOrdering(){
|
||||||
|
for(RouteMapper mapper : this.mappers) mapper.activeReOrdering();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
Context exec(RequestType type, String path, User user, HttpReader reader, HttpWriter writer) throws Exception{
|
Context exec(RequestType type, String path, User user, HttpReader reader, HttpWriter writer) throws Exception{
|
||||||
if(type == null) return null;
|
if(type == null) return null;
|
||||||
Context context = new Context(type, path, user, writer, this.headers[type.ordinal()]);
|
Context context = new Context(type, path, user, writer, this.headers[type.ordinal()]);
|
||||||
for(Entry<Response, Route> routes : this.responses[type.ordinal()].entrySet()){
|
if(this.mappers[type.ordinal()].exec(context, path, user, reader, writer)) return context;
|
||||||
Matcher matcher = this.patterns.get(routes.getKey()).matcher(path);
|
|
||||||
if(matcher.matches()){
|
|
||||||
if(user == null && routes.getValue().needLogin()){
|
|
||||||
writer.response(401, "Access-Control-Allow-Origin: *");
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
if(routes.getValue().websocket()){
|
|
||||||
switchToWebSocket(reader, writer);
|
|
||||||
reader = new WebSocketReader(reader);
|
|
||||||
writer = new WebSocketWriter(writer);
|
|
||||||
}
|
|
||||||
routes.getKey().exec(matcher, context, reader, writer);
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(noFileFound != null) noFileFound.exec(null, context, reader, writer);
|
if(noFileFound != null) noFileFound.exec(null, context, reader, writer);
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
@ -192,74 +131,46 @@ public class Router<U extends User>{
|
||||||
return this.logger;
|
return this.logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void switchToWebSocket(HttpReader reader, HttpWriter writer) throws Exception{
|
public void listen(int port, boolean ssl) throws Exception{
|
||||||
String key = reader.getHeader("Sec-WebSocket-Key");
|
if (ssl) { // Not needed with the use of a proxy
|
||||||
if (key == null) throw new IllegalArgumentException();
|
try {
|
||||||
|
SSLServerSocketFactory ssf = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
|
||||||
|
serverSocket = (SSLServerSocket) ssf.createServerSocket(port);
|
||||||
|
|
||||||
writer.write("HTTP/1.1 101 Switching Protocols\n");
|
while (!serverSocket.isClosed()) {
|
||||||
writer.write("Connection: Upgrade\n");
|
Socket socket = serverSocket.accept();
|
||||||
writer.write("Upgrade: websocket\n");
|
Client<U> client = new Client<>(socket, this);
|
||||||
writer.write("Sec-WebSocket-Accept: " + printBase64Binary(MessageDigest.getInstance("SHA-1")
|
client.start();
|
||||||
.digest((key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").getBytes("UTF-8"))) + "\n");
|
}
|
||||||
writer.write("\n");
|
} catch (Exception e) {
|
||||||
writer.flush();
|
e.printStackTrace();
|
||||||
|
} finally {
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try{
|
||||||
|
serverSocket = new ServerSocket(port);
|
||||||
|
while (!serverSocket.isClosed()) {
|
||||||
|
Socket socket = serverSocket.accept();
|
||||||
|
Client<U> client = new Client<>(socket, this);
|
||||||
|
client.start();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} finally {
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void stop(){
|
||||||
// From javax.xml.bind.DatatypeConverter
|
if(serverSocket == null) return;
|
||||||
private String printBase64Binary(byte[] array){
|
try {
|
||||||
char[] arrayOfChar = new char[(array.length + 2) / 3 * 4];
|
serverSocket.close();
|
||||||
int i = _printBase64Binary(array, 0, array.length, arrayOfChar, 0);
|
serverSocket = null;
|
||||||
assert i == arrayOfChar.length;
|
}catch(Exception e){
|
||||||
return new String(arrayOfChar);
|
e.printStackTrace();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int _printBase64Binary(byte[] paramArrayOfbyte, int paramInt1, int paramInt2,
|
|
||||||
char[] paramArrayOfchar, int paramInt3){
|
|
||||||
int i = paramInt2;
|
|
||||||
int j;
|
|
||||||
for (j = paramInt1; i >= 3; j += 3){
|
|
||||||
paramArrayOfchar[paramInt3++] = encode(paramArrayOfbyte[j] >> 2);
|
|
||||||
paramArrayOfchar[paramInt3++] = encode(
|
|
||||||
(paramArrayOfbyte[j] & 0x3) << 4 | paramArrayOfbyte[j + 1] >> 4 & 0xF);
|
|
||||||
paramArrayOfchar[paramInt3++] = encode(
|
|
||||||
(paramArrayOfbyte[j + 1] & 0xF) << 2 | paramArrayOfbyte[j + 2] >> 6 & 0x3);
|
|
||||||
paramArrayOfchar[paramInt3++] = encode(paramArrayOfbyte[j + 2] & 0x3F);
|
|
||||||
i -= 3;
|
|
||||||
}
|
|
||||||
if (i == 1){
|
|
||||||
paramArrayOfchar[paramInt3++] = encode(paramArrayOfbyte[j] >> 2);
|
|
||||||
paramArrayOfchar[paramInt3++] = encode((paramArrayOfbyte[j] & 0x3) << 4);
|
|
||||||
paramArrayOfchar[paramInt3++] = '=';
|
|
||||||
paramArrayOfchar[paramInt3++] = '=';
|
|
||||||
}
|
|
||||||
if (i == 2){
|
|
||||||
paramArrayOfchar[paramInt3++] = encode(paramArrayOfbyte[j] >> 2);
|
|
||||||
paramArrayOfchar[paramInt3++] = encode(
|
|
||||||
(paramArrayOfbyte[j] & 0x3) << 4 | paramArrayOfbyte[j + 1] >> 4 & 0xF);
|
|
||||||
paramArrayOfchar[paramInt3++] = encode((paramArrayOfbyte[j + 1] & 0xF) << 2);
|
|
||||||
paramArrayOfchar[paramInt3++] = '=';
|
|
||||||
}
|
|
||||||
return paramInt3;
|
|
||||||
}
|
|
||||||
|
|
||||||
private char encode(int paramInt){
|
|
||||||
return encodeMap[paramInt & 0x3F];
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final char[] encodeMap = initEncodeMap();
|
|
||||||
|
|
||||||
private static char[] initEncodeMap(){
|
|
||||||
char[] arrayOfChar = new char[64];
|
|
||||||
byte b;
|
|
||||||
for (b = 0; b < 26; b++)
|
|
||||||
arrayOfChar[b] = (char) (65 + b);
|
|
||||||
for (b = 26; b < 52; b++)
|
|
||||||
arrayOfChar[b] = (char) (97 + b - 26);
|
|
||||||
for (b = 52; b < 62; b++)
|
|
||||||
arrayOfChar[b] = (char) (48 + b - 52);
|
|
||||||
arrayOfChar[62] = '+';
|
|
||||||
arrayOfChar[63] = '/';
|
|
||||||
return arrayOfChar;
|
|
||||||
}
|
|
||||||
}
|
}
|
Loading…
Add table
Reference in a new issue