1 /*
2  * Copyright (c) 1995, 2020, 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 sun.net.www.protocol.http;
27 
28 import java.security.PrivilegedAction;
29 import java.util.Arrays;
30 import java.net.URL;
31 import java.net.URLConnection;
32 import java.net.ProtocolException;
33 import java.net.HttpRetryException;
34 import java.net.PasswordAuthentication;
35 import java.net.Authenticator;
36 import java.net.HttpCookie;
37 import java.net.InetAddress;
38 import java.net.UnknownHostException;
39 import java.net.SocketTimeoutException;
40 import java.net.SocketPermission;
41 import java.net.Proxy;
42 import java.net.ProxySelector;
43 import java.net.URI;
44 import java.net.InetSocketAddress;
45 import java.net.CookieHandler;
46 import java.net.ResponseCache;
47 import java.net.CacheResponse;
48 import java.net.SecureCacheResponse;
49 import java.net.CacheRequest;
50 import java.net.URLPermission;
51 import java.net.Authenticator.RequestorType;
52 import java.security.AccessController;
53 import java.security.PrivilegedExceptionAction;
54 import java.security.PrivilegedActionException;
55 import java.io.*;
56 import java.util.ArrayList;
57 import java.util.Collections;
58 import java.util.Date;
59 import java.util.Map;
60 import java.util.List;
61 import java.util.Locale;
62 import java.util.StringTokenizer;
63 import java.util.Iterator;
64 import java.util.HashSet;
65 import java.util.HashMap;
66 import java.util.Set;
67 import java.util.StringJoiner;
68 import jdk.internal.access.JavaNetHttpCookieAccess;
69 import jdk.internal.access.SharedSecrets;
70 import sun.net.*;
71 import sun.net.util.IPAddressUtil;
72 import sun.net.www.*;
73 import sun.net.www.http.HttpClient;
74 import sun.net.www.http.PosterOutputStream;
75 import sun.net.www.http.ChunkedInputStream;
76 import sun.net.www.http.ChunkedOutputStream;
77 import sun.util.logging.PlatformLogger;
78 import java.text.SimpleDateFormat;
79 import java.util.TimeZone;
80 import java.net.MalformedURLException;
81 import java.nio.ByteBuffer;
82 import java.util.Objects;
83 import java.util.Properties;
84 import java.util.concurrent.locks.ReentrantLock;
85 
86 import static sun.net.www.protocol.http.AuthScheme.BASIC;
87 import static sun.net.www.protocol.http.AuthScheme.DIGEST;
88 import static sun.net.www.protocol.http.AuthScheme.NTLM;
89 import static sun.net.www.protocol.http.AuthScheme.NEGOTIATE;
90 import static sun.net.www.protocol.http.AuthScheme.KERBEROS;
91 import static sun.net.www.protocol.http.AuthScheme.UNKNOWN;
92 import sun.security.action.GetIntegerAction;
93 import sun.security.action.GetPropertyAction;
94 
95 /**
96  * A class to represent an HTTP connection to a remote object.
97  */
98 
99 
100 public class HttpURLConnection extends java.net.HttpURLConnection {
101 
102     static final String HTTP_CONNECT = "CONNECT";
103 
104     static final String version;
105     public static final String userAgent;
106 
107     /* max # of allowed re-directs */
108     static final int defaultmaxRedirects = 20;
109     static final int maxRedirects;
110 
111     /* Not all servers support the (Proxy)-Authentication-Info headers.
112      * By default, we don't require them to be sent
113      */
114     static final boolean validateProxy;
115     static final boolean validateServer;
116 
117     /** A, possibly empty, set of authentication schemes that are disabled
118      *  when proxying plain HTTP ( not HTTPS ). */
119     static final Set<String> disabledProxyingSchemes;
120 
121     /** A, possibly empty, set of authentication schemes that are disabled
122      *  when setting up a tunnel for HTTPS ( HTTP CONNECT ). */
123     static final Set<String> disabledTunnelingSchemes;
124 
125     private StreamingOutputStream strOutputStream;
126     private static final String RETRY_MSG1 =
127         "cannot retry due to proxy authentication, in streaming mode";
128     private static final String RETRY_MSG2 =
129         "cannot retry due to server authentication, in streaming mode";
130     private static final String RETRY_MSG3 =
131         "cannot retry due to redirection, in streaming mode";
132 
133     /*
134      * System properties related to error stream handling:
135      *
136      * sun.net.http.errorstream.enableBuffering = <boolean>
137      *
138      * With the above system property set to true (default is false),
139      * when the response code is >=400, the HTTP handler will try to
140      * buffer the response body (up to a certain amount and within a
141      * time limit). Thus freeing up the underlying socket connection
142      * for reuse. The rationale behind this is that usually when the
143      * server responds with a >=400 error (client error or server
144      * error, such as 404 file not found), the server will send a
145      * small response body to explain who to contact and what to do to
146      * recover. With this property set to true, even if the
147      * application doesn't call getErrorStream(), read the response
148      * body, and then call close(), the underlying socket connection
149      * can still be kept-alive and reused. The following two system
150      * properties provide further control to the error stream
151      * buffering behaviour.
152      *
153      * sun.net.http.errorstream.timeout = <int>
154      *     the timeout (in millisec) waiting the error stream
155      *     to be buffered; default is 300 ms
156      *
157      * sun.net.http.errorstream.bufferSize = <int>
158      *     the size (in bytes) to use for the buffering the error stream;
159      *     default is 4k
160      */
161 
162 
163     /* Should we enable buffering of error streams? */
164     private static boolean enableESBuffer = false;
165 
166     /* timeout waiting for read for buffered error stream;
167      */
168     private static int timeout4ESBuffer = 0;
169 
170     /* buffer size for buffered error stream;
171      */
172     private static int bufSize4ES = 0;
173 
174     /*
175      * Restrict setting of request headers through the public api
176      * consistent with JavaScript XMLHttpRequest2 with a few
177      * exceptions. Disallowed headers are silently ignored for
178      * backwards compatibility reasons rather than throwing a
179      * SecurityException. For example, some applets set the
180      * Host header since old JREs did not implement HTTP 1.1.
181      * Additionally, any header starting with Sec- is
182      * disallowed.
183      *
184      * The following headers are allowed for historical reasons:
185      *
186      * Accept-Charset, Accept-Encoding, Cookie, Cookie2, Date,
187      * Referer, TE, User-Agent, headers beginning with Proxy-.
188      *
189      * The following headers are allowed in a limited form:
190      *
191      * Connection: close
192      *
193      * See http://www.w3.org/TR/XMLHttpRequest2.
194      */
195     private static final boolean allowRestrictedHeaders;
196     private static final Set<String> restrictedHeaderSet;
197     private static final String[] restrictedHeaders = {
198         /* Restricted by XMLHttpRequest2 */
199         //"Accept-Charset",
200         //"Accept-Encoding",
201         "Access-Control-Request-Headers",
202         "Access-Control-Request-Method",
203         "Connection", /* close is allowed */
204         "Content-Length",
205         //"Cookie",
206         //"Cookie2",
207         "Content-Transfer-Encoding",
208         //"Date",
209         //"Expect",
210         "Host",
211         "Keep-Alive",
212         "Origin",
213         // "Referer",
214         // "TE",
215         "Trailer",
216         "Transfer-Encoding",
217         "Upgrade",
218         //"User-Agent",
219         "Via"
220     };
221 
getNetProperty(String name)222     private static String getNetProperty(String name) {
223         PrivilegedAction<String> pa = () -> NetProperties.get(name);
224         return AccessController.doPrivileged(pa);
225     }
226 
schemesListToSet(String list)227     private static Set<String> schemesListToSet(String list) {
228         if (list == null || list.isEmpty())
229             return Collections.emptySet();
230 
231         Set<String> s = new HashSet<>();
232         String[] parts = list.split("\\s*,\\s*");
233         for (String part : parts)
234             s.add(part.toLowerCase(Locale.ROOT));
235         return s;
236     }
237 
238     static {
239         Properties props = GetPropertyAction.privilegedGetProperties();
240         maxRedirects = GetIntegerAction.privilegedGetProperty(
241                 "http.maxRedirects", defaultmaxRedirects);
242         version = props.getProperty("java.version");
243         String agent = props.getProperty("http.agent");
244         if (agent == null) {
245             agent = "Java/"+version;
246         } else {
247             agent = agent + " Java/"+version;
248         }
249         userAgent = agent;
250 
251         // A set of net properties to control the use of authentication schemes
252         // when proxying/tunneling.
253         String p = getNetProperty("jdk.http.auth.tunneling.disabledSchemes");
254         disabledTunnelingSchemes = schemesListToSet(p);
255         p = getNetProperty("jdk.http.auth.proxying.disabledSchemes");
256         disabledProxyingSchemes = schemesListToSet(p);
257 
258         validateProxy = Boolean.parseBoolean(
259                 props.getProperty("http.auth.digest.validateProxy"));
260         validateServer = Boolean.parseBoolean(
261                 props.getProperty("http.auth.digest.validateServer"));
262 
263         enableESBuffer = Boolean.parseBoolean(
264                 props.getProperty("sun.net.http.errorstream.enableBuffering"));
265         timeout4ESBuffer = GetIntegerAction.privilegedGetProperty(
266                 "sun.net.http.errorstream.timeout", 300);
267         if (timeout4ESBuffer <= 0) {
268             timeout4ESBuffer = 300; // use the default
269         }
270 
271         bufSize4ES = GetIntegerAction.privilegedGetProperty(
272                 "sun.net.http.errorstream.bufferSize", 4096);
273         if (bufSize4ES <= 0) {
274             bufSize4ES = 4096; // use the default
275         }
276 
277         allowRestrictedHeaders = Boolean.parseBoolean(
278                 props.getProperty("sun.net.http.allowRestrictedHeaders"));
279         if (!allowRestrictedHeaders) {
280             restrictedHeaderSet = new HashSet<>(restrictedHeaders.length);
281             for (int i=0; i < restrictedHeaders.length; i++) {
restrictedHeaderSet.add(restrictedHeaders[i].toLowerCase())282                 restrictedHeaderSet.add(restrictedHeaders[i].toLowerCase());
283             }
284         } else {
285             restrictedHeaderSet = null;
286         }
287     }
288 
289     static final String httpVersion = "HTTP/1.1";
290     static final String acceptString =
291         "text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2";
292 
293     // the following http request headers should NOT have their values
294     // returned for security reasons.
295     private static final String[] EXCLUDE_HEADERS = {
296             "Proxy-Authorization",
297             "Authorization"
298     };
299 
300     // also exclude system cookies when any might be set
301     private static final String[] EXCLUDE_HEADERS2= {
302             "Proxy-Authorization",
303             "Authorization",
304             "Cookie",
305             "Cookie2"
306     };
307 
308     protected HttpClient http;
309     protected Handler handler;
310     protected Proxy instProxy;
311     protected volatile Authenticator authenticator;
312     protected volatile String authenticatorKey;
313 
314     private CookieHandler cookieHandler;
315     private final ResponseCache cacheHandler;
316 
317     private volatile boolean usingProxy;
318 
319     // the cached response, and cached response headers and body
320     protected CacheResponse cachedResponse;
321     private MessageHeader cachedHeaders;
322     private InputStream cachedInputStream;
323 
324     /* output stream to server */
325     protected PrintStream ps = null;
326 
327     /* buffered error stream */
328     private InputStream errorStream = null;
329 
330     /* User set Cookies */
331     private boolean setUserCookies = true;
332     private String userCookies = null;
333     private String userCookies2 = null;
334 
335     /* We only have a single static authenticator for now.
336      * REMIND:  backwards compatibility with JDK 1.1.  Should be
337      * eliminated for JDK 2.0.
338      */
339     @Deprecated
340     private static HttpAuthenticator defaultAuth;
341 
342     /* all the headers we send
343      * NOTE: do *NOT* dump out the content of 'requests' in the
344      * output or stacktrace since it may contain security-sensitive
345      * headers such as those defined in EXCLUDE_HEADERS.
346      */
347     private MessageHeader requests;
348 
349     /* The headers actually set by the user are recorded here also
350      */
351     private MessageHeader userHeaders;
352 
353     /* Headers and request method cannot be changed
354      * once this flag is set in :-
355      *     - getOutputStream()
356      *     - getInputStream())
357      *     - connect()
358      * Access is protected by connectionLock.
359      */
360     private boolean connecting = false;
361 
362     /* The following two fields are only used with Digest Authentication */
363     String domain;      /* The list of authentication domains */
364     DigestAuthentication.Parameters digestparams;
365 
366     /* Current credentials in use */
367     AuthenticationInfo  currentProxyCredentials = null;
368     AuthenticationInfo  currentServerCredentials = null;
369     boolean             needToCheck = true;
370     private boolean doingNTLM2ndStage = false; /* doing the 2nd stage of an NTLM server authentication */
371     private boolean doingNTLMp2ndStage = false; /* doing the 2nd stage of an NTLM proxy authentication */
372 
373     /* try auth without calling Authenticator. Used for transparent NTLM authentication */
374     private boolean tryTransparentNTLMServer = true;
375     private boolean tryTransparentNTLMProxy = true;
376     private boolean useProxyResponseCode = false;
377 
378     /* Used by Windows specific code */
379     private Object authObj;
380 
381     /* Set if the user is manually setting the Authorization or Proxy-Authorization headers */
382     boolean isUserServerAuth;
383     boolean isUserProxyAuth;
384 
385     String serverAuthKey, proxyAuthKey;
386 
387     /* Progress source */
388     protected ProgressSource pi;
389 
390     /* all the response headers we get back */
391     private MessageHeader responses;
392     /* the stream _from_ the server */
393     private InputStream inputStream = null;
394     /* post stream _to_ the server, if any */
395     private PosterOutputStream poster = null;
396 
397     /* Indicates if the std. request headers have been set in requests. */
398     private boolean setRequests=false;
399 
400     /* Indicates whether a request has already failed or not */
401     private boolean failedOnce=false;
402 
403     /* Remembered Exception, we will throw it again if somebody
404        calls getInputStream after disconnect */
405     private Exception rememberedException = null;
406 
407     /* If we decide we want to reuse a client, we put it here */
408     private HttpClient reuseClient = null;
409 
410     /* Tunnel states */
411     public enum TunnelState {
412         /* No tunnel */
413         NONE,
414 
415         /* Setting up a tunnel */
416         SETUP,
417 
418         /* Tunnel has been successfully setup */
419         TUNNELING
420     }
421 
422     private TunnelState tunnelState = TunnelState.NONE;
423 
424     /* Redefine timeouts from java.net.URLConnection as we need -1 to mean
425      * not set. This is to ensure backward compatibility.
426      */
427     private int connectTimeout = NetworkClient.DEFAULT_CONNECT_TIMEOUT;
428     private int readTimeout = NetworkClient.DEFAULT_READ_TIMEOUT;
429 
430     /* A permission converted from a URLPermission */
431     private SocketPermission socketPermission;
432 
433     /* Logging support */
434     private static final PlatformLogger logger =
435             PlatformLogger.getLogger("sun.net.www.protocol.http.HttpURLConnection");
436 
437     /* Lock */
438     private final ReentrantLock connectionLock = new ReentrantLock();
439 
lock()440     private final void lock() {
441         connectionLock.lock();
442     }
443 
unlock()444     private final void unlock() {
445         connectionLock.unlock();
446     }
447 
isLockHeldByCurrentThread()448     public final boolean isLockHeldByCurrentThread() {
449         return connectionLock.isHeldByCurrentThread();
450     }
451 
452 
453     /*
454      * privileged request password authentication
455      *
456      */
457     private static PasswordAuthentication
privilegedRequestPasswordAuthentication( final Authenticator authenticator, final String host, final InetAddress addr, final int port, final String protocol, final String prompt, final String scheme, final URL url, final RequestorType authType)458     privilegedRequestPasswordAuthentication(
459                             final Authenticator authenticator,
460                             final String host,
461                             final InetAddress addr,
462                             final int port,
463                             final String protocol,
464                             final String prompt,
465                             final String scheme,
466                             final URL url,
467                             final RequestorType authType) {
468         return java.security.AccessController.doPrivileged(
469             new java.security.PrivilegedAction<>() {
470                 public PasswordAuthentication run() {
471                     if (logger.isLoggable(PlatformLogger.Level.FINEST)) {
472                         logger.finest("Requesting Authentication: host =" + host + " url = " + url);
473                     }
474                     PasswordAuthentication pass = Authenticator.requestPasswordAuthentication(
475                         authenticator, host, addr, port, protocol,
476                         prompt, scheme, url, authType);
477                     if (logger.isLoggable(PlatformLogger.Level.FINEST)) {
478                         logger.finest("Authentication returned: " + (pass != null ? pass.toString() : "null"));
479                     }
480                     return pass;
481                 }
482             });
483     }
484 
485     private boolean isRestrictedHeader(String key, String value) {
486         if (allowRestrictedHeaders) {
487             return false;
488         }
489 
490         key = key.toLowerCase();
491         if (restrictedHeaderSet.contains(key)) {
492             /*
493              * Exceptions to restricted headers:
494              *
495              * Allow "Connection: close".
496              */
497             if (key.equals("connection") && value.equalsIgnoreCase("close")) {
498                 return false;
499             }
500             return true;
501         } else if (key.startsWith("sec-")) {
502             return true;
503         }
504         return false;
505     }
506 
507     /*
508      * Checks the validity of http message header and whether the header
509      * is restricted and throws IllegalArgumentException if invalid or
510      * restricted.
511      */
512     private boolean isExternalMessageHeaderAllowed(String key, String value) {
513         checkMessageHeader(key, value);
514         if (!isRestrictedHeader(key, value)) {
515             return true;
516         }
517         return false;
518     }
519 
520     /* Logging support */
521     public static PlatformLogger getHttpLogger() {
522         return logger;
523     }
524 
525     /* Used for Windows NTLM implementation */
526     public Object authObj() {
527         return authObj;
528     }
529 
530     public void authObj(Object authObj) {
531         this.authObj = authObj;
532     }
533 
534     @Override
535     public void setAuthenticator(Authenticator auth) {
536         lock();
537         try {
538             if (connecting || connected) {
539                 throw new IllegalStateException(
540                         "Authenticator must be set before connecting");
541             }
542             authenticator = Objects.requireNonNull(auth);
543             authenticatorKey = AuthenticatorKeys.getKey(authenticator);
544         } finally {
545             unlock();
546         }
547     }
548 
549     public String getAuthenticatorKey() {
550         String k = authenticatorKey;
551         if (k == null) return AuthenticatorKeys.getKey(authenticator);
552         return k;
553     }
554 
555     /*
556      * checks the validity of http message header and throws
557      * IllegalArgumentException if invalid.
558      */
559     private void checkMessageHeader(String key, String value) {
560         char LF = '\n';
561         int index = key.indexOf(LF);
562         int index1 = key.indexOf(':');
563         if (index != -1 || index1 != -1) {
564             throw new IllegalArgumentException(
565                 "Illegal character(s) in message header field: " + key);
566         }
567         else {
568             if (value == null) {
569                 return;
570             }
571 
572             index = value.indexOf(LF);
573             while (index != -1) {
574                 index++;
575                 if (index < value.length()) {
576                     char c = value.charAt(index);
577                     if ((c==' ') || (c=='\t')) {
578                         // ok, check the next occurrence
579                         index = value.indexOf(LF, index);
580                         continue;
581                     }
582                 }
583                 throw new IllegalArgumentException(
584                     "Illegal character(s) in message header value: " + value);
585             }
586         }
587     }
588 
589     public void setRequestMethod(String method)
590                         throws ProtocolException {
591         lock();
592         try {
593             if (connecting) {
594                 throw new IllegalStateException("connect in progress");
595             }
596             super.setRequestMethod(method);
597         } finally {
598             unlock();
599         }
600     }
601 
602     /* adds the standard key/val pairs to reqests if necessary & write to
603      * given PrintStream
604      */
605     private void writeRequests() throws IOException {
606         assert isLockHeldByCurrentThread();
607 
608         /* print all message headers in the MessageHeader
609          * onto the wire - all the ones we've set and any
610          * others that have been set
611          */
612         // send any pre-emptive authentication
613         if (http.usingProxy && tunnelState() != TunnelState.TUNNELING) {
614             setPreemptiveProxyAuthentication(requests);
615         }
616         if (!setRequests) {
617 
618             /* We're very particular about the order in which we
619              * set the request headers here.  The order should not
620              * matter, but some careless CGI programs have been
621              * written to expect a very particular order of the
622              * standard headers.  To name names, the order in which
623              * Navigator3.0 sends them.  In particular, we make *sure*
624              * to send Content-type: <> and Content-length:<> second
625              * to last and last, respectively, in the case of a POST
626              * request.
627              */
628             if (!failedOnce) {
629                 checkURLFile();
630                 requests.prepend(method + " " + getRequestURI()+" "  +
631                                  httpVersion, null);
632             }
633             if (!getUseCaches()) {
634                 requests.setIfNotSet ("Cache-Control", "no-cache");
635                 requests.setIfNotSet ("Pragma", "no-cache");
636             }
637             requests.setIfNotSet("User-Agent", userAgent);
638             int port = url.getPort();
639             String host = stripIPv6ZoneId(url.getHost());
640             if (port != -1 && port != url.getDefaultPort()) {
641                 host += ":" + String.valueOf(port);
642             }
643             String reqHost = requests.findValue("Host");
644             if (reqHost == null ||
645                 (!reqHost.equalsIgnoreCase(host) && !checkSetHost()))
646             {
647                 requests.set("Host", host);
648             }
649             requests.setIfNotSet("Accept", acceptString);
650 
651             /*
652              * For HTTP/1.1 the default behavior is to keep connections alive.
653              * However, we may be talking to a 1.0 server so we should set
654              * keep-alive just in case, except if we have encountered an error
655              * or if keep alive is disabled via a system property
656              */
657 
658             // Try keep-alive only on first attempt
659             if (!failedOnce && http.getHttpKeepAliveSet()) {
660                 if (http.usingProxy && tunnelState() != TunnelState.TUNNELING) {
661                     requests.setIfNotSet("Proxy-Connection", "keep-alive");
662                 } else {
663                     requests.setIfNotSet("Connection", "keep-alive");
664                 }
665             } else {
666                 /*
667                  * RFC 2616 HTTP/1.1 section 14.10 says:
668                  * HTTP/1.1 applications that do not support persistent
669                  * connections MUST include the "close" connection option
670                  * in every message
671                  */
672                 requests.setIfNotSet("Connection", "close");
673             }
674             // Set modified since if necessary
675             long modTime = getIfModifiedSince();
676             if (modTime != 0 ) {
677                 Date date = new Date(modTime);
678                 //use the preferred date format according to RFC 2068(HTTP1.1),
679                 // RFC 822 and RFC 1123
680                 SimpleDateFormat fo =
681                   new SimpleDateFormat ("EEE, dd MMM yyyy HH:mm:ss 'GMT'", Locale.US);
682                 fo.setTimeZone(TimeZone.getTimeZone("GMT"));
683                 requests.setIfNotSet("If-Modified-Since", fo.format(date));
684             }
685             // check for preemptive authorization
686             AuthenticationInfo sauth = AuthenticationInfo.getServerAuth(url,
687                                              getAuthenticatorKey());
688             if (sauth != null && sauth.supportsPreemptiveAuthorization() ) {
689                 // Sets "Authorization"
690                 requests.setIfNotSet(sauth.getHeaderName(), sauth.getHeaderValue(url,method));
691                 currentServerCredentials = sauth;
692             }
693 
694             if (!method.equals("PUT") && (poster != null || streaming())) {
695                 requests.setIfNotSet ("Content-type",
696                         "application/x-www-form-urlencoded");
697             }
698 
699             boolean chunked = false;
700 
701             if (streaming()) {
702                 if (chunkLength != -1) {
703                     requests.set ("Transfer-Encoding", "chunked");
704                     chunked = true;
705                 } else { /* fixed content length */
706                     if (fixedContentLengthLong != -1) {
707                         requests.set ("Content-Length",
708                                       String.valueOf(fixedContentLengthLong));
709                     } else if (fixedContentLength != -1) {
710                         requests.set ("Content-Length",
711                                       String.valueOf(fixedContentLength));
712                     }
713                 }
714             } else if (poster != null) {
715                 /* add Content-Length & POST/PUT data */
716                 // safe to synchronize on poster: this is
717                 // a simple subclass of ByteArrayOutputStream
718                 synchronized (poster) {
719                     /* close it, so no more data can be added */
720                     poster.close();
721                     requests.set("Content-Length",
722                                  String.valueOf(poster.size()));
723                 }
724             }
725 
726             if (!chunked) {
727                 if (requests.findValue("Transfer-Encoding") != null) {
728                     requests.remove("Transfer-Encoding");
729                     if (logger.isLoggable(PlatformLogger.Level.WARNING)) {
730                         logger.warning(
731                             "use streaming mode for chunked encoding");
732                     }
733                 }
734             }
735 
736             // get applicable cookies based on the uri and request headers
737             // add them to the existing request headers
738             setCookieHeader();
739 
740             setRequests=true;
741         }
742         if (logger.isLoggable(PlatformLogger.Level.FINE)) {
743             logger.fine(requests.toString());
744         }
745         http.writeRequests(requests, poster, streaming());
746         if (ps.checkError()) {
747             String proxyHost = http.getProxyHostUsed();
748             int proxyPort = http.getProxyPortUsed();
749             disconnectInternal();
750             if (failedOnce) {
751                 throw new IOException("Error writing to server");
752             } else { // try once more
753                 failedOnce=true;
754                 if (proxyHost != null) {
755                     setProxiedClient(url, proxyHost, proxyPort);
756                 } else {
757                     setNewClient (url);
758                 }
759                 ps = (PrintStream) http.getOutputStream();
760                 connected=true;
761                 responses = new MessageHeader();
762                 setRequests=false;
763                 writeRequests();
764             }
765         }
766     }
767 
768     private boolean checkSetHost() {
769         SecurityManager s = System.getSecurityManager();
770         if (s != null) {
771             String name = s.getClass().getName();
772             if (name.equals("sun.plugin2.applet.AWTAppletSecurityManager") ||
773                 name.equals("sun.plugin2.applet.FXAppletSecurityManager") ||
774                 name.equals("com.sun.javaws.security.JavaWebStartSecurity") ||
775                 name.equals("sun.plugin.security.ActivatorSecurityManager"))
776             {
777                 int CHECK_SET_HOST = -2;
778                 try {
779                     s.checkConnect(url.toExternalForm(), CHECK_SET_HOST);
780                 } catch (SecurityException ex) {
781                     return false;
782                 }
783             }
784         }
785         return true;
786     }
787 
788     private void checkURLFile() {
789         SecurityManager s = System.getSecurityManager();
790         if (s != null) {
791             String name = s.getClass().getName();
792             if (name.equals("sun.plugin2.applet.AWTAppletSecurityManager") ||
793                 name.equals("sun.plugin2.applet.FXAppletSecurityManager") ||
794                 name.equals("com.sun.javaws.security.JavaWebStartSecurity") ||
795                 name.equals("sun.plugin.security.ActivatorSecurityManager"))
796             {
797                 int CHECK_SUBPATH = -3;
798                 try {
799                     s.checkConnect(url.toExternalForm(), CHECK_SUBPATH);
800                 } catch (SecurityException ex) {
801                     throw new SecurityException("denied access outside a permitted URL subpath", ex);
802                 }
803             }
804         }
805     }
806 
807     /**
808      * Create a new HttpClient object, bypassing the cache of
809      * HTTP client objects/connections.
810      *
811      * @param url       the URL being accessed
812      */
813     protected void setNewClient (URL url)
814     throws IOException {
815         setNewClient(url, false);
816     }
817 
818     /**
819      * Obtain a HttpsClient object. Use the cached copy if specified.
820      *
821      * @param url       the URL being accessed
822      * @param useCache  whether the cached connection should be used
823      *        if present
824      */
825     protected void setNewClient (URL url, boolean useCache)
826         throws IOException {
827         http = HttpClient.New(url, null, -1, useCache, connectTimeout, this);
828         http.setReadTimeout(readTimeout);
829     }
830 
831 
832     /**
833      * Create a new HttpClient object, set up so that it uses
834      * per-instance proxying to the given HTTP proxy.  This
835      * bypasses the cache of HTTP client objects/connections.
836      *
837      * @param url       the URL being accessed
838      * @param proxyHost the proxy host to use
839      * @param proxyPort the proxy port to use
840      */
841     protected void setProxiedClient (URL url, String proxyHost, int proxyPort)
842     throws IOException {
843         setProxiedClient(url, proxyHost, proxyPort, false);
844     }
845 
846     /**
847      * Obtain a HttpClient object, set up so that it uses per-instance
848      * proxying to the given HTTP proxy. Use the cached copy of HTTP
849      * client objects/connections if specified.
850      *
851      * @param url       the URL being accessed
852      * @param proxyHost the proxy host to use
853      * @param proxyPort the proxy port to use
854      * @param useCache  whether the cached connection should be used
855      *        if present
856      */
857     protected void setProxiedClient (URL url,
858                                      String proxyHost, int proxyPort,
859                                      boolean useCache)
860         throws IOException {
861         proxiedConnect(url, proxyHost, proxyPort, useCache);
862     }
863 
864     protected void proxiedConnect(URL url,
865                                   String proxyHost, int proxyPort,
866                                   boolean useCache)
867         throws IOException {
868         http = HttpClient.New (url, proxyHost, proxyPort, useCache,
869             connectTimeout, this);
870         http.setReadTimeout(readTimeout);
871     }
872 
873     protected HttpURLConnection(URL u, Handler handler)
874     throws IOException {
875         // we set proxy == null to distinguish this case with the case
876         // when per connection proxy is set
877         this(u, null, handler);
878     }
879 
880     private static String checkHost(String h) throws IOException {
881         if (h != null) {
882             if (h.indexOf('\n') > -1) {
883                 throw new MalformedURLException("Illegal character in host");
884             }
885         }
886         return h;
887     }
888     public HttpURLConnection(URL u, String host, int port) throws IOException {
889         this(u, new Proxy(Proxy.Type.HTTP,
890                 InetSocketAddress.createUnresolved(checkHost(host), port)));
891     }
892 
893     /** this constructor is used by other protocol handlers such as ftp
894         that want to use http to fetch urls on their behalf.*/
895     public HttpURLConnection(URL u, Proxy p) throws IOException {
896         this(u, p, new Handler());
897     }
898 
899     private static URL checkURL(URL u) throws IOException {
900         if (u != null) {
901             if (u.toExternalForm().indexOf('\n') > -1) {
902                 throw new MalformedURLException("Illegal character in URL");
903             }
904         }
905         String s = IPAddressUtil.checkAuthority(u);
906         if (s != null) {
907             throw new MalformedURLException(s);
908         }
909         return u;
910     }
911 
912     protected HttpURLConnection(URL u, Proxy p, Handler handler)
913             throws IOException {
914         super(checkURL(u));
915         requests = new MessageHeader();
916         responses = new MessageHeader();
917         userHeaders = new MessageHeader();
918         this.handler = handler;
919         instProxy = p;
920         if (instProxy instanceof sun.net.ApplicationProxy) {
921             /* Application set Proxies should not have access to cookies
922              * in a secure environment unless explicitly allowed. */
923             try {
924                 cookieHandler = CookieHandler.getDefault();
925             } catch (SecurityException se) { /* swallow exception */ }
926         } else {
927             cookieHandler = java.security.AccessController.doPrivileged(
928                 new java.security.PrivilegedAction<>() {
929                 public CookieHandler run() {
930                     return CookieHandler.getDefault();
931                 }
932             });
933         }
934         cacheHandler = java.security.AccessController.doPrivileged(
935             new java.security.PrivilegedAction<>() {
936                 public ResponseCache run() {
937                 return ResponseCache.getDefault();
938             }
939         });
940     }
941 
942     /**
943      * @deprecated.  Use java.net.Authenticator.setDefault() instead.
944      */
945     @Deprecated
946     public static void setDefaultAuthenticator(HttpAuthenticator a) {
947         defaultAuth = a;
948     }
949 
950     /**
951      * opens a stream allowing redirects only to the same host.
952      */
953     public static InputStream openConnectionCheckRedirects(URLConnection c)
954         throws IOException
955     {
956         boolean redir;
957         int redirects = 0;
958         InputStream in;
959         Authenticator a = null;
960 
961         do {
962             if (c instanceof HttpURLConnection) {
963                 ((HttpURLConnection) c).setInstanceFollowRedirects(false);
964                 if (a == null) {
965                     a = ((HttpURLConnection) c).authenticator;
966                 }
967             }
968 
969             // We want to open the input stream before
970             // getting headers, because getHeaderField()
971             // et al swallow IOExceptions.
972             in = c.getInputStream();
973             redir = false;
974 
975             if (c instanceof HttpURLConnection) {
976                 HttpURLConnection http = (HttpURLConnection) c;
977                 int stat = http.getResponseCode();
978                 if (stat >= 300 && stat <= 307 && stat != 306 &&
979                         stat != HttpURLConnection.HTTP_NOT_MODIFIED) {
980                     URL base = http.getURL();
981                     String loc = http.getHeaderField("Location");
982                     URL target = null;
983                     if (loc != null) {
984                         target = new URL(base, loc);
985                     }
986                     http.disconnect();
987                     if (target == null
988                         || !base.getProtocol().equals(target.getProtocol())
989                         || base.getPort() != target.getPort()
990                         || !hostsEqual(base, target)
991                         || redirects >= 5)
992                     {
993                         throw new SecurityException("illegal URL redirect");
994                     }
995                     redir = true;
996                     c = target.openConnection();
997                     if (a != null && c instanceof HttpURLConnection) {
998                         ((HttpURLConnection)c).setAuthenticator(a);
999                     }
1000                     redirects++;
1001                 }
1002             }
1003         } while (redir);
1004         return in;
1005     }
1006 
1007 
1008     //
1009     // Same as java.net.URL.hostsEqual
1010     //
1011     private static boolean hostsEqual(URL u1, URL u2) {
1012         final String h1 = u1.getHost();
1013         final String h2 = u2.getHost();
1014 
1015         if (h1 == null) {
1016             return h2 == null;
1017         } else if (h2 == null) {
1018             return false;
1019         } else if (h1.equalsIgnoreCase(h2)) {
1020             return true;
1021         }
1022         // Have to resolve addresses before comparing, otherwise
1023         // names like tachyon and tachyon.eng would compare different
1024         final boolean result[] = {false};
1025 
1026         java.security.AccessController.doPrivileged(
1027             new java.security.PrivilegedAction<>() {
1028                 public Void run() {
1029                 try {
1030                     InetAddress a1 = InetAddress.getByName(h1);
1031                     InetAddress a2 = InetAddress.getByName(h2);
1032                     result[0] = a1.equals(a2);
1033                 } catch(UnknownHostException | SecurityException e) {
1034                 }
1035                 return null;
1036             }
1037         });
1038 
1039         return result[0];
1040     }
1041 
1042     // overridden in HTTPS subclass
1043 
1044     public void connect() throws IOException {
1045         lock();
1046         try {
1047             connecting = true;
1048         } finally {
1049             unlock();
1050         }
1051         plainConnect();
1052     }
1053 
1054     private boolean checkReuseConnection () {
1055         if (connected) {
1056             return true;
1057         }
1058         if (reuseClient != null) {
1059             http = reuseClient;
1060             http.setReadTimeout(getReadTimeout());
1061             http.reuse = false;
1062             reuseClient = null;
1063             connected = true;
1064             return true;
1065         }
1066         return false;
1067     }
1068 
1069     private String getHostAndPort(URL url) {
1070         String host = url.getHost();
1071         final String hostarg = host;
1072         try {
1073             // lookup hostname and use IP address if available
1074             host = AccessController.doPrivileged(
1075                 new PrivilegedExceptionAction<>() {
1076                     public String run() throws IOException {
1077                             InetAddress addr = InetAddress.getByName(hostarg);
1078                             return addr.getHostAddress();
1079                     }
1080                 }
1081             );
1082         } catch (PrivilegedActionException e) {}
1083         int port = url.getPort();
1084         if (port == -1) {
1085             String scheme = url.getProtocol();
1086             if ("http".equals(scheme)) {
1087                 return host + ":80";
1088             } else { // scheme must be https
1089                 return host + ":443";
1090             }
1091         }
1092         return host + ":" + Integer.toString(port);
1093     }
1094 
1095     protected void plainConnect() throws IOException {
1096         lock();
1097         try {
1098             if (connected) {
1099                 return;
1100             }
1101         } finally {
1102             unlock();
1103         }
1104         SocketPermission p = URLtoSocketPermission(this.url);
1105         if (p != null) {
1106             try {
1107                 AccessController.doPrivilegedWithCombiner(
1108                     new PrivilegedExceptionAction<>() {
1109                         public Void run() throws IOException {
1110                             plainConnect0();
1111                             return null;
1112                         }
1113                     }, null, p
1114                 );
1115             } catch (PrivilegedActionException e) {
1116                     throw (IOException) e.getException();
1117             }
1118         } else {
1119             // run without additional permission
1120             plainConnect0();
1121         }
1122     }
1123 
1124     /**
1125      *  if the caller has a URLPermission for connecting to the
1126      *  given URL, then return a SocketPermission which permits
1127      *  access to that destination. Return null otherwise. The permission
1128      *  is cached in a field (which can only be changed by redirects)
1129      */
1130     SocketPermission URLtoSocketPermission(URL url) throws IOException {
1131 
1132         if (socketPermission != null) {
1133             return socketPermission;
1134         }
1135 
1136         SecurityManager sm = System.getSecurityManager();
1137 
1138         if (sm == null) {
1139             return null;
1140         }
1141 
1142         // the permission, which we might grant
1143 
1144         SocketPermission newPerm = new SocketPermission(
1145             getHostAndPort(url), "connect"
1146         );
1147 
1148         String actions = getRequestMethod()+":" +
1149                 getUserSetHeaders().getHeaderNamesInList();
1150 
1151         String urlstring = url.getProtocol() + "://" + url.getAuthority()
1152                 + url.getPath();
1153 
1154         URLPermission p = new URLPermission(urlstring, actions);
1155         try {
1156             sm.checkPermission(p);
1157             socketPermission = newPerm;
1158             return socketPermission;
1159         } catch (SecurityException e) {
1160             // fall thru
1161         }
1162         return null;
1163     }
1164 
1165     protected void plainConnect0()  throws IOException {
1166         // try to see if request can be served from local cache
1167         if (cacheHandler != null && getUseCaches()) {
1168             try {
1169                 URI uri = ParseUtil.toURI(url);
1170                 if (uri != null) {
1171                     cachedResponse = cacheHandler.get(uri, getRequestMethod(), getUserSetHeaders().getHeaders());
1172                     if ("https".equalsIgnoreCase(uri.getScheme())
1173                         && !(cachedResponse instanceof SecureCacheResponse)) {
1174                         cachedResponse = null;
1175                     }
1176                     if (logger.isLoggable(PlatformLogger.Level.FINEST)) {
1177                         logger.finest("Cache Request for " + uri + " / " + getRequestMethod());
1178                         logger.finest("From cache: " + (cachedResponse != null ? cachedResponse.toString() : "null"));
1179                     }
1180                     if (cachedResponse != null) {
1181                         cachedHeaders = mapToMessageHeader(cachedResponse.getHeaders());
1182                         cachedInputStream = cachedResponse.getBody();
1183                     }
1184                 }
1185             } catch (IOException ioex) {
1186                 // ignore and commence normal connection
1187             }
1188             if (cachedHeaders != null && cachedInputStream != null) {
1189                 connected = true;
1190                 return;
1191             } else {
1192                 cachedResponse = null;
1193             }
1194         }
1195         try {
1196             /* Try to open connections using the following scheme,
1197              * return on the first one that's successful:
1198              * 1) if (instProxy != null)
1199              *        connect to instProxy; raise exception if failed
1200              * 2) else use system default ProxySelector
1201              * 3) else make a direct connection if ProxySelector is not present
1202              */
1203 
1204             if (instProxy == null) { // no instance Proxy is set
1205                 /**
1206                  * Do we have to use a proxy?
1207                  */
1208                 ProxySelector sel =
1209                     java.security.AccessController.doPrivileged(
1210                         new java.security.PrivilegedAction<>() {
1211                             public ProxySelector run() {
1212                                      return ProxySelector.getDefault();
1213                                  }
1214                              });
1215                 if (sel != null) {
1216                     URI uri = sun.net.www.ParseUtil.toURI(url);
1217                     if (logger.isLoggable(PlatformLogger.Level.FINEST)) {
1218                         logger.finest("ProxySelector Request for " + uri);
1219                     }
1220                     final List<Proxy> proxies;
1221                     try {
1222                         proxies = sel.select(uri);
1223                     } catch (IllegalArgumentException iae) {
1224                         throw new IOException("Failed to select a proxy", iae);
1225                     }
1226                     final Iterator<Proxy> it = proxies.iterator();
1227                     Proxy p;
1228                     while (it.hasNext()) {
1229                         p = it.next();
1230                         try {
1231                             if (!failedOnce) {
1232                                 http = getNewHttpClient(url, p, connectTimeout);
1233                                 http.setReadTimeout(readTimeout);
1234                             } else {
1235                                 // make sure to construct new connection if first
1236                                 // attempt failed
1237                                 http = getNewHttpClient(url, p, connectTimeout, false);
1238                                 http.setReadTimeout(readTimeout);
1239                             }
1240                             if (logger.isLoggable(PlatformLogger.Level.FINEST)) {
1241                                 if (p != null) {
1242                                     logger.finest("Proxy used: " + p.toString());
1243                                 }
1244                             }
1245                             break;
1246                         } catch (IOException ioex) {
1247                             if (p != Proxy.NO_PROXY) {
1248                                 sel.connectFailed(uri, p.address(), ioex);
1249                                 if (!it.hasNext()) {
1250                                     if (logger.isLoggable(PlatformLogger.Level.FINEST)) {
1251                                         logger.finest("Retrying with proxy: " + p.toString());
1252                                     }
1253                                     http = getNewHttpClient(url, p, connectTimeout, false);
1254                                     http.setReadTimeout(readTimeout);
1255                                     break;
1256                                 }
1257                             } else {
1258                                 throw ioex;
1259                             }
1260                             continue;
1261                         }
1262                     }
1263                 } else {
1264                     // No proxy selector, create http client with no proxy
1265                     if (!failedOnce) {
1266                         http = getNewHttpClient(url, null, connectTimeout);
1267                         http.setReadTimeout(readTimeout);
1268                     } else {
1269                         // make sure to construct new connection if first
1270                         // attempt failed
1271                         http = getNewHttpClient(url, null, connectTimeout, false);
1272                         http.setReadTimeout(readTimeout);
1273                     }
1274                 }
1275             } else {
1276                 if (!failedOnce) {
1277                     http = getNewHttpClient(url, instProxy, connectTimeout);
1278                     http.setReadTimeout(readTimeout);
1279                 } else {
1280                     // make sure to construct new connection if first
1281                     // attempt failed
1282                     http = getNewHttpClient(url, instProxy, connectTimeout, false);
1283                     http.setReadTimeout(readTimeout);
1284                 }
1285             }
1286 
1287             usingProxy = usingProxy || usingProxyInternal();
1288             ps = (PrintStream)http.getOutputStream();
1289         } catch (IOException e) {
1290             throw e;
1291         }
1292         // constructor to HTTP client calls openserver
1293         connected = true;
1294     }
1295 
1296     // subclass HttpsClient will overwrite & return an instance of HttpsClient
1297     protected HttpClient getNewHttpClient(URL url, Proxy p, int connectTimeout)
1298         throws IOException {
1299         return HttpClient.New(url, p, connectTimeout, this);
1300     }
1301 
1302     // subclass HttpsClient will overwrite & return an instance of HttpsClient
1303     protected HttpClient getNewHttpClient(URL url, Proxy p,
1304                                           int connectTimeout, boolean useCache)
1305         throws IOException {
1306         return HttpClient.New(url, p, connectTimeout, useCache, this);
1307     }
1308 
1309     private void expect100Continue() throws IOException {
1310             // Expect: 100-Continue was set, so check the return code for
1311             // Acceptance
1312             int oldTimeout = http.getReadTimeout();
1313             boolean enforceTimeOut = false;
1314             boolean timedOut = false;
1315             if (oldTimeout <= 0) {
1316                 // 5s read timeout in case the server doesn't understand
1317                 // Expect: 100-Continue
1318                 http.setReadTimeout(5000);
1319                 enforceTimeOut = true;
1320             }
1321 
1322             try {
1323                 http.parseHTTP(responses, pi, this);
1324             } catch (SocketTimeoutException se) {
1325                 if (!enforceTimeOut) {
1326                     throw se;
1327                 }
1328                 timedOut = true;
1329                 http.setIgnoreContinue(true);
1330             }
1331             if (!timedOut) {
1332                 // Can't use getResponseCode() yet
1333                 String resp = responses.getValue(0);
1334                 // Parse the response which is of the form:
1335                 // HTTP/1.1 417 Expectation Failed
1336                 // HTTP/1.1 100 Continue
1337                 if (resp != null && resp.startsWith("HTTP/")) {
1338                     String[] sa = resp.split("\\s+");
1339                     responseCode = -1;
1340                     try {
1341                         // Response code is 2nd token on the line
1342                         if (sa.length > 1)
1343                             responseCode = Integer.parseInt(sa[1]);
1344                     } catch (NumberFormatException numberFormatException) {
1345                     }
1346                 }
1347                 if (responseCode != 100) {
1348                     throw new ProtocolException("Server rejected operation");
1349                 }
1350             }
1351 
1352             http.setReadTimeout(oldTimeout);
1353 
1354             responseCode = -1;
1355             responses.reset();
1356             // Proceed
1357     }
1358 
1359     /*
1360      * Allowable input/output sequences:
1361      * [interpreted as request entity]
1362      * - get output, [write output,] get input, [read input]
1363      * - get output, [write output]
1364      * [interpreted as GET]
1365      * - get input, [read input]
1366      * Disallowed:
1367      * - get input, [read input,] get output, [write output]
1368      */
1369 
1370     @Override
1371     public OutputStream getOutputStream() throws IOException {
1372         lock();
1373         try {
1374             connecting = true;
1375             SocketPermission p = URLtoSocketPermission(this.url);
1376 
1377             if (p != null) {
1378                 try {
1379                     return AccessController.doPrivilegedWithCombiner(
1380                             new PrivilegedExceptionAction<>() {
1381                                 public OutputStream run() throws IOException {
1382                                     return getOutputStream0();
1383                                 }
1384                             }, null, p
1385                     );
1386                 } catch (PrivilegedActionException e) {
1387                     throw (IOException) e.getException();
1388                 }
1389             } else {
1390                 return getOutputStream0();
1391             }
1392         } finally {
1393             unlock();
1394         }
1395     }
1396 
1397     private OutputStream getOutputStream0() throws IOException {
1398         assert isLockHeldByCurrentThread();
1399         try {
1400             if (!doOutput) {
1401                 throw new ProtocolException("cannot write to a URLConnection"
1402                                + " if doOutput=false - call setDoOutput(true)");
1403             }
1404 
1405             if (method.equals("GET")) {
1406                 method = "POST"; // Backward compatibility
1407             }
1408             if ("TRACE".equals(method) && "http".equals(url.getProtocol())) {
1409                 throw new ProtocolException("HTTP method TRACE" +
1410                                             " doesn't support output");
1411             }
1412 
1413             // if there's already an input stream open, throw an exception
1414             if (inputStream != null) {
1415                 throw new ProtocolException("Cannot write output after reading input.");
1416             }
1417 
1418             if (!checkReuseConnection())
1419                 connect();
1420 
1421             boolean expectContinue = false;
1422             String expects = requests.findValue("Expect");
1423             if ("100-Continue".equalsIgnoreCase(expects) && streaming()) {
1424                 http.setIgnoreContinue(false);
1425                 expectContinue = true;
1426             }
1427 
1428             if (streaming() && strOutputStream == null) {
1429                 writeRequests();
1430             }
1431 
1432             if (expectContinue) {
1433                 expect100Continue();
1434             }
1435             ps = (PrintStream)http.getOutputStream();
1436             if (streaming()) {
1437                 if (strOutputStream == null) {
1438                     if (chunkLength != -1) { /* chunked */
1439                          strOutputStream = new StreamingOutputStream(
1440                                new ChunkedOutputStream(ps, chunkLength), -1L);
1441                     } else { /* must be fixed content length */
1442                         long length = 0L;
1443                         if (fixedContentLengthLong != -1) {
1444                             length = fixedContentLengthLong;
1445                         } else if (fixedContentLength != -1) {
1446                             length = fixedContentLength;
1447                         }
1448                         strOutputStream = new StreamingOutputStream(ps, length);
1449                     }
1450                 }
1451                 return strOutputStream;
1452             } else {
1453                 if (poster == null) {
1454                     poster = new PosterOutputStream();
1455                 }
1456                 return poster;
1457             }
1458         } catch (RuntimeException e) {
1459             disconnectInternal();
1460             throw e;
1461         } catch (ProtocolException e) {
1462             // Save the response code which may have been set while enforcing
1463             // the 100-continue. disconnectInternal() forces it to -1
1464             int i = responseCode;
1465             disconnectInternal();
1466             responseCode = i;
1467             throw e;
1468         } catch (IOException e) {
1469             disconnectInternal();
1470             throw e;
1471         }
1472     }
1473 
1474     public boolean streaming () {
1475         return (fixedContentLength != -1) || (fixedContentLengthLong != -1) ||
1476                (chunkLength != -1);
1477     }
1478 
1479     /*
1480      * get applicable cookies based on the uri and request headers
1481      * add them to the existing request headers
1482      */
1483     private void setCookieHeader() throws IOException {
1484         if (cookieHandler != null) {
1485             // we only want to capture the user defined Cookies once, as
1486             // they cannot be changed by user code after we are connected,
1487             // only internally.
1488 
1489             // we should only reach here when called from
1490             // writeRequest, which in turn is only called by
1491             // getInputStream0
1492             assert isLockHeldByCurrentThread();
1493             if (setUserCookies) {
1494                 int k = requests.getKey("Cookie");
1495                 if (k != -1)
1496                     userCookies = requests.getValue(k);
1497                 k = requests.getKey("Cookie2");
1498                 if (k != -1)
1499                     userCookies2 = requests.getValue(k);
1500                 setUserCookies = false;
1501             }
1502 
1503             // remove old Cookie header before setting new one.
1504             requests.remove("Cookie");
1505             requests.remove("Cookie2");
1506 
1507             URI uri = ParseUtil.toURI(url);
1508             if (uri != null) {
1509                 if (logger.isLoggable(PlatformLogger.Level.FINEST)) {
1510                     logger.finest("CookieHandler request for " + uri);
1511                 }
1512                 Map<String, List<String>> cookies
1513                     = cookieHandler.get(
1514                         uri, requests.getHeaders(EXCLUDE_HEADERS));
1515                 if (!cookies.isEmpty()) {
1516                     if (logger.isLoggable(PlatformLogger.Level.FINEST)) {
1517                         logger.finest("Cookies retrieved: " + cookies.toString());
1518                     }
1519                     for (Map.Entry<String, List<String>> entry :
1520                              cookies.entrySet()) {
1521                         String key = entry.getKey();
1522                         // ignore all entries that don't have "Cookie"
1523                         // or "Cookie2" as keys
1524                         if (!"Cookie".equalsIgnoreCase(key) &&
1525                             !"Cookie2".equalsIgnoreCase(key)) {
1526                             continue;
1527                         }
1528                         List<String> l = entry.getValue();
1529                         if (l != null && !l.isEmpty()) {
1530                             StringJoiner cookieValue = new StringJoiner("; ");
1531                             for (String value : l) {
1532                                 cookieValue.add(value);
1533                             }
1534                             requests.add(key, cookieValue.toString());
1535                         }
1536                     }
1537                 }
1538             }
1539             if (userCookies != null) {
1540                 int k;
1541                 if ((k = requests.getKey("Cookie")) != -1)
1542                     requests.set("Cookie", requests.getValue(k) + ";" + userCookies);
1543                 else
1544                     requests.set("Cookie", userCookies);
1545             }
1546             if (userCookies2 != null) {
1547                 int k;
1548                 if ((k = requests.getKey("Cookie2")) != -1)
1549                     requests.set("Cookie2", requests.getValue(k) + ";" + userCookies2);
1550                 else
1551                     requests.set("Cookie2", userCookies2);
1552             }
1553 
1554         } // end of getting cookies
1555     }
1556 
1557     @Override
1558     public InputStream getInputStream() throws IOException {
1559         lock();
1560         try {
1561             connecting = true;
1562             SocketPermission p = URLtoSocketPermission(this.url);
1563 
1564             if (p != null) {
1565                 try {
1566                     return AccessController.doPrivilegedWithCombiner(
1567                             new PrivilegedExceptionAction<>() {
1568                                 public InputStream run() throws IOException {
1569                                     return getInputStream0();
1570                                 }
1571                             }, null, p
1572                     );
1573                 } catch (PrivilegedActionException e) {
1574                     throw (IOException) e.getException();
1575                 }
1576             } else {
1577                 return getInputStream0();
1578             }
1579         } finally {
1580             unlock();
1581         }
1582     }
1583 
1584     @SuppressWarnings("empty-statement")
1585     private InputStream getInputStream0() throws IOException {
1586 
1587         assert isLockHeldByCurrentThread();
1588         if (!doInput) {
1589             throw new ProtocolException("Cannot read from URLConnection"
1590                    + " if doInput=false (call setDoInput(true))");
1591         }
1592 
1593         if (rememberedException != null) {
1594             if (rememberedException instanceof RuntimeException)
1595                 throw new RuntimeException(rememberedException);
1596             else {
1597                 throw getChainedException((IOException)rememberedException);
1598             }
1599         }
1600 
1601         if (inputStream != null) {
1602             return inputStream;
1603         }
1604 
1605         if (streaming() ) {
1606             if (strOutputStream == null) {
1607                 getOutputStream();
1608             }
1609             /* make sure stream is closed */
1610             strOutputStream.close ();
1611             if (!strOutputStream.writtenOK()) {
1612                 throw new IOException ("Incomplete output stream");
1613             }
1614         }
1615 
1616         int redirects = 0;
1617         int respCode = 0;
1618         long cl = -1;
1619         AuthenticationInfo serverAuthentication = null;
1620         AuthenticationInfo proxyAuthentication = null;
1621         AuthenticationHeader srvHdr = null;
1622 
1623         /**
1624          * Failed Negotiate
1625          *
1626          * In some cases, the Negotiate auth is supported for the
1627          * remote host but the negotiate process still fails (For
1628          * example, if the web page is located on a backend server
1629          * and delegation is needed but fails). The authentication
1630          * process will start again, and we need to detect this
1631          * kind of failure and do proper fallback (say, to NTLM).
1632          *
1633          * In order to achieve this, the inNegotiate flag is set
1634          * when the first negotiate challenge is met (and reset
1635          * if authentication is finished). If a fresh new negotiate
1636          * challenge (no parameter) is found while inNegotiate is
1637          * set, we know there's a failed auth attempt recently.
1638          * Here we'll ignore the header line so that fallback
1639          * can be practiced.
1640          *
1641          * inNegotiateProxy is for proxy authentication.
1642          */
1643         boolean inNegotiate = false;
1644         boolean inNegotiateProxy = false;
1645 
1646         // If the user has set either of these headers then do not remove them
1647         isUserServerAuth = requests.getKey("Authorization") != -1;
1648         isUserProxyAuth = requests.getKey("Proxy-Authorization") != -1;
1649 
1650         try {
1651             do {
1652                 if (!checkReuseConnection())
1653                     connect();
1654 
1655                 if (cachedInputStream != null) {
1656                     return cachedInputStream;
1657                 }
1658 
1659                 // Check if URL should be metered
1660                 boolean meteredInput = ProgressMonitor.getDefault().shouldMeterInput(url, method);
1661 
1662                 if (meteredInput)   {
1663                     pi = new ProgressSource(url, method);
1664                     pi.beginTracking();
1665                 }
1666 
1667                 /* REMIND: This exists to fix the HttpsURLConnection subclass.
1668                  * Hotjava needs to run on JDK1.1FCS.  Do proper fix once a
1669                  * proper solution for SSL can be found.
1670                  */
1671                 ps = (PrintStream)http.getOutputStream();
1672 
1673                 if (!streaming()) {
1674                     writeRequests();
1675                 }
1676                 http.parseHTTP(responses, pi, this);
1677                 if (logger.isLoggable(PlatformLogger.Level.FINE)) {
1678                     logger.fine(responses.toString());
1679                 }
1680 
1681                 boolean b1 = responses.filterNTLMResponses("WWW-Authenticate");
1682                 boolean b2 = responses.filterNTLMResponses("Proxy-Authenticate");
1683                 if (b1 || b2) {
1684                     if (logger.isLoggable(PlatformLogger.Level.FINE)) {
1685                         logger.fine(">>>> Headers are filtered");
1686                         logger.fine(responses.toString());
1687                     }
1688                 }
1689 
1690                 inputStream = http.getInputStream();
1691 
1692                 respCode = getResponseCode();
1693                 if (respCode == -1) {
1694                     disconnectInternal();
1695                     throw new IOException ("Invalid Http response");
1696                 }
1697                 if (respCode == HTTP_PROXY_AUTH) {
1698                     if (streaming()) {
1699                         disconnectInternal();
1700                         throw new HttpRetryException (
1701                             RETRY_MSG1, HTTP_PROXY_AUTH);
1702                     }
1703 
1704                     // Read comments labeled "Failed Negotiate" for details.
1705                     boolean dontUseNegotiate = false;
1706                     Iterator<String> iter = responses.multiValueIterator("Proxy-Authenticate");
1707                     while (iter.hasNext()) {
1708                         String value = iter.next().trim();
1709                         if (value.equalsIgnoreCase("Negotiate") ||
1710                                 value.equalsIgnoreCase("Kerberos")) {
1711                             if (!inNegotiateProxy) {
1712                                 inNegotiateProxy = true;
1713                             } else {
1714                                 dontUseNegotiate = true;
1715                                 doingNTLMp2ndStage = false;
1716                                 proxyAuthentication = null;
1717                             }
1718                             break;
1719                         }
1720                     }
1721 
1722                     // changes: add a 3rd parameter to the constructor of
1723                     // AuthenticationHeader, so that NegotiateAuthentication.
1724                     // isSupported can be tested.
1725                     // The other 2 appearances of "new AuthenticationHeader" is
1726                     // altered in similar ways.
1727 
1728                     AuthenticationHeader authhdr = new AuthenticationHeader (
1729                             "Proxy-Authenticate",
1730                             responses,
1731                             new HttpCallerInfo(url,
1732                                                http.getProxyHostUsed(),
1733                                                http.getProxyPortUsed(),
1734                                                authenticator),
1735                             dontUseNegotiate,
1736                             disabledProxyingSchemes
1737                     );
1738 
1739                     if (!doingNTLMp2ndStage) {
1740                         proxyAuthentication =
1741                             resetProxyAuthentication(proxyAuthentication, authhdr);
1742                         if (proxyAuthentication != null) {
1743                             redirects++;
1744                             disconnectInternal();
1745                             continue;
1746                         }
1747                     } else {
1748                         /* in this case, only one header field will be present */
1749                         String raw = responses.findValue ("Proxy-Authenticate");
1750                         reset ();
1751                         if (!proxyAuthentication.setHeaders(this,
1752                                                         authhdr.headerParser(), raw)) {
1753                             disconnectInternal();
1754                             throw new IOException ("Authentication failure");
1755                         }
1756                         if (serverAuthentication != null && srvHdr != null &&
1757                                 !serverAuthentication.setHeaders(this,
1758                                                         srvHdr.headerParser(), raw)) {
1759                             disconnectInternal ();
1760                             throw new IOException ("Authentication failure");
1761                         }
1762                         authObj = null;
1763                         doingNTLMp2ndStage = false;
1764                         continue;
1765                     }
1766                 } else {
1767                     inNegotiateProxy = false;
1768                     doingNTLMp2ndStage = false;
1769                     if (!isUserProxyAuth)
1770                         requests.remove("Proxy-Authorization");
1771                 }
1772 
1773                 // cache proxy authentication info
1774                 if (proxyAuthentication != null) {
1775                     // cache auth info on success, domain header not relevant.
1776                     proxyAuthentication.addToCache();
1777                 }
1778 
1779                 if (respCode == HTTP_UNAUTHORIZED) {
1780                     if (streaming()) {
1781                         disconnectInternal();
1782                         throw new HttpRetryException (
1783                             RETRY_MSG2, HTTP_UNAUTHORIZED);
1784                     }
1785 
1786                     // Read comments labeled "Failed Negotiate" for details.
1787                     boolean dontUseNegotiate = false;
1788                     Iterator<String> iter = responses.multiValueIterator("WWW-Authenticate");
1789                     while (iter.hasNext()) {
1790                         String value = iter.next().trim();
1791                         if (value.equalsIgnoreCase("Negotiate") ||
1792                                 value.equalsIgnoreCase("Kerberos")) {
1793                             if (!inNegotiate) {
1794                                 inNegotiate = true;
1795                             } else {
1796                                 dontUseNegotiate = true;
1797                                 doingNTLM2ndStage = false;
1798                                 serverAuthentication = null;
1799                             }
1800                             break;
1801                         }
1802                     }
1803 
1804                     srvHdr = new AuthenticationHeader (
1805                             "WWW-Authenticate", responses,
1806                             new HttpCallerInfo(url, authenticator),
1807                             dontUseNegotiate
1808                     );
1809 
1810                     String raw = srvHdr.raw();
1811                     if (!doingNTLM2ndStage) {
1812                         if ((serverAuthentication != null)&&
1813                             serverAuthentication.getAuthScheme() != NTLM) {
1814                             if (serverAuthentication.isAuthorizationStale (raw)) {
1815                                 /* we can retry with the current credentials */
1816                                 disconnectWeb();
1817                                 redirects++;
1818                                 requests.set(serverAuthentication.getHeaderName(),
1819                                             serverAuthentication.getHeaderValue(url, method));
1820                                 currentServerCredentials = serverAuthentication;
1821                                 setCookieHeader();
1822                                 continue;
1823                             } else {
1824                                 serverAuthentication.removeFromCache();
1825                             }
1826                         }
1827                         serverAuthentication = getServerAuthentication(srvHdr);
1828                         currentServerCredentials = serverAuthentication;
1829 
1830                         if (serverAuthentication != null) {
1831                             disconnectWeb();
1832                             redirects++; // don't let things loop ad nauseum
1833                             setCookieHeader();
1834                             continue;
1835                         }
1836                     } else {
1837                         reset ();
1838                         /* header not used for ntlm */
1839                         if (!serverAuthentication.setHeaders(this, null, raw)) {
1840                             disconnectWeb();
1841                             throw new IOException ("Authentication failure");
1842                         }
1843                         doingNTLM2ndStage = false;
1844                         authObj = null;
1845                         setCookieHeader();
1846                         continue;
1847                     }
1848                 }
1849                 // cache server authentication info
1850                 if (serverAuthentication != null) {
1851                     // cache auth info on success
1852                     if (!(serverAuthentication instanceof DigestAuthentication) ||
1853                         (domain == null)) {
1854                         if (serverAuthentication instanceof BasicAuthentication) {
1855                             // check if the path is shorter than the existing entry
1856                             String npath = AuthenticationInfo.reducePath (url.getPath());
1857                             String opath = serverAuthentication.path;
1858                             if (!opath.startsWith (npath) || npath.length() >= opath.length()) {
1859                                 /* npath is longer, there must be a common root */
1860                                 npath = BasicAuthentication.getRootPath (opath, npath);
1861                             }
1862                             // remove the entry and create a new one
1863                             BasicAuthentication a =
1864                                 (BasicAuthentication) serverAuthentication.clone();
1865                             serverAuthentication.removeFromCache();
1866                             a.path = npath;
1867                             serverAuthentication = a;
1868                         }
1869                         serverAuthentication.addToCache();
1870                     } else {
1871                         // what we cache is based on the domain list in the request
1872                         DigestAuthentication srv = (DigestAuthentication)
1873                             serverAuthentication;
1874                         StringTokenizer tok = new StringTokenizer (domain," ");
1875                         String realm = srv.realm;
1876                         PasswordAuthentication pw = srv.pw;
1877                         digestparams = srv.params;
1878                         while (tok.hasMoreTokens()) {
1879                             String path = tok.nextToken();
1880                             try {
1881                                 /* path could be an abs_path or a complete URI */
1882                                 URL u = new URL (url, path);
1883                                 DigestAuthentication d = new DigestAuthentication (
1884                                                    false, u, realm, "Digest", pw,
1885                                                    digestparams, srv.authenticatorKey);
1886                                 d.addToCache ();
1887                             } catch (Exception e) {}
1888                         }
1889                     }
1890                 }
1891 
1892                 // some flags should be reset to its initialized form so that
1893                 // even after a redirect the necessary checks can still be
1894                 // preformed.
1895                 inNegotiate = false;
1896                 inNegotiateProxy = false;
1897 
1898                 //serverAuthentication = null;
1899                 doingNTLMp2ndStage = false;
1900                 doingNTLM2ndStage = false;
1901                 if (!isUserServerAuth)
1902                     requests.remove("Authorization");
1903                 if (!isUserProxyAuth)
1904                     requests.remove("Proxy-Authorization");
1905 
1906                 if (respCode == HTTP_OK) {
1907                     checkResponseCredentials (false);
1908                 } else {
1909                     needToCheck = false;
1910                 }
1911 
1912                 // a flag need to clean
1913                 needToCheck = true;
1914 
1915                 if (followRedirect()) {
1916                     /* if we should follow a redirect, then the followRedirects()
1917                      * method will disconnect() and re-connect us to the new
1918                      * location
1919                      */
1920                     redirects++;
1921 
1922                     // redirecting HTTP response may have set cookie, so
1923                     // need to re-generate request header
1924                     setCookieHeader();
1925 
1926                     continue;
1927                 }
1928 
1929                 try {
1930                     cl = Long.parseLong(responses.findValue("content-length"));
1931                 } catch (Exception exc) { };
1932 
1933                 if (method.equals("HEAD") || cl == 0 ||
1934                     respCode == HTTP_NOT_MODIFIED ||
1935                     respCode == HTTP_NO_CONTENT) {
1936 
1937                     if (pi != null) {
1938                         pi.finishTracking();
1939                         pi = null;
1940                     }
1941                     http.finished();
1942                     http = null;
1943                     inputStream = new EmptyInputStream();
1944                     connected = false;
1945                 }
1946 
1947                 if (respCode == 200 || respCode == 203 || respCode == 206 ||
1948                     respCode == 300 || respCode == 301 || respCode == 410) {
1949                     if (cacheHandler != null && getUseCaches()) {
1950                         // give cache a chance to save response in cache
1951                         URI uri = ParseUtil.toURI(url);
1952                         if (uri != null) {
1953                             URLConnection uconn = this;
1954                             if ("https".equalsIgnoreCase(uri.getScheme())) {
1955                                 try {
1956                                 // use reflection to get to the public
1957                                 // HttpsURLConnection instance saved in
1958                                 // DelegateHttpsURLConnection
1959                                 uconn = (URLConnection)this.getClass().getField("httpsURLConnection").get(this);
1960                                 } catch (IllegalAccessException |
1961                                          NoSuchFieldException e) {
1962                                     // ignored; use 'this'
1963                                 }
1964                             }
1965                             CacheRequest cacheRequest =
1966                                 cacheHandler.put(uri, uconn);
1967                             if (cacheRequest != null && http != null) {
1968                                 http.setCacheRequest(cacheRequest);
1969                                 inputStream = new HttpInputStream(inputStream, cacheRequest);
1970                             }
1971                         }
1972                     }
1973                 }
1974 
1975                 if (!(inputStream instanceof HttpInputStream)) {
1976                     inputStream = new HttpInputStream(inputStream);
1977                 }
1978 
1979                 if (respCode >= 400) {
1980                     if (respCode == 404 || respCode == 410) {
1981                         throw new FileNotFoundException(url.toString());
1982                     } else {
1983                         throw new java.io.IOException("Server returned HTTP" +
1984                               " response code: " + respCode + " for URL: " +
1985                               url.toString());
1986                     }
1987                 }
1988                 poster = null;
1989                 strOutputStream = null;
1990                 return inputStream;
1991             } while (redirects < maxRedirects);
1992 
1993             throw new ProtocolException("Server redirected too many " +
1994                                         " times ("+ redirects + ")");
1995         } catch (RuntimeException e) {
1996             disconnectInternal();
1997             rememberedException = e;
1998             throw e;
1999         } catch (IOException e) {
2000             rememberedException = e;
2001 
2002             // buffer the error stream if bytes < 4k
2003             // and it can be buffered within 1 second
2004             String te = responses.findValue("Transfer-Encoding");
2005             if (http != null && http.isKeepingAlive() && enableESBuffer &&
2006                 (cl > 0 || (te != null && te.equalsIgnoreCase("chunked")))) {
2007                 errorStream = ErrorStream.getErrorStream(inputStream, cl, http);
2008             }
2009             throw e;
2010         } finally {
2011             if (proxyAuthKey != null) {
2012                 AuthenticationInfo.endAuthRequest(proxyAuthKey);
2013             }
2014             if (serverAuthKey != null) {
2015                 AuthenticationInfo.endAuthRequest(serverAuthKey);
2016             }
2017         }
2018     }
2019 
2020     /*
2021      * Creates a chained exception that has the same type as
2022      * original exception and with the same message. Right now,
2023      * there is no convenient APIs for doing so.
2024      */
2025     private IOException getChainedException(final IOException rememberedException) {
2026         try {
2027             final Object[] args = { rememberedException.getMessage() };
2028             IOException chainedException =
2029                 java.security.AccessController.doPrivileged(
2030                     new java.security.PrivilegedExceptionAction<>() {
2031                         public IOException run() throws Exception {
2032                             return (IOException)
2033                                 rememberedException.getClass()
2034                                 .getConstructor(new Class<?>[] { String.class })
2035                                 .newInstance(args);
2036                         }
2037                     });
2038             chainedException.initCause(rememberedException);
2039             return chainedException;
2040         } catch (Exception ignored) {
2041             return rememberedException;
2042         }
2043     }
2044 
2045     @Override
2046     public InputStream getErrorStream() {
2047         if (connected && responseCode >= 400) {
2048             // Client Error 4xx and Server Error 5xx
2049             if (errorStream != null) {
2050                 return errorStream;
2051             } else if (inputStream != null) {
2052                 return inputStream;
2053             }
2054         }
2055         return null;
2056     }
2057 
2058     /**
2059      * set or reset proxy authentication info in request headers
2060      * after receiving a 407 error. In the case of NTLM however,
2061      * receiving a 407 is normal and we just skip the stale check
2062      * because ntlm does not support this feature.
2063      */
2064     private AuthenticationInfo
2065         resetProxyAuthentication(AuthenticationInfo proxyAuthentication, AuthenticationHeader auth) throws IOException {
2066 
2067         // Only called from getInputStream0 and doTunneling0
2068         assert isLockHeldByCurrentThread();
2069 
2070         if ((proxyAuthentication != null )&&
2071              proxyAuthentication.getAuthScheme() != NTLM) {
2072             String raw = auth.raw();
2073             if (proxyAuthentication.isAuthorizationStale (raw)) {
2074                 /* we can retry with the current credentials */
2075                 String value;
2076                 if (proxyAuthentication instanceof DigestAuthentication) {
2077                     DigestAuthentication digestProxy = (DigestAuthentication)
2078                         proxyAuthentication;
2079                     if (tunnelState() == TunnelState.SETUP) {
2080                         value = digestProxy.getHeaderValue(connectRequestURI(url), HTTP_CONNECT);
2081                     } else {
2082                         value = digestProxy.getHeaderValue(getRequestURI(), method);
2083                     }
2084                 } else {
2085                     value = proxyAuthentication.getHeaderValue(url, method);
2086                 }
2087                 requests.set(proxyAuthentication.getHeaderName(), value);
2088                 currentProxyCredentials = proxyAuthentication;
2089                 return proxyAuthentication;
2090             } else {
2091                 proxyAuthentication.removeFromCache();
2092             }
2093         }
2094         proxyAuthentication = getHttpProxyAuthentication(auth);
2095         currentProxyCredentials = proxyAuthentication;
2096         return  proxyAuthentication;
2097     }
2098 
2099     /**
2100      * Returns the tunnel state.
2101      *
2102      * @return  the state
2103      */
2104     TunnelState tunnelState() {
2105         return tunnelState;
2106     }
2107 
2108     /**
2109      * Set the tunneling status.
2110      *
2111      * @param tunnelState the state
2112      */
2113     public void setTunnelState(TunnelState tunnelState) {
2114         this.tunnelState = tunnelState;
2115     }
2116 
2117     /**
2118      * establish a tunnel through proxy server
2119      */
2120     public void doTunneling() throws IOException {
2121         lock();
2122         try {
2123             doTunneling0();
2124         } finally{
2125             unlock();
2126         }
2127     }
2128 
2129     private void doTunneling0() throws IOException {
2130         int retryTunnel = 0;
2131         String statusLine = "";
2132         int respCode = 0;
2133         AuthenticationInfo proxyAuthentication = null;
2134         String proxyHost = null;
2135         int proxyPort = -1;
2136 
2137         assert isLockHeldByCurrentThread();
2138 
2139         // save current requests so that they can be restored after tunnel is setup.
2140         MessageHeader savedRequests = requests;
2141         requests = new MessageHeader();
2142 
2143         // Read comments labeled "Failed Negotiate" for details.
2144         boolean inNegotiateProxy = false;
2145 
2146         try {
2147             /* Actively setting up a tunnel */
2148             setTunnelState(TunnelState.SETUP);
2149 
2150             do {
2151                 if (!checkReuseConnection()) {
2152                     proxiedConnect(url, proxyHost, proxyPort, false);
2153                 }
2154                 // send the "CONNECT" request to establish a tunnel
2155                 // through proxy server
2156                 sendCONNECTRequest();
2157                 responses.reset();
2158 
2159                 // There is no need to track progress in HTTP Tunneling,
2160                 // so ProgressSource is null.
2161                 http.parseHTTP(responses, null, this);
2162 
2163                 /* Log the response to the CONNECT */
2164                 if (logger.isLoggable(PlatformLogger.Level.FINE)) {
2165                     logger.fine(responses.toString());
2166                 }
2167 
2168                 if (responses.filterNTLMResponses("Proxy-Authenticate")) {
2169                     if (logger.isLoggable(PlatformLogger.Level.FINE)) {
2170                         logger.fine(">>>> Headers are filtered");
2171                         logger.fine(responses.toString());
2172                     }
2173                 }
2174 
2175                 statusLine = responses.getValue(0);
2176                 StringTokenizer st = new StringTokenizer(statusLine);
2177                 st.nextToken();
2178                 respCode = Integer.parseInt(st.nextToken().trim());
2179                 if (respCode == HTTP_PROXY_AUTH) {
2180                     // Read comments labeled "Failed Negotiate" for details.
2181                     boolean dontUseNegotiate = false;
2182                     Iterator<String> iter = responses.multiValueIterator("Proxy-Authenticate");
2183                     while (iter.hasNext()) {
2184                         String value = iter.next().trim();
2185                         if (value.equalsIgnoreCase("Negotiate") ||
2186                                 value.equalsIgnoreCase("Kerberos")) {
2187                             if (!inNegotiateProxy) {
2188                                 inNegotiateProxy = true;
2189                             } else {
2190                                 dontUseNegotiate = true;
2191                                 doingNTLMp2ndStage = false;
2192                                 proxyAuthentication = null;
2193                             }
2194                             break;
2195                         }
2196                     }
2197 
2198                     AuthenticationHeader authhdr = new AuthenticationHeader(
2199                             "Proxy-Authenticate",
2200                             responses,
2201                             new HttpCallerInfo(url,
2202                                                http.getProxyHostUsed(),
2203                                                http.getProxyPortUsed(),
2204                                                authenticator),
2205                             dontUseNegotiate,
2206                             disabledTunnelingSchemes
2207                     );
2208                     if (!doingNTLMp2ndStage) {
2209                         proxyAuthentication =
2210                             resetProxyAuthentication(proxyAuthentication, authhdr);
2211                         if (proxyAuthentication != null) {
2212                             proxyHost = http.getProxyHostUsed();
2213                             proxyPort = http.getProxyPortUsed();
2214                             disconnectInternal();
2215                             retryTunnel++;
2216                             continue;
2217                         }
2218                     } else {
2219                         String raw = responses.findValue ("Proxy-Authenticate");
2220                         reset ();
2221                         if (!proxyAuthentication.setHeaders(this,
2222                                                 authhdr.headerParser(), raw)) {
2223                             disconnectInternal();
2224                             throw new IOException ("Authentication failure");
2225                         }
2226                         authObj = null;
2227                         doingNTLMp2ndStage = false;
2228                         continue;
2229                     }
2230                 }
2231                 // cache proxy authentication info
2232                 if (proxyAuthentication != null) {
2233                     // cache auth info on success, domain header not relevant.
2234                     proxyAuthentication.addToCache();
2235                 }
2236 
2237                 if (respCode == HTTP_OK) {
2238                     setTunnelState(TunnelState.TUNNELING);
2239                     break;
2240                 }
2241                 // we don't know how to deal with other response code
2242                 // so disconnect and report error
2243                 disconnectInternal();
2244                 setTunnelState(TunnelState.NONE);
2245                 break;
2246             } while (retryTunnel < maxRedirects);
2247 
2248             if (retryTunnel >= maxRedirects || (respCode != HTTP_OK)) {
2249                 if (respCode != HTTP_PROXY_AUTH) {
2250                     // remove all but authenticate responses
2251                     responses.reset();
2252                 }
2253                 throw new IOException("Unable to tunnel through proxy."+
2254                                       " Proxy returns \"" +
2255                                       statusLine + "\"");
2256             }
2257         } finally  {
2258             if (proxyAuthKey != null) {
2259                 AuthenticationInfo.endAuthRequest(proxyAuthKey);
2260             }
2261         }
2262 
2263         // restore original request headers
2264         requests = savedRequests;
2265 
2266         // reset responses
2267         responses.reset();
2268     }
2269 
2270     static String connectRequestURI(URL url) {
2271         String host = url.getHost();
2272         int port = url.getPort();
2273         port = port != -1 ? port : url.getDefaultPort();
2274 
2275         return host + ":" + port;
2276     }
2277 
2278     /**
2279      * send a CONNECT request for establishing a tunnel to proxy server
2280      */
2281     private void sendCONNECTRequest() throws IOException {
2282         int port = url.getPort();
2283 
2284         requests.set(0, HTTP_CONNECT + " " + connectRequestURI(url)
2285                          + " " + httpVersion, null);
2286         requests.setIfNotSet("User-Agent", userAgent);
2287 
2288         String host = url.getHost();
2289         if (port != -1 && port != url.getDefaultPort()) {
2290             host += ":" + String.valueOf(port);
2291         }
2292         requests.setIfNotSet("Host", host);
2293 
2294         // Not really necessary for a tunnel, but can't hurt
2295         requests.setIfNotSet("Accept", acceptString);
2296 
2297         if (http.getHttpKeepAliveSet()) {
2298             requests.setIfNotSet("Proxy-Connection", "keep-alive");
2299         }
2300 
2301         setPreemptiveProxyAuthentication(requests);
2302 
2303          /* Log the CONNECT request */
2304         if (logger.isLoggable(PlatformLogger.Level.FINE)) {
2305             logger.fine(requests.toString());
2306         }
2307 
2308         http.writeRequests(requests, null);
2309     }
2310 
2311     /**
2312      * Sets pre-emptive proxy authentication in header
2313      */
2314     private void setPreemptiveProxyAuthentication(MessageHeader requests) throws IOException {
2315         AuthenticationInfo pauth
2316             = AuthenticationInfo.getProxyAuth(http.getProxyHostUsed(),
2317                                               http.getProxyPortUsed(),
2318                                               getAuthenticatorKey());
2319         if (pauth != null && pauth.supportsPreemptiveAuthorization()) {
2320             String value;
2321             if (pauth instanceof DigestAuthentication) {
2322                 DigestAuthentication digestProxy = (DigestAuthentication) pauth;
2323                 if (tunnelState() == TunnelState.SETUP) {
2324                     value = digestProxy
2325                         .getHeaderValue(connectRequestURI(url), HTTP_CONNECT);
2326                 } else {
2327                     value = digestProxy.getHeaderValue(getRequestURI(), method);
2328                 }
2329             } else {
2330                 value = pauth.getHeaderValue(url, method);
2331             }
2332 
2333             // Sets "Proxy-authorization"
2334             requests.set(pauth.getHeaderName(), value);
2335             currentProxyCredentials = pauth;
2336         }
2337     }
2338 
2339     /**
2340      * Gets the authentication for an HTTP proxy, and applies it to
2341      * the connection.
2342      */
2343     @SuppressWarnings("fallthrough")
2344     private AuthenticationInfo getHttpProxyAuthentication(AuthenticationHeader authhdr) {
2345 
2346         assert isLockHeldByCurrentThread();
2347 
2348         /* get authorization from authenticator */
2349         AuthenticationInfo ret = null;
2350         String raw = authhdr.raw();
2351         String host = http.getProxyHostUsed();
2352         int port = http.getProxyPortUsed();
2353         if (host != null && authhdr.isPresent()) {
2354             HeaderParser p = authhdr.headerParser();
2355             String realm = p.findValue("realm");
2356             String charset = p.findValue("charset");
2357             boolean isUTF8 = (charset != null && charset.equalsIgnoreCase("UTF-8"));
2358             String scheme = authhdr.scheme();
2359             AuthScheme authScheme = UNKNOWN;
2360             if ("basic".equalsIgnoreCase(scheme)) {
2361                 authScheme = BASIC;
2362             } else if ("digest".equalsIgnoreCase(scheme)) {
2363                 authScheme = DIGEST;
2364             } else if ("ntlm".equalsIgnoreCase(scheme)) {
2365                 authScheme = NTLM;
2366                 doingNTLMp2ndStage = true;
2367             } else if ("Kerberos".equalsIgnoreCase(scheme)) {
2368                 authScheme = KERBEROS;
2369                 doingNTLMp2ndStage = true;
2370             } else if ("Negotiate".equalsIgnoreCase(scheme)) {
2371                 authScheme = NEGOTIATE;
2372                 doingNTLMp2ndStage = true;
2373             }
2374 
2375             if (realm == null)
2376                 realm = "";
2377             proxyAuthKey = AuthenticationInfo.getProxyAuthKey(host, port, realm,
2378                                 authScheme, getAuthenticatorKey());
2379             ret = AuthenticationInfo.getProxyAuth(proxyAuthKey);
2380             if (ret == null) {
2381                 switch (authScheme) {
2382                 case BASIC:
2383                     InetAddress addr = null;
2384                     try {
2385                         final String finalHost = host;
2386                         addr = java.security.AccessController.doPrivileged(
2387                             new java.security.PrivilegedExceptionAction<>() {
2388                                 public InetAddress run()
2389                                     throws java.net.UnknownHostException {
2390                                     return InetAddress.getByName(finalHost);
2391                                 }
2392                             });
2393                     } catch (java.security.PrivilegedActionException ignored) {
2394                         // User will have an unknown host.
2395                     }
2396                     PasswordAuthentication a =
2397                         privilegedRequestPasswordAuthentication(
2398                                     authenticator,
2399                                     host, addr, port, "http",
2400                                     realm, scheme, url, RequestorType.PROXY);
2401                     if (a != null) {
2402                         ret = new BasicAuthentication(true, host, port, realm, a,
2403                                              isUTF8, getAuthenticatorKey());
2404                     }
2405                     break;
2406                 case DIGEST:
2407                     a = privilegedRequestPasswordAuthentication(
2408                                     authenticator,
2409                                     host, null, port, url.getProtocol(),
2410                                     realm, scheme, url, RequestorType.PROXY);
2411                     if (a != null) {
2412                         DigestAuthentication.Parameters params =
2413                             new DigestAuthentication.Parameters();
2414                         ret = new DigestAuthentication(true, host, port, realm,
2415                                              scheme, a, params,
2416                                              getAuthenticatorKey());
2417                     }
2418                     break;
2419                 case NTLM:
2420                     if (NTLMAuthenticationProxy.supported) {
2421                         /* tryTransparentNTLMProxy will always be true the first
2422                          * time around, but verify that the platform supports it
2423                          * otherwise don't try. */
2424                         if (tryTransparentNTLMProxy) {
2425                             tryTransparentNTLMProxy =
2426                                     NTLMAuthenticationProxy.supportsTransparentAuth;
2427                             /* If the platform supports transparent authentication
2428                              * then normally it's ok to do transparent auth to a proxy
2429                              * because we generally trust proxies (chosen by the user)
2430                              * But not in the case of 305 response where the server
2431                              * chose it. */
2432                             if (tryTransparentNTLMProxy && useProxyResponseCode) {
2433                                 tryTransparentNTLMProxy = false;
2434                             }
2435 
2436                         }
2437                         a = null;
2438                         if (tryTransparentNTLMProxy) {
2439                             logger.finest("Trying Transparent NTLM authentication");
2440                         } else {
2441                             a = privilegedRequestPasswordAuthentication(
2442                                                 authenticator,
2443                                                 host, null, port, url.getProtocol(),
2444                                                 "", scheme, url, RequestorType.PROXY);
2445                         }
2446                         /* If we are not trying transparent authentication then
2447                          * we need to have a PasswordAuthentication instance. For
2448                          * transparent authentication (Windows only) the username
2449                          * and password will be picked up from the current logged
2450                          * on users credentials.
2451                          */
2452                         if (tryTransparentNTLMProxy ||
2453                               (!tryTransparentNTLMProxy && a != null)) {
2454                             ret = NTLMAuthenticationProxy.proxy.create(true, host,
2455                                     port, a, getAuthenticatorKey());
2456                         }
2457 
2458                         /* set to false so that we do not try again */
2459                         tryTransparentNTLMProxy = false;
2460                     }
2461                     break;
2462                 case NEGOTIATE:
2463                     ret = new NegotiateAuthentication(new HttpCallerInfo(authhdr.getHttpCallerInfo(), "Negotiate"));
2464                     break;
2465                 case KERBEROS:
2466                     ret = new NegotiateAuthentication(new HttpCallerInfo(authhdr.getHttpCallerInfo(), "Kerberos"));
2467                     break;
2468                 case UNKNOWN:
2469                     if (logger.isLoggable(PlatformLogger.Level.FINEST)) {
2470                         logger.finest("Unknown/Unsupported authentication scheme: " + scheme);
2471                     }
2472                 /*fall through*/
2473                 default:
2474                     throw new AssertionError("should not reach here");
2475                 }
2476             }
2477             // For backwards compatibility, we also try defaultAuth
2478             // REMIND:  Get rid of this for JDK2.0.
2479 
2480             if (ret == null && defaultAuth != null
2481                 && defaultAuth.schemeSupported(scheme)) {
2482                 try {
2483                     URL u = new URL("http", host, port, "/");
2484                     String a = defaultAuth.authString(u, scheme, realm);
2485                     if (a != null) {
2486                         ret = new BasicAuthentication (true, host, port, realm, a,
2487                                   getAuthenticatorKey());
2488                         // not in cache by default - cache on success
2489                     }
2490                 } catch (java.net.MalformedURLException ignored) {
2491                 }
2492             }
2493             if (ret != null) {
2494                 if (!ret.setHeaders(this, p, raw)) {
2495                     ret = null;
2496                 }
2497             }
2498         }
2499         if (logger.isLoggable(PlatformLogger.Level.FINER)) {
2500             logger.finer("Proxy Authentication for " + authhdr.toString() +" returned " + (ret != null ? ret.toString() : "null"));
2501         }
2502         return ret;
2503     }
2504 
2505     /**
2506      * Gets the authentication for an HTTP server, and applies it to
2507      * the connection.
2508      * @param authhdr the AuthenticationHeader which tells what auth scheme is
2509      * preferred.
2510      */
2511     @SuppressWarnings("fallthrough")
2512     private AuthenticationInfo getServerAuthentication(AuthenticationHeader authhdr) {
2513 
2514         // Only called from getInputStream0
2515         assert isLockHeldByCurrentThread();
2516 
2517         /* get authorization from authenticator */
2518         AuthenticationInfo ret = null;
2519         String raw = authhdr.raw();
2520         /* When we get an NTLM auth from cache, don't set any special headers */
2521         if (authhdr.isPresent()) {
2522             HeaderParser p = authhdr.headerParser();
2523             String realm = p.findValue("realm");
2524             String scheme = authhdr.scheme();
2525             String charset = p.findValue("charset");
2526             boolean isUTF8 = (charset != null && charset.equalsIgnoreCase("UTF-8"));
2527             AuthScheme authScheme = UNKNOWN;
2528             if ("basic".equalsIgnoreCase(scheme)) {
2529                 authScheme = BASIC;
2530             } else if ("digest".equalsIgnoreCase(scheme)) {
2531                 authScheme = DIGEST;
2532             } else if ("ntlm".equalsIgnoreCase(scheme)) {
2533                 authScheme = NTLM;
2534                 doingNTLM2ndStage = true;
2535             } else if ("Kerberos".equalsIgnoreCase(scheme)) {
2536                 authScheme = KERBEROS;
2537                 doingNTLM2ndStage = true;
2538             } else if ("Negotiate".equalsIgnoreCase(scheme)) {
2539                 authScheme = NEGOTIATE;
2540                 doingNTLM2ndStage = true;
2541             }
2542 
2543             domain = p.findValue ("domain");
2544             if (realm == null)
2545                 realm = "";
2546             serverAuthKey = AuthenticationInfo.getServerAuthKey(url, realm, authScheme,
2547                                                getAuthenticatorKey());
2548             ret = AuthenticationInfo.getServerAuth(serverAuthKey);
2549             InetAddress addr = null;
2550             if (ret == null) {
2551                 try {
2552                     addr = InetAddress.getByName(url.getHost());
2553                 } catch (java.net.UnknownHostException ignored) {
2554                     // User will have addr = null
2555                 }
2556             }
2557             // replacing -1 with default port for a protocol
2558             int port = url.getPort();
2559             if (port == -1) {
2560                 port = url.getDefaultPort();
2561             }
2562             if (ret == null) {
2563                 switch(authScheme) {
2564                 case KERBEROS:
2565                     ret = new NegotiateAuthentication(new HttpCallerInfo(authhdr.getHttpCallerInfo(), "Kerberos"));
2566                     break;
2567                 case NEGOTIATE:
2568                     ret = new NegotiateAuthentication(new HttpCallerInfo(authhdr.getHttpCallerInfo(), "Negotiate"));
2569                     break;
2570                 case BASIC:
2571                     PasswordAuthentication a =
2572                         privilegedRequestPasswordAuthentication(
2573                             authenticator,
2574                             url.getHost(), addr, port, url.getProtocol(),
2575                             realm, scheme, url, RequestorType.SERVER);
2576                     if (a != null) {
2577                         ret = new BasicAuthentication(false, url, realm, a,
2578                                     isUTF8, getAuthenticatorKey());
2579                     }
2580                     break;
2581                 case DIGEST:
2582                     a = privilegedRequestPasswordAuthentication(
2583                             authenticator,
2584                             url.getHost(), addr, port, url.getProtocol(),
2585                             realm, scheme, url, RequestorType.SERVER);
2586                     if (a != null) {
2587                         digestparams = new DigestAuthentication.Parameters();
2588                         ret = new DigestAuthentication(false, url, realm, scheme,
2589                                     a, digestparams,
2590                                     getAuthenticatorKey());
2591                     }
2592                     break;
2593                 case NTLM:
2594                     if (NTLMAuthenticationProxy.supported) {
2595                         URL url1;
2596                         try {
2597                             url1 = new URL (url, "/"); /* truncate the path */
2598                         } catch (Exception e) {
2599                             url1 = url;
2600                         }
2601 
2602                         /* tryTransparentNTLMServer will always be true the first
2603                          * time around, but verify that the platform supports it
2604                          * otherwise don't try. */
2605                         if (tryTransparentNTLMServer) {
2606                             tryTransparentNTLMServer =
2607                                     NTLMAuthenticationProxy.supportsTransparentAuth;
2608                             /* If the platform supports transparent authentication
2609                              * then check if we are in a secure environment
2610                              * whether, or not, we should try transparent authentication.*/
2611                             if (tryTransparentNTLMServer) {
2612                                 tryTransparentNTLMServer =
2613                                         NTLMAuthenticationProxy.isTrustedSite(url);
2614                             }
2615                         }
2616                         a = null;
2617                         if (tryTransparentNTLMServer) {
2618                             logger.finest("Trying Transparent NTLM authentication");
2619                         } else {
2620                             a = privilegedRequestPasswordAuthentication(
2621                                 authenticator,
2622                                 url.getHost(), addr, port, url.getProtocol(),
2623                                 "", scheme, url, RequestorType.SERVER);
2624                         }
2625 
2626                         /* If we are not trying transparent authentication then
2627                          * we need to have a PasswordAuthentication instance. For
2628                          * transparent authentication (Windows only) the username
2629                          * and password will be picked up from the current logged
2630                          * on users credentials.
2631                          */
2632                         if (tryTransparentNTLMServer ||
2633                               (!tryTransparentNTLMServer && a != null)) {
2634                             ret = NTLMAuthenticationProxy.proxy.create(false,
2635                                      url1, a, getAuthenticatorKey());
2636                         }
2637 
2638                         /* set to false so that we do not try again */
2639                         tryTransparentNTLMServer = false;
2640                     }
2641                     break;
2642                 case UNKNOWN:
2643                     if (logger.isLoggable(PlatformLogger.Level.FINEST)) {
2644                         logger.finest("Unknown/Unsupported authentication scheme: " + scheme);
2645                     }
2646                 /*fall through*/
2647                 default:
2648                     throw new AssertionError("should not reach here");
2649                 }
2650             }
2651 
2652             // For backwards compatibility, we also try defaultAuth
2653             // REMIND:  Get rid of this for JDK2.0.
2654 
2655             if (ret == null && defaultAuth != null
2656                 && defaultAuth.schemeSupported(scheme)) {
2657                 String a = defaultAuth.authString(url, scheme, realm);
2658                 if (a != null) {
2659                     ret = new BasicAuthentication (false, url, realm, a,
2660                                     getAuthenticatorKey());
2661                     // not in cache by default - cache on success
2662                 }
2663             }
2664 
2665             if (ret != null ) {
2666                 if (!ret.setHeaders(this, p, raw)) {
2667                     ret = null;
2668                 }
2669             }
2670         }
2671         if (logger.isLoggable(PlatformLogger.Level.FINER)) {
2672             logger.finer("Server Authentication for " + authhdr.toString() +" returned " + (ret != null ? ret.toString() : "null"));
2673         }
2674         return ret;
2675     }
2676 
2677     /* inclose will be true if called from close(), in which case we
2678      * force the call to check because this is the last chance to do so.
2679      * If not in close(), then the authentication info could arrive in a trailer
2680      * field, which we have not read yet.
2681      */
2682     private void checkResponseCredentials (boolean inClose) throws IOException {
2683         try {
2684             if (!needToCheck)
2685                 return;
2686             if ((validateProxy && currentProxyCredentials != null) &&
2687                 (currentProxyCredentials instanceof DigestAuthentication)) {
2688                 String raw = responses.findValue ("Proxy-Authentication-Info");
2689                 if (inClose || (raw != null)) {
2690                     DigestAuthentication da = (DigestAuthentication)
2691                         currentProxyCredentials;
2692                     da.checkResponse (raw, method, getRequestURI());
2693                     currentProxyCredentials = null;
2694                 }
2695             }
2696             if ((validateServer && currentServerCredentials != null) &&
2697                 (currentServerCredentials instanceof DigestAuthentication)) {
2698                 String raw = responses.findValue ("Authentication-Info");
2699                 if (inClose || (raw != null)) {
2700                     DigestAuthentication da = (DigestAuthentication)
2701                         currentServerCredentials;
2702                     da.checkResponse (raw, method, url);
2703                     currentServerCredentials = null;
2704                 }
2705             }
2706             if ((currentServerCredentials==null) && (currentProxyCredentials == null)) {
2707                 needToCheck = false;
2708             }
2709         } catch (IOException e) {
2710             disconnectInternal();
2711             connected = false;
2712             throw e;
2713         }
2714     }
2715 
2716    /* The request URI used in the request line for this request.
2717     * Also, needed for digest authentication
2718     */
2719 
2720     String requestURI = null;
2721 
2722     String getRequestURI() throws IOException {
2723         if (requestURI == null) {
2724             requestURI = http.getURLFile();
2725         }
2726         return requestURI;
2727     }
2728 
2729     /* Tells us whether to follow a redirect.  If so, it
2730      * closes the connection (break any keep-alive) and
2731      * resets the url, re-connects, and resets the request
2732      * property.
2733      */
2734     private boolean followRedirect() throws IOException {
2735         if (!getInstanceFollowRedirects()) {
2736             return false;
2737         }
2738 
2739         final int stat = getResponseCode();
2740         if (stat < 300 || stat > 307 || stat == 306
2741                                 || stat == HTTP_NOT_MODIFIED) {
2742             return false;
2743         }
2744         final String loc = getHeaderField("Location");
2745         if (loc == null) {
2746             /* this should be present - if not, we have no choice
2747              * but to go forward w/ the response we got
2748              */
2749             return false;
2750         }
2751 
2752         URL locUrl;
2753         try {
2754             locUrl = new URL(loc);
2755             if (!url.getProtocol().equalsIgnoreCase(locUrl.getProtocol())) {
2756                 return false;
2757             }
2758 
2759         } catch (MalformedURLException mue) {
2760           // treat loc as a relative URI to conform to popular browsers
2761           locUrl = new URL(url, loc);
2762         }
2763 
2764         final URL locUrl0 = locUrl;
2765         socketPermission = null; // force recalculation
2766         SocketPermission p = URLtoSocketPermission(locUrl);
2767 
2768         if (p != null) {
2769             try {
2770                 return AccessController.doPrivilegedWithCombiner(
2771                     new PrivilegedExceptionAction<>() {
2772                         public Boolean run() throws IOException {
2773                             return followRedirect0(loc, stat, locUrl0);
2774                         }
2775                     }, null, p
2776                 );
2777             } catch (PrivilegedActionException e) {
2778                 throw (IOException) e.getException();
2779             }
2780         } else {
2781             // run without additional permission
2782             return followRedirect0(loc, stat, locUrl);
2783         }
2784     }
2785 
2786     /* Tells us whether to follow a redirect.  If so, it
2787      * closes the connection (break any keep-alive) and
2788      * resets the url, re-connects, and resets the request
2789      * property.
2790      */
2791     private boolean followRedirect0(String loc, int stat, URL locUrl)
2792         throws IOException
2793     {
2794         assert isLockHeldByCurrentThread();
2795 
2796         disconnectInternal();
2797         if (streaming()) {
2798             throw new HttpRetryException (RETRY_MSG3, stat, loc);
2799         }
2800         if (logger.isLoggable(PlatformLogger.Level.FINE)) {
2801             logger.fine("Redirected from " + url + " to " + locUrl);
2802         }
2803 
2804         // clear out old response headers!!!!
2805         responses = new MessageHeader();
2806         if (stat == HTTP_USE_PROXY) {
2807             /* This means we must re-request the resource through the
2808              * proxy denoted in the "Location:" field of the response.
2809              * Judging by the spec, the string in the Location header
2810              * _should_ denote a URL - let's hope for "http://my.proxy.org"
2811              * Make a new HttpClient to the proxy, using HttpClient's
2812              * Instance-specific proxy fields, but note we're still fetching
2813              * the same URL.
2814              */
2815             String proxyHost = locUrl.getHost();
2816             int proxyPort = locUrl.getPort();
2817 
2818             SecurityManager security = System.getSecurityManager();
2819             if (security != null) {
2820                 security.checkConnect(proxyHost, proxyPort);
2821             }
2822 
2823             setProxiedClient (url, proxyHost, proxyPort);
2824             requests.set(0, method + " " + getRequestURI()+" "  +
2825                              httpVersion, null);
2826             connected = true;
2827             // need to remember this in case NTLM proxy authentication gets
2828             // used. We can't use transparent authentication when user
2829             // doesn't know about proxy.
2830             useProxyResponseCode = true;
2831         } else {
2832             final URL prevURL = url;
2833 
2834             // maintain previous headers, just change the name
2835             // of the file we're getting
2836             url = locUrl;
2837             requestURI = null; // force it to be recalculated
2838             if (method.equals("POST") && !Boolean.getBoolean("http.strictPostRedirect") && (stat!=307)) {
2839                 /* The HTTP/1.1 spec says that a redirect from a POST
2840                  * *should not* be immediately turned into a GET, and
2841                  * that some HTTP/1.0 clients incorrectly did this.
2842                  * Correct behavior redirects a POST to another POST.
2843                  * Unfortunately, since most browsers have this incorrect
2844                  * behavior, the web works this way now.  Typical usage
2845                  * seems to be:
2846                  *   POST a login code or passwd to a web page.
2847                  *   after validation, the server redirects to another
2848                  *     (welcome) page
2849                  *   The second request is (erroneously) expected to be GET
2850                  *
2851                  * We will do the incorrect thing (POST-->GET) by default.
2852                  * We will provide the capability to do the "right" thing
2853                  * (POST-->POST) by a system property, "http.strictPostRedirect=true"
2854                  */
2855 
2856                 requests = new MessageHeader();
2857                 setRequests = false;
2858                 super.setRequestMethod("GET"); // avoid the connecting check
2859                 poster = null;
2860                 if (!checkReuseConnection())
2861                     connect();
2862 
2863                 if (!sameDestination(prevURL, url)) {
2864                     // Ensures pre-redirect user-set cookie will not be reset.
2865                     // CookieHandler, if any, will be queried to determine
2866                     // cookies for redirected URL, if any.
2867                     userCookies = null;
2868                     userCookies2 = null;
2869                 }
2870             } else {
2871                 if (!checkReuseConnection())
2872                     connect();
2873                 /* Even after a connect() call, http variable still can be
2874                  * null, if a ResponseCache has been installed and it returns
2875                  * a non-null CacheResponse instance. So check nullity before using it.
2876                  *
2877                  * And further, if http is null, there's no need to do anything
2878                  * about request headers because successive http session will use
2879                  * cachedInputStream/cachedHeaders anyway, which is returned by
2880                  * CacheResponse.
2881                  */
2882                 if (http != null) {
2883                     requests.set(0, method + " " + getRequestURI()+" "  +
2884                                  httpVersion, null);
2885                     int port = url.getPort();
2886                     String host = stripIPv6ZoneId(url.getHost());
2887                     if (port != -1 && port != url.getDefaultPort()) {
2888                         host += ":" + String.valueOf(port);
2889                     }
2890                     requests.set("Host", host);
2891                 }
2892 
2893                 if (!sameDestination(prevURL, url)) {
2894                     // Redirecting to a different destination will drop any
2895                     // security-sensitive headers, regardless of whether
2896                     // they are user-set or not. CookieHandler, if any, will be
2897                     // queried to determine cookies for redirected URL, if any.
2898                     userCookies = null;
2899                     userCookies2 = null;
2900                     requests.remove("Cookie");
2901                     requests.remove("Cookie2");
2902                     requests.remove("Authorization");
2903 
2904                     // check for preemptive authorization
2905                     AuthenticationInfo sauth =
2906                             AuthenticationInfo.getServerAuth(url, getAuthenticatorKey());
2907                     if (sauth != null && sauth.supportsPreemptiveAuthorization() ) {
2908                         // Sets "Authorization"
2909                         requests.setIfNotSet(sauth.getHeaderName(), sauth.getHeaderValue(url,method));
2910                         currentServerCredentials = sauth;
2911                     }
2912                 }
2913             }
2914         }
2915         return true;
2916     }
2917 
2918     /* Returns true iff the given URLs have the same host and effective port. */
2919     private static boolean sameDestination(URL firstURL, URL secondURL) {
2920         assert firstURL.getProtocol().equalsIgnoreCase(secondURL.getProtocol()):
2921                "protocols not equal: " + firstURL +  " - " + secondURL;
2922 
2923         if (!firstURL.getHost().equalsIgnoreCase(secondURL.getHost()))
2924             return false;
2925 
2926         int firstPort = firstURL.getPort();
2927         if (firstPort == -1)
2928             firstPort = firstURL.getDefaultPort();
2929         int secondPort = secondURL.getPort();
2930         if (secondPort == -1)
2931             secondPort = secondURL.getDefaultPort();
2932         if (firstPort != secondPort)
2933             return false;
2934 
2935         return true;
2936     }
2937 
2938     /* dummy byte buffer for reading off socket prior to closing */
2939     byte[] cdata = new byte [128];
2940 
2941     /**
2942      * Reset (without disconnecting the TCP conn) in order to do another transaction with this instance
2943      */
2944     private void reset() throws IOException {
2945         http.reuse = true;
2946         /* must save before calling close */
2947         reuseClient = http;
2948         InputStream is = http.getInputStream();
2949         if (!method.equals("HEAD")) {
2950             try {
2951                 /* we want to read the rest of the response without using the
2952                  * hurry mechanism, because that would close the connection
2953                  * if everything is not available immediately
2954                  */
2955                 if ((is instanceof ChunkedInputStream) ||
2956                     (is instanceof MeteredStream)) {
2957                     /* reading until eof will not block */
2958                     while (is.read (cdata) > 0) {}
2959                 } else {
2960                     /* raw stream, which will block on read, so only read
2961                      * the expected number of bytes, probably 0
2962                      */
2963                     long cl = 0;
2964                     int n = 0;
2965                     String cls = responses.findValue ("Content-Length");
2966                     if (cls != null) {
2967                         try {
2968                             cl = Long.parseLong (cls);
2969                         } catch (NumberFormatException e) {
2970                             cl = 0;
2971                         }
2972                     }
2973                     for (long i=0; i<cl; ) {
2974                         if ((n = is.read (cdata)) == -1) {
2975                             break;
2976                         } else {
2977                             i+= n;
2978                         }
2979                     }
2980                 }
2981             } catch (IOException e) {
2982                 http.reuse = false;
2983                 reuseClient = null;
2984                 disconnectInternal();
2985                 return;
2986             }
2987             try {
2988                 if (is instanceof MeteredStream) {
2989                     is.close();
2990                 }
2991             } catch (IOException e) { }
2992         }
2993         responseCode = -1;
2994         responses = new MessageHeader();
2995         connected = false;
2996     }
2997 
2998     /**
2999      * Disconnect from the web server at the first 401 error. Do not
3000      * disconnect when using a proxy, a good proxy should have already
3001      * closed the connection to the web server.
3002      */
3003     private void disconnectWeb() throws IOException {
3004         if (usingProxyInternal() && http.isKeepingAlive()) {
3005             responseCode = -1;
3006             // clean up, particularly, skip the content part
3007             // of a 401 error response
3008             reset();
3009         } else {
3010             disconnectInternal();
3011         }
3012     }
3013 
3014     /**
3015      * Disconnect from the server (for internal use)
3016      */
3017     private void disconnectInternal() {
3018         responseCode = -1;
3019         inputStream = null;
3020         if (pi != null) {
3021             pi.finishTracking();
3022             pi = null;
3023         }
3024         if (http != null) {
3025             http.closeServer();
3026             http = null;
3027             connected = false;
3028         }
3029     }
3030 
3031     /**
3032      * Disconnect from the server (public API)
3033      */
3034     public void disconnect() {
3035 
3036         responseCode = -1;
3037         if (pi != null) {
3038             pi.finishTracking();
3039             pi = null;
3040         }
3041 
3042         if (http != null) {
3043             /*
3044              * If we have an input stream this means we received a response
3045              * from the server. That stream may have been read to EOF and
3046              * depending on the stream type may already be closed or
3047              * the http client may be returned to the keep-alive cache.
3048              * If the http client has been returned to the keep-alive cache
3049              * it may be closed (idle timeout) or may be allocated to
3050              * another request.
3051              *
3052              * In other to avoid timing issues we close the input stream
3053              * which will either close the underlying connection or return
3054              * the client to the cache. If there's a possibility that the
3055              * client has been returned to the cache (ie: stream is a keep
3056              * alive stream or a chunked input stream) then we remove an
3057              * idle connection to the server. Note that this approach
3058              * can be considered an approximation in that we may close a
3059              * different idle connection to that used by the request.
3060              * Additionally it's possible that we close two connections
3061              * - the first becuase it wasn't an EOF (and couldn't be
3062              * hurried) - the second, another idle connection to the
3063              * same server. The is okay because "disconnect" is an
3064              * indication that the application doesn't intend to access
3065              * this http server for a while.
3066              */
3067 
3068             if (inputStream != null) {
3069                 HttpClient hc = http;
3070 
3071                 // un-synchronized
3072                 boolean ka = hc.isKeepingAlive();
3073 
3074                 try {
3075                     inputStream.close();
3076                 } catch (IOException ioe) { }
3077 
3078                 // if the connection is persistent it may have been closed
3079                 // or returned to the keep-alive cache. If it's been returned
3080                 // to the keep-alive cache then we would like to close it
3081                 // but it may have been allocated
3082 
3083                 if (ka) {
3084                     hc.closeIdleConnection();
3085                 }
3086 
3087 
3088             } else {
3089                 // We are deliberatly being disconnected so HttpClient
3090                 // should not try to resend the request no matter what stage
3091                 // of the connection we are in.
3092                 http.setDoNotRetry(true);
3093 
3094                 http.closeServer();
3095             }
3096 
3097             //      poster = null;
3098             http = null;
3099             connected = false;
3100         }
3101         cachedInputStream = null;
3102         if (cachedHeaders != null) {
3103             cachedHeaders.reset();
3104         }
3105     }
3106 
3107     /**
3108      * Returns true only if the established connection is using a proxy
3109      */
3110     boolean usingProxyInternal() {
3111         if (http != null) {
3112             return (http.getProxyHostUsed() != null);
3113         }
3114         return false;
3115     }
3116 
3117     /**
3118      * Returns true if the established connection is using a proxy
3119      * or if a proxy is specified for the inactive connection
3120      */
3121     @Override
3122     public boolean usingProxy() {
3123         if (usingProxy || usingProxyInternal())
3124             return true;
3125 
3126         if (instProxy != null)
3127             return instProxy.type().equals(Proxy.Type.HTTP);
3128 
3129         return false;
3130     }
3131 
3132     // constant strings represent set-cookie header names
3133     private static final String SET_COOKIE = "set-cookie";
3134     private static final String SET_COOKIE2 = "set-cookie2";
3135 
3136     /**
3137      * Returns a filtered version of the given headers value.
3138      *
3139      * Note: The implementation currently only filters out HttpOnly cookies
3140      *       from Set-Cookie and Set-Cookie2 headers.
3141      */
3142     private String filterHeaderField(String name, String value) {
3143         if (value == null)
3144             return null;
3145 
3146         if (SET_COOKIE.equalsIgnoreCase(name) ||
3147             SET_COOKIE2.equalsIgnoreCase(name)) {
3148 
3149             // Filtering only if there is a cookie handler. [Assumption: the
3150             // cookie handler will store/retrieve the HttpOnly cookies]
3151             if (cookieHandler == null || value.isEmpty())
3152                 return value;
3153 
3154             JavaNetHttpCookieAccess access =
3155                     SharedSecrets.getJavaNetHttpCookieAccess();
3156             StringJoiner retValue = new StringJoiner(",");  // RFC 2965, comma separated
3157             List<HttpCookie> cookies = access.parse(value);
3158             for (HttpCookie cookie : cookies) {
3159                 // skip HttpOnly cookies
3160                 if (!cookie.isHttpOnly())
3161                     retValue.add(access.header(cookie));
3162             }
3163             return retValue.toString();
3164         }
3165 
3166         return value;
3167     }
3168 
3169     // Cache the filtered response headers so that they don't need
3170     // to be generated for every getHeaderFields() call.
3171     private Map<String, List<String>> filteredHeaders;  // null
3172 
3173     private Map<String, List<String>> getFilteredHeaderFields() {
3174         if (filteredHeaders != null)
3175             return filteredHeaders;
3176 
3177         Map<String, List<String>> headers, tmpMap = new HashMap<>();
3178 
3179         if (cachedHeaders != null)
3180             headers = cachedHeaders.getHeaders();
3181         else
3182             headers = responses.getHeaders();
3183 
3184         for (Map.Entry<String, List<String>> e: headers.entrySet()) {
3185             String key = e.getKey();
3186             List<String> values = e.getValue(), filteredVals = new ArrayList<>();
3187             for (String value : values) {
3188                 String fVal = filterHeaderField(key, value);
3189                 if (fVal != null)
3190                     filteredVals.add(fVal);
3191             }
3192             if (!filteredVals.isEmpty())
3193                 tmpMap.put(key, Collections.unmodifiableList(filteredVals));
3194         }
3195 
3196         return filteredHeaders = Collections.unmodifiableMap(tmpMap);
3197     }
3198 
3199     /**
3200      * Gets a header field by name. Returns null if not known.
3201      * @param name the name of the header field
3202      */
3203     @Override
3204     public String getHeaderField(String name) {
3205         try {
3206             getInputStream();
3207         } catch (IOException e) {}
3208 
3209         if (cachedHeaders != null) {
3210             return filterHeaderField(name, cachedHeaders.findValue(name));
3211         }
3212 
3213         return filterHeaderField(name, responses.findValue(name));
3214     }
3215 
3216     /**
3217      * Returns an unmodifiable Map of the header fields.
3218      * The Map keys are Strings that represent the
3219      * response-header field names. Each Map value is an
3220      * unmodifiable List of Strings that represents
3221      * the corresponding field values.
3222      *
3223      * @return a Map of header fields
3224      * @since 1.4
3225      */
3226     @Override
3227     public Map<String, List<String>> getHeaderFields() {
3228         try {
3229             getInputStream();
3230         } catch (IOException e) {}
3231 
3232         return getFilteredHeaderFields();
3233     }
3234 
3235     /**
3236      * Gets a header field by index. Returns null if not known.
3237      * @param n the index of the header field
3238      */
3239     @Override
3240     public String getHeaderField(int n) {
3241         try {
3242             getInputStream();
3243         } catch (IOException e) {}
3244 
3245         if (cachedHeaders != null) {
3246            return filterHeaderField(cachedHeaders.getKey(n),
3247                                     cachedHeaders.getValue(n));
3248         }
3249         return filterHeaderField(responses.getKey(n), responses.getValue(n));
3250     }
3251 
3252     /**
3253      * Gets a header field by index. Returns null if not known.
3254      * @param n the index of the header field
3255      */
3256     @Override
3257     public String getHeaderFieldKey(int n) {
3258         try {
3259             getInputStream();
3260         } catch (IOException e) {}
3261 
3262         if (cachedHeaders != null) {
3263             return cachedHeaders.getKey(n);
3264         }
3265 
3266         return responses.getKey(n);
3267     }
3268 
3269     /**
3270      * Sets request property. If a property with the key already
3271      * exists, overwrite its value with the new value.
3272      * @param value the value to be set
3273      */
3274     @Override
3275     public void setRequestProperty(String key, String value) {
3276         lock();
3277         try {
3278             if (connected || connecting)
3279                 throw new IllegalStateException("Already connected");
3280             if (key == null)
3281                 throw new NullPointerException("key is null");
3282 
3283             if (isExternalMessageHeaderAllowed(key, value)) {
3284                 requests.set(key, value);
3285                 if (!key.equalsIgnoreCase("Content-Type")) {
3286                     userHeaders.set(key, value);
3287                 }
3288             }
3289         } finally {
3290             unlock();
3291         }
3292     }
3293 
3294     MessageHeader getUserSetHeaders() {
3295         return userHeaders;
3296     }
3297 
3298     /**
3299      * Adds a general request property specified by a
3300      * key-value pair.  This method will not overwrite
3301      * existing values associated with the same key.
3302      *
3303      * @param   key     the keyword by which the request is known
3304      *                  (e.g., "<code>accept</code>").
3305      * @param   value  the value associated with it.
3306      * @see #getRequestProperty(java.lang.String)
3307      * @since 1.4
3308      */
3309     @Override
3310     public void addRequestProperty(String key, String value) {
3311         lock();
3312         try {
3313             if (connected || connecting)
3314                 throw new IllegalStateException("Already connected");
3315             if (key == null)
3316                 throw new NullPointerException("key is null");
3317 
3318             if (isExternalMessageHeaderAllowed(key, value)) {
3319                 requests.add(key, value);
3320                 if (!key.equalsIgnoreCase("Content-Type")) {
3321                     userHeaders.add(key, value);
3322                 }
3323             }
3324         } finally {
3325             unlock();
3326         }
3327     }
3328 
3329     //
3330     // Set a property for authentication.  This can safely disregard
3331     // the connected test.
3332     //
3333     public void setAuthenticationProperty(String key, String value) {
3334         // Only called by the implementation of AuthenticationInfo::setHeaders(...)
3335         // in AuthenticationInfo subclasses, which is only called from
3336         // methods from HttpURLConnection protected by the connectionLock.
3337         assert isLockHeldByCurrentThread();
3338 
3339         checkMessageHeader(key, value);
3340         requests.set(key, value);
3341     }
3342 
3343     @Override
3344     public String getRequestProperty (String key) {
3345         lock();
3346         try {
3347             if (key == null) {
3348                 return null;
3349             }
3350 
3351             // don't return headers containing security sensitive information
3352             for (int i = 0; i < EXCLUDE_HEADERS.length; i++) {
3353                 if (key.equalsIgnoreCase(EXCLUDE_HEADERS[i])) {
3354                     return null;
3355                 }
3356             }
3357             if (!setUserCookies) {
3358                 if (key.equalsIgnoreCase("Cookie")) {
3359                     return userCookies;
3360                 }
3361                 if (key.equalsIgnoreCase("Cookie2")) {
3362                     return userCookies2;
3363                 }
3364             }
3365             return requests.findValue(key);
3366         } finally {
3367             unlock();
3368         }
3369     }
3370 
3371     /**
3372      * Returns an unmodifiable Map of general request
3373      * properties for this connection. The Map keys
3374      * are Strings that represent the request-header
3375      * field names. Each Map value is a unmodifiable List
3376      * of Strings that represents the corresponding
3377      * field values.
3378      *
3379      * @return  a Map of the general request properties for this connection.
3380      * @throws IllegalStateException if already connected
3381      * @since 1.4
3382      */
3383     @Override
3384     public Map<String, List<String>> getRequestProperties() {
3385         lock();
3386         try {
3387             if (connected)
3388                 throw new IllegalStateException("Already connected");
3389 
3390             // exclude headers containing security-sensitive info
3391             if (setUserCookies) {
3392                 return requests.getHeaders(EXCLUDE_HEADERS);
3393             }
3394             /*
3395              * The cookies in the requests message headers may have
3396              * been modified. Use the saved user cookies instead.
3397              */
3398             Map<String, List<String>> userCookiesMap = null;
3399             if (userCookies != null || userCookies2 != null) {
3400                 userCookiesMap = new HashMap<>();
3401                 if (userCookies != null) {
3402                     userCookiesMap.put("Cookie", Arrays.asList(userCookies));
3403                 }
3404                 if (userCookies2 != null) {
3405                     userCookiesMap.put("Cookie2", Arrays.asList(userCookies2));
3406                 }
3407             }
3408             return requests.filterAndAddHeaders(EXCLUDE_HEADERS2, userCookiesMap);
3409         } finally {
3410             unlock();
3411         }
3412     }
3413 
3414     @Override
3415     public void setConnectTimeout(int timeout) {
3416         if (timeout < 0)
3417             throw new IllegalArgumentException("timeouts can't be negative");
3418         connectTimeout = timeout;
3419     }
3420 
3421 
3422     /**
3423      * Returns setting for connect timeout.
3424      * <p>
3425      * 0 return implies that the option is disabled
3426      * (i.e., timeout of infinity).
3427      *
3428      * @return an <code>int</code> that indicates the connect timeout
3429      *         value in milliseconds
3430      * @see java.net.URLConnection#setConnectTimeout(int)
3431      * @see java.net.URLConnection#connect()
3432      * @since 1.5
3433      */
3434     @Override
3435     public int getConnectTimeout() {
3436         return (connectTimeout < 0 ? 0 : connectTimeout);
3437     }
3438 
3439     /**
3440      * Sets the read timeout to a specified timeout, in
3441      * milliseconds. A non-zero value specifies the timeout when
3442      * reading from Input stream when a connection is established to a
3443      * resource. If the timeout expires before there is data available
3444      * for read, a java.net.SocketTimeoutException is raised. A
3445      * timeout of zero is interpreted as an infinite timeout.
3446      *
3447      * <p> Some non-standard implementation of this method ignores the
3448      * specified timeout. To see the read timeout set, please call
3449      * getReadTimeout().
3450      *
3451      * @param timeout an <code>int</code> that specifies the timeout
3452      * value to be used in milliseconds
3453      * @throws IllegalArgumentException if the timeout parameter is negative
3454      *
3455      * @see java.net.URLConnection#getReadTimeout()
3456      * @see java.io.InputStream#read()
3457      * @since 1.5
3458      */
3459     @Override
3460     public void setReadTimeout(int timeout) {
3461         if (timeout < 0)
3462             throw new IllegalArgumentException("timeouts can't be negative");
3463         readTimeout = timeout;
3464     }
3465 
3466     /**
3467      * Returns setting for read timeout. 0 return implies that the
3468      * option is disabled (i.e., timeout of infinity).
3469      *
3470      * @return an <code>int</code> that indicates the read timeout
3471      *         value in milliseconds
3472      *
3473      * @see java.net.URLConnection#setReadTimeout(int)
3474      * @see java.io.InputStream#read()
3475      * @since 1.5
3476      */
3477     @Override
3478     public int getReadTimeout() {
3479         return readTimeout < 0 ? 0 : readTimeout;
3480     }
3481 
3482     public CookieHandler getCookieHandler() {
3483         return cookieHandler;
3484     }
3485 
3486     String getMethod() {
3487         return method;
3488     }
3489 
3490     private MessageHeader mapToMessageHeader(Map<String, List<String>> map) {
3491         MessageHeader headers = new MessageHeader();
3492         if (map == null || map.isEmpty()) {
3493             return headers;
3494         }
3495         for (Map.Entry<String, List<String>> entry : map.entrySet()) {
3496             String key = entry.getKey();
3497             List<String> values = entry.getValue();
3498             for (String value : values) {
3499                 if (key == null) {
3500                     headers.prepend(key, value);
3501                 } else {
3502                     headers.add(key, value);
3503                 }
3504             }
3505         }
3506         return headers;
3507     }
3508 
3509     /**
3510      * Returns the given host, without the IPv6 Zone Id, if present.
3511      * (e.g. [fe80::a00:27ff:aaaa:aaaa%eth0] -> [fe80::a00:27ff:aaaa:aaaa])
3512      *
3513      * @param host host address (not null, not empty)
3514      * @return host address without Zone Id
3515      */
3516     static String stripIPv6ZoneId(String host) {
3517         if (host.charAt(0) != '[') { // not an IPv6-literal
3518             return host;
3519         }
3520         int i = host.lastIndexOf('%');
3521         if (i == -1) { // doesn't contain zone_id
3522             return host;
3523         }
3524         return host.substring(0, i) + "]";
3525     }
3526 
3527     /* The purpose of this wrapper is just to capture the close() call
3528      * so we can check authentication information that may have
3529      * arrived in a Trailer field
3530      */
3531     class HttpInputStream extends FilterInputStream {
3532         private CacheRequest cacheRequest;
3533         private OutputStream outputStream;
3534         private boolean marked = false;
3535         private int inCache = 0;
3536         private int markCount = 0;
3537         private boolean closed;  // false
3538 
3539         public HttpInputStream (InputStream is) {
3540             super (is);
3541             this.cacheRequest = null;
3542             this.outputStream = null;
3543         }
3544 
3545         public HttpInputStream (InputStream is, CacheRequest cacheRequest) {
3546             super (is);
3547             this.cacheRequest = cacheRequest;
3548             try {
3549                 this.outputStream = cacheRequest.getBody();
3550             } catch (IOException ioex) {
3551                 this.cacheRequest.abort();
3552                 this.cacheRequest = null;
3553                 this.outputStream = null;
3554             }
3555         }
3556 
3557         /**
3558          * Marks the current position in this input stream. A subsequent
3559          * call to the <code>reset</code> method repositions this stream at
3560          * the last marked position so that subsequent reads re-read the same
3561          * bytes.
3562          * <p>
3563          * The <code>readlimit</code> argument tells this input stream to
3564          * allow that many bytes to be read before the mark position gets
3565          * invalidated.
3566          * <p>
3567          * This method simply performs <code>in.mark(readlimit)</code>.
3568          *
3569          * @param   readlimit   the maximum limit of bytes that can be read before
3570          *                      the mark position becomes invalid.
3571          * @see     java.io.FilterInputStream#in
3572          * @see     java.io.FilterInputStream#reset()
3573          */
3574         // safe to use synchronized here: super method is synchronized too
3575         // and involves no blocking operation; only mark & reset are
3576         // synchronized in the super class hierarchy.
3577         @Override
3578         public synchronized void mark(int readlimit) {
3579             super.mark(readlimit);
3580             if (cacheRequest != null) {
3581                 marked = true;
3582                 markCount = 0;
3583             }
3584         }
3585 
3586         /**
3587          * Repositions this stream to the position at the time the
3588          * <code>mark</code> method was last called on this input stream.
3589          * <p>
3590          * This method
3591          * simply performs <code>in.reset()</code>.
3592          * <p>
3593          * Stream marks are intended to be used in
3594          * situations where you need to read ahead a little to see what's in
3595          * the stream. Often this is most easily done by invoking some
3596          * general parser. If the stream is of the type handled by the
3597          * parse, it just chugs along happily. If the stream is not of
3598          * that type, the parser should toss an exception when it fails.
3599          * If this happens within readlimit bytes, it allows the outer
3600          * code to reset the stream and try another parser.
3601          *
3602          * @exception  IOException  if the stream has not been marked or if the
3603          *               mark has been invalidated.
3604          * @see        java.io.FilterInputStream#in
3605          * @see        java.io.FilterInputStream#mark(int)
3606          */
3607         // safe to use synchronized here: super method is synchronized too
3608         // and involves no blocking operation; only mark & reset are
3609         // synchronized in the super class hierarchy.
3610         @Override
3611         public synchronized void reset() throws IOException {
3612             super.reset();
3613             if (cacheRequest != null) {
3614                 marked = false;
3615                 inCache += markCount;
3616             }
3617         }
3618 
3619         private void ensureOpen() throws IOException {
3620             if (closed)
3621                 throw new IOException("stream is closed");
3622         }
3623 
3624         @Override
3625         public int read() throws IOException {
3626             ensureOpen();
3627             try {
3628                 byte[] b = new byte[1];
3629                 int ret = read(b);
3630                 return (ret == -1? ret : (b[0] & 0x00FF));
3631             } catch (IOException ioex) {
3632                 if (cacheRequest != null) {
3633                     cacheRequest.abort();
3634                 }
3635                 throw ioex;
3636             }
3637         }
3638 
3639         @Override
3640         public int read(byte[] b) throws IOException {
3641             return read(b, 0, b.length);
3642         }
3643 
3644         @Override
3645         public int read(byte[] b, int off, int len) throws IOException {
3646             ensureOpen();
3647             try {
3648                 int newLen = super.read(b, off, len);
3649                 int nWrite;
3650                 // write to cache
3651                 if (inCache > 0) {
3652                     if (inCache >= newLen) {
3653                         inCache -= newLen;
3654                         nWrite = 0;
3655                     } else {
3656                         nWrite = newLen - inCache;
3657                         inCache = 0;
3658                     }
3659                 } else {
3660                     nWrite = newLen;
3661                 }
3662                 if (nWrite > 0 && outputStream != null)
3663                     outputStream.write(b, off + (newLen-nWrite), nWrite);
3664                 if (marked) {
3665                     markCount += newLen;
3666                 }
3667                 return newLen;
3668             } catch (IOException ioex) {
3669                 if (cacheRequest != null) {
3670                     cacheRequest.abort();
3671                 }
3672                 throw ioex;
3673             }
3674         }
3675 
3676         /* skip() calls read() in order to ensure that entire response gets
3677          * cached. same implementation as InputStream.skip */
3678 
3679         private byte[] skipBuffer;
3680         private static final int SKIP_BUFFER_SIZE = 8096;
3681 
3682         @Override
3683         public long skip (long n) throws IOException {
3684             ensureOpen();
3685             long remaining = n;
3686             int nr;
3687             if (skipBuffer == null)
3688                 skipBuffer = new byte[SKIP_BUFFER_SIZE];
3689 
3690             byte[] localSkipBuffer = skipBuffer;
3691 
3692             if (n <= 0) {
3693                 return 0;
3694             }
3695 
3696             while (remaining > 0) {
3697                 nr = read(localSkipBuffer, 0,
3698                           (int) Math.min(SKIP_BUFFER_SIZE, remaining));
3699                 if (nr < 0) {
3700                     break;
3701                 }
3702                 remaining -= nr;
3703             }
3704 
3705             return n - remaining;
3706         }
3707 
3708         @Override
3709         public void close () throws IOException {
3710             if (closed)
3711                 return;
3712 
3713             try {
3714                 if (outputStream != null) {
3715                     if (read() != -1) {
3716                         cacheRequest.abort();
3717                     } else {
3718                         outputStream.close();
3719                     }
3720                 }
3721                 super.close ();
3722             } catch (IOException ioex) {
3723                 if (cacheRequest != null) {
3724                     cacheRequest.abort();
3725                 }
3726                 throw ioex;
3727             } finally {
3728                 closed = true;
3729                 HttpURLConnection.this.http = null;
3730                 checkResponseCredentials (true);
3731             }
3732         }
3733     }
3734 
3735     class StreamingOutputStream extends FilterOutputStream {
3736 
3737         long expected;
3738         long written;
3739         boolean closed;
3740         boolean error;
3741         IOException errorExcp;
3742 
3743         /**
3744          * expectedLength == -1 if the stream is chunked
3745          * expectedLength > 0 if the stream is fixed content-length
3746          *    In the 2nd case, we make sure the expected number
3747          *    of bytes are actually written
3748          */
3749         StreamingOutputStream (OutputStream os, long expectedLength) {
3750             super (os);
3751             expected = expectedLength;
3752             written = 0L;
3753             closed = false;
3754             error = false;
3755         }
3756 
3757         @Override
3758         public void write (int b) throws IOException {
3759             checkError();
3760             written ++;
3761             if (expected != -1L && written > expected) {
3762                 throw new IOException ("too many bytes written");
3763             }
3764             out.write (b);
3765         }
3766 
3767         @Override
3768         public void write (byte[] b) throws IOException {
3769             write (b, 0, b.length);
3770         }
3771 
3772         @Override
3773         public void write (byte[] b, int off, int len) throws IOException {
3774             checkError();
3775             written += len;
3776             if (expected != -1L && written > expected) {
3777                 out.close ();
3778                 throw new IOException ("too many bytes written");
3779             }
3780             out.write (b, off, len);
3781         }
3782 
3783         void checkError () throws IOException {
3784             if (closed) {
3785                 throw new IOException ("Stream is closed");
3786             }
3787             if (error) {
3788                 throw errorExcp;
3789             }
3790             if (out instanceof PrintStream) {
3791                 if (((PrintStream) out).checkError()) {
3792                     throw new IOException("Error writing request body to server");
3793                 }
3794             } else if (out instanceof ChunkedOutputStream) {
3795                 if (((ChunkedOutputStream) out).checkError()) {
3796                     throw new IOException("Error writing request body to server");
3797                 }
3798             }
3799         }
3800 
3801         /* this is called to check that all the bytes
3802          * that were supposed to be written were written
3803          * and that the stream is now closed().
3804          */
3805         boolean writtenOK () {
3806             return closed && ! error;
3807         }
3808 
3809         @Override
3810         public void close () throws IOException {
3811             if (closed) {
3812                 return;
3813             }
3814             closed = true;
3815             if (expected != -1L) {
3816                 /* not chunked */
3817                 if (written != expected) {
3818                     error = true;
3819                     errorExcp = new IOException ("insufficient data written");
3820                     out.close ();
3821                     throw errorExcp;
3822                 }
3823                 super.flush(); /* can't close the socket */
3824             } else {
3825                 /* chunked */
3826                 super.close (); /* force final chunk to be written */
3827                 /* trailing \r\n */
3828                 OutputStream o = http.getOutputStream();
3829                 o.write ('\r');
3830                 o.write ('\n');
3831                 o.flush();
3832             }
3833         }
3834     }
3835 
3836 
3837     static class ErrorStream extends InputStream {
3838         ByteBuffer buffer;
3839         InputStream is;
3840 
3841         private ErrorStream(ByteBuffer buf) {
3842             buffer = buf;
3843             is = null;
3844         }
3845 
3846         private ErrorStream(ByteBuffer buf, InputStream is) {
3847             buffer = buf;
3848             this.is = is;
3849         }
3850 
3851         // when this method is called, it's either the case that cl > 0, or
3852         // if chunk-encoded, cl = -1; in other words, cl can't be 0
3853         public static InputStream getErrorStream(InputStream is, long cl, HttpClient http) {
3854 
3855             // cl can't be 0; this following is here for extra precaution
3856             if (cl == 0) {
3857                 return null;
3858             }
3859 
3860             try {
3861                 // set SO_TIMEOUT to 1/5th of the total timeout
3862                 // remember the old timeout value so that we can restore it
3863                 int oldTimeout = http.getReadTimeout();
3864                 http.setReadTimeout(timeout4ESBuffer/5);
3865 
3866                 long expected = 0;
3867                 boolean isChunked = false;
3868                 // the chunked case
3869                 if (cl < 0) {
3870                     expected = bufSize4ES;
3871                     isChunked = true;
3872                 } else {
3873                     expected = cl;
3874                 }
3875                 if (expected <= bufSize4ES) {
3876                     int exp = (int) expected;
3877                     byte[] buffer = new byte[exp];
3878                     int count = 0, time = 0, len = 0;
3879                     do {
3880                         try {
3881                             len = is.read(buffer, count,
3882                                              buffer.length - count);
3883                             if (len < 0) {
3884                                 if (isChunked) {
3885                                     // chunked ended
3886                                     // if chunked ended prematurely,
3887                                     // an IOException would be thrown
3888                                     break;
3889                                 }
3890                                 // the server sends less than cl bytes of data
3891                                 throw new IOException("the server closes"+
3892                                                       " before sending "+cl+
3893                                                       " bytes of data");
3894                             }
3895                             count += len;
3896                         } catch (SocketTimeoutException ex) {
3897                             time += timeout4ESBuffer/5;
3898                         }
3899                     } while (count < exp && time < timeout4ESBuffer);
3900 
3901                     // reset SO_TIMEOUT to old value
3902                     http.setReadTimeout(oldTimeout);
3903 
3904                     // if count < cl at this point, we will not try to reuse
3905                     // the connection
3906                     if (count == 0) {
3907                         // since we haven't read anything,
3908                         // we will return the underlying
3909                         // inputstream back to the application
3910                         return null;
3911                     }  else if ((count == expected && !(isChunked)) || (isChunked && len <0)) {
3912                         // put the connection into keep-alive cache
3913                         // the inputstream will try to do the right thing
3914                         is.close();
3915                         return new ErrorStream(ByteBuffer.wrap(buffer, 0, count));
3916                     } else {
3917                         // we read part of the response body
3918                         return new ErrorStream(
3919                                       ByteBuffer.wrap(buffer, 0, count), is);
3920                     }
3921                 }
3922                 return null;
3923             } catch (IOException ioex) {
3924                 // ioex.printStackTrace();
3925                 return null;
3926             }
3927         }
3928 
3929         @Override
3930         public int available() throws IOException {
3931             if (is == null) {
3932                 return buffer.remaining();
3933             } else {
3934                 return buffer.remaining()+is.available();
3935             }
3936         }
3937 
3938         public int read() throws IOException {
3939             byte[] b = new byte[1];
3940             int ret = read(b);
3941             return (ret == -1? ret : (b[0] & 0x00FF));
3942         }
3943 
3944         @Override
3945         public int read(byte[] b) throws IOException {
3946             return read(b, 0, b.length);
3947         }
3948 
3949         @Override
3950         public int read(byte[] b, int off, int len) throws IOException {
3951             int rem = buffer.remaining();
3952             if (rem > 0) {
3953                 int ret = rem < len? rem : len;
3954                 buffer.get(b, off, ret);
3955                 return ret;
3956             } else {
3957                 if (is == null) {
3958                     return -1;
3959                 } else {
3960                     return is.read(b, off, len);
3961                 }
3962             }
3963         }
3964 
3965         @Override
3966         public void close() throws IOException {
3967             buffer = null;
3968             if (is != null) {
3969                 is.close();
3970             }
3971         }
3972     }
3973 }
3974 
3975 /** An input stream that just returns EOF.  This is for
3976  * HTTP URLConnections that are KeepAlive && use the
3977  * HEAD method - i.e., stream not dead, but nothing to be read.
3978  */
3979 
3980 class EmptyInputStream extends InputStream {
3981 
3982     @Override
3983     public int available() {
3984         return 0;
3985     }
3986 
3987     public int read() {
3988         return -1;
3989     }
3990 }
3991