001 /*
002 * Copyright 1996-2007 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025
026 package java.net;
027
028 import java.io.InputStream;
029 import java.io.IOException;
030 import java.security.Permission;
031 import java.util.Date;
032
033 /**
034 * A URLConnection with support for HTTP-specific features. See
035 * <A HREF="http://www.w3.org/pub/WWW/Protocols/"> the spec </A> for
036 * details.
037 * <p>
038 *
039 * Each HttpURLConnection instance is used to make a single request
040 * but the underlying network connection to the HTTP server may be
041 * transparently shared by other instances. Calling the close() methods
042 * on the InputStream or OutputStream of an HttpURLConnection
043 * after a request may free network resources associated with this
044 * instance but has no effect on any shared persistent connection.
045 * Calling the disconnect() method may close the underlying socket
046 * if a persistent connection is otherwise idle at that time.
047 *
048 * @see java.net.HttpURLConnection#disconnect()
049 * @since JDK1.1
050 */
051 abstract public class HttpURLConnection extends URLConnection {
052 /* instance variables */
053
054 /**
055 * The HTTP method (GET,POST,PUT,etc.).
056 */
057 protected String method = "GET";
058
059 /**
060 * The chunk-length when using chunked encoding streaming mode for output.
061 * A value of <code>-1</code> means chunked encoding is disabled for output.
062 * @since 1.5
063 */
064 protected int chunkLength = -1;
065
066 /**
067 * The fixed content-length when using fixed-length streaming mode.
068 * A value of <code>-1</code> means fixed-length streaming mode is disabled
069 * for output.
070 * @since 1.5
071 */
072 protected int fixedContentLength = -1;
073
074 /**
075 * Returns the key for the <code>n</code><sup>th</sup> header field.
076 * Some implementations may treat the <code>0</code><sup>th</sup>
077 * header field as special, i.e. as the status line returned by the HTTP
078 * server. In this case, {@link #getHeaderField(int) getHeaderField(0)} returns the status
079 * line, but <code>getHeaderFieldKey(0)</code> returns null.
080 *
081 * @param n an index, where n >=0.
082 * @return the key for the <code>n</code><sup>th</sup> header field,
083 * or <code>null</code> if the key does not exist.
084 */
085 public String getHeaderFieldKey(int n) {
086 return null;
087 }
088
089 /**
090 * This method is used to enable streaming of a HTTP request body
091 * without internal buffering, when the content length is known in
092 * advance.
093 * <p>
094 * An exception will be thrown if the application
095 * attempts to write more data than the indicated
096 * content-length, or if the application closes the OutputStream
097 * before writing the indicated amount.
098 * <p>
099 * 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(
125 "Chunked encoding streaming mode set");
126 }
127 if (contentLength < 0) {
128 throw new IllegalArgumentException("invalid content length");
129 }
130 fixedContentLength = contentLength;
131 }
132
133 /* Default chunk size (including chunk header) if not specified;
134 * we want to keep this in sync with the one defined in
135 * sun.net.www.http.ChunkedOutputStream
136 */
137 private static final int DEFAULT_CHUNK_SIZE = 4096;
138
139 /**
140 * This method is used to enable streaming of a HTTP request body
141 * without internal buffering, when the content length is <b>not</b>
142 * known in advance. In this mode, chunked transfer encoding
143 * is used to send the request body. Note, not all HTTP servers
144 * support this mode.
145 * <p>
146 * When output streaming is enabled, authentication
147 * and redirection cannot be handled automatically.
148 * A HttpRetryException will be thrown when reading
149 * the response if authentication or redirection are required.
150 * This exception can be queried for the details of the error.
151 * <p>
152 * This method must be called before the URLConnection is connected.
153 *
154 * @param chunklen The number of bytes to write in each chunk.
155 * If chunklen is less than or equal to zero, a default
156 * value will be used.
157 *
158 * @throws IllegalStateException if URLConnection is already connected
159 * or if a different streaming mode is already enabled.
160 *
161 * @see #setFixedLengthStreamingMode(int)
162 * @since 1.5
163 */
164 public void setChunkedStreamingMode(int chunklen) {
165 if (connected) {
166 throw new IllegalStateException(
167 "Can't set streaming mode: already connected");
168 }
169 if (fixedContentLength != -1) {
170 throw new IllegalStateException(
171 "Fixed length streaming mode set");
172 }
173 chunkLength = chunklen <= 0 ? DEFAULT_CHUNK_SIZE : chunklen;
174 }
175
176 /**
177 * Returns the value for the <code>n</code><sup>th</sup> header field.
178 * Some implementations may treat the <code>0</code><sup>th</sup>
179 * header field as special, i.e. as the status line returned by the HTTP
180 * server.
181 * <p>
182 * This method can be used in conjunction with the
183 * {@link #getHeaderFieldKey getHeaderFieldKey} method to iterate through all
184 * the headers in the message.
185 *
186 * @param n an index, where n>=0.
187 * @return the value of the <code>n</code><sup>th</sup> header field,
188 * or <code>null</code> if the value does not exist.
189 * @see java.net.HttpURLConnection#getHeaderFieldKey(int)
190 */
191 public String getHeaderField(int n) {
192 return null;
193 }
194
195 /**
196 * An <code>int</code> representing the three digit HTTP Status-Code.
197 * <ul>
198 * <li> 1xx: Informational
199 * <li> 2xx: Success
200 * <li> 3xx: Redirection
201 * <li> 4xx: Client Error
202 * <li> 5xx: Server Error
203 * </ul>
204 */
205 protected int responseCode = -1;
206
207 /**
208 * The HTTP response message.
209 */
210 protected String responseMessage = null;
211
212 /* static variables */
213
214 /* do we automatically follow redirects? The default is true. */
215 private static boolean followRedirects = true;
216
217 /**
218 * If <code>true</code>, the protocol will automatically follow redirects.
219 * If <code>false</code>, the protocol will not automatically follow
220 * redirects.
221 * <p>
222 * This field is set by the <code>setInstanceFollowRedirects</code>
223 * method. Its value is returned by the <code>getInstanceFollowRedirects</code>
224 * method.
225 * <p>
226 * Its default value is based on the value of the static followRedirects
227 * at HttpURLConnection construction time.
228 *
229 * @see java.net.HttpURLConnection#setInstanceFollowRedirects(boolean)
230 * @see java.net.HttpURLConnection#getInstanceFollowRedirects()
231 * @see java.net.HttpURLConnection#setFollowRedirects(boolean)
232 */
233 protected boolean instanceFollowRedirects = followRedirects;
234
235 /* valid HTTP methods */
236 private static final String[] methods = { "GET", "POST", "HEAD",
237 "OPTIONS", "PUT", "DELETE", "TRACE" };
238
239 /**
240 * Constructor for the HttpURLConnection.
241 * @param u the URL
242 */
243 protected HttpURLConnection(URL u) {
244 super (u);
245 }
246
247 /**
248 * Sets whether HTTP redirects (requests with response code 3xx) should
249 * be automatically followed by this class. True by default. Applets
250 * cannot change this variable.
251 * <p>
252 * If there is a security manager, this method first calls
253 * the security manager's <code>checkSetFactory</code> method
254 * to ensure the operation is allowed.
255 * This could result in a SecurityException.
256 *
257 * @param set a <code>boolean</code> indicating whether or not
258 * to follow HTTP redirects.
259 * @exception SecurityException if a security manager exists and its
260 * <code>checkSetFactory</code> method doesn't
261 * allow the operation.
262 * @see SecurityManager#checkSetFactory
263 * @see #getFollowRedirects()
264 */
265 public static void setFollowRedirects(boolean set) {
266 SecurityManager sec = System.getSecurityManager();
267 if (sec != null) {
268 // seems to be the best check here...
269 sec.checkSetFactory();
270 }
271 followRedirects = set;
272 }
273
274 /**
275 * Returns a <code>boolean</code> indicating
276 * whether or not HTTP redirects (3xx) should
277 * be automatically followed.
278 *
279 * @return <code>true</code> if HTTP redirects should
280 * be automatically followed, <tt>false</tt> if not.
281 * @see #setFollowRedirects(boolean)
282 */
283 public static boolean getFollowRedirects() {
284 return followRedirects;
285 }
286
287 /**
288 * Sets whether HTTP redirects (requests with response code 3xx) should
289 * be automatically followed by this <code>HttpURLConnection</code>
290 * instance.
291 * <p>
292 * The default value comes from followRedirects, which defaults to
293 * true.
294 *
295 * @param followRedirects a <code>boolean</code> indicating
296 * whether or not to follow HTTP redirects.
297 *
298 * @see java.net.HttpURLConnection#instanceFollowRedirects
299 * @see #getInstanceFollowRedirects
300 * @since 1.3
301 */
302 public void setInstanceFollowRedirects(boolean followRedirects) {
303 instanceFollowRedirects = followRedirects;
304 }
305
306 /**
307 * Returns the value of this <code>HttpURLConnection</code>'s
308 * <code>instanceFollowRedirects</code> field.
309 *
310 * @return the value of this <code>HttpURLConnection</code>'s
311 * <code>instanceFollowRedirects</code> field.
312 * @see java.net.HttpURLConnection#instanceFollowRedirects
313 * @see #setInstanceFollowRedirects(boolean)
314 * @since 1.3
315 */
316 public boolean getInstanceFollowRedirects() {
317 return instanceFollowRedirects;
318 }
319
320 /**
321 * Set the method for the URL request, one of:
322 * <UL>
323 * <LI>GET
324 * <LI>POST
325 * <LI>HEAD
326 * <LI>OPTIONS
327 * <LI>PUT
328 * <LI>DELETE
329 * <LI>TRACE
330 * </UL> are legal, subject to protocol restrictions. The default
331 * method is GET.
332 *
333 * @param method the HTTP method
334 * @exception ProtocolException if the method cannot be reset or if
335 * the requested method isn't valid for HTTP.
336 * @see #getRequestMethod()
337 */
338 public void setRequestMethod(String method)
339 throws ProtocolException {
340 if (connected) {
341 throw new ProtocolException(
342 "Can't reset method: already connected");
343 }
344 // This restriction will prevent people from using this class to
345 // experiment w/ new HTTP methods using java. But it should
346 // be placed for security - the request String could be
347 // arbitrarily long.
348
349 for (int i = 0; i < methods.length; i++) {
350 if (methods[i].equals(method)) {
351 this .method = method;
352 return;
353 }
354 }
355 throw new ProtocolException("Invalid HTTP method: " + method);
356 }
357
358 /**
359 * Get the request method.
360 * @return the HTTP request method
361 * @see #setRequestMethod(java.lang.String)
362 */
363 public String getRequestMethod() {
364 return method;
365 }
366
367 /**
368 * Gets the status code from an HTTP response message.
369 * For example, in the case of the following status lines:
370 * <PRE>
371 * HTTP/1.0 200 OK
372 * HTTP/1.0 401 Unauthorized
373 * </PRE>
374 * It will return 200 and 401 respectively.
375 * Returns -1 if no code can be discerned
376 * from the response (i.e., the response is not valid HTTP).
377 * @throws IOException if an error occurred connecting to the server.
378 * @return the HTTP Status-Code, or -1
379 */
380 public int getResponseCode() throws IOException {
381 /*
382 * We're got the response code already
383 */
384 if (responseCode != -1) {
385 return responseCode;
386 }
387
388 /*
389 * Ensure that we have connected to the server. Record
390 * exception as we need to re-throw it if there isn't
391 * a status line.
392 */
393 Exception exc = null;
394 try {
395 getInputStream();
396 } catch (Exception e) {
397 exc = e;
398 }
399
400 /*
401 * If we can't a status-line then re-throw any exception
402 * that getInputStream threw.
403 */
404 String statusLine = getHeaderField(0);
405 if (statusLine == null) {
406 if (exc != null) {
407 if (exc instanceof RuntimeException)
408 throw (RuntimeException) exc;
409 else
410 throw (IOException) exc;
411 }
412 return -1;
413 }
414
415 /*
416 * Examine the status-line - should be formatted as per
417 * section 6.1 of RFC 2616 :-
418 *
419 * Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase
420 *
421 * If status line can't be parsed return -1.
422 */
423 if (statusLine.startsWith("HTTP/1.")) {
424 int codePos = statusLine.indexOf(' ');
425 if (codePos > 0) {
426
427 int phrasePos = statusLine.indexOf(' ', codePos + 1);
428 if (phrasePos > 0 && phrasePos < statusLine.length()) {
429 responseMessage = statusLine
430 .substring(phrasePos + 1);
431 }
432
433 // deviation from RFC 2616 - don't reject status line
434 // if SP Reason-Phrase is not included.
435 if (phrasePos < 0)
436 phrasePos = statusLine.length();
437
438 try {
439 responseCode = Integer.parseInt(statusLine
440 .substring(codePos + 1, phrasePos));
441 return responseCode;
442 } catch (NumberFormatException e) {
443 }
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 * Indicates that other requests to the server
481 * are unlikely in the near future. Calling disconnect()
482 * should not imply that this HttpURLConnection
483 * instance can be reused for other requests.
484 */
485 public abstract void disconnect();
486
487 /**
488 * Indicates if the connection is going through a proxy.
489 * @return a boolean indicating if the connection is
490 * using a proxy.
491 */
492 public abstract boolean usingProxy();
493
494 /**
495 * Returns a {@link SocketPermission} object representing the
496 * permission necessary to connect to the destination host and port.
497 *
498 * @exception IOException if an error occurs while computing
499 * the permission.
500 *
501 * @return a <code>SocketPermission</code> object representing the
502 * permission necessary to connect to the destination
503 * host and port.
504 */
505 public Permission getPermission() throws IOException {
506 int port = url.getPort();
507 port = port < 0 ? 80 : port;
508 String host = url.getHost() + ":" + port;
509 Permission permission = new SocketPermission(host, "connect");
510 return permission;
511 }
512
513 /**
514 * Returns the error stream if the connection failed
515 * but the server sent useful data nonetheless. The
516 * typical example is when an HTTP server responds
517 * with a 404, which will cause a FileNotFoundException
518 * to be thrown in connect, but the server sent an HTML
519 * help page with suggestions as to what to do.
520 *
521 * <p>This method will not cause a connection to be initiated. If
522 * the connection was not connected, or if the server did not have
523 * an error while connecting or if the server had an error but
524 * no error data was sent, this method will return null. This is
525 * the default.
526 *
527 * @return an error stream if any, null if there have been no
528 * errors, the connection is not connected or the server sent no
529 * useful data.
530 */
531 public InputStream getErrorStream() {
532 return null;
533 }
534
535 /**
536 * The response codes for HTTP, as of version 1.1.
537 */
538
539 // REMIND: do we want all these??
540 // Others not here that we do want??
541 /* 2XX: generally "OK" */
542
543 /**
544 * HTTP Status-Code 200: OK.
545 */
546 public static final int HTTP_OK = 200;
547
548 /**
549 * HTTP Status-Code 201: Created.
550 */
551 public static final int HTTP_CREATED = 201;
552
553 /**
554 * HTTP Status-Code 202: Accepted.
555 */
556 public static final int HTTP_ACCEPTED = 202;
557
558 /**
559 * HTTP Status-Code 203: Non-Authoritative Information.
560 */
561 public static final int HTTP_NOT_AUTHORITATIVE = 203;
562
563 /**
564 * HTTP Status-Code 204: No Content.
565 */
566 public static final int HTTP_NO_CONTENT = 204;
567
568 /**
569 * HTTP Status-Code 205: Reset Content.
570 */
571 public static final int HTTP_RESET = 205;
572
573 /**
574 * HTTP Status-Code 206: Partial Content.
575 */
576 public static final int HTTP_PARTIAL = 206;
577
578 /* 3XX: relocation/redirect */
579
580 /**
581 * HTTP Status-Code 300: Multiple Choices.
582 */
583 public static final int HTTP_MULT_CHOICE = 300;
584
585 /**
586 * HTTP Status-Code 301: Moved Permanently.
587 */
588 public static final int HTTP_MOVED_PERM = 301;
589
590 /**
591 * HTTP Status-Code 302: Temporary Redirect.
592 */
593 public static final int HTTP_MOVED_TEMP = 302;
594
595 /**
596 * HTTP Status-Code 303: See Other.
597 */
598 public static final int HTTP_SEE_OTHER = 303;
599
600 /**
601 * HTTP Status-Code 304: Not Modified.
602 */
603 public static final int HTTP_NOT_MODIFIED = 304;
604
605 /**
606 * HTTP Status-Code 305: Use Proxy.
607 */
608 public static final int HTTP_USE_PROXY = 305;
609
610 /* 4XX: client error */
611
612 /**
613 * HTTP Status-Code 400: Bad Request.
614 */
615 public static final int HTTP_BAD_REQUEST = 400;
616
617 /**
618 * HTTP Status-Code 401: Unauthorized.
619 */
620 public static final int HTTP_UNAUTHORIZED = 401;
621
622 /**
623 * HTTP Status-Code 402: Payment Required.
624 */
625 public static final int HTTP_PAYMENT_REQUIRED = 402;
626
627 /**
628 * HTTP Status-Code 403: Forbidden.
629 */
630 public static final int HTTP_FORBIDDEN = 403;
631
632 /**
633 * HTTP Status-Code 404: Not Found.
634 */
635 public static final int HTTP_NOT_FOUND = 404;
636
637 /**
638 * HTTP Status-Code 405: Method Not Allowed.
639 */
640 public static final int HTTP_BAD_METHOD = 405;
641
642 /**
643 * HTTP Status-Code 406: Not Acceptable.
644 */
645 public static final int HTTP_NOT_ACCEPTABLE = 406;
646
647 /**
648 * HTTP Status-Code 407: Proxy Authentication Required.
649 */
650 public static final int HTTP_PROXY_AUTH = 407;
651
652 /**
653 * HTTP Status-Code 408: Request Time-Out.
654 */
655 public static final int HTTP_CLIENT_TIMEOUT = 408;
656
657 /**
658 * HTTP Status-Code 409: Conflict.
659 */
660 public static final int HTTP_CONFLICT = 409;
661
662 /**
663 * HTTP Status-Code 410: Gone.
664 */
665 public static final int HTTP_GONE = 410;
666
667 /**
668 * HTTP Status-Code 411: Length Required.
669 */
670 public static final int HTTP_LENGTH_REQUIRED = 411;
671
672 /**
673 * HTTP Status-Code 412: Precondition Failed.
674 */
675 public static final int HTTP_PRECON_FAILED = 412;
676
677 /**
678 * HTTP Status-Code 413: Request Entity Too Large.
679 */
680 public static final int HTTP_ENTITY_TOO_LARGE = 413;
681
682 /**
683 * HTTP Status-Code 414: Request-URI Too Large.
684 */
685 public static final int HTTP_REQ_TOO_LONG = 414;
686
687 /**
688 * HTTP Status-Code 415: Unsupported Media Type.
689 */
690 public static final int HTTP_UNSUPPORTED_TYPE = 415;
691
692 /* 5XX: server error */
693
694 /**
695 * HTTP Status-Code 500: Internal Server Error.
696 * @deprecated it is misplaced and shouldn't have existed.
697 */
698 @Deprecated
699 public static final int HTTP_SERVER_ERROR = 500;
700
701 /**
702 * HTTP Status-Code 500: Internal Server Error.
703 */
704 public static final int HTTP_INTERNAL_ERROR = 500;
705
706 /**
707 * HTTP Status-Code 501: Not Implemented.
708 */
709 public static final int HTTP_NOT_IMPLEMENTED = 501;
710
711 /**
712 * HTTP Status-Code 502: Bad Gateway.
713 */
714 public static final int HTTP_BAD_GATEWAY = 502;
715
716 /**
717 * HTTP Status-Code 503: Service Unavailable.
718 */
719 public static final int HTTP_UNAVAILABLE = 503;
720
721 /**
722 * HTTP Status-Code 504: Gateway Timeout.
723 */
724 public static final int HTTP_GATEWAY_TIMEOUT = 504;
725
726 /**
727 * HTTP Status-Code 505: HTTP Version Not Supported.
728 */
729 public static final int HTTP_VERSION = 505;
730
731 }
|