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