1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.http2;
20
21 import java.io.IOException;
22 import java.nio.ByteBuffer;
23 import java.util.Queue;
24 import java.util.concurrent.Executor;
25 import java.util.concurrent.atomic.AtomicLong;
26
27 import org.eclipse.jetty.http2.parser.Parser;
28 import org.eclipse.jetty.io.AbstractConnection;
29 import org.eclipse.jetty.io.ByteBufferPool;
30 import org.eclipse.jetty.io.EndPoint;
31 import org.eclipse.jetty.util.BufferUtil;
32 import org.eclipse.jetty.util.Callback;
33 import org.eclipse.jetty.util.ConcurrentArrayQueue;
34 import org.eclipse.jetty.util.log.Log;
35 import org.eclipse.jetty.util.log.Logger;
36 import org.eclipse.jetty.util.thread.ExecutionStrategy;
37
38 public class HTTP2Connection extends AbstractConnection
39 {
40 protected static final Logger LOG = Log.getLogger(HTTP2Connection.class);
41
42 private final Queue<Runnable> tasks = new ConcurrentArrayQueue<>();
43 private final HTTP2Producer producer = new HTTP2Producer();
44 private final AtomicLong bytesIn = new AtomicLong();
45 private final ByteBufferPool byteBufferPool;
46 private final Parser parser;
47 private final ISession session;
48 private final int bufferSize;
49 private final ExecutionStrategy executionStrategy;
50
51 public HTTP2Connection(ByteBufferPool byteBufferPool, Executor executor, EndPoint endPoint, Parser parser, ISession session, int bufferSize, ExecutionStrategy.Factory executionFactory)
52 {
53 super(endPoint, executor);
54 this.byteBufferPool = byteBufferPool;
55 this.parser = parser;
56 this.session = session;
57 this.bufferSize = bufferSize;
58 this.executionStrategy = executionFactory.newExecutionStrategy(producer, executor);
59 }
60
61 @Override
62 public long getBytesIn()
63 {
64 return bytesIn.get();
65 }
66
67 @Override
68 public long getBytesOut()
69 {
70 return session.getBytesWritten();
71 }
72
73 public ISession getSession()
74 {
75 return session;
76 }
77
78 protected Parser getParser()
79 {
80 return parser;
81 }
82
83 protected void setInputBuffer(ByteBuffer buffer)
84 {
85 producer.buffer = buffer;
86 }
87
88 @Override
89 public void onOpen()
90 {
91 if (LOG.isDebugEnabled())
92 LOG.debug("HTTP2 Open {} ", this);
93 super.onOpen();
94 executionStrategy.execute();
95 }
96
97 @Override
98 public void onClose()
99 {
100 if (LOG.isDebugEnabled())
101 LOG.debug("HTTP2 Close {} ", this);
102 super.onClose();
103 }
104
105 @Override
106 public void onFillable()
107 {
108 if (LOG.isDebugEnabled())
109 LOG.debug("HTTP2 onFillable {} ", this);
110 executionStrategy.execute();
111 }
112
113 private int fill(EndPoint endPoint, ByteBuffer buffer)
114 {
115 try
116 {
117 if (endPoint.isInputShutdown())
118 return -1;
119 return endPoint.fill(buffer);
120 }
121 catch (IOException x)
122 {
123 LOG.debug("Could not read from " + endPoint, x);
124 return -1;
125 }
126 }
127
128 @Override
129 public boolean onIdleExpired()
130 {
131 boolean idle = isFillInterested();
132 if (idle)
133 {
134 boolean close = session.onIdleTimeout();
135 if (close)
136 session.close(ErrorCode.NO_ERROR.code, "idle_timeout", Callback.NOOP);
137 }
138 return false;
139 }
140
141 protected void offerTask(Runnable task, boolean dispatch)
142 {
143 tasks.offer(task);
144 if (dispatch)
145 executionStrategy.dispatch();
146 else
147 executionStrategy.execute();
148 }
149
150 @Override
151 public void close()
152 {
153
154
155 session.close(ErrorCode.NO_ERROR.code, "close", Callback.NOOP);
156 }
157
158 protected class HTTP2Producer implements ExecutionStrategy.Producer
159 {
160 private final Callback fillCallback = new FillCallback();
161 private ByteBuffer buffer;
162
163 @Override
164 public Runnable produce()
165 {
166 Runnable task = tasks.poll();
167 if (LOG.isDebugEnabled())
168 LOG.debug("Dequeued task {}", task);
169 if (task != null)
170 return task;
171
172 if (isFillInterested())
173 return null;
174
175 if (buffer == null)
176 buffer = byteBufferPool.acquire(bufferSize, false);
177 boolean looping = BufferUtil.hasContent(buffer);
178 while (true)
179 {
180 if (looping)
181 {
182 while (buffer.hasRemaining())
183 parser.parse(buffer);
184
185 task = tasks.poll();
186 if (LOG.isDebugEnabled())
187 LOG.debug("Dequeued new task {}", task);
188 if (task != null)
189 {
190 release();
191 return task;
192 }
193 }
194
195 int filled = fill(getEndPoint(), buffer);
196 if (LOG.isDebugEnabled())
197 LOG.debug("Filled {} bytes", filled);
198
199 if (filled == 0)
200 {
201 release();
202 getEndPoint().fillInterested(fillCallback);
203 return null;
204 }
205 else if (filled < 0)
206 {
207 release();
208 session.onShutdown();
209 return null;
210 }
211 else
212 {
213 bytesIn.addAndGet(filled);
214 }
215
216 looping = true;
217 }
218 }
219
220 private void release()
221 {
222 if (buffer != null && !buffer.hasRemaining())
223 {
224 byteBufferPool.release(buffer);
225 buffer = null;
226 }
227 }
228 }
229
230 private class FillCallback implements Callback.NonBlocking
231 {
232 @Override
233 public void succeeded()
234 {
235 onFillable();
236 }
237
238 @Override
239 public void failed(Throwable x)
240 {
241 onFillInterestedFailed(x);
242 }
243 }
244 }