All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.milton.simpleton.SslSimpletonServer Maven / Gradle / Ivy

Go to download

Milton Community Edition: Supports DAV level 1 and is available on Apache2 license

There is a newer version: 4.0.3.2215
Show newest version
/*
 *
 * Copyright 2014 McEvoy Software Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.milton.simpleton;

import io.milton.http.HttpManager;
import io.milton.http.http11.Http11ResponseHandler;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SocketChannel;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.Security;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.X509TrustManager;
import org.simpleframework.http.Request;
import org.simpleframework.http.Response;
import org.simpleframework.http.core.Container;
import org.simpleframework.http.core.ContainerServer;
import org.simpleframework.transport.Socket;
import org.simpleframework.transport.connect.Connection;
import org.simpleframework.transport.connect.SocketConnection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *
 * @author bradm
 */
public class SslSimpletonServer implements Container {

    private static final Logger log = LoggerFactory.getLogger( SslSimpletonServer.class );
    public static final String DEFAULT_KEYSTORE_ALGORITHM = ( Security.getProperty( "ssl.KeyManagerFactory.algorithm" ) == null ? "SunX509" : Security.getProperty( "ssl.KeyManagerFactory.algorithm" ) );
    protected final Stage dispatchStage;
	private final HttpManager httpManager;
    private final Http11ResponseHandler responseHandler;
    private int sslPort = 443;
    private File keystoreFile;
    private String keystorePassword;
    private Thread thMonitor;
    private boolean stopped;
    private int maxQueueTimeMillis = 10000;
    private int maxProcessTimeMillis = 60000;
    private Connection connection;
    private Connection sslConnection;
    private String sslProtocol = "TLS";
    private String keystoreType = "JKS";
    private String keystoreAlgorithm = DEFAULT_KEYSTORE_ALGORITHM;


    public SslSimpletonServer(HttpManager httpManager, Http11ResponseHandler responseHandler, int capacity, int numThreads) {
		this.httpManager = httpManager;
        dispatchStage = new Stage<>("dispatchStage", capacity, numThreads, false);
        this.responseHandler = responseHandler;
		thMonitor = new Thread(new TaskMonitor());
    }

    public void start() {
        stopped = false;

        sslConnection = initHttps( sslPort );

        thMonitor = new Thread( new TaskMonitor() );
        thMonitor.start();
    }


    /**
     * Setting up certificates
    EG C:\Program Files\Java\jdk1.6.0_10\bin>keytool -genkey -keystore certs -keyalg rsa -alias jamie -storepass serverkspw -keypass serverpw
    note that 'first name last name' should be machine name

     * @param connection
     * @param port
     */
    protected SocketConnection initHttps( int port ) {
        SSLServerSocketFactory fac = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();

        log.info( "initHttps: port: " + port + " sslProtocol: " + sslProtocol + " keystoreAlgorithm:" + keystoreAlgorithm );
        try {
            KeyStore keystore = KeyStore.getInstance( keystoreType );
            keystore.load( new FileInputStream( keystoreFile ), keystorePassword.toCharArray() );
            log.info( "listing aliases defined in keystore" );
            Enumeration aliases = keystore.aliases();
            while( aliases.hasMoreElements() ) {
                String a = aliases.nextElement();
                log.info( " - alias: " + a );
                Certificate cert = keystore.getCertificate( a );
                log.info("   - cert type: " + cert.getType());
                log.info("   - algorithm: " + cert.getPublicKey().getAlgorithm() );
                log.info("   - format: " + cert.getPublicKey().getFormat() );
            }


            KeyManagerFactory kmf = KeyManagerFactory.getInstance( keystoreAlgorithm );
            kmf.init( keystore, keystorePassword.toCharArray() );

            X509TrustManager trustManager = new AnonymousTrustManager();
            X509TrustManager[] trustManagers = new X509TrustManager[]{trustManager};

            SSLContext sslc = SSLContext.getInstance( sslProtocol ); // An SSLContext is an environment for implementing JSSE. It is used to create a ServerSocketFactory
            sslc.init( kmf.getKeyManagers(), trustManagers, null );


            ContainerServer processor = new ContainerServer(this, 25);
            org.simpleframework.transport.Server secure = new SecureProcessor(processor, sslc);
            SocketConnection ssl = new SocketConnection(secure);
            InetSocketAddress address = new InetSocketAddress( port );
            ssl.connect( address, sslc );

            log.debug( "server running on: " + address );

            return ssl;
        } catch( java.net.BindException ex ) {
            throw new RuntimeException( "Couldnt bind to port: " + port );
        } catch( UnrecoverableKeyException | KeyStoreException | CertificateException | NoSuchAlgorithmException | IOException ex ) {
            throw new RuntimeException( ex );
        } catch( Exception ex ) {
            throw new RuntimeException( ex );
        }
    }

    public void stop() {
        try {
            dispatchStage.close();
        } catch( IOException ex ) {
            log.error( "exception closing dispatchStage", ex );
        }
        stopped = true;
        thMonitor.interrupt();

        if( connection != null ) {
            try {
                connection.close();
            } catch( Exception ex ) {
                log.error( "exception closing http connection", ex );
            }
        }
        if( sslConnection != null ) {
            try {
                sslConnection.close();
            } catch( Exception ex ) {
                log.error( "exception closing ssl connection", ex );
            }
        }

    }

