package org.dcache.services.httpd.handlers; import com.google.common.base.Splitter; import com.google.common.base.Throwables; import com.google.common.collect.Iterables; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.handler.AbstractHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.util.Collections; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import dmg.util.HttpBasicAuthenticationException; import dmg.util.HttpException; import org.dcache.services.httpd.exceptions.OnErrorException; import org.dcache.services.httpd.util.AliasEntry; /** * Responsible for parsing the request to find the correct alias type and * passing the context to the alias handler. * * @author arossi */ public class HandlerDelegator extends AbstractHandler { private static final Logger logger = LoggerFactory.getLogger(HandlerDelegator.class); private static final Splitter PATH_SPLITTER = Splitter.on('/').omitEmptyStrings(); private static String extractAlias(String requestURI) { return Iterables.getFirst(PATH_SPLITTER.split(requestURI), "<home>"); } private static void handleException(Exception e, String uri, HttpServletResponse response) { if (e instanceof ServletException) { final Throwable cause = e.getCause(); if (cause instanceof HttpException) { printHttpException((HttpException) cause, response); } logger.warn("Problem with {}: {}", uri, e.getMessage()); } else if (e instanceof HttpException) { printHttpException((HttpException) e, response); logger.warn("Problem with {}: {}", uri, e.getMessage()); } else if (e instanceof RuntimeException) { printHttpException(new HttpException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Internal problem processing request"), response); logger.warn("Bug found, please report it", e); } else { printHttpException(new HttpException(HttpServletResponse.SC_BAD_REQUEST, "Bad Request : " + e), response); logger.warn("Problem in HttpServiceCellHandler: {}", e.getMessage()); } } private static void printHttpException(HttpException exception, HttpServletResponse response) { try { if (exception instanceof HttpBasicAuthenticationException) { final String realm = ((HttpBasicAuthenticationException) exception).getRealm(); response.setHeader("WWW-Authenticate", "Basic realm=\"" + realm + "\""); } response.sendError(exception.getErrorCode(), exception.getMessage()); } catch (final IOException e) { logger.warn("Failed to send reply: {}", e.getMessage()); } } private final Map<String, AliasEntry> aliases = new ConcurrentHashMap<>(); @Override public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException { String uri = null; String alias; AliasEntry entry = null; try { uri = request.getRequestURI(); alias = extractAlias(uri); logger.debug("handle {}, {}", uri, alias); entry = aliases.get(alias); if (entry == null) { entry = aliases.get("<default>"); } if (entry == null) { throw new HttpException(HttpServletResponse.SC_NOT_FOUND, "Alias not found : " + alias); } logger.debug("alias: {}, entry {}", alias, entry); /* * The exclusion of POST used to be absolute; we allow it now only * on webapps. */ if (!request.getMethod().equals("GET") && entry.getType() != AliasEntry.AliasType.WEBAPP) { throw new HttpException(HttpServletResponse.SC_NOT_IMPLEMENTED, "Method not implemented: " + request.getMethod()); } /* * check for overwritten alias */ final String alternate = entry.getOverwrite(); if (alternate != null) { logger.debug("handle, overwritten alias: {}", alternate); final AliasEntry overwrittenEntry = aliases.get(alternate); if (overwrittenEntry != null) { entry = overwrittenEntry; } logger.debug("handle, alias {}, entry {}", alternate, entry); } final Handler handler = entry.getHandler(); logger.debug("got handler: {}", handler); if (handler != null) { handler.handle(target, baseRequest, request, response); } } catch (final Exception e) { if (entry != null && e.getCause() instanceof OnErrorException) { final String alternate = entry.getOnError(); if (alternate != null) { logger.debug("handle, onError alias: {}", alternate); final AliasEntry overwrittenEntry = aliases.get(alternate); if (overwrittenEntry != null) { entry = overwrittenEntry; logger.debug("handle, alias {}, entry {}", alternate, entry); final Handler handler = entry.getHandler(); if (handler != null) { try { handler.handle(target, baseRequest, request, response); } catch (final ServletException t) { handleException(t, uri, response); } } } else { handleException(new HttpException(HttpServletResponse.SC_NOT_FOUND, "Not found : " + entry.getSpecificString()), uri, response); } } } else { handleException(e, uri, response); } } logger.info("Finished"); } public AliasEntry removeAlias(String name) throws InvocationTargetException { AliasEntry entry = aliases.remove(name); if (entry != null) { try { Handler handler = entry.getHandler(); removeBean(handler); if (handler.isStarted()) { handler.stop(); } } catch (Exception e) { Throwables.throwIfUnchecked(e); throw new InvocationTargetException(e, "Handler failed to stop."); } } return entry; } @Override protected void doStart() throws Exception { for (AliasEntry entry : aliases.values()) { entry.getHandler().setServer(getServer()); } super.doStart(); } public void addAlias(String name, AliasEntry entry) throws InvocationTargetException { Handler handler = entry.getHandler(); addBean(handler, true); if (isStarted() && !handler.isStarted()) { try { handler.setServer(getServer()); handler.start(); } catch (Exception e) { Throwables.throwIfUnchecked(e); throw new InvocationTargetException(e, "Handler failed to start."); } } aliases.put(name, entry); } public Map<String, AliasEntry> getAliases() { return Collections.unmodifiableMap(aliases); } public AliasEntry getAlias(String name) { return aliases.get(name); } }