View Javadoc

1   /*
2    * Copyright (c) 2012 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
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         // Volatile read to ensure visibility of buffer and handler
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             // Save buffer and handler in order to finish the write later in flush()
136             this.writeBuffer = buffer;
137             this.writeHandler = handler;
138             this.writeContext = context;
139             // Volatile write to ensure visibility of write fields
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                 // Volatile write to ensure visibility of write fields
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                 // We need to gently close first, to allow
168                 // SSL close alerts to be sent by Jetty
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 }