1 /* 2 * Copyright (c) 1996, 2010, 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 java.net; 27 28 import java.io.InputStream; 29 import java.io.IOException; 30 import java.security.Permission; 31 import java.util.Date; 32 33 /** 34 * A URLConnection with support for HTTP-specific features. See 35 * <A HREF="http://www.w3.org/pub/WWW/Protocols/"> the spec </A> for 36 * details. 37 * <p> 38 * 39 * Each HttpURLConnection instance is used to make a single request 40 * but the underlying network connection to the HTTP server may be 41 * transparently shared by other instances. Calling the close() methods 42 * on the InputStream or OutputStream of an HttpURLConnection 43 * after a request may free network resources associated with this 44 * instance but has no effect on any shared persistent connection. 45 * Calling the disconnect() method may close the underlying socket 46 * if a persistent connection is otherwise idle at that time. 47 * 48 * @see java.net.HttpURLConnection#disconnect() 49 * @since JDK1.1 50 */ 51 abstract public class HttpURLConnection extends URLConnection { 52 /* instance variables */ 53 54 /** 55 * The HTTP method (GET,POST,PUT,etc.). 56 */ 57 protected String method = "GET"; 58 59 /** 60 * The chunk-length when using chunked encoding streaming mode for output. 61 * A value of <code>-1</code> means chunked encoding is disabled for output. 62 * @since 1.5 63 */ 64 protected int chunkLength = -1; 65 66 /** 67 * The fixed content-length when using fixed-length streaming mode. 68 * A value of <code>-1</code> means fixed-length streaming mode is disabled 69 * for output. 70 * @since 1.5 71 */ 72 protected int fixedContentLength = -1; 73 74 /** 75 * Returns the key for the <code>n</code><sup>th</sup> header field. 76 * Some implementations may treat the <code>0</code><sup>th</sup> 77 * header field as special, i.e. as the status line returned by the HTTP 78 * server. In this case, {@link #getHeaderField(int) getHeaderField(0)} returns the status 79 * line, but <code>getHeaderFieldKey(0)</code> returns null. 80 * 81 * @param n an index, where n >=0. 82 * @return the key for the <code>n</code><sup>th</sup> header field, 83 * or <code>null</code> if the key does not exist. 84 */ 85 public String getHeaderFieldKey (int n) { 86 return null; 87 } 88 89 /** 90 * This method is used to enable streaming of a HTTP request body 91 * without internal buffering, when the content length is known in 92 * advance. 93 * <p> 94 * An exception will be thrown if the application 95 * attempts to write more data than the indicated 96 * content-length, or if the application closes the OutputStream 97 * before writing the indicated amount. 98 * <p> 99 * When output streaming is enabled, authentication 100 * and redirection cannot be handled automatically. 101 * A HttpRetryException will be thrown when reading 102 * the response if authentication or redirection are required. 103 * This exception can be queried for the details of the error. 104 * <p> 105 * This method must be called before the URLConnection is connected. 106 * 107 * @param contentLength The number of bytes which will be written 108 * to the OutputStream. 109 * 110 * @throws IllegalStateException if URLConnection is already connected 111 * or if a different streaming mode is already enabled. 112 * 113 * @throws IllegalArgumentException if a content length less than 114 * zero is specified. 115 * 116 * @see #setChunkedStreamingMode(int) 117 * @since 1.5 118 */ 119 public void setFixedLengthStreamingMode (int contentLength) { 120 if (connected) { 121 throw new IllegalStateException ("Already connected"); 122 } 123 if (chunkLength != -1) { 124 throw new IllegalStateException ("Chunked encoding streaming mode set"); 125 } 126 if (contentLength < 0) { 127 throw new IllegalArgumentException ("invalid content length"); 128 } 129 fixedContentLength = contentLength; 130 } 131 132 /* Default chunk size (including chunk header) if not specified; 133 * we want to keep this in sync with the one defined in 134 * sun.net.www.http.ChunkedOutputStream 135 */ 136 private static final int DEFAULT_CHUNK_SIZE = 4096; 137 138 /** 139 * This method is used to enable streaming of a HTTP request body 140 * without internal buffering, when the content length is <b>not</b> 141 * known in advance. In this mode, chunked transfer encoding 142 * is used to send the request body. Note, not all HTTP servers 143 * support this mode. 144 * <p> 145 * When output streaming is enabled, authentication 146 * and redirection cannot be handled automatically. 147 * A HttpRetryException will be thrown when reading 148 * the response if authentication or redirection are required. 149 * This exception can be queried for the details of the error. 150 * <p> 151 * This method must be called before the URLConnection is connected. 152 * 153 * @param chunklen The number of bytes to write in each chunk. 154 * If chunklen is less than or equal to zero, a default 155 * value will be used. 156 * 157 * @throws IllegalStateException if URLConnection is already connected 158 * or if a different streaming mode is already enabled. 159 * 160 * @see #setFixedLengthStreamingMode(int) 161 * @since 1.5 162 */ 163 public void setChunkedStreamingMode (int chunklen) { 164 if (connected) { 165 throw new IllegalStateException ("Can't set streaming mode: already connected"); 166 } 167 if (fixedContentLength != -1) { 168 throw new IllegalStateException ("Fixed length streaming mode set"); 169 } 170 chunkLength = chunklen <=0? DEFAULT_CHUNK_SIZE : chunklen; 171 } 172 173 /** 174 * Returns the value for the <code>n</code><sup>th</sup> header field. 175 * Some implementations may treat the <code>0</code><sup>th</sup> 176 * header field as special, i.e. as the status line returned by the HTTP 177 * server. 178 * <p> 179 * This method can be used in conjunction with the 180 * {@link #getHeaderFieldKey getHeaderFieldKey} method to iterate through all 181 * the headers in the message. 182 * 183 * @param n an index, where n>=0. 184 * @return the value of the <code>n</code><sup>th</sup> header field, 185 * or <code>null</code> if the value does not exist. 186 * @see java.net.HttpURLConnection#getHeaderFieldKey(int) 187 */ 188 public String getHeaderField(int n) { 189 return null; 190 } 191 192 /** 193 * An <code>int</code> representing the three digit HTTP Status-Code. 194 * <ul> 195 * <li> 1xx: Informational 196 * <li> 2xx: Success 197 * <li> 3xx: Redirection 198 * <li> 4xx: Client Error 199 * <li> 5xx: Server Error 200 * </ul> 201 */ 202 protected int responseCode = -1; 203 204 /** 205 * The HTTP response message. 206 */ 207 protected String responseMessage = null; 208 209 /* static variables */ 210 211 /* do we automatically follow redirects? The default is true. */ 212 private static boolean followRedirects = true; 213 214 /** 215 * If <code>true</code>, the protocol will automatically follow redirects. 216 * If <code>false</code>, the protocol will not automatically follow 217 * redirects. 218 * <p> 219 * This field is set by the <code>setInstanceFollowRedirects</code> 220 * method. Its value is returned by the <code>getInstanceFollowRedirects</code> 221 * method. 222 * <p> 223 * Its default value is based on the value of the static followRedirects 224 * at HttpURLConnection construction time. 225 * 226 * @see java.net.HttpURLConnection#setInstanceFollowRedirects(boolean) 227 * @see java.net.HttpURLConnection#getInstanceFollowRedirects() 228 * @see java.net.HttpURLConnection#setFollowRedirects(boolean) 229 */ 230 protected boolean instanceFollowRedirects = followRedirects; 231 232 /* valid HTTP methods */ 233 private static final String[] methods = { 234 "GET", "POST", "HEAD", "OPTIONS", "PUT", "DELETE", "TRACE" 235 }; 236 237 /** 238 * Constructor for the HttpURLConnection. 239 * @param u the URL 240 */ 241 protected HttpURLConnection (URL u) { 242 super(u); 243 } 244 245 /** 246 * Sets whether HTTP redirects (requests with response code 3xx) should 247 * be automatically followed by this class. True by default. Applets 248 * cannot change this variable. 249 * <p> 250 * If there is a security manager, this method first calls 251 * the security manager's <code>checkSetFactory</code> method 252 * to ensure the operation is allowed. 253 * This could result in a SecurityException. 254 * 255 * @param set a <code>boolean</code> indicating whether or not 256 * to follow HTTP redirects. 257 * @exception SecurityException if a security manager exists and its 258 * <code>checkSetFactory</code> method doesn't 259 * allow the operation. 260 * @see SecurityManager#checkSetFactory 261 * @see #getFollowRedirects() 262 */ 263 public static void setFollowRedirects(boolean set) { 264 SecurityManager sec = System.getSecurityManager(); 265 if (sec != null) { 266 // seems to be the best check here... 267 sec.checkSetFactory(); 268 } 269 followRedirects = set; 270 } 271 272 /** 273 * Returns a <code>boolean</code> indicating 274 * whether or not HTTP redirects (3xx) should 275 * be automatically followed. 276 * 277 * @return <code>true</code> if HTTP redirects should 278 * be automatically followed, <tt>false</tt> if not. 279 * @see #setFollowRedirects(boolean) 280 */ 281 public static boolean getFollowRedirects() { 282 return followRedirects; 283 } 284 285 /** 286 * Sets whether HTTP redirects (requests with response code 3xx) should 287 * be automatically followed by this <code>HttpURLConnection</code> 288 * instance. 289 * <p> 290 * The default value comes from followRedirects, which defaults to 291 * true. 292 * 293 * @param followRedirects a <code>boolean</code> indicating 294 * whether or not to follow HTTP redirects. 295 * 296 * @see java.net.HttpURLConnection#instanceFollowRedirects 297 * @see #getInstanceFollowRedirects 298 * @since 1.3 299 */ 300 public void setInstanceFollowRedirects(boolean followRedirects) { 301 instanceFollowRedirects = followRedirects; 302 } 303 304 /** 305 * Returns the value of this <code>HttpURLConnection</code>'s 306 * <code>instanceFollowRedirects</code> field. 307 * 308 * @return the value of this <code>HttpURLConnection</code>'s 309 * <code>instanceFollowRedirects</code> field. 310 * @see java.net.HttpURLConnection#instanceFollowRedirects 311 * @see #setInstanceFollowRedirects(boolean) 312 * @since 1.3 313 */ 314 public boolean getInstanceFollowRedirects() { 315 return instanceFollowRedirects; 316 } 317 318 /** 319 * Set the method for the URL request, one of: 320 * <UL> 321 * <LI>GET 322 * <LI>POST 323 * <LI>HEAD 324 * <LI>OPTIONS 325 * <LI>PUT 326 * <LI>DELETE 327 * <LI>TRACE 328 * </UL> are legal, subject to protocol restrictions. The default 329 * method is GET. 330 * 331 * @param method the HTTP method 332 * @exception ProtocolException if the method cannot be reset or if 333 * the requested method isn't valid for HTTP. 334 * @see #getRequestMethod() 335 */ 336 public void setRequestMethod(String method) throws ProtocolException { 337 if (connected) { 338 throw new ProtocolException("Can't reset method: already connected"); 339 } 340 // This restriction will prevent people from using this class to 341 // experiment w/ new HTTP methods using java. But it should 342 // be placed for security - the request String could be 343 // arbitrarily long. 344 345 for (int i = 0; i < methods.length; i++) { 346 if (methods[i].equals(method)) { 347 if (method.equals("TRACE")) { 348 SecurityManager s = System.getSecurityManager(); 349 if (s != null) { 350 s.checkPermission(new NetPermission("allowHttpTrace")); 351 } 352 } 353 this.method = method; 354 return; 355 } 356 } 357 throw new ProtocolException("Invalid HTTP method: " + method); 358 } 359 360 /** 361 * Get the request method. 362 * @return the HTTP request method 363 * @see #setRequestMethod(java.lang.String) 364 */ 365 public String getRequestMethod() { 366 return method; 367 } 368 369 /** 370 * Gets the status code from an HTTP response message. 371 * For example, in the case of the following status lines: 372 * <PRE> 373 * HTTP/1.0 200 OK 374 * HTTP/1.0 401 Unauthorized 375 * </PRE> 376 * It will return 200 and 401 respectively. 377 * Returns -1 if no code can be discerned 378 * from the response (i.e., the response is not valid HTTP). 379 * @throws IOException if an error occurred connecting to the server. 380 * @return the HTTP Status-Code, or -1 381 */ 382 public int getResponseCode() throws IOException { 383 /* 384 * We're got the response code already 385 */ 386 if (responseCode != -1) { 387 return responseCode; 388 } 389 390 /* 391 * Ensure that we have connected to the server. Record 392 * exception as we need to re-throw it if there isn't 393 * a status line. 394 */ 395 Exception exc = null; 396 try { 397 getInputStream(); 398 } catch (Exception e) { 399 exc = e; 400 } 401 402 /* 403 * If we can't a status-line then re-throw any exception 404 * that getInputStream threw. 405 */ 406 String statusLine = getHeaderField(0); 407 if (statusLine == null) { 408 if (exc != null) { 409 if (exc instanceof RuntimeException) 410 throw (RuntimeException)exc; 411 else 412 throw (IOException)exc; 413 } 414 return -1; 415 } 416 417 /* 418 * Examine the status-line - should be formatted as per 419 * section 6.1 of RFC 2616 :- 420 * 421 * Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase 422 * 423 * If status line can't be parsed return -1. 424 */ 425 if (statusLine.startsWith("HTTP/1.")) { 426 int codePos = statusLine.indexOf(' '); 427 if (codePos > 0) { 428 429 int phrasePos = statusLine.indexOf(' ', codePos+1); 430 if (phrasePos > 0 && phrasePos < statusLine.length()) { 431 responseMessage = statusLine.substring(phrasePos+1); 432 } 433 434 // deviation from RFC 2616 - don't reject status line 435 // if SP Reason-Phrase is not included. 436 if (phrasePos < 0) 437 phrasePos = statusLine.length(); 438 439 try { 440 responseCode = Integer.parseInt 441 (statusLine.substring(codePos+1, phrasePos)); 442 return responseCode; 443 } catch (NumberFormatException e) { } 444 } 445 } 446 return -1; 447 } 448 449 /** 450 * Gets the HTTP response message, if any, returned along with the 451 * response code from a server. From responses like: 452 * <PRE> 453 * HTTP/1.0 200 OK 454 * HTTP/1.0 404 Not Found 455 * </PRE> 456 * Extracts the Strings "OK" and "Not Found" respectively. 457 * Returns null if none could be discerned from the responses 458 * (the result was not valid HTTP). 459 * @throws IOException if an error occurred connecting to the server. 460 * @return the HTTP response message, or <code>null</code> 461 */ 462 public String getResponseMessage() throws IOException { 463 getResponseCode(); 464 return responseMessage; 465 } 466 467 @SuppressWarnings("deprecation") 468 public long getHeaderFieldDate(String name, long Default) { 469 String dateString = getHeaderField(name); 470 try { 471 if (dateString.indexOf("GMT") == -1) { 472 dateString = dateString+" GMT"; 473 } 474 return Date.parse(dateString); 475 } catch (Exception e) { 476 } 477 return Default; 478 } 479 480 481 /** 482 * Indicates that other requests to the server 483 * are unlikely in the near future. Calling disconnect() 484 * should not imply that this HttpURLConnection 485 * instance can be reused for other requests. 486 */ 487 public abstract void disconnect(); 488 489 /** 490 * Indicates if the connection is going through a proxy. 491 * @return a boolean indicating if the connection is 492 * using a proxy. 493 */ 494 public abstract boolean usingProxy(); 495 496 public Permission getPermission() throws IOException { 497 int port = url.getPort(); 498 port = port < 0 ? 80 : port; 499 String host = url.getHost() + ":" + port; 500 Permission permission = new SocketPermission(host, "connect"); 501 return permission; 502 } 503 504 /** 505 * Returns the error stream if the connection failed 506 * but the server sent useful data nonetheless. The 507 * typical example is when an HTTP server responds 508 * with a 404, which will cause a FileNotFoundException 509 * to be thrown in connect, but the server sent an HTML 510 * help page with suggestions as to what to do. 511 * 512 * <p>This method will not cause a connection to be initiated. If 513 * the connection was not connected, or if the server did not have 514 * an error while connecting or if the server had an error but 515 * no error data was sent, this method will return null. This is 516 * the default. 517 * 518 * @return an error stream if any, null if there have been no 519 * errors, the connection is not connected or the server sent no 520 * useful data. 521 */ 522 public InputStream getErrorStream() { 523 return null; 524 } 525 526 /** 527 * The response codes for HTTP, as of version 1.1. 528 */ 529 530 // REMIND: do we want all these?? 531 // Others not here that we do want?? 532 533 /* 2XX: generally "OK" */ 534 535 /** 536 * HTTP Status-Code 200: OK. 537 */ 538 public static final int HTTP_OK = 200; 539 540 /** 541 * HTTP Status-Code 201: Created. 542 */ 543 public static final int HTTP_CREATED = 201; 544 545 /** 546 * HTTP Status-Code 202: Accepted. 547 */ 548 public static final int HTTP_ACCEPTED = 202; 549 550 /** 551 * HTTP Status-Code 203: Non-Authoritative Information. 552 */ 553 public static final int HTTP_NOT_AUTHORITATIVE = 203; 554 555 /** 556 * HTTP Status-Code 204: No Content. 557 */ 558 public static final int HTTP_NO_CONTENT = 204; 559 560 /** 561 * HTTP Status-Code 205: Reset Content. 562 */ 563 public static final int HTTP_RESET = 205; 564 565 /** 566 * HTTP Status-Code 206: Partial Content. 567 */ 568 public static final int HTTP_PARTIAL = 206; 569 570 /* 3XX: relocation/redirect */ 571 572 /** 573 * HTTP Status-Code 300: Multiple Choices. 574 */ 575 public static final int HTTP_MULT_CHOICE = 300; 576 577 /** 578 * HTTP Status-Code 301: Moved Permanently. 579 */ 580 public static final int HTTP_MOVED_PERM = 301; 581 582 /** 583 * HTTP Status-Code 302: Temporary Redirect. 584 */ 585 public static final int HTTP_MOVED_TEMP = 302; 586 587 /** 588 * HTTP Status-Code 303: See Other. 589 */ 590 public static final int HTTP_SEE_OTHER = 303; 591 592 /** 593 * HTTP Status-Code 304: Not Modified. 594 */ 595 public static final int HTTP_NOT_MODIFIED = 304; 596 597 /** 598 * HTTP Status-Code 305: Use Proxy. 599 */ 600 public static final int HTTP_USE_PROXY = 305; 601 602 /* 4XX: client error */ 603 604 /** 605 * HTTP Status-Code 400: Bad Request. 606 */ 607 public static final int HTTP_BAD_REQUEST = 400; 608 609 /** 610 * HTTP Status-Code 401: Unauthorized. 611 */ 612 public static final int HTTP_UNAUTHORIZED = 401; 613 614 /** 615 * HTTP Status-Code 402: Payment Required. 616 */ 617 public static final int HTTP_PAYMENT_REQUIRED = 402; 618 619 /** 620 * HTTP Status-Code 403: Forbidden. 621 */ 622 public static final int HTTP_FORBIDDEN = 403; 623 624 /** 625 * HTTP Status-Code 404: Not Found. 626 */ 627 public static final int HTTP_NOT_FOUND = 404; 628 629 /** 630 * HTTP Status-Code 405: Method Not Allowed. 631 */ 632 public static final int HTTP_BAD_METHOD = 405; 633 634 /** 635 * HTTP Status-Code 406: Not Acceptable. 636 */ 637 public static final int HTTP_NOT_ACCEPTABLE = 406; 638 639 /** 640 * HTTP Status-Code 407: Proxy Authentication Required. 641 */ 642 public static final int HTTP_PROXY_AUTH = 407; 643 644 /** 645 * HTTP Status-Code 408: Request Time-Out. 646 */ 647 public static final int HTTP_CLIENT_TIMEOUT = 408; 648 649 /** 650 * HTTP Status-Code 409: Conflict. 651 */ 652 public static final int HTTP_CONFLICT = 409; 653 654 /** 655 * HTTP Status-Code 410: Gone. 656 */ 657 public static final int HTTP_GONE = 410; 658 659 /** 660 * HTTP Status-Code 411: Length Required. 661 */ 662 public static final int HTTP_LENGTH_REQUIRED = 411; 663 664 /** 665 * HTTP Status-Code 412: Precondition Failed. 666 */ 667 public static final int HTTP_PRECON_FAILED = 412; 668 669 /** 670 * HTTP Status-Code 413: Request Entity Too Large. 671 */ 672 public static final int HTTP_ENTITY_TOO_LARGE = 413; 673 674 /** 675 * HTTP Status-Code 414: Request-URI Too Large. 676 */ 677 public static final int HTTP_REQ_TOO_LONG = 414; 678 679 /** 680 * HTTP Status-Code 415: Unsupported Media Type. 681 */ 682 public static final int HTTP_UNSUPPORTED_TYPE = 415; 683 684 /* 5XX: server error */ 685 686 /** 687 * HTTP Status-Code 500: Internal Server Error. 688 * @deprecated it is misplaced and shouldn't have existed. 689 */ 690 @Deprecated 691 public static final int HTTP_SERVER_ERROR = 500; 692 693 /** 694 * HTTP Status-Code 500: Internal Server Error. 695 */ 696 public static final int HTTP_INTERNAL_ERROR = 500; 697 698 /** 699 * HTTP Status-Code 501: Not Implemented. 700 */ 701 public static final int HTTP_NOT_IMPLEMENTED = 501; 702 703 /** 704 * HTTP Status-Code 502: Bad Gateway. 705 */ 706 public static final int HTTP_BAD_GATEWAY = 502; 707 708 /** 709 * HTTP Status-Code 503: Service Unavailable. 710 */ 711 public static final int HTTP_UNAVAILABLE = 503; 712 713 /** 714 * HTTP Status-Code 504: Gateway Timeout. 715 */ 716 public static final int HTTP_GATEWAY_TIMEOUT = 504; 717 718 /** 719 * HTTP Status-Code 505: HTTP Version Not Supported. 720 */ 721 public static final int HTTP_VERSION = 505; 722 723 }