Removed the restriction that transport updates have to be written in

delimited form.
This commit is contained in:
akwizgran
2011-08-14 11:41:56 +02:00
parent c2b0f0ab5a
commit 254da2da27
9 changed files with 119 additions and 46 deletions

View File

@@ -19,6 +19,7 @@ public interface Tags {
static final int OFFER = 9; static final int OFFER = 9;
static final int OFFER_ID = 10; static final int OFFER_ID = 10;
static final int REQUEST = 11; static final int REQUEST = 11;
static final int SUBSCRIPTIONS = 12; static final int SUBSCRIPTION_UPDATE = 12;
static final int TRANSPORTS = 13; static final int TRANSPORT_PROPERTIES = 13;
static final int TRANSPORT_UPDATE = 14;
} }

View File

@@ -9,6 +9,12 @@ public interface Reader {
boolean eof() throws IOException; boolean eof() throws IOException;
void close() throws IOException; void close() throws IOException;
void setMaxStringLength(int length);
void resetMaxStringLength();
void setMaxBytesLength(int length);
void resetMaxBytesLength();
void addConsumer(Consumer c); void addConsumer(Consumer c);
void removeConsumer(Consumer c); void removeConsumer(Consumer c);

View File

@@ -30,8 +30,8 @@ class ProtocolReaderImpl implements ProtocolReader {
reader.addObjectReader(Tags.BATCH, batchReader); reader.addObjectReader(Tags.BATCH, batchReader);
reader.addObjectReader(Tags.OFFER, offerReader); reader.addObjectReader(Tags.OFFER, offerReader);
reader.addObjectReader(Tags.REQUEST, requestReader); reader.addObjectReader(Tags.REQUEST, requestReader);
reader.addObjectReader(Tags.SUBSCRIPTIONS, subscriptionReader); reader.addObjectReader(Tags.SUBSCRIPTION_UPDATE, subscriptionReader);
reader.addObjectReader(Tags.TRANSPORTS, transportReader); reader.addObjectReader(Tags.TRANSPORT_UPDATE, transportReader);
} }
public boolean hasAck() throws IOException { public boolean hasAck() throws IOException {
@@ -67,19 +67,19 @@ class ProtocolReaderImpl implements ProtocolReader {
} }
public boolean hasSubscriptionUpdate() throws IOException { public boolean hasSubscriptionUpdate() throws IOException {
return reader.hasUserDefined(Tags.SUBSCRIPTIONS); return reader.hasUserDefined(Tags.SUBSCRIPTION_UPDATE);
} }
public SubscriptionUpdate readSubscriptionUpdate() throws IOException { public SubscriptionUpdate readSubscriptionUpdate() throws IOException {
return reader.readUserDefined(Tags.SUBSCRIPTIONS, return reader.readUserDefined(Tags.SUBSCRIPTION_UPDATE,
SubscriptionUpdate.class); SubscriptionUpdate.class);
} }
public boolean hasTransportUpdate() throws IOException { public boolean hasTransportUpdate() throws IOException {
return reader.hasUserDefined(Tags.TRANSPORTS); return reader.hasUserDefined(Tags.TRANSPORT_UPDATE);
} }
public TransportUpdate readTransportUpdate() throws IOException { public TransportUpdate readTransportUpdate() throws IOException {
return reader.readUserDefined(Tags.TRANSPORTS, TransportUpdate.class); return reader.readUserDefined(Tags.TRANSPORT_UPDATE, TransportUpdate.class);
} }
} }

View File

