1 // ========================================================================
2 // Copyright (c) 2004-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;
15
16 import java.io.IOException;
17 import java.net.InetAddress;
18 import java.net.Socket;
19 import java.net.UnknownHostException;
20 import java.util.concurrent.atomic.AtomicLong;
21
22 import javax.servlet.ServletRequest;
23
24 import org.eclipse.jetty.http.HttpBuffers;
25 import org.eclipse.jetty.http.HttpFields;
26 import org.eclipse.jetty.http.HttpHeaders;
27 import org.eclipse.jetty.http.HttpSchemes;
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.util.component.AbstractLifeCycle;
32 import org.eclipse.jetty.util.component.AggregateLifeCycle;
33 import org.eclipse.jetty.util.component.Dumpable;
34 import org.eclipse.jetty.util.component.LifeCycle;
35 import org.eclipse.jetty.util.log.Log;
36 import org.eclipse.jetty.util.log.Logger;
37 import org.eclipse.jetty.util.statistic.CounterStatistic;
38 import org.eclipse.jetty.util.statistic.SampleStatistic;
39 import org.eclipse.jetty.util.thread.ThreadPool;
40
41 /**
42 * Abstract Connector implementation. This abstract implementation of the Connector interface provides:
43 * <ul>
44 * <li>AbstractLifeCycle implementation</li>
45 * <li>Implementations for connector getters and setters</li>
46 * <li>Buffer management</li>
47 * <li>Socket configuration</li>
48 * <li>Base acceptor thread</li>
49 * <li>Optional reverse proxy headers checking</li>
50 * </ul>
51 *
52 *
53 */
54 public abstract class AbstractConnector extends HttpBuffers implements Connector, Dumpable
55 {
56 private static final Logger LOG = Log.getLogger(AbstractConnector.class);
57
58 private String _name;
59
60 private Server _server;
61 private ThreadPool _threadPool;
62 private String _host;
63 private int _port = 0;
64 private String _integralScheme = HttpSchemes.HTTPS;
65 private int _integralPort = 0;
66 private String _confidentialScheme = HttpSchemes.HTTPS;
67 private int _confidentialPort = 0;
68 private int _acceptQueueSize = 0;
69 private int _acceptors = 1;
70 private int _acceptorPriorityOffset = 0;
71 private boolean _useDNS;
72 private boolean _forwarded;
73 private String _hostHeader;
74
75 private String _forwardedHostHeader = HttpHeaders.X_FORWARDED_HOST;
76 private String _forwardedServerHeader = HttpHeaders.X_FORWARDED_SERVER;
77 private String _forwardedForHeader = HttpHeaders.X_FORWARDED_FOR;
78 private String _forwardedProtoHeader = HttpHeaders.X_FORWARDED_PROTO;
79 private String _forwardedCipherSuiteHeader;
80 private String _forwardedSslSessionIdHeader;
81 private boolean _reuseAddress = true;
82
83 protected int _maxIdleTime = 200000;
84 protected int _lowResourceMaxIdleTime = -1;
85 protected int _soLingerTime = -1;
86
87 private transient Thread[] _acceptorThread;
88
89 private final AtomicLong _statsStartedAt = new AtomicLong(-1L);
90
91 /** connections to server */
92 private final CounterStatistic _connectionStats = new CounterStatistic();
93 /** requests per connection */
94 private final SampleStatistic _requestStats = new SampleStatistic();
95 /** duration of a connection */
96 private final SampleStatistic _connectionDurationStats = new SampleStatistic();
97
98 /* ------------------------------------------------------------ */
99 /**
100 */
101 public AbstractConnector()
102 {
103 }
104
105 /* ------------------------------------------------------------ */
106 /*
107 */
108 public Server getServer()
109 {
110 return _server;
111 }
112
113 /* ------------------------------------------------------------ */
114 public void setServer(Server server)
115 {
116 _server = server;
117 }
118
119 /* ------------------------------------------------------------ */
120 public ThreadPool getThreadPool()
121 {
122 return _threadPool;
123 }
124
125 /* ------------------------------------------------------------ */
126 public void setThreadPool(ThreadPool pool)
127 {
128 _threadPool = pool;
129 }
130
131 /* ------------------------------------------------------------ */
132 /**
133 */
134 public void setHost(String host)
135 {
136 _host = host;
137 }
138
139 /* ------------------------------------------------------------ */
140 /*
141 */
142 public String getHost()
143 {
144 return _host;
145 }
146
147 /* ------------------------------------------------------------ */
148 public void setPort(int port)
149 {
150 _port = port;
151 }
152
153 /* ------------------------------------------------------------ */
154 public int getPort()
155 {
156 return _port;
157 }
158
159 /* ------------------------------------------------------------ */
160 /**
161 * @return Returns the maxIdleTime.
162 */
163 public int getMaxIdleTime()
164 {
165 return _maxIdleTime;
166 }
167
168 /* ------------------------------------------------------------ */
169 /**
170 * Set the maximum Idle time for a connection, which roughly translates to the {@link Socket#setSoTimeout(int)} call, although with NIO implementations
171 * other mechanisms may be used to implement the timeout. The max idle time is applied:
172 * <ul>
173 * <li>When waiting for a new request to be received on a connection</li>
174 * <li>When reading the headers and content of a request</li>
175 * <li>When writing the headers and content of a response</li>
176 * </ul>
177 * Jetty interprets this value as the maximum time between some progress being made on the connection. So if a single byte is read or written, then the
178 * timeout (if implemented by jetty) is reset. However, in many instances, the reading/writing is delegated to the JVM, and the semantic is more strictly
179 * enforced as the maximum time a single read/write operation can take. Note, that as Jetty supports writes of memory mapped file buffers, then a write may
180 * take many 10s of seconds for large content written to a slow device.
181 * <p>
182 * Previously, Jetty supported separate idle timeouts and IO operation timeouts, however the expense of changing the value of soTimeout was significant, so
183 * these timeouts were merged. With the advent of NIO, it may be possible to again differentiate these values (if there is demand).
184 *
185 * @param maxIdleTime
186 * The maxIdleTime to set.
187 */
188 public void setMaxIdleTime(int maxIdleTime)
189 {
190 _maxIdleTime = maxIdleTime;
191 }
192
193 /* ------------------------------------------------------------ */
194 /**
195 * @return Returns the maxIdleTime when resources are low.
196 */
197 public int getLowResourcesMaxIdleTime()
198 {
199 return _lowResourceMaxIdleTime;
200 }
201
202 /* ------------------------------------------------------------ */
203 /**
204 * @param maxIdleTime
205 * The maxIdleTime to set when resources are low.
206 */
207 public void setLowResourcesMaxIdleTime(int maxIdleTime)
208 {
209 _lowResourceMaxIdleTime = maxIdleTime;
210 }
211
212 /* ------------------------------------------------------------ */
213 /**
214 * @return Returns the maxIdleTime when resources are low.
215 * @deprecated
216 */
217 public final int getLowResourceMaxIdleTime()
218 {
219 return getLowResourcesMaxIdleTime();
220 }
221
222 /* ------------------------------------------------------------ */
223 /**
224 * @param maxIdleTime
225 * The maxIdleTime to set when resources are low.
226 * @deprecated
227 */
228 public final void setLowResourceMaxIdleTime(int maxIdleTime)
229 {
230 setLowResourcesMaxIdleTime(maxIdleTime);
231 }
232
233 /* ------------------------------------------------------------ */
234 /**
235 * @return Returns the soLingerTime.
236 */
237 public int getSoLingerTime()
238 {
239 return _soLingerTime;
240 }
241
242 /* ------------------------------------------------------------ */
243 /**
244 * @return Returns the acceptQueueSize.
245 */
246 public int getAcceptQueueSize()
247 {
248 return _acceptQueueSize;
249 }
250
251 /* ------------------------------------------------------------ */
252 /**
253 * @param acceptQueueSize
254 * The acceptQueueSize to set.
255 */
256 public void setAcceptQueueSize(int acceptQueueSize)
257 {
258 _acceptQueueSize = acceptQueueSize;
259 }
260
261 /* ------------------------------------------------------------ */
262 /**
263 * @return Returns the number of acceptor threads.
264 */
265 public int getAcceptors()
266 {
267 return _acceptors;
268 }
269
270 /* ------------------------------------------------------------ */
271 /**
272 * @param acceptors
273 * The number of acceptor threads to set.
274 */
275 public void setAcceptors(int acceptors)
276 {
277 if (acceptors > 2 * Runtime.getRuntime().availableProcessors())
278 LOG.warn("Acceptors should be <=2*availableProcessors: " + this);
279 _acceptors = acceptors;
280 }
281
282 /* ------------------------------------------------------------ */
283 /**
284 * @param soLingerTime
285 * The soLingerTime to set or -1 to disable.
286 */
287 public void setSoLingerTime(int soLingerTime)
288 {
289 _soLingerTime = soLingerTime;
290 }
291
292 /* ------------------------------------------------------------ */
293 @Override
294 protected void doStart() throws Exception
295 {
296 if (_server == null)
297 throw new IllegalStateException("No server");
298
299 // open listener port
300 open();
301
302 super.doStart();
303
304 if (_threadPool == null)
305 _threadPool = _server.getThreadPool();
306 if (_threadPool != _server.getThreadPool() && (_threadPool instanceof LifeCycle))
307 ((LifeCycle)_threadPool).start();
308
309 // Start selector thread
310 synchronized (this)
311 {
312 _acceptorThread = new Thread[getAcceptors()];
313
314 for (int i = 0; i < _acceptorThread.length; i++)
315 _threadPool.dispatch(new Acceptor(i));
316 if (_threadPool.isLowOnThreads())
317 LOG.warn("insufficient threads configured for {}",this);
318 }
319
320 LOG.info("Started {}",this);
321 }
322
323 /* ------------------------------------------------------------ */
324 @Override
325 protected void doStop() throws Exception
326 {
327 try
328 {
329 close();
330 }
331 catch (IOException e)
332 {
333 LOG.warn(e);
334 }
335
336 if (_threadPool != _server.getThreadPool() && _threadPool instanceof LifeCycle)
337 ((LifeCycle)_threadPool).stop();
338
339 super.doStop();
340
341 Thread[] acceptors = null;
342 synchronized (this)
343 {
344 acceptors = _acceptorThread;
345 _acceptorThread = null;
346 }
347 if (acceptors != null)
348 {
349 for (int i = 0; i < acceptors.length; i++)
350 {
351 Thread thread = acceptors[i];
352 if (thread != null)
353 thread.interrupt();
354 }
355 }
356 }
357
358 /* ------------------------------------------------------------ */
359 public void join() throws InterruptedException
360 {
361 Thread[] threads = _acceptorThread;
362 if (threads != null)
363 for (int i = 0; i < threads.length; i++)
364 if (threads[i] != null)
365 threads[i].join();
366 }
367
368 /* ------------------------------------------------------------ */
369 protected void configure(Socket socket) throws IOException
370 {
371 try
372 {
373 socket.setTcpNoDelay(true);
374 if (_soLingerTime >= 0)
375 socket.setSoLinger(true,_soLingerTime / 1000);
376 else
377 socket.setSoLinger(false,0);
378 }
379 catch (Exception e)
380 {
381 LOG.ignore(e);
382 }
383 }
384
385 /* ------------------------------------------------------------ */
386 public void customize(EndPoint endpoint, Request request) throws IOException
387 {
388 if (isForwarded())
389 checkForwardedHeaders(endpoint,request);
390 }
391
392 /* ------------------------------------------------------------ */
393 protected void checkForwardedHeaders(EndPoint endpoint, Request request) throws IOException
394 {
395 HttpFields httpFields = request.getConnection().getRequestFields();
396
397 // Do SSL first
398 if (getForwardedCipherSuiteHeader()!=null)
399 {
400 String cipher_suite=httpFields.getStringField(getForwardedCipherSuiteHeader());
401 if (cipher_suite!=null)
402 request.setAttribute("javax.servlet.request.cipher_suite",cipher_suite);
403 }
404 if (getForwardedSslSessionIdHeader()!=null)
405 {
406 String ssl_session_id=httpFields.getStringField(getForwardedSslSessionIdHeader());
407 if(ssl_session_id!=null)
408 {
409 request.setAttribute("javax.servlet.request.ssl_session_id", ssl_session_id);
410 request.setScheme(HttpSchemes.HTTPS);
411 }
412 }
413
414 // Retrieving headers from the request
415 String forwardedHost = getLeftMostFieldValue(httpFields,getForwardedHostHeader());
416 String forwardedServer = getLeftMostFieldValue(httpFields,getForwardedServerHeader());
417 String forwardedFor = getLeftMostFieldValue(httpFields,getForwardedForHeader());
418 String forwardedProto = getLeftMostFieldValue(httpFields,getForwardedProtoHeader());
419
420 if (_hostHeader != null)
421 {
422 // Update host header
423 httpFields.put(HttpHeaders.HOST_BUFFER,_hostHeader);
424 request.setServerName(null);
425 request.setServerPort(-1);
426 request.getServerName();
427 }
428 else if (forwardedHost != null)
429 {
430 // Update host header
431 httpFields.put(HttpHeaders.HOST_BUFFER,forwardedHost);
432 request.setServerName(null);
433 request.setServerPort(-1);
434 request.getServerName();
435 }
436 else if (forwardedServer != null)
437 {
438 // Use provided server name
439 request.setServerName(forwardedServer);
440 }
441
442 if (forwardedFor != null)
443 {
444 request.setRemoteAddr(forwardedFor);
445 InetAddress inetAddress = null;
446
447 if (_useDNS)
448 {
449 try
450 {
451 inetAddress = InetAddress.getByName(forwardedFor);
452 }
453 catch (UnknownHostException e)
454 {
455 LOG.ignore(e);
456 }
457 }
458
459 request.setRemoteHost(inetAddress == null?forwardedFor:inetAddress.getHostName());
460 }
461
462 if (forwardedProto != null)
463 {
464 request.setScheme(forwardedProto);
465 }
466 }
467
468 /* ------------------------------------------------------------ */
469 protected String getLeftMostFieldValue(HttpFields fields, String header)
470 {
471 if (header == null)
472 return null;
473
474 String headerValue = fields.getStringField(header);
475
476 if (headerValue == null)
477 return null;
478
479 int commaIndex = headerValue.indexOf(',');
480
481 if (commaIndex == -1)
482 {
483 // Single value
484 return headerValue;
485 }
486
487 // The left-most value is the farthest downstream client
488 return headerValue.substring(0,commaIndex);
489 }
490
491 /* ------------------------------------------------------------ */
492 public void persist(EndPoint endpoint) throws IOException
493 {
494 }
495
496 /* ------------------------------------------------------------ */
497 /*
498 * @see org.eclipse.jetty.server.Connector#getConfidentialPort()
499 */
500 public int getConfidentialPort()
501 {
502 return _confidentialPort;
503 }
504
505 /* ------------------------------------------------------------ */
506 /* ------------------------------------------------------------ */
507 /*
508 * @see org.eclipse.jetty.server.Connector#getConfidentialScheme()
509 */
510 public String getConfidentialScheme()
511 {
512 return _confidentialScheme;
513 }
514
515 /* ------------------------------------------------------------ */
516 /*
517 * @see org.eclipse.jetty.server.Connector#isConfidential(org.eclipse.jetty.server .Request)
518 */
519 public boolean isIntegral(Request request)
520 {
521 return false;
522 }
523
524 /* ------------------------------------------------------------ */
525 /*
526 * @see org.eclipse.jetty.server.Connector#getConfidentialPort()
527 */
528 public int getIntegralPort()
529 {
530 return _integralPort;
531 }
532
533 /* ------------------------------------------------------------ */
534 /*
535 * @see org.eclipse.jetty.server.Connector#getIntegralScheme()
536 */
537 public String getIntegralScheme()
538 {
539 return _integralScheme;
540 }
541
542 /* ------------------------------------------------------------ */
543 /*
544 * @see org.eclipse.jetty.server.Connector#isConfidential(org.eclipse.jetty.server.Request)
545 */
546 public boolean isConfidential(Request request)
547 {
548 return _forwarded && request.getScheme().equalsIgnoreCase(HttpSchemes.HTTPS);
549 }
550
551 /* ------------------------------------------------------------ */
552 /**
553 * @param confidentialPort
554 * The confidentialPort to set.
555 */
556 public void setConfidentialPort(int confidentialPort)
557 {
558 _confidentialPort = confidentialPort;
559 }
560
561 /* ------------------------------------------------------------ */
562 /**
563 * @param confidentialScheme
564 * The confidentialScheme to set.
565 */
566 public void setConfidentialScheme(String confidentialScheme)
567 {
568 _confidentialScheme = confidentialScheme;
569 }
570
571 /* ------------------------------------------------------------ */
572 /**
573 * @param integralPort
574 * The integralPort to set.
575 */
576 public void setIntegralPort(int integralPort)
577 {
578 _integralPort = integralPort;
579 }
580
581 /* ------------------------------------------------------------ */
582 /**
583 * @param integralScheme
584 * The integralScheme to set.
585 */
586 public void setIntegralScheme(String integralScheme)
587 {
588 _integralScheme = integralScheme;
589 }
590
591 /* ------------------------------------------------------------ */
592 protected abstract void accept(int acceptorID) throws IOException, InterruptedException;
593
594 /* ------------------------------------------------------------ */
595 public void stopAccept(int acceptorID) throws Exception
596 {
597 }
598
599 /* ------------------------------------------------------------ */
600 public boolean getResolveNames()
601 {
602 return _useDNS;
603 }
604
605 /* ------------------------------------------------------------ */
606 public void setResolveNames(boolean resolve)
607 {
608 _useDNS = resolve;
609 }
610
611 /* ------------------------------------------------------------ */
612 /**
613 * Is reverse proxy handling on?
614 *
615 * @return true if this connector is checking the x-forwarded-for/host/server headers
616 */
617 public boolean isForwarded()
618 {
619 return _forwarded;
620 }
621
622 /* ------------------------------------------------------------ */
623 /**
624 * Set reverse proxy handling. If set to true, then the X-Forwarded headers (or the headers set in their place) are looked for to set the request protocol,
625 * host, server and client ip.
626 *
627 * @param check
628 * true if this connector is checking the x-forwarded-for/host/server headers
629 * @set {@link #setForwardedForHeader(String)}
630 * @set {@link #setForwardedHostHeader(String)}
631 * @set {@link #setForwardedProtoHeader(String)}
632 * @set {@link #setForwardedServerHeader(String)}
633 */
634 public void setForwarded(boolean check)
635 {
636 if (check)
637 LOG.debug(this + " is forwarded");
638 _forwarded = check;
639 }
640
641 /* ------------------------------------------------------------ */
642 public String getHostHeader()
643 {
644 return _hostHeader;
645 }
646
647 /* ------------------------------------------------------------ */
648 /**
649 * Set a forced valued for the host header to control what is returned by {@link ServletRequest#getServerName()} and {@link ServletRequest#getServerPort()}.
650 * This value is only used if {@link #isForwarded()} is true.
651 *
652 * @param hostHeader
653 * The value of the host header to force.
654 */
655 public void setHostHeader(String hostHeader)
656 {
657 _hostHeader = hostHeader;
658 }
659
660 /* ------------------------------------------------------------ */
661 /*
662 *
663 * @see #setForwarded(boolean)
664 */
665 public String getForwardedHostHeader()
666 {
667 return _forwardedHostHeader;
668 }
669
670 /* ------------------------------------------------------------ */
671 /**
672 * @param forwardedHostHeader
673 * The header name for forwarded hosts (default x-forwarded-host)
674 * @see #setForwarded(boolean)
675 */
676 public void setForwardedHostHeader(String forwardedHostHeader)
677 {
678 _forwardedHostHeader = forwardedHostHeader;
679 }
680
681 /* ------------------------------------------------------------ */
682 /**
683 * @return the header name for forwarded server.
684 * @see #setForwarded(boolean)
685 */
686 public String getForwardedServerHeader()
687 {
688 return _forwardedServerHeader;
689 }
690
691 /* ------------------------------------------------------------ */
692 /**
693 * @param forwardedServerHeader
694 * The header name for forwarded server (default x-forwarded-server)
695 * @see #setForwarded(boolean)
696 */
697 public void setForwardedServerHeader(String forwardedServerHeader)
698 {
699 _forwardedServerHeader = forwardedServerHeader;
700 }
701
702 /* ------------------------------------------------------------ */
703 /**
704 * @see #setForwarded(boolean)
705 */
706 public String getForwardedForHeader()
707 {
708 return _forwardedForHeader;
709 }
710
711 /* ------------------------------------------------------------ */
712 /**
713 * @param forwardedRemoteAddressHeader
714 * The header name for forwarded for (default x-forwarded-for)
715 * @see #setForwarded(boolean)
716 */
717 public void setForwardedForHeader(String forwardedRemoteAddressHeader)
718 {
719 _forwardedForHeader = forwardedRemoteAddressHeader;
720 }
721
722 /* ------------------------------------------------------------ */
723 /**
724 * Get the forwardedProtoHeader.
725 *
726 * @return the forwardedProtoHeader (default X-Forwarded-For)
727 * @see #setForwarded(boolean)
728 */
729 public String getForwardedProtoHeader()
730 {
731 return _forwardedProtoHeader;
732 }
733
734 /* ------------------------------------------------------------ */
735 /**
736 * Set the forwardedProtoHeader.
737 *
738 * @param forwardedProtoHeader
739 * the forwardedProtoHeader to set (default X-Forwarded-For)
740 * @see #setForwarded(boolean)
741 */
742 public void setForwardedProtoHeader(String forwardedProtoHeader)
743 {
744 _forwardedProtoHeader = forwardedProtoHeader;
745 }
746
747 /* ------------------------------------------------------------ */
748 /**
749 * @return The header name holding a forwarded cipher suite (default null)
750 */
751 public String getForwardedCipherSuiteHeader()
752 {
753 return _forwardedCipherSuiteHeader;
754 }
755
756 /* ------------------------------------------------------------ */
757 /**
758 * @param forwardedCipherSuite
759 * The header name holding a forwarded cipher suite (default null)
760 */
761 public void setForwardedCipherSuiteHeader(String forwardedCipherSuite)
762 {
763 _forwardedCipherSuiteHeader = forwardedCipherSuite;
764 }
765
766 /* ------------------------------------------------------------ */
767 /**
768 * @return The header name holding a forwarded SSL Session ID (default null)
769 */
770 public String getForwardedSslSessionIdHeader()
771 {
772 return _forwardedSslSessionIdHeader;
773 }
774
775 /* ------------------------------------------------------------ */
776 /**
777 * @param forwardedSslSessionId
778 * The header name holding a forwarded SSL Session ID (default null)
779 */
780 public void setForwardedSslSessionIdHeader(String forwardedSslSessionId)
781 {
782 _forwardedSslSessionIdHeader = forwardedSslSessionId;
783 }
784
785 /* ------------------------------------------------------------ */
786 @Override
787 public String toString()
788 {
789 String name = this.getClass().getName();
790 int dot = name.lastIndexOf('.');
791 if (dot > 0)
792 name = name.substring(dot + 1);
793
794 return name + "@" + (getHost() == null?"0.0.0.0":getHost()) + ":" + (getLocalPort() <= 0?getPort():getLocalPort()) + " "
795 + AbstractLifeCycle.getState(this);
796 }
797
798 /* ------------------------------------------------------------ */
799 /* ------------------------------------------------------------ */
800 /* ------------------------------------------------------------ */
801 private class Acceptor implements Runnable
802 {
803 int _acceptor = 0;
804
805 Acceptor(int id)
806 {
807 _acceptor = id;
808 }
809
810 /* ------------------------------------------------------------ */
811 public void run()
812 {
813 Thread current = Thread.currentThread();
814 String name;
815 synchronized (AbstractConnector.this)
816 {
817 if (_acceptorThread == null)
818 return;
819
820 _acceptorThread[_acceptor] = current;
821 name = _acceptorThread[_acceptor].getName();
822 current.setName(name + " Acceptor" + _acceptor + " " + AbstractConnector.this);
823 }
824 int old_priority = current.getPriority();
825
826 try
827 {
828 current.setPriority(old_priority - _acceptorPriorityOffset);
829 while (isRunning() && getConnection() != null)
830 {
831 try
832 {
833 accept(_acceptor);
834 }
835 catch (EofException e)
836 {
837 LOG.ignore(e);
838 }
839 catch (IOException e)
840 {
841 LOG.ignore(e);
842 }
843 catch (InterruptedException x)
844 {
845 // Connector has been stopped
846 LOG.ignore(x);
847 }
848 catch (ThreadDeath e)
849 {
850 throw e;
851 }
852 catch (Throwable e)
853 {
854 LOG.warn(e);
855 }
856 }
857 }
858 finally
859 {
860 current.setPriority(old_priority);
861 current.setName(name);
862
863 synchronized (AbstractConnector.this)
864 {
865 if (_acceptorThread != null)
866 _acceptorThread[_acceptor] = null;
867 }
868 }
869 }
870 }
871
872 /* ------------------------------------------------------------ */
873 public String getName()
874 {
875 if (_name == null)
876 _name = (getHost() == null?"0.0.0.0":getHost()) + ":" + (getLocalPort() <= 0?getPort():getLocalPort());
877 return _name;
878 }
879
880 /* ------------------------------------------------------------ */
881 public void setName(String name)
882 {
883 _name = name;
884 }
885
886 /* ------------------------------------------------------------ */
887 /**
888 * @return Get the number of requests handled by this connector since last call of statsReset(). If setStatsOn(false) then this is undefined.
889 */
890 public int getRequests()
891 {
892 return (int)_requestStats.getTotal();
893 }
894
895 /* ------------------------------------------------------------ */
896 /**
897 * @return Returns the connectionsDurationTotal.
898 */
899 public long getConnectionsDurationTotal()
900 {
901 return _connectionDurationStats.getTotal();
902 }
903
904 /* ------------------------------------------------------------ */
905 /**
906 * @return Number of connections accepted by the server since statsReset() called. Undefined if setStatsOn(false).
907 */
908 public int getConnections()
909 {
910 return (int)_connectionStats.getTotal();
911 }
912
913 /* ------------------------------------------------------------ */
914 /**
915 * @return Number of connections currently open that were opened since statsReset() called. Undefined if setStatsOn(false).
916 */
917 public int getConnectionsOpen()
918 {
919 return (int)_connectionStats.getCurrent();
920 }
921
922 /* ------------------------------------------------------------ */
923 /**
924 * @return Maximum number of connections opened simultaneously since statsReset() called. Undefined if setStatsOn(false).
925 */
926 public int getConnectionsOpenMax()
927 {
928 return (int)_connectionStats.getMax();
929 }
930
931 /* ------------------------------------------------------------ */
932 /**
933 * @return Mean duration in milliseconds of open connections since statsReset() called. Undefined if setStatsOn(false).
934 */
935 public double getConnectionsDurationMean()
936 {
937 return _connectionDurationStats.getMean();
938 }
939
940 /* ------------------------------------------------------------ */
941 /**
942 * @return Maximum duration in milliseconds of an open connection since statsReset() called. Undefined if setStatsOn(false).
943 */
944 public long getConnectionsDurationMax()
945 {
946 return _connectionDurationStats.getMax();
947 }
948
949 /* ------------------------------------------------------------ */
950 /**
951 * @return Standard deviation of duration in milliseconds of open connections since statsReset() called. Undefined if setStatsOn(false).
952 */
953 public double getConnectionsDurationStdDev()
954 {
955 return _connectionDurationStats.getStdDev();
956 }
957
958 /* ------------------------------------------------------------ */
959 /**
960 * @return Mean number of requests per connection since statsReset() called. Undefined if setStatsOn(false).
961 */
962 public double getConnectionsRequestsMean()
963 {
964 return _requestStats.getMean();
965 }
966
967 /* ------------------------------------------------------------ */
968 /**
969 * @return Maximum number of requests per connection since statsReset() called. Undefined if setStatsOn(false).
970 */
971 public int getConnectionsRequestsMax()
972 {
973 return (int)_requestStats.getMax();
974 }
975
976 /* ------------------------------------------------------------ */
977 /**
978 * @return Standard deviation of number of requests per connection since statsReset() called. Undefined if setStatsOn(false).
979 */
980 public double getConnectionsRequestsStdDev()
981 {
982 return _requestStats.getStdDev();
983 }
984
985 /* ------------------------------------------------------------ */
986 /**
987 * Reset statistics.
988 */
989 public void statsReset()
990 {
991 updateNotEqual(_statsStartedAt,-1,System.currentTimeMillis());
992
993 _requestStats.reset();
994 _connectionStats.reset();
995 _connectionDurationStats.reset();
996 }
997
998 /* ------------------------------------------------------------ */
999 public void setStatsOn(boolean on)
1000 {
1001 if (on && _statsStartedAt.get() != -1)
1002 return;
1003
1004 LOG.debug("Statistics on = " + on + " for " + this);
1005
1006 statsReset();
1007 _statsStartedAt.set(on?System.currentTimeMillis():-1);
1008 }
1009
1010 /* ------------------------------------------------------------ */
1011 /**
1012 * @return True if statistics collection is turned on.
1013 */
1014 public boolean getStatsOn()
1015 {
1016 return _statsStartedAt.get() != -1;
1017 }
1018
1019 /* ------------------------------------------------------------ */
1020 /**
1021 * @return Timestamp stats were started at.
1022 */
1023 public long getStatsOnMs()
1024 {
1025 long start = _statsStartedAt.get();
1026
1027 return (start != -1)?(System.currentTimeMillis() - start):0;
1028 }
1029
1030 /* ------------------------------------------------------------ */
1031 protected void connectionOpened(Connection connection)
1032 {
1033 if (_statsStartedAt.get() == -1)
1034 return;
1035
1036 _connectionStats.increment();
1037 }
1038
1039 /* ------------------------------------------------------------ */
1040 protected void connectionUpgraded(Connection oldConnection, Connection newConnection)
1041 {
1042 _requestStats.set((oldConnection instanceof HttpConnection)?((HttpConnection)oldConnection).getRequests():0);
1043 }
1044
1045 /* ------------------------------------------------------------ */
1046 protected void connectionClosed(Connection connection)
1047 {
1048 connection.closed();
1049
1050 if (_statsStartedAt.get() == -1)
1051 return;
1052
1053 long duration = System.currentTimeMillis() - connection.getTimeStamp();
1054 int requests = (connection instanceof HttpConnection)?((HttpConnection)connection).getRequests():0;
1055 _requestStats.set(requests);
1056 _connectionStats.decrement();
1057 _connectionDurationStats.set(duration);
1058 }
1059
1060 /* ------------------------------------------------------------ */
1061 /**
1062 * @return the acceptorPriority
1063 */
1064 public int getAcceptorPriorityOffset()
1065 {
1066 return _acceptorPriorityOffset;
1067 }
1068
1069 /* ------------------------------------------------------------ */
1070 /**
1071 * Set the priority offset of the acceptor threads. The priority is adjusted by this amount (default 0) to either favour the acceptance of new threads and
1072 * newly active connections or to favour the handling of already dispatched connections.
1073 *
1074 * @param offset
1075 * the amount to alter the priority of the acceptor threads.
1076 */
1077 public void setAcceptorPriorityOffset(int offset)
1078 {
1079 _acceptorPriorityOffset = offset;
1080 }
1081
1082 /* ------------------------------------------------------------ */
1083 /**
1084 * @return True if the the server socket will be opened in SO_REUSEADDR mode.
1085 */
1086 public boolean getReuseAddress()
1087 {
1088 return _reuseAddress;
1089 }
1090
1091 /* ------------------------------------------------------------ */
1092 /**
1093 * @param reuseAddress
1094 * True if the the server socket will be opened in SO_REUSEADDR mode.
1095 */
1096 public void setReuseAddress(boolean reuseAddress)
1097 {
1098 _reuseAddress = reuseAddress;
1099 }
1100
1101 /* ------------------------------------------------------------ */
1102 public boolean isLowResources()
1103 {
1104 if (_threadPool != null)
1105 return _threadPool.isLowOnThreads();
1106 return _server.getThreadPool().isLowOnThreads();
1107 }
1108
1109 /* ------------------------------------------------------------ */
1110 private void updateNotEqual(AtomicLong valueHolder, long compare, long value)
1111 {
1112 long oldValue = valueHolder.get();
1113 while (compare != oldValue)
1114 {
1115 if (valueHolder.compareAndSet(oldValue,value))
1116 break;
1117 oldValue = valueHolder.get();
1118 }
1119 }
1120
1121 /* ------------------------------------------------------------ */
1122 public String dump()
1123 {
1124 return AggregateLifeCycle.dump(this);
1125 }
1126
1127 /* ------------------------------------------------------------ */
1128 public void dump(Appendable out, String indent) throws IOException
1129 {
1130 out.append(String.valueOf(this)).append("\n");
1131 }
1132
1133 }