1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17
18package java.net;
19
20import java.io.IOException;
21
22import org.apache.harmony.luni.util.Msg;
23
24/**
25 * This abstract subclass of {@code URLConnection} defines methods for managing
26 * HTTP connection according to the description given by RFC 2068.
27 *
28 * @see ContentHandler
29 * @see URL
30 * @see URLConnection
31 * @see URLStreamHandler
32 */
33public abstract class HttpURLConnection extends URLConnection {
34    @SuppressWarnings("nls")
35    private String methodTokens[] = { "GET", "DELETE", "HEAD", "OPTIONS",
36            "POST", "PUT", "TRACE" };
37
38   /**
39     * The HTTP request method of this {@code HttpURLConnection}. The default
40     * value is {@code "GET"}.
41     */
42    protected String method = "GET"; //$NON-NLS-1$
43
44    /**
45     * The status code of the response obtained from the HTTP request. The
46     * default value is {@code -1}.
47     * <p>
48     * <li>1xx: Informational</li>
49     * <li>2xx: Success</li>
50     * <li>3xx: Relocation/Redirection</li>
51     * <li>4xx: Client Error</li>
52     * <li>5xx: Server Error</li>
53     */
54    protected int responseCode = -1;
55
56    /**
57     * The HTTP response message which corresponds to the response code.
58     */
59    protected String responseMessage;
60
61    /**
62     * Flag to define whether the protocol will automatically follow redirects
63     * or not. The default value is {@code true}.
64     */
65    protected boolean instanceFollowRedirects = followRedirects;
66
67    private static boolean followRedirects = true;
68
69    /**
70     * If the HTTP chunked encoding is enabled this parameter defines the
71     * chunk-length. Default value is {@code -1} that means the chunked encoding
72     * mode is disabled.
73     */
74    protected int chunkLength = -1;
75
76    /**
77     * If using HTTP fixed-length streaming mode this parameter defines the
78     * fixed length of content. Default value is {@code -1} that means the
79     * fixed-length streaming mode is disabled.
80     */
81    protected int fixedContentLength = -1;
82
83    private final static int DEFAULT_CHUNK_LENGTH = 1024;
84
85    // 2XX: generally "OK"
86    // 3XX: relocation/redirect
87    // 4XX: client error
88    // 5XX: server error
89    /**
90     * Numeric status code, 202: Accepted
91     */
92    public final static int HTTP_ACCEPTED = 202;
93
94    /**
95     * Numeric status code, 502: Bad Gateway
96     */
97    public final static int HTTP_BAD_GATEWAY = 502;
98
99    /**
100     * Numeric status code, 405: Bad Method
101     */
102    public final static int HTTP_BAD_METHOD = 405;
103
104    /**
105     * Numeric status code, 400: Bad Request
106     */
107    public final static int HTTP_BAD_REQUEST = 400;
108
109    /**
110     * Numeric status code, 408: Client Timeout
111     */
112    public final static int HTTP_CLIENT_TIMEOUT = 408;
113
114    /**
115     * Numeric status code, 409: Conflict
116     */
117    public final static int HTTP_CONFLICT = 409;
118
119    /**
120     * Numeric status code, 201: Created
121     */
122    public final static int HTTP_CREATED = 201;
123
124    /**
125     * Numeric status code, 413: Entity too large
126     */
127    public final static int HTTP_ENTITY_TOO_LARGE = 413;
128
129    /**
130     * Numeric status code, 403: Forbidden
131     */
132    public final static int HTTP_FORBIDDEN = 403;
133
134    /**
135     * Numeric status code, 504: Gateway timeout
136     */
137    public final static int HTTP_GATEWAY_TIMEOUT = 504;
138
139    /**
140     * Numeric status code, 410: Gone
141     */
142    public final static int HTTP_GONE = 410;
143
144    /**
145     * Numeric status code, 500: Internal error
146     */
147    public final static int HTTP_INTERNAL_ERROR = 500;
148
149    /**
150     * Numeric status code, 411: Length required
151     */
152    public final static int HTTP_LENGTH_REQUIRED = 411;
153
154    /**
155     * Numeric status code, 301 Moved permanently
156     */
157    public final static int HTTP_MOVED_PERM = 301;
158
159    /**
160     * Numeric status code, 302: Moved temporarily
161     */
162    public final static int HTTP_MOVED_TEMP = 302;
163
164    /**
165     * Numeric status code, 300: Multiple choices
166     */
167    public final static int HTTP_MULT_CHOICE = 300;
168
169    /**
170     * Numeric status code, 204: No content
171     */
172    public final static int HTTP_NO_CONTENT = 204;
173
174    /**
175     * Numeric status code, 406: Not acceptable
176     */
177    public final static int HTTP_NOT_ACCEPTABLE = 406;
178
179    /**
180     * Numeric status code, 203: Not authoritative
181     */
182    public final static int HTTP_NOT_AUTHORITATIVE = 203;
183
184    /**
185     * Numeric status code, 404: Not found
186     */
187    public final static int HTTP_NOT_FOUND = 404;
188
189    /**
190     * Numeric status code, 501: Not implemented
191     */
192    public final static int HTTP_NOT_IMPLEMENTED = 501;
193
194    /**
195     * Numeric status code, 304: Not modified
196     */
197    public final static int HTTP_NOT_MODIFIED = 304;
198
199    /**
200     * Numeric status code, 200: OK
201     */
202    public final static int HTTP_OK = 200;
203
204    /**
205     * Numeric status code, 206: Partial
206     */
207    public final static int HTTP_PARTIAL = 206;
208
209    /**
210     * Numeric status code, 402: Payment required
211     */
212    public final static int HTTP_PAYMENT_REQUIRED = 402;
213
214    /**
215     * Numeric status code, 412: Precondition failed
216     */
217    public final static int HTTP_PRECON_FAILED = 412;
218
219    /**
220     * Numeric status code, 407: Proxy authentication required
221     */
222    public final static int HTTP_PROXY_AUTH = 407;
223
224    /**
225     * Numeric status code, 414: Request too long
226     */
227    public final static int HTTP_REQ_TOO_LONG = 414;
228
229    /**
230     * Numeric status code, 205: Reset
231     */
232    public final static int HTTP_RESET = 205;
233
234    /**
235     * Numeric status code, 303: See other
236     */
237    public final static int HTTP_SEE_OTHER = 303;
238
239    /**
240     * Numeric status code, 500: Internal error
241     *
242     * @deprecated Use {@link #HTTP_INTERNAL_ERROR}
243     */
244    @Deprecated
245    public final static int HTTP_SERVER_ERROR = 500;
246
247    /**
248     * Numeric status code, 305: Use proxy
249     */
250    public final static int HTTP_USE_PROXY = 305;
251
252    /**
253     * Numeric status code, 401: Unauthorized
254     */
255    public final static int HTTP_UNAUTHORIZED = 401;
256
257    /**
258     * Numeric status code, 415: Unsupported type
259     */
260    public final static int HTTP_UNSUPPORTED_TYPE = 415;
261
262    /**
263     * Numeric status code, 503: Unavailable
264     */
265    public final static int HTTP_UNAVAILABLE = 503;
266
267    /**
268     * Numeric status code, 505: Version not supported
269     */
270    public final static int HTTP_VERSION = 505;
271
272    /**
273     * Constructs a new {@code HttpURLConnection} instance pointing to the
274     * resource specified by the {@code url}.
275     *
276     * @param url
277     *            the URL of this connection.
278     * @see URL
279     * @see URLConnection
280     */
281    protected HttpURLConnection(URL url) {
282        super(url);
283    }
284
285    /**
286     * Closes the connection to the HTTP server.
287     *
288     * @see URLConnection#connect()
289     * @see URLConnection#connected
290     */
291    public abstract void disconnect();
292
293    /**
294     * Returns an input stream from the server in the case of an error such as
295     * the requested file has not been found on the remote server. This stream
296     * can be used to read the data the server will send back.
297     *
298     * @return the error input stream returned by the server.
299     */
300    public java.io.InputStream getErrorStream() {
301        return null;
302    }
303
304    /**
305     * Returns the value of {@code followRedirects} which indicates if this
306     * connection follows a different URL redirected by the server. It is
307     * enabled by default.
308     *
309     * @return the value of the flag.
310     * @see #setFollowRedirects
311     */
312    public static boolean getFollowRedirects() {
313        return followRedirects;
314    }
315
316    /**
317     * Returns the permission object (in this case {@code SocketPermission})
318     * with the host and the port number as the target name and {@code
319     * "resolve, connect"} as the action list. If the port number of this URL
320     * instance is lower than {@code 0} the port will be set to {@code 80}.
321     *
322     * @return the permission object required for this connection.
323     * @throws IOException
324     *             if an IO exception occurs during the creation of the
325     *             permission object.
326     */
327    @Override
328    public java.security.Permission getPermission() throws IOException {
329        int port = url.getPort();
330        if (port < 0) {
331            port = 80;
332        }
333        return new SocketPermission(url.getHost() + ":" + port, //$NON-NLS-1$
334                "connect, resolve"); //$NON-NLS-1$
335    }
336
337    /**
338     * Returns the request method which will be used to make the request to the
339     * remote HTTP server. All possible methods of this HTTP implementation is
340     * listed in the class definition.
341     *
342     * @return the request method string.
343     * @see #method
344     * @see #setRequestMethod
345     */
346    public String getRequestMethod() {
347        return method;
348    }
349
350    /**
351     * Returns the response code returned by the remote HTTP server.
352     *
353     * @return the response code, -1 if no valid response code.
354     * @throws IOException
355     *             if there is an IO error during the retrieval.
356     * @see #getResponseMessage
357     */
358    public int getResponseCode() throws IOException {
359        // Call getInputStream() first since getHeaderField() doesn't return
360        // exceptions
361        getInputStream();
362        String response = getHeaderField(0);
363        if (response == null) {
364            return -1;
365        }
366        response = response.trim();
367        int mark = response.indexOf(" ") + 1; //$NON-NLS-1$
368        if (mark == 0) {
369            return -1;
370        }
371        int last = mark + 3;
372        if (last > response.length()) {
373            last = response.length();
374        }
375        responseCode = Integer.parseInt(response.substring(mark, last));
376        if (last + 1 <= response.length()) {
377            responseMessage = response.substring(last + 1);
378        }
379        return responseCode;
380    }
381
382    /**
383     * Returns the response message returned by the remote HTTP server.
384     *
385     * @return the response message. {@code null} if no such response exists.
386     * @throws IOException
387     *             if there is an error during the retrieval.
388     * @see #getResponseCode()
389     */
390    public String getResponseMessage() throws IOException {
391        if (responseMessage != null) {
392            return responseMessage;
393        }
394        getResponseCode();
395        return responseMessage;
396    }
397
398    /**
399     * Sets the flag of whether this connection will follow redirects returned
400     * by the remote server. This method can only be called with the permission
401     * from the security manager.
402     *
403     * @param auto
404     *            the value to enable or disable this option.
405     * @see SecurityManager#checkSetFactory()
406     */
407    public static void setFollowRedirects(boolean auto) {
408        SecurityManager security = System.getSecurityManager();
409        if (security != null) {
410            security.checkSetFactory();
411        }
412        followRedirects = auto;
413    }
414
415    /**
416     * Sets the request command which will be sent to the remote HTTP server.
417     * This method can only be called before the connection is made.
418     *
419     * @param method
420     *            the string representing the method to be used.
421     * @throws ProtocolException
422     *             if this is called after connected, or the method is not
423     *             supported by this HTTP implementation.
424     * @see #getRequestMethod()
425     * @see #method
426     */
427    public void setRequestMethod(String method) throws ProtocolException {
428        if (connected) {
429            throw new ProtocolException(Msg.getString("K0037")); //$NON-NLS-1$
430        }
431        for (int i = 0; i < methodTokens.length; i++) {
432            if (methodTokens[i].equals(method)) {
433                // if there is a supported method that matches the desired
434                // method, then set the current method and return
435                this.method = methodTokens[i];
436                return;
437            }
438        }
439        // if none matches, then throw ProtocolException
440        throw new ProtocolException();
441    }
442
443    /**
444     * Returns whether this connection uses a proxy server or not.
445     *
446     * @return {@code true} if this connection passes a proxy server, false
447     *         otherwise.
448     */
449    public abstract boolean usingProxy();
450
451    /**
452     * Returns whether this connection follows redirects.
453     *
454     * @return {@code true} if this connection follows redirects, false
455     *         otherwise.
456     */
457    public boolean getInstanceFollowRedirects() {
458        return instanceFollowRedirects;
459    }
460
461    /**
462     * Sets whether this connection follows redirects.
463     *
464     * @param followRedirects
465     *            {@code true} if this connection will follows redirects, false
466     *            otherwise.
467     */
468    public void setInstanceFollowRedirects(boolean followRedirects) {
469        instanceFollowRedirects = followRedirects;
470    }
471
472    /**
473     * Returns the date value in milliseconds since {@code 01.01.1970, 00:00h}
474     * corresponding to the header field {@code field}. The {@code defaultValue}
475     * will be returned if no such field can be found in the response header.
476     *
477     * @param field
478     *            the header field name.
479     * @param defaultValue
480     *            the default value to use if the specified header field wont be
481     *            found.
482     * @return the header field represented in milliseconds since January 1,
483     *         1970 GMT.
484     */
485    @Override
486    public long getHeaderFieldDate(String field, long defaultValue) {
487        return super.getHeaderFieldDate(field, defaultValue);
488    }
489
490    /**
491     * If the length of a HTTP request body is known ahead, sets fixed length to
492     * enable streaming without buffering. Sets after connection will cause an
493     * exception.
494     *
495     * @see #setChunkedStreamingMode
496     * @param contentLength
497     *            the fixed length of the HTTP request body.
498     * @throws IllegalStateException
499     *             if already connected or an other mode already set.
500     * @throws IllegalArgumentException
501     *             if {@code contentLength} is less than zero.
502     */
503    public void setFixedLengthStreamingMode(int contentLength) {
504        if (super.connected) {
505            throw new IllegalStateException(Msg.getString("K0079")); //$NON-NLS-1$
506        }
507        if (0 < chunkLength) {
508            throw new IllegalStateException(Msg.getString("KA003")); //$NON-NLS-1$
509        }
510        if (0 > contentLength) {
511            throw new IllegalArgumentException(Msg.getString("K0051")); //$NON-NLS-1$
512        }
513        this.fixedContentLength = contentLength;
514    }
515
516    /**
517     * If the length of a HTTP request body is NOT known ahead, enable chunked
518     * transfer encoding to enable streaming with buffering. Notice that not all
519     * http servers support this mode. Sets after connection will cause an
520     * exception.
521     *
522     * @see #setFixedLengthStreamingMode
523     * @param chunklen
524     *            the length of a chunk.
525     * @throws IllegalStateException
526     *             if already connected or an other mode already set.
527     */
528    public void setChunkedStreamingMode(int chunklen) {
529        if (super.connected) {
530            throw new IllegalStateException(Msg.getString("K0079")); //$NON-NLS-1$
531        }
532        if (0 <= fixedContentLength) {
533            throw new IllegalStateException(Msg.getString("KA003")); //$NON-NLS-1$
534        }
535        if (0 >= chunklen) {
536            chunkLength = DEFAULT_CHUNK_LENGTH;
537        } else {
538            chunkLength = chunklen;
539        }
540    }
541}
542