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.BlockingHttpConnection;
33  import org.eclipse.jetty.server.HttpConnection;
34  import org.eclipse.jetty.server.Request;
35  import org.eclipse.jetty.util.log.Log;
36  import org.eclipse.jetty.util.log.Logger;
37  
38  
39  /* ------------------------------------------------------------------------------- */
40  /**  Socket Connector.
41   * This connector implements a traditional blocking IO and threading model.
42   * Normal JRE sockets are used and a thread is allocated per connection.
43   * Buffers are managed so that large buffers are only allocated to active connections.
44   *
45   * This Connector should only be used if NIO is not available.
46   *
47   * @org.apache.xbean.XBean element="bioConnector" description="Creates a BIO based socket connector"
48   *
49   *
50   */
51  public class SocketConnector extends AbstractConnector
52  {
53      private static final Logger LOG = Log.getLogger(SocketConnector.class);
54  
55      protected ServerSocket _serverSocket;
56      protected final Set<EndPoint> _connections;
57      protected volatile int _localPort=-1;
58  
59      /* ------------------------------------------------------------ */
60      /** Constructor.
61       *
62       */
63      public SocketConnector()
64      {
65          _connections=new HashSet<EndPoint>();
66      }
67  
68      /* ------------------------------------------------------------ */
69      public Object getConnection()
70      {
71          return _serverSocket;
72      }
73  
74      /* ------------------------------------------------------------ */
75      public void open() throws IOException
76      {
77          // Create a new server socket and set to non blocking mode
78          if (_serverSocket==null || _serverSocket.isClosed())
79          _serverSocket= newServerSocket(getHost(),getPort(),getAcceptQueueSize());
80          _serverSocket.setReuseAddress(getReuseAddress());
81          _localPort=_serverSocket.getLocalPort();
82          if (_localPort<=0)
83              throw new IllegalStateException("port not allocated for "+this);
84              
85      }
86  
87      /* ------------------------------------------------------------ */
88      protected ServerSocket newServerSocket(String host, int port,int backlog) throws IOException
89      {
90          ServerSocket ss= host==null?
91              new ServerSocket(port,backlog):
92              new ServerSocket(port,backlog,InetAddress.getByName(host));
93  
94          return ss;
95      }
96  
97      /* ------------------------------------------------------------ */
98      public void close() throws IOException
99      {
100         if (_serverSocket!=null)
101             _serverSocket.close();
102         _serverSocket=null;
103         _localPort=-2;
104     }
105 
106     /* ------------------------------------------------------------ */
107     @Override
108     public void accept(int acceptorID)
109     	throws IOException, InterruptedException
110     {
111         Socket socket = _serverSocket.accept();
112         configure(socket);
113 
114         ConnectorEndPoint connection=new ConnectorEndPoint(socket);
115         connection.dispatch();
116     }
117 
118     /* ------------------------------------------------------------------------------- */
119     /**
120      * Allows subclass to override Conection if required.
121      */
122     protected Connection newConnection(EndPoint endpoint)
123     {
124         return new BlockingHttpConnection(this, endpoint, getServer());
125     }
126 
127     /* ------------------------------------------------------------------------------- */
128     @Override
129     public void customize(EndPoint endpoint, Request request)
130         throws IOException
131     {
132         ConnectorEndPoint connection = (ConnectorEndPoint)endpoint;
133         int lrmit = isLowResources()?_lowResourceMaxIdleTime:_maxIdleTime;
134         connection.setMaxIdleTime(lrmit);
135 
136         super.customize(endpoint, request);
137     }
138 
139     /* ------------------------------------------------------------------------------- */
140     public int getLocalPort()
141     {
142         return _localPort;
143     }
144 
145     /* ------------------------------------------------------------------------------- */
146     @Override
147     protected void doStart() throws Exception
148     {
149         _connections.clear();
150         super.doStart();
151     }
152 
153     /* ------------------------------------------------------------------------------- */
154     @Override
155     protected void doStop() throws Exception
156     {
157         super.doStop();
158         Set set=null;
159 
160         synchronized(_connections)
161         {
162             set= new HashSet(_connections);
163         }
164 
165         Iterator iter=set.iterator();
166         while(iter.hasNext())
167         {
168             ConnectorEndPoint connection = (ConnectorEndPoint)iter.next();
169             connection.close();
170         }
171     }
172 
173     /* ------------------------------------------------------------------------------- */
174     /* ------------------------------------------------------------------------------- */
175     /* ------------------------------------------------------------------------------- */
176     protected class ConnectorEndPoint extends SocketEndPoint implements Runnable, ConnectedEndPoint
177     {
178         boolean _dispatched=false;
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 (HttpException e)
255             {
256                 LOG.debug("BAD", e);
257                 try{close();}
258                 catch(IOException e2){LOG.ignore(e2);}
259             }
260             catch(Exception e)
261             {
262                 LOG.warn("handle failed?",e);
263                 try{close();}
264                 catch(IOException e2){LOG.ignore(e2);}
265             }
266             finally
267             {
268                 connectionClosed(_connection);
269                 synchronized(_connections)
270                 {
271                     _connections.remove(this);
272                 }
273 
274                 // wait for client to close, but if not, close ourselves.
275                 try
276                 {
277                     if (!_socket.isClosed())
278                     {
279                         long timestamp=System.currentTimeMillis();
280                         int max_idle=getMaxIdleTime(); 
281 
282                         _socket.setSoTimeout(getMaxIdleTime());
283                         int c=0;
284                         do
285                         {
286                             c = _socket.getInputStream().read();
287                         }
288                         while (c>=0 && (System.currentTimeMillis()-timestamp)<max_idle);
289                         if (!_socket.isClosed())
290                             _socket.close();
291                     }
292                 }
293                 catch(IOException e)
294                 {
295                     LOG.ignore(e);
296                 }
297             }
298         }
299     }
300 }