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 com.squareup.okhttp;
19
20import java.io.IOException;
21import java.io.InputStream;
22import java.net.ProtocolException;
23import java.net.Proxy;
24import java.net.SocketPermission;
25import java.net.URL;
26import java.net.URLConnection;
27import java.util.Arrays;
28import libcore.net.http.HttpEngine;
29
30/**
31 * An {@link java.net.URLConnection} for HTTP (<a
32 * href="http://tools.ietf.org/html/rfc2616">RFC 2616</a>) used to send and
33 * receive data over the web. Data may be of any type and length. This class may
34 * be used to send and receive streaming data whose length is not known in
35 * advance.
36 *
37 * <p>Uses of this class follow a pattern:
38 * <ol>
39 *   <li>Obtain a new {@code HttpURLConnection} by calling {@link
40 *       java.net.URL#openConnection() URL.openConnection()} and casting the result to
41 *       {@code HttpURLConnection}.
42 *   <li>Prepare the request. The primary property of a request is its URI.
43 *       Request headers may also include metadata such as credentials, preferred
44 *       content types, and session cookies.
45 *   <li>Optionally upload a request body. Instances must be configured with
46 *       {@link #setDoOutput(boolean) setDoOutput(true)} if they include a
47 *       request body. Transmit data by writing to the stream returned by {@link
48 *       #getOutputStream()}.
49 *   <li>Read the response. Response headers typically include metadata such as
50 *       the response body's content type and length, modified dates and session
51 *       cookies. The response body may be read from the stream returned by {@link
52 *       #getInputStream()}. If the response has no body, that method returns an
53 *       empty stream.
54 *   <li>Disconnect. Once the response body has been read, the {@code
55 *       HttpURLConnection} should be closed by calling {@link #disconnect()}.
56 *       Disconnecting releases the resources held by a connection so they may
57 *       be closed or reused.
58 * </ol>
59 *
60 * <p>For example, to retrieve the webpage at {@code http://www.android.com/}:
61 * <pre>   {@code
62 *   URL url = new URL("http://www.android.com/");
63 *   HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
64 *   try {
65 *     InputStream in = new BufferedInputStream(urlConnection.getInputStream());
66 *     readStream(in);
67 *   } finally {
68 *     urlConnection.disconnect();
69 *   }
70 * }</pre>
71 *
72 * <h3>Secure Communication with HTTPS</h3>
73 * Calling {@link java.net.URL#openConnection()} on a URL with the "https"
74 * scheme will return an {@code HttpsURLConnection}, which allows for
75 * overriding the default {@link javax.net.ssl.HostnameVerifier
76 * HostnameVerifier} and {@link javax.net.ssl.SSLSocketFactory
77 * SSLSocketFactory}. An application-supplied {@code SSLSocketFactory}
78 * created from an {@link javax.net.ssl.SSLContext SSLContext} can
79 * provide a custom {@link javax.net.ssl.X509TrustManager
80 * X509TrustManager} for verifying certificate chains and a custom
81 * {@link javax.net.ssl.X509KeyManager X509KeyManager} for supplying
82 * client certificates. See {@link OkHttpsConnection HttpsURLConnection} for
83 * more details.
84 *
85 * <h3>Response Handling</h3>
86 * {@code HttpURLConnection} will follow up to five HTTP redirects. It will
87 * follow redirects from one origin server to another. This implementation
88 * doesn't follow redirects from HTTPS to HTTP or vice versa.
89 *
90 * <p>If the HTTP response indicates that an error occurred, {@link
91 * #getInputStream()} will throw an {@link java.io.IOException}. Use {@link
92 * #getErrorStream()} to read the error response. The headers can be read in
93 * the normal way using {@link #getHeaderFields()},
94 *
95 * <h3>Posting Content</h3>
96 * To upload data to a web server, configure the connection for output using
97 * {@link #setDoOutput(boolean) setDoOutput(true)}.
98 *
99 * <p>For best performance, you should call either {@link
100 * #setFixedLengthStreamingMode(int)} when the body length is known in advance,
101 * or {@link #setChunkedStreamingMode(int)} when it is not. Otherwise {@code
102 * HttpURLConnection} will be forced to buffer the complete request body in
103 * memory before it is transmitted, wasting (and possibly exhausting) heap and
104 * increasing latency.
105 *
106 * <p>For example, to perform an upload: <pre>   {@code
107 *   HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
108 *   try {
109 *     urlConnection.setDoOutput(true);
110 *     urlConnection.setChunkedStreamingMode(0);
111 *
112 *     OutputStream out = new BufferedOutputStream(urlConnection.getOutputStream());
113 *     writeStream(out);
114 *
115 *     InputStream in = new BufferedInputStream(urlConnection.getInputStream());
116 *     readStream(in);
117 *   } finally {
118 *     urlConnection.disconnect();
119 *   }
120 * }</pre>
121 *
122 * <h3>Performance</h3>
123 * The input and output streams returned by this class are <strong>not
124 * buffered</strong>. Most callers should wrap the returned streams with {@link
125 * java.io.BufferedInputStream BufferedInputStream} or {@link
126 * java.io.BufferedOutputStream BufferedOutputStream}. Callers that do only bulk
127 * reads or writes may omit buffering.
128 *
129 * <p>When transferring large amounts of data to or from a server, use streams
130 * to limit how much data is in memory at once. Unless you need the entire
131 * body to be in memory at once, process it as a stream (rather than storing
132 * the complete body as a single byte array or string).
133 *
134 * <p>To reduce latency, this class may reuse the same underlying {@code Socket}
135 * for multiple request/response pairs. As a result, HTTP connections may be
136 * held open longer than necessary. Calls to {@link #disconnect()} may return
137 * the socket to a pool of connected sockets. This behavior can be disabled by
138 * setting the {@code http.keepAlive} system property to {@code false} before
139 * issuing any HTTP requests. The {@code http.maxConnections} property may be
140 * used to control how many idle connections to each server will be held.
141 *
142 * <p>By default, this implementation of {@code HttpURLConnection} requests that
143 * servers use gzip compression. Since {@link #getContentLength()} returns the
144 * number of bytes transmitted, you cannot use that method to predict how many
145 * bytes can be read from {@link #getInputStream()}. Instead, read that stream
146 * until it is exhausted: when {@link java.io.InputStream#read} returns -1. Gzip
147 * compression can be disabled by setting the acceptable encodings in the
148 * request header: <pre>   {@code
149 *   urlConnection.setRequestProperty("Accept-Encoding", "identity");
150 * }</pre>
151 *
152 * <h3>Handling Network Sign-On</h3>
153 * Some Wi-Fi networks block Internet access until the user clicks through a
154 * sign-on page. Such sign-on pages are typically presented by using HTTP
155 * redirects. You can use {@link #getURL()} to test if your connection has been
156 * unexpectedly redirected. This check is not valid until <strong>after</strong>
157 * the response headers have been received, which you can trigger by calling
158 * {@link #getHeaderFields()} or {@link #getInputStream()}. For example, to
159 * check that a response was not redirected to an unexpected host:
160 * <pre>   {@code
161 *   HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
162 *   try {
163 *     InputStream in = new BufferedInputStream(urlConnection.getInputStream());
164 *     if (!url.getHost().equals(urlConnection.getURL().getHost())) {
165 *       // we were redirected! Kick the user out to the browser to sign on?
166 *     }
167 *     ...
168 *   } finally {
169 *     urlConnection.disconnect();
170 *   }
171 * }</pre>
172 *
173 * <h3>HTTP Authentication</h3>
174 * {@code HttpURLConnection} supports <a
175 * href="http://www.ietf.org/rfc/rfc2617">HTTP basic authentication</a>. Use
176 * {@link java.net.Authenticator} to set the VM-wide authentication handler:
177 * <pre>   {@code
178 *   Authenticator.setDefault(new Authenticator() {
179 *     protected PasswordAuthentication getPasswordAuthentication() {
180 *       return new PasswordAuthentication(username, password.toCharArray());
181 *     }
182 *   });
183 * }</pre>
184 * Unless paired with HTTPS, this is <strong>not</strong> a secure mechanism for
185 * user authentication. In particular, the username, password, request and
186 * response are all transmitted over the network without encryption.
187 *
188 * <h3>Sessions with Cookies</h3>
189 * To establish and maintain a potentially long-lived session between client
190 * and server, {@code HttpURLConnection} includes an extensible cookie manager.
191 * Enable VM-wide cookie management using {@link java.net.CookieHandler} and {@link
192 * java.net.CookieManager}: <pre>   {@code
193 *   CookieManager cookieManager = new CookieManager();
194 *   CookieHandler.setDefault(cookieManager);
195 * }</pre>
196 * By default, {@code CookieManager} accepts cookies from the <a
197 * href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec1.html">origin
198 * server</a> only. Two other policies are included: {@link
199 * java.net.CookiePolicy#ACCEPT_ALL} and {@link java.net.CookiePolicy#ACCEPT_NONE}. Implement
200 * {@link java.net.CookiePolicy} to define a custom policy.
201 *
202 * <p>The default {@code CookieManager} keeps all accepted cookies in memory. It
203 * will forget these cookies when the VM exits. Implement {@link java.net.CookieStore} to
204 * define a custom cookie store.
205 *
206 * <p>In addition to the cookies set by HTTP responses, you may set cookies
207 * programmatically. To be included in HTTP request headers, cookies must have
208 * the domain and path properties set.
209 *
210 * <p>By default, new instances of {@code HttpCookie} work only with servers
211 * that support <a href="http://www.ietf.org/rfc/rfc2965.txt">RFC 2965</a>
212 * cookies. Many web servers support only the older specification, <a
213 * href="http://www.ietf.org/rfc/rfc2109.txt">RFC 2109</a>. For compatibility
214 * with the most web servers, set the cookie version to 0.
215 *
216 * <p>For example, to receive {@code www.twitter.com} in French: <pre>   {@code
217 *   HttpCookie cookie = new HttpCookie("lang", "fr");
218 *   cookie.setDomain("twitter.com");
219 *   cookie.setPath("/");
220 *   cookie.setVersion(0);
221 *   cookieManager.getCookieStore().add(new URI("http://twitter.com/"), cookie);
222 * }</pre>
223 *
224 * <h3>HTTP Methods</h3>
225 * <p>{@code HttpURLConnection} uses the {@code GET} method by default. It will
226 * use {@code POST} if {@link #setDoOutput setDoOutput(true)} has been called.
227 * Other HTTP methods ({@code OPTIONS}, {@code HEAD}, {@code PUT}, {@code
228 * DELETE} and {@code TRACE}) can be used with {@link #setRequestMethod}.
229 *
230 * <h3>Proxies</h3>
231 * By default, this class will connect directly to the <a
232 * href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec1.html">origin
233 * server</a>. It can also connect via an {@link java.net.Proxy.Type#HTTP HTTP} or {@link
234 * java.net.Proxy.Type#SOCKS SOCKS} proxy. To use a proxy, use {@link
235 * java.net.URL#openConnection(java.net.Proxy) URL.openConnection(Proxy)} when creating the
236 * connection.
237 *
238 * <h3>IPv6 Support</h3>
239 * <p>This class includes transparent support for IPv6. For hosts with both IPv4
240 * and IPv6 addresses, it will attempt to connect to each of a host's addresses
241 * until a connection is established.
242 *
243 * <h3>Response Caching</h3>
244 * Android 4.0 (Ice Cream Sandwich) includes a response cache. See {@code
245 * android.net.http.HttpResponseCache} for instructions on enabling HTTP caching
246 * in your application.
247 *
248 * <h3>Avoiding Bugs In Earlier Releases</h3>
249 * Prior to Android 2.2 (Froyo), this class had some frustrating bugs. In
250 * particular, calling {@code close()} on a readable {@code InputStream} could
251 * <a href="http://code.google.com/p/android/issues/detail?id=2939">poison the
252 * connection pool</a>. Work around this by disabling connection pooling:
253 * <pre>   {@code
254 * private void disableConnectionReuseIfNecessary() {
255 *   // Work around pre-Froyo bugs in HTTP connection reuse.
256 *   if (Integer.parseInt(Build.VERSION.SDK) < Build.VERSION_CODES.FROYO) {
257 *     System.setProperty("http.keepAlive", "false");
258 *   }
259 * }}</pre>
260 *
261 * <p>Each instance of {@code HttpURLConnection} may be used for one
262 * request/response pair. Instances of this class are not thread safe.
263 */
264public abstract class OkHttpConnection extends URLConnection {
265
266    /**
267     * The subset of HTTP methods that the user may select via {@link
268     * #setRequestMethod(String)}.
269     */
270    private static final String[] PERMITTED_USER_METHODS = {
271            HttpEngine.OPTIONS,
272            HttpEngine.GET,
273            HttpEngine.HEAD,
274            HttpEngine.POST,
275            HttpEngine.PUT,
276            HttpEngine.DELETE,
277            HttpEngine.TRACE
278            // Note: we don't allow users to specify "CONNECT"
279    };
280
281    /**
282     * The HTTP request method of this {@code HttpURLConnection}. The default
283     * value is {@code "GET"}.
284     */
285    protected String method = HttpEngine.GET;
286
287    /**
288     * The status code of the response obtained from the HTTP request. The
289     * default value is {@code -1}.
290     * <p>
291     * <li>1xx: Informational</li>
292     * <li>2xx: Success</li>
293     * <li>3xx: Relocation/Redirection</li>
294     * <li>4xx: Client Error</li>
295     * <li>5xx: Server Error</li>
296     */
297    protected int responseCode = -1;
298
299    /**
300     * The HTTP response message which corresponds to the response code.
301     */
302    protected String responseMessage;
303
304    /**
305     * Flag to define whether the protocol will automatically follow redirects
306     * or not. The default value is {@code true}.
307     */
308    protected boolean instanceFollowRedirects = followRedirects;
309
310    private static boolean followRedirects = true;
311
312    /**
313     * If the HTTP chunked encoding is enabled this parameter defines the
314     * chunk-length. Default value is {@code -1} that means the chunked encoding
315     * mode is disabled.
316     */
317    protected int chunkLength = -1;
318
319    /**
320     * If using HTTP fixed-length streaming mode this parameter defines the
321     * fixed length of content. Default value is {@code -1} that means the
322     * fixed-length streaming mode is disabled.
323     */
324    protected int fixedContentLength = -1;
325
326    // 2XX: generally "OK"
327    // 3XX: relocation/redirect
328    // 4XX: client error
329    // 5XX: server error
330    /**
331     * Numeric status code, 202: Accepted.
332     */
333    public static final int HTTP_ACCEPTED = 202;
334
335    /**
336     * Numeric status code, 502: Bad Gateway.
337     */
338    public static final int HTTP_BAD_GATEWAY = 502;
339
340    /**
341     * Numeric status code, 405: Bad Method.
342     */
343    public static final int HTTP_BAD_METHOD = 405;
344
345    /**
346     * Numeric status code, 400: Bad Request.
347     */
348    public static final int HTTP_BAD_REQUEST = 400;
349
350    /**
351     * Numeric status code, 408: Client Timeout.
352     */
353    public static final int HTTP_CLIENT_TIMEOUT = 408;
354
355    /**
356     * Numeric status code, 409: Conflict.
357     */
358    public static final int HTTP_CONFLICT = 409;
359
360    /**
361     * Numeric status code, 201: Created.
362     */
363    public static final int HTTP_CREATED = 201;
364
365    /**
366     * Numeric status code, 413: Entity too large.
367     */
368    public static final int HTTP_ENTITY_TOO_LARGE = 413;
369
370    /**
371     * Numeric status code, 403: Forbidden.
372     */
373    public static final int HTTP_FORBIDDEN = 403;
374
375    /**
376     * Numeric status code, 504: Gateway timeout.
377     */
378    public static final int HTTP_GATEWAY_TIMEOUT = 504;
379
380    /**
381     * Numeric status code, 410: Gone.
382     */
383    public static final int HTTP_GONE = 410;
384
385    /**
386     * Numeric status code, 500: Internal error.
387     */
388    public static final int HTTP_INTERNAL_ERROR = 500;
389
390    /**
391     * Numeric status code, 411: Length required.
392     */
393    public static final int HTTP_LENGTH_REQUIRED = 411;
394
395    /**
396     * Numeric status code, 301 Moved permanently.
397     */
398    public static final int HTTP_MOVED_PERM = 301;
399
400    /**
401     * Numeric status code, 302: Moved temporarily.
402     */
403    public static final int HTTP_MOVED_TEMP = 302;
404
405    /**
406     * Numeric status code, 300: Multiple choices.
407     */
408    public static final int HTTP_MULT_CHOICE = 300;
409
410    /**
411     * Numeric status code, 204: No content.
412     */
413    public static final int HTTP_NO_CONTENT = 204;
414
415    /**
416     * Numeric status code, 406: Not acceptable.
417     */
418    public static final int HTTP_NOT_ACCEPTABLE = 406;
419
420    /**
421     * Numeric status code, 203: Not authoritative.
422     */
423    public static final int HTTP_NOT_AUTHORITATIVE = 203;
424
425    /**
426     * Numeric status code, 404: Not found.
427     */
428    public static final int HTTP_NOT_FOUND = 404;
429
430    /**
431     * Numeric status code, 501: Not implemented.
432     */
433    public static final int HTTP_NOT_IMPLEMENTED = 501;
434
435    /**
436     * Numeric status code, 304: Not modified.
437     */
438    public static final int HTTP_NOT_MODIFIED = 304;
439
440    /**
441     * Numeric status code, 200: OK.
442     */
443    public static final int HTTP_OK = 200;
444
445    /**
446     * Numeric status code, 206: Partial.
447     */
448    public static final int HTTP_PARTIAL = 206;
449
450    /**
451     * Numeric status code, 402: Payment required.
452     */
453    public static final int HTTP_PAYMENT_REQUIRED = 402;
454
455    /**
456     * Numeric status code, 412: Precondition failed.
457     */
458    public static final int HTTP_PRECON_FAILED = 412;
459
460    /**
461     * Numeric status code, 407: Proxy authentication required.
462     */
463    public static final int HTTP_PROXY_AUTH = 407;
464
465    /**
466     * Numeric status code, 414: Request too long.
467     */
468    public static final int HTTP_REQ_TOO_LONG = 414;
469
470    /**
471     * Numeric status code, 205: Reset.
472     */
473    public static final int HTTP_RESET = 205;
474
475    /**
476     * Numeric status code, 303: See other.
477     */
478    public static final int HTTP_SEE_OTHER = 303;
479
480    /**
481     * Numeric status code, 500: Internal error.
482     *
483     * @deprecated Use {@link #HTTP_INTERNAL_ERROR}
484     */
485    @Deprecated
486    public static final int HTTP_SERVER_ERROR = 500;
487
488    /**
489     * Numeric status code, 305: Use proxy.
490     *
491     * <p>Like Firefox and Chrome, this class doesn't honor this response code.
492     * Other implementations respond to this status code by retrying the request
493     * using the HTTP proxy named by the response's Location header field.
494     */
495    public static final int HTTP_USE_PROXY = 305;
496
497    /**
498     * Numeric status code, 401: Unauthorized.
499     */
500    public static final int HTTP_UNAUTHORIZED = 401;
501
502    /**
503     * Numeric status code, 415: Unsupported type.
504     */
505    public static final int HTTP_UNSUPPORTED_TYPE = 415;
506
507    /**
508     * Numeric status code, 503: Unavailable.
509     */
510    public static final int HTTP_UNAVAILABLE = 503;
511
512    /**
513     * Numeric status code, 505: Version not supported.
514     */
515    public static final int HTTP_VERSION = 505;
516
517    /**
518     * Returns a new OkHttpConnection or OkHttpsConnection to {@code url}.
519     */
520    public static OkHttpConnection open(URL url) {
521        String protocol = url.getProtocol();
522        if (protocol.equals("http")) {
523            return new libcore.net.http.HttpURLConnectionImpl(url, 80);
524        } else if (protocol.equals("https")) {
525            return new libcore.net.http.HttpsURLConnectionImpl(url, 443);
526        } else {
527            throw new IllegalArgumentException();
528        }
529    }
530
531    /**
532     * Returns a new OkHttpConnection or OkHttpsConnection to {@code url} that
533     * connects via {@code proxy}.
534     */
535    public static OkHttpConnection open(URL url, Proxy proxy) {
536        String protocol = url.getProtocol();
537        if (protocol.equals("http")) {
538            return new libcore.net.http.HttpURLConnectionImpl(url, 80, proxy);
539        } else if (protocol.equals("https")) {
540            return new libcore.net.http.HttpsURLConnectionImpl(url, 443, proxy);
541        } else {
542            throw new IllegalArgumentException();
543        }
544    }
545
546    /**
547     * Constructs a new {@code HttpURLConnection} instance pointing to the
548     * resource specified by the {@code url}.
549     *
550     * @param url
551     *            the URL of this connection.
552     * @see java.net.URL
553     * @see java.net.URLConnection
554     */
555    protected OkHttpConnection(URL url) {
556        super(url);
557    }
558
559    /**
560     * Releases this connection so that its resources may be either reused or
561     * closed.
562     *
563     * <p>Unlike other Java implementations, this will not necessarily close
564     * socket connections that can be reused. You can disable all connection
565     * reuse by setting the {@code http.keepAlive} system property to {@code
566     * false} before issuing any HTTP requests.
567     */
568    public abstract void disconnect();
569
570    /**
571     * Returns an input stream from the server in the case of an error such as
572     * the requested file has not been found on the remote server. This stream
573     * can be used to read the data the server will send back.
574     *
575     * @return the error input stream returned by the server.
576     */
577    public InputStream getErrorStream() {
578        return null;
579    }
580
581    /**
582     * Returns the value of {@code followRedirects} which indicates if this
583     * connection follows a different URL redirected by the server. It is
584     * enabled by default.
585     *
586     * @return the value of the flag.
587     * @see #setFollowRedirects
588     */
589    public static boolean getFollowRedirects() {
590        return followRedirects;
591    }
592
593    /**
594     * Returns the permission object (in this case {@code SocketPermission})
595     * with the host and the port number as the target name and {@code
596     * "resolve, connect"} as the action list. If the port number of this URL
597     * instance is lower than {@code 0} the port will be set to {@code 80}.
598     *
599     * @return the permission object required for this connection.
600     * @throws java.io.IOException
601     *             if an IO exception occurs during the creation of the
602     *             permission object.
603     */
604    @Override
605    public java.security.Permission getPermission() throws IOException {
606        int port = url.getPort();
607        if (port < 0) {
608            port = 80;
609        }
610        return new SocketPermission(url.getHost() + ":" + port,
611                "connect, resolve");
612    }
613
614    /**
615     * Returns the request method which will be used to make the request to the
616     * remote HTTP server. All possible methods of this HTTP implementation is
617     * listed in the class definition.
618     *
619     * @return the request method string.
620     * @see #method
621     * @see #setRequestMethod
622     */
623    public String getRequestMethod() {
624        return method;
625    }
626
627    /**
628     * Returns the response code returned by the remote HTTP server.
629     *
630     * @return the response code, -1 if no valid response code.
631     * @throws java.io.IOException
632     *             if there is an IO error during the retrieval.
633     * @see #getResponseMessage
634     */
635    public int getResponseCode() throws IOException {
636        // Call getInputStream() first since getHeaderField() doesn't return
637        // exceptions
638        getInputStream();
639        String response = getHeaderField(0);
640        if (response == null) {
641            return -1;
642        }
643        response = response.trim();
644        int mark = response.indexOf(" ") + 1;
645        if (mark == 0) {
646            return -1;
647        }
648        int last = mark + 3;
649        if (last > response.length()) {
650            last = response.length();
651        }
652        responseCode = Integer.parseInt(response.substring(mark, last));
653        if (last + 1 <= response.length()) {
654            responseMessage = response.substring(last + 1);
655        }
656        return responseCode;
657    }
658
659    /**
660     * Returns the response message returned by the remote HTTP server.
661     *
662     * @return the response message. {@code null} if no such response exists.
663     * @throws java.io.IOException
664     *             if there is an error during the retrieval.
665     * @see #getResponseCode()
666     */
667    public String getResponseMessage() throws IOException {
668        if (responseMessage != null) {
669            return responseMessage;
670        }
671        getResponseCode();
672        return responseMessage;
673    }
674
675    /**
676     * Sets the flag of whether this connection will follow redirects returned
677     * by the remote server.
678     *
679     * @param auto
680     *            the value to enable or disable this option.
681     */
682    public static void setFollowRedirects(boolean auto) {
683        followRedirects = auto;
684    }
685
686    /**
687     * Sets the request command which will be sent to the remote HTTP server.
688     * This method can only be called before the connection is made.
689     *
690     * @param method
691     *            the string representing the method to be used.
692     * @throws java.net.ProtocolException
693     *             if this is called after connected, or the method is not
694     *             supported by this HTTP implementation.
695     * @see #getRequestMethod()
696     * @see #method
697     */
698    public void setRequestMethod(String method) throws ProtocolException {
699        if (connected) {
700            throw new ProtocolException("Connection already established");
701        }
702        for (String permittedUserMethod : PERMITTED_USER_METHODS) {
703            if (permittedUserMethod.equals(method)) {
704                // if there is a supported method that matches the desired
705                // method, then set the current method and return
706                this.method = permittedUserMethod;
707                return;
708            }
709        }
710        // if none matches, then throw ProtocolException
711        throw new ProtocolException("Unknown method '" + method + "'; must be one of "
712                + Arrays.toString(PERMITTED_USER_METHODS));
713    }
714
715    /**
716     * Returns whether this connection uses a proxy server or not.
717     *
718     * @return {@code true} if this connection passes a proxy server, false
719     *         otherwise.
720     */
721    public abstract boolean usingProxy();
722
723    /**
724     * Returns the encoding used to transmit the response body over the network.
725     * This is null or "identity" if the content was not encoded, or "gzip" if
726     * the body was gzip compressed. Most callers will be more interested in the
727     * {@link #getContentType() content type}, which may also include the
728     * content's character encoding.
729     */
730    @Override public String getContentEncoding() {
731        return super.getContentEncoding(); // overridden for Javadoc only
732    }
733
734    /**
735     * Returns whether this connection follows redirects.
736     *
737     * @return {@code true} if this connection follows redirects, false
738     *         otherwise.
739     */
740    public boolean getInstanceFollowRedirects() {
741        return instanceFollowRedirects;
742    }
743
744    /**
745     * Sets whether this connection follows redirects.
746     *
747     * @param followRedirects
748     *            {@code true} if this connection will follows redirects, false
749     *            otherwise.
750     */
751    public void setInstanceFollowRedirects(boolean followRedirects) {
752        instanceFollowRedirects = followRedirects;
753    }
754
755    /**
756     * Returns the date value in milliseconds since {@code 01.01.1970, 00:00h}
757     * corresponding to the header field {@code field}. The {@code defaultValue}
758     * will be returned if no such field can be found in the response header.
759     *
760     * @param field
761     *            the header field name.
762     * @param defaultValue
763     *            the default value to use if the specified header field wont be
764     *            found.
765     * @return the header field represented in milliseconds since January 1,
766     *         1970 GMT.
767     */
768    @Override
769    public long getHeaderFieldDate(String field, long defaultValue) {
770        return super.getHeaderFieldDate(field, defaultValue);
771    }
772
773    /**
774     * If the length of a HTTP request body is known ahead, sets fixed length to
775     * enable streaming without buffering. Sets after connection will cause an
776     * exception.
777     *
778     * @see #setChunkedStreamingMode
779     * @param contentLength
780     *            the fixed length of the HTTP request body.
781     * @throws IllegalStateException
782     *             if already connected or another mode already set.
783     * @throws IllegalArgumentException
784     *             if {@code contentLength} is less than zero.
785     */
786    public void setFixedLengthStreamingMode(int contentLength) {
787        if (super.connected) {
788            throw new IllegalStateException("Already connected");
789        }
790        if (chunkLength > 0) {
791            throw new IllegalStateException("Already in chunked mode");
792        }
793        if (contentLength < 0) {
794            throw new IllegalArgumentException("contentLength < 0");
795        }
796        this.fixedContentLength = contentLength;
797    }
798
799    /**
800     * Stream a request body whose length is not known in advance. Old HTTP/1.0
801     * only servers may not support this mode.
802     *
803     * <p>When HTTP chunked encoding is used, the stream is divided into
804     * chunks, each prefixed with a header containing the chunk's size. Setting
805     * a large chunk length requires a large internal buffer, potentially
806     * wasting memory. Setting a small chunk length increases the number of
807     * bytes that must be transmitted because of the header on every chunk.
808     * Most caller should use {@code 0} to get the system default.
809     *
810     * @see #setFixedLengthStreamingMode
811     * @param chunkLength the length to use, or {@code 0} for the default chunk
812     *     length.
813     * @throws IllegalStateException if already connected or another mode
814     *     already set.
815     */
816    public void setChunkedStreamingMode(int chunkLength) {
817        if (super.connected) {
818            throw new IllegalStateException("Already connected");
819        }
820        if (fixedContentLength >= 0) {
821            throw new IllegalStateException("Already in fixed-length mode");
822        }
823        if (chunkLength <= 0) {
824            this.chunkLength = HttpEngine.DEFAULT_CHUNK_LENGTH;
825        } else {
826            this.chunkLength = chunkLength;
827        }
828    }
829}
830