1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.websocket.common.extensions.compress;
20
21 import java.nio.ByteBuffer;
22 import java.util.zip.DataFormatException;
23
24 import org.eclipse.jetty.util.log.Log;
25 import org.eclipse.jetty.util.log.Logger;
26 import org.eclipse.jetty.websocket.api.BadPayloadException;
27 import org.eclipse.jetty.websocket.api.BatchMode;
28 import org.eclipse.jetty.websocket.api.WriteCallback;
29 import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig;
30 import org.eclipse.jetty.websocket.api.extensions.Frame;
31 import org.eclipse.jetty.websocket.common.OpCode;
32
33
34
35
36
37
38 public class PerMessageDeflateExtension extends CompressExtension
39 {
40 private static final Logger LOG = Log.getLogger(PerMessageDeflateExtension.class);
41
42 private ExtensionConfig configRequested;
43 private ExtensionConfig configNegotiated;
44 private boolean incomingContextTakeover = true;
45 private boolean outgoingContextTakeover = true;
46 private boolean incomingCompressed;
47
48 @Override
49 public String getName()
50 {
51 return "permessage-deflate";
52 }
53
54 @Override
55 public void incomingFrame(Frame frame)
56 {
57
58
59
60
61
62
63 if (frame.getType().isData())
64 {
65 incomingCompressed = frame.isRsv1();
66 }
67
68 if (OpCode.isControlFrame(frame.getOpCode()) || !incomingCompressed)
69 {
70 nextIncomingFrame(frame);
71 return;
72 }
73
74 ByteAccumulator accumulator = newByteAccumulator();
75
76 try
77 {
78 ByteBuffer payload = frame.getPayload();
79 decompress(accumulator, payload);
80 if (frame.isFin())
81 {
82 decompress(accumulator, TAIL_BYTES_BUF.slice());
83 }
84
85 forwardIncoming(frame, accumulator);
86 }
87 catch (DataFormatException e)
88 {
89 throw new BadPayloadException(e);
90 }
91
92 if (frame.isFin())
93 incomingCompressed = false;
94 }
95
96 @Override
97 protected void nextIncomingFrame(Frame frame)
98 {
99 if (frame.isFin() && !incomingContextTakeover)
100 {
101 LOG.debug("Incoming Context Reset");
102 decompressCount.set(0);
103 getInflater().reset();
104 }
105 super.nextIncomingFrame(frame);
106 }
107
108 @Override
109 protected void nextOutgoingFrame(Frame frame, WriteCallback callback, BatchMode batchMode)
110 {
111 if (frame.isFin() && !outgoingContextTakeover)
112 {
113 LOG.debug("Outgoing Context Reset");
114 getDeflater().reset();
115 }
116 super.nextOutgoingFrame(frame, callback, batchMode);
117 }
118
119 @Override
120 int getRsvUseMode()
121 {
122 return RSV_USE_ONLY_FIRST;
123 }
124
125 @Override
126 int getTailDropMode()
127 {
128 return TAIL_DROP_FIN_ONLY;
129 }
130
131 @Override
132 public void setConfig(final ExtensionConfig config)
133 {
134 configRequested = new ExtensionConfig(config);
135 configNegotiated = new ExtensionConfig(config.getName());
136
137 for (String key : config.getParameterKeys())
138 {
139 key = key.trim();
140 switch (key)
141 {
142 case "client_max_window_bits":
143 case "server_max_window_bits":
144 {
145
146
147 break;
148 }
149 case "client_no_context_takeover":
150 {
151 configNegotiated.setParameter("client_no_context_takeover");
152 switch (getPolicy().getBehavior())
153 {
154 case CLIENT:
155 incomingContextTakeover = false;
156 break;
157 case SERVER:
158 outgoingContextTakeover = false;
159 break;
160 }
161 break;
162 }
163 case "server_no_context_takeover":
164 {
165 configNegotiated.setParameter("server_no_context_takeover");
166 switch (getPolicy().getBehavior())
167 {
168 case CLIENT:
169 outgoingContextTakeover = false;
170 break;
171 case SERVER:
172 incomingContextTakeover = false;
173 break;
174 }
175 break;
176 }
177 default:
178 {
179 throw new IllegalArgumentException();
180 }
181 }
182 }
183
184 LOG.debug("config: outgoingContextTakover={}, incomingContextTakeover={} : {}", outgoingContextTakeover, incomingContextTakeover, this);
185
186 super.setConfig(configNegotiated);
187 }
188
189 @Override
190 public String toString()
191 {
192 return String.format("%s[requested=\"%s\", negotiated=\"%s\"]",
193 getClass().getSimpleName(),
194 configRequested.getParameterizedName(),
195 configNegotiated.getParameterizedName());
196 }
197 }