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 public long getHeaderFieldDate(String name, long Default) { 468 String dateString = getHeaderField(name); 469 try { 470 if (dateString.indexOf("GMT") == -1) { 471 dateString = dateString+" GMT"; 472 } 473 return Date.parse(dateString); 474 } catch (Exception e) { 475 } 476 return Default; 477 } 478 479 480 /** 481 * Indicates that other requests to the server 482 * are unlikely in the near future. Calling disconnect() 483 * should not imply that this HttpURLConnection 484 * instance can be reused for other requests. 485 */ 486 public abstract void disconnect(); 487 488 /** 489 * Indicates if the connection is going through a proxy. 490 * @return a boolean indicating if the connection is 491 * using a proxy. 492 */ 493 public abstract boolean usingProxy(); 494 495 public Permission getPermission() throws IOException { 496 int port = url.getPort(); 497 port = port < 0 ? 80 : port; 498 String host = url.getHost() + ":" + port; 499 Permission permission = new SocketPermission(host, "connect"); 500 return permission; 501 } 502 503 /** 504 * Returns the error stream if the connection failed 505 * but the server sent useful data nonetheless. The 506 * typical example is when an HTTP server responds 507 * with a 404, which will cause a FileNotFoundException 508 * to be thrown in connect, but the server sent an HTML 509 * help page with suggestions as to what to do. 510 * 511 * <p>This method will not cause a connection to be initiated. If 512 * the connection was not connected, or if the server did not have 513 * an error while connecting or if the server had an error but 514 * no error data was sent, this method will return null. This is 515 * the default. 516 * 517 * @return an error stream if any, null if there have been no 518 * errors, the connection is not connected or the server sent no 519 * useful data. 520 */ 521 public InputStream getErrorStream() { 522 return null; 523 } 524 525 /** 526 * The response codes for HTTP, as of version 1.1. 527 */ 528 529 // REMIND: do we want all these?? 530 // Others not here that we do want?? 531 532 /* 2XX: generally "OK" */ 533 534 /** 535 * HTTP Status-Code 200: OK. 536 */ 537 public static final int HTTP_OK = 200; 538 539 /** 540 * HTTP Status-Code 201: Created. 541 */ 542 public static final int HTTP_CREATED = 201; 543 544 /** 545 * HTTP Status-Code 202: Accepted. 546 */ 547 public static final int HTTP_ACCEPTED = 202; 548 549 /** 550 * HTTP Status-Code 203: Non-Authoritative Information. 551 */ 552 public static final int HTTP_NOT_AUTHORITATIVE = 203; 553 554 /** 555 * HTTP Status-Code 204: No Content. 556 */ 557 public static final int HTTP_NO_CONTENT = 204; 558 559 /** 560 * HTTP Status-Code 205: Reset Content. 561 */ 562 public static final int HTTP_RESET = 205; 563 564 /** 565 * HTTP Status-Code 206: Partial Content. 566 */ 567 public static final int HTTP_PARTIAL = 206; 568 569 /* 3XX: relocation/redirect */ 570 571 /** 572 * HTTP Status-Code 300: Multiple Choices. 573 */ 574 public static final int HTTP_MULT_CHOICE = 300; 575 576 /** 577 * HTTP Status-Code 301: Moved Permanently. 578 */ 579 public static final int HTTP_MOVED_PERM = 301; 580 581 /** 582 * HTTP Status-Code 302: Temporary Redirect. 583 */ 584 public static final int HTTP_MOVED_TEMP = 302; 585 586 /** 587 * HTTP Status-Code 303: See Other. 588 */ 589 public static final int HTTP_SEE_OTHER = 303; 590 591 /** 592 * HTTP Status-Code 304: Not Modified. 593 */ 594 public static final int HTTP_NOT_MODIFIED = 304; 595 596 /** 597 * HTTP Status-Code 305: Use Proxy. 598 */ 599 public static final int HTTP_USE_PROXY = 305; 600 601 /* 4XX: client error */ 602 603 /** 604 * HTTP Status-Code 400: Bad Request. 605 */ 606 public static final int HTTP_BAD_REQUEST = 400; 607 608 /** 609 * HTTP Status-Code 401: Unauthorized. 610 */ 611 public static final int HTTP_UNAUTHORIZED = 401; 612 613 /** 614 * HTTP Status-Code 402: Payment Required. 615 */ 616 public static final int HTTP_PAYMENT_REQUIRED = 402; 617 618 /** 619 * HTTP Status-Code 403: Forbidden. 620 */ 621 public static final int HTTP_FORBIDDEN = 403; 622 623 /** 624 * HTTP Status-Code 404: Not Found. 625 */ 626 public static final int HTTP_NOT_FOUND = 404; 627 628 /** 629 * HTTP Status-Code 405: Method Not Allowed. 630 */ 631 public static final int HTTP_BAD_METHOD = 405; 632 633 /** 634 * HTTP Status-Code 406: Not Acceptable. 635 */ 636 public static final int HTTP_NOT_ACCEPTABLE = 406; 637 638 /** 639 * HTTP Status-Code 407: Proxy Authentication Required. 640 */ 641 public static final int HTTP_PROXY_AUTH = 407; 642 643 /** 644 * HTTP Status-Code 408: Request Time-Out. 645 */ 646 public static final int HTTP_CLIENT_TIMEOUT = 408; 647 648 /** 649 * HTTP Status-Code 409: Conflict. 650 */ 651 public static final int HTTP_CONFLICT = 409; 652 653 /** 654 * HTTP Status-Code 410: Gone. 655 */ 656 public static final int HTTP_GONE = 410; 657 658 /** 659 * HTTP Status-Code 411: Length Required. 660 */ 661 public static final int HTTP_LENGTH_REQUIRED = 411; 662 663 /** 664 * HTTP Status-Code 412: Precondition Failed. 665 */ 666 public static final int HTTP_PRECON_FAILED = 412; 667 668 /** 669 * HTTP Status-Code 413: Request Entity Too Large. 670 */ 671 public static final int HTTP_ENTITY_TOO_LARGE = 413; 672 673 /** 674 * HTTP Status-Code 414: Request-URI Too Large. 675 */ 676 public static final int HTTP_REQ_TOO_LONG = 414; 677 678 /** 679 * HTTP Status-Code 415: Unsupported Media Type. 680 */ 681 public static final int HTTP_UNSUPPORTED_TYPE = 415; 682 683 /* 5XX: server error */ 684 685 /** 686 * HTTP Status-Code 500: Internal Server Error. 687 * @deprecated it is misplaced and shouldn't have existed. 688 */ 689 @Deprecated 690 public static final int HTTP_SERVER_ERROR = 500; 691 692 /** 693 * HTTP Status-Code 500: Internal Server Error. 694 */ 695 public static final int HTTP_INTERNAL_ERROR = 500; 696 697 /** 698 * HTTP Status-Code 501: Not Implemented. 699 */ 700 public static final int HTTP_NOT_IMPLEMENTED = 501; 701 702 /** 703 * HTTP Status-Code 502: Bad Gateway. 704 */ 705 public static final int HTTP_BAD_GATEWAY = 502; 706 707 /** 708 * HTTP Status-Code 503: Service Unavailable. 709 */ 710 public static final int HTTP_UNAVAILABLE = 503; 711 712 /** 713 * HTTP Status-Code 504: Gateway Timeout. 714 */ 715 public static final int HTTP_GATEWAY_TIMEOUT = 504; 716 717 /** 718 * HTTP Status-Code 505: HTTP Version Not Supported. 719 */ 720 public static final int HTTP_VERSION = 505; 721 722 }