blob: 337af1193adaf0640ac21e65c2e32c8e219ba427 [file] [log] [blame]
Jake Slack03928ae2014-05-13 18:41:56 -07001//
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
19package org.eclipse.jetty.server.bio;
20
21import java.io.IOException;
22import java.net.InetAddress;
23import java.net.ServerSocket;
24import java.net.Socket;
25import java.net.SocketException;
26import java.util.HashSet;
27import java.util.Set;
28
29import org.eclipse.jetty.http.HttpException;
30import org.eclipse.jetty.io.Buffer;
31import org.eclipse.jetty.io.ConnectedEndPoint;
32import org.eclipse.jetty.io.Connection;
33import org.eclipse.jetty.io.EndPoint;
34import org.eclipse.jetty.io.EofException;
35import org.eclipse.jetty.io.bio.SocketEndPoint;
36import org.eclipse.jetty.server.AbstractConnector;
37import org.eclipse.jetty.server.AbstractHttpConnection;
38import org.eclipse.jetty.server.BlockingHttpConnection;
39import org.eclipse.jetty.server.Request;
40import org.eclipse.jetty.util.component.AggregateLifeCycle;
41import org.eclipse.jetty.util.log.Log;
42import 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 */
57public 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 */
69 public SocketConnector()
70 {
71 _connections=new HashSet<EndPoint>();
72 }
73
74 /* ------------------------------------------------------------ */
75 public Object getConnection()
76 {
77 return _serverSocket;
78 }
79
80 /* ------------------------------------------------------------ */
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 /* ------------------------------------------------------------ */
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 /* ------------------------------------------------------------ */
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
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 */
128 protected Connection newConnection(EndPoint endpoint)
129 {
130 return new BlockingHttpConnection(this, endpoint, getServer());
131 }
132
133 /* ------------------------------------------------------------------------------- */
134 @Override
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 /* ------------------------------------------------------------------------------- */
146 public int getLocalPort()
147 {
148 return _localPort;
149 }
150
151 /* ------------------------------------------------------------------------------- */
152 @Override
153 protected void doStart() throws Exception
154 {
155 _connections.clear();
156 super.doStart();
157 }
158
159 /* ------------------------------------------------------------------------------- */
160 @Override
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
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
196 public ConnectorEndPoint(Socket socket) throws IOException
197 {
198 super(socket,_maxIdleTime);
199 _connection = newConnection(this);
200 _socket=socket;
201 }
202
203 public Connection getConnection()
204 {
205 return _connection;
206 }
207
208 public void setConnection(Connection connection)
209 {
210 if (_connection!=connection && _connection!=null)
211 connectionUpgraded(_connection,connection);
212 _connection=connection;
213 }
214
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
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
239 public void close() throws IOException
240 {
241 if (_connection instanceof AbstractHttpConnection)
242 ((AbstractHttpConnection)_connection).getRequest().getAsyncContinuation().cancel();
243 super.close();
244 }
245
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}