http protocol detector (#12452)

* http protocol detector

* make differnece between rest and qos http

* set rest http wire protocol spi order  after qos http detector

* make differnce between rest and qos http detector

* add differnt  service to server  url

* adjust code position

* complete some change request

* passing ServiceDeployer to  PortUnificationServer through URL

* merge code and add qos detect ut
This commit is contained in:
suncairong163 2023-06-08 11:13:35 +08:00 committed by GitHub
parent 391d3cdb35
commit 85bc6ea472
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 664 additions and 529 deletions

View File

@ -609,9 +609,7 @@ public interface CommonConstants {
/**
*
* used in JVMUtil.java ,Control stack print lines, default is 32 lines
*
*/
String DUBBO_JSTACK_MAXLINE = "dubbo.jstack-dump.max-line";
@ -634,4 +632,6 @@ public interface CommonConstants {
String DUBBO_PACKABLE_METHOD_FACTORY = "dubbo.application.parameters." + PACKABLE_METHOD_FACTORY_KEY;
String REST_SERVICE_DEPLOYER_URL_ATTRIBUTE_KEY = "restServiceDeployerAttributeKey";
}

View File

@ -22,7 +22,7 @@ import org.apache.dubbo.rpc.model.FrameworkModel;
public class QosDetector implements ProtocolDetector {
private final QosHTTP1Detector qosHTTP1Detector = new QosHTTP1Detector();
private final QosHTTP1Detector qosHTTP1Detector;
private final TelnetDetector telnetDetector;
private boolean QosEnableFlag = true;
@ -32,6 +32,7 @@ public class QosDetector implements ProtocolDetector {
public QosDetector(FrameworkModel frameworkModel) {
this.telnetDetector = new TelnetDetector(frameworkModel);
qosHTTP1Detector = new QosHTTP1Detector(frameworkModel);
}
@Override

View File

@ -16,24 +16,61 @@
*/
package org.apache.dubbo.qos.pu;
import org.apache.dubbo.remoting.api.ProtocolDetector;
import org.apache.dubbo.qos.api.BaseCommand;
import org.apache.dubbo.remoting.api.AbstractHttpProtocolDetector;
import org.apache.dubbo.remoting.buffer.ChannelBuffer;
import org.apache.dubbo.rpc.model.FrameworkModel;
public class QosHTTP1Detector implements ProtocolDetector {
private static boolean isHttp(int magic) {
return magic == 'G' || magic == 'P';
public class QosHTTP1Detector extends AbstractHttpProtocolDetector {
private static final char[][] QOS_METHODS_PREFIX = getQOSHttpMethodsPrefix();
FrameworkModel frameworkModel;
public QosHTTP1Detector(FrameworkModel frameworkModel) {
this.frameworkModel = frameworkModel;
}
@Override
public Result detect(ChannelBuffer in) {
if (in.readableBytes() < 2) {
return Result.needMoreData();
}
final int magic = in.getByte(in.readerIndex());
// h2 starts with "PR"
if (isHttp(magic) && in.getByte(in.readerIndex()+1) != 'R' ){
if (prefixMatch(QOS_METHODS_PREFIX, in, 3)) {
// make distinguish from rest ,read request url
String requestURL = readRequestLine(in);
// url split by / length judge
if (!isQosRequestURL(requestURL)) {
return Result.unrecognized();
}
// command exist judge, when /cmd or /cmd/appName we prefer response by rest http
// decrease the affect to user
BaseCommand command = commandExist(requestURL);
if (command == null) {
return Result.unrecognized();
}
return Result.recognized();
}
return Result.unrecognized();
}
private BaseCommand commandExist(String requestURL) {
BaseCommand command = null;
try {
String cmd = splitAndGetFirst(requestURL);
command = frameworkModel.getExtensionLoader(BaseCommand.class).getExtension(cmd);
} catch (Throwable throwable) {
//can't find command
}
return command;
}
}

View File

@ -18,9 +18,12 @@
package org.apache.dubbo.qos.pu;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.remoting.api.ProtocolDetector;
import org.apache.dubbo.remoting.api.pu.ChannelOperator;
import org.apache.dubbo.remoting.buffer.HeapChannelBuffer;
import org.apache.dubbo.rpc.model.FrameworkModel;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import static org.mockito.ArgumentMatchers.anyList;
@ -38,4 +41,25 @@ class QosWireProtocolTest {
verify(channelOperator).configChannelHandler(anyList());
}
@Test
void testQosHttp1Detector() {
QosHTTP1Detector qosHTTP1Detector = new QosHTTP1Detector(FrameworkModel.defaultModel());
HeapChannelBuffer heapChannelBuffer = new HeapChannelBuffer(30);
heapChannelBuffer.writeBytes("GET /ls HTTP/1.1".getBytes());
ProtocolDetector.Result detect = qosHTTP1Detector.detect(heapChannelBuffer);
Assertions.assertEquals(ProtocolDetector.Result.recognized().flag(), detect.flag());
heapChannelBuffer = new HeapChannelBuffer(30);
heapChannelBuffer.writeBytes("GET /ls/appName HTTP/1.1".getBytes());
detect = qosHTTP1Detector.detect(heapChannelBuffer);
Assertions.assertEquals(ProtocolDetector.Result.recognized().flag(), detect.flag());
heapChannelBuffer = new HeapChannelBuffer(30);
heapChannelBuffer.writeBytes("GET /rest/demo HTTP/1.1".getBytes());
detect = qosHTTP1Detector.detect(heapChannelBuffer);
Assertions.assertEquals(ProtocolDetector.Result.unrecognized().flag(), detect.flag());
}
}

View File

@ -18,6 +18,9 @@
package org.apache.dubbo.remoting;
import java.util.Arrays;
import java.util.List;
public interface Constants {
String BUFFER_KEY = "buffer";
@ -161,4 +164,8 @@ public interface Constants {
String OK_HTTP = "ok-http";
String URL_CONNECTION = "url-connection";
String APACHE_HTTP_CLIENT = "apache-http-client";
String PORT_UNIFICATION_NETTY4_SERVER = "netty4";
List<String> REST_SERVER = Arrays.asList("jetty", "tomcat", "netty");
}

View File

@ -0,0 +1,176 @@
/*
* 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.api;
import org.apache.dubbo.remoting.buffer.ChannelBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static org.apache.dubbo.remoting.api.AbstractHttpProtocolDetector.HttpMethod.DELETE;
import static org.apache.dubbo.remoting.api.AbstractHttpProtocolDetector.HttpMethod.GET;
import static org.apache.dubbo.remoting.api.AbstractHttpProtocolDetector.HttpMethod.HEAD;
import static org.apache.dubbo.remoting.api.AbstractHttpProtocolDetector.HttpMethod.OPTIONS;
import static org.apache.dubbo.remoting.api.AbstractHttpProtocolDetector.HttpMethod.PATCH;
import static org.apache.dubbo.remoting.api.AbstractHttpProtocolDetector.HttpMethod.POST;
import static org.apache.dubbo.remoting.api.AbstractHttpProtocolDetector.HttpMethod.PUT;
import static org.apache.dubbo.remoting.api.AbstractHttpProtocolDetector.HttpMethod.TRACE;
/**
* http protocol detector
*/
public abstract class AbstractHttpProtocolDetector implements ProtocolDetector {
protected int empty = ' ';
protected static String SIMPLE_HTTP = "XXX HTTP/1";
protected static final List<HttpMethod> QOS_HTTP_METHOD = Arrays.asList(GET, POST);
/**
* rank by frequency
* first GET ,POST,DELETE,PUT
* second HEAD,PATCH,OPTIONS,TRACE
*/
protected static final List<HttpMethod> HTTP_METHODS = Arrays.asList(GET, POST, DELETE, PUT, HEAD, PATCH, OPTIONS, TRACE);
protected static char[][] getHttpMethodsPrefix(int length, List<HttpMethod> httpMethods) {
if (0 >= length || length > 3) {
throw new IllegalArgumentException("Current substring length is beyond Http methods length");
}
List<char[]> prefix = new ArrayList<>();
for (HttpMethod httpMethod : httpMethods) {
prefix.add(httpMethod.getValue().substring(0, length).toCharArray());
}
return prefix.toArray(new char[0][]);
}
protected static char[][] getHttpMethodsPrefix() {
return getHttpMethodsPrefix(3, HTTP_METHODS);
}
protected static char[][] getQOSHttpMethodsPrefix() {
return getHttpMethodsPrefix(3, QOS_HTTP_METHOD);
}
/**
* qos /name/appName
*
* @param requestUrl
* @return
*/
protected boolean isQosRequestURL(String requestUrl) {
if (requestUrl == null) {
return false;
}
String[] split = requestUrl.split("/");
if (split.length <= 3) {
return true;
}
return false;
}
protected String splitAndGetFirst(String str) {
return splitAndGet(str, 1);
}
protected String splitAndGet(String str, int index) {
if (str == null) {
return null;
}
String[] split = str.split("/");
if (split.length - 1 < index) {
return null;
}
return split[index];
}
/**
* between first and second empty char
*
* @param buffer
* @return
*/
protected String readRequestLine(ChannelBuffer buffer) {
// GET /test/demo HTTP/1.1
int firstEmptyIndex = 0;
// read first empty
for (int i = 0; i < Integer.MAX_VALUE; i++) {
int read = getByteByIndex(buffer, i);
if (read == empty) {
firstEmptyIndex = i;
break;
}
}
StringBuilder stringBuilder = new StringBuilder();
for (int i = firstEmptyIndex + 1; i < Integer.MAX_VALUE; i++) {
int read = getByteByIndex(buffer, i);
// second empty break
if (read == empty) {
break;
}
stringBuilder.append((char) read);
}
return stringBuilder.toString();
}
public enum HttpMethod {
GET("GET"),
HEAD("HEAD"),
POST("POST"),
PUT("PUT"),
PATCH("PATCH"),
DELETE("DELETE"),
OPTIONS("OPTIONS"),
TRACE("TRACE");
HttpMethod(String value) {
this.value = value;
}
private String value;
public String getValue() {
return value;
}
}
}

View File

@ -74,4 +74,33 @@ public interface ProtocolDetector {
enum Flag {
RECOGNIZED, UNRECOGNIZED, NEED_MORE_DATA
}
default int getByteByIndex(ChannelBuffer buffer, int index) {
return buffer.getByte(buffer.readerIndex() + index);
}
default boolean prefixMatch(char[][] prefixes, ChannelBuffer buffer, int length) {
int[] ints = new int[length];
for (int i = 0; i < length; i++) {
ints[i] = getByteByIndex(buffer, i);
}
// prefix match
for (char[] prefix : prefixes) {
boolean matched = true;
for (int j = 0; j < length; j++) {
if (prefix[j] != ints[j]) {
matched = false;
break;
}
}
if (matched) {
return true;
}
}
return false;
}
}

View File

@ -30,6 +30,7 @@ import org.apache.dubbo.remoting.api.pu.ChannelOperator;
import org.apache.dubbo.remoting.api.pu.DefaultCodec;
import org.apache.dubbo.remoting.transport.codec.CodecAdapter;
import java.util.Collection;
import java.util.List;
public class NettyConfigOperator implements ChannelOperator {
@ -55,14 +56,14 @@ public class NettyConfigOperator implements ChannelOperator {
}
if (url.getOrDefaultFrameworkModel().getExtensionLoader(Codec2.class).hasExtension(codecName)) {
codec2 = url.getOrDefaultFrameworkModel().getExtensionLoader(Codec2.class).getExtension(codecName);
} else if(url.getOrDefaultFrameworkModel().getExtensionLoader(Codec.class).hasExtension(codecName)){
} else if (url.getOrDefaultFrameworkModel().getExtensionLoader(Codec.class).hasExtension(codecName)) {
codec2 = new CodecAdapter(url.getOrDefaultFrameworkModel().getExtensionLoader(Codec.class)
.getExtension(codecName));
}else {
} else {
codec2 = url.getOrDefaultFrameworkModel().getExtensionLoader(Codec2.class).getExtension("default");
}
if (!(codec2 instanceof DefaultCodec)){
if (!(codec2 instanceof DefaultCodec)) {
((NettyChannel) channel).setCodec(codec2);
NettyCodecAdapter codec = new NettyCodecAdapter(codec2, channel.getUrl(), handler);
((NettyChannel) channel).getNioChannel().pipeline().addLast(
@ -72,21 +73,17 @@ public class NettyConfigOperator implements ChannelOperator {
);
}
for (ChannelHandler handler: handlerList) {
for (ChannelHandler handler : handlerList) {
if (handler instanceof ChannelHandlerPretender) {
Object realHandler = ((ChannelHandlerPretender) handler).getRealHandler();
if(realHandler instanceof io.netty.channel.ChannelHandler) {
((NettyChannel) channel).getNioChannel().pipeline().addLast(
(io.netty.channel.ChannelHandler) realHandler
);
}
addRealHandler(realHandler);
}
}
// todo distinguish between client and server channel
if( isClientSide(channel)){
if (isClientSide(channel)) {
//todo config client channel handler
}else {
} else {
NettyServerHandler sh = new NettyServerHandler(channel.getUrl(), handler);
((NettyChannel) channel).getNioChannel().pipeline().addLast(
sh
@ -94,6 +91,28 @@ public class NettyConfigOperator implements ChannelOperator {
}
}
private void addRealHandler(Object realHandler) {
if (realHandler instanceof Collection) {
Collection realHandlers = (Collection) realHandler;
for (Object handler : realHandlers) {
addChannelHandler(handler);
}
} else {
addChannelHandler(realHandler);
}
}
private void addChannelHandler(Object channelHandler) {
if (!(channelHandler instanceof io.netty.channel.ChannelHandler)) {
return;
}
((NettyChannel) channel).getNioChannel().pipeline().addLast((io.netty.channel.ChannelHandler) channelHandler);
}
public void setDetectResult(ProtocolDetector.Result detectResult) {
this.detectResult = detectResult;
}

View File

@ -103,6 +103,7 @@ public class NettyPortUnificationServerHandler extends ByteToMessageDecoder {
enableSsl(ctx, providerConnectionConfig);
} else {
for (final WireProtocol protocol : protocols) {
in.markReaderIndex();
ChannelBuffer buf = new NettyBackedChannelBuffer(in);
final ProtocolDetector.Result result = protocol.detector().detect(buf);

View File

@ -25,7 +25,7 @@
<artifactId>dubbo-rpc-rest</artifactId>
<packaging>jar</packaging>
<name>${project.artifactId}</name>
<description>The JAX-RS rpc module of dubbo project</description>
<description>The rest rpc module of dubbo project</description>
<properties>
<skip_maven_deploy>false</skip_maven_deploy>
</properties>
@ -40,6 +40,12 @@
<artifactId>dubbo-remoting-http</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-remoting-netty4</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-cluster</artifactId>
@ -119,12 +125,6 @@
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-codec-http</artifactId>

View File

@ -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.rest;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelOption;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.utils.NetUtils;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.metadata.rest.PathMatcher;
import org.apache.dubbo.metadata.rest.RestMethodMetadata;
import org.apache.dubbo.metadata.rest.ServiceRestMetadata;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.protocol.rest.constans.RestConstant;
import org.apache.dubbo.rpc.protocol.rest.exception.mapper.ExceptionMapper;
import org.apache.dubbo.rpc.protocol.rest.handler.NettyHttpHandler;
import org.apache.dubbo.rpc.protocol.rest.netty.NettyServer;
import org.apache.dubbo.rpc.protocol.rest.netty.RestHttpRequestDecoder;
import org.apache.dubbo.rpc.protocol.rest.netty.UnSharedHandlerCreator;
import org.apache.dubbo.rpc.protocol.rest.netty.ssl.SslServerTlsHandler;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import static org.apache.dubbo.common.constants.CommonConstants.BACKLOG_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SPLIT_PATTERN;
import static org.apache.dubbo.common.constants.CommonConstants.IO_THREADS_KEY;
import static org.apache.dubbo.remoting.Constants.BIND_IP_KEY;
import static org.apache.dubbo.remoting.Constants.BIND_PORT_KEY;
import static org.apache.dubbo.remoting.Constants.DEFAULT_IO_THREADS;
/**
* netty http server
*/
public class NettyHttpRestServer implements RestProtocolServer {
private final PathAndInvokerMapper pathAndInvokerMapper = new PathAndInvokerMapper();
private final ExceptionMapper exceptionMapper = new ExceptionMapper();
private NettyServer server = getNettyServer();
/**
* for triple override
*
* @return
*/
protected NettyServer getNettyServer() {
return new NettyServer();
}
private String address;
private final Map<String, Object> attributes = new ConcurrentHashMap<>();
@Override
public String getAddress() {
return address;
}
@Override
public void setAddress(String address) {
this.address = address;
}
@Override
public void close() {
server.stop();
}
@Override
public Map<String, Object> getAttributes() {
return attributes;
}
@Override
public void start(URL url) {
registerExceptionMapper(url);
String bindIp = url.getParameter(BIND_IP_KEY, url.getHost());
if (!url.isAnyHost() && NetUtils.isValidLocalHost(bindIp)) {
server.setHostname(bindIp);
}
server.setPort(url.getParameter(BIND_PORT_KEY, url.getPort()));
// child options
server.setChildChannelOptions(getChildChannelOptionMap(url));
// set options
server.setChannelOptions(getChannelOptionMap(url));
// set unshared callback
server.setUnSharedHandlerCallBack(getUnSharedHttpChannelHandlers());
// set channel handler and @Shared
server.setChannelHandlers(getChannelHandlers(url));
server.setIoWorkerCount(url.getParameter(IO_THREADS_KEY, DEFAULT_IO_THREADS));
server.start(url);
}
private UnSharedHandlerCreator getUnSharedHttpChannelHandlers() {
return new UnSharedHandlerCreator() {
@Override
public List<ChannelHandler> getUnSharedHandlers(URL url) {
return Arrays.asList(
// add SslServerTlsHandler
new SslServerTlsHandler(url),
new HttpRequestDecoder(
url.getParameter(RestConstant.MAX_INITIAL_LINE_LENGTH_PARAM, RestConstant.MAX_INITIAL_LINE_LENGTH),
url.getParameter(RestConstant.MAX_HEADER_SIZE_PARAM, RestConstant.MAX_HEADER_SIZE),
url.getParameter(RestConstant.MAX_CHUNK_SIZE_PARAM, RestConstant.MAX_CHUNK_SIZE)),
new HttpObjectAggregator(url.getParameter(RestConstant.MAX_REQUEST_SIZE_PARAM, RestConstant.MAX_REQUEST_SIZE)),
new HttpResponseEncoder(), new RestHttpRequestDecoder(new NettyHttpHandler(pathAndInvokerMapper, exceptionMapper), url))
;
}
};
}
/**
* create child channel options map
*
* @param url
* @return
*/
protected Map<ChannelOption, Object> getChildChannelOptionMap(URL url) {
Map<ChannelOption, Object> channelOption = new HashMap<>();
channelOption.put(ChannelOption.SO_KEEPALIVE, url.getParameter(Constants.KEEP_ALIVE_KEY, Constants.DEFAULT_KEEP_ALIVE));
return channelOption;
}
/**
* create channel options map
*
* @param url
* @return
*/
protected Map<ChannelOption, Object> getChannelOptionMap(URL url) {
Map<ChannelOption, Object> options = new HashMap<>();
options.put(ChannelOption.SO_REUSEADDR, Boolean.TRUE);
options.put(ChannelOption.TCP_NODELAY, Boolean.TRUE);
options.put(ChannelOption.SO_BACKLOG, url.getPositiveParameter(BACKLOG_KEY, org.apache.dubbo.remoting.Constants.DEFAULT_BACKLOG));
options.put(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
return options;
}
/**
* create channel handler
*
* @param url
* @return
*/
protected List<ChannelHandler> getChannelHandlers(URL url) {
List<ChannelHandler> channelHandlers = new ArrayList<>();
return channelHandlers;
}
@Override
public void deploy(ServiceRestMetadata serviceRestMetadata, Invoker invoker) {
Map<PathMatcher, RestMethodMetadata> pathToServiceMapContainPathVariable =
serviceRestMetadata.getPathContainPathVariableToServiceMap();
pathAndInvokerMapper.addPathAndInvoker(pathToServiceMapContainPathVariable, invoker);
Map<PathMatcher, RestMethodMetadata> pathToServiceMapUnContainPathVariable =
serviceRestMetadata.getPathUnContainPathVariableToServiceMap();
pathAndInvokerMapper.addPathAndInvoker(pathToServiceMapUnContainPathVariable, invoker);
}
@Override
public void undeploy(ServiceRestMetadata serviceRestMetadata) {
Map<PathMatcher, RestMethodMetadata> pathToServiceMapContainPathVariable =
serviceRestMetadata.getPathContainPathVariableToServiceMap();
pathToServiceMapContainPathVariable.keySet().stream().forEach(pathAndInvokerMapper::removePath);
Map<PathMatcher, RestMethodMetadata> pathToServiceMapUnContainPathVariable =
serviceRestMetadata.getPathUnContainPathVariableToServiceMap();
pathToServiceMapUnContainPathVariable.keySet().stream().forEach(pathAndInvokerMapper::removePath);
}
private void registerExceptionMapper(URL url) {
for (String clazz : COMMA_SPLIT_PATTERN.split(url.getParameter(Constants.EXTENSION_KEY, RpcExceptionMapper.class.getName()))) {
if (!StringUtils.isEmpty(clazz)) {
exceptionMapper.registerMapper(clazz);
}
}
}
}

View File

@ -19,6 +19,8 @@ package org.apache.dubbo.rpc.protocol.rest;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.utils.ConcurrentHashMapUtils;
import org.apache.dubbo.metadata.rest.ServiceRestMetadata;
import org.apache.dubbo.remoting.api.pu.DefaultPuHandler;
import org.apache.dubbo.remoting.exchange.PortUnificationExchanger;
import org.apache.dubbo.remoting.http.RestClient;
import org.apache.dubbo.remoting.http.factory.RestClientFactory;
import org.apache.dubbo.rpc.Exporter;
@ -30,6 +32,8 @@ import org.apache.dubbo.rpc.protocol.AbstractExporter;
import org.apache.dubbo.rpc.protocol.AbstractProtocol;
import org.apache.dubbo.rpc.protocol.rest.annotation.consumer.HttpConnectionPreBuildIntercept;
import org.apache.dubbo.rpc.protocol.rest.annotation.metadata.MetadataResolver;
import org.apache.dubbo.rpc.protocol.rest.deploy.ServiceDeployer;
import org.apache.dubbo.rpc.protocol.rest.deploy.ServiceDeployerManager;
import java.util.Map;
@ -39,17 +43,14 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.REST_SERVICE_DEPLOYER_URL_ATTRIBUTE_KEY;
import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_ERROR_CLOSE_CLIENT;
import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_ERROR_CLOSE_SERVER;
import static org.apache.dubbo.remoting.Constants.SERVER_KEY;
import static org.apache.dubbo.rpc.protocol.rest.constans.RestConstant.PATH_SEPARATOR;
public class RestProtocol extends AbstractProtocol {
private static final int DEFAULT_PORT = 80;
private static final String DEFAULT_SERVER = Constants.NETTY_HTTP;
private final RestServerFactory serverFactory = new RestServerFactory();
private final ConcurrentMap<String, ReferenceCountedClient<? extends RestClient>> clients = new ConcurrentHashMap<>();
@ -89,24 +90,22 @@ public class RestProtocol extends AbstractProtocol {
url, getContextPath(url));
// TODO add Extension filter
// create rest server
RestProtocolServer server = (RestProtocolServer) ConcurrentHashMapUtils.computeIfAbsent(serverMap, getAddr(url), restServer -> {
RestProtocolServer s = serverFactory.createServer(url.getParameter(SERVER_KEY, DEFAULT_SERVER));
s.setAddress(url.getAddress());
s.start(url);
return s;
});
// deploy service
URL newURL = ServiceDeployerManager.deploy(url, serviceRestMetadata, invoker);
// create server
PortUnificationExchanger.bind(newURL, new DefaultPuHandler());
ServiceDeployer serviceDeployer = (ServiceDeployer) newURL.getAttribute(REST_SERVICE_DEPLOYER_URL_ATTRIBUTE_KEY);
server.deploy(serviceRestMetadata, invoker);
URL finalUrl = newURL;
exporter = new AbstractExporter<T>(invoker) {
@Override
public void afterUnExport() {
destroyInternal(url);
destroyInternal(finalUrl);
exporterMap.remove(uri);
server.undeploy(serviceRestMetadata);
serviceDeployer.undeploy(serviceRestMetadata);
}
};
exporterMap.put(uri, exporter);
@ -164,6 +163,10 @@ public class RestProtocol extends AbstractProtocol {
if (logger.isInfoEnabled()) {
logger.info("Destroying protocol [" + this.getClass().getSimpleName() + "] ...");
}
PortUnificationExchanger.close();
ServiceDeployerManager.close();
super.destroy();
for (Map.Entry<String, ProtocolServer> entry : serverMap.entrySet()) {

View File

@ -1,34 +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.rest;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.metadata.rest.ServiceRestMetadata;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.ProtocolServer;
public interface RestProtocolServer extends ProtocolServer {
void start(URL url);
void deploy(ServiceRestMetadata serviceRestMetadata, Invoker invoker);
void undeploy(ServiceRestMetadata serviceRestMetadata);
}

View File

@ -1,30 +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.rest;
/**
* Only the server that implements servlet container
* could support something like @Context injection of servlet objects.
*/
public class RestServerFactory {
public RestProtocolServer createServer(String name) {
return new NettyHttpRestServer();
}
}

View File

@ -0,0 +1,77 @@
/*
* 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.rest.deploy;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.metadata.rest.PathMatcher;
import org.apache.dubbo.metadata.rest.RestMethodMetadata;
import org.apache.dubbo.metadata.rest.ServiceRestMetadata;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.protocol.rest.Constants;
import org.apache.dubbo.rpc.protocol.rest.PathAndInvokerMapper;
import org.apache.dubbo.rpc.protocol.rest.RpcExceptionMapper;
import org.apache.dubbo.rpc.protocol.rest.exception.mapper.ExceptionMapper;
import java.util.Map;
import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SPLIT_PATTERN;
public class ServiceDeployer {
private final PathAndInvokerMapper pathAndInvokerMapper = new PathAndInvokerMapper();
private final ExceptionMapper exceptionMapper = new ExceptionMapper();
public void deploy(ServiceRestMetadata serviceRestMetadata, Invoker invoker) {
Map<PathMatcher, RestMethodMetadata> pathToServiceMapContainPathVariable =
serviceRestMetadata.getPathContainPathVariableToServiceMap();
pathAndInvokerMapper.addPathAndInvoker(pathToServiceMapContainPathVariable, invoker);
Map<PathMatcher, RestMethodMetadata> pathToServiceMapUnContainPathVariable =
serviceRestMetadata.getPathUnContainPathVariableToServiceMap();
pathAndInvokerMapper.addPathAndInvoker(pathToServiceMapUnContainPathVariable, invoker);
}
public void undeploy(ServiceRestMetadata serviceRestMetadata) {
Map<PathMatcher, RestMethodMetadata> pathToServiceMapContainPathVariable =
serviceRestMetadata.getPathContainPathVariableToServiceMap();
pathToServiceMapContainPathVariable.keySet().stream().forEach(pathAndInvokerMapper::removePath);
Map<PathMatcher, RestMethodMetadata> pathToServiceMapUnContainPathVariable =
serviceRestMetadata.getPathUnContainPathVariableToServiceMap();
pathToServiceMapUnContainPathVariable.keySet().stream().forEach(pathAndInvokerMapper::removePath);
}
public void registerExceptionMapper(URL url) {
for (String clazz : COMMA_SPLIT_PATTERN.split(url.getParameter(Constants.EXTENSION_KEY, RpcExceptionMapper.class.getName()))) {
if (!StringUtils.isEmpty(clazz)) {
exceptionMapper.registerMapper(clazz);
}
}
}
public PathAndInvokerMapper getPathAndInvokerMapper() {
return pathAndInvokerMapper;
}
public ExceptionMapper getExceptionMapper() {
return exceptionMapper;
}
}

View File

@ -0,0 +1,80 @@
/*
* 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.rest.deploy;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.utils.ConcurrentHashMapUtils;
import org.apache.dubbo.metadata.rest.ServiceRestMetadata;
import org.apache.dubbo.rpc.Invoker;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicBoolean;
import static org.apache.dubbo.common.constants.CommonConstants.REST_SERVICE_DEPLOYER_URL_ATTRIBUTE_KEY;
import static org.apache.dubbo.remoting.Constants.PORT_UNIFICATION_NETTY4_SERVER;
import static org.apache.dubbo.remoting.Constants.REST_SERVER;
import static org.apache.dubbo.remoting.Constants.SERVER_KEY;
public class ServiceDeployerManager {
private static final ConcurrentMap<String, ServiceDeployer> serviceDeployers = new ConcurrentHashMap<>();
public static URL deploy(final URL currentURL, ServiceRestMetadata serviceRestMetadata, Invoker invoker) {
AtomicBoolean isNewCreate = new AtomicBoolean();
ServiceDeployer newServiceDeployer = ConcurrentHashMapUtils.computeIfAbsent(serviceDeployers, currentURL.getAddress(), address -> {
ServiceDeployer serviceDeployer = new ServiceDeployer();
isNewCreate.set(true);
return serviceDeployer;
});
// register service
newServiceDeployer.deploy(serviceRestMetadata, invoker);
// register exception mapper
newServiceDeployer.registerExceptionMapper(currentURL);
// not new URL
if (!isNewCreate.get()) {
return currentURL;
}
URL tmp = currentURL;
// adapt to older rest versions
if (REST_SERVER.contains(tmp.getParameter(SERVER_KEY))) {
tmp = tmp.addParameter(SERVER_KEY, PORT_UNIFICATION_NETTY4_SERVER);
}
// passing ServiceDeployer to PortUnificationServer through URL
// add attribute for server build
tmp = tmp.putAttribute(REST_SERVICE_DEPLOYER_URL_ATTRIBUTE_KEY, newServiceDeployer);
return tmp;
}
public static void close() {
serviceDeployers.clear();
}
}

View File

@ -29,6 +29,7 @@ import org.apache.dubbo.rpc.RpcInvocation;
import org.apache.dubbo.rpc.protocol.rest.PathAndInvokerMapper;
import org.apache.dubbo.rpc.protocol.rest.RestRPCInvocationUtil;
import org.apache.dubbo.rpc.protocol.rest.RestHeaderEnum;
import org.apache.dubbo.rpc.protocol.rest.deploy.ServiceDeployer;
import org.apache.dubbo.rpc.protocol.rest.exception.MediaTypeUnSupportException;
import org.apache.dubbo.rpc.protocol.rest.exception.ParamParseException;
import org.apache.dubbo.rpc.protocol.rest.exception.PathNoFoundException;
@ -58,6 +59,11 @@ public class NettyHttpHandler implements HttpHandler<NettyRequestFacade, NettyHt
this.exceptionMapper = exceptionMapper;
}
public NettyHttpHandler(ServiceDeployer serviceDeployer) {
this.pathAndInvokerMapper = serviceDeployer.getPathAndInvokerMapper();
this.exceptionMapper = serviceDeployer.getExceptionMapper();
}
@Override
public void handle(NettyRequestFacade requestFacade, NettyHttpResponse nettyHttpResponse) throws IOException {

View File

@ -1,184 +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.rest.netty;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.timeout.IdleStateHandler;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.utils.NamedThreadFactory;
import org.apache.dubbo.rpc.protocol.rest.constans.RestConstant;
import java.net.InetSocketAddress;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import static org.apache.dubbo.remoting.Constants.EVENT_LOOP_BOSS_POOL_NAME;
import static org.apache.dubbo.remoting.Constants.EVENT_LOOP_WORKER_POOL_NAME;
public class NettyServer {
protected ServerBootstrap bootstrap = new ServerBootstrap();
protected String hostname = null;
protected int configuredPort = 8080;
protected int runtimePort = -1;
private EventLoopGroup eventLoopGroup;
private EventLoopGroup workerLoopGroup;
private int ioWorkerCount = Runtime.getRuntime().availableProcessors() * 2;
private List<ChannelHandler> channelHandlers = Collections.emptyList();
private Map<ChannelOption, Object> channelOptions = Collections.emptyMap();
private Map<ChannelOption, Object> childChannelOptions = Collections.emptyMap();
private UnSharedHandlerCreator unSharedHandlerCallBack;
public NettyServer() {
}
/**
* Specify the worker count to use. For more information about this please see the javadocs of {@link EventLoopGroup}
*
* @param ioWorkerCount worker count
*/
public void setIoWorkerCount(int ioWorkerCount) {
this.ioWorkerCount = ioWorkerCount;
}
public String getHostname() {
return hostname;
}
public void setHostname(String hostname) {
this.hostname = hostname;
}
public int getPort() {
return runtimePort > 0 ? runtimePort : configuredPort;
}
public void setPort(int port) {
this.configuredPort = port;
}
/**
* Add additional {@link io.netty.channel.ChannelHandler}s to the {@link io.netty.bootstrap.ServerBootstrap}.
* <p>The additional channel handlers are being added <em>before</em> the HTTP handling.</p>
*
* @param channelHandlers the additional {@link io.netty.channel.ChannelHandler}s.
*/
public void setChannelHandlers(final List<ChannelHandler> channelHandlers) {
this.channelHandlers = channelHandlers == null ? Collections.<ChannelHandler>emptyList() : channelHandlers;
}
/**
* Add Netty {@link io.netty.channel.ChannelOption}s to the {@link io.netty.bootstrap.ServerBootstrap}.
*
* @param channelOptions the additional {@link io.netty.channel.ChannelOption}s.
* @see io.netty.bootstrap.ServerBootstrap#option(io.netty.channel.ChannelOption, Object)
*/
public void setChannelOptions(final Map<ChannelOption, Object> channelOptions) {
this.channelOptions = channelOptions == null ? Collections.<ChannelOption, Object>emptyMap() : channelOptions;
}
/**
* Add child options to the {@link io.netty.bootstrap.ServerBootstrap}.
*
* @param channelOptions the additional child {@link io.netty.channel.ChannelOption}s.
* @see io.netty.bootstrap.ServerBootstrap#childOption(io.netty.channel.ChannelOption, Object)
*/
public void setChildChannelOptions(final Map<ChannelOption, Object> channelOptions) {
this.childChannelOptions = channelOptions == null ? Collections.<ChannelOption, Object>emptyMap() : channelOptions;
}
public void setUnSharedHandlerCallBack(UnSharedHandlerCreator unSharedHandlerCallBack) {
this.unSharedHandlerCallBack = unSharedHandlerCallBack;
}
public void start(URL url) {
eventLoopGroup = new NioEventLoopGroup(1, new NamedThreadFactory(EVENT_LOOP_BOSS_POOL_NAME));
workerLoopGroup = new NioEventLoopGroup(ioWorkerCount, new NamedThreadFactory(EVENT_LOOP_WORKER_POOL_NAME));
// Configure the server.
bootstrap.group(eventLoopGroup, workerLoopGroup)
.channel(NioServerSocketChannel.class)
.childHandler(setupHandlers(url));
for (Map.Entry<ChannelOption, Object> entry : channelOptions.entrySet()) {
bootstrap.option(entry.getKey(), entry.getValue());
}
for (Map.Entry<ChannelOption, Object> entry : childChannelOptions.entrySet()) {
bootstrap.childOption(entry.getKey(), entry.getValue());
}
final InetSocketAddress socketAddress;
if (null == getHostname() || getHostname().isEmpty()) {
socketAddress = new InetSocketAddress(configuredPort);
} else {
socketAddress = new InetSocketAddress(hostname, configuredPort);
}
Channel channel = bootstrap.bind(socketAddress).syncUninterruptibly().channel();
runtimePort = ((InetSocketAddress) channel.localAddress()).getPort();
}
protected ChannelHandler setupHandlers(URL url) {
return new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline channelPipeline = ch.pipeline();
int idleTimeout = url.getParameter(RestConstant.IDLE_TIMEOUT_PARAM, RestConstant.IDLE_TIMEOUT);
if (idleTimeout > 0) {
channelPipeline.addLast(new IdleStateHandler(0, 0, idleTimeout));
}
channelPipeline.addLast(channelHandlers.toArray(new ChannelHandler[channelHandlers.size()]));
List<ChannelHandler> unSharedHandlers = unSharedHandlerCallBack.getUnSharedHandlers(url);
for (ChannelHandler unSharedHandler : unSharedHandlers) {
channelPipeline.addLast(unSharedHandler);
}
}
};
}
public void stop() {
runtimePort = -1;
eventLoopGroup.shutdownGracefully();
}
}

View File

@ -0,0 +1,52 @@
/*
* 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.rest.pu;
import org.apache.dubbo.remoting.api.AbstractHttpProtocolDetector;
import org.apache.dubbo.remoting.buffer.ChannelBuffer;
import org.apache.dubbo.rpc.model.FrameworkModel;
/**
* rest http protocol detector
*/
public class RestHttp1Detector extends AbstractHttpProtocolDetector {
private static final char[][] HTTP_METHODS_PREFIX = getHttpMethodsPrefix();
private FrameworkModel frameworkModel;
public RestHttp1Detector(FrameworkModel frameworkModel) {
this.frameworkModel = frameworkModel;
}
@Override
public Result detect(ChannelBuffer in) {
int i = in.readableBytes();
// length judge
if (i < SIMPLE_HTTP.length()) {
return Result.unrecognized();
}
if (prefixMatch(HTTP_METHODS_PREFIX, in, 3)) {
return Result.recognized();
}
return Result.unrecognized();
}
}

View File

@ -0,0 +1,79 @@
/*
* 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.rest.pu;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.remoting.ChannelHandler;
import org.apache.dubbo.remoting.api.AbstractWireProtocol;
import org.apache.dubbo.remoting.api.pu.ChannelHandlerPretender;
import org.apache.dubbo.remoting.api.pu.ChannelOperator;
import org.apache.dubbo.rpc.model.FrameworkModel;
import org.apache.dubbo.rpc.model.ScopeModelAware;
import org.apache.dubbo.rpc.protocol.rest.constans.RestConstant;
import org.apache.dubbo.rpc.protocol.rest.deploy.ServiceDeployer;
import org.apache.dubbo.rpc.protocol.rest.handler.NettyHttpHandler;
import org.apache.dubbo.rpc.protocol.rest.netty.RestHttpRequestDecoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static org.apache.dubbo.common.constants.CommonConstants.REST_SERVICE_DEPLOYER_URL_ATTRIBUTE_KEY;
@Activate(order = Integer.MAX_VALUE)
public class RestHttp1WireProtocol extends AbstractWireProtocol implements ScopeModelAware {
private static final ServiceDeployer emptyServiceDeployer = new ServiceDeployer();
public RestHttp1WireProtocol(FrameworkModel frameworkModel) {
super(new RestHttp1Detector(frameworkModel));
}
@Override
public void configServerProtocolHandler(URL url, ChannelOperator operator) {
// h1 upgrade to don`t response 101 ,cause there is no h1 stream handler
// TODO add h1 stream handler
// pathAndInvokerMapper, exceptionMapper getFrom url
ServiceDeployer serviceDeployer = (ServiceDeployer) url.getAttribute(REST_SERVICE_DEPLOYER_URL_ATTRIBUTE_KEY);
// maybe current request is qos http or no rest service export
if (serviceDeployer == null) {
serviceDeployer = emptyServiceDeployer;
}
List<Object> channelHandlers = Arrays.asList(new HttpRequestDecoder(
url.getParameter(RestConstant.MAX_INITIAL_LINE_LENGTH_PARAM, RestConstant.MAX_INITIAL_LINE_LENGTH),
url.getParameter(RestConstant.MAX_HEADER_SIZE_PARAM, RestConstant.MAX_HEADER_SIZE),
url.getParameter(RestConstant.MAX_CHUNK_SIZE_PARAM, RestConstant.MAX_CHUNK_SIZE)),
new HttpObjectAggregator(url.getParameter(RestConstant.MAX_REQUEST_SIZE_PARAM, RestConstant.MAX_REQUEST_SIZE)),
new HttpResponseEncoder(), new RestHttpRequestDecoder(new NettyHttpHandler(serviceDeployer), url));
List<ChannelHandler> handlers = new ArrayList<>();
handlers.add(new ChannelHandlerPretender(channelHandlers));
operator.configChannelHandler(handlers);
}
}

View File

@ -0,0 +1 @@
rest=org.apache.dubbo.rpc.protocol.rest.pu.RestHttp1WireProtocol

View File

@ -74,6 +74,7 @@ class JaxrsRestProtocolTest {
private final URL exportUrl = URL.valueOf("rest://127.0.0.1:" + availablePort + "/rest?interface=org.apache.dubbo.rpc.protocol.rest.DemoService");
private final ModuleServiceRepository repository = ApplicationModel.defaultModel().getDefaultModule().getServiceRepository();
private final ExceptionMapper exceptionMapper = new ExceptionMapper();
private static final String SERVER = "netty4";
@AfterEach
public void tearDown() {
@ -197,7 +198,7 @@ class JaxrsRestProtocolTest {
URL url = this.registerProvider(exportUrl, server, DemoService.class);
URL nettyUrl = url.addParameter(SERVER_KEY, "netty");
URL nettyUrl = url.addParameter(SERVER_KEY, SERVER);
Exporter<DemoService> exporter = protocol.export(proxy.getInvoker(new DemoServiceImpl(), DemoService.class, nettyUrl));
DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, nettyUrl));
@ -230,7 +231,7 @@ class JaxrsRestProtocolTest {
URL url = this.registerProvider(exportUrl, server, DemoService.class);
URL nettyUrl = url.addParameter(SERVER_KEY, "netty");
URL nettyUrl = url.addParameter(SERVER_KEY, SERVER);
Exporter<DemoService> exporter = protocol.export(proxy.getInvoker(server, DemoService.class, nettyUrl));
DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, nettyUrl));
@ -259,7 +260,7 @@ class JaxrsRestProtocolTest {
URL url = this.registerProvider(exportUrl, server, DemoService.class);
URL nettyUrl = url.addParameter(SERVER_KEY, "netty")
URL nettyUrl = url.addParameter(SERVER_KEY, SERVER)
.addParameter(EXTENSION_KEY, "org.apache.dubbo.rpc.protocol.rest.support.LoggingFilter");
Exporter<DemoService> exporter = protocol.export(proxy.getInvoker(server, DemoService.class, nettyUrl));
@ -279,7 +280,7 @@ class JaxrsRestProtocolTest {
URL url = this.registerProvider(exportUrl, server, DemoService.class);
// use RpcContextFilter
URL nettyUrl = url.addParameter(SERVER_KEY, "netty")
URL nettyUrl = url.addParameter(SERVER_KEY, SERVER)
.addParameter(EXTENSION_KEY, "org.apache.dubbo.rpc.protocol.rest.RpcContextFilter");
Exporter<DemoService> exporter = protocol.export(proxy.getInvoker(server, DemoService.class, nettyUrl));
@ -402,7 +403,7 @@ class JaxrsRestProtocolTest {
URL url = this.registerProvider(exportUrl, server, DemoService.class);
URL nettyUrl = url.addParameter(SERVER_KEY, "netty")
URL nettyUrl = url.addParameter(SERVER_KEY, SERVER)
.addParameter(EXTENSION_KEY, "org.apache.dubbo.rpc.protocol.rest.support.LoggingFilter");
Exporter<DemoService> exporter = protocol.export(proxy.getInvoker(server, DemoService.class, nettyUrl));
@ -453,7 +454,7 @@ class JaxrsRestProtocolTest {
URL url = this.registerProvider(exportUrl, server, DemoService.class);
URL nettyUrl = url.addParameter(SERVER_KEY, "netty")
URL nettyUrl = url.addParameter(SERVER_KEY, SERVER)
.addParameter(EXTENSION_KEY, "org.apache.dubbo.rpc.protocol.rest.support.LoggingFilter");
Exporter<DemoService> exporter = protocol.export(proxy.getInvoker(server, DemoService.class, nettyUrl));
@ -485,7 +486,7 @@ class JaxrsRestProtocolTest {
URL url = this.registerProvider(exportUrl, server, DemoService.class);
URL nettyUrl = url.addParameter(SERVER_KEY, "netty")
URL nettyUrl = url.addParameter(SERVER_KEY, SERVER)
.addParameter(EXTENSION_KEY, "org.apache.dubbo.rpc.protocol.rest.support.LoggingFilter");
Exporter<DemoService> exporter = protocol.export(proxy.getInvoker(server, DemoService.class, nettyUrl));

View File

@ -39,6 +39,7 @@ public class ResteasyResponseTest {
private final int availablePort = NetUtils.getAvailablePort();
private final URL exportUrl = URL.valueOf("rest://127.0.0.1:" + availablePort + "/rest?interface=org.apache.dubbo.rpc.protocol.rest.rest.RestDemoService");
private final ModuleServiceRepository repository = ApplicationModel.defaultModel().getDefaultModule().getServiceRepository();
private static final String SERVER = "netty4";
@AfterEach
public void tearDown() {
@ -50,7 +51,7 @@ public class ResteasyResponseTest {
RestDemoService server = new RestDemoServiceImpl();
URL url = this.registerProvider(exportUrl, server, RestDemoService.class);
URL nettyUrl = url.addParameter(SERVER_KEY, "netty").addParameter("timeout", 3000000);
URL nettyUrl = url.addParameter(SERVER_KEY, SERVER).addParameter("timeout", 3000000);
protocol.export(proxy.getInvoker(new RestDemoServiceImpl(), RestDemoService.class, nettyUrl));

View File

@ -59,9 +59,12 @@ import static org.hamcrest.MatcherAssert.assertThat;
public class SpringMvcRestProtocolTest {
private Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getExtension("rest");
private ProxyFactory proxy = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
private final static int availablePort = NetUtils.getAvailablePort();
private final static URL exportUrl = URL.valueOf("rest://127.0.0.1:" + availablePort + "/rest?interface=org.apache.dubbo.rpc.protocol.rest.mvc.SpringRestDemoService");
private static URL getUrl() {
return URL.valueOf("rest://127.0.0.1:" + NetUtils.getAvailablePort() + "/rest?interface=org.apache.dubbo.rpc.protocol.rest.mvc.SpringRestDemoService");
}
private static final String SERVER = "netty4";
private final ModuleServiceRepository repository = ApplicationModel.defaultModel().getDefaultModule().getServiceRepository();
@ -83,12 +86,12 @@ public class SpringMvcRestProtocolTest {
}
public Exporter<SpringRestDemoService> getExport(URL url, SpringRestDemoService server) {
url = url.addParameter(SERVER_KEY, Constants.NETTY_HTTP);
url = url.addParameter(SERVER_KEY, SERVER);
return protocol.export(proxy.getInvoker(server, getServerClass(), url));
}
public Exporter<SpringRestDemoService> getExceptionHandlerExport(URL url, SpringRestDemoService server) {
url = url.addParameter(SERVER_KEY, Constants.NETTY_HTTP);
url = url.addParameter(SERVER_KEY, SERVER);
url = url.addParameter(EXTENSION_KEY, TestExceptionMapper.class.getName());
return protocol.export(proxy.getInvoker(server, getServerClass(), url));
}
@ -177,7 +180,7 @@ public class SpringMvcRestProtocolTest {
void testExport() {
SpringRestDemoService server = getServerImpl();
URL url = this.registerProvider(exportUrl, server, SpringRestDemoService.class);
URL url = this.registerProvider(getUrl(), server, SpringRestDemoService.class);
RpcContext.getClientAttachment().setAttachment("timeout", "200");
Exporter<SpringRestDemoService> exporter = getExport(url, server);
@ -194,7 +197,7 @@ public class SpringMvcRestProtocolTest {
void testNettyServer() {
SpringRestDemoService server = getServerImpl();
URL nettyUrl = this.registerProvider(exportUrl, server, SpringRestDemoService.class);
URL nettyUrl = this.registerProvider(getUrl(), server, SpringRestDemoService.class);
Exporter<SpringRestDemoService> exporter = getExport(nettyUrl, server);
@ -213,7 +216,7 @@ public class SpringMvcRestProtocolTest {
Assertions.assertThrows(RpcException.class, () -> {
SpringRestDemoService server = getServerImpl();
URL url = this.registerProvider(exportUrl, server, SpringRestDemoService.class);
URL url = this.registerProvider(getUrl(), server, SpringRestDemoService.class);
URL servletUrl = url.addParameter(SERVER_KEY, "servlet");
@ -227,7 +230,7 @@ public class SpringMvcRestProtocolTest {
exceptionMapper.unRegisterMapper(RuntimeException.class);
SpringRestDemoService server = getServerImpl();
URL nettyUrl = this.registerProvider(exportUrl, server, SpringRestDemoService.class);
URL nettyUrl = this.registerProvider(getUrl(), server, SpringRestDemoService.class);
Exporter<SpringRestDemoService> exporter = getExport(nettyUrl, server);
@ -241,7 +244,7 @@ public class SpringMvcRestProtocolTest {
void testInvoke() {
SpringRestDemoService server = getServerImpl();
URL url = this.registerProvider(exportUrl, server, SpringRestDemoService.class);
URL url = this.registerProvider(getUrl(), server, SpringRestDemoService.class);
Exporter<SpringRestDemoService> exporter = getExport(url, server);
@ -255,7 +258,7 @@ public class SpringMvcRestProtocolTest {
void testFilter() {
SpringRestDemoService server = getServerImpl();
URL url = this.registerProvider(exportUrl, server, SpringRestDemoService.class);
URL url = this.registerProvider(getUrl(), server, SpringRestDemoService.class);
Exporter<SpringRestDemoService> exporter = getExport(url, server);
@ -272,7 +275,7 @@ public class SpringMvcRestProtocolTest {
void testRpcContextFilter() {
SpringRestDemoService server = getServerImpl();
URL nettyUrl = this.registerProvider(exportUrl, server, SpringRestDemoService.class);
URL nettyUrl = this.registerProvider(getUrl(), server, SpringRestDemoService.class);
// use RpcContextFilter
// URL nettyUrl = url.addParameter(SERVER_KEY, "netty")
@ -307,7 +310,7 @@ public class SpringMvcRestProtocolTest {
Assertions.assertThrows(RuntimeException.class, () -> {
SpringRestDemoService server = getServerImpl();
URL url = this.registerProvider(exportUrl, server, SpringRestDemoService.class);
URL url = this.registerProvider(getUrl(), server, SpringRestDemoService.class);
URL nettyUrl = url.addParameter(EXTENSION_KEY, "com.not.existing.Filter");
Exporter<SpringRestDemoService> exporter = getExport(nettyUrl, server);
@ -325,7 +328,7 @@ public class SpringMvcRestProtocolTest {
SpringRestDemoService server = getServerImpl();
URL exceptionUrl = this.registerProvider(exportUrl, server, SpringRestDemoService.class);
URL exceptionUrl = this.registerProvider(getUrl(), server, SpringRestDemoService.class);
Exporter<SpringRestDemoService> exporter = getExceptionHandlerExport(exceptionUrl, server);
@ -342,7 +345,7 @@ public class SpringMvcRestProtocolTest {
void testFormConsumerParser() {
SpringRestDemoService server = getServerImpl();
URL nettyUrl = this.registerProvider(exportUrl, server, SpringRestDemoService.class);
URL nettyUrl = this.registerProvider(getUrl(), server, SpringRestDemoService.class);
Exporter<SpringRestDemoService> exporter = getExport(nettyUrl, server);
@ -368,7 +371,7 @@ public class SpringMvcRestProtocolTest {
void testPrimitive() {
SpringRestDemoService server = getServerImpl();
URL nettyUrl = this.registerProvider(exportUrl, server, SpringRestDemoService.class);
URL nettyUrl = this.registerProvider(getUrl(), server, SpringRestDemoService.class);
Exporter<SpringRestDemoService> exporter = getExport(nettyUrl, server);