Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable running Graylog REST API on different context path #2634

Merged
merged 17 commits into from Aug 10, 2016
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions UPGRADING.rst
Expand Up @@ -23,8 +23,8 @@ Web Interface Listener
----------------------

Graylog 2.0.x has been using separate listeners for the REST API and the web interface by default. The Graylog REST API on ``http://127.0.0.1:12900``, the Graylog web interface on ``http://127.0.0.1:9000``.
Beginning with Graylog 2.1.0 it is possible to run both the REST API and the web interface on the same host/port-combination and this is now the default. This means that the REST API is still running on ``http://127.0.0.1:12900`` per default, but the web interface is now running on ``http://127.0.0.1:12900/web``.
Furthermore, all requests going to ``http://127.0.0.1:12900/`` requesting a content-type of ``text/html`` or ``application/xhtml+xml`` are redirected to the web interface, therefore making it even easier to set up Graylog and use it behind proxies, expose it externally etc.
Beginning with Graylog 2.1.0 it is possible to run both the REST API and the web interface on the same host/port-combination and this is now the default. This means that the REST API is now running on ``http://127.0.0.1:9000/api/`` by default and the web interface is now running on ``http://127.0.0.1:9000/``.
Furthermore, all requests going to ``http://127.0.0.1:9000/api/`` requesting a content-type of ``text/html`` or ``application/xhtml+xml`` are redirected to the web interface, therefore making it even easier to set up Graylog and use it behind proxies, expose it externally etc.

Please take not that you can still run the REST API and the web interface on two separate listeners. If you are running a Graylog 2.0.x configuration specifying ``web_listen_uri`` explicitly and you want to keep that, you do not have to change anything.

Expand Down
12 changes: 5 additions & 7 deletions graylog2-server/src/main/java/org/graylog2/Configuration.java
Expand Up @@ -38,9 +38,7 @@
import java.util.Collections;
import java.util.Set;

import static org.graylog2.plugin.Tools.getUriWithDefaultPath;
import static org.graylog2.plugin.Tools.getUriWithPort;
import static org.graylog2.plugin.Tools.getUriWithScheme;
import static org.graylog2.plugin.Tools.normalizeURI;

/**
* Helper class to hold configuration of Graylog
Expand All @@ -54,10 +52,10 @@ public class Configuration extends BaseConfiguration {
private String passwordSecret;

@Parameter(value = "rest_listen_uri", required = true, validator = URIAbsoluteValidator.class)
private URI restListenUri = URI.create("http://127.0.0.1:" + GRAYLOG_DEFAULT_PORT + "/");
private URI restListenUri = URI.create("http://127.0.0.1:" + GRAYLOG_DEFAULT_PORT + "/api/");

@Parameter(value = "web_listen_uri", required = true, validator = URIAbsoluteValidator.class)
private URI webListenUri = URI.create("http://127.0.0.1:" + GRAYLOG_DEFAULT_WEB_PORT + "/web");
private URI webListenUri = URI.create("http://127.0.0.1:" + GRAYLOG_DEFAULT_WEB_PORT + "/");

@Parameter(value = "output_batch_size", required = true, validator = PositiveIntegerValidator.class)
private int outputBatchSize = 500;
Expand Down Expand Up @@ -208,12 +206,12 @@ public String getNodeIdFile() {

@Override
public URI getRestListenUri() {
return getUriWithDefaultPath(getUriWithPort(getUriWithScheme(restListenUri, getRestUriScheme()), GRAYLOG_DEFAULT_PORT), "/");
return normalizeURI(restListenUri, getRestUriScheme(), GRAYLOG_DEFAULT_PORT, "/");
}

@Override
public URI getWebListenUri() {
return getUriWithDefaultPath(getUriWithPort(getUriWithScheme(webListenUri, getWebUriScheme()), GRAYLOG_DEFAULT_WEB_PORT), "/");
return normalizeURI(webListenUri, getWebUriScheme(), GRAYLOG_DEFAULT_WEB_PORT, "/");
}

public String getRootUsername() {
Expand Down

This file was deleted.

Expand Up @@ -35,7 +35,6 @@
import org.graylog2.bindings.PeriodicalBindings;
import org.graylog2.bindings.PersistenceServicesBindings;
import org.graylog2.bindings.ServerBindings;
import org.graylog2.bindings.WebInterfaceModule;
import org.graylog2.bindings.WidgetStrategyBindings;
import org.graylog2.bootstrap.Main;
import org.graylog2.bootstrap.ServerBootstrap;
Expand Down Expand Up @@ -116,10 +115,6 @@ protected List<Module> getCommandBindings() {
new DecoratorBindings()
);

if (configuration.isWebEnable() && !configuration.isRestAndWebOnSamePort()) {
modules.add(new WebInterfaceModule());
}

return modules.build();
}

Expand Down

This file was deleted.

@@ -0,0 +1,77 @@
/**
* This file is part of Graylog.
*
* Graylog is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Graylog is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Graylog. If not, see <http://www.gnu.org/licenses/>.
*/
package org.graylog2.jersey;

