diff --git a/doc/protocol.md b/doc/protocol.md index 29622e2..99817d0 100644 --- a/doc/protocol.md +++ b/doc/protocol.md @@ -26,7 +26,7 @@ All messages begins with `P2P-JAVA-PROJECT VERSION 1.0\n` (this version of the p ## Requests and responses codes - REQUESTS (msb is 0): - `LIST` (0x00) - - `DOWNLOAD` (0x01) + - `LOAD` (0x01) - RESPONSES (msb is 1): - `LIST` (0x80) @@ -39,10 +39,10 @@ All messages begins with `P2P-JAVA-PROJECT VERSION 1.0\n` (this version of the p Payload size for list request is always zero. Payload for list response is filenames separated by `\n`. -### Download +### Load #### Not found Response when the file requested is not found on the server. -Payload size for Not found is zero +Payload size for Not found is zero. #### Protocol error Response when the request cannot be interpreted. @@ -50,7 +50,7 @@ Payload size for Protocol error is zero #### Internal error Response in internal failure case. -Payload size for Internal error is zero +Payload size for Internal error is zero. #### Load response Payload contains @@ -61,3 +61,6 @@ Payload contains [\n] [FILE CONTENT] ``` + +#### Load request +Payload contains only the name of the file to load. diff --git a/src/protocolP2P/CodeType.java b/src/protocolP2P/CodeType.java new file mode 100644 index 0000000..6fa1198 --- /dev/null +++ b/src/protocolP2P/CodeType.java @@ -0,0 +1,5 @@ +package protocolP2P; +public enum CodeType { + REQUEST, + RESPONSE +} diff --git a/src/protocolP2P/FileList.java b/src/protocolP2P/FileList.java new file mode 100644 index 0000000..60fd6b8 --- /dev/null +++ b/src/protocolP2P/FileList.java @@ -0,0 +1,36 @@ +package protocolP2P; +import java.util.ArrayList; +import protocolP2P.Payload; +import protocolP2P.RequestResponseCode; +import exception.ProtocolError; + +public class FileList extends Payload { + private final static RequestResponseCode requestResponseCode = RequestResponseCode.LIST_RESPONSE; + private ArrayList content; + + public FileList(ArrayList content) { + this.content = content; + } + + public FileList(byte[] datagram) throws ProtocolError { + //TODO + assert RequestResponseCode.fromCode(datagram[RequestResponseCode.RRCODE_POSITION]) == RequestResponseCode.LIST_RESPONSE : "FileList subclass is incompatible with this datagram, request/response code must be checked before using this constructor"; + int size = (datagram[PAYLOAD_SIZE_POSITON] << (8*3)) | (datagram[PAYLOAD_SIZE_POSITON+1] << (8*2)) | (datagram[PAYLOAD_SIZE_POSITON+2] << 8) | datagram[PAYLOAD_SIZE_POSITON+3]; + + } + + /** To datagram with padding */ + public byte[] toDatagram() { + byte[] datagram; // java initialize all to zero + // size is keep blank (ProtocolP2PDatagram will handle it) + // set request/response code + datagram[RequestResponseCode.RRCODE_POSITION] = requestResponseCode.codeValue; + // bits 16-31 are reserved for future use + //TODO size + int size = ; + for(i=0;i<4;i++) { + datagram[Payload.PAYLOAD_SIZE_POSITON + i] = (byte) (size >> (8 * (3 - i)) & 0xFF); + } + //TODO content + } +} diff --git a/src/protocolP2P/FilePart.java b/src/protocolP2P/FilePart.java new file mode 100644 index 0000000..6351a72 --- /dev/null +++ b/src/protocolP2P/FilePart.java @@ -0,0 +1,41 @@ +package protocolP2P; +import protocolP2P.Payload; +import protocolP2P.RequestResponseCode; +import exception.ProtocolError; + +public class FilePart extends Payload { + private static final RequestResponseCode requestResponseCode = RequestResponseCode.LOAD_RESPONSE; + private String filename; + private int totalSize; + private int offset; + private byte[] partialContent; + + public FilePart(String filename, int totalSize, int offset, byte[] partialContent) { + this.filename = filename; + this.totalSize = totalSize; + this.offset = offset; + this.partialContent = partialContent; + } + + public FilePart(byte[] datagram) throws ProtocolError { + //TODO + assert RequestResponseCode.fromCode(datagram[RequestResponseCode.RRCODE_POSITION]) == RequestResponseCode.LOAD_RESPONSE : "FilePart subclass is incompatible with this datagram, request/response code must be checked before using this constructor"; + int size = (datagram[PAYLOAD_SIZE_POSITON] << (8*3)) | (datagram[PAYLOAD_SIZE_POSITON+1] << (8*2)) | (datagram[PAYLOAD_SIZE_POSITON+2] << 8) | datagram[PAYLOAD_SIZE_POSITON+3]; + } + + /** To datagram with padding */ + public byte[] toDatagram() { + byte[] datagram; // java initialize all to zero + // size is keep blank (ProtocolP2PDatagram will handle it) + // set request/response code + datagram[RequestResponseCode.RRCODE_POSITION] = requestResponseCode.codeValue; + // bits 16-31 are reserved for future use + //TODO size + int size = ; + for(i=0;i<4;i++) { + datagram[Payload.PAYLOAD_SIZE_POSITON + i] = (byte) (size >> (8 * (3 - i)) & 0xFF); + } + //TODO content + } + +} diff --git a/src/protocolP2P/Payload.java b/src/protocolP2P/Payload.java new file mode 100644 index 0000000..3526cd6 --- /dev/null +++ b/src/protocolP2P/Payload.java @@ -0,0 +1,34 @@ +package protocolP2P; +import protocolP2P.RequestResponseCode; +import protocolP2P.FilePart; +import protocolP2P.FileList; +import exception.ProtocolError; + +public class Payload { + private RequestResponseCode requestResponseCode; + protected final static int PAYLOAD_SIZE_POSITON = 4; + protected final static int PAYLOAD_START_POSITION = 8; + + /** To create request/response with a payload size of zero */ + public Payload(RequestResponseCode requestResponseCode) { + assert requestResponseCode != requestResponseCode.LIST_RESPONSE : "LIST_RESPONSE must use FilePart class"; + assert requestResponseCode != requestResponseCode.LOAD_RESPONSE : "LOAD_RESPONSE must use FileList class"; + this.requestResponseCode = requestResponseCode; + } + + public Payload(byte[] datagram) throws ProtocolError { + assert RequestResponseCode.fromCode(datagram[RequestResponseCode.RRCODE_POSITION]) != RequestResponseCode.LIST_RESPONSE : "LIST_RESPONSE must use FilePart class"; + assert RequestResponseCode.fromCode(datagram[RequestResponseCode.RRCODE_POSITION]) != RequestResponseCode.LOAD_RESPONSE : "LOAD_RESPONSE must use FileList class"; + requestResponseCode = RequestResponseCode.fromCode(datagram[RequestResponseCode.RRCODE_POSITION]); + } + + /** Payload with padding */ + public byte[] toDatagram() { + byte [] datagram = new byte[8]; // java initialize all to zero + // size is keep blank (ProtocolP2PDatagram will handle it) + // set request/response code + datagram[RequestResponseCode.RRCODE_POSITION] = requestResponseCode.codeValue; + // bits 16-31 are reserved for future use + // payload size is 0 (this is what java have initialized datagram) + } +} diff --git a/src/protocolP2P/ProtocolP2PDatagram.java b/src/protocolP2P/ProtocolP2PDatagram.java new file mode 100644 index 0000000..19ab192 --- /dev/null +++ b/src/protocolP2P/ProtocolP2PDatagram.java @@ -0,0 +1,53 @@ +package protocolP2P; +import exception.ProtocolError; +import protocolP2P.Payload; +import protocolP2P.RequestResponseCode; +import java.util.ArrayList; +import java.lang.Byte; + +public class ProtocolP2PDatagram { + private final static byte PROTOCOL_VERSION = 0x11; + private final static int VERSION_POSITON = 0; + private byte version; + private Payload payload; + + public ProtocolP2PDatagram(Payload payload) { + version = PROTOCOL_VERSION; + this.payload = payload; + } + + public ProtocolP2PDatagram(byte[] datagram) throws ProtocolError { + // unwrap version + version = datagram[VERSION_POSITON]; + checkProtocolVersion(); // this can throw ProtocolError + RequestResponseCode r = RequestResponseCode.fromCode(datagram[RequestResponseCode.RRCODE_POSITION]); // this can throw ProtocolError + switch (r) { + case RequestResponseCode.LIST_RESPONSE: + payload = (Payload) new FileList(datagram); + break; + case RequestResponseCode.LOAD_RESPONSE: + payload = (Payload) new FilePart(datagram); + break; + default: + payload = new Payload(datagram); + break; + } + } + + public byte[] toDatagram() { + byte[] datagram = payload.toDatagram(); + datagram[VERSION_POSITON] = version; + return datagram; + } + + public Payload getPayload() { + return payload; + } + + private void checkProtocolVersion() throws ProtocolError { + if (PROTOCOL_VERSION != version) { + throw new ProtocolError(); + } + } +} + diff --git a/src/protocolP2P/RequestResponseCode.java b/src/protocolP2P/RequestResponseCode.java new file mode 100644 index 0000000..da6a923 --- /dev/null +++ b/src/protocolP2P/RequestResponseCode.java @@ -0,0 +1,62 @@ +package protocolP2P; +import protocolP2P.CodeType; +import exception.ProtocolError; +import java.util.HashMap; +import java.util.Map; +import java.lang.Byte; + +/** Request/Response code enum. + * @author Louis Royer + * @author Flavien Haas + * @author JS Auge + * @version 1.0 + */ +public enum RequestResponseCode { + LIST_REQUEST(CodeType.REQUEST, (byte)0x00), + LOAD_REQUEST(CodeType.REQUEST, (byte)0x01), + LIST_RESPONSE(CodeType.RESPONSE, (byte)0x80), + LOAD_RESPONSE(CodeType.RESPONSE, (byte)0x81), + NOT_FOUND(CodeType.RESPONSE, (byte)0x82), + PROTOCOL_ERROR(CodeType.RESPONSE, (byte)0x83), + INTERNAL_ERROR(CodeType.RESPONSE, (byte)0x84); + + public final CodeType codeType; + public final byte codeValue; + protected final static int RRCODE_POSITION = 1; + + /* To be able to convert code to enum */ + private static final Map BY_CODE = new HashMap<>(); + /* Initialization of HashMap */ + static { + for (RequestResponseCode r: values()) { + assert !BY_CODE.containsKey(Byte.valueOf(r.codeValue)) : "Duplicate in " + RequestResponseCode.class.getCanonicalName(); + BY_CODE.put(Byte.valueOf(r.codeValue), r); + } + } + + /** Private constructor + * @param codeType type of code (request or response) + * @param codeValue value of the element in datagram + * @return enum element + */ + private RequestResponseCode(CodeType codeType, byte codeValue) { + this.codeType = codeType; + this.codeValue = codeValue; + } + + /** Gives enum from datagram code. + * @param code value of the element in datagram + * @return enum element + */ + public static RequestResponseCode fromCode(byte code) throws ProtocolError { + byte code = BY_CODE.get(Byte.valueOf(code)); + if (code == null) { + throw new ProtocolError(); + } + return code; + } + + +} + +