From 205273ca8cd8d04483e7161c9561710c6dc1e03a Mon Sep 17 00:00:00 2001 From: Sean Yang Date: Tue, 19 Dec 2023 02:04:19 +0800 Subject: [PATCH] v2 --- .editorconfig | 38 +- .../apache/dubbo/common/io/StreamUtils.java | 52 ++ .../dubbo/common/utils/CollectionUtils.java | 49 +- .../apache/dubbo/common/utils/LRU2Cache.java | 48 +- .../apache/dubbo/common/utils/LRUCache.java | 75 ++- .../dubbo/common/utils/StringUtils.java | 69 +++ .../apache/dubbo/common/utils/TimeUtils.java | 25 + .../dubbo/reactive/CreateObserverAdapter.java | 63 -- .../reactive/ManyToManyMethodHandlerTest.java | 48 -- .../reactive/ManyToOneMethodHandlerTest.java | 70 --- .../reactive/OneToManyMethodHandlerTest.java | 71 --- .../AbstractServerHttpChannelObserver.java | 42 +- .../dubbo/remoting/http12/HttpChannel.java | 2 + .../dubbo/remoting/http12/HttpCookie.java | 123 ++++ .../dubbo/remoting/http12/HttpMethods.java | 60 ++ .../dubbo/remoting/http12/HttpRequest.java | 134 +++++ .../dubbo/remoting/http12/HttpResponse.java | 85 +++ .../dubbo/remoting/http12/HttpResult.java | 29 + .../dubbo/remoting/http12/HttpUtils.java | 195 +++++++ .../http12/exception/DecodeException.java | 4 + .../http12/exception/HttpStatusException.java | 5 + .../http12/h2/Http2ChannelDelegate.java | 5 + .../DefaultHttpMessageAdapterFactory.java | 37 ++ .../http12/message/DefaultHttpRequest.java | 539 ++++++++++++++++++ .../http12/message/DefaultHttpResponse.java | 270 +++++++++ .../http12/message/DefaultHttpResult.java | 56 ++ .../message/HttpMessageAdapterFactory.java | 35 ++ .../http12/message/HttpMessageDecoder.java | 5 +- .../http12/message/HttpMessageEncoder.java | 3 +- .../http12/message/codec/CodecUtils.java | 121 ++-- .../http12/message/codec/JsonCodec.java | 18 +- .../http12/message/codec/JsonPbCodec.java | 15 +- .../message/codec/MultipartDecoder.java | 2 +- .../http12/message/codec/PlainTextCodec.java | 22 +- .../message/codec/UrlEncodeFormCodec.java | 12 +- .../http12/message/codec/XmlCodec.java | 6 +- .../http12/netty4/h1/NettyHttp1Channel.java | 5 + .../h1/NettyHttp1ConnectionHandler.java | 76 +-- .../netty4/h2/NettyH2StreamChannel.java | 5 + .../h2/NettyHttp2ProtocolSelectorHandler.java | 16 +- .../http12/message/codec/CodeUtilsTest.java | 13 +- dubbo-rpc/dubbo-rpc-triple/pom.xml | 22 + .../rpc/protocol/tri/DescriptorUtils.java | 155 +++++ .../rpc/protocol/tri/RequestMetadata.java | 4 +- .../rpc/protocol/tri/TripleConstant.java | 19 +- .../rpc/protocol/tri/TripleHeaderEnum.java | 1 + .../rpc/protocol/tri/TripleHttp2Protocol.java | 12 - .../dubbo/rpc/protocol/tri/TripleInvoker.java | 2 +- .../rpc/protocol/tri/TripleProtocol.java | 24 +- ...ripleReflectionTypeDescriberRegistrar.java | 3 +- .../protocol/tri/call/AbstractServerCall.java | 438 -------------- .../tri/call/AbstractServerCallListener.java | 102 ---- .../tri/call/BiStreamServerCallListener.java | 61 -- .../call/ReflectionAbstractServerCall.java | 214 ------- .../rpc/protocol/tri/call/ServerCall.java | 75 --- .../call/ServerStreamServerCallListener.java | 51 -- .../tri/call/StubAbstractServerCall.java | 66 --- .../tri/call/UnaryServerCallListener.java | 84 --- .../tri/h12/AbstractServerCallListener.java | 4 +- .../h12/AbstractServerTransportListener.java | 382 +++---------- .../protocol/tri/h12/HttpHandlerMapping.java | 105 ++++ .../tri/h12/TripleProtocolDetector.java | 34 +- .../tri/h12/grpc/GrpcCompositeCodec.java | 5 +- .../h12/grpc/GrpcCompositeCodecFactory.java | 7 +- .../tri/h12/grpc/GrpcHandlerMapping.java | 53 ++ .../GrpcHttp2ServerTransportListener.java | 156 ++--- .../rpc/protocol/tri/h12/grpc/GrpcUtils.java | 51 -- .../h12/grpc/ProtobufHttpMessageCodec.java | 5 +- .../DefaultHttp11ServerTransportListener.java | 69 +-- .../GenericHttp2ServerTransportListener.java | 154 ++--- .../h12/http2/Http2ServerStreamObserver.java | 2 +- .../observer/ServerCallToObserverAdapter.java | 136 ----- .../protocol/tri/rest/ArgumentConverter.java | 24 + .../protocol/tri/rest/ArgumentResolver.java | 28 + .../tri/rest/RestHttpMessageCodec.java | 85 +++ .../protocol/tri/rest/RestInvokeFilter.java | 41 ++ .../DefaultRequestMappingRegistry.java | 100 ++++ .../tri/rest/mapping/MethodWalker.java | 26 + .../protocol/tri/rest/mapping/RadixTree.java | 28 + .../tri/rest/mapping/RequestMapping.java | 45 ++ .../rest/mapping/RequestMappingRegistry.java | 35 ++ .../mapping/condition/AbstractCondition.java | 22 + .../tri/rest/mapping/condition/Condition.java | 28 + .../mapping/condition/ConsumesCondition.java | 19 + .../mapping/condition/HeadersCondition.java | 19 + .../mapping/condition/MethodsCondition.java | 19 + .../mapping/condition/ParamsCondition.java | 19 + .../mapping/condition/PathsCondition.java | 19 + .../mapping/condition/ProducesCondition.java | 19 + .../tri/rest/meta/MappingDescriptor.java | 71 +++ .../tri/rest/meta/ParameterDescriptor.java | 51 ++ .../tri/rest/support/resteasy/.gitkeeper | 0 .../tri/rest/support/servlet/.gitkeeper | 0 .../ServletHttpMessageAdapterFactory.java | 38 ++ .../servlet/ServletHttpRequestAdaptee.java | 385 +++++++++++++ .../servlet/ServletHttpResponseAdaptee.java | 137 +++++ .../tri/rest/support/spring/.gitkeeper | 0 .../tri/route/DefaultRequestRouter.java | 68 +++ .../rpc/protocol/tri/route/HandlerInfo.java | 126 ++++ .../protocol/tri/route/HandlerMapping.java | 31 + .../rpc/protocol/tri/route/RequestRouter.java | 30 + .../tri/route/RpcInvocationBuildContext.java | 51 ++ .../tri/service/TriBuiltinService.java | 4 +- .../rpc/protocol/tri/stream/ServerStream.java | 64 --- .../rpc/protocol/tri/stream/StreamUtils.java | 249 ++++---- .../tri/stream/TripleServerStream.java | 505 ---------------- .../AbstractH2TransportListener.java | 2 +- .../TripleHttp2FrameServerHandler.java | 115 ---- .../tri/call/ReflectionServerCallTest.java | 92 --- .../protocol/tri/call/StubServerCallTest.java | 68 --- .../protocol/tri/stream/StreamUtilsTest.java | 6 +- 111 files changed, 4319 insertions(+), 3394 deletions(-) delete mode 100644 dubbo-plugin/dubbo-reactive/src/test/java/org/apache/dubbo/reactive/CreateObserverAdapter.java delete mode 100644 dubbo-plugin/dubbo-reactive/src/test/java/org/apache/dubbo/reactive/ManyToManyMethodHandlerTest.java delete mode 100644 dubbo-plugin/dubbo-reactive/src/test/java/org/apache/dubbo/reactive/ManyToOneMethodHandlerTest.java delete mode 100644 dubbo-plugin/dubbo-reactive/src/test/java/org/apache/dubbo/reactive/OneToManyMethodHandlerTest.java create mode 100644 dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/HttpCookie.java create mode 100644 dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/HttpMethods.java create mode 100644 dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/HttpRequest.java create mode 100644 dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/HttpResponse.java create mode 100644 dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/HttpResult.java create mode 100644 dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/HttpUtils.java create mode 100644 dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/DefaultHttpMessageAdapterFactory.java create mode 100644 dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/DefaultHttpRequest.java create mode 100644 dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/DefaultHttpResponse.java create mode 100644 dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/DefaultHttpResult.java create mode 100644 dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/HttpMessageAdapterFactory.java create mode 100644 dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/DescriptorUtils.java delete mode 100644 dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/AbstractServerCall.java delete mode 100644 dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/AbstractServerCallListener.java delete mode 100644 dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/BiStreamServerCallListener.java delete mode 100644 dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/ReflectionAbstractServerCall.java delete mode 100644 dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/ServerCall.java delete mode 100644 dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/ServerStreamServerCallListener.java delete mode 100644 dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/StubAbstractServerCall.java delete mode 100644 dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/UnaryServerCallListener.java create mode 100644 dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/HttpHandlerMapping.java create mode 100644 dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/grpc/GrpcHandlerMapping.java delete mode 100644 dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/grpc/GrpcUtils.java delete mode 100644 dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/observer/ServerCallToObserverAdapter.java create mode 100644 dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/ArgumentConverter.java create mode 100644 dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/ArgumentResolver.java create mode 100644 dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/RestHttpMessageCodec.java create mode 100644 dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/RestInvokeFilter.java create mode 100644 dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/DefaultRequestMappingRegistry.java create mode 100644 dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/MethodWalker.java create mode 100644 dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RadixTree.java create mode 100644 dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RequestMapping.java create mode 100644 dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RequestMappingRegistry.java create mode 100644 dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/AbstractCondition.java create mode 100644 dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/Condition.java create mode 100644 dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/ConsumesCondition.java create mode 100644 dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/HeadersCondition.java create mode 100644 dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/MethodsCondition.java create mode 100644 dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/ParamsCondition.java create mode 100644 dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/PathsCondition.java create mode 100644 dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/ProducesCondition.java create mode 100644 dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/meta/MappingDescriptor.java create mode 100644 dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/meta/ParameterDescriptor.java create mode 100644 dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/resteasy/.gitkeeper create mode 100644 dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/.gitkeeper create mode 100644 dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/ServletHttpMessageAdapterFactory.java create mode 100644 dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/ServletHttpRequestAdaptee.java create mode 100644 dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/ServletHttpResponseAdaptee.java create mode 100644 dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/.gitkeeper create mode 100644 dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/route/DefaultRequestRouter.java create mode 100644 dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/route/HandlerInfo.java create mode 100644 dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/route/HandlerMapping.java create mode 100644 dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/route/RequestRouter.java create mode 100644 dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/route/RpcInvocationBuildContext.java delete mode 100644 dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/stream/ServerStream.java delete mode 100644 dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/stream/TripleServerStream.java delete mode 100644 dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/transport/TripleHttp2FrameServerHandler.java delete mode 100644 dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/call/ReflectionServerCallTest.java delete mode 100644 dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/call/StubServerCallTest.java diff --git a/.editorconfig b/.editorconfig index 68a55e785793..f1ceb18698e3 100644 --- a/.editorconfig +++ b/.editorconfig @@ -23,12 +23,44 @@ root = true [*] charset = utf-8 end_of_line = lf +indent_size = 4 +indent_style = space +tab_width = 4 +max_line_length = 360 insert_final_newline = true +trim_trailing_whitespace = true # 4 space indentation -[*.{java,xml}] -indent_style = space -indent_size = 4 +[*.java] +ij_java_continuation_indent_size = 8 +ij_java_align_multiline_deconstruction_list_components = false +ij_java_align_multiline_parameters = false +ij_java_call_parameters_new_line_after_left_paren = true +ij_java_call_parameters_wrap = normal +ij_java_keep_control_statement_in_one_line = false +ij_java_keep_first_column_comment = false +ij_java_keep_line_breaks = false +ij_java_method_call_chain_wrap = normal +ij_java_method_parameters_new_line_after_left_paren = true +ij_java_rparen_on_new_line_in_deconstruction_pattern = false +ij_java_wrap_first_method_in_call_chain = true +ij_java_wrap_long_lines = true +ij_java_class_count_to_use_import_on_demand = 999 +ij_java_names_count_to_use_import_on_demand = 999 +ij_java_insert_inner_class_imports = true +ij_java_for_brace_force = always +ij_java_if_brace_force = always +ij_java_blank_lines_around_class = 1 + +[*.json] +tab_width = 2 + +[*.{yml,yaml}] +indent_size = 2 + +[*.xml] +ij_xml_attribute_wrap = off +ij_xml_keep_blank_lines = 1 [pom.xml] indent_size = 2 diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/io/StreamUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/io/StreamUtils.java index b0badff64ec7..9e10135d87bb 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/io/StreamUtils.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/io/StreamUtils.java @@ -16,8 +16,13 @@ */ package org.apache.dubbo.common.io; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; /** * Stream utils. @@ -229,4 +234,51 @@ public static void skipUnusedStream(InputStream is) throws IOException { is.skip(is.available()); } } + + public static void copy(InputStream in, OutputStream out) throws IOException { + if (in.getClass() == ByteArrayInputStream.class) { + copy((ByteArrayInputStream) in, out); + return; + } + byte[] buffer = new byte[4096]; + int bytesRead; + while ((bytesRead = in.read(buffer)) != -1) { + out.write(buffer, 0, bytesRead); + } + } + + public static void copy(ByteArrayInputStream in, OutputStream out) throws IOException { + int len = in.available(); + byte[] buffer = new byte[len]; + in.read(buffer, 0, len); + out.write(buffer, 0, len); + } + + public static byte[] readBytes(InputStream in) throws IOException { + if (in.getClass() == ByteArrayInputStream.class) { + return readBytes((ByteArrayInputStream) in); + } + ByteArrayOutputStream out = new ByteArrayOutputStream(); + byte[] buffer = new byte[4096]; + int bytesRead; + while ((bytesRead = in.read(buffer)) != -1) { + out.write(buffer, 0, bytesRead); + } + return out.toByteArray(); + } + + public static byte[] readBytes(ByteArrayInputStream in) throws IOException { + int len = in.available(); + byte[] bytes = new byte[len]; + in.read(bytes, 0, len); + return bytes; + } + + public static String toString(InputStream in) throws IOException { + return new String(readBytes(in), StandardCharsets.UTF_8); + } + + public static String toString(InputStream in, Charset charset) throws IOException { + return new String(readBytes(in), charset); + } } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/CollectionUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/CollectionUtils.java index df6388c936da..1bcae7a093f1 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/CollectionUtils.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/CollectionUtils.java @@ -24,11 +24,13 @@ import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; +import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; import static java.util.Collections.emptySet; @@ -404,13 +406,19 @@ public static T first(Collection values) { return null; } if (values instanceof List) { - List list = (List) values; - return list.get(0); + return ((List) values).get(0); } else { return values.iterator().next(); } } + public static T first(List values) { + if (isEmpty(values)) { + return null; + } + return values.get(0); + } + public static Set toTreeSet(Set set) { if (isEmpty(set)) { return set; @@ -420,4 +428,41 @@ public static Set toTreeSet(Set set) { } return set; } + + public static Set newHashSet(int expectedSize) { + return new HashSet<>(capacity(expectedSize)); + } + + public static Set newLinkedHashSet(int expectedSize) { + return new LinkedHashSet<>(capacity(expectedSize)); + } + + public static Set newConcurrentHashSet(int expectedSize) { + return Collections.newSetFromMap(newConcurrentHashMap(expectedSize)); + } + + public static Map newHashMap(int expectedSize) { + return new HashMap<>(capacity(expectedSize)); + } + + public static Map newLinkedHashMap(int expectedSize) { + return new LinkedHashMap<>(capacity(expectedSize)); + } + + public static Map newConcurrentHashMap(int expectedSize) { + return new ConcurrentHashMap<>(capacity(expectedSize)); + } + + public static int capacity(int expectedSize) { + if (expectedSize < 3) { + if (expectedSize < 0) { + throw new IllegalArgumentException("expectedSize cannot be negative but was: " + expectedSize); + } + return expectedSize + 1; + } + if (expectedSize < 1 << (Integer.SIZE - 2)) { + return (int) (expectedSize / 0.75F + 1.0F); + } + return Integer.MAX_VALUE; + } } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/LRU2Cache.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/LRU2Cache.java index 2a725e933434..17450952cf21 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/LRU2Cache.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/LRU2Cache.java @@ -17,15 +17,16 @@ package org.apache.dubbo.common.utils; import java.util.LinkedHashMap; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.function.Function; /** * LRU-2 - *

+ *

* When the data accessed for the first time, add it to history list. If the size of history list reaches max capacity, eliminate the earliest data (first in first out). * When the data already exists in the history list, and be accessed for the second time, then it will be put into cache. - * + *

