View | Details | Raw Unified | Return to bug 48738
Collapse All | Expand All

(-)java/org/apache/coyote/http11/InternalAprOutputBuffer.java (+20 lines)
Lines 31-36 Link Here
31
import org.apache.coyote.ActionCode;
31
import org.apache.coyote.ActionCode;
32
import org.apache.coyote.OutputBuffer;
32
import org.apache.coyote.OutputBuffer;
33
import org.apache.coyote.Response;
33
import org.apache.coyote.Response;
34
import org.apache.coyote.http11.filters.GzipOutputFilter;
34
35
35
/**
36
/**
36
 * Output buffer.
37
 * Output buffer.
Lines 94-99 Link Here
94
    protected static StringManager sm =
95
    protected static StringManager sm =
95
        StringManager.getManager(Constants.Package);
96
        StringManager.getManager(Constants.Package);
96
97
98
    /**
99
     * Logger.
100
     */
101
    private static final org.apache.juli.logging.Log log
102
        = org.apache.juli.logging.LogFactory.getLog(
103
                InternalAprOutputBuffer.class);
97
104
98
    // ----------------------------------------------------- Instance Variables
105
    // ----------------------------------------------------- Instance Variables
99
106
Lines 280-285 Link Here
280
287
281
        }
288
        }
282
289
290
        // go through the filters and if there is gzip filter
291
        // invoke it to flush
292
        for (int i = 0; i <= lastActiveFilter; i++) {
293
            if (activeFilters[i] instanceof GzipOutputFilter) {
294
                if (log.isDebugEnabled()) {
295
                    log.debug("Flushing the gzip filter at position " + i +
296
                            " of the filter chain...");
297
                }
298
                ((GzipOutputFilter) activeFilters[i]).flush();
299
                break;
300
            }
301
        }
302
        
283
        // Flush the current buffer
303
        // Flush the current buffer
284
        flushBuffer();
304
        flushBuffer();
285
305
(-)java/org/apache/coyote/http11/InternalNioOutputBuffer.java (-1 / +20 lines)
Lines 25-30 Link Here
25
import org.apache.coyote.ActionCode;
25
import org.apache.coyote.ActionCode;
26
import org.apache.coyote.OutputBuffer;
26
import org.apache.coyote.OutputBuffer;
27
import org.apache.coyote.Response;
27
import org.apache.coyote.Response;
28
import org.apache.coyote.http11.filters.GzipOutputFilter;
28
import org.apache.tomcat.util.buf.ByteChunk;
29
import org.apache.tomcat.util.buf.ByteChunk;
29
import org.apache.tomcat.util.buf.CharChunk;
30
import org.apache.tomcat.util.buf.CharChunk;
30
import org.apache.tomcat.util.buf.MessageBytes;
31
import org.apache.tomcat.util.buf.MessageBytes;
Lines 34-40 Link Here
34
import org.apache.tomcat.util.net.NioEndpoint;
35
import org.apache.tomcat.util.net.NioEndpoint;
35
import org.apache.tomcat.util.net.NioSelectorPool;
36
import org.apache.tomcat.util.net.NioSelectorPool;
36
import org.apache.tomcat.util.res.StringManager;
37
import org.apache.tomcat.util.res.StringManager;
37
import java.io.EOFException;
38
import org.apache.tomcat.util.MutableInteger;
38
import org.apache.tomcat.util.MutableInteger;
39
39
40
/**
40
/**
Lines 103-108 Link Here
103
    protected static StringManager sm =
103
    protected static StringManager sm =
104
        StringManager.getManager(Constants.Package);
104
        StringManager.getManager(Constants.Package);
105
105
106
    /**
107
     * Logger.
108
     */
109
    private static final org.apache.juli.logging.Log log
110
        = org.apache.juli.logging.LogFactory.getLog(
111
                InternalNioOutputBuffer.class);
106
112
107
    // ----------------------------------------------------- Instance Variables
113
    // ----------------------------------------------------- Instance Variables
108
114
Lines 296-301 Link Here
296
302
297
        }
303
        }
298
304
305
        // go through the filters and if there is gzip filter
306
        // invoke it to flush
307
        for (int i = 0; i <= lastActiveFilter; i++) {
308
            if (activeFilters[i] instanceof GzipOutputFilter) {
309
                if (log.isDebugEnabled()) {
310
                    log.debug("Flushing the gzip filter at position " + i +
311
                            " of the filter chain...");
312
                }
313
                ((GzipOutputFilter) activeFilters[i]).flush();
314
                break;
315
            }
316
        }
317
        
299
        // Flush the current buffer
318
        // Flush the current buffer
300
        flushBuffer();
319
        flushBuffer();
301
320
(-)java/org/apache/coyote/http11/InternalOutputBuffer.java (+19 lines)
Lines 32-37 Link Here
32
import org.apache.coyote.ActionCode;
32
import org.apache.coyote.ActionCode;
33
import org.apache.coyote.OutputBuffer;
33
import org.apache.coyote.OutputBuffer;
34
import org.apache.coyote.Response;
34
import org.apache.coyote.Response;
35
import org.apache.coyote.http11.filters.GzipOutputFilter;
35
36
36
/**
37
/**
37
 * Output buffer.
38
 * Output buffer.
Lines 90-95 Link Here
90
    protected static StringManager sm =
91
    protected static StringManager sm =
91
        StringManager.getManager(Constants.Package);
92
        StringManager.getManager(Constants.Package);
92
93
94
    /**
95
     * Logger.
96
     */
97
    private static final org.apache.juli.logging.Log log
98
        = org.apache.juli.logging.LogFactory.getLog(InternalOutputBuffer.class);
93
99
94
    // ----------------------------------------------------- Instance Variables
100
    // ----------------------------------------------------- Instance Variables
95
101
Lines 294-299 Link Here
294
300
295
        }
301
        }
296
302
303
        // go through the filters and if there is gzip filter
304
        // invoke it to flush
305
        for (int i = 0; i <= lastActiveFilter; i++) {
306
            if (activeFilters[i] instanceof GzipOutputFilter) {
307
                if (log.isDebugEnabled()) {
308
                    log.debug("Flushing the gzip filter at position " + i +
309
                            " of the filter chain...");
310
                }
311
                ((GzipOutputFilter) activeFilters[i]).flush();
312
                break;
313
            }
314
        }
315
        
297
        // Flush the current buffer
316
        // Flush the current buffer
298
        if (useSocketBuffer) {
317
        if (useSocketBuffer) {
299
            socketBuffer.flushBuffer();
318
            socketBuffer.flushBuffer();
(-)java/org/apache/coyote/http11/filters/FlushableGZIPOutputStream.java (+108 lines)
Line 0 Link Here
1
/*
2
 *  Licensed to the Apache Software Foundation (ASF) under one or more
3
 *  contributor license agreements.  See the NOTICE file distributed with
4
 *  this work for additional information regarding copyright ownership.
5
 *  The ASF licenses this file to You under the Apache License, Version 2.0
6
 *  (the "License"); you may not use this file except in compliance with
7
 *  the License.  You may obtain a copy of the License at
8
 *
9
 *      http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 *  Unless required by applicable law or agreed to in writing, software
12
 *  distributed under the License is distributed on an "AS IS" BASIS,
13
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 *  See the License for the specific language governing permissions and
15
 *  limitations under the License.
16
 */
17
18
package org.apache.coyote.http11.filters;
19
20
import java.io.IOException;
21
import java.io.OutputStream;
22
import java.util.zip.Deflater;
23
import java.util.zip.GZIPOutputStream;
24
25
/**
26
 * Extension of {@link GZIPOutputStream} to workaround for a couple of long
27
 * standing JDK bugs
28
 * (<a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4255743">Bug
29
 * 4255743</a> and
30
 * <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4813885">Bug
31
 * 4813885</a>) so the GZIP'd output can be flushed. 
32
 */
33
public class FlushableGZIPOutputStream extends GZIPOutputStream {
34
    public FlushableGZIPOutputStream(OutputStream os) throws IOException {
35
        super(os);
36
    }
37
38
    private static final byte[] EMPTYBYTEARRAY = new byte[0];
39
    private boolean hasData = false;
40
41
    /**
42
     * Here we make sure we have received data, so that the header has been for
43
     * sure written to the output stream already.
44
     */
45
    @Override
46
    public synchronized void write(byte[] bytes, int i, int i1)
47
            throws IOException {
48
        super.write(bytes, i, i1);
49
        hasData = true;
50
    }
51
52
    @Override
53
    public synchronized void write(int i) throws IOException {
54
        super.write(i);
55
        hasData = true;
56
    }
57
58
    @Override
59
    public synchronized void write(byte[] bytes) throws IOException {
60
        super.write(bytes);
61
        hasData = true;
62
    }
63
64
    @Override
65
    public synchronized void flush() throws IOException {
66
        if (!hasData) {
67
            return; // do not allow the gzip header to be flushed on its own
68
        }
69
70
        // trick the deflater to flush
71
        /**
72
         * Now this is tricky: We force the Deflater to flush its data by
73
         * switching compression level. As yet, a perplexingly simple workaround
74
         * for
75
         * http://developer.java.sun.com/developer/bugParade/bugs/4255743.html
76
         */
77
        if (!def.finished()) {
78
            def.setInput(EMPTYBYTEARRAY, 0, 0);
79
80
            def.setLevel(Deflater.NO_COMPRESSION);
81
            deflate();
82
83
            def.setLevel(Deflater.DEFAULT_COMPRESSION);
84
            deflate();
85
86
            out.flush();
87
        }
88
89
        hasData = false; // no more data to flush
90
    }
91
92
    /*
93
     * Keep on calling deflate until it runs dry. The default implementation
94
     * only does it once and can therefore hold onto data when they need to be
95
     * flushed out.
96
     */
97
    @Override
98
    protected void deflate() throws IOException {
99
        int len;
100
        do {
101
            len = def.deflate(buf, 0, buf.length);
102
            if (len > 0) {
103
                out.write(buf, 0, len);
104
            }
105
        } while (len != 0);
106
    }
107
108
}
0
  + native
109
  + native
(-)java/org/apache/coyote/http11/filters/GzipOutputFilter.java (-2 / +26 lines)
Lines 42-47 Link Here
42
    protected static final ByteChunk ENCODING = new ByteChunk();
42
    protected static final ByteChunk ENCODING = new ByteChunk();
43
43
44
44
45
    /**
46
     * Logger.
47
     */
48
    protected static org.apache.juli.logging.Log log =
49
        org.apache.juli.logging.LogFactory.getLog(GzipOutputFilter.class);
50
51
45
    // ----------------------------------------------------- Static Initializer
52
    // ----------------------------------------------------- Static Initializer
46
53
47
54
Lines 82-88 Link Here
82
    public int doWrite(ByteChunk chunk, Response res)
89
    public int doWrite(ByteChunk chunk, Response res)
83
        throws IOException {
90
        throws IOException {
84
        if (compressionStream == null) {
91
        if (compressionStream == null) {
85
            compressionStream = new GZIPOutputStream(fakeOutputStream);
92
            compressionStream = new FlushableGZIPOutputStream(fakeOutputStream);
86
        }
93
        }
87
        compressionStream.write(chunk.getBytes(), chunk.getStart(), 
94
        compressionStream.write(chunk.getBytes(), chunk.getStart(), 
88
                                chunk.getLength());
95
                                chunk.getLength());
Lines 92-97 Link Here
92
99
93
    // --------------------------------------------------- OutputFilter Methods
100
    // --------------------------------------------------- OutputFilter Methods
94
101
102
    /**
103
     * Added to allow flushing to happen for the gzip'ed outputstream
104
     */
105
    public void flush() {
106
        if (compressionStream != null) {
107
            try {
108
                if (log.isDebugEnabled()) {
109
                    log.debug("Flushing the compression stream!");
110
                }
111
                compressionStream.flush();
112
            } catch (IOException e) {
113
                if (log.isDebugEnabled()) {
114
                    log.debug("Ignored exception while flushing gzip filter", e);
115
                }
116
            }
117
        }
118
    }
95
119
96
    /**
120
    /**
97
     * Some filters need additional parameters from the response. All the 
121
     * Some filters need additional parameters from the response. All the 
Lines 117-123 Link Here
117
    public long end()
141
    public long end()
118
        throws IOException {
142
        throws IOException {
119
        if (compressionStream == null) {
143
        if (compressionStream == null) {
120
            compressionStream = new GZIPOutputStream(fakeOutputStream);
144
            compressionStream = new FlushableGZIPOutputStream(fakeOutputStream);
121
        }
145
        }
122
        compressionStream.finish();
146
        compressionStream.finish();
123
        compressionStream.close();
147
        compressionStream.close();

Return to bug 48738