View Javadoc
1   /*
2    * Copyright 2025 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package io.netty.handler.codec.compression;
17  
18  import io.netty.buffer.ByteBuf;
19  import io.netty.util.internal.ObjectUtil;
20  
21  /**
22   * Most decompressor implementations play fast and loose with {@link Decompressor} API contracts. This wrapper makes
23   * sure callers follow that contract.
24   */
25  final class DefensiveDecompressor implements Decompressor {
26      private final Decompressor delegate;
27      private Status status;
28      private boolean closed;
29      private boolean failed;
30  
31      DefensiveDecompressor(Decompressor delegate) {
32          this.delegate = ObjectUtil.checkNotNull(delegate, "delegate");
33      }
34  
35      @Override
36      public Status status() throws DecompressionException {
37          checkReady();
38          try {
39              status = delegate.status();
40          } catch (Exception e) {
41              failed = true;
42              throw e;
43          }
44          return status;
45      }
46  
47      @Override
48      public void addInput(ByteBuf buf) throws DecompressionException {
49          try {
50              checkReady();
51              checkState(Status.NEED_INPUT);
52          } catch (Throwable t) {
53              buf.release();
54              throw t;
55          }
56          try {
57              delegate.addInput(buf);
58          } catch (Exception e) {
59              failed = true;
60              throw e;
61          }
62          status = null;
63      }
64  
65      @Override
66      public void endOfInput() throws DecompressionException {
67          checkReady();
68          checkState(Status.NEED_INPUT);
69          try {
70              delegate.endOfInput();
71          } catch (Exception e) {
72              failed = true;
73              throw e;
74          }
75          status = null;
76      }
77  
78      @Override
79      public ByteBuf takeOutput() throws DecompressionException {
80          checkReady();
81          checkState(Status.NEED_OUTPUT);
82          ByteBuf out;
83          try {
84              out = delegate.takeOutput();
85          } catch (Exception e) {
86              failed = true;
87              throw e;
88          }
89          status = null;
90          return out;
91      }
92  
93      @Override
94      public void close() {
95          closed = true;
96          delegate.close();
97      }
98  
99      private void checkReady() {
100         if (closed) {
101             throw new IllegalStateException("Already closed");
102         }
103         if (failed) {
104             throw new IllegalStateException("Previous call failed");
105         }
106     }
107 
108     private void checkState(Status expected) {
109         if (this.status != expected) {
110             throw new IllegalStateException("Not in expected state " + expected + ", was " + this.status);
111         }
112     }
113 }