/* * Copyright (C) 2013 Square, Inc. * * 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 retrofit.client; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Field; import java.net.HttpURLConnection; import java.net.ProtocolException; import java.net.URL; import java.util.ArrayList; import java.util.List; import java.util.Map; import retrofit.RetrofitError; import retrofit.mime.TypedInput; import retrofit.mime.TypedOutput; /** * Retrofit client that uses {@link HttpURLConnection} for communication. */ public class UrlConnectionClient implements Client { private final Field methodField; public UrlConnectionClient() { try { this.methodField = HttpURLConnection.class.getDeclaredField("method"); this.methodField.setAccessible(true); } catch (NoSuchFieldException e) { throw RetrofitError.unexpectedError(null, e); } } @Override public Response execute(Request request) throws IOException { HttpURLConnection connection = openConnection(request); prepareRequest(connection, request); return readResponse(connection); } protected HttpURLConnection openConnection(Request request) throws IOException { HttpURLConnection connection = (HttpURLConnection) new URL(request.getUrl()).openConnection(); connection.setConnectTimeout(Defaults.CONNECT_TIMEOUT_MILLIS); connection.setReadTimeout(Defaults.READ_TIMEOUT_MILLIS); return connection; } void prepareRequest(HttpURLConnection connection, Request request) throws IOException { // HttpURLConnection artificially restricts request method try { connection.setRequestMethod(request.getMethod()); } catch (ProtocolException e) { try { methodField.set(connection, request.getMethod()); } catch (IllegalAccessException e1) { throw RetrofitError.unexpectedError(request.getUrl(), e1); } } connection.setDoInput(true); for (Header header : request.getHeaders()) { connection.addRequestProperty(header.getName(), header.getValue()); } TypedOutput body = request.getBody(); if (body != null) { connection.setDoOutput(true); connection.addRequestProperty("Content-Type", body.mimeType()); long length = body.length(); if (length != -1) { connection.addRequestProperty("Content-Length", String.valueOf(length)); } body.writeTo(connection.getOutputStream()); } } Response readResponse(HttpURLConnection connection) throws IOException { int status = connection.getResponseCode(); String reason = connection.getResponseMessage(); List<Header> headers = new ArrayList<Header>(); for (Map.Entry<String, List<String>> field : connection.getHeaderFields().entrySet()) { String name = field.getKey(); for (String value : field.getValue()) { headers.add(new Header(name, value)); } } String mimeType = connection.getContentType(); int length = connection.getContentLength(); InputStream stream; if (status >= 400) { stream = connection.getErrorStream(); } else { stream = connection.getInputStream(); } TypedInput responseBody = new TypedInputStream(mimeType, length, stream); return new Response(status, reason, headers, responseBody); } private static class TypedInputStream implements TypedInput { private final String mimeType; private final long length; private final InputStream stream; private TypedInputStream(String mimeType, long length, InputStream stream) { this.mimeType = mimeType; this.length = length; this.stream = stream; } @Override public String mimeType() { return mimeType; } @Override public long length() { return length; } @Override public InputStream in() throws IOException { return stream; } } }