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