1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.chemistry.opencmis.client.bindings.spi.http;
20
21 import static org.apache.chemistry.opencmis.commons.impl.CollectionsHelper.isNotEmpty;
22
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.io.OutputStream;
26 import java.math.BigInteger;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.concurrent.TimeUnit;
30
31 import javax.net.ssl.HostnameVerifier;
32 import javax.net.ssl.SSLSocketFactory;
33 import javax.net.ssl.X509TrustManager;
34
35 import org.apache.chemistry.opencmis.client.bindings.impl.ClientVersion;
36 import org.apache.chemistry.opencmis.client.bindings.impl.CmisBindingsHelper;
37 import org.apache.chemistry.opencmis.client.bindings.spi.AbstractAuthenticationProvider;
38 import org.apache.chemistry.opencmis.client.bindings.spi.BindingSession;
39 import org.apache.chemistry.opencmis.commons.SessionParameter;
40 import org.apache.chemistry.opencmis.commons.exceptions.CmisConnectionException;
41 import org.apache.chemistry.opencmis.commons.exceptions.CmisRuntimeException;
42 import org.apache.chemistry.opencmis.commons.impl.UrlBuilder;
43 import org.apache.chemistry.opencmis.commons.spi.AuthenticationProvider;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
46
47 import okhttp3.MediaType;
48 import okhttp3.OkHttpClient;
49 import okhttp3.Request;
50 import okhttp3.RequestBody;
51 import okio.BufferedSink;
52
53 public class OkHttpHttpInvoker implements HttpInvoker {
54
55 private static final Logger LOG = LoggerFactory.getLogger(OkHttpHttpInvoker.class);
56
57 protected static final String HTTP_CLIENT = "org.apache.chemistry.opencmis.client.bindings.spi.http.OkHttpHttpInvoker.httpClient";
58
59 public OkHttpHttpInvoker() {
60 }
61
62 @Override
63 public Response invokeGET(UrlBuilder url, BindingSession session) {
64 return invoke(url, "GET", null, null, null, session, null, null);
65 }
66
67 @Override
68 public Response invokeGET(UrlBuilder url, BindingSession session, BigInteger offset, BigInteger length) {
69 return invoke(url, "GET", null, null, null, session, offset, length);
70 }
71
72 @Override
73 public Response invokePOST(UrlBuilder url, String contentType, Output writer, BindingSession session) {
74 return invoke(url, "POST", contentType, null, writer, session, null, null);
75 }
76
77 @Override
78 public Response invokePUT(UrlBuilder url, String contentType, Map<String, String> headers, Output writer,
79 BindingSession session) {
80 return invoke(url, "PUT", contentType, headers, writer, session, null, null);
81 }
82
83 @Override
84 public Response invokeDELETE(UrlBuilder url, BindingSession session) {
85 return invoke(url, "DELETE", null, null, null, session, null, null);
86 }
87
88 private Response invoke(UrlBuilder url, String method, final String contentType, Map<String, String> headers,
89 final Output writer, BindingSession session, BigInteger offset, BigInteger length) {
90 int respCode = -1;
91
92 try {
93
94 if (LOG.isDebugEnabled()) {
95 LOG.debug("Session {}: {} {}", session.getSessionId(), method, url);
96 }
97
98
99 OkHttpClient httpclient = (OkHttpClient) session.get(HTTP_CLIENT);
100 if (httpclient == null) {
101 session.writeLock();
102 try {
103 httpclient = (OkHttpClient) session.get(HTTP_CLIENT);
104 if (httpclient == null) {
105 httpclient = createClientBuilder(session).build();
106 session.put(HTTP_CLIENT, httpclient, true);
107 }
108 } finally {
109 session.writeUnlock();
110 }
111 }
112
113
114 Request.Builder requestBuilder = new Request.Builder().url(url.toString());
115
116
117 RequestBody body = null;
118 if (writer != null) {
119 body = new RequestBody() {
120
121 @Override
122 public void writeTo(BufferedSink sink) throws IOException {
123 try {
124 OutputStream out = sink.outputStream();
125 writer.write(out);
126 out.flush();
127 } catch (IOException ioe) {
128 throw ioe;
129 } catch (Exception e) {
130 throw new IOException("Could not send stream to server: " + e.toString(), e);
131 }
132 }
133
134 @Override
135 public MediaType contentType() {
136 if (contentType != null) {
137 return MediaType.parse(contentType);
138 } else {
139 return MediaType.parse("application/octet-stream");
140 }
141 }
142 };
143 }
144
145 if ("GET".equals(method)) {
146 requestBuilder.get();
147 } else if ("POST".equals(method)) {
148 requestBuilder.post(body);
149 } else if ("PUT".equals(method)) {
150 requestBuilder.put(body);
151 } else if ("DELETE".equals(method)) {
152 requestBuilder.delete();
153 } else {
154 throw new CmisRuntimeException("Invalid HTTP method!");
155 }
156
157
158 if (contentType != null) {
159 requestBuilder.header("Content-Type", contentType);
160 }
161
162 if (headers != null) {
163 for (Map.Entry<String, String> header : headers.entrySet()) {
164 requestBuilder.addHeader(header.getKey(), header.getValue());
165 }
166 }
167
168 requestBuilder.header("User-Agent",
169 (String) session.get(SessionParameter.USER_AGENT, ClientVersion.OPENCMIS_USER_AGENT));
170
171
172 AuthenticationProvider authProvider = CmisBindingsHelper.getAuthenticationProvider(session);
173 if (authProvider != null) {
174 Map<String, List<String>> httpHeaders = authProvider.getHTTPHeaders(url.toString());
175 if (httpHeaders != null) {
176 for (Map.Entry<String, List<String>> header : httpHeaders.entrySet()) {
177 if (header.getKey() != null && isNotEmpty(header.getValue())) {
178 String key = header.getKey();
179 if (key.equalsIgnoreCase("user-agent")) {
180 requestBuilder.header("User-Agent", header.getValue().get(0));
181 } else {
182 for (String value : header.getValue()) {
183 if (value != null) {
184 requestBuilder.addHeader(key, value);
185 }
186 }
187 }
188 }
189 }
190 }
191 }
192
193
194 if (offset != null || length != null) {
195 StringBuilder sb = new StringBuilder("bytes=");
196
197 if ((offset == null) || (offset.signum() == -1)) {
198 offset = BigInteger.ZERO;
199 }
200
201 sb.append(offset.toString());
202 sb.append('-');
203
204 if (length != null && length.signum() == 1) {
205 sb.append(offset.add(length.subtract(BigInteger.ONE)).toString());
206 }
207
208 requestBuilder.header("Range", sb.toString());
209 }
210
211
212 Object compression = session.get(SessionParameter.COMPRESSION);
213 if (compression != null && Boolean.parseBoolean(compression.toString())) {
214 requestBuilder.header("Accept-Encoding", "gzip,deflate");
215 }
216
217
218 if (session.get(CmisBindingsHelper.ACCEPT_LANGUAGE) instanceof String) {
219 requestBuilder.header("Accept-Language", session.get(CmisBindingsHelper.ACCEPT_LANGUAGE).toString());
220 }
221
222 okhttp3.Response okResponse = httpclient.newCall(requestBuilder.build()).execute();
223
224
225 respCode = okResponse.code();
226 InputStream inputStream = null;
227 InputStream errorStream = null;
228
229 if (respCode == 200 || respCode == 201 || respCode == 203 || respCode == 206) {
230 inputStream = okResponse.body().byteStream();
231 } else {
232 errorStream = okResponse.body().byteStream();
233 }
234
235 Map<String, List<String>> responseHeaders = okResponse.headers().toMultimap();
236
237
238 if (LOG.isTraceEnabled()) {
239 LOG.trace("Session {}: {} {} > Headers: {}", session.getSessionId(), method, url,
240 responseHeaders.toString());
241 }
242
243
244 if (authProvider != null) {
245 authProvider.putResponseHeaders(url.toString(), respCode, responseHeaders);
246 }
247
248
249 return new Response(respCode, okResponse.message(), responseHeaders, inputStream, errorStream);
250 } catch (Exception e) {
251 throw new CmisConnectionException(url.toString(), respCode, e);
252 }
253 }
254
255
256
257
258
259
260
261
262
263
264
265
266 @SuppressWarnings("deprecation")
267 protected OkHttpClient.Builder createClientBuilder(BindingSession session) {
268 OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder();
269
270
271 int connectTimeout = session.get(SessionParameter.CONNECT_TIMEOUT, -1);
272 if (connectTimeout >= 0) {
273 clientBuilder.connectTimeout(connectTimeout, TimeUnit.MILLISECONDS);
274 }
275
276 int readTimeout = session.get(SessionParameter.READ_TIMEOUT, -1);
277 if (readTimeout >= 0) {
278 clientBuilder.readTimeout(readTimeout, TimeUnit.MILLISECONDS);
279 }
280
281 AuthenticationProvider authProvider = CmisBindingsHelper.getAuthenticationProvider(session);
282 if (authProvider != null) {
283 SSLSocketFactory sf = authProvider.getSSLSocketFactory();
284 if (sf != null) {
285 X509TrustManager tm = null;
286
287 if (authProvider instanceof AbstractAuthenticationProvider) {
288 tm = ((AbstractAuthenticationProvider) authProvider).getTrustManager();
289 }
290
291 if (tm == null) {
292 clientBuilder.sslSocketFactory(sf);
293 } else {
294 clientBuilder.sslSocketFactory(sf, tm);
295 }
296 }
297
298 HostnameVerifier hv = authProvider.getHostnameVerifier();
299 if (hv != null) {
300 clientBuilder.hostnameVerifier(hv);
301 }
302 }
303
304 return clientBuilder;
305 }
306 }