    @Override
    public void handle( Request request, Response response ) {
        Task task = new Task( httpManager, request, response );
        try {
            dispatchStage.enqueue( task );
        } catch( Exception e ) {
            log.warn( "exception dispatching request: " + e.getMessage() );
            SimpleMiltonRequest req = new SimpleMiltonRequest( request );
            SimpleMiltonResponse resp = new SimpleMiltonResponse( response );
            respondError( req, resp, e.getMessage() );
        }
    }

    public void respondError( SimpleMiltonRequest req, SimpleMiltonResponse resp, String reason ) {
        responseHandler.respondServerError( req, resp, reason );
    }

    private void respondError( Task t ) {
        try {
            log.warn( "setting error status becaue request could not be processed" );
            t.response.setCode( 500 );
//            t.response.commit();
            t.response.close();
        } catch( Exception e ) {
            log.error( "error setting last chance error status", e );
        }
    }

    public Integer getHttpPort() {
        return sslPort;
    }


    public void setHttpPort( Integer sslPort ) {
        this.sslPort = sslPort;
    }

    public File getKeystoreFile() {
        return keystoreFile;
    }

    public void setKeystoreFile( File keystoreFile ) {
        this.keystoreFile = keystoreFile;
    }

    public String getKeystorePassword() {
        return keystorePassword;
    }

    public void setKeystorePassword( String keystorePassword ) {
        this.keystorePassword = keystorePassword;
    }

    public String getSslProtocol() {
        return sslProtocol;
    }

    public void setSslProtocol( String sslProtocol ) {
        this.sslProtocol = sslProtocol;
    }

    public String getKeystoreAlgorithm() {
        return keystoreAlgorithm;
    }

    public void setKeystoreAlgorithm( String keystoreAlgorithm ) {
        this.keystoreAlgorithm = keystoreAlgorithm;
    }

    public String getKeystoreType() {
        return keystoreType;
    }

    public void setKeystoreType( String keystoreType ) {
        this.keystoreType = keystoreType;
    }

    public int getMaxProcessTimeMillis() {
        return maxProcessTimeMillis;
    }

    public void setMaxProcessTimeMillis( int maxProcessTimeMillis ) {
        this.maxProcessTimeMillis = maxProcessTimeMillis;
    }

    public int getMaxQueueTimeMillis() {
        return maxQueueTimeMillis;
    }

    public void setMaxQueueTimeMillis( int maxQueueTimeMillis ) {
        this.maxQueueTimeMillis = maxQueueTimeMillis;
    }

    public class TaskMonitor implements Runnable {

        public void run() {
            boolean isInterrupted = false;
            while( !stopped && !isInterrupted ) {
                checkTasks();
                try {
                    Thread.sleep( 1000 );
                } catch( InterruptedException ex ) {
                    log.debug( "interrupted" );
                    isInterrupted = true;
                }
            }
        }
    }

    private void checkTasks() {
        long l;
        for( Task t : this.dispatchStage.queue ) {
            // check enqueue time
            l = System.currentTimeMillis() - t.enqueueTime;
            if( l > maxQueueTimeMillis ) {
                // bif it
                log.warn( "XXX task is too long in queue: " + l + "ms. " + t );
                log.warn( "Queue Size: " + dispatchStage.queue.size() );
                log.warn( "listing contents of queue -" );
                for( Task q : dispatchStage.queue ) {
                    log.warn( " - " + q.request.getTarget() );
                }
                log.warn( "---" );
                this.dispatchStage.queue.remove( t );
                respondError( t );
            } else {
                if( t.startTime > 0 ) {
                    // check process time
                    l = System.currentTimeMillis() - t.startTime;
                    if( l > maxProcessTimeMillis ) {
                        log.warn( "**** task is too long being processed: " + l + "ms. " + t );
                        t.thisThread.interrupt();
                    }
                }
            }
        }
    }

    public static class AnonymousTrustManager implements X509TrustManager {

        public boolean isClientTrusted( X509Certificate[] cert ) {
            return true;
        }

        public boolean isServerTrusted( X509Certificate[] cert ) {
            return true;
        }

        public X509Certificate[] getAcceptedIssuers() {
            return new X509Certificate[0];
        }

        public void checkClientTrusted( X509Certificate[] arg0, String arg1 )
            throws CertificateException {
        }

        public void checkServerTrusted( X509Certificate[] arg0, String arg1 )
            throws CertificateException {
        }
    }

    private static class SecureProcessor implements org.simpleframework.transport.Server {

		private final ContainerServer processor;
		private final SSLContext context;

        public SecureProcessor( ContainerServer processor, SSLContext context ) {
            this.processor = processor;
            this.context = context;
        }

        public void process( Socket pipeline ) throws IOException {
            final SocketChannel channel = pipeline.getChannel();
            final Map map = new HashMap();
            Socket secure = new Socket() {

                private SSLEngine engine;

                public Map getAttributes() {
                    return map;
                }

                public SocketChannel getChannel() {
                    return channel;
                }

                public SSLEngine getEngine() {
                    if( engine == null ) {
                        engine = context.createSSLEngine();
                    }
                    return engine;
                }
            };
            processor.process( secure );
        }

        public void stop() {
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy