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