parent
594e4e1567
commit
06f719086e
|
|
@ -144,7 +144,7 @@
|
|||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-rpc-jsonrpc</artifactId>
|
||||
<artifactId>dubbo-rpc-http</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
|
|
@ -165,7 +165,7 @@
|
|||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-rpc-http</artifactId>
|
||||
<artifactId>dubbo-rpc-http-invoker</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
|
|
@ -581,10 +581,10 @@
|
|||
<include>org.apache.dubbo:dubbo-rpc-api</include>
|
||||
<include>org.apache.dubbo:dubbo-rpc-dubbo</include>
|
||||
<include>org.apache.dubbo:dubbo-rpc-injvm</include>
|
||||
<include>org.apache.dubbo:dubbo-rpc-jsonrpc</include>
|
||||
<include>org.apache.dubbo:dubbo-rpc-http</include>
|
||||
<include>org.apache.dubbo:dubbo-rpc-rmi</include>
|
||||
<include>org.apache.dubbo:dubbo-rpc-hessian</include>
|
||||
<include>org.apache.dubbo:dubbo-rpc-http</include>
|
||||
<include>org.apache.dubbo:dubbo-rpc-http-invoker</include>
|
||||
<include>org.apache.dubbo:dubbo-rpc-webservice</include>
|
||||
<include>org.apache.dubbo:dubbo-rpc-thrift</include>
|
||||
<include>org.apache.dubbo:dubbo-rpc-memcached</include>
|
||||
|
|
|
|||
|
|
@ -174,7 +174,7 @@
|
|||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-rpc-jsonrpc</artifactId>
|
||||
<artifactId>dubbo-rpc-http</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
|
@ -189,7 +189,7 @@
|
|||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-rpc-http</artifactId>
|
||||
<artifactId>dubbo-rpc-http-invoker</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
|
|
|||
|
|
@ -14,24 +14,20 @@
|
|||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<artifactId>dubbo-rpc</artifactId>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-rpc</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>dubbo-rpc-jsonrpc</artifactId>
|
||||
|
||||
<description>The JSON-RPC module of dubbo project</description>
|
||||
|
||||
<artifactId>dubbo-rpc-http-invoker</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>${project.artifactId}</name>
|
||||
<description>The http rpc module of dubbo project</description>
|
||||
<properties>
|
||||
<skip_maven_deploy>false</skip_maven_deploy>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
|
|
@ -48,14 +44,14 @@
|
|||
<artifactId>spring-context</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.briandilley.jsonrpc4j</groupId>
|
||||
<artifactId>jsonrpc4j</artifactId>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.portlet</groupId>
|
||||
<artifactId>portlet-api</artifactId>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-serialization-jdk</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
</project>
|
||||
|
|
@ -19,7 +19,7 @@ package com.alibaba.dubbo.rpc.protocol.http;
|
|||
import org.aopalliance.intercept.MethodInvocation;
|
||||
|
||||
@Deprecated
|
||||
public class HttpRemoteInvocation extends org.apache.dubbo.rpc.protocol.http.HttpRemoteInvocation {
|
||||
public class HttpRemoteInvocation extends org.apache.dubbo.rpc.protocol.httpinvoker.HttpRemoteInvocation {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public HttpRemoteInvocation(MethodInvocation methodInvocation) {
|
||||
|
|
@ -0,0 +1,225 @@
|
|||
/*
|
||||
* 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.httpinvoker;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.utils.StringUtils;
|
||||
import org.apache.dubbo.common.Version;
|
||||
import org.apache.dubbo.remoting.Constants;
|
||||
import org.apache.dubbo.remoting.http.HttpBinder;
|
||||
import org.apache.dubbo.remoting.http.HttpHandler;
|
||||
import org.apache.dubbo.remoting.http.HttpServer;
|
||||
import org.apache.dubbo.rpc.RpcContext;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.protocol.AbstractProxyProtocol;
|
||||
import org.apache.dubbo.rpc.service.GenericService;
|
||||
import org.apache.dubbo.rpc.support.ProtocolUtils;
|
||||
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.springframework.remoting.RemoteAccessException;
|
||||
import org.springframework.remoting.httpinvoker.HttpComponentsHttpInvokerRequestExecutor;
|
||||
import org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean;
|
||||
import org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter;
|
||||
import org.springframework.remoting.httpinvoker.SimpleHttpInvokerRequestExecutor;
|
||||
import org.springframework.remoting.support.RemoteInvocation;
|
||||
import org.springframework.remoting.support.RemoteInvocationFactory;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_TIMEOUT;
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.RELEASE_KEY;
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY;
|
||||
import static org.apache.dubbo.remoting.Constants.DUBBO_VERSION_KEY;
|
||||
import static org.apache.dubbo.rpc.Constants.GENERIC_KEY;
|
||||
|
||||
/**
|
||||
* HttpInvokerProtocol
|
||||
*/
|
||||
public class HttpInvokerProtocol extends AbstractProxyProtocol {
|
||||
|
||||
public static final int DEFAULT_PORT = 80;
|
||||
|
||||
private final Map<String, HttpServer> serverMap = new ConcurrentHashMap<String, HttpServer>();
|
||||
|
||||
private final Map<String, HttpInvokerServiceExporter> skeletonMap = new ConcurrentHashMap<String, HttpInvokerServiceExporter>();
|
||||
|
||||
private HttpBinder httpBinder;
|
||||
|
||||
public HttpInvokerProtocol() {
|
||||
super(RemoteAccessException.class);
|
||||
}
|
||||
|
||||
public void setHttpBinder(HttpBinder httpBinder) {
|
||||
this.httpBinder = httpBinder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDefaultPort() {
|
||||
return DEFAULT_PORT;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T> Runnable doExport(final T impl, Class<T> type, URL url) throws RpcException {
|
||||
String addr = getAddr(url);
|
||||
HttpServer server = serverMap.get(addr);
|
||||
if (server == null) {
|
||||
server = httpBinder.bind(url, new InternalHandler());
|
||||
serverMap.put(addr, server);
|
||||
}
|
||||
final String path = url.getAbsolutePath();
|
||||
skeletonMap.put(path, createExporter(impl, type));
|
||||
|
||||
final String genericPath = path + "/" + GENERIC_KEY;
|
||||
|
||||
skeletonMap.put(genericPath, createExporter(impl, GenericService.class));
|
||||
return new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
skeletonMap.remove(path);
|
||||
skeletonMap.remove(genericPath);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private <T> HttpInvokerServiceExporter createExporter(T impl, Class<?> type) {
|
||||
final HttpInvokerServiceExporter httpServiceExporter = new HttpInvokerServiceExporter();
|
||||
httpServiceExporter.setServiceInterface(type);
|
||||
httpServiceExporter.setService(impl);
|
||||
try {
|
||||
httpServiceExporter.afterPropertiesSet();
|
||||
} catch (Exception e) {
|
||||
throw new RpcException(e.getMessage(), e);
|
||||
}
|
||||
return httpServiceExporter;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <T> T doRefer(final Class<T> serviceType, final URL url) throws RpcException {
|
||||
final String generic = url.getParameter(GENERIC_KEY);
|
||||
final boolean isGeneric = ProtocolUtils.isGeneric(generic) || serviceType.equals(GenericService.class);
|
||||
|
||||
final HttpInvokerProxyFactoryBean httpProxyFactoryBean = new HttpInvokerProxyFactoryBean();
|
||||
httpProxyFactoryBean.setRemoteInvocationFactory(new RemoteInvocationFactory() {
|
||||
@Override
|
||||
public RemoteInvocation createRemoteInvocation(MethodInvocation methodInvocation) {
|
||||
RemoteInvocation invocation;
|
||||
/*
|
||||
package was renamed to 'org.apache.dubbo' in v2.7.0, so only provider versions after v2.7.0 can
|
||||
recognize org.apache.xxx.HttpRemoteInvocation'.
|
||||
*/
|
||||
if (Version.isRelease270OrHigher(url.getParameter(RELEASE_KEY))) {
|
||||
invocation = new HttpRemoteInvocation(methodInvocation);
|
||||
} else {
|
||||
/*
|
||||
The customized 'com.alibaba.dubbo.rpc.protocol.http.HttpRemoteInvocation' was firstly introduced
|
||||
in v2.6.3. The main purpose is to support transformation of attachments in HttpInvokerProtocol, see
|
||||
https://github.com/apache/dubbo/pull/1827. To guarantee interoperability with lower
|
||||
versions, we need to check if the provider is v2.6.3 or higher before sending customized
|
||||
HttpRemoteInvocation.
|
||||
*/
|
||||
if (Version.isRelease263OrHigher(url.getParameter(DUBBO_VERSION_KEY))) {
|
||||
invocation = new com.alibaba.dubbo.rpc.protocol.http.HttpRemoteInvocation(methodInvocation);
|
||||
} else {
|
||||
invocation = new RemoteInvocation(methodInvocation);
|
||||
}
|
||||
}
|
||||
if (isGeneric) {
|
||||
invocation.addAttribute(GENERIC_KEY, generic);
|
||||
}
|
||||
return invocation;
|
||||
}
|
||||
});
|
||||
|
||||
String key = url.toIdentityString();
|
||||
if (isGeneric) {
|
||||
key = key + "/" + GENERIC_KEY;
|
||||
}
|
||||
|
||||
httpProxyFactoryBean.setServiceUrl(key.replace("http-invoker","http"));
|
||||
httpProxyFactoryBean.setServiceInterface(serviceType);
|
||||
String client = url.getParameter(Constants.CLIENT_KEY);
|
||||
if (StringUtils.isEmpty(client) || "simple".equals(client)) {
|
||||
SimpleHttpInvokerRequestExecutor httpInvokerRequestExecutor = new SimpleHttpInvokerRequestExecutor() {
|
||||
@Override
|
||||
protected void prepareConnection(HttpURLConnection con,
|
||||
int contentLength) throws IOException {
|
||||
super.prepareConnection(con, contentLength);
|
||||
con.setReadTimeout(url.getParameter(TIMEOUT_KEY, DEFAULT_TIMEOUT));
|
||||
con.setConnectTimeout(url.getParameter(Constants.CONNECT_TIMEOUT_KEY, Constants.DEFAULT_CONNECT_TIMEOUT));
|
||||
}
|
||||
};
|
||||
httpProxyFactoryBean.setHttpInvokerRequestExecutor(httpInvokerRequestExecutor);
|
||||
} else if ("commons".equals(client)) {
|
||||
HttpComponentsHttpInvokerRequestExecutor httpInvokerRequestExecutor = new HttpComponentsHttpInvokerRequestExecutor();
|
||||
httpInvokerRequestExecutor.setReadTimeout(url.getParameter(TIMEOUT_KEY, DEFAULT_TIMEOUT));
|
||||
httpInvokerRequestExecutor.setConnectTimeout(url.getParameter(Constants.CONNECT_TIMEOUT_KEY, Constants.DEFAULT_CONNECT_TIMEOUT));
|
||||
httpProxyFactoryBean.setHttpInvokerRequestExecutor(httpInvokerRequestExecutor);
|
||||
} else {
|
||||
throw new IllegalStateException("Unsupported http protocol client " + client + ", only supported: simple, commons");
|
||||
}
|
||||
httpProxyFactoryBean.afterPropertiesSet();
|
||||
return (T) httpProxyFactoryBean.getObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getErrorCode(Throwable e) {
|
||||
if (e instanceof RemoteAccessException) {
|
||||
e = e.getCause();
|
||||
}
|
||||
if (e != null) {
|
||||
Class<?> cls = e.getClass();
|
||||
if (SocketTimeoutException.class.equals(cls)) {
|
||||
return RpcException.TIMEOUT_EXCEPTION;
|
||||
} else if (IOException.class.isAssignableFrom(cls)) {
|
||||
return RpcException.NETWORK_EXCEPTION;
|
||||
} else if (ClassNotFoundException.class.isAssignableFrom(cls)) {
|
||||
return RpcException.SERIALIZATION_EXCEPTION;
|
||||
}
|
||||
}
|
||||
return super.getErrorCode(e);
|
||||
}
|
||||
|
||||
private class InternalHandler implements HttpHandler {
|
||||
|
||||
@Override
|
||||
public void handle(HttpServletRequest request, HttpServletResponse response)
|
||||
throws IOException, ServletException {
|
||||
String uri = request.getRequestURI();
|
||||
HttpInvokerServiceExporter skeleton = skeletonMap.get(uri);
|
||||
if (!"POST".equalsIgnoreCase(request.getMethod())) {
|
||||
response.setStatus(500);
|
||||
} else {
|
||||
RpcContext.getContext().setRemoteAddress(request.getRemoteAddr(), request.getRemotePort());
|
||||
try {
|
||||
skeleton.handleRequest(request, response);
|
||||
} catch (Throwable e) {
|
||||
throw new ServletException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.dubbo.rpc.protocol.http;
|
||||
package org.apache.dubbo.rpc.protocol.httpinvoker;
|
||||
|
||||
import org.apache.dubbo.common.utils.StringUtils;
|
||||
import org.apache.dubbo.rpc.RpcContext;
|
||||
|
|
@ -0,0 +1 @@
|
|||
http-invoker=org.apache.dubbo.rpc.protocol.httpinvoker.HttpInvokerProtocol
|
||||
|
|
@ -0,0 +1,203 @@
|
|||
/*
|
||||
* 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.httpinvoker;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.beanutil.JavaBeanDescriptor;
|
||||
import org.apache.dubbo.common.beanutil.JavaBeanSerializeUtil;
|
||||
import org.apache.dubbo.common.extension.ExtensionLoader;
|
||||
import org.apache.dubbo.common.serialize.ObjectInput;
|
||||
import org.apache.dubbo.common.serialize.ObjectOutput;
|
||||
import org.apache.dubbo.common.serialize.Serialization;
|
||||
import org.apache.dubbo.common.serialize.nativejava.NativeJavaSerialization;
|
||||
import org.apache.dubbo.rpc.Exporter;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.Protocol;
|
||||
import org.apache.dubbo.rpc.ProxyFactory;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.service.GenericService;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
/**
|
||||
* HttpInvokerProtocolTest
|
||||
*/
|
||||
public class HttpInvokerProtocolTest {
|
||||
|
||||
@Test
|
||||
public void testHttpProtocol() {
|
||||
HttpServiceImpl server = new HttpServiceImpl();
|
||||
Assertions.assertFalse(server.isCalled());
|
||||
ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
|
||||
Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
|
||||
URL url = URL.valueOf("http-invoker://127.0.0.1:5342/" + HttpService.class.getName() + "?release=2.7.0");
|
||||
Exporter<HttpService> exporter = protocol.export(proxyFactory.getInvoker(server, HttpService.class, url));
|
||||
Invoker<HttpService> invoker = protocol.refer(HttpService.class, url);
|
||||
HttpService client = proxyFactory.getProxy(invoker);
|
||||
String result = client.sayHello("haha");
|
||||
Assertions.assertTrue(server.isCalled());
|
||||
Assertions.assertEquals("Hello, haha", result);
|
||||
invoker.destroy();
|
||||
exporter.unexport();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenericInvoke() {
|
||||
HttpServiceImpl server = new HttpServiceImpl();
|
||||
Assertions.assertFalse(server.isCalled());
|
||||
ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
|
||||
Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
|
||||
URL url = URL.valueOf("http-invoker://127.0.0.1:5342/" + HttpService.class.getName() + "?release=2.7.0");
|
||||
Exporter<HttpService> exporter = protocol.export(proxyFactory.getInvoker(server, HttpService.class, url));
|
||||
Invoker<GenericService> invoker = protocol.refer(GenericService.class, url);
|
||||
GenericService client = proxyFactory.getProxy(invoker, true);
|
||||
String result = (String) client.$invoke("sayHello", new String[]{"java.lang.String"}, new Object[]{"haha"});
|
||||
Assertions.assertTrue(server.isCalled());
|
||||
Assertions.assertEquals("Hello, haha", result);
|
||||
invoker.destroy();
|
||||
exporter.unexport();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenericInvokeWithNativeJava() throws IOException, ClassNotFoundException {
|
||||
HttpServiceImpl server = new HttpServiceImpl();
|
||||
Assertions.assertFalse(server.isCalled());
|
||||
ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
|
||||
Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
|
||||
URL url = URL.valueOf("http-invoker://127.0.0.1:5342/" + HttpService.class.getName() + "?release=2.7.0&generic=nativejava");
|
||||
Exporter<HttpService> exporter = protocol.export(proxyFactory.getInvoker(server, HttpService.class, url));
|
||||
Invoker<GenericService> invoker = protocol.refer(GenericService.class, url);
|
||||
GenericService client = proxyFactory.getProxy(invoker);
|
||||
|
||||
Serialization serialization = new NativeJavaSerialization();
|
||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||
|
||||
ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
|
||||
objectOutput.writeObject("haha");
|
||||
objectOutput.flushBuffer();
|
||||
|
||||
Object result = client.$invoke("sayHello", new String[]{"java.lang.String"}, new Object[]{byteArrayOutputStream.toByteArray()});
|
||||
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream((byte[]) result);
|
||||
ObjectInput objectInput = serialization.deserialize(url, byteArrayInputStream);
|
||||
Assertions.assertTrue(server.isCalled());
|
||||
Assertions.assertEquals("Hello, haha", objectInput.readObject());
|
||||
invoker.destroy();
|
||||
exporter.unexport();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenericInvokeWithBean() {
|
||||
HttpServiceImpl server = new HttpServiceImpl();
|
||||
Assertions.assertFalse(server.isCalled());
|
||||
ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
|
||||
Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
|
||||
URL url = URL.valueOf("http-invoker://127.0.0.1:5342/" + HttpService.class.getName() + "?release=2.7.0&generic=bean");
|
||||
Exporter<HttpService> exporter = protocol.export(proxyFactory.getInvoker(server, HttpService.class, url));
|
||||
Invoker<GenericService> invoker = protocol.refer(GenericService.class, url);
|
||||
GenericService client = proxyFactory.getProxy(invoker);
|
||||
|
||||
JavaBeanDescriptor javaBeanDescriptor = JavaBeanSerializeUtil.serialize("haha");
|
||||
|
||||
Object result = client.$invoke("sayHello", new String[]{"java.lang.String"}, new Object[]{javaBeanDescriptor});
|
||||
Assertions.assertTrue(server.isCalled());
|
||||
Assertions.assertEquals("Hello, haha", JavaBeanSerializeUtil.deserialize((JavaBeanDescriptor) result));
|
||||
invoker.destroy();
|
||||
exporter.unexport();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOverload() {
|
||||
HttpServiceImpl server = new HttpServiceImpl();
|
||||
Assertions.assertFalse(server.isCalled());
|
||||
ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
|
||||
Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
|
||||
URL url = URL.valueOf("http-invoker://127.0.0.1:5342/" + HttpService.class.getName() + "?release=2.7.0&hessian.overload.method=true&hessian2.request=false");
|
||||
Exporter<HttpService> exporter = protocol.export(proxyFactory.getInvoker(server, HttpService.class, url));
|
||||
Invoker<HttpService> invoker = protocol.refer(HttpService.class, url);
|
||||
HttpService client = proxyFactory.getProxy(invoker);
|
||||
String result = client.sayHello("haha");
|
||||
Assertions.assertEquals("Hello, haha", result);
|
||||
result = client.sayHello("haha", 1);
|
||||
Assertions.assertEquals("Hello, haha. ", result);
|
||||
invoker.destroy();
|
||||
exporter.unexport();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleClient() {
|
||||
HttpServiceImpl server = new HttpServiceImpl();
|
||||
Assertions.assertFalse(server.isCalled());
|
||||
ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
|
||||
Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
|
||||
URL url = URL.valueOf("http-invoker://127.0.0.1:5342/" + HttpService.class.getName() + "?release=2.7.0&client=simple");
|
||||
Exporter<HttpService> exporter = protocol.export(proxyFactory.getInvoker(server, HttpService.class, url));
|
||||
Invoker<HttpService> invoker = protocol.refer(HttpService.class, url);
|
||||
HttpService client = proxyFactory.getProxy(invoker);
|
||||
String result = client.sayHello("haha");
|
||||
Assertions.assertTrue(server.isCalled());
|
||||
Assertions.assertEquals("Hello, haha", result);
|
||||
invoker.destroy();
|
||||
exporter.unexport();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTimeOut() {
|
||||
HttpServiceImpl server = new HttpServiceImpl();
|
||||
ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
|
||||
Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
|
||||
URL url = URL.valueOf("http-invoker://127.0.0.1:5342/" + HttpService.class.getName() + "?release=2.7.0&timeout=10");
|
||||
Exporter<HttpService> exporter = protocol.export(proxyFactory.getInvoker(server, HttpService.class, url));
|
||||
Invoker<HttpService> invoker = protocol.refer(HttpService.class, url);
|
||||
HttpService client = proxyFactory.getProxy(invoker);
|
||||
try {
|
||||
client.timeOut(6000);
|
||||
fail();
|
||||
} catch (RpcException expected) {
|
||||
Assertions.assertTrue(expected.isTimeout());
|
||||
} finally {
|
||||
invoker.destroy();
|
||||
exporter.unexport();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomException() {
|
||||
HttpServiceImpl server = new HttpServiceImpl();
|
||||
ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
|
||||
Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
|
||||
URL url = URL.valueOf("http-invoker://127.0.0.1:5342/" + HttpService.class.getName() + "?release=2.7.0");
|
||||
Exporter<HttpService> exporter = protocol.export(proxyFactory.getInvoker(server, HttpService.class, url));
|
||||
Invoker<HttpService> invoker = protocol.refer(HttpService.class, url);
|
||||
HttpService client = proxyFactory.getProxy(invoker);
|
||||
try {
|
||||
client.customException();
|
||||
fail();
|
||||
} catch (HttpServiceImpl.MyException expected) {
|
||||
}
|
||||
invoker.destroy();
|
||||
exporter.unexport();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.rpc.protocol.http;
|
||||
package org.apache.dubbo.rpc.protocol.httpinvoker;
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.rpc.protocol.http;
|
||||
package org.apache.dubbo.rpc.protocol.httpinvoker;
|
||||
|
||||
/**
|
||||
* HttpServiceImpl
|
||||
|
|
@ -14,20 +14,24 @@
|
|||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-rpc</artifactId>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>dubbo-rpc-http</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>${project.artifactId}</name>
|
||||
<description>The http rpc module of dubbo project</description>
|
||||
|
||||
<description>The JSON-RPC module of dubbo project</description>
|
||||
|
||||
<properties>
|
||||
<skip_maven_deploy>false</skip_maven_deploy>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
|
|
@ -44,14 +48,14 @@
|
|||
<artifactId>spring-context</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
<groupId>com.github.briandilley.jsonrpc4j</groupId>
|
||||
<artifactId>jsonrpc4j</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-serialization-jdk</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
<scope>test</scope>
|
||||
<groupId>javax.portlet</groupId>
|
||||
<artifactId>portlet-api</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
</project>
|
||||
|
|
@ -1,225 +1,163 @@
|
|||
/*
|
||||
* 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.http;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.utils.StringUtils;
|
||||
import org.apache.dubbo.common.Version;
|
||||
import org.apache.dubbo.remoting.Constants;
|
||||
import org.apache.dubbo.remoting.http.HttpBinder;
|
||||
import org.apache.dubbo.remoting.http.HttpHandler;
|
||||
import org.apache.dubbo.remoting.http.HttpServer;
|
||||
import org.apache.dubbo.rpc.RpcContext;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.protocol.AbstractProxyProtocol;
|
||||
import org.apache.dubbo.rpc.service.GenericService;
|
||||
import org.apache.dubbo.rpc.support.ProtocolUtils;
|
||||
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.springframework.remoting.RemoteAccessException;
|
||||
import org.springframework.remoting.httpinvoker.HttpComponentsHttpInvokerRequestExecutor;
|
||||
import org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean;
|
||||
import org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter;
|
||||
import org.springframework.remoting.httpinvoker.SimpleHttpInvokerRequestExecutor;
|
||||
import org.springframework.remoting.support.RemoteInvocation;
|
||||
import org.springframework.remoting.support.RemoteInvocationFactory;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_TIMEOUT;
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.RELEASE_KEY;
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY;
|
||||
import static org.apache.dubbo.remoting.Constants.DUBBO_VERSION_KEY;
|
||||
import static org.apache.dubbo.rpc.Constants.GENERIC_KEY;
|
||||
|
||||
/**
|
||||
* HttpProtocol
|
||||
*/
|
||||
public class HttpProtocol extends AbstractProxyProtocol {
|
||||
|
||||
public static final int DEFAULT_PORT = 80;
|
||||
|
||||
private final Map<String, HttpServer> serverMap = new ConcurrentHashMap<String, HttpServer>();
|
||||
|
||||
private final Map<String, HttpInvokerServiceExporter> skeletonMap = new ConcurrentHashMap<String, HttpInvokerServiceExporter>();
|
||||
|
||||
private HttpBinder httpBinder;
|
||||
|
||||
public HttpProtocol() {
|
||||
super(RemoteAccessException.class);
|
||||
}
|
||||
|
||||
public void setHttpBinder(HttpBinder httpBinder) {
|
||||
this.httpBinder = httpBinder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDefaultPort() {
|
||||
return DEFAULT_PORT;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T> Runnable doExport(final T impl, Class<T> type, URL url) throws RpcException {
|
||||
String addr = getAddr(url);
|
||||
HttpServer server = serverMap.get(addr);
|
||||
if (server == null) {
|
||||
server = httpBinder.bind(url, new InternalHandler());
|
||||
serverMap.put(addr, server);
|
||||
}
|
||||
final String path = url.getAbsolutePath();
|
||||
skeletonMap.put(path, createExporter(impl, type));
|
||||
|
||||
final String genericPath = path + "/" + GENERIC_KEY;
|
||||
|
||||
skeletonMap.put(genericPath, createExporter(impl, GenericService.class));
|
||||
return new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
skeletonMap.remove(path);
|
||||
skeletonMap.remove(genericPath);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private <T> HttpInvokerServiceExporter createExporter(T impl, Class<?> type) {
|
||||
final HttpInvokerServiceExporter httpServiceExporter = new HttpInvokerServiceExporter();
|
||||
httpServiceExporter.setServiceInterface(type);
|
||||
httpServiceExporter.setService(impl);
|
||||
try {
|
||||
httpServiceExporter.afterPropertiesSet();
|
||||
} catch (Exception e) {
|
||||
throw new RpcException(e.getMessage(), e);
|
||||
}
|
||||
return httpServiceExporter;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <T> T doRefer(final Class<T> serviceType, final URL url) throws RpcException {
|
||||
final String generic = url.getParameter(GENERIC_KEY);
|
||||
final boolean isGeneric = ProtocolUtils.isGeneric(generic) || serviceType.equals(GenericService.class);
|
||||
|
||||
final HttpInvokerProxyFactoryBean httpProxyFactoryBean = new HttpInvokerProxyFactoryBean();
|
||||
httpProxyFactoryBean.setRemoteInvocationFactory(new RemoteInvocationFactory() {
|
||||
@Override
|
||||
public RemoteInvocation createRemoteInvocation(MethodInvocation methodInvocation) {
|
||||
RemoteInvocation invocation;
|
||||
/*
|
||||
package was renamed to 'org.apache.dubbo' in v2.7.0, so only provider versions after v2.7.0 can
|
||||
recognize org.apache.xxx.HttpRemoteInvocation'.
|
||||
*/
|
||||
if (Version.isRelease270OrHigher(url.getParameter(RELEASE_KEY))) {
|
||||
invocation = new HttpRemoteInvocation(methodInvocation);
|
||||
} else {
|
||||
/*
|
||||
The customized 'com.alibaba.dubbo.rpc.protocol.http.HttpRemoteInvocation' was firstly introduced
|
||||
in v2.6.3. The main purpose is to support transformation of attachments in HttpProtocol, see
|
||||
https://github.com/apache/dubbo/pull/1827. To guarantee interoperability with lower
|
||||
versions, we need to check if the provider is v2.6.3 or higher before sending customized
|
||||
HttpRemoteInvocation.
|
||||
*/
|
||||
if (Version.isRelease263OrHigher(url.getParameter(DUBBO_VERSION_KEY))) {
|
||||
invocation = new com.alibaba.dubbo.rpc.protocol.http.HttpRemoteInvocation(methodInvocation);
|
||||
} else {
|
||||
invocation = new RemoteInvocation(methodInvocation);
|
||||
}
|
||||
}
|
||||
if (isGeneric) {
|
||||
invocation.addAttribute(GENERIC_KEY, generic);
|
||||
}
|
||||
return invocation;
|
||||
}
|
||||
});
|
||||
|
||||
String key = url.toIdentityString();
|
||||
if (isGeneric) {
|
||||
key = key + "/" + GENERIC_KEY;
|
||||
}
|
||||
|
||||
httpProxyFactoryBean.setServiceUrl(key);
|
||||
httpProxyFactoryBean.setServiceInterface(serviceType);
|
||||
String client = url.getParameter(Constants.CLIENT_KEY);
|
||||
if (StringUtils.isEmpty(client) || "simple".equals(client)) {
|
||||
SimpleHttpInvokerRequestExecutor httpInvokerRequestExecutor = new SimpleHttpInvokerRequestExecutor() {
|
||||
@Override
|
||||
protected void prepareConnection(HttpURLConnection con,
|
||||
int contentLength) throws IOException {
|
||||
super.prepareConnection(con, contentLength);
|
||||
con.setReadTimeout(url.getParameter(TIMEOUT_KEY, DEFAULT_TIMEOUT));
|
||||
con.setConnectTimeout(url.getParameter(Constants.CONNECT_TIMEOUT_KEY, Constants.DEFAULT_CONNECT_TIMEOUT));
|
||||
}
|
||||
};
|
||||
httpProxyFactoryBean.setHttpInvokerRequestExecutor(httpInvokerRequestExecutor);
|
||||
} else if ("commons".equals(client)) {
|
||||
HttpComponentsHttpInvokerRequestExecutor httpInvokerRequestExecutor = new HttpComponentsHttpInvokerRequestExecutor();
|
||||
httpInvokerRequestExecutor.setReadTimeout(url.getParameter(TIMEOUT_KEY, DEFAULT_TIMEOUT));
|
||||
httpInvokerRequestExecutor.setConnectTimeout(url.getParameter(Constants.CONNECT_TIMEOUT_KEY, Constants.DEFAULT_CONNECT_TIMEOUT));
|
||||
httpProxyFactoryBean.setHttpInvokerRequestExecutor(httpInvokerRequestExecutor);
|
||||
} else {
|
||||
throw new IllegalStateException("Unsupported http protocol client " + client + ", only supported: simple, commons");
|
||||
}
|
||||
httpProxyFactoryBean.afterPropertiesSet();
|
||||
return (T) httpProxyFactoryBean.getObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getErrorCode(Throwable e) {
|
||||
if (e instanceof RemoteAccessException) {
|
||||
e = e.getCause();
|
||||
}
|
||||
if (e != null) {
|
||||
Class<?> cls = e.getClass();
|
||||
if (SocketTimeoutException.class.equals(cls)) {
|
||||
return RpcException.TIMEOUT_EXCEPTION;
|
||||
} else if (IOException.class.isAssignableFrom(cls)) {
|
||||
return RpcException.NETWORK_EXCEPTION;
|
||||
} else if (ClassNotFoundException.class.isAssignableFrom(cls)) {
|
||||
return RpcException.SERIALIZATION_EXCEPTION;
|
||||
}
|
||||
}
|
||||
return super.getErrorCode(e);
|
||||
}
|
||||
|
||||
private class InternalHandler implements HttpHandler {
|
||||
|
||||
@Override
|
||||
public void handle(HttpServletRequest request, HttpServletResponse response)
|
||||
throws IOException, ServletException {
|
||||
String uri = request.getRequestURI();
|
||||
HttpInvokerServiceExporter skeleton = skeletonMap.get(uri);
|
||||
if (!"POST".equalsIgnoreCase(request.getMethod())) {
|
||||
response.setStatus(500);
|
||||
} else {
|
||||
RpcContext.getContext().setRemoteAddress(request.getRemoteAddr(), request.getRemotePort());
|
||||
try {
|
||||
skeleton.handleRequest(request, response);
|
||||
} catch (Throwable e) {
|
||||
throw new ServletException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.http;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.remoting.http.HttpBinder;
|
||||
import org.apache.dubbo.remoting.http.HttpHandler;
|
||||
import org.apache.dubbo.remoting.http.HttpServer;
|
||||
import org.apache.dubbo.rpc.RpcContext;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.protocol.AbstractProxyProtocol;
|
||||
|
||||
import com.googlecode.jsonrpc4j.HttpException;
|
||||
import com.googlecode.jsonrpc4j.JsonRpcClientException;
|
||||
import com.googlecode.jsonrpc4j.JsonRpcServer;
|
||||
import com.googlecode.jsonrpc4j.spring.JsonProxyFactoryBean;
|
||||
import org.springframework.remoting.RemoteAccessException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class HttpProtocol extends AbstractProxyProtocol {
|
||||
|
||||
public static final String ACCESS_CONTROL_ALLOW_ORIGIN_HEADER = "Access-Control-Allow-Origin";
|
||||
public static final String ACCESS_CONTROL_ALLOW_METHODS_HEADER = "Access-Control-Allow-Methods";
|
||||
public static final String ACCESS_CONTROL_ALLOW_HEADERS_HEADER = "Access-Control-Allow-Headers";
|
||||
|
||||
private final Map<String, HttpServer> serverMap = new ConcurrentHashMap<>();
|
||||
|
||||
private final Map<String, JsonRpcServer> skeletonMap = new ConcurrentHashMap<>();
|
||||
|
||||
private HttpBinder httpBinder;
|
||||
|
||||
public HttpProtocol() {
|
||||
super(HttpException.class, JsonRpcClientException.class);
|
||||
}
|
||||
|
||||
public void setHttpBinder(HttpBinder httpBinder) {
|
||||
this.httpBinder = httpBinder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDefaultPort() {
|
||||
return 80;
|
||||
}
|
||||
|
||||
private class InternalHandler implements HttpHandler {
|
||||
|
||||
private boolean cors;
|
||||
|
||||
public InternalHandler(boolean cors) {
|
||||
this.cors = cors;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException {
|
||||
String uri = request.getRequestURI();
|
||||
JsonRpcServer skeleton = skeletonMap.get(uri);
|
||||
if (cors) {
|
||||
response.setHeader(ACCESS_CONTROL_ALLOW_ORIGIN_HEADER, "*");
|
||||
response.setHeader(ACCESS_CONTROL_ALLOW_METHODS_HEADER, "POST");
|
||||
response.setHeader(ACCESS_CONTROL_ALLOW_HEADERS_HEADER, "*");
|
||||
}
|
||||
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
|
||||
response.setStatus(200);
|
||||
} else if ("POST".equalsIgnoreCase(request.getMethod())) {
|
||||
|
||||
RpcContext.getContext().setRemoteAddress(request.getRemoteAddr(), request.getRemotePort());
|
||||
try {
|
||||
skeleton.handle(request.getInputStream(), response.getOutputStream());
|
||||
} catch (Throwable e) {
|
||||
throw new ServletException(e);
|
||||
}
|
||||
} else {
|
||||
response.setStatus(500);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T> Runnable doExport(T impl, Class<T> type, URL url) throws RpcException {
|
||||
String addr = url.getIp() + ":" + url.getPort();
|
||||
HttpServer server = serverMap.get(addr);
|
||||
if (server == null) {
|
||||
server = httpBinder.bind(url, new InternalHandler(url.getParameter("cors", false)));
|
||||
serverMap.put(addr, server);
|
||||
}
|
||||
final String path = url.getAbsolutePath();
|
||||
JsonRpcServer skeleton = new JsonRpcServer(impl, type);
|
||||
skeletonMap.put(path, skeleton);
|
||||
return () -> skeletonMap.remove(path);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
protected <T> T doRefer(final Class<T> serviceType, URL url) throws RpcException {
|
||||
JsonProxyFactoryBean jsonProxyFactoryBean = new JsonProxyFactoryBean();
|
||||
jsonProxyFactoryBean.setServiceUrl(url.setProtocol("http").toIdentityString());
|
||||
jsonProxyFactoryBean.setServiceInterface(serviceType);
|
||||
|
||||
jsonProxyFactoryBean.afterPropertiesSet();
|
||||
return (T) jsonProxyFactoryBean.getObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getErrorCode(Throwable e) {
|
||||
if (e instanceof RemoteAccessException) {
|
||||
e = e.getCause();
|
||||
}
|
||||
if (e != null) {
|
||||
Class<?> cls = e.getClass();
|
||||
if (SocketTimeoutException.class.equals(cls)) {
|
||||
return RpcException.TIMEOUT_EXCEPTION;
|
||||
} else if (IOException.class.isAssignableFrom(cls)) {
|
||||
return RpcException.NETWORK_EXCEPTION;
|
||||
} else if (ClassNotFoundException.class.isAssignableFrom(cls)) {
|
||||
return RpcException.SERIALIZATION_EXCEPTION;
|
||||
}
|
||||
}
|
||||
return super.getErrorCode(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
super.destroy();
|
||||
for (String key : new ArrayList<>(serverMap.keySet())) {
|
||||
HttpServer server = serverMap.remove(key);
|
||||
if (server != null) {
|
||||
try {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Close jsonrpc server " + server.getUrl());
|
||||
}
|
||||
server.close();
|
||||
} catch (Throwable t) {
|
||||
logger.warn(t.getMessage(), t);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1 +1 @@
|
|||
http=org.apache.dubbo.rpc.protocol.http.HttpProtocol
|
||||
http=org.apache.dubbo.rpc.protocol.http.HttpProtocol
|
||||
|
|
@ -1,203 +1,69 @@
|
|||
/*
|
||||
* 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.http;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.beanutil.JavaBeanDescriptor;
|
||||
import org.apache.dubbo.common.beanutil.JavaBeanSerializeUtil;
|
||||
import org.apache.dubbo.common.extension.ExtensionLoader;
|
||||
import org.apache.dubbo.common.serialize.ObjectInput;
|
||||
import org.apache.dubbo.common.serialize.ObjectOutput;
|
||||
import org.apache.dubbo.common.serialize.Serialization;
|
||||
import org.apache.dubbo.common.serialize.nativejava.NativeJavaSerialization;
|
||||
import org.apache.dubbo.rpc.Exporter;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.Protocol;
|
||||
import org.apache.dubbo.rpc.ProxyFactory;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.service.GenericService;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
/**
|
||||
* HttpProtocolTest
|
||||
*/
|
||||
public class HttpProtocolTest {
|
||||
|
||||
@Test
|
||||
public void testHttpProtocol() {
|
||||
HttpServiceImpl server = new HttpServiceImpl();
|
||||
Assertions.assertFalse(server.isCalled());
|
||||
ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
|
||||
Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
|
||||
URL url = URL.valueOf("http://127.0.0.1:5342/" + HttpService.class.getName() + "?release=2.7.0");
|
||||
Exporter<HttpService> exporter = protocol.export(proxyFactory.getInvoker(server, HttpService.class, url));
|
||||
Invoker<HttpService> invoker = protocol.refer(HttpService.class, url);
|
||||
HttpService client = proxyFactory.getProxy(invoker);
|
||||
String result = client.sayHello("haha");
|
||||
Assertions.assertTrue(server.isCalled());
|
||||
Assertions.assertEquals("Hello, haha", result);
|
||||
invoker.destroy();
|
||||
exporter.unexport();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenericInvoke() {
|
||||
HttpServiceImpl server = new HttpServiceImpl();
|
||||
Assertions.assertFalse(server.isCalled());
|
||||
ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
|
||||
Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
|
||||
URL url = URL.valueOf("http://127.0.0.1:5342/" + HttpService.class.getName() + "?release=2.7.0");
|
||||
Exporter<HttpService> exporter = protocol.export(proxyFactory.getInvoker(server, HttpService.class, url));
|
||||
Invoker<GenericService> invoker = protocol.refer(GenericService.class, url);
|
||||
GenericService client = proxyFactory.getProxy(invoker, true);
|
||||
String result = (String) client.$invoke("sayHello", new String[]{"java.lang.String"}, new Object[]{"haha"});
|
||||
Assertions.assertTrue(server.isCalled());
|
||||
Assertions.assertEquals("Hello, haha", result);
|
||||
invoker.destroy();
|
||||
exporter.unexport();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenericInvokeWithNativeJava() throws IOException, ClassNotFoundException {
|
||||
HttpServiceImpl server = new HttpServiceImpl();
|
||||
Assertions.assertFalse(server.isCalled());
|
||||
ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
|
||||
Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
|
||||
URL url = URL.valueOf("http://127.0.0.1:5342/" + HttpService.class.getName() + "?release=2.7.0&generic=nativejava");
|
||||
Exporter<HttpService> exporter = protocol.export(proxyFactory.getInvoker(server, HttpService.class, url));
|
||||
Invoker<GenericService> invoker = protocol.refer(GenericService.class, url);
|
||||
GenericService client = proxyFactory.getProxy(invoker);
|
||||
|
||||
Serialization serialization = new NativeJavaSerialization();
|
||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||
|
||||
ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
|
||||
objectOutput.writeObject("haha");
|
||||
objectOutput.flushBuffer();
|
||||
|
||||
Object result = client.$invoke("sayHello", new String[]{"java.lang.String"}, new Object[]{byteArrayOutputStream.toByteArray()});
|
||||
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream((byte[]) result);
|
||||
ObjectInput objectInput = serialization.deserialize(url, byteArrayInputStream);
|
||||
Assertions.assertTrue(server.isCalled());
|
||||
Assertions.assertEquals("Hello, haha", objectInput.readObject());
|
||||
invoker.destroy();
|
||||
exporter.unexport();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenericInvokeWithBean() {
|
||||
HttpServiceImpl server = new HttpServiceImpl();
|
||||
Assertions.assertFalse(server.isCalled());
|
||||
ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
|
||||
Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
|
||||
URL url = URL.valueOf("http://127.0.0.1:5342/" + HttpService.class.getName() + "?release=2.7.0&generic=bean");
|
||||
Exporter<HttpService> exporter = protocol.export(proxyFactory.getInvoker(server, HttpService.class, url));
|
||||
Invoker<GenericService> invoker = protocol.refer(GenericService.class, url);
|
||||
GenericService client = proxyFactory.getProxy(invoker);
|
||||
|
||||
JavaBeanDescriptor javaBeanDescriptor = JavaBeanSerializeUtil.serialize("haha");
|
||||
|
||||
Object result = client.$invoke("sayHello", new String[]{"java.lang.String"}, new Object[]{javaBeanDescriptor});
|
||||
Assertions.assertTrue(server.isCalled());
|
||||
Assertions.assertEquals("Hello, haha", JavaBeanSerializeUtil.deserialize((JavaBeanDescriptor) result));
|
||||
invoker.destroy();
|
||||
exporter.unexport();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOverload() {
|
||||
HttpServiceImpl server = new HttpServiceImpl();
|
||||
Assertions.assertFalse(server.isCalled());
|
||||
ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
|
||||
Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
|
||||
URL url = URL.valueOf("http://127.0.0.1:5342/" + HttpService.class.getName() + "?release=2.7.0&hessian.overload.method=true&hessian2.request=false");
|
||||
Exporter<HttpService> exporter = protocol.export(proxyFactory.getInvoker(server, HttpService.class, url));
|
||||
Invoker<HttpService> invoker = protocol.refer(HttpService.class, url);
|
||||
HttpService client = proxyFactory.getProxy(invoker);
|
||||
String result = client.sayHello("haha");
|
||||
Assertions.assertEquals("Hello, haha", result);
|
||||
result = client.sayHello("haha", 1);
|
||||
Assertions.assertEquals("Hello, haha. ", result);
|
||||
invoker.destroy();
|
||||
exporter.unexport();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleClient() {
|
||||
HttpServiceImpl server = new HttpServiceImpl();
|
||||
Assertions.assertFalse(server.isCalled());
|
||||
ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
|
||||
Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
|
||||
URL url = URL.valueOf("http://127.0.0.1:5342/" + HttpService.class.getName() + "?release=2.7.0&client=simple");
|
||||
Exporter<HttpService> exporter = protocol.export(proxyFactory.getInvoker(server, HttpService.class, url));
|
||||
Invoker<HttpService> invoker = protocol.refer(HttpService.class, url);
|
||||
HttpService client = proxyFactory.getProxy(invoker);
|
||||
String result = client.sayHello("haha");
|
||||
Assertions.assertTrue(server.isCalled());
|
||||
Assertions.assertEquals("Hello, haha", result);
|
||||
invoker.destroy();
|
||||
exporter.unexport();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTimeOut() {
|
||||
HttpServiceImpl server = new HttpServiceImpl();
|
||||
ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
|
||||
Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
|
||||
URL url = URL.valueOf("http://127.0.0.1:5342/" + HttpService.class.getName() + "?release=2.7.0&timeout=10");
|
||||
Exporter<HttpService> exporter = protocol.export(proxyFactory.getInvoker(server, HttpService.class, url));
|
||||
Invoker<HttpService> invoker = protocol.refer(HttpService.class, url);
|
||||
HttpService client = proxyFactory.getProxy(invoker);
|
||||
try {
|
||||
client.timeOut(6000);
|
||||
fail();
|
||||
} catch (RpcException expected) {
|
||||
Assertions.assertTrue(expected.isTimeout());
|
||||
} finally {
|
||||
invoker.destroy();
|
||||
exporter.unexport();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomException() {
|
||||
HttpServiceImpl server = new HttpServiceImpl();
|
||||
ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
|
||||
Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
|
||||
URL url = URL.valueOf("http://127.0.0.1:5342/" + HttpService.class.getName() + "?release=2.7.0");
|
||||
Exporter<HttpService> exporter = protocol.export(proxyFactory.getInvoker(server, HttpService.class, url));
|
||||
Invoker<HttpService> invoker = protocol.refer(HttpService.class, url);
|
||||
HttpService client = proxyFactory.getProxy(invoker);
|
||||
try {
|
||||
client.customException();
|
||||
fail();
|
||||
} catch (HttpServiceImpl.MyException expected) {
|
||||
}
|
||||
invoker.destroy();
|
||||
exporter.unexport();
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.http;
|
||||
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.extension.ExtensionLoader;
|
||||
import org.apache.dubbo.rpc.Exporter;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.Protocol;
|
||||
import org.apache.dubbo.rpc.ProxyFactory;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
public class HttpProtocolTest {
|
||||
|
||||
@Test
|
||||
public void testJsonrpcProtocol() {
|
||||
JsonRpcServiceImpl server = new JsonRpcServiceImpl();
|
||||
assertFalse(server.isCalled());
|
||||
ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
|
||||
Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
|
||||
URL url = URL.valueOf("http://127.0.0.1:5342/" + JsonRpcService.class.getName() + "?version=1.0.0");
|
||||
Exporter<JsonRpcService> exporter = protocol.export(proxyFactory.getInvoker(server, JsonRpcService.class, url));
|
||||
Invoker<JsonRpcService> invoker = protocol.refer(JsonRpcService.class, url);
|
||||
JsonRpcService client = proxyFactory.getProxy(invoker);
|
||||
String result = client.sayHello("haha");
|
||||
assertTrue(server.isCalled());
|
||||
assertEquals("Hello, haha", result);
|
||||
invoker.destroy();
|
||||
exporter.unexport();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJsonrpcProtocolForServerJetty() {
|
||||
JsonRpcServiceImpl server = new JsonRpcServiceImpl();
|
||||
assertFalse(server.isCalled());
|
||||
ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
|
||||
Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
|
||||
URL url = URL.valueOf("http://127.0.0.1:5342/" + JsonRpcService.class.getName() + "?version=1.0.0&server=jetty");
|
||||
Exporter<JsonRpcService> exporter = protocol.export(proxyFactory.getInvoker(server, JsonRpcService.class, url));
|
||||
Invoker<JsonRpcService> invoker = protocol.refer(JsonRpcService.class, url);
|
||||
JsonRpcService client = proxyFactory.getProxy(invoker);
|
||||
String result = client.sayHello("haha");
|
||||
assertTrue(server.isCalled());
|
||||
assertEquals("Hello, haha", result);
|
||||
invoker.destroy();
|
||||
exporter.unexport();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.rpc.protocol.jsonrpc;
|
||||
package org.apache.dubbo.rpc.protocol.http;
|
||||
|
||||
public interface JsonRpcService {
|
||||
String sayHello(String name);
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.dubbo.rpc.protocol.jsonrpc;
|
||||
package org.apache.dubbo.rpc.protocol.http;
|
||||
|
||||
public class JsonRpcServiceImpl implements JsonRpcService {
|
||||
private boolean called;
|
||||
|
|
@ -1,163 +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.jsonrpc;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.remoting.http.HttpBinder;
|
||||
import org.apache.dubbo.remoting.http.HttpHandler;
|
||||
import org.apache.dubbo.remoting.http.HttpServer;
|
||||
import org.apache.dubbo.rpc.RpcContext;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.protocol.AbstractProxyProtocol;
|
||||
|
||||
import com.googlecode.jsonrpc4j.HttpException;
|
||||
import com.googlecode.jsonrpc4j.JsonRpcClientException;
|
||||
import com.googlecode.jsonrpc4j.JsonRpcServer;
|
||||
import com.googlecode.jsonrpc4j.spring.JsonProxyFactoryBean;
|
||||
import org.springframework.remoting.RemoteAccessException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class JsonRpcProtocol extends AbstractProxyProtocol {
|
||||
|
||||
public static final String ACCESS_CONTROL_ALLOW_ORIGIN_HEADER = "Access-Control-Allow-Origin";
|
||||
public static final String ACCESS_CONTROL_ALLOW_METHODS_HEADER = "Access-Control-Allow-Methods";
|
||||
public static final String ACCESS_CONTROL_ALLOW_HEADERS_HEADER = "Access-Control-Allow-Headers";
|
||||
|
||||
private final Map<String, HttpServer> serverMap = new ConcurrentHashMap<>();
|
||||
|
||||
private final Map<String, JsonRpcServer> skeletonMap = new ConcurrentHashMap<>();
|
||||
|
||||
private HttpBinder httpBinder;
|
||||
|
||||
public JsonRpcProtocol() {
|
||||
super(HttpException.class, JsonRpcClientException.class);
|
||||
}
|
||||
|
||||
public void setHttpBinder(HttpBinder httpBinder) {
|
||||
this.httpBinder = httpBinder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDefaultPort() {
|
||||
return 80;
|
||||
}
|
||||
|
||||
private class InternalHandler implements HttpHandler {
|
||||
|
||||
private boolean cors;
|
||||
|
||||
public InternalHandler(boolean cors) {
|
||||
this.cors = cors;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException {
|
||||
String uri = request.getRequestURI();
|
||||
JsonRpcServer skeleton = skeletonMap.get(uri);
|
||||
if (cors) {
|
||||
response.setHeader(ACCESS_CONTROL_ALLOW_ORIGIN_HEADER, "*");
|
||||
response.setHeader(ACCESS_CONTROL_ALLOW_METHODS_HEADER, "POST");
|
||||
response.setHeader(ACCESS_CONTROL_ALLOW_HEADERS_HEADER, "*");
|
||||
}
|
||||
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
|
||||
response.setStatus(200);
|
||||
} else if ("POST".equalsIgnoreCase(request.getMethod())) {
|
||||
|
||||
RpcContext.getContext().setRemoteAddress(request.getRemoteAddr(), request.getRemotePort());
|
||||
try {
|
||||
skeleton.handle(request.getInputStream(), response.getOutputStream());
|
||||
} catch (Throwable e) {
|
||||
throw new ServletException(e);
|
||||
}
|
||||
} else {
|
||||
response.setStatus(500);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T> Runnable doExport(T impl, Class<T> type, URL url) throws RpcException {
|
||||
String addr = url.getIp() + ":" + url.getPort();
|
||||
HttpServer server = serverMap.get(addr);
|
||||
if (server == null) {
|
||||
server = httpBinder.bind(url, new InternalHandler(url.getParameter("cors", false)));
|
||||
serverMap.put(addr, server);
|
||||
}
|
||||
final String path = url.getAbsolutePath();
|
||||
JsonRpcServer skeleton = new JsonRpcServer(impl, type);
|
||||
skeletonMap.put(path, skeleton);
|
||||
return () -> skeletonMap.remove(path);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
protected <T> T doRefer(final Class<T> serviceType, URL url) throws RpcException {
|
||||
JsonProxyFactoryBean jsonProxyFactoryBean = new JsonProxyFactoryBean();
|
||||
jsonProxyFactoryBean.setServiceUrl(url.setProtocol("http").toIdentityString());
|
||||
jsonProxyFactoryBean.setServiceInterface(serviceType);
|
||||
|
||||
jsonProxyFactoryBean.afterPropertiesSet();
|
||||
return (T) jsonProxyFactoryBean.getObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getErrorCode(Throwable e) {
|
||||
if (e instanceof RemoteAccessException) {
|
||||
e = e.getCause();
|
||||
}
|
||||
if (e != null) {
|
||||
Class<?> cls = e.getClass();
|
||||
if (SocketTimeoutException.class.equals(cls)) {
|
||||
return RpcException.TIMEOUT_EXCEPTION;
|
||||
} else if (IOException.class.isAssignableFrom(cls)) {
|
||||
return RpcException.NETWORK_EXCEPTION;
|
||||
} else if (ClassNotFoundException.class.isAssignableFrom(cls)) {
|
||||
return RpcException.SERIALIZATION_EXCEPTION;
|
||||
}
|
||||
}
|
||||
return super.getErrorCode(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
super.destroy();
|
||||
for (String key : new ArrayList<>(serverMap.keySet())) {
|
||||
HttpServer server = serverMap.remove(key);
|
||||
if (server != null) {
|
||||
try {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Close jsonrpc server " + server.getUrl());
|
||||
}
|
||||
server.close();
|
||||
} catch (Throwable t) {
|
||||
logger.warn(t.getMessage(), t);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
jsonrpc=org.apache.dubbo.rpc.protocol.jsonrpc.JsonRpcProtocol
|
||||
|
|
@ -1,69 +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.jsonrpc;
|
||||
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.extension.ExtensionLoader;
|
||||
import org.apache.dubbo.rpc.Exporter;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.Protocol;
|
||||
import org.apache.dubbo.rpc.ProxyFactory;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
public class JsonRpcProtocolTest {
|
||||
|
||||
@Test
|
||||
public void testJsonrpcProtocol() {
|
||||
JsonRpcServiceImpl server = new JsonRpcServiceImpl();
|
||||
assertFalse(server.isCalled());
|
||||
ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
|
||||
Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
|
||||
URL url = URL.valueOf("jsonrpc://127.0.0.1:5342/" + JsonRpcService.class.getName() + "?version=1.0.0");
|
||||
Exporter<JsonRpcService> exporter = protocol.export(proxyFactory.getInvoker(server, JsonRpcService.class, url));
|
||||
Invoker<JsonRpcService> invoker = protocol.refer(JsonRpcService.class, url);
|
||||
JsonRpcService client = proxyFactory.getProxy(invoker);
|
||||
String result = client.sayHello("haha");
|
||||
assertTrue(server.isCalled());
|
||||
assertEquals("Hello, haha", result);
|
||||
invoker.destroy();
|
||||
exporter.unexport();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJsonrpcProtocolForServerJetty9() {
|
||||
JsonRpcServiceImpl server = new JsonRpcServiceImpl();
|
||||
assertFalse(server.isCalled());
|
||||
ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
|
||||
Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
|
||||
URL url = URL.valueOf("jsonrpc://127.0.0.1:5342/" + JsonRpcService.class.getName() + "?version=1.0.0&server=jetty9");
|
||||
Exporter<JsonRpcService> exporter = protocol.export(proxyFactory.getInvoker(server, JsonRpcService.class, url));
|
||||
Invoker<JsonRpcService> invoker = protocol.refer(JsonRpcService.class, url);
|
||||
JsonRpcService client = proxyFactory.getProxy(invoker);
|
||||
String result = client.sayHello("haha");
|
||||
assertTrue(server.isCalled());
|
||||
assertEquals("Hello, haha", result);
|
||||
invoker.destroy();
|
||||
exporter.unexport();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -32,10 +32,10 @@
|
|||
<module>dubbo-rpc-api</module>
|
||||
<module>dubbo-rpc-dubbo</module>
|
||||
<module>dubbo-rpc-injvm</module>
|
||||
<module>dubbo-rpc-jsonrpc</module>
|
||||
<module>dubbo-rpc-http</module>
|
||||
<module>dubbo-rpc-rmi</module>
|
||||
<module>dubbo-rpc-hessian</module>
|
||||
<module>dubbo-rpc-http</module>
|
||||
<module>dubbo-rpc-http-invoker</module>
|
||||
<module>dubbo-rpc-webservice</module>
|
||||
<module>dubbo-rpc-native-thrift</module>
|
||||
<module>dubbo-rpc-thrift</module>
|
||||
|
|
|
|||
Loading…
Reference in New Issue