package net.i2p.data.i2cp; /* * free (adj.): unencumbered; not under the control of others * Written by jrandom in 2003 and released into the public domain * with no warranty of any kind, either expressed or implied. * It probably won't make your computer catch on fire, or eat * your children, but it might. Use at your own risk. * */ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import net.i2p.data.DataFormatException; import net.i2p.data.DataHelper; import net.i2p.data.DataStructureImpl; /** * Defines the base message implementation. * * @author jrandom */ public abstract class I2CPMessageImpl extends DataStructureImpl implements I2CPMessage { public I2CPMessageImpl() { // nop } /** * Validate the type and size of the message, and then read the message into the data structures. <p> * * @throws IOException */ public void readMessage(InputStream in) throws I2CPMessageException, IOException { int length = 0; try { length = (int) DataHelper.readLong(in, 4); } catch (DataFormatException dfe) { throw new I2CPMessageException("Error reading the length bytes", dfe); } if (length < 0) throw new I2CPMessageException("Invalid message length specified"); int type = -1; try { type = (int) DataHelper.readLong(in, 1); } catch (DataFormatException dfe) { throw new I2CPMessageException("Error reading the type byte", dfe); } readMessage(in, length, type); } /** * Read the body into the data structures * * @param length number of bytes in the message payload * @throws IOException */ public void readMessage(InputStream in, int length, int type) throws I2CPMessageException, IOException { if (type != getType()) throw new I2CPMessageException("Invalid message type (found: " + type + " supported: " + getType() + " class: " + getClass().getName() + ")"); if (length < 0) throw new IOException("Negative payload size"); /* byte buf[] = new byte[length]; int read = DataHelper.read(in, buf); if (read != length) throw new IOException("Not able to read enough bytes [" + read + "] read, expected [ " + length + "]"); ByteArrayInputStream bis = new ByteArrayInputStream(buf); doReadMessage(bis, length); */ doReadMessage(in, length); } /** * Read in the payload part of the message (after the initial 4 byte size and 1 * byte type) * * @param buf InputStream * @param size payload size * @throws I2CPMessageException * @throws IOException */ protected abstract void doReadMessage(InputStream buf, int size) throws I2CPMessageException, IOException; /** * Write out the payload part of the message (not including the 4 byte size and * 1 byte type) * * @return byte array * @throws I2CPMessageException * @throws IOException */ protected abstract byte[] doWriteMessage() throws I2CPMessageException, IOException; /** * Write out the full message to the stream, including the 4 byte size and 1 * byte type header. * * @throws IOException */ public void writeMessage(OutputStream out) throws I2CPMessageException, IOException { byte[] data = doWriteMessage(); try { DataHelper.writeLong(out, 4, data.length); out.write((byte) getType()); } catch (DataFormatException dfe) { throw new I2CPMessageException("Unable to write the message length or type", dfe); } out.write(data); } public void readBytes(InputStream in) throws DataFormatException, IOException { try { readMessage(in); } catch (I2CPMessageException ime) { throw new DataFormatException("Error reading the message", ime); } } public void writeBytes(OutputStream out) throws DataFormatException, IOException { try { writeMessage(out); } catch (I2CPMessageException ime) { throw new DataFormatException("Error writing the message", ime); } } /** * Return the SessionId for this type of message. * Most but not all message types include a SessionId. * The ones that do already define getSessionId(), but some return a SessionId and * some return a long, so we define a new method here. * * @return null always. Extending classes with a SessionId must override. * @since 0.9.21 */ public SessionId sessionId() { return null; } }