View Javadoc

1   //========================================================================
2   //Copyright 2011-2012 Mort Bay Consulting Pty. Ltd.
3   //------------------------------------------------------------------------
4   //All rights reserved. This program and the accompanying materials
5   //are made available under the terms of the Eclipse Public License v1.0
6   //and Apache License v2.0 which accompanies this distribution.
7   //The Eclipse Public License is available at
8   //http://www.eclipse.org/legal/epl-v10.html
9   //The Apache License v2.0 is available at
10  //http://www.opensource.org/licenses/apache2.0.php
11  //You may elect to redistribute this code under either of these licenses.
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         // Volatile read to ensure visibility of buffer and handler
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             // Save buffer and handler in order to finish the write later in flush()
135             this.writeBuffer = buffer;
136             this.writeHandler = handler;
137             this.writeContext = context;
138             // Volatile write to ensure visibility of write fields
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                 // Volatile write to ensure visibility of write fields
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                 // We need to gently close first, to allow
167                 // SSL close alerts to be sent by Jetty
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 }