@@ -26,7 +26,7 @@ class SubscriptionReader implements ObjectReader<SubscriptionUpdate> {
Consumer counting = new CountingConsumer(SubscriptionUpdate.MAX_SIZE); Consumer counting = new CountingConsumer(SubscriptionUpdate.MAX_SIZE);
// Read the data // Read the data
r.addConsumer(counting); r.addConsumer(counting);
r.readUserDefinedTag(Tags.SUBSCRIPTIONS); r.readUserDefinedTag(Tags.SUBSCRIPTION_UPDATE);
r.addObjectReader(Tags.GROUP, groupReader); r.addObjectReader(Tags.GROUP, groupReader);
Map<Group, Long> subs = r.readMap(Group.class, Long.class); Map<Group, Long> subs = r.readMap(Group.class, Long.class);
r.removeObjectReader(Tags.GROUP); r.removeObjectReader(Tags.GROUP);

View File

@@ -1,6 +1,7 @@
package net.sf.briar.protocol; package net.sf.briar.protocol;
import java.io.IOException; import java.io.IOException;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.TreeMap; import java.util.TreeMap;
@@ -13,9 +14,11 @@ import net.sf.briar.api.serial.Reader;
class TransportReader implements ObjectReader<TransportUpdate> { class TransportReader implements ObjectReader<TransportUpdate> {
private final TransportFactory transportFactory; private final TransportFactory transportFactory;
private final ObjectReader<TransportProperties> propertiesReader;
TransportReader(TransportFactory transportFactory) { TransportReader(TransportFactory transportFactory) {
this.transportFactory = transportFactory; this.transportFactory = transportFactory;
propertiesReader = new TransportPropertiesReader();
} }
public TransportUpdate readObject(Reader r) throws IOException { public TransportUpdate readObject(Reader r) throws IOException {
@@ -23,27 +26,41 @@ class TransportReader implements ObjectReader<TransportUpdate> {
Consumer counting = new CountingConsumer(TransportUpdate.MAX_SIZE); Consumer counting = new CountingConsumer(TransportUpdate.MAX_SIZE);
// Read the data // Read the data
r.addConsumer(counting); r.addConsumer(counting);
r.readUserDefinedTag(Tags.TRANSPORTS); r.readUserDefinedTag(Tags.TRANSPORT_UPDATE);
// Transport maps are always written in delimited form r.addObjectReader(Tags.TRANSPORT_PROPERTIES, propertiesReader);
r.setMaxStringLength(TransportUpdate.MAX_SIZE);
List<TransportProperties> l = r.readList(TransportProperties.class);
r.resetMaxStringLength();
r.removeObjectReader(Tags.TRANSPORT_PROPERTIES);
Map<String, Map<String, String>> transports = Map<String, Map<String, String>> transports =
new TreeMap<String, Map<String, String>>(); new TreeMap<String, Map<String, String>>();
r.readMapStart(); for(TransportProperties t : l) transports.put(t.name, t.properties);
while(!r.hasMapEnd()) {
String name = r.readString(TransportUpdate.MAX_SIZE);
Map<String, String> properties = new TreeMap<String, String>();
r.readMapStart();
while(!r.hasMapEnd()) {
String key = r.readString(TransportUpdate.MAX_SIZE);
String value = r.readString(TransportUpdate.MAX_SIZE);
properties.put(key, value);
}
r.readMapEnd();
transports.put(name, properties);
}
r.readMapEnd();
long timestamp = r.readInt64(); long timestamp = r.readInt64();
r.removeConsumer(counting); r.removeConsumer(counting);
// Build and return the transport update // Build and return the transport update
return transportFactory.createTransports(transports, timestamp); return transportFactory.createTransports(transports, timestamp);
} }
private static class TransportProperties {
private final String name;
private final Map<String, String> properties;
TransportProperties(String name, Map<String, String> properties) {
this.name = name;
this.properties = properties;
}
}
private static class TransportPropertiesReader
implements ObjectReader<TransportProperties> {
public TransportProperties readObject(Reader r) throws IOException {
r.readUserDefinedTag(Tags.TRANSPORT_PROPERTIES);
String name = r.readString();
Map<String, String> properties =
r.readMap(String.class, String.class);
return new TransportProperties(name, properties);
}
}
} }

View File

@@ -21,7 +21,7 @@ class SubscriptionWriterImpl implements SubscriptionWriter {
} }
public void writeSubscriptions(Map<Group, Long> subs) throws IOException { public void writeSubscriptions(Map<Group, Long> subs) throws IOException {
w.writeUserDefinedTag(Tags.SUBSCRIPTIONS); w.writeUserDefinedTag(Tags.SUBSCRIPTION_UPDATE);
w.writeMap(subs); w.writeMap(subs);
w.writeInt64(System.currentTimeMillis()); w.writeInt64(System.currentTimeMillis());
out.flush(); out.flush();

View File

@@ -22,19 +22,14 @@ class TransportWriterImpl implements TransportWriter {
public void writeTransports(Map<String, Map<String, String>> transports) public void writeTransports(Map<String, Map<String, String>> transports)
throws IOException { throws IOException {
w.writeUserDefinedTag(Tags.TRANSPORTS); w.writeUserDefinedTag(Tags.TRANSPORT_UPDATE);
// Transport maps are always written in delimited form w.writeListStart();
w.writeMapStart();
for(Entry<String, Map<String, String>> e : transports.entrySet()) { for(Entry<String, Map<String, String>> e : transports.entrySet()) {
w.writeUserDefinedTag(Tags.TRANSPORT_PROPERTIES);
w.writeString(e.getKey()); w.writeString(e.getKey());
w.writeMapStart(); w.writeMap(e.getValue());
for(Entry<String, String> e1 : e.getValue().entrySet()) {
w.writeString(e1.getKey());
w.writeString(e1.getValue());
}
w.writeMapEnd();
} }
w.writeMapEnd(); w.writeListEnd();
w.writeInt64(System.currentTimeMillis()); w.writeInt64(System.currentTimeMillis());
out.flush(); out.flush();
} }

View File

@@ -26,6 +26,8 @@ class ReaderImpl implements Reader {
private boolean hasLookahead = false, eof = false; private boolean hasLookahead = false, eof = false;
private byte next, nextNext; private byte next, nextNext;
private byte[] buf = null; private byte[] buf = null;
private int maxStringLength = Integer.MAX_VALUE;
private int maxBytesLength = Integer.MAX_VALUE;
ReaderImpl(InputStream in) { ReaderImpl(InputStream in) {
this.in = in; this.in = in;
@@ -71,6 +73,22 @@ class ReaderImpl implements Reader {
in.close(); in.close();
} }
public void setMaxStringLength(int length) {
maxStringLength = length;
}
public void resetMaxStringLength() {
maxStringLength = Integer.MAX_VALUE;
}
public void setMaxBytesLength(int length) {
maxBytesLength = length;
}
public void resetMaxBytesLength() {
maxBytesLength = Integer.MAX_VALUE;
}
public void addConsumer(Consumer c) { public void addConsumer(Consumer c) {
Consumer[] newConsumers = new Consumer[consumers.length + 1]; Consumer[] newConsumers = new Consumer[consumers.length + 1];
System.arraycopy(consumers, 0, newConsumers, 0, consumers.length); System.arraycopy(consumers, 0, newConsumers, 0, consumers.length);
@@ -268,21 +286,26 @@ class ReaderImpl implements Reader {
} }
public String readString() throws IOException { public String readString() throws IOException {
return readString(Integer.MAX_VALUE);
}
public String readString(int maxLength) throws IOException {
if(!hasString()) throw new FormatException(); if(!hasString()) throw new FormatException();
consumeLookahead(); consumeLookahead();
int length; int length;
if(next == Tag.STRING) length = readLength(); if(next == Tag.STRING) length = readLength();
else length = 0xFF & next ^ Tag.SHORT_STRING; else length = 0xFF & next ^ Tag.SHORT_STRING;
if(length > maxLength) throw new FormatException(); if(length > maxStringLength) throw new FormatException();
if(length == 0) return ""; if(length == 0) return "";
readIntoBuffer(length); readIntoBuffer(length);
return new String(buf, 0, length, "UTF-8"); return new String(buf, 0, length, "UTF-8");
} }
public String readString(int maxLength) throws IOException {
setMaxStringLength(maxLength);
try {
return readString();
} finally {
resetMaxStringLength();
}
}
private int readLength() throws IOException { private int readLength() throws IOException {
if(!hasLength()) throw new FormatException(); if(!hasLength()) throw new FormatException();
if(next >= 0) return readUint7(); if(next >= 0) return readUint7();
@@ -306,22 +329,27 @@ class ReaderImpl implements Reader {
} }
public byte[] readBytes() throws IOException { public byte[] readBytes() throws IOException {
return readBytes(Integer.MAX_VALUE);
}
public byte[] readBytes(int maxLength) throws IOException {
if(!hasBytes()) throw new FormatException(); if(!hasBytes()) throw new FormatException();
consumeLookahead(); consumeLookahead();
int length; int length;
if(next == Tag.BYTES) length = readLength(); if(next == Tag.BYTES) length = readLength();
else length = 0xFF & next ^ Tag.SHORT_BYTES; else length = 0xFF & next ^ Tag.SHORT_BYTES;
if(length > maxLength) throw new FormatException(); if(length > maxBytesLength) throw new FormatException();
if(length == 0) return EMPTY_BUFFER; if(length == 0) return EMPTY_BUFFER;
byte[] b = new byte[length]; byte[] b = new byte[length];
readIntoBuffer(b, length); readIntoBuffer(b, length);
return b; return b;
} }
public byte[] readBytes(int maxLength) throws IOException {
setMaxBytesLength(maxLength);
try {
return readBytes();
} finally {
resetMaxBytesLength();
}
}
public boolean hasList() throws IOException { public boolean hasList() throws IOException {
if(!hasLookahead) readLookahead(true); if(!hasLookahead) readLookahead(true);
if(eof) return false; if(eof) return false;

View File

@@ -4,6 +4,7 @@ import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
@@ -485,6 +486,31 @@ public class ReaderImplTest extends TestCase {
assertEquals("bar", e.getValue().s); assertEquals("bar", e.getValue().s);
} }
@Test
public void testMaxLengthAppliesInsideMap() throws Exception {
setContents("B" + "1" + "83666F6F" + "93010203");
r.setMaxStringLength(3);
r.setMaxBytesLength(3);
Map<String, Bytes> m = r.readMap(String.class, Bytes.class);
String key = "foo";
Bytes value = new Bytes(new byte[] {1, 2, 3});
assertEquals(Collections.singletonMap(key, value), m);
// The max string length should be applied inside the map
setContents("B" + "1" + "83666F6F" + "93010203");
r.setMaxStringLength(2);
try {
r.readMap(String.class, Bytes.class);
fail();
} catch(FormatException expected) {}
// The max bytes length should be applied inside the map
setContents("B" + "1" + "83666F6F" + "93010203");
r.setMaxBytesLength(2);
try {
r.readMap(String.class, Bytes.class);
fail();
} catch(FormatException expected) {}
}
@Test @Test
public void testReadEmptyInput() throws Exception { public void testReadEmptyInput() throws Exception {
setContents(""); setContents("");