1c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath/* 2c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * Copyright (C) 2012 The Android Open Source Project 3c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * 4c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * Licensed under the Apache License, Version 2.0 (the "License"); 5c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * you may not use this file except in compliance with the License. 6c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * You may obtain a copy of the License at 7c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * 8c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * http://www.apache.org/licenses/LICENSE-2.0 9c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * 10c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * Unless required by applicable law or agreed to in writing, software 11c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * distributed under the License is distributed on an "AS IS" BASIS, 12c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * See the License for the specific language governing permissions and 14c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * limitations under the License. 15c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath */ 16c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 17c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathpackage libcore.net.http; 18c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 19c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.io.BufferedOutputStream; 20c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.io.ByteArrayOutputStream; 21c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.io.IOException; 22c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.io.InputStream; 23c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.io.OutputStream; 24c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.net.CacheRequest; 25c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.net.CookieHandler; 26c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.net.URL; 27c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport libcore.io.Streams; 28c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport libcore.util.Libcore; 29c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 30c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathfinal class HttpTransport implements Transport { 31c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath /** 32c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * The maximum number of bytes to buffer when sending headers and a request 33c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * body. When the headers and body can be sent in a single write, the 34c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * request completes sooner. In one WiFi benchmark, using a large enough 35c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * buffer sped up some uploads by half. 36c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath */ 37c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath private static final int MAX_REQUEST_BUFFER_LENGTH = 32768; 38c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 39c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath private final HttpEngine httpEngine; 40c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath private final InputStream socketIn; 41c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath private final OutputStream socketOut; 42c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 43c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath /** 44c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * This stream buffers the request headers and the request body when their 45c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * combined size is less than MAX_REQUEST_BUFFER_LENGTH. By combining them 46c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * we can save socket writes, which in turn saves a packet transmission. 47c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * This is socketOut if the request size is large or unknown. 48c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath */ 49c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath private OutputStream requestOut; 50c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 51c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath public HttpTransport(HttpEngine httpEngine, 52c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath OutputStream outputStream, InputStream inputStream) { 53c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath this.httpEngine = httpEngine; 54c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath this.socketOut = outputStream; 55c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath this.requestOut = outputStream; 56c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath this.socketIn = inputStream; 57c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 58c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 59c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath @Override public OutputStream createRequestBody() throws IOException { 60c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath boolean chunked = httpEngine.requestHeaders.isChunked(); 61c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath if (!chunked 62c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath && httpEngine.policy.getChunkLength() > 0 63c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath && httpEngine.connection.httpMinorVersion != 0) { 64c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath httpEngine.requestHeaders.setChunked(); 65c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath chunked = true; 66c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 67c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 68c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath // Stream a request body of unknown length. 69c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath if (chunked) { 70c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath int chunkLength = httpEngine.policy.getChunkLength(); 71c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath if (chunkLength == -1) { 72c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath chunkLength = HttpEngine.DEFAULT_CHUNK_LENGTH; 73c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 74c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath writeRequestHeaders(); 75c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath return new ChunkedOutputStream(requestOut, chunkLength); 76c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 77c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 78c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath // Stream a request body of a known length. 79c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath int fixedContentLength = httpEngine.policy.getFixedContentLength(); 80c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath if (fixedContentLength != -1) { 81c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath httpEngine.requestHeaders.setContentLength(fixedContentLength); 82c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath writeRequestHeaders(); 83c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath return new FixedLengthOutputStream(requestOut, fixedContentLength); 84c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 85c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 86c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath // Buffer a request body of a known length. 87c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath int contentLength = httpEngine.requestHeaders.getContentLength(); 88c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath if (contentLength != -1) { 89c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath writeRequestHeaders(); 90c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath return new RetryableOutputStream(contentLength); 91c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 92c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 93c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath // Buffer a request body of an unknown length. Don't write request 94c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath // headers until the entire body is ready; otherwise we can't set the 95c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath // Content-Length header correctly. 96c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath return new RetryableOutputStream(); 97c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 98c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 99c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath @Override public void flushRequest() throws IOException { 100c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath requestOut.flush(); 101c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath requestOut = socketOut; 102c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 103c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 104c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath @Override public void writeRequestBody(RetryableOutputStream requestBody) throws IOException { 105c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath requestBody.writeToSocket(requestOut); 106c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 107c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 108c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath /** 109c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * Prepares the HTTP headers and sends them to the server. 110c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * 111c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * <p>For streaming requests with a body, headers must be prepared 112c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * <strong>before</strong> the output stream has been written to. Otherwise 113c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * the body would need to be buffered! 114c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * 115c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * <p>For non-streaming requests with a body, headers must be prepared 116c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * <strong>after</strong> the output stream has been written to and closed. 117c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * This ensures that the {@code Content-Length} header field receives the 118c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * proper value. 119c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath */ 120c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath public void writeRequestHeaders() throws IOException { 121c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath if (httpEngine.sentRequestMillis != -1) { 122c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath throw new IllegalStateException(); 123c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 124c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath httpEngine.sentRequestMillis = System.currentTimeMillis(); 125c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 126c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath int contentLength = httpEngine.requestHeaders.getContentLength(); 127c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath RawHeaders headersToSend = getNetworkRequestHeaders(); 128c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath byte[] bytes = headersToSend.toHeaderString().getBytes("ISO-8859-1"); 129c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 130c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath if (contentLength != -1 && bytes.length + contentLength <= MAX_REQUEST_BUFFER_LENGTH) { 131c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath requestOut = new BufferedOutputStream(socketOut, bytes.length + contentLength); 132c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 133c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 134c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath requestOut.write(bytes); 135c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 136c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 137c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath private RawHeaders getNetworkRequestHeaders() { 138c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath return httpEngine.method == HttpEngine.CONNECT 139c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath ? getTunnelNetworkRequestHeaders() 140c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath : httpEngine.requestHeaders.getHeaders(); 141c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 142c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 143c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath /** 144c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * If we're establishing an HTTPS tunnel with CONNECT (RFC 2817 5.2), send 145c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * only the minimum set of headers. This avoids sending potentially 146c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * sensitive data like HTTP cookies to the proxy unencrypted. 147c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath */ 148c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath private RawHeaders getTunnelNetworkRequestHeaders() { 149c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath RequestHeaders privateHeaders = httpEngine.requestHeaders; 150c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath URL url = httpEngine.policy.getURL(); 151c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 152c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath RawHeaders result = new RawHeaders(); 153c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath result.setStatusLine("CONNECT " + url.getHost() + ":" + Libcore.getEffectivePort(url) 154c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath + " HTTP/1.1"); 155c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 156c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath // Always set Host and User-Agent. 157c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath String host = privateHeaders.getHost(); 158c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath if (host == null) { 159c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath host = httpEngine.getOriginAddress(url); 160c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 161c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath result.set("Host", host); 162c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 163c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath String userAgent = privateHeaders.getUserAgent(); 164c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath if (userAgent == null) { 165c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath userAgent = httpEngine.getDefaultUserAgent(); 166c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 167c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath result.set("User-Agent", userAgent); 168c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 169c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath // Copy over the Proxy-Authorization header if it exists. 170c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath String proxyAuthorization = privateHeaders.getProxyAuthorization(); 171c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath if (proxyAuthorization != null) { 172c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath result.set("Proxy-Authorization", proxyAuthorization); 173c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 174c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 175c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath // Always set the Proxy-Connection to Keep-Alive for the benefit of 176c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath // HTTP/1.0 proxies like Squid. 177c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath result.set("Proxy-Connection", "Keep-Alive"); 178c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath return result; 179c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 180c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 181c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath @Override public ResponseHeaders readResponseHeaders() throws IOException { 182c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath RawHeaders headers; 183c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath do { 184c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath headers = new RawHeaders(); 185c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath headers.setStatusLine(Streams.readAsciiLine(socketIn)); 186c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath httpEngine.connection.httpMinorVersion = headers.getHttpMinorVersion(); 187c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath readHeaders(headers); 188c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } while (headers.getResponseCode() == HttpEngine.HTTP_CONTINUE); 189c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath return new ResponseHeaders(httpEngine.uri, headers); 190c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 191c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 192c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath /** 193c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * Reads headers or trailers and updates the cookie store. 194c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath */ 195c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath private void readHeaders(RawHeaders headers) throws IOException { 196c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath // parse the result headers until the first blank line 197c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath String line; 198c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath while ((line = Streams.readAsciiLine(socketIn)).length() != 0) { 199c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath headers.addLine(line); 200c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 201c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 202c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath CookieHandler cookieHandler = CookieHandler.getDefault(); 203c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath if (cookieHandler != null) { 204c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath cookieHandler.put(httpEngine.uri, headers.toMultimap()); 205c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 206c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 207c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 208c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath public boolean makeReusable(OutputStream requestBodyOut, InputStream responseBodyIn) { 209c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath // We cannot reuse sockets that have incomplete output. 210c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath if (requestBodyOut != null && !((AbstractHttpOutputStream) requestBodyOut).closed) { 211c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath return false; 212c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 213c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 214c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath // If the headers specify that the connection shouldn't be reused, don't reuse it. 215c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath if (httpEngine.requestHeaders.hasConnectionClose() 216c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath || (httpEngine.responseHeaders != null 217c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath && httpEngine.responseHeaders.hasConnectionClose())) { 218c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath return false; 219c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 220c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 221c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath if (responseBodyIn instanceof UnknownLengthHttpInputStream) { 222c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath return false; 223c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 224c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 225c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath if (responseBodyIn != null) { 226c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath // Discard the response body before the connection can be reused. 227c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath try { 228c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath Streams.skipAll(responseBodyIn); 229c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } catch (IOException e) { 230c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath return false; 231c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 232c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 233c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 234c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath return true; 235c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 236c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 237c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath @Override public InputStream getTransferStream(CacheRequest cacheRequest) throws IOException { 238c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath if (!httpEngine.hasResponseBody()) { 239c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath return new FixedLengthInputStream(socketIn, cacheRequest, httpEngine, 0); 240c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 241c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 242c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath if (httpEngine.responseHeaders.isChunked()) { 243c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath return new ChunkedInputStream(socketIn, cacheRequest, this); 244c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 245c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 246c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath if (httpEngine.responseHeaders.getContentLength() != -1) { 247c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath return new FixedLengthInputStream(socketIn, cacheRequest, httpEngine, 248c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath httpEngine.responseHeaders.getContentLength()); 249c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 250c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 251c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath /* 252c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * Wrap the input stream from the HttpConnection (rather than 253c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * just returning "socketIn" directly here), so that we can control 254c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * its use after the reference escapes. 255c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath */ 256c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath return new UnknownLengthHttpInputStream(socketIn, cacheRequest, httpEngine); 257c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 258c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 259c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath /** 260c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * An HTTP body with a fixed length known in advance. 261c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath */ 262c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath private static final class FixedLengthOutputStream extends AbstractHttpOutputStream { 263c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath private final OutputStream socketOut; 264c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath private int bytesRemaining; 265c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 266c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath private FixedLengthOutputStream(OutputStream socketOut, int bytesRemaining) { 267c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath this.socketOut = socketOut; 268c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath this.bytesRemaining = bytesRemaining; 269c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 270c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 271c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath @Override public void write(byte[] buffer, int offset, int count) throws IOException { 272c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath checkNotClosed(); 273c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath Libcore.checkOffsetAndCount(buffer.length, offset, count); 274c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath if (count > bytesRemaining) { 275c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath throw new IOException("expected " + bytesRemaining 276c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath + " bytes but received " + count); 277c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 278c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath socketOut.write(buffer, offset, count); 279c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath bytesRemaining -= count; 280c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 281c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 282c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath @Override public void flush() throws IOException { 283c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath if (closed) { 284c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath return; // don't throw; this stream might have been closed on the caller's behalf 285c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 286c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath socketOut.flush(); 287c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 288c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 289c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath @Override public void close() throws IOException { 290c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath if (closed) { 291c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath return; 292c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 293c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath closed = true; 294c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath if (bytesRemaining > 0) { 295c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath throw new IOException("unexpected end of stream"); 296c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 297c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 298c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 299c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 300c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath /** 301c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * An HTTP body with alternating chunk sizes and chunk bodies. Chunks are 302c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * buffered until {@code maxChunkLength} bytes are ready, at which point the 303c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * chunk is written and the buffer is cleared. 304c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath */ 305c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath private static final class ChunkedOutputStream extends AbstractHttpOutputStream { 306c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath private static final byte[] CRLF = {'\r', '\n'}; 307c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath private static final byte[] HEX_DIGITS = { 308c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' 309c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath }; 310c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath private static final byte[] FINAL_CHUNK = new byte[] {'0', '\r', '\n', '\r', '\n'}; 311c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 312c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath /** Scratch space for up to 8 hex digits, and then a constant CRLF. */ 313c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath private final byte[] hex = {0, 0, 0, 0, 0, 0, 0, 0, '\r', '\n'}; 314c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 315c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath private final OutputStream socketOut; 316c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath private final int maxChunkLength; 317c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath private final ByteArrayOutputStream bufferedChunk; 318c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 319c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath private ChunkedOutputStream(OutputStream socketOut, int maxChunkLength) { 320c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath this.socketOut = socketOut; 321c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath this.maxChunkLength = Math.max(1, dataLength(maxChunkLength)); 322c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath this.bufferedChunk = new ByteArrayOutputStream(maxChunkLength); 323c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 324c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 325c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath /** 326c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * Returns the amount of data that can be transmitted in a chunk whose total 327c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * length (data+headers) is {@code dataPlusHeaderLength}. This is presumably 328c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * useful to match sizes with wire-protocol packets. 329c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath */ 330c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath private int dataLength(int dataPlusHeaderLength) { 331c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath int headerLength = 4; // "\r\n" after the size plus another "\r\n" after the data 332c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath for (int i = dataPlusHeaderLength - headerLength; i > 0; i >>= 4) { 333c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath headerLength++; 334c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 335c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath return dataPlusHeaderLength - headerLength; 336c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 337c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 338c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath @Override public synchronized void write(byte[] buffer, int offset, int count) 339c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath throws IOException { 340c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath checkNotClosed(); 341c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath Libcore.checkOffsetAndCount(buffer.length, offset, count); 342c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 343c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath while (count > 0) { 344c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath int numBytesWritten; 345c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 346c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath if (bufferedChunk.size() > 0 || count < maxChunkLength) { 347c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath // fill the buffered chunk and then maybe write that to the stream 348c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath numBytesWritten = Math.min(count, maxChunkLength - bufferedChunk.size()); 349c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath // TODO: skip unnecessary copies from buffer->bufferedChunk? 350c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath bufferedChunk.write(buffer, offset, numBytesWritten); 351c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath if (bufferedChunk.size() == maxChunkLength) { 352c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath writeBufferedChunkToSocket(); 353c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 354c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 355c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } else { 356c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath // write a single chunk of size maxChunkLength to the stream 357c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath numBytesWritten = maxChunkLength; 358c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath writeHex(numBytesWritten); 359c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath socketOut.write(buffer, offset, numBytesWritten); 360c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath socketOut.write(CRLF); 361c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 362c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 363c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath offset += numBytesWritten; 364c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath count -= numBytesWritten; 365c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 366c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 367c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 368c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath /** 369c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * Equivalent to, but cheaper than writing Integer.toHexString().getBytes() 370c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * followed by CRLF. 371c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath */ 372c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath private void writeHex(int i) throws IOException { 373c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath int cursor = 8; 374c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath do { 375c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath hex[--cursor] = HEX_DIGITS[i & 0xf]; 376c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } while ((i >>>= 4) != 0); 377c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath socketOut.write(hex, cursor, hex.length - cursor); 378c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 379c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 380c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath @Override public synchronized void flush() throws IOException { 381c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath if (closed) { 382c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath return; // don't throw; this stream might have been closed on the caller's behalf 383c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 384c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath writeBufferedChunkToSocket(); 385c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath socketOut.flush(); 386c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 387c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 388c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath @Override public synchronized void close() throws IOException { 389c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath if (closed) { 390c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath return; 391c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 392c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath closed = true; 393c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath writeBufferedChunkToSocket(); 394c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath socketOut.write(FINAL_CHUNK); 395c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 396c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 397c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath private void writeBufferedChunkToSocket() throws IOException { 398c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath int size = bufferedChunk.size(); 399c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath if (size <= 0) { 400c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath return; 401c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 402c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 403c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath writeHex(size); 404c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath bufferedChunk.writeTo(socketOut); 405c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath bufferedChunk.reset(); 406c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath socketOut.write(CRLF); 407c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 408c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 409c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 410c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath /** 411c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * An HTTP body with a fixed length specified in advance. 412c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath */ 413c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath private static class FixedLengthInputStream extends AbstractHttpInputStream { 414c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath private int bytesRemaining; 415c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 416c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath public FixedLengthInputStream(InputStream is, CacheRequest cacheRequest, 417c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath HttpEngine httpEngine, int length) throws IOException { 418c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath super(is, httpEngine, cacheRequest); 419c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath bytesRemaining = length; 420c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath if (bytesRemaining == 0) { 421c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath endOfInput(true); 422c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 423c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 424c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 425c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath @Override public int read(byte[] buffer, int offset, int count) throws IOException { 426c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath Libcore.checkOffsetAndCount(buffer.length, offset, count); 427c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath checkNotClosed(); 428c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath if (bytesRemaining == 0) { 429c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath return -1; 430c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 431c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath int read = in.read(buffer, offset, Math.min(count, bytesRemaining)); 432c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath if (read == -1) { 433c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath unexpectedEndOfInput(); // the server didn't supply the promised content length 434c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath throw new IOException("unexpected end of stream"); 435c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 436c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath bytesRemaining -= read; 437c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath cacheWrite(buffer, offset, read); 438c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath if (bytesRemaining == 0) { 439c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath endOfInput(true); 440c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 441c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath return read; 442c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 443c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 444c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath @Override public int available() throws IOException { 445c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath checkNotClosed(); 446c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath return bytesRemaining == 0 ? 0 : Math.min(in.available(), bytesRemaining); 447c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 448c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 449c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath @Override public void close() throws IOException { 450c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath if (closed) { 451c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath return; 452c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 453c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath closed = true; 454c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath if (bytesRemaining != 0) { 455c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath unexpectedEndOfInput(); 456c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 457c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 458c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 459c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 460c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath /** 461c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * An HTTP body with alternating chunk sizes and chunk bodies. 462c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath */ 463c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath private static class ChunkedInputStream extends AbstractHttpInputStream { 464c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath private static final int MIN_LAST_CHUNK_LENGTH = "\r\n0\r\n\r\n".length(); 465c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath private static final int NO_CHUNK_YET = -1; 466c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath private final HttpTransport transport; 467c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath private int bytesRemainingInChunk = NO_CHUNK_YET; 468c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath private boolean hasMoreChunks = true; 469c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 470c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath ChunkedInputStream(InputStream is, CacheRequest cacheRequest, 471c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath HttpTransport transport) throws IOException { 472c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath super(is, transport.httpEngine, cacheRequest); 473c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath this.transport = transport; 474c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 475c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 476c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath @Override public int read(byte[] buffer, int offset, int count) throws IOException { 477c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath Libcore.checkOffsetAndCount(buffer.length, offset, count); 478c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath checkNotClosed(); 479c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 480c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath if (!hasMoreChunks) { 481c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath return -1; 482c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 483c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath if (bytesRemainingInChunk == 0 || bytesRemainingInChunk == NO_CHUNK_YET) { 484c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath readChunkSize(); 485c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath if (!hasMoreChunks) { 486c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath return -1; 487c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 488c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 489c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath int read = in.read(buffer, offset, Math.min(count, bytesRemainingInChunk)); 490c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath if (read == -1) { 491c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath unexpectedEndOfInput(); // the server didn't supply the promised chunk length 492c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath throw new IOException("unexpected end of stream"); 493c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 494c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath bytesRemainingInChunk -= read; 495c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath cacheWrite(buffer, offset, read); 496c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 497c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath /* 498c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * If we're at the end of a chunk and the next chunk size is readable, 499c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * read it! Reading the last chunk causes the underlying connection to 500c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * be recycled and we want to do that as early as possible. Otherwise 501c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * self-delimiting streams like gzip will never be recycled. 502c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * http://code.google.com/p/android/issues/detail?id=7059 503c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath */ 504c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath if (bytesRemainingInChunk == 0 && in.available() >= MIN_LAST_CHUNK_LENGTH) { 505c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath readChunkSize(); 506c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 507c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 508c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath return read; 509c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 510c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 511c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath private void readChunkSize() throws IOException { 512c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath // read the suffix of the previous chunk 513c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath if (bytesRemainingInChunk != NO_CHUNK_YET) { 514c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath Streams.readAsciiLine(in); 515c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 516c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath String chunkSizeString = Streams.readAsciiLine(in); 517c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath int index = chunkSizeString.indexOf(";"); 518c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath if (index != -1) { 519c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath chunkSizeString = chunkSizeString.substring(0, index); 520c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 521c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath try { 522c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath bytesRemainingInChunk = Integer.parseInt(chunkSizeString.trim(), 16); 523c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } catch (NumberFormatException e) { 524c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath throw new IOException("Expected a hex chunk size, but was " + chunkSizeString); 525c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 526c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath if (bytesRemainingInChunk == 0) { 527c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath hasMoreChunks = false; 528c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath transport.readHeaders(httpEngine.responseHeaders.getHeaders()); 529c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath endOfInput(true); 530c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 531c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 532c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 533c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath @Override public int available() throws IOException { 534c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath checkNotClosed(); 535c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath if (!hasMoreChunks || bytesRemainingInChunk == NO_CHUNK_YET) { 536c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath return 0; 537c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 538c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath return Math.min(in.available(), bytesRemainingInChunk); 539c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 540c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 541c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath @Override public void close() throws IOException { 542c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath if (closed) { 543c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath return; 544c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 545c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 546c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath closed = true; 547c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath if (hasMoreChunks) { 548c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath unexpectedEndOfInput(); 549c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 550c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 551c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 552c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 553c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath /** 554c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * An HTTP payload terminated by the end of the socket stream. 555c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath */ 556c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath private static final class UnknownLengthHttpInputStream extends AbstractHttpInputStream { 557c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath private boolean inputExhausted; 558c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 559c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath private UnknownLengthHttpInputStream(InputStream is, CacheRequest cacheRequest, 560c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath HttpEngine httpEngine) throws IOException { 561c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath super(is, httpEngine, cacheRequest); 562c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 563c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 564c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath @Override public int read(byte[] buffer, int offset, int count) throws IOException { 565c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath Libcore.checkOffsetAndCount(buffer.length, offset, count); 566c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath checkNotClosed(); 567c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath if (in == null || inputExhausted) { 568c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath return -1; 569c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 570c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath int read = in.read(buffer, offset, count); 571c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath if (read == -1) { 572c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath inputExhausted = true; 573c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath endOfInput(false); 574c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath return -1; 575c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 576c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath cacheWrite(buffer, offset, read); 577c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath return read; 578c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 579c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 580c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath @Override public int available() throws IOException { 581c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath checkNotClosed(); 582c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath return in == null ? 0 : in.available(); 583c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 584c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 585c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath @Override public void close() throws IOException { 586c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath if (closed) { 587c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath return; 588c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 589c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath closed = true; 590c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath if (!inputExhausted) { 591c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath unexpectedEndOfInput(); 592c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 593c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 594c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 595c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath} 596