001/* 002 * Copyright 2002-2019 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.concurrent.TimeUnit; 023 024import okhttp3.Cache; 025import okhttp3.OkHttpClient; 026import okhttp3.Request; 027import okhttp3.RequestBody; 028 029import org.springframework.beans.factory.DisposableBean; 030import org.springframework.http.HttpHeaders; 031import org.springframework.http.HttpMethod; 032import org.springframework.lang.Nullable; 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> 3.x to create requests. 039 * 040 * @author Luciano Leggieri 041 * @author Arjen Poutsma 042 * @author Roy Clarkson 043 * @since 4.3 044 */ 045@SuppressWarnings("deprecation") 046public class OkHttp3ClientHttpRequestFactory 047 implements ClientHttpRequestFactory, AsyncClientHttpRequestFactory, DisposableBean { 048 049 private OkHttpClient client; 050 051 private final boolean defaultClient; 052 053 054 /** 055 * Create a factory with a default {@link OkHttpClient} instance. 056 */ 057 public OkHttp3ClientHttpRequestFactory() { 058 this.client = new OkHttpClient(); 059 this.defaultClient = true; 060 } 061 062 /** 063 * Create a factory with the given {@link OkHttpClient} instance. 064 * @param client the client to use 065 */ 066 public OkHttp3ClientHttpRequestFactory(OkHttpClient client) { 067 Assert.notNull(client, "OkHttpClient must not be null"); 068 this.client = client; 069 this.defaultClient = false; 070 } 071 072 073 /** 074 * Set the underlying read timeout in milliseconds. 075 * A value of 0 specifies an infinite timeout. 076 */ 077 public void setReadTimeout(int readTimeout) { 078 this.client = this.client.newBuilder() 079 .readTimeout(readTimeout, TimeUnit.MILLISECONDS) 080 .build(); 081 } 082 083 /** 084 * Set the underlying write timeout in milliseconds. 085 * A value of 0 specifies an infinite timeout. 086 */ 087 public void setWriteTimeout(int writeTimeout) { 088 this.client = this.client.newBuilder() 089 .writeTimeout(writeTimeout, TimeUnit.MILLISECONDS) 090 .build(); 091 } 092 093 /** 094 * Set the underlying connect timeout in milliseconds. 095 * A value of 0 specifies an infinite timeout. 096 */ 097 public void setConnectTimeout(int connectTimeout) { 098 this.client = this.client.newBuilder() 099 .connectTimeout(connectTimeout, TimeUnit.MILLISECONDS) 100 .build(); 101 } 102 103 104 @Override 105 public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) { 106 return new OkHttp3ClientHttpRequest(this.client, uri, httpMethod); 107 } 108 109 @Override 110 public AsyncClientHttpRequest createAsyncRequest(URI uri, HttpMethod httpMethod) { 111 return new OkHttp3AsyncClientHttpRequest(this.client, uri, httpMethod); 112 } 113 114 115 @Override 116 public void destroy() throws IOException { 117 if (this.defaultClient) { 118 // Clean up the client if we created it in the constructor 119 Cache cache = this.client.cache(); 120 if (cache != null) { 121 cache.close(); 122 } 123 this.client.dispatcher().executorService().shutdown(); 124 this.client.connectionPool().evictAll(); 125 } 126 } 127 128 129 static Request buildRequest(HttpHeaders headers, byte[] content, URI uri, HttpMethod method) 130 throws MalformedURLException { 131 132 okhttp3.MediaType contentType = getContentType(headers); 133 RequestBody body = (content.length > 0 || 134 okhttp3.internal.http.HttpMethod.requiresRequestBody(method.name()) ? 135 RequestBody.create(contentType, content) : null); 136 137 Request.Builder builder = new Request.Builder().url(uri.toURL()).method(method.name(), body); 138 headers.forEach((headerName, headerValues) -> { 139 for (String headerValue : headerValues) { 140 builder.addHeader(headerName, headerValue); 141 } 142 }); 143 return builder.build(); 144 } 145 146 @Nullable 147 private static okhttp3.MediaType getContentType(HttpHeaders headers) { 148 String rawContentType = headers.getFirst(HttpHeaders.CONTENT_TYPE); 149 return (StringUtils.hasText(rawContentType) ? okhttp3.MediaType.parse(rawContentType) : null); 150 } 151 152}