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;
362 synchronized(this)
363 {
364 threads= _acceptorThread;
365 }
366 if (threads != null)
367 for (int i = 0; i < threads.length; i++)
368 if (threads[i] != null)
369 threads[i].join();
370 }
371
372 /* ------------------------------------------------------------ */
373 protected void configure(Socket socket) throws IOException
374 {
375 try
376 {
377 socket.setTcpNoDelay(true);
378 if (_soLingerTime >= 0)
379 socket.setSoLinger(true,_soLingerTime / 1000);
380 else
381 socket.setSoLinger(false,0);
382 }
383 catch (Exception e)
384 {
385 LOG.ignore(e);
386 }
387 }
388
389 /* ------------------------------------------------------------ */
390 public void customize(EndPoint endpoint, Request request) throws IOException
391 {
392 if (isForwarded())
393 checkForwardedHeaders(endpoint,request);
394 }
395
396 /* ------------------------------------------------------------ */
397 protected void checkForwardedHeaders(EndPoint endpoint, Request request) throws IOException
398 {
399 HttpFields httpFields = request.getConnection().getRequestFields();
400
401 // Do SSL first
402 if (getForwardedCipherSuiteHeader()!=null)
403 {
404 String cipher_suite=httpFields.getStringField(getForwardedCipherSuiteHeader());
405 if (cipher_suite!=null)
406 request.setAttribute("javax.servlet.request.cipher_suite",cipher_suite);
407 }
408 if (getForwardedSslSessionIdHeader()!=null)
409 {
410 String ssl_session_id=httpFields.getStringField(getForwardedSslSessionIdHeader());
411 if(ssl_session_id!=null)
412 {
413 request.setAttribute("javax.servlet.request.ssl_session_id", ssl_session_id);
414 request.setScheme(HttpSchemes.HTTPS);
415 }
416 }
417
418 // Retrieving headers from the request
419 String forwardedHost = getLeftMostFieldValue(httpFields,getForwardedHostHeader());
420 String forwardedServer = getLeftMostFieldValue(httpFields,getForwardedServerHeader());
421 String forwardedFor = getLeftMostFieldValue(httpFields,getForwardedForHeader());
422 String forwardedProto = getLeftMostFieldValue(httpFields,getForwardedProtoHeader());
423
424 if (_hostHeader != null)
425 {
426 // Update host header
427 httpFields.put(HttpHeaders.HOST_BUFFER,_hostHeader);
428 request.setServerName(null);
429 request.setServerPort(-1);
430 request.getServerName();
431 }
432 else if (forwardedHost != null)
433 {
434 // Update host header
435 httpFields.put(HttpHeaders.HOST_BUFFER,forwardedHost);
436 request.setServerName(null);
437 request.setServerPort(-1);
438 request.getServerName();
439 }
440 else if (forwardedServer != null)
441 {
442 // Use provided server name
443 request.setServerName(forwardedServer);
444 }
445
446 if (forwardedFor != null)
447 {
448 request.setRemoteAddr(forwardedFor);
449 InetAddress inetAddress = null;
450
451 if (_useDNS)
452 {
453 try
454 {
455 inetAddress = InetAddress.getByName(forwardedFor);
456 }
457 catch (UnknownHostException e)
458 {
459 LOG.ignore(e);
460 }
461 }
462
463 request.setRemoteHost(inetAddress == null?forwardedFor:inetAddress.getHostName());
464 }
465
466 if (forwardedProto != null)
467 {
468 request.setScheme(forwardedProto);
469 }
470 }
471
472 /* ------------------------------------------------------------ */
473 protected String getLeftMostFieldValue(HttpFields fields, String header)
474 {
475 if (header == null)
476 return null;
477
478 String headerValue = fields.getStringField(header);
479
480 if (headerValue == null)
481 return null;
482
483 int commaIndex = headerValue.indexOf(',');
484
485 if (commaIndex == -1)
486 {
487 // Single value
488 return headerValue;
489 }
490
491 // The left-most value is the farthest downstream client
492 return headerValue.substring(0,commaIndex);
493 }
494
495 /* ------------------------------------------------------------ */
496 public void persist(EndPoint endpoint) throws IOException
497 {
498 }
499
500 /* ------------------------------------------------------------ */
501 /*
502 * @see org.eclipse.jetty.server.Connector#getConfidentialPort()
503 */
504 public int getConfidentialPort()
505 {
506 return _confidentialPort;
507 }
508
509 /* ------------------------------------------------------------ */
510 /* ------------------------------------------------------------ */
511 /*
512 * @see org.eclipse.jetty.server.Connector#getConfidentialScheme()
513 */
514 public String getConfidentialScheme()
515 {
516 return _confidentialScheme;
517 }
518
519 /* ------------------------------------------------------------ */
520 /*
521 * @see org.eclipse.jetty.server.Connector#isConfidential(org.eclipse.jetty.server .Request)
522 */
523 public boolean isIntegral(Request request)
524 {
525 return false;
526 }
527
528 /* ------------------------------------------------------------ */
529 /*
530 * @see org.eclipse.jetty.server.Connector#getConfidentialPort()
531 */
532 public int getIntegralPort()
533 {
534 return _integralPort;
535 }
536
537 /* ------------------------------------------------------------ */
538 /*
539 * @see org.eclipse.jetty.server.Connector#getIntegralScheme()
540 */
541 public String getIntegralScheme()
542 {
543 return _integralScheme;
544 }
545
546 /* ------------------------------------------------------------ */
547 /*
548 * @see org.eclipse.jetty.server.Connector#isConfidential(org.eclipse.jetty.server.Request)
549 */
550 public boolean isConfidential(Request request)
551 {
552 return _forwarded && request.getScheme().equalsIgnoreCase(HttpSchemes.HTTPS);
553 }
554
555 /* ------------------------------------------------------------ */
556 /**
557 * @param confidentialPort
558 * The confidentialPort to set.
559 */
560 public void setConfidentialPort(int confidentialPort)
561 {
562 _confidentialPort = confidentialPort;
563 }
564
565 /* ------------------------------------------------------------ */
566 /**
567 * @param confidentialScheme
568 * The confidentialScheme to set.
569 */
570 public void setConfidentialScheme(String confidentialScheme)
571 {
572 _confidentialScheme = confidentialScheme;
573 }
574
575 /* ------------------------------------------------------------ */
576 /**
577 * @param integralPort
578 * The integralPort to set.
579 */
580 public void setIntegralPort(int integralPort)
581 {
582 _integralPort = integralPort;
583 }
584
585 /* ------------------------------------------------------------ */
586 /**
587 * @param integralScheme
588 * The integralScheme to set.
589 */
590 public void setIntegralScheme(String integralScheme)
591 {
592 _integralScheme = integralScheme;
593 }
594
595 /* ------------------------------------------------------------ */
596 protected abstract void accept(int acceptorID) throws IOException, InterruptedException;
597
598 /* ------------------------------------------------------------ */
599 public void stopAccept(int acceptorID) throws Exception
600 {
601 }
602
603 /* ------------------------------------------------------------ */
604 public boolean getResolveNames()
605 {
606 return _useDNS;
607 }
608
609 /* ------------------------------------------------------------ */
610 public void setResolveNames(boolean resolve)
611 {
612 _useDNS = resolve;
613 }
614
615 /* ------------------------------------------------------------ */
616 /**
617 * Is reverse proxy handling on?
618 *
619 * @return true if this connector is checking the x-forwarded-for/host/server headers
620 */
621 public boolean isForwarded()
622 {
623 return _forwarded;
624 }
625
626 /* ------------------------------------------------------------ */
627 /**
628 * 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,
629 * host, server and client ip.
630 *
631 * @param check
632 * true if this connector is checking the x-forwarded-for/host/server headers
633 * @set {@link #setForwardedForHeader(String)}
634 * @set {@link #setForwardedHostHeader(String)}
635 * @set {@link #setForwardedProtoHeader(String)}
636 * @set {@link #setForwardedServerHeader(String)}
637 */
638 public void setForwarded(boolean check)
639 {
640 if (check)
641 LOG.debug("{} is forwarded",this);
642 _forwarded = check;
643 }
644
645 /* ------------------------------------------------------------ */
646 public String getHostHeader()
647 {
648 return _hostHeader;
649 }
650
651 /* ------------------------------------------------------------ */
652 /**
653 * Set a forced valued for the host header to control what is returned by {@link ServletRequest#getServerName()} and {@link ServletRequest#getServerPort()}.
654 * This value is only used if {@link #isForwarded()} is true.
655 *
656 * @param hostHeader
657 * The value of the host header to force.
658 */
659 public void setHostHeader(String hostHeader)
660 {
661 _hostHeader = hostHeader;
662 }
663
664 /* ------------------------------------------------------------ */
665 /*
666 *
667 * @see #setForwarded(boolean)
668 */
669 public String getForwardedHostHeader()
670 {
671 return _forwardedHostHeader;
672 }
673
674 /* ------------------------------------------------------------ */
675 /**
676 * @param forwardedHostHeader
677 * The header name for forwarded hosts (default x-forwarded-host)
678 * @see #setForwarded(boolean)
679 */
680 public void setForwardedHostHeader(String forwardedHostHeader)
681 {
682 _forwardedHostHeader = forwardedHostHeader;
683 }
684
685 /* ------------------------------------------------------------ */
686 /**
687 * @return the header name for forwarded server.
688 * @see #setForwarded(boolean)
689 */
690 public String getForwardedServerHeader()
691 {
692 return _forwardedServerHeader;
693 }
694
695 /* ------------------------------------------------------------ */
696 /**
697 * @param forwardedServerHeader
698 * The header name for forwarded server (default x-forwarded-server)
699 * @see #setForwarded(boolean)
700 */
701 public void setForwardedServerHeader(String forwardedServerHeader)
702 {
703 _forwardedServerHeader = forwardedServerHeader;
704 }
705
706 /* ------------------------------------------------------------ */
707 /**
708 * @see #setForwarded(boolean)
709 */
710 public String getForwardedForHeader()
711 {
712 return _forwardedForHeader;
713 }
714
715 /* ------------------------------------------------------------ */
716 /**
717 * @param forwardedRemoteAddressHeader
718 * The header name for forwarded for (default x-forwarded-for)
719 * @see #setForwarded(boolean)
720 */
721 public void setForwardedForHeader(String forwardedRemoteAddressHeader)
722 {
723 _forwardedForHeader = forwardedRemoteAddressHeader;
724 }
725
726 /* ------------------------------------------------------------ */
727 /**
728 * Get the forwardedProtoHeader.
729 *
730 * @return the forwardedProtoHeader (default X-Forwarded-For)
731 * @see #setForwarded(boolean)
732 */
733 public String getForwardedProtoHeader()
734 {
735 return _forwardedProtoHeader;
736 }
737
738 /* ------------------------------------------------------------ */
739 /**
740 * Set the forwardedProtoHeader.
741 *
742 * @param forwardedProtoHeader
743 * the forwardedProtoHeader to set (default X-Forwarded-For)
744 * @see #setForwarded(boolean)
745 */
746 public void setForwardedProtoHeader(String forwardedProtoHeader)
747 {
748 _forwardedProtoHeader = forwardedProtoHeader;
749 }
750
751 /* ------------------------------------------------------------ */
752 /**
753 * @return The header name holding a forwarded cipher suite (default null)
754 */
755 public String getForwardedCipherSuiteHeader()
756 {
757 return _forwardedCipherSuiteHeader;
758 }
759
760 /* ------------------------------------------------------------ */
761 /**
762 * @param forwardedCipherSuite
763 * The header name holding a forwarded cipher suite (default null)
764 */
765 public void setForwardedCipherSuiteHeader(String forwardedCipherSuite)
766 {
767 _forwardedCipherSuiteHeader = forwardedCipherSuite;
768 }
769
770 /* ------------------------------------------------------------ */
771 /**
772 * @return The header name holding a forwarded SSL Session ID (default null)
773 */
774 public String getForwardedSslSessionIdHeader()
775 {
776 return _forwardedSslSessionIdHeader;
777 }
778
779 /* ------------------------------------------------------------ */
780 /**
781 * @param forwardedSslSessionId
782 * The header name holding a forwarded SSL Session ID (default null)
783 */
784 public void setForwardedSslSessionIdHeader(String forwardedSslSessionId)
785 {
786 _forwardedSslSessionIdHeader = forwardedSslSessionId;
787 }
788
789 /* ------------------------------------------------------------ */
790 @Override
791 public String toString()
792 {
793 String name = this.getClass().getName();
794 int dot = name.lastIndexOf('.');
795 if (dot > 0)
796 name = name.substring(dot + 1);
797
798 return name + "@" + (getHost() == null?"0.0.0.0":getHost()) + ":" + (getLocalPort() <= 0?getPort():getLocalPort()) + " "
799 + AbstractLifeCycle.getState(this);
800 }
801
802 /* ------------------------------------------------------------ */
803 /* ------------------------------------------------------------ */
804 /* ------------------------------------------------------------ */
805 private class Acceptor implements Runnable
806 {
807 int _acceptor = 0;
808
809 Acceptor(int id)
810 {
811 _acceptor = id;
812 }
813
814 /* ------------------------------------------------------------ */
815 public void run()
816 {
817 Thread current = Thread.currentThread();
818 String name;
819 synchronized (AbstractConnector.this)
820 {
821 if (_acceptorThread == null)
822 return;
823
824 _acceptorThread[_acceptor] = current;
825 name = _acceptorThread[_acceptor].getName();
826 current.setName(name + " Acceptor" + _acceptor + " " + AbstractConnector.this);
827 }
828 int old_priority = current.getPriority();
829
830 try
831 {
832 current.setPriority(old_priority - _acceptorPriorityOffset);
833 while (isRunning() && getConnection() != null)
834 {
835 try
836 {
837 accept(_acceptor);
838 }
839 catch (EofException e)
840 {
841 LOG.ignore(e);
842 }
843 catch (IOException e)
844 {
845 LOG.ignore(e);
846 }
847 catch (InterruptedException x)
848 {
849 // Connector has been stopped
850 LOG.ignore(x);
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 if (LOG.isDebugEnabled())
1005 LOG.debug("Statistics on = " + on + " for " + this);
1006
1007 statsReset();
1008 _statsStartedAt.set(on?System.currentTimeMillis():-1);
1009 }
1010
1011 /* ------------------------------------------------------------ */
1012 /**
1013 * @return True if statistics collection is turned on.
1014 */
1015 public boolean getStatsOn()
1016 {
1017 return _statsStartedAt.get() != -1;
1018 }
1019
1020 /* ------------------------------------------------------------ */
1021 /**
1022 * @return Timestamp stats were started at.
1023 */
1024 public long getStatsOnMs()
1025 {
1026 long start = _statsStartedAt.get();
1027
1028 return (start != -1)?(System.currentTimeMillis() - start):0;
1029 }
1030
1031 /* ------------------------------------------------------------ */
1032 protected void connectionOpened(Connection connection)
1033 {
1034 if (_statsStartedAt.get() == -1)
1035 return;
1036
1037 _connectionStats.increment();
1038 }
1039
1040 /* ------------------------------------------------------------ */
1041 protected void connectionUpgraded(Connection oldConnection, Connection newConnection)
1042 {
1043 _requestStats.set((oldConnection instanceof AbstractHttpConnection)?((AbstractHttpConnection)oldConnection).getRequests():0);
1044 }
1045
1046 /* ------------------------------------------------------------ */
1047 protected void connectionClosed(Connection connection)
1048 {
1049 connection.onClose();
1050
1051 if (_statsStartedAt.get() == -1)
1052 return;
1053
1054 long duration = System.currentTimeMillis() - connection.getTimeStamp();
1055 int requests = (connection instanceof AbstractHttpConnection)?((AbstractHttpConnection)connection).getRequests():0;
1056 _requestStats.set(requests);
1057 _connectionStats.decrement();
1058 _connectionDurationStats.set(duration);
1059 }
1060
1061 /* ------------------------------------------------------------ */
1062 /**
1063 * @return the acceptorPriority
1064 */
1065 public int getAcceptorPriorityOffset()
1066 {
1067 return _acceptorPriorityOffset;
1068 }
1069
1070 /* ------------------------------------------------------------ */
1071 /**
1072 * 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
1073 * newly active connections or to favour the handling of already dispatched connections.
1074 *
1075 * @param offset
1076 * the amount to alter the priority of the acceptor threads.
1077 */
1078 public void setAcceptorPriorityOffset(int offset)
1079 {
1080 _acceptorPriorityOffset = offset;
1081 }
1082
1083 /* ------------------------------------------------------------ */
1084 /**
1085 * @return True if the the server socket will be opened in SO_REUSEADDR mode.
1086 */
1087 public boolean getReuseAddress()
1088 {
1089 return _reuseAddress;
1090 }
1091
1092 /* ------------------------------------------------------------ */
1093 /**
1094 * @param reuseAddress
1095 * True if the the server socket will be opened in SO_REUSEADDR mode.
1096 */
1097 public void setReuseAddress(boolean reuseAddress)
1098 {
1099 _reuseAddress = reuseAddress;
1100 }
1101
1102 /* ------------------------------------------------------------ */
1103 public boolean isLowResources()
1104 {
1105 if (_threadPool != null)
1106 return _threadPool.isLowOnThreads();
1107 return _server.getThreadPool().isLowOnThreads();
1108 }
1109
1110 /* ------------------------------------------------------------ */
1111 private void updateNotEqual(AtomicLong valueHolder, long compare, long value)
1112 {
1113 long oldValue = valueHolder.get();
1114 while (compare != oldValue)
1115 {
1116 if (valueHolder.compareAndSet(oldValue,value))
1117 break;
1118 oldValue = valueHolder.get();
1119 }
1120 }
1121
1122 /* ------------------------------------------------------------ */
1123 public String dump()
1124 {
1125 return AggregateLifeCycle.dump(this);
1126 }
1127
1128 /* ------------------------------------------------------------ */
1129 public void dump(Appendable out, String indent) throws IOException
1130 {
1131 out.append(String.valueOf(this)).append("\n");
1132 }
1133
1134 }