I2P Address: [http://git.idk.i2p]

Skip to content
Snippets Groups Projects
Commit 37791e83 authored by zzz's avatar zzz
Browse files

Merge branch 'i2ptunnel-keepalive-util' into 'master'

i2ptunnel: Prep for keepalive

See merge request !166
parents 20cbd0ee e5b9f9f1
No related branches found
No related tags found
1 merge request!166i2ptunnel: Prep for keepalive
Pipeline #1468 passed
......@@ -16,6 +16,7 @@ import java.util.Locale;
import net.i2p.I2PAppContext;
import net.i2p.data.ByteArray;
import net.i2p.data.DataHelper;
import net.i2p.i2ptunnel.util.GunzipOutputStream;
import net.i2p.util.ByteCache;
import net.i2p.util.Log;
......
......@@ -28,7 +28,7 @@ import net.i2p.data.Destination;
import net.i2p.data.PrivateKey;
import net.i2p.data.PublicKey;
import net.i2p.data.SigningPublicKey;
import net.i2p.i2ptunnel.GunzipOutputStream;
import net.i2p.i2ptunnel.util.GunzipOutputStream;
import net.i2p.i2ptunnel.I2PTunnelHTTPClientBase;
import net.i2p.util.FileUtil;
import net.i2p.util.PortMapper;
......
package net.i2p.i2ptunnel.util;
import java.io.EOFException;
import java.io.IOException;
import java.io.OutputStream;
/**
* An OutputStream that limits how many bytes are written
*
* @since 0.9.62
*/
public class ByteLimitOutputStream extends LimitOutputStream {
private final long _limit;
private long _count;
/**
* @param limit greater than zero
*/
public ByteLimitOutputStream(OutputStream out, DoneCallback done, long limit) {
super(out, done);
if (limit <= 0)
throw new IllegalArgumentException();
_limit = limit;
}
@Override
public void write(byte src[], int off, int len) throws IOException {
if (len == 0)
return;
if (_isDone)
throw new EOFException("done");
long togo = _limit - _count;
boolean last = len >= togo;
if (last)
len = (int) togo;
super.write(src, off, len);
_count += len;
if (last)
setDone();
}
/*
public static void main(String[] args) throws Exception {
if (args.length != 1) {
System.err.println("Usage: ByteLimitOutputStream length < in > out");
System.exit(1);
}
Test test = new Test();
long limit = Long.parseLong(args[0]);
test.test(limit);
}
static class Test implements DoneCallback {
private boolean run = true;
public void test(long limit) throws Exception {
LimitOutputStream lout = new ByteLimitOutputStream(System.out, this, limit);
final byte buf[] = new byte[4096];
try {
int read;
while (run && (read = System.in.read(buf)) != -1) {
lout.write(buf, 0, read);
}
} finally {
lout.close();
}
}
public void streamDone() {
System.err.println("Done");
run = false;
}
}
*/
}
package net.i2p.i2ptunnel.util;
import java.io.EOFException;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import net.i2p.data.DataHelper;
/**
* Simple stream for checking and optionally removing RFC2616 chunked encoding to the output.
*
* @since 0.9.62
*/
public class DechunkedOutputStream extends LimitOutputStream {
private final boolean _strip;
private State _state = State.LEN;
// During main part, how much is remaining in the chunk
// During the trailer, counts the trailer header size
private int _remaining;
private static final byte[] CRLF = DataHelper.getASCII("\r\n");
private enum State { LEN, CR, LF, DATA, TRAILER, DONE }
public DechunkedOutputStream(OutputStream raw, DoneCallback callback, boolean strip) {
super(raw, callback);
_strip = strip;
}
@Override
public void write(byte buf[], int off, int len) throws IOException {
if (len <= 0)
return;
for (int i = 0; i < len; i++) {
// _state is what we are expecting next
//System.err.println("State: " + _state + " i=" + i + " len=" + len + " remaining=" + _remaining + " char=0x" + Integer.toHexString(buf[off + i] & 0xff));
switch (_state) {
// collect chunk len and possible ';' then wait for extension if any and CRLF
case LEN: {
int c = buf[off + i] & 0xff;
if (c >= '0' && c <= '9') {
if (_remaining >= 0x8000000)
throw new IOException("Chunk length too big");
_remaining <<= 4;
_remaining |= c - '0';
} else if (c >= 'a' && c <= 'f') {
if (_remaining >= 0x800000)
throw new IOException("Chunk length too big");
_remaining <<= 4;
_remaining |= 10 + c - 'a';
} else if (c >= 'A' && c <= 'F') {
if (_remaining >= 0x800000)
throw new IOException("Chunk length too big");
_remaining <<= 4;
_remaining |= 10 + c - 'A';
} else if (c == ';') {
_state = State.CR;
} else if (c == '\r') {
_state = State.LF;
} else if (c == '\n') {
if (_remaining > 0)
_state = State.DATA;
else
_state = State.TRAILER;
} else {
throw new IOException("Unexpected length char 0x" + Integer.toHexString(c));
}
if (!_strip)
out.write(buf, off + i, 1);
break;
}
// collect any chunk extension and CR then wait for LF
case CR: {
int c = buf[off + i] & 0xff;
if (c == '\r') {
_state = State.LF;
} else if (c == '\n') {
if (_remaining > 0)
_state = State.DATA;
else
_state = State.TRAILER;
} else {
// chunk extension between the ';' and the CR
}
if (!_strip)
out.write(buf, off + i, 1);
break;
}
// collect LF then wait for DATA
case LF: {
int c = buf[off + i] & 0xff;
if (c == '\n') {
if (_remaining > 0)
_state = State.DATA;
else
_state = State.TRAILER;
} else {
throw new IOException("no LF after CR");
}
if (!_strip)
out.write(buf, off + i, 1);
break;
}
// collect DATA then wait for LEN
case DATA: {
int towrite = Math.min(_remaining, len - i);
out.write(buf, off + i, towrite);
// loop will increment
i += towrite - 1;
_remaining -= towrite;
if (_remaining <= 0)
_state = State.LEN;
break;
}
// swallow and discard the Trailer headers until we find a plain CRLF
// we reuse _remaining here to count the size of the header
case TRAILER: {
int c = buf[off + i] & 0xff;
if (c == '\r') {
// stay here
} else if (c == '\n') {
if (_remaining <= 0) {
// that's it!
if (!_strip)
out.write(buf, off + i, 1);
_state = State.DONE;
setDone();
return;
} else {
// stay here
_remaining = 0;
}
} else {
_remaining++;
}
if (!_strip)
out.write(buf, off + i, 1);
break;
}
case DONE: {
throw new EOFException((len - i) + " extra bytes written after chunking done");
}
}
}
}
/*
public static void main(String[] args) throws Exception {
if (args.length != 1) {
System.err.println("Usage: DechunkedOutputStream true/false < in > out");
System.exit(1);
}
Test test = new Test();
boolean strip = Boolean.parseBoolean(args[0]);
test.test(strip);
}
static class Test implements DoneCallback {
private boolean run = true;
public void test(boolean strip) throws Exception {
LimitOutputStream cout = new DechunkedOutputStream(System.out, this, strip);
final byte buf[] = new byte[4096];
try {
int read;
while (run && (read = System.in.read(buf)) != -1) {
cout.write(buf, 0, read);
}
} finally {
cout.close();
}
}
public void streamDone() {
System.err.println("Done");
run = false;
}
}
*/
}
package net.i2p.i2ptunnel.util;
import java.io.OutputStream;
/**
* Write to nowhere
*
* @since 0.9.62 copied from susimail
*/
public class DummyOutputStream extends OutputStream {
public void write(int val) {}
@Override
public void write(byte src[]) {}
@Override
public void write(byte src[], int off, int len) {}
@Override
public void flush() {}
@Override
public void close() {}
}
package net.i2p.i2ptunnel;
package net.i2p.i2ptunnel.util;
import java.io.EOFException;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
......@@ -7,7 +8,10 @@ import java.util.zip.CRC32;
import java.util.zip.Inflater;
import java.util.zip.InflaterOutputStream;
import net.i2p.I2PAppContext;
import net.i2p.data.DataHelper;
import net.i2p.i2ptunnel.util.LimitOutputStream.DoneCallback;
import net.i2p.util.Log;
/**
* Gunzip implementation per
......@@ -22,7 +26,7 @@ import net.i2p.data.DataHelper;
* Not a public API, subject to change, not for external use.
*
* Modified from net.i2p.util.ResettableGZIPInputStream to use Java 6 InflaterOutputstream
* @since 0.9.21, public since 0.9.50 for LocalHTTPServer
* @since 0.9.21, public since 0.9.50 for LocalHTTPServer, moved to util in 0.9.62
*/
public class GunzipOutputStream extends InflaterOutputStream {
private static final int FOOTER_SIZE = 8; // CRC32 + ISIZE
......@@ -38,12 +42,28 @@ public class GunzipOutputStream extends InflaterOutputStream {
private HeaderState _state = HeaderState.MB1;
private int _flags;
private int _extHdrToRead;
private final DoneCallback _callback;
private final Log _log;
private static final OutputStream DUMMY_OUT = new DummyOutputStream();
/**
* Build a new Gunzip stream
*/
public GunzipOutputStream(OutputStream uncompressedStream) throws IOException {
this(uncompressedStream, null);
}
/**
* With a callback when done
*
* @param cb may be null
* @since 0.9.62
*/
public GunzipOutputStream(OutputStream uncompressedStream, DoneCallback cb) throws IOException {
super(new CRC32OutputStream(uncompressedStream), new Inflater(true));
_log = I2PAppContext.getGlobalContext().logManager().getLog(GunzipOutputStream.class);
_callback = cb;
}
@Override
......@@ -57,7 +77,10 @@ public class GunzipOutputStream extends InflaterOutputStream {
if (_complete) {
// shortcircuit so the inflater doesn't try to refill
// with the footer's data (which would fail, causing ZLIB err)
return;
IOException ioe = new EOFException("Extra data written to gunzipper");
if (_log.shouldWarn())
_log.warn("EOF", ioe);
throw ioe;
}
boolean isFinished = inf.finished();
for (int i = off; i < off + len; i++) {
......@@ -85,6 +108,8 @@ public class GunzipOutputStream extends InflaterOutputStream {
verifyFooter();
_complete = true;
_validated = true;
if (_callback != null)
_callback.streamDone();
return;
} catch (IOException ioe) {
// failed at 7, retry at 8
......@@ -147,6 +172,8 @@ public class GunzipOutputStream extends InflaterOutputStream {
@Override
public void close() throws IOException {
if (_log.shouldWarn())
_log.warn("Closing " + this);
_complete = true;
_state = HeaderState.DONE;
super.close();
......
package net.i2p.i2ptunnel.util;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
/**
* Base class for limiting writes and calling a callback when finished
*
* @since 0.9.62
*/
public abstract class LimitOutputStream extends FilterOutputStream {
private final byte _buf1[];
protected final DoneCallback _callback;
protected boolean _isDone;
public interface DoneCallback { public void streamDone(); }
/**
* @param done non-null
*/
public LimitOutputStream(OutputStream out, DoneCallback done) {
super(out);
_callback = done;
_buf1 = new byte[1];
}
@Override
public void write(int c) throws IOException {
_buf1[0] = (byte)c;
write(_buf1, 0, 1);
}
/**
* Subclasses MUST override the following method
* such that it calls done() when finished
* and throws EOFException if called again
*/
@Override
public void write(byte buf[], int off, int len) throws IOException {
out.write(buf, off, len);
}
protected boolean isDone() { return _isDone; }
/**
* flush(), call the callback, and set _isDone
*/
protected void setDone() throws IOException {
if (_isDone)
throw new IllegalStateException("already done");
flush();
_callback.streamDone();
_isDone = true;
}
}
<html>
<body>
<p>
HTTP utilities.
Not for external use; not maintained as a stable API.
Since 0.9.62
</p>
</body>
</html>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment