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 jakarta.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
079         *            302 (Found (former Moved temporarily)) or
080         *            303 (See other) or
081         *            307 (Temporary redirect) or
082         *            308 (Permanent recirect)
083         */
084        public RedirectRequestHandler(final String redirectUrl, final int status)
085        {
086                if ((status != HttpServletResponse.SC_MOVED_PERMANENTLY) &&
087                        (status != HttpServletResponse.SC_FOUND) &&
088                        (status != HttpServletResponse.SC_SEE_OTHER) &&
089                        (status != HttpServletResponse.SC_TEMPORARY_REDIRECT) &&
090                        (status != 308 /* Constant HttpServletResponse.SC_PERMANENT_REDIRECT, requires Jakarta servlet-api 6.1 */))
091                {
092                        throw new IllegalStateException("Status must be either 301, 302, 303, 307 or 308 but was: " + status);
093                }
094                this.redirectUrl = Args.notEmpty(redirectUrl, "redirectUrl");
095                this.status = status;
096        }
097
098        /**
099         * @return redirect url
100         */
101        public String getRedirectUrl()
102        {
103                return redirectUrl;
104        }
105
106        /**
107         * @return http redirect status code
108         */
109        public int getStatus()
110        {
111                return status;
112        }
113
114        public RedirectRequestHandler mode(Mode mode)
115        {
116                this.mode = mode != null ? mode : Mode.AUTO;
117                return this;
118        }
119
120        @Override
121        public void respond(final IRequestCycle requestCycle)
122        {
123                final String location;
124
125                final String url = getRedirectUrl();
126
127                if (url.charAt(0) == '/')
128                {
129                        // context-absolute url
130                        location = requestCycle.getUrlRenderer().renderContextRelativeUrl(url);
131                }
132                else
133                {
134                        // if relative url, servlet container will translate to absolute as
135                        // per the servlet spec
136                        // if absolute url still do the same
137                        location = url;
138                }
139
140                WebResponse response = (WebResponse)requestCycle.getResponse();
141
142                if (mode == Mode.REDIRECT)
143                {
144                        response.sendRedirect(location);
145                }
146                else if (mode == Mode.STATUS)
147                {
148                        setStatus(response, requestCycle, location);
149                }
150                // Mode.AUTO
151                else if (status == HttpServletResponse.SC_FOUND)
152                {
153                        response.sendRedirect(location);
154                }
155                else
156                {
157                        setStatus(response, requestCycle, location);
158                }
159        }
160
161        private void setStatus(final WebResponse response, final IRequestCycle requestCycle, final String location) {
162                response.setStatus(status);
163
164                if (((WebRequest)requestCycle.getRequest()).isAjax())
165                {
166                        response.setHeader("Ajax-Location", location);
167                }
168                else
169                {
170                        response.setHeader("Location", location);
171                }
172        }
173}