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