Search in sources :

Example 11 with I2PAppThread

use of net.i2p.util.I2PAppThread in project i2p.i2p by i2p.

the class RouterConsoleRunner method startConsole.

/**
 *  http://irc.codehaus.org/display/JETTY/Porting+to+jetty6
 *
 *<pre>
 *	Server
 *		HandlerCollection
 *			HostCheckHandler (extends GzipHandler)
 *				ContextHandlerCollection
 *					LocaleWebAppHandler (routerconsole)
 *						SessionHandler
 *						SecurityHandler
 *						ServletHandler
 *							servlets...
 *					WebAppContext (i2psnark)
 *					WebAppContext (i2ptunnel)
 *					WebAppContext (imagegen)
 *					WebAppContext (susidns)
 *					WebAppContext (susimail)
 *					WebAppContext (for each plugin with a .war)
 *			DefaultHandler
 *			RequestLogHandler (opt)
 *</pre>
 *
 *  Porting to Jetty 9:
 *
 *  http://dev.eclipse.org/mhonarc/lists/jetty-dev/msg01952.html
 *  You are missing a few facts about Jetty 9.1 ...
 *  First, there are no longer any blocking connectors.
 *  Its all async / nio connectors now. (mainly because that's the direction that the servlet api 3.1 is taking)
 *
 *  Next, there is only 1 connector.   The ServerConnector.
 *  However, it takes 1 or more ConnectionFactory implementations to know how to handle the incoming connection.
 *  We have factories for HTTP (0.9 thru 1.1), SPDY, SSL-http, and SSL-npn so far.
 *  This list of factories will expand as the future of connectivity to web servers is ever growing (think HTTP/2)
 *
 *  Use the embedded examples for help understanding this.
 *  http://git.eclipse.org/c/jetty/org.eclipse.jetty.project.git/tree/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyConnectors.java?id=jetty-9.1.0.RC0
 */
