Skip to content

Commit f0fdff1

Browse files
committed
OkHttpClientHttpRequestFactory allows POST request without body
Issue: SPR-15015 (cherry picked from commit 2954385)
1 parent d15df34 commit f0fdff1

File tree

4 files changed

+126
-107
lines changed

4 files changed

+126
-107
lines changed

spring-web/src/main/java/org/springframework/http/client/OkHttp3ClientHttpRequestFactory.java

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import java.io.IOException;
2020
import java.net.MalformedURLException;
2121
import java.net.URI;
22-
import java.net.URL;
2322
import java.util.List;
2423
import java.util.Map;
2524
import java.util.concurrent.TimeUnit;
@@ -73,7 +72,7 @@ public OkHttp3ClientHttpRequestFactory(OkHttpClient client) {
7372
/**
7473
* Sets the underlying read timeout in milliseconds.
7574
* A value of 0 specifies an infinite timeout.
76-
* @see okhttp3.OkHttpClient.Builder#readTimeout(long, TimeUnit)
75+
* @see OkHttpClient.Builder#readTimeout(long, TimeUnit)
7776
*/
7877
public void setReadTimeout(int readTimeout) {
7978
this.client = this.client.newBuilder()
@@ -84,7 +83,7 @@ public void setReadTimeout(int readTimeout) {
8483
/**
8584
* Sets the underlying write timeout in milliseconds.
8685
* A value of 0 specifies an infinite timeout.
87-
* @see okhttp3.OkHttpClient.Builder#writeTimeout(long, TimeUnit)
86+
* @see OkHttpClient.Builder#writeTimeout(long, TimeUnit)
8887
*/
8988
public void setWriteTimeout(int writeTimeout) {
9089
this.client = this.client.newBuilder()
@@ -95,7 +94,7 @@ public void setWriteTimeout(int writeTimeout) {
9594
/**
9695
* Sets the underlying connect timeout in milliseconds.
9796
* A value of 0 specifies an infinite timeout.
98-
* @see okhttp3.OkHttpClient.Builder#connectTimeout(long, TimeUnit)
97+
* @see OkHttpClient.Builder#connectTimeout(long, TimeUnit)
9998
*/
10099
public void setConnectTimeout(int connectTimeout) {
101100
this.client = this.client.newBuilder()
@@ -127,23 +126,21 @@ public void destroy() throws IOException {
127126
}
128127

129128

130-
static Request buildRequest(HttpHeaders headers, byte[] content, URI uri,
131-
HttpMethod method) throws MalformedURLException {
129+
static Request buildRequest(HttpHeaders headers, byte[] content, URI uri, HttpMethod method)
130+
throws MalformedURLException {
132131

133132
okhttp3.MediaType contentType = getContentType(headers);
134-
RequestBody body = (content.length > 0 ? RequestBody.create(contentType, content) : null);
135-
136-
URL url = uri.toURL();
137-
String methodName = method.name();
138-
Request.Builder builder = new Request.Builder().url(url).method(methodName, body);
133+
RequestBody body = (content.length > 0 ||
134+
okhttp3.internal.http.HttpMethod.requiresRequestBody(method.name()) ?
135+
RequestBody.create(contentType, content) : null);
139136

137+
Request.Builder builder = new Request.Builder().url(uri.toURL()).method(method.name(), body);
140138
for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
141139
String headerName = entry.getKey();
142140
for (String headerValue : entry.getValue()) {
143141
builder.addHeader(headerName, headerValue);
144142
}
145143
}
146-
147144
return builder.build();
148145
}
149146

spring-web/src/main/java/org/springframework/http/client/OkHttpClientHttpRequestFactory.java

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import java.io.IOException;
2020
import java.net.MalformedURLException;
2121
import java.net.URI;
22-
import java.net.URL;
2322
import java.util.List;
2423
import java.util.Map;
2524
import java.util.concurrent.TimeUnit;
@@ -121,23 +120,21 @@ public void destroy() throws IOException {
121120
}
122121

123122

124-
static Request buildRequest(HttpHeaders headers, byte[] content, URI uri,
125-
HttpMethod method) throws MalformedURLException {
123+
static Request buildRequest(HttpHeaders headers, byte[] content, URI uri, HttpMethod method)
124+
throws MalformedURLException {
126125

127126
com.squareup.okhttp.MediaType contentType = getContentType(headers);
128-
RequestBody body = (content.length > 0 ? RequestBody.create(contentType, content) : null);
129-
130-
URL url = uri.toURL();
131-
String methodName = method.name();
132-
Request.Builder builder = new Request.Builder().url(url).method(methodName, body);
127+
RequestBody body = (content.length > 0 ||
128+
com.squareup.okhttp.internal.http.HttpMethod.requiresRequestBody(method.name()) ?
129+
RequestBody.create(contentType, content) : null);
133130

131+
Request.Builder builder = new Request.Builder().url(uri.toURL()).method(method.name(), body);
134132
for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
135133
String headerName = entry.getKey();
136134
for (String headerValue : entry.getValue()) {
137135
builder.addHeader(headerName, headerValue);
138136
}
139137
}
140-
141138
return builder.build();
142139
}
143140

spring-web/src/test/java/org/springframework/web/client/AbstractMockWebServerTestCase.java

Lines changed: 94 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
1+
/*
2+
* Copyright 2002-2016 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
117
package org.springframework.web.client;
218

319
import java.io.EOFException;
@@ -15,21 +31,18 @@
1531

1632
import org.springframework.http.MediaType;
1733

18-
import static org.junit.Assert.assertEquals;
19-
import static org.junit.Assert.assertNotNull;
20-
import static org.junit.Assert.assertThat;
21-
import static org.junit.Assert.assertTrue;
34+
import static org.junit.Assert.*;
2235

2336
/**
2437
* @author Brian Clozel
2538
*/
2639
public class AbstractMockWebServerTestCase {
2740

28-
protected static final String helloWorld = "H\u00e9llo W\u00f6rld";
41+
private static final Charset UTF_8 = Charset.forName("UTF-8");
2942

30-
private static final Charset UTF_8 = Charset.forName("UTF-8");
43+
private static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1");
3144

32-
private static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1");
45+
protected static final String helloWorld = "H\u00e9llo W\u00f6rld";
3346

3447
protected static final MediaType textContentType =
3548
new MediaType("text", "plain", Collections.singletonMap("charset", "UTF-8"));
@@ -40,6 +53,7 @@ public class AbstractMockWebServerTestCase {
4053

4154
protected String baseUrl;
4255

56+
4357
@Before
4458
public void setUp() throws Exception {
4559
this.server = new MockWebServer();
@@ -54,68 +68,9 @@ public void tearDown() throws Exception {
5468
this.server.shutdown();
5569
}
5670

57-
protected class TestDispatcher extends Dispatcher {
58-
@Override
59-
public MockResponse dispatch(RecordedRequest request) throws InterruptedException {
60-
try {
61-
byte[] helloWorldBytes = helloWorld.getBytes(UTF_8);
62-
63-
if (request.getPath().equals("/get")) {
64-
return getRequest(request, helloWorldBytes, textContentType.toString());
65-
}
66-
else if (request.getPath().equals("/get/nothing")) {
67-
return getRequest(request, new byte[0], textContentType.toString());
68-
}
69-
else if (request.getPath().equals("/get/nocontenttype")) {
70-
return getRequest(request, helloWorldBytes, null);
71-
}
72-
else if (request.getPath().equals("/post")) {
73-
return postRequest(request, helloWorld, "/post/1", textContentType.toString(), helloWorldBytes);
74-
}
75-
else if (request.getPath().equals("/jsonpost")) {
76-
return jsonPostRequest(request, "/jsonpost/1", "application/json; charset=utf-8");
77-
}
78-
else if (request.getPath().equals("/status/nocontent")) {
79-
return new MockResponse().setResponseCode(204);
80-
}
81-
else if (request.getPath().equals("/status/notmodified")) {
82-
return new MockResponse().setResponseCode(304);
83-
}
84-
else if (request.getPath().equals("/status/notfound")) {
85-
return new MockResponse().setResponseCode(404);
86-
}
87-
else if (request.getPath().equals("/status/server")) {
88-
return new MockResponse().setResponseCode(500);
89-
}
90-
else if (request.getPath().contains("/uri/")) {
91-
return new MockResponse().setBody(request.getPath()).setHeader("Content-Type", "text/plain");
92-
}
93-
else if (request.getPath().equals("/multipart")) {
94-
return multipartRequest(request);
95-
}
96-
else if (request.getPath().equals("/form")) {
97-
return formRequest(request);
98-
}
99-
else if (request.getPath().equals("/delete")) {
100-
return new MockResponse().setResponseCode(200);
101-
}
102-
else if (request.getPath().equals("/patch")) {
103-
return patchRequest(request, helloWorld, textContentType.toString(), helloWorldBytes);
104-
}
105-
else if (request.getPath().equals("/put")) {
106-
return putRequest(request, helloWorld);
107-
}
108-
return new MockResponse().setResponseCode(404);
109-
}
110-
catch (Throwable exc) {
111-
return new MockResponse().setResponseCode(500).setBody(exc.toString());
112-
}
113-
}
114-
}
115-
11671

11772
private MockResponse getRequest(RecordedRequest request, byte[] body, String contentType) {
118-
if(request.getMethod().equals("OPTIONS")) {
73+
if (request.getMethod().equals("OPTIONS")) {
11974
return new MockResponse().setResponseCode(200).setHeader("Allow", "GET, OPTIONS, HEAD, TRACE");
12075
}
12176
Buffer buf = new Buffer();
@@ -138,7 +93,7 @@ private MockResponse postRequest(RecordedRequest request, String expectedRequest
13893
String requestContentType = request.getHeader("Content-Type");
13994
assertNotNull("No content-type", requestContentType);
14095
Charset charset = ISO_8859_1;
141-
if(requestContentType.indexOf("charset=") > -1) {
96+
if (requestContentType.contains("charset=")) {
14297
String charsetName = requestContentType.split("charset=")[1];
14398
charset = Charset.forName(charsetName);
14499
}
@@ -154,10 +109,11 @@ private MockResponse postRequest(RecordedRequest request, String expectedRequest
154109
}
155110

156111
private MockResponse jsonPostRequest(RecordedRequest request, String location, String contentType) {
157-
158-
assertTrue("Invalid request content-length",
159-
Integer.parseInt(request.getHeader("Content-Length")) > 0);
160-
assertNotNull("No content-type", request.getHeader("Content-Type"));
112+
if (request.getBodySize() > 0) {
113+
assertTrue("Invalid request content-length",
114+
Integer.parseInt(request.getHeader("Content-Length")) > 0);
115+
assertNotNull("No content-type", request.getHeader("Content-Type"));
116+
}
161117
return new MockResponse()
162118
.setHeader("Location", baseUrl + location)
163119
.setHeader("Content-Type", contentType)
@@ -177,8 +133,8 @@ private MockResponse multipartRequest(RecordedRequest request) {
177133
assertPart(body, "form-data", boundary, "name 2", "text/plain", "value 2+2");
178134
assertFilePart(body, "form-data", boundary, "logo", "logo.jpg", "image/jpeg");
179135
}
180-
catch (EOFException e) {
181-
throw new RuntimeException(e);
136+
catch (EOFException ex) {
137+
throw new IllegalStateException(ex);
182138
}
183139
return new MockResponse().setResponseCode(200);
184140
}
@@ -221,13 +177,14 @@ private MockResponse formRequest(RecordedRequest request) {
221177

222178
private MockResponse patchRequest(RecordedRequest request, String expectedRequestContent,
223179
String contentType, byte[] responseBody) {
180+
224181
assertEquals("PATCH", request.getMethod());
225182
assertTrue("Invalid request content-length",
226183
Integer.parseInt(request.getHeader("Content-Length")) > 0);
227184
String requestContentType = request.getHeader("Content-Type");
228185
assertNotNull("No content-type", requestContentType);
229186
Charset charset = ISO_8859_1;
230-
if(requestContentType.indexOf("charset=") > -1) {
187+
if (requestContentType.contains("charset=")) {
231188
String charsetName = requestContentType.split("charset=")[1];
232189
charset = Charset.forName(charsetName);
233190
}
@@ -246,12 +203,73 @@ private MockResponse putRequest(RecordedRequest request, String expectedRequestC
246203
String requestContentType = request.getHeader("Content-Type");
247204
assertNotNull("No content-type", requestContentType);
248205
Charset charset = ISO_8859_1;
249-
if(requestContentType.indexOf("charset=") > -1) {
206+
if (requestContentType.contains("charset=")) {
250207
String charsetName = requestContentType.split("charset=")[1];
251208
charset = Charset.forName(charsetName);
252209
}
253210
assertEquals("Invalid request body", expectedRequestContent, request.getBody().readString(charset));
254211
return new MockResponse().setResponseCode(202);
255212
}
256213

214+
215+
protected class TestDispatcher extends Dispatcher {
216+
217+
@Override
218+
public MockResponse dispatch(RecordedRequest request) throws InterruptedException {
219+
try {
220+
byte[] helloWorldBytes = helloWorld.getBytes(UTF_8);
221+
222+
if (request.getPath().equals("/get")) {
223+
return getRequest(request, helloWorldBytes, textContentType.toString());
224+
}
225+
else if (request.getPath().equals("/get/nothing")) {
226+
return getRequest(request, new byte[0], textContentType.toString());
227+
}
228+
else if (request.getPath().equals("/get/nocontenttype")) {
229+
return getRequest(request, helloWorldBytes, null);
230+
}
231+
else if (request.getPath().equals("/post")) {
232+
return postRequest(request, helloWorld, "/post/1", textContentType.toString(), helloWorldBytes);
233+
}
234+
else if (request.getPath().equals("/jsonpost")) {
235+
return jsonPostRequest(request, "/jsonpost/1", "application/json; charset=utf-8");
236+
}
237+
else if (request.getPath().equals("/status/nocontent")) {
238+
return new MockResponse().setResponseCode(204);
239+
}
240+
else if (request.getPath().equals("/status/notmodified")) {
241+
return new MockResponse().setResponseCode(304);
242+
}
243+
else if (request.getPath().equals("/status/notfound")) {
244+
return new MockResponse().setResponseCode(404);
245+
}
246+
else if (request.getPath().equals("/status/server")) {
247+
return new MockResponse().setResponseCode(500);
248+
}
249+
else if (request.getPath().contains("/uri/")) {
250+
return new MockResponse().setBody(request.getPath()).setHeader("Content-Type", "text/plain");
251+
}
252+
else if (request.getPath().equals("/multipart")) {
253+
return multipartRequest(request);
254+
}
255+
else if (request.getPath().equals("/form")) {
256+
return formRequest(request);
257+
}
258+
else if (request.getPath().equals("/delete")) {
259+
return new MockResponse().setResponseCode(200);
260+
}
261+
else if (request.getPath().equals("/patch")) {
262+
return patchRequest(request, helloWorld, textContentType.toString(), helloWorldBytes);
263+
}
264+
else if (request.getPath().equals("/put")) {
265+
return putRequest(request, helloWorld);
266+
}
267+
return new MockResponse().setResponseCode(404);
268+
}
269+
catch (Throwable ex) {
270+
return new MockResponse().setResponseCode(500).setBody(ex.toString());
271+
}
272+
}
273+
}
274+
257275
}

0 commit comments

Comments
 (0)