View Javadoc

1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  
28  package org.apache.http.impl.client;
29  
30  import java.net.URI;
31  import java.net.URISyntaxException;
32  
33  import org.apache.commons.logging.Log;
34  import org.apache.commons.logging.LogFactory;
35  import org.apache.http.Header;
36  import org.apache.http.HttpHost;
37  import org.apache.http.HttpRequest;
38  import org.apache.http.HttpResponse;
39  import org.apache.http.HttpStatus;
40  import org.apache.http.ProtocolException;
41  import org.apache.http.annotation.Immutable;
42  import org.apache.http.client.CircularRedirectException;
43  import org.apache.http.client.RedirectStrategy;
44  import org.apache.http.client.config.RequestConfig;
45  import org.apache.http.client.methods.HttpGet;
46  import org.apache.http.client.methods.HttpHead;
47  import org.apache.http.client.methods.HttpUriRequest;
48  import org.apache.http.client.methods.RequestBuilder;
49  import org.apache.http.client.protocol.HttpClientContext;
50  import org.apache.http.client.utils.URIUtils;
51  import org.apache.http.protocol.HttpContext;
52  import org.apache.http.protocol.HttpCoreContext;
53  import org.apache.http.util.Args;
54  import org.apache.http.util.Asserts;
55  
56  /**
57   * Default implementation of {@link RedirectStrategy}. This strategy honors the restrictions
58   * on automatic redirection of entity enclosing methods such as POST and PUT imposed by the
59   * HTTP specification. <tt>302 Moved Temporarily</tt>, <tt>301 Moved Permanently</tt> and
60   * <tt>307 Temporary Redirect</tt> status codes will result in an automatic redirect of
61   * HEAD and GET methods only. POST and PUT methods will not be automatically redirected
62   * as requiring user confirmation.
63   * <p/>
64   * The restriction on automatic redirection of POST methods can be relaxed by using
65   * {@link LaxRedirectStrategy} instead of {@link DefaultRedirectStrategy}.
66   *
67   * @see LaxRedirectStrategy
68   * @since 4.1
69   */
70  @Immutable
71  public class DefaultRedirectStrategy implements RedirectStrategy {
72  
73      private final Log log = LogFactory.getLog(getClass());
74  
75      public static final String REDIRECT_LOCATIONS = "http.protocol.redirect-locations";
76  
77      public static final DefaultRedirectStrategy INSTANCE = new DefaultRedirectStrategy();
78  
79      /**
80       * Redirectable methods.
81       */
82      private static final String[] REDIRECT_METHODS = new String[] {
83          HttpGet.METHOD_NAME,
84          HttpHead.METHOD_NAME
85      };
86  
87      public DefaultRedirectStrategy() {
88          super();
89      }
90  
91      public boolean isRedirected(
92              final HttpRequest request,
93              final HttpResponse response,
94              final HttpContext context) throws ProtocolException {
95          Args.notNull(request, "HTTP request");
96          Args.notNull(response, "HTTP response");
97  
98          final int statusCode = response.getStatusLine().getStatusCode();
99          final String method = request.getRequestLine().getMethod();
100         final Header locationHeader = response.getFirstHeader("location");
101         switch (statusCode) {
102         case HttpStatus.SC_MOVED_TEMPORARILY:
103             return isRedirectable(method) && locationHeader != null;
104         case HttpStatus.SC_MOVED_PERMANENTLY:
105         case HttpStatus.SC_TEMPORARY_REDIRECT:
106             return isRedirectable(method);
107         case HttpStatus.SC_SEE_OTHER:
108             return true;
109         default:
110             return false;
111         } //end of switch
112     }
113 
114     public URI getLocationURI(
115             final HttpRequest request,
116             final HttpResponse response,
117             final HttpContext context) throws ProtocolException {
118         Args.notNull(request, "HTTP request");
119         Args.notNull(response, "HTTP response");
120         Args.notNull(context, "HTTP context");
121 
122         final HttpClientContext clientContext = HttpClientContext.adapt(context);
123 
124         //get the location header to find out where to redirect to
125         final Header locationHeader = response.getFirstHeader("location");
126         if (locationHeader == null) {
127             // got a redirect response, but no location header
128             throw new ProtocolException(
129                     "Received redirect response " + response.getStatusLine()
130                     + " but no location header");
131         }
132         final String location = locationHeader.getValue();
133         if (this.log.isDebugEnabled()) {
134             this.log.debug("Redirect requested to location '" + location + "'");
135         }
136 
137         final RequestConfig config = clientContext.getRequestConfig();
138 
139         URI uri = createLocationURI(location);
140 
141         // rfc2616 demands the location value be a complete URI
142         // Location       = "Location" ":" absoluteURI
143         try {
144             // Drop fragment
145             uri = URIUtils.rewriteURI(uri);
146             if (!uri.isAbsolute()) {
147                 if (!config.isRelativeRedirectsAllowed()) {
148                     throw new ProtocolException("Relative redirect location '"
149                             + uri + "' not allowed");
150                 }
151                 // Adjust location URI
152                 final HttpHost target = (HttpHost) context.getAttribute(HttpCoreContext.HTTP_TARGET_HOST);
153                 Asserts.notNull(target, "Target host");
154                 final URI requestURI = new URI(request.getRequestLine().getUri());
155                 final URI absoluteRequestURI = URIUtils.rewriteURI(requestURI, target, true);
156                 uri = URIUtils.resolve(absoluteRequestURI, uri);
157             }
158         } catch (final URISyntaxException ex) {
159             throw new ProtocolException(ex.getMessage(), ex);
160         }
161 
162         RedirectLocations redirectLocations = (RedirectLocations) context.getAttribute(
163                 REDIRECT_LOCATIONS);
164         if (redirectLocations == null) {
165             redirectLocations = new RedirectLocations();
166             context.setAttribute(REDIRECT_LOCATIONS, redirectLocations);
167         }
168         if (!config.isCircularRedirectsAllowed()) {
169             if (redirectLocations.contains(uri)) {
170                 throw new CircularRedirectException("Circular redirect to '" + uri + "'");
171             }
172         }
173         redirectLocations.add(uri);
174         return uri;
175     }
176 
177     /**
178      * @since 4.1
179      */
180     protected URI createLocationURI(final String location) throws ProtocolException {
181         try {
182             return new URI(location).normalize();
183         } catch (final URISyntaxException ex) {
184             throw new ProtocolException("Invalid redirect URI: " + location, ex);
185         }
186     }
187 
188     /**
189      * @since 4.2
190      */
191     protected boolean isRedirectable(final String method) {
192         for (final String m: REDIRECT_METHODS) {
193             if (m.equalsIgnoreCase(method)) {
194                 return true;
195             }
196         }
197         return false;
198     }
199 
200     public HttpUriRequest getRedirect(
201             final HttpRequest request,
202             final HttpResponse response,
203             final HttpContext context) throws ProtocolException {
204         final URI uri = getLocationURI(request, response, context);
205         final String method = request.getRequestLine().getMethod();
206         if (method.equalsIgnoreCase(HttpHead.METHOD_NAME)) {
207             return new HttpHead(uri);
208         } else if (method.equalsIgnoreCase(HttpGet.METHOD_NAME)) {
209             return new HttpGet(uri);
210         } else {
211             final int status = response.getStatusLine().getStatusCode();
212             if (status == HttpStatus.SC_TEMPORARY_REDIRECT) {
213                 return RequestBuilder.copy(request).setUri(uri).build();
214             } else {
215                 return new HttpGet(uri);
216             }
217         }
218     }
219 
220 }