HurlStack.java revision d56b88ae161057e848e7410d1b9ce5b0b8c427fc
1/* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.volley.toolbox; 18 19import com.android.volley.AuthFailureError; 20import com.android.volley.Request; 21 22import org.apache.http.Header; 23import org.apache.http.HttpEntity; 24import org.apache.http.HttpResponse; 25import org.apache.http.ProtocolVersion; 26import org.apache.http.StatusLine; 27import org.apache.http.entity.BasicHttpEntity; 28import org.apache.http.message.BasicHeader; 29import org.apache.http.message.BasicHttpResponse; 30import org.apache.http.message.BasicStatusLine; 31 32import java.io.DataOutputStream; 33import java.io.IOException; 34import java.io.InputStream; 35import java.net.HttpURLConnection; 36import java.net.URL; 37import java.util.HashMap; 38import java.util.List; 39import java.util.Map; 40import java.util.Map.Entry; 41 42/** 43 * An {@link HttpStack} based on {@link HttpURLConnection}. 44 */ 45public class HurlStack implements HttpStack { 46 47 /** 48 * An interface for transforming URLs before use. 49 */ 50 public interface UrlRewriter { 51 /** 52 * Returns a URL to use instead of the provided one, or null to indicate 53 * this URL should not be used at all. 54 */ 55 public String rewriteUrl(String originalUrl); 56 } 57 58 private final UrlRewriter mUrlRewriter; 59 60 public HurlStack() { 61 this(null); 62 } 63 64 /** 65 * @param urlRewriter Rewriter to use for request URLs 66 */ 67 public HurlStack(UrlRewriter urlRewriter) { 68 mUrlRewriter = urlRewriter; 69 } 70 71 @Override 72 public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders) 73 throws IOException, AuthFailureError { 74 String url = request.getUrl(); 75 HashMap<String, String> map = new HashMap<String, String>(); 76 map.putAll(request.getHeaders()); 77 map.putAll(additionalHeaders); 78 if (mUrlRewriter != null) { 79 String rewritten = mUrlRewriter.rewriteUrl(url); 80 if (rewritten == null) { 81 throw new IOException("URL blocked by rewriter: " + url); 82 } 83 url = rewritten; 84 } 85 URL parsedUrl = new URL(url); 86 HttpURLConnection connection = openConnection(parsedUrl, request); 87 for (String headerName : map.keySet()) { 88 connection.addRequestProperty(headerName, map.get(headerName)); 89 } 90 handlePost(connection, request); 91 // Initialize HttpResponse with data from the HttpURLConnection. 92 ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1); 93 int responseCode = connection.getResponseCode(); 94 if (responseCode == -1) { 95 // -1 is returned by getResponseCode() if the response code could not be retrieved. 96 // Signal to the caller that something was wrong with the connection. 97 throw new IOException("Could not retrieve response code from HttpUrlConnection."); 98 } 99 StatusLine responseStatus = new BasicStatusLine(protocolVersion, 100 connection.getResponseCode(), connection.getResponseMessage()); 101 BasicHttpResponse response = new BasicHttpResponse(responseStatus); 102 response.setEntity(entityFromConnection(connection)); 103 for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) { 104 if (header.getKey() != null) { 105 Header h = new BasicHeader(header.getKey(), header.getValue().get(0)); 106 response.addHeader(h); 107 } 108 } 109 return response; 110 } 111 112 /** 113 * Initializes an {@link HttpEntity} from the given {@link HttpURLConnection}. 114 * @param connection 115 * @return an HttpEntity populated with data from <code>connection</code>. 116 */ 117 private static HttpEntity entityFromConnection(HttpURLConnection connection) { 118 BasicHttpEntity entity = new BasicHttpEntity(); 119 InputStream inputStream; 120 try { 121 inputStream = connection.getInputStream(); 122 } catch (IOException ioe) { 123 inputStream = connection.getErrorStream(); 124 } 125 entity.setContent(inputStream); 126 entity.setContentLength(connection.getContentLength()); 127 entity.setContentEncoding(connection.getContentEncoding()); 128 entity.setContentType(connection.getContentType()); 129 return entity; 130 } 131 132 /** 133 * Opens an {@link HttpURLConnection} with parameters. 134 * @param url 135 * @return an open connection 136 * @throws IOException 137 */ 138 private HttpURLConnection openConnection(URL url, Request<?> request) throws IOException { 139 HttpURLConnection connection = (HttpURLConnection) url.openConnection(); 140 141 int timeoutMs = request.getTimeoutMs(); 142 connection.setConnectTimeout(timeoutMs); 143 connection.setReadTimeout(timeoutMs); 144 connection.setUseCaches(false); 145 connection.setDoInput(true); 146 return connection; 147 } 148 149 private void handlePost(HttpURLConnection connection, Request<?> request) 150 throws IOException, AuthFailureError { 151 byte[] postBody = request.getPostBody(); 152 if (postBody == null) return; 153 // Prepare output. There is no need to set Content-Length explicitly, 154 // since this is handled by HttpURLConnection using the size of the prepared output stream. 155 connection.setDoOutput(true); 156 connection.setRequestMethod("POST"); 157 connection.addRequestProperty("Content-Type", request.getPostBodyContentType()); 158 DataOutputStream out = new DataOutputStream(connection.getOutputStream()); 159 out.write(postBody); 160 out.close(); 161 } 162} 163