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