| /* |
| * 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; |
| } |
| |
| } |