PageRenderTime 8ms CodeModel.GetById 7ms app.highlight 99ms RepoModel.GetById 1ms app.codeStats 1ms

/tc6.0.x/tags/TOMCAT_6_0_10/java/org/apache/tomcat/util/net/AprEndpoint.java

#
Java | 1980 lines | 1062 code | 337 blank | 581 comment | 237 complexity | fb5ae153c121dee7d38b73031d5d9f96 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1/*
   2 *  Licensed to the Apache Software Foundation (ASF) under one or more
   3 *  contributor license agreements.  See the NOTICE file distributed with
   4 *  this work for additional information regarding copyright ownership.
   5 *  The ASF licenses this file to You under the Apache License, Version 2.0
   6 *  (the "License"); you may not use this file except in compliance with
   7 *  the License.  You may obtain a copy of the License at
   8 *
   9 *      http://www.apache.org/licenses/LICENSE-2.0
  10 *
  11 *  Unless required by applicable law or agreed to in writing, software
  12 *  distributed under the License is distributed on an "AS IS" BASIS,
  13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 *  See the License for the specific language governing permissions and
  15 *  limitations under the License.
  16 */
  17
  18package org.apache.tomcat.util.net;
  19
  20import java.net.InetAddress;
  21import java.util.ArrayList;
  22import java.util.HashMap;
  23import java.util.concurrent.Executor;
  24
  25import org.apache.juli.logging.Log;
  26import org.apache.juli.logging.LogFactory;
  27import org.apache.tomcat.jni.Address;
  28import org.apache.tomcat.jni.Error;
  29import org.apache.tomcat.jni.File;
  30import org.apache.tomcat.jni.Library;
  31import org.apache.tomcat.jni.OS;
  32import org.apache.tomcat.jni.Poll;
  33import org.apache.tomcat.jni.Pool;
  34import org.apache.tomcat.jni.SSL;
  35import org.apache.tomcat.jni.SSLContext;
  36import org.apache.tomcat.jni.SSLSocket;
  37import org.apache.tomcat.jni.Socket;
  38import org.apache.tomcat.jni.Status;
  39import org.apache.tomcat.util.res.StringManager;
  40
  41/**
  42 * APR tailored thread pool, providing the following services:
  43 * <ul>
  44 * <li>Socket acceptor thread</li>
  45 * <li>Socket poller thread</li>
  46 * <li>Sendfile thread</li>
  47 * <li>Worker threads pool</li>
  48 * </ul>
  49 *
  50 * When switching to Java 5, there's an opportunity to use the virtual
  51 * machine's thread pool.
  52 *
  53 * @author Mladen Turk
  54 * @author Remy Maucherat
  55 */
  56public class AprEndpoint {
  57
  58
  59    // -------------------------------------------------------------- Constants
  60
  61
  62    protected static Log log = LogFactory.getLog(AprEndpoint.class);
  63
  64    protected static StringManager sm =
  65        StringManager.getManager("org.apache.tomcat.util.net.res");
  66
  67
  68    /**
  69     * The Request attribute key for the cipher suite.
  70     */
  71    public static final String CIPHER_SUITE_KEY = "javax.servlet.request.cipher_suite";
  72
  73    /**
  74     * The Request attribute key for the key size.
  75     */
  76    public static final String KEY_SIZE_KEY = "javax.servlet.request.key_size";
  77
  78    /**
  79     * The Request attribute key for the client certificate chain.
  80     */
  81    public static final String CERTIFICATE_KEY = "javax.servlet.request.X509Certificate";
  82
  83    /**
  84     * The Request attribute key for the session id.
  85     * This one is a Tomcat extension to the Servlet spec.
  86     */
  87    public static final String SESSION_ID_KEY = "javax.servlet.request.ssl_session";
  88
  89
  90    // ----------------------------------------------------------------- Fields
  91
  92
  93    /**
  94     * Available workers.
  95     */
  96    protected WorkerStack workers = null;
  97
  98
  99    /**
 100     * Running state of the endpoint.
 101     */
 102    protected volatile boolean running = false;
 103
 104
 105    /**
 106     * Will be set to true whenever the endpoint is paused.
 107     */
 108    protected volatile boolean paused = false;
 109
 110
 111    /**
 112     * Track the initialization state of the endpoint.
 113     */
 114    protected boolean initialized = false;
 115
 116
 117    /**
 118     * Current worker threads busy count.
 119     */
 120    protected int curThreadsBusy = 0;
 121
 122
 123    /**
 124     * Current worker threads count.
 125     */
 126    protected int curThreads = 0;
 127
 128
 129    /**
 130     * Sequence number used to generate thread names.
 131     */
 132    protected int sequence = 0;
 133
 134
 135    /**
 136     * Root APR memory pool.
 137     */
 138    protected long rootPool = 0;
 139
 140
 141    /**
 142     * Server socket "pointer".
 143     */
 144    protected long serverSock = 0;
 145
 146
 147    /**
 148     * APR memory pool for the server socket.
 149     */
 150    protected long serverSockPool = 0;
 151
 152
 153    /**
 154     * SSL context.
 155     */
 156    protected long sslContext = 0;
 157
 158
 159    // ------------------------------------------------------------- Properties
 160
 161
 162    /**
 163     * External Executor based thread pool.
 164     */
 165    protected Executor executor = null;
 166    public void setExecutor(Executor executor) { this.executor = executor; }
 167    public Executor getExecutor() { return executor; }
 168
 169
 170    /**
 171     * Maximum amount of worker threads.
 172     */
 173    protected int maxThreads = 40;
 174    public void setMaxThreads(int maxThreads) { this.maxThreads = maxThreads; }
 175    public int getMaxThreads() { return maxThreads; }
 176
 177
 178    /**
 179     * Priority of the acceptor and poller threads.
 180     */
 181    protected int threadPriority = Thread.NORM_PRIORITY;
 182    public void setThreadPriority(int threadPriority) { this.threadPriority = threadPriority; }
 183    public int getThreadPriority() { return threadPriority; }
 184
 185
 186    /**
 187     * Size of the socket poller.
 188     */
 189    protected int pollerSize = 8 * 1024;
 190    public void setPollerSize(int pollerSize) { this.pollerSize = pollerSize; }
 191    public int getPollerSize() { return pollerSize; }
 192
 193
 194    /**
 195     * Size of the sendfile (= concurrent files which can be served).
 196     */
 197    protected int sendfileSize = 1 * 1024;
 198    public void setSendfileSize(int sendfileSize) { this.sendfileSize = sendfileSize; }
 199    public int getSendfileSize() { return sendfileSize; }
 200
 201
 202    /**
 203     * Server socket port.
 204     */
 205    protected int port;
 206    public int getPort() { return port; }
 207    public void setPort(int port ) { this.port=port; }
 208
 209
 210    /**
 211     * Address for the server socket.
 212     */
 213    protected InetAddress address;
 214    public InetAddress getAddress() { return address; }
 215    public void setAddress(InetAddress address) { this.address = address; }
 216
 217
 218    /**
 219     * Handling of accepted sockets.
 220     */
 221    protected Handler handler = null;
 222    public void setHandler(Handler handler ) { this.handler = handler; }
 223    public Handler getHandler() { return handler; }
 224
 225
 226    /**
 227     * Allows the server developer to specify the backlog that
 228     * should be used for server sockets. By default, this value
 229     * is 100.
 230     */
 231    protected int backlog = 100;
 232    public void setBacklog(int backlog) { if (backlog > 0) this.backlog = backlog; }
 233    public int getBacklog() { return backlog; }
 234
 235
 236    /**
 237     * Socket TCP no delay.
 238     */
 239    protected boolean tcpNoDelay = false;
 240    public boolean getTcpNoDelay() { return tcpNoDelay; }
 241    public void setTcpNoDelay(boolean tcpNoDelay) { this.tcpNoDelay = tcpNoDelay; }
 242
 243
 244    /**
 245     * Socket linger.
 246     */
 247    protected int soLinger = 100;
 248    public int getSoLinger() { return soLinger; }
 249    public void setSoLinger(int soLinger) { this.soLinger = soLinger; }
 250
 251
 252    /**
 253     * Socket timeout.
 254     */
 255    protected int soTimeout = -1;
 256    public int getSoTimeout() { return soTimeout; }
 257    public void setSoTimeout(int soTimeout) { this.soTimeout = soTimeout; }
 258
 259
 260    /**
 261     * Keep-Alive timeout.
 262     */
 263    protected int keepAliveTimeout = -1;
 264    public int getKeepAliveTimeout() { return keepAliveTimeout; }
 265    public void setKeepAliveTimeout(int keepAliveTimeout) { this.keepAliveTimeout = keepAliveTimeout; }
 266
 267
 268    /**
 269     * Timeout on first request read before going to the poller, in ms.
 270     */
 271    protected int firstReadTimeout = -1;
 272    public int getFirstReadTimeout() { return firstReadTimeout; }
 273    public void setFirstReadTimeout(int firstReadTimeout) { this.firstReadTimeout = firstReadTimeout; }
 274
 275
 276    /**
 277     * Poll interval, in microseconds. The smaller the value, the more CPU the poller
 278     * will use, but the more responsive to activity it will be.
 279     */
 280    protected int pollTime = 2000;
 281    public int getPollTime() { return pollTime; }
 282    public void setPollTime(int pollTime) { if (pollTime > 0) { this.pollTime = pollTime; } }
 283
 284
 285    /**
 286     * The default is true - the created threads will be
 287     *  in daemon mode. If set to false, the control thread
 288     *  will not be daemon - and will keep the process alive.
 289     */
 290    protected boolean daemon = true;
 291    public void setDaemon(boolean b) { daemon = b; }
 292    public boolean getDaemon() { return daemon; }
 293
 294
 295    /**
 296     * Name of the thread pool, which will be used for naming child threads.
 297     */
 298    protected String name = "TP";
 299    public void setName(String name) { this.name = name; }
 300    public String getName() { return name; }
 301
 302
 303    /**
 304     * Use endfile for sending static files.
 305     */
 306    protected boolean useSendfile = Library.APR_HAS_SENDFILE;
 307    public void setUseSendfile(boolean useSendfile) { this.useSendfile = useSendfile; }
 308    public boolean getUseSendfile() { return useSendfile; }
 309
 310
 311    /**
 312     * Allow comet request handling.
 313     */
 314    protected boolean useComet = true;
 315    public void setUseComet(boolean useComet) { this.useComet = useComet; }
 316    public boolean getUseComet() { return useComet; }
 317
 318
 319    /**
 320     * Acceptor thread count.
 321     */
 322    protected int acceptorThreadCount = 0;
 323    public void setAcceptorThreadCount(int acceptorThreadCount) { this.acceptorThreadCount = acceptorThreadCount; }
 324    public int getAcceptorThreadCount() { return acceptorThreadCount; }
 325
 326
 327    /**
 328     * Sendfile thread count.
 329     */
 330    protected int sendfileThreadCount = 0;
 331    public void setSendfileThreadCount(int sendfileThreadCount) { this.sendfileThreadCount = sendfileThreadCount; }
 332    public int getSendfileThreadCount() { return sendfileThreadCount; }
 333
 334
 335    /**
 336     * Poller thread count.
 337     */
 338    protected int pollerThreadCount = 0;
 339    public void setPollerThreadCount(int pollerThreadCount) { this.pollerThreadCount = pollerThreadCount; }
 340    public int getPollerThreadCount() { return pollerThreadCount; }
 341
 342
 343    /**
 344     * The socket poller.
 345     */
 346    protected Poller[] pollers = null;
 347    protected int pollerRoundRobin = 0;
 348    public Poller getPoller() {
 349        pollerRoundRobin = (pollerRoundRobin + 1) % pollers.length;
 350        return pollers[pollerRoundRobin];
 351    }
 352
 353
 354    /**
 355     * The socket poller used for Comet support.
 356     */
 357    protected Poller[] cometPollers = null;
 358    protected int cometPollerRoundRobin = 0;
 359    public Poller getCometPoller() {
 360        cometPollerRoundRobin = (cometPollerRoundRobin + 1) % cometPollers.length;
 361        return cometPollers[cometPollerRoundRobin];
 362    }
 363
 364
 365    /**
 366     * The static file sender.
 367     */
 368    protected Sendfile[] sendfiles = null;
 369    protected int sendfileRoundRobin = 0;
 370    public Sendfile getSendfile() {
 371        sendfileRoundRobin = (sendfileRoundRobin + 1) % sendfiles.length;
 372        return sendfiles[sendfileRoundRobin];
 373    }
 374
 375
 376    /**
 377     * Dummy maxSpareThreads property.
 378     */
 379    public int getMaxSpareThreads() { return 0; }
 380
 381
 382    /**
 383     * Dummy minSpareThreads property.
 384     */
 385    public int getMinSpareThreads() { return 0; }
 386
 387
 388    /**
 389     * SSL engine.
 390     */
 391    protected boolean SSLEnabled = false;
 392    public boolean isSSLEnabled() { return SSLEnabled; }
 393    public void setSSLEnabled(boolean SSLEnabled) { this.SSLEnabled = SSLEnabled; }
 394
 395
 396    /**
 397     * SSL protocols.
 398     */
 399    protected String SSLProtocol = "all";
 400    public String getSSLProtocol() { return SSLProtocol; }
 401    public void setSSLProtocol(String SSLProtocol) { this.SSLProtocol = SSLProtocol; }
 402
 403
 404    /**
 405     * SSL password (if a cert is encrypted, and no password has been provided, a callback
 406     * will ask for a password).
 407     */
 408    protected String SSLPassword = null;
 409    public String getSSLPassword() { return SSLPassword; }
 410    public void setSSLPassword(String SSLPassword) { this.SSLPassword = SSLPassword; }
 411
 412
 413    /**
 414     * SSL cipher suite.
 415     */
 416    protected String SSLCipherSuite = "ALL";
 417    public String getSSLCipherSuite() { return SSLCipherSuite; }
 418    public void setSSLCipherSuite(String SSLCipherSuite) { this.SSLCipherSuite = SSLCipherSuite; }
 419
 420
 421    /**
 422     * SSL certificate file.
 423     */
 424    protected String SSLCertificateFile = null;
 425    public String getSSLCertificateFile() { return SSLCertificateFile; }
 426    public void setSSLCertificateFile(String SSLCertificateFile) { this.SSLCertificateFile = SSLCertificateFile; }
 427
 428
 429    /**
 430     * SSL certificate key file.
 431     */
 432    protected String SSLCertificateKeyFile = null;
 433    public String getSSLCertificateKeyFile() { return SSLCertificateKeyFile; }
 434    public void setSSLCertificateKeyFile(String SSLCertificateKeyFile) { this.SSLCertificateKeyFile = SSLCertificateKeyFile; }
 435
 436
 437    /**
 438     * SSL certificate chain file.
 439     */
 440    protected String SSLCertificateChainFile = null;
 441    public String getSSLCertificateChainFile() { return SSLCertificateChainFile; }
 442    public void setSSLCertificateChainFile(String SSLCertificateChainFile) { this.SSLCertificateChainFile = SSLCertificateChainFile; }
 443
 444
 445    /**
 446     * SSL CA certificate path.
 447     */
 448    protected String SSLCACertificatePath = null;
 449    public String getSSLCACertificatePath() { return SSLCACertificatePath; }
 450    public void setSSLCACertificatePath(String SSLCACertificatePath) { this.SSLCACertificatePath = SSLCACertificatePath; }
 451
 452
 453    /**
 454     * SSL CA certificate file.
 455     */
 456    protected String SSLCACertificateFile = null;
 457    public String getSSLCACertificateFile() { return SSLCACertificateFile; }
 458    public void setSSLCACertificateFile(String SSLCACertificateFile) { this.SSLCACertificateFile = SSLCACertificateFile; }
 459
 460
 461    /**
 462     * SSL CA revocation path.
 463     */
 464    protected String SSLCARevocationPath = null;
 465    public String getSSLCARevocationPath() { return SSLCARevocationPath; }
 466    public void setSSLCARevocationPath(String SSLCARevocationPath) { this.SSLCARevocationPath = SSLCARevocationPath; }
 467
 468
 469    /**
 470     * SSL CA revocation file.
 471     */
 472    protected String SSLCARevocationFile = null;
 473    public String getSSLCARevocationFile() { return SSLCARevocationFile; }
 474    public void setSSLCARevocationFile(String SSLCARevocationFile) { this.SSLCARevocationFile = SSLCARevocationFile; }
 475
 476
 477    /**
 478     * SSL verify client.
 479     */
 480    protected String SSLVerifyClient = "none";
 481    public String getSSLVerifyClient() { return SSLVerifyClient; }
 482    public void setSSLVerifyClient(String SSLVerifyClient) { this.SSLVerifyClient = SSLVerifyClient; }
 483
 484
 485    /**
 486     * SSL verify depth.
 487     */
 488    protected int SSLVerifyDepth = 10;
 489    public int getSSLVerifyDepth() { return SSLVerifyDepth; }
 490    public void setSSLVerifyDepth(int SSLVerifyDepth) { this.SSLVerifyDepth = SSLVerifyDepth; }
 491
 492
 493    // --------------------------------------------------------- Public Methods
 494
 495
 496    /**
 497     * Number of keepalive sockets.
 498     */
 499    public int getKeepAliveCount() {
 500        if (pollers == null) {
 501            return 0;
 502        } else {
 503            int keepAliveCount = 0;
 504            for (int i = 0; i < pollers.length; i++) {
 505                keepAliveCount += pollers[i].getKeepAliveCount();
 506            }
 507            return keepAliveCount;
 508        }
 509    }
 510
 511
 512    /**
 513     * Number of sendfile sockets.
 514     */
 515    public int getSendfileCount() {
 516        if (sendfiles == null) {
 517            return 0;
 518        } else {
 519            int sendfileCount = 0;
 520            for (int i = 0; i < sendfiles.length; i++) {
 521                sendfileCount += sendfiles[i].getSendfileCount();
 522            }
 523            return sendfileCount;
 524        }
 525    }
 526
 527
 528    /**
 529     * Return the amount of threads that are managed by the pool.
 530     *
 531     * @return the amount of threads that are managed by the pool
 532     */
 533    public int getCurrentThreadCount() {
 534        return curThreads;
 535    }
 536
 537
 538    /**
 539     * Return the amount of threads currently busy.
 540     *
 541     * @return the amount of threads currently busy
 542     */
 543    public int getCurrentThreadsBusy() {
 544        return curThreadsBusy;
 545    }
 546
 547
 548    /**
 549     * Return the state of the endpoint.
 550     *
 551     * @return true if the endpoint is running, false otherwise
 552     */
 553    public boolean isRunning() {
 554        return running;
 555    }
 556
 557
 558    /**
 559     * Return the state of the endpoint.
 560     *
 561     * @return true if the endpoint is paused, false otherwise
 562     */
 563    public boolean isPaused() {
 564        return paused;
 565    }
 566
 567
 568    // ----------------------------------------------- Public Lifecycle Methods
 569
 570
 571    /**
 572     * Initialize the endpoint.
 573     */
 574    public void init()
 575        throws Exception {
 576
 577        if (initialized)
 578            return;
 579        
 580        // Create the root APR memory pool
 581        rootPool = Pool.create(0);
 582        // Create the pool for the server socket
 583        serverSockPool = Pool.create(rootPool);
 584        // Create the APR address that will be bound
 585        String addressStr = null;
 586        if (address == null) {
 587            addressStr = null;
 588        } else {
 589            addressStr = address.getHostAddress();
 590        }
 591        int family = Socket.APR_INET;
 592        if (Library.APR_HAVE_IPV6 && (addressStr == null || addressStr.indexOf(':') >= 0)) {
 593            family = Socket.APR_UNSPEC;
 594        }
 595        long inetAddress = Address.info(addressStr, family,
 596                port, 0, rootPool);
 597        // Create the APR server socket
 598        serverSock = Socket.create(family, Socket.SOCK_STREAM,
 599                Socket.APR_PROTO_TCP, rootPool);
 600        if (OS.IS_UNIX) {
 601            Socket.optSet(serverSock, Socket.APR_SO_REUSEADDR, 1);
 602        }
 603        // Deal with the firewalls that tend to drop the inactive sockets
 604        Socket.optSet(serverSock, Socket.APR_SO_KEEPALIVE, 1);
 605        // Bind the server socket
 606        int ret = Socket.bind(serverSock, inetAddress);
 607        if (ret != 0) {
 608            throw new Exception(sm.getString("endpoint.init.bind", "" + ret, Error.strerror(ret)));
 609        }
 610        // Start listening on the server socket
 611        ret = Socket.listen(serverSock, backlog);
 612        if (ret != 0) {
 613            throw new Exception(sm.getString("endpoint.init.listen", "" + ret, Error.strerror(ret)));
 614        }
 615        if (OS.IS_WIN32 || OS.IS_WIN64) {
 616            // On Windows set the reuseaddr flag after the bind/listen
 617            Socket.optSet(serverSock, Socket.APR_SO_REUSEADDR, 1);
 618        }
 619
 620        // Sendfile usage on systems which don't support it cause major problems
 621        if (useSendfile && !Library.APR_HAS_SENDFILE) {
 622            log.warn(sm.getString("endpoint.sendfile.nosupport"));
 623            useSendfile = false;
 624        }
 625
 626        // Initialize thread count defaults for acceptor, poller and sendfile
 627        if (acceptorThreadCount == 0) {
 628            // FIXME: Doesn't seem to work that well with multiple accept threads
 629            acceptorThreadCount = 1;
 630        }
 631        if (pollerThreadCount == 0) {
 632            if ((OS.IS_WIN32 || OS.IS_WIN64) && (pollerSize > 1024)) {
 633                // The maximum per poller to get reasonable performance is 1024
 634                pollerThreadCount = pollerSize / 1024;
 635                // Adjust poller size so that it won't reach the limit
 636                pollerSize = pollerSize - (pollerSize % 1024);
 637            } else {
 638                // No explicit poller size limitation
 639                pollerThreadCount = 1;
 640            }
 641        }
 642        if (sendfileThreadCount == 0) {
 643            if ((OS.IS_WIN32 || OS.IS_WIN64) && (sendfileSize > 1024)) {
 644                // The maximum per poller to get reasonable performance is 1024
 645                sendfileThreadCount = sendfileSize / 1024;
 646                // Adjust poller size so that it won't reach the limit
 647                sendfileSize = sendfileSize - (sendfileSize % 1024);
 648            } else {
 649                // No explicit poller size limitation
 650                // FIXME: Default to one per CPU ?
 651                sendfileThreadCount = 1;
 652            }
 653        }
 654        
 655        // Delay accepting of new connections until data is available
 656        // Only Linux kernels 2.4 + have that implemented
 657        // on other platforms this call is noop and will return APR_ENOTIMPL.
 658        Socket.optSet(serverSock, Socket.APR_TCP_DEFER_ACCEPT, 1);
 659
 660        // Initialize SSL if needed
 661        if (SSLEnabled) {
 662            
 663            // SSL protocol
 664            int value = SSL.SSL_PROTOCOL_ALL;
 665            if ("SSLv2".equalsIgnoreCase(SSLProtocol)) {
 666                value = SSL.SSL_PROTOCOL_SSLV2;
 667            } else if ("SSLv3".equalsIgnoreCase(SSLProtocol)) {
 668                value = SSL.SSL_PROTOCOL_SSLV3;
 669            } else if ("TLSv1".equalsIgnoreCase(SSLProtocol)) {
 670                value = SSL.SSL_PROTOCOL_TLSV1;
 671            } else if ("SSLv2+SSLv3".equalsIgnoreCase(SSLProtocol)) {
 672                value = SSL.SSL_PROTOCOL_SSLV2 | SSL.SSL_PROTOCOL_SSLV3;
 673            }
 674            // Create SSL Context
 675            sslContext = SSLContext.make(rootPool, value, SSL.SSL_MODE_SERVER);
 676            // List the ciphers that the client is permitted to negotiate
 677            SSLContext.setCipherSuite(sslContext, SSLCipherSuite);
 678            // Load Server key and certificate
 679            SSLContext.setCertificate(sslContext, SSLCertificateFile, SSLCertificateKeyFile, SSLPassword, SSL.SSL_AIDX_RSA);
 680            // Set certificate chain file
 681            SSLContext.setCertificateChainFile(sslContext, SSLCertificateChainFile, false);
 682            // Support Client Certificates
 683            SSLContext.setCACertificate(sslContext, SSLCACertificateFile, SSLCACertificatePath);
 684            // Set revocation
 685            SSLContext.setCARevocation(sslContext, SSLCARevocationFile, SSLCARevocationPath);
 686            // Client certificate verification
 687            value = SSL.SSL_CVERIFY_NONE;
 688            if ("optional".equalsIgnoreCase(SSLVerifyClient)) {
 689                value = SSL.SSL_CVERIFY_OPTIONAL;
 690            } else if ("require".equalsIgnoreCase(SSLVerifyClient)) {
 691                value = SSL.SSL_CVERIFY_REQUIRE;
 692            } else if ("optionalNoCA".equalsIgnoreCase(SSLVerifyClient)) {
 693                value = SSL.SSL_CVERIFY_OPTIONAL_NO_CA;
 694            }
 695            SSLContext.setVerify(sslContext, value, SSLVerifyDepth);
 696            // For now, sendfile is not supported with SSL
 697            useSendfile = false;
 698        }
 699
 700        initialized = true;
 701
 702    }
 703
 704
 705    /**
 706     * Start the APR endpoint, creating acceptor, poller and sendfile threads.
 707     */
 708    public void start()
 709        throws Exception {
 710        // Initialize socket if not done before
 711        if (!initialized) {
 712            init();
 713        }
 714        if (!running) {
 715            running = true;
 716            paused = false;
 717
 718            // Create worker collection
 719            if (executor == null) {
 720                workers = new WorkerStack(maxThreads);
 721            }
 722
 723            // Start acceptor threads
 724            for (int i = 0; i < acceptorThreadCount; i++) {
 725                Thread acceptorThread = new Thread(new Acceptor(), getName() + "-Acceptor-" + i);
 726                acceptorThread.setPriority(threadPriority);
 727                acceptorThread.setDaemon(daemon);
 728                acceptorThread.start();
 729            }
 730
 731            // Start poller threads
 732            pollers = new Poller[pollerThreadCount];
 733            for (int i = 0; i < pollerThreadCount; i++) {
 734                pollers[i] = new Poller(false);
 735                pollers[i].init();
 736                Thread pollerThread = new Thread(pollers[i], getName() + "-Poller-" + i);
 737                pollerThread.setPriority(threadPriority);
 738                pollerThread.setDaemon(true);
 739                pollerThread.start();
 740            }
 741
 742            // Start comet poller threads
 743            cometPollers = new Poller[pollerThreadCount];
 744            for (int i = 0; i < pollerThreadCount; i++) {
 745                cometPollers[i] = new Poller(true);
 746                cometPollers[i].init();
 747                Thread pollerThread = new Thread(cometPollers[i], getName() + "-CometPoller-" + i);
 748                pollerThread.setPriority(threadPriority);
 749                pollerThread.setDaemon(true);
 750                pollerThread.start();
 751            }
 752
 753            // Start sendfile threads
 754            if (useSendfile) {
 755                sendfiles = new Sendfile[sendfileThreadCount];
 756                for (int i = 0; i < sendfileThreadCount; i++) {
 757                    sendfiles[i] = new Sendfile();
 758                    sendfiles[i].init();
 759                    Thread sendfileThread = new Thread(sendfiles[i], getName() + "-Sendfile-" + i);
 760                    sendfileThread.setPriority(threadPriority);
 761                    sendfileThread.setDaemon(true);
 762                    sendfileThread.start();
 763                }
 764            }
 765        }
 766    }
 767
 768
 769    /**
 770     * Pause the endpoint, which will make it stop accepting new sockets.
 771     */
 772    public void pause() {
 773        if (running && !paused) {
 774            paused = true;
 775            unlockAccept();
 776        }
 777    }
 778
 779
 780    /**
 781     * Resume the endpoint, which will make it start accepting new sockets
 782     * again.
 783     */
 784    public void resume() {
 785        if (running) {
 786            paused = false;
 787        }
 788    }
 789
 790
 791    /**
 792     * Stop the endpoint. This will cause all processing threads to stop.
 793     */
 794    public void stop() {
 795        if (running) {
 796            running = false;
 797            unlockAccept();
 798            for (int i = 0; i < pollers.length; i++) {
 799                pollers[i].destroy();
 800            }
 801            pollers = null;
 802            for (int i = 0; i < cometPollers.length; i++) {
 803                cometPollers[i].destroy();
 804            }
 805            cometPollers = null;
 806            if (useSendfile) {
 807                for (int i = 0; i < sendfiles.length; i++) {
 808                    sendfiles[i].destroy();
 809                }
 810                sendfiles = null;
 811            }
 812        }
 813    }
 814
 815
 816    /**
 817     * Deallocate APR memory pools, and close server socket.
 818     */
 819    public void destroy() throws Exception {
 820        if (running) {
 821            stop();
 822        }
 823        Pool.destroy(serverSockPool);
 824        serverSockPool = 0;
 825        // Close server socket
 826        Socket.close(serverSock);
 827        serverSock = 0;
 828        sslContext = 0;
 829        // Close all APR memory pools and resources
 830        Pool.destroy(rootPool);
 831        rootPool = 0;
 832        initialized = false;
 833    }
 834
 835
 836    // ------------------------------------------------------ Protected Methods
 837
 838
 839    /**
 840     * Get a sequence number used for thread naming.
 841     */
 842    protected int getSequence() {
 843        return sequence++;
 844    }
 845
 846
 847    /**
 848     * Unlock the server socket accept using a bugus connection.
 849     */
 850    protected void unlockAccept() {
 851        java.net.Socket s = null;
 852        try {
 853            // Need to create a connection to unlock the accept();
 854            if (address == null) {
 855                s = new java.net.Socket("127.0.0.1", port);
 856            } else {
 857                s = new java.net.Socket(address, port);
 858                // setting soLinger to a small value will help shutdown the
 859                // connection quicker
 860                s.setSoLinger(true, 0);
 861            }
 862        } catch(Exception e) {
 863            if (log.isDebugEnabled()) {
 864                log.debug(sm.getString("endpoint.debug.unlock", "" + port), e);
 865            }
 866        } finally {
 867            if (s != null) {
 868                try {
 869                    s.close();
 870                } catch (Exception e) {
 871                    // Ignore
 872                }
 873            }
 874        }
 875    }
 876
 877
 878    /**
 879     * Process the specified connection.
 880     */
 881    protected boolean setSocketOptions(long socket) {
 882        // Process the connection
 883        int step = 1;
 884        try {
 885
 886            // 1: Set socket options: timeout, linger, etc
 887            if (soLinger >= 0)
 888                Socket.optSet(socket, Socket.APR_SO_LINGER, soLinger);
 889            if (tcpNoDelay)
 890                Socket.optSet(socket, Socket.APR_TCP_NODELAY, (tcpNoDelay ? 1 : 0));
 891            if (soTimeout > 0)
 892                Socket.timeoutSet(socket, soTimeout * 1000);
 893
 894            // 2: SSL handshake
 895            step = 2;
 896            if (sslContext != 0) {
 897                SSLSocket.attach(sslContext, socket);
 898                if (SSLSocket.handshake(socket) != 0) {
 899                    if (log.isDebugEnabled()) {
 900                        log.debug(sm.getString("endpoint.err.handshake") + ": " + SSL.getLastError());
 901                    }
 902                    return false;
 903                }
 904            }
 905
 906        } catch (Throwable t) {
 907            if (log.isDebugEnabled()) {
 908                if (step == 2) {
 909                    log.debug(sm.getString("endpoint.err.handshake"), t);
 910                } else {
 911                    log.debug(sm.getString("endpoint.err.unexpected"), t);
 912                }
 913            }
 914            // Tell to close the socket
 915            return false;
 916        }
 917        return true;
 918    }
 919
 920
 921    /**
 922     * Create (or allocate) and return an available processor for use in
 923     * processing a specific HTTP request, if possible.  If the maximum
 924     * allowed processors have already been created and are in use, return
 925     * <code>null</code> instead.
 926     */
 927    protected Worker createWorkerThread() {
 928
 929        synchronized (workers) {
 930            if (workers.size() > 0) {
 931                curThreadsBusy++;
 932                return (workers.pop());
 933            }
 934            if ((maxThreads > 0) && (curThreads < maxThreads)) {
 935                curThreadsBusy++;
 936                return (newWorkerThread());
 937            } else {
 938                if (maxThreads < 0) {
 939                    curThreadsBusy++;
 940                    return (newWorkerThread());
 941                } else {
 942                    return (null);
 943                }
 944            }
 945        }
 946
 947    }
 948
 949
 950    /**
 951     * Create and return a new processor suitable for processing HTTP
 952     * requests and returning the corresponding responses.
 953     */
 954    protected Worker newWorkerThread() {
 955
 956        Worker workerThread = new Worker();
 957        workerThread.start();
 958        return (workerThread);
 959
 960    }
 961
 962
 963    /**
 964     * Return a new worker thread, and block while to worker is available.
 965     */
 966    protected Worker getWorkerThread() {
 967        // Allocate a new worker thread
 968        Worker workerThread = createWorkerThread();
 969        while (workerThread == null) {
 970            try {
 971                synchronized (workers) {
 972                    workers.wait();
 973                }
 974            } catch (InterruptedException e) {
 975                // Ignore
 976            }
 977            workerThread = createWorkerThread();
 978        }
 979        return workerThread;
 980    }
 981
 982
 983    /**
 984     * Recycle the specified Processor so that it can be used again.
 985     *
 986     * @param workerThread The processor to be recycled
 987     */
 988    protected void recycleWorkerThread(Worker workerThread) {
 989        synchronized (workers) {
 990            workers.push(workerThread);
 991            curThreadsBusy--;
 992            workers.notify();
 993        }
 994    }
 995
 996    
 997    /**
 998     * Allocate a new poller of the specified size.
 999     */
1000    protected long allocatePoller(int size, long pool, int timeout) {
1001        try {
1002            return Poll.create(size, pool, 0, timeout * 1000);
1003        } catch (Error e) {
1004            if (Status.APR_STATUS_IS_EINVAL(e.getError())) {
1005                log.info(sm.getString("endpoint.poll.limitedpollsize", "" + size));
1006                return 0;
1007            } else {
1008                log.error(sm.getString("endpoint.poll.initfail"), e);
1009                return -1;
1010            }
1011        }
1012    }
1013
1014    
1015    /**
1016     * Process given socket.
1017     */
1018    protected boolean processSocketWithOptions(long socket) {
1019        try {
1020            if (executor == null) {
1021                getWorkerThread().assignWithOptions(socket);
1022            } else {
1023                executor.execute(new SocketWithOptionsProcessor(socket));
1024            }
1025        } catch (Throwable t) {
1026            // This means we got an OOM or similar creating a thread, or that
1027            // the pool and its queue are full
1028            log.error(sm.getString("endpoint.process.fail"), t);
1029            return false;
1030        }
1031        return true;
1032    }
1033    
1034
1035    /**
1036     * Process given socket.
1037     */
1038    protected boolean processSocket(long socket) {
1039        try {
1040            if (executor == null) {
1041                getWorkerThread().assign(socket);
1042            } else {
1043                executor.execute(new SocketProcessor(socket));
1044            }
1045        } catch (Throwable t) {
1046            // This means we got an OOM or similar creating a thread, or that
1047            // the pool and its queue are full
1048            log.error(sm.getString("endpoint.process.fail"), t);
1049            return false;
1050        }
1051        return true;
1052    }
1053    
1054
1055    /**
1056     * Process given socket for an event.
1057     */
1058    protected boolean processSocket(long socket, SocketStatus status) {
1059        try {
1060            if (executor == null) {
1061                getWorkerThread().assign(socket, status);
1062            } else {
1063                executor.execute(new SocketEventProcessor(socket, status));
1064            }
1065        } catch (Throwable t) {
1066            // This means we got an OOM or similar creating a thread, or that
1067            // the pool and its queue are full
1068            log.error(sm.getString("endpoint.process.fail"), t);
1069            return false;
1070        }
1071        return true;
1072    }
1073    
1074
1075    // --------------------------------------------------- Acceptor Inner Class
1076
1077
1078    /**
1079     * Server socket acceptor thread.
1080     */
1081    protected class Acceptor implements Runnable {
1082
1083
1084        /**
1085         * The background thread that listens for incoming TCP/IP connections and
1086         * hands them off to an appropriate processor.
1087         */
1088        public void run() {
1089
1090            // Loop until we receive a shutdown command
1091            while (running) {
1092
1093                // Loop if endpoint is paused
1094                while (paused) {
1095                    try {
1096                        Thread.sleep(1000);
1097                    } catch (InterruptedException e) {
1098                        // Ignore
1099                    }
1100                }
1101
1102                try {
1103                    // Accept the next incoming connection from the server socket
1104                    long socket = Socket.accept(serverSock);
1105                    // Hand this socket off to an appropriate processor
1106                    if (!processSocketWithOptions(socket)) {
1107                        // Close socket and pool right away
1108                        Socket.destroy(socket);
1109                    }
1110                } catch (Throwable t) {
1111                    log.error(sm.getString("endpoint.accept.fail"), t);
1112                }
1113
1114                // The processor will recycle itself when it finishes
1115
1116            }
1117
1118        }
1119
1120    }
1121
1122
1123    // ----------------------------------------------------- Poller Inner Class
1124
1125
1126    /**
1127     * Poller class.
1128     */
1129    public class Poller implements Runnable {
1130
1131        protected long serverPollset = 0;
1132        protected long pool = 0;
1133        protected long[] desc;
1134
1135        protected long[] addS;
1136        protected int addCount = 0;
1137        
1138        protected boolean comet = true;
1139
1140        protected int keepAliveCount = 0;
1141        public int getKeepAliveCount() { return keepAliveCount; }
1142
1143        public Poller(boolean comet) {
1144            this.comet = comet;
1145        }
1146        
1147        /**
1148         * Create the poller. With some versions of APR, the maximum poller size will
1149         * be 62 (recompiling APR is necessary to remove this limitation).
1150         */
1151        protected void init() {
1152            pool = Pool.create(serverSockPool);
1153            int size = pollerSize / pollerThreadCount;
1154            int timeout = keepAliveTimeout;
1155            if (timeout < 0) {
1156                timeout = soTimeout;
1157            }
1158            if (comet) {
1159                // FIXME: Find an appropriate timeout value, for now, "longer than usual"
1160                // semms appropriate
1161                timeout = soTimeout * 50;
1162            }
1163            serverPollset = allocatePoller(size, pool, timeout);
1164            if (serverPollset == 0 && size > 1024) {
1165                size = 1024;
1166                serverPollset = allocatePoller(size, pool, timeout);
1167            }
1168            if (serverPollset == 0) {
1169                size = 62;
1170                serverPollset = allocatePoller(size, pool, timeout);
1171            }
1172            desc = new long[size * 2];
1173            keepAliveCount = 0;
1174            addS = new long[size];
1175            addCount = 0;
1176        }
1177
1178        /**
1179         * Destroy the poller.
1180         */
1181        protected void destroy() {
1182            // Wait for polltime before doing anything, so that the poller threads
1183            // exit, otherwise parallel descturction of sockets which are still
1184            // in the poller can cause problems
1185            try {
1186                synchronized (this) {
1187                    this.wait(pollTime / 1000);
1188                }
1189            } catch (InterruptedException e) {
1190                // Ignore
1191            }
1192            // Close all sockets in the add queue
1193            for (int i = 0; i < addCount; i++) {
1194                if (comet) {
1195                    processSocket(addS[i], SocketStatus.STOP);
1196                } else {
1197                    Socket.destroy(addS[i]);
1198                }
1199            }
1200            // Close all sockets still in the poller
1201            int rv = Poll.pollset(serverPollset, desc);
1202            if (rv > 0) {
1203                for (int n = 0; n < rv; n++) {
1204                    if (comet) {
1205                        processSocket(desc[n*2+1], SocketStatus.STOP);
1206                    } else {
1207                        Socket.destroy(desc[n*2+1]);
1208                    }
1209                }
1210            }
1211            Pool.destroy(pool);
1212            keepAliveCount = 0;
1213            addCount = 0;
1214        }
1215
1216        /**
1217         * Add specified socket and associated pool to the poller. The socket will
1218         * be added to a temporary array, and polled first after a maximum amount
1219         * of time equal to pollTime (in most cases, latency will be much lower,
1220         * however).
1221         *
1222         * @param socket to add to the poller
1223         */
1224        public void add(long socket) {
1225            synchronized (this) {
1226                // Add socket to the list. Newly added sockets will wait
1227                // at most for pollTime before being polled
1228                if (addCount >= addS.length) {
1229                    // Can't do anything: close the socket right away
1230                    if (comet) {
1231                        processSocket(socket, SocketStatus.ERROR);
1232                    } else {
1233                        Socket.destroy(socket);
1234                    }
1235                    return;
1236                }
1237                addS[addCount] = socket;
1238                addCount++;
1239                this.notify();
1240            }
1241        }
1242
1243        /**
1244         * The background thread that listens for incoming TCP/IP connections and
1245         * hands them off to an appropriate processor.
1246         */
1247        public void run() {
1248
1249            long maintainTime = 0;
1250            // Loop until we receive a shutdown command
1251            while (running) {
1252                // Loop if endpoint is paused
1253                while (paused) {
1254                    try {
1255                        Thread.sleep(1000);
1256                    } catch (InterruptedException e) {
1257                        // Ignore
1258                    }
1259                }
1260
1261                while (keepAliveCount < 1 && addCount < 1) {
1262                    // Reset maintain time.
1263                    maintainTime = 0;
1264                    try {
1265                        synchronized (this) {
1266                            this.wait();
1267                        }
1268                    } catch (InterruptedException e) {
1269                        // Ignore
1270                    }
1271                }
1272
1273                try {
1274                    // Add sockets which are waiting to the poller
1275                    if (addCount > 0) {
1276                        synchronized (this) {
1277                            for (int i = (addCount - 1); i >= 0; i--) {
1278                                int rv = Poll.add
1279                                    (serverPollset, addS[i], Poll.APR_POLLIN);
1280                                if (rv == Status.APR_SUCCESS) {
1281                                    keepAliveCount++;
1282                                } else {
1283                                    // Can't do anything: close the socket right away
1284                                    if (comet) {
1285                                        processSocket(addS[i], SocketStatus.ERROR);
1286                                    } else {
1287                                        Socket.destroy(addS[i]);
1288                                    }
1289                                }
1290                            }
1291                            addCount = 0;
1292                        }
1293                    }
1294
1295                    maintainTime += pollTime;
1296                    // Pool for the specified interval
1297                    int rv = Poll.poll(serverPollset, pollTime, desc, true);
1298                    if (rv > 0) {
1299                        keepAliveCount -= rv;
1300                        for (int n = 0; n < rv; n++) {
1301                            // Check for failed sockets and hand this socket off to a worker
1302                            if (((desc[n*2] & Poll.APR_POLLHUP) == Poll.APR_POLLHUP)
1303                                    || ((desc[n*2] & Poll.APR_POLLERR) == Poll.APR_POLLERR)
1304                                    || (comet && (!processSocket(desc[n*2+1], SocketStatus.OPEN))) 
1305                                    || (!comet && (!processSocket(desc[n*2+1])))) {
1306                                // Close socket and clear pool
1307                                if (comet) {
1308                                    processSocket(desc[n*2+1], SocketStatus.DISCONNECT);
1309                                } else {
1310                                    Socket.destroy(desc[n*2+1]);
1311                                }
1312                                continue;
1313                            }
1314                        }
1315                    } else if (rv < 0) {
1316                        int errn = -rv;
1317                        /* Any non timeup or interrupted error is critical */
1318                        if ((errn != Status.TIMEUP) && (errn != Status.EINTR)) {
1319                            if (errn >  Status.APR_OS_START_USERERR) {
1320                                errn -=  Status.APR_OS_START_USERERR;
1321                            }
1322                            log.error(sm.getString("endpoint.poll.fail", "" + errn, Error.strerror(errn)));
1323                            // Handle poll critical failure
1324                            synchronized (this) {
1325                                destroy();
1326                                init();
1327                            }
1328                            continue;
1329                        }
1330                    }
1331                    if (soTimeout > 0 && maintainTime > 1000000L && running) {
1332                        rv = Poll.maintain(serverPollset, desc, true);
1333                        maintainTime = 0;
1334                        if (rv > 0) {
1335                            keepAliveCount -= rv;
1336                            for (int n = 0; n < rv; n++) {
1337                                // Close socket and clear pool
1338                                if (comet) {
1339                                    processSocket(desc[n], SocketStatus.TIMEOUT);
1340                                } else {
1341                                    Socket.destroy(desc[n]);
1342                                }
1343                            }
1344                        }
1345                    }
1346                } catch (Throwable t) {
1347                    log.error(sm.getString("endpoint.poll.error"), t);
1348                }
1349
1350            }
1351
1352            synchronized (this) {
1353                this.notifyAll();
1354            }
1355
1356        }
1357        
1358    }
1359
1360
1361    // ----------------------------------------------------- Worker Inner Class
1362
1363
1364    /**
1365     * Server processor class.
1366     */
1367    protected class Worker implements Runnable {
1368
1369
1370        protected Thread thread = null;
1371        protected boolean available = false;
1372        protected long socket = 0;
1373        protected SocketStatus status = null;
1374        protected boolean options = false;
1375
1376
1377        /**
1378         * Process an incoming TCP/IP connection on the specified socket.  Any
1379         * exception that occurs during processing must be logged and swallowed.
1380         * <b>NOTE</b>:  This method is called from our Connector's thread.  We
1381         * must assign it to our own thread so that multiple simultaneous
1382         * requests can be handled.
1383         *
1384         * @param socket TCP socket to process
1385         */
1386        protected synchronized void assignWithOptions(long socket) {
1387
1388            // Wait for the Processor to get the previous Socket
1389            while (available) {
1390                try {
1391                    wait();
1392                } catch (InterruptedException e) {
1393                }
1394            }
1395
1396            // Store the newly available Socket and notify our thread
1397            this.socket = socket;
1398            status = null;
1399            options = true;
1400            available = true;
1401            notifyAll();
1402
1403        }
1404
1405
1406        /**
1407         * Process an incoming TCP/IP connection on the specified socket.  Any
1408         * exception that occurs during processing must be logged and swallowed.
1409         * <b>NOTE</b>:  This method is called from our Connector's thread.  We
1410         * must assign it to our own thread so that multiple simultaneous
1411         * requests can be handled.
1412         *
1413         * @param socket TCP socket to process
1414         */
1415        protected synchronized void assign(long socket) {
1416
1417            // Wait for the Processor to get the previous Socket
1418            while (available) {
1419                try {
1420                    wait();
1421                } catch (InterruptedException e) {
1422                }
1423            }
1424
1425            // Store the newly available Socket and notify our thread
1426            this.socket = socket;
1427            status = null;
1428            options = false;
1429            available = true;
1430            notifyAll();
1431
1432        }
1433
1434
1435        protected synchronized void assign(long socket, SocketStatus status) {
1436
1437            // Wait for the Processor to get the previous Socket
1438            while (available) {
1439                try {
1440                    wait();
1441                } catch (InterruptedException e) {
1442                }
1443            }
1444
1445            // Store the newly available Socket and notify our thread
1446            this.socket = socket;
1447            this.status = status;
1448            options = false;
1449            available = true;
1450            notifyAll();
1451
1452        }
1453
1454
1455        /**
1456         * Await a newly assigned Socket from our Connector, or <code>null</code>
1457         * if we are supposed to shut down.
1458         */
1459        protected synchronized long await() {
1460
1461            // Wait for the Connector to provide a new Socket
1462            while (!available) {
1463                try {
1464                    wait();
1465                } catch (InterruptedException e) {
1466                }
1467            }
1468
1469            // Notify the Connector that we have received this Socket
1470            long socket = this.socket;
1471            available = false;
1472            notifyAll();
1473
1474            return (socket);
1475
1476        }
1477
1478
1479        /**
1480         * The background thread that listens for incoming TCP/IP connections and
1481         * hands them off to an appropriate processor.
1482         */
1483        public void run() {
1484
1485            // Process requests until we receive a shutdown signal
1486            while (running) {
1487
1488                // Wait for the next socket to be assigned
1489                long socket = await();
1490                if (socket == 0)
1491                    continue;
1492
1493                // Process the request from this socket
1494                if ((status != null) && (handler.event(socket, status) == Handler.SocketState.CLOSED)) {
1495                    // Close socket and pool
1496                    Socket.destroy(socket);
1497                    socket = 0;
1498                } else if ((status == null) && ((options && !setSocketOptions(socket)) 
1499                        || handler.process(socket) == Handler.SocketState.CLOSED)) {
1500                    // Close socket and pool
1501                    Socket.destroy(socket);
1502                    socket = 0;
1503                }
1504
1505                // Finish up this request
1506                recycleWorkerThread(this);
1507
1508            }
1509
1510        }
1511
1512
1513        /**
1514         * Start the background processing thread.
1515         */
1516        public void start() {
1517            thread = new Thread(this);
1518            thread.setName(getName() + "-" + (++curThreads));
1519            thread.setDaemon(true);
1520            thread.start();
1521        }
1522
1523
1524    }
1525
1526
1527    // ----------------------------------------------- SendfileData Inner Class
1528
1529
1530    /**
1531     * SendfileData class.
1532     */
1533    public static class SendfileData {
1534        // File
1535        public String fileName;
1536        public long fd;
1537        public long fdpool;
1538        // Range information
1539        public long start;
1540        public long end;
1541        // Socket and socket pool
1542        public long socket;
1543        // Position
1544        public long pos;
1545        // KeepAlive flag
1546        public boolean keepAlive;
1547    }
1548
1549
1550    // --------------------------------------------------- Sendfile Inner Class
1551
1552
1553    /**
1554     * Sendfile class.
1555     */
1556    public class Sendfile implements Runnable {
1557
1558        protected long sendfilePollset = 0;
1559        protected long pool = 0;
1560        protected long[] desc;
1561        protected HashMap<Long, SendfileData> sendfileData;
1562        
1563        protected int sendfileCount;
1564        public int getSendfileCount() { return sendfileCount; }
1565
1566        protected ArrayList<SendfileData> addS;
1567
1568        /**
1569         * Create the sendfile poller. With some versions of APR, the maximum poller size will
1570         * be 62 (reocmpiling APR is necessary to remove this limitatio

Large files files are truncated, but you can click here to view the full file