* TODO, consider replacing with ConcurrentHashMap to improve performance under concurrency */ public class LRU2Cache extends LinkedHashMap { @@ -33,11 +34,10 @@ public class LRU2Cache extends LinkedHashMap { private static final long serialVersionUID = -5167631809472116969L; private static final float DEFAULT_LOAD_FACTOR = 0.75f; - private static final int DEFAULT_MAX_CAPACITY = 1000; - private final Lock lock = new ReentrantLock(); - private volatile int maxCapacity; + private final ReadWriteLock lock = new ReentrantReadWriteLock(); + private volatile int maxCapacity; // as history list private final PreCache preCache; @@ -58,27 +58,27 @@ protected boolean removeEldestEntry(java.util.Map.Entry eldest) { @Override public boolean containsKey(Object key) { - lock.lock(); + lock.readLock().lock(); try { return super.containsKey(key); } finally { - lock.unlock(); + lock.readLock().unlock(); } } @Override public V get(Object key) { - lock.lock(); + lock.readLock().lock(); try { return super.get(key); } finally { - lock.unlock(); + lock.readLock().unlock(); } } @Override public V put(K key, V value) { - lock.lock(); + lock.writeLock().lock(); try { if (preCache.containsKey(key)) { // add it to cache @@ -90,39 +90,49 @@ public V put(K key, V value) { return value; } } finally { - lock.unlock(); + lock.writeLock().unlock(); + } + } + + @Override + public V computeIfAbsent(K key, Function fn) { + V value = get(key); + if (value == null) { + value = fn.apply(key); + put(key, value); } + return value; } @Override public V remove(Object key) { - lock.lock(); + lock.writeLock().lock(); try { preCache.remove(key); return super.remove(key); } finally { - lock.unlock(); + lock.writeLock().unlock(); } } @Override public int size() { - lock.lock(); + lock.readLock().lock(); try { return super.size(); } finally { - lock.unlock(); + lock.readLock().unlock(); } } @Override public void clear() { - lock.lock(); + lock.writeLock().lock(); try { preCache.clear(); super.clear(); } finally { - lock.unlock(); + lock.writeLock().unlock(); } } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/LRUCache.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/LRUCache.java index 76d6c19cb713..2b0e9c7d56b2 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/LRUCache.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/LRUCache.java @@ -17,8 +17,9 @@ package org.apache.dubbo.common.utils; import java.util.LinkedHashMap; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.function.Function; /** * A 'least recently used' cache based on LinkedHashMap. @@ -31,9 +32,9 @@ public class LRUCache extends LinkedHashMap { private static final long serialVersionUID = -5167631809472116969L; private static final float DEFAULT_LOAD_FACTOR = 0.75f; - private static final int DEFAULT_MAX_CAPACITY = 1000; - private final Lock lock = new ReentrantLock(); + + private final ReadWriteLock lock = new ReentrantReadWriteLock(); private volatile int maxCapacity; public LRUCache() { @@ -51,71 +52,85 @@ protected boolean removeEldestEntry(java.util.Map.Entry eldest) { } @Override - public boolean containsKey(Object key) { - lock.lock(); + public V get(Object key) { + lock.readLock().lock(); try { - return super.containsKey(key); + return super.get(key); } finally { - lock.unlock(); + lock.readLock().unlock(); } } @Override - public V get(Object key) { - lock.lock(); + public void clear() { + lock.writeLock().lock(); try { - return super.get(key); + super.clear(); } finally { - lock.unlock(); + lock.writeLock().unlock(); } } @Override - public V put(K key, V value) { - lock.lock(); + public int size() { + lock.readLock().lock(); try { - return super.put(key, value); + return super.size(); } finally { - lock.unlock(); + lock.readLock().unlock(); } } @Override - public V remove(Object key) { - lock.lock(); + public boolean containsKey(Object key) { + lock.readLock().lock(); try { - return super.remove(key); + return super.containsKey(key); } finally { - lock.unlock(); + lock.readLock().unlock(); } } @Override - public int size() { - lock.lock(); + public V put(K key, V value) { + lock.writeLock().lock(); try { - return super.size(); + return super.put(key, value); } finally { - lock.unlock(); + lock.writeLock().unlock(); } } @Override - public void clear() { - lock.lock(); + public V remove(Object key) { + lock.writeLock().lock(); try { - super.clear(); + return super.remove(key); } finally { - lock.unlock(); + lock.writeLock().unlock(); + } + } + + @Override + public V computeIfAbsent(K key, Function fn) { + V value = get(key); + if (value == null) { + lock.writeLock().lock(); + try { + return super.computeIfAbsent(key, fn); + } finally { + lock.writeLock().unlock(); + } } + return value; } public void lock() { - lock.lock(); + lock.writeLock().lock(); } public void releaseLock() { - lock.unlock(); + lock.writeLock().unlock(); } public int getMaxCapacity() { diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/StringUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/StringUtils.java index 0a8cba4bcd42..34cff6af5dc9 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/StringUtils.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/StringUtils.java @@ -1268,4 +1268,73 @@ public static boolean startsWithIgnoreCase(String str, String prefix) { // return str.substring(0, prefix.length()).equalsIgnoreCase(prefix); return str.regionMatches(true, 0, prefix, 0, prefix.length()); } + + public static String substringBefore(String str, int separator) { + if (isEmpty(str)) { + return str; + } + int pos = str.indexOf(separator); + return pos == INDEX_NOT_FOUND ? str : str.substring(0, pos); + } + + public static String substringBefore(String str, String separator) { + if (isEmpty(str) || separator == null) { + return str; + } + if (separator.isEmpty()) { + return EMPTY_STRING; + } + int pos = str.indexOf(separator); + return pos == INDEX_NOT_FOUND ? str : str.substring(0, pos); + } + + /** + * Tokenize the given String into a String array. + * Trims tokens and omits empty tokens. + */ + public static String[] tokenize(String str, char... separators) { + if (isEmpty(str)) { + return new String[0]; + } + if (separators == null) { + separators = new char[] {','}; + } + List tokens = new ArrayList<>(); + int start = -1, end = 0; + int i = 0; + out: + for (int len = str.length(), sLen = separators.length; i < len; i++) { + char c = str.charAt(i); + for (int j = 0; j < sLen; j++) { + if (c == separators[j]) { + if (start > -1) { + tokens.add(str.substring(start, end)); + start = -1; + } + continue out; + } + } + switch (c) { + case ' ': + case '\t': + case '\n': + case '\r': + break; + default: + if (start == -1) { + start = i; + } + end = i + 1; + break; + } + } + if (start > -1) { + String part = str.substring(start, end); + if (tokens.isEmpty()) { + return new String[] {part}; + } + tokens.add(part); + } + return tokens.toArray(new String[0]); + } } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/TimeUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/TimeUtils.java index d6b18476f34b..c21a0de7513d 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/TimeUtils.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/TimeUtils.java @@ -73,4 +73,29 @@ private static synchronized void startTicker() { isTickerAlive = true; } } + + public static Long parseTimeoutToMills(String timeoutVal) { + if (StringUtils.isEmpty(timeoutVal) || StringUtils.isContains(timeoutVal, "null")) { + return null; + } + long value = Long.parseLong(timeoutVal.substring(0, timeoutVal.length() - 1)); + char unit = timeoutVal.charAt(timeoutVal.length() - 1); + switch (unit) { + case 'n': + return TimeUnit.NANOSECONDS.toMillis(value); + case 'u': + return TimeUnit.MICROSECONDS.toMillis(value); + case 'm': + return value; + case 'S': + return TimeUnit.SECONDS.toMillis(value); + case 'M': + return TimeUnit.MINUTES.toMillis(value); + case 'H': + return TimeUnit.HOURS.toMillis(value); + default: + // invalid timeout config + return null; + } + } } diff --git a/dubbo-plugin/dubbo-reactive/src/test/java/org/apache/dubbo/reactive/CreateObserverAdapter.java b/dubbo-plugin/dubbo-reactive/src/test/java/org/apache/dubbo/reactive/CreateObserverAdapter.java deleted file mode 100644 index 99f44c7cc580..000000000000 --- a/dubbo-plugin/dubbo-reactive/src/test/java/org/apache/dubbo/reactive/CreateObserverAdapter.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 - * - * http://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.apache.dubbo.reactive; - -import org.apache.dubbo.rpc.protocol.tri.observer.ServerCallToObserverAdapter; - -import java.util.concurrent.atomic.AtomicInteger; - -import org.mockito.Mockito; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.doAnswer; - -public class CreateObserverAdapter { - - private ServerCallToObserverAdapter responseObserver; - private AtomicInteger nextCounter; - private AtomicInteger completeCounter; - private AtomicInteger errorCounter; - - CreateObserverAdapter() { - - nextCounter = new AtomicInteger(); - completeCounter = new AtomicInteger(); - errorCounter = new AtomicInteger(); - - responseObserver = Mockito.mock(ServerCallToObserverAdapter.class); - doAnswer(o -> nextCounter.incrementAndGet()).when(responseObserver).onNext(anyString()); - doAnswer(o -> completeCounter.incrementAndGet()).when(responseObserver).onCompleted(); - doAnswer(o -> errorCounter.incrementAndGet()).when(responseObserver).onError(any(Throwable.class)); - } - - public AtomicInteger getCompleteCounter() { - return completeCounter; - } - - public AtomicInteger getNextCounter() { - return nextCounter; - } - - public AtomicInteger getErrorCounter() { - return errorCounter; - } - - public ServerCallToObserverAdapter getResponseObserver() { - return this.responseObserver; - } -} diff --git a/dubbo-plugin/dubbo-reactive/src/test/java/org/apache/dubbo/reactive/ManyToManyMethodHandlerTest.java b/dubbo-plugin/dubbo-reactive/src/test/java/org/apache/dubbo/reactive/ManyToManyMethodHandlerTest.java deleted file mode 100644 index ddad0dd86f85..000000000000 --- a/dubbo-plugin/dubbo-reactive/src/test/java/org/apache/dubbo/reactive/ManyToManyMethodHandlerTest.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 - * - * http://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.apache.dubbo.reactive; - -import org.apache.dubbo.common.stream.StreamObserver; -import org.apache.dubbo.reactive.handler.ManyToManyMethodHandler; - -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -/** - * Unit test for ManyToManyMethodHandler - */ -public final class ManyToManyMethodHandlerTest { - @Test - void testInvoke() throws ExecutionException, InterruptedException { - CreateObserverAdapter creator = new CreateObserverAdapter(); - - ManyToManyMethodHandler handler = - new ManyToManyMethodHandler<>(requestFlux -> requestFlux.map(r -> r + "0")); - CompletableFuture> future = handler.invoke(new Object[] {creator.getResponseObserver()}); - StreamObserver requestObserver = future.get(); - for (int i = 0; i < 10; i++) { - requestObserver.onNext(String.valueOf(i)); - } - requestObserver.onCompleted(); - Assertions.assertEquals(10, creator.getNextCounter().get()); - Assertions.assertEquals(0, creator.getErrorCounter().get()); - Assertions.assertEquals(1, creator.getCompleteCounter().get()); - } -} diff --git a/dubbo-plugin/dubbo-reactive/src/test/java/org/apache/dubbo/reactive/ManyToOneMethodHandlerTest.java b/dubbo-plugin/dubbo-reactive/src/test/java/org/apache/dubbo/reactive/ManyToOneMethodHandlerTest.java deleted file mode 100644 index 693d4cce6237..000000000000 --- a/dubbo-plugin/dubbo-reactive/src/test/java/org/apache/dubbo/reactive/ManyToOneMethodHandlerTest.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 - * - * http://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.apache.dubbo.reactive; - -import org.apache.dubbo.common.stream.StreamObserver; -import org.apache.dubbo.reactive.handler.ManyToOneMethodHandler; - -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -/** - * Unit test for ManyToOneMethodHandler - */ -public final class ManyToOneMethodHandlerTest { - - private StreamObserver requestObserver; - private CreateObserverAdapter creator; - - @BeforeEach - void init() throws ExecutionException, InterruptedException { - creator = new CreateObserverAdapter(); - ManyToOneMethodHandler handler = new ManyToOneMethodHandler<>(requestFlux -> - requestFlux.map(Integer::valueOf).reduce(Integer::sum).map(String::valueOf)); - CompletableFuture> future = handler.invoke(new Object[] {creator.getResponseObserver()}); - requestObserver = future.get(); - } - - @Test - void testInvoker() { - for (int i = 0; i < 10; i++) { - requestObserver.onNext(String.valueOf(i)); - } - requestObserver.onCompleted(); - Assertions.assertEquals(1, creator.getNextCounter().get()); - Assertions.assertEquals(0, creator.getErrorCounter().get()); - Assertions.assertEquals(1, creator.getCompleteCounter().get()); - } - - @Test - void testError() { - for (int i = 0; i < 10; i++) { - if (i == 6) { - requestObserver.onError(new Throwable()); - } - requestObserver.onNext(String.valueOf(i)); - } - requestObserver.onCompleted(); - Assertions.assertEquals(0, creator.getNextCounter().get()); - Assertions.assertEquals(1, creator.getErrorCounter().get()); - Assertions.assertEquals(0, creator.getCompleteCounter().get()); - } -} diff --git a/dubbo-plugin/dubbo-reactive/src/test/java/org/apache/dubbo/reactive/OneToManyMethodHandlerTest.java b/dubbo-plugin/dubbo-reactive/src/test/java/org/apache/dubbo/reactive/OneToManyMethodHandlerTest.java deleted file mode 100644 index e81e0a77c7c3..000000000000 --- a/dubbo-plugin/dubbo-reactive/src/test/java/org/apache/dubbo/reactive/OneToManyMethodHandlerTest.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 - * - * http://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.apache.dubbo.reactive; - -import org.apache.dubbo.reactive.handler.OneToManyMethodHandler; - -import java.util.concurrent.CompletableFuture; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import reactor.core.publisher.Flux; - -/** - * Unit test for OneToManyMethodHandler - */ -public final class OneToManyMethodHandlerTest { - - private CreateObserverAdapter creator; - - @BeforeEach - void init() { - creator = new CreateObserverAdapter(); - } - - @Test - void testInvoke() { - String request = "1,2,3,4,5,6,7"; - OneToManyMethodHandler handler = - new OneToManyMethodHandler<>(requestMono -> requestMono.flatMapMany(r -> Flux.fromArray(r.split(",")))); - CompletableFuture future = handler.invoke(new Object[] {request, creator.getResponseObserver()}); - Assertions.assertTrue(future.isDone()); - Assertions.assertEquals(7, creator.getNextCounter().get()); - Assertions.assertEquals(0, creator.getErrorCounter().get()); - Assertions.assertEquals(1, creator.getCompleteCounter().get()); - } - - @Test - void testError() { - String request = "1,2,3,4,5,6,7"; - OneToManyMethodHandler handler = - new OneToManyMethodHandler<>(requestMono -> Flux.create(emitter -> { - for (int i = 0; i < 10; i++) { - if (i == 6) { - emitter.error(new Throwable()); - } else { - emitter.next(String.valueOf(i)); - } - } - })); - CompletableFuture future = handler.invoke(new Object[] {request, creator.getResponseObserver()}); - Assertions.assertTrue(future.isDone()); - Assertions.assertEquals(6, creator.getNextCounter().get()); - Assertions.assertEquals(1, creator.getErrorCounter().get()); - Assertions.assertEquals(0, creator.getCompleteCounter().get()); - } -} diff --git a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/AbstractServerHttpChannelObserver.java b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/AbstractServerHttpChannelObserver.java index d13c4feeec0e..ffcbb1797de0 100644 --- a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/AbstractServerHttpChannelObserver.java +++ b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/AbstractServerHttpChannelObserver.java @@ -20,6 +20,9 @@ import org.apache.dubbo.remoting.http12.exception.HttpStatusException; import org.apache.dubbo.remoting.http12.message.HttpMessageEncoder; +import java.util.List; +import java.util.Map; + public abstract class AbstractServerHttpChannelObserver implements CustomizableHttpChannelObserver { private HeadersCustomizer headersCustomizer = HeadersCustomizer.NO_OP; @@ -72,12 +75,19 @@ protected TrailersCustomizer getTrailersCustomizer() { @Override public void onNext(Object data) { try { + if (data instanceof HttpResult) { + HttpResult result = (HttpResult) data; + if (!headerSent) { + doSendHeaders(String.valueOf(result.getStatus()), result.getHeaders()); + } + data = result.getBody(); + } if (!headerSent) { - doSendHeaders(HttpStatus.OK.getStatusString()); + doSendHeaders(HttpStatus.OK.getStatusString(), null); } HttpOutputMessage outputMessage = encodeHttpOutputMessage(data); preOutputMessage(outputMessage); - this.responseEncoder.encode(outputMessage.getBody(), data); + responseEncoder.encode(outputMessage.getBody(), data); getHttpChannel().writeMessage(outputMessage); postOutputMessage(outputMessage); } catch (Throwable e) { @@ -106,15 +116,15 @@ public void onError(Throwable throwable) { httpStatusCode = ((HttpStatusException) throwable).getStatusCode(); } if (!headerSent) { - doSendHeaders(String.valueOf(httpStatusCode)); + doSendHeaders(String.valueOf(httpStatusCode), null); } try { ErrorResponse errorResponse = new ErrorResponse(); errorResponse.setStatus(String.valueOf(httpStatusCode)); errorResponse.setMessage(throwable.getMessage()); - this.errorResponseCustomizer.accept(errorResponse, throwable); + errorResponseCustomizer.accept(errorResponse, throwable); HttpOutputMessage httpOutputMessage = encodeHttpOutputMessage(errorResponse); - this.responseEncoder.encode(httpOutputMessage.getBody(), errorResponse); + responseEncoder.encode(httpOutputMessage.getBody(), errorResponse); getHttpChannel().writeMessage(httpOutputMessage); } catch (Throwable ex) { throwable = new EncodeException(ex); @@ -133,17 +143,19 @@ public HttpChannel getHttpChannel() { return httpChannel; } - private void doSendHeaders(String statusCode) { + private void doSendHeaders(String statusCode, Map> additionalHeaders) { HttpMetadata httpMetadata = encodeHttpMetadata(); - httpMetadata.headers().set(HttpHeaderNames.STATUS.getName(), statusCode); - httpMetadata - .headers() - .set( - HttpHeaderNames.CONTENT_TYPE.getName(), - responseEncoder.mediaType().getName()); - this.headersCustomizer.accept(httpMetadata.headers()); + HttpHeaders headers = httpMetadata.headers(); + headers.set(HttpHeaderNames.STATUS.getName(), statusCode); + headers.set( + HttpHeaderNames.CONTENT_TYPE.getName(), + responseEncoder.mediaType().getName()); + headersCustomizer.accept(headers); + if (additionalHeaders != null) { + headers.putAll(additionalHeaders); + } getHttpChannel().writeHeader(httpMetadata); - this.headerSent = true; + headerSent = true; } protected void doOnCompleted(Throwable throwable) { @@ -151,7 +163,7 @@ protected void doOnCompleted(Throwable throwable) { if (httpMetadata == null) { return; } - this.trailersCustomizer.accept(httpMetadata.headers(), throwable); + trailersCustomizer.accept(httpMetadata.headers(), throwable); getHttpChannel().writeHeader(httpMetadata); } } diff --git a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/HttpChannel.java b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/HttpChannel.java index 795bf8ee3773..67c2a1cfebb9 100644 --- a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/HttpChannel.java +++ b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/HttpChannel.java @@ -29,5 +29,7 @@ public interface HttpChannel { SocketAddress remoteAddress(); + SocketAddress localAddress(); + void flush(); } diff --git a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/HttpCookie.java b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/HttpCookie.java new file mode 100644 index 000000000000..4a310d50dadf --- /dev/null +++ b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/HttpCookie.java @@ -0,0 +1,123 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * http://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.apache.dubbo.remoting.http12; + +import org.apache.dubbo.common.utils.Assert; +import org.apache.dubbo.common.utils.StringUtils; + +public final class HttpCookie { + + private final String name; + private String value; + private String domain; + private String path; + private long maxAge = Long.MIN_VALUE; + private boolean secure; + private boolean httpOnly; + private String sameSite; + + public HttpCookie(String name, String value) { + name = StringUtils.trim(name); + Assert.notEmptyString(name, "name is required"); + this.name = name.trim(); + setValue(value); + } + + public String name() { + return name; + } + + public String value() { + return value; + } + + public void setValue(String value) { + Assert.notNull(name, "value can not be null"); + this.value = value; + } + + public String domain() { + return domain; + } + + public void setDomain(String domain) { + this.domain = domain; + } + + public String path() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public long maxAge() { + return maxAge; + } + + public void setMaxAge(long maxAge) { + this.maxAge = maxAge; + } + + public boolean secure() { + return secure; + } + + public void setSecure(boolean secure) { + this.secure = secure; + } + + public boolean httpOnly() { + return httpOnly; + } + + public void setHttpOnly(boolean httpOnly) { + this.httpOnly = httpOnly; + } + + public String sameSite() { + return sameSite; + } + + public void setSameSite(String sameSite) { + this.sameSite = sameSite; + } + + public String toString() { + StringBuilder buf = new StringBuilder(name).append('=').append(value); + if (domain != null) { + buf.append(", domain=").append(domain); + } + if (path != null) { + buf.append(", path=").append(path); + } + if (maxAge >= 0) { + buf.append(", maxAge=").append(maxAge).append('s'); + } + if (secure) { + buf.append(", secure"); + } + if (httpOnly) { + buf.append(", HTTPOnly"); + } + if (sameSite != null) { + buf.append(", SameSite=").append(sameSite); + } + return buf.toString(); + } +} diff --git a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/HttpMethods.java b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/HttpMethods.java new file mode 100644 index 000000000000..8032c6295398 --- /dev/null +++ b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/HttpMethods.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * http://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.apache.dubbo.remoting.http12; + +import java.nio.charset.StandardCharsets; + +public enum HttpMethods { + GET, + POST, + PUT, + DELETE, + HEAD, + OPTIONS, + PATCH, + TRACE; + + public static final byte[][] HTTP_METHODS_BYTES; + + static { + HttpMethods[] methods = values(); + int len = methods.length; + HTTP_METHODS_BYTES = new byte[len][]; + for (int i = 0; i < len; i++) { + HTTP_METHODS_BYTES[i] = methods[i].name().getBytes(StandardCharsets.ISO_8859_1); + } + } + + @SuppressWarnings("StringEquality") + public static HttpMethods of(String name) { + // fast-path + if (name == GET.name()) { + return GET; + } else if (name == POST.name()) { + return POST; + } + return valueOf(name); + } + + public static boolean isGet(String name) { + return GET.name().equals(name); + } + + public static boolean isPost(String name) { + return POST.name().equals(name); + } +} diff --git a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/HttpRequest.java b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/HttpRequest.java new file mode 100644 index 000000000000..904e01320a9f --- /dev/null +++ b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/HttpRequest.java @@ -0,0 +1,134 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * http://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.apache.dubbo.remoting.http12; + +import java.io.InputStream; +import java.util.*; + +public interface HttpRequest extends RequestMetadata { + + boolean IsHttp2(); + + String method(); + + void setMethod(String method); + + String uri(); + + void setUri(String uri); + + String path(); + + String query(); + + String header(String name); + + List headerValues(String name); + + Date dateHeader(String name); + + boolean hasHeader(String name); + + Collection headerNames(); + + HttpHeaders headers(); + + void setHeader(String name, String value); + + void setHeader(String name, List value); + + void setHeader(String name, Date value); + + Collection cookies(); + + HttpCookie cookie(String name); + + String contentType(); + + void setContentType(String contentType); + + int contentLength(); + + String mediaType(); + + String charset(); + + List accept(); + + Locale locale(); + + List locales(); + + String scheme(); + + String serverHost(); + + String serverName(); + + int serverPort(); + + String remoteHost(); + + String remoteAddr(); + + int remotePort(); + + String localHost(); + + String localAddr(); + + int localPort(); + + String parameter(String name); + + String parameter(String name, String defaultValue); + + List parameterValues(String name); + + Collection parameterNames(); + + Collection parts(); + + FileUpload part(String name); + + Object attribute(String name); + + T attribute(String name, Class type); + + void setAttribute(String name, Object value); + + Collection attributeNames(); + + Map attributes(); + + InputStream inputStream(); + + void setInputStream(InputStream is); + + interface FileUpload { + + String name(); + + String filename(); + + String contentType(); + + int size(); + + InputStream inputStream(); + } +} diff --git a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/HttpResponse.java b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/HttpResponse.java new file mode 100644 index 000000000000..41ef10e589ea --- /dev/null +++ b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/HttpResponse.java @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * http://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.apache.dubbo.remoting.http12; + +import java.io.OutputStream; +import java.util.Collection; +import java.util.Date; +import java.util.List; + +public interface HttpResponse { + + int status(); + + void setStatus(int status); + + String header(String name); + + List headerValues(String name); + + Date dateHeader(String name); + + boolean hasHeader(String name); + + Collection headerNames(); + + HttpHeaders headers(); + + void addHeader(String name, String value); + + void setHeader(String name, String value); + + void setHeader(String name, List value); + + void setHeader(String name, Date value); + + void addCookie(HttpCookie cookie); + + String contentType(); + + void setContentType(String contentType); + + String mediaType(); + + String charset(); + + String locale(); + + void setLocale(String locale); + + OutputStream outputStream(); + + void setOutputStream(OutputStream os); + + Object body(); + + void setBody(Object body); + + void sendRedirect(String location); + + void sendError(int status); + + void sendError(int status, String message); + + boolean isCommitted(); + + void commit(); + + void reset(); + + void resetBuffer(); +} diff --git a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/HttpResult.java b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/HttpResult.java new file mode 100644 index 000000000000..2b6a00d21ad9 --- /dev/null +++ b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/HttpResult.java @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * http://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.apache.dubbo.remoting.http12; + +import java.util.List; +import java.util.Map; + +public interface HttpResult { + + int getStatus(); + + Map> getHeaders(); + + T getBody(); +} diff --git a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/HttpUtils.java b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/HttpUtils.java new file mode 100644 index 000000000000..888f924e6fa3 --- /dev/null +++ b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/HttpUtils.java @@ -0,0 +1,195 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * http://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.apache.dubbo.remoting.http12; + +import org.apache.dubbo.common.io.StreamUtils; +import org.apache.dubbo.common.utils.StringUtils; +import org.apache.dubbo.remoting.http12.exception.DecodeException; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.TreeMap; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufInputStream; +import io.netty.buffer.Unpooled; +import io.netty.handler.codec.http.DefaultFullHttpRequest; +import io.netty.handler.codec.http.DefaultHttpHeaders; +import io.netty.handler.codec.http.HttpMethod; +import io.netty.handler.codec.http.HttpVersion; +import io.netty.handler.codec.http.cookie.Cookie; +import io.netty.handler.codec.http.cookie.CookieHeaderNames.SameSite; +import io.netty.handler.codec.http.cookie.DefaultCookie; +import io.netty.handler.codec.http.cookie.ServerCookieDecoder; +import io.netty.handler.codec.http.cookie.ServerCookieEncoder; +import io.netty.handler.codec.http.multipart.Attribute; +import io.netty.handler.codec.http.multipart.DefaultHttpDataFactory; +import io.netty.handler.codec.http.multipart.FileUpload; +import io.netty.handler.codec.http.multipart.HttpDataFactory; +import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder; +import io.netty.handler.codec.http.multipart.InterfaceHttpData; + +public final class HttpUtils { + + public static final HttpDataFactory DATA_FACTORY = new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE); + + private HttpUtils() {} + + public static List decodeCookies(String value) { + List cookies = new ArrayList<>(); + for (Cookie c : ServerCookieDecoder.LAX.decodeAll(value)) { + cookies.add(new HttpCookie(c.name(), c.value())); + } + return cookies; + } + + public static List encodeCookies(Collection cookies) { + List encodedCookies = new ArrayList<>(cookies.size()); + for (HttpCookie cookie : cookies) { + DefaultCookie c = new DefaultCookie(cookie.name(), cookie.value()); + c.setPath(cookie.path()); + c.setDomain(cookie.domain()); + c.setMaxAge(cookie.maxAge()); + c.setSecure(cookie.secure()); + c.setHttpOnly(cookie.httpOnly()); + c.setSameSite(SameSite.valueOf(cookie.sameSite())); + encodedCookies.add(ServerCookieEncoder.LAX.encode(c)); + } + return encodedCookies; + } + + public static List parseAccept(String header) { + Map locales = new TreeMap<>(); + if (header == null) { + return Collections.emptyList(); + } + for (String item : StringUtils.tokenize(header, ',')) { + String[] pair = StringUtils.tokenize(item, ';'); + locales.put(pair.length > 1 ? Float.parseFloat(pair[1]) : 1.0F, pair[0]); + } + return new ArrayList<>(locales.values()); + } + + public static List parseAcceptLanguage(String header) { + Map locales = new TreeMap<>(); + if (header == null) { + return Collections.emptyList(); + } + for (String item : StringUtils.tokenize(header, ',')) { + String[] pair = StringUtils.tokenize(item, ';'); + Locale locale; + String[] parts = StringUtils.tokenize(pair[0], '-', '_'); + switch (parts.length) { + case 2: + locale = new Locale(parts[0], parts[1]); + break; + case 3: + locale = new Locale(parts[0], parts[1], parts[2]); + break; + default: + locale = new Locale(parts[0]); + break; + } + locales.put(pair.length > 1 ? Float.parseFloat(pair[1]) : 1.0F, locale); + } + return new ArrayList<>(locales.values()); + } + + public static HttpPostRequestDecoder createPostRequestDecoder( + HttpRequest request, InputStream inputStream, String charset) { + ByteBuf data; + try { + data = Unpooled.wrappedBuffer(StreamUtils.readBytes(inputStream)); + } catch (IOException e) { + throw new DecodeException("Error while reading post data: " + e.getMessage(), e); + } finally { + try { + inputStream.close(); + } catch (IOException ignored) { + } + } + DefaultFullHttpRequest nRequest = new DefaultFullHttpRequest( + HttpVersion.HTTP_1_1, + HttpMethod.POST, + request.uri(), + data, + new DefaultHttpHeaders(), + new DefaultHttpHeaders(false)); + request.headers().forEach(nRequest.headers()::set); + if (charset == null) { + return new HttpPostRequestDecoder(DATA_FACTORY, nRequest); + } else { + return new HttpPostRequestDecoder(DATA_FACTORY, nRequest, Charset.forName(charset)); + } + } + + public static String readPostValue(InterfaceHttpData item) { + try { + return ((Attribute) item).getValue(); + } catch (IOException e) { + throw new DecodeException("Error while reading post value: " + e.getMessage(), e); + } + } + + public static HttpRequest.FileUpload readUpload(InterfaceHttpData item) { + return new DefaultFileUploadAdaptee((FileUpload) item); + } + + private static class DefaultFileUploadAdaptee implements HttpRequest.FileUpload { + private final FileUpload fu; + private InputStream inputStream; + + public DefaultFileUploadAdaptee(FileUpload fu) { + this.fu = fu; + } + + @Override + public String name() { + return fu.getName(); + } + + @Override + public String filename() { + return fu.getFilename(); + } + + @Override + public String contentType() { + return fu.getContentType(); + } + + @Override + public int size() { + return (int) fu.length(); + } + + @Override + public InputStream inputStream() { + if (inputStream == null) { + inputStream = new ByteBufInputStream(fu.content()); + } + return inputStream; + } + } +} diff --git a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/exception/DecodeException.java b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/exception/DecodeException.java index af426da47d4e..78dfa937347a 100644 --- a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/exception/DecodeException.java +++ b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/exception/DecodeException.java @@ -29,4 +29,8 @@ public DecodeException(String message) { public DecodeException(Throwable cause) { super(500, cause); } + + public DecodeException(String message, Throwable cause) { + super(500, message, cause); + } } diff --git a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/exception/HttpStatusException.java b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/exception/HttpStatusException.java index dd9104aa5fc1..bf86615da11b 100644 --- a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/exception/HttpStatusException.java +++ b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/exception/HttpStatusException.java @@ -34,6 +34,11 @@ public HttpStatusException(int statusCode, Throwable cause) { this.statusCode = statusCode; } + public HttpStatusException(int statusCode, String message, Throwable cause) { + super(message, cause); + this.statusCode = statusCode; + } + public int getStatusCode() { return statusCode; } diff --git a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/h2/Http2ChannelDelegate.java b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/h2/Http2ChannelDelegate.java index 9c5aaea10426..994ceaee9659 100644 --- a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/h2/Http2ChannelDelegate.java +++ b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/h2/Http2ChannelDelegate.java @@ -49,6 +49,11 @@ public SocketAddress remoteAddress() { return h2StreamChannel.remoteAddress(); } + @Override + public SocketAddress localAddress() { + return h2StreamChannel.localAddress(); + } + @Override public void flush() { h2StreamChannel.flush(); diff --git a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/DefaultHttpMessageAdapterFactory.java b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/DefaultHttpMessageAdapterFactory.java new file mode 100644 index 000000000000..ce07adc5caad --- /dev/null +++ b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/DefaultHttpMessageAdapterFactory.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * http://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.apache.dubbo.remoting.http12.message; + +import org.apache.dubbo.common.extension.Activate; +import org.apache.dubbo.remoting.http12.HttpChannel; +import org.apache.dubbo.remoting.http12.HttpMetadata; +import org.apache.dubbo.remoting.http12.HttpResponse; + +@Activate +public final class DefaultHttpMessageAdapterFactory + implements HttpMessageAdapterFactory { + + @Override + public DefaultHttpRequest adapterRequest(HttpMetadata rawRequest, HttpChannel channel) { + return new DefaultHttpRequest(rawRequest, channel); + } + + @Override + public HttpResponse adapterResponse(DefaultHttpRequest request, HttpMetadata rawRequest, Void rawResponse) { + return null; + } +} diff --git a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/DefaultHttpRequest.java b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/DefaultHttpRequest.java new file mode 100644 index 000000000000..27b7b51d2e53 --- /dev/null +++ b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/DefaultHttpRequest.java @@ -0,0 +1,539 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * http://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.apache.dubbo.remoting.http12.message; + +import org.apache.dubbo.common.utils.CollectionUtils; +import org.apache.dubbo.common.utils.StringUtils; +import org.apache.dubbo.remoting.http12.HttpChannel; +import org.apache.dubbo.remoting.http12.HttpCookie; +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.HttpMethods; +import org.apache.dubbo.remoting.http12.HttpRequest; +import org.apache.dubbo.remoting.http12.HttpUtils; +import org.apache.dubbo.remoting.http12.RequestMetadata; +import org.apache.dubbo.remoting.http12.h2.Http2Header; + +import java.io.InputStream; +import java.net.InetSocketAddress; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; + +import io.netty.handler.codec.DateFormatter; +import io.netty.handler.codec.http.QueryStringDecoder; +import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder; +import io.netty.handler.codec.http.multipart.InterfaceHttpData; +import io.netty.handler.codec.http.multipart.InterfaceHttpData.HttpDataType; + +public class DefaultHttpRequest implements HttpRequest { + + private final HttpMetadata metadata; + private final HttpChannel channel; + private final HttpHeaders headers; + + private String method; + private String uri; + private List cookies; + private String contentType; + private List accept; + private List locales; + private QueryStringDecoder decoder; + private HttpPostRequestDecoder postDecoder; + private boolean postParsed; + private Map attributes; + private InputStream inputStream; + + public DefaultHttpRequest(HttpMetadata metadata, HttpChannel channel) { + this.metadata = metadata; + this.channel = channel; + headers = metadata.headers(); + if (metadata instanceof RequestMetadata) { + RequestMetadata requestMetadata = (RequestMetadata) metadata; + method = requestMetadata.method(); + uri = requestMetadata.path(); + } else { + throw new UnsupportedOperationException(); + } + } + + @Override + public boolean IsHttp2() { + return metadata instanceof Http2Header; + } + + @Override + public String method() { + return method; + } + + @Override + public void setMethod(String method) { + this.method = method; + } + + @Override + public String uri() { + return uri; + } + + @Override + public void setUri(String uri) { + this.uri = uri; + decoder = null; + postDecoder = null; + postParsed = false; + } + + @Override + public String path() { + return getDecoder().path(); + } + + @Override + public String query() { + return getDecoder().rawQuery(); + } + + @Override + public String header(String name) { + return headers.getFirst(name); + } + + @Override + public List headerValues(String name) { + return headers.get(name); + } + + @Override + public Date dateHeader(String name) { + String value = headers.getFirst(name); + return StringUtils.isEmpty(value) ? null : DateFormatter.parseHttpDate(value); + } + + @Override + public boolean hasHeader(String name) { + return headers.containsKey(name); + } + + @Override + public Collection headerNames() { + return headers.keySet(); + } + + @Override + public HttpHeaders headers() { + return headers; + } + + @Override + public void setHeader(String name, String value) { + headers.set(name, value); + } + + @Override + public void setHeader(String name, Date value) { + headers.set(name, DateFormatter.format(value)); + } + + @Override + public void setHeader(String name, List value) { + headers.put(name, value); + } + + @Override + public Collection cookies() { + if (cookies == null) { + parseCookies(); + } + return cookies; + } + + @Override + public HttpCookie cookie(String name) { + if (cookies == null) { + parseCookies(); + } + for (int i = 0, len = cookies.size(); i < len; i++) { + HttpCookie cookie = cookies.get(i); + if (cookie.name().equals(name)) { + return cookie; + } + } + return null; + } + + private void parseCookies() { + cookies = HttpUtils.decodeCookies(headers.getFirst("cookie")); + } + + @Override + public String contentType() { + return getContentType0(); + } + + private String getContentType0() { + if (contentType == null) { + String value = headers.getFirst(HttpHeaderNames.CONTENT_TYPE.getName()); + contentType = value == null ? StringUtils.EMPTY_STRING : value.trim(); + } + return contentType.isEmpty() ? null : contentType; + } + + @Override + public void setContentType(String contentType) { + this.contentType = contentType == null ? StringUtils.EMPTY_STRING : contentType.trim(); + headers.set(HttpHeaderNames.CONTENT_TYPE.getName(), getContentType0()); + } + + @Override + public int contentLength() { + String value = headers.getFirst(HttpHeaderNames.CONTENT_LENGTH.getName()); + return value == null ? 0 : Integer.parseInt(value); + } + + @Override + public String mediaType() { + String contentType = getContentType0(); + if (contentType == null) { + return null; + } + int pos = contentType.indexOf(';'); + return pos == -1 ? contentType : contentType.substring(0, pos).trim(); + } + + @Override + public String charset() { + String charset = getCharset0(); + return charset == null ? StandardCharsets.UTF_8.name() : charset; + } + + private String getCharset0() { + String contentType = getContentType0(); + if (contentType == null) { + return null; + } + int pos = contentType.lastIndexOf("charset="); + return pos == -1 ? null : contentType.substring(pos + 8); + } + + @Override + public List accept() { + if (accept == null) { + accept = HttpUtils.parseAccept(headers.getFirst("accept-language")); + } + return accept; + } + + @Override + public Locale locale() { + return locales().get(0); + } + + @Override + public List locales() { + if (locales == null) { + locales = HttpUtils.parseAcceptLanguage(headers.getFirst("accept-language")); + if (locales.isEmpty()) { + locales.add(Locale.getDefault()); + } + } + return locales; + } + + @Override + public String scheme() { + String scheme = headers.getFirst("x-forwarded-proto"); + if (IsHttp2()) { + scheme = headers.getFirst(":scheme"); + } + return scheme == null ? "https" : scheme; + } + + @Override + public String serverHost() { + String host = getHost0(); + return host == null ? localHost() + ':' + localPort() : host; + } + + @Override + public String serverName() { + String host = getHost0(); + if (host != null) { + int pos = host.lastIndexOf(':'); + return pos == -1 ? host : host.substring(0, pos); + } + return localHost(); + } + + @Override + public int serverPort() { + String port = headers.getFirst("x-forwarded-port"); + if (port != null) { + return Integer.parseInt(port); + } + String host = getHost0(); + if (host != null) { + int pos = host.lastIndexOf(':'); + return pos == -1 ? -1 : Integer.parseInt(host.substring(0, pos)); + } + return localPort(); + } + + private String getHost0() { + return headers.getFirst(IsHttp2() ? ":authority" : "host"); + } + + @Override + public String remoteHost() { + return getRemoteAddress().getAddress().getHostName(); + } + + @Override + public String remoteAddr() { + return getRemoteAddress().getAddress().getHostAddress(); + } + + @Override + public int remotePort() { + return getRemoteAddress().getPort(); + } + + private InetSocketAddress getRemoteAddress() { + return (InetSocketAddress) channel.remoteAddress(); + } + + @Override + public String localHost() { + return getLocalAddress().getAddress().getHostAddress(); + } + + @Override + public String localAddr() { + return getLocalAddress().getAddress().getHostAddress(); + } + + @Override + public int localPort() { + return getLocalAddress().getPort(); + } + + private InetSocketAddress getLocalAddress() { + return (InetSocketAddress) channel.localAddress(); + } + + @Override + public String parameter(String name) { + List values = getDecoder().parameters().get(name); + if (CollectionUtils.isNotEmpty(values)) { + return values.get(0); + } + HttpPostRequestDecoder postDecoder = getPostDecoder(); + if (postDecoder == null) { + return null; + } + List items = postDecoder.getBodyHttpDatas(name); + if (items == null) { + return null; + } + for (int i = 0, len = items.size(); i < len; i++) { + InterfaceHttpData item = items.get(i); + if (item.getHttpDataType() == HttpDataType.Attribute) { + return HttpUtils.readPostValue(item); + } + } + return null; + } + + @Override + public String parameter(String name, String defaultValue) { + String value = parameter(name); + return value == null ? defaultValue : value; + } + + @Override + public List parameterValues(String name) { + List values = getDecoder().parameters().get(name); + HttpPostRequestDecoder postDecoder = getPostDecoder(); + if (postDecoder == null) { + return values; + } + List items = postDecoder.getBodyHttpDatas(name); + if (items == null) { + return values; + } + for (int i = 0, len = items.size(); i < len; i++) { + InterfaceHttpData item = items.get(i); + if (item.getHttpDataType() == HttpDataType.Attribute) { + if (values == null) { + values = new ArrayList<>(); + } + values.add(HttpUtils.readPostValue(item)); + } + } + return values; + } + + @Override + public Collection parameterNames() { + Set names = getDecoder().parameters().keySet(); + HttpPostRequestDecoder postDecoder = getPostDecoder(); + if (postDecoder == null) { + return names; + } + List items = postDecoder.getBodyHttpDatas(); + if (items == null) { + return names; + } + Set allNames = null; + for (int i = 0, len = items.size(); i < len; i++) { + InterfaceHttpData item = items.get(i); + if (item.getHttpDataType() == HttpDataType.Attribute) { + if (allNames == null) { + allNames = new LinkedHashSet<>(names); + } + allNames.add(item.getName()); + } + } + return allNames; + } + + @Override + public Collection parts() { + HttpPostRequestDecoder postDecoder = getPostDecoder(); + if (postDecoder == null) { + return Collections.emptyList(); + } + List items = postDecoder.getBodyHttpDatas(); + if (items == null) { + return Collections.emptyList(); + } + List fileUploads = new ArrayList<>(); + for (int i = 0, len = items.size(); i < len; i++) { + InterfaceHttpData item = items.get(i); + if (item.getHttpDataType() == HttpDataType.FileUpload) { + fileUploads.add(HttpUtils.readUpload(item)); + } + } + return fileUploads; + } + + @Override + public FileUpload part(String name) { + HttpPostRequestDecoder postDecoder = getPostDecoder(); + if (postDecoder == null) { + return null; + } + List items = postDecoder.getBodyHttpDatas(name); + if (items == null) { + return null; + } + + for (int i = 0, len = items.size(); i < len; i++) { + InterfaceHttpData item = items.get(i); + if (item.getHttpDataType() == HttpDataType.FileUpload) { + return HttpUtils.readUpload(item); + } + } + return null; + } + + private QueryStringDecoder getDecoder() { + if (decoder == null) { + String charset = getCharset0(); + if (charset == null) { + decoder = new QueryStringDecoder(uri); + } else { + decoder = new QueryStringDecoder(uri, Charset.forName(charset)); + } + } + return decoder; + } + + private HttpPostRequestDecoder getPostDecoder() { + if (inputStream == null) { + return null; + } + if (postDecoder == null) { + if (postParsed) { + return null; + } + if (HttpMethods.isPost(method)) { + postDecoder = HttpUtils.createPostRequestDecoder(this, inputStream, getCharset0()); + } + postParsed = true; + } + return postDecoder; + } + + @Override + public Object attribute(String name) { + return getAttributes().get(name); + } + + @Override + @SuppressWarnings("unchecked") + public T attribute(String name, Class type) { + return (T) getAttributes().get(name); + } + + @Override + public void setAttribute(String name, Object value) { + getAttributes().put(name, value); + } + + @Override + public Collection attributeNames() { + return getAttributes().keySet(); + } + + @Override + public Map attributes() { + return getAttributes(); + } + + private Map getAttributes() { + if (attributes == null) { + attributes = new HashMap<>(); + } + return attributes; + } + + @Override + public InputStream inputStream() { + return inputStream; + } + + @Override + public void setInputStream(InputStream is) { + inputStream = is; + if (HttpMethods.isPost(method)) { + postDecoder = null; + postParsed = false; + } + } +} diff --git a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/DefaultHttpResponse.java b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/DefaultHttpResponse.java new file mode 100644 index 000000000000..5a33624ec56d --- /dev/null +++ b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/DefaultHttpResponse.java @@ -0,0 +1,270 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * http://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.apache.dubbo.remoting.http12.message; + +import org.apache.dubbo.common.utils.StringUtils; +import org.apache.dubbo.remoting.http12.HttpCookie; +import org.apache.dubbo.remoting.http12.HttpHeaderNames; +import org.apache.dubbo.remoting.http12.HttpHeaders; +import org.apache.dubbo.remoting.http12.HttpResponse; +import org.apache.dubbo.remoting.http12.HttpResult; +import org.apache.dubbo.remoting.http12.HttpUtils; + +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; + +import io.netty.handler.codec.DateFormatter; + +public class DefaultHttpResponse implements HttpResponse, HttpResult { + + private final AtomicBoolean committed = new AtomicBoolean(); + private final HttpHeaders headers = new HttpHeaders(); + + private int status; + private String contentType; + private List cookies; + private OutputStream outputStream; + private Object body; + + @Override + public int status() { + return status; + } + + @Override + public void setStatus(int status) { + this.status = status; + } + + @Override + public String header(String name) { + return headers.getFirst(name); + } + + @Override + public List headerValues(String name) { + return headers.get(name); + } + + @Override + public Date dateHeader(String name) { + String value = headers.getFirst(name); + return StringUtils.isEmpty(value) ? null : DateFormatter.parseHttpDate(value); + } + + @Override + public boolean hasHeader(String name) { + return headers.containsKey(name); + } + + @Override + public Collection headerNames() { + return headers.keySet(); + } + + @Override + public HttpHeaders headers() { + return headers; + } + + @Override + public void addHeader(String name, String value) { + headers.computeIfAbsent(name, k -> new ArrayList<>()).add(value); + } + + @Override + public void setHeader(String name, String value) { + headers.set(name, value); + } + + @Override + public void setHeader(String name, Date value) { + headers.set(name, DateFormatter.format(value)); + } + + @Override + public void setHeader(String name, List value) { + headers.put(name, value); + } + + @Override + public void addCookie(HttpCookie cookie) { + if (cookies == null) { + cookies = new ArrayList<>(); + } + cookies.add(cookie); + } + + @Override + public String contentType() { + return getContentType0(); + } + + private String getContentType0() { + if (contentType == null) { + String value = headers.getFirst(HttpHeaderNames.CONTENT_TYPE.getName()); + contentType = value == null ? StringUtils.EMPTY_STRING : value.trim(); + } + return contentType.isEmpty() ? null : contentType; + } + + @Override + public void setContentType(String contentType) { + this.contentType = contentType == null ? StringUtils.EMPTY_STRING : contentType.trim(); + headers.set(HttpHeaderNames.CONTENT_TYPE.getName(), getContentType0()); + } + + @Override + public String mediaType() { + String contentType = getContentType0(); + if (contentType == null) { + return null; + } + int pos = contentType.indexOf(';'); + return pos == -1 ? contentType : contentType.substring(0, pos).trim(); + } + + @Override + public String charset() { + String charset = getCharset0(); + return charset == null ? StandardCharsets.UTF_8.name() : charset; + } + + private String getCharset0() { + String contentType = getContentType0(); + if (contentType == null) { + return null; + } + int pos = contentType.lastIndexOf("charset="); + return pos == -1 ? null : contentType.substring(pos + 8); + } + + @Override + public String locale() { + return headers.getFirst("content-Language"); + } + + @Override + public void setLocale(String locale) { + headers.set("content-Language", locale); + } + + @Override + public OutputStream outputStream() { + if (outputStream == null) { + outputStream = new ByteArrayOutputStream(1024); + } + return null; + } + + @Override + public void setOutputStream(OutputStream os) { + outputStream = os; + } + + @Override + public Object body() { + return body; + } + + @Override + public void setBody(Object body) { + this.body = body; + } + + @Override + public void sendRedirect(String location) { + setStatus(302); + setHeader("location", location); + commit(); + } + + @Override + public void sendError(int status) { + setStatus(status); + commit(); + } + + @Override + public void sendError(int status, String message) { + setStatus(status); + setBody(message); + commit(); + } + + @Override + public boolean isCommitted() { + return committed.get(); + } + + @Override + public void commit() { + if (committed.compareAndSet(false, true)) { + return; + } + + if (cookies != null) { + headers.put("set-cookie", HttpUtils.encodeCookies(cookies)); + } + + throw new IllegalStateException("The response has been committed"); + } + + @Override + public void reset() { + headers.clear(); + status = 0; + contentType = null; + cookies = null; + resetBuffer(); + body = null; + committed.set(false); + } + + @Override + public void resetBuffer() { + if (outputStream != null) { + if (outputStream instanceof ByteArrayOutputStream) { + ((ByteArrayOutputStream) outputStream).reset(); + return; + } + throw new UnsupportedOperationException("the output stream is not supported to reset"); + } + } + + @Override + public int getStatus() { + return status; + } + + @Override + public Map> getHeaders() { + return headers; + } + + @Override + public Object getBody() { + return body == null ? outputStream : body; + } +} diff --git a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/DefaultHttpResult.java b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/DefaultHttpResult.java new file mode 100644 index 000000000000..84f9e3a35d52 --- /dev/null +++ b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/DefaultHttpResult.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * http://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.apache.dubbo.remoting.http12.message; + +import org.apache.dubbo.remoting.http12.HttpResult; + +import java.util.List; +import java.util.Map; + +public class DefaultHttpResult implements HttpResult { + + private int status; + private Map> headers; + private T body; + + @Override + public int getStatus() { + return status; + } + + public void setStatus(int status) { + this.status = status; + } + + @Override + public Map> getHeaders() { + return headers; + } + + public void setHeaders(Map> headers) { + this.headers = headers; + } + + @Override + public T getBody() { + return body; + } + + public void setBody(T body) { + this.body = body; + } +} diff --git a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/HttpMessageAdapterFactory.java b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/HttpMessageAdapterFactory.java new file mode 100644 index 000000000000..735b408aec38 --- /dev/null +++ b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/HttpMessageAdapterFactory.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * http://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.apache.dubbo.remoting.http12.message; + +import org.apache.dubbo.common.extension.ExtensionScope; +import org.apache.dubbo.common.extension.SPI; +import org.apache.dubbo.remoting.http12.HttpChannel; +import org.apache.dubbo.remoting.http12.HttpRequest; +import org.apache.dubbo.remoting.http12.HttpResponse; + +@SPI(scope = ExtensionScope.FRAMEWORK) +public interface HttpMessageAdapterFactory
{ + + HR adapterRequest(REQUEST rawRequest, HttpChannel channel); + + HttpResponse adapterResponse(HR request, REQUEST rawRequest, RESPONSE rawResponse); + + default HttpResponse adapterResponse(HR request, REQUEST rawRequest) { + return adapterResponse(request, rawRequest, null); + } +} diff --git a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/HttpMessageDecoder.java b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/HttpMessageDecoder.java index 94e247a57df8..9e27c15711c4 100644 --- a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/HttpMessageDecoder.java +++ b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/HttpMessageDecoder.java @@ -16,6 +16,7 @@ */ package org.apache.dubbo.remoting.http12.message; +import org.apache.dubbo.common.utils.ArrayUtils; import org.apache.dubbo.remoting.http12.exception.DecodeException; import java.io.InputStream; @@ -26,9 +27,7 @@ public interface HttpMessageDecoder extends CodecMediaType { default Object[] decode(InputStream inputStream, Class[] targetTypes) throws DecodeException { // default decode first target type - return new Object[] { - this.decode(inputStream, targetTypes == null || targetTypes.length == 0 ? null : targetTypes[0]) - }; + return new Object[] {decode(inputStream, ArrayUtils.isEmpty(targetTypes) ? null : targetTypes[0])}; } MediaType mediaType(); diff --git a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/HttpMessageEncoder.java b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/HttpMessageEncoder.java index d4701ce5012f..e8a30f578387 100644 --- a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/HttpMessageEncoder.java +++ b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/HttpMessageEncoder.java @@ -16,6 +16,7 @@ */ package org.apache.dubbo.remoting.http12.message; +import org.apache.dubbo.common.utils.ArrayUtils; import org.apache.dubbo.remoting.http12.exception.EncodeException; import java.io.OutputStream; @@ -26,6 +27,6 @@ public interface HttpMessageEncoder extends CodecMediaType { default void encode(OutputStream outputStream, Object[] data) throws EncodeException { // default encode first data - this.encode(outputStream, data == null || data.length == 0 ? null : data[0]); + encode(outputStream, ArrayUtils.isEmpty(data) ? null : data[0]); } } diff --git a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/codec/CodecUtils.java b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/codec/CodecUtils.java index 601d82c57975..7150f2f17718 100644 --- a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/codec/CodecUtils.java +++ b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/codec/CodecUtils.java @@ -17,132 +17,75 @@ package org.apache.dubbo.remoting.http12.message.codec; import org.apache.dubbo.common.URL; -import org.apache.dubbo.remoting.http12.HttpHeaders; import org.apache.dubbo.remoting.http12.exception.UnsupportedMediaTypeException; -import org.apache.dubbo.remoting.http12.message.HttpMessageCodec; import org.apache.dubbo.remoting.http12.message.HttpMessageDecoder; import org.apache.dubbo.remoting.http12.message.HttpMessageDecoderFactory; import org.apache.dubbo.remoting.http12.message.HttpMessageEncoder; import org.apache.dubbo.remoting.http12.message.HttpMessageEncoderFactory; -import org.apache.dubbo.remoting.http12.message.MediaType; import org.apache.dubbo.rpc.model.FrameworkModel; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; -import io.netty.handler.codec.CodecException; +public final class CodecUtils { -public class CodecUtils { - - private final FrameworkModel frameworkModel; - - private final List decoders; - - private final List encoders; - - private final Map encoderCache; - - private final Map decoderCache; + private final List decoderFactories; + private final List encoderFactories; + private final Map> encoderCache = new ConcurrentHashMap<>(); + private final Map> decoderCache = new ConcurrentHashMap<>(); public CodecUtils(FrameworkModel frameworkModel) { - this.encoderCache = new ConcurrentHashMap<>(1); - this.decoderCache = new ConcurrentHashMap<>(1); - this.frameworkModel = frameworkModel; - this.decoders = frameworkModel + decoderFactories = frameworkModel .getExtensionLoader(HttpMessageDecoderFactory.class) .getActivateExtensions(); - this.encoders = frameworkModel + encoderFactories = frameworkModel .getExtensionLoader(HttpMessageEncoderFactory.class) .getActivateExtensions(); - decoders.forEach( - decoderFactory -> decoderCache.put(decoderFactory.mediaType().getName(), decoderFactory)); - encoders.forEach( - encoderFactory -> encoderCache.put(encoderFactory.mediaType().getName(), encoderFactory)); + decoderFactories.forEach(factory -> decoderCache.put(factory.mediaType().getName(), Optional.of(factory))); + encoderFactories.forEach(factory -> encoderCache.put(factory.mediaType().getName(), Optional.of(factory))); } - public HttpMessageDecoder determineHttpMessageDecoder(FrameworkModel frameworkModel, String contentType, URL url) { - return determineHttpMessageDecoderFactory(contentType).createCodec(url, frameworkModel, contentType); + public HttpMessageDecoder determineHttpMessageDecoder(URL url, FrameworkModel frameworkModel, String mediaType) { + return determineHttpMessageDecoderFactory(mediaType) + .orElseThrow(() -> new UnsupportedMediaTypeException(mediaType)) + .createCodec(url, frameworkModel, mediaType); } - public HttpMessageEncoder determineHttpMessageEncoder(FrameworkModel frameworkModel, HttpHeaders headers, URL url) { - String mediaType = getEncodeMediaType(headers); - return determineHttpMessageEncoderFactory(mediaType).createCodec(url, frameworkModel, mediaType); + public HttpMessageEncoder determineHttpMessageEncoder(URL url, FrameworkModel frameworkModel, String mediaType) { + return determineHttpMessageEncoderFactory(mediaType) + .orElseThrow(() -> new UnsupportedMediaTypeException(mediaType)) + .createCodec(url, frameworkModel, mediaType); } - public HttpMessageDecoderFactory determineHttpMessageDecoderFactory(String mediaType) { - HttpMessageDecoderFactory factory = decoderCache.computeIfAbsent(mediaType, k -> { - for (HttpMessageDecoderFactory decoderFactory : decoders) { + public Optional determineHttpMessageDecoderFactory(String mediaType) { + return decoderCache.computeIfAbsent(mediaType, k -> { + for (HttpMessageDecoderFactory decoderFactory : decoderFactories) { if (decoderFactory.supports(mediaType)) { - return decoderFactory; + return Optional.of(decoderFactory); } } - return new UnsupportedCodecFactory(); + return Optional.empty(); }); - if (factory instanceof UnsupportedCodecFactory) { - throw new UnsupportedMediaTypeException(mediaType); - } - return factory; } - public HttpMessageEncoderFactory determineHttpMessageEncoderFactory(String mediaType) { - HttpMessageEncoderFactory factory = encoderCache.computeIfAbsent(mediaType, k -> { - for (HttpMessageEncoderFactory encoderFactory : encoders) { + public Optional determineHttpMessageEncoderFactory(String mediaType) { + return encoderCache.computeIfAbsent(mediaType, k -> { + for (HttpMessageEncoderFactory encoderFactory : encoderFactories) { if (encoderFactory.supports(mediaType)) { - return encoderFactory; + return Optional.of(encoderFactory); } } - return new UnsupportedCodecFactory(); + return Optional.empty(); }); - if (factory instanceof UnsupportedCodecFactory) { - throw new UnsupportedMediaTypeException(mediaType); - } - return factory; - } - - public List getDecoders() { - return decoders; - } - - public List getEncoders() { - return encoders; - } - - static class UnsupportedCodecFactory implements HttpMessageEncoderFactory, HttpMessageDecoderFactory { - @Override - public MediaType mediaType() { - throw new CodecException(); - } - - @Override - public boolean supports(String mediaType) { - throw new CodecException(); - } - - @Override - public HttpMessageCodec createCodec(URL url, FrameworkModel frameworkModel, String mediaType) { - throw new CodecException(); - } } - public static String getEncodeMediaType(HttpHeaders headers) { - String mediaType = headers.getAccept(); - if (mediaType == null) { - mediaType = headers.getContentType(); - } - return mediaType; + public List getDecoderFactories() { + return decoderFactories; } - public static ByteArrayOutputStream toByteArrayStream(InputStream in) throws IOException { - ByteArrayOutputStream result = new ByteArrayOutputStream(); - byte[] buffer = new byte[1024]; - int length; - while ((length = in.read(buffer)) != -1) { - result.write(buffer, 0, length); - } - return result; + public List getEncoderFactories() { + return encoderFactories; } } diff --git a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/codec/JsonCodec.java b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/codec/JsonCodec.java index da38387df0db..b787bf089bed 100644 --- a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/codec/JsonCodec.java +++ b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/codec/JsonCodec.java @@ -16,6 +16,7 @@ */ package org.apache.dubbo.remoting.http12.message.codec; +import org.apache.dubbo.common.io.StreamUtils; import org.apache.dubbo.common.utils.JsonUtils; import org.apache.dubbo.remoting.http12.exception.DecodeException; import org.apache.dubbo.remoting.http12.exception.EncodeException; @@ -67,13 +68,7 @@ public void encode(OutputStream outputStream, Object[] data) throws EncodeExcept public Object decode(InputStream body, Class targetType) throws DecodeException { try { try { - int len; - byte[] data = new byte[4096]; - StringBuilder builder = new StringBuilder(4096); - while ((len = body.read(data)) != -1) { - builder.append(new String(data, 0, len)); - } - return JsonUtils.toJavaObject(builder.toString(), targetType); + return JsonUtils.toJavaObject(StreamUtils.toString(body), targetType); } finally { body.close(); } @@ -87,14 +82,7 @@ public Object[] decode(InputStream dataInputStream, Class[] targetTypes) thro List result = new ArrayList<>(); try { try { - int len; - byte[] data = new byte[4096]; - StringBuilder builder = new StringBuilder(4096); - while ((len = dataInputStream.read(data)) != -1) { - builder.append(new String(data, 0, len)); - } - String jsonString = builder.toString(); - List jsonObjects = JsonUtils.toJavaList(jsonString, Object.class); + List jsonObjects = JsonUtils.toJavaList(StreamUtils.toString(dataInputStream), Object.class); for (int i = 0; i < targetTypes.length; i++) { Object jsonObject = jsonObjects.get(i); diff --git a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/codec/JsonPbCodec.java b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/codec/JsonPbCodec.java index caac2c3fb462..49b8788c4e82 100644 --- a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/codec/JsonPbCodec.java +++ b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/codec/JsonPbCodec.java @@ -16,10 +16,10 @@ */ package org.apache.dubbo.remoting.http12.message.codec; +import org.apache.dubbo.common.io.StreamUtils; import org.apache.dubbo.common.utils.MethodUtils; import org.apache.dubbo.remoting.http12.exception.DecodeException; import org.apache.dubbo.remoting.http12.exception.EncodeException; -import org.apache.dubbo.remoting.http12.message.MediaType; import java.io.IOException; import java.io.InputStream; @@ -31,11 +31,6 @@ public class JsonPbCodec extends JsonCodec { - @Override - public MediaType mediaType() { - return MediaType.APPLICATION_JSON_VALUE; - } - @Override public void encode(OutputStream outputStream, Object unSerializedBody) throws EncodeException { try { @@ -59,15 +54,9 @@ public void encode(OutputStream outputStream, Object[] data) throws EncodeExcept public Object decode(InputStream body, Class targetType) throws DecodeException { try { if (isProtobuf(targetType)) { - int len; - byte[] data = new byte[4096]; - StringBuilder builder = new StringBuilder(4096); - while ((len = body.read(data)) != -1) { - builder.append(new String(data, 0, len)); - } Message.Builder newBuilder = (Message.Builder) MethodUtils.findMethod(targetType, "newBuilder").invoke(null); - JsonFormat.parser().ignoringUnknownFields().merge(builder.toString(), newBuilder); + JsonFormat.parser().ignoringUnknownFields().merge(StreamUtils.toString(body), newBuilder); return newBuilder.build(); } } catch (Throwable e) { diff --git a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/codec/MultipartDecoder.java b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/codec/MultipartDecoder.java index 560e5db1a786..4242bcd293fb 100644 --- a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/codec/MultipartDecoder.java +++ b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/codec/MultipartDecoder.java @@ -77,7 +77,7 @@ public Object[] decode(InputStream inputStream, Class[] targetTypes) throws D continue; } res[i] = codecUtils - .determineHttpMessageDecoder(frameworkModel, part.headers.getContentType(), url) + .determineHttpMessageDecoder(url, frameworkModel, part.headers.getContentType()) .decode(new ByteArrayInputStream(part.content), targetTypes[i]); } return res; diff --git a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/codec/PlainTextCodec.java b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/codec/PlainTextCodec.java index 3516c749dc11..e07df957da8c 100644 --- a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/codec/PlainTextCodec.java +++ b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/codec/PlainTextCodec.java @@ -16,6 +16,7 @@ */ package org.apache.dubbo.remoting.http12.message.codec; +import org.apache.dubbo.common.io.StreamUtils; import org.apache.dubbo.remoting.http12.exception.DecodeException; import org.apache.dubbo.remoting.http12.exception.EncodeException; import org.apache.dubbo.remoting.http12.message.HttpMessageCodec; @@ -37,14 +38,14 @@ public PlainTextCodec(String contentType) { @Override public void encode(OutputStream outputStream, Object data) throws EncodeException { - if (!(data instanceof String)) { - throw new EncodeException("PlainText media-type only supports String as return type."); - } try { - outputStream.write(((String) data).getBytes()); + if (data instanceof String) { + outputStream.write(((String) data).getBytes(StandardCharsets.UTF_8)); + } } catch (IOException e) { throw new EncodeException(e); } + throw new EncodeException("PlainText media-type only supports String as return type."); } @Override @@ -58,19 +59,16 @@ public Object decode(InputStream inputStream, Class targetType) throws Decode if (!String.class.equals(targetType)) { throw new DecodeException("Plain text content only supports String as method param."); } - Charset charset; - if (contentType.contains("charset=")) { + int pos = contentType.indexOf("charset="); + if (pos > 0) { try { - charset = Charset.forName(contentType.substring(contentType.indexOf("charset=") + 8)); + Charset charset = Charset.forName(contentType.substring(pos + 8)); + return StreamUtils.toString(inputStream, charset); } catch (Exception e) { throw new DecodeException("Unsupported charset:" + e.getMessage()); } - if (!charset.equals(StandardCharsets.UTF_8) && !charset.equals(StandardCharsets.US_ASCII)) { - String origin = CodecUtils.toByteArrayStream(inputStream).toString(charset.name()); - return new String(origin.getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8); - } } - return CodecUtils.toByteArrayStream(inputStream).toString(StandardCharsets.UTF_8.name()); + return StreamUtils.toString(inputStream); } catch (Exception e) { throw new DecodeException(e); } diff --git a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/codec/UrlEncodeFormCodec.java b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/codec/UrlEncodeFormCodec.java index 2e16b6d38282..cf390331a271 100644 --- a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/codec/UrlEncodeFormCodec.java +++ b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/codec/UrlEncodeFormCodec.java @@ -17,6 +17,7 @@ package org.apache.dubbo.remoting.http12.message.codec; import org.apache.dubbo.common.convert.ConverterUtil; +import org.apache.dubbo.common.io.StreamUtils; import org.apache.dubbo.remoting.http12.exception.DecodeException; import org.apache.dubbo.remoting.http12.exception.EncodeException; import org.apache.dubbo.remoting.http12.message.HttpMessageCodec; @@ -25,6 +26,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.net.URLDecoder; +import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.HashMap; @@ -48,11 +50,14 @@ public void encode(OutputStream outputStream, Object data) throws EncodeExceptio for (Map.Entry e : ((Map) data).entrySet()) { String k = e.getKey().toString(); String v = e.getValue().toString(); - toWrite.append(k).append("=").append(v).append("&"); + toWrite.append(k) + .append("=") + .append(URLEncoder.encode(v, StandardCharsets.UTF_8.name())) + .append("&"); } if (toWrite.length() > 1) { outputStream.write( - toWrite.substring(0, toWrite.length() - 1).getBytes()); + toWrite.substring(0, toWrite.length() - 1).getBytes(StandardCharsets.UTF_8)); } } else { throw new EncodeException("UrlEncodeFrom media-type only supports String or Map as return type."); @@ -84,8 +89,7 @@ else if (Arrays.stream(targetTypes) throw new DecodeException( "For x-www-form-urlencoded MIME type, please use Map/String/base-types as method param."); } - String decoded = URLDecoder.decode( - CodecUtils.toByteArrayStream(inputStream).toString(), StandardCharsets.UTF_8.name()) + String decoded = URLDecoder.decode(StreamUtils.toString(inputStream), StandardCharsets.UTF_8.name()) .trim(); Map res = toMap(decoded, targetTypes, toMap); if (toMap) { diff --git a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/codec/XmlCodec.java b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/codec/XmlCodec.java index 9d2bd8f02045..96ddfefc486e 100644 --- a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/codec/XmlCodec.java +++ b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/codec/XmlCodec.java @@ -30,7 +30,6 @@ import java.io.InputStream; import java.io.OutputStream; -import java.io.StringReader; import org.xml.sax.InputSource; @@ -55,10 +54,7 @@ public Object decode(InputStream inputStream, Class targetType) throws Decode spf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); // Do unmarshall operation - Source xmlSource = new SAXSource( - spf.newSAXParser().getXMLReader(), - new InputSource(new StringReader( - CodecUtils.toByteArrayStream(inputStream).toString()))); + Source xmlSource = new SAXSource(spf.newSAXParser().getXMLReader(), new InputSource(inputStream)); JAXBContext context = JAXBContext.newInstance(targetType); Unmarshaller unmarshaller = context.createUnmarshaller(); return unmarshaller.unmarshal(xmlSource); diff --git a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/netty4/h1/NettyHttp1Channel.java b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/netty4/h1/NettyHttp1Channel.java index c71cd1cee994..443bd21aec06 100644 --- a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/netty4/h1/NettyHttp1Channel.java +++ b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/netty4/h1/NettyHttp1Channel.java @@ -60,6 +60,11 @@ public SocketAddress remoteAddress() { return channel.remoteAddress(); } + @Override + public SocketAddress localAddress() { + return channel.localAddress(); + } + @Override public void flush() {} } diff --git a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/netty4/h1/NettyHttp1ConnectionHandler.java b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/netty4/h1/NettyHttp1ConnectionHandler.java index c43fa7cab600..20681d77e309 100644 --- a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/netty4/h1/NettyHttp1ConnectionHandler.java +++ b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/netty4/h1/NettyHttp1ConnectionHandler.java @@ -18,16 +18,9 @@ import org.apache.dubbo.common.URL; import org.apache.dubbo.common.threadpool.ThreadPool; -import org.apache.dubbo.common.utils.Assert; -import org.apache.dubbo.common.utils.StringUtils; -import org.apache.dubbo.remoting.http12.HttpHeaderNames; -import org.apache.dubbo.remoting.http12.HttpHeaders; -import org.apache.dubbo.remoting.http12.exception.UnsupportedMediaTypeException; import org.apache.dubbo.remoting.http12.h1.Http1Request; -import org.apache.dubbo.remoting.http12.h1.Http1ServerChannelObserver; import org.apache.dubbo.remoting.http12.h1.Http1ServerTransportListener; import org.apache.dubbo.remoting.http12.h1.Http1ServerTransportListenerFactory; -import org.apache.dubbo.remoting.http12.message.codec.CodecUtils; import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.concurrent.Executor; @@ -37,83 +30,36 @@ public class NettyHttp1ConnectionHandler extends SimpleChannelInboundHandler { - private Http1ServerTransportListenerFactory http1ServerTransportListenerFactory; + private final URL url; private final FrameworkModel frameworkModel; - private final URL url; + private final Http1ServerTransportListenerFactory http1ServerTransportListenerFactory; private final Executor executor; - private final CodecUtils codecUtils; - - private Http1ServerChannelObserver errorResponseObserver; - - public NettyHttp1ConnectionHandler(URL url, FrameworkModel frameworkModel) { - this.url = url; - this.frameworkModel = frameworkModel; - this.executor = url.getOrDefaultFrameworkModel() - .getExtensionLoader(ThreadPool.class) - .getAdaptiveExtension() - .getExecutor(url); - this.codecUtils = frameworkModel.getBeanFactory().getBean(CodecUtils.class); - } - public NettyHttp1ConnectionHandler( URL url, FrameworkModel frameworkModel, Http1ServerTransportListenerFactory http1ServerTransportListenerFactory) { this.url = url; this.frameworkModel = frameworkModel; - this.executor = url.getOrDefaultFrameworkModel() + this.http1ServerTransportListenerFactory = http1ServerTransportListenerFactory; + executor = url.getOrDefaultFrameworkModel() .getExtensionLoader(ThreadPool.class) .getAdaptiveExtension() .getExecutor(url); - this.codecUtils = frameworkModel.getBeanFactory().getBean(CodecUtils.class); - this.http1ServerTransportListenerFactory = http1ServerTransportListenerFactory; - } - - public void setHttp1ServerTransportListenerFactory( - Http1ServerTransportListenerFactory http1ServerTransportListenerFactory) { - this.http1ServerTransportListenerFactory = http1ServerTransportListenerFactory; } + /** + * process h1 request + */ protected void channelRead0(ChannelHandlerContext ctx, Http1Request http1Request) { - // process h1 request - Http1ServerTransportListener http1TransportListener = initTransportListenerIfNecessary(ctx, http1Request); - initErrorResponseObserver(ctx, http1Request); - executor.execute(() -> { - try { - http1TransportListener.onMetadata(http1Request); - http1TransportListener.onData(http1Request); - } catch (Exception e) { - errorResponseObserver.onError(e); - } - }); - } - - private Http1ServerTransportListener initTransportListenerIfNecessary( - ChannelHandlerContext ctx, Http1Request http1Request) { - // each h1 request create http1TransportListener instance - Http1ServerTransportListenerFactory http1ServerTransportListenerFactory = - this.http1ServerTransportListenerFactory; - Assert.notNull(http1ServerTransportListenerFactory, "http1ServerTransportListenerFactory must be not null."); Http1ServerTransportListener http1TransportListener = http1ServerTransportListenerFactory.newInstance( new NettyHttp1Channel(ctx.channel()), url, frameworkModel); - - HttpHeaders headers = http1Request.headers(); - String contentType = headers.getFirst(HttpHeaderNames.CONTENT_TYPE.getName()); - if (!StringUtils.hasText(contentType)) { - throw new UnsupportedMediaTypeException(contentType); - } - // check ContentType - codecUtils.determineHttpMessageDecoder(frameworkModel, headers.getContentType(), url); - return http1TransportListener; - } - - private void initErrorResponseObserver(ChannelHandlerContext ctx, Http1Request request) { - this.errorResponseObserver = new Http1ServerChannelObserver(new NettyHttp1Channel(ctx.channel())); - this.errorResponseObserver.setResponseEncoder( - codecUtils.determineHttpMessageEncoder(frameworkModel, request.headers(), url)); + executor.execute(() -> { + http1TransportListener.onMetadata(http1Request); + http1TransportListener.onData(http1Request); + }); } } diff --git a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/netty4/h2/NettyH2StreamChannel.java b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/netty4/h2/NettyH2StreamChannel.java index 05941658cfa8..f85b12763435 100644 --- a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/netty4/h2/NettyH2StreamChannel.java +++ b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/netty4/h2/NettyH2StreamChannel.java @@ -66,6 +66,11 @@ public SocketAddress remoteAddress() { return this.http2StreamChannel.remoteAddress(); } + @Override + public SocketAddress localAddress() { + return this.http2StreamChannel.localAddress(); + } + @Override public void flush() { this.http2StreamChannel.flush(); diff --git a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/netty4/h2/NettyHttp2ProtocolSelectorHandler.java b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/netty4/h2/NettyHttp2ProtocolSelectorHandler.java index f102a8732444..cc82019a7e4b 100644 --- a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/netty4/h2/NettyHttp2ProtocolSelectorHandler.java +++ b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/netty4/h2/NettyHttp2ProtocolSelectorHandler.java @@ -17,7 +17,6 @@ package org.apache.dubbo.remoting.http12.netty4.h2; import org.apache.dubbo.common.URL; -import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.remoting.http12.HttpHeaderNames; import org.apache.dubbo.remoting.http12.HttpHeaders; import org.apache.dubbo.remoting.http12.HttpMetadata; @@ -38,16 +37,11 @@ public class NettyHttp2ProtocolSelectorHandler extends SimpleChannelInboundHandler { - private Http2ServerTransportListenerFactory defaultHttp2ServerTransportListenerFactory; - private final URL url; private final FrameworkModel frameworkModel; - public NettyHttp2ProtocolSelectorHandler(URL url, FrameworkModel frameworkModel) { - this.url = url; - this.frameworkModel = frameworkModel; - } + private final Http2ServerTransportListenerFactory defaultHttp2ServerTransportListenerFactory; public NettyHttp2ProtocolSelectorHandler( URL url, @@ -62,11 +56,7 @@ public NettyHttp2ProtocolSelectorHandler( protected void channelRead0(ChannelHandlerContext ctx, HttpMetadata metadata) { HttpHeaders headers = metadata.headers(); String contentType = headers.getFirst(HttpHeaderNames.CONTENT_TYPE.getName()); - // 415 - if (!StringUtils.hasText(contentType)) { - throw new UnsupportedMediaTypeException(contentType); - } - Http2ServerTransportListenerFactory factory = adaptHttp2ServerTransportListenerFactory(contentType); + Http2ServerTransportListenerFactory factory = determineHttp2ServerTransportListenerFactory(contentType); if (factory == null) { throw new UnsupportedMediaTypeException(contentType); } @@ -84,7 +74,7 @@ protected void channelRead0(ChannelHandlerContext ctx, HttpMetadata metadata) { ctx.fireChannelRead(metadata); } - private Http2ServerTransportListenerFactory adaptHttp2ServerTransportListenerFactory(String contentType) { + private Http2ServerTransportListenerFactory determineHttp2ServerTransportListenerFactory(String contentType) { Set http2ServerTransportListenerFactories = frameworkModel .getExtensionLoader(Http2ServerTransportListenerFactory.class) .getSupportedExtensionInstances(); diff --git a/dubbo-remoting/dubbo-remoting-http12/src/test/java/org/apache/dubbo/remoting/http12/message/codec/CodeUtilsTest.java b/dubbo-remoting/dubbo-remoting-http12/src/test/java/org/apache/dubbo/remoting/http12/message/codec/CodeUtilsTest.java index b8436c1c171f..7057f5f9cee5 100644 --- a/dubbo-remoting/dubbo-remoting-http12/src/test/java/org/apache/dubbo/remoting/http12/message/codec/CodeUtilsTest.java +++ b/dubbo-remoting/dubbo-remoting-http12/src/test/java/org/apache/dubbo/remoting/http12/message/codec/CodeUtilsTest.java @@ -39,13 +39,14 @@ void testDetermineHttpCodec() { HttpHeaderNames.CONTENT_TYPE.getName(), Collections.singletonList(MediaType.APPLICATION_JSON_VALUE.getName())); HttpMessageDecoder decoder = - codecUtils.determineHttpMessageDecoder(FrameworkModel.defaultModel(), headers.getContentType(), null); + codecUtils.determineHttpMessageDecoder(null, FrameworkModel.defaultModel(), headers.getContentType()); Assertions.assertNotNull(decoder); Assertions.assertEquals(JsonPbCodec.class, decoder.getClass()); HttpMessageEncoder encoder; // If no Accept header provided, use Content-Type to find encoder - encoder = codecUtils.determineHttpMessageEncoder(FrameworkModel.defaultModel(), headers, null); + encoder = codecUtils.determineHttpMessageEncoder( + null, FrameworkModel.defaultModel(), MediaType.APPLICATION_JSON_VALUE.getName()); Assertions.assertNotNull(encoder); Assertions.assertEquals(JsonPbCodec.class, encoder.getClass()); @@ -54,17 +55,19 @@ void testDetermineHttpCodec() { HttpHeaderNames.CONTENT_TYPE.getName(), Collections.singletonList(MediaType.MULTIPART_FORM_DATA.getName())); decoder = - codecUtils.determineHttpMessageDecoder(FrameworkModel.defaultModel(), headers1.getContentType(), null); + codecUtils.determineHttpMessageDecoder(null, FrameworkModel.defaultModel(), headers1.getContentType()); Assertions.assertNotNull(decoder); Assertions.assertEquals(MultipartDecoder.class, decoder.getClass()); Assertions.assertThrows( UnsupportedMediaTypeException.class, - () -> codecUtils.determineHttpMessageEncoder(FrameworkModel.defaultModel(), headers1, null)); + () -> codecUtils.determineHttpMessageEncoder( + null, FrameworkModel.defaultModel(), MediaType.APPLICATION_JSON_VALUE.getName())); headers1.put( HttpHeaderNames.ACCEPT.getName(), Collections.singletonList(MediaType.APPLICATION_JSON_VALUE.getName())); - encoder = codecUtils.determineHttpMessageEncoder(FrameworkModel.defaultModel(), headers1, null); + encoder = codecUtils.determineHttpMessageEncoder( + null, FrameworkModel.defaultModel(), MediaType.APPLICATION_JSON_VALUE.getName()); Assertions.assertNotNull(encoder); Assertions.assertEquals(JsonPbCodec.class, encoder.getClass()); } diff --git a/dubbo-rpc/dubbo-rpc-triple/pom.xml b/dubbo-rpc/dubbo-rpc-triple/pom.xml index bbaa7edd6849..cc9cdd04ff42 100644 --- a/dubbo-rpc/dubbo-rpc-triple/pom.xml +++ b/dubbo-rpc/dubbo-rpc-triple/pom.xml @@ -55,6 +55,28 @@ com.google.protobuf protobuf-java + + org.springframework + spring-webmvc + 3.2.18.RELEASE + true + + + org.springframework + spring-web + 3.2.18.RELEASE + true + + + javax.servlet + javax.servlet-api + true + + + org.jboss.resteasy + resteasy-jaxrs + provided + org.springframework spring-test diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/DescriptorUtils.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/DescriptorUtils.java new file mode 100644 index 000000000000..64f25fff4c5d --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/DescriptorUtils.java @@ -0,0 +1,155 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * http://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.apache.dubbo.rpc.protocol.tri; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.constants.CommonConstants; +import org.apache.dubbo.common.utils.CollectionUtils; +import org.apache.dubbo.remoting.http12.exception.UnimplementedException; +import org.apache.dubbo.rpc.Invoker; +import org.apache.dubbo.rpc.model.MethodDescriptor; +import org.apache.dubbo.rpc.model.ProviderModel; +import org.apache.dubbo.rpc.model.ServiceDescriptor; +import org.apache.dubbo.rpc.protocol.tri.TripleCustomerProtocolWapper.TripleRequestWrapper; +import org.apache.dubbo.rpc.service.ServiceDescriptorInternalCache; +import org.apache.dubbo.rpc.stub.StubSuppliers; + +import java.util.Arrays; +import java.util.List; + +/** + * The MetaUtils provides utility methods for working with service descriptors and method descriptors. + */ +public final class DescriptorUtils { + + private DescriptorUtils() {} + + public static ServiceDescriptor findServiceDescriptor(Invoker invoker, String serviceName, boolean hasStub) + throws UnimplementedException { + ServiceDescriptor result; + if (hasStub) { + result = getStubServiceDescriptor(invoker.getUrl(), serviceName); + } else { + result = getReflectionServiceDescriptor(invoker.getUrl()); + } + if (result == null) { + throw new UnimplementedException("service:" + serviceName); + } + return result; + } + + public static ServiceDescriptor getStubServiceDescriptor(URL url, String serviceName) { + ServiceDescriptor serviceDescriptor; + if (url.getServiceModel() != null) { + serviceDescriptor = url.getServiceModel().getServiceModel(); + } else { + serviceDescriptor = StubSuppliers.getServiceDescriptor(serviceName); + } + return serviceDescriptor; + } + + public static ServiceDescriptor getReflectionServiceDescriptor(URL url) { + ProviderModel providerModel = (ProviderModel) url.getServiceModel(); + if (providerModel == null || providerModel.getServiceModel() == null) { + return null; + } + return providerModel.getServiceModel(); + } + + public static MethodDescriptor findMethodDescriptor( + ServiceDescriptor serviceDescriptor, String originalMethodName, boolean hasStub) + throws UnimplementedException { + MethodDescriptor result; + if (hasStub) { + result = serviceDescriptor.getMethods(originalMethodName).get(0); + } else { + result = findReflectionMethodDescriptor(serviceDescriptor, originalMethodName); + } + return result; + } + + public static MethodDescriptor findReflectionMethodDescriptor( + ServiceDescriptor serviceDescriptor, String methodName) { + MethodDescriptor methodDescriptor = null; + if (isGeneric(methodName)) { + // There should be one and only one + methodDescriptor = ServiceDescriptorInternalCache.genericService() + .getMethods(methodName) + .get(0); + } else if (isEcho(methodName)) { + // There should be one and only one + return ServiceDescriptorInternalCache.echoService() + .getMethods(methodName) + .get(0); + } else { + List methodDescriptors = serviceDescriptor.getMethods(methodName); + // try lower-case method + if (CollectionUtils.isEmpty(methodDescriptors)) { + String lowerMethod = Character.toLowerCase(methodName.charAt(0)) + methodName.substring(1); + methodDescriptors = serviceDescriptor.getMethods(lowerMethod); + } + if (CollectionUtils.isEmpty(methodDescriptors)) { + return null; + } + // In most cases there is only one method + if (methodDescriptors.size() == 1) { + methodDescriptor = methodDescriptors.get(0); + } + // generated unary method ,use unary type + // Response foo(Request) + // void foo(Request,StreamObserver) + if (methodDescriptors.size() == 2) { + if (methodDescriptors.get(1).getRpcType() == MethodDescriptor.RpcType.SERVER_STREAM) { + methodDescriptor = methodDescriptors.get(0); + } else if (methodDescriptors.get(0).getRpcType() == MethodDescriptor.RpcType.SERVER_STREAM) { + methodDescriptor = methodDescriptors.get(1); + } + } + } + return methodDescriptor; + } + + public static MethodDescriptor findTripleMethodDescriptor( + ServiceDescriptor serviceDescriptor, String methodName, byte[] data) { + MethodDescriptor methodDescriptor = findReflectionMethodDescriptor(serviceDescriptor, methodName); + if (methodDescriptor == null) { + List methodDescriptors = serviceDescriptor.getMethods(methodName); + TripleRequestWrapper request = TripleRequestWrapper.parseFrom(data); + String[] paramTypes = request.getArgTypes().toArray(new String[0]); + // wrapper mode the method can overload so maybe list + for (MethodDescriptor descriptor : methodDescriptors) { + // params type is array + if (Arrays.equals(descriptor.getCompatibleParamSignatures(), paramTypes)) { + methodDescriptor = descriptor; + break; + } + } + if (methodDescriptor == null) { + throw new UnimplementedException("method:" + methodName); + } + } + return methodDescriptor; + } + + private static boolean isGeneric(String methodName) { + return CommonConstants.$INVOKE.equals(methodName) || CommonConstants.$INVOKE_ASYNC.equals(methodName); + } + + private static boolean isEcho(String methodName) { + return CommonConstants.$ECHO.equals(methodName); + } +} diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/RequestMetadata.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/RequestMetadata.java index 90477e7336ee..a015603d0635 100644 --- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/RequestMetadata.java +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/RequestMetadata.java @@ -55,7 +55,7 @@ public DefaultHttp2Headers toHeaders() { .authority(address) .method(HttpMethod.POST.asciiName()) .path("/" + service + "/" + method.getMethodName()) - .set(TripleHeaderEnum.CONTENT_TYPE_KEY.getHeader(), TripleConstant.CONTENT_PROTO) + .set(TripleHeaderEnum.CONTENT_TYPE_KEY.getHeader(), TripleConstant.CONTENT_TYPE_PROTO) .set(HttpHeaderNames.TE, HttpHeaderValues.TRAILERS); setIfNotNull(header, TripleHeaderEnum.TIMEOUT.getHeader(), timeout); if (!ignoreDefaultVersion || !"1.0.0".equals(version)) { @@ -67,7 +67,7 @@ public DefaultHttp2Headers toHeaders() { if (!Identity.MESSAGE_ENCODING.equals(compressor.getMessageEncoding())) { setIfNotNull(header, TripleHeaderEnum.GRPC_ENCODING.getHeader(), compressor.getMessageEncoding()); } - StreamUtils.convertAttachment(header, attachments, convertNoLowerHeader); + StreamUtils.putHeaders(header, attachments, convertNoLowerHeader); return header; } diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleConstant.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleConstant.java index 6ca934255fd7..56331e7f38e6 100644 --- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleConstant.java +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleConstant.java @@ -16,14 +16,16 @@ */ package org.apache.dubbo.rpc.protocol.tri; +import org.apache.dubbo.remoting.http12.message.MediaType; + import io.netty.util.AsciiString; public class TripleConstant { - public static final String CONTENT_PROTO = "application/grpc+proto"; - public static final String APPLICATION_GRPC = "application/grpc"; - public static final String TEXT_PLAIN_UTF8 = "text/plain; encoding=utf-8"; - public static final String TRI_VERSION = "3.0-TRI"; + public static final String CONTENT_TYPE_PROTO = "application/grpc+proto"; + public static final MediaType MEDIA_TYPE_GRPC = new MediaType("application", "grpc"); + + public static final String DEFAULT_VERSION = "1.0.0"; public static final String SERIALIZATION_KEY = "serialization"; public static final String TE_KEY = "te"; @@ -35,4 +37,13 @@ public class TripleConstant { public static final AsciiString HTTPS_SCHEME = AsciiString.of("https"); public static final AsciiString HTTP_SCHEME = AsciiString.of("http"); + + public static final String REMOTE_ADDRESS_KEY = "tri.remote.address"; + public static final String HANDLER_TYPE_KEY = "tri.handler.type"; + public static final String HTTP_REQUEST_KEY = "tri.http.request"; + public static final String HTTP_RESPONSE_KEY = "tri.http.response"; + + public static final String TRIPLE_HANDLER_TYPE_REST = "rest"; + public static final String TRIPLE_HANDLER_TYPE_HTTP = "http"; + public static final String TRIPLE_HANDLER_TYPE_GRPC = "grpc"; } diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleHeaderEnum.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleHeaderEnum.java index db1018e2d575..66f942e8cdd9 100644 --- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleHeaderEnum.java +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleHeaderEnum.java @@ -41,6 +41,7 @@ public enum TripleHeaderEnum { CONSUMER_APP_NAME_KEY("tri-consumer-appname"), SERVICE_VERSION("tri-service-version"), SERVICE_GROUP("tri-service-group"), + SERVICE_TIMEOUT("tri-service-timeout"), TRI_HEADER_CONVERT("tri-header-convert"), diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleHttp2Protocol.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleHttp2Protocol.java index 1a82e343a3c7..ea088c9af195 100644 --- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleHttp2Protocol.java +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleHttp2Protocol.java @@ -20,7 +20,6 @@ import org.apache.dubbo.common.config.Configuration; import org.apache.dubbo.common.config.ConfigurationUtils; import org.apache.dubbo.common.extension.Activate; -import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.remoting.ChannelHandler; import org.apache.dubbo.remoting.api.AbstractWireProtocol; import org.apache.dubbo.remoting.api.pu.ChannelHandlerPretender; @@ -32,7 +31,6 @@ import org.apache.dubbo.remoting.http12.netty4.h2.NettyHttp2FrameCodec; import org.apache.dubbo.remoting.http12.netty4.h2.NettyHttp2ProtocolSelectorHandler; import org.apache.dubbo.remoting.utils.UrlUtils; -import org.apache.dubbo.rpc.HeaderFilter; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.ScopeModelAware; import org.apache.dubbo.rpc.protocol.tri.h12.TripleProtocolDetector; @@ -43,7 +41,6 @@ import org.apache.dubbo.rpc.protocol.tri.transport.TripleTailHandler; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import io.netty.channel.ChannelDuplexHandler; @@ -60,7 +57,6 @@ import io.netty.handler.flush.FlushConsolidationHandler; import io.netty.handler.logging.LogLevel; -import static org.apache.dubbo.common.constants.CommonConstants.HEADER_FILTER_KEY; import static org.apache.dubbo.rpc.Constants.H2_SETTINGS_ENABLE_PUSH_KEY; import static org.apache.dubbo.rpc.Constants.H2_SETTINGS_HEADER_TABLE_SIZE_KEY; import static org.apache.dubbo.rpc.Constants.H2_SETTINGS_INITIAL_WINDOW_SIZE_KEY; @@ -84,7 +80,6 @@ public class TripleHttp2Protocol extends AbstractWireProtocol implements ScopeMo public static final Http2FrameLogger SERVER_LOGGER = new Http2FrameLogger(LogLevel.DEBUG, "H2_SERVER"); - private ExtensionLoader filtersLoader; private FrameworkModel frameworkModel; public TripleHttp2Protocol() { @@ -94,7 +89,6 @@ public TripleHttp2Protocol() { @Override public void setFrameworkModel(FrameworkModel frameworkModel) { this.frameworkModel = frameworkModel; - this.filtersLoader = frameworkModel.getExtensionLoader(HeaderFilter.class); } @Override @@ -158,12 +152,6 @@ private void configurerHttp1Handlers(URL url, List handlers) { private void configurerHttp2Handlers(URL url, List handlers) { Configuration config = ConfigurationUtils.getGlobalConfiguration(url.getOrDefaultApplicationModel()); - final List headFilters; - if (filtersLoader != null) { - headFilters = filtersLoader.getActivateExtension(url, HEADER_FILTER_KEY); - } else { - headFilters = Collections.emptyList(); - } final Http2FrameCodec codec = TripleHttp2FrameCodecBuilder.forServer() .customizeConnection((connection) -> connection .remote() diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleInvoker.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleInvoker.java index 4754d2bf4160..3e0add6c5ff8 100644 --- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleInvoker.java +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleInvoker.java @@ -289,7 +289,7 @@ RequestMetadata createRequest(MethodDescriptor methodDescriptor, Invocation invo meta.packableMethod = (PackableMethod) methodDescriptor; } else { meta.packableMethod = packableMethodCache.computeIfAbsent( - methodDescriptor, (md) -> packableMethodFactory.create(md, url, TripleConstant.CONTENT_PROTO)); + methodDescriptor, (md) -> packableMethodFactory.create(md, url, TripleConstant.CONTENT_TYPE_PROTO)); } meta.convertNoLowerHeader = TripleProtocol.CONVERT_NO_LOWER_HEADER; meta.ignoreDefaultVersion = TripleProtocol.IGNORE_1_0_0_VERSION; diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleProtocol.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleProtocol.java index f5d0223467d8..0aa566729aa7 100644 --- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleProtocol.java +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleProtocol.java @@ -34,6 +34,7 @@ import org.apache.dubbo.rpc.protocol.AbstractExporter; import org.apache.dubbo.rpc.protocol.AbstractProtocol; import org.apache.dubbo.rpc.protocol.tri.compressor.DeCompressor; +import org.apache.dubbo.rpc.protocol.tri.rest.mapping.RequestMappingRegistry; import org.apache.dubbo.rpc.protocol.tri.service.TriBuiltinService; import java.util.Objects; @@ -55,7 +56,9 @@ public class TripleProtocol extends AbstractProtocol { private static final Logger logger = LoggerFactory.getLogger(TripleProtocol.class); + private final PathResolver pathResolver; + private final RequestMappingRegistry requestMappingRegistry; private final TriBuiltinService triBuiltinService; private final String acceptEncodings; @@ -70,9 +73,9 @@ public class TripleProtocol extends AbstractProtocol { public TripleProtocol(FrameworkModel frameworkModel) { this.frameworkModel = frameworkModel; - this.triBuiltinService = new TriBuiltinService(frameworkModel); - this.pathResolver = - frameworkModel.getExtensionLoader(PathResolver.class).getDefaultExtension(); + triBuiltinService = new TriBuiltinService(frameworkModel); + pathResolver = frameworkModel.getDefaultExtension(PathResolver.class); + requestMappingRegistry = frameworkModel.getDefaultExtension(RequestMappingRegistry.class); CONVERT_NO_LOWER_HEADER = ConfigurationUtils.getEnvConfiguration(ApplicationModel.defaultModel()) .getBoolean(H2_SUPPORT_NO_LOWER_HEADER_KEY, true); IGNORE_1_0_0_VERSION = ConfigurationUtils.getEnvConfiguration(ApplicationModel.defaultModel()) @@ -81,7 +84,7 @@ public TripleProtocol(FrameworkModel frameworkModel) { .getBoolean(H2_RESOLVE_FALLBACK_TO_DEFAULT_KEY, true); Set supported = frameworkModel.getExtensionLoader(DeCompressor.class).getSupportedExtensions(); - this.acceptEncodings = String.join(",", supported); + acceptEncodings = String.join(",", supported); } @Override @@ -93,11 +96,13 @@ public int getDefaultPort() { public Exporter export(Invoker invoker) throws RpcException { URL url = invoker.getUrl(); String key = serviceKey(url); - final AbstractExporter exporter = new AbstractExporter(invoker) { + AbstractExporter exporter = new AbstractExporter(invoker) { @Override public void afterUnExport() { pathResolver.remove(url.getServiceKey()); pathResolver.remove(url.getServiceModel().getServiceModel().getInterfaceName()); + // unregister rest request mapping + requestMappingRegistry.unregister(invoker); // set service status if (triBuiltinService.enable()) { triBuiltinService @@ -133,14 +138,16 @@ public void afterUnExport() { if (previous != null) { logger.info("Already exists an invoker[" + previous.getUrl() + "] on path[" + url.getServiceModel().getServiceModel().getInterfaceName() - + "], dubbo will skip override with invoker[" - + url + "]"); + + "], dubbo will skip override with invoker[" + url + "]"); } else { logger.info("Add fallback triple invoker[" + url + "] to path[" + url.getServiceModel().getServiceModel().getInterfaceName() + "] with invoker[" + url + "]"); } } + // register rest request mapping + requestMappingRegistry.register(invoker); + // set service status if (triBuiltinService.enable()) { triBuiltinService @@ -187,10 +194,11 @@ protected Invoker protocolBindingRefer(Class type, URL url) throws Rpc @Override public void destroy() { if (logger.isInfoEnabled()) { - logger.info("Destroying protocol [" + this.getClass().getSimpleName() + "] ..."); + logger.info("Destroying protocol [" + getClass().getSimpleName() + "] ..."); } PortUnificationExchanger.close(); pathResolver.destroy(); + requestMappingRegistry.destroy(); super.destroy(); } } diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/aot/TripleReflectionTypeDescriberRegistrar.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/aot/TripleReflectionTypeDescriberRegistrar.java index 0ac720184b63..1f69b886097f 100644 --- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/aot/TripleReflectionTypeDescriberRegistrar.java +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/aot/TripleReflectionTypeDescriberRegistrar.java @@ -22,7 +22,6 @@ import org.apache.dubbo.rpc.protocol.tri.transport.TripleCommandOutBoundHandler; import org.apache.dubbo.rpc.protocol.tri.transport.TripleGoAwayHandler; import org.apache.dubbo.rpc.protocol.tri.transport.TripleHttp2ClientResponseHandler; -import org.apache.dubbo.rpc.protocol.tri.transport.TripleHttp2FrameServerHandler; import org.apache.dubbo.rpc.protocol.tri.transport.TripleServerConnectionHandler; import org.apache.dubbo.rpc.protocol.tri.transport.TripleTailHandler; @@ -35,7 +34,7 @@ public class TripleReflectionTypeDescriberRegistrar implements ReflectionTypeDes @Override public List getTypeDescribers() { List typeDescribers = new ArrayList<>(); - typeDescribers.add(buildTypeDescriberWithPublicMethod(TripleHttp2FrameServerHandler.class)); + // typeDescribers.add(buildTypeDescriberWithPublicMethod(TripleHttp2FrameServerHandler.class)); typeDescribers.add(buildTypeDescriberWithPublicMethod(TripleCommandOutBoundHandler.class)); typeDescribers.add(buildTypeDescriberWithPublicMethod(TripleTailHandler.class)); typeDescribers.add(buildTypeDescriberWithPublicMethod(TripleServerConnectionHandler.class)); diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/AbstractServerCall.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/AbstractServerCall.java deleted file mode 100644 index fb8f516367e7..000000000000 --- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/AbstractServerCall.java +++ /dev/null @@ -1,438 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 - * - * http://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.apache.dubbo.rpc.protocol.tri.call; - -import org.apache.dubbo.common.URL; -import org.apache.dubbo.common.constants.CommonConstants; -import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; -import org.apache.dubbo.common.logger.LoggerFactory; -import org.apache.dubbo.common.utils.StringUtils; -import org.apache.dubbo.rpc.CancellationContext; -import org.apache.dubbo.rpc.Invoker; -import org.apache.dubbo.rpc.RpcContext; -import org.apache.dubbo.rpc.RpcInvocation; -import org.apache.dubbo.rpc.TriRpcStatus; -import org.apache.dubbo.rpc.model.FrameworkModel; -import org.apache.dubbo.rpc.model.MethodDescriptor; -import org.apache.dubbo.rpc.model.PackableMethod; -import org.apache.dubbo.rpc.model.ServiceDescriptor; -import org.apache.dubbo.rpc.protocol.tri.ClassLoadUtil; -import org.apache.dubbo.rpc.protocol.tri.TripleConstant; -import org.apache.dubbo.rpc.protocol.tri.TripleHeaderEnum; -import org.apache.dubbo.rpc.protocol.tri.compressor.Compressor; -import org.apache.dubbo.rpc.protocol.tri.compressor.Identity; -import org.apache.dubbo.rpc.protocol.tri.observer.ServerCallToObserverAdapter; -import org.apache.dubbo.rpc.protocol.tri.stream.ServerStream; -import org.apache.dubbo.rpc.protocol.tri.stream.StreamUtils; - -import java.util.Map; -import java.util.Objects; -import java.util.concurrent.Executor; -import java.util.concurrent.TimeUnit; - -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.handler.codec.http2.DefaultHttp2Headers; -import io.netty.util.concurrent.Future; - -import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_FAILED_CREATE_STREAM_TRIPLE; -import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_FAILED_PARSE; -import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_FAILED_REQUEST; -import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_FAILED_SERIALIZE_TRIPLE; - -public abstract class AbstractServerCall implements ServerCall, ServerStream.Listener { - - public static final String REMOTE_ADDRESS_KEY = "tri.remote.address"; - private static final ErrorTypeAwareLogger LOGGER = LoggerFactory.getErrorTypeAwareLogger(AbstractServerCall.class); - - public final Invoker invoker; - public final FrameworkModel frameworkModel; - public final ServerStream stream; - public final Executor executor; - public final String methodName; - public final String serviceName; - public final ServiceDescriptor serviceDescriptor; - private final String acceptEncoding; - public boolean autoRequestN = true; - public Long timeout; - ServerCall.Listener listener; - private Compressor compressor; - private boolean headerSent; - private boolean closed; - CancellationContext cancellationContext; - protected MethodDescriptor methodDescriptor; - protected PackableMethod packableMethod; - protected Map requestMetadata; - - private Integer exceptionCode = CommonConstants.TRI_EXCEPTION_CODE_NOT_EXISTS; - - public Integer getExceptionCode() { - return exceptionCode; - } - - public void setExceptionCode(Integer exceptionCode) { - this.exceptionCode = exceptionCode; - } - - private boolean isNeedReturnException = false; - - public boolean isNeedReturnException() { - return isNeedReturnException; - } - - public void setNeedReturnException(boolean needReturnException) { - isNeedReturnException = needReturnException; - } - - AbstractServerCall( - Invoker invoker, - ServerStream stream, - FrameworkModel frameworkModel, - ServiceDescriptor serviceDescriptor, - String acceptEncoding, - String serviceName, - String methodName, - Executor executor) { - Objects.requireNonNull(serviceDescriptor, "No service descriptor found for " + invoker.getUrl()); - this.invoker = invoker; - // is already serialized in the stream, so we don't need to serialize it again. - this.executor = executor; - this.frameworkModel = frameworkModel; - this.serviceDescriptor = serviceDescriptor; - this.serviceName = serviceName; - this.methodName = methodName; - this.stream = stream; - this.acceptEncoding = acceptEncoding; - } - - // stream listener start - @Override - public void onHeader(Map requestMetadata) { - this.requestMetadata = requestMetadata; - if (serviceDescriptor == null) { - responseErr(TriRpcStatus.UNIMPLEMENTED.withDescription("Service not found:" + serviceName)); - return; - } - startCall(); - } - - protected void startCall() { - RpcInvocation invocation = buildInvocation(methodDescriptor); - listener = startInternalCall(invocation, methodDescriptor, invoker); - } - - @Override - public final void request(int numMessages) { - stream.request(numMessages); - } - - @Override - public final void sendMessage(Object message) { - if (closed) { - throw new IllegalStateException("Stream has already canceled"); - } - // is already in executor - doSendMessage(message); - } - - private void doSendMessage(Object message) { - if (closed) { - return; - } - if (!headerSent) { - sendHeader(); - } - final byte[] data; - try { - data = packableMethod.packResponse(message); - } catch (Exception e) { - close( - TriRpcStatus.INTERNAL - .withDescription("Serialize response failed") - .withCause(e), - null); - LOGGER.error( - PROTOCOL_FAILED_SERIALIZE_TRIPLE, - "", - "", - String.format("Serialize triple response failed, service=%s method=%s", serviceName, methodName), - e); - return; - } - if (data == null) { - close(TriRpcStatus.INTERNAL.withDescription("Missing response"), null); - return; - } - Future future; - if (compressor != null) { - int compressedFlag = Identity.MESSAGE_ENCODING.equals(compressor.getMessageEncoding()) ? 0 : 1; - final byte[] compressed = compressor.compress(data); - future = stream.sendMessage(compressed, compressedFlag); - } else { - future = stream.sendMessage(data, 0); - } - future.addListener(f -> { - if (!f.isSuccess()) { - cancelDual(TriRpcStatus.CANCELLED - .withDescription("Send message failed") - .withCause(f.cause())); - } - }); - } - - @Override - public final void onComplete() { - if (listener == null) { - // It will enter here when there is an error in the header - return; - } - // Both 'onError' and 'onComplete' are termination operators. - // The stream will be closed when 'onError' was called, and 'onComplete' is not allowed to be called again. - if (isClosed()) { - return; - } - listener.onComplete(); - } - - @Override - public final void onMessage(byte[] message, boolean isReturnTriException) { - ClassLoader tccl = Thread.currentThread().getContextClassLoader(); - try { - Object instance = parseSingleMessage(message); - listener.onMessage(instance, message.length); - } catch (Exception e) { - final TriRpcStatus status = - TriRpcStatus.UNKNOWN.withDescription("Server error").withCause(e); - close(status, null); - LOGGER.error( - PROTOCOL_FAILED_REQUEST, - "", - "", - "Process request failed. service=" + serviceName + " method=" + methodName, - e); - } finally { - ClassLoadUtil.switchContextLoader(tccl); - } - } - - protected abstract Object parseSingleMessage(byte[] data) throws Exception; - - @Override - public final void onCancelByRemote(TriRpcStatus status) { - closed = true; - if (listener == null) { - return; - } - cancellationContext.cancel(status.cause); - listener.onCancel(status); - } - // stream listener end - - public final boolean isClosed() { - return closed; - } - - /** - * Build the RpcInvocation with metadata and execute headerFilter - * - * @return RpcInvocation - */ - protected RpcInvocation buildInvocation(MethodDescriptor methodDescriptor) { - final URL url = invoker.getUrl(); - RpcInvocation inv = new RpcInvocation( - url.getServiceModel(), - methodDescriptor.getMethodName(), - serviceDescriptor.getInterfaceName(), - url.getProtocolServiceKey(), - methodDescriptor.getParameterClasses(), - new Object[0]); - inv.setTargetServiceUniqueName(url.getServiceKey()); - inv.setReturnTypes(methodDescriptor.getReturnTypes()); - inv.setObjectAttachments(StreamUtils.toAttachments(requestMetadata)); - inv.put(REMOTE_ADDRESS_KEY, stream.remoteAddress()); - // handle timeout - String timeout = (String) requestMetadata.get(TripleHeaderEnum.TIMEOUT.getHeader()); - try { - if (Objects.nonNull(timeout)) { - this.timeout = parseTimeoutToMills(timeout); - } - } catch (Throwable t) { - LOGGER.warn( - PROTOCOL_FAILED_PARSE, - "", - "", - String.format( - "Failed to parse request timeout set from:%s, service=%s " + "method=%s", - timeout, serviceDescriptor.getInterfaceName(), methodName)); - } - if (null != requestMetadata.get(TripleHeaderEnum.CONSUMER_APP_NAME_KEY.getHeader())) { - inv.put( - TripleHeaderEnum.CONSUMER_APP_NAME_KEY, - requestMetadata.get(TripleHeaderEnum.CONSUMER_APP_NAME_KEY.getHeader())); - } - return inv; - } - - private void sendHeader() { - if (closed) { - return; - } - if (headerSent) { - throw new IllegalStateException("Header has already sent"); - } - headerSent = true; - DefaultHttp2Headers headers = new DefaultHttp2Headers(); - headers.status(HttpResponseStatus.OK.codeAsText()); - headers.set(HttpHeaderNames.CONTENT_TYPE, TripleConstant.CONTENT_PROTO); - if (acceptEncoding != null) { - headers.set(HttpHeaderNames.ACCEPT_ENCODING, acceptEncoding); - } - if (compressor != null) { - headers.set(TripleHeaderEnum.GRPC_ENCODING.getHeader(), compressor.getMessageEncoding()); - } - if (!exceptionCode.equals(CommonConstants.TRI_EXCEPTION_CODE_NOT_EXISTS)) { - headers.set(TripleHeaderEnum.TRI_EXCEPTION_CODE.getHeader(), String.valueOf(exceptionCode)); - } - // send header failed will reset stream and close request observer cause no more data will be sent - stream.sendHeader(headers).addListener(f -> { - if (!f.isSuccess()) { - cancelDual(TriRpcStatus.INTERNAL.withCause(f.cause())); - } - }); - } - - private void cancelDual(TriRpcStatus status) { - closed = true; - listener.onCancel(status); - cancellationContext.cancel(status.asException()); - } - - public void cancelByLocal(Throwable throwable) { - if (closed) { - return; - } - closed = true; - cancellationContext.cancel(throwable); - stream.cancelByLocal(TriRpcStatus.CANCELLED.withCause(throwable)); - } - - public void setCompression(String compression) { - if (headerSent) { - throw new IllegalStateException("Can not set compression after header sent"); - } - this.compressor = Compressor.getCompressor(frameworkModel, compression); - } - - public void disableAutoRequestN() { - autoRequestN = false; - } - - public boolean isAutoRequestN() { - return autoRequestN; - } - - public void close(TriRpcStatus status, Map attachments) { - doClose(status, attachments); - } - - private void doClose(TriRpcStatus status, Map attachments) { - if (closed) { - return; - } - closed = true; - stream.complete(status, attachments, isNeedReturnException, exceptionCode); - } - - protected Long parseTimeoutToMills(String timeoutVal) { - if (StringUtils.isEmpty(timeoutVal) || StringUtils.isContains(timeoutVal, "null")) { - return null; - } - long value = Long.parseLong(timeoutVal.substring(0, timeoutVal.length() - 1)); - char unit = timeoutVal.charAt(timeoutVal.length() - 1); - switch (unit) { - case 'n': - return TimeUnit.NANOSECONDS.toMillis(value); - case 'u': - return TimeUnit.MICROSECONDS.toMillis(value); - case 'm': - return value; - case 'S': - return TimeUnit.SECONDS.toMillis(value); - case 'M': - return TimeUnit.MINUTES.toMillis(value); - case 'H': - return TimeUnit.HOURS.toMillis(value); - default: - // invalid timeout config - return null; - } - } - - /** - * Error in create stream, unsupported config or triple protocol error. - * - * @param status response status - */ - protected void responseErr(TriRpcStatus status) { - if (closed) { - return; - } - closed = true; - stream.complete(status, null, false, CommonConstants.TRI_EXCEPTION_CODE_NOT_EXISTS); - LOGGER.error( - PROTOCOL_FAILED_REQUEST, - "", - "", - "Triple request error: service=" + serviceName + " method" + methodName, - status.asException()); - } - - protected ServerCall.Listener startInternalCall( - RpcInvocation invocation, MethodDescriptor methodDescriptor, Invoker invoker) { - this.cancellationContext = RpcContext.getCancellationContext(); - ServerCallToObserverAdapter responseObserver = - new ServerCallToObserverAdapter<>(this, cancellationContext); - try { - ServerCall.Listener listener; - switch (methodDescriptor.getRpcType()) { - case UNARY: - listener = new UnaryServerCallListener( - invocation, invoker, responseObserver, packableMethod.needWrapper()); - request(2); - break; - case SERVER_STREAM: - listener = new ServerStreamServerCallListener(invocation, invoker, responseObserver); - request(2); - break; - case BI_STREAM: - case CLIENT_STREAM: - listener = new BiStreamServerCallListener(invocation, invoker, responseObserver); - request(1); - break; - default: - throw new IllegalStateException("Can not reach here"); - } - return listener; - } catch (Exception e) { - LOGGER.error(PROTOCOL_FAILED_CREATE_STREAM_TRIPLE, "", "", "Create triple stream failed", e); - responseErr(TriRpcStatus.INTERNAL - .withDescription("Create stream failed") - .withCause(e)); - } - return null; - } -} diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/AbstractServerCallListener.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/AbstractServerCallListener.java deleted file mode 100644 index 7fa838d8d75b..000000000000 --- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/AbstractServerCallListener.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 - * - * http://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.apache.dubbo.rpc.protocol.tri.call; - -import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; -import org.apache.dubbo.common.logger.LoggerFactory; -import org.apache.dubbo.rpc.CancellationContext; -import org.apache.dubbo.rpc.Invoker; -import org.apache.dubbo.rpc.Result; -import org.apache.dubbo.rpc.RpcContext; -import org.apache.dubbo.rpc.RpcInvocation; -import org.apache.dubbo.rpc.TriRpcStatus; -import org.apache.dubbo.rpc.protocol.tri.TripleHeaderEnum; -import org.apache.dubbo.rpc.protocol.tri.observer.ServerCallToObserverAdapter; - -import java.net.InetSocketAddress; - -import static org.apache.dubbo.common.constants.CommonConstants.REMOTE_APPLICATION_KEY; -import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_TIMEOUT_SERVER; - -public abstract class AbstractServerCallListener implements AbstractServerCall.Listener { - - private static final ErrorTypeAwareLogger LOGGER = - LoggerFactory.getErrorTypeAwareLogger(AbstractServerCallListener.class); - public final CancellationContext cancellationContext; - final RpcInvocation invocation; - final Invoker invoker; - final ServerCallToObserverAdapter responseObserver; - - public AbstractServerCallListener( - RpcInvocation invocation, Invoker invoker, ServerCallToObserverAdapter responseObserver) { - this.invocation = invocation; - this.invoker = invoker; - this.cancellationContext = responseObserver.cancellationContext; - this.responseObserver = responseObserver; - } - - public void invoke() { - RpcContext.restoreCancellationContext(cancellationContext); - InetSocketAddress remoteAddress = - (InetSocketAddress) invocation.getAttributes().remove(AbstractServerCall.REMOTE_ADDRESS_KEY); - RpcContext.getServiceContext().setRemoteAddress(remoteAddress); - String remoteApp = (String) invocation.getAttributes().remove(TripleHeaderEnum.CONSUMER_APP_NAME_KEY); - if (null != remoteApp) { - RpcContext.getServiceContext().setRemoteApplicationName(remoteApp); - invocation.setAttachmentIfAbsent(REMOTE_APPLICATION_KEY, remoteApp); - } - final long stInMillis = System.currentTimeMillis(); - try { - final Result response = invoker.invoke(invocation); - response.whenCompleteWithContext((r, t) -> { - responseObserver.setResponseAttachments(response.getObjectAttachments()); - if (t != null) { - responseObserver.onError(t); - return; - } - if (response.hasException()) { - doOnResponseHasException(response.getException()); - return; - } - final long cost = System.currentTimeMillis() - stInMillis; - if (responseObserver.isTimeout(cost)) { - LOGGER.error( - PROTOCOL_TIMEOUT_SERVER, - "", - "", - String.format( - "Invoke timeout at server side, ignored to send response. service=%s method=%s cost=%s", - invocation.getTargetServiceUniqueName(), invocation.getMethodName(), cost)); - responseObserver.onCompleted(TriRpcStatus.DEADLINE_EXCEEDED); - return; - } - onReturn(r.getValue()); - }); - } catch (Exception e) { - responseObserver.onError(e); - } finally { - RpcContext.removeCancellationContext(); - RpcContext.removeContext(); - } - } - - protected void doOnResponseHasException(Throwable t) { - responseObserver.onError(t); - } - - public abstract void onReturn(Object value); -} diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/BiStreamServerCallListener.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/BiStreamServerCallListener.java deleted file mode 100644 index e37ac9d006ce..000000000000 --- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/BiStreamServerCallListener.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 - * - * http://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.apache.dubbo.rpc.protocol.tri.call; - -import org.apache.dubbo.common.stream.StreamObserver; -import org.apache.dubbo.rpc.Invoker; -import org.apache.dubbo.rpc.RpcInvocation; -import org.apache.dubbo.rpc.TriRpcStatus; -import org.apache.dubbo.rpc.protocol.tri.observer.ServerCallToObserverAdapter; - -public class BiStreamServerCallListener extends AbstractServerCallListener { - - private StreamObserver requestObserver; - - public BiStreamServerCallListener( - RpcInvocation invocation, Invoker invoker, ServerCallToObserverAdapter responseObserver) { - super(invocation, invoker, responseObserver); - invocation.setArguments(new Object[] {responseObserver}); - invoke(); - } - - @Override - public void onReturn(Object value) { - this.requestObserver = (StreamObserver) value; - } - - @Override - public void onMessage(Object message, int actualContentLength) { - if (message instanceof Object[]) { - message = ((Object[]) message)[0]; - } - requestObserver.onNext(message); - if (responseObserver.isAutoRequestN()) { - responseObserver.request(1); - } - } - - @Override - public void onCancel(TriRpcStatus status) { - requestObserver.onError(status.asException()); - } - - @Override - public void onComplete() { - requestObserver.onCompleted(); - } -} diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/ReflectionAbstractServerCall.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/ReflectionAbstractServerCall.java deleted file mode 100644 index 4be61a00df8a..000000000000 --- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/ReflectionAbstractServerCall.java +++ /dev/null @@ -1,214 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 - * - * http://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.apache.dubbo.rpc.protocol.tri.call; - -import org.apache.dubbo.common.URL; -import org.apache.dubbo.common.config.ConfigurationUtils; -import org.apache.dubbo.common.constants.CommonConstants; -import org.apache.dubbo.common.utils.CollectionUtils; -import org.apache.dubbo.rpc.HeaderFilter; -import org.apache.dubbo.rpc.Invoker; -import org.apache.dubbo.rpc.RpcInvocation; -import org.apache.dubbo.rpc.TriRpcStatus; -import org.apache.dubbo.rpc.model.FrameworkModel; -import org.apache.dubbo.rpc.model.MethodDescriptor; -import org.apache.dubbo.rpc.model.MethodDescriptor.RpcType; -import org.apache.dubbo.rpc.model.PackableMethod; -import org.apache.dubbo.rpc.model.PackableMethodFactory; -import org.apache.dubbo.rpc.model.ProviderModel; -import org.apache.dubbo.rpc.model.ServiceDescriptor; -import org.apache.dubbo.rpc.protocol.tri.ClassLoadUtil; -import org.apache.dubbo.rpc.protocol.tri.TripleCustomerProtocolWapper; -import org.apache.dubbo.rpc.protocol.tri.stream.ServerStream; -import org.apache.dubbo.rpc.service.ServiceDescriptorInternalCache; - -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.Executor; - -import io.netty.handler.codec.http.HttpHeaderNames; - -import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_KEY; -import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_PACKABLE_METHOD_FACTORY; - -public class ReflectionAbstractServerCall extends AbstractServerCall { - - private static final String PACKABLE_METHOD_CACHE = "PACKABLE_METHOD_CACHE"; - private final List headerFilters; - private List methodDescriptors; - - public ReflectionAbstractServerCall( - Invoker invoker, - ServerStream serverStream, - FrameworkModel frameworkModel, - String acceptEncoding, - String serviceName, - String methodName, - List headerFilters, - Executor executor) { - super( - invoker, - serverStream, - frameworkModel, - getServiceDescriptor(invoker.getUrl()), - acceptEncoding, - serviceName, - methodName, - executor); - this.headerFilters = headerFilters; - } - - private static ServiceDescriptor getServiceDescriptor(URL url) { - ProviderModel providerModel = (ProviderModel) url.getServiceModel(); - if (providerModel == null || providerModel.getServiceModel() == null) { - return null; - } - return providerModel.getServiceModel(); - } - - private boolean isEcho(String methodName) { - return CommonConstants.$ECHO.equals(methodName); - } - - private boolean isGeneric(String methodName) { - return CommonConstants.$INVOKE.equals(methodName) || CommonConstants.$INVOKE_ASYNC.equals(methodName); - } - - @Override - public void startCall() { - if (isGeneric(methodName)) { - // There should be one and only one - methodDescriptor = ServiceDescriptorInternalCache.genericService() - .getMethods(methodName) - .get(0); - } else if (isEcho(methodName)) { - // There should be one and only one - methodDescriptor = ServiceDescriptorInternalCache.echoService() - .getMethods(methodName) - .get(0); - } else { - methodDescriptors = serviceDescriptor.getMethods(methodName); - // try lower-case method - if (CollectionUtils.isEmpty(methodDescriptors)) { - final String lowerMethod = Character.toLowerCase(methodName.charAt(0)) + methodName.substring(1); - methodDescriptors = serviceDescriptor.getMethods(lowerMethod); - } - if (CollectionUtils.isEmpty(methodDescriptors)) { - responseErr(TriRpcStatus.UNIMPLEMENTED.withDescription( - "Method : " + methodName + " not found of service:" + serviceName)); - return; - } - // In most cases there is only one method - if (methodDescriptors.size() == 1) { - methodDescriptor = methodDescriptors.get(0); - } - // generated unary method ,use unary type - // Response foo(Request) - // void foo(Request,StreamObserver) - if (methodDescriptors.size() == 2) { - if (methodDescriptors.get(1).getRpcType() == RpcType.SERVER_STREAM) { - methodDescriptor = methodDescriptors.get(0); - } else if (methodDescriptors.get(0).getRpcType() == RpcType.SERVER_STREAM) { - methodDescriptor = methodDescriptors.get(1); - } - } - } - if (methodDescriptor != null) { - loadPackableMethod(invoker.getUrl()); - } - trySetListener(); - if (listener == null) { - // wrap request , need one message - request(1); - } - } - - private void trySetListener() { - if (listener != null) { - return; - } - if (methodDescriptor == null) { - return; - } - if (isClosed()) { - return; - } - RpcInvocation invocation = buildInvocation(methodDescriptor); - if (isClosed()) { - return; - } - headerFilters.forEach(f -> f.invoke(invoker, invocation)); - if (isClosed()) { - return; - } - listener = ReflectionAbstractServerCall.this.startInternalCall(invocation, methodDescriptor, invoker); - } - - @Override - protected Object parseSingleMessage(byte[] data) throws Exception { - trySetMethodDescriptor(data); - trySetListener(); - if (isClosed()) { - return null; - } - ClassLoadUtil.switchContextLoader(invoker.getUrl().getServiceModel().getClassLoader()); - return packableMethod.getRequestUnpack().unpack(data); - } - - private void trySetMethodDescriptor(byte[] data) { - if (methodDescriptor != null) { - return; - } - final TripleCustomerProtocolWapper.TripleRequestWrapper request; - request = TripleCustomerProtocolWapper.TripleRequestWrapper.parseFrom(data); - - final String[] paramTypes = - request.getArgTypes().toArray(new String[request.getArgs().size()]); - // wrapper mode the method can overload so maybe list - for (MethodDescriptor descriptor : methodDescriptors) { - // params type is array - if (Arrays.equals(descriptor.getCompatibleParamSignatures(), paramTypes)) { - methodDescriptor = descriptor; - break; - } - } - if (methodDescriptor == null) { - ReflectionAbstractServerCall.this.close( - TriRpcStatus.UNIMPLEMENTED.withDescription( - "Method :" + methodName + "[" + Arrays.toString(paramTypes) + "] " + "not found of service:" - + serviceDescriptor.getInterfaceName()), - null); - return; - } - loadPackableMethod(invoker.getUrl()); - } - - @SuppressWarnings("unchecked") - private void loadPackableMethod(URL url) { - Map cacheMap = (Map) url.getServiceModel() - .getServiceMetadata() - .getAttributeMap() - .computeIfAbsent(PACKABLE_METHOD_CACHE, (k) -> new ConcurrentHashMap<>()); - packableMethod = cacheMap.computeIfAbsent(methodDescriptor, (md) -> frameworkModel - .getExtensionLoader(PackableMethodFactory.class) - .getExtension(ConfigurationUtils.getGlobalConfiguration(url.getApplicationModel()) - .getString(DUBBO_PACKABLE_METHOD_FACTORY, DEFAULT_KEY)) - .create(methodDescriptor, url, (String) requestMetadata.get(HttpHeaderNames.CONTENT_TYPE.toString()))); - } -} diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/ServerCall.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/ServerCall.java deleted file mode 100644 index e5bdb365139c..000000000000 --- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/ServerCall.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 - * - * http://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.apache.dubbo.rpc.protocol.tri.call; - -import org.apache.dubbo.rpc.TriRpcStatus; - -import java.util.Map; - -/** - * ServerCall manipulates server details of a RPC call. Request messages are acquired by {@link - * Listener}. Backpressure is supported by {@link #request(int)}.Response messages are sent by - * {@link ServerCall#sendMessage(Object)}. - */ -public interface ServerCall { - - /** - * A listener to receive request messages. - */ - interface Listener { - - /** - * Callback when a request message is received. - * - * @param message message received - * @param actualContentLength actual content length from body - */ - void onMessage(Object message, int actualContentLength); - - /** - * @param status when the call is canceled. - */ - void onCancel(TriRpcStatus status); - - /** - * Request completed. - */ - void onComplete(); - } - - /** - * Send message to client - * - * @param message message to send - */ - void sendMessage(Object message); - - /** - * Request more request data from the client. - * - * @param numMessages max number of messages - */ - void request(int numMessages); - - /** - * Close the call. - * - * @param status status of the call to send to the client - * @param responseAttrs response attachments - */ - void close(TriRpcStatus status, Map responseAttrs); -} diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/ServerStreamServerCallListener.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/ServerStreamServerCallListener.java deleted file mode 100644 index 8e5ea19caf15..000000000000 --- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/ServerStreamServerCallListener.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 - * - * http://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.apache.dubbo.rpc.protocol.tri.call; - -import org.apache.dubbo.rpc.Invoker; -import org.apache.dubbo.rpc.RpcInvocation; -import org.apache.dubbo.rpc.TriRpcStatus; -import org.apache.dubbo.rpc.protocol.tri.observer.ServerCallToObserverAdapter; - -public class ServerStreamServerCallListener extends AbstractServerCallListener { - - public ServerStreamServerCallListener( - RpcInvocation invocation, Invoker invoker, ServerCallToObserverAdapter responseObserver) { - super(invocation, invoker, responseObserver); - } - - @Override - public void onReturn(Object value) {} - - @Override - public void onMessage(Object message, int actualContentLength) { - if (message instanceof Object[]) { - message = ((Object[]) message)[0]; - } - invocation.setArguments(new Object[] {message, responseObserver}); - } - - @Override - public void onCancel(TriRpcStatus status) { - responseObserver.onError(status.asException()); - } - - @Override - public void onComplete() { - invoke(); - } -} diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/StubAbstractServerCall.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/StubAbstractServerCall.java deleted file mode 100644 index 139c42c415a8..000000000000 --- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/StubAbstractServerCall.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 - * - * http://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.apache.dubbo.rpc.protocol.tri.call; - -import org.apache.dubbo.common.URL; -import org.apache.dubbo.rpc.Invoker; -import org.apache.dubbo.rpc.model.FrameworkModel; -import org.apache.dubbo.rpc.model.ServiceDescriptor; -import org.apache.dubbo.rpc.model.StubMethodDescriptor; -import org.apache.dubbo.rpc.protocol.tri.stream.ServerStream; -import org.apache.dubbo.rpc.stub.StubSuppliers; - -import java.util.concurrent.Executor; - -public class StubAbstractServerCall extends AbstractServerCall { - - public StubAbstractServerCall( - Invoker invoker, - ServerStream serverStream, - FrameworkModel frameworkModel, - String acceptEncoding, - String serviceName, - String methodName, - Executor executor) { - super( - invoker, - serverStream, - frameworkModel, - getServiceDescriptor(invoker.getUrl(), serviceName), - acceptEncoding, - serviceName, - methodName, - executor); - this.methodDescriptor = serviceDescriptor.getMethods(methodName).get(0); - this.packableMethod = (StubMethodDescriptor) methodDescriptor; - } - - private static ServiceDescriptor getServiceDescriptor(URL url, String serviceName) { - ServiceDescriptor serviceDescriptor; - if (url.getServiceModel() != null) { - serviceDescriptor = url.getServiceModel().getServiceModel(); - } else { - serviceDescriptor = StubSuppliers.getServiceDescriptor(serviceName); - } - return serviceDescriptor; - } - - @Override - protected Object parseSingleMessage(byte[] data) throws Exception { - return packableMethod.parseRequest(data); - } -} diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/UnaryServerCallListener.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/UnaryServerCallListener.java deleted file mode 100644 index 9e6da9158f2b..000000000000 --- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/call/UnaryServerCallListener.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 - * - * http://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.apache.dubbo.rpc.protocol.tri.call; - -import org.apache.dubbo.remoting.Constants; -import org.apache.dubbo.rpc.Invoker; -import org.apache.dubbo.rpc.RpcException; -import org.apache.dubbo.rpc.RpcInvocation; -import org.apache.dubbo.rpc.TriRpcStatus; -import org.apache.dubbo.rpc.protocol.tri.observer.ServerCallToObserverAdapter; - -public class UnaryServerCallListener extends AbstractServerCallListener { - - private final boolean needWrapper; - - public UnaryServerCallListener( - RpcInvocation invocation, - Invoker invoker, - ServerCallToObserverAdapter responseObserver, - boolean needWrapper) { - super(invocation, invoker, responseObserver); - this.needWrapper = needWrapper; - } - - @Override - public void onReturn(Object value) { - responseObserver.onNext(value); - responseObserver.onCompleted(); - } - - @Override - public void onMessage(Object message, int actualContentLength) { - if (message instanceof Object[]) { - invocation.setArguments((Object[]) message); - } else { - invocation.setArguments(new Object[] {message}); - } - invocation.put(Constants.CONTENT_LENGTH_KEY, actualContentLength); - } - - @Override - public void onCancel(TriRpcStatus status) { - // ignored - } - - @Override - protected void doOnResponseHasException(Throwable t) { - if (needWrapper) { - onReturnException((Exception) t); - } else { - super.doOnResponseHasException(t); - } - } - - private void onReturnException(Exception value) { - TriRpcStatus status = TriRpcStatus.getStatus(value); - int exceptionCode = status.code.code; - if (exceptionCode == TriRpcStatus.UNKNOWN.code.code) { - exceptionCode = RpcException.BIZ_EXCEPTION; - } - responseObserver.setExceptionCode(exceptionCode); - responseObserver.setNeedReturnException(true); - onReturn(value); - } - - @Override - public void onComplete() { - invoke(); - } -} diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/AbstractServerCallListener.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/AbstractServerCallListener.java index 689d9d03e39f..1f4f984e9ecf 100644 --- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/AbstractServerCallListener.java +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/AbstractServerCallListener.java @@ -25,8 +25,8 @@ import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcContext; import org.apache.dubbo.rpc.RpcInvocation; +import org.apache.dubbo.rpc.protocol.tri.TripleConstant; import org.apache.dubbo.rpc.protocol.tri.TripleHeaderEnum; -import org.apache.dubbo.rpc.protocol.tri.call.AbstractServerCall; import java.net.InetSocketAddress; @@ -57,7 +57,7 @@ public void invoke() { ((Http2CancelableStreamObserver) responseObserver).getCancellationContext()); } InetSocketAddress remoteAddress = - (InetSocketAddress) invocation.getAttributes().remove(AbstractServerCall.REMOTE_ADDRESS_KEY); + (InetSocketAddress) invocation.getAttributes().remove(TripleConstant.REMOTE_ADDRESS_KEY); RpcContext.getServiceContext().setRemoteAddress(remoteAddress); String remoteApp = (String) invocation.getAttributes().remove(TripleHeaderEnum.CONSUMER_APP_NAME_KEY); if (null != remoteApp) { diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/AbstractServerTransportListener.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/AbstractServerTransportListener.java index 1b8d5090888a..0e7fc25b38a8 100644 --- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/AbstractServerTransportListener.java +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/AbstractServerTransportListener.java @@ -20,50 +20,29 @@ import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; -import org.apache.dubbo.common.utils.CollectionUtils; -import org.apache.dubbo.common.utils.JsonUtils; import org.apache.dubbo.remoting.http12.HttpChannel; -import org.apache.dubbo.remoting.http12.HttpHeaderNames; -import org.apache.dubbo.remoting.http12.HttpHeaders; import org.apache.dubbo.remoting.http12.HttpInputMessage; -import org.apache.dubbo.remoting.http12.HttpMetadata; import org.apache.dubbo.remoting.http12.HttpStatus; import org.apache.dubbo.remoting.http12.HttpTransportListener; import org.apache.dubbo.remoting.http12.RequestMetadata; import org.apache.dubbo.remoting.http12.exception.HttpStatusException; -import org.apache.dubbo.remoting.http12.exception.IllegalPathException; -import org.apache.dubbo.remoting.http12.exception.UnimplementedException; -import org.apache.dubbo.remoting.http12.exception.UnsupportedMediaTypeException; -import org.apache.dubbo.remoting.http12.message.HttpMessageDecoder; import org.apache.dubbo.remoting.http12.message.MethodMetadata; -import org.apache.dubbo.remoting.http12.message.codec.CodecUtils; import org.apache.dubbo.rpc.HeaderFilter; import org.apache.dubbo.rpc.Invoker; -import org.apache.dubbo.rpc.PathResolver; import org.apache.dubbo.rpc.RpcInvocation; -import org.apache.dubbo.rpc.TriRpcStatus; import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.model.MethodDescriptor; -import org.apache.dubbo.rpc.model.ProviderModel; -import org.apache.dubbo.rpc.model.ServiceDescriptor; +import org.apache.dubbo.rpc.protocol.tri.DescriptorUtils; import org.apache.dubbo.rpc.protocol.tri.TripleConstant; import org.apache.dubbo.rpc.protocol.tri.TripleHeaderEnum; -import org.apache.dubbo.rpc.protocol.tri.TripleProtocol; +import org.apache.dubbo.rpc.protocol.tri.route.RequestRouter; +import org.apache.dubbo.rpc.protocol.tri.route.RpcInvocationBuildContext; import org.apache.dubbo.rpc.protocol.tri.stream.StreamUtils; -import org.apache.dubbo.rpc.service.ServiceDescriptorInternalCache; -import org.apache.dubbo.rpc.stub.StubSuppliers; -import java.io.InputStream; import java.lang.reflect.InvocationTargetException; -import java.util.Collections; -import java.util.HashMap; import java.util.List; -import java.util.Map; -import java.util.Optional; import java.util.concurrent.Executor; -import java.util.function.Supplier; -import static org.apache.dubbo.common.constants.CommonConstants.HEADER_FILTER_KEY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_ERROR_USE_THREAD_POOL; import static org.apache.dubbo.common.constants.LoggerCodeConstants.INTERNAL_ERROR; import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_FAILED_PARSE; @@ -74,65 +53,37 @@ public abstract class AbstractServerTransportListener
headerFilters; - private HttpMessageDecoder httpMessageDecoder; - - private Invoker invoker; - - private ServiceDescriptor serviceDescriptor; - - private MethodDescriptor methodDescriptor; - - private RpcInvocation rpcInvocation; - - private MethodMetadata methodMetadata; - - private HEADER httpMetadata; - private Executor executor; - - private boolean hasStub; - + private HEADER httpMetadata; + private RpcInvocationBuildContext context; private HttpMessageListener httpMessageListener; - protected CodecUtils codecUtils; - public AbstractServerTransportListener(FrameworkModel frameworkModel, URL url, HttpChannel httpChannel) { this.frameworkModel = frameworkModel; this.url = url; this.httpChannel = httpChannel; - this.pathResolver = - frameworkModel.getExtensionLoader(PathResolver.class).getDefaultExtension(); - this.headerFilters = - frameworkModel.getExtensionLoader(HeaderFilter.class).getActivateExtension(url, HEADER_FILTER_KEY); - this.codecUtils = frameworkModel.getBeanFactory().getOrRegisterBean(CodecUtils.class); - } - - protected Executor initializeExecutor(HEADER metadata) { - // default direct executor - return Runnable::run; + requestRouter = frameworkModel.getDefaultExtension(RequestRouter.class); + headerFilters = frameworkModel + .getExtensionLoader(HeaderFilter.class) + .getActivateExtension(url, CommonConstants.HEADER_FILTER_KEY); } @Override public void onMetadata(HEADER metadata) { - try { - this.executor = initializeExecutor(metadata); + executor = initializeExecutor(metadata); } catch (Throwable throwable) { LOGGER.error(COMMON_ERROR_USE_THREAD_POOL, "", "", "initialize executor fail.", throwable); onError(throwable); return; } - if (this.executor == null) { + if (executor == null) { LOGGER.error(INTERNAL_ERROR, "", "", "executor must be not null."); onError(new NullPointerException("initializeExecutor return null")); return; @@ -147,41 +98,29 @@ public void onMetadata(HEADER metadata) { }); } + protected Executor initializeExecutor(HEADER metadata) { + // default direct executor + return Runnable::run; + } + protected void doOnMetadata(HEADER metadata) { onPrepareMetadata(metadata); - this.httpMetadata = metadata; - String path = metadata.path(); - HttpHeaders headers = metadata.headers(); - // 1.check necessary header - String contentType = headers.getFirst(HttpHeaderNames.CONTENT_TYPE.getName()); - if (contentType == null) { - throw new UnsupportedMediaTypeException( - "'" + HttpHeaderNames.CONTENT_TYPE.getName() + "' must be not null."); - } + httpMetadata = metadata; - // 2. check service - String[] parts = path.split("/"); - if (parts.length != 3) { - throw new IllegalPathException(path); - } - String serviceName = parts[1]; - this.hasStub = pathResolver.hasNativeStub(path); - this.invoker = getInvoker(metadata, serviceName); - if (invoker == null) { - throw new UnimplementedException(serviceName); + context = requestRouter.route(url, metadata, httpChannel); + if (context == null) { + throw new HttpStatusException(404, "Invoker not found"); } - this.httpMessageDecoder = - codecUtils.determineHttpMessageDecoder(getFrameworkModel(), headers.getContentType(), getUrl()); - setServiceDescriptor(findServiceDescriptor(invoker, serviceName, hasStub)); - setHttpMessageListener(newHttpMessageListener()); + + setHttpMessageListener(buildHttpMessageListener()); onMetadataCompletion(metadata); } - protected abstract HttpMessageListener newHttpMessageListener(); + protected abstract HttpMessageListener buildHttpMessageListener(); @Override public void onData(MESSAGE message) { - this.executor.execute(() -> { + executor.execute(() -> { try { doOnData(message); } catch (Throwable e) { @@ -192,10 +131,9 @@ public void onData(MESSAGE message) { } protected void doOnData(MESSAGE message) { - // decode message onPrepareData(message); - InputStream body = message.getBody(); - httpMessageListener.onMessage(body); + // decode message + httpMessageListener.onMessage(message.getBody()); onDataCompletion(message); } @@ -231,266 +169,76 @@ protected void onError(Throwable throwable) { throw new HttpStatusException(HttpStatus.INTERNAL_SERVER_ERROR.getCode(), throwable); } - private Invoker getInvoker(HEADER metadata, String serviceName) { - HttpHeaders headers = metadata.headers(); - final String version = headers.containsKey(TripleHeaderEnum.SERVICE_VERSION.getHeader()) - ? headers.get(TripleHeaderEnum.SERVICE_VERSION.getHeader()).toString() - : null; - final String group = headers.containsKey(TripleHeaderEnum.SERVICE_GROUP.getHeader()) - ? headers.get(TripleHeaderEnum.SERVICE_GROUP.getHeader()).toString() - : null; - final String key = URL.buildKey(serviceName, group, version); - Invoker invoker = pathResolver.resolve(key); - if (invoker == null && TripleProtocol.RESOLVE_FALLBACK_TO_DEFAULT) { - invoker = pathResolver.resolve(URL.buildKey(serviceName, group, "1.0.0")); - } - if (invoker == null && TripleProtocol.RESOLVE_FALLBACK_TO_DEFAULT) { - invoker = pathResolver.resolve(serviceName); - } - return invoker; - } - - private static ServiceDescriptor findServiceDescriptor(Invoker invoker, String serviceName, boolean hasStub) - throws UnimplementedException { - ServiceDescriptor result; - if (hasStub) { - result = getStubServiceDescriptor(invoker.getUrl(), serviceName); - } else { - result = getReflectionServiceDescriptor(invoker.getUrl()); + protected RpcInvocation buildRpcInvocation(RpcInvocationBuildContext context) { + MethodDescriptor methodDescriptor = context.getMethodDescriptor(); + if (methodDescriptor == null) { + methodDescriptor = DescriptorUtils.findMethodDescriptor( + context.getServiceDescriptor(), context.getMethodName(), context.isHasStub()); + context.setMethodDescriptor(methodDescriptor); } - if (result == null) { - throw new UnimplementedException("service:" + serviceName); + MethodMetadata methodMetadata = context.getMethodMetadata(); + if (methodMetadata == null) { + methodMetadata = MethodMetadata.fromMethodDescriptor(methodDescriptor); + context.setMethodMetadata(methodMetadata); } - return result; - } - protected static MethodDescriptor findMethodDescriptor( - ServiceDescriptor serviceDescriptor, String originalMethodName, boolean hasStub) - throws UnimplementedException { - MethodDescriptor result; - if (hasStub) { - result = serviceDescriptor.getMethods(originalMethodName).get(0); - } else { - result = findReflectionMethodDescriptor(serviceDescriptor, originalMethodName); - } - return result; - } - - protected RpcInvocation buildRpcInvocation( - Invoker invoker, ServiceDescriptor serviceDescriptor, MethodDescriptor methodDescriptor) { - final URL url = invoker.getUrl(); + Invoker invoker = context.getInvoker(); + URL url = invoker.getUrl(); RpcInvocation inv = new RpcInvocation( url.getServiceModel(), methodDescriptor.getMethodName(), - serviceDescriptor.getInterfaceName(), + context.getServiceDescriptor().getInterfaceName(), url.getProtocolServiceKey(), methodDescriptor.getParameterClasses(), new Object[0]); inv.setTargetServiceUniqueName(url.getServiceKey()); inv.setReturnTypes(methodDescriptor.getReturnTypes()); - Map headers = getHttpMetadata().headers().toSingleValueMap(); - Map requestMetadata = headersToMap(headers, () -> { - return Optional.ofNullable(headers.get(TripleHeaderEnum.TRI_HEADER_CONVERT.getHeader())) - .map(CharSequence::toString) - .orElse(null); - }); - inv.setObjectAttachments(StreamUtils.toAttachments(requestMetadata)); + inv.setObjectAttachments(StreamUtils.toAttachments(httpMetadata.headers())); + inv.put(TripleConstant.REMOTE_ADDRESS_KEY, httpChannel.remoteAddress()); + inv.getAttributes().putAll(context.getAttributes()); - inv.put("tri.remote.address", httpChannel.remoteAddress()); // customizer RpcInvocation headerFilters.forEach(f -> f.invoke(invoker, inv)); - return inv; - } - - protected static ServiceDescriptor getStubServiceDescriptor(URL url, String serviceName) { - ServiceDescriptor serviceDescriptor; - if (url.getServiceModel() != null) { - serviceDescriptor = url.getServiceModel().getServiceModel(); - } else { - serviceDescriptor = StubSuppliers.getServiceDescriptor(serviceName); - } - return serviceDescriptor; - } - - protected static ServiceDescriptor getReflectionServiceDescriptor(URL url) { - ProviderModel providerModel = (ProviderModel) url.getServiceModel(); - if (providerModel == null || providerModel.getServiceModel() == null) { - return null; - } - return providerModel.getServiceModel(); - } - protected static boolean isEcho(String methodName) { - return CommonConstants.$ECHO.equals(methodName); + return onBuildRpcInvocationCompletion(inv); } - protected static boolean isGeneric(String methodName) { - return CommonConstants.$INVOKE.equals(methodName) || CommonConstants.$INVOKE_ASYNC.equals(methodName); - } - - protected static MethodDescriptor findReflectionMethodDescriptor( - ServiceDescriptor serviceDescriptor, String methodName) { - MethodDescriptor methodDescriptor = null; - if (isGeneric(methodName)) { - // There should be one and only one - methodDescriptor = ServiceDescriptorInternalCache.genericService() - .getMethods(methodName) - .get(0); - } else if (isEcho(methodName)) { - // There should be one and only one - return ServiceDescriptorInternalCache.echoService() - .getMethods(methodName) - .get(0); - } else { - List methodDescriptors = serviceDescriptor.getMethods(methodName); - // try lower-case method - if (CollectionUtils.isEmpty(methodDescriptors)) { - final String lowerMethod = Character.toLowerCase(methodName.charAt(0)) + methodName.substring(1); - methodDescriptors = serviceDescriptor.getMethods(lowerMethod); - } - if (CollectionUtils.isEmpty(methodDescriptors)) { - return null; - } - // In most cases there is only one method - if (methodDescriptors.size() == 1) { - methodDescriptor = methodDescriptors.get(0); - } - // generated unary method ,use unary type - // Response foo(Request) - // void foo(Request,StreamObserver) - if (methodDescriptors.size() == 2) { - if (methodDescriptors.get(1).getRpcType() == MethodDescriptor.RpcType.SERVER_STREAM) { - methodDescriptor = methodDescriptors.get(0); - } else if (methodDescriptors.get(0).getRpcType() == MethodDescriptor.RpcType.SERVER_STREAM) { - methodDescriptor = methodDescriptors.get(1); - } + protected RpcInvocation onBuildRpcInvocationCompletion(RpcInvocation invocation) { + String timeoutString = httpMetadata.headers().getFirst(TripleHeaderEnum.SERVICE_TIMEOUT.getHeader()); + try { + if (null != timeoutString) { + Long timeout = Long.parseLong(timeoutString); + invocation.put(CommonConstants.TIMEOUT_KEY, timeout); } + } catch (Throwable t) { + LOGGER.warn( + PROTOCOL_FAILED_PARSE, + "", + "", + String.format( + "Failed to parse request timeout set from:%s, service=%s " + "method=%s", + timeoutString, context.getServiceDescriptor().getInterfaceName(), context.getMethodName())); } - return methodDescriptor; + return invocation; } - protected FrameworkModel getFrameworkModel() { + protected final FrameworkModel getFrameworkModel() { return frameworkModel; } - protected HEADER getHttpMetadata() { + protected final HEADER getHttpMetadata() { return httpMetadata; } - protected Invoker getInvoker() { - return invoker; - } - - protected ServiceDescriptor getServiceDescriptor() { - return serviceDescriptor; - } - - protected MethodDescriptor getMethodDescriptor() { - return methodDescriptor; - } - - public void setServiceDescriptor(ServiceDescriptor serviceDescriptor) { - this.serviceDescriptor = serviceDescriptor; - } - - public void setMethodDescriptor(MethodDescriptor methodDescriptor) { - this.methodDescriptor = methodDescriptor; - } - - public void setMethodMetadata(MethodMetadata methodMetadata) { - this.methodMetadata = methodMetadata; - } - - protected RpcInvocation getRpcInvocation() { - return rpcInvocation; - } - - public void setRpcInvocation(RpcInvocation rpcInvocation) { - this.rpcInvocation = rpcInvocation; - } - - protected MethodMetadata getMethodMetadata() { - return methodMetadata; - } - - protected HttpMessageDecoder getHttpMessageDecoder() { - return this.httpMessageDecoder; + public final RpcInvocationBuildContext getContext() { + return context; } - protected CodecUtils getCodecUtils() { - return this.codecUtils; + protected final HttpMessageListener getHttpMessageListener() { + return httpMessageListener; } protected void setHttpMessageListener(HttpMessageListener httpMessageListener) { this.httpMessageListener = httpMessageListener; } - - protected HttpMessageListener getHttpMessageListener() { - return httpMessageListener; - } - - protected PathResolver getPathResolver() { - return pathResolver; - } - - protected final URL getUrl() { - return url; - } - - protected HttpMetadata getMetadata() { - return httpMetadata; - } - - public boolean isHasStub() { - return hasStub; - } - - protected Map headersToMap( - Map headers, Supplier convertUpperHeaderSupplier) { - if (headers == null) { - return Collections.emptyMap(); - } - Map attachments = new HashMap<>(headers.size()); - for (Map.Entry header : headers.entrySet()) { - String key = header.getKey(); - if (key.endsWith(TripleConstant.HEADER_BIN_SUFFIX) - && key.length() > TripleConstant.HEADER_BIN_SUFFIX.length()) { - try { - String realKey = key.substring(0, key.length() - TripleConstant.HEADER_BIN_SUFFIX.length()); - byte[] value = StreamUtils.decodeASCIIByte(header.getValue()); - attachments.put(realKey, value); - } catch (Exception e) { - LOGGER.error(PROTOCOL_FAILED_PARSE, "", "", "Failed to parse response attachment key=" + key, e); - } - } else { - attachments.put(key, header.getValue()); - } - } - - // try converting upper key - Object obj = convertUpperHeaderSupplier.get(); - if (obj == null) { - return attachments; - } - if (obj instanceof String) { - String json = TriRpcStatus.decodeMessage((String) obj); - Map map = JsonUtils.toJavaObject(json, Map.class); - for (Map.Entry entry : map.entrySet()) { - Object val = attachments.remove(entry.getKey()); - if (val != null) { - attachments.put(entry.getValue(), val); - } - } - } else { - // If convertUpperHeaderSupplier does not return String, just fail... - // Internal invocation, use INTERNAL_ERROR instead. - - LOGGER.error( - INTERNAL_ERROR, - "wrong internal invocation", - "", - "Triple convertNoLowerCaseHeader error, obj is not String"); - } - return attachments; - } } diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/HttpHandlerMapping.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/HttpHandlerMapping.java new file mode 100644 index 000000000000..abc10af32124 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/HttpHandlerMapping.java @@ -0,0 +1,105 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * http://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.apache.dubbo.rpc.protocol.tri.h12; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.extension.Activate; +import org.apache.dubbo.common.utils.StringUtils; +import org.apache.dubbo.remoting.http12.HttpRequest; +import org.apache.dubbo.remoting.http12.HttpResponse; +import org.apache.dubbo.remoting.http12.message.codec.CodecUtils; +import org.apache.dubbo.rpc.Invoker; +import org.apache.dubbo.rpc.PathResolver; +import org.apache.dubbo.rpc.model.FrameworkModel; +import org.apache.dubbo.rpc.protocol.tri.DescriptorUtils; +import org.apache.dubbo.rpc.protocol.tri.TripleConstant; +import org.apache.dubbo.rpc.protocol.tri.TripleHeaderEnum; +import org.apache.dubbo.rpc.protocol.tri.TripleProtocol; +import org.apache.dubbo.rpc.protocol.tri.route.HandlerInfo; +import org.apache.dubbo.rpc.protocol.tri.route.HandlerMapping; + +import java.util.List; + +@Activate(order = -2000) +public class HttpHandlerMapping implements HandlerMapping { + + private final FrameworkModel frameworkModel; + private final PathResolver pathResolver; + private final CodecUtils codecUtils; + + public HttpHandlerMapping(FrameworkModel frameworkModel) { + this.frameworkModel = frameworkModel; + pathResolver = frameworkModel.getDefaultExtension(PathResolver.class); + codecUtils = frameworkModel.getBeanFactory().getOrRegisterBean(CodecUtils.class); + } + + @Override + public HandlerInfo getHandler(URL url, HttpRequest request, HttpResponse response) { + if (!supportContentType(request.contentType())) { + return null; + } + + String path = request.path(); + List parts = StringUtils.splitToList(path, '/'); + if (parts.size() != 3) { + return null; + } + + String serviceName = parts.get(1); + String version = request.header(TripleHeaderEnum.SERVICE_VERSION.getHeader()); + String group = request.header(TripleHeaderEnum.SERVICE_GROUP.getHeader()); + String key = URL.buildKey(serviceName, group, version); + Invoker invoker = pathResolver.resolve(key); + if (invoker == null) { + if (TripleProtocol.RESOLVE_FALLBACK_TO_DEFAULT) { + invoker = pathResolver.resolve(URL.buildKey(serviceName, group, TripleConstant.DEFAULT_VERSION)); + if (invoker == null) { + invoker = pathResolver.resolve(serviceName); + if (invoker == null) { + return null; + } + } + } + } + + HandlerInfo info = new HandlerInfo(); + info.setInvoker(invoker); + info.setHasStub(pathResolver.hasNativeStub(path)); + info.setServiceDescriptor(DescriptorUtils.findServiceDescriptor(invoker, serviceName, info.isHasStub())); + info.setMethodName(parts.get(2)); + determineHttpMessageCodec(info, url, request); + return info; + } + + protected boolean supportContentType(String contentType) { + return true; + } + + protected void determineHttpMessageCodec(HandlerInfo info, URL url, HttpRequest request) { + info.setHttpMessageDecoder(codecUtils.determineHttpMessageDecoder(url, frameworkModel, request.contentType())); + info.setHttpMessageEncoder(codecUtils.determineHttpMessageEncoder(url, frameworkModel, request.contentType())); + } + + protected final FrameworkModel getFrameworkModel() { + return frameworkModel; + } + + @Override + public String getType() { + return TripleConstant.TRIPLE_HANDLER_TYPE_HTTP; + } +} diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/TripleProtocolDetector.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/TripleProtocolDetector.java index 9c7cd7138e60..5fa91a97f7c9 100644 --- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/TripleProtocolDetector.java +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/TripleProtocolDetector.java @@ -20,16 +20,14 @@ import org.apache.dubbo.remoting.buffer.ByteBufferBackedChannelBuffer; import org.apache.dubbo.remoting.buffer.ChannelBuffer; import org.apache.dubbo.remoting.buffer.ChannelBuffers; +import org.apache.dubbo.remoting.http12.HttpMethods; import io.netty.handler.codec.http2.Http2CodecUtil; -import static java.lang.Math.min; - public class TripleProtocolDetector implements ProtocolDetector { public static final String HTTP_VERSION = "HTTP_VERSION"; - - private final ChannelBuffer clientPrefaceString = new ByteBufferBackedChannelBuffer( + private static final ChannelBuffer CLIENT_PREFACE_STRING = new ByteBufferBackedChannelBuffer( Http2CodecUtil.connectionPrefaceBuf().nioBuffer()); @Override @@ -38,8 +36,8 @@ public Result detect(ChannelBuffer in) { if (in.readableBytes() < 2) { return Result.needMoreData(); } - byte[] magics = new byte[5]; - in.getBytes(in.readerIndex(), magics, 0, 5); + byte[] magics = new byte[7]; + in.getBytes(in.readerIndex(), magics, 0, 7); if (isHttp(magics)) { Result recognized = Result.recognized(); recognized.setAttribute(HTTP_VERSION, HttpVersion.HTTP1.getVersion()); @@ -48,9 +46,9 @@ public Result detect(ChannelBuffer in) { in.resetReaderIndex(); // http2 - int prefaceLen = clientPrefaceString.readableBytes(); - int bytesRead = min(in.readableBytes(), prefaceLen); - if (bytesRead == 0 || !ChannelBuffers.prefixEquals(in, clientPrefaceString, bytesRead)) { + int prefaceLen = CLIENT_PREFACE_STRING.readableBytes(); + int bytesRead = Math.min(in.readableBytes(), prefaceLen); + if (bytesRead == 0 || !ChannelBuffers.prefixEquals(in, CLIENT_PREFACE_STRING, bytesRead)) { return Result.unrecognized(); } if (bytesRead == prefaceLen) { @@ -62,16 +60,22 @@ public Result detect(ChannelBuffer in) { } private static boolean isHttp(byte[] magic) { - if (magic[0] == 'G' && magic[1] == 'E' && magic[2] == 'T') { - return true; - } - if (magic[0] == 'P' && magic[1] == 'O' && magic[2] == 'S' && magic[3] == 'T') { - return true; + for (int i = 0; i < 8; i++) { + byte[] methodBytes = HttpMethods.HTTP_METHODS_BYTES[i]; + int end = methodBytes.length - 1; + for (int j = 0; j <= end; j++) { + if (magic[j] != methodBytes[j]) { + break; + } + if (j == end) { + return true; + } + } } return false; } - public static enum HttpVersion { + public enum HttpVersion { HTTP1("http1"), HTTP2("http2"); diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/grpc/GrpcCompositeCodec.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/grpc/GrpcCompositeCodec.java index 96b18cf2ec3f..ac910da7ee14 100644 --- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/grpc/GrpcCompositeCodec.java +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/grpc/GrpcCompositeCodec.java @@ -20,6 +20,7 @@ import org.apache.dubbo.remoting.http12.exception.EncodeException; import org.apache.dubbo.remoting.http12.message.HttpMessageCodec; import org.apache.dubbo.remoting.http12.message.MediaType; +import org.apache.dubbo.rpc.protocol.tri.TripleConstant; import java.io.IOException; import java.io.InputStream; @@ -31,8 +32,6 @@ public class GrpcCompositeCodec implements HttpMessageCodec { - private static final MediaType MEDIA_TYPE = new MediaType("application", "grpc"); - private final ProtobufHttpMessageCodec protobufHttpMessageCodec; private final WrapperHttpMessageCodec wrapperHttpMessageCodec; @@ -99,7 +98,7 @@ private static void writeLength(OutputStream outputStream, int length) { @Override public MediaType mediaType() { - return MEDIA_TYPE; + return TripleConstant.MEDIA_TYPE_GRPC; } private static boolean isProtobuf(Object data) { diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/grpc/GrpcCompositeCodecFactory.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/grpc/GrpcCompositeCodecFactory.java index 29a7de9dfd54..8f18e8d6aa45 100644 --- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/grpc/GrpcCompositeCodecFactory.java +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/grpc/GrpcCompositeCodecFactory.java @@ -24,15 +24,14 @@ import org.apache.dubbo.remoting.http12.message.MediaType; import org.apache.dubbo.remoting.utils.UrlUtils; import org.apache.dubbo.rpc.model.FrameworkModel; +import org.apache.dubbo.rpc.protocol.tri.TripleConstant; @Activate public class GrpcCompositeCodecFactory implements HttpMessageEncoderFactory, HttpMessageDecoderFactory { - private static final MediaType MEDIA_TYPE = new MediaType("application", "grpc"); - @Override public HttpMessageCodec createCodec(URL url, FrameworkModel frameworkModel, String mediaType) { - final String serializeName = UrlUtils.serializationOrDefault(url); + String serializeName = UrlUtils.serializationOrDefault(url); WrapperHttpMessageCodec wrapperHttpMessageCodec = new WrapperHttpMessageCodec(url, frameworkModel); wrapperHttpMessageCodec.setSerializeType(serializeName); ProtobufHttpMessageCodec protobufHttpMessageCodec = new ProtobufHttpMessageCodec(); @@ -41,6 +40,6 @@ public HttpMessageCodec createCodec(URL url, FrameworkModel frameworkModel, Stri @Override public MediaType mediaType() { - return MEDIA_TYPE; + return TripleConstant.MEDIA_TYPE_GRPC; } } diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/grpc/GrpcHandlerMapping.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/grpc/GrpcHandlerMapping.java new file mode 100644 index 000000000000..ebb7b0b1d50f --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/grpc/GrpcHandlerMapping.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * http://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.apache.dubbo.rpc.protocol.tri.h12.grpc; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.extension.Activate; +import org.apache.dubbo.remoting.http12.HttpRequest; +import org.apache.dubbo.remoting.http12.message.HttpMessageCodec; +import org.apache.dubbo.rpc.model.FrameworkModel; +import org.apache.dubbo.rpc.protocol.tri.TripleConstant; +import org.apache.dubbo.rpc.protocol.tri.h12.HttpHandlerMapping; +import org.apache.dubbo.rpc.protocol.tri.route.HandlerInfo; + +@Activate(order = -3000) +public final class GrpcHandlerMapping extends HttpHandlerMapping { + + public static final GrpcCompositeCodecFactory CODEC_FACTORY = new GrpcCompositeCodecFactory(); + + public GrpcHandlerMapping(FrameworkModel frameworkModel) { + super(frameworkModel); + } + + @Override + protected boolean supportContentType(String contentType) { + return contentType.startsWith(TripleConstant.MEDIA_TYPE_GRPC.getName()); + } + + @Override + protected void determineHttpMessageCodec(HandlerInfo info, URL url, HttpRequest request) { + HttpMessageCodec codec = CODEC_FACTORY.createCodec(url, getFrameworkModel(), request.contentType()); + info.setHttpMessageDecoder(codec); + info.setHttpMessageEncoder(codec); + } + + @Override + public String getType() { + return TripleConstant.TRIPLE_HANDLER_TYPE_GRPC; + } +} diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/grpc/GrpcHttp2ServerTransportListener.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/grpc/GrpcHttp2ServerTransportListener.java index de216cee5621..5885fff928a3 100644 --- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/grpc/GrpcHttp2ServerTransportListener.java +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/grpc/GrpcHttp2ServerTransportListener.java @@ -17,8 +17,11 @@ package org.apache.dubbo.rpc.protocol.tri.h12.grpc; import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.constants.CommonConstants; +import org.apache.dubbo.common.io.StreamUtils; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; +import org.apache.dubbo.common.utils.TimeUtils; import org.apache.dubbo.remoting.http12.HttpHeaders; import org.apache.dubbo.remoting.http12.exception.DecodeException; import org.apache.dubbo.remoting.http12.exception.UnimplementedException; @@ -27,41 +30,31 @@ import org.apache.dubbo.remoting.http12.h2.Http2TransportListener; import org.apache.dubbo.remoting.http12.message.MethodMetadata; import org.apache.dubbo.remoting.http12.message.StreamingDecoder; -import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcInvocation; import org.apache.dubbo.rpc.TriRpcStatus; import org.apache.dubbo.rpc.model.FrameworkModel; -import org.apache.dubbo.rpc.model.MethodDescriptor; -import org.apache.dubbo.rpc.model.ServiceDescriptor; -import org.apache.dubbo.rpc.protocol.tri.TripleCustomerProtocolWapper; -import org.apache.dubbo.rpc.protocol.tri.call.AbstractServerCall; +import org.apache.dubbo.rpc.protocol.tri.DescriptorUtils; import org.apache.dubbo.rpc.protocol.tri.compressor.DeCompressor; import org.apache.dubbo.rpc.protocol.tri.compressor.Identity; import org.apache.dubbo.rpc.protocol.tri.h12.HttpMessageListener; import org.apache.dubbo.rpc.protocol.tri.h12.http2.GenericHttp2ServerTransportListener; +import org.apache.dubbo.rpc.protocol.tri.route.RpcInvocationBuildContext; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; -import java.io.OutputStream; -import java.util.Arrays; -import java.util.List; -import java.util.Objects; import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_FAILED_PARSE; public class GrpcHttp2ServerTransportListener extends GenericHttp2ServerTransportListener implements Http2TransportListener { - private static final ErrorTypeAwareLogger LOGGER = LoggerFactory.getErrorTypeAwareLogger(AbstractServerCall.class); + private static final ErrorTypeAwareLogger LOGGER = + LoggerFactory.getErrorTypeAwareLogger(GrpcHttp2ServerTransportListener.class); public GrpcHttp2ServerTransportListener(H2StreamChannel h2StreamChannel, URL url, FrameworkModel frameworkModel) { super(h2StreamChannel, url, frameworkModel); - initialize(); - } - - private void initialize() { getServerChannelObserver().setTrailersCustomizer(this::grpcTrailersCustomize); } @@ -78,44 +71,14 @@ private static String httpStatusToGrpcStatus(Throwable throwable) { return String.valueOf(TriRpcStatus.INTERNAL.code.code); } - @Override - protected RpcInvocation buildRpcInvocation( - Invoker invoker, ServiceDescriptor serviceDescriptor, MethodDescriptor methodDescriptor) { - RpcInvocation rpcInvocation = super.buildRpcInvocation(invoker, serviceDescriptor, methodDescriptor); - HttpHeaders headers = getHttpMetadata().headers(); - String timeoutString = headers.getFirst(GrpcHeaderNames.GRPC_TIMEOUT.getName()); - try { - if (Objects.nonNull(timeoutString)) { - Long timeout = GrpcUtils.parseTimeoutToMills(timeoutString); - rpcInvocation.put("timeout", timeout); - } - } catch (Throwable t) { - LOGGER.warn( - PROTOCOL_FAILED_PARSE, - "", - "", - String.format( - "Failed to parse request timeout set from:%s, service=%s " + "method=%s", - timeoutString, - serviceDescriptor.getInterfaceName(), - getMethodDescriptor().getMethodName())); - } - return rpcInvocation; - } - @Override protected StreamingDecoder newStreamingDecoder() { return new GrpcStreamingDecoder(); } @Override - protected HttpMessageListener newHttpMessageListener() { - Http2Header httpMetadata = getHttpMetadata(); - boolean hasStub = getPathResolver().hasNativeStub(httpMetadata.path()); - if (hasStub) { - return GrpcHttp2ServerTransportListener.super.newHttpMessageListener(); - } - return new LazyFindMethodListener(); + protected HttpMessageListener buildHttpMessageListener() { + return getContext().isHasStub() ? super.buildHttpMessageListener() : new LazyFindMethodListener(); } @Override @@ -138,6 +101,28 @@ private void processGrpcHeaders(Http2Header metadata) { } } + @Override + protected RpcInvocation onBuildRpcInvocationCompletion(RpcInvocation invocation) { + String timeoutString = getHttpMetadata().headers().getFirst(GrpcHeaderNames.GRPC_TIMEOUT.getName()); + try { + if (null != timeoutString) { + Long timeout = TimeUtils.parseTimeoutToMills(timeoutString); + invocation.put(CommonConstants.TIMEOUT_KEY, timeout); + } + } catch (Throwable t) { + LOGGER.warn( + PROTOCOL_FAILED_PARSE, + "", + "", + String.format( + "Failed to parse request timeout set from:%s, service=%s " + "method=%s", + timeoutString, + getContext().getServiceDescriptor().getInterfaceName(), + getContext().getMethodName())); + } + return invocation; + } + @Override protected GrpcStreamingDecoder getStreamingDecoder() { return (GrpcStreamingDecoder) super.getStreamingDecoder(); @@ -148,9 +133,9 @@ private class LazyFindMethodListener implements HttpMessageListener { private final StreamingDecoder streamingDecoder; private LazyFindMethodListener() { - this.streamingDecoder = new GrpcStreamingDecoder(); - this.streamingDecoder.setFragmentListener(new DetermineMethodDescriptorListener()); - this.streamingDecoder.request(Integer.MAX_VALUE); + streamingDecoder = new GrpcStreamingDecoder(); + streamingDecoder.setFragmentListener(new DetermineMethodDescriptorListener()); + streamingDecoder.request(Integer.MAX_VALUE); } @Override @@ -172,66 +157,31 @@ public void onClose() { @Override public void onFragmentMessage(InputStream dataHeader, InputStream rawMessage) { try { - ByteArrayOutputStream merge = + ByteArrayOutputStream merged = new ByteArrayOutputStream(dataHeader.available() + rawMessage.available()); - transferToOutputStream(merge, dataHeader); - ByteArrayOutputStream bos = new ByteArrayOutputStream(rawMessage.available()); - transferToOutputStream(bos, rawMessage); - byte[] data = bos.toByteArray(); - MethodDescriptor methodDescriptor = getMethodDescriptor(); - if (methodDescriptor == null) { - Http2Header httpMetadata = getHttpMetadata(); - String path = httpMetadata.path(); - String[] parts = path.split("/"); - String originalMethodName = parts[2]; - methodDescriptor = findReflectionMethodDescriptor(getServiceDescriptor(), originalMethodName); - if (methodDescriptor == null) { - List methodDescriptors = - getServiceDescriptor().getMethods(originalMethodName); - final TripleCustomerProtocolWapper.TripleRequestWrapper request; - request = TripleCustomerProtocolWapper.TripleRequestWrapper.parseFrom(data); - final String[] paramTypes = request.getArgTypes() - .toArray(new String[request.getArgs().size()]); - // wrapper mode the method can overload so maybe list - for (MethodDescriptor descriptor : methodDescriptors) { - // params type is array - if (Arrays.equals(descriptor.getCompatibleParamSignatures(), paramTypes)) { - methodDescriptor = descriptor; - break; - } - } - if (methodDescriptor == null) { - throw new UnimplementedException("method:" + originalMethodName); - } - } - setMethodDescriptor(methodDescriptor); - setMethodMetadata(MethodMetadata.fromMethodDescriptor(methodDescriptor)); - setRpcInvocation(buildRpcInvocation(getInvoker(), getServiceDescriptor(), methodDescriptor)); + StreamUtils.copy(dataHeader, merged); + byte[] data = StreamUtils.readBytes(rawMessage); + + RpcInvocationBuildContext context = getContext(); + if (null == context.getMethodDescriptor()) { + context.setMethodDescriptor(DescriptorUtils.findTripleMethodDescriptor( + context.getServiceDescriptor(), context.getMethodName(), data)); + + setHttpMessageListener(GrpcHttp2ServerTransportListener.super.buildHttpMessageListener()); + // replace decoder - HttpMessageListener httpMessageListener = - GrpcHttp2ServerTransportListener.super.newHttpMessageListener(); - GrpcCompositeCodec grpcCompositeCodec = (GrpcCompositeCodec) getHttpMessageDecoder(); - grpcCompositeCodec.setDecodeTypes(getMethodMetadata().getActualRequestTypes()); - grpcCompositeCodec.setEncodeTypes( - new Class[] {getMethodMetadata().getActualResponseType()}); - GrpcHttp2ServerTransportListener.super - .getServerChannelObserver() - .setResponseEncoder(grpcCompositeCodec); - setHttpMessageListener(httpMessageListener); + GrpcCompositeCodec grpcCompositeCodec = (GrpcCompositeCodec) context.getHttpMessageDecoder(); + MethodMetadata methodMetadata = context.getMethodMetadata(); + grpcCompositeCodec.setDecodeTypes(methodMetadata.getActualRequestTypes()); + grpcCompositeCodec.setEncodeTypes(new Class[] {methodMetadata.getActualResponseType()}); + getServerChannelObserver().setResponseEncoder(grpcCompositeCodec); } - transferToOutputStream(merge, new ByteArrayInputStream(data)); - getHttpMessageListener().onMessage(new ByteArrayInputStream(merge.toByteArray())); + + merged.write(data); + getHttpMessageListener().onMessage(new ByteArrayInputStream(merged.toByteArray())); } catch (IOException e) { throw new DecodeException(e); } } - - private void transferToOutputStream(OutputStream out, InputStream inputStream) throws IOException { - byte[] bytes = new byte[1024]; - int len; - while ((len = inputStream.read(bytes)) != -1) { - out.write(bytes, 0, len); - } - } } } 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 deleted file mode 100644 index 889b387071a8..000000000000 --- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/grpc/GrpcUtils.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 - * - * http://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.apache.dubbo.rpc.protocol.tri.h12.grpc; - -import org.apache.dubbo.common.utils.StringUtils; - -import java.util.concurrent.TimeUnit; - -public class GrpcUtils { - - private GrpcUtils() {} - - public static Long parseTimeoutToMills(String timeoutVal) { - if (StringUtils.isEmpty(timeoutVal) || StringUtils.isContains(timeoutVal, "null")) { - return null; - } - long value = Long.parseLong(timeoutVal.substring(0, timeoutVal.length() - 1)); - char unit = timeoutVal.charAt(timeoutVal.length() - 1); - switch (unit) { - case 'n': - return TimeUnit.NANOSECONDS.toMillis(value); - case 'u': - return TimeUnit.MICROSECONDS.toMillis(value); - case 'm': - return value; - case 'S': - return TimeUnit.SECONDS.toMillis(value); - case 'M': - return TimeUnit.MINUTES.toMillis(value); - case 'H': - return TimeUnit.HOURS.toMillis(value); - default: - // invalid timeout config - return null; - } - } -} diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/grpc/ProtobufHttpMessageCodec.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/grpc/ProtobufHttpMessageCodec.java index 011fd6b8a933..330a44696e85 100644 --- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/grpc/ProtobufHttpMessageCodec.java +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/grpc/ProtobufHttpMessageCodec.java @@ -21,6 +21,7 @@ import org.apache.dubbo.remoting.http12.message.HttpMessageCodec; import org.apache.dubbo.remoting.http12.message.MediaType; import org.apache.dubbo.rpc.protocol.tri.SingleProtobufUtils; +import org.apache.dubbo.rpc.protocol.tri.TripleConstant; import java.io.IOException; import java.io.InputStream; @@ -28,8 +29,6 @@ public class ProtobufHttpMessageCodec implements HttpMessageCodec { - private static final MediaType MEDIA_TYPE = new MediaType("application", "x-protobuf"); - @Override public void encode(OutputStream outputStream, Object data) throws EncodeException { try { @@ -50,6 +49,6 @@ public Object decode(InputStream inputStream, Class targetType) throws Decode @Override public MediaType mediaType() { - return MEDIA_TYPE; + return TripleConstant.MEDIA_TYPE_GRPC; } } diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/http1/DefaultHttp11ServerTransportListener.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/http1/DefaultHttp11ServerTransportListener.java index 389e48bac3e1..d1da3e89cd79 100644 --- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/http1/DefaultHttp11ServerTransportListener.java +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/http1/DefaultHttp11ServerTransportListener.java @@ -26,10 +26,7 @@ import org.apache.dubbo.remoting.http12.h1.Http1ServerStreamChannelObserver; import org.apache.dubbo.remoting.http12.h1.Http1ServerTransportListener; import org.apache.dubbo.remoting.http12.message.DefaultListeningDecoder; -import org.apache.dubbo.remoting.http12.message.HttpMessageDecoder; -import org.apache.dubbo.remoting.http12.message.ListeningDecoder; import org.apache.dubbo.remoting.http12.message.MediaType; -import org.apache.dubbo.remoting.http12.message.MethodMetadata; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.RpcInvocation; import org.apache.dubbo.rpc.model.FrameworkModel; @@ -40,67 +37,59 @@ import org.apache.dubbo.rpc.protocol.tri.h12.ServerCallListener; import org.apache.dubbo.rpc.protocol.tri.h12.ServerStreamServerCallListener; import org.apache.dubbo.rpc.protocol.tri.h12.UnaryServerCallListener; +import org.apache.dubbo.rpc.protocol.tri.route.RpcInvocationBuildContext; public class DefaultHttp11ServerTransportListener extends AbstractServerTransportListener implements Http1ServerTransportListener { private final HttpChannel httpChannel; - - private final URL url; + private Http1ServerChannelObserver serverChannelObserver; public DefaultHttp11ServerTransportListener(HttpChannel httpChannel, URL url, FrameworkModel frameworkModel) { super(frameworkModel, url, httpChannel); - this.url = url; this.httpChannel = httpChannel; } + @Override + protected HttpMessageListener buildHttpMessageListener() { + RpcInvocationBuildContext context = getContext(); + RpcInvocation rpcInvocation = buildRpcInvocation(context); + + ServerCallListener serverCallListener = + startListener(rpcInvocation, context.getMethodDescriptor(), context.getInvoker()); + + DefaultListeningDecoder listeningDecoder = new DefaultListeningDecoder( + context.getHttpMessageDecoder(), context.getMethodMetadata().getActualRequestTypes()); + listeningDecoder.setListener(serverCallListener::onMessage); + return new DefaultHttpMessageListener(listeningDecoder); + } + private ServerCallListener startListener( RpcInvocation invocation, MethodDescriptor methodDescriptor, Invoker invoker) { switch (methodDescriptor.getRpcType()) { case UNARY: - Http1ServerChannelObserver http1ChannelObserver = new Http1ServerChannelObserver(httpChannel); - http1ChannelObserver.setResponseEncoder(getCodecUtils() - .determineHttpMessageEncoder( - getFrameworkModel(), getHttpMetadata().headers(), getUrl())); - return new AutoCompleteUnaryServerCallListener(invocation, invoker, http1ChannelObserver); + serverChannelObserver = new Http1ServerChannelObserver(httpChannel); + return new AutoCompleteUnaryServerCallListener(invocation, invoker, serverChannelObserver); case SERVER_STREAM: - Http1ServerChannelObserver serverStreamChannelObserver = - new Http1ServerStreamChannelObserver(httpChannel); - serverStreamChannelObserver.setResponseEncoder(getCodecUtils() - .determineHttpMessageEncoder( - getFrameworkModel(), getHttpMetadata().headers(), getUrl())); - serverStreamChannelObserver.setHeadersCustomizer((headers) -> headers.set( + serverChannelObserver = new Http1ServerStreamChannelObserver(httpChannel); + serverChannelObserver.setHeadersCustomizer((headers) -> headers.set( HttpHeaderNames.CONTENT_TYPE.getName(), MediaType.TEXT_EVENT_STREAM_VALUE.getName())); - return new AutoCompleteServerStreamServerCallListener(invocation, invoker, serverStreamChannelObserver); + return new AutoCompleteServerStreamServerCallListener(invocation, invoker, serverChannelObserver); default: throw new UnsupportedOperationException("HTTP1.x only support unary and server-stream"); } } @Override - protected HttpMessageListener newHttpMessageListener() { - RequestMetadata httpMetadata = getHttpMetadata(); - String path = httpMetadata.path(); - String[] parts = path.split("/"); - String originalMethodName = parts[2]; - boolean hasStub = getPathResolver().hasNativeStub(path); - MethodDescriptor methodDescriptor = findMethodDescriptor(getServiceDescriptor(), originalMethodName, hasStub); - MethodMetadata methodMetadata = MethodMetadata.fromMethodDescriptor(methodDescriptor); - RpcInvocation rpcInvocation = buildRpcInvocation(getInvoker(), getServiceDescriptor(), methodDescriptor); - setMethodDescriptor(methodDescriptor); - setMethodMetadata(methodMetadata); - setRpcInvocation(rpcInvocation); - ListeningDecoder listeningDecoder = - newListeningDecoder(getHttpMessageDecoder(), methodMetadata.getActualRequestTypes()); - return new DefaultHttpMessageListener(listeningDecoder); + protected void onMetadataCompletion(RequestMetadata metadata) { + serverChannelObserver.setResponseEncoder(getContext().getHttpMessageEncoder()); + super.onMetadataCompletion(metadata); } - private ListeningDecoder newListeningDecoder(HttpMessageDecoder decoder, Class[] actualRequestTypes) { - DefaultListeningDecoder defaultListeningDecoder = new DefaultListeningDecoder(decoder, actualRequestTypes); - ServerCallListener serverCallListener = startListener(getRpcInvocation(), getMethodDescriptor(), getInvoker()); - defaultListeningDecoder.setListener(serverCallListener::onMessage); - return defaultListeningDecoder; + @Override + protected void onError(Throwable throwable) { + serverChannelObserver.onError(throwable); } private static class AutoCompleteUnaryServerCallListener extends UnaryServerCallListener { @@ -113,7 +102,7 @@ public AutoCompleteUnaryServerCallListener( @Override public void onMessage(Object message) { super.onMessage(message); - super.onComplete(); + onComplete(); } } @@ -127,7 +116,7 @@ public AutoCompleteServerStreamServerCallListener( @Override public void onMessage(Object message) { super.onMessage(message); - super.onComplete(); + onComplete(); } } } diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/http2/GenericHttp2ServerTransportListener.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/http2/GenericHttp2ServerTransportListener.java index a859ae48ae27..8669cc4e97d9 100644 --- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/http2/GenericHttp2ServerTransportListener.java +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/http2/GenericHttp2ServerTransportListener.java @@ -19,7 +19,6 @@ import org.apache.dubbo.common.URL; import org.apache.dubbo.common.threadpool.manager.ExecutorRepository; import org.apache.dubbo.common.threadpool.serial.SerializingExecutor; -import org.apache.dubbo.remoting.http12.RequestMetadata; import org.apache.dubbo.remoting.http12.exception.HttpStatusException; import org.apache.dubbo.remoting.http12.h2.H2StreamChannel; import org.apache.dubbo.remoting.http12.h2.Http2Header; @@ -46,31 +45,32 @@ import org.apache.dubbo.rpc.protocol.tri.h12.ServerStreamServerCallListener; import org.apache.dubbo.rpc.protocol.tri.h12.UnaryServerCallListener; import org.apache.dubbo.rpc.protocol.tri.h12.grpc.StreamingHttpMessageListener; +import org.apache.dubbo.rpc.protocol.tri.route.RpcInvocationBuildContext; import java.util.concurrent.Executor; public class GenericHttp2ServerTransportListener extends AbstractServerTransportListener implements Http2TransportListener { - private final Http2ServerChannelObserver serverChannelObserver; - - private final H2StreamChannel h2StreamChannel; - private final ExecutorSupport executorSupport; - private final StreamingDecoder streamingDecoder; + private final Http2ServerChannelObserver serverChannelObserver; private ServerCallListener serverCallListener; public GenericHttp2ServerTransportListener( H2StreamChannel h2StreamChannel, URL url, FrameworkModel frameworkModel) { super(frameworkModel, url, h2StreamChannel); - this.h2StreamChannel = h2StreamChannel; - this.executorSupport = ExecutorRepository.getInstance(url.getOrDefaultApplicationModel()) + executorSupport = ExecutorRepository.getInstance(url.getOrDefaultApplicationModel()) .getExecutorSupport(url); - this.streamingDecoder = newStreamingDecoder(); - this.serverChannelObserver = new Http2ServerCallToObserverAdapter(frameworkModel, h2StreamChannel); - this.serverChannelObserver.setStreamingDecoder(streamingDecoder); + streamingDecoder = newStreamingDecoder(); + serverChannelObserver = new Http2ServerCallToObserverAdapter(frameworkModel, h2StreamChannel); + serverChannelObserver.setStreamingDecoder(streamingDecoder); + } + + protected StreamingDecoder newStreamingDecoder() { + // default no op + return new NoOpStreamingDecoder(); } @Override @@ -79,6 +79,28 @@ protected Executor initializeExecutor(Http2Header metadata) { return new SerializingExecutor(executor); } + protected void doOnMetadata(Http2Header metadata) { + if (metadata.isEndStream()) { + return; + } + super.doOnMetadata(metadata); + } + + @Override + protected HttpMessageListener buildHttpMessageListener() { + RpcInvocationBuildContext context = getContext(); + RpcInvocation rpcInvocation = buildRpcInvocation(context); + + serverCallListener = startListener(rpcInvocation, context.getMethodDescriptor(), context.getInvoker()); + + DefaultListeningDecoder listeningDecoder = new DefaultListeningDecoder( + context.getHttpMessageDecoder(), context.getMethodMetadata().getActualRequestTypes()); + listeningDecoder.setListener(new Http2StreamingDecodeListener(serverCallListener)); + streamingDecoder.setFragmentListener(new StreamingDecoder.DefaultFragmentListener(listeningDecoder)); + getServerChannelObserver().setStreamingDecoder(streamingDecoder); + return new StreamingHttpMessageListener(streamingDecoder); + } + private ServerCallListener startListener( RpcInvocation invocation, MethodDescriptor methodDescriptor, Invoker invoker) { Http2ServerChannelObserver responseObserver = getServerChannelObserver(); @@ -86,14 +108,13 @@ private ServerCallListener startListener( responseObserver.setCancellationContext(cancellationContext); switch (methodDescriptor.getRpcType()) { case UNARY: - Http2Header httpMetadata = getHttpMetadata(); - boolean hasStub = getPathResolver().hasNativeStub(httpMetadata.path()); boolean applyCustomizeException = false; - if (!hasStub) { + if (!getContext().isHasStub()) { + MethodMetadata methodMetadata = getContext().getMethodMetadata(); applyCustomizeException = ReflectionPackableMethod.needWrap( methodDescriptor, - getMethodMetadata().getActualRequestTypes(), - getMethodMetadata().getActualResponseType()); + methodMetadata.getActualRequestTypes(), + methodMetadata.getActualResponseType()); } UnaryServerCallListener unaryServerCallListener = startUnary(invocation, invoker, responseObserver); unaryServerCallListener.setApplyCustomizeException(applyCustomizeException); @@ -108,63 +129,25 @@ private ServerCallListener startListener( } } - public Http2ServerChannelObserver getServerChannelObserver() { - return serverChannelObserver; - } - - @Override - public void cancelByRemote(long errorCode) { - this.serverChannelObserver.cancel(new HttpStatusException((int) errorCode)); - this.serverCallListener.onCancel(errorCode); - } - - protected StreamingDecoder newStreamingDecoder() { - // default no op - return new NoOpStreamingDecoder(); + private UnaryServerCallListener startUnary( + RpcInvocation invocation, Invoker invoker, Http2ServerChannelObserver responseObserver) { + return new UnaryServerCallListener(invocation, invoker, responseObserver); } - protected void doOnMetadata(Http2Header metadata) { - if (metadata.isEndStream()) { - return; - } - super.doOnMetadata(metadata); + private ServerStreamServerCallListener startServerStreaming( + RpcInvocation invocation, Invoker invoker, Http2ServerChannelObserver responseObserver) { + return new ServerStreamServerCallListener(invocation, invoker, responseObserver); } - @Override - protected HttpMessageListener newHttpMessageListener() { - RequestMetadata httpMetadata = getHttpMetadata(); - String path = httpMetadata.path(); - String[] parts = path.split("/"); - String originalMethodName = parts[2]; - MethodDescriptor methodDescriptor = getMethodDescriptor(); - if (methodDescriptor == null) { - methodDescriptor = findMethodDescriptor(getServiceDescriptor(), originalMethodName, isHasStub()); - setMethodDescriptor(methodDescriptor); - } - MethodMetadata methodMetadata = getMethodMetadata(); - if (methodMetadata == null) { - methodMetadata = MethodMetadata.fromMethodDescriptor(getMethodDescriptor()); - setMethodMetadata(methodMetadata); - } - RpcInvocation rpcInvocation = getRpcInvocation(); - if (rpcInvocation == null) { - setRpcInvocation(buildRpcInvocation(getInvoker(), getServiceDescriptor(), methodDescriptor)); - } - initializeServerCallListener(); - DefaultListeningDecoder defaultListeningDecoder = new DefaultListeningDecoder( - getHttpMessageDecoder(), getMethodMetadata().getActualRequestTypes()); - defaultListeningDecoder.setListener(new Http2StreamingDecodeListener(serverCallListener)); - streamingDecoder.setFragmentListener(new StreamingDecoder.DefaultFragmentListener(defaultListeningDecoder)); - getServerChannelObserver().setStreamingDecoder(streamingDecoder); - return new StreamingHttpMessageListener(streamingDecoder); + private BiStreamServerCallListener startBiStreaming( + RpcInvocation invocation, Invoker invoker, Http2ServerChannelObserver responseObserver) { + return new BiStreamServerCallListener(invocation, invoker, responseObserver); } @Override protected void onMetadataCompletion(Http2Header metadata) { - super.onMetadataCompletion(metadata); - this.serverChannelObserver.setResponseEncoder( - getCodecUtils().determineHttpMessageEncoder(getFrameworkModel(), metadata.headers(), getUrl())); - this.serverChannelObserver.request(1); + serverChannelObserver.setResponseEncoder(getContext().getHttpMessageEncoder()); + serverChannelObserver.request(1); } @Override @@ -179,8 +162,18 @@ protected void onError(Throwable throwable) { serverChannelObserver.onError(throwable); } + @Override + public void cancelByRemote(long errorCode) { + serverChannelObserver.cancel(new HttpStatusException((int) errorCode)); + serverCallListener.onCancel(errorCode); + } + protected StreamingDecoder getStreamingDecoder() { - return this.streamingDecoder; + return streamingDecoder; + } + + protected final Http2ServerChannelObserver getServerChannelObserver() { + return serverChannelObserver; } private static class Http2StreamingDecodeListener implements ListeningDecoder.Listener { @@ -193,37 +186,12 @@ private Http2StreamingDecodeListener(ServerCallListener serverCallListener) { @Override public void onMessage(Object message) { - this.serverCallListener.onMessage(message); + serverCallListener.onMessage(message); } @Override public void onClose() { - this.serverCallListener.onComplete(); - } - } - - private void initializeServerCallListener() { - if (serverCallListener == null) { - this.serverCallListener = startListener(getRpcInvocation(), getMethodDescriptor(), getInvoker()); + serverCallListener.onComplete(); } } - - private UnaryServerCallListener startUnary( - RpcInvocation invocation, Invoker invoker, Http2ServerChannelObserver responseObserver) { - return new UnaryServerCallListener(invocation, invoker, responseObserver); - } - - private ServerStreamServerCallListener startServerStreaming( - RpcInvocation invocation, Invoker invoker, Http2ServerChannelObserver responseObserver) { - return new ServerStreamServerCallListener(invocation, invoker, responseObserver); - } - - private BiStreamServerCallListener startBiStreaming( - RpcInvocation invocation, Invoker invoker, Http2ServerChannelObserver responseObserver) { - return new BiStreamServerCallListener(invocation, invoker, responseObserver); - } - - protected ServerCallListener getServerCallListener() { - return serverCallListener; - } } diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/http2/Http2ServerStreamObserver.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/http2/Http2ServerStreamObserver.java index 7a0f96c282dd..58fa91996e46 100644 --- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/http2/Http2ServerStreamObserver.java +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/http2/Http2ServerStreamObserver.java @@ -63,7 +63,7 @@ public Map getResponseAttachments() { protected HttpMetadata encodeTrailers(Throwable throwable) { HttpMetadata httpMetadata = super.encodeTrailers(throwable); HttpHeaders headers = httpMetadata.headers(); - StreamUtils.convertAttachment(headers, attachments, TripleProtocol.CONVERT_NO_LOWER_HEADER); + StreamUtils.putHeaders(headers, attachments, TripleProtocol.CONVERT_NO_LOWER_HEADER); return httpMetadata; } } diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/observer/ServerCallToObserverAdapter.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/observer/ServerCallToObserverAdapter.java deleted file mode 100644 index f0d84f1939c2..000000000000 --- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/observer/ServerCallToObserverAdapter.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 - * - * http://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.apache.dubbo.rpc.protocol.tri.observer; - -import org.apache.dubbo.common.constants.CommonConstants; -import org.apache.dubbo.common.logger.Logger; -import org.apache.dubbo.common.logger.LoggerFactory; -import org.apache.dubbo.rpc.CancellationContext; -import org.apache.dubbo.rpc.TriRpcStatus; -import org.apache.dubbo.rpc.protocol.tri.CancelableStreamObserver; -import org.apache.dubbo.rpc.protocol.tri.ServerStreamObserver; -import org.apache.dubbo.rpc.protocol.tri.call.AbstractServerCall; - -import java.util.Map; - -public class ServerCallToObserverAdapter extends CancelableStreamObserver implements ServerStreamObserver { - - private static final Logger LOGGER = LoggerFactory.getLogger(CancelableStreamObserver.class); - public final CancellationContext cancellationContext; - private final AbstractServerCall call; - private Map attachments; - private boolean terminated = false; - - private boolean isNeedReturnException = false; - - private Integer exceptionCode = CommonConstants.TRI_EXCEPTION_CODE_NOT_EXISTS; - - public Integer getExceptionCode() { - return exceptionCode; - } - - public void setExceptionCode(Integer exceptionCode) { - this.exceptionCode = exceptionCode; - } - - public boolean isNeedReturnException() { - return isNeedReturnException; - } - - public void setNeedReturnException(boolean needReturnException) { - isNeedReturnException = needReturnException; - } - - public ServerCallToObserverAdapter(AbstractServerCall call, CancellationContext cancellationContext) { - this.call = call; - this.cancellationContext = cancellationContext; - } - - public boolean isAutoRequestN() { - return call.isAutoRequestN(); - } - - public boolean isTerminated() { - return terminated; - } - - private void setTerminated() { - this.terminated = true; - } - - @Override - public void onNext(Object data) { - if (isTerminated()) { - throw new IllegalStateException("Stream observer has been terminated, no more data is allowed"); - } - call.setExceptionCode(exceptionCode); - call.setNeedReturnException(isNeedReturnException); - call.sendMessage(data); - } - - @Override - public void onError(Throwable throwable) { - final TriRpcStatus status = TriRpcStatus.getStatus(throwable); - onCompleted(status); - } - - public void onCompleted(TriRpcStatus status) { - if (isTerminated()) { - return; - } - call.setExceptionCode(exceptionCode); - call.setNeedReturnException(isNeedReturnException); - call.close(status, attachments); - setTerminated(); - } - - @Override - public void onCompleted() { - onCompleted(TriRpcStatus.OK); - } - - public void setResponseAttachments(Map attachments) { - this.attachments = attachments; - } - - @Override - public void setCompression(String compression) { - call.setCompression(compression); - } - - public void cancel(Throwable throwable) { - if (terminated) { - return; - } - setTerminated(); - call.cancelByLocal(throwable); - } - - public boolean isTimeout(long cost) { - return call.timeout != null && call.timeout < cost; - } - - @Override - public void disableAutoFlowControl() { - call.disableAutoRequestN(); - } - - @Override - public void request(int count) { - call.request(count); - } -} diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/ArgumentConverter.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/ArgumentConverter.java new file mode 100644 index 000000000000..3f8cfe7cce41 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/ArgumentConverter.java @@ -0,0 +1,24 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * http://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.apache.dubbo.rpc.protocol.tri.rest; + +import org.apache.dubbo.rpc.protocol.tri.rest.meta.ParameterDescriptor; + +public interface ArgumentConverter { + + T convert(Object value, Class targetType, ParameterDescriptor parameter); +} diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/ArgumentResolver.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/ArgumentResolver.java new file mode 100644 index 000000000000..13a18d4e0a17 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/ArgumentResolver.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * http://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.apache.dubbo.rpc.protocol.tri.rest; + +import org.apache.dubbo.remoting.http12.HttpRequest; +import org.apache.dubbo.remoting.http12.HttpResponse; +import org.apache.dubbo.rpc.protocol.tri.rest.meta.ParameterDescriptor; + +public interface ArgumentResolver { + + boolean support(ParameterDescriptor parameter); + + Object resolve(ParameterDescriptor parameter, HttpRequest request, HttpResponse response); +} diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/RestHttpMessageCodec.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/RestHttpMessageCodec.java new file mode 100644 index 000000000000..c79c4d73a93a --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/RestHttpMessageCodec.java @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * http://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.apache.dubbo.rpc.protocol.tri.rest; + +import org.apache.dubbo.remoting.http12.HttpRequest; +import org.apache.dubbo.remoting.http12.HttpResponse; +import org.apache.dubbo.remoting.http12.exception.DecodeException; +import org.apache.dubbo.remoting.http12.exception.EncodeException; +import org.apache.dubbo.remoting.http12.message.HttpMessageCodec; +import org.apache.dubbo.remoting.http12.message.HttpMessageDecoder; +import org.apache.dubbo.remoting.http12.message.HttpMessageEncoder; +import org.apache.dubbo.remoting.http12.message.MediaType; +import org.apache.dubbo.rpc.protocol.tri.rest.meta.ParameterDescriptor; + +import java.io.InputStream; +import java.io.OutputStream; + +public class RestHttpMessageCodec implements HttpMessageCodec { + + private final HttpRequest request; + private final HttpResponse response; + private final ParameterDescriptor[] parameters; + private final HttpMessageDecoder httpMessageDecoder; + private final HttpMessageEncoder httpMessageEncoder; + private final ArgumentResolver argumentResolver; + private final ArgumentConverter argumentConverter; + + public RestHttpMessageCodec( + HttpRequest request, + HttpResponse response, + ParameterDescriptor[] parameters, + HttpMessageDecoder httpMessageDecoder, + HttpMessageEncoder httpMessageEncoder, + ArgumentResolver argumentResolver, + ArgumentConverter argumentConverter) { + this.request = request; + this.response = response; + this.parameters = parameters; + this.httpMessageDecoder = httpMessageDecoder; + this.httpMessageEncoder = httpMessageEncoder; + this.argumentResolver = argumentResolver; + this.argumentConverter = argumentConverter; + } + + @Override + public Object decode(InputStream inputStream, Class targetType) throws DecodeException { + return decode(inputStream, new Class[] {targetType}); + } + + @Override + public Object[] decode(InputStream inputStream, Class[] targetTypes) throws DecodeException { + int len = parameters.length; + Object[] args = new Object[len]; + for (int i = 0; i < len; i++) { + ParameterDescriptor parameter = parameters[i]; + Object arg = argumentResolver.resolve(parameter, request, response); + args[i] = argumentConverter.convert(arg, parameter.getType(), parameter); + } + return args; + } + + @Override + public MediaType mediaType() { + return MediaType.ALL_VALUE; + } + + @Override + public void encode(OutputStream outputStream, Object data) throws EncodeException { + httpMessageEncoder.encode(outputStream, data); + } +} diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/RestInvokeFilter.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/RestInvokeFilter.java new file mode 100644 index 000000000000..44d696d400e6 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/RestInvokeFilter.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * http://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.apache.dubbo.rpc.protocol.tri.rest; + +import org.apache.dubbo.common.constants.CommonConstants; +import org.apache.dubbo.common.extension.Activate; +import org.apache.dubbo.rpc.BaseFilter; +import org.apache.dubbo.rpc.Filter; +import org.apache.dubbo.rpc.Invocation; +import org.apache.dubbo.rpc.Invoker; +import org.apache.dubbo.rpc.Result; +import org.apache.dubbo.rpc.RpcException; + +@Activate(group = CommonConstants.PROVIDER, order = Integer.MIN_VALUE + 200) +public class RestInvokeFilter implements Filter, BaseFilter.Listener { + + @Override + public Result invoke(Invoker invoker, Invocation invocation) throws RpcException { + return null; + } + + @Override + public void onResponse(Result appResponse, Invoker invoker, Invocation invocation) {} + + @Override + public void onError(Throwable t, Invoker invoker, Invocation invocation) {} +} diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/DefaultRequestMappingRegistry.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/DefaultRequestMappingRegistry.java new file mode 100644 index 000000000000..2f48995d6751 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/DefaultRequestMappingRegistry.java @@ -0,0 +1,100 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * http://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.apache.dubbo.rpc.protocol.tri.rest.mapping; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.extension.Activate; +import org.apache.dubbo.remoting.http12.HttpRequest; +import org.apache.dubbo.remoting.http12.HttpResponse; +import org.apache.dubbo.rpc.Invoker; +import org.apache.dubbo.rpc.protocol.tri.TripleConstant; +import org.apache.dubbo.rpc.protocol.tri.rest.meta.MappingDescriptor; +import org.apache.dubbo.rpc.protocol.tri.route.HandlerInfo; +import org.apache.dubbo.rpc.protocol.tri.route.HandlerMapping; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +@Activate(order = -1000) +public class DefaultRequestMappingRegistry implements RequestMappingRegistry, HandlerMapping { + + private final Map> directMappings = new HashMap<>(); + private final RadixTree treeMappings = new RadixTree<>(); + private final ReadWriteLock lock = new ReentrantReadWriteLock(); + private final Lock readLook = lock.readLock(); + + @Override + public void register(Invoker invoker) {} + + @Override + public void unregister(Invoker invoker) { + Class serviceClass = + invoker.getUrl().getServiceModel().getProxyObject().getClass(); + } + + @Override + public void destroy() { + lock.writeLock().lock(); + try { + directMappings.clear(); + treeMappings.clear(); + } finally { + lock.writeLock().unlock(); + } + } + + @Override + public HandlerInfo getHandler(URL url, HttpRequest request, HttpResponse response) { + MappingDescriptor descriptor = lookup(request); + + if (descriptor == null) { + return null; + } + + HandlerInfo info = new HandlerInfo(); + info.setHasStub(descriptor.isHasStub()); + info.setInvoker(descriptor.getInvoker()); + info.setMethodMetadata(descriptor.getMethodMetadata()); + info.setServiceDescriptor(descriptor.getServiceDescriptor()); + info.setMethodDescriptor(descriptor.getMethodDescriptor()); + return info; + } + + protected final MappingDescriptor lookup(HttpRequest request) { + readLook.lock(); + try { + List matched = directMappings.get(request.path()); + matched = treeMappings.match(request.path()); + + if (!matched.isEmpty()) { + return matched.get(0).getDescriptor(); + } + } finally { + readLook.unlock(); + } + return null; + } + + @Override + public String getType() { + return TripleConstant.TRIPLE_HANDLER_TYPE_REST; + } +} diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/MethodWalker.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/MethodWalker.java new file mode 100644 index 000000000000..d2860065c931 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/MethodWalker.java @@ -0,0 +1,26 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * http://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.apache.dubbo.rpc.protocol.tri.rest.mapping; + +import org.springframework.asm.MethodVisitor; + +public final class MethodWalker { + + public void walk(Class clazz, MethodVisitor visitor) {} + + public interface MethodVisitor {} +} diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RadixTree.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RadixTree.java new file mode 100644 index 000000000000..6c9f0ef61681 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RadixTree.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * http://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.apache.dubbo.rpc.protocol.tri.rest.mapping; + +import java.util.List; + +public class RadixTree { + + public void clear() {} + + public List match(String path) { + return null; + } +} diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RequestMapping.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RequestMapping.java new file mode 100644 index 000000000000..3d16086bd3fa --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RequestMapping.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * http://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.apache.dubbo.rpc.protocol.tri.rest.mapping; + +import org.apache.dubbo.remoting.http12.HttpRequest; +import org.apache.dubbo.rpc.protocol.tri.rest.mapping.condition.Condition; +import org.apache.dubbo.rpc.protocol.tri.rest.meta.MappingDescriptor; + +public class RequestMapping implements Condition { + + private MappingDescriptor descriptor; + + @Override + public RequestMapping combine(RequestMapping other) { + return null; + } + + @Override + public RequestMapping match(HttpRequest request) { + return null; + } + + @Override + public int compareTo(RequestMapping other, HttpRequest request) { + return 0; + } + + public MappingDescriptor getDescriptor() { + return descriptor; + } +} diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RequestMappingRegistry.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RequestMappingRegistry.java new file mode 100644 index 000000000000..95121923cc17 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RequestMappingRegistry.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * http://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.apache.dubbo.rpc.protocol.tri.rest.mapping; + +import org.apache.dubbo.common.extension.ExtensionScope; +import org.apache.dubbo.common.extension.SPI; +import org.apache.dubbo.rpc.Invoker; +import org.apache.dubbo.rpc.protocol.tri.TripleConstant; + +/** + * RequestMappingRegistry used for registering and unregistering rest request mappings. + */ +@SPI(value = TripleConstant.TRIPLE_HANDLER_TYPE_REST, scope = ExtensionScope.FRAMEWORK) +public interface RequestMappingRegistry { + + void register(Invoker invoker); + + void unregister(Invoker invoker); + + void destroy(); +} diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/AbstractCondition.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/AbstractCondition.java new file mode 100644 index 000000000000..34a2094ac293 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/AbstractCondition.java @@ -0,0 +1,22 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * http://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.apache.dubbo.rpc.protocol.tri.rest.mapping.condition; + +import org.apache.dubbo.remoting.http12.HttpRequest; + +public abstract class AbstractCondition, R extends HttpRequest> + implements Condition {} diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/Condition.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/Condition.java new file mode 100644 index 000000000000..aa34a44be67c --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/Condition.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * http://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.apache.dubbo.rpc.protocol.tri.rest.mapping.condition; + +import org.apache.dubbo.remoting.http12.HttpRequest; + +public interface Condition { + + T combine(T other); + + T match(R request); + + int compareTo(T other, R request); +} diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/ConsumesCondition.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/ConsumesCondition.java new file mode 100644 index 000000000000..4b18337422e3 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/ConsumesCondition.java @@ -0,0 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * http://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.apache.dubbo.rpc.protocol.tri.rest.mapping.condition; + +public class ConsumesCondition {} diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/HeadersCondition.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/HeadersCondition.java new file mode 100644 index 000000000000..27512e731c96 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/HeadersCondition.java @@ -0,0 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * http://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.apache.dubbo.rpc.protocol.tri.rest.mapping.condition; + +public class HeadersCondition {} diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/MethodsCondition.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/MethodsCondition.java new file mode 100644 index 000000000000..8a988fc8d528 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/MethodsCondition.java @@ -0,0 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * http://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.apache.dubbo.rpc.protocol.tri.rest.mapping.condition; + +public class MethodsCondition {} diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/ParamsCondition.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/ParamsCondition.java new file mode 100644 index 000000000000..a327af02cf25 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/ParamsCondition.java @@ -0,0 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * http://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.apache.dubbo.rpc.protocol.tri.rest.mapping.condition; + +public class ParamsCondition {} diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/PathsCondition.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/PathsCondition.java new file mode 100644 index 000000000000..48fb2b6c7f56 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/PathsCondition.java @@ -0,0 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * http://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.apache.dubbo.rpc.protocol.tri.rest.mapping.condition; + +public class PathsCondition {} diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/ProducesCondition.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/ProducesCondition.java new file mode 100644 index 000000000000..1c4cc068f0d5 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/ProducesCondition.java @@ -0,0 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * http://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.apache.dubbo.rpc.protocol.tri.rest.mapping.condition; + +public class ProducesCondition {} diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/meta/MappingDescriptor.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/meta/MappingDescriptor.java new file mode 100644 index 000000000000..410323aa6a34 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/meta/MappingDescriptor.java @@ -0,0 +1,71 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * http://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.apache.dubbo.rpc.protocol.tri.rest.meta; + +import org.apache.dubbo.remoting.http12.message.MethodMetadata; +import org.apache.dubbo.rpc.Invoker; +import org.apache.dubbo.rpc.model.MethodDescriptor; +import org.apache.dubbo.rpc.model.ServiceDescriptor; + +public final class MappingDescriptor { + + private final Invoker invoker; + private final boolean hasStub; + private final ParameterDescriptor[] parameterDescriptors; + private final MethodMetadata methodMetadata; + private final MethodDescriptor methodDescriptor; + private final ServiceDescriptor serviceDescriptor; + + public MappingDescriptor( + Invoker invoker, + boolean hasStub, + ParameterDescriptor[] parameterDescriptors, + MethodMetadata methodMetadata, + MethodDescriptor methodDescriptor, + ServiceDescriptor serviceDescriptor) { + this.invoker = invoker; + this.hasStub = hasStub; + this.parameterDescriptors = parameterDescriptors; + this.methodMetadata = methodMetadata; + this.methodDescriptor = methodDescriptor; + this.serviceDescriptor = serviceDescriptor; + } + + public Invoker getInvoker() { + return invoker; + } + + public boolean isHasStub() { + return hasStub; + } + + public ParameterDescriptor[] getParameterDescriptors() { + return parameterDescriptors; + } + + public MethodMetadata getMethodMetadata() { + return methodMetadata; + } + + public MethodDescriptor getMethodDescriptor() { + return methodDescriptor; + } + + public ServiceDescriptor getServiceDescriptor() { + return serviceDescriptor; + } +} diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/meta/ParameterDescriptor.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/meta/ParameterDescriptor.java new file mode 100644 index 000000000000..0fbff464fd2a --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/meta/ParameterDescriptor.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * http://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.apache.dubbo.rpc.protocol.tri.rest.meta; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; + +public final class ParameterDescriptor { + + private final String name; + private final Class type; + private final Type genericType; + private final Annotation[] annotations; + + public ParameterDescriptor(String name, Class type, Type genericType, Annotation[] annotations) { + this.name = name; + this.type = type; + this.genericType = genericType; + this.annotations = annotations; + } + + public String getName() { + return name; + } + + public Class getType() { + return type; + } + + public Type getGenericType() { + return genericType; + } + + public Annotation[] getAnnotations() { + return annotations; + } +} diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/resteasy/.gitkeeper b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/resteasy/.gitkeeper new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/.gitkeeper b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/.gitkeeper new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/ServletHttpMessageAdapterFactory.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/ServletHttpMessageAdapterFactory.java new file mode 100644 index 000000000000..6282494f2464 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/ServletHttpMessageAdapterFactory.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * http://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.apache.dubbo.rpc.protocol.tri.rest.support.servlet; + +import org.apache.dubbo.common.extension.Activate; +import org.apache.dubbo.remoting.http12.HttpChannel; +import org.apache.dubbo.remoting.http12.HttpMetadata; +import org.apache.dubbo.remoting.http12.HttpResponse; +import org.apache.dubbo.remoting.http12.message.HttpMessageAdapterFactory; + +@Activate(order = -100, onClass = "javax.servlet.http.HttpServletRequest") +public final class ServletHttpMessageAdapterFactory + implements HttpMessageAdapterFactory { + + @Override + public ServletHttpRequestAdaptee adapterRequest(HttpMetadata rawRequest, HttpChannel channel) { + return new ServletHttpRequestAdaptee(rawRequest, channel); + } + + @Override + public HttpResponse adapterResponse(ServletHttpRequestAdaptee request, HttpMetadata rawRequest, Void rawResponse) { + return new ServletHttpResponseAdaptee(); + } +} diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/ServletHttpRequestAdaptee.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/ServletHttpRequestAdaptee.java new file mode 100644 index 000000000000..e6cde73a88e8 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/ServletHttpRequestAdaptee.java @@ -0,0 +1,385 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * http://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.apache.dubbo.rpc.protocol.tri.rest.support.servlet; + +import org.apache.dubbo.remoting.http12.HttpChannel; +import org.apache.dubbo.remoting.http12.HttpMetadata; +import org.apache.dubbo.remoting.http12.message.DefaultHttpRequest; + +import javax.servlet.AsyncContext; +import javax.servlet.DispatcherType; +import javax.servlet.RequestDispatcher; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.ServletInputStream; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import javax.servlet.http.HttpUpgradeHandler; +import javax.servlet.http.Part; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.security.Principal; +import java.util.Collection; +import java.util.Enumeration; +import java.util.Locale; +import java.util.Map; + +public final class ServletHttpRequestAdaptee extends DefaultHttpRequest implements HttpServletRequest { + + public ServletHttpRequestAdaptee(HttpMetadata metadata, HttpChannel channel) { + super(metadata, channel); + } + + @Override + public String getAuthType() { + return null; + } + + @Override + public Cookie[] getCookies() { + return new Cookie[0]; + } + + @Override + public long getDateHeader(String name) { + return 0; + } + + @Override + public String getHeader(String name) { + return null; + } + + @Override + public Enumeration getHeaders(String name) { + return null; + } + + @Override + public Enumeration getHeaderNames() { + return null; + } + + @Override + public int getIntHeader(String name) { + return 0; + } + + @Override + public String getMethod() { + return null; + } + + @Override + public String getPathInfo() { + return null; + } + + @Override + public String getPathTranslated() { + return null; + } + + @Override + public String getContextPath() { + return null; + } + + @Override + public String getQueryString() { + return null; + } + + @Override + public String getRemoteUser() { + return null; + } + + @Override + public boolean isUserInRole(String role) { + return false; + } + + @Override + public Principal getUserPrincipal() { + return null; + } + + @Override + public String getRequestedSessionId() { + return null; + } + + @Override + public String getRequestURI() { + return null; + } + + @Override + public StringBuffer getRequestURL() { + return null; + } + + @Override + public String getServletPath() { + return null; + } + + @Override + public HttpSession getSession(boolean create) { + return null; + } + + @Override + public HttpSession getSession() { + return null; + } + + @Override + public String changeSessionId() { + return null; + } + + @Override + public boolean isRequestedSessionIdValid() { + return false; + } + + @Override + public boolean isRequestedSessionIdFromCookie() { + return false; + } + + @Override + public boolean isRequestedSessionIdFromURL() { + return false; + } + + @Override + public boolean isRequestedSessionIdFromUrl() { + return false; + } + + @Override + public boolean authenticate(HttpServletResponse response) throws IOException, ServletException { + return false; + } + + @Override + public void login(String username, String password) throws ServletException {} + + @Override + public void logout() throws ServletException {} + + @Override + public Collection getParts() throws IOException, ServletException { + return null; + } + + @Override + public Part getPart(String name) throws IOException, ServletException { + return null; + } + + @Override + public T upgrade(Class handlerClass) throws IOException, ServletException { + return null; + } + + @Override + public Object getAttribute(String name) { + return null; + } + + @Override + public Enumeration getAttributeNames() { + return null; + } + + @Override + public String getCharacterEncoding() { + return null; + } + + @Override + public void setCharacterEncoding(String env) throws UnsupportedEncodingException {} + + @Override + public int getContentLength() { + return 0; + } + + @Override + public long getContentLengthLong() { + return 0; + } + + @Override + public String getContentType() { + return null; + } + + @Override + public ServletInputStream getInputStream() throws IOException { + return null; + } + + @Override + public String getParameter(String name) { + return null; + } + + @Override + public Enumeration getParameterNames() { + return null; + } + + @Override + public String[] getParameterValues(String name) { + return new String[0]; + } + + @Override + public Map getParameterMap() { + return null; + } + + @Override + public String getProtocol() { + return null; + } + + @Override + public String getScheme() { + return null; + } + + @Override + public String getServerName() { + return null; + } + + @Override + public int getServerPort() { + return 0; + } + + @Override + public BufferedReader getReader() throws IOException { + return null; + } + + @Override + public String getRemoteAddr() { + return null; + } + + @Override + public String getRemoteHost() { + return null; + } + + @Override + public void removeAttribute(String name) {} + + @Override + public Locale getLocale() { + return null; + } + + @Override + public Enumeration getLocales() { + return null; + } + + @Override + public boolean isSecure() { + return false; + } + + @Override + public RequestDispatcher getRequestDispatcher(String path) { + return null; + } + + @Override + public String getRealPath(String path) { + return null; + } + + @Override + public int getRemotePort() { + return 0; + } + + @Override + public String getLocalName() { + return null; + } + + @Override + public String getLocalAddr() { + return null; + } + + @Override + public int getLocalPort() { + return 0; + } + + @Override + public ServletContext getServletContext() { + return null; + } + + @Override + public AsyncContext startAsync() throws IllegalStateException { + return null; + } + + @Override + public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) + throws IllegalStateException { + return null; + } + + @Override + public boolean isAsyncStarted() { + return false; + } + + @Override + public boolean isAsyncSupported() { + return true; + } + + @Override + public AsyncContext getAsyncContext() { + return null; + } + + @Override + public DispatcherType getDispatcherType() { + return DispatcherType.REQUEST; + } +} diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/ServletHttpResponseAdaptee.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/ServletHttpResponseAdaptee.java new file mode 100644 index 000000000000..8f988b64b749 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/servlet/ServletHttpResponseAdaptee.java @@ -0,0 +1,137 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * http://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.apache.dubbo.rpc.protocol.tri.rest.support.servlet; + +import org.apache.dubbo.remoting.http12.message.DefaultHttpResponse; + +import javax.servlet.ServletOutputStream; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletResponse; + +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Collection; +import java.util.Locale; + +public class ServletHttpResponseAdaptee extends DefaultHttpResponse implements HttpServletResponse { + + @Override + public void addCookie(Cookie cookie) {} + + @Override + public boolean containsHeader(String name) { + return false; + } + + @Override + public String encodeURL(String url) { + return null; + } + + @Override + public String encodeRedirectURL(String url) { + return null; + } + + @Override + public String encodeUrl(String url) { + return null; + } + + @Override + public String encodeRedirectUrl(String url) { + return null; + } + + @Override + public void setDateHeader(String name, long date) {} + + @Override + public void addDateHeader(String name, long date) {} + + @Override + public void setIntHeader(String name, int value) {} + + @Override + public void addIntHeader(String name, int value) {} + + @Override + public void setStatus(int sc, String sm) {} + + @Override + public String getHeader(String name) { + return null; + } + + @Override + public Collection getHeaders(String name) { + return null; + } + + @Override + public Collection getHeaderNames() { + return null; + } + + @Override + public String getCharacterEncoding() { + return null; + } + + @Override + public String getContentType() { + return null; + } + + @Override + public ServletOutputStream getOutputStream() throws IOException { + return null; + } + + @Override + public PrintWriter getWriter() throws IOException { + return null; + } + + @Override + public void setCharacterEncoding(String charset) {} + + @Override + public void setContentLength(int len) {} + + @Override + public void setContentLengthLong(long len) {} + + @Override + public void setBufferSize(int size) {} + + @Override + public int getBufferSize() { + return 0; + } + + @Override + public void flushBuffer() throws IOException {} + + @Override + public void setLocale(Locale loc) {} + + @Override + public Locale getLocale() { + return null; + } +} diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/.gitkeeper b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/.gitkeeper new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/route/DefaultRequestRouter.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/route/DefaultRequestRouter.java new file mode 100644 index 000000000000..06f854382cde --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/route/DefaultRequestRouter.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * http://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.apache.dubbo.rpc.protocol.tri.route; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.extension.ExtensionAccessor; +import org.apache.dubbo.common.extension.ExtensionAccessorAware; +import org.apache.dubbo.remoting.http12.*; +import org.apache.dubbo.remoting.http12.exception.UnsupportedMediaTypeException; +import org.apache.dubbo.remoting.http12.message.HttpMessageAdapterFactory; +import org.apache.dubbo.rpc.protocol.tri.TripleConstant; + +import java.util.List; + +public final class DefaultRequestRouter implements ExtensionAccessorAware, RequestRouter { + + private HttpMessageAdapterFactory httpMessageAdapterFactory; + private List handlerMappings; + + @Override + @SuppressWarnings("unchecked") + public void setExtensionAccessor(ExtensionAccessor extensionAccessor) { + httpMessageAdapterFactory = extensionAccessor + .getExtensionLoader(HttpMessageAdapterFactory.class) + .getActivateExtensions() + .get(0); + handlerMappings = + extensionAccessor.getExtensionLoader(HandlerMapping.class).getActivateExtensions(); + } + + @Override + public RpcInvocationBuildContext route(URL url, RequestMetadata metadata, HttpChannel httpChannel) { + HttpRequest request = httpMessageAdapterFactory.adapterRequest(metadata, httpChannel); + HttpResponse response = httpMessageAdapterFactory.adapterResponse(request, metadata); + + for (int i = 0, len = handlerMappings.size(); i < len; i++) { + HandlerMapping handlerMapping = handlerMappings.get(i); + HandlerInfo info = handlerMapping.getHandler(url, request, response); + if (info == null) { + continue; + } + info.setAttribute(TripleConstant.HANDLER_TYPE_KEY, handlerMapping.getType()); + info.setAttribute(TripleConstant.HTTP_REQUEST_KEY, request); + info.setAttribute(TripleConstant.HTTP_RESPONSE_KEY, response); + return info; + } + + if (null != request.contentType()) { + throw new UnsupportedMediaTypeException(request.contentType()); + } + + return null; + } +} diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/route/HandlerInfo.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/route/HandlerInfo.java new file mode 100644 index 000000000000..7bc55e3efe40 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/route/HandlerInfo.java @@ -0,0 +1,126 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * http://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.apache.dubbo.rpc.protocol.tri.route; + +import org.apache.dubbo.remoting.http12.message.HttpMessageDecoder; +import org.apache.dubbo.remoting.http12.message.HttpMessageEncoder; +import org.apache.dubbo.remoting.http12.message.MethodMetadata; +import org.apache.dubbo.rpc.Invoker; +import org.apache.dubbo.rpc.model.MethodDescriptor; +import org.apache.dubbo.rpc.model.ServiceDescriptor; + +import java.util.Map; + +public final class HandlerInfo implements RpcInvocationBuildContext { + + private Invoker invoker; + private boolean hasStub; + private String methodName; + private MethodDescriptor methodDescriptor; + private MethodMetadata methodMetadata; + private ServiceDescriptor serviceDescriptor; + private HttpMessageDecoder httpMessageDecoder; + private HttpMessageEncoder httpMessageEncoder; + private Map attributes; + + @Override + public Invoker getInvoker() { + return invoker; + } + + public void setInvoker(Invoker invoker) { + this.invoker = invoker; + } + + @Override + public boolean isHasStub() { + return hasStub; + } + + public void setHasStub(boolean hasStub) { + this.hasStub = hasStub; + } + + @Override + public String getMethodName() { + return methodName; + } + + public void setMethodName(String methodName) { + this.methodName = methodName; + } + + @Override + public MethodDescriptor getMethodDescriptor() { + return methodDescriptor; + } + + @Override + public MethodMetadata getMethodMetadata() { + return methodMetadata; + } + + @Override + public void setMethodMetadata(MethodMetadata methodMetadata) { + this.methodMetadata = methodMetadata; + } + + @Override + public void setMethodDescriptor(MethodDescriptor methodDescriptor) { + this.methodDescriptor = methodDescriptor; + } + + @Override + public ServiceDescriptor getServiceDescriptor() { + return serviceDescriptor; + } + + public void setServiceDescriptor(ServiceDescriptor serviceDescriptor) { + this.serviceDescriptor = serviceDescriptor; + } + + @Override + public HttpMessageDecoder getHttpMessageDecoder() { + return httpMessageDecoder; + } + + public void setHttpMessageDecoder(HttpMessageDecoder httpMessageDecoder) { + this.httpMessageDecoder = httpMessageDecoder; + } + + @Override + public HttpMessageEncoder getHttpMessageEncoder() { + return httpMessageEncoder; + } + + public void setHttpMessageEncoder(HttpMessageEncoder httpMessageEncoder) { + this.httpMessageEncoder = httpMessageEncoder; + } + + @Override + public Map getAttributes() { + return attributes; + } + + public void setAttributes(Map attributes) { + this.attributes = attributes; + } + + public void setAttribute(String key, Object value) { + attributes.put(key, value); + } +} diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/route/HandlerMapping.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/route/HandlerMapping.java new file mode 100644 index 000000000000..78dbe9d79890 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/route/HandlerMapping.java @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * http://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.apache.dubbo.rpc.protocol.tri.route; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.extension.ExtensionScope; +import org.apache.dubbo.common.extension.SPI; +import org.apache.dubbo.remoting.http12.HttpRequest; +import org.apache.dubbo.remoting.http12.HttpResponse; + +@SPI(scope = ExtensionScope.FRAMEWORK) +public interface HandlerMapping { + + HandlerInfo getHandler(URL url, HttpRequest request, HttpResponse response); + + String getType(); +} diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/route/RequestRouter.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/route/RequestRouter.java new file mode 100644 index 000000000000..2f2cf913c2f9 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/route/RequestRouter.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * http://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.apache.dubbo.rpc.protocol.tri.route; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.constants.CommonConstants; +import org.apache.dubbo.common.extension.ExtensionScope; +import org.apache.dubbo.common.extension.SPI; +import org.apache.dubbo.remoting.http12.HttpChannel; +import org.apache.dubbo.remoting.http12.RequestMetadata; + +@SPI(value = CommonConstants.TRIPLE, scope = ExtensionScope.FRAMEWORK) +public interface RequestRouter { + + RpcInvocationBuildContext route(URL url, RequestMetadata metadata, HttpChannel httpChannel); +} diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/route/RpcInvocationBuildContext.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/route/RpcInvocationBuildContext.java new file mode 100644 index 000000000000..1bf6bc629c5a --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/route/RpcInvocationBuildContext.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * http://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.apache.dubbo.rpc.protocol.tri.route; + +import org.apache.dubbo.remoting.http12.message.HttpMessageDecoder; +import org.apache.dubbo.remoting.http12.message.HttpMessageEncoder; +import org.apache.dubbo.remoting.http12.message.MethodMetadata; +import org.apache.dubbo.rpc.Invoker; +import org.apache.dubbo.rpc.model.MethodDescriptor; +import org.apache.dubbo.rpc.model.ServiceDescriptor; + +import java.util.Map; + +public interface RpcInvocationBuildContext { + + Invoker getInvoker(); + + boolean isHasStub(); + + String getMethodName(); + + MethodMetadata getMethodMetadata(); + + void setMethodMetadata(MethodMetadata methodMetadata); + + MethodDescriptor getMethodDescriptor(); + + void setMethodDescriptor(MethodDescriptor methodDescriptor); + + ServiceDescriptor getServiceDescriptor(); + + HttpMessageDecoder getHttpMessageDecoder(); + + HttpMessageEncoder getHttpMessageEncoder(); + + Map getAttributes(); +} diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/service/TriBuiltinService.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/service/TriBuiltinService.java index b45a40ef069e..6ec289195e7f 100644 --- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/service/TriBuiltinService.java +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/service/TriBuiltinService.java @@ -68,8 +68,8 @@ public void init() { healthStatusManager = new HealthStatusManager(new TriHealthImpl()); healthService = healthStatusManager.getHealthService(); reflectionServiceV1Alpha = new ReflectionV1AlphaService(); - proxyFactory = frameworkModel.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension(); - pathResolver = frameworkModel.getExtensionLoader(PathResolver.class).getDefaultExtension(); + proxyFactory = frameworkModel.getAdaptiveExtension(ProxyFactory.class); + pathResolver = frameworkModel.getDefaultExtension(PathResolver.class); addSingleBuiltinService(DubboHealthTriple.SERVICE_NAME, healthService, Health.class); addSingleBuiltinService( ReflectionV1AlphaService.SERVICE_NAME, reflectionServiceV1Alpha, ReflectionV1AlphaService.class); diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/stream/ServerStream.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/stream/ServerStream.java deleted file mode 100644 index 88f45284d4e6..000000000000 --- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/stream/ServerStream.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 - * - * http://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.apache.dubbo.rpc.protocol.tri.stream; - -import org.apache.dubbo.rpc.TriRpcStatus; - -import java.util.Map; - -import io.netty.util.concurrent.Future; - -/** - * ServerStream is used to send response to client and receive requests from client. {@link - * Listener} is used to receive requests from client. - */ -public interface ServerStream extends Stream { - - interface Listener extends Stream.Listener { - - /** - * Callback when receive headers - * - * @param headers headers received from remote peer - */ - void onHeader(Map headers); - - /** - * Callback when no more data from client side - */ - void onComplete(); - } - - /** - * Complete the stream, send response to client - * - * @param status response status - * @param attachments response attachments - * @return a future that indicates the completion of send trailers - */ - Future complete( - TriRpcStatus status, Map attachments, boolean isNeedReturnException, int exceptionCode); - - /** - * Send message to client - * - * @param message raw message - * @param compressFlag whether to compress the message - * @return a future that indicates the completion of send message - */ - Future sendMessage(byte[] message, int compressFlag); -} diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/stream/StreamUtils.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/stream/StreamUtils.java index b1ec7306a00a..2494505b0a64 100644 --- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/stream/StreamUtils.java +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/stream/StreamUtils.java @@ -18,6 +18,7 @@ import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; +import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.JsonUtils; import org.apache.dubbo.common.utils.LRU2Cache; import org.apache.dubbo.remoting.http12.HttpHeaders; @@ -28,17 +29,22 @@ import java.nio.charset.StandardCharsets; import java.util.Base64; import java.util.Collections; +import java.util.Date; import java.util.HashMap; +import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.function.BiConsumer; +import io.netty.handler.codec.DateFormatter; import io.netty.handler.codec.http2.DefaultHttp2Headers; +import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_FAILED_PARSE; import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_UNSUPPORTED; -public class StreamUtils { +public final class StreamUtils { - protected static final ErrorTypeAwareLogger LOGGER = LoggerFactory.getErrorTypeAwareLogger(StreamUtils.class); + private static final ErrorTypeAwareLogger LOGGER = LoggerFactory.getErrorTypeAwareLogger(StreamUtils.class); private static final Base64.Decoder BASE64_DECODER = Base64.getDecoder(); private static final Base64.Encoder BASE64_ENCODER = Base64.getEncoder().withoutPadding(); @@ -47,163 +53,188 @@ public class StreamUtils { private static final Map lruHeaderMap = new LRU2Cache<>(MAX_LRU_HEADER_MAP_SIZE); + private StreamUtils() {} + public static String encodeBase64ASCII(byte[] in) { - byte[] bytes = encodeBase64(in); - return new String(bytes, StandardCharsets.US_ASCII); + return new String(encodeBase64(in), StandardCharsets.US_ASCII); } public static byte[] encodeBase64(byte[] in) { return BASE64_ENCODER.encode(in); } - public static byte[] decodeASCIIByte(CharSequence value) { - return BASE64_DECODER.decode(value.toString().getBytes(StandardCharsets.US_ASCII)); + public static byte[] decodeASCIIByte(String value) { + return BASE64_DECODER.decode(value.getBytes(StandardCharsets.US_ASCII)); } - public static Map toAttachments(Map origin) { - if (origin == null || origin.isEmpty()) { - return Collections.emptyMap(); - } - Map res = new HashMap<>(origin.size()); - origin.forEach((k, v) -> { - if (TripleHeaderEnum.containsExcludeAttachments(k)) { - return; - } - res.put(k, v); - }); - return res; + /** + * Parse and put attachments into headers. + * Ignore Http2 PseudoHeaderName and internal name. + * Only strings, dates, and byte arrays are allowed. + * + * @param headers the headers + * @param attachments the attachments + * @param needConvertHeaderKey whether need to convert the header key to lower-case + */ + public static void putHeaders( + DefaultHttp2Headers headers, Map attachments, boolean needConvertHeaderKey) { + putHeaders(attachments, needConvertHeaderKey, headers::set); } /** - * Parse and put the KV pairs into metadata. Ignore Http2 PseudoHeaderName and internal name. - * Only raw byte array or string value will be put. + * Parse and put attachments into headers. + * Ignore Http2 PseudoHeaderName and internal name. + * Only strings, dates, and byte arrays are allowed. * - * @param headers the metadata holder - * @param attachments KV pairs - * @param needConvertHeaderKey convert flag + * @param headers the headers + * @param attachments the attachments + * @param needConvertHeaderKey whether need to convert the header key to lower-case */ - public static void convertAttachment( - DefaultHttp2Headers headers, Map attachments, boolean needConvertHeaderKey) { - if (attachments == null) { - return; - } - Map needConvertKey = new HashMap<>(); - for (Map.Entry entry : attachments.entrySet()) { - String key = lruHeaderMap.get(entry.getKey()); - if (key == null) { - final String lowerCaseKey = entry.getKey().toLowerCase(Locale.ROOT); - lruHeaderMap.put(entry.getKey(), lowerCaseKey); - key = lowerCaseKey; - } - if (TripleHeaderEnum.containsExcludeAttachments(key)) { - continue; - } - final Object v = entry.getValue(); - if (v == null) { - continue; - } - if (needConvertHeaderKey && !key.equals(entry.getKey())) { - needConvertKey.put(key, entry.getKey()); - } - convertSingleAttachment(headers, key, v); - } - if (!needConvertKey.isEmpty()) { - String needConvertJson = JsonUtils.toJson(needConvertKey); - headers.add(TripleHeaderEnum.TRI_HEADER_CONVERT.getHeader(), TriRpcStatus.encodeMessage(needConvertJson)); - } + public static void putHeaders(HttpHeaders headers, Map attachments, boolean needConvertHeaderKey) { + putHeaders(attachments, needConvertHeaderKey, headers::set); } - public static void convertAttachment( - HttpHeaders headers, Map attachments, boolean needConvertHeaderKey) { - if (attachments == null) { + private static void putHeaders( + Map attachments, boolean needConvertHeaderKey, BiConsumer consumer) { + if (CollectionUtils.isEmptyMap(attachments)) { return; } - Map needConvertKey = new HashMap<>(); + Map needConvertKeys = new HashMap<>(); for (Map.Entry entry : attachments.entrySet()) { - String key = lruHeaderMap.get(entry.getKey()); - if (key == null) { - final String lowerCaseKey = entry.getKey().toLowerCase(Locale.ROOT); - lruHeaderMap.put(entry.getKey(), lowerCaseKey); - key = lowerCaseKey; - } - if (TripleHeaderEnum.containsExcludeAttachments(key)) { + Object value = entry.getValue(); + if (value == null) { continue; } - final Object v = entry.getValue(); - if (v == null) { + + String key = entry.getKey(); + String lowerCaseKey = lruHeaderMap.computeIfAbsent(key, k -> k.toLowerCase(Locale.ROOT)); + if (TripleHeaderEnum.containsExcludeAttachments(lowerCaseKey)) { continue; } - if (needConvertHeaderKey && !key.equals(entry.getKey())) { - needConvertKey.put(key, entry.getKey()); + if (needConvertHeaderKey && !lowerCaseKey.equals(key)) { + needConvertKeys.put(lowerCaseKey, key); } - convertSingleAttachment(headers, key, v); + putHeader(consumer, lowerCaseKey, value); } - if (!needConvertKey.isEmpty()) { - String needConvertJson = JsonUtils.toJson(needConvertKey); - headers.set(TripleHeaderEnum.TRI_HEADER_CONVERT.getHeader(), TriRpcStatus.encodeMessage(needConvertJson)); + if (needConvertKeys.isEmpty()) { + return; } - } - - public static void convertAttachment(DefaultHttp2Headers headers, Map attachments) { - convertAttachment(headers, attachments, false); + consumer.accept( + TripleHeaderEnum.TRI_HEADER_CONVERT.getHeader(), + TriRpcStatus.encodeMessage(JsonUtils.toJson(needConvertKeys))); } /** - * Convert each user's attach value to metadata + * Put a KV pairs into headers. * - * @param headers outbound headers - * @param key metadata key - * @param v metadata value (Metadata Only string and byte arrays are allowed) + * @param consumer outbound headers consumer + * @param key the key of the attachment + * @param value the value of the attachment (Only strings, dates, and byte arrays are allowed in the attachment value.) */ - private static void convertSingleAttachment(DefaultHttp2Headers headers, String key, Object v) { + private static void putHeader(BiConsumer consumer, String key, Object value) { try { - if (v instanceof String || v instanceof Number || v instanceof Boolean) { - String str = v.toString(); - headers.set(key, str); - } else if (v instanceof byte[]) { - String str = encodeBase64ASCII((byte[]) v); - headers.set(key + TripleConstant.HEADER_BIN_SUFFIX, str); + if (value instanceof CharSequence || value instanceof Number || value instanceof Boolean) { + String str = value.toString(); + consumer.accept(key, str); + } else if (value instanceof Date) { + consumer.accept(key, DateFormatter.format((Date) value)); + } else if (value instanceof byte[]) { + String str = encodeBase64ASCII((byte[]) value); + consumer.accept(key + TripleConstant.HEADER_BIN_SUFFIX, str); } else { LOGGER.warn( PROTOCOL_UNSUPPORTED, "", "", "Unsupported attachment k: " + key + " class: " - + v.getClass().getName()); + + value.getClass().getName()); } } catch (Throwable t) { LOGGER.warn( PROTOCOL_UNSUPPORTED, "", "", - "Meet exception when convert single attachment key:" + key + " value=" + v, + "Meet exception when convert single attachment key:" + key + " value=" + value, t); } } - private static void convertSingleAttachment(HttpHeaders headers, String key, Object v) { - try { - if (v instanceof String || v instanceof Number || v instanceof Boolean) { - String str = v.toString(); - headers.set(key, str); - } else if (v instanceof byte[]) { - String str = encodeBase64ASCII((byte[]) v); - headers.set(key + TripleConstant.HEADER_BIN_SUFFIX, str); + /** + * Convert the given map to attachments. Ignore Http2 PseudoHeaderName and internal name. + * + * @param map The map + * @return the attachments + */ + public static Map toAttachments(Map map) { + if (CollectionUtils.isEmptyMap(map)) { + return Collections.emptyMap(); + } + Map res = new HashMap<>(map.size()); + for (Map.Entry entry : map.entrySet()) { + String key = entry.getKey(); + if (TripleHeaderEnum.containsExcludeAttachments(key)) { + continue; + } + res.put(key, entry.getValue()); + } + return res; + } + + /** + * Parse and convert headers to attachments. Ignore Http2 PseudoHeaderName and internal name. + * + * @param headers the headers + * @return the attachments + */ + public static Map toAttachments(HttpHeaders headers) { + if (headers == null) { + return Collections.emptyMap(); + } + + Map attachments = CollectionUtils.newHashMap(headers.size()); + for (Map.Entry> entry : headers.entrySet()) { + String key = entry.getKey(); + String value = CollectionUtils.first(entry.getValue()); + int len = key.length() - TripleConstant.HEADER_BIN_SUFFIX.length(); + if (len > 0 && TripleConstant.HEADER_BIN_SUFFIX.equals(key.substring(len))) { + try { + putAttachment(attachments, key.substring(0, len), value == null ? null : decodeASCIIByte(value)); + } catch (Exception e) { + LOGGER.error(PROTOCOL_FAILED_PARSE, "", "", "Failed to parse response attachment key=" + key, e); + } } else { - LOGGER.warn( - PROTOCOL_UNSUPPORTED, - "", - "", - "Unsupported attachment k: " + key + " class: " - + v.getClass().getName()); + putAttachment(attachments, key, value); } - } catch (Throwable t) { - LOGGER.warn( - PROTOCOL_UNSUPPORTED, - "", - "", - "Meet exception when convert single attachment key:" + key + " value=" + v, - t); } + + // try converting upper key + String converted = headers.getFirst(TripleHeaderEnum.TRI_HEADER_CONVERT.getHeader()); + if (converted == null) { + return attachments; + } + String json = TriRpcStatus.decodeMessage(converted); + Map map = JsonUtils.toJavaObject(json, Map.class); + for (Map.Entry entry : map.entrySet()) { + String key = entry.getKey(); + Object value = attachments.remove(key); + if (value != null) { + putAttachment(attachments, key, value); + } + } + return attachments; + } + + /** + * Put a KV pairs into attachments. + * + * @param attachments the map to which the attachment will be added + * @param key the key of the header + * @param value the value of the header + */ + private static void putAttachment(Map attachments, String key, Object value) { + if (TripleHeaderEnum.containsExcludeAttachments(key)) { + return; + } + attachments.put(key, value); } } diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/stream/TripleServerStream.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/stream/TripleServerStream.java deleted file mode 100644 index 0aaca484910f..000000000000 --- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/stream/TripleServerStream.java +++ /dev/null @@ -1,505 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 - * - * http://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.apache.dubbo.rpc.protocol.tri.stream; - -import org.apache.dubbo.common.URL; -import org.apache.dubbo.common.constants.CommonConstants; -import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; -import org.apache.dubbo.common.logger.LoggerFactory; -import org.apache.dubbo.common.utils.StringUtils; -import org.apache.dubbo.rpc.HeaderFilter; -import org.apache.dubbo.rpc.Invoker; -import org.apache.dubbo.rpc.PathResolver; -import org.apache.dubbo.rpc.TriRpcStatus; -import org.apache.dubbo.rpc.model.FrameworkModel; -import org.apache.dubbo.rpc.protocol.tri.ExceptionUtils; -import org.apache.dubbo.rpc.protocol.tri.TripleConstant; -import org.apache.dubbo.rpc.protocol.tri.TripleHeaderEnum; -import org.apache.dubbo.rpc.protocol.tri.TripleProtocol; -import org.apache.dubbo.rpc.protocol.tri.call.ReflectionAbstractServerCall; -import org.apache.dubbo.rpc.protocol.tri.call.StubAbstractServerCall; -import org.apache.dubbo.rpc.protocol.tri.command.CancelQueueCommand; -import org.apache.dubbo.rpc.protocol.tri.command.DataQueueCommand; -import org.apache.dubbo.rpc.protocol.tri.command.HeaderQueueCommand; -import org.apache.dubbo.rpc.protocol.tri.command.TextDataQueueCommand; -import org.apache.dubbo.rpc.protocol.tri.compressor.DeCompressor; -import org.apache.dubbo.rpc.protocol.tri.compressor.Identity; -import org.apache.dubbo.rpc.protocol.tri.frame.Deframer; -import org.apache.dubbo.rpc.protocol.tri.frame.TriDecoder; -import org.apache.dubbo.rpc.protocol.tri.transport.AbstractH2TransportListener; -import org.apache.dubbo.rpc.protocol.tri.transport.H2TransportListener; -import org.apache.dubbo.rpc.protocol.tri.transport.TripleWriteQueue; - -import java.io.IOException; -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.Executor; - -import com.google.protobuf.Any; -import com.google.rpc.DebugInfo; -import com.google.rpc.Status; -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelFuture; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.handler.codec.http.HttpUtil; -import io.netty.handler.codec.http2.DefaultHttp2Headers; -import io.netty.handler.codec.http2.Http2Error; -import io.netty.handler.codec.http2.Http2Headers; -import io.netty.handler.codec.http2.Http2StreamChannel; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.concurrent.Future; - -import static io.netty.handler.codec.http.HttpResponseStatus.OK; -import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_FAILED_REQUEST; - -public class TripleServerStream extends AbstractStream implements ServerStream { - - private static final ErrorTypeAwareLogger LOGGER = LoggerFactory.getErrorTypeAwareLogger(TripleServerStream.class); - public final ServerTransportObserver transportObserver = new ServerTransportObserver(); - private final TripleWriteQueue writeQueue; - private final PathResolver pathResolver; - private final List filters; - private final String acceptEncoding; - private boolean headerSent; - private boolean trailersSent; - private volatile boolean reset; - private ServerStream.Listener listener; - private final InetSocketAddress remoteAddress; - private Deframer deframer; - private boolean rst = false; - private final Http2StreamChannel http2StreamChannel; - private final TripleStreamChannelFuture tripleStreamChannelFuture; - - public TripleServerStream( - Http2StreamChannel channel, - FrameworkModel frameworkModel, - Executor executor, - PathResolver pathResolver, - String acceptEncoding, - List filters, - TripleWriteQueue writeQueue) { - super(executor, frameworkModel); - this.pathResolver = pathResolver; - this.acceptEncoding = acceptEncoding; - this.filters = filters; - this.writeQueue = writeQueue; - this.remoteAddress = (InetSocketAddress) channel.remoteAddress(); - this.http2StreamChannel = channel; - this.tripleStreamChannelFuture = new TripleStreamChannelFuture(channel); - } - - @Override - public SocketAddress remoteAddress() { - return remoteAddress; - } - - @Override - public void request(int n) { - deframer.request(n); - } - - public ChannelFuture reset(Http2Error cause) { - ChannelFuture checkResult = preCheck(); - if (!checkResult.isSuccess()) { - return checkResult; - } - this.rst = true; - return writeQueue.enqueue(CancelQueueCommand.createCommand(tripleStreamChannelFuture, cause)); - } - - @Override - public ChannelFuture sendHeader(Http2Headers headers) { - if (reset) { - return http2StreamChannel.newFailedFuture( - new IllegalStateException("Stream already reset, no more headers allowed")); - } - if (headerSent) { - return http2StreamChannel.newFailedFuture(new IllegalStateException("Header already sent")); - } - if (trailersSent) { - return http2StreamChannel.newFailedFuture(new IllegalStateException("Trailers already sent")); - } - ChannelFuture checkResult = preCheck(); - if (!checkResult.isSuccess()) { - return checkResult; - } - headerSent = true; - return writeQueue - .enqueue(HeaderQueueCommand.createHeaders(tripleStreamChannelFuture, headers, false)) - .addListener(f -> { - if (!f.isSuccess()) { - reset(Http2Error.INTERNAL_ERROR); - } - }); - } - - @Override - public Future cancelByLocal(TriRpcStatus status) { - if (LOGGER.isDebugEnabled()) { - LOGGER.debug(String.format("Cancel stream:%s by local: %s", http2StreamChannel, status)); - } - return reset(Http2Error.CANCEL); - } - - @Override - public ChannelFuture complete( - TriRpcStatus status, Map attachments, boolean isNeedReturnException, int exceptionCode) { - Http2Headers trailers = - getTrailers(status, attachments, isNeedReturnException, CommonConstants.TRI_EXCEPTION_CODE_NOT_EXISTS); - return sendTrailers(trailers); - } - - private ChannelFuture sendTrailers(Http2Headers trailers) { - if (reset) { - return http2StreamChannel.newFailedFuture( - new IllegalStateException("Stream already reset, no more trailers allowed")); - } - if (trailersSent) { - return http2StreamChannel.newFailedFuture(new IllegalStateException("Trailers already sent")); - } - ChannelFuture checkResult = preCheck(); - if (!checkResult.isSuccess()) { - return checkResult; - } - headerSent = true; - trailersSent = true; - return writeQueue - .enqueue(HeaderQueueCommand.createHeaders(tripleStreamChannelFuture, trailers, true)) - .addListener(f -> { - if (!f.isSuccess()) { - reset(Http2Error.INTERNAL_ERROR); - } - }); - } - - private Http2Headers getTrailers( - TriRpcStatus rpcStatus, Map attachments, boolean isNeedReturnException, int exceptionCode) { - DefaultHttp2Headers headers = new DefaultHttp2Headers(); - if (!headerSent) { - headers.status(HttpResponseStatus.OK.codeAsText()); - headers.set(HttpHeaderNames.CONTENT_TYPE, TripleConstant.CONTENT_PROTO); - } - StreamUtils.convertAttachment(headers, attachments, TripleProtocol.CONVERT_NO_LOWER_HEADER); - headers.set(TripleHeaderEnum.STATUS_KEY.getHeader(), String.valueOf(rpcStatus.code.code)); - if (rpcStatus.isOk()) { - return headers; - } - String grpcMessage = getGrpcMessage(rpcStatus); - grpcMessage = TriRpcStatus.encodeMessage(TriRpcStatus.limitSizeTo1KB(grpcMessage)); - headers.set(TripleHeaderEnum.MESSAGE_KEY.getHeader(), grpcMessage); - if (!getGrpcStatusDetailEnabled()) { - return headers; - } - Status.Builder builder = - Status.newBuilder().setCode(rpcStatus.code.code).setMessage(grpcMessage); - Throwable throwable = rpcStatus.cause; - if (throwable == null) { - Status status = builder.build(); - headers.set( - TripleHeaderEnum.STATUS_DETAIL_KEY.getHeader(), - StreamUtils.encodeBase64ASCII(status.toByteArray())); - return headers; - } - DebugInfo debugInfo = DebugInfo.newBuilder() - .addAllStackEntries(ExceptionUtils.getStackFrameList(throwable, 6)) - // can not use now - // .setDetail(throwable.getMessage()) - .build(); - builder.addDetails(Any.pack(debugInfo)); - Status status = builder.build(); - headers.set( - TripleHeaderEnum.STATUS_DETAIL_KEY.getHeader(), StreamUtils.encodeBase64ASCII(status.toByteArray())); - return headers; - } - - private String getGrpcMessage(TriRpcStatus status) { - if (StringUtils.isNotEmpty(status.description)) { - return status.description; - } - return Optional.ofNullable(status.cause).map(Throwable::getMessage).orElse("unknown"); - } - - @Override - public ChannelFuture sendMessage(byte[] message, int compressFlag) { - if (reset) { - return http2StreamChannel.newFailedFuture( - new IllegalStateException("Stream already reset, no more body allowed")); - } - if (!headerSent) { - return http2StreamChannel.newFailedFuture( - new IllegalStateException("Headers did not sent before send body")); - } - if (trailersSent) { - return http2StreamChannel.newFailedFuture( - new IllegalStateException("Trailers already sent, no more body allowed")); - } - ChannelFuture checkResult = preCheck(); - if (!checkResult.isSuccess()) { - return checkResult; - } - return writeQueue.enqueue(DataQueueCommand.create(tripleStreamChannelFuture, message, false, compressFlag)); - } - - /** - * Error before create server stream, http plain text will be returned - * - * @param code code of error - * @param status status of error - */ - private void responsePlainTextError(int code, TriRpcStatus status) { - ChannelFuture checkResult = preCheck(); - if (!checkResult.isSuccess()) { - return; - } - Http2Headers headers = new DefaultHttp2Headers(true) - .status(String.valueOf(code)) - .setInt(TripleHeaderEnum.STATUS_KEY.getHeader(), status.code.code) - .set(TripleHeaderEnum.MESSAGE_KEY.getHeader(), status.description) - .set(TripleHeaderEnum.CONTENT_TYPE_KEY.getHeader(), TripleConstant.TEXT_PLAIN_UTF8); - writeQueue.enqueue(HeaderQueueCommand.createHeaders(tripleStreamChannelFuture, headers, false)); - writeQueue.enqueue(TextDataQueueCommand.createCommand(tripleStreamChannelFuture, status.description, true)); - } - - /** - * Error in create stream, unsupported config or triple protocol error. There is no return value - * because stream will be reset if send trailers failed. - * - * @param status status of error - */ - private void responseErr(TriRpcStatus status) { - Http2Headers trailers = new DefaultHttp2Headers() - .status(OK.codeAsText()) - .set(HttpHeaderNames.CONTENT_TYPE, TripleConstant.CONTENT_PROTO) - .setInt(TripleHeaderEnum.STATUS_KEY.getHeader(), status.code.code) - .set(TripleHeaderEnum.MESSAGE_KEY.getHeader(), status.toEncodedMessage()); - sendTrailers(trailers); - } - - private Invoker getInvoker(Http2Headers headers, String serviceName) { - final String version = headers.contains(TripleHeaderEnum.SERVICE_VERSION.getHeader()) - ? headers.get(TripleHeaderEnum.SERVICE_VERSION.getHeader()).toString() - : null; - final String group = headers.contains(TripleHeaderEnum.SERVICE_GROUP.getHeader()) - ? headers.get(TripleHeaderEnum.SERVICE_GROUP.getHeader()).toString() - : null; - final String key = URL.buildKey(serviceName, group, version); - Invoker invoker = pathResolver.resolve(key); - if (invoker == null && TripleProtocol.RESOLVE_FALLBACK_TO_DEFAULT) { - invoker = pathResolver.resolve(URL.buildKey(serviceName, group, "1.0.0")); - } - if (invoker == null && TripleProtocol.RESOLVE_FALLBACK_TO_DEFAULT) { - invoker = pathResolver.resolve(serviceName); - } - return invoker; - } - - private ChannelFuture preCheck() { - if (!http2StreamChannel.isActive()) { - return http2StreamChannel.newFailedFuture(new IOException("stream channel is closed")); - } - if (rst) { - return http2StreamChannel.newFailedFuture(new IOException("stream channel has reset")); - } - return http2StreamChannel.newSucceededFuture(); - } - - public class ServerTransportObserver extends AbstractH2TransportListener implements H2TransportListener { - - /** - * must starts from application/grpc - */ - private boolean supportContentType(String contentType) { - if (contentType == null) { - return false; - } - return contentType.startsWith(TripleConstant.APPLICATION_GRPC); - } - - @Override - public void onHeader(Http2Headers headers, boolean endStream) { - executor.execute(() -> processHeader(headers, endStream)); - } - - private void processHeader(Http2Headers headers, boolean endStream) { - if (!HttpMethod.POST.asciiName().contentEquals(headers.method())) { - responsePlainTextError( - HttpResponseStatus.METHOD_NOT_ALLOWED.code(), - TriRpcStatus.INTERNAL.withDescription( - String.format("Method '%s' is not supported", headers.method()))); - return; - } - - if (headers.path() == null) { - responsePlainTextError( - HttpResponseStatus.NOT_FOUND.code(), - TriRpcStatus.fromCode(TriRpcStatus.Code.UNIMPLEMENTED.code) - .withDescription("Expected path but is missing")); - return; - } - - final String path = headers.path().toString(); - if (path.charAt(0) != '/') { - responsePlainTextError( - HttpResponseStatus.NOT_FOUND.code(), - TriRpcStatus.fromCode(TriRpcStatus.Code.UNIMPLEMENTED.code) - .withDescription(String.format("Expected path to start with /: %s", path))); - return; - } - - final CharSequence contentType = HttpUtil.getMimeType(headers.get(HttpHeaderNames.CONTENT_TYPE)); - if (contentType == null) { - responsePlainTextError( - HttpResponseStatus.UNSUPPORTED_MEDIA_TYPE.code(), - TriRpcStatus.fromCode(TriRpcStatus.Code.INTERNAL.code) - .withDescription("Content-Type is missing from the request")); - return; - } - - final String contentString = contentType.toString(); - if (!supportContentType(contentString)) { - responsePlainTextError( - HttpResponseStatus.UNSUPPORTED_MEDIA_TYPE.code(), - TriRpcStatus.fromCode(TriRpcStatus.Code.INTERNAL.code) - .withDescription(String.format("Content-Type '%s' is not supported", contentString))); - return; - } - - String[] parts = path.split("/"); - if (parts.length != 3) { - responseErr(TriRpcStatus.UNIMPLEMENTED.withDescription("Bad path format:" + path)); - return; - } - String serviceName = parts[1]; - String originalMethodName = parts[2]; - - Invoker invoker = getInvoker(headers, serviceName); - if (invoker == null) { - responseErr(TriRpcStatus.UNIMPLEMENTED.withDescription("Service not found:" + serviceName)); - return; - } - - if (endStream) { - return; - } - - DeCompressor deCompressor = DeCompressor.NONE; - CharSequence messageEncoding = headers.get(TripleHeaderEnum.GRPC_ENCODING.getHeader()); - if (null != messageEncoding) { - String compressorStr = messageEncoding.toString(); - if (!Identity.MESSAGE_ENCODING.equals(compressorStr)) { - DeCompressor compressor = DeCompressor.getCompressor(frameworkModel, compressorStr); - if (null == compressor) { - responseErr(TriRpcStatus.fromCode(TriRpcStatus.Code.UNIMPLEMENTED.code) - .withDescription(String.format("Grpc-encoding '%s' is not supported", compressorStr))); - return; - } - deCompressor = compressor; - } - } - - Map requestMetadata = headersToMap( - headers, () -> Optional.ofNullable(headers.get(TripleHeaderEnum.TRI_HEADER_CONVERT.getHeader())) - .map(CharSequence::toString) - .orElse(null)); - boolean hasStub = pathResolver.hasNativeStub(path); - if (hasStub) { - listener = new StubAbstractServerCall( - invoker, - TripleServerStream.this, - frameworkModel, - acceptEncoding, - serviceName, - originalMethodName, - executor); - } else { - listener = new ReflectionAbstractServerCall( - invoker, - TripleServerStream.this, - frameworkModel, - acceptEncoding, - serviceName, - originalMethodName, - filters, - executor); - } - // must before onHeader - deframer = new TriDecoder(deCompressor, new ServerDecoderListener(listener)); - listener.onHeader(requestMetadata); - } - - @Override - public void onData(ByteBuf data, boolean endStream) { - try { - executor.execute(() -> doOnData(data, endStream)); - } catch (Throwable t) { - // Tasks will be rejected when the thread pool is closed or full, - // ByteBuf needs to be released to avoid out of heap memory leakage. - // For example, ThreadLessExecutor will be shutdown when request timeout {@link AsyncRpcResult} - ReferenceCountUtil.release(data); - LOGGER.error(PROTOCOL_FAILED_REQUEST, "", "", "submit onData task failed", t); - } - } - - private void doOnData(ByteBuf data, boolean endStream) { - if (deframer == null) { - return; - } - deframer.deframe(data); - if (endStream) { - deframer.close(); - } - } - - @Override - public void cancelByRemote(long errorCode) { - TripleServerStream.this.reset = true; - if (!trailersSent) { - // send rst if stream not closed - reset(Http2Error.valueOf(errorCode)); - } - if (listener == null) { - return; - } - executor.execute(() -> listener.onCancelByRemote( - TriRpcStatus.CANCELLED.withDescription("Canceled by client ,errorCode=" + errorCode))); - } - } - - private static class ServerDecoderListener implements TriDecoder.Listener { - - private final ServerStream.Listener listener; - - public ServerDecoderListener(ServerStream.Listener listener) { - this.listener = listener; - } - - @Override - public void onRawMessage(byte[] data) { - listener.onMessage(data, false); - } - - @Override - public void close() { - listener.onComplete(); - } - } -} diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/transport/AbstractH2TransportListener.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/transport/AbstractH2TransportListener.java index fe5c341b2060..1033ea26cbd0 100644 --- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/transport/AbstractH2TransportListener.java +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/transport/AbstractH2TransportListener.java @@ -56,7 +56,7 @@ protected Map headersToMap(Http2Headers trailers, Supplier TripleConstant.HEADER_BIN_SUFFIX.length()) { try { String realKey = key.substring(0, key.length() - TripleConstant.HEADER_BIN_SUFFIX.length()); - byte[] value = StreamUtils.decodeASCIIByte(header.getValue()); + byte[] value = StreamUtils.decodeASCIIByte(header.getValue().toString()); attachments.put(realKey, value); } catch (Exception e) { LOGGER.error(PROTOCOL_FAILED_PARSE, "", "", "Failed to parse response attachment key=" + key, e); diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/transport/TripleHttp2FrameServerHandler.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/transport/TripleHttp2FrameServerHandler.java deleted file mode 100644 index daebefc3394d..000000000000 --- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/transport/TripleHttp2FrameServerHandler.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 - * - * http://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.apache.dubbo.rpc.protocol.tri.transport; - -import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; -import org.apache.dubbo.common.logger.LoggerFactory; -import org.apache.dubbo.rpc.HeaderFilter; -import org.apache.dubbo.rpc.PathResolver; -import org.apache.dubbo.rpc.TriRpcStatus; -import org.apache.dubbo.rpc.executor.ExecutorSupport; -import org.apache.dubbo.rpc.model.FrameworkModel; -import org.apache.dubbo.rpc.protocol.tri.compressor.DeCompressor; -import org.apache.dubbo.rpc.protocol.tri.stream.TripleServerStream; - -import java.util.List; -import java.util.concurrent.Executor; - -import io.netty.channel.ChannelDuplexHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.http2.Http2DataFrame; -import io.netty.handler.codec.http2.Http2HeadersFrame; -import io.netty.handler.codec.http2.Http2ResetFrame; -import io.netty.handler.codec.http2.Http2StreamChannel; -import io.netty.util.ReferenceCountUtil; -import io.netty.util.ReferenceCounted; - -import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_FAILED_RESPONSE; - -public class TripleHttp2FrameServerHandler extends ChannelDuplexHandler { - - private static final ErrorTypeAwareLogger LOGGER = - LoggerFactory.getErrorTypeAwareLogger(TripleHttp2FrameServerHandler.class); - private final PathResolver pathResolver; - private final ExecutorSupport executorSupport; - private final String acceptEncoding; - private final TripleServerStream tripleServerStream; - - public TripleHttp2FrameServerHandler( - FrameworkModel frameworkModel, - ExecutorSupport executorSupport, - List filters, - Http2StreamChannel channel, - TripleWriteQueue writeQueue) { - this.executorSupport = executorSupport; - this.acceptEncoding = String.join( - ",", frameworkModel.getExtensionLoader(DeCompressor.class).getSupportedExtensions()); - this.pathResolver = - frameworkModel.getExtensionLoader(PathResolver.class).getDefaultExtension(); - // The executor will be assigned in onHeadersRead method - tripleServerStream = new TripleServerStream( - channel, frameworkModel, null, pathResolver, acceptEncoding, filters, writeQueue); - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - if (msg instanceof Http2HeadersFrame) { - onHeadersRead(ctx, (Http2HeadersFrame) msg); - } else if (msg instanceof Http2DataFrame) { - onDataRead(ctx, (Http2DataFrame) msg); - } else if (msg instanceof ReferenceCounted) { - // ignored - ReferenceCountUtil.release(msg); - } - } - - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - if (evt instanceof Http2ResetFrame) { - onResetRead(ctx, (Http2ResetFrame) evt); - } else { - super.userEventTriggered(ctx, evt); - } - } - - public void onResetRead(ChannelHandlerContext ctx, Http2ResetFrame frame) { - LOGGER.warn( - PROTOCOL_FAILED_RESPONSE, "", "", "Triple Server received remote reset errorCode=" + frame.errorCode()); - if (tripleServerStream != null) { - tripleServerStream.transportObserver.cancelByRemote(frame.errorCode()); - } - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - if (LOGGER.isWarnEnabled()) { - LOGGER.warn(PROTOCOL_FAILED_RESPONSE, "", "", "Exception in processing triple message", cause); - } - TriRpcStatus status = TriRpcStatus.getStatus(cause, "Provider's error:\n" + cause.getMessage()); - tripleServerStream.cancelByLocal(status); - } - - public void onDataRead(ChannelHandlerContext ctx, Http2DataFrame msg) throws Exception { - tripleServerStream.transportObserver.onData(msg.content(), msg.isEndStream()); - } - - public void onHeadersRead(ChannelHandlerContext ctx, Http2HeadersFrame msg) throws Exception { - Executor executor = executorSupport.getExecutor(msg.headers()); - tripleServerStream.setExecutor(executor); - tripleServerStream.transportObserver.onHeader(msg.headers(), msg.isEndStream()); - } -} diff --git a/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/call/ReflectionServerCallTest.java b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/call/ReflectionServerCallTest.java deleted file mode 100644 index c598305ce62d..000000000000 --- a/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/call/ReflectionServerCallTest.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 - * - * http://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.apache.dubbo.rpc.protocol.tri.call; - -import org.apache.dubbo.common.URL; -import org.apache.dubbo.rpc.Invoker; -import org.apache.dubbo.rpc.model.FrameworkModel; -import org.apache.dubbo.rpc.model.MethodDescriptor; -import org.apache.dubbo.rpc.model.ProviderModel; -import org.apache.dubbo.rpc.model.ReflectionMethodDescriptor; -import org.apache.dubbo.rpc.model.ServiceDescriptor; -import org.apache.dubbo.rpc.model.ServiceMetadata; -import org.apache.dubbo.rpc.protocol.tri.DescriptorService; -import org.apache.dubbo.rpc.protocol.tri.HelloReply; -import org.apache.dubbo.rpc.protocol.tri.stream.TripleServerStream; - -import java.lang.reflect.Method; -import java.util.Collections; - -import io.netty.util.concurrent.ImmediateEventExecutor; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; - -import static org.junit.jupiter.api.Assertions.fail; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.when; - -class ReflectionServerCallTest { - - @Test - void doStartCall() throws NoSuchMethodException { - Invoker invoker = Mockito.mock(Invoker.class); - TripleServerStream serverStream = Mockito.mock(TripleServerStream.class); - ProviderModel providerModel = Mockito.mock(ProviderModel.class); - ServiceMetadata serviceMetadata = new ServiceMetadata(); - Method method = DescriptorService.class.getMethod("sayHello", HelloReply.class); - MethodDescriptor methodDescriptor = new ReflectionMethodDescriptor(method); - URL url = Mockito.mock(URL.class); - when(invoker.getUrl()).thenReturn(url); - when(url.getServiceModel()).thenReturn(providerModel); - when(providerModel.getServiceMetadata()).thenReturn(serviceMetadata); - - String service = "testService"; - String methodName = "method"; - try { - ReflectionAbstractServerCall call = new ReflectionAbstractServerCall( - invoker, - serverStream, - new FrameworkModel(), - "", - service, - methodName, - Collections.emptyList(), - ImmediateEventExecutor.INSTANCE); - fail(); - } catch (Exception e) { - // pass - } - - ServiceDescriptor serviceDescriptor = Mockito.mock(ServiceDescriptor.class); - when(serviceDescriptor.getMethods(anyString())).thenReturn(Collections.singletonList(methodDescriptor)); - - when(providerModel.getServiceModel()).thenReturn(serviceDescriptor); - - ReflectionAbstractServerCall call2 = new ReflectionAbstractServerCall( - invoker, - serverStream, - new FrameworkModel(), - "", - service, - methodName, - Collections.emptyList(), - ImmediateEventExecutor.INSTANCE); - call2.onHeader(Collections.emptyMap()); - call2.onMessage(new byte[0], false); - call2.onComplete(); - } -} diff --git a/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/call/StubServerCallTest.java b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/call/StubServerCallTest.java deleted file mode 100644 index b500e91bbc55..000000000000 --- a/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/call/StubServerCallTest.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 - * - * http://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.apache.dubbo.rpc.protocol.tri.call; - -import org.apache.dubbo.common.URL; -import org.apache.dubbo.rpc.Invoker; -import org.apache.dubbo.rpc.model.FrameworkModel; -import org.apache.dubbo.rpc.model.MethodDescriptor.RpcType; -import org.apache.dubbo.rpc.model.ProviderModel; -import org.apache.dubbo.rpc.model.ServiceDescriptor; -import org.apache.dubbo.rpc.model.StubMethodDescriptor; -import org.apache.dubbo.rpc.protocol.tri.stream.TripleServerStream; - -import java.util.Collections; - -import io.netty.util.concurrent.ImmediateEventExecutor; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.when; - -class StubServerCallTest { - - @Test - void doStartCall() throws Exception { - Invoker invoker = Mockito.mock(Invoker.class); - TripleServerStream tripleServerStream = Mockito.mock(TripleServerStream.class); - ProviderModel providerModel = Mockito.mock(ProviderModel.class); - ServiceDescriptor serviceDescriptor = Mockito.mock(ServiceDescriptor.class); - StubMethodDescriptor methodDescriptor = Mockito.mock(StubMethodDescriptor.class); - URL url = Mockito.mock(URL.class); - when(invoker.getUrl()).thenReturn(url); - when(url.getServiceModel()).thenReturn(providerModel); - when(providerModel.getServiceModel()).thenReturn(serviceDescriptor); - when(serviceDescriptor.getMethods(anyString())).thenReturn(Collections.singletonList(methodDescriptor)); - when(methodDescriptor.getRpcType()).thenReturn(RpcType.UNARY); - when(methodDescriptor.parseRequest(any(byte[].class))).thenReturn("test"); - String service = "testService"; - String method = "method"; - StubAbstractServerCall call = new StubAbstractServerCall( - invoker, - tripleServerStream, - new FrameworkModel(), - "", - service, - method, - ImmediateEventExecutor.INSTANCE); - call.onHeader(Collections.emptyMap()); - call.onMessage(new byte[0], false); - call.onComplete(); - } -} diff --git a/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/stream/StreamUtilsTest.java b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/stream/StreamUtilsTest.java index 07d08513e0e5..fc79f4c683c9 100644 --- a/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/stream/StreamUtilsTest.java +++ b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/stream/StreamUtilsTest.java @@ -55,7 +55,7 @@ void testConvertAttachment() throws InterruptedException { attachments.put("Upper", "Upper"); attachments.put("obj", new Object()); - StreamUtils.convertAttachment(headers, attachments, false); + StreamUtils.putHeaders(headers, attachments, false); Assertions.assertNull(headers.get(TripleHeaderEnum.PATH_KEY.getHeader())); Assertions.assertNull(headers.get("Upper")); Assertions.assertNull(headers.get("obj")); @@ -63,7 +63,7 @@ void testConvertAttachment() throws InterruptedException { headers = new DefaultHttp2Headers(); headers.add("key", "value"); - StreamUtils.convertAttachment(headers, attachments, true); + StreamUtils.putHeaders(headers, attachments, true); Assertions.assertNull(headers.get(TripleHeaderEnum.PATH_KEY.getHeader())); Assertions.assertNull(headers.get("Upper")); Assertions.assertNull(headers.get("obj")); @@ -89,7 +89,7 @@ void testConvertAttachment() throws InterruptedException { executorService.execute(() -> { DefaultHttp2Headers headers2 = new DefaultHttp2Headers(); headers2.add("key", "value"); - StreamUtils.convertAttachment(headers2, attachments2, true); + StreamUtils.putHeaders(headers2, attachments2, true); if (headers2.get(TripleHeaderEnum.PATH_KEY.getHeader()) != null) { return;