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