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