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;
20
21 import java.io.IOException;
22 import java.util.ArrayList;
23 import java.util.List;
24 import java.util.ListIterator;
25
26 import org.eclipse.jetty.util.annotation.ManagedAttribute;
27 import org.eclipse.jetty.util.annotation.ManagedObject;
28 import org.eclipse.jetty.util.component.ContainerLifeCycle;
29 import org.eclipse.jetty.util.log.Log;
30 import org.eclipse.jetty.util.log.Logger;
31 import org.eclipse.jetty.websocket.api.WebSocketException;
32 import org.eclipse.jetty.websocket.api.WriteCallback;
33 import org.eclipse.jetty.websocket.api.extensions.Extension;
34 import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig;
35 import org.eclipse.jetty.websocket.api.extensions.ExtensionFactory;
36 import org.eclipse.jetty.websocket.api.extensions.Frame;
37 import org.eclipse.jetty.websocket.api.extensions.IncomingFrames;
38 import org.eclipse.jetty.websocket.api.extensions.OutgoingFrames;
39 import org.eclipse.jetty.websocket.common.Generator;
40 import org.eclipse.jetty.websocket.common.Parser;
41
42
43
44
45 @ManagedObject("Extension Stack")
46 public class ExtensionStack extends ContainerLifeCycle implements IncomingFrames, OutgoingFrames
47 {
48 private static final Logger LOG = Log.getLogger(ExtensionStack.class);
49 private final ExtensionFactory factory;
50 private List<Extension> extensions;
51 private IncomingFrames nextIncoming;
52 private OutgoingFrames nextOutgoing;
53
54 public ExtensionStack(ExtensionFactory factory)
55 {
56 this.factory = factory;
57 }
58
59 public void configure(Generator generator)
60 {
61 generator.configureFromExtensions(extensions);
62 }
63
64 public void configure(Parser parser)
65 {
66 parser.configureFromExtensions(extensions);
67 }
68
69 @Override
70 protected void doStart() throws Exception
71 {
72 super.doStart();
73 LOG.debug("doStart");
74
75
76 if ((extensions != null) && (extensions.size() > 0))
77 {
78 ListIterator<Extension> eiter = extensions.listIterator();
79
80
81 while (eiter.hasNext())
82 {
83 Extension ext = eiter.next();
84 ext.setNextOutgoingFrames(nextOutgoing);
85 nextOutgoing = ext;
86 }
87
88
89 while (eiter.hasPrevious())
90 {
91 Extension ext = eiter.previous();
92 ext.setNextIncomingFrames(nextIncoming);
93 nextIncoming = ext;
94 }
95 }
96 }
97
98 @Override
99 public void dump(Appendable out, String indent) throws IOException
100 {
101 super.dump(out,indent);
102
103 IncomingFrames websocket = getLastIncoming();
104 OutgoingFrames network = getLastOutgoing();
105
106 out.append(indent).append(" +- Stack\n");
107 out.append(indent).append(" +- Network : ").append(network.toString()).append('\n');
108 for (Extension ext : extensions)
109 {
110 out.append(indent).append(" +- Extension: ").append(ext.toString()).append('\n');
111 }
112 out.append(indent).append(" +- Websocket: ").append(websocket.toString()).append('\n');
113 }
114
115 @ManagedAttribute(name = "Extension List", readonly = true)
116 public List<Extension> getExtensions()
117 {
118 return extensions;
119 }
120
121 private IncomingFrames getLastIncoming()
122 {
123 IncomingFrames last = nextIncoming;
124 boolean done = false;
125 while (!done)
126 {
127 if (last instanceof AbstractExtension)
128 {
129 last = ((AbstractExtension)last).getNextIncoming();
130 }
131 else
132 {
133 done = true;
134 }
135 }
136 return last;
137 }
138
139 private OutgoingFrames getLastOutgoing()
140 {
141 OutgoingFrames last = nextOutgoing;
142 boolean done = false;
143 while (!done)
144 {
145 if (last instanceof AbstractExtension)
146 {
147 last = ((AbstractExtension)last).getNextOutgoing();
148 }
149 else
150 {
151 done = true;
152 }
153 }
154 return last;
155 }
156
157
158
159
160
161
162 public List<ExtensionConfig> getNegotiatedExtensions()
163 {
164 List<ExtensionConfig> ret = new ArrayList<>();
165 if (extensions == null)
166 {
167 return ret;
168 }
169
170 for (Extension ext : extensions)
171 {
172 ret.add(ext.getConfig());
173 }
174 return ret;
175 }
176
177 @ManagedAttribute(name = "Next Incoming Frames Handler", readonly = true)
178 public IncomingFrames getNextIncoming()
179 {
180 return nextIncoming;
181 }
182
183 @ManagedAttribute(name = "Next Outgoing Frames Handler", readonly = true)
184 public OutgoingFrames getNextOutgoing()
185 {
186 return nextOutgoing;
187 }
188
189 public boolean hasNegotiatedExtensions()
190 {
191 return (this.extensions != null) && (this.extensions.size() > 0);
192 }
193
194 @Override
195 public void incomingError(WebSocketException e)
196 {
197 nextIncoming.incomingError(e);
198 }
199
200 @Override
201 public void incomingFrame(Frame frame)
202 {
203 nextIncoming.incomingFrame(frame);
204 }
205
206
207
208
209
210
211
212
213
214 public void negotiate(List<ExtensionConfig> configs)
215 {
216 LOG.debug("Extension Configs={}",configs);
217 this.extensions = new ArrayList<>();
218 for (ExtensionConfig config : configs)
219 {
220 Extension ext = factory.newInstance(config);
221 if (ext == null)
222 {
223
224 continue;
225 }
226 extensions.add(ext);
227 LOG.debug("Adding Extension: {}",ext);
228 }
229
230 addBean(extensions);
231 }
232
233 @Override
234 public void outgoingFrame(Frame frame, WriteCallback callback)
235 {
236 nextOutgoing.outgoingFrame(frame,callback);
237 }
238
239 public void setNextIncoming(IncomingFrames nextIncoming)
240 {
241 this.nextIncoming = nextIncoming;
242 }
243
244 public void setNextOutgoing(OutgoingFrames nextOutgoing)
245 {
246 this.nextOutgoing = nextOutgoing;
247 }
248
249 @Override
250 public String toString()
251 {
252 StringBuilder s = new StringBuilder();
253 s.append("ExtensionStack[");
254 s.append("extensions=");
255 if (extensions == null)
256 {
257 s.append("<null>");
258 }
259 else
260 {
261 s.append('[');
262 boolean delim = false;
263 for (Extension ext : extensions)
264 {
265 if (delim)
266 {
267 s.append(',');
268 }
269 if (ext == null)
270 {
271 s.append("<null>");
272 }
273 else
274 {
275 s.append(ext.getName());
276 }
277 delim = true;
278 }
279 s.append(']');
280 }
281 s.append(",incoming=").append((this.nextIncoming == null)?"<null>":this.nextIncoming.getClass().getName());
282 s.append(",outgoing=").append((this.nextOutgoing == null)?"<null>":this.nextOutgoing.getClass().getName());
283 s.append("]");
284 return s.toString();
285 }
286 }
287