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      public Connection handle() throws IOException
49      {
50          Connection connection = this;
51          boolean some_progress=false;
52          boolean progress=true;
53  
54          try
55          {
56              setCurrentConnection(this);
57              
58              // don't check for idle while dispatched (unless blocking IO is done).
59              _asyncEndp.setCheckForIdle(false);
60              
61  
62              // While progress and the connection has not changed
63              while (progress && connection==this)
64              {
65                  progress=false;
66                  try
67                  {
68                      // Handle resumed request
69                      if (_request._async.isAsync())
70                      { 
71                         if (_request._async.isDispatchable())
72                             handleRequest();
73                      }
74                      // else Parse more input
75                      else if (!_parser.isComplete() && _parser.parseAvailable())
76                          progress=true;
77  
78                      // Generate more output
79                      if (_generator.isCommitted() && !_generator.isComplete() && !_endp.isOutputShutdown())
80                          if (_generator.flushBuffer()>0)
81                              progress=true;
82  
83                      // Flush output
84                      _endp.flush();
85  
86                      // Has any IO been done by the endpoint itself since last loop
87                      if (_asyncEndp.hasProgressed())
88                          progress=true;
89                  }
90                  catch (HttpException e)
91                  {
92                      if (LOG.isDebugEnabled())
93                      {
94                          LOG.debug("uri="+_uri);
95                          LOG.debug("fields="+_requestFields);
96                          LOG.debug(e);
97                      }
98                      progress=true;
99                      _generator.sendError(e.getStatus(), e.getReason(), null, true);
100                 }
101                 finally
102                 {
103                     some_progress|=progress;
104                     //  Is this request/response round complete and are fully flushed?
105                     if (_parser.isComplete() && _generator.isComplete())
106                     {
107                         // Reset the parser/generator
108                         progress=true;
109 
110                         // look for a switched connection instance?
111                         if (_response.getStatus()==HttpStatus.SWITCHING_PROTOCOLS_101)
112                         {
113                             Connection switched=(Connection)_request.getAttribute("org.eclipse.jetty.io.Connection");
114                             if (switched!=null)
115                                 connection=switched;
116                         }
117 
118                         reset();
119 
120                         // TODO Is this still required?
121                         if (!_generator.isPersistent() && !_endp.isOutputShutdown())
122                         {
123                             LOG.warn("Safety net oshut!!!  IF YOU SEE THIS, PLEASE RAISE BUGZILLA");
124                             _endp.shutdownOutput();
125                         }
126                     }
127                     else if (_request.getAsyncContinuation().isAsyncStarted())
128                     {
129                         // The request is suspended, so even though progress has been made, break the while loop
130                         LOG.debug("suspended {}",this);
131                         // TODO: breaking inside finally blocks is bad: rethink how we should exit from here
132                         break;
133                     }
134                 }
135             }
136         }
137         finally
138         {
139             setCurrentConnection(null);
140             
141             // If we are not suspended
142             if (!_request.getAsyncContinuation().isAsyncStarted())
143             {
144                 // return buffers
145                 _parser.returnBuffers();
146                 _generator.returnBuffers();
147             }
148             
149             if (_request.getAsyncContinuation().isComplete() || _request.getAsyncContinuation().isInitial())
150             {
151                 _asyncEndp.setCheckForIdle(true);
152             }
153             
154             // Safety net to catch spinning
155             if (some_progress)
156                 _total_no_progress=0;
157             else
158             {
159                 _total_no_progress++;
160                 if (NO_PROGRESS_INFO>0 && _total_no_progress%NO_PROGRESS_INFO==0 && (NO_PROGRESS_CLOSE<=0 || _total_no_progress< NO_PROGRESS_CLOSE))
161                     LOG.info("EndPoint making no progress: "+_total_no_progress+" "+_endp+" "+this);
162                 if (NO_PROGRESS_CLOSE>0 && _total_no_progress==NO_PROGRESS_CLOSE)
163                 {
164                     LOG.warn("Closing EndPoint making no progress: "+_total_no_progress+" "+_endp+" "+this);
165                     if (_endp instanceof SelectChannelEndPoint)
166                         ((SelectChannelEndPoint)_endp).getChannel().close();
167                 }
168             }
169         }
170         return connection;
171     }
172 
173     public void onInputShutdown() throws IOException
174     {
175         // If we don't have a committed response and we are not suspended
176         if (_generator.isIdle() && !_request.getAsyncContinuation().isSuspended())
177         {
178             // then no more can happen, so close.
179             _endp.close();
180         }
181     }
182 
183 }