diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt index 50b9b3e228..1e1b8526fc 100644 --- a/Documentation/config-gerrit.txt +++ b/Documentation/config-gerrit.txt @@ -2666,6 +2666,28 @@ org.anyorg.MySecureIPFilter that performs source IP security filtering: filterClass = org.anyorg.MySecureIPFilter ---- +[[filterClass.className.initParam]]filterClass..initParam:: ++ +Gerrit supports customized pluggable HTTP filters as `filterClass`. This +option allows to pass extra initialization parameters to the filter. It +allows for multiple key/value pairs to be passed in this pattern: ++ +---- +initParam = = +---- +For a comprehensive example: ++ +---- +[httpd] + filterClass = org.anyorg.AFilter + filterClass = org.anyorg.BFilter +[filterClass "org.anyorg.AFilter"] + key1 = value1 + key2 = value2 +[filterClass "org.anyorg.BFilter"] + key3 = value3 +---- + [[httpd.idleTimeout]]httpd.idleTimeout:: + Maximum idle time for a connection, which roughly translates to the diff --git a/java/com/google/gerrit/pgm/http/jetty/JettyServer.java b/java/com/google/gerrit/pgm/http/jetty/JettyServer.java index 22bc21d6ff..096e4a18c0 100644 --- a/java/com/google/gerrit/pgm/http/jetty/JettyServer.java +++ b/java/com/google/gerrit/pgm/http/jetty/JettyServer.java @@ -36,8 +36,10 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.EnumSet; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; import javax.servlet.DispatcherType; @@ -411,10 +413,20 @@ public class JettyServer { Class filterClass = (Class) Class.forName(filterClassName); Filter filter = env.webInjector.getInstance(filterClass); - app.addFilter( - new FilterHolder(filter), - "/*", - EnumSet.of(DispatcherType.REQUEST, DispatcherType.ASYNC)); + + Map initParams = new HashMap<>(); + Set initParamKeys = cfg.getNames("filterClass", filterClassName, true); + initParamKeys.forEach( + paramKey -> { + String paramValue = cfg.getString("filterClass", filterClassName, paramKey); + initParams.put(paramKey, paramValue); + }); + + FilterHolder filterHolder = new FilterHolder(filter); + if (initParams.size() > 0) { + filterHolder.setInitParameters(initParams); + } + app.addFilter(filterHolder, "/*", EnumSet.of(DispatcherType.REQUEST, DispatcherType.ASYNC)); } catch (Throwable e) { throw new IllegalArgumentException( "Unable to instantiate front-end HTTP Filter " + filterClassName, e); diff --git a/javatests/com/google/gerrit/acceptance/filter/BUILD b/javatests/com/google/gerrit/acceptance/filter/BUILD new file mode 100644 index 0000000000..22aead3b80 --- /dev/null +++ b/javatests/com/google/gerrit/acceptance/filter/BUILD @@ -0,0 +1,22 @@ +load("//javatests/com/google/gerrit/acceptance:tests.bzl", "acceptance_tests") + +acceptance_tests( + srcs = glob([ + "*IT.java", + ]), + group = "filter", + labels = ["filter"], + deps = [ + ":util", + ], +) + +java_library( + name = "util", + testonly = True, + srcs = [ + "FakeMustInitParamsFilter.java", + "FakeNoInitParamsFilter.java", + ], + deps = ["//java/com/google/gerrit/acceptance:lib"], +) diff --git a/javatests/com/google/gerrit/acceptance/filter/FakeMustInitParamsFilter.java b/javatests/com/google/gerrit/acceptance/filter/FakeMustInitParamsFilter.java new file mode 100644 index 0000000000..89d268ef81 --- /dev/null +++ b/javatests/com/google/gerrit/acceptance/filter/FakeMustInitParamsFilter.java @@ -0,0 +1,56 @@ +// Copyright (C) 2019 The Android Open Source Project +// +// 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 com.google.gerrit.acceptance.filter; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; + +public class FakeMustInitParamsFilter implements Filter { + + // `PARAM_X` and `PARAM_Y` are init param keys + private static final String INIT_PARAM_1 = "PARAM-1"; + private static final String INIT_PARAM_2 = "PARAM-2"; + // the map is used for testing + private static final Map initParams = new HashMap<>(); + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + initParams.put(INIT_PARAM_1, filterConfig.getInitParameter(INIT_PARAM_1)); + initParams.put(INIT_PARAM_2, filterConfig.getInitParameter(INIT_PARAM_2)); + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + chain.doFilter(request, response); + } + + @Override + public void destroy() { + // do nothing. + } + + // the function is used for testing + Map getInitParams() { + return initParams; + } +} diff --git a/javatests/com/google/gerrit/acceptance/filter/FakeNoInitParamsFilter.java b/javatests/com/google/gerrit/acceptance/filter/FakeNoInitParamsFilter.java new file mode 100644 index 0000000000..6fd6366802 --- /dev/null +++ b/javatests/com/google/gerrit/acceptance/filter/FakeNoInitParamsFilter.java @@ -0,0 +1,42 @@ +// Copyright (C) 2019 The Android Open Source Project +// +// 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 com.google.gerrit.acceptance.filter; + +import java.io.IOException; +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; + +public class FakeNoInitParamsFilter implements Filter { + @Override + public void init(FilterConfig filterConfig) throws ServletException { + // no init params in this filter. + // do nothing. + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + chain.doFilter(request, response); + } + + @Override + public void destroy() { + // do nothing. + } +} diff --git a/javatests/com/google/gerrit/acceptance/filter/FilterClassIT.java b/javatests/com/google/gerrit/acceptance/filter/FilterClassIT.java new file mode 100644 index 0000000000..a23c5ce10e --- /dev/null +++ b/javatests/com/google/gerrit/acceptance/filter/FilterClassIT.java @@ -0,0 +1,57 @@ +// Copyright (C) 2019 The Android Open Source Project +// +// 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 com.google.gerrit.acceptance.filter; + +import com.google.gerrit.acceptance.AbstractDaemonTest; +import com.google.gerrit.testing.ConfigSuite; +import org.eclipse.jgit.errors.ConfigInvalidException; +import org.eclipse.jgit.lib.Config; +import org.junit.Assert; +import org.junit.Test; + +public class FilterClassIT extends AbstractDaemonTest { + @ConfigSuite.Default + public static Config enableFilter() throws ConfigInvalidException { + Config cfg = new Config(); + cfg.fromText( + "" + + "[httpd]\n" + + " filterClass = com.google.gerrit.acceptance.filter.FakeNoInitParamsFilter\n" + + " filterClass = com.google.gerrit.acceptance.filter.FakeMustInitParamsFilter\n" + + "[filterClass \"com.google.gerrit.acceptance.filter.FakeMustInitParamsFilter\"]\n" + + " PARAM-1 = hello\n" + + " PARAM-2 = world\n"); + return cfg; + } + + @Test + public void filterLoad() { + FakeNoInitParamsFilter fakeNoInitParamsFilter = + server.getTestInjector().getBinding(FakeNoInitParamsFilter.class).getProvider().get(); + Assert.assertNotNull(fakeNoInitParamsFilter); + FakeMustInitParamsFilter fakeMustInitParamsFilter = + server.getTestInjector().getBinding(FakeMustInitParamsFilter.class).getProvider().get(); + Assert.assertNotNull(fakeMustInitParamsFilter); + } + + @Test + public void filterInitParams() { + FakeMustInitParamsFilter fakeMustInitParamsFilter = + server.getTestInjector().getBinding(FakeMustInitParamsFilter.class).getProvider().get(); + Assert.assertEquals(2, fakeMustInitParamsFilter.getInitParams().size()); + Assert.assertEquals("hello", fakeMustInitParamsFilter.getInitParams().get("PARAM-1")); + Assert.assertEquals("world", fakeMustInitParamsFilter.getInitParams().get("PARAM-2")); + } +}