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.net.SocketException;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.util.Deque;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Arrays;
|
||||
import org.apache.guacamole.GuacamoleConnectionClosedException;
|
||||
import org.apache.guacamole.GuacamoleException;
|
||||
import org.apache.guacamole.GuacamoleServerException;
|
||||
import org.apache.guacamole.GuacamoleUpstreamTimeoutException;
|
||||
import org.apache.guacamole.protocol.GuacamoleInstruction;
|
||||
import org.apache.guacamole.protocol.GuacamoleParser;
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
|
||||
/**
|
||||
* The GuacamoleParser instance for parsing instructions.
|
||||
*/
|
||||
private GuacamoleParser parser = new GuacamoleParser();
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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];
|
||||
|
||||
@@ -74,7 +79,7 @@ public class ReaderGuacamoleReader implements GuacamoleReader {
|
||||
@Override
|
||||
public boolean available() throws GuacamoleException {
|
||||
try {
|
||||
return input.ready() || usedLength != 0;
|
||||
return input.ready() || usedLength > parseStart || parser.hasNext();
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new GuacamoleServerException(e);
|
||||
@@ -83,97 +88,47 @@ public class ReaderGuacamoleReader implements GuacamoleReader {
|
||||
|
||||
@Override
|
||||
public char[] read() throws GuacamoleException {
|
||||
GuacamoleInstruction instruction = readInstruction();
|
||||
if (instruction == null)
|
||||
return null;
|
||||
|
||||
return instruction.toString().toCharArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuacamoleInstruction readInstruction() throws GuacamoleException {
|
||||
try {
|
||||
// Loop until the parser has prepared a full instruction
|
||||
while (!parser.hasNext()) {
|
||||
|
||||
// While we're blocking, or input is available
|
||||
for (;;) {
|
||||
// Parse as much data from the buffer as we can
|
||||
int parsed = 0;
|
||||
while (parseStart < usedLength && (parsed = parser.append(buffer, parseStart, usedLength - parseStart)) != 0) {
|
||||
parseStart += parsed;
|
||||
}
|
||||
|
||||
// Length of element
|
||||
int elementLength = 0;
|
||||
|
||||
// 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 still don't have a full instruction attempt to read more data into the buffer
|
||||
if (!parser.hasNext()) {
|
||||
|
||||
// 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
|
||||
else
|
||||
throw new GuacamoleServerException("Non-numeric character in element length.");
|
||||
// Read more instruction data into the buffer
|
||||
int numRead = input.read(buffer, usedLength, buffer.length - usedLength);
|
||||
if (numRead == -1)
|
||||
break;
|
||||
|
||||
usedLength += numRead;
|
||||
|
||||
}
|
||||
|
||||
// If past threshold, resize buffer before reading
|
||||
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
|
||||
return parser.next();
|
||||
|
||||
}
|
||||
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 {
|
||||
|
||||
// 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));
|
||||
|
||||
@@ -75,12 +75,46 @@ public class ReaderGuacamoleReaderTest {
|
||||
assertEquals(0, instruction.getArgs().size());
|
||||
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
|
||||
instruction = reader.readInstruction();
|
||||
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