/src/com/dercd/cdio/CDProtocol.java
https://gitlab.com/Moylle/CDAPI · Java · 481 lines · 433 code · 34 blank · 14 comment · 72 complexity · 57a9d88966ed18a5f42c939e6df291db MD5 · raw file
- package com.dercd.cdio;
-
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.OutputStream;
- import java.io.PrintWriter;
- import java.io.StringWriter;
- import java.net.Socket;
- import java.nio.charset.Charset;
- import java.util.function.Consumer;
-
- public class CDProtocol
- {
- public AfterVerificationDelegate afterVerification;
- Socket socket;
- InputStream inputStream;
- OutputStream outputStream;
- private String keyWord;
- private static final int keyWordLength = 32;
- private int maxLength;
-
- protected DataInputObject dio;
- protected RawDataInputObject dioRaw;
- private boolean verifyed = false;
- private int length = -1;
- private int processed = 0;
- public boolean useBigEndian = true;
- public Charset encoding = Charset.forName("UTF-8");
- protected boolean rawGothrough;
-
- private final byte[] ringBuffer;
- protected int buf_start;
- protected int buf_current;
- protected final int buf_capacity;
- protected boolean receivingRaw = false;
- protected final boolean[] currentOptions = new boolean[8];
-
- public Consumer<String> out;
-
- public static Charset ascii = Charset.forName("ASCII");
-
-
- public CDProtocol(Socket socket, int maxLength, int buf_capacity, boolean incomming) throws Exception
- {
- this(socket, maxLength, buf_capacity, incomming, null, null, null, true);
- }
- public CDProtocol(Socket socket, int maxLength, int buf_capacity, boolean incomming, String hashedKeyWord, DataInputObject dio, RawDataInputObject dioRaw, boolean useBigEndian) throws Exception
- {
- this.socket = socket;
- this.inputStream = socket.getInputStream();
- this.outputStream = socket.getOutputStream();
- setKeyword(hashedKeyWord);
- if (buf_capacity < keyWordLength)
- throw new Exception("Buffer have to be at least " + keyWordLength + " bytes long (keyword length)");
- this.buf_capacity = buf_capacity;
- this.ringBuffer = new byte[buf_capacity];
- setMaxLength(maxLength);
- setDIO(dio);
- setDIORaw(dioRaw);
- }
-
-
- public synchronized void process(byte[] data, int length) throws IOException
- {
- int overstrip = addRange(data, length);
- if (!this.verifyed)
- {
- if (!checkIdentification())
- {
- println("Identification failed");
- close();
- throw new IOException("Identification failed");
- }
- if (this.verifyed)
- {
- println("Identification successfull");
- if(this.afterVerification != null)
- this.afterVerification.afterVerification(this);
- }
- else
- return;
- }
- boolean toProcess = true;
- while ((toProcess && this.buf_current != this.buf_start))
- {
- if (this.length == -1)
- {
- if (currentLength() < 5)
- return;
- resetOptions();
- int l = 0;
- int bufStart;
- if (this.useBigEndian)
- for (int i = 0; i < 4; ++i)
- {
- bufStart = getBufStart();
- //DEBUG
- //System.out.println("Lenght byte: " + (this.ringBuffer[bufStart]) + ", Converted: " + (this.ringBuffer[bufStart] & 0xFF));
- l = (l << 8) | (this.ringBuffer[bufStart] & 0xFF);
- setBufStart(bufStart + 1);
- }
- else
- for (int i = 0; i < 4; ++i)
- {
- bufStart = getBufStart();
- l = (l >> 8) | (this.ringBuffer[bufStart] & 0xFF);
- setBufStart(bufStart + 1);
- }
- //DEBUG
- //System.out.println("Received len: " + l);
- if (l <= 0)
- throw new IOException("CDProtocol: Length out of bounds (" + l + "). Terminating connection");
- if (l > this.maxLength)
- throw new IOException("CDProtocol: Length over " + this.maxLength + " chars (" + l + "). Terminating connection");
- this.length = l;
- extractOptions();
- }
- if (this.currentOptions[0])
- if (this.rawGothrough)
- toProcess = procReadyRawGT();
- else
- toProcess = procReadyEntire(true);
- else
- toProcess = procReadyEntire(false);
- if (overstrip != 0)
- {
- addRange(data, overstrip, length - overstrip);
- toProcess = true;
- }
- }
- }
-
- protected int addRange(byte[] input, int length)
- {
- return addRange(input, length, 0);
- }
- protected int addRange(byte[] input, int length, int start)
- {
- int overstrip = 0;
- if (currentLength() + length >= this.buf_capacity)
- {
- overstrip = length - (this.buf_capacity - 1 - currentLength());
- length -= overstrip;
- }
- int bufCurrent;
- for (int i = 0; i < length; ++i)
- {
- bufCurrent = getBufCurrent();
- //DEBUG
- //System.out.println("Buf: char " + ((int) input[i]) + ", byte " + ((byte) input[i]));
- this.ringBuffer[bufCurrent] = input[i];
- setBufCurrent(bufCurrent + 1);
- }
- return overstrip;
- }
-
- protected void extractOptions()
- {
- int bufStart = getBufStart();
- byte b = this.ringBuffer[bufStart];
- setBufStart(bufStart + 1);
- if ((b & OptionMask.RAW.getValue()) == 1)
- this.currentOptions[0] = true;
- }
-
- protected void resetOptions()
- {
- for (int i = this.currentOptions.length - 1; i >= 0; --i)
- this.currentOptions[i] = false;
- }
-
- protected byte[] createConnectedArray()
- {
- return createConnectedArray(-1);
- }
- protected byte[] createConnectedArray(int length)
- {
- if (length <= 0)
- length = currentLength();
- byte[] b = new byte[length];
- int j = this.buf_start, i = 0;
- for (; i < length && j < this.buf_capacity; ++i, ++j)
- b[i] = this.ringBuffer[j];
- j = 0;
- for (; i < length; ++i, ++j)
- b[i] = this.ringBuffer[j];
- return b;
- }
-
- protected void callDIO(byte[] data, int start, int length, boolean raw)
- {
- if (raw)
- this.dioRaw.input(data, true, start, length);
- else
- this.dio.input(new String(data, start, length, this.encoding));
- }
-
- protected boolean procReadyEntire(boolean raw)
- {
- int bufCount = currentLength();
- if (bufCount < this.length)
- return false;
- if (this.buf_current > this.buf_start || this.buf_start + this.length < this.buf_capacity)
- callDIO(this.ringBuffer, this.buf_start, this.length, raw);
- else
- callDIO(createConnectedArray(bufCount == this.length ? -1 : this.length), 0, this.length, raw);
- if (bufCount == this.length)
- this.buf_start = this.buf_current;
- else
- setBufStart(getBufStart() + this.length);
- this.length = -1;
- this.notifyAll();
- return true;
- }
-
- protected boolean procReadyRawGT()
- {
- int toProcess = this.length - this.processed, bufCount = currentLength();
- if (toProcess <= bufCount)
- {
- this.processed = 0;
- this.length = -1;
- if (toProcess == bufCount)
- {
- if (isOverlap())
- this.dioRaw.input(createConnectedArray(), true, 0, toProcess);
- else
- this.dioRaw.input(this.ringBuffer, true, this.buf_start, bufCount);
- this.buf_start = this.buf_current;
- }
- else
- {
- if (this.buf_start + toProcess >= this.buf_capacity)
- this.dioRaw.input(createConnectedArray(toProcess), true, 0, toProcess);
- else
- this.dioRaw.input(this.ringBuffer, true, this.buf_start, toProcess);
- setBufStart(getBufStart() + toProcess);
- }
- this.notifyAll();
- }
- else
- {
- if (isOverlap())
- this.dioRaw.input(createConnectedArray(), false, 0, bufCount);
- else
- this.dioRaw.input(this.ringBuffer, false, this.buf_start, bufCount);
- this.buf_start = this.buf_current;
- this.processed += bufCount;
- }
- return true;
- }
-
- private boolean checkIdentification()
- {
- try
- {
- if (currentLength() < keyWordLength)
- return true;
- String s = new String(this.ringBuffer, this.buf_start, keyWordLength, ascii);
- if (!s.startsWith(this.keyWord))
- return false;
- setBufStart(getBufStart() + keyWordLength);
- this.verifyed = true;
- return true;
- }
- catch (Exception x)
- {}
- return false;
- }
-
- public boolean isAlive()
- {
- return this.socket != null && this.socket.isConnected();
- }
-
- public void send(String s)
- {
- try
- {
- //DEBUG
- //System.out.println("Sending: '" + s + "'");
- sendRaw(s.getBytes(this.encoding));
- }
- catch (Throwable t)
- {
- printException(t);
- close();
- }
- }
-
- public void sendRaw(byte[] b, OptionMask... options)
- {
- sendRaw(b, true, 0, -1, options);
- }
- public void sendRaw(byte[] b, boolean addHeader, OptionMask... options)
- {
- sendRaw(b, addHeader, 0, -1, options);
- }
- public void sendRaw(byte[] b, int start, OptionMask... options)
- {
- sendRaw(b, true, start, -1, options);
- }
- public void sendRaw(byte[] b, boolean addHeader, int start, OptionMask... options)
- {
- sendRaw(b, addHeader, start, -1, options);
- }
- public void sendRaw(byte[] b, int start, int length, OptionMask... options)
- {
- sendRaw(b, true, start, length, options);
- }
- public void sendRaw(byte[] b, boolean addHeader, int start, int length, OptionMask... options)
- {
- try
- {
- //DEBUG
- //System.out.println("Alive: " + isAlive());
- //System.out.println("Verified: " + this.verifyed);
- if (!isAlive() || !this.verifyed)
- return;
- if (length == -1)
- length = b.length;
- //DEBUG
- //System.out.println("Length: " + length);
- //System.out.println("Real lenght: " + b.length);
- synchronized (this.socket)
- {
- if (addHeader)
- {
- if (this.useBigEndian)
- for (int i = 3; i >= 0; i--)
- this.outputStream.write(length >> (8 * i));
- else
- for (int i = 0; i < 4; i++)
- this.outputStream.write(length >> (8 * i));
- int optionByte = 0;
- for (OptionMask om : options)
- optionByte |= om.getValue();
- this.outputStream.write(optionByte);
- }
- this.outputStream.write(b, start, length);
- this.outputStream.flush();
- }
- }
- catch (Throwable t)
- {
- printException(t);
- close();
- }
- }
-
- public void close()
- {
- try
- {
- this.socket.close();
- }
- catch (Exception x)
- {}
- println("Closed");
- }
-
- public boolean isVerifyed()
- {
- return this.verifyed;
- }
-
- public int getMaxLength()
- {
- return this.maxLength;
- }
- public void setMaxLength(int value) throws Exception
- {
- if (value > this.ringBuffer.length)
- throw new Exception("MaxLength must not be higher than buf_capacity");
- this.maxLength = value;
- }
-
- private int getBufCurrent()
- {
- return this.buf_current;
- }
- private void setBufCurrent(int value)
- {
- this.buf_current = value >= this.buf_capacity ? value - this.buf_capacity : value;
- }
-
- private int getBufStart()
- {
- return this.buf_start;
- }
- private void setBufStart(int value)
- {
- this.buf_start = value >= this.buf_capacity ? value - this.buf_capacity : value;
- }
-
- public void setKeyword(byte[] keyWord)
- {
- this.keyWord = CDConnection.hash(keyWord != null ? keyWord : new byte[0]);
- }
- public void setKeyword(String hashedKeyWord) throws Exception
- {
- if(hashedKeyWord == null)
- setKeyword(new byte[0]);
- else if(hashedKeyWord.length() != keyWordLength)
- throw new Exception("Hashed keyword must have a length of " + keyWordLength + " (keyWordLength)");
- this.keyWord = hashedKeyWord;
- }
-
- protected int currentLength()
- {
- int length = this.buf_current - this.buf_start;
- return length < 0 ? length + this.buf_capacity : length;
- }
-
- protected boolean isOverlap()
- {
- return this.buf_current < this.buf_start;
- }
-
- public void setDIO(DataInputObject value)
- {
- this.dio = value != null ? value : (input) -> {};
- }
- public void setDIORaw(RawDataInputObject value)
- {
- this.dioRaw = value != null ? value : (input, end, start, length) -> {};
- }
-
- public boolean getRawGothrough()
- {
- return this.rawGothrough;
- }
- public void setRawGothrough(boolean rawGothrough) throws InterruptedException
- {
- synchronized (this)
- {
- if (this.length != -1)
- this.wait();
- this.rawGothrough = rawGothrough;
- }
- }
- public void setRawGothroughAsync(boolean rawGothrough)
- {
- new Thread(() -> {
- try
- {
- setRawGothrough(rawGothrough);
- }
- catch (Exception x)
- {
- x.printStackTrace();
- }
- }).start();
- }
-
- public void resetVerification()
- {
- this.verifyed = false;
- }
-
- protected void println(String s)
- {
- printlnRaw("CDP: " + s);
- }
- protected void printlnRaw(String s)
- {
- if(this.out != null)
- this.out.accept(s);
- }
-
- protected void printException(Throwable t)
- {
- if(this.out != null)
- {
- StringWriter w = new StringWriter();
- t.printStackTrace(new PrintWriter(w));
- for(String line : w.toString().split("\\r?\\n|\\r"))
- this.out.accept(line);
- }
- }
- }