blob: 5254dac1fd4f8b1a0bedaf874261f120d0790dfb [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.directory.server.integration.http;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FilenameFilter;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.Set;
import java.util.UUID;
import org.apache.directory.api.ldap.model.constants.SchemaConstants;
import org.apache.directory.api.ldap.model.entry.Entry;
import org.apache.directory.api.ldap.model.name.Dn;
import org.apache.directory.server.bridge.http.HttpDirectoryService;
import org.apache.directory.server.constants.ServerDNConstants;
import org.apache.directory.server.core.api.DirectoryService;
import org.apache.directory.server.core.security.TlsKeyGenerator;
import org.apache.directory.server.i18n.I18n;
import org.apache.directory.server.protocol.shared.transport.TcpTransport;
import org.bouncycastle.jce.provider.X509CertParser;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.SecureRequestCustomizer;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.SslConnectionFactory;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.HandlerList;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jetty.xml.XmlConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Class to start the jetty http server
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
*/
public class HttpServer
{
/** the jetty http server instance */
private Server jetty;
/** jetty config file */
private String confFile;
/** a collection to hold the configured web applications */
private Set<WebApp> webApps;
/** Transport for http */
private TcpTransport httpTransport = null;
/** Transport for https */
private TcpTransport httpsTransport = null;
/** protocol identifier for http */
public static final String HTTP_TRANSPORT_ID = "http";
/** protocol identifier for https */
public static final String HTTPS_TRANSPORT_ID = "https";
/** an internal flag to check the server configuration */
private boolean configured = false;
private static final Logger LOG = LoggerFactory.getLogger( HttpServer.class );
private DirectoryService dirService;
public HttpServer()
{
}
/**
* starts the jetty http server
*
* @param dirService The DirectoryService instance
* @throws Exception If Jetty can't be started
*/
public void start( DirectoryService dirService ) throws Exception
{
this.dirService = dirService;
XmlConfiguration jettyConf = null;
if ( confFile != null )
{
InputStream input = Files.newInputStream( Paths.get( confFile ) );
jettyConf = new XmlConfiguration( input );
LOG.info( "configuring jetty http server from the configuration file {}", confFile );
try
{
jetty = new Server();
jettyConf.configure( jetty );
configured = true;
}
catch ( Exception e )
{
LOG.error( I18n.err( I18n.ERR_120 ) );
throw e;
}
}
else
{
LOG.info( "No configuration file set, looking for web apps" );
configureServerThroughCode();
}
if ( configured )
{
Handler[] handlers = jetty.getHandlers();
for ( Handler h : handlers )
{
if ( h instanceof ContextHandler )
{
ContextHandler ch = ( ContextHandler ) h;
ch.setAttribute( HttpDirectoryService.KEY, new HttpDirectoryService( dirService ) );
}
}
LOG.info( "starting jetty http server" );
jetty.start();
}
else
{
jetty = null;
LOG.warn( "Error while configuring the http server, skipping the http server startup" );
}
}
/*
* configure the jetty server programmatically without using any configuration file
*/
private void configureServerThroughCode()
{
try
{
jetty = new Server();
if ( httpTransport != null )
{
ServerConnector httpConnector = new ServerConnector( jetty );
httpConnector.setPort( httpTransport.getPort() );
httpConnector.setHost( httpTransport.getAddress() );
jetty.addConnector( httpConnector );
}
if ( httpsTransport != null )
{
// load the admin entry to get the private key and certificate
Dn adminDn = dirService.getDnFactory().create( ServerDNConstants.ADMIN_SYSTEM_DN );
Entry adminEntry = dirService.getAdminSession().lookup( adminDn, SchemaConstants.ALL_USER_ATTRIBUTES,
SchemaConstants.ALL_OPERATIONAL_ATTRIBUTES );
File confDir = dirService.getInstanceLayout().getConfDirectory();
File ksFile = new File( confDir, "httpserver.generated.ks" );
String password = UUID.randomUUID().toString();
KeyStore ks = KeyStore.getInstance( KeyStore.getDefaultType() );
ks.load( null, null );
X509CertParser parser = new X509CertParser();
parser.engineInit( new ByteArrayInputStream( adminEntry.get( TlsKeyGenerator.USER_CERTIFICATE_AT )
.getBytes() ) );
X509Certificate cert = ( X509Certificate ) parser.engineRead();
ks.setCertificateEntry( "cert", cert );
KeyPair keyPair = TlsKeyGenerator.getKeyPair( adminEntry );
ks.setKeyEntry( "privatekey", keyPair.getPrivate(), password.toCharArray(), new Certificate[]
{ cert } );
try ( OutputStream stream = Files.newOutputStream( ksFile.toPath() ) )
{
ks.store( stream, password.toCharArray() );
}
SslContextFactory sslContextFactory = new SslContextFactory();
sslContextFactory.setKeyStoreType( KeyStore.getDefaultType() );
sslContextFactory.setKeyStorePath( ksFile.getAbsolutePath() );
sslContextFactory.setKeyStorePassword( password );
sslContextFactory.setKeyManagerPassword( password );
HttpConfiguration httpsConfiguration = new HttpConfiguration();
httpsConfiguration.setSecureScheme( HTTPS_TRANSPORT_ID );
httpsConfiguration.setSecurePort( httpsTransport.getPort() );
httpsConfiguration.addCustomizer( new SecureRequestCustomizer() );
ServerConnector httpsConnector = new ServerConnector( jetty, new SslConnectionFactory( sslContextFactory, "http/1.1" ), new HttpConnectionFactory( httpsConfiguration ) );
httpsConnector.setPort( httpsTransport.getPort() );
httpsConnector.setHost( httpsTransport.getAddress() );
jetty.addConnector( httpsConnector );
}
HandlerList handlers = new HandlerList();
for ( WebApp w : webApps )
{
WebAppContext webapp = new WebAppContext();
webapp.setWar( w.getWarFile() );
webapp.setContextPath( w.getContextPath() );
handlers.addHandler( webapp );
webapp.setParentLoaderPriority( true );
}
// add web apps from the webapps directory inside directory service's working directory
// the exploded or archived wars
File webAppDir = new File( dirService.getInstanceLayout().getInstanceDirectory(), "webapps" );
FilenameFilter webAppFilter = new FilenameFilter()
{
public boolean accept( File dir, String name )
{
return name.endsWith( ".war" );
}
};
if ( webAppDir.exists() )
{
File[] appList = webAppDir.listFiles( webAppFilter );
for ( File app : appList )
{
WebAppContext webapp = new WebAppContext();
webapp.setWar( app.getAbsolutePath() );
String ctxName = app.getName();
int pos = ctxName.indexOf( '.' );
if ( pos > 0 )
{
ctxName = ctxName.substring( 0, pos );
}
webapp.setContextPath( "/" + ctxName );
handlers.addHandler( webapp );
webapp.setParentLoaderPriority( true );
}
}
jetty.setHandler( handlers );
configured = true;
}
catch ( Exception e )
{
LOG.error( I18n.err( I18n.ERR_121 ), e );
}
}
/**
* stops the jetty http server
*
* @throws Exception If Jetty can't be stopped
*/
public void stop() throws Exception
{
if ( jetty != null && jetty.isStarted() )
{
LOG.info( "stopping jetty http server" );
jetty.stop();
}
}
public void setConfFile( String confFile )
{
this.confFile = confFile;
}
public Set<WebApp> getWebApps()
{
return webApps;
}
public void setWebApps( Set<WebApp> webapps )
{
this.webApps = webapps;
}
public TcpTransport getHttpTransport()
{
return httpTransport;
}
public void setHttpTransport( TcpTransport httpTransport )
{
this.httpTransport = httpTransport;
}
public TcpTransport getHttpsTransport()
{
return httpsTransport;
}
public void setHttpsTransport( TcpTransport httpsTransport )
{
this.httpsTransport = httpsTransport;
}
}