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.io.IOException;
20 import java.nio.ByteBuffer;
21
22 import org.eclipse.jetty.io.AbstractConnection;
23 import org.eclipse.jetty.io.AsyncEndPoint;
24 import org.eclipse.jetty.io.Buffer;
25 import org.eclipse.jetty.io.Connection;
26 import org.eclipse.jetty.io.nio.AsyncConnection;
27 import org.eclipse.jetty.io.nio.DirectNIOBuffer;
28 import org.eclipse.jetty.io.nio.IndirectNIOBuffer;
29 import org.eclipse.jetty.io.nio.NIOBuffer;
30 import org.eclipse.jetty.spdy.api.Handler;
31 import org.eclipse.jetty.spdy.api.Session;
32 import org.eclipse.jetty.spdy.parser.Parser;
33 import org.eclipse.jetty.util.log.Log;
34 import org.eclipse.jetty.util.log.Logger;
35
36 public class SPDYAsyncConnection extends AbstractConnection implements AsyncConnection, Controller<StandardSession.FrameBytes>, IdleListener
37 {
38 private static final Logger logger = Log.getLogger(SPDYAsyncConnection.class);
39 private final ByteBufferPool bufferPool;
40 private final Parser parser;
41 private volatile Session session;
42 private ByteBuffer writeBuffer;
43 private Handler<StandardSession.FrameBytes> writeHandler;
44 private StandardSession.FrameBytes writeContext;
45 private volatile boolean writePending;
46
47 public SPDYAsyncConnection(AsyncEndPoint endPoint, ByteBufferPool bufferPool, Parser parser)
48 {
49 super(endPoint);
50 this.bufferPool = bufferPool;
51 this.parser = parser;
52 onIdle(true);
53 }
54
55 @Override
56 public Connection handle() throws IOException
57 {
58 AsyncEndPoint endPoint = getEndPoint();
59 boolean progress = true;
60 while (endPoint.isOpen() && progress)
61 {
62 int filled = fill();
63 progress = filled > 0;
64
65 int flushed = flush();
66 progress |= flushed > 0;
67
68 endPoint.flush();
69
70 progress |= endPoint.hasProgressed();
71
72 if (!progress && filled < 0)
73 {
74 onInputShutdown();
75 close(false);
76 }
77 }
78 return this;
79 }
80
81 public int fill() throws IOException
82 {
83 ByteBuffer buffer = bufferPool.acquire(8192, true);
84 NIOBuffer jettyBuffer = new DirectNIOBuffer(buffer, false);
85 jettyBuffer.setPutIndex(jettyBuffer.getIndex());
86 AsyncEndPoint endPoint = getEndPoint();
87 int filled = endPoint.fill(jettyBuffer);
88 logger.debug("Filled {} from {}", filled, endPoint);
89 if (filled <= 0)
90 return filled;
91
92 buffer.limit(jettyBuffer.putIndex());
93 buffer.position(jettyBuffer.getIndex());
94 parser.parse(buffer);
95
96 bufferPool.release(buffer);
97
98 return filled;
99 }
100
101 public int flush()
102 {
103 int result = 0;
104
105 if (writePending)
106 result = write(writeBuffer, writeHandler, writeContext);
107 logger.debug("Flushed {} to {}", result, getEndPoint());
108 return result;
109 }
110
111 @Override
112 public int write(ByteBuffer buffer, Handler<StandardSession.FrameBytes> handler, StandardSession.FrameBytes context)
113 {
114 int remaining = buffer.remaining();
115 Buffer jettyBuffer = buffer.isDirect() ? new DirectNIOBuffer(buffer, false) : new IndirectNIOBuffer(buffer, false);
116 AsyncEndPoint endPoint = getEndPoint();
117 try
118 {
119 int written = endPoint.flush(jettyBuffer);
120 logger.debug("Written {} bytes, {} remaining", written, jettyBuffer.length());
121 }
122 catch (Exception x)
123 {
124 close(false);
125 handler.failed(x);
126 }
127 finally
128 {
129 buffer.limit(jettyBuffer.putIndex());
130 buffer.position(jettyBuffer.getIndex());
131 }
132
133 if (buffer.hasRemaining())
134 {
135
136 this.writeBuffer = buffer;
137 this.writeHandler = handler;
138 this.writeContext = context;
139
140 writePending = true;
141 endPoint.scheduleWrite();
142 }
143 else
144 {
145 if (writePending)
146 {
147 this.writeBuffer = null;
148 this.writeHandler = null;
149 this.writeContext = null;
150
151 writePending = false;
152 }
153 handler.completed(context);
154 }
155
156 return remaining - buffer.remaining();
157 }
158
159 @Override
160 public void close(boolean onlyOutput)
161 {
162 try
163 {
164 AsyncEndPoint endPoint = getEndPoint();
165 try
166 {
167
168
169 logger.debug("Shutting down output {}", endPoint);
170 endPoint.shutdownOutput();
171 if (!onlyOutput)
172 {
173 logger.debug("Closing {}", endPoint);
174 endPoint.close();
175 }
176 }
177 catch (IOException x)
178 {
179 endPoint.close();
180 }
181 }
182 catch (IOException x)
183 {
184 logger.ignore(x);
185 }
186 }
187
188 @Override
189 public void onIdle(boolean idle)
190 {
191 getEndPoint().setCheckForIdle(idle);
192 }
193
194 @Override
195 public AsyncEndPoint getEndPoint()
196 {
197 return (AsyncEndPoint)super.getEndPoint();
198 }
199
200 @Override
201 public boolean isIdle()
202 {
203 return false;
204 }
205
206 @Override
207 public boolean isSuspended()
208 {
209 return false;
210 }
211
212 @Override
213 public void onClose()
214 {
215 }
216
217 @Override
218 public void onInputShutdown() throws IOException
219 {
220 }
221
222 @Override
223 public void onIdleExpired(long idleForMs)
224 {
225 session.goAway();
226 }
227
228 protected Session getSession()
229 {
230 return session;
231 }
232
233 protected void setSession(Session session)
234 {
235 this.session = session;
236 }
237 }