import com.google.common.collect.ImmutableMap;
import org.glassfish.jersey.server.model.ModelProcessor;
import org.glassfish.jersey.server.model.Resource;
import org.glassfish.jersey.server.model.ResourceModel;

import javax.ws.rs.core.Configuration;
import javax.ws.rs.ext.Provider;
import java.util.Map;
import java.util.Optional;

@Provider
public class PrefixAddingModelProcessor implements ModelProcessor {
private final Map<String, String> packagePrefixes;

public PrefixAddingModelProcessor(Map<String, String> packagePrefixes) {
this.packagePrefixes = ImmutableMap.copyOf(packagePrefixes);
}

@Override
public ResourceModel processResourceModel(ResourceModel model, Configuration config) {
// Create new resource model.
final ResourceModel.Builder resourceModelBuilder = new ResourceModel.Builder(false);
for (final Resource resource : model.getResources()) {
for (Class handlerClass : resource.getHandlerClasses()) {
final String packageName = handlerClass.getPackage().getName();

final Optional<String> packagePrefix = packagePrefixes.entrySet().stream()
.filter(entry -> packageName.startsWith(entry.getKey()))
.sorted((o1, o2) -> -o1.getKey().compareTo(o2.getKey()))
.map(Map.Entry::getValue)
.findFirst();

if (packagePrefix.isPresent()) {
final String prefixedPath = prefixPath(packagePrefix.get(), resource.getPath());
final Resource newResource = Resource.builder(resource)
.path(prefixedPath)
.build();

resourceModelBuilder.addResource(newResource);
} else {
resourceModelBuilder.addResource(resource);
}
}
}

return resourceModelBuilder.build();
}

private String prefixPath(String prefix, String path) {
final String sanitizedPrefix = prefix.endsWith("/") ? prefix : prefix + "/";
final String sanitizedPath = path.startsWith("/") ? path.substring(1) : path;
return sanitizedPrefix + sanitizedPath;
}

@Override
public ResourceModel processSubResource(ResourceModel model, Configuration config) {
return model;
}
}
Expand Up @@ -25,7 +25,6 @@
import com.github.joschi.jadconfig.validators.StringNotBlankValidator;
import com.github.joschi.jadconfig.validators.URIAbsoluteValidator;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
import com.lmax.disruptor.BlockingWaitStrategy;
import com.lmax.disruptor.BusySpinWaitStrategy;
import com.lmax.disruptor.SleepingWaitStrategy;
Expand All @@ -36,15 +35,16 @@

import java.net.InetAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.UnknownHostException;
import java.nio.file.Files;
import java.nio.file.Path;

@SuppressWarnings("FieldMayBeFinal")
public abstract class BaseConfiguration {
private static final Logger LOG = LoggerFactory.getLogger(BaseConfiguration.class);
protected static final int GRAYLOG_DEFAULT_PORT = 12900;
protected static final int GRAYLOG_DEFAULT_WEB_PORT = 12900;
protected static final int GRAYLOG_DEFAULT_PORT = 9000;
protected static final int GRAYLOG_DEFAULT_WEB_PORT = 9000;

@Parameter(value = "shutdown_timeout", validator = PositiveIntegerValidator.class)
protected int shutdownTimeout = 30000;
Expand Down Expand Up @@ -183,7 +183,7 @@ public URI getRestTransportUri() {
LOG.warn("\"{}\" is not a valid setting for \"rest_transport_uri\". Using default [{}].", restTransportUri, getDefaultRestTransportUri());
return getDefaultRestTransportUri();
} else {
return Tools.getUriWithPort(restTransportUri, GRAYLOG_DEFAULT_PORT);
return Tools.normalizeURI(restTransportUri, getRestUriScheme(), GRAYLOG_DEFAULT_PORT, "/");
}
}

Expand All @@ -209,8 +209,19 @@ protected URI getDefaultRestTransportUri() {
throw new RuntimeException("No rest_transport_uri.", e);
}

transportUri = Tools.getUriWithPort(
URI.create("http://" + guessedAddress.getHostAddress() + ":" + listenUri.getPort()), GRAYLOG_DEFAULT_PORT);
try {
transportUri = new URI(
listenUri.getScheme(),
listenUri.getUserInfo(),
guessedAddress.getHostAddress(),
listenUri.getPort(),
listenUri.getPath(),
listenUri.getQuery(),
listenUri.getFragment()
);
} catch (URISyntaxException e) {
throw new RuntimeException("Invalid rest_transport_uri.", e);
}
} else {
transportUri = listenUri;
}
Expand Down Expand Up @@ -455,14 +466,6 @@ public void validateRestAndWebListenConfigConflict() throws ValidationException
}
}

@ValidatorMethod
@SuppressWarnings("unused")
public void validateWebHasPathPrefixIfOnSamePort() throws ValidationException {
if (isRestAndWebOnSamePort() && (Strings.isNullOrEmpty(getWebPrefix()) || getWebPrefix().equals("/"))) {
throw new ValidationException("If REST and Web Interface are served on the same host/port, the web interface must have a path prefix!");
}
}

@ValidatorMethod
@SuppressWarnings("unused")
public void validateWebAndRestHaveSameProtocolIfOnSamePort() throws ValidationException {
Expand Down