Add ProtocolParser to parse RawPacket into PrimitiveRequest using the Tokenizer #187

Merged
lars.winzer merged 6 commits from feat/protocol-parser into main 2026-03-15 16:19:05 +01:00
5 changed files with 135 additions and 1 deletions
@@ -0,0 +1,4 @@
package ch.unibas.dmi.dbis.cs108.casono.server.network.parser;
/** Used in the PrimitiveRequest class to store the key of a parameter with its respective value */
public record Parameter(String key, String value) {}
@@ -0,0 +1,6 @@
package ch.unibas.dmi.dbis.cs108.casono.server.network.parser;
import java.util.List;
/** Created by the ProtocolParser to allow easy access to the request contents */
public record PrimitiveRequest(int requestId, String command, List<Parameter> parameters) {}
@@ -0,0 +1,110 @@
package ch.unibas.dmi.dbis.cs108.casono.server.network.parser;
import ch.unibas.dmi.dbis.cs108.casono.server.network.transport.RawPacket;
import ch.unibas.dmi.dbis.cs108.casono.server.tokenizer.RawToken;
import ch.unibas.dmi.dbis.cs108.casono.server.tokenizer.Token;
import ch.unibas.dmi.dbis.cs108.casono.server.tokenizer.TokenClassifier;
import ch.unibas.dmi.dbis.cs108.casono.server.tokenizer.TokenType;
import ch.unibas.dmi.dbis.cs108.casono.server.tokenizer.Tokenizer;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
/** ProtocolParser, used to parse RawPacket into a PrimitiveRequest */
public class ProtocolParser {
/**
* Parses the payload of the provided RawPacket
*
* @param packet the RawPacket containing the recieved data
* @return created PrimitiveRequest
*/
public static PrimitiveRequest parse(RawPacket packet) {
List<RawToken> rawTokens = Tokenizer.tokenize(packet.payload());
List<Token> tokens = TokenClassifier.classify(rawTokens);
Iterator<Token> iterator = tokens.iterator();
String command = readCommand(iterator);
List<Parameter> parameters = readParameters(iterator);
return new PrimitiveRequest(packet.requestId(), command, parameters);
}
/**
* Reads the first token, which is ensured to be the command of the request
*
* @param iterator
* @return read command
*/
private static String readCommand(Iterator<Token> iterator) {
Token token = iterator.next();
if (token.type() != TokenType.COMMAND) {
throw new ProtocolParserException("Expected COMMAND got " + token.type());
}
return token.value();
}
/**
* Iterates over the remaining tokens, calling helperfunctions to validate token type
*
* @param iterator
* @return list containing all parsed parameters
*/
private static List<Parameter> readParameters(Iterator<Token> iterator) {
List<Parameter> parameters = new ArrayList<>();
try {
while (iterator.hasNext()) {
Token nextToken = iterator.next();
if (nextToken.type() == TokenType.EOF) {
break;
}
String key = readKey(nextToken);
readSeperator(iterator.next());
String value = readValue(iterator.next());
parameters.add(new Parameter(key, value));
}
} catch (NoSuchElementException e) {
throw new ProtocolParserException("Ran out of tokens while reading parameter");
}
return parameters;
}
/**
* @param token the current token
* @return
*/
private static String readKey(Token token) {
if (token.type() != TokenType.KEY) {
throw new ProtocolParserException("Expected KEY got " + token.type());
}
return token.value();
}
/**
* @param token the current token
*/
private static void readSeperator(Token token) {
if (token.type() != TokenType.SEPARATOR) {
throw new ProtocolParserException("Expected SEPARATOR got " + token.type());
}
}
/**
* @param token the current token
* @return
*/
private static String readValue(Token token) {
if (token.type() != TokenType.VALUE) {
throw new ProtocolParserException("Expected VALUE got " + token.type());
}
return token.value();
}
}
@@ -0,0 +1,7 @@
package ch.unibas.dmi.dbis.cs108.casono.server.network.parser;
public class ProtocolParserException extends RuntimeException {
public ProtocolParserException(String message) {
super(message);
}
}
@@ -2,6 +2,9 @@ package ch.unibas.dmi.dbis.cs108.casono.server.network.sessions;
import ch.unibas.dmi.dbis.cs108.casono.server.network.events.DisconnectEvent;
import ch.unibas.dmi.dbis.cs108.casono.server.network.events.EventBus;
import ch.unibas.dmi.dbis.cs108.casono.server.network.parser.PrimitiveRequest;
import ch.unibas.dmi.dbis.cs108.casono.server.network.parser.ProtocolParser;
import ch.unibas.dmi.dbis.cs108.casono.server.network.transport.RawPacket;
import ch.unibas.dmi.dbis.cs108.casono.server.network.transport.TransportLayer;
import java.io.EOFException;
import java.io.IOException;
@@ -64,7 +67,11 @@ public class Session implements Runnable {
public void run() {
while (running) {
try {
logger.debug("Recieved: {}", transport.read());
RawPacket rawPacket = transport.read();
logger.debug("Recieved: {}", rawPacket);
PrimitiveRequest primitiveRequest = ProtocolParser.parse(rawPacket);
logger.debug("Parsed request to {}", primitiveRequest);
} catch (EOFException e) {
logger.info("Client disconnected");
eventBus.publish(new DisconnectEvent(id));