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