Line data Source code
1 : // Copyright (C) 2015 The Android Open Source Project 2 : // 3 : // Licensed under the Apache License, Version 2.0 (the "License"); 4 : // you may not use this file except in compliance with the License. 5 : // You may obtain a copy of the License at 6 : // 7 : // http://www.apache.org/licenses/LICENSE-2.0 8 : // 9 : // Unless required by applicable law or agreed to in writing, software 10 : // distributed under the License is distributed on an "AS IS" BASIS, 11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 : // See the License for the specific language governing permissions and 13 : // limitations under the License. 14 : 15 : package com.google.gerrit.httpd.plugins; 16 : 17 : import static com.google.gerrit.extensions.api.lfs.LfsDefinitions.CONTENTTYPE_VND_GIT_LFS_JSON; 18 : import static java.nio.charset.StandardCharsets.UTF_8; 19 : import static javax.servlet.http.HttpServletResponse.SC_NOT_IMPLEMENTED; 20 : 21 : import com.google.common.flogger.FluentLogger; 22 : import com.google.gerrit.common.Nullable; 23 : import com.google.gerrit.httpd.resources.Resource; 24 : import com.google.gerrit.server.config.GerritServerConfig; 25 : import com.google.gerrit.server.plugins.Plugin; 26 : import com.google.gerrit.server.plugins.ReloadPluginListener; 27 : import com.google.gerrit.server.plugins.StartPluginListener; 28 : import com.google.gerrit.util.http.CacheHeaders; 29 : import com.google.inject.Inject; 30 : import com.google.inject.Singleton; 31 : import com.google.inject.servlet.GuiceFilter; 32 : import java.io.BufferedWriter; 33 : import java.io.IOException; 34 : import java.io.OutputStreamWriter; 35 : import java.io.Writer; 36 : import java.util.ArrayList; 37 : import java.util.List; 38 : import java.util.concurrent.atomic.AtomicReference; 39 : import javax.servlet.FilterChain; 40 : import javax.servlet.ServletConfig; 41 : import javax.servlet.ServletContext; 42 : import javax.servlet.ServletException; 43 : import javax.servlet.http.HttpServlet; 44 : import javax.servlet.http.HttpServletRequest; 45 : import javax.servlet.http.HttpServletResponse; 46 : import org.eclipse.jgit.lib.Config; 47 : 48 : @Singleton 49 : public class LfsPluginServlet extends HttpServlet 50 : implements StartPluginListener, ReloadPluginListener { 51 : private static final long serialVersionUID = 1L; 52 : 53 99 : private static final FluentLogger logger = FluentLogger.forEnclosingClass(); 54 : 55 : private static final String MESSAGE_LFS_NOT_CONFIGURED = 56 : "{\"message\":\"No LFS plugin is configured to handle LFS requests.\"}"; 57 : 58 99 : private List<Plugin> pending = new ArrayList<>(); 59 : private final String pluginName; 60 : private final FilterChain chain; 61 : private AtomicReference<GuiceFilter> filter; 62 : 63 : @Inject 64 99 : LfsPluginServlet(@GerritServerConfig Config cfg) { 65 99 : this.pluginName = cfg.getString("lfs", null, "plugin"); 66 99 : this.chain = 67 0 : (req, res) -> Resource.NOT_FOUND.send((HttpServletRequest) req, (HttpServletResponse) res); 68 99 : this.filter = new AtomicReference<>(); 69 99 : } 70 : 71 : @Override 72 : protected void service(HttpServletRequest req, HttpServletResponse res) 73 : throws ServletException, IOException { 74 0 : if (filter.get() == null) { 75 0 : responseLfsNotConfigured(res); 76 0 : return; 77 : } 78 0 : filter.get().doFilter(req, res, chain); 79 0 : } 80 : 81 : @Override 82 : public synchronized void init(ServletConfig config) throws ServletException { 83 99 : super.init(config); 84 : 85 99 : for (Plugin plugin : pending) { 86 0 : install(plugin); 87 0 : } 88 99 : pending = null; 89 99 : } 90 : 91 : @Override 92 : public synchronized void onStartPlugin(Plugin plugin) { 93 9 : if (pending != null) { 94 0 : pending.add(plugin); 95 : } else { 96 9 : install(plugin); 97 : } 98 9 : } 99 : 100 : @Override 101 : public void onReloadPlugin(Plugin oldPlugin, Plugin newPlugin) { 102 1 : install(newPlugin); 103 1 : } 104 : 105 : private void responseLfsNotConfigured(HttpServletResponse res) throws IOException { 106 0 : CacheHeaders.setNotCacheable(res); 107 0 : res.setContentType(CONTENTTYPE_VND_GIT_LFS_JSON); 108 0 : res.setStatus(SC_NOT_IMPLEMENTED); 109 0 : Writer w = new BufferedWriter(new OutputStreamWriter(res.getOutputStream(), UTF_8)); 110 0 : w.write(MESSAGE_LFS_NOT_CONFIGURED); 111 0 : w.flush(); 112 0 : } 113 : 114 : private void install(Plugin plugin) { 115 9 : if (!plugin.getName().equals(pluginName)) { 116 9 : return; 117 : } 118 0 : final GuiceFilter guiceFilter = load(plugin); 119 0 : plugin.add(() -> filter.compareAndSet(guiceFilter, null)); 120 0 : filter.set(guiceFilter); 121 0 : } 122 : 123 : @Nullable 124 : private GuiceFilter load(Plugin plugin) { 125 0 : if (plugin.getHttpInjector() != null) { 126 0 : final String name = plugin.getName(); 127 : final GuiceFilter guiceFilter; 128 : try { 129 0 : guiceFilter = plugin.getHttpInjector().getInstance(GuiceFilter.class); 130 0 : } catch (RuntimeException e) { 131 0 : logger.atWarning().withCause(e).log("Plugin %s cannot load GuiceFilter", name); 132 0 : return null; 133 0 : } 134 : 135 : try { 136 0 : ServletContext ctx = PluginServletContext.create(plugin, "/"); 137 0 : guiceFilter.init(new WrappedFilterConfig(ctx)); 138 0 : } catch (ServletException e) { 139 0 : logger.atWarning().withCause(e).log("Plugin %s failed to initialize HTTP", name); 140 0 : return null; 141 0 : } 142 : 143 0 : plugin.add(guiceFilter::destroy); 144 0 : return guiceFilter; 145 : } 146 0 : return null; 147 : } 148 : }