HttpClient based ClientHttpRequestFactory
As JDK17 is now the baseline it is possible to use the HttpClient provided by Java.
This commit is contained in:
parent
c2e3fed8dc
commit
2ca8dd2faa
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* Copyright 2023-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.http.client;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
|
||||
/**
|
||||
* {@link ClientHttpRequest} implementation based on the Java {@code HttpClient}.
|
||||
*
|
||||
* @author Marten Deinum
|
||||
* @since 6.1
|
||||
*/
|
||||
public class JdkClientClientHttpRequest extends AbstractBufferingClientHttpRequest {
|
||||
|
||||
/*
|
||||
* The JDK HttpRequest doesn't allow all headers to be set. The named headers are taken from the default
|
||||
* implementation for HttpRequest.
|
||||
*/
|
||||
private static final List<String> DISALLOWED_HEADERS =
|
||||
List.of("connection", "content-length", "expect", "host", "upgrade");
|
||||
|
||||
private final HttpClient client;
|
||||
private final URI uri;
|
||||
private final HttpMethod method;
|
||||
public JdkClientClientHttpRequest(HttpClient client, URI uri, HttpMethod method) {
|
||||
this.client = client;
|
||||
this.uri = uri;
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpMethod getMethod() {
|
||||
return this.method;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI getURI() {
|
||||
return this.uri;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ClientHttpResponse executeInternal(HttpHeaders headers, byte[] content) throws IOException {
|
||||
|
||||
HttpRequest.Builder builder = HttpRequest.newBuilder(this.uri)
|
||||
.method(getMethod().name(), HttpRequest.BodyPublishers.ofByteArray(content));
|
||||
|
||||
addHeaders(headers, builder);
|
||||
HttpRequest request = builder.build();
|
||||
HttpResponse<InputStream> response;
|
||||
try {
|
||||
response = this.client.send(request, HttpResponse.BodyHandlers.ofInputStream());
|
||||
} catch (InterruptedException ex)
|
||||
{
|
||||
Thread.currentThread().interrupt();
|
||||
throw new IllegalStateException("Request interupted.", ex);
|
||||
}
|
||||
return new JdkClientClientHttpResponse(response);
|
||||
}
|
||||
|
||||
private static void addHeaders(HttpHeaders headers, HttpRequest.Builder builder) {
|
||||
headers.forEach((headerName, headerValues) -> {
|
||||
if (!DISALLOWED_HEADERS.contains(headerName.toLowerCase())) {
|
||||
for (String headerValue : headerValues) {
|
||||
builder.header(headerName, headerValue);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright 2023-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.http.client;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
|
||||
import org.springframework.http.HttpMethod;
|
||||
|
||||
|
||||
/**
|
||||
* {@link ClientHttpRequestFactory} implementation that uses a
|
||||
* <a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.net.http/java/net/http/HttpClient.html">HttpClient</a> to create requests.
|
||||
*
|
||||
* @author Marten Deinum
|
||||
* @since 6.1
|
||||
*/
|
||||
public class JdkClientClientHttpRequestFactory implements ClientHttpRequestFactory {
|
||||
|
||||
private HttpClient client;
|
||||
|
||||
private final boolean defaultClient;
|
||||
|
||||
|
||||
public JdkClientClientHttpRequestFactory() {
|
||||
this.client = HttpClient.newHttpClient();
|
||||
this.defaultClient = true;
|
||||
}
|
||||
|
||||
public JdkClientClientHttpRequestFactory(HttpClient client) {
|
||||
this.client = client;
|
||||
this.defaultClient = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
|
||||
return new JdkClientClientHttpRequest(this.client, uri, httpMethod);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* Copyright 2023-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.http.client;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.http.HttpResponse;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.HttpStatusCode;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.StreamUtils;
|
||||
|
||||
/**
|
||||
* {@link ClientHttpResponse} implementation based on the Java {@code HttpClient}.
|
||||
*
|
||||
* @author Marten Deinum
|
||||
* @since 6.1
|
||||
*/
|
||||
public class JdkClientClientHttpResponse implements ClientHttpResponse {
|
||||
|
||||
private final HttpResponse<InputStream> response;
|
||||
@Nullable
|
||||
private volatile HttpHeaders headers;
|
||||
|
||||
public JdkClientClientHttpResponse(HttpResponse<InputStream> response) {
|
||||
this.response = response;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpStatusCode getStatusCode() throws IOException {
|
||||
return HttpStatusCode.valueOf(this.response.statusCode());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public int getRawStatusCode() {
|
||||
return this.response.statusCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStatusText() {
|
||||
HttpStatus status = HttpStatus.resolve(this.response.statusCode());
|
||||
return (status != null) ? status.getReasonPhrase() : "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getBody() throws IOException {
|
||||
InputStream body = this.response.body();
|
||||
return (body != null ? body : InputStream.nullInputStream());
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpHeaders getHeaders() {
|
||||
HttpHeaders headers = this.headers;
|
||||
if (headers == null) {
|
||||
headers = new HttpHeaders();
|
||||
for (String headerName : this.response.headers().map().keySet()) {
|
||||
for (String headerValue : this.response.headers().allValues(headerName)) {
|
||||
headers.add(headerName, headerValue);
|
||||
}
|
||||
}
|
||||
this.headers = headers;
|
||||
}
|
||||
return headers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
InputStream body = this.response.body();
|
||||
try {
|
||||
try {
|
||||
StreamUtils.drain(body);
|
||||
}
|
||||
finally {
|
||||
body.close();
|
||||
}
|
||||
}
|
||||
catch (IOException ex) {
|
||||
// Ignore exception on close...
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright 2023-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.http.client;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.http.HttpMethod;
|
||||
|
||||
/**
|
||||
* @author Marten Deinum
|
||||
*/
|
||||
public class JdkClientClientHttpRequestFactoryTests extends AbstractHttpRequestFactoryTests {
|
||||
|
||||
@Override
|
||||
protected ClientHttpRequestFactory createRequestFactory() {
|
||||
return new JdkClientClientHttpRequestFactory();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void httpMethods() throws Exception {
|
||||
super.httpMethods();
|
||||
assertHttpMethod("patch", HttpMethod.PATCH);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue