我想编写Java应用程序,用PHP将文件上传到Apache服务器.Java代码使用Jakarta HttpClient库版本4.0 beta2:
import java.io.File;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpVersion;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.FileEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.CoreProtocolPNames;
import org.apache.http.util.EntityUtils;
public class PostFile {
public static void main(String[] args) throws Exception {
HttpClient httpclient = new DefaultHttpClient();
httpclient.getParams().setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1);
HttpPost httppost = new HttpPost("http://localhost:9002/upload.php");
File file = new File("c:/TRASH/zaba_1.jpg");
FileEntity reqEntity = new FileEntity(file, "binary/octet-stream");
httppost.setEntity(reqEntity);
reqEntity.setContentType("binary/octet-stream");
System.out.println("executing request " + httppost.getRequestLine());
HttpResponse response = httpclient.execute(httppost);
HttpEntity resEntity = response.getEntity();
System.out.println(response.getStatusLine());
if (resEntity != null) {
System.out.println(EntityUtils.toString(resEntity)); …Run Code Online (Sandbox Code Playgroud) 我正在使用 Java Corretto 21.0.0.35.1 build 21+35-LTS和内置 Java HTTP 客户端来检索InputStream. 我正在使用虚拟线程发出并行请求,并且在大多数情况下,它运行良好。然而,有时,我的测试会遇到“固定”事件,如下面的堆栈跟踪所示。
我相信 JDK 已经更新为完全支持虚拟线程,并且根据我的理解,HTTP 客户端根本不应该固定承载线程。但是,似乎在读取并(自动)关闭InputStream.
这是预期的行为吗?或者它仍然是 JDK 中的一个错误吗?
代码:
HttpResponse<InputStream> response = httpClient.send(request, HttpResponse.BodyHandlers.ofInputStream());
try (InputStream responseBody = response.body()) {
return parser.parse(responseBody); // LINE 52 in the trace below
}
Run Code Online (Sandbox Code Playgroud)
踪迹
* Pinning event captured:
java.lang.VirtualThread.parkOnCarrierThread(java.lang.VirtualThread.java:687)
java.lang.VirtualThread.park(java.lang.VirtualThread.java:603)
java.lang.System$2.parkVirtualThread(java.lang.System$2.java:2639)
jdk.internal.misc.VirtualThreads.park(jdk.internal.misc.VirtualThreads.java:54)
java.util.concurrent.locks.LockSupport.park(java.util.concurrent.locks.LockSupport.java:219)
java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(java.util.concurrent.locks.AbstractQueuedSynchronizer.java:754)
java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(java.util.concurrent.locks.AbstractQueuedSynchronizer.java:990)
java.util.concurrent.locks.ReentrantLock$Sync.lock(java.util.concurrent.locks.ReentrantLock$Sync.java:153)
java.util.concurrent.locks.ReentrantLock.lock(java.util.concurrent.locks.ReentrantLock.java:322)
sun.nio.ch.SocketChannelImpl.implCloseNonBlockingMode(sun.nio.ch.SocketChannelImpl.java:1091)
sun.nio.ch.SocketChannelImpl.implCloseSelectableChannel(sun.nio.ch.SocketChannelImpl.java:1124)
java.nio.channels.spi.AbstractSelectableChannel.implCloseChannel(java.nio.channels.spi.AbstractSelectableChannel.java:258)
java.nio.channels.spi.AbstractInterruptibleChannel.close(java.nio.channels.spi.AbstractInterruptibleChannel.java:113)
jdk.internal.net.http.PlainHttpConnection.close(jdk.internal.net.http.PlainHttpConnection.java:427)
jdk.internal.net.http.PlainHttpConnection.close(jdk.internal.net.http.PlainHttpConnection.java:406)
jdk.internal.net.http.Http1Response.lambda$readBody$1(jdk.internal.net.http.Http1Response.java:355)
jdk.internal.net.http.Http1Response$$Lambda+0x00007f4cb5e6c438.749276779.accept(jdk.internal.net.http.Http1Response$$Lambda+0x00007f4cb5e6c438.749276779.java:-1)
jdk.internal.net.http.ResponseContent$ChunkedBodyParser.onError(jdk.internal.net.http.ResponseContent$ChunkedBodyParser.java:185)
jdk.internal.net.http.Http1Response$BodyReader.onReadError(jdk.internal.net.http.Http1Response$BodyReader.java:677)
jdk.internal.net.http.Http1AsyncReceiver.checkForErrors(jdk.internal.net.http.Http1AsyncReceiver.java:302)
jdk.internal.net.http.Http1AsyncReceiver.flush(jdk.internal.net.http.Http1AsyncReceiver.java:268)
jdk.internal.net.http.Http1AsyncReceiver$$Lambda+0x00007f4cb5e31228.555093431.run(jdk.internal.net.http.Http1AsyncReceiver$$Lambda+0x00007f4cb5e31228.555093431.java:-1)
jdk.internal.net.http.common.SequentialScheduler$LockingRestartableTask.run(jdk.internal.net.http.common.SequentialScheduler$LockingRestartableTask.java:182)
jdk.internal.net.http.common.SequentialScheduler$CompleteRestartableTask.run(jdk.internal.net.http.common.SequentialScheduler$CompleteRestartableTask.java:149)
jdk.internal.net.http.common.SequentialScheduler$SchedulableTask.run(jdk.internal.net.http.common.SequentialScheduler$SchedulableTask.java:207)
jdk.internal.net.http.HttpClientImpl$DelegatingExecutor.execute(jdk.internal.net.http.HttpClientImpl$DelegatingExecutor.java:177)
jdk.internal.net.http.common.SequentialScheduler.runOrSchedule(jdk.internal.net.http.common.SequentialScheduler.java:282)
jdk.internal.net.http.common.SequentialScheduler.runOrSchedule(jdk.internal.net.http.common.SequentialScheduler.java:251)
jdk.internal.net.http.Http1AsyncReceiver.onReadError(jdk.internal.net.http.Http1AsyncReceiver.java:516)
jdk.internal.net.http.Http1AsyncReceiver.lambda$handlePendingDelegate$3(jdk.internal.net.http.Http1AsyncReceiver.java:380)
jdk.internal.net.http.Http1AsyncReceiver$$Lambda+0x00007f4cb5e33ca0.84679411.run(jdk.internal.net.http.Http1AsyncReceiver$$Lambda+0x00007f4cb5e33ca0.84679411.java:-1) …Run Code Online (Sandbox Code Playgroud) 有时需要允许不安全的HTTPS连接,例如在一些应该与任何站点一起使用的Web爬行应用程序中.我在旧的HttpsURLConnection API中使用了一个这样的解决方案,最近被JDK 11中的新HttpClient API取代了.使用这个新API允许不安全的HTTPS连接(自签名或过期证书)的方法是什么?
UPD:我尝试的代码(在Kotlin中,但直接映射到Java):
val trustAllCerts = arrayOf<TrustManager>(object: X509TrustManager {
override fun getAcceptedIssuers(): Array<X509Certificate>? = null
override fun checkClientTrusted(certs: Array<X509Certificate>, authType: String) {}
override fun checkServerTrusted(certs: Array<X509Certificate>, authType: String) {}
})
val sslContext = SSLContext.getInstance("SSL")
sslContext.init(null, trustAllCerts, SecureRandom())
val sslParams = SSLParameters()
// This should prevent host validation
sslParams.endpointIdentificationAlgorithm = ""
httpClient = HttpClient.newBuilder()
.sslContext(sslContext)
.sslParameters(sslParams)
.build()
Run Code Online (Sandbox Code Playgroud)
但在发送时我有异常(尝试使用自签名证书的localhost):
java.io.IOException: No name matching localhost found
Run Code Online (Sandbox Code Playgroud)
使用IP地址而不是localhost会出现"无主题替代名称存在"异常.
在对JDK进行一些调试之后,我发现sslParams在抛出异常的地方真的被忽略了,并且使用了一些本地创建的实例.进一步调试显示,影响主机名验证算法的唯一方法是将jdk.internal.httpclient.disableHostnameVerification系统属性设置为true.这似乎是一个解决方案.SSLParameters在上面的代码中没有任何影响,所以这部分可以被丢弃.使其仅在全局配置看起来像新的HttpClient API中的严重设计缺陷.
这是我的示例代码.查询编码为UTF-8:
HttpRequest request = HttpRequest.newBuilder()
.header("content-type", "application/json;charset=UTF-8")
.uri(URI.create("http://localhost:8080/test?param1=test%C5%84"))
.GET()
.build();
HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_2)
.build()
.send(request, HttpResponse.BodyHandler.asString(Charset.forName("UTF-8")));
Run Code Online (Sandbox Code Playgroud)
运行此示例后,我得到以下异常:
java.lang.IllegalArgumentException: char=324
at jdk.incubator.httpclient/jdk.incubator.http.internal.hpack.Huffman.codeOf(Huffman.java:559)
at jdk.incubator.httpclient/jdk.incubator.http.internal.hpack.Huffman.lengthOf(Huffman.java:524)
at jdk.incubator.httpclient/jdk.incubator.http.internal.hpack.StringWriter.configure(StringWriter.java:79)
at jdk.incubator.httpclient/jdk.incubator.http.internal.hpack.StringWriter.configure(StringWriter.java:62)
at jdk.incubator.httpclient/jdk.incubator.http.internal.hpack.IndexNameValueWriter.value(IndexNameValueWriter.java:64)
at jdk.incubator.httpclient/jdk.incubator.http.internal.hpack.Encoder.literal(Encoder.java:422)
at jdk.incubator.httpclient/jdk.incubator.http.internal.hpack.Encoder.header(Encoder.java:245)
at jdk.incubator.httpclient/jdk.incubator.http.internal.hpack.Encoder.header(Encoder.java:198)
at jdk.incubator.httpclient/jdk.incubator.http.Http2Connection.encodeHeadersImpl(Http2Connection.java:927)
at jdk.incubator.httpclient/jdk.incubator.http.Http2Connection.encodeHeaders(Http2Connection.java:878)
at jdk.incubator.httpclient/jdk.incubator.http.Http2Connection.encodeHeaders(Http2Connection.java:951)
at jdk.incubator.httpclient/jdk.incubator.http.Http2Connection.sendFrame(Http2Connection.java:984)
at jdk.incubator.httpclient/jdk.incubator.http.Stream.sendHeadersAsync(Stream.java:547)
at jdk.incubator.httpclient/jdk.incubator.http.Exchange.lambda$responseAsyncImpl0$8(Exchange.java:322)
at java.base/java.util.concurrent.CompletableFuture$UniCompose.tryFire(CompletableFuture.java:1072)
at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:506)
at java.base/java.util.concurrent.CompletableFuture.complete(CompletableFuture.java:2073)
at jdk.incubator.httpclient/jdk.incubator.http.internal.common.SSLFlowDelegate.setALPN(SSLFlowDelegate.java:164)
at jdk.incubator.httpclient/jdk.incubator.http.internal.common.SSLFlowDelegate.access$200(SSLFlowDelegate.java:81)
at jdk.incubator.httpclient/jdk.incubator.http.internal.common.SSLFlowDelegate$Reader.processData(SSLFlowDelegate.java:340)
at jdk.incubator.httpclient/jdk.incubator.http.internal.common.SSLFlowDelegate$Reader$ReaderDownstreamPusher.run(SSLFlowDelegate.java:215)
at jdk.incubator.httpclient/jdk.incubator.http.internal.common.SequentialScheduler$SynchronizedRestartableTask.run(SequentialScheduler.java:175)
at jdk.incubator.httpclient/jdk.incubator.http.internal.common.SequentialScheduler$CompleteRestartableTask.run(SequentialScheduler.java:147)
at jdk.incubator.httpclient/jdk.incubator.http.internal.common.SequentialScheduler$TryEndDeferredCompleter.complete(SequentialScheduler.java:315)
at jdk.incubator.httpclient/jdk.incubator.http.internal.common.SequentialScheduler$CompleteRestartableTask.run(SequentialScheduler.java:149)
at jdk.incubator.httpclient/jdk.incubator.http.internal.common.SequentialScheduler$SchedulableTask.run(SequentialScheduler.java:198)
at jdk.incubator.httpclient/jdk.incubator.http.internal.common.SequentialScheduler.runOrSchedule(SequentialScheduler.java:271)
at jdk.incubator.httpclient/jdk.incubator.http.internal.common.SequentialScheduler.runOrSchedule(SequentialScheduler.java:224)
at jdk.incubator.httpclient/jdk.incubator.http.internal.common.SSLFlowDelegate$Reader.incoming(SSLFlowDelegate.java:242)
at jdk.incubator.httpclient/jdk.incubator.http.internal.common.SubscriberWrapper.incomingCaller(SubscriberWrapper.java:388)
at jdk.incubator.httpclient/jdk.incubator.http.internal.common.SubscriberWrapper.onNext(SubscriberWrapper.java:343) …Run Code Online (Sandbox Code Playgroud) 我正在尝试使用Java 9 HttpClient.
HttpRequest的javadoc中的基本示例可以正常工作:
HttpResponse response = HttpRequest.create(new URI("http://stackoverflow.com/"))
.version(java.net.http.HttpClient.Version.HTTP_2)
.followRedirects(HttpClient.Redirect.ALWAYS)
.GET()
.response();
int statusCode = response.statusCode();
String responseBody = response.body(HttpResponse.asString());
System.out.println("statusCode = " + statusCode);
System.out.println("responseBody = " + responseBody);
Run Code Online (Sandbox Code Playgroud)
但是,在尝试使用时sendAsyncMulti,它不起作用.没有创建文件E:\foo,未达到printlnafter join,也没有异常,虽然我基本上是从HttpResponse.multiFileJavadoc复制了这个例子.我希望一些HTTP响应将保存在该目录中.我还试图删除HTTP2和followRedirects,谷歌等其他URL,但它没有改变任何东西.我究竟做错了什么?
CompletableFuture<Map<URI,Path>> cf =
HttpRequest.create(new URI("http://stackoverflow.com/"))
.version(java.net.http.HttpClient.Version.HTTP_2)
.followRedirects(HttpClient.Redirect.ALWAYS)
.GET()
.multiResponseAsync(HttpResponse.multiFile(Paths.get("E:\\foo")));
Map<URI,Path> results = cf.join();
System.out.println("after join");
Run Code Online (Sandbox Code Playgroud)
如果它是相关的,这是我使用的版本(JDK 9的最新版本):
java version "9-ea"
Java(TM) SE Runtime Environment (build 9-ea+126)
Java HotSpot(TM) Server VM (build 9-ea+126, mixed mode)
Run Code Online (Sandbox Code Playgroud) 我在使用HttpClient::send带有 custom 的Java 11 直接将 JSON 反序列化为自定义对象时遇到问题HttpResponse.BodyHandler。我在回答这个 SO question时遇到了这个问题。
我使用的版本:
我创建了一个简单的泛型JsonBodyHandler类,它实现HttpResponse.BodyHandler:
public class JsonBodyHandler<W> implements HttpResponse.BodyHandler<W> {
private final Class<W> wClass;
public JsonBodyHandler(Class<W> wClass) {
this.wClass = wClass;
}
@Override
public HttpResponse.BodySubscriber<W> apply(HttpResponse.ResponseInfo responseInfo) {
return asJSON(wClass);
}
}
Run Code Online (Sandbox Code Playgroud)
该asJSON方法定义为:
public static <W> HttpResponse.BodySubscriber<W> asJSON(Class<W> targetType) {
HttpResponse.BodySubscriber<String> upstream = HttpResponse.BodySubscribers.ofString(StandardCharsets.UTF_8);
return HttpResponse.BodySubscribers.mapping(
upstream,
(String body) -> {
try {
ObjectMapper objectMapper …Run Code Online (Sandbox Code Playgroud) 使用HttpClientJava 11(JDK,而不是 Apache),如何重试请求?
假设我想重试一个请求最多10 次,如果它没有返回状态代码或200抛出异常。
目前,我正在循环中重新安排返回的未来,我想知道是否有更好或更优雅的方式。
CompletableFuture<HttpResponse<Foo>> task = client.sendAsync(request, bodyHandler);
for (int i = 0; i < 10; i++) {
task = task.thenComposeAsync(response -> response.statusCode() == 200 ?
CompletableFuture.completedFuture(response) :
client.sendAsync(request, bodyHandler));
}
// Do something with 'task' ...
Run Code Online (Sandbox Code Playgroud)
如果我们也为特殊情况添加重试,我最终会得到
CompletableFuture<HttpResponse<Foo>> task = client.sendAsync(request, bodyHandler);
for (int i = 0; i < 10; i++) {
task = task.thenComposeAsync(response ->
response.statusCode() == 200 ?
CompletableFuture.completedFuture(response) :
client.sendAsync(request, bodyHandler))
.exceptionallyComposeAsync(e -> …Run Code Online (Sandbox Code Playgroud) 所以我想尝试http客户端
package com.company;
import jdk.incubator.http.HttpClient;
public class Main {
public static void main(String[] args) {
HttpClient client = HttpClient.newHttpClient();
}
}
Run Code Online (Sandbox Code Playgroud)
我的模块信息看起来像这样
module com.company {
requires jdk.incubator.httpclient;
}
Run Code Online (Sandbox Code Playgroud)
但我明白了 java.lang.NoClassDefFoundError: jdk/incubator/http/HttpClient
我真的不明白为什么.我的java版本是"build 9-ea + 169",我使用最新版本的IntelliJ idea(2017.1.3).我查看了这个答案,看起来我只需要将需求添加到文件中,但由于某种原因它不起作用.
我试图每隔几分钟不断地向 REST API 发送 GET 和 POST 请求。问题是,在恰好 1000 个请求之后,我收到了一个GOAWAY帧(和一个IOException):
GOAWAY 帧(类型=0x7)用于启动连接关闭或发出严重错误情况的信号。
HTTP/2 规范
我做了一些研究,发现 1000 个请求不仅是nginx 的默认最大值,Cloudfront(相关的 Chromium 问题)和 Discord 也表现出相同的行为。
我尝试使用具有默认 HTTP/2 配置的本地 nginx 服务器重现此问题:
服务器 {
听 443 http2 ssl;
http2_max_requests 1000;
...
}
var client = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_2)
.build();
for (var i = 0; i < 1100; i++) {
var url = URI.create(String.format("https://localhost/images/test%d.jpg", i));
var request = HttpRequest.newBuilder().uri(url).build();
client.send(request, HttpResponse.BodyHandlers.discarding());
System.out.printf("Image %d processed%n", i);
}
Run Code Online (Sandbox Code Playgroud)
在大约 1000 个请求之后,我收到了 …
有没有办法java.net.http.HttpClient立即释放它所持有的资源?
在内部,它包含一个选择器,一个连接池和一个Executor(当使用默认值时).但它没有实现Closeable/ AutoCloseable.
java ×10
java-http-client ×10
java-11 ×4
java-9 ×3
http2 ×2
http ×1
jackson ×1
java-21 ×1
php ×1
project-loom ×1
request ×1
retry-logic ×1
url-encoding ×1