001/*
002 * Copyright 2002-2016 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.http.client;
018
019import java.io.IOException;
020import java.net.MalformedURLException;
021import java.net.URI;
022import java.util.List;
023import java.util.Map;
024import java.util.concurrent.TimeUnit;
025
026import com.squareup.okhttp.OkHttpClient;
027import com.squareup.okhttp.Request;
028import com.squareup.okhttp.RequestBody;
029
030import org.springframework.beans.factory.DisposableBean;
031import org.springframework.http.HttpHeaders;
032import org.springframework.http.HttpMethod;
033import org.springframework.util.Assert;
034import org.springframework.util.StringUtils;
035
036/**
037 * {@link ClientHttpRequestFactory} implementation that uses
038 * <a href="https://square.github.io/okhttp/">OkHttp</a> 2.x to create requests.
039 *
040 * @author Luciano Leggieri
041 * @author Arjen Poutsma
042 * @since 4.2
043 * @see org.springframework.http.client.OkHttp3ClientHttpRequestFactory
044 */
045public class OkHttpClientHttpRequestFactory
046                implements ClientHttpRequestFactory, AsyncClientHttpRequestFactory, DisposableBean {
047
048        private final OkHttpClient client;
049
050        private final boolean defaultClient;
051
052
053        /**
054         * Create a factory with a default {@link OkHttpClient} instance.
055         */
056        public OkHttpClientHttpRequestFactory() {
057                this.client = new OkHttpClient();
058                this.defaultClient = true;
059        }
060
061        /**
062         * Create a factory with the given {@link OkHttpClient} instance.
063         * @param client the client to use
064         */
065        public OkHttpClientHttpRequestFactory(OkHttpClient client) {
066                Assert.notNull(client, "OkHttpClient must not be null");
067                this.client = client;
068                this.defaultClient = false;
069        }
070
071
072        /**
073         * Sets the underlying read timeout in milliseconds.
074         * A value of 0 specifies an infinite timeout.
075         * @see OkHttpClient#setReadTimeout(long, TimeUnit)
076         */
077        public void setReadTimeout(int readTimeout) {
078                this.client.setReadTimeout(readTimeout, TimeUnit.MILLISECONDS);
079        }
080
081        /**
082         * Sets the underlying write timeout in milliseconds.
083         * A value of 0 specifies an infinite timeout.
084         * @see OkHttpClient#setWriteTimeout(long, TimeUnit)
085         */
086        public void setWriteTimeout(int writeTimeout) {
087                this.client.setWriteTimeout(writeTimeout, TimeUnit.MILLISECONDS);
088        }
089
090        /**
091         * Sets the underlying connect timeout in milliseconds.
092         * A value of 0 specifies an infinite timeout.
093         * @see OkHttpClient#setConnectTimeout(long, TimeUnit)
094         */
095        public void setConnectTimeout(int connectTimeout) {
096                this.client.setConnectTimeout(connectTimeout, TimeUnit.MILLISECONDS);
097        }
098
099
100        @Override
101        public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) {
102                return new OkHttpClientHttpRequest(this.client, uri, httpMethod);
103        }
104
105        @Override
106        public AsyncClientHttpRequest createAsyncRequest(URI uri, HttpMethod httpMethod) {
107                return new OkHttpAsyncClientHttpRequest(this.client, uri, httpMethod);
108        }
109
110
111        @Override
112        public void destroy() throws IOException {
113                if (this.defaultClient) {
114                        // Clean up the client if we created it in the constructor
115                        if (this.client.getCache() != null) {
116                                this.client.getCache().close();
117                        }
118                        this.client.getDispatcher().getExecutorService().shutdown();
119                }
120        }
121
122
123        static Request buildRequest(HttpHeaders headers, byte[] content, URI uri, HttpMethod method)
124                        throws MalformedURLException {
125
126                com.squareup.okhttp.MediaType contentType = getContentType(headers);
127                RequestBody body = (content.length > 0 ||
128                                com.squareup.okhttp.internal.http.HttpMethod.requiresRequestBody(method.name()) ?
129                                RequestBody.create(contentType, content) : null);
130
131                Request.Builder builder = new Request.Builder().url(uri.toURL()).method(method.name(), body);
132                for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
133                        String headerName = entry.getKey();
134                        for (String headerValue : entry.getValue()) {
135                                builder.addHeader(headerName, headerValue);
136                        }
137                }
138                return builder.build();
139        }
140
141        private static com.squareup.okhttp.MediaType getContentType(HttpHeaders headers) {
142                String rawContentType = headers.getFirst(HttpHeaders.CONTENT_TYPE);
143                return (StringUtils.hasText(rawContentType) ?
144                                com.squareup.okhttp.MediaType.parse(rawContentType) : null);
145        }
146
147}