package dev.peerat.framework; import java.lang.reflect.Method; import java.security.MessageDigest; import java.util.List; import java.util.regex.Matcher; import dev.peerat.framework.utils.json.JsonMap; public class RouteMapper{ private Router router; private RouteState[] states; private int[] calls; private int dif; public RouteMapper(Router router){ this.router = router; this.states = new RouteState[0]; } public void register(Object instance, Method method, Route route){ int length = states.length+1; method.setAccessible(true); this.states = addElement(states, new RouteState[length], new RouteState(instance, method, route)); if(this.calls != null) this.calls = new int[length]; } public void activeReOrdering(){ this.calls = new int[states.length]; this.dif = 2; } public boolean exec(Context context, HttpReader reader, HttpWriter writer, List interceptors) throws Exception{ RouteState target = null; Matcher matcher = null; String path = context.getPath(); 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(state.needLogin()){ String token = reader.readJson().get("token"); context.setUser(this.router.getUser(token)); } } if((!context.isLogged()) && state.needLogin()){ writer.response(401, router.getDefaultHeaders(context.getType())); return true; } target = state; order(i); break; } } } if(target != null){ for(RouteInterceptor interceptor : interceptors){ if(!interceptor.intercept(matcher, context, reader, writer, target.getMethod())) return true; } target.getMethod().invoke(target.getInstance(), target.bindMethod(matcher, context, reader, writer)); return true; } return false; } private void order(int index){ if(this.calls == null) return; int call = this.calls[index]++; if(call >= (Integer.MAX_VALUE-(dif+1))){ for(int i = 0; i < this.calls.length; i++) this.calls[i]/=dif; dif++; call = this.calls[index]; } if(index < 1) return; if(call > this.calls[index-1]){ switchElement(states, index, index-1); this.calls[index] = this.calls[index-1]; this.calls[index-1] = call; } } private E[] addElement(E[] current, E[] growed, E element){ System.arraycopy(current, 0, growed, 0, current.length); growed[current.length] = element; return growed; } private 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; } }