001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.wicket.request.http.handler;
018
019import javax.servlet.http.HttpServletResponse;
020
021import org.apache.wicket.request.IRequestCycle;
022import org.apache.wicket.request.IRequestHandler;
023import org.apache.wicket.request.http.WebRequest;
024import org.apache.wicket.request.http.WebResponse;
025import org.apache.wicket.util.lang.Args;
026
027/**
028 * A request handler that redirects to the given url.
029 * 
030 * the url should be one of the following:
031 * <ul>
032 * <li>Fully qualified "http://foo.com/bar"</li>
033 * <li>Relative to the Wicket filter/servlet, e.g. "?wicket:interface=foo", "mounted_page"</li>
034 * <li>Absolute within your web application's <strong>context root</strong>, e.g. "/foo.html"</li>
035 * </ul>
036 * 
037 * @author igor.vaynberg
038 * @author jcompagner
039 */
040public class RedirectRequestHandler implements IRequestHandler
041{
042        public enum Mode
043        {
044                /**
045                 * Use {@link WebResponse#sendRedirect(String)}.
046                 * It will call {@link WebResponse#encodeRedirectURL(CharSequence)} and may URL-encode some parts of the {@code location}
047                 */
048                REDIRECT,
049                /**
050                 * Use {@link WebResponse#setStatus(int)} + {@link WebResponse#setHeader(String, String)}.
051                 * Sets the {@code location} as a value of {@code Location} response header as is, i.e. without url-encoding.
052                 */
053                STATUS,
054                /**
055                 * Decide dynamically depending on the value of {@code status} field
056                 */
057                AUTO;
058        }
059
060        private final String redirectUrl;
061        private final int status;
062
063        private Mode mode = Mode.AUTO;
064
065        /**
066         * @param redirectUrl
067         *            URL to redirect to.
068         */
069        public RedirectRequestHandler(final String redirectUrl)
070        {
071                this(redirectUrl, HttpServletResponse.SC_MOVED_TEMPORARILY);
072        }
073
074        /**
075         * @param redirectUrl
076         *            URL to redirect to.
077         * @param status
078         *            301 (Moved permanently) or 302 (Moved temporarily)
079         */
080        public RedirectRequestHandler(final String redirectUrl, final int status)
081        {
082                if ((status != HttpServletResponse.SC_MOVED_PERMANENTLY) &&
083                        (status != HttpServletResponse.SC_MOVED_TEMPORARILY) &&
084                        (status != HttpServletResponse.SC_SEE_OTHER))
085                {
086                        throw new IllegalStateException("Status must be either 301, 302 or 303, but was: " + status);
087                }
088                this.redirectUrl = Args.notEmpty(redirectUrl, "redirectUrl");
089                this.status = status;
090        }
091
092        /**
093         * @return redirect url
094         */
095        public String getRedirectUrl()
096        {
097                return redirectUrl;
098        }
099
100        /**
101         * @return http redirect status code
102         */
103        public int getStatus()
104        {
105                return status;
106        }
107
108        public RedirectRequestHandler mode(Mode mode)
109        {
110                this.mode = mode != null ? mode : Mode.AUTO;
111                return this;
112        }
113
114        @Override
115        public void respond(final IRequestCycle requestCycle)
116        {
117                final String location;
118
119                final String url = getRedirectUrl();
120
121                if (url.charAt(0) == '/')
122                {
123                        // context-absolute url
124                        location = requestCycle.getUrlRenderer().renderContextRelativeUrl(url);
125                }
126                else
127                {
128                        // if relative url, servlet container will translate to absolute as
129                        // per the servlet spec
130                        // if absolute url still do the same
131                        location = url;
132                }
133
134                WebResponse response = (WebResponse)requestCycle.getResponse();
135
136                if (mode == Mode.REDIRECT)
137                {
138                        response.sendRedirect(location);
139                }
140                else if (mode == Mode.STATUS)
141                {
142                        setStatus(response, requestCycle, location);
143                }
144                // Mode.AUTO
145                else if (status == HttpServletResponse.SC_MOVED_TEMPORARILY)
146                {
147                        response.sendRedirect(location);
148                }
149                else
150                {
151                        setStatus(response, requestCycle, location);
152                }
153        }
154
155        private void setStatus(final WebResponse response, final IRequestCycle requestCycle, final String location) {
156                response.setStatus(status);
157
158                if (((WebRequest)requestCycle.getRequest()).isAjax())
159                {
160                        response.setHeader("Ajax-Location", location);
161                }
162                else
163                {
164                        response.setHeader("Location", location);
165                }
166        }
167}