Skip to content

Commit

Permalink
Allow disabling HTTP in Neo4j harness
Browse files Browse the repository at this point in the history
Before this change, it wasn't possible to start a database using `Neo4jRule`
with HTTP turned off. Every started database needed to have HTTP enabled and
started an embedded Jetty on the default/configured HTTP port.

This commit allows disabling the HTTP connector via:

```
new Neo4jRule().withConfig( "dbms.connector.http.enabled", "false" );
```

It is also possible to disable both HTTP and HTTPS making the database only
accessible via Bolt:

```
new Neo4jRule().withConfig( "dbms.connector.http.enabled", "false" )
               .withConfig( "dbms.connector.https.enabled", "false" );
```

Such configuration will make Neo4j not start an embedded Jetty server. Neo4j
browser and REST endpoints will thus not be accessible.

HTTP connector was mandatory because server builder was only able to
instantiate Neo4j server, which created the database. So lifecycle of the
core database was managed entirely by the server. This commit makes the
in-process server builder able to create core database separately from a
server component. Based on connector configuration it can then decide if
server component is needed or not. Dummy `DisabledNeoServer` is instantiated
when HTTP and HTTPS are turned off.
  • Loading branch information
lutovich committed Sep 18, 2018
1 parent 34dd061 commit adc01aa
Show file tree
Hide file tree
Showing 8 changed files with 222 additions and 77 deletions.
Expand Up @@ -35,13 +35,13 @@
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.config.Setting;
import org.neo4j.graphdb.facade.GraphDatabaseDependencies;
import org.neo4j.graphdb.facade.GraphDatabaseFacadeFactory;
import org.neo4j.graphdb.factory.GraphDatabaseSettings;
import org.neo4j.harness.ServerControls;
import org.neo4j.harness.TestServerBuilder;
import org.neo4j.io.fs.DefaultFileSystemAbstraction;
import org.neo4j.io.fs.FileSystemAbstraction;
import org.neo4j.kernel.configuration.BoltConnector;
import org.neo4j.kernel.configuration.Config;
import org.neo4j.kernel.configuration.HttpConnector;
import org.neo4j.kernel.configuration.HttpConnector.Encryption;
import org.neo4j.kernel.configuration.Settings;
Expand All @@ -52,11 +52,16 @@
import org.neo4j.kernel.lifecycle.Lifecycle;
import org.neo4j.kernel.lifecycle.LifecycleAdapter;
import org.neo4j.logging.FormattedLogProvider;
import org.neo4j.logging.LogProvider;
import org.neo4j.logging.LogTimeZone;
import org.neo4j.server.AbstractNeoServer;
import org.neo4j.server.DisabledNeoServer;
import org.neo4j.server.NeoServer;
import org.neo4j.server.configuration.ServerSettings;
import org.neo4j.server.configuration.ThirdPartyJaxRsPackage;
import org.neo4j.server.database.GraphFactory;

import static org.neo4j.graphdb.facade.GraphDatabaseFacadeFactory.Dependencies;
import static org.neo4j.graphdb.factory.GraphDatabaseSettings.auth_enabled;
import static org.neo4j.graphdb.factory.GraphDatabaseSettings.data_directory;
import static org.neo4j.graphdb.factory.GraphDatabaseSettings.db_timezone;
Expand Down Expand Up @@ -148,14 +153,21 @@ public ServerControls newServer()
config.put( ServerSettings.third_party_packages.name(), toStringForThirdPartyPackageProperty( extensions.toList() ) );
config.put( GraphDatabaseSettings.store_internal_log_path.name(), internalLogFile.getAbsolutePath() );

FormattedLogProvider userLogProvider = FormattedLogProvider.withZoneId( logZoneIdFrom( config ) ).toOutputStream( logOutputStream );
LogProvider userLogProvider = FormattedLogProvider.withZoneId( logZoneIdFrom( config ) ).toOutputStream( logOutputStream );
GraphDatabaseDependencies dependencies = GraphDatabaseDependencies.newDependencies();
Iterable<KernelExtensionFactory<?>> kernelExtensions =
append( new Neo4jHarnessExtensions( procedures ), dependencies.kernelExtensions() );
dependencies = dependencies.kernelExtensions( kernelExtensions ).userLogProvider( userLogProvider );

AbstractNeoServer neoServer = createNeoServer( config, dependencies, userLogProvider );
InProcessServerControls controls = new InProcessServerControls( serverFolder, userLogFile, internalLogFile, neoServer, logOutputStream );
Config dbConfig = Config.defaults( config );
GraphFactory graphFactory = createGraphFactory( dbConfig );
boolean httpAndHttpsDisabled = dbConfig.enabledHttpConnectors().isEmpty();

NeoServer server = httpAndHttpsDisabled
? new DisabledNeoServer( graphFactory, dependencies, dbConfig, userLogProvider )
: createNeoServer( graphFactory, dbConfig, dependencies, userLogProvider );

InProcessServerControls controls = new InProcessServerControls( serverFolder, userLogFile, internalLogFile, server, logOutputStream );
controls.start();

try
Expand All @@ -175,8 +187,9 @@ public ServerControls newServer()
}
}

protected abstract AbstractNeoServer createNeoServer( Map<String,String> config,
GraphDatabaseFacadeFactory.Dependencies dependencies, FormattedLogProvider userLogProvider );
protected abstract GraphFactory createGraphFactory( Config config );

protected abstract AbstractNeoServer createNeoServer( GraphFactory graphFactory, Config config, Dependencies dependencies, LogProvider userLogProvider );

@Override
public TestServerBuilder withConfig( Setting<?> key, String value )
Expand Down
Expand Up @@ -20,13 +20,14 @@
package org.neo4j.harness.internal;

import java.io.File;
import java.util.Map;

import org.neo4j.graphdb.facade.GraphDatabaseFacadeFactory;
import org.neo4j.graphdb.facade.GraphDatabaseFacadeFactory.Dependencies;
import org.neo4j.kernel.configuration.Config;
import org.neo4j.logging.FormattedLogProvider;
import org.neo4j.logging.LogProvider;
import org.neo4j.server.AbstractNeoServer;
import org.neo4j.server.CommunityNeoServer;
import org.neo4j.server.database.CommunityGraphFactory;
import org.neo4j.server.database.GraphFactory;

public class InProcessServerBuilder extends AbstractInProcessServerBuilder
{
Expand All @@ -41,9 +42,14 @@ public InProcessServerBuilder( File workingDir )
}

@Override
protected AbstractNeoServer createNeoServer( Map<String,String> config,
GraphDatabaseFacadeFactory.Dependencies dependencies, FormattedLogProvider userLogProvider )
protected GraphFactory createGraphFactory( Config config )
{
return new CommunityNeoServer( Config.defaults( config ), dependencies, userLogProvider );
return new CommunityGraphFactory();
}

@Override
protected AbstractNeoServer createNeoServer( GraphFactory graphFactory, Config config, Dependencies dependencies, LogProvider userLogProvider )
{
return new CommunityNeoServer( config, graphFactory, dependencies, userLogProvider );
}
}
Expand Up @@ -33,19 +33,23 @@
import org.neo4j.harness.ServerControls;
import org.neo4j.helpers.HostnamePort;
import org.neo4j.io.fs.FileUtils;
import org.neo4j.kernel.configuration.Connector;
import org.neo4j.kernel.configuration.ConnectorPortRegister;
import org.neo4j.server.AbstractNeoServer;
import org.neo4j.kernel.internal.GraphDatabaseAPI;
import org.neo4j.server.NeoServer;

import static org.neo4j.kernel.configuration.HttpConnector.Encryption;

public class InProcessServerControls implements ServerControls
{
private final File serverFolder;
private final File userLogFile;
private final File internalLogFile;
private final AbstractNeoServer server;
private final NeoServer server;
private final Closeable additionalClosable;
private ConnectorPortRegister connectorPortRegister;

public InProcessServerControls( File serverFolder, File userLogFile, File internalLogFile, AbstractNeoServer server, Closeable additionalClosable )
public InProcessServerControls( File serverFolder, File userLogFile, File internalLogFile, NeoServer server, Closeable additionalClosable )
{
this.serverFolder = serverFolder;
this.userLogFile = userLogFile;
Expand All @@ -57,26 +61,31 @@ public InProcessServerControls( File serverFolder, File userLogFile, File intern
@Override
public URI boltURI()
{
HostnamePort boltHostNamePort = connectorPortRegister.getLocalAddress( "bolt" );
return URI.create( "bolt://" + boltHostNamePort.getHost() + ":" + boltHostNamePort.getPort() );
return server.getConfig()
.enabledBoltConnectors()
.stream()
.findFirst()
.map( connector -> connectorUri( "bolt", connector ) )
.orElseThrow( () -> new IllegalStateException( "Bolt connector is not configured" ) );
}

@Override
public URI httpURI()
{
return server.baseUri();
return httpConnectorUri( "http", Encryption.NONE )
.orElseThrow( () -> new IllegalStateException( "HTTP connector is not configured" ) );
}

@Override
public Optional<URI> httpsURI()
{
return server.httpsUri();
return httpConnectorUri( "https", Encryption.TLS );
}

public void start()
{
this.server.start();
this.connectorPortRegister = server.getDependencyResolver().resolveDependency( ConnectorPortRegister.class );
this.connectorPortRegister = connectorPortRegister( server );
}

@Override
Expand Down Expand Up @@ -152,4 +161,25 @@ public Configuration config()
{
return server.getConfig();
}

private Optional<URI> httpConnectorUri( String scheme, Encryption encryption )
{
return server.getConfig()
.enabledHttpConnectors()
.stream()
.filter( connector -> connector.encryptionLevel() == encryption )
.findFirst()
.map( connector -> connectorUri( scheme, connector ) );
}

private URI connectorUri( String scheme, Connector connector )
{
HostnamePort boltHostNamePort = connectorPortRegister.getLocalAddress( connector.key() );
return URI.create( scheme + "://" + boltHostNamePort + "/" );
}

private static ConnectorPortRegister connectorPortRegister( NeoServer server )
{
return ((GraphDatabaseAPI) server.getDatabase().getGraph()).getDependencyResolver().resolveDependency( ConnectorPortRegister.class );
}
}
Expand Up @@ -40,6 +40,7 @@

import org.neo4j.bolt.v1.transport.socket.client.SocketConnection;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Label;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.ResourceIterable;
import org.neo4j.graphdb.Transaction;
Expand All @@ -66,8 +67,10 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.neo4j.harness.TestServerBuilders.newInProcessBuilder;
import static org.neo4j.helpers.collection.Iterators.single;
import static org.neo4j.server.ServerTestUtils.verifyConnector;

public class InProcessBuilderTestIT
public class InProcessServerBuilderIT
{
@Rule
public TestDirectory testDir = TestDirectory.testDirectory();
Expand Down Expand Up @@ -267,6 +270,92 @@ public void shouldFailWhenProvidingANonDirectoryAsSource() throws IOException

}

@Test
public void shouldStartServerWithHttpHttpsAndBoltDisabled()
{
testStartupWithConnectors( false, false, false );
}

@Test
public void shouldStartServerWithHttpEnabledAndHttpsBoltDisabled()
{
testStartupWithConnectors( true, false, false );
}

@Test
public void shouldStartServerWithHttpsEnabledAndHttpBoltDisabled()
{
testStartupWithConnectors( false, true, false );
}

@Test
public void shouldStartServerWithBoltEnabledAndHttpHttpsDisabled()
{
testStartupWithConnectors( false, false, true );
}

@Test
public void shouldStartServerWithHttpHttpsEnabledAndBoltDisabled()
{
testStartupWithConnectors( true, true, false );
}

@Test
public void shouldStartServerWithHttpBoltEnabledAndHttpsDisabled()
{
testStartupWithConnectors( true, false, true );
}

@Test
public void shouldStartServerWithHttpsBoltEnabledAndHttpDisabled()
{
testStartupWithConnectors( false, true, true );
}

private void testStartupWithConnectors( boolean httpEnabled, boolean httpsEnabled, boolean boltEnabled )
{
int httpPort = httpEnabled ? 0 : 7474;
int httpsPort = httpsEnabled ? 0 : 7473;
int boltPort = boltEnabled ? 0 : 7687;

TestServerBuilder serverBuilder = newInProcessBuilder( testDir.directory() )
.withConfig( "dbms.connector.http.enabled", Boolean.toString( httpEnabled ) )
.withConfig( "dbms.connector.http.listen_address", ":" + httpPort )
.withConfig( "dbms.connector.https.enabled", Boolean.toString( httpsEnabled ) )
.withConfig( "dbms.connector.https.listen_address", ":" + httpsPort )
.withConfig( "dbms.connector.bolt.enabled", Boolean.toString( boltEnabled ) )
.withConfig( "dbms.connector.bolt.listen_address", ":" + boltPort );

try ( ServerControls server = serverBuilder.newServer() )
{
GraphDatabaseService db = server.graph();

assertDbAccessible( db );
verifyConnector( db, "http", 7474, httpEnabled );
verifyConnector( db, "https", 7473, httpsEnabled );
verifyConnector( db, "bolt", 7687, boltEnabled );
}
}

private static void assertDbAccessible( GraphDatabaseService db )
{
Label label = () -> "Person";
String propertyKey = "name";
String propertyValue = "Thor Odinson";

try ( Transaction tx = db.beginTx() )
{
db.createNode( label ).setProperty( propertyKey, propertyValue );
tx.success();
}
try ( Transaction tx = db.beginTx() )
{
Node node = single( db.findNodes( label ) );
assertEquals( propertyValue, node.getProperty( propertyKey ) );
tx.success();
}
}

private void assertDBConfig( ServerControls server, String expected, String key ) throws JsonParseException
{
JsonNode beans = HTTP.GET(
Expand Down
Expand Up @@ -38,7 +38,7 @@
import static org.neo4j.server.AbstractNeoServer.NEO4J_IS_STARTING_MESSAGE;
import static org.neo4j.server.exception.ServerStartupErrors.translateToServerStartupError;

class DisabledNeoServer implements NeoServer
public class DisabledNeoServer implements NeoServer
{
private final GraphFactory graphFactory;
private final Dependencies dependencies;
Expand All @@ -47,7 +47,7 @@ class DisabledNeoServer implements NeoServer
private Database db;
private final LifeSupport life = new LifeSupport();

DisabledNeoServer( GraphFactory graphFactory, Dependencies dependencies, Config config, LogProvider logProvider )
public DisabledNeoServer( GraphFactory graphFactory, Dependencies dependencies, Config config, LogProvider logProvider )
{
this.graphFactory = graphFactory;
this.dependencies = dependencies;
Expand Down

0 comments on commit adc01aa

Please sign in to comment.