mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-06 05:07:41 +00:00
GUACAMOLE-2036: Merge migration of ReaderGuacamoleReader internals to GuacamoleParser.
This commit is contained in:
@@ -24,13 +24,13 @@ import java.io.IOException;
|
|||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
import java.net.SocketException;
|
import java.net.SocketException;
|
||||||
import java.net.SocketTimeoutException;
|
import java.net.SocketTimeoutException;
|
||||||
import java.util.Deque;
|
import java.util.Arrays;
|
||||||
import java.util.LinkedList;
|
|
||||||
import org.apache.guacamole.GuacamoleConnectionClosedException;
|
import org.apache.guacamole.GuacamoleConnectionClosedException;
|
||||||
import org.apache.guacamole.GuacamoleException;
|
import org.apache.guacamole.GuacamoleException;
|
||||||
import org.apache.guacamole.GuacamoleServerException;
|
import org.apache.guacamole.GuacamoleServerException;
|
||||||
import org.apache.guacamole.GuacamoleUpstreamTimeoutException;
|
import org.apache.guacamole.GuacamoleUpstreamTimeoutException;
|
||||||
import org.apache.guacamole.protocol.GuacamoleInstruction;
|
import org.apache.guacamole.protocol.GuacamoleInstruction;
|
||||||
|
import org.apache.guacamole.protocol.GuacamoleParser;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A GuacamoleReader which wraps a standard Java Reader, using that Reader as
|
* A GuacamoleReader which wraps a standard Java Reader, using that Reader as
|
||||||
@@ -38,6 +38,11 @@ import org.apache.guacamole.protocol.GuacamoleInstruction;
|
|||||||
*/
|
*/
|
||||||
public class ReaderGuacamoleReader implements GuacamoleReader {
|
public class ReaderGuacamoleReader implements GuacamoleReader {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The GuacamoleParser instance for parsing instructions.
|
||||||
|
*/
|
||||||
|
private GuacamoleParser parser = new GuacamoleParser();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapped Reader to be used for all input.
|
* Wrapped Reader to be used for all input.
|
||||||
*/
|
*/
|
||||||
@@ -57,10 +62,10 @@ public class ReaderGuacamoleReader implements GuacamoleReader {
|
|||||||
* The location within the received data buffer that parsing should begin
|
* The location within the received data buffer that parsing should begin
|
||||||
* when more data is read.
|
* when more data is read.
|
||||||
*/
|
*/
|
||||||
private int parseStart;
|
private int parseStart = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The buffer holding all received, unparsed data.
|
* The buffer holding all received data.
|
||||||
*/
|
*/
|
||||||
private char[] buffer = new char[20480];
|
private char[] buffer = new char[20480];
|
||||||
|
|
||||||
@@ -74,7 +79,7 @@ public class ReaderGuacamoleReader implements GuacamoleReader {
|
|||||||
@Override
|
@Override
|
||||||
public boolean available() throws GuacamoleException {
|
public boolean available() throws GuacamoleException {
|
||||||
try {
|
try {
|
||||||
return input.ready() || usedLength != 0;
|
return input.ready() || usedLength > parseStart || parser.hasNext();
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
throw new GuacamoleServerException(e);
|
throw new GuacamoleServerException(e);
|
||||||
@@ -83,97 +88,47 @@ public class ReaderGuacamoleReader implements GuacamoleReader {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public char[] read() throws GuacamoleException {
|
public char[] read() throws GuacamoleException {
|
||||||
|
GuacamoleInstruction instruction = readInstruction();
|
||||||
|
if (instruction == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return instruction.toString().toCharArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GuacamoleInstruction readInstruction() throws GuacamoleException {
|
||||||
try {
|
try {
|
||||||
|
// Loop until the parser has prepared a full instruction
|
||||||
|
while (!parser.hasNext()) {
|
||||||
|
|
||||||
// While we're blocking, or input is available
|
// Parse as much data from the buffer as we can
|
||||||
for (;;) {
|
int parsed = 0;
|
||||||
|
while (parseStart < usedLength && (parsed = parser.append(buffer, parseStart, usedLength - parseStart)) != 0) {
|
||||||
|
parseStart += parsed;
|
||||||
|
}
|
||||||
|
|
||||||
// Length of element
|
// If we still don't have a full instruction attempt to read more data into the buffer
|
||||||
int elementLength = 0;
|
if (!parser.hasNext()) {
|
||||||
|
|
||||||
// Resume where we left off
|
|
||||||
int i = parseStart;
|
|
||||||
|
|
||||||
// Parse instruction in buffer
|
|
||||||
while (i < usedLength) {
|
|
||||||
|
|
||||||
// Read character
|
|
||||||
char readChar = buffer[i++];
|
|
||||||
|
|
||||||
// If digit, update length
|
|
||||||
if (readChar >= '0' && readChar <= '9')
|
|
||||||
elementLength = elementLength * 10 + readChar - '0';
|
|
||||||
|
|
||||||
// If not digit, check for end-of-length character
|
|
||||||
else if (readChar == '.') {
|
|
||||||
|
|
||||||
// Check if element present in buffer
|
|
||||||
if (i + elementLength < usedLength) {
|
|
||||||
|
|
||||||
// Get terminator
|
|
||||||
char terminator = buffer[i + elementLength];
|
|
||||||
|
|
||||||
// Move to character after terminator
|
|
||||||
i += elementLength + 1;
|
|
||||||
|
|
||||||
// Reset length
|
|
||||||
elementLength = 0;
|
|
||||||
|
|
||||||
// Continue here if necessary
|
|
||||||
parseStart = i;
|
|
||||||
|
|
||||||
// If terminator is semicolon, we have a full
|
|
||||||
// instruction.
|
|
||||||
if (terminator == ';') {
|
|
||||||
|
|
||||||
// Copy instruction data
|
|
||||||
char[] instruction = new char[i];
|
|
||||||
System.arraycopy(buffer, 0, instruction, 0, i);
|
|
||||||
|
|
||||||
// Update buffer
|
|
||||||
usedLength -= i;
|
|
||||||
parseStart = 0;
|
|
||||||
System.arraycopy(buffer, i, buffer, 0, usedLength);
|
|
||||||
|
|
||||||
return instruction;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle invalid terminator characters
|
|
||||||
else if (terminator != ',')
|
|
||||||
throw new GuacamoleServerException("Element terminator of instruction was not ';' nor ','");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, read more data
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
|
|
||||||
|
// If we have already parsed some of the buffer and the buffer is almost full then we can trim the parsed data off the buffer
|
||||||
|
if (parseStart > 0 && buffer.length - usedLength < GuacamoleParser.INSTRUCTION_MAX_LENGTH) {
|
||||||
|
System.arraycopy(buffer, parseStart, buffer, 0, usedLength - parseStart);
|
||||||
|
usedLength -= parseStart;
|
||||||
|
parseStart = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, parse error
|
// Read more instruction data into the buffer
|
||||||
else
|
int numRead = input.read(buffer, usedLength, buffer.length - usedLength);
|
||||||
throw new GuacamoleServerException("Non-numeric character in element length.");
|
if (numRead == -1)
|
||||||
|
break;
|
||||||
|
|
||||||
|
usedLength += numRead;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// If past threshold, resize buffer before reading
|
return parser.next();
|
||||||
if (usedLength > buffer.length/2) {
|
|
||||||
char[] biggerBuffer = new char[buffer.length*2];
|
|
||||||
System.arraycopy(buffer, 0, biggerBuffer, 0, usedLength);
|
|
||||||
buffer = biggerBuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attempt to fill buffer
|
|
||||||
int numRead = input.read(buffer, usedLength, buffer.length - usedLength);
|
|
||||||
if (numRead == -1)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
// Update used length
|
|
||||||
usedLength += numRead;
|
|
||||||
|
|
||||||
} // End read loop
|
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (SocketTimeoutException e) {
|
catch (SocketTimeoutException e) {
|
||||||
@@ -188,80 +143,4 @@ public class ReaderGuacamoleReader implements GuacamoleReader {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public GuacamoleInstruction readInstruction() throws GuacamoleException {
|
|
||||||
|
|
||||||
// Get instruction
|
|
||||||
char[] instructionBuffer = read();
|
|
||||||
|
|
||||||
// If EOF, return EOF
|
|
||||||
if (instructionBuffer == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
// Start of element
|
|
||||||
int elementStart = 0;
|
|
||||||
|
|
||||||
// Build list of elements
|
|
||||||
Deque<String> elements = new LinkedList<String>();
|
|
||||||
while (elementStart < instructionBuffer.length) {
|
|
||||||
|
|
||||||
// Find end of length
|
|
||||||
int lengthEnd = -1;
|
|
||||||
for (int i=elementStart; i<instructionBuffer.length; i++) {
|
|
||||||
if (instructionBuffer[i] == '.') {
|
|
||||||
lengthEnd = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// read() is required to return a complete instruction. If it does
|
|
||||||
// not, this is a severe internal error.
|
|
||||||
if (lengthEnd == -1)
|
|
||||||
throw new GuacamoleServerException("Read returned incomplete instruction.");
|
|
||||||
|
|
||||||
// Parse length
|
|
||||||
int length = Integer.parseInt(new String(
|
|
||||||
instructionBuffer,
|
|
||||||
elementStart,
|
|
||||||
lengthEnd - elementStart
|
|
||||||
));
|
|
||||||
|
|
||||||
// Parse element from just after period
|
|
||||||
elementStart = lengthEnd + 1;
|
|
||||||
String element = new String(
|
|
||||||
instructionBuffer,
|
|
||||||
elementStart,
|
|
||||||
length
|
|
||||||
);
|
|
||||||
|
|
||||||
// Append element to list of elements
|
|
||||||
elements.addLast(element);
|
|
||||||
|
|
||||||
// Read terminator after element
|
|
||||||
elementStart += length;
|
|
||||||
char terminator = instructionBuffer[elementStart];
|
|
||||||
|
|
||||||
// Continue reading instructions after terminator
|
|
||||||
elementStart++;
|
|
||||||
|
|
||||||
// If we've reached the end of the instruction
|
|
||||||
if (terminator == ';')
|
|
||||||
break;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pull opcode off elements list
|
|
||||||
String opcode = elements.removeFirst();
|
|
||||||
|
|
||||||
// Create instruction
|
|
||||||
GuacamoleInstruction instruction = new GuacamoleInstruction(
|
|
||||||
opcode,
|
|
||||||
elements.toArray(new String[elements.size()])
|
|
||||||
);
|
|
||||||
|
|
||||||
// Return parsed instruction
|
|
||||||
return instruction;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -41,7 +41,7 @@ public class ReaderGuacamoleReaderTest {
|
|||||||
public void testReader() throws GuacamoleException {
|
public void testReader() throws GuacamoleException {
|
||||||
|
|
||||||
// Test string
|
// Test string
|
||||||
final String test = "1.a,2.bc,3.def,10.helloworld;4.test,5.test2;0.;3.foo;";
|
final String test = "1.a,2.bc,3.def,10.helloworld;4.test,5.test2;0.;3.foo;1.\uD83E\uDD79;";
|
||||||
|
|
||||||
GuacamoleReader reader = new ReaderGuacamoleReader(new StringReader(test));
|
GuacamoleReader reader = new ReaderGuacamoleReader(new StringReader(test));
|
||||||
|
|
||||||
@@ -75,12 +75,46 @@ public class ReaderGuacamoleReaderTest {
|
|||||||
assertEquals(0, instruction.getArgs().size());
|
assertEquals(0, instruction.getArgs().size());
|
||||||
assertEquals("foo", instruction.getOpcode());
|
assertEquals("foo", instruction.getOpcode());
|
||||||
|
|
||||||
|
// Validate fifth test instruction
|
||||||
|
instruction = reader.readInstruction();
|
||||||
|
assertNotNull(instruction);
|
||||||
|
assertEquals(0, instruction.getArgs().size());
|
||||||
|
assertEquals("\uD83E\uDD79", instruction.getOpcode());
|
||||||
|
|
||||||
// There should be no more instructions
|
// There should be no more instructions
|
||||||
instruction = reader.readInstruction();
|
instruction = reader.readInstruction();
|
||||||
assertNull(instruction);
|
assertNull(instruction);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test of ReaderGuacamoleReader's read method.
|
||||||
|
*
|
||||||
|
* @throws GuacamoleException If an error occurs while reading the instructions.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testRead() throws GuacamoleException {
|
||||||
|
// Test string containing multiple instructions
|
||||||
|
final String test = "3.foo,3.bar;2.az,4.bazz;";
|
||||||
|
|
||||||
|
ReaderGuacamoleReader reader = new ReaderGuacamoleReader(new StringReader(test));
|
||||||
|
|
||||||
|
// Expected character arrays for the instructions
|
||||||
|
char[] expectedFirstInstruction = "3.foo,3.bar;".toCharArray();
|
||||||
|
char[] expectedSecondInstruction = "2.az,4.bazz;".toCharArray();
|
||||||
|
|
||||||
|
// Read first instruction and verify
|
||||||
|
char[] firstInstructionChars = reader.read();
|
||||||
|
assertNotNull(firstInstructionChars);
|
||||||
|
assertArrayEquals(expectedFirstInstruction, firstInstructionChars);
|
||||||
|
|
||||||
|
// Read second instruction and verify
|
||||||
|
char[] secondInstructionChars = reader.read();
|
||||||
|
assertNotNull(secondInstructionChars);
|
||||||
|
assertArrayEquals(expectedSecondInstruction, secondInstructionChars);
|
||||||
|
|
||||||
|
// Verify that there are no more instructions
|
||||||
|
assertNull(reader.read());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user