/*
* Copyright (C) 2013 Peng fei Pan <sky@xiaopan.me>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.xiaopan.sketch.http;
import android.os.Build;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.util.List;
import java.util.Map;
import me.xiaopan.sketch.util.SketchUtils;
public class HurlStack implements HttpStack {
protected String logName = "HurlStack";
private int readTimeout = DEFAULT_READ_TIMEOUT;
private int maxRetryCount = DEFAULT_MAX_RETRY_COUNT;
private int connectTimeout = DEFAULT_CONNECT_TIMEOUT;
private String userAgent;
private Map<String, String> setExtraHeaders;
private Map<String, String> addExtraHeaders;
@Override
public int getMaxRetryCount() {
return maxRetryCount;
}
@Override
public HurlStack setMaxRetryCount(int maxRetryCount) {
this.maxRetryCount = maxRetryCount;
return this;
}
@Override
public int getConnectTimeout() {
return connectTimeout;
}
@Override
public HurlStack setConnectTimeout(int connectTimeout) {
this.connectTimeout = connectTimeout;
return this;
}
@Override
public int getReadTimeout() {
return readTimeout;
}
@Override
public HurlStack setReadTimeout(int readTimeout) {
this.readTimeout = readTimeout;
return this;
}
@Override
public String getUserAgent() {
return userAgent;
}
@Override
public HurlStack setUserAgent(String userAgent) {
this.userAgent = userAgent;
return this;
}
@Override
public Map<String, String> getExtraHeaders() {
return setExtraHeaders;
}
@Override
public HurlStack setExtraHeaders(Map<String, String> extraHeaders) {
this.setExtraHeaders = extraHeaders;
return this;
}
@Override
public Map<String, String> getAddExtraHeaders() {
return addExtraHeaders;
}
@Override
public HurlStack addExtraHeaders(Map<String, String> extraHeaders) {
this.addExtraHeaders = extraHeaders;
return this;
}
@Override
public boolean canRetry(Throwable throwable) {
return throwable instanceof SocketTimeoutException;
}
@Override
public String getKey() {
return String.format("%s(maxRetryCount=%d,connectTimeout=%d,readTimeout=%d,userAgent=%s)",
logName, maxRetryCount, connectTimeout, readTimeout, userAgent);
}
@Override
public ImageHttpResponse getHttpResponse(String uri) throws IOException {
HttpURLConnection connection = (HttpURLConnection) new URL(uri).openConnection();
connection.setConnectTimeout(connectTimeout);
connection.setReadTimeout(readTimeout);
connection.setDoInput(true);
// HTTP connection reuse which was buggy pre-froyo
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.FROYO) {
connection.setRequestProperty("http.keepAlive", "false");
}
if (userAgent != null) {
connection.setRequestProperty("User-Agent", userAgent);
}
if (addExtraHeaders != null && addExtraHeaders.size() > 0) {
for (Map.Entry<String, String> entry : addExtraHeaders.entrySet()) {
connection.addRequestProperty(entry.getKey(), entry.getValue());
}
}
if (setExtraHeaders != null && setExtraHeaders.size() > 0) {
for (Map.Entry<String, String> entry : setExtraHeaders.entrySet()) {
connection.setRequestProperty(entry.getKey(), entry.getValue());
}
}
processRequest(uri, connection);
connection.connect();
return new HttpUrlConnectionResponse(connection);
}
protected void processRequest(@SuppressWarnings("UnusedParameters") String uri,
@SuppressWarnings("UnusedParameters") HttpURLConnection connection) {
}
private static class HttpUrlConnectionResponse implements ImageHttpResponse {
private HttpURLConnection connection;
HttpUrlConnectionResponse(HttpURLConnection connection) {
this.connection = connection;
}
@Override
public int getResponseCode() throws IOException {
return connection.getResponseCode();
}
@Override
public String getResponseMessage() throws IOException {
return connection.getResponseMessage();
}
@Override
public long getContentLength() {
return connection.getContentLength();
}
@Override
public String getResponseHeadersString() {
Map<String, List<String>> headers = connection.getHeaderFields();
if (headers == null || headers.size() == 0) {
return null;
}
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("[");
for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
if (stringBuilder.length() != 1) {
stringBuilder.append(", ");
}
stringBuilder.append("{");
stringBuilder.append(entry.getKey());
stringBuilder.append(":");
List<String> values = entry.getValue();
if (values.size() == 0) {
stringBuilder.append("");
} else if (values.size() == 1) {
stringBuilder.append(values.get(0));
} else {
stringBuilder.append(values.toString());
}
stringBuilder.append("}");
}
stringBuilder.append("]");
return stringBuilder.toString();
}
@Override
public InputStream getContent() throws IOException {
return connection.getInputStream();
}
@Override
public void releaseConnection() {
try {
SketchUtils.close(getContent());
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public boolean isContentChunked() {
String transferEncodingValue = connection.getHeaderField("Transfer-Encoding");
if (transferEncodingValue != null) {
transferEncodingValue = transferEncodingValue.trim();
}
return transferEncodingValue != null && "chunked".equalsIgnoreCase(transferEncodingValue);
}
}
}