1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.eclipse.jetty.spdy;
18
19 import java.nio.ByteBuffer;
20 import java.util.Map;
21 import java.util.concurrent.ConcurrentHashMap;
22 import java.util.concurrent.Future;
23 import java.util.concurrent.TimeUnit;
24 import java.util.concurrent.atomic.AtomicInteger;
25
26 import org.eclipse.jetty.spdy.api.ByteBufferDataInfo;
27 import org.eclipse.jetty.spdy.api.DataInfo;
28 import org.eclipse.jetty.spdy.api.Handler;
29 import org.eclipse.jetty.spdy.api.HeadersInfo;
30 import org.eclipse.jetty.spdy.api.ReplyInfo;
31 import org.eclipse.jetty.spdy.api.RstInfo;
32 import org.eclipse.jetty.spdy.api.Session;
33 import org.eclipse.jetty.spdy.api.Stream;
34 import org.eclipse.jetty.spdy.api.StreamFrameListener;
35 import org.eclipse.jetty.spdy.api.StreamStatus;
36 import org.eclipse.jetty.spdy.frames.ControlFrame;
37 import org.eclipse.jetty.spdy.frames.DataFrame;
38 import org.eclipse.jetty.spdy.frames.HeadersFrame;
39 import org.eclipse.jetty.spdy.frames.SynReplyFrame;
40 import org.eclipse.jetty.spdy.frames.SynStreamFrame;
41 import org.eclipse.jetty.spdy.frames.WindowUpdateFrame;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
44
45 public class StandardStream implements IStream
46 {
47 private static final Logger logger = LoggerFactory.getLogger(Stream.class);
48 private final Map<String, Object> attributes = new ConcurrentHashMap<>();
49 private final SynStreamFrame frame;
50 private final ISession session;
51 private final AtomicInteger windowSize;
52 private volatile StreamFrameListener listener;
53 private volatile boolean opened;
54 private volatile boolean halfClosed;
55 private volatile boolean closed;
56
57 public StandardStream(SynStreamFrame frame, ISession session, int windowSize)
58 {
59 this.frame = frame;
60 this.session = session;
61 this.windowSize = new AtomicInteger(windowSize);
62 this.halfClosed = frame.isClose();
63 }
64
65 @Override
66 public int getId()
67 {
68 return frame.getStreamId();
69 }
70
71 @Override
72 public byte getPriority()
73 {
74 return frame.getPriority();
75 }
76
77 @Override
78 public int getWindowSize()
79 {
80 return windowSize.get();
81 }
82
83 @Override
84 public void updateWindowSize(int delta)
85 {
86 int size = windowSize.addAndGet(delta);
87 logger.debug("Updated window size by {}, new window size {}", delta, size);
88 }
89
90 @Override
91 public Session getSession()
92 {
93 return session;
94 }
95
96 public boolean isHalfClosed()
97 {
98 return halfClosed;
99 }
100
101 @Override
102 public Object getAttribute(String key)
103 {
104 return attributes.get(key);
105 }
106
107 @Override
108 public void setAttribute(String key, Object value)
109 {
110 attributes.put(key, value);
111 }
112
113 @Override
114 public Object removeAttribute(String key)
115 {
116 return attributes.remove(key);
117 }
118
119 @Override
120 public void setStreamFrameListener(StreamFrameListener listener)
121 {
122 this.listener = listener;
123 }
124
125 @Override
126 public void updateCloseState(boolean close)
127 {
128 if (close)
129 {
130 if (isHalfClosed())
131 closed = true;
132 else
133 halfClosed = true;
134 }
135 }
136
137 @Override
138 public void process(ControlFrame frame)
139 {
140 switch (frame.getType())
141 {
142 case SYN_STREAM:
143 {
144 opened = true;
145 break;
146 }
147 case SYN_REPLY:
148 {
149 opened = true;
150 SynReplyFrame synReply = (SynReplyFrame)frame;
151 updateCloseState(synReply.isClose());
152 ReplyInfo replyInfo = new ReplyInfo(synReply.getHeaders(), synReply.isClose());
153 notifyOnReply(replyInfo);
154 break;
155 }
156 case HEADERS:
157 {
158 HeadersFrame headers = (HeadersFrame)frame;
159 updateCloseState(headers.isClose());
160 HeadersInfo headersInfo = new HeadersInfo(headers.getHeaders(), headers.isClose(), headers.isResetCompression());
161 notifyOnHeaders(headersInfo);
162 break;
163 }
164 case WINDOW_UPDATE:
165 {
166 WindowUpdateFrame windowUpdate = (WindowUpdateFrame)frame;
167 updateWindowSize(windowUpdate.getWindowDelta());
168 break;
169 }
170 case RST_STREAM:
171 {
172
173 break;
174 }
175 default:
176 {
177 throw new IllegalStateException();
178 }
179 }
180 session.flush();
181 }
182
183 @Override
184 public void process(DataFrame frame, ByteBuffer data)
185 {
186 if (!opened)
187 {
188 session.rst(new RstInfo(getId(), StreamStatus.PROTOCOL_ERROR));
189 return;
190 }
191
192 updateCloseState(frame.isClose());
193
194 ByteBufferDataInfo dataInfo = new ByteBufferDataInfo(data, frame.isClose(), frame.isCompress())
195 {
196 @Override
197 public void consume(int delta)
198 {
199 super.consume(delta);
200
201
202
203
204
205
206
207
208
209 if (consumed() == length() && !isClosed())
210 windowUpdate(length());
211 }
212 };
213 notifyOnData(dataInfo);
214 session.flush();
215 }
216
217 private void windowUpdate(int delta)
218 {
219 if (delta > 0)
220 {
221 WindowUpdateFrame windowUpdateFrame = new WindowUpdateFrame(session.getVersion(), getId(), delta);
222 session.control(this, windowUpdateFrame, 0, TimeUnit.MILLISECONDS, null, null);
223 }
224 }
225
226 private void notifyOnReply(ReplyInfo replyInfo)
227 {
228 final StreamFrameListener listener = this.listener;
229 try
230 {
231 if (listener != null)
232 {
233 logger.debug("Invoking reply callback with {} on listener {}", replyInfo, listener);
234 listener.onReply(this, replyInfo);
235 }
236 }
237 catch (Exception x)
238 {
239 logger.info("Exception while notifying listener " + listener, x);
240 }
241 }
242
243 private void notifyOnHeaders(HeadersInfo headersInfo)
244 {
245 final StreamFrameListener listener = this.listener;
246 try
247 {
248 if (listener != null)
249 {
250 logger.debug("Invoking headers callback with {} on listener {}", frame, listener);
251 listener.onHeaders(this, headersInfo);
252 }
253 }
254 catch (Exception x)
255 {
256 logger.info("Exception while notifying listener " + listener, x);
257 }
258 }
259
260 private void notifyOnData(DataInfo dataInfo)
261 {
262 final StreamFrameListener listener = this.listener;
263 try
264 {
265 if (listener != null)
266 {
267 logger.debug("Invoking data callback with {} on listener {}", dataInfo, listener);
268 listener.onData(this, dataInfo);
269 logger.debug("Invoked data callback with {} on listener {}", dataInfo, listener);
270 }
271 }
272 catch (Exception x)
273 {
274 logger.info("Exception while notifying listener " + listener, x);
275 }
276 }
277
278 @Override
279 public Future<Void> reply(ReplyInfo replyInfo)
280 {
281 Promise<Void> result = new Promise<>();
282 reply(replyInfo, 0, TimeUnit.MILLISECONDS, result);
283 return result;
284 }
285
286 @Override
287 public void reply(ReplyInfo replyInfo, long timeout, TimeUnit unit, Handler<Void> handler)
288 {
289 updateCloseState(replyInfo.isClose());
290 SynReplyFrame frame = new SynReplyFrame(session.getVersion(), replyInfo.getFlags(), getId(), replyInfo.getHeaders());
291 session.control(this, frame, timeout, unit, handler, null);
292 }
293
294 @Override
295 public Future<Void> data(DataInfo dataInfo)
296 {
297 Promise<Void> result = new Promise<>();
298 data(dataInfo, 0, TimeUnit.MILLISECONDS, result);
299 return result;
300 }
301
302 @Override
303 public void data(DataInfo dataInfo, long timeout, TimeUnit unit, Handler<Void> handler)
304 {
305
306
307 session.data(this, dataInfo, timeout, unit, handler, null);
308 }
309
310 @Override
311 public Future<Void> headers(HeadersInfo headersInfo)
312 {
313 Promise<Void> result = new Promise<>();
314 headers(headersInfo, 0, TimeUnit.MILLISECONDS, result);
315 return result;
316 }
317
318 @Override
319 public void headers(HeadersInfo headersInfo, long timeout, TimeUnit unit, Handler<Void> handler)
320 {
321 updateCloseState(headersInfo.isClose());
322 HeadersFrame frame = new HeadersFrame(session.getVersion(), headersInfo.getFlags(), getId(), headersInfo.getHeaders());
323 session.control(this, frame, timeout, unit, handler, null);
324 }
325
326 @Override
327 public boolean isClosed()
328 {
329 return closed;
330 }
331
332 @Override
333 public String toString()
334 {
335 return String.format("stream=%d v%d closed=%s", getId(), session.getVersion(), isClosed() ? "true" : isHalfClosed() ? "half" : "false");
336 }
337 }