在 URL 查询参数中使用某些字符时出现 Java 9 HttpClient 异常
Posted
技术标签:
【中文标题】在 URL 查询参数中使用某些字符时出现 Java 9 HttpClient 异常【英文标题】:Java 9 HttpClient exception when using certain characters in URL query parameters 【发布时间】:2018-11-24 03:17:22 【问题描述】:这是我的示例代码。查询编码为 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")));
运行此示例后,出现以下异常:
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)
at jdk.incubator.httpclient/jdk.incubator.http.internal.common.SubscriberWrapper.onNext(SubscriberWrapper.java:58)
at jdk.incubator.httpclient/jdk.incubator.http.SocketTube$InternalReadPublisher$InternalReadSubscription.read(SocketTube.java:739)
at jdk.incubator.httpclient/jdk.incubator.http.SocketTube$SocketFlowTask.run(SocketTube.java:171)
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.SocketTube$InternalReadPublisher$InternalReadSubscription.signalReadable(SocketTube.java:675)
at jdk.incubator.httpclient/jdk.incubator.http.SocketTube$InternalReadPublisher$ReadEvent.signalEvent(SocketTube.java:829)
at jdk.incubator.httpclient/jdk.incubator.http.SocketTube$SocketFlowEvent.handle(SocketTube.java:243)
at jdk.incubator.httpclient/jdk.incubator.http.HttpClientImpl$SelectorManager.handleEvent(HttpClientImpl.java:769)
at jdk.incubator.httpclient/jdk.incubator.http.HttpClientImpl$SelectorManager.run(HttpClientImpl.java:731)
char=324
表示从查询中解码ń
当我阅读堆栈跟踪时,我在类中发现了jdk.incubator.http.Stream<T>
这个方法:
private void setPseudoHeaderFields()
HttpHeadersImpl hdrs = requestPseudoHeaders;
String method = request.method();
hdrs.setHeader(":method", method);
URI uri = request.uri();
hdrs.setHeader(":scheme", uri.getScheme());
// TODO: userinfo deprecated. Needs to be removed
hdrs.setHeader(":authority", uri.getAuthority());
// TODO: ensure header names beginning with : not in user headers
String query = uri.getQuery();
String path = uri.getPath();
if (path == null || path.isEmpty())
if (method.equalsIgnoreCase("OPTIONS"))
path = "*";
else
path = "/";
if (query != null)
path += "?" + query;
hdrs.setHeader(":path", path);
在这个方法中使用了uri.getQuery()
,它为我们提供了解码后的查询并导致上述异常。
当我在调试模式下使用uri.getRawQuery()
(它为我们提供了编码查询)时,一切都很好。
我的问题是:这是错误还是故意使用?如果不是bug,如何避免异常?
【问题讨论】:
test%C5%84
也许(额外的 %)?如"...?param=" + URLEncoder.encode("ń", "UTF-8")
对不起,这是我的错误。应该有test%C5%84
,但在方法setPseudoHeaderFields()
中我得到...testń
,以及未来的异常。
似乎 Huffman 压缩使用简单的字符到字节转换 à la ISO-8859-1。因此,就我非常有限的经验而言,这似乎是一个错误。您可能会恢复为 Base64:"...?param1=" + Base64.getEncoder().encode("ń")
,但您也需要自己解码参数。
【参考方案1】:
这是一个错误:
使用某些 UTF-8 字符时带有 jdk.incubator.httpclient 的 java.lang.IllegalArgumentException 某些 UTF-8 字符(如“š”)不能与 HttpClient 一起使用。然而 其他类似“å”的可以。
见:https://bugs.openjdk.java.net/browse/JDK-8201238
Java 9/10 HttpClient 已孵化。它尚未准备好生产。它在包名称中非常清楚地说明了这一点。因此,它很可能包含错误。
它将作为 JEP 321 的一部分在 Java 11 (currently scheduled for release 25th September 2018) 中正确发布。
【讨论】:
注意:HTTP 客户端 API 将在 Java 11 中标准化,并将移至名为java.net.http
的新包(和模块)中。 (JEP 321)
@Slaw 我为此感到非常高兴,终于在 jdk 本身中有一个标准的 http 客户端!
您可以计划migrate to Java-11 for using the standardized http
module.。我可以确认code in question 可以与 JDK/11 (jdk-11-ea+17) 一起正常工作。以上是关于在 URL 查询参数中使用某些字符时出现 Java 9 HttpClient 异常的主要内容,如果未能解决你的问题,请参考以下文章
在配置单元中发出选择查询时出现异常'java.lang.IllegalArgumentException(无法从空字符串创建路径)'
wordpress - 在 cpanel 中删除某些文件管理器时出现错误 URL