1 /* 2 * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.incubator.http; 27 28 import java.io.IOException; 29 import java.io.UncheckedIOException; 30 import java.net.URI; 31 import jdk.incubator.http.internal.common.Utils; 32 33 class RedirectFilter implements HeaderFilter { 34 35 HttpRequestImpl request; 36 HttpClientImpl client; 37 HttpClient.Redirect policy; 38 String method; 39 MultiExchange<?,?> exchange; 40 static final int DEFAULT_MAX_REDIRECTS = 5; 41 URI uri; 42 43 static final int max_redirects = Utils.getIntegerNetProperty( 44 "jdk.httpclient.redirects.retrylimit", DEFAULT_MAX_REDIRECTS 45 ); 46 47 // A public no-arg constructor is required by FilterFactory 48 public RedirectFilter() {} 49 50 @Override 51 public synchronized void request(HttpRequestImpl r, MultiExchange<?,?> e) throws IOException { 52 this.request = r; 53 this.client = e.client(); 54 this.policy = client.followRedirects(); 55 56 this.method = r.method(); 57 this.uri = r.uri(); 58 this.exchange = e; 59 } 60 61 @Override 62 public synchronized HttpRequestImpl response(Response r) throws IOException { 63 return handleResponse(r); 64 } 65 66 /** 67 * checks to see if new request needed and returns it. 68 * Null means response is ok to return to user. 69 */ 70 private HttpRequestImpl handleResponse(Response r) { 71 int rcode = r.statusCode(); 72 if (rcode == 200 || policy == HttpClient.Redirect.NEVER) { 73 return null; 74 } 75 if (rcode >= 300 && rcode <= 399) { 76 URI redir = getRedirectedURI(r.headers()); 77 if (canRedirect(redir) && ++exchange.numberOfRedirects < max_redirects) { 78 //System.out.println("Redirecting to: " + redir); 79 return new HttpRequestImpl(redir, method, request); 80 } else { 81 //System.out.println("Redirect: giving up"); 82 return null; 83 } 84 } 85 return null; 86 } 87 88 private URI getRedirectedURI(HttpHeaders headers) { 89 URI redirectedURI; 90 redirectedURI = headers.firstValue("Location") 91 .map(URI::create) 92 .orElseThrow(() -> new UncheckedIOException( 93 new IOException("Invalid redirection"))); 94 95 // redirect could be relative to original URL, but if not 96 // then redirect is used. 97 redirectedURI = uri.resolve(redirectedURI); 98 return redirectedURI; 99 } 100 101 private boolean canRedirect(URI redir) { 102 String newScheme = redir.getScheme(); 103 String oldScheme = uri.getScheme(); 104 switch (policy) { 105 case ALWAYS: 106 return true; 107 case NEVER: 108 return false; 109 case SECURE: 110 return newScheme.equalsIgnoreCase("https"); 111 case SAME_PROTOCOL: 112 return newScheme.equalsIgnoreCase(oldScheme); 113 default: 114 throw new InternalError(); 115 } 116 } 117 }