This is an automated email from the ASF dual-hosted git repository.

albumenj pushed a commit to branch 3.3
in repository https://gitbox.apache.org/repos/asf/dubbo.git


The following commit(s) were added to refs/heads/3.3 by this push:
     new 611de81d97 Rest bugfix (#14599)
611de81d97 is described below

commit 611de81d97132a30bbe5498462222493a8994c31
Author: Sean Yang <[email protected]>
AuthorDate: Wed Aug 28 15:26:03 2024 +0800

    Rest bugfix (#14599)
---
 .../src/main/resources/application.yml             |  2 +-
 .../protocol/tri/servlet/ServletStreamChannel.java | 24 +++++++++---
 .../rpc/protocol/tri/servlet/TripleFilter.java     | 45 ++++++++++++++++++++--
 .../http12/h1/Http1ServerUnaryChannelObserver.java | 11 +++++-
 .../apache/dubbo/remoting/http12/rest/Mapping.java | 44 +++++++++++++++++++++
 .../apache/dubbo/remoting/http12/rest/Param.java   | 30 ++++++++++++++-
 .../GrpcHttp2ServerTransportListenerFactory.java   |  3 +-
 .../tri/h12/grpc/GrpcRequestHandlerMapping.java    |  4 +-
 .../dubbo/rpc/protocol/tri/h12/grpc/GrpcUtils.java |  5 +++
 .../h12/http2/Http2ServerUnaryChannelObserver.java | 11 +++++-
 .../rpc/protocol/tri/h3/negotiation/Helper.java    |  3 +-
 .../support/basic/FallbackArgumentResolver.java    |  6 ++-
 .../rpc/protocol/tri/rest/util/RequestUtils.java   |  4 +-
 .../tri/rest/support/basic/RestProtocolTest.groovy | 12 ++++++
 .../rpc/protocol/tri/test/MockH2StreamChannel.java |  6 ++-
 .../DubboTriple3AutoConfiguration.java             |  2 +-
 .../DubboTripleAutoConfiguration.java              |  2 +-
 17 files changed, 188 insertions(+), 26 deletions(-)

diff --git 
a/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-servlet/src/main/resources/application.yml
 
b/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-servlet/src/main/resources/application.yml
index e03758b7cc..f9628d8341 100644
--- 
a/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-servlet/src/main/resources/application.yml
+++ 
b/dubbo-demo/dubbo-demo-spring-boot/dubbo-demo-spring-boot-servlet/src/main/resources/application.yml
@@ -27,7 +27,7 @@ dubbo:
     port: ${server.port}
     triple:
       servlet:
-        enable: true
+        enabled: true
   registry:
     id: zk-registry
     address: zookeeper://127.0.0.1:2181
diff --git 
a/dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/servlet/ServletStreamChannel.java
 
b/dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/servlet/ServletStreamChannel.java
index 72168f3b3d..d45fb4a0f4 100644
--- 
a/dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/servlet/ServletStreamChannel.java
+++ 
b/dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/servlet/ServletStreamChannel.java
@@ -18,11 +18,11 @@ package org.apache.dubbo.rpc.protocol.tri.servlet;
 
 import org.apache.dubbo.common.logger.Logger;
 import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.utils.CollectionUtils;
 import org.apache.dubbo.remoting.http12.HttpHeaderNames;
 import org.apache.dubbo.remoting.http12.HttpHeaders;
 import org.apache.dubbo.remoting.http12.HttpMetadata;
 import org.apache.dubbo.remoting.http12.HttpOutputMessage;
-import org.apache.dubbo.remoting.http12.HttpVersion;
 import org.apache.dubbo.remoting.http12.exception.HttpStatusException;
 import org.apache.dubbo.remoting.http12.h2.H2StreamChannel;
 import org.apache.dubbo.remoting.http12.h2.Http2Header;
@@ -130,7 +130,12 @@ final class ServletStreamChannel implements 
H2StreamChannel {
 
     @Override
     public CompletableFuture<Void> writeHeader(HttpMetadata httpMetadata) {
-        boolean endStream = ((Http2Header) httpMetadata).isEndStream();
+        boolean endStream = false;
+        boolean isHttp1 = true;
+        if (httpMetadata instanceof Http2Header) {
+            endStream = ((Http2Header) httpMetadata).isEndStream();
+            isHttp1 = false;
+        }
         try {
             HttpHeaders headers = httpMetadata.headers();
             if (endStream) {
@@ -155,6 +160,11 @@ final class ServletStreamChannel implements 
H2StreamChannel {
                     response.setStatus(Integer.parseInt(values.get(0)));
                     continue;
                 }
+                if (isHttp1
+                        && 
HttpHeaderNames.TRANSFER_ENCODING.getName().equals(key)
+                        && "chunked".equals(CollectionUtils.first(values))) {
+                    continue;
+                }
                 if (values.size() == 1) {
                     response.setHeader(key, values.get(0));
                 } else {
@@ -175,13 +185,15 @@ final class ServletStreamChannel implements 
H2StreamChannel {
 
     @Override
     public CompletableFuture<Void> writeMessage(HttpOutputMessage 
httpOutputMessage) {
-        boolean endStream = ((Http2OutputMessage) 
httpOutputMessage).isEndStream();
+        boolean endStream = false;
+        if (httpOutputMessage instanceof Http2OutputMessage) {
+            endStream = ((Http2OutputMessage) httpOutputMessage).isEndStream();
+        } else if (httpOutputMessage == HttpOutputMessage.EMPTY_MESSAGE) {
+            endStream = true;
+        }
         try {
             ByteArrayOutputStream bos = (ByteArrayOutputStream) 
httpOutputMessage.getBody();
             ServletOutputStream out = response.getOutputStream();
-            if 
(!HttpVersion.HTTP2.getProtocol().equals(request.getProtocol())) {
-                response.setContentLength(bos.size());
-            }
             bos.writeTo(out);
             out.flush();
         } catch (Throwable t) {
diff --git 
a/dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/servlet/TripleFilter.java
 
b/dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/servlet/TripleFilter.java
index c95f177b57..c7d456b2fb 100644
--- 
a/dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/servlet/TripleFilter.java
+++ 
b/dubbo-plugin/dubbo-triple-servlet/src/main/java/org/apache/dubbo/rpc/protocol/tri/servlet/TripleFilter.java
@@ -19,6 +19,9 @@ package org.apache.dubbo.rpc.protocol.tri.servlet;
 import org.apache.dubbo.common.io.StreamUtils;
 import org.apache.dubbo.common.logger.Logger;
 import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.remoting.http12.HttpVersion;
+import org.apache.dubbo.remoting.http12.h1.Http1InputMessage;
+import org.apache.dubbo.remoting.http12.h1.Http1ServerTransportListener;
 import org.apache.dubbo.remoting.http12.h2.Http2InputMessageFrame;
 import org.apache.dubbo.remoting.http12.h2.Http2ServerTransportListenerFactory;
 import org.apache.dubbo.remoting.http12.h2.Http2TransportListener;
@@ -31,6 +34,7 @@ import org.apache.dubbo.rpc.protocol.tri.TripleHeaderEnum;
 import org.apache.dubbo.rpc.protocol.tri.h12.grpc.GrpcHeaderNames;
 import 
org.apache.dubbo.rpc.protocol.tri.h12.grpc.GrpcHttp2ServerTransportListener;
 import org.apache.dubbo.rpc.protocol.tri.h12.grpc.GrpcUtils;
+import 
org.apache.dubbo.rpc.protocol.tri.h12.http1.DefaultHttp11ServerTransportListenerFactory;
 import 
org.apache.dubbo.rpc.protocol.tri.h12.http2.GenericHttp2ServerTransportListenerFactory;
 import 
org.apache.dubbo.rpc.protocol.tri.rest.mapping.DefaultRequestMappingRegistry;
 import org.apache.dubbo.rpc.protocol.tri.rest.mapping.RequestMappingRegistry;
@@ -75,11 +79,23 @@ public class TripleFilter implements Filter {
         HttpServletRequest request = (HttpServletRequest) servletRequest;
         HttpServletResponse response = (HttpServletResponse) servletResponse;
 
-        if (!hasGrpcMapping(request) && 
!mappingRegistry.exists(request.getRequestURI(), request.getMethod())) {
-            chain.doFilter(request, response);
-            return;
+        boolean isHttp2 = 
HttpVersion.HTTP2.getProtocol().equals(request.getProtocol());
+        if (isHttp2) {
+            if (hasGrpcMapping(request) || 
mappingRegistry.exists(request.getRequestURI(), request.getMethod())) {
+                handleHttp2(request, response);
+                return;
+            }
+        } else {
+            if (mappingRegistry.exists(request.getRequestURI(), 
request.getMethod())) {
+                handleHttp1(request, response);
+                return;
+            }
         }
 
+        chain.doFilter(request, response);
+    }
+
+    private void handleHttp2(HttpServletRequest request, HttpServletResponse 
response) {
         AsyncContext context = request.startAsync(request, response);
         ServletStreamChannel channel = new ServletStreamChannel(request, 
response, context);
         try {
@@ -101,14 +117,37 @@ public class TripleFilter implements Filter {
         }
     }
 
+    private void handleHttp1(HttpServletRequest request, HttpServletResponse 
response) {
+        AsyncContext context = request.startAsync(request, response);
+        ServletStreamChannel channel = new ServletStreamChannel(request, 
response, context);
+        try {
+            Http1ServerTransportListener listener = 
DefaultHttp11ServerTransportListenerFactory.INSTANCE.newInstance(
+                    channel, ServletExchanger.getUrl(), 
FrameworkModel.defaultModel());
+            channel.setGrpc(false);
+            context.setTimeout(resolveTimeout(request, false));
+            listener.onMetadata(new HttpMetadataAdapter(request));
+            ServletInputStream is = request.getInputStream();
+            listener.onData(new Http1InputMessage(
+                    is.available() == 0 ? StreamUtils.EMPTY : new 
ByteArrayInputStream(StreamUtils.readBytes(is))));
+        } catch (Throwable t) {
+            LOGGER.info("Failed to process request", t);
+            channel.writeError(Code.UNKNOWN.code, t);
+        }
+    }
+
     @Override
     public void destroy() {}
 
     private boolean hasGrpcMapping(HttpServletRequest request) {
+        if (!GrpcUtils.isGrpcRequest(request.getContentType())) {
+            return false;
+        }
+
         RequestPath path = RequestPath.parse(request.getRequestURI());
         if (path == null) {
             return false;
         }
+
         String group = 
request.getHeader(TripleHeaderEnum.SERVICE_GROUP.getHeader());
         String version = 
request.getHeader(TripleHeaderEnum.SERVICE_VERSION.getHeader());
         return pathResolver.resolve(path.getPath(), group, version) != null;
diff --git 
a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/h1/Http1ServerUnaryChannelObserver.java
 
b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/h1/Http1ServerUnaryChannelObserver.java
index 8cb2a36edd..32c93c13aa 100644
--- 
a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/h1/Http1ServerUnaryChannelObserver.java
+++ 
b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/h1/Http1ServerUnaryChannelObserver.java
@@ -16,6 +16,7 @@
  */
 package org.apache.dubbo.remoting.http12.h1;
 
+import org.apache.dubbo.common.logger.FluentLogger;
 import org.apache.dubbo.remoting.http12.HttpChannel;
 import org.apache.dubbo.remoting.http12.HttpHeaderNames;
 import org.apache.dubbo.remoting.http12.HttpMetadata;
@@ -27,6 +28,8 @@ import io.netty.buffer.ByteBufOutputStream;
 
 public class Http1ServerUnaryChannelObserver extends 
Http1ServerChannelObserver {
 
+    private static final FluentLogger LOGGER = 
FluentLogger.of(Http1ServerUnaryChannelObserver.class);
+
     public Http1ServerUnaryChannelObserver(HttpChannel httpChannel) {
         super(httpChannel);
     }
@@ -42,7 +45,13 @@ public class Http1ServerUnaryChannelObserver extends 
Http1ServerChannelObserver
     protected void doOnError(Throwable throwable) throws Throwable {
         String statusCode = resolveStatusCode(throwable);
         Object data = buildErrorResponse(statusCode, throwable);
-        HttpOutputMessage httpOutputMessage = buildMessage(data);
+        HttpOutputMessage httpOutputMessage;
+        try {
+            httpOutputMessage = buildMessage(data);
+        } catch (Throwable t) {
+            LOGGER.internalError("Failed to build message", t);
+            httpOutputMessage = encodeHttpOutputMessage(data);
+        }
         sendHeader(buildMetadata(statusCode, data, httpOutputMessage));
         sendMessage(httpOutputMessage);
     }
diff --git 
a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/rest/Mapping.java
 
b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/rest/Mapping.java
index 3832333142..f42e11768f 100644
--- 
a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/rest/Mapping.java
+++ 
b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/rest/Mapping.java
@@ -24,24 +24,68 @@ import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 
+/**
+ * Annotation for mapping Dubbo services to Rest endpoints.
+ *
+ * <p>
+ * Example usage:
+ * <pre>
+ * &#64;Mapping(value = "/example", method = HttpMethods.GET, produces = 
"application/json")
+ * String handleExample();
+ * </pre>
+ * @see <a 
href="https://dubbo-next.staged.apache.org/zh-cn/overview/mannual/java-sdk/reference-manual/protocol/tripe-rest-manual/#Q6XyG";>Tripe
 Rest Manual</a>
+ */
 @Target({ElementType.TYPE, ElementType.METHOD})
 @Retention(RetentionPolicy.RUNTIME)
 @Documented
 public @interface Mapping {
 
+    /**
+     * Alias for {@link #path()}.
+     * The path patterns to be mapped.
+     * If not specified, the method or class name is used as the default.
+     */
     String[] value() default {};
 
+    /**
+     * Specifies the path patterns to be mapped.
+     * If not specified, the method or class name is used as the default.
+     */
     String[] path() default {};
 
+    /**
+     * Defines the HTTP methods supported by the mapped handler.
+     * Supports values like GET, POST, etc.
+     */
     HttpMethods[] method() default {};
 
+    /**
+     * Specifies the request parameters that must be present for this mapping 
to be invoked.
+     * Example: "param1=value1", "param2".
+     */
     String[] params() default {};
 
+    /**
+     * Specifies the request headers that must be present for this mapping to 
be invoked.
+     * Example: "content-type=application/json".
+     */
     String[] headers() default {};
 
+    /**
+     * Specifies the media types that the mapped handler consumes.
+     * Example: "application/json", "text/plain".
+     */
     String[] consumes() default {};
 
+    /**
+     * Specifies the media types that the mapped handler produces.
+     * Example: "application/json", "text/html".
+     */
     String[] produces() default {};
 
+    /**
+     * Indicates whether the mapping is enabled.
+     * Defaults to {@code true}. If set to {@code false}, the mapping will be 
ignored.
+     */
     boolean enabled() default true;
 }
diff --git 
a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/rest/Param.java
 
b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/rest/Param.java
index 2ff94d4d32..108dfd44db 100644
--- 
a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/rest/Param.java
+++ 
b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/rest/Param.java
@@ -22,6 +22,16 @@ import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 
+/**
+ * Annotation for defining parameters on Dubbo service method parameters.
+ * Provides metadata such as parameter type, default value, and whether the 
parameter is required.
+ *
+ * <p>Example usage:</p>
+ * <pre>
+ * &#64;Param(value = "x-version", type = ParamType.Header, required = false) 
String version
+ * </pre>
+ * @see <a 
href="https://dubbo-next.staged.apache.org/zh-cn/overview/mannual/java-sdk/reference-manual/protocol/tripe-rest-manual/#kmCzf";>Tripe
 Rest Manual</a>
+ */
 @Target({ElementType.PARAMETER, ElementType.FIELD})
 @Retention(RetentionPolicy.RUNTIME)
 @Documented
@@ -29,11 +39,27 @@ public @interface Param {
 
     String DEFAULT_NONE = "\n\t\t\n\t\t\n_DEFAULT_NONE_\n\t\t\t\t\n";
 
-    ParamType type() default ParamType.Param;
-
+    /**
+     * The name of the parameter.
+     * If not specified, the parameter name is derived from the method 
signature or field name.
+     */
     String value() default "";
 
+    /**
+     * The type of the parameter, such as query, header, or path variable.
+     * Defaults to {@link ParamType#Param}.
+     */
+    ParamType type() default ParamType.Param;
+
+    /**
+     * Indicates whether the parameter is required.
+     * Defaults to {@code true}. If set to {@code false}, the parameter is 
optional.
+     */
     boolean required() default true;
 
+    /**
+     * Specifies a default value for the parameter.
+     * Defaults to {@link #DEFAULT_NONE}, indicating that there is no default 
value.
+     */
     String defaultValue() default DEFAULT_NONE;
 }
diff --git 
a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/grpc/GrpcHttp2ServerTransportListenerFactory.java
 
b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/grpc/GrpcHttp2ServerTransportListenerFactory.java
index a80131dc9e..c488204e90 100644
--- 
a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/grpc/GrpcHttp2ServerTransportListenerFactory.java
+++ 
b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/grpc/GrpcHttp2ServerTransportListenerFactory.java
@@ -21,7 +21,6 @@ import org.apache.dubbo.remoting.http12.h2.H2StreamChannel;
 import org.apache.dubbo.remoting.http12.h2.Http2ServerTransportListenerFactory;
 import org.apache.dubbo.remoting.http12.h2.Http2TransportListener;
 import org.apache.dubbo.rpc.model.FrameworkModel;
-import org.apache.dubbo.rpc.protocol.tri.TripleHeaderEnum;
 
 public class GrpcHttp2ServerTransportListenerFactory implements 
Http2ServerTransportListenerFactory {
 
@@ -32,6 +31,6 @@ public class GrpcHttp2ServerTransportListenerFactory 
implements Http2ServerTrans
 
     @Override
     public boolean supportContentType(String contentType) {
-        return contentType != null && 
contentType.startsWith(TripleHeaderEnum.APPLICATION_GRPC.getHeader());
+        return GrpcUtils.isGrpcRequest(contentType);
     }
 }
diff --git 
a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/grpc/GrpcRequestHandlerMapping.java
 
b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/grpc/GrpcRequestHandlerMapping.java
index b5467f2d50..6038a3b839 100644
--- 
a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/grpc/GrpcRequestHandlerMapping.java
+++ 
b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/grpc/GrpcRequestHandlerMapping.java
@@ -23,7 +23,6 @@ import org.apache.dubbo.remoting.http12.HttpResponse;
 import org.apache.dubbo.remoting.http12.HttpStatus;
 import org.apache.dubbo.remoting.http12.exception.HttpStatusException;
 import org.apache.dubbo.remoting.http12.message.HttpMessageCodec;
-import org.apache.dubbo.remoting.http12.message.MediaType;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.PathResolver;
 import org.apache.dubbo.rpc.model.FrameworkModel;
@@ -49,8 +48,7 @@ public final class GrpcRequestHandlerMapping implements 
RequestHandlerMapping {
 
     @Override
     public RequestHandler getRequestHandler(URL url, HttpRequest request, 
HttpResponse response) {
-        String contentType = request.contentType();
-        if (contentType == null || 
!contentType.startsWith(MediaType.APPLICATION_GRPC.getName())) {
+        if (!GrpcUtils.isGrpcRequest(request.contentType())) {
             return null;
         }
 
diff --git 
a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/grpc/GrpcUtils.java
 
b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/grpc/GrpcUtils.java
index 889b387071..a6ba31f462 100644
--- 
a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/grpc/GrpcUtils.java
+++ 
b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/grpc/GrpcUtils.java
@@ -17,6 +17,7 @@
 package org.apache.dubbo.rpc.protocol.tri.h12.grpc;
 
 import org.apache.dubbo.common.utils.StringUtils;
+import org.apache.dubbo.rpc.protocol.tri.TripleHeaderEnum;
 
 import java.util.concurrent.TimeUnit;
 
@@ -48,4 +49,8 @@ public class GrpcUtils {
                 return null;
         }
     }
+
+    public static boolean isGrpcRequest(String contentType) {
+        return contentType != null && 
contentType.startsWith(TripleHeaderEnum.APPLICATION_GRPC.getHeader());
+    }
 }
diff --git 
a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/http2/Http2ServerUnaryChannelObserver.java
 
b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/http2/Http2ServerUnaryChannelObserver.java
index 14df1bbb4e..f37974c23e 100644
--- 
a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/http2/Http2ServerUnaryChannelObserver.java
+++ 
b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/http2/Http2ServerUnaryChannelObserver.java
@@ -16,12 +16,15 @@
  */
 package org.apache.dubbo.rpc.protocol.tri.h12.http2;
 
+import org.apache.dubbo.common.logger.FluentLogger;
 import org.apache.dubbo.remoting.http12.HttpOutputMessage;
 import org.apache.dubbo.remoting.http12.h2.H2StreamChannel;
 import org.apache.dubbo.rpc.model.FrameworkModel;
 
 public class Http2ServerUnaryChannelObserver extends 
Http2ServerCallToObserverAdapter {
 
+    private static final FluentLogger LOGGER = 
FluentLogger.of(Http2ServerUnaryChannelObserver.class);
+
     public Http2ServerUnaryChannelObserver(FrameworkModel frameworkModel, 
H2StreamChannel h2StreamChannel) {
         super(frameworkModel, h2StreamChannel);
     }
@@ -37,7 +40,13 @@ public class Http2ServerUnaryChannelObserver extends 
Http2ServerCallToObserverAd
     protected void doOnError(Throwable throwable) throws Throwable {
         String statusCode = resolveStatusCode(throwable);
         Object data = buildErrorResponse(statusCode, throwable);
-        HttpOutputMessage httpOutputMessage = buildMessage(data);
+        HttpOutputMessage httpOutputMessage;
+        try {
+            httpOutputMessage = buildMessage(data);
+        } catch (Throwable t) {
+            LOGGER.internalError("Failed to build message", t);
+            httpOutputMessage = encodeHttpOutputMessage(data);
+        }
         sendHeader(buildMetadata(statusCode, data, httpOutputMessage));
         sendMessage(httpOutputMessage);
     }
diff --git 
a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h3/negotiation/Helper.java
 
b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h3/negotiation/Helper.java
index 0cc705272c..f981048943 100644
--- 
a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h3/negotiation/Helper.java
+++ 
b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h3/negotiation/Helper.java
@@ -21,7 +21,6 @@ import org.apache.dubbo.remoting.ChannelHandler;
 import org.apache.dubbo.remoting.RemotingException;
 import org.apache.dubbo.remoting.api.connection.AbstractConnectionClient;
 import org.apache.dubbo.remoting.exchange.PortUnificationExchanger;
-import 
org.apache.dubbo.remoting.transport.netty4.AbstractNettyConnectionClient;
 import org.apache.dubbo.remoting.transport.netty4.NettyHttp3ConnectionClient;
 import org.apache.dubbo.rpc.protocol.tri.ExceptionUtils;
 
@@ -33,7 +32,7 @@ public class Helper {
         return new AutoSwitchConnectionClient(url, 
PortUnificationExchanger.connect(url, handler));
     }
 
-    public static AbstractNettyConnectionClient createHttp3Client(URL url, 
ChannelHandler handler) {
+    public static AbstractConnectionClient createHttp3Client(URL url, 
ChannelHandler handler) {
         try {
             return new NettyHttp3ConnectionClient(url, handler);
         } catch (RemotingException e) {
diff --git 
a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/basic/FallbackArgumentResolver.java
 
b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/basic/FallbackArgumentResolver.java
index 4402c30c04..35c40c881e 100644
--- 
a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/basic/FallbackArgumentResolver.java
+++ 
b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/basic/FallbackArgumentResolver.java
@@ -82,7 +82,11 @@ public class FallbackArgumentResolver extends 
AbstractArgumentResolver {
                 try {
                     Object body = RequestUtils.decodeBody(request, 
meta.genericType());
                     if (body != null) {
-                        return body;
+                        if (body != RequestUtils.EMPTY_BODY) {
+                            return body;
+                        }
+                        Object value = single ? request.parameter(meta.name()) 
: request.parameterValues(meta.name());
+                        return value == null ? body : value;
                     }
                 } catch (DecodeException ignored) {
                 }
diff --git 
a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/util/RequestUtils.java
 
b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/util/RequestUtils.java
index 45d4cde2f0..bb152cc7a6 100644
--- 
a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/util/RequestUtils.java
+++ 
b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/util/RequestUtils.java
@@ -42,6 +42,8 @@ import java.util.StringTokenizer;
 
 public final class RequestUtils {
 
+    public static final String EMPTY_BODY = "";
+
     private RequestUtils() {}
 
     public static boolean isMultiPart(HttpRequest request) {
@@ -185,7 +187,7 @@ public final class RequestUtils {
                 if (type instanceof Class) {
                     Class<?> clazz = (Class<?>) type;
                     if (clazz == String.class) {
-                        return StringUtils.EMPTY_STRING;
+                        return EMPTY_BODY;
                     }
                     if (clazz == byte[].class) {
                         return new byte[0];
diff --git 
a/dubbo-rpc/dubbo-rpc-triple/src/test/groovy/org/apache/dubbo/rpc/protocol/tri/rest/support/basic/RestProtocolTest.groovy
 
b/dubbo-rpc/dubbo-rpc-triple/src/test/groovy/org/apache/dubbo/rpc/protocol/tri/rest/support/basic/RestProtocolTest.groovy
index 572adc6f09..3466cb775e 100644
--- 
a/dubbo-rpc/dubbo-rpc-triple/src/test/groovy/org/apache/dubbo/rpc/protocol/tri/rest/support/basic/RestProtocolTest.groovy
+++ 
b/dubbo-rpc/dubbo-rpc-triple/src/test/groovy/org/apache/dubbo/rpc/protocol/tri/rest/support/basic/RestProtocolTest.groovy
@@ -41,6 +41,18 @@ class RestProtocolTest extends BaseServiceTest {
             '/hello.yml?name=world' | 'hello world'
     }
 
+    def "hello world by post"() {
+        expect:
+            runner.post(path, body) == output
+        where:
+            path                | body       | output
+            '/hello?name=world' | null       | 'hello world'
+            '/hello'            | ''         | 'hello '
+            '/hello?name=world' | ''         | 'hello world'
+            '/hello'            | '"world"'  | 'hello world'
+            '/hello?name=world' | '"galaxy"' | 'hello galaxy'
+    }
+
     def "argument test"() {
         expect:
             runner.post(path, body) == output
diff --git 
a/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/test/MockH2StreamChannel.java
 
b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/test/MockH2StreamChannel.java
index d6a31e6e41..b81ef4992d 100644
--- 
a/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/test/MockH2StreamChannel.java
+++ 
b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/test/MockH2StreamChannel.java
@@ -35,7 +35,11 @@ public class MockH2StreamChannel implements H2StreamChannel {
 
     @Override
     public CompletableFuture<Void> writeHeader(HttpMetadata httpMetadata) {
-        this.httpMetadata = httpMetadata;
+        if (this.httpMetadata == null) {
+            this.httpMetadata = httpMetadata;
+        } else {
+            this.httpMetadata.headers().putAll(httpMetadata.headers());
+        }
         return CompletableFuture.completedFuture(null);
     }
 
diff --git 
a/dubbo-spring-boot/dubbo-spring-boot-3-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboTriple3AutoConfiguration.java
 
b/dubbo-spring-boot/dubbo-spring-boot-3-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboTriple3AutoConfiguration.java
index 81ef0b0582..6b03e6be7d 100644
--- 
a/dubbo-spring-boot/dubbo-spring-boot-3-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboTriple3AutoConfiguration.java
+++ 
b/dubbo-spring-boot/dubbo-spring-boot-3-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboTriple3AutoConfiguration.java
@@ -39,7 +39,7 @@ public class DubboTriple3AutoConfiguration {
     @Configuration(proxyBeanMethods = false)
     @ConditionalOnClass(Filter.class)
     @ConditionalOnWebApplication(type = Type.SERVLET)
-    @ConditionalOnProperty(prefix = PREFIX, name = "enable")
+    @ConditionalOnProperty(prefix = PREFIX, name = "enabled")
     public static class TripleServletConfiguration {
 
         @Bean
diff --git 
a/dubbo-spring-boot/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboTripleAutoConfiguration.java
 
b/dubbo-spring-boot/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboTripleAutoConfiguration.java
index c750552f68..16f21dfef8 100644
--- 
a/dubbo-spring-boot/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboTripleAutoConfiguration.java
+++ 
b/dubbo-spring-boot/dubbo-spring-boot-autoconfigure/src/main/java/org/apache/dubbo/spring/boot/autoconfigure/DubboTripleAutoConfiguration.java
@@ -40,7 +40,7 @@ public class DubboTripleAutoConfiguration {
     @Configuration(proxyBeanMethods = false)
     @ConditionalOnClass(Filter.class)
     @ConditionalOnWebApplication(type = Type.SERVLET)
-    @ConditionalOnProperty(prefix = PREFIX, name = "enable")
+    @ConditionalOnProperty(prefix = PREFIX, name = "enabled")
     public static class TripleServletConfiguration {
 
         @Bean

Reply via email to