Browse Source
As JDK17 is now the baseline it is possible to use the HttpClient provided by Java.pull/30772/head
4 changed files with 285 additions and 0 deletions
@ -0,0 +1,92 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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