Changeset 4456048


Ignore:
Timestamp:
Oct 2, 2010 2:43:40 PM (10 years ago)
Author:
zzz <zzz@…>
Branches:
master
Children:
7424fdd
Parents:
4c31c70
Message:
  • HTTPResponseOutputStream
    • More caching
    • Stats cleanup
    • Max header length check
    • Catch OOM
    • Initializer cleanup
    • Javadoc
Location:
apps/i2ptunnel/java/src/net/i2p/i2ptunnel
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • apps/i2ptunnel/java/src/net/i2p/i2ptunnel/HTTPResponseOutputStream.java

    r4c31c70 r4456048  
    2525
    2626/**
     27 * This does the transparent gzip decompression on the client side.
     28 * Extended in I2PTunnelHTTPServer to do the compression on the server side.
     29 *
    2730 * Simple stream for delivering an HTTP response to
    2831 * the client, trivially filtered to make sure "Connection: close"
     
    3437 */
    3538class HTTPResponseOutputStream extends FilterOutputStream {
    36     private I2PAppContext _context;
    37     private Log _log;
    38     private ByteCache _cache;
     39    private final I2PAppContext _context;
     40    private final Log _log;
    3941    protected ByteArray _headerBuffer;
    4042    private boolean _headerWritten;
    41     private byte _buf1[];
     43    private final byte _buf1[];
    4244    protected boolean _gzip;
    4345    private long _dataWritten;
    4446    private InternalGZIPInputStream _in;
    4547    private static final int CACHE_SIZE = 8*1024;
     48    private static final ByteCache _cache = ByteCache.getInstance(8, CACHE_SIZE);
     49    // OOM DOS prevention
     50    private static final int MAX_HEADER_SIZE = 64*1024;
    4651   
    4752    public HTTPResponseOutputStream(OutputStream raw) {
    4853        super(raw);
    4954        _context = I2PAppContext.getGlobalContext();
    50         _context.statManager().createRateStat("i2ptunnel.httpCompressionRatio", "ratio of compressed size to decompressed size after transfer", "I2PTunnel", new long[] { 60*1000, 30*60*1000 });
    51         _context.statManager().createRateStat("i2ptunnel.httpCompressed", "compressed size transferred", "I2PTunnel", new long[] { 60*1000, 30*60*1000 });
    52         _context.statManager().createRateStat("i2ptunnel.httpExpanded", "size transferred after expansion", "I2PTunnel", new long[] { 60*1000, 30*60*1000 });
     55        _context.statManager().createRateStat("i2ptunnel.httpCompressionRatio", "ratio of compressed size to decompressed size after transfer", "I2PTunnel", new long[] { 60*60*1000 });
     56        _context.statManager().createRateStat("i2ptunnel.httpCompressed", "compressed size transferred", "I2PTunnel", new long[] { 60*60*1000 });
     57        _context.statManager().createRateStat("i2ptunnel.httpExpanded", "size transferred after expansion", "I2PTunnel", new long[] { 60*60*1000 });
    5358        _log = _context.logManager().getLog(getClass());
    54         _cache = ByteCache.getInstance(8, CACHE_SIZE);
    5559        _headerBuffer = _cache.acquire();
    56         _headerWritten = false;
    57         _gzip = false;
    58         _dataWritten = 0;
    5960        _buf1 = new byte[1];
    6061    }
     
    9798    }
    9899   
    99     /** grow (and free) the buffer as necessary */
    100     private void ensureCapacity() {
     100    /**
     101     *  grow (and free) the buffer as necessary
     102     *  @throws IOException if the headers are too big
     103     */
     104    private void ensureCapacity() throws IOException {
     105        if (_headerBuffer.getValid() >= MAX_HEADER_SIZE)
     106            throw new IOException("Max header size exceeded: " + MAX_HEADER_SIZE);
    101107        if (_headerBuffer.getValid() + 1 >= _headerBuffer.getData().length) {
    102108            int newSize = (int)(_headerBuffer.getData().length * 1.5);
     
    105111            newBuf.setValid(_headerBuffer.getValid());
    106112            newBuf.setOffset(0);
     113            // if we changed the ByteArray size, don't put it back in the cache
    107114            if (_headerBuffer.getData().length == CACHE_SIZE)
    108115                _cache.release(_headerBuffer);
     
    220227        PipedInputStream pi = new PipedInputStream();
    221228        PipedOutputStream po = new PipedOutputStream(pi);
    222         new I2PAppThread(new Pusher(pi, out), "HTTP decompresser").start();
     229        new I2PAppThread(new Pusher(pi, out), "HTTP decompressor").start();
    223230        out = po;
    224231    }
     
    232239        }
    233240        public void run() {
    234             OutputStream to = null;
    235241            _in = null;
    236             long start = System.currentTimeMillis();
    237242            long written = 0;
     243            ByteArray ba = null;
    238244            try {
    239245                _in = new InternalGZIPInputStream(_inRaw);
    240                 byte buf[] = new byte[8192];
     246                ba = _cache.acquire();
     247                byte buf[] = ba.getData();
    241248                int read = -1;
    242249                while ( (read = _in.read(buf)) != -1) {
     
    252259                if (_log.shouldLog(Log.WARN))
    253260                    _log.warn("Error decompressing: " + written + ", " + (_in != null ? _in.getTotalRead() + "/" + _in.getTotalExpanded() : ""), ioe);
     261            } catch (OutOfMemoryError oom) {
     262                _log.error("OOM in HTTP Decompressor", oom);
    254263            } finally {
    255264                if (_log.shouldLog(Log.WARN) && (_in != null))
     
    260269                                + ", finished=" + _in.getFinished()
    261270                               : ""));
     271                if (ba != null)
     272                    _cache.release(ba);
    262273                if (_out != null) try {
    263274                    _out.close();
    264275                } catch (IOException ioe) {}
    265276            }
    266             long end = System.currentTimeMillis();
     277
    267278            double compressed = (_in != null ? _in.getTotalRead() : 0);
    268279            double expanded = (_in != null ? _in.getTotalExpanded() : 0);
    269             double ratio = 0;
    270             if (expanded > 0)
    271                 ratio = compressed/expanded;
    272 
    273             _context.statManager().addRateData("i2ptunnel.httpCompressionRatio", (int)(100d*ratio), end-start);
    274             _context.statManager().addRateData("i2ptunnel.httpCompressed", (long)compressed, end-start);
    275             _context.statManager().addRateData("i2ptunnel.httpExpanded", (long)expanded, end-start);
    276         }
    277     }
    278 
     280            if (compressed > 0 && expanded > 0) {
     281                // only update the stats if we did something
     282                double ratio = compressed/expanded;
     283                _context.statManager().addRateData("i2ptunnel.httpCompressionRatio", (int)(100d*ratio), 0);
     284                _context.statManager().addRateData("i2ptunnel.httpCompressed", (long)compressed, 0);
     285                _context.statManager().addRateData("i2ptunnel.httpExpanded", (long)expanded, 0);
     286            }
     287        }
     288    }
     289
     290    /** just a wrapper to provide stats for debugging */
    279291    private static class InternalGZIPInputStream extends GZIPInputStream {
    280292        public InternalGZIPInputStream(InputStream in) throws IOException {
     
    295307            }
    296308        }
     309
     310        /**
     311         *  From Inflater javadoc:
     312         *  Returns the total number of bytes remaining in the input buffer. This can be used to find out
     313         *  what bytes still remain in the input buffer after decompression has finished.
     314         */
    297315        public long getRemaining() {
    298316            try {
  • apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPServer.java

    r4c31c70 r4456048  
    291291    }
    292292
     293    /** just a wrapper to provide stats for debugging */
    293294    private static class InternalGZIPOutputStream extends GZIPOutputStream {
    294295        public InternalGZIPOutputStream(OutputStream target) throws IOException {
Note: See TracChangeset for help on using the changeset viewer.