1// Copyright 2013 Square, Inc.
2package com.squareup.okhttp.apache;
3
4import com.squareup.okhttp.Headers;
5import com.squareup.okhttp.OkHttpClient;
6import com.squareup.okhttp.Request;
7import com.squareup.okhttp.RequestBody;
8import com.squareup.okhttp.Response;
9import com.squareup.okhttp.ResponseBody;
10import java.io.IOException;
11import java.net.InetSocketAddress;
12import java.net.Proxy;
13import org.apache.http.Header;
14import org.apache.http.HttpEntity;
15import org.apache.http.HttpEntityEnclosingRequest;
16import org.apache.http.HttpHost;
17import org.apache.http.HttpRequest;
18import org.apache.http.HttpResponse;
19import org.apache.http.RequestLine;
20import org.apache.http.client.HttpClient;
21import org.apache.http.client.ResponseHandler;
22import org.apache.http.client.methods.HttpUriRequest;
23import org.apache.http.conn.ClientConnectionManager;
24import org.apache.http.conn.params.ConnRouteParams;
25import org.apache.http.entity.InputStreamEntity;
26import org.apache.http.message.BasicHttpResponse;
27import org.apache.http.params.AbstractHttpParams;
28import org.apache.http.params.HttpParams;
29import org.apache.http.protocol.HttpContext;
30
31import static java.net.Proxy.Type.HTTP;
32import static org.apache.http.HttpVersion.HTTP_1_1;
33
34/**
35 * Implements Apache's {@link HttpClient} API using {@link OkHttpClient}.
36 * <p>
37 * <strong>Warning:</strong> Many core features of Apache HTTP client are not implemented by this
38 * API. This includes the keep-alive strategy, cookie store, credentials provider, route planner
39 * and others.
40 */
41public final class OkApacheClient implements HttpClient {
42  private static Request transformRequest(HttpRequest request) {
43    Request.Builder builder = new Request.Builder();
44
45    RequestLine requestLine = request.getRequestLine();
46    String method = requestLine.getMethod();
47    builder.url(requestLine.getUri());
48
49    String contentType = null;
50    for (Header header : request.getAllHeaders()) {
51      String name = header.getName();
52      if ("Content-Type".equalsIgnoreCase(name)) {
53        contentType = header.getValue();
54      } else {
55        builder.header(name, header.getValue());
56      }
57    }
58
59    RequestBody body = null;
60    if (request instanceof HttpEntityEnclosingRequest) {
61      HttpEntity entity = ((HttpEntityEnclosingRequest) request).getEntity();
62      if (entity != null) {
63        // Wrap the entity in a custom Body which takes care of the content, length, and type.
64        body = new HttpEntityBody(entity, contentType);
65
66        Header encoding = entity.getContentEncoding();
67        if (encoding != null) {
68          builder.header(encoding.getName(), encoding.getValue());
69        }
70      } else {
71        body = RequestBody.create(null, new byte[0]);
72      }
73    }
74    builder.method(method, body);
75
76    return builder.build();
77  }
78
79  private static HttpResponse transformResponse(Response response) throws IOException {
80    int code = response.code();
81    String message = response.message();
82    BasicHttpResponse httpResponse = new BasicHttpResponse(HTTP_1_1, code, message);
83
84    ResponseBody body = response.body();
85    InputStreamEntity entity = new InputStreamEntity(body.byteStream(), body.contentLength());
86    httpResponse.setEntity(entity);
87
88    Headers headers = response.headers();
89    for (int i = 0, size = headers.size(); i < size; i++) {
90      String name = headers.name(i);
91      String value = headers.value(i);
92      httpResponse.addHeader(name, value);
93      if ("Content-Type".equalsIgnoreCase(name)) {
94        entity.setContentType(value);
95      } else if ("Content-Encoding".equalsIgnoreCase(name)) {
96        entity.setContentEncoding(value);
97      }
98    }
99
100    return httpResponse;
101  }
102
103  private final HttpParams params = new AbstractHttpParams() {
104    @Override public Object getParameter(String name) {
105      if (name.equals(ConnRouteParams.DEFAULT_PROXY)) {
106        Proxy proxy = client.getProxy();
107        if (proxy == null) {
108          return null;
109        }
110        InetSocketAddress address = (InetSocketAddress) proxy.address();
111        return new HttpHost(address.getHostName(), address.getPort());
112      }
113      throw new IllegalArgumentException(name);
114    }
115
116    @Override public HttpParams setParameter(String name, Object value) {
117      if (name.equals(ConnRouteParams.DEFAULT_PROXY)) {
118        HttpHost host = (HttpHost) value;
119        Proxy proxy = null;
120        if (host != null) {
121          proxy = new Proxy(HTTP, new InetSocketAddress(host.getHostName(), host.getPort()));
122        }
123        client.setProxy(proxy);
124        return this;
125      }
126      throw new IllegalArgumentException(name);
127    }
128
129    @Override public HttpParams copy() {
130      throw new UnsupportedOperationException();
131    }
132
133    @Override public boolean removeParameter(String name) {
134      throw new UnsupportedOperationException();
135    }
136  };
137
138  private final OkHttpClient client;
139
140  public OkApacheClient() {
141    this(new OkHttpClient());
142  }
143
144  public OkApacheClient(OkHttpClient client) {
145    this.client = client;
146  }
147
148  @Override public HttpParams getParams() {
149    return params;
150  }
151
152  @Override public ClientConnectionManager getConnectionManager() {
153    throw new UnsupportedOperationException();
154  }
155
156  @Override public HttpResponse execute(HttpUriRequest request) throws IOException {
157    return execute(null, request, (HttpContext) null);
158  }
159
160  @Override public HttpResponse execute(HttpUriRequest request, HttpContext context)
161      throws IOException {
162    return execute(null, request, context);
163  }
164
165  @Override public HttpResponse execute(HttpHost host, HttpRequest request) throws IOException {
166    return execute(host, request, (HttpContext) null);
167  }
168
169  @Override public HttpResponse execute(HttpHost host, HttpRequest request, HttpContext context)
170      throws IOException {
171    Request okRequest = transformRequest(request);
172    Response okResponse = client.newCall(okRequest).execute();
173    return transformResponse(okResponse);
174  }
175
176  @Override public <T> T execute(HttpUriRequest request, ResponseHandler<? extends T> handler)
177      throws IOException {
178    return execute(null, request, handler, null);
179  }
180
181  @Override public <T> T execute(HttpUriRequest request, ResponseHandler<? extends T> handler,
182      HttpContext context) throws IOException {
183    return execute(null, request, handler, context);
184  }
185
186  @Override public <T> T execute(HttpHost host, HttpRequest request,
187      ResponseHandler<? extends T> handler) throws IOException {
188    return execute(host, request, handler, null);
189  }
190
191  @Override public <T> T execute(HttpHost host, HttpRequest request,
192      ResponseHandler<? extends T> handler, HttpContext context) throws IOException {
193    HttpResponse response = execute(host, request, context);
194    try {
195      return handler.handleResponse(response);
196    } finally {
197      consumeContentQuietly(response);
198    }
199  }
200
201  private static void consumeContentQuietly(HttpResponse response) {
202    try {
203      response.getEntity().consumeContent();
204    } catch (Throwable ignored) {
205    }
206  }
207}
208