public void startConsole() {
    File workDir = new SecureDirectory(_context.getTempDir(), "jetty-work");
    boolean workDirRemoved = FileUtil.rmdir(workDir, false);
    if (!workDirRemoved)
        System.err.println("ERROR: Unable to remove Jetty temporary work directory");
    boolean workDirCreated = workDir.mkdirs();
    if (!workDirCreated)
        System.err.println("ERROR: Unable to create Jetty temporary work directory");
    // so Jetty can find WebAppConfiguration
    System.setProperty("jetty.class.path", _context.getBaseDir() + "/lib/routerconsole.jar");
    // FIXME
    // http://dev.eclipse.org/mhonarc/lists/jetty-users/msg03487.html
    // _server.setGracefulShutdown(1000);
    // In Jetty 6, QTP was not concurrent, so we switched to
    // ThreadPoolExecutor with a fixed-size queue, a set maxThreads,
    // and a RejectedExecutionPolicy of CallerRuns.
    // Unfortunately, CallerRuns causes lockups in Jetty NIO (ticket #1395)
    // In addition, no flavor of TPE gives us what QTP does:
    // - TPE direct handoff (which we were using) never queues.
    // This doesn't provide any burst management when maxThreads is reached.
    // CallerRuns was an attempt to work around that.
    // - TPE unbounded queue does not adjust the number of threads.
    // This doesn't provide automatic resource management.
    // - TPE bounded queue does not add threads until the queue is full.
    // This doesn't provide good responsiveness to even small bursts.
    // QTP adds threads as soon as the queue is non-empty.
    // QTP as of Jetty 7 uses concurrent.
    // QTP unbounded queue is the default in Jetty.
    // So switch back to QTP with a bounded queue.
    // 
    // ref:
    // http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/ThreadPoolExecutor.html
    // https://wiki.eclipse.org/Jetty/Howto/High_Load
    // 
    // try {
    // ThreadPool ctp = new CustomThreadPoolExecutor();
    // // Gone in Jetty 7
    // //ctp.prestartAllCoreThreads();
    // _server.setThreadPool(ctp);
    // } catch (Throwable t) {
    // class not found...
    // System.out.println("INFO: Jetty concurrent ThreadPool unavailable, using QueuedThreadPool");
    LinkedBlockingQueue<Runnable> lbq = new LinkedBlockingQueue<Runnable>(4 * MAX_THREADS);
    // min and max threads will be reset below
    QueuedThreadPool qtp = new QueuedThreadPool(MAX_THREADS, MIN_THREADS, MAX_IDLE_TIME, lbq);
    qtp.setName(THREAD_NAME);
    qtp.setDaemon(true);
    _server = new Server(qtp);
    // }
    HandlerCollection hColl = new HandlerCollection();
    ContextHandlerCollection chColl = new ContextHandlerCollection();
    HostCheckHandler chCollWrapper = new HostCheckHandler(_context);
    chCollWrapper.setHandler(chColl);
    // gone in Jetty 7
    // _server.addHandler(hColl);
    _server.setHandler(hColl);
    hColl.addHandler(chCollWrapper);
    hColl.addHandler(new DefaultHandler());
    String log = _context.getProperty("routerconsole.log");
    if (log != null) {
        File logFile = new File(log);
        if (!logFile.isAbsolute())
            logFile = new File(_context.getLogDir(), "logs/" + log);
        try {
            RequestLogHandler rhl = new RequestLogHandler();
            rhl.setRequestLog(new NCSARequestLog(logFile.getAbsolutePath()));
            hColl.addHandler(rhl);
        } catch (Exception ioe) {
            System.err.println("ERROR: Unable to create Jetty log: " + ioe);
        }
    }
    boolean rewrite = false;
    Properties props = webAppProperties();
    if (props.isEmpty()) {
        props.setProperty(PREFIX + ROUTERCONSOLE + ENABLED, "true");
        rewrite = true;
    }
    // Get an absolute path with a trailing slash for the webapps dir
    // We assume relative to the base install dir for backward compatibility
    File app = new File(_webAppsDir);
    if (!app.isAbsolute()) {
        app = new File(_context.getBaseDir(), _webAppsDir);
        try {
            _webAppsDir = app.getCanonicalPath();
        } catch (IOException ioe) {
        }
    }
    if (!_webAppsDir.endsWith("/"))
        _webAppsDir += '/';
    Set<String> listenHosts = new HashSet<String>(8);
    HandlerWrapper rootWebApp = null;
    ServletHandler rootServletHandler = null;
    List<Connector> connectors = new ArrayList<Connector>(4);
    try {
        int boundAddresses = 0;
        SortedSet<String> addresses = Addresses.getAllAddresses();
        boolean hasIPV4 = addresses.contains("0.0.0.0");
        boolean hasIPV6 = addresses.contains("0:0:0:0:0:0:0:0");
        // add standard listeners
        int lport = 0;
        if (_listenPort != null) {
            try {
                lport = Integer.parseInt(_listenPort);
            } catch (NumberFormatException nfe) {
            }
            if (lport <= 0)
                System.err.println("Bad routerconsole port " + _listenPort);
        }
        if (lport > 0) {
            List<String> hosts = new ArrayList<String>(2);
            StringTokenizer tok = new StringTokenizer(_listenHost, " ,");
            while (tok.hasMoreTokens()) {
                String host = tok.nextToken().trim();
                try {
                    // connectors are bad
                    if ((!hasIPV6) && Addresses.isIPv6Address(host))
                        throw new IOException("IPv6 addresses unsupported");
                    if ((!hasIPV4) && Addresses.isIPv4Address(host))
                        throw new IOException("IPv4 addresses unsupported");
                    ServerSocket testSock = null;
                    try {
                        // On Windows, this was passing and Jetty was still failing,
                        // possibly due to %scope_id ???
                        // https://issues.apache.org/jira/browse/ZOOKEEPER-667
                        // so do exactly what Jetty does in SelectChannelConnector.open()
                        testSock = new ServerSocket();
                        InetSocketAddress isa = new InetSocketAddress(host, 0);
                        testSock.bind(isa);
                    } finally {
                        if (testSock != null)
                            try {
                                testSock.close();
                            } catch (IOException ioe) {
                            }
                    }
                    HttpConfiguration httpConfig = new HttpConfiguration();
                    // number of acceptors, (default) number of selectors
                    ServerConnector lsnr = new ServerConnector(_server, 1, 0, new HttpConnectionFactory(httpConfig));
                    // lsnr.setUseDirectBuffers(false);  // default true seems to be leaky
                    lsnr.setHost(host);
                    lsnr.setPort(lport);
                    // default 10 sec
                    lsnr.setIdleTimeout(90 * 1000);
                    // all with same name will use the same thread pool
                    lsnr.setName("ConsoleSocket");
                    // _server.addConnector(lsnr);
                    connectors.add(lsnr);
                    boundAddresses++;
                    hosts.add(host);
                } catch (Exception ioe) {
                    System.err.println("Unable to bind routerconsole to " + host + " port " + _listenPort + ": " + ioe);
                    System.err.println("You may ignore this warning if the console is still available at http://localhost:" + _listenPort);
                }
            }
            if (hosts.isEmpty()) {
                _context.portMapper().register(PortMapper.SVC_CONSOLE, lport);
            } else {
                // put IPv4 first
                Collections.sort(hosts, new HostComparator());
                _context.portMapper().register(PortMapper.SVC_CONSOLE, hosts.get(0), lport);
                // note that we could still fail in connector.start() below
                listenHosts.addAll(hosts);
            }
        }
        // add SSL listeners
        int sslPort = 0;
        if (_sslListenPort != null) {
            try {
                sslPort = Integer.parseInt(_sslListenPort);
            } catch (NumberFormatException nfe) {
            }
            if (sslPort <= 0)
                System.err.println("Bad routerconsole SSL port " + _sslListenPort);
        }
        if (sslPort > 0) {
            File keyStore = new File(_context.getConfigDir(), "keystore/console.ks");
            // Put the list of hosts together early, so we can put it in the selfsigned cert.
            StringTokenizer tok = new StringTokenizer(_sslListenHost, " ,");
            Set<String> altNames = new HashSet<String>(4);
            while (tok.hasMoreTokens()) {
                String s = tok.nextToken().trim();
                if (!s.equals("0.0.0.0") && !s.equals("::") && !s.equals("0:0:0:0:0:0:0:0"))
                    altNames.add(s);
            }
            String allowed = _context.getProperty(PROP_ALLOWED_HOSTS);
            if (allowed != null) {
                tok = new StringTokenizer(allowed, " ,");
                while (tok.hasMoreTokens()) {
                    altNames.add(tok.nextToken().trim());
                }
            }
            if (verifyKeyStore(keyStore, altNames)) {
                // the keystore path and password
                SslContextFactory sslFactory = new SslContextFactory(keyStore.getAbsolutePath());
                sslFactory.setKeyStorePassword(_context.getProperty(PROP_KEYSTORE_PASSWORD, KeyStoreUtil.DEFAULT_KEYSTORE_PASSWORD));
                // the X.509 cert password (if not present, verifyKeyStore() returned false)
                sslFactory.setKeyManagerPassword(_context.getProperty(PROP_KEY_PASSWORD, "thisWontWork"));
                sslFactory.addExcludeProtocols(I2PSSLSocketFactory.EXCLUDE_PROTOCOLS.toArray(new String[I2PSSLSocketFactory.EXCLUDE_PROTOCOLS.size()]));
                sslFactory.addExcludeCipherSuites(I2PSSLSocketFactory.EXCLUDE_CIPHERS.toArray(new String[I2PSSLSocketFactory.EXCLUDE_CIPHERS.size()]));
                List<String> hosts = new ArrayList<String>(2);
                tok = new StringTokenizer(_sslListenHost, " ,");
                while (tok.hasMoreTokens()) {
                    String host = tok.nextToken().trim();
                    // doing it this way means we don't have to escape an IPv6 host with []
                    try {
                        // connectors are bad
                        if ((!hasIPV6) && Addresses.isIPv6Address(host))
                            throw new IOException("IPv6 addresses unsupported");
                        if ((!hasIPV4) && Addresses.isIPv4Address(host))
                            throw new IOException("IPv4 addresses unsupported");
                        ServerSocket testSock = null;
                        try {
                            // see comments above
                            testSock = new ServerSocket();
                            InetSocketAddress isa = new InetSocketAddress(host, 0);
                            testSock.bind(isa);
                        } finally {
                            if (testSock != null)
                                try {
                                    testSock.close();
                                } catch (IOException ioe) {
                                }
                        }
                        HttpConfiguration httpConfig = new HttpConfiguration();
                        httpConfig.setSecureScheme("https");
                        httpConfig.setSecurePort(sslPort);
                        httpConfig.addCustomizer(new SecureRequestCustomizer());
                        // number of acceptors, (default) number of selectors
                        ServerConnector ssll = new ServerConnector(_server, 1, 0, new SslConnectionFactory(sslFactory, "http/1.1"), new HttpConnectionFactory(httpConfig));
                        // sssll.setUseDirectBuffers(false);  // default true seems to be leaky
                        ssll.setHost(host);
                        ssll.setPort(sslPort);
                        // default 10 sec
                        ssll.setIdleTimeout(90 * 1000);
                        // all with same name will use the same thread pool
                        ssll.setName("ConsoleSocket");
                        // _server.addConnector(ssll);
                        connectors.add(ssll);
                        boundAddresses++;
                        hosts.add(host);
                    } catch (Exception e) {
                        System.err.println("Unable to bind routerconsole to " + host + " port " + sslPort + " for SSL: " + e);
                        if (SystemVersion.isGNU())
                            System.err.println("Probably because GNU classpath does not support Sun keystores");
                        System.err.println("You may ignore this warning if the console is still available at https://localhost:" + sslPort);
                    }
                }
                if (hosts.isEmpty()) {
                    _context.portMapper().register(PortMapper.SVC_HTTPS_CONSOLE, sslPort);
                } else {
                    // put IPv4 first
                    Collections.sort(hosts, new HostComparator());
                    _context.portMapper().register(PortMapper.SVC_HTTPS_CONSOLE, hosts.get(0), sslPort);
                    // note that we could still fail in connector.start() below
                    listenHosts.addAll(hosts);
                }
            } else {
                System.err.println("Unable to create or access keystore for SSL: " + keyStore.getAbsolutePath());
            }
        }
        if (boundAddresses <= 0) {
            System.err.println("Unable to bind routerconsole to any address on port " + _listenPort + (sslPort > 0 ? (" or SSL port " + sslPort) : ""));
            return;
        }
        // Each address spawns a Connector and an Acceptor thread
        // If the min is less than this, we have no thread for the handlers or the expiration thread.
        qtp.setMinThreads(MIN_THREADS + (2 * boundAddresses));
        qtp.setMaxThreads(MAX_THREADS + (2 * boundAddresses));
        File tmpdir = new SecureDirectory(workDir, ROUTERCONSOLE + "-" + (_listenPort != null ? _listenPort : _sslListenPort));
        tmpdir.mkdir();
        rootServletHandler = new ServletHandler();
        rootWebApp = new LocaleWebAppHandler(_context, "/", _webAppsDir + ROUTERCONSOLE + ".war", tmpdir, rootServletHandler);
        try {
            // Not sure who is supposed to call this, but unless we do,
            // all the jsps die NPE, because JspFactory.getDefaultContext() returns null.
            // We probably have to do this because we don't bundle the Jetty annotations jar and scanner.
            // This is only with Tomcat 8, not with the Jetty (Eclipse) jsp impl.
            // Got a clue from this ancient post for Tomcat 6:
            // https://bz.apache.org/bugzilla/show_bug.cgi?id=39804
            // see also apps/jetty/build.xml
            Class.forName("org.eclipse.jetty.apache.jsp.JettyJasperInitializer");
        } catch (ClassNotFoundException cnfe) {
            System.err.println("Warning: JettyJasperInitializer not found");
        }
        WebAppContext wac = (WebAppContext) (rootWebApp.getHandler());
        initialize(_context, wac);
        WebAppStarter.setWebAppConfiguration(wac);
        chColl.addHandler(rootWebApp);
    } catch (Exception ioe) {
        ioe.printStackTrace();
    }
    // fix up the allowed hosts set (see HostCheckHandler)
    if (listenHosts.contains("0.0.0.0") || listenHosts.contains("::") || listenHosts.contains("0:0:0:0:0:0:0:0")) {
        // empty set says all are valid
        listenHosts.clear();
    } else {
        listenHosts.add("localhost");
        listenHosts.add("127.0.0.1");
        listenHosts.add("::1");
        listenHosts.add("0:0:0:0:0:0:0:1");
        String allowed = _context.getProperty(PROP_ALLOWED_HOSTS);
        if (allowed != null) {
            StringTokenizer tok = new StringTokenizer(allowed, " ,");
            while (tok.hasMoreTokens()) {
                listenHosts.add(tok.nextToken());
            }
        }
    }
    chCollWrapper.setListenHosts(listenHosts);
    // https://bugs.eclipse.org/bugs/show_bug.cgi?id=364936
    // WARN:oejw.WebAppContext:Failed startup of context o.e.j.w.WebAppContext{/,jar:file:/.../webapps/routerconsole.war!/},/.../webapps/routerconsole.war
    // java.lang.IllegalStateException: zip file closed
    Resource.setDefaultUseCaches(false);
    try {
        // start does a mapContexts()
        _server.start();
    } catch (Throwable me) {
        // NoClassFoundDefError from a webapp is a throwable, not an exception
        System.err.println("Error starting the Router Console server: " + me);
        me.printStackTrace();
    }
    if (_server.isRunning()) {
        // Add and start the connectors one-by-one
        boolean error = false;
        for (Connector conn : connectors) {
            try {
                _server.addConnector(conn);
                // start after adding so it gets the right thread pool
                conn.start();
            } catch (Throwable me) {
                try {
                    _server.removeConnector(conn);
                } catch (Throwable t) {
                    t.printStackTrace();
                }
                System.err.println("WARNING: Error starting " + conn + ": " + me);
                me.printStackTrace();
                error = true;
            }
        }
        if (error) {
            String port = (_listenPort != null) ? _listenPort : ((_sslListenPort != null) ? _sslListenPort : Integer.toString(DEFAULT_LISTEN_PORT));
            System.err.println("WARNING: Error starting one or more listeners of the Router Console server.\n" + "If your console is still accessible at http://127.0.0.1:" + port + "/,\n" + "this may be a problem only with binding to the IPV6 address ::1.\n" + "If so, you may ignore this error, or remove the\n" + "\"::1,\" in the \"clientApp.0.args\" line of the clients.config file.");
        }
    }
    // Start all the other webapps after the server is up,
    // so things start faster.
    // Jetty 6 starts the connector before the router console is ready
    // This also prevents one webapp from breaking the whole thing
    List<String> notStarted = new ArrayList<String>();
    if (_server.isRunning()) {
        File dir = new File(_webAppsDir);
        File[] files = dir.listFiles(WAR_FILTER);
        if (files != null) {
            for (int i = 0; i < files.length; i++) {
                String appName = files[i].getName();
                appName = appName.substring(0, appName.lastIndexOf(".war"));
                String enabled = props.getProperty(PREFIX + appName + ENABLED);
                if (appName.equals("addressbook")) {
                    // addressbook.war is now empty, thread is started by SusiDNS
                    if (enabled != null) {
                        props.remove(PREFIX + "addressbook" + ENABLED);
                        rewrite = true;
                    }
                } else if (!"false".equals(enabled)) {
                    try {
                        String path = files[i].getCanonicalPath();
                        WebAppStarter.startWebApp(_context, chColl, appName, path);
                        if (enabled == null) {
                            // do this so configclients.jsp knows about all apps from reading the config
                            props.setProperty(PREFIX + appName + ENABLED, "true");
                            rewrite = true;
                        }
                    } catch (Throwable t) {
                        System.err.println("ERROR: Failed to start " + appName + ' ' + t);
                        t.printStackTrace();
                        notStarted.add(appName);
                    }
                } else {
                    notStarted.add(appName);
                }
            }
            changeState(RUNNING);
            if (_mgr != null)
                _mgr.register(this);
        }
    } else {
        System.err.println("ERROR: Router console did not start, not starting webapps");
        changeState(START_FAILED);
    }
    if (rewrite)
        storeWebAppProperties(_context, props);
    if (rootServletHandler != null && notStarted.size() > 0) {
        // map each not-started webapp to the error page
        ServletHolder noWebApp = rootServletHandler.getServlet("net.i2p.router.web.jsp.nowebapp_jsp");
        for (int i = 0; i < notStarted.size(); i++) {
            // we want a new handler for each one since if the webapp is started we remove the handler???
            try {
                if (noWebApp != null) {
                    String path = '/' + notStarted.get(i);
                    // LocaleWebAppsHandler adds a .jsp
                    rootServletHandler.addServletWithMapping(noWebApp, path + ".jsp");
                    rootServletHandler.addServletWithMapping(noWebApp, path + "/*");
                } else {
                    System.err.println("Can't find nowebapp.jsp?");
                }
            } catch (Throwable me) {
                System.err.println(me);
                me.printStackTrace();
            }
        }
    }
    Thread t = new I2PAppThread(new StatSummarizer(), "StatSummarizer", true);
    t.setPriority(Thread.NORM_PRIORITY - 1);
    t.start();
    ConsoleUpdateManager um = new ConsoleUpdateManager(_context, _mgr, null);
    um.start();
    NewsManager nm = new NewsManager(_context, _mgr, null);
    nm.startup();
    if (PluginStarter.pluginsEnabled(_context)) {
        t = new I2PAppThread(new PluginStarter(_context), "PluginStarter", true);
        t.setPriority(Thread.NORM_PRIORITY - 1);
        t.start();
    }
    // RouterAppManager registers its own hook
    if (_mgr == null)
        _context.addShutdownTask(new ServerShutdown());
    ConfigServiceHandler.registerSignalHandler(_context);
}
Also used : Server(org.eclipse.jetty.server.Server) InetSocketAddress(java.net.InetSocketAddress) ArrayList(java.util.ArrayList) ContextHandlerCollection(org.eclipse.jetty.server.handler.ContextHandlerCollection) HttpConfiguration(org.eclipse.jetty.server.HttpConfiguration) LinkedBlockingQueue(java.util.concurrent.LinkedBlockingQueue) ServerConnector(org.eclipse.jetty.server.ServerConnector) WebAppContext(org.eclipse.jetty.webapp.WebAppContext) NewsManager(net.i2p.router.news.NewsManager) HashSet(java.util.HashSet) File(java.io.File) AbstractConnector(org.eclipse.jetty.server.AbstractConnector) ServerConnector(org.eclipse.jetty.server.ServerConnector) Connector(org.eclipse.jetty.server.Connector) ServletHandler(org.eclipse.jetty.servlet.ServletHandler) ServletHolder(org.eclipse.jetty.servlet.ServletHolder) Properties(java.util.Properties) SslConnectionFactory(org.eclipse.jetty.server.SslConnectionFactory) HandlerWrapper(org.eclipse.jetty.server.handler.HandlerWrapper) I2PAppThread(net.i2p.util.I2PAppThread) SslContextFactory(org.eclipse.jetty.util.ssl.SslContextFactory) QueuedThreadPool(org.eclipse.jetty.util.thread.QueuedThreadPool) RequestLogHandler(org.eclipse.jetty.server.handler.RequestLogHandler) NCSARequestLog(org.eclipse.jetty.server.NCSARequestLog) ContextHandlerCollection(org.eclipse.jetty.server.handler.ContextHandlerCollection) HandlerCollection(org.eclipse.jetty.server.handler.HandlerCollection) SecureRequestCustomizer(org.eclipse.jetty.server.SecureRequestCustomizer) HttpConnectionFactory(org.eclipse.jetty.server.HttpConnectionFactory) ServerSocket(java.net.ServerSocket) ConsoleUpdateManager(net.i2p.router.update.ConsoleUpdateManager) IOException(java.io.IOException) UnsupportedEncodingException(java.io.UnsupportedEncodingException) IOException(java.io.IOException) Constraint(org.eclipse.jetty.util.security.Constraint) DefaultHandler(org.eclipse.jetty.server.handler.DefaultHandler) I2PAppThread(net.i2p.util.I2PAppThread) StringTokenizer(java.util.StringTokenizer) SecureDirectory(net.i2p.util.SecureDirectory)

