/* * #%L * Wisdom-Framework * %% * Copyright (C) 2013 - 2014 Wisdom Framework * %% * 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. * #L% */ package org.wisdom.framework.filters; import com.google.common.base.Splitter; import com.google.common.base.Strings; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; import org.apache.http.Header; import org.apache.http.HttpEntity; import org.apache.http.HttpHost; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.cookie.Cookie; import org.apache.http.entity.ByteArrayEntity; import org.apache.http.entity.ContentType; import org.apache.http.impl.client.BasicCookieStore; import org.apache.http.impl.client.DefaultRedirectStrategy; import org.apache.http.impl.client.HttpClients; import org.apache.http.message.BasicHttpEntityEnclosingRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.wisdom.api.bodies.RenderableStream; import org.wisdom.api.configuration.Configuration; import org.wisdom.api.http.*; import org.wisdom.api.interception.Filter; import org.wisdom.api.interception.RequestContext; import org.wisdom.api.router.Route; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.util.*; import java.util.concurrent.Callable; import java.util.regex.Pattern; /** * A filter implementation to extend to create return a {@code REDIRECT} result to a specific location. */ public class RedirectFilter implements Filter { private final Configuration configuration; protected Logger logger; private String redirectTo; private String prefix; /** * Default constructor, not configuration. */ public RedirectFilter() { this(null); } /** * Constructor receiving a configuration. * * @param conf the configuration */ public RedirectFilter(Configuration conf) { configuration = conf; logger = createLogger(); redirectTo = getRedirectTo(); prefix = getPrefix(); if (redirectTo == null) { throw new IllegalStateException("The 'redirectTo' parameter is required"); } if (prefix == null) { prefix = ""; } } /** * Creates the logger instance used by this filter. It can be overridden to customize the logger instance. * * @return the logger */ protected Logger createLogger() { return LoggerFactory.getLogger(RedirectFilter.class.getName() + "-" + uri().toString()); } /** * The interception method. It just returns a {@code REDIRECT} result to the computed URI. * * @param route the route * @param context the filter context * @return the result * @throws Exception if anything bad happen */ @Override public Result call(final Route route, final RequestContext context) throws Exception { URI redirectedURI = rewriteURI(context.request()); logger.debug("Redirecting request - rewriting {} to {}", context.request().uri(), redirectedURI); if (redirectedURI == null) { return onRewriteFailed(context); } return Results.redirect(redirectedURI.toString()); } /** * Callback invokes when the URL rewrite fails. By default, it returns an internal error. * * @param context the request context * @return the result in case of rewrite failure, an internal error by default. */ protected Result onRewriteFailed(RequestContext context) { return Results.internalServerError("Cannot redirect request - failed to compute destination"); } /** * Computes the URI where the request need to be transferred. * * @param request the request * @return the URI * @throws java.net.URISyntaxException if the URI cannot be computed */ public URI rewriteURI(Request request) throws URISyntaxException { String path = request.path(); if (!path.startsWith(prefix)) { return null; } StringBuilder uri = new StringBuilder(redirectTo); if (redirectTo.endsWith("/")) { uri.setLength(uri.length() - 1); } String rest = path.substring(prefix.length()); if (!rest.startsWith("/") && !rest.isEmpty()) { uri.append("/"); } uri.append(rest); // Do we have a query String int index = request.uri().indexOf("?"); String query = null; if (index != -1) { // Remove the ? query = request.uri().substring(index + 1); } if (!Strings.isNullOrEmpty(query)) { uri.append("?").append(query); } return URI.create(uri.toString()).normalize(); } /** * Gets the Regex Pattern used to determine whether the route is handled by the filter or not. * Notice that the router are caching these patterns and so cannot be changed. */ @Override public Pattern uri() { return Pattern.compile(getPrefix() + ".*"); } /** * Gets the filter priority, determining the position of the filter in the filter chain. Filter with a high * priority are called first. Notice that the router are caching these priorities and so cannot changed. * <p> * It is heavily recommended to allow configuring the priority from the Application Configuration. * * @return the priority */ @Override public int priority() { return 1000; } /** * Gets the destination of the redirection. By default, it returns the 'redirectTo' entry of the configuration * object. It can be overridden to return any value. * * @return the URL of the destination */ protected String getRedirectTo() { if (configuration == null) { return null; } else { return configuration.get("redirectTo"); } } /** * Gets the prefix of the redirect filter. By default, it returns the 'prefix' entry of the configuration object. It * can be overridden to return any value. * * @return the URL of the destination */ protected String getPrefix() { if (configuration == null) { return ""; } else { return configuration.get("prefix"); } } }