From 12f2561921c74d871acd240256fbad99de504cc4 Mon Sep 17 00:00:00 2001 From: jeffcheasey88 <66554203+jeffcheasey88@users.noreply.github.com> Date: Fri, 11 Apr 2025 14:39:33 +0200 Subject: [PATCH] StrictReader (will be optimise later) -> use a 1 byte buffer to read data, so it can read bytes when needed without skiping any --- src/dev/peerat/framework/HttpReader.java | 46 ++- src/dev/peerat/framework/Router.java | 10 +- src/dev/peerat/framework/StrictReader.java | 321 +++++++++++++++++++++ 3 files changed, 369 insertions(+), 8 deletions(-) create mode 100644 src/dev/peerat/framework/StrictReader.java diff --git a/src/dev/peerat/framework/HttpReader.java b/src/dev/peerat/framework/HttpReader.java index 9aa3372..f0ca8eb 100644 --- a/src/dev/peerat/framework/HttpReader.java +++ b/src/dev/peerat/framework/HttpReader.java @@ -1,11 +1,12 @@ package dev.peerat.framework; -import java.io.BufferedReader; +import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; import java.net.Socket; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -23,14 +24,14 @@ public class HttpReader{ private Socket socket; private InputStream in; - private BufferedReader reader; + private StrictReader reader; private Map 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 +67,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 +134,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; + } } \ No newline at end of file diff --git a/src/dev/peerat/framework/Router.java b/src/dev/peerat/framework/Router.java index 48057ea..f2d3917 100644 --- a/src/dev/peerat/framework/Router.java +++ b/src/dev/peerat/framework/Router.java @@ -1,6 +1,11 @@ package dev.peerat.framework; +import static dev.peerat.framework.RequestType.OPTIONS; +import static dev.peerat.framework.RequestType.POST; + import java.io.BufferedReader; +import java.io.File; +import java.io.FileOutputStream; import java.io.InputStream; import java.io.InputStreamReader; import java.lang.reflect.Constructor; @@ -9,6 +14,7 @@ import java.net.ServerSocket; import java.net.Socket; import java.util.ArrayList; import java.util.List; +import java.util.regex.Matcher; import javax.net.ssl.SSLServerSocket; import javax.net.ssl.SSLServerSocketFactory; @@ -17,9 +23,7 @@ import dev.peerat.framework.auth.Authenticator; public class Router{ - public static void main(String[] args){ - - } + public static void main(String[] args) throws Exception{} private Locker logger; private Locker exceptions; diff --git a/src/dev/peerat/framework/StrictReader.java b/src/dev/peerat/framework/StrictReader.java new file mode 100644 index 0000000..cf041e1 --- /dev/null +++ b/src/dev/peerat/framework/StrictReader.java @@ -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; + } +}