Example 12 with I2PAppThread

use of net.i2p.util.I2PAppThread in project i2p.i2p by i2p.

the class PluginStarter method updateAll.

/**
 *  threaded
 *  @since 0.8.13, public since 0.9.33, was package private
 */
public static void updateAll(RouterContext ctx) {
    Thread t = new I2PAppThread(new PluginUpdater(ctx), "PluginUpdater", true);
    t.start();
}
Also used : I2PAppThread(net.i2p.util.I2PAppThread) I2PAppThread(net.i2p.util.I2PAppThread)

Example 13 with I2PAppThread

use of net.i2p.util.I2PAppThread in project i2p.i2p by i2p.

the class I2PSessionImpl method connect.

/**
 * Connect to the router and establish a session.  This call blocks until
 * a session is granted.
 *
 * Should be threadsafe, other threads will block until complete.
 * Disconnect / destroy from another thread may be called simultaneously and
 * will (should?) interrupt the connect.
 *
 * Connecting a primary session will not automatically connect subsessions.
 * Connecting a subsession will automatically connect the primary session
 * if not previously connected.
 *
 * @throws I2PSessionException if there is a configuration error or the router is
 *                             not reachable
 */
public void connect() throws I2PSessionException {
    synchronized (_stateLock) {
        boolean wasOpening = false;
        boolean loop = true;
        while (loop) {
            switch(_state) {
                case INIT:
                    loop = false;
                    break;
                case CLOSED:
                    if (wasOpening)
                        throw new I2PSessionException("connect by other thread failed");
                    loop = false;
                    break;
                case OPENING:
                case GOTDATE:
                    wasOpening = true;
                    try {
                        _stateLock.wait(10 * 1000);
                    } catch (InterruptedException ie) {
                        throw new I2PSessionException("Interrupted", ie);
                    }
                    break;
                case CLOSING:
                    throw new I2PSessionException("close in progress");
                case OPEN:
                    return;
            }
        }
        changeState(State.OPENING);
    }
    _availabilityNotifier.stopNotifying();
    if ((_options != null) && (I2PClient.PROP_RELIABILITY_GUARANTEED.equals(_options.getProperty(I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_BEST_EFFORT)))) {
        if (_log.shouldLog(Log.ERROR))
            _log.error("I2CP guaranteed delivery mode has been removed, using best effort.");
    }
    boolean success = false;
    long startConnect = _context.clock().now();
    try {
        // protect w/ closeSocket()
        synchronized (_stateLock) {
            // If we are in the router JVM, connect using the internal queue
            if (_context.isRouterContext()) {
                // _socket and _writer remain null
                InternalClientManager mgr = _context.internalClientManager();
                if (mgr == null)
                    throw new I2PSessionException("Router is not ready for connections");
                // the following may throw an I2PSessionException
                _queue = mgr.connect();
                _reader = new QueuedI2CPMessageReader(_queue, this);
            } else {
                if (SystemVersion.isAndroid() && _options.getProperty(PROP_DOMAIN_SOCKET) != null) {
                    try {
                        Class<?> clazz = Class.forName("net.i2p.client.DomainSocketFactory");
                        Constructor<?> ctor = clazz.getDeclaredConstructor(I2PAppContext.class);
                        Object fact = ctor.newInstance(_context);
                        Method createSocket = clazz.getDeclaredMethod("createSocket", String.class);
                        try {
                            _socket = (Socket) createSocket.invoke(fact, _options.getProperty(PROP_DOMAIN_SOCKET));
                        } catch (InvocationTargetException e) {
                            throw new I2PSessionException("Cannot create domain socket", e);
                        }
                    } catch (ClassNotFoundException e) {
                        throw new I2PSessionException("Cannot load DomainSocketFactory", e);
                    } catch (NoSuchMethodException e) {
                        throw new I2PSessionException("Cannot load DomainSocketFactory", e);
                    } catch (InstantiationException e) {
                        throw new I2PSessionException("Cannot load DomainSocketFactory", e);
                    } catch (IllegalAccessException e) {
                        throw new I2PSessionException("Cannot load DomainSocketFactory", e);
                    } catch (InvocationTargetException e) {
                        throw new I2PSessionException("Cannot load DomainSocketFactory", e);
                    }
                } else if (Boolean.parseBoolean(_options.getProperty(PROP_ENABLE_SSL))) {
                    try {
                        I2PSSLSocketFactory fact = new I2PSSLSocketFactory(_context, false, "certificates/i2cp");
                        _socket = fact.createSocket(_hostname, _portNum);
                        _socket.setKeepAlive(true);
                    } catch (GeneralSecurityException gse) {
                        IOException ioe = new IOException("SSL Fail");
                        ioe.initCause(gse);
                        throw ioe;
                    }
                } else {
                    _socket = new Socket(_hostname, _portNum);
                    _socket.setKeepAlive(true);
                }
                // _socket.setSoTimeout(1000000); // Uhmmm we could really-really use a real timeout, and handle it.
                OutputStream out = _socket.getOutputStream();
                out.write(I2PClient.PROTOCOL_BYTE);
                out.flush();
                _writer = new ClientWriterRunner(out, this);
                _writer.startWriting();
                InputStream in = new BufferedInputStream(_socket.getInputStream(), BUF_SIZE);
                _reader = new I2CPMessageReader(in, this);
            }
        }
        if (_log.shouldLog(Log.DEBUG))
            _log.debug(getPrefix() + "before startReading");
        _reader.startReading();
        if (_log.shouldLog(Log.DEBUG))
            _log.debug(getPrefix() + "Before getDate");
        Properties auth = null;
        if ((!_context.isRouterContext()) && _options.containsKey(PROP_USER) && _options.containsKey(PROP_PW)) {
            // Only supported by routers 0.9.11 or higher, but we don't know the version yet.
            // Auth will also be sent in the SessionConfig.
            auth = new OrderedProperties();
            auth.setProperty(PROP_USER, _options.getProperty(PROP_USER));
            auth.setProperty(PROP_PW, _options.getProperty(PROP_PW));
        }
        sendMessage_unchecked(new GetDateMessage(CoreVersion.VERSION, auth));
        waitForDate();
        if (_log.shouldLog(Log.DEBUG))
            _log.debug(getPrefix() + "Before producer.connect()");
        _producer.connect(this);
        if (_log.shouldLog(Log.DEBUG))
            _log.debug(getPrefix() + "After producer.connect()");
        // wait until we have created a lease set
        int waitcount = 0;
        while (_leaseSet == null) {
            if (waitcount++ > 5 * 60) {
                throw new IOException("No tunnels built after waiting 5 minutes. Your network connection may be down, or there is severe network congestion.");
            }
            synchronized (_leaseSetWait) {
                // InterruptedException caught below
                _leaseSetWait.wait(1000);
            }
            // if we got a disconnect message while waiting
            if (isClosed())
                throw new IOException("Disconnected from router while waiting for tunnels");
        }
        if (_log.shouldLog(Log.INFO)) {
            long connected = _context.clock().now();
            _log.info(getPrefix() + "Lease set created with inbound tunnels after " + (connected - startConnect) + "ms - ready to participate in the network!");
        }
        Thread notifier = new I2PAppThread(_availabilityNotifier, "ClientNotifier " + getPrefix(), true);
        notifier.start();
        startIdleMonitor();
        startVerifyUsage();
        success = true;
        // now send CreateSessionMessages for all subsessions, one at a time, must wait for each response
        synchronized (_subsessionLock) {
            for (SubSession ss : _subsessions) {
                if (_log.shouldLog(Log.INFO))
                    _log.info(getPrefix() + "Connecting subsession " + ss);
                _producer.connect(ss);
            }
        }
    } catch (InterruptedException ie) {
        throw new I2PSessionException("Interrupted", ie);
    } catch (UnknownHostException uhe) {
        throw new I2PSessionException(getPrefix() + "Cannot connect to the router on " + _hostname + ':' + _portNum, uhe);
    } catch (IOException ioe) {
        // Generate the best error message as this will be logged
        String msg;
        if (_context.isRouterContext())
            msg = "Failed to build tunnels";
        else if (SystemVersion.isAndroid() && _options.getProperty(PROP_DOMAIN_SOCKET) != null)
            msg = "Failed to bind to the router on " + _options.getProperty(PROP_DOMAIN_SOCKET) + " and build tunnels";
        else
            msg = "Cannot connect to the router on " + _hostname + ':' + _portNum + " and build tunnels";
        throw new I2PSessionException(getPrefix() + msg, ioe);
    } finally {
        if (success) {
            changeState(State.OPEN);
        } else {
            _availabilityNotifier.stopNotifying();
            synchronized (_stateLock) {
                changeState(State.CLOSING);
                try {
                    _producer.disconnect(this);
                } catch (I2PSessionException ipe) {
                }
                closeSocket();
            }
        }
    }
}
Also used : OutputStream(java.io.OutputStream) OrderedProperties(net.i2p.util.OrderedProperties) Properties(java.util.Properties) I2PAppThread(net.i2p.util.I2PAppThread) BufferedInputStream(java.io.BufferedInputStream) OrderedProperties(net.i2p.util.OrderedProperties) QueuedI2CPMessageReader(net.i2p.internal.QueuedI2CPMessageReader) GetDateMessage(net.i2p.data.i2cp.GetDateMessage) InternalClientManager(net.i2p.internal.InternalClientManager) UnknownHostException(java.net.UnknownHostException) BufferedInputStream(java.io.BufferedInputStream) InputStream(java.io.InputStream) GeneralSecurityException(java.security.GeneralSecurityException) Method(java.lang.reflect.Method) IOException(java.io.IOException) InvocationTargetException(java.lang.reflect.InvocationTargetException) I2PAppThread(net.i2p.util.I2PAppThread) I2PSSLSocketFactory(net.i2p.util.I2PSSLSocketFactory) I2PSessionException(net.i2p.client.I2PSessionException) QueuedI2CPMessageReader(net.i2p.internal.QueuedI2CPMessageReader) I2CPMessageReader(net.i2p.data.i2cp.I2CPMessageReader) Socket(java.net.Socket)

Example 14 with I2PAppThread

use of net.i2p.util.I2PAppThread in project i2p.i2p by i2p.

the class DisconnectMessageHandler method handleMessage.

public void handleMessage(I2CPMessage message, I2PSessionImpl session) {
    if (_log.shouldLog(Log.DEBUG))
        _log.debug("Handle message " + message);
    String reason = ((DisconnectMessage) message).getReason();
    session.propogateError(reason, new I2PSessionException("Disconnect Message received: " + reason));
    session.destroySession(false);
    if (reason.contains("restart")) {
        Thread t = new I2PAppThread(new Reconnector(session), "Reconnect " + session, true);
        t.start();
    }
}
Also used : DisconnectMessage(net.i2p.data.i2cp.DisconnectMessage) I2PSessionException(net.i2p.client.I2PSessionException) I2PAppThread(net.i2p.util.I2PAppThread) I2PAppThread(net.i2p.util.I2PAppThread)

Example 15 with I2PAppThread

use of net.i2p.util.I2PAppThread in project i2p.i2p by i2p.

the class BOB method run.

/**
 * @since 0.9.10
 */
public void run() {
    if (listener == null)
        return;
    changeState(RUNNING);
    _log.info("BOB is now running.");
    if (_mgr != null)
        _mgr.register(this);
    int i = 0;
    boolean g = false;
    spin.set(true);
    try {
        Socket server = null;
        while (spin.get()) {
            try {
                server = listener.accept();
                server.setKeepAlive(true);
                g = true;
            } catch (ConnectException ce) {
                g = false;
            } catch (SocketTimeoutException ste) {
                g = false;
            }
            if (g) {
                DoCMDS conn_c = new DoCMDS(spin, lock, server, props, database, _log);
                Thread t = new I2PAppThread(conn_c);
                t.setName("BOB.DoCMDS " + i);
                t.start();
                i++;
            }
        }
        changeState(STOPPING);
    } catch (Exception e) {
        if (spin.get())
            _log.error("Unexpected error while listening for connections", e);
        else
            e = null;
        changeState(STOPPING, e);
    } finally {
        _log.info("BOB is now shutting down...");
        // Clean up everything.
        try {
            listener.close();
        } catch (Exception ex) {
        // nop
        }
        // Find all our "BOB.DoCMDS" threads, wait for them to be finished.
        // We could order them to stop, but that could cause nasty issues in the locks.
        visitAllThreads();
        database.getReadLock();
        NamedDB nickinfo;
        try {
            for (Object ndb : database.values()) {
                nickinfo = (NamedDB) ndb;
                nickinfo.getReadLock();
                boolean released = false;
                try {
                    if (nickinfo.get(P_RUNNING).equals(Boolean.TRUE) && nickinfo.get(P_STOPPING).equals(Boolean.FALSE) && nickinfo.get(P_STARTING).equals(Boolean.FALSE)) {
                        nickinfo.releaseReadLock();
                        released = true;
                        nickinfo.getWriteLock();
                        try {
                            nickinfo.add(P_STOPPING, Boolean.TRUE);
                        } finally {
                            nickinfo.releaseWriteLock();
                        }
                    }
                } finally {
                    if (!released)
                        nickinfo.releaseReadLock();
                }
            }
        } finally {
            database.releaseReadLock();
        }
        changeState(STOPPED);
        _log.info("BOB is now stopped.");
    }
}
Also used : SocketTimeoutException(java.net.SocketTimeoutException) Socket(java.net.Socket) ServerSocket(java.net.ServerSocket) I2PAppThread(net.i2p.util.I2PAppThread) IOException(java.io.IOException) FileNotFoundException(java.io.FileNotFoundException) SocketTimeoutException(java.net.SocketTimeoutException) ConnectException(java.net.ConnectException) ConnectException(java.net.ConnectException) I2PAppThread(net.i2p.util.I2PAppThread)

Aggregations

I2PAppThread (net.i2p.util.I2PAppThread)52 IOException (java.io.IOException)18 I2PException (net.i2p.I2PException)9 I2PSocket (net.i2p.client.streaming.I2PSocket)7 InterruptedIOException (java.io.InterruptedIOException)6 ConnectException (java.net.ConnectException)5 Socket (java.net.Socket)5 I2PSessionException (net.i2p.client.I2PSessionException)5 Destination (net.i2p.data.Destination)5 File (java.io.File)3 InputStream (java.io.InputStream)3 OutputStream (java.io.OutputStream)3 UnsupportedEncodingException (java.io.UnsupportedEncodingException)3 ServerSocket (java.net.ServerSocket)3 SocketTimeoutException (java.net.SocketTimeoutException)3 UnknownHostException (java.net.UnknownHostException)3 Properties (java.util.Properties)3 I2PServerSocket (net.i2p.client.streaming.I2PServerSocket)3 DataFormatException (net.i2p.data.DataFormatException)3 FileNotFoundException (java.io.FileNotFoundException)2