• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
18 package com.squareup.okhttp;
19 
20 import java.io.IOException;
21 import java.io.InputStream;
22 import java.net.ProtocolException;
23 import java.net.Proxy;
24 import java.net.SocketPermission;
25 import java.net.URL;
26 import java.net.URLConnection;
27 import java.util.Arrays;
28 import 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  */
264 public 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      */
open(URL url)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      */
open(URL url, Proxy proxy)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      */
OkHttpConnection(URL url)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      */
disconnect()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      */
getErrorStream()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      */
getFollowRedirects()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
getPermission()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      */
getRequestMethod()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      */
getResponseCode()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      */
getResponseMessage()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      */
setFollowRedirects(boolean auto)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      */
setRequestMethod(String method)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      */
usingProxy()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      */
getContentEncoding()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      */
getInstanceFollowRedirects()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      */
setInstanceFollowRedirects(boolean followRedirects)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
getHeaderFieldDate(String field, long defaultValue)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      */
setFixedLengthStreamingMode(int contentLength)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      */
setChunkedStreamingMode(int chunkLength)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