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