2004-12-08 jrandom
* Revised the buffering when reading from the SAM client and writing to the stream. Also added a thread (sigh) so we don't block the SAM client from giving us more messages for abnormally long periods of time. * Display the router version in the logs on startup (oft requested) * Fix a race during the closing of a messageOutputStream
This commit is contained in:
@ -15,8 +15,10 @@ import java.io.InterruptedIOException;
|
|||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.net.ConnectException;
|
import java.net.ConnectException;
|
||||||
import java.net.NoRouteToHostException;
|
import java.net.NoRouteToHostException;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
@ -29,6 +31,7 @@ import net.i2p.client.streaming.I2PSocketManagerFactory;
|
|||||||
import net.i2p.client.streaming.I2PSocketOptions;
|
import net.i2p.client.streaming.I2PSocketOptions;
|
||||||
import net.i2p.data.Base64;
|
import net.i2p.data.Base64;
|
||||||
import net.i2p.data.ByteArray;
|
import net.i2p.data.ByteArray;
|
||||||
|
import net.i2p.data.DataHelper;
|
||||||
import net.i2p.data.DataFormatException;
|
import net.i2p.data.DataFormatException;
|
||||||
import net.i2p.data.Destination;
|
import net.i2p.data.Destination;
|
||||||
import net.i2p.util.ByteCache;
|
import net.i2p.util.ByteCache;
|
||||||
@ -53,7 +56,10 @@ public class SAMStreamSession {
|
|||||||
private I2PSocketManager socketMgr = null;
|
private I2PSocketManager socketMgr = null;
|
||||||
|
|
||||||
private Object handlersMapLock = new Object();
|
private Object handlersMapLock = new Object();
|
||||||
|
/** stream id (Long) to SAMStreamSessionSocketReader */
|
||||||
private HashMap handlersMap = new HashMap();
|
private HashMap handlersMap = new HashMap();
|
||||||
|
/** stream id (Long) to StreamSender */
|
||||||
|
private HashMap sendersMap = new HashMap();
|
||||||
|
|
||||||
private Object idLock = new Object();
|
private Object idLock = new Object();
|
||||||
private int lastNegativeId = 0;
|
private int lastNegativeId = 0;
|
||||||
@ -61,6 +67,14 @@ public class SAMStreamSession {
|
|||||||
// Can we create outgoing connections?
|
// Can we create outgoing connections?
|
||||||
private boolean canCreate = false;
|
private boolean canCreate = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* should we flush every time we get a STREAM SEND, or leave that up to
|
||||||
|
* the streaming lib to decide?
|
||||||
|
*/
|
||||||
|
private boolean forceFlush = false;
|
||||||
|
public static String PROP_FORCE_FLUSH = "sam.forceFlush";
|
||||||
|
public static String DEFAULT_FORCE_FLUSH = "false";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new SAM STREAM session.
|
* Create a new SAM STREAM session.
|
||||||
*
|
*
|
||||||
@ -109,9 +123,6 @@ public class SAMStreamSession {
|
|||||||
} catch (NumberFormatException nfe) {
|
} catch (NumberFormatException nfe) {
|
||||||
throw new SAMException("Invalid I2CP port specified [" + port + "]");
|
throw new SAMException("Invalid I2CP port specified [" + port + "]");
|
||||||
}
|
}
|
||||||
// streams MUST be mode=guaranteed (though i think the socket manager
|
|
||||||
// enforces this anyway...
|
|
||||||
allprops.setProperty(I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_GUARANTEED);
|
|
||||||
|
|
||||||
_log.debug("Creating I2PSocketManager...");
|
_log.debug("Creating I2PSocketManager...");
|
||||||
socketMgr = I2PSocketManagerFactory.createManager(destStream,
|
socketMgr = I2PSocketManagerFactory.createManager(destStream,
|
||||||
@ -122,6 +133,8 @@ public class SAMStreamSession {
|
|||||||
throw new SAMException("Error creating I2PSocketManager");
|
throw new SAMException("Error creating I2PSocketManager");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
forceFlush = Boolean.valueOf(allprops.getProperty(PROP_FORCE_FLUSH, DEFAULT_FORCE_FLUSH)).booleanValue();
|
||||||
|
|
||||||
boolean canReceive = false;
|
boolean canReceive = false;
|
||||||
if (dir.equals("BOTH")) {
|
if (dir.equals("BOTH")) {
|
||||||
canCreate = true;
|
canCreate = true;
|
||||||
@ -199,14 +212,14 @@ public class SAMStreamSession {
|
|||||||
*
|
*
|
||||||
* @param data Bytes to be sent
|
* @param data Bytes to be sent
|
||||||
*
|
*
|
||||||
* @return True if the data was sent, false otherwise
|
* @return True if the data was queued for sending, false otherwise
|
||||||
*/
|
*/
|
||||||
public boolean sendBytes(int id, InputStream in, int size) throws IOException {
|
public boolean sendBytes(int id, InputStream in, int size) throws IOException {
|
||||||
SAMStreamSessionSocketHandler handler = getSocketHandler(id);
|
StreamSender sender = getSender(id);
|
||||||
|
|
||||||
if (handler == null) {
|
if (sender == null) {
|
||||||
if (_log.shouldLog(Log.WARN))
|
if (_log.shouldLog(Log.WARN))
|
||||||
_log.error("Trying to send bytes through inexistent handler " +id);
|
_log.warn("Trying to send bytes through nonexistent handler " +id);
|
||||||
// even though it failed, we need to read those bytes!
|
// even though it failed, we need to read those bytes!
|
||||||
for (int i = 0; i < size; i++) {
|
for (int i = 0; i < size; i++) {
|
||||||
int c = in.read();
|
int c = in.read();
|
||||||
@ -216,7 +229,8 @@ public class SAMStreamSession {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return handler.sendBytes(in, size);
|
sender.sendBytes(in, size);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -256,13 +270,15 @@ public class SAMStreamSession {
|
|||||||
* @return An id associated to the socket handler
|
* @return An id associated to the socket handler
|
||||||
*/
|
*/
|
||||||
private int createSocketHandler(I2PSocket s, int id) {
|
private int createSocketHandler(I2PSocket s, int id) {
|
||||||
SAMStreamSessionSocketHandler handler;
|
SAMStreamSessionSocketReader reader = null;
|
||||||
|
StreamSender sender = null;
|
||||||
if (id == 0) {
|
if (id == 0) {
|
||||||
id = createUniqueId();
|
id = createUniqueId();
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
handler = new SAMStreamSessionSocketHandler(s, id);
|
reader = new SAMStreamSessionSocketReader(s, id);
|
||||||
|
sender = new StreamSender(s, id);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
_log.error("IOException when creating SAM STREAM session socket handler", e);
|
_log.error("IOException when creating SAM STREAM session socket handler", e);
|
||||||
recv.stopStreamReceiving();
|
recv.stopStreamReceiving();
|
||||||
@ -270,10 +286,13 @@ public class SAMStreamSession {
|
|||||||
}
|
}
|
||||||
|
|
||||||
synchronized (handlersMapLock) {
|
synchronized (handlersMapLock) {
|
||||||
handlersMap.put(new Integer(id), handler);
|
handlersMap.put(new Integer(id), reader);
|
||||||
|
sendersMap.put(new Integer(id), sender);
|
||||||
}
|
}
|
||||||
|
|
||||||
I2PThread t = new I2PThread(handler, "SAMStreamSessionSocketHandler");
|
I2PThread t = new I2PThread(reader, "SAMReader" + id);
|
||||||
|
t.start();
|
||||||
|
t = new I2PThread(sender, "SAMSender" + id);
|
||||||
t.start();
|
t.start();
|
||||||
|
|
||||||
return id;
|
return id;
|
||||||
@ -291,9 +310,14 @@ public class SAMStreamSession {
|
|||||||
*
|
*
|
||||||
* @param id Handler id
|
* @param id Handler id
|
||||||
*/
|
*/
|
||||||
private SAMStreamSessionSocketHandler getSocketHandler(int id) {
|
private SAMStreamSessionSocketReader getSocketReader(int id) {
|
||||||
synchronized (handlersMapLock) {
|
synchronized (handlersMapLock) {
|
||||||
return (SAMStreamSessionSocketHandler)handlersMap.get(new Integer(id));
|
return (SAMStreamSessionSocketReader)handlersMap.get(new Integer(id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private StreamSender getSender(int id) {
|
||||||
|
synchronized (handlersMapLock) {
|
||||||
|
return (StreamSender)sendersMap.get(new Integer(id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -314,18 +338,19 @@ public class SAMStreamSession {
|
|||||||
* @param id Handler id to be removed
|
* @param id Handler id to be removed
|
||||||
*/
|
*/
|
||||||
private void removeSocketHandler(int id) {
|
private void removeSocketHandler(int id) {
|
||||||
SAMStreamSessionSocketHandler removed;
|
SAMStreamSessionSocketReader reader = null;
|
||||||
|
StreamSender sender = null;
|
||||||
|
|
||||||
synchronized (handlersMapLock) {
|
synchronized (handlersMapLock) {
|
||||||
removed = (SAMStreamSessionSocketHandler)handlersMap.remove(new Integer(id));
|
reader = (SAMStreamSessionSocketReader)handlersMap.remove(new Integer(id));
|
||||||
|
sender = (StreamSender)sendersMap.remove(new Integer(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (removed == null) {
|
if (reader != null)
|
||||||
// ignore - likely the socket handler was already removed by removeAllSocketHandlers
|
reader.stopRunning();
|
||||||
} else {
|
if (sender != null)
|
||||||
removed.stopRunning();
|
sender.stopRunning();
|
||||||
_log.debug("Removed SAM STREAM session socket handler " + id);
|
_log.debug("Removed SAM STREAM session socket handler " + id);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -344,9 +369,11 @@ public class SAMStreamSession {
|
|||||||
|
|
||||||
while (iter.hasNext()) {
|
while (iter.hasNext()) {
|
||||||
id = (Integer)iter.next();
|
id = (Integer)iter.next();
|
||||||
((SAMStreamSessionSocketHandler)handlersMap.get(id)).stopRunning();
|
((SAMStreamSessionSocketReader)handlersMap.get(id)).stopRunning();
|
||||||
|
((StreamSender)sendersMap.get(id)).stopRunning();
|
||||||
}
|
}
|
||||||
handlersMap.clear();
|
handlersMap.clear();
|
||||||
|
sendersMap.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -440,10 +467,9 @@ public class SAMStreamSession {
|
|||||||
*
|
*
|
||||||
* @author human
|
* @author human
|
||||||
*/
|
*/
|
||||||
public class SAMStreamSessionSocketHandler implements Runnable {
|
public class SAMStreamSessionSocketReader implements Runnable {
|
||||||
|
|
||||||
private I2PSocket i2pSocket = null;
|
private I2PSocket i2pSocket = null;
|
||||||
private OutputStream i2pSocketOS = null;
|
|
||||||
|
|
||||||
private Object runningLock = new Object();
|
private Object runningLock = new Object();
|
||||||
private boolean stillRunning = true;
|
private boolean stillRunning = true;
|
||||||
@ -451,72 +477,20 @@ public class SAMStreamSession {
|
|||||||
private int id;
|
private int id;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new SAM STREAM session socket handler
|
* Create a new SAM STREAM session socket reader
|
||||||
*
|
*
|
||||||
* @param s Socket to be handled
|
* @param s Socket to be handled
|
||||||
* @param id Unique id assigned to the handler
|
* @param id Unique id assigned to the handler
|
||||||
*/
|
*/
|
||||||
public SAMStreamSessionSocketHandler(I2PSocket s, int id) throws IOException {
|
public SAMStreamSessionSocketReader(I2PSocket s, int id) throws IOException {
|
||||||
_log.debug("Instantiating new SAM STREAM session socket handler");
|
_log.debug("Instantiating new SAM STREAM session socket handler");
|
||||||
|
|
||||||
i2pSocket = s;
|
i2pSocket = s;
|
||||||
i2pSocketOS = s.getOutputStream();
|
|
||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send bytes through the SAM STREAM session socket handler
|
* Stop a SAM STREAM session socket reader
|
||||||
*
|
|
||||||
* @param data Data to be sent
|
|
||||||
*
|
|
||||||
* @return True if data has been sent without errors, false otherwise
|
|
||||||
*/
|
|
||||||
public boolean sendBytes(InputStream in, int size) throws IOException {
|
|
||||||
if (_log.shouldLog(Log.DEBUG)) {
|
|
||||||
_log.debug("Handler " + id + ": sending " + size
|
|
||||||
+ " bytes");
|
|
||||||
}
|
|
||||||
ByteCache cache = ByteCache.getInstance(1024, 4);
|
|
||||||
ByteArray ba = cache.acquire();
|
|
||||||
int remaining = size;
|
|
||||||
try {
|
|
||||||
byte buf[] = ba.getData();
|
|
||||||
while (remaining > 0) {
|
|
||||||
int read = in.read(buf, 0, remaining > buf.length ? buf.length : remaining);
|
|
||||||
if (read == -1) {
|
|
||||||
throw new IOException("Insufficient data from the SAM client (" + remaining + "/" + size + ")");
|
|
||||||
} else if (read > 0) {
|
|
||||||
remaining -= read;
|
|
||||||
try {
|
|
||||||
i2pSocketOS.write(buf, 0, read);
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
// ok, the stream failed, but the SAM client didn't
|
|
||||||
if (_log.shouldLog(Log.WARN))
|
|
||||||
_log.warn("Stream failed", ioe);
|
|
||||||
|
|
||||||
removeSocketHandler(id);
|
|
||||||
|
|
||||||
// emtpy the remaining payload so we can continue
|
|
||||||
for (int i = remaining; i > 0; i--) {
|
|
||||||
int c = in.read();
|
|
||||||
if (c == -1)
|
|
||||||
throw new IOException("Stream closed, but the SAM client didn't send enough anyway ("
|
|
||||||
+ i + " remaining)");
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
cache.release(ba);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stop a SAM STREAM session socket handler
|
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public void stopRunning() {
|
public void stopRunning() {
|
||||||
@ -575,4 +549,99 @@ public class SAMStreamSession {
|
|||||||
_log.debug("Shutting down SAM STREAM session socket handler " +id);
|
_log.debug("Shutting down SAM STREAM session socket handler " +id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lets us push data through the stream without blocking, (even after exceeding
|
||||||
|
* the I2PSocket's buffer)
|
||||||
|
*/
|
||||||
|
private class StreamSender implements Runnable {
|
||||||
|
private List _data;
|
||||||
|
private int _id;
|
||||||
|
private ByteCache _cache;
|
||||||
|
private OutputStream _out = null;
|
||||||
|
private boolean _stillRunning;
|
||||||
|
|
||||||
|
public StreamSender(I2PSocket s, int id) throws IOException {
|
||||||
|
_data = new ArrayList(1);
|
||||||
|
_id = id;
|
||||||
|
_cache = ByteCache.getInstance(4, 32*1024);
|
||||||
|
_out = s.getOutputStream();
|
||||||
|
_stillRunning = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send bytes through the SAM STREAM session socket sender
|
||||||
|
*
|
||||||
|
* @param data Data to be sent
|
||||||
|
*
|
||||||
|
* @throws IOException if the client didnt provide enough data
|
||||||
|
*/
|
||||||
|
public void sendBytes(InputStream in, int size) throws IOException {
|
||||||
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
|
_log.debug("Handler " + _id + ": sending " + size + " bytes");
|
||||||
|
|
||||||
|
ByteArray ba = _cache.acquire();
|
||||||
|
int read = DataHelper.read(in, ba.getData(), 0, size);
|
||||||
|
if (read != size)
|
||||||
|
throw new IOException("Insufficient data from the SAM client (" + read + "/" + size + ")");
|
||||||
|
|
||||||
|
ba.setValid(read);
|
||||||
|
synchronized (_data) {
|
||||||
|
_data.add(ba);
|
||||||
|
_data.notifyAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop a SAM STREAM session socket sender
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public void stopRunning() {
|
||||||
|
_log.debug("stopRunning() invoked on socket sender " + _id);
|
||||||
|
_stillRunning = false;
|
||||||
|
synchronized (_data) {
|
||||||
|
_data.clear();
|
||||||
|
_data.notifyAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run() {
|
||||||
|
ByteArray data = null;
|
||||||
|
while (_stillRunning) {
|
||||||
|
data = null;
|
||||||
|
try {
|
||||||
|
synchronized (_data) {
|
||||||
|
if (_data.size() > 0)
|
||||||
|
data = (ByteArray)_data.remove(0);
|
||||||
|
else
|
||||||
|
_data.wait(5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data != null) {
|
||||||
|
try {
|
||||||
|
_out.write(data.getData(), 0, data.getValid());
|
||||||
|
if (forceFlush) {
|
||||||
|
// i dont like doing this, but it clears the buffer issues
|
||||||
|
_out.flush();
|
||||||
|
}
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
// ok, the stream failed, but the SAM client didn't
|
||||||
|
if (_log.shouldLog(Log.WARN))
|
||||||
|
_log.warn("Stream failed", ioe);
|
||||||
|
|
||||||
|
removeSocketHandler(_id);
|
||||||
|
stopRunning();
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
_cache.release(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (InterruptedException ie) {}
|
||||||
|
}
|
||||||
|
synchronized (_data) {
|
||||||
|
_data.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -394,15 +394,12 @@ public class Connection {
|
|||||||
if (_socket != null)
|
if (_socket != null)
|
||||||
_socket.destroy();
|
_socket.destroy();
|
||||||
_socket = null;
|
_socket = null;
|
||||||
_inputStream = null;
|
|
||||||
if (_outputStream != null)
|
if (_outputStream != null)
|
||||||
_outputStream.destroy();
|
_outputStream.destroy();
|
||||||
_outputStream = null;
|
|
||||||
_outboundQueue = null;
|
|
||||||
if (_receiver != null)
|
if (_receiver != null)
|
||||||
_receiver.destroy();
|
_receiver.destroy();
|
||||||
if (_activityTimer != null)
|
if (_activityTimer != null)
|
||||||
SimpleTimer.getInstance().addEvent(_activityTimer, 1);
|
SimpleTimer.getInstance().removeEvent(_activityTimer);
|
||||||
_activityTimer = null;
|
_activityTimer = null;
|
||||||
|
|
||||||
if (!_disconnectScheduled) {
|
if (!_disconnectScheduled) {
|
||||||
|
@ -155,12 +155,13 @@ public class MessageOutputStream extends OutputStream {
|
|||||||
}
|
}
|
||||||
public void timeReached() {
|
public void timeReached() {
|
||||||
_enqueued = false;
|
_enqueued = false;
|
||||||
|
DataReceiver rec = _dataReceiver;
|
||||||
long timeLeft = (_lastBuffered + _passiveFlushDelay - _context.clock().now());
|
long timeLeft = (_lastBuffered + _passiveFlushDelay - _context.clock().now());
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug("flusher time reached: left = " + timeLeft);
|
_log.debug("flusher time reached: left = " + timeLeft);
|
||||||
if (timeLeft > 0)
|
if (timeLeft > 0)
|
||||||
enqueue();
|
enqueue();
|
||||||
else if (_dataReceiver.writeInProcess())
|
else if ( (rec != null) && (rec.writeInProcess()) )
|
||||||
enqueue(); // don't passive flush if there is a write being done (unacked outbound)
|
enqueue(); // don't passive flush if there is a write being done (unacked outbound)
|
||||||
else
|
else
|
||||||
doFlush();
|
doFlush();
|
||||||
|
@ -18,6 +18,7 @@ import java.io.Serializable;
|
|||||||
*/
|
*/
|
||||||
public class ByteArray implements Serializable, Comparable {
|
public class ByteArray implements Serializable, Comparable {
|
||||||
private byte[] _data;
|
private byte[] _data;
|
||||||
|
private int _valid;
|
||||||
|
|
||||||
public ByteArray() {
|
public ByteArray() {
|
||||||
this(null);
|
this(null);
|
||||||
@ -25,6 +26,7 @@ public class ByteArray implements Serializable, Comparable {
|
|||||||
|
|
||||||
public ByteArray(byte[] data) {
|
public ByteArray(byte[] data) {
|
||||||
_data = data;
|
_data = data;
|
||||||
|
_valid = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final byte[] getData() {
|
public final byte[] getData() {
|
||||||
@ -34,6 +36,14 @@ public class ByteArray implements Serializable, Comparable {
|
|||||||
public void setData(byte[] data) {
|
public void setData(byte[] data) {
|
||||||
_data = data;
|
_data = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* how many of the bytes in the array are 'valid'?
|
||||||
|
* this property does not necessarily have meaning for all byte
|
||||||
|
* arrays.
|
||||||
|
*/
|
||||||
|
public final int getValid() { return _valid; }
|
||||||
|
public final void setValid(int valid) { _valid = valid; }
|
||||||
|
|
||||||
public final boolean equals(Object o) {
|
public final boolean equals(Object o) {
|
||||||
if (o == null) return false;
|
if (o == null) return false;
|
||||||
|
@ -658,12 +658,14 @@ public class DataHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static int read(InputStream in, byte target[]) throws IOException {
|
public static int read(InputStream in, byte target[]) throws IOException {
|
||||||
int cur = 0;
|
return read(in, target, 0, target.length);
|
||||||
while (cur < target.length) {
|
}
|
||||||
int numRead = in.read(target, cur, target.length - cur);
|
public static int read(InputStream in, byte target[], int offset, int length) throws IOException {
|
||||||
|
int cur = offset;
|
||||||
|
while (cur < length) {
|
||||||
|
int numRead = in.read(target, cur, length - cur);
|
||||||
if (numRead == -1) {
|
if (numRead == -1) {
|
||||||
if (cur == 0) return -1; // throw new EOFException("EOF Encountered during reading");
|
if (cur == offset) return -1; // throw new EOFException("EOF Encountered during reading");
|
||||||
|
|
||||||
return cur;
|
return cur;
|
||||||
}
|
}
|
||||||
cur += numRead;
|
cur += numRead;
|
||||||
@ -671,6 +673,7 @@ public class DataHelper {
|
|||||||
return cur;
|
return cur;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read a newline delimited line from the stream, returning the line (without
|
* Read a newline delimited line from the stream, returning the line (without
|
||||||
* the newline), or null if EOF reached before the newline was found
|
* the newline), or null if EOF reached before the newline was found
|
||||||
|
10
history.txt
10
history.txt
@ -1,4 +1,12 @@
|
|||||||
$Id: history.txt,v 1.99 2004/12/06 00:03:58 jrandom Exp $
|
$Id: history.txt,v 1.100 2004/12/06 20:09:20 jrandom Exp $
|
||||||
|
|
||||||
|
2004-12-08 jrandom
|
||||||
|
* Revised the buffering when reading from the SAM client and writing
|
||||||
|
to the stream. Also added a thread (sigh) so we don't block the
|
||||||
|
SAM client from giving us more messages for abnormally long periods
|
||||||
|
of time.
|
||||||
|
* Display the router version in the logs on startup (oft requested)
|
||||||
|
* Fix a race during the closing of a messageOutputStream
|
||||||
|
|
||||||
2004-12-06 jrandom
|
2004-12-06 jrandom
|
||||||
* Don't do a 'passive flush' while there are already outbound messages
|
* Don't do a 'passive flush' while there are already outbound messages
|
||||||
|
@ -792,6 +792,8 @@ public class Router {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String args[]) {
|
public static void main(String args[]) {
|
||||||
|
System.out.println("Starting I2P " + RouterVersion.VERSION + "-" + RouterVersion.BUILD);
|
||||||
|
System.out.println(RouterVersion.ID);
|
||||||
installUpdates();
|
installUpdates();
|
||||||
verifyWrapperConfig();
|
verifyWrapperConfig();
|
||||||
Router r = new Router();
|
Router r = new Router();
|
||||||
|
@ -15,9 +15,9 @@ import net.i2p.CoreVersion;
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class RouterVersion {
|
public class RouterVersion {
|
||||||
public final static String ID = "$Revision: 1.104 $ $Date: 2004/12/06 00:03:57 $";
|
public final static String ID = "$Revision: 1.105 $ $Date: 2004/12/06 20:09:20 $";
|
||||||
public final static String VERSION = "0.4.2.2";
|
public final static String VERSION = "0.4.2.2";
|
||||||
public final static long BUILD = 8;
|
public final static long BUILD = 9;
|
||||||
public static void main(String args[]) {
|
public static void main(String args[]) {
|
||||||
System.out.println("I2P Router version: " + VERSION);
|
System.out.println("I2P Router version: " + VERSION);
|
||||||
System.out.println("Router ID: " + RouterVersion.ID);
|
System.out.println("Router ID: " + RouterVersion.ID);
|
||||||
|
Reference in New Issue
Block a user