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

(-)java/org/apache/coyote/http11/filters/FlushableGZIPOutputStream.java (-32 / +67 lines)
Lines 35-92 Link Here
35
        super(os);
35
        super(os);
36
    }
36
    }
37
37
38
    private static final byte[] EMPTYBYTEARRAY = new byte[0];
39
    private boolean hasData = false;
40
41
    /**
38
    /**
42
     * Here we make sure we have received data, so that the header has been for
39
     * It is used to reserve one byte of real data so that it can be used when
43
     * sure written to the output stream already.
40
     * flushing the stream.
44
     */
41
     */
42
    private byte[] lastByte = new byte[1];
43
    private boolean hasLastByte = false;
44
45
    @Override
45
    @Override
46
    public synchronized void write(byte[] bytes, int i, int i1)
46
    public void write(byte[] bytes) throws IOException {
47
        write(bytes, 0, bytes.length);
48
    }
49
50
    @Override
51
    public synchronized void write(byte[] bytes, int offset, int length)
47
            throws IOException {
52
            throws IOException {
48
        super.write(bytes, i, i1);
53
        if (length > 0) {
49
        hasData = true;
54
            flushLastByte();
55
            if (length > 1) {
56
                super.write(bytes, offset, length - 1);
57
            }
58
            rememberLastByte(bytes[offset + length - 1]);
59
        }
50
    }
60
    }
51
61
52
    @Override
62
    @Override
53
    public synchronized void write(int i) throws IOException {
63
    public synchronized void write(int i) throws IOException {
54
        super.write(i);
64
        flushLastByte();
55
        hasData = true;
65
        rememberLastByte((byte) i);
56
    }
66
    }
57
67
58
    @Override
68
    @Override
59
    public synchronized void write(byte[] bytes) throws IOException {
69
    public synchronized void finish() throws IOException {
60
        super.write(bytes);
70
        try {
61
        hasData = true;
71
            flushLastByte();
72
        } catch (IOException ignore) {
73
            // If our write failed, then trailer write in finish() will fail
74
            // with IOException as well, but it will leave Deflater in more
75
            // consistent state.
76
        }
77
        super.finish();
62
    }
78
    }
63
79
64
    @Override
80
    @Override
65
    public synchronized void flush() throws IOException {
81
    public synchronized void close() throws IOException {
66
        if (!hasData) {
82
        try {
67
            return; // do not allow the gzip header to be flushed on its own
83
            flushLastByte();
84
        } catch (IOException ignored) {
85
            // Ignore. As OutputStream#close() says, the contract of close()
86
            // is to close the stream. It does not matter much if the
87
            // stream is not writable any more.
68
        }
88
        }
89
        super.close();
90
    }
69
91
70
        // trick the deflater to flush
92
    private void rememberLastByte(byte b) {
71
        /**
93
        lastByte[0] = b;
72
         * Now this is tricky: We force the Deflater to flush its data by
94
        hasLastByte = true;
73
         * switching compression level. As yet, a perplexingly simple workaround
95
    }
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
96
80
            def.setLevel(Deflater.NO_COMPRESSION);
97
    private void flushLastByte() throws IOException {
81
            deflate();
98
        if (hasLastByte) {
99
            // Clear the flag first, because write() may fail
100
            hasLastByte = false;
101
            super.write(lastByte, 0, 1);
102
        }
103
    }
82
104
83
            def.setLevel(Deflater.DEFAULT_COMPRESSION);
105
    @Override
84
            deflate();
106
    public synchronized void flush() throws IOException {
107
        if (hasLastByte) {
108
            // - do not allow the gzip header to be flushed on its own
109
            // - do not do anything if there is no data to send
85
110
86
            out.flush();
111
            // trick the deflater to flush
112
            /**
113
             * Now this is tricky: We force the Deflater to flush its data by
114
             * switching compression level. As yet, a perplexingly simple workaround
115
             * for
116
             * http://developer.java.sun.com/developer/bugParade/bugs/4255743.html
117
             */
118
            if (!def.finished()) {
119
                def.setLevel(Deflater.NO_COMPRESSION);
120
                flushLastByte();
121
                def.setLevel(Deflater.DEFAULT_COMPRESSION);
122
            }
87
        }
123
        }
88
124
        out.flush();
89
        hasData = false; // no more data to flush
90
    }
125
    }
91
126
92
    /*
127
    /*

Return to bug 52121