View Javadoc

1   // ========================================================================
2   // Copyright (c) 2003-2009 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.bio;
15  
16  import java.io.IOException;
17  import java.net.InetAddress;
18  import java.net.ServerSocket;
19  import java.net.Socket;
20  import java.util.HashSet;
21  import java.util.Iterator;
22  import java.util.Set;
23  
24  import org.eclipse.jetty.http.HttpException;
25  import org.eclipse.jetty.io.Buffer;
26  import org.eclipse.jetty.io.ConnectedEndPoint;
27  import org.eclipse.jetty.io.Connection;
28  import org.eclipse.jetty.io.EndPoint;
29  import org.eclipse.jetty.io.EofException;
30  import org.eclipse.jetty.io.bio.SocketEndPoint;
31  import org.eclipse.jetty.server.AbstractConnector;
32  import org.eclipse.jetty.server.HttpConnection;
33  import org.eclipse.jetty.server.Request;
34  import org.eclipse.jetty.util.log.Log;
35  
36  
37  /* ------------------------------------------------------------------------------- */
38  /**  Socket Connector.
39   * This connector implements a traditional blocking IO and threading model.
40   * Normal JRE sockets are used and a thread is allocated per connection.
41   * Buffers are managed so that large buffers are only allocated to active connections.
42   *
43   * This Connector should only be used if NIO is not available.
44   *
45   * @org.apache.xbean.XBean element="bioConnector" description="Creates a BIO based socket connector"
46   *
47   *
48   */
49  public class SocketConnector extends AbstractConnector
50  {
51      protected ServerSocket _serverSocket;
52      protected final Set<EndPoint> _connections;
53      protected volatile int _localPort=-1;
54  
55      /* ------------------------------------------------------------ */
56      /** Constructor.
57       *
58       */
59      public SocketConnector()
60      {
61          _connections=new HashSet<EndPoint>();
62      }
63  
64      /* ------------------------------------------------------------ */
65      public Object getConnection()
66      {
67          return _serverSocket;
68      }
69  
70      /* ------------------------------------------------------------ */
71      public void open() throws IOException
72      {
73          // Create a new server socket and set to non blocking mode
74          if (_serverSocket==null || _serverSocket.isClosed())
75          _serverSocket= newServerSocket(getHost(),getPort(),getAcceptQueueSize());
76          _serverSocket.setReuseAddress(getReuseAddress());
77          _localPort=_serverSocket.getLocalPort();
78          if (_localPort<=0)
79              throw new IllegalStateException("port not allocated for "+this);
80              
81      }
82  
83      /* ------------------------------------------------------------ */
84      protected ServerSocket newServerSocket(String host, int port,int backlog) throws IOException
85      {
86          ServerSocket ss= host==null?
87              new ServerSocket(port,backlog):
88              new ServerSocket(port,backlog,InetAddress.getByName(host));
89  
90          return ss;
91      }
92  
93      /* ------------------------------------------------------------ */
94      public void close() throws IOException
95      {
96          if (_serverSocket!=null)
97              _serverSocket.close();
98          _serverSocket=null;
99          _localPort=-2;
100     }
101 
102     /* ------------------------------------------------------------ */
103     @Override
104     public void accept(int acceptorID)
105     	throws IOException, InterruptedException
106     {
107         Socket socket = _serverSocket.accept();
108         configure(socket);
109 
110         ConnectorEndPoint connection=new ConnectorEndPoint(socket);
111         connection.dispatch();
112     }
113 
114     /* ------------------------------------------------------------------------------- */
115     /**
116      * Allows subclass to override Conection if required.
117      */
118     protected Connection newConnection(EndPoint endpoint)
119     {
120         return new HttpConnection(this, endpoint, getServer());
121     }
122 
123     /* ------------------------------------------------------------------------------- */
124     @Override
125     public void customize(EndPoint endpoint, Request request)
126         throws IOException
127     {
128         ConnectorEndPoint connection = (ConnectorEndPoint)endpoint;
129         int lrmit = isLowResources()?_lowResourceMaxIdleTime:_maxIdleTime;
130         connection.setMaxIdleTime(lrmit);
131 
132         super.customize(endpoint, request);
133     }
134 
135     /* ------------------------------------------------------------------------------- */
136     public int getLocalPort()
137     {
138         return _localPort;
139     }
140 
141     /* ------------------------------------------------------------------------------- */
142     @Override
143     protected void doStart() throws Exception
144     {
145         _connections.clear();
146         super.doStart();
147     }
148 
149     /* ------------------------------------------------------------------------------- */
150     @Override
151     protected void doStop() throws Exception
152     {
153         super.doStop();
154         Set set=null;
155 
156         synchronized(_connections)
157         {
158             set= new HashSet(_connections);
159         }
160 
161         Iterator iter=set.iterator();
162         while(iter.hasNext())
163         {
164             ConnectorEndPoint connection = (ConnectorEndPoint)iter.next();
165             connection.close();
166         }
167     }
168 
169     /* ------------------------------------------------------------------------------- */
170     /* ------------------------------------------------------------------------------- */
171     /* ------------------------------------------------------------------------------- */
172     protected class ConnectorEndPoint extends SocketEndPoint implements Runnable, ConnectedEndPoint
173     {
174         boolean _dispatched=false;
175         volatile Connection _connection;
176         protected final Socket _socket;
177 
178         public ConnectorEndPoint(Socket socket) throws IOException
179         {
180             super(socket,_maxIdleTime);
181             _connection = newConnection(this);
182             _socket=socket;
183         }
184 
185         public Connection getConnection()
186         {
187             return _connection;
188         }
189 
190         public void setConnection(Connection connection)
191         {
192             if (_connection!=connection)
193                 connectionUpgraded(_connection,connection);
194             _connection=connection;
195         }
196 
197         public void dispatch() throws IOException
198         {
199             if (getThreadPool()==null || !getThreadPool().dispatch(this))
200             {
201                 Log.warn("dispatch failed for {}",_connection);
202                 close();
203             }
204         }
205 
206         @Override
207         public int fill(Buffer buffer) throws IOException
208         {
209             int l = super.fill(buffer);
210             if (l<0)
211                 close();
212             return l;
213         }
214 
215         @Override
216         public void close() throws IOException
217         {
218             if (_connection instanceof HttpConnection)
219                 ((HttpConnection)_connection).getRequest().getAsyncContinuation().cancel();
220             super.close();
221         }
222 
223         public void run()
224         {
225             try
226             {
227                 connectionOpened(_connection);
228                 synchronized(_connections)
229                 {
230                     _connections.add(this);
231                 }
232 
233                 while (isStarted() && !isClosed())
234                 {
235                     if (_connection.isIdle())
236                     {
237                         if (isLowResources())
238                             setMaxIdleTime(getLowResourcesMaxIdleTime());
239                     }
240 
241                     _connection=_connection.handle();
242                 }
243             }
244             catch (EofException e)
245             {
246                 Log.debug("EOF", e);
247                 try{close();}
248                 catch(IOException e2){Log.ignore(e2);}
249             }
250             catch (HttpException e)
251             {
252                 Log.debug("BAD", e);
253                 try{close();}
254                 catch(IOException e2){Log.ignore(e2);}
255             }
256             catch(Exception e)
257             {
258                 Log.warn("handle failed?",e);
259                 try{close();}
260                 catch(IOException e2){Log.ignore(e2);}
261             }
262             finally
263             {
264                 connectionClosed(_connection);
265                 synchronized(_connections)
266                 {
267                     _connections.remove(this);
268                 }
269 
270                 // wait for client to close, but if not, close ourselves.
271                 try
272                 {
273                     if (!_socket.isClosed())
274                     {
275                         long timestamp=System.currentTimeMillis();
276                         int max_idle=getMaxIdleTime(); 
277 
278                         _socket.setSoTimeout(getMaxIdleTime());
279                         int c=0;
280                         do
281                         {
282                             c = _socket.getInputStream().read();
283                         }
284                         while (c>=0 && (System.currentTimeMillis()-timestamp)<max_idle);
285                         if (!_socket.isClosed())
286                             _socket.close();
287                     }
288                 }
289                 catch(IOException e)
290                 {
291                     Log.ignore(e);
292                 }
293             }
294         }
295     }
296 }