More v3 support

Convert IDs from ints to Strings
Wait for STREAM STATUS
Open 2nd socket for sender
v3 sender working
This commit is contained in:
zzz
2015-11-25 22:59:41 +00:00
parent 612e01cbbf
commit 868e5e988c
5 changed files with 192 additions and 137 deletions

View File

@ -10,9 +10,9 @@ public class SAMClientEventListenerImpl implements SAMReader.SAMClientEventListe
public void helloReplyReceived(boolean ok, String version) {}
public void namingReplyReceived(String name, String result, String value, String message) {}
public void sessionStatusReceived(String result, String destination, String message) {}
public void streamClosedReceived(String result, int id, String message) {}
public void streamConnectedReceived(String remoteDestination, int id) {}
public void streamDataReceived(int id, byte[] data, int offset, int length) {}
public void streamStatusReceived(String result, int id, String message) {}
public void streamClosedReceived(String result, String id, String message) {}
public void streamConnectedReceived(String remoteDestination, String id) {}
public void streamDataReceived(String id, byte[] data, int offset, int length) {}
public void streamStatusReceived(String result, String id, String message) {}
public void unknownMessageReceived(String major, String minor, Properties params) {}
}

View File

@ -18,8 +18,10 @@ public class SAMEventHandler extends SAMClientEventListenerImpl {
private String _version;
private final Object _helloLock = new Object();
private Boolean _sessionCreateOk;
private Boolean _streamStatusOk;
private final Object _sessionCreateLock = new Object();
private final Object _namingReplyLock = new Object();
private final Object _streamStatusLock = new Object();
private final Map<String,String> _namingReplies = new HashMap<String,String>();
public SAMEventHandler(I2PAppContext ctx) {
@ -27,7 +29,7 @@ public class SAMEventHandler extends SAMClientEventListenerImpl {
_log = ctx.logManager().getLog(getClass());
}
@Override
@Override
public void helloReplyReceived(boolean ok, String version) {
synchronized (_helloLock) {
if (ok)
@ -39,7 +41,7 @@ public class SAMEventHandler extends SAMClientEventListenerImpl {
}
}
@Override
@Override
public void sessionStatusReceived(String result, String destination, String msg) {
synchronized (_sessionCreateLock) {
if (SAMReader.SAMClientEventListener.SESSION_STATUS_OK.equals(result))
@ -50,7 +52,7 @@ public class SAMEventHandler extends SAMClientEventListenerImpl {
}
}
@Override
@Override
public void namingReplyReceived(String name, String result, String value, String msg) {
synchronized (_namingReplyLock) {
if (SAMReader.SAMClientEventListener.NAMING_REPLY_OK.equals(result))
@ -61,7 +63,18 @@ public class SAMEventHandler extends SAMClientEventListenerImpl {
}
}
@Override
@Override
public void streamStatusReceived(String result, String id, String message) {
synchronized (_streamStatusLock) {
if (SAMReader.SAMClientEventListener.SESSION_STATUS_OK.equals(result))
_streamStatusOk = Boolean.TRUE;
else
_streamStatusOk = Boolean.FALSE;
_streamStatusLock.notifyAll();
}
}
@Override
public void unknownMessageReceived(String major, String minor, Properties params) {
_log.error("Unhandled message: [" + major + "] [" + minor + "] [" + params + "]");
}
@ -107,6 +120,24 @@ public class SAMEventHandler extends SAMClientEventListenerImpl {
}
}
/**
* Wait for the stream to be created, returning true if everything went ok
*
* @return true if everything ok
*/
public boolean waitForStreamStatusReply() {
while (true) {
try {
synchronized (_streamStatusLock) {
if (_streamStatusOk == null)
_streamStatusLock.wait();
else
return _streamStatusOk.booleanValue();
}
} catch (InterruptedException ie) {}
}
}
/**
* Return the destination found matching the name, or null if the key was
* not able to be retrieved.

View File

@ -73,10 +73,10 @@ public class SAMReader {
public void helloReplyReceived(boolean ok, String version);
public void sessionStatusReceived(String result, String destination, String message);
public void streamStatusReceived(String result, int id, String message);
public void streamConnectedReceived(String remoteDestination, int id);
public void streamClosedReceived(String result, int id, String message);
public void streamDataReceived(int id, byte data[], int offset, int length);
public void streamStatusReceived(String result, String id, String message);
public void streamConnectedReceived(String remoteDestination, String id);
public void streamClosedReceived(String result, String id, String message);
public void streamDataReceived(String id, byte data[], int offset, int length);
public void namingReplyReceived(String name, String result, String value, String message);
public void destReplyReceived(String publicKey, String privateKey);
@ -181,24 +181,17 @@ public class SAMReader {
String result = params.getProperty("RESULT");
String id = params.getProperty("ID");
String msg = params.getProperty("MESSAGE");
if (id != null) {
try {
_listener.streamStatusReceived(result, Integer.parseInt(id), msg);
} catch (NumberFormatException nfe) {
_listener.unknownMessageReceived(major, minor, params);
}
} else {
_listener.unknownMessageReceived(major, minor, params);
}
// id is null in v3, so pass it through regardless
//if (id != null) {
_listener.streamStatusReceived(result, id, msg);
//} else {
// _listener.unknownMessageReceived(major, minor, params);
//}
} else if ("CONNECTED".equals(minor)) {
String dest = params.getProperty("DESTINATION");
String id = params.getProperty("ID");
if (id != null) {
try {
_listener.streamConnectedReceived(dest, Integer.parseInt(id));
} catch (NumberFormatException nfe) {
_listener.unknownMessageReceived(major, minor, params);
}
_listener.streamConnectedReceived(dest, id);
} else {
_listener.unknownMessageReceived(major, minor, params);
}
@ -207,11 +200,7 @@ public class SAMReader {
String id = params.getProperty("ID");
String msg = params.getProperty("MESSAGE");
if (id != null) {
try {
_listener.streamClosedReceived(result, Integer.parseInt(id), msg);
} catch (NumberFormatException nfe) {
_listener.unknownMessageReceived(major, minor, params);
}
_listener.streamClosedReceived(result, id, msg);
} else {
_listener.unknownMessageReceived(major, minor, params);
}
@ -220,7 +209,6 @@ public class SAMReader {
String size = params.getProperty("SIZE");
if (id != null) {
try {
int idVal = Integer.parseInt(id);
int sizeVal = Integer.parseInt(size);
byte data[] = new byte[sizeVal];
@ -228,7 +216,7 @@ public class SAMReader {
if (read != sizeVal) {
_listener.unknownMessageReceived(major, minor, params);
} else {
_listener.streamDataReceived(idVal, data, 0, sizeVal);
_listener.streamDataReceived(id, data, 0, sizeVal);
}
} catch (NumberFormatException nfe) {
_listener.unknownMessageReceived(major, minor, params);

View File

@ -30,14 +30,12 @@ public class SAMStreamSend {
private final String _destFile;
private final String _dataFile;
private String _conOptions;
private Socket _samSocket;
private OutputStream _samOut;
private InputStream _samIn;
private SAMReader _reader;
private SAMReader _reader, _reader2;
private boolean _isV3;
private String _v3ID;
//private boolean _dead;
private final SAMEventHandler _eventHandler;
/** Connection id (Integer) to peer (Flooder) */
private final Map<Integer, Sender> _remotePeers;
private final Map<String, Sender> _remotePeers;
public static void main(String args[]) {
if (args.length < 4) {
@ -60,27 +58,48 @@ public class SAMStreamSend {
_destFile = destFile;
_dataFile = dataFile;
_conOptions = "";
_eventHandler = new SendEventHandler(_context);
_remotePeers = new HashMap<Integer,Sender>();
_remotePeers = new HashMap<String, Sender>();
}
public void startup(String version) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Starting up");
boolean ok = connect();
if (_log.shouldLog(Log.DEBUG))
_log.debug("Connected: " + ok);
if (ok) {
_reader = new SAMReader(_context, _samIn, _eventHandler);
try {
Socket sock = connect();
SAMEventHandler eventHandler = new SendEventHandler(_context);
_reader = new SAMReader(_context, sock.getInputStream(), eventHandler);
_reader.startReading();
if (_log.shouldLog(Log.DEBUG))
_log.debug("Reader created");
String ourDest = handshake(version);
OutputStream out = sock.getOutputStream();
String ourDest = handshake(out, version, true, eventHandler);
if (ourDest == null)
throw new IOException("handshake failed");
if (_log.shouldLog(Log.DEBUG))
_log.debug("Handshake complete. we are " + ourDest);
if (ourDest != null) {
send();
if (_isV3) {
Socket sock2 = connect();
eventHandler = new SendEventHandler(_context);
_reader2 = new SAMReader(_context, sock2.getInputStream(), eventHandler);
_reader2.startReading();
if (_log.shouldLog(Log.DEBUG))
_log.debug("Reader2 created");
out = sock2.getOutputStream();
String ok = handshake(out, version, false, eventHandler);
if (ok == null)
throw new IOException("2nd handshake failed");
if (_log.shouldLog(Log.DEBUG))
_log.debug("Handshake2 complete.");
}
if (ourDest != null) {
send(out, eventHandler);
}
} catch (IOException e) {
_log.error("Unable to connect to SAM at " + _samHost + ":" + _samPort, e);
if (_reader != null)
_reader.stopReading();
if (_reader2 != null)
_reader2.stopReading();
}
}
@ -88,10 +107,10 @@ public class SAMStreamSend {
public SendEventHandler(I2PAppContext ctx) { super(ctx); }
@Override
public void streamClosedReceived(String result, int id, String message) {
public void streamClosedReceived(String result, String id, String message) {
Sender sender = null;
synchronized (_remotePeers) {
sender = _remotePeers.remove(Integer.valueOf(id));
sender = _remotePeers.remove(id);
}
if (sender != null) {
sender.closed();
@ -103,53 +122,49 @@ public class SAMStreamSend {
}
}
private boolean connect() {
try {
_samSocket = new Socket(_samHost, Integer.parseInt(_samPort));
_samOut = _samSocket.getOutputStream();
_samIn = _samSocket.getInputStream();
return true;
} catch (Exception e) {
_log.error("Unable to connect to SAM at " + _samHost + ":" + _samPort, e);
return false;
}
private Socket connect() throws IOException {
return new Socket(_samHost, Integer.parseInt(_samPort));
}
private String handshake(String version) {
synchronized (_samOut) {
/** @return our b64 dest or null */
private String handshake(OutputStream samOut, String version, boolean isMaster, SAMEventHandler eventHandler) {
synchronized (samOut) {
try {
_samOut.write(("HELLO VERSION MIN=1.0 MAX=" + version + '\n').getBytes());
_samOut.flush();
samOut.write(("HELLO VERSION MIN=1.0 MAX=" + version + '\n').getBytes());
samOut.flush();
if (_log.shouldLog(Log.DEBUG))
_log.debug("Hello sent");
String hisVersion = _eventHandler.waitForHelloReply();
String hisVersion = eventHandler.waitForHelloReply();
if (_log.shouldLog(Log.DEBUG))
_log.debug("Hello reply found: " + hisVersion);
if (hisVersion == null)
throw new IOException("Hello failed");
boolean isV3 = VersionComparator.comp(hisVersion, "3") >= 0;
if (isV3) {
if (!isMaster)
return "OK";
_isV3 = VersionComparator.comp(hisVersion, "3") >= 0;
if (_isV3) {
byte[] id = new byte[5];
_context.random().nextBytes(id);
_conOptions = "ID=" + Base32.encode(id);
_v3ID = Base32.encode(id);
_conOptions = "ID=" + _v3ID;
}
String req = "SESSION CREATE STYLE=STREAM DESTINATION=TRANSIENT " + _conOptions + "\n";
_samOut.write(req.getBytes());
_samOut.flush();
samOut.write(req.getBytes());
samOut.flush();
if (_log.shouldLog(Log.DEBUG))
_log.debug("Session create sent");
boolean ok = _eventHandler.waitForSessionCreateReply();
boolean ok = eventHandler.waitForSessionCreateReply();
if (!ok)
throw new IOException("Session create failed");
if (_log.shouldLog(Log.DEBUG))
_log.debug("Session create reply found: " + ok);
req = "NAMING LOOKUP NAME=ME\n";
_samOut.write(req.getBytes());
_samOut.flush();
samOut.write(req.getBytes());
samOut.flush();
if (_log.shouldLog(Log.DEBUG))
_log.debug("Naming lookup sent");
String destination = _eventHandler.waitForNamingReply("ME");
String destination = eventHandler.waitForNamingReply("ME");
if (_log.shouldLog(Log.DEBUG))
_log.debug("Naming lookup reply found: " + destination);
if (destination == null) {
@ -166,8 +181,8 @@ public class SAMStreamSend {
}
}
private void send() {
Sender sender = new Sender();
private void send(OutputStream samOut, SAMEventHandler eventHandler) {
Sender sender = new Sender(samOut, eventHandler);
boolean ok = sender.openConnection();
if (ok) {
I2PAppThread t = new I2PAppThread(sender, "Sender");
@ -176,14 +191,26 @@ public class SAMStreamSend {
}
private class Sender implements Runnable {
private int _connectionId;
private final String _connectionId;
private String _remoteDestination;
private InputStream _in;
private volatile boolean _closed;
private long _started;
private long _totalSent;
private final OutputStream _samOut;
private final SAMEventHandler _eventHandler;
public Sender() {}
public Sender(OutputStream samOut, SAMEventHandler eventHandler) {
_samOut = samOut;
_eventHandler = eventHandler;
synchronized (_remotePeers) {
if (_v3ID != null)
_connectionId = _v3ID;
else
_connectionId = Integer.toString(_remotePeers.size() + 1);
_remotePeers.put(_connectionId, Sender.this);
}
}
public boolean openConnection() {
FileInputStream fin = null;
@ -193,10 +220,6 @@ public class SAMStreamSend {
int read = DataHelper.read(fin, dest);
_remoteDestination = new String(dest, 0, read);
synchronized (_remotePeers) {
_connectionId = _remotePeers.size() + 1;
_remotePeers.put(Integer.valueOf(_connectionId), Sender.this);
}
_context.statManager().createRateStat("send." + _connectionId + ".totalSent", "Data size sent", "swarm", new long[] { 30*1000, 60*1000, 5*60*1000 });
_context.statManager().createRateStat("send." + _connectionId + ".started", "When we start", "swarm", new long[] { 5*60*1000 });
@ -207,6 +230,10 @@ public class SAMStreamSend {
_samOut.write(msg);
_samOut.flush();
}
_log.debug("STREAM CONNECT sent, waiting for STREAM STATUS...");
boolean ok = _eventHandler.waitForStreamStatusReply();
if (!ok)
throw new IOException("STREAM CONNECT failed");
_in = new FileInputStream(_dataFile);
return true;
@ -222,7 +249,7 @@ public class SAMStreamSend {
}
}
public int getConnectionId() { return _connectionId; }
public String getConnectionId() { return _connectionId; }
public String getDestination() { return _remoteDestination; }
public void closed() {
@ -252,9 +279,11 @@ public class SAMStreamSend {
_log.debug("Sending " + read + " on " + _connectionId + " after " + (now-lastSend));
lastSend = now;
byte msg[] = ("STREAM SEND ID=" + _connectionId + " SIZE=" + read + "\n").getBytes();
synchronized (_samOut) {
_samOut.write(msg);
if (!_isV3) {
byte msg[] = ("STREAM SEND ID=" + _connectionId + " SIZE=" + read + "\n").getBytes();
_samOut.write(msg);
}
_samOut.write(data, 0, read);
_samOut.flush();
}
@ -268,14 +297,23 @@ public class SAMStreamSend {
}
}
byte msg[] = ("STREAM CLOSE ID=" + _connectionId + "\n").getBytes();
try {
synchronized (_samOut) {
_samOut.write(msg);
_samOut.flush();
if (_isV3) {
try {
_samOut.close();
} catch (IOException ioe) {
_log.info("Error closing", ioe);
}
} else {
byte msg[] = ("STREAM CLOSE ID=" + _connectionId + "\n").getBytes();
try {
synchronized (_samOut) {
_samOut.write(msg);
_samOut.flush();
_samOut.close();
}
} catch (IOException ioe) {
_log.info("Error closing", ioe);
}
} catch (IOException ioe) {
_log.info("Error closing", ioe);
}
closed();
@ -283,6 +321,8 @@ public class SAMStreamSend {
_log.debug("Runner exiting");
if (toSend != _totalSent)
_log.error("Only sent " + _totalSent + " of " + toSend + " bytes");
if (_reader2 != null)
_reader2.stopReading();
// stop the reader, since we're only doing this once for testing
// you wouldn't do this in a real application
_reader.stopReading();

View File

@ -31,14 +31,12 @@ public class SAMStreamSink {
private final String _destFile;
private final String _sinkDir;
private String _conOptions;
private Socket _samSocket;
private OutputStream _samOut;
private InputStream _samIn;
private SAMReader _reader;
private boolean _isV3;
//private boolean _dead;
private final SAMEventHandler _eventHandler;
/** Connection id (Integer) to peer (Flooder) */
private final Map<Integer, Sink> _remotePeers;
private final Map<String, Sink> _remotePeers;
public static void main(String args[]) {
if (args.length < 4) {
@ -61,21 +59,20 @@ public class SAMStreamSink {
_sinkDir = sinkDir;
_conOptions = "";
_eventHandler = new SinkEventHandler(_context);
_remotePeers = new HashMap<Integer,Sink>();
_remotePeers = new HashMap<String, Sink>();
}
public void startup(String version) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Starting up");
boolean ok = connect();
if (_log.shouldLog(Log.DEBUG))
_log.debug("Connected: " + ok);
if (ok) {
_reader = new SAMReader(_context, _samIn, _eventHandler);
try {
Socket sock = connect();
_reader = new SAMReader(_context, sock.getInputStream(), _eventHandler);
_reader.startReading();
if (_log.shouldLog(Log.DEBUG))
_log.debug("Reader created");
String ourDest = handshake(version);
OutputStream out = sock.getOutputStream();
String ourDest = handshake(out, version, true);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Handshake complete. we are " + ourDest);
if (ourDest != null) {
@ -84,6 +81,8 @@ public class SAMStreamSink {
} else {
_reader.stopReading();
}
} catch (IOException e) {
_log.error("Unable to connect to SAM at " + _samHost + ":" + _samPort, e);
}
}
@ -92,10 +91,10 @@ public class SAMStreamSink {
public SinkEventHandler(I2PAppContext ctx) { super(ctx); }
@Override
public void streamClosedReceived(String result, int id, String message) {
public void streamClosedReceived(String result, String id, String message) {
Sink sink = null;
synchronized (_remotePeers) {
sink = _remotePeers.remove(Integer.valueOf(id));
sink = _remotePeers.remove(id);
}
if (sink != null) {
sink.closed();
@ -107,10 +106,10 @@ public class SAMStreamSink {
}
@Override
public void streamDataReceived(int id, byte data[], int offset, int length) {
public void streamDataReceived(String id, byte data[], int offset, int length) {
Sink sink = null;
synchronized (_remotePeers) {
sink = _remotePeers.get(Integer.valueOf(id));
sink = _remotePeers.get(id);
}
if (sink != null) {
sink.received(data, offset, length);
@ -120,14 +119,14 @@ public class SAMStreamSink {
}
@Override
public void streamConnectedReceived(String dest, int id) {
public void streamConnectedReceived(String dest, String id) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Connection " + id + " received from " + dest);
try {
Sink sink = new Sink(id, dest);
synchronized (_remotePeers) {
_remotePeers.put(Integer.valueOf(id), sink);
_remotePeers.put(id, sink);
}
} catch (IOException ioe) {
_log.error("Error creating a new sink", ioe);
@ -135,23 +134,16 @@ public class SAMStreamSink {
}
}
private boolean connect() {
try {
_samSocket = new Socket(_samHost, Integer.parseInt(_samPort));
_samOut = _samSocket.getOutputStream();
_samIn = _samSocket.getInputStream();
return true;
} catch (Exception e) {
_log.error("Unable to connect to SAM at " + _samHost + ":" + _samPort, e);
return false;
}
private Socket connect() throws IOException {
return new Socket(_samHost, Integer.parseInt(_samPort));
}
private String handshake(String version) {
synchronized (_samOut) {
/** @return our b64 dest or null */
private String handshake(OutputStream samOut, String version, boolean isMaster) {
synchronized (samOut) {
try {
_samOut.write(("HELLO VERSION MIN=1.0 MAX=" + version + '\n').getBytes());
_samOut.flush();
samOut.write(("HELLO VERSION MIN=1.0 MAX=" + version + '\n').getBytes());
samOut.flush();
if (_log.shouldLog(Log.DEBUG))
_log.debug("Hello sent");
String hisVersion = _eventHandler.waitForHelloReply();
@ -159,9 +151,9 @@ public class SAMStreamSink {
_log.debug("Hello reply found: " + hisVersion);
if (hisVersion == null)
throw new IOException("Hello failed");
boolean isV3 = VersionComparator.comp(hisVersion, "3") >= 0;
_isV3 = VersionComparator.comp(hisVersion, "3") >= 0;
String dest;
if (isV3) {
if (_isV3) {
// we use the filename as the name in sam.keys
// and read it in ourselves
File keys = new File("sam.keys");
@ -183,17 +175,19 @@ public class SAMStreamSink {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Requesting new transient destination");
}
byte[] id = new byte[5];
_context.random().nextBytes(id);
_conOptions = "ID=" + Base32.encode(id);
if (isMaster) {
byte[] id = new byte[5];
_context.random().nextBytes(id);
_conOptions = "ID=" + Base32.encode(id);
}
} else {
// we use the filename as the name in sam.keys
// and give it to the SAM server
dest = _destFile;
}
String req = "SESSION CREATE STYLE=STREAM DESTINATION=" + dest + " " + _conOptions + "\n";
_samOut.write(req.getBytes());
_samOut.flush();
samOut.write(req.getBytes());
samOut.flush();
if (_log.shouldLog(Log.DEBUG))
_log.debug("Session create sent");
boolean ok = _eventHandler.waitForSessionCreateReply();
@ -203,8 +197,8 @@ public class SAMStreamSink {
_log.debug("Session create reply found: " + ok);
req = "NAMING LOOKUP NAME=ME\n";
_samOut.write(req.getBytes());
_samOut.flush();
samOut.write(req.getBytes());
samOut.flush();
if (_log.shouldLog(Log.DEBUG))
_log.debug("Naming lookup sent");
String destination = _eventHandler.waitForNamingReply("ME");
@ -227,11 +221,13 @@ public class SAMStreamSink {
private boolean writeDest(String dest) {
File f = new File(_destFile);
/*
if (f.exists()) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Destination file exists, not overwriting:" + _destFile);
_log.debug("Destination file exists, not overwriting: " + _destFile);
return false;
}
*/
FileOutputStream fos = null;
try {
fos = new FileOutputStream(f);
@ -248,14 +244,14 @@ public class SAMStreamSink {
}
private class Sink {
private final int _connectionId;
private final String _connectionId;
private final String _remoteDestination;
private volatile boolean _closed;
private final long _started;
private long _lastReceivedOn;
private final OutputStream _out;
public Sink(int conId, String remDest) throws IOException {
public Sink(String conId, String remDest) throws IOException {
_connectionId = conId;
_remoteDestination = remDest;
_closed = false;
@ -273,7 +269,7 @@ public class SAMStreamSink {
_started = _context.clock().now();
}
public int getConnectionId() { return _connectionId; }
public String getConnectionId() { return _connectionId; }
public String getDestination() { return _remoteDestination; }
public void closed() {