View Javadoc

1   // ========================================================================
2   // Copyright (c) 2006-2011 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  package org.eclipse.jetty.server;
15  
16  import java.io.IOException;
17  
18  import org.eclipse.jetty.http.HttpException;
19  import org.eclipse.jetty.http.HttpStatus;
20  import org.eclipse.jetty.io.AsyncEndPoint;
21  import org.eclipse.jetty.io.Connection;
22  import org.eclipse.jetty.io.EndPoint;
23  import org.eclipse.jetty.io.nio.AsyncConnection;
24  import org.eclipse.jetty.io.nio.SelectChannelEndPoint;
25  import org.eclipse.jetty.util.log.Log;
26  import org.eclipse.jetty.util.log.Logger;
27  
28  
29  /* ------------------------------------------------------------ */
30  /** Asychronous Server HTTP connection
31   *
32   */
33  public class AsyncHttpConnection extends AbstractHttpConnection implements AsyncConnection
34  {
35      private final static int NO_PROGRESS_INFO = Integer.getInteger("org.mortbay.jetty.NO_PROGRESS_INFO",100);
36      private final static int NO_PROGRESS_CLOSE = Integer.getInteger("org.mortbay.jetty.NO_PROGRESS_CLOSE",200);
37  
38      private static final Logger LOG = Log.getLogger(AsyncHttpConnection.class);
39      private int _total_no_progress;
40      private final AsyncEndPoint _asyncEndp;
41  
42      public AsyncHttpConnection(Connector connector, EndPoint endpoint, Server server)
43      {
44          super(connector,endpoint,server);
45          _asyncEndp=(AsyncEndPoint)endpoint;
46      }
47  
48      @Override
49      public Connection handle() throws IOException
50      {
51          Connection connection = this;
52          boolean some_progress=false;
53          boolean progress=true;
54  
55          try
56          {
57              setCurrentConnection(this);
58  
59              // don't check for idle while dispatched (unless blocking IO is done).
60              _asyncEndp.setCheckForIdle(false);
61  
62  
63              // While progress and the connection has not changed
64              while (progress && connection==this)
65              {
66                  progress=false;
67                  try
68                  {
69                      // Handle resumed request
70                      if (_request._async.isAsync())
71                      {
72                         if (_request._async.isDispatchable())
73                             handleRequest();
74                      }
75                      // else Parse more input
76                      else if (!_parser.isComplete() && _parser.parseAvailable())
77                          progress=true;
78  
79                      // Generate more output
80                      if (_generator.isCommitted() && !_generator.isComplete() && !_endp.isOutputShutdown())
81                          if (_generator.flushBuffer()>0)
82                              progress=true;
83  
84                      // Flush output
85                      _endp.flush();
86  
87                      // Has any IO been done by the endpoint itself since last loop
88                      if (_asyncEndp.hasProgressed())
89                          progress=true;
90                  }
91                  catch (HttpException e)
92                  {
93                      if (LOG.isDebugEnabled())
94                      {
95                          LOG.debug("uri="+_uri);
96                          LOG.debug("fields="+_requestFields);
97                          LOG.debug(e);
98                      }
99                      progress=true;
100                     _generator.sendError(e.getStatus(), e.getReason(), null, true);
101                 }
102                 finally
103                 {
104                     some_progress|=progress;
105                     //  Is this request/response round complete and are fully flushed?
106                     if (_parser.isComplete() && _generator.isComplete())
107                     {
108                         // Reset the parser/generator
109                         progress=true;
110 
111                         // look for a switched connection instance?
112                         if (_response.getStatus()==HttpStatus.SWITCHING_PROTOCOLS_101)
113                         {
114                             Connection switched=(Connection)_request.getAttribute("org.eclipse.jetty.io.Connection");
115                             if (switched!=null)
116                                 connection=switched;
117                         }
118 
119                         reset();
120 
121                         // TODO Is this still required?
122                         if (!_generator.isPersistent() && !_endp.isOutputShutdown())
123                         {
124                             LOG.warn("Safety net oshut!!!  IF YOU SEE THIS, PLEASE RAISE BUGZILLA");
125                             _endp.shutdownOutput();
126                         }
127                     }
128                     else if (_request.getAsyncContinuation().isAsyncStarted())
129                     {
130                         // The request is suspended, so even though progress has been made,
131                         // exit the while loop by setting progress to false
132                         LOG.debug("suspended {}",this);
133                         progress=false;
134                     }
135                 }
136             }
137         }
138         finally
139         {
140             setCurrentConnection(null);
141 
142             // If we are not suspended
143             if (!_request.getAsyncContinuation().isAsyncStarted())
144             {
145                 // return buffers
146                 _parser.returnBuffers();
147                 _generator.returnBuffers();
148 
149                 // reenable idle checking unless request is suspended
150                 _asyncEndp.setCheckForIdle(true);
151             }
152 
153             // Safety net to catch spinning
154             if (some_progress)
155                 _total_no_progress=0;
156             else
157             {
158                 _total_no_progress++;
159                 if (NO_PROGRESS_INFO>0 && _total_no_progress%NO_PROGRESS_INFO==0 && (NO_PROGRESS_CLOSE<=0 || _total_no_progress< NO_PROGRESS_CLOSE))
160                     LOG.info("EndPoint making no progress: "+_total_no_progress+" "+_endp+" "+this);
161                 if (NO_PROGRESS_CLOSE>0 && _total_no_progress==NO_PROGRESS_CLOSE)
162                 {
163                     LOG.warn("Closing EndPoint making no progress: "+_total_no_progress+" "+_endp+" "+this);
164                     if (_endp instanceof SelectChannelEndPoint)
165                         ((SelectChannelEndPoint)_endp).getChannel().close();
166                 }
167             }
168         }
169         return connection;
170     }
171 
172     public void onInputShutdown() throws IOException
173     {
174         // If we don't have a committed response and we are not suspended
175         if (_generator.isIdle() && !_request.getAsyncContinuation().isSuspended())
176         {
177             // then no more can happen, so close.
178             _endp.close();
179         }
180     }
181 
182 }