├── .gitignore
├── LICENSE
├── README.md
├── com.qcloud.weapp.demo
├── .classpath
├── .project
├── .settings
│ ├── .jsdtscope
│ ├── org.eclipse.jdt.core.prefs
│ ├── org.eclipse.wst.common.component
│ ├── org.eclipse.wst.common.project.facet.core.xml
│ ├── org.eclipse.wst.jsdt.ui.superType.container
│ ├── org.eclipse.wst.jsdt.ui.superType.name
│ └── org.eclipse.wst.ws.service.policy.prefs
├── WebContent
│ ├── META-INF
│ │ └── MANIFEST.MF
│ └── WEB-INF
│ │ └── web.xml
├── lib
│ └── org.json.jar
└── src
│ └── com
│ └── qcloud
│ └── weapp
│ └── demo
│ ├── ChatTunnelHandler.java
│ ├── QCloud.java
│ ├── Startup.java
│ └── servlet
│ ├── LoginServlet.java
│ ├── TunnelServlet.java
│ └── UserServlet.java
├── com.qcloud.weapp.sdk
├── .classpath
├── .gitignore
├── .project
├── .settings
│ ├── org.eclipse.jdt.core.prefs
│ ├── org.eclipse.wst.common.component
│ └── org.eclipse.wst.common.project.facet.core.xml
├── lib
│ └── org.json.jar
└── src
│ ├── META-INF
│ └── MANIFEST.MF
│ └── com
│ └── qcloud
│ └── weapp
│ ├── Configuration.java
│ ├── ConfigurationException.java
│ ├── ConfigurationManager.java
│ ├── Hash.java
│ ├── HttpRequest.java
│ ├── authorization
│ ├── AuthorizationAPI.java
│ ├── AuthorizationAPIException.java
│ ├── Constants.java
│ ├── LoginService.java
│ ├── LoginServiceException.java
│ └── UserInfo.java
│ └── tunnel
│ ├── EmitError.java
│ ├── EmitResult.java
│ ├── Tunnel.java
│ ├── TunnelAPI.java
│ ├── TunnelClient.java
│ ├── TunnelHandleOptions.java
│ ├── TunnelHandler.java
│ ├── TunnelInvalidInfo.java
│ ├── TunnelInvalidType.java
│ ├── TunnelMessage.java
│ ├── TunnelRoom.java
│ └── TunnelService.java
└── com.qcloud.weapp.test
├── .classpath
├── .gitignore
├── .project
├── .settings
└── org.eclipse.jdt.core.prefs
├── lib
├── mockito-all-1.10.19.jar
└── org.json.jar
└── src
└── com
└── qcloud
└── weapp
└── test
├── HttpMock.java
├── URLConnectionMock.java
├── authorization
├── LoginServiceTest.java
└── LoginServiceTestHelper.java
└── tunnel
├── TunnelServiceTest.java
└── TunnelServiceTestHelper.java
/.gitignore:
--------------------------------------------------------------------------------
1 | build
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 by Tencent Cloud
4 |
5 | Permission is hereby granted, free of charge, to any person
6 | obtaining a copy of this software and associated documentation
7 | files (the "Software"), to deal in the Software without
8 | restriction, including without limitation the rights to use,
9 | copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the
11 | Software is furnished to do so, subject to the following
12 | conditions:
13 |
14 | The above copyright notice and this permission notice shall be
15 | included in all copies or substantial portions of the Software.
16 |
17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24 | OTHER DEALINGS IN THE SOFTWARE.
25 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Wafer 服务端 SDK - Java
2 | =================================
3 |
4 | [](LICENSE)
5 |
6 | 本项目是 [Wafer](https://github.com/tencentyun/wafer) 组成部分,以 SDK 的形式为业务服务器提供以下服务:
7 |
8 | + [会话服务](https://github.com/tencentyun/wafer/wiki/会话服务)
9 | + [信道服务](https://github.com/tencentyun/wafer/wiki/信道服务)
10 |
11 | ## SDK 获取
12 |
13 | 本项目遵守 [MIT](LICENSE) 协议,可以直接[下载 SDK 源码][sdk-download]进行修改、编译和发布。
14 |
15 | > 如果使用[自动部署](https://github.com/tencentyun/wafer/wiki/%E8%87%AA%E5%8A%A8%E9%83%A8%E7%BD%B2)并选择 Java 语言,则分配的业务服务器里已经部署了本 SDK 和 Demo 的发行版本。
16 |
17 | ## API
18 |
19 | 请参考[线上 API 文档][api-url]。
20 |
21 | ## 使用示例(Servlet)
22 |
23 | ### 配置 SDK
24 |
25 | SDK 必须经过初始化配置之后才能使用。可以选择使用代码初始化或者配置文件初始化。初始化配置建议在 `Servlet::init()` 里进行。
26 |
27 | 使用代码初始化:
28 |
29 | ```java
30 | import com.qcloud.weapp.*;
31 |
32 | Configuration configuration = new Configuration();
33 |
34 | // 业务服务器访问域名
35 | configuration.setServerHost("199447.qcloud.la");
36 | // 鉴权服务地址
37 | configuration.setAuthServerUrl("http://10.0.12.135/mina_auth/");
38 | // 信道服务地址
39 | configuration.setTunnelServerUrl("https://ws.qcloud.com/");
40 | // 信道服务签名 key
41 | configuration.setTunnelSignatureKey("my$ecretkey");
42 | // 网络请求超时设置,单位为秒
43 | configuration.setNetworkTimeout(30);
44 |
45 | ConfigurationManager.setup(configuration);
46 | ```
47 |
48 | 使用配置文件初始化:
49 |
50 | ```java
51 | import com.qcloud.weapp.*;
52 |
53 | var configFilePath = "/etc/qcloud/sdk.config";
54 | ConfigurationManager.setupFromFile(configFilePath);
55 | ```
56 |
57 | 关于 SDK 配置字段的含义以及配置文件格式的更多信息,请参考[服务端 SDK 配置][sdk-config-wiki]。
58 |
59 | ### 使用 SDK 提供登录服务
60 |
61 | #### 登录
62 |
63 | 业务服务器提供一个路由处理客户端的登录请求,直接把该请求交给 SDK 来处理即可完成登录。登录成功后,可以获取用户信息。
64 |
65 | ```java
66 | import com.qcloud.weapp.*;
67 | import com.qcloud.weapp.authorization.*;
68 |
69 | @WebServlet("/login")
70 | public class LoginServlet extends HttpServlet {
71 | /**
72 | * 处理登录请求
73 | * */
74 | protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
75 | // 通过 ServletRequest 和 ServletResponse 初始化登录服务
76 | LoginService service = new LoginService(request, response);
77 | try {
78 | // 调用登录接口,如果登录成功可以获得登录信息
79 | UserInfo userInfo = service.login();
80 | System.out.println("========= LoginSuccess, UserInfo: ==========");
81 | System.out.println(userInfo.toString());
82 | } catch (LoginServiceException e) {
83 | // 登录失败会抛出登录失败异常
84 | e.printStackTrace();
85 | } catch (ConfigurationException e) {
86 | // SDK 如果还没有配置会抛出配置异常
87 | e.printStackTrace();
88 | }
89 | }
90 | }
91 | ```
92 |
93 | > 如果登录失败,[login()][login-api] 方法会抛出异常,需要使用 try-catch 来捕获异常。该异常可以不用处理,抛出来是为了方便业务服务器可以进行记录和监控。
94 |
95 | #### 获取会话状态
96 |
97 | 客户端交给业务服务器的请求,业务服务器可以通过 SDK 来检查该请求是否包含合法的微信小程序会话。如果包含,则会返回会话对应的用户信息。
98 |
99 | ```java
100 | import com.qcloud.weapp.*;
101 | import com.qcloud.weapp.authorization.*;
102 |
103 | @WebServlet("/user")
104 | public class UserServlet extends HttpServlet {
105 | /**
106 | * 从请求中获取会话中的用户信息
107 | */
108 | protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
109 | LoginService service = new LoginService(request, response);
110 | try {
111 | // 调用检查登录接口,成功后可以获得用户信息,进行正常的业务请求
112 | UserInfo userInfo = service.check();
113 |
114 | // 获取会话成功,输出获得的用户信息
115 | JSONObject result = new JSONObject();
116 | JSONObject data = new JSONObject();
117 | data.put("userInfo", new JSONObject(userInfo));
118 | result.put("code", 0);
119 | result.put("message", "OK");
120 | result.put("data", data);
121 | response.setContentType("application/json");
122 | response.setCharacterEncoding("utf-8");
123 | response.getWriter().write(result.toString());
124 |
125 | } catch (LoginServiceException e) {
126 | e.printStackTrace();
127 | } catch (JSONException e) {
128 | e.printStackTrace();
129 | } catch (ConfigurationException e) {
130 | e.printStackTrace();
131 | }
132 | }
133 | }
134 |
135 | ```
136 |
137 | > 如果检查会话失败,或者会话无效,[check()][check-api] 方法会抛出异常,需要使用 try-catch 来捕获异常。该异常可以不用处理,抛出来是为了方便业务服务器可以进行记录和监控。
138 |
139 | 阅读解决方案文档中的[会话服务][session-service-wiki]了解更多解决方案中关于鉴权服务的技术资料。
140 |
141 | ### 使用 SDK 提供信道服务
142 |
143 | 业务在一个路由上提供信道服务,只需把该路由上的请求都交给 SDK 的信道服务处理即可。
144 |
145 | ```java
146 | import com.qcloud.weapp.*;
147 | import com.qcloud.weapp.tunnel.*;
148 | import com.qcloud.weapp.demo.ChatTunnelHandler;
149 |
150 | @WebServlet("/tunnel")
151 | public class TunnelServlet extends HttpServlet {
152 | /**
153 | * 把所有的请求交给 SDK 处理,提供 TunnelHandler 处理信道事件
154 | */
155 | protected void service(HttpServletRequest request, HttpServletResponse response)
156 | throws ServletException, IOException {
157 |
158 | // 创建信道服务处理信道相关请求
159 | TunnelService tunnelService = new TunnelService(request, response);
160 |
161 | try {
162 | // 配置是可选的,配置 CheckLogin 为 true 的话,会在隧道建立之前获取用户信息,以便业务将隧道和用户关联起来
163 | TunnelHandleOptions options = new TunnelHandleOptions();
164 | options.setCheckLogin(true);
165 |
166 | // 需要实现信道处理器,ChatTunnelHandler 是一个实现的范例
167 | tunnelService.handle(new ChatTunnelHandler(), options);
168 | } catch (ConfigurationException e) {
169 | e.printStackTrace();
170 | }
171 | }
172 | }
173 | ```
174 |
175 | 使用信道服务需要实现处理器,来获取处理信道的各种事件,具体可参考接口 [TunnelHandler][tunnel-handler-api] 的 API 文档以及配套 Demo 中的 [ChatTunnelHandler][chat-handler-source] 的实现。
176 |
177 | 阅读解决方案文档中的[信道服务][tunnel-service-wiki]了解更多解决方案中关于鉴权服务的技术资料。
178 |
179 |
180 | ## 在DEMO基础上进行开发
181 | 将编译好的文件放到 /var/lib/tomcat/webapps/ 目录下
182 |
183 | tomcat代码配置 /etc/tomcat
184 |
185 | 重启tomcat服务 service tomcat restart
186 |
187 |
188 |
189 |
190 | ## 反馈和贡献
191 |
192 | 如有问题,欢迎使用 [Issues][new-issue] 提出,也欢迎广大开发者给我们提 [Pull Request][pr]。
193 |
194 | [wafer]: https://github.com/tencentyun/wafer "查看 Wafer 根项目"
195 | [sdk-download]: https://github.com/tencentyun/wafer-java-server-sdk/archive/master.zip "下载 Java SDK 源码"
196 | [api-url]: https://tencentyun.github.io/wafer-java-server-sdk/api/ "查看 Java SDK API 文档"
197 | [sdk-config-wiki]: https://github.com/tencentyun/wafer/wiki/%E6%9C%8D%E5%8A%A1%E7%AB%AF-SDK-%E9%85%8D%E7%BD%AE "查看服务端 SDK 配置"
198 | [session-service-wiki]: https://github.com/tencentyun/wafer/wiki/会话服务 "查看关于会话服务的更多资料"
199 | [tunnel-service-wiki]: https://github.com/tencentyun/wafer/wiki/信道服务 "查看关于信道服务的更多资料"
200 | [login-api]: https://tencentyun.github.io/wafer-java-server-sdk/api/com/qcloud/weapp/authorization/LoginService.html#login-- "查看 LoginService::login() 方法 API"
201 | [check-api]: https://tencentyun.github.io/wafer-java-server-sdk/api/com/qcloud/weapp/authorization/LoginService.html#check-- "查看 LoginService::ckeck() 方法 API"
202 | [tunnel-handler-api]: https://tencentyun.github.io/wafer-java-server-sdk/api/com/qcloud/weapp/tunnel/TunnelHandler.html "查看 TunnelHandler 接口 API 文档"
203 | [chat-handler-source]: https://github.com/tencentyun/wafer-java-server-sdk/blob/master/com.qcloud.weapp.demo/src/com/qcloud/weapp/demo/ChatTunnelHandler.java "查看 ChatTunnelHandler 示例代码"
204 |
205 | [new-issue]: https://github.com/CFETeam/wafer-server-sdk-csharp/issues/new "反馈建议和问题"
206 | [pr]: https://github.com/CFETeam/wafer-server-sdk-csharp/pulls "创建 Pull Request"
207 |
208 | ## LICENSE
209 |
210 | [MIT](LICENSE)
211 |
--------------------------------------------------------------------------------
/com.qcloud.weapp.demo/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/com.qcloud.weapp.demo/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | com.qcloud.weapp.demo
4 |
5 |
6 | com.qcloud.weapp.sdk
7 |
8 |
9 |
10 | org.eclipse.jdt.core.javabuilder
11 |
12 |
13 |
14 |
15 | org.eclipse.wst.common.project.facet.core.builder
16 |
17 |
18 |
19 |
20 | org.eclipse.wst.validation.validationbuilder
21 |
22 |
23 |
24 |
25 |
26 | org.eclipse.jem.workbench.JavaEMFNature
27 | org.eclipse.wst.common.modulecore.ModuleCoreNature
28 | org.eclipse.wst.common.project.facet.core.nature
29 | org.eclipse.jdt.core.javanature
30 | org.eclipse.wst.jsdt.core.jsNature
31 |
32 |
33 |
--------------------------------------------------------------------------------
/com.qcloud.weapp.demo/.settings/.jsdtscope:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/com.qcloud.weapp.demo/.settings/org.eclipse.jdt.core.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
3 | org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
4 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
5 | org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
6 | org.eclipse.jdt.core.compiler.compliance=1.8
7 | org.eclipse.jdt.core.compiler.debug.lineNumber=generate
8 | org.eclipse.jdt.core.compiler.debug.localVariable=generate
9 | org.eclipse.jdt.core.compiler.debug.sourceFile=generate
10 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
11 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
12 | org.eclipse.jdt.core.compiler.source=1.8
13 |
--------------------------------------------------------------------------------
/com.qcloud.weapp.demo/.settings/org.eclipse.wst.common.component:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | uses
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/com.qcloud.weapp.demo/.settings/org.eclipse.wst.common.project.facet.core.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/com.qcloud.weapp.demo/.settings/org.eclipse.wst.jsdt.ui.superType.container:
--------------------------------------------------------------------------------
1 | org.eclipse.wst.jsdt.launching.baseBrowserLibrary
--------------------------------------------------------------------------------
/com.qcloud.weapp.demo/.settings/org.eclipse.wst.jsdt.ui.superType.name:
--------------------------------------------------------------------------------
1 | Window
--------------------------------------------------------------------------------
/com.qcloud.weapp.demo/.settings/org.eclipse.wst.ws.service.policy.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | org.eclipse.wst.ws.service.policy.projectEnabled=false
3 |
--------------------------------------------------------------------------------
/com.qcloud.weapp.demo/WebContent/META-INF/MANIFEST.MF:
--------------------------------------------------------------------------------
1 | Manifest-Version: 1.0
2 | Class-Path:
3 |
4 |
--------------------------------------------------------------------------------
/com.qcloud.weapp.demo/WebContent/WEB-INF/web.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | com.qcloud.weapp.demo
4 |
--------------------------------------------------------------------------------
/com.qcloud.weapp.demo/lib/org.json.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tencentyun/wafer-java-server-sdk/ccb281e985cc725c6f516f1ba47b46ad375e7b64/com.qcloud.weapp.demo/lib/org.json.jar
--------------------------------------------------------------------------------
/com.qcloud.weapp.demo/src/com/qcloud/weapp/demo/ChatTunnelHandler.java:
--------------------------------------------------------------------------------
1 | package com.qcloud.weapp.demo;
2 |
3 | import java.util.HashMap;
4 |
5 | import org.json.JSONException;
6 | import org.json.JSONObject;
7 |
8 | import com.qcloud.weapp.authorization.UserInfo;
9 | import com.qcloud.weapp.tunnel.EmitError;
10 | import com.qcloud.weapp.tunnel.EmitResult;
11 | import com.qcloud.weapp.tunnel.Tunnel;
12 | import com.qcloud.weapp.tunnel.TunnelHandler;
13 | import com.qcloud.weapp.tunnel.TunnelInvalidInfo;
14 | import com.qcloud.weapp.tunnel.TunnelMessage;
15 | import com.qcloud.weapp.tunnel.TunnelRoom;
16 |
17 | /**
18 | *
实现 WebSocket 信道处理器
19 | * 本示例配合客户端 Demo 实现一个简单的聊天室功能
20 | *
21 | * 信道处理器需要处理信道的完整声明周期,包括:
22 | *
23 | * - onTunnelRequest() - 当用户发起信道请求的时候,会得到用户信息,此时可以关联信道 ID 和用户信息
24 | * - onTunnelConnect() - 当用户建立了信道连接之后,可以记录下已经连接的信道
25 | * - onTunnelMessage() - 当用户消息发送到信道上时,使用该函数处理信道的消息
26 | * - onTunnelClose() - 当信道关闭时,清理关于该信道的信息,以及回收相关资源
27 | *
28 | * */
29 | public class ChatTunnelHandler implements TunnelHandler {
30 |
31 | /**
32 | * 记录 WebSocket 信道对应的用户。在实际的业务中,应该使用数据库进行存储跟踪,这里作为示例只是演示其作用
33 | * */
34 | private static HashMap userMap = new HashMap();
35 |
36 | /**
37 | * 创建一个房间,包含当前已连接的 WebSocket 信道列表
38 | * */
39 | private static TunnelRoom room = new TunnelRoom();
40 |
41 | /**
42 | * 实现 OnTunnelRequest 方法
43 | * 在客户端请求 WebSocket 信道连接之后,会调用 OnTunnelRequest 方法,此时可以把信道 ID 和用户信息关联起来
44 | * */
45 | @Override
46 | public void onTunnelRequest(Tunnel tunnel, UserInfo userInfo) {
47 | if (tunnel.getTunnelId() == "test") {
48 | userInfo = new UserInfo();
49 | }
50 | if (userInfo != null) {
51 | userMap.put(tunnel.getTunnelId(), userInfo);
52 | }
53 | System.out.println(String.format("Tunnel Connected: %s", tunnel.getTunnelId()));
54 | }
55 |
56 | /**
57 | * 实现 OnTunnelConnect 方法
58 | * 在客户端成功连接 WebSocket 信道服务之后会调用该方法,此时通知所有其它在线的用户当前总人数以及刚加入的用户是谁
59 | * */
60 | @Override
61 | public void onTunnelConnect(Tunnel tunnel) {
62 | if (userMap.containsKey(tunnel.getTunnelId())) {
63 | room.addTunnel(tunnel);
64 | JSONObject peopleMessage = new JSONObject();
65 | try {
66 | peopleMessage.put("total", room.getTunnelCount());
67 | peopleMessage.put("enter", new JSONObject(userMap.get(tunnel.getTunnelId())));
68 | } catch (JSONException e) {
69 | e.printStackTrace();
70 | }
71 | broadcast("people", peopleMessage);
72 | } else {
73 | closeTunnel(tunnel);
74 | }
75 | }
76 |
77 | /**
78 | * 实现 OnTunnelMessage 方法
79 | * 客户端推送消息到 WebSocket 信道服务器上后,会调用该方法,此时可以处理信道的消息。
80 | * 在本示例,我们处理 "speak" 类型的消息,该消息表示有用户发言。我们把这个发言的信息广播到所有在线的 WebSocket 信道上
81 | * */
82 | @Override
83 | public void onTunnelMessage(Tunnel tunnel, TunnelMessage message) {
84 | if (message.getType().equals("speak") && userMap.containsKey(tunnel.getTunnelId())) {
85 | JSONObject speakMessage = new JSONObject();
86 | try {
87 | JSONObject messageContent = (JSONObject) message.getContent();
88 | speakMessage.put("word", messageContent.getString("word"));
89 | speakMessage.put("who", new JSONObject(userMap.get(tunnel.getTunnelId())));
90 | } catch (JSONException e) {
91 | e.printStackTrace();
92 | }
93 | broadcast("speak", speakMessage);
94 | } else {
95 | closeTunnel(tunnel);
96 | }
97 |
98 | }
99 |
100 | /**
101 | * 实现 OnTunnelClose 方法
102 | * 客户端关闭 WebSocket 信道或者被信道服务器判断为已断开后,会调用该方法,此时可以进行清理及通知操作
103 | * */
104 | @Override
105 | public void onTunnelClose(Tunnel tunnel) {
106 | UserInfo leaveUser = null;
107 | if (userMap.containsKey(tunnel.getTunnelId())) {
108 | leaveUser = userMap.get(tunnel.getTunnelId());
109 | userMap.remove(tunnel.getTunnelId());
110 | }
111 | room.removeTunnel(tunnel);
112 | JSONObject peopleMessage = new JSONObject();
113 | try {
114 | peopleMessage.put("total", room.getTunnelCount());
115 | peopleMessage.put("leave", new JSONObject(leaveUser));
116 | } catch (JSONException e) {
117 | e.printStackTrace();
118 | }
119 | broadcast("people", peopleMessage);
120 | }
121 |
122 | /**
123 | * 关闭指定的信道
124 | * */
125 | private void closeTunnel(Tunnel tunnel) {
126 | try {
127 | tunnel.close();
128 | } catch (EmitError e) {
129 | e.printStackTrace();
130 | }
131 | }
132 |
133 | /**
134 | * 广播消息到房间里所有的信道
135 | * */
136 | private void broadcast(String messageType, JSONObject messageContent) {
137 | try {
138 | EmitResult result = room.broadcast(messageType, messageContent);
139 | // 广播后发现的无效信道进行清理
140 | for (TunnelInvalidInfo invalidInfo : result.getTunnelInvalidInfos()) {
141 | onTunnelClose(Tunnel.getById(invalidInfo.getTunnelId()));
142 | }
143 | } catch (EmitError e) {
144 | // 如果消息发送发生异常,这里可以进行错误处理或者重试的逻辑
145 | e.printStackTrace();
146 | }
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/com.qcloud.weapp.demo/src/com/qcloud/weapp/demo/QCloud.java:
--------------------------------------------------------------------------------
1 | package com.qcloud.weapp.demo;
2 |
3 | import org.json.JSONException;
4 |
5 | import com.qcloud.weapp.ConfigurationManager;
6 | import com.qcloud.weapp.ConfigurationException;
7 |
8 | public class QCloud {
9 |
10 | public static void setupSDK() {
11 | try {
12 | String configFilePath = getConfigFilePath();
13 | System.out.println("QCloud SDK 配置文件路径:" + configFilePath);
14 |
15 | ConfigurationManager.setupFromFile(configFilePath);
16 | System.out.println("QCloud SDK 已成功配置!");
17 | } catch (JSONException e) {
18 | e.printStackTrace();
19 | } catch (ConfigurationException e) {
20 | e.printStackTrace();
21 | }
22 | }
23 |
24 | private static String getConfigFilePath() {
25 | String osName = System.getProperty("os.name").toLowerCase();
26 | String defaultConfigFilePath = null;
27 | boolean isWindows = osName.indexOf("windows") > -1;
28 | boolean isLinux = osName.indexOf("linux") > -1;
29 |
30 | if (isWindows) {
31 | defaultConfigFilePath = "C:\\qcloud\\sdk.config";
32 | }
33 | else if (isLinux) {
34 | defaultConfigFilePath = "/etc/qcloud/sdk.config";
35 | }
36 | return defaultConfigFilePath;
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/com.qcloud.weapp.demo/src/com/qcloud/weapp/demo/Startup.java:
--------------------------------------------------------------------------------
1 | package com.qcloud.weapp.demo;
2 |
3 | import javax.servlet.ServletConfig;
4 | import javax.servlet.ServletException;
5 | import javax.servlet.annotation.WebServlet;
6 | import javax.servlet.http.HttpServlet;
7 |
8 | /**
9 | * Servlet implementation class Startup
10 | */
11 | @WebServlet(name="Startup", urlPatterns = {}, loadOnStartup = 1)
12 | public class Startup extends HttpServlet {
13 | private static final long serialVersionUID = 1L;
14 |
15 | /**
16 | * @see Servlet#init(ServletConfig)
17 | */
18 | @Override
19 | public void init(ServletConfig config) throws ServletException {
20 | QCloud.setupSDK();
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/com.qcloud.weapp.demo/src/com/qcloud/weapp/demo/servlet/LoginServlet.java:
--------------------------------------------------------------------------------
1 | package com.qcloud.weapp.demo.servlet;
2 |
3 | import java.io.IOException;
4 | import javax.servlet.ServletException;
5 | import javax.servlet.annotation.WebServlet;
6 | import javax.servlet.http.HttpServlet;
7 | import javax.servlet.http.HttpServletRequest;
8 | import javax.servlet.http.HttpServletResponse;
9 |
10 | import com.qcloud.weapp.ConfigurationException;
11 | import com.qcloud.weapp.authorization.LoginService;
12 | import com.qcloud.weapp.authorization.LoginServiceException;
13 | import com.qcloud.weapp.authorization.UserInfo;
14 |
15 | @WebServlet("/login")
16 | public class LoginServlet extends HttpServlet {
17 |
18 | private static final long serialVersionUID = 6585319986631669934L;
19 |
20 | /**
21 | * 处理登录请求
22 | * */
23 | protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
24 | // 通过 ServletRequest 和 ServletResponse 初始化登录服务
25 | LoginService service = new LoginService(request, response);
26 | try {
27 | // 调用登录接口,如果登录成功可以获得登录信息
28 | UserInfo userInfo = service.login();
29 | System.out.println("========= LoginSuccess, UserInfo: ==========");
30 | System.out.println(userInfo.toString());
31 | } catch (LoginServiceException e) {
32 | // 登录失败会抛出登录失败异常
33 | e.printStackTrace();
34 | } catch (ConfigurationException e) {
35 | // SDK 如果还没有配置会抛出配置异常
36 | e.printStackTrace();
37 | }
38 | }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/com.qcloud.weapp.demo/src/com/qcloud/weapp/demo/servlet/TunnelServlet.java:
--------------------------------------------------------------------------------
1 | package com.qcloud.weapp.demo.servlet;
2 |
3 | import java.io.IOException;
4 | import javax.servlet.ServletException;
5 | import javax.servlet.annotation.WebServlet;
6 | import javax.servlet.http.HttpServlet;
7 | import javax.servlet.http.HttpServletRequest;
8 | import javax.servlet.http.HttpServletResponse;
9 |
10 | import com.qcloud.weapp.ConfigurationException;
11 | import com.qcloud.weapp.tunnel.TunnelHandleOptions;
12 | import com.qcloud.weapp.tunnel.TunnelService;
13 | import com.qcloud.weapp.demo.ChatTunnelHandler;
14 |
15 | /**
16 | * 使用 SDK 提供信道服务
17 | */
18 | @WebServlet("/tunnel")
19 | public class TunnelServlet extends HttpServlet {
20 | private static final long serialVersionUID = -6490955903032763981L;
21 |
22 | /**
23 | * 把所有的请求交给 SDK 处理,提供 TunnelHandler 处理信道事件
24 | */
25 | protected void service(HttpServletRequest request, HttpServletResponse response)
26 | throws ServletException, IOException {
27 |
28 | // 创建信道服务处理信道相关请求
29 | TunnelService tunnelService = new TunnelService(request, response);
30 |
31 | try {
32 | // 配置是可选的,配置 CheckLogin 为 true 的话,会在隧道建立之前获取用户信息,以便业务将隧道和用户关联起来
33 | TunnelHandleOptions options = new TunnelHandleOptions();
34 | options.setCheckLogin(true);
35 |
36 | // 需要实现信道处理器,ChatTunnelHandler 是一个实现的范例
37 | tunnelService.handle(new ChatTunnelHandler(), options);
38 | } catch (ConfigurationException e) {
39 | e.printStackTrace();
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/com.qcloud.weapp.demo/src/com/qcloud/weapp/demo/servlet/UserServlet.java:
--------------------------------------------------------------------------------
1 | package com.qcloud.weapp.demo.servlet;
2 |
3 | import java.io.IOException;
4 | import javax.servlet.ServletException;
5 | import javax.servlet.annotation.WebServlet;
6 | import javax.servlet.http.HttpServlet;
7 | import javax.servlet.http.HttpServletRequest;
8 | import javax.servlet.http.HttpServletResponse;
9 |
10 | import org.json.JSONException;
11 | import org.json.JSONObject;
12 |
13 | import com.qcloud.weapp.ConfigurationException;
14 | import com.qcloud.weapp.authorization.LoginService;
15 | import com.qcloud.weapp.authorization.LoginServiceException;
16 | import com.qcloud.weapp.authorization.UserInfo;
17 |
18 | /**
19 | * Servlet implementation class UserServlet
20 | */
21 | @WebServlet("/user")
22 | public class UserServlet extends HttpServlet {
23 |
24 | private static final long serialVersionUID = 6579706670441711811L;
25 |
26 | /**
27 | * 从请求中获取会话中的用户信息
28 | */
29 | protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
30 | LoginService service = new LoginService(request, response);
31 | try {
32 | // 调用检查登录接口,成功后可以获得用户信息,进行正常的业务请求
33 | UserInfo userInfo = service.check();
34 |
35 | // 获取会话成功,输出获得的用户信息
36 | JSONObject result = new JSONObject();
37 | JSONObject data = new JSONObject();
38 | data.put("userInfo", new JSONObject(userInfo));
39 | result.put("code", 0);
40 | result.put("message", "OK");
41 | result.put("data", data);
42 | response.setContentType("application/json");
43 | response.setCharacterEncoding("utf-8");
44 | response.getWriter().write(result.toString());
45 |
46 | } catch (LoginServiceException e) {
47 | e.printStackTrace();
48 | } catch (JSONException e) {
49 | e.printStackTrace();
50 | } catch (ConfigurationException e) {
51 | e.printStackTrace();
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/com.qcloud.weapp.sdk/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/com.qcloud.weapp.sdk/.gitignore:
--------------------------------------------------------------------------------
1 | /bin/
2 |
--------------------------------------------------------------------------------
/com.qcloud.weapp.sdk/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | com.qcloud.weapp.sdk
4 |
5 |
6 |
7 |
8 |
9 | org.eclipse.wst.common.project.facet.core.builder
10 |
11 |
12 |
13 |
14 | org.eclipse.jdt.core.javabuilder
15 |
16 |
17 |
18 |
19 | org.eclipse.wst.validation.validationbuilder
20 |
21 |
22 |
23 |
24 |
25 | org.eclipse.jem.workbench.JavaEMFNature
26 | org.eclipse.wst.common.modulecore.ModuleCoreNature
27 | org.eclipse.jdt.core.javanature
28 | org.eclipse.wst.common.project.facet.core.nature
29 |
30 |
31 |
--------------------------------------------------------------------------------
/com.qcloud.weapp.sdk/.settings/org.eclipse.jdt.core.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
3 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
4 | org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
5 | org.eclipse.jdt.core.compiler.compliance=1.6
6 | org.eclipse.jdt.core.compiler.debug.lineNumber=generate
7 | org.eclipse.jdt.core.compiler.debug.localVariable=generate
8 | org.eclipse.jdt.core.compiler.debug.sourceFile=generate
9 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
10 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
11 | org.eclipse.jdt.core.compiler.source=1.6
12 |
--------------------------------------------------------------------------------
/com.qcloud.weapp.sdk/.settings/org.eclipse.wst.common.component:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/com.qcloud.weapp.sdk/.settings/org.eclipse.wst.common.project.facet.core.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/com.qcloud.weapp.sdk/lib/org.json.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tencentyun/wafer-java-server-sdk/ccb281e985cc725c6f516f1ba47b46ad375e7b64/com.qcloud.weapp.sdk/lib/org.json.jar
--------------------------------------------------------------------------------
/com.qcloud.weapp.sdk/src/META-INF/MANIFEST.MF:
--------------------------------------------------------------------------------
1 | Manifest-Version: 1.0
2 | Class-Path:
3 |
4 |
--------------------------------------------------------------------------------
/com.qcloud.weapp.sdk/src/com/qcloud/weapp/Configuration.java:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | */
4 | package com.qcloud.weapp;
5 |
6 | /**
7 | * 表示 SDK 配置
8 | * @see com.qcloud.weapp.ConfigurationManager
9 | * @see 服务端 SDK 配置
10 | */
11 | public class Configuration {
12 |
13 | /** The server host. */
14 | private String serverHost;
15 |
16 | /** The auth server url. */
17 | private String authServerUrl;
18 |
19 | /** The tunnel server url. */
20 | private String tunnelServerUrl;
21 |
22 | /** The tunnel signature key. */
23 | private String tunnelSignatureKey;
24 |
25 | /** The network proxy. */
26 | private String networkProxy;
27 |
28 | /** The network timeout. */
29 | private int networkTimeout;
30 |
31 | /**
32 | * Gets the server host.
33 | *
34 | * @return the server host
35 | */
36 | public String getServerHost() {
37 | return serverHost;
38 | }
39 |
40 | /**
41 | * Sets the server host.
42 | *
43 | * @param serverHost the new server host
44 | */
45 | public void setServerHost(String serverHost) {
46 | this.serverHost = serverHost;
47 | }
48 |
49 | /**
50 | * Gets the auth server url.
51 | *
52 | * @return the auth server url
53 | */
54 | public String getAuthServerUrl() {
55 | return authServerUrl;
56 | }
57 |
58 | /**
59 | * Sets the auth server url.
60 | *
61 | * @param authServerUrl the new auth server url
62 | */
63 | public void setAuthServerUrl(String authServerUrl) {
64 | this.authServerUrl = authServerUrl;
65 | }
66 |
67 | /**
68 | * Gets the tunnel server url.
69 | *
70 | * @return the tunnel server url
71 | */
72 | public String getTunnelServerUrl() {
73 | return tunnelServerUrl;
74 | }
75 |
76 | /**
77 | * Sets the tunnel server url.
78 | *
79 | * @param tunnelServerUrl the new tunnel server url
80 | */
81 | public void setTunnelServerUrl(String tunnelServerUrl) {
82 | this.tunnelServerUrl = tunnelServerUrl;
83 | }
84 |
85 | /**
86 | * Gets the tunnel signature key.
87 | *
88 | * @return the tunnel signature key
89 | */
90 | public String getTunnelSignatureKey() {
91 | return tunnelSignatureKey;
92 | }
93 |
94 | /**
95 | * Sets the tunnel signature key.
96 | *
97 | * @param tunnelSignatureKey the new tunnel signature key
98 | */
99 | public void setTunnelSignatureKey(String tunnelSignatureKey) {
100 | this.tunnelSignatureKey = tunnelSignatureKey;
101 | }
102 |
103 | /**
104 | * Gets the network proxy.
105 | *
106 | * @return the network proxy
107 | */
108 | public String getNetworkProxy() {
109 | return networkProxy;
110 | }
111 |
112 | /**
113 | * Sets the network proxy.
114 | *
115 | * @param networkProxy the new network proxy
116 | */
117 | public void setNetworkProxy(String networkProxy) {
118 | this.networkProxy = networkProxy;
119 | }
120 |
121 | /**
122 | * Gets the network timeout.
123 | *
124 | * @return the network timeout
125 | */
126 | public int getNetworkTimeout() {
127 | return networkTimeout;
128 | }
129 |
130 | /**
131 | * Sets the network timeout.
132 | *
133 | * @param networkTimeout the new network timeout
134 | */
135 | public void setNetworkTimeout(Integer networkTimeout) {
136 | this.networkTimeout = networkTimeout;
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/com.qcloud.weapp.sdk/src/com/qcloud/weapp/ConfigurationException.java:
--------------------------------------------------------------------------------
1 | package com.qcloud.weapp;
2 |
3 | /**
4 | * 表示配置时产生的异常
5 | * */
6 | public class ConfigurationException extends Exception {
7 | private static final long serialVersionUID = 570042088042301018L;
8 |
9 | ConfigurationException(String message) {
10 | super(message);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/com.qcloud.weapp.sdk/src/com/qcloud/weapp/ConfigurationManager.java:
--------------------------------------------------------------------------------
1 | package com.qcloud.weapp;
2 |
3 | import java.io.BufferedReader;
4 | import java.io.FileReader;
5 | import java.io.IOException;
6 |
7 | import org.json.JSONException;
8 | import org.json.JSONObject;
9 |
10 | /**
11 | * 配置管理,使用该类进行 SDK 配置
12 | * */
13 | public class ConfigurationManager {
14 |
15 | private static Configuration currentConfiguration;
16 |
17 | /**
18 | * 获取当前的 SDK 配置
19 | * */
20 | public static Configuration getCurrentConfiguration() throws ConfigurationException {
21 | if (currentConfiguration == null) {
22 | throw new ConfigurationException("SDK 还没有进行配置,请调用 ConfigurationManager.setup() 方法配置 SDK");
23 | }
24 | return currentConfiguration;
25 | }
26 |
27 | /**
28 | * 使用指定的配置初始化 SDK
29 | *
30 | * @param configuration 配置
31 | * @see 服务端 SDK 配置
32 | * */
33 | public static void setup(Configuration configuration) throws ConfigurationException {
34 | if (configuration == null) {
35 | throw new ConfigurationException("配置不能为空");
36 | }
37 | if (configuration.getServerHost() == null) throw new ConfigurationException("服务器主机配置不能为空");
38 | if (configuration.getAuthServerUrl() == null) throw new ConfigurationException("鉴权服务器配置不能为空");
39 | if (configuration.getTunnelServerUrl() == null) throw new ConfigurationException("信道服务器配置不能为空");
40 | if (configuration.getTunnelSignatureKey() == null) throw new ConfigurationException("SDK 密钥配置不能为空");
41 | currentConfiguration = configuration;
42 | }
43 |
44 | /**
45 | * 从配置文件初始化 SDK
46 | *
47 | * @param configFilePath 配置文件的路径
48 | * @see 服务端 SDK 配置
49 | * */
50 | public static void setupFromFile(String configFilePath) throws JSONException, ConfigurationException {
51 | JSONObject configs = new JSONObject(getConfigJson(configFilePath));
52 | Configuration configuration = new Configuration();
53 | configuration.setServerHost(configs.getString("serverHost"));
54 | configuration.setAuthServerUrl(configs.getString("authServerUrl"));
55 | configuration.setTunnelServerUrl(configs.getString("tunnelServerUrl"));
56 | configuration.setTunnelSignatureKey(configs.getString("tunnelSignatureKey"));
57 | if (configs.has("networkProxy")) {
58 | configuration.setNetworkProxy(configs.getString("networkProxy"));
59 | }
60 | if (configs.has("networkTimeout")) {
61 | configuration.setNetworkTimeout(configs.getInt("networkTimeout"));
62 | }
63 | ConfigurationManager.setup(configuration);
64 | }
65 |
66 | private static String getConfigJson(String configFilePath) {
67 |
68 | String configJsonText = null;
69 |
70 | try {
71 | BufferedReader br = new BufferedReader(new FileReader(configFilePath));
72 | StringBuilder sb = new StringBuilder();
73 | String line;
74 | while((line = br.readLine()) != null) {
75 | sb.append(line);
76 | sb.append(System.lineSeparator());
77 | }
78 | configJsonText = sb.toString();
79 | br.close();
80 | } catch (IOException e) {
81 | e.printStackTrace();
82 | }
83 |
84 | return configJsonText;
85 | }
86 |
87 | }
88 |
89 |
--------------------------------------------------------------------------------
/com.qcloud.weapp.sdk/src/com/qcloud/weapp/Hash.java:
--------------------------------------------------------------------------------
1 | package com.qcloud.weapp;
2 |
3 | import java.security.MessageDigest;
4 |
5 | /**
6 | * 工具类,用于计算哈希值(SDK 内部使用)
7 | * */
8 | public class Hash {
9 |
10 | /**
11 | * 计算字符串的 sha1 哈希值
12 | * */
13 | public static String sha1(String str) {
14 | return compute(str, "SHA-1");
15 | }
16 |
17 | /**
18 | * 计算字符串的 md5 哈希值
19 | * */
20 | public static String md5(String str) {
21 | return compute(str, "MD5");
22 | }
23 |
24 | public static String compute(String str, String algorithm) {
25 | if (str == null) {
26 | return null;
27 | }
28 | try {
29 | MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
30 | messageDigest.update(str.getBytes("utf-8"));
31 | return byteArrayToHexString(messageDigest.digest());
32 | } catch (Exception e) {
33 | throw new RuntimeException(e);
34 | }
35 | }
36 |
37 | private static String byteArrayToHexString(byte[] b) {
38 | String result = "";
39 | for (int i = 0; i < b.length; i++) {
40 | result += Integer.toString((b[i] & 0xff) + 0x100, 16).substring(1);
41 | }
42 | return result;
43 | }
44 |
45 | }
--------------------------------------------------------------------------------
/com.qcloud.weapp.sdk/src/com/qcloud/weapp/HttpRequest.java:
--------------------------------------------------------------------------------
1 | package com.qcloud.weapp;
2 |
3 | import java.io.BufferedReader;
4 | import java.io.IOException;
5 | import java.io.InputStreamReader;
6 | import java.io.OutputStreamWriter;
7 | import java.net.HttpURLConnection;
8 | import java.net.InetSocketAddress;
9 | import java.net.Proxy;
10 | import java.net.URL;
11 | import java.util.regex.Matcher;
12 | import java.util.regex.Pattern;
13 |
14 | /**
15 | * 用于创建网络请求,SDK 内部使用
16 | * */
17 | public class HttpRequest {
18 |
19 | public interface ConnectionProvider {
20 | HttpURLConnection getConnection(String url, Proxy proxy) throws IOException;
21 | }
22 |
23 | private static ConnectionProvider connectionProvider = new ConnectionProvider() {
24 |
25 | @Override
26 | public HttpURLConnection getConnection(String url, Proxy proxy) throws IOException {
27 | if (proxy == null) {
28 | return (HttpURLConnection) new URL(url).openConnection();
29 | } else {
30 | return (HttpURLConnection) new URL(url).openConnection(proxy);
31 | }
32 | }
33 | };
34 |
35 | public static void setUrlProvider(ConnectionProvider provider) {
36 | connectionProvider = provider;
37 | }
38 |
39 | public static ConnectionProvider getUrlProvider() {
40 | return connectionProvider;
41 | }
42 |
43 | private String url;
44 |
45 | public HttpRequest(String url) {
46 | this.url = url;
47 | }
48 |
49 | public String post(String body) throws IOException {
50 | HttpURLConnection connection;
51 | Proxy proxy = null;
52 | try {
53 | String proxyString = ConfigurationManager.getCurrentConfiguration().getNetworkProxy();
54 | if (proxyString != null) {
55 | Pattern proxyPattern = Pattern.compile("^(.+)\\:(\\d+)$");
56 | Matcher proxyMatch = proxyPattern.matcher(proxyString);
57 | if (proxyMatch.find()) {
58 | String host = proxyMatch.group(1);
59 | String port = proxyMatch.group(2);
60 | proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(host, Integer.parseInt(port)));
61 | }
62 | }
63 | } catch (ConfigurationException e) {
64 | e.printStackTrace();
65 | }
66 |
67 | connection = connectionProvider.getConnection(url, proxy);
68 |
69 | int networkTimeout = 30000;
70 |
71 | try {
72 | networkTimeout = ConfigurationManager.getCurrentConfiguration().getNetworkTimeout();
73 | if (networkTimeout == 0) {
74 | networkTimeout = 30000;
75 | }
76 | } catch (ConfigurationException e) {
77 | e.printStackTrace();
78 | }
79 |
80 | connection.setConnectTimeout(networkTimeout);
81 | connection.setReadTimeout(networkTimeout);
82 | connection.setDoOutput(true);
83 | connection.setRequestMethod("POST");
84 | connection.setRequestProperty("Content-Type", "application/json;charset=utf-8");
85 |
86 | // send the request
87 | OutputStreamWriter requestWriter = new OutputStreamWriter(connection.getOutputStream(), "UTF-8");
88 | requestWriter.write(body);
89 | requestWriter.flush();
90 |
91 | // read the response
92 | BufferedReader responseReader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
93 | StringBuffer responseBuffer = new StringBuffer();
94 | for (String line; (line = responseReader.readLine()) != null;) {
95 | responseBuffer.append(line);
96 | }
97 | return responseBuffer.toString();
98 | }
99 |
100 | }
101 |
--------------------------------------------------------------------------------
/com.qcloud.weapp.sdk/src/com/qcloud/weapp/authorization/AuthorizationAPI.java:
--------------------------------------------------------------------------------
1 | package com.qcloud.weapp.authorization;
2 |
3 | import java.io.IOException;
4 | import java.util.HashMap;
5 | import java.util.Map;
6 |
7 | import org.json.JSONException;
8 | import org.json.JSONObject;
9 |
10 | import com.qcloud.weapp.ConfigurationException;
11 | import com.qcloud.weapp.ConfigurationManager;
12 | import com.qcloud.weapp.HttpRequest;
13 |
14 | class AuthorizationAPI {
15 |
16 | private String getAPIUrl() throws ConfigurationException {
17 | return ConfigurationManager.getCurrentConfiguration().getAuthServerUrl();
18 | }
19 |
20 | public JSONObject login(String code, String encryptedData, String iv) throws AuthorizationAPIException, ConfigurationException {
21 | Map params = new HashMap();
22 | params.put("code", code);
23 | params.put("encrypt_data", encryptedData);
24 | params.put("iv", iv);
25 | return request("qcloud.cam.id_skey", params);
26 | }
27 |
28 | public JSONObject checkLogin(String id, String skey) throws AuthorizationAPIException, ConfigurationException {
29 | Map params = new HashMap();
30 | params.put("id", id);
31 | params.put("skey", skey);
32 | return request("qcloud.cam.auth", params);
33 | }
34 |
35 | public JSONObject request(String apiName, Map apiParams) throws AuthorizationAPIException, ConfigurationException {
36 | String requestBody = null;
37 | String responseBody = null;
38 |
39 | try {
40 | HttpRequest request = new HttpRequest(getAPIUrl());
41 |
42 | requestBody = buildRequestBody(apiName, apiParams);
43 | System.out.println("==============Auth Request=============");
44 | System.out.println(requestBody);
45 |
46 | responseBody = request.post(requestBody);
47 | System.out.println("==============Auth Response=============");
48 | System.out.println(requestBody);
49 | } catch (IOException e) {
50 | throw new AuthorizationAPIException("连接鉴权服务错误,请检查网络状态" + getAPIUrl() + e.getMessage());
51 | }
52 |
53 | JSONObject body = null;
54 | int returnCode = 0;
55 | String returnMessage = null;
56 |
57 | try {
58 | body = new JSONObject(responseBody);
59 | returnCode = body.getInt("returnCode");
60 | returnMessage = body.getString("returnMessage");
61 | } catch (JSONException e) {
62 | throw new AuthorizationAPIException("调用鉴权服务失败:返回了非法的 JSON 字符串", e);
63 | }
64 |
65 | if (returnCode != 0) {
66 | AuthorizationAPIException error = new AuthorizationAPIException(String.format("调用鉴权服务失败:#%d - %s", returnCode, returnMessage));
67 | error.setCode(returnCode);
68 | throw error;
69 | }
70 | JSONObject returnData = null;
71 | try {
72 | returnData = body.getJSONObject("returnData");
73 | } catch (JSONException e) {}
74 |
75 | return returnData;
76 | }
77 |
78 | private String buildRequestBody(String apiName, Map apiParams) {
79 | JSONObject jsonObject = new JSONObject();
80 | try {
81 | JSONObject interfaceJson = new JSONObject();
82 | interfaceJson.put("interfaceName", apiName);
83 | interfaceJson.put("para", apiParams);
84 |
85 | jsonObject.put("version", 1);
86 | jsonObject.put("componentName", "MA");
87 | jsonObject.put("interface", interfaceJson);
88 | } catch (JSONException e) {
89 | e.printStackTrace();
90 | }
91 |
92 | return jsonObject.toString();
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/com.qcloud.weapp.sdk/src/com/qcloud/weapp/authorization/AuthorizationAPIException.java:
--------------------------------------------------------------------------------
1 | package com.qcloud.weapp.authorization;
2 |
3 | class AuthorizationAPIException extends Exception {
4 | private static final long serialVersionUID = -3088657611850871775L;
5 | private int code;
6 |
7 | public int getCode() {
8 | return code;
9 | }
10 |
11 | public void setCode(int code) {
12 | this.code = code;
13 | }
14 |
15 | public AuthorizationAPIException(String message, Exception inner) {
16 | super(message, inner);
17 | }
18 |
19 | public AuthorizationAPIException(String message) {
20 | this(message, null);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/com.qcloud.weapp.sdk/src/com/qcloud/weapp/authorization/Constants.java:
--------------------------------------------------------------------------------
1 | package com.qcloud.weapp.authorization;
2 |
3 | /**
4 | * 登录服务常量,包括登录错误类型
5 | * */
6 | public final class Constants {
7 | static final String WX_SESSION_MAGIC_ID = "F2C224D4-2BCE-4C64-AF9F-A6D872000D1A";
8 | static final String WX_HEADER_CODE = "X-WX-Code";
9 | static final String WX_HEADER_ID = "X-WX-Id";
10 | static final String WX_HEADER_SKEY = "X-WX-Skey";
11 | static final String WX_HEADER_ENCRYPTED_DATA = "X-WX-Encrypted-Data";
12 | static final String WX_HEADER_IV = "X-WX-IV";
13 |
14 | /**
15 | * 表示登录失败
16 | * */
17 | public static final String ERR_LOGIN_FAILED = "ERR_LOGIN_FAILED";
18 |
19 | /**
20 | * 表示会话过期的错误
21 | * */
22 | public static final String ERR_INVALID_SESSION = "ERR_INVALID_SESSION";
23 |
24 | /**
25 | * 表示检查登录态失败
26 | * */
27 | public static final String ERR_CHECK_LOGIN_FAILED = "ERR_CHECK_LOGIN_FAILED";
28 | }
29 |
--------------------------------------------------------------------------------
/com.qcloud.weapp.sdk/src/com/qcloud/weapp/authorization/LoginService.java:
--------------------------------------------------------------------------------
1 | package com.qcloud.weapp.authorization;
2 |
3 | import java.io.IOException;
4 |
5 | import javax.servlet.http.HttpServletRequest;
6 | import javax.servlet.http.HttpServletResponse;
7 |
8 | import org.json.JSONException;
9 | import org.json.JSONObject;
10 |
11 | import com.qcloud.weapp.ConfigurationException;
12 |
13 | /**
14 | * 提供登录服务
15 | * */
16 | public class LoginService {
17 | private HttpServletRequest request;
18 | private HttpServletResponse response;
19 |
20 | /**
21 | * 从 Servlet Request 和 Servlet Response 创建登录服务
22 | * @param request Servlet Request
23 | * @param response Servlet Response
24 | * */
25 | public LoginService(HttpServletRequest request, HttpServletResponse response) {
26 | this.request = request;
27 | this.response = response;
28 | }
29 |
30 | private void writeJson(JSONObject json) {
31 | try {
32 | this.response.setContentType("application/json");
33 | this.response.setCharacterEncoding("utf-8");
34 | this.response.getWriter().print(json.toString());
35 | } catch (IOException e) {
36 | e.printStackTrace();
37 | }
38 | }
39 |
40 | private JSONObject prepareResponseJson() {
41 | JSONObject json = new JSONObject();
42 | try {
43 | json.put(Constants.WX_SESSION_MAGIC_ID, 1);
44 | } catch (JSONException e) {
45 | e.printStackTrace();
46 | }
47 | return json;
48 | }
49 |
50 | private JSONObject getJsonForError(Exception error, int errorCode) {
51 | JSONObject json = prepareResponseJson();
52 | try {
53 | json.put("code", errorCode);
54 | if (error instanceof LoginServiceException) {
55 | json.put("error", ((LoginServiceException) error).getType());
56 | }
57 | json.put("message", error.getMessage());
58 | } catch (JSONException e) {
59 | e.printStackTrace();
60 | }
61 | return json;
62 | }
63 |
64 | private JSONObject getJsonForError(Exception error) {
65 | return getJsonForError(error, -1);
66 | }
67 |
68 | /**
69 | * 处理登录请求
70 | * @return 登录成功将返回用户信息
71 | * */
72 | public UserInfo login() throws IllegalArgumentException, LoginServiceException, ConfigurationException {
73 | String code = getHeader(Constants.WX_HEADER_CODE);
74 | String encryptedData = getHeader(Constants.WX_HEADER_ENCRYPTED_DATA);
75 | String iv = getHeader(Constants.WX_HEADER_IV);
76 |
77 | AuthorizationAPI api = new AuthorizationAPI();
78 | JSONObject loginResult;
79 |
80 | try {
81 | loginResult = api.login(code, encryptedData, iv);
82 | } catch (AuthorizationAPIException apiError) {
83 | LoginServiceException error = new LoginServiceException(Constants.ERR_LOGIN_FAILED, apiError.getMessage(), apiError);
84 | writeJson(getJsonForError(error));
85 | throw error;
86 | }
87 |
88 | JSONObject json = prepareResponseJson();
89 | JSONObject session = new JSONObject();
90 | JSONObject userInfo = null;
91 | try {
92 | session.put("id", loginResult.get("id"));
93 | session.put("skey", loginResult.get("skey"));
94 | json.put("session", session);
95 | writeJson(json);
96 | } catch (JSONException e) {
97 | e.printStackTrace();
98 | }
99 |
100 | try {
101 | userInfo = loginResult.getJSONObject("user_info");
102 | } catch (JSONException e) {
103 | e.printStackTrace();
104 | }
105 |
106 | return UserInfo.BuildFromJson(userInfo);
107 | }
108 |
109 | /**
110 | * 检查当前请求的会话状态
111 | * @return 如果包含可用会话,将会返回会话对应的用户信息
112 | * */
113 | public UserInfo check() throws LoginServiceException, ConfigurationException {
114 | String id = getHeader(Constants.WX_HEADER_ID);
115 | String skey = getHeader(Constants.WX_HEADER_SKEY);
116 |
117 | AuthorizationAPI api = new AuthorizationAPI();
118 | JSONObject checkLoginResult = null;
119 | try {
120 | checkLoginResult = api.checkLogin(id, skey);
121 | } catch (AuthorizationAPIException apiError) {
122 | String errorType = Constants.ERR_CHECK_LOGIN_FAILED;
123 | if (apiError.getCode() == 60011 || apiError.getCode() == 60012) {
124 | errorType = Constants.ERR_INVALID_SESSION;
125 | }
126 | LoginServiceException error = new LoginServiceException(errorType, apiError.getMessage(), apiError);
127 | writeJson(getJsonForError(error));
128 | throw error;
129 | }
130 | JSONObject userInfo = null;
131 | try {
132 | userInfo = checkLoginResult.getJSONObject("user_info");
133 | } catch (JSONException e) {
134 | e.printStackTrace();
135 | }
136 | return UserInfo.BuildFromJson(userInfo);
137 | }
138 |
139 | private String getHeader(String key) throws LoginServiceException {
140 | String value = request.getHeader(key);
141 | if (value == null || value.isEmpty()) {
142 | LoginServiceException error = new LoginServiceException("INVALID_REQUEST", String.format("请求头不包含 %s,请配合客户端 SDK 使用", key));
143 | writeJson(getJsonForError(error));
144 | throw error;
145 | }
146 | return value;
147 | }
148 |
149 | }
150 |
--------------------------------------------------------------------------------
/com.qcloud.weapp.sdk/src/com/qcloud/weapp/authorization/LoginServiceException.java:
--------------------------------------------------------------------------------
1 | package com.qcloud.weapp.authorization;
2 |
3 | /**
4 | * 表示登录异常
5 | * */
6 | public class LoginServiceException extends Exception {
7 |
8 | private static final long serialVersionUID = 7179434716738339025L;
9 | private String type;
10 |
11 | LoginServiceException(String type, String message, Exception innerException) {
12 | super(message, innerException);
13 | this.type = type;
14 | }
15 |
16 | LoginServiceException(String type, String message) {
17 | this(type, message, null);
18 | }
19 |
20 | /**
21 | * 获取登录异常的类型,具体的取值可参考 Constans 里面的常量
22 | * @see com.qcloud.weapp.authorization.Constants
23 | * */
24 | public String getType() {
25 | return this.type;
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/com.qcloud.weapp.sdk/src/com/qcloud/weapp/authorization/UserInfo.java:
--------------------------------------------------------------------------------
1 | package com.qcloud.weapp.authorization;
2 |
3 | import org.json.JSONException;
4 | import org.json.JSONObject;
5 |
6 | /**
7 | * 表示微信用户信息.
8 | */
9 | public class UserInfo {
10 |
11 | /** The open id. */
12 | private String openId;
13 |
14 | /** The nick name. */
15 | private String nickName;
16 |
17 | /** The avatar url. */
18 | private String avatarUrl;
19 |
20 | /** The gender. */
21 | private Integer gender;
22 |
23 | /** The language. */
24 | private String language;
25 |
26 | /** The city. */
27 | private String city;
28 |
29 | /** The province. */
30 | private String province;
31 |
32 | /** The country. */
33 | private String country;
34 |
35 | /**
36 | * Builds the from json.
37 | *
38 | * @param json the json
39 | * @return the user info
40 | */
41 | static UserInfo BuildFromJson(JSONObject json) {
42 | if (json == null) return null;
43 |
44 | UserInfo userInfo = new UserInfo();
45 | try {
46 | if (json.has("openId")) userInfo.openId = json.getString("openId");
47 | if (json.has("nickName")) userInfo.nickName = json.getString("nickName");
48 | if (json.has("avatarUrl")) userInfo.avatarUrl = json.getString("avatarUrl");
49 | if (json.has("gender")) userInfo.gender = json.getInt("gender");
50 | if (json.has("language")) userInfo.language = json.getString("language");
51 | if (json.has("city")) userInfo.city = json.getString("city");
52 | if (json.has("province")) userInfo.province = json.getString("province");
53 | if (json.has("country")) userInfo.country = json.getString("country");
54 | } catch (JSONException e) {
55 | e.printStackTrace();
56 | }
57 | return userInfo;
58 | }
59 |
60 | /**
61 | * Gets the open id.
62 | *
63 | * @return the open id
64 | */
65 | public String getOpenId() {
66 | return openId;
67 | }
68 |
69 | /**
70 | * Gets the nick name.
71 | *
72 | * @return the nick name
73 | */
74 | public String getNickName() {
75 | return nickName;
76 | }
77 |
78 | /**
79 | * Gets the avatar url.
80 | *
81 | * @return the avatar url
82 | */
83 | public String getAvatarUrl() {
84 | return avatarUrl;
85 | }
86 |
87 | /**
88 | * Gets the gender.
89 | *
90 | * @return the gender
91 | */
92 | public Integer getGender() {
93 | return gender;
94 | }
95 |
96 | /**
97 | * Gets the language.
98 | *
99 | * @return the language
100 | */
101 | public String getLanguage() {
102 | return language;
103 | }
104 |
105 | /**
106 | * Gets the city.
107 | *
108 | * @return the city
109 | */
110 | public String getCity() {
111 | return city;
112 | }
113 |
114 | /**
115 | * Gets the province.
116 | *
117 | * @return the province
118 | */
119 | public String getProvince() {
120 | return province;
121 | }
122 |
123 | /**
124 | * Gets the country.
125 | *
126 | * @return the country
127 | */
128 | public String getCountry() {
129 | return country;
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/com.qcloud.weapp.sdk/src/com/qcloud/weapp/tunnel/EmitError.java:
--------------------------------------------------------------------------------
1 | package com.qcloud.weapp.tunnel;
2 |
3 | /**
4 | * 表示信道消息发送时发生的异常
5 | * */
6 | public class EmitError extends Exception {
7 | private static final long serialVersionUID = 4722717669710824633L;
8 |
9 | EmitError(String message, Exception inner) {
10 | super(message, inner);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/com.qcloud.weapp.sdk/src/com/qcloud/weapp/tunnel/EmitResult.java:
--------------------------------------------------------------------------------
1 | package com.qcloud.weapp.tunnel;
2 |
3 | import java.util.ArrayList;
4 |
5 | /**
6 | * 表示向信道发送消息后反馈的结果,可能会包含无效信道的列表
7 | * @see com.qcloud.weapp.tunnel.Tunnel
8 | * @see com.qcloud.weapp.tunnel.TunnelRoom
9 | * */
10 | public class EmitResult {
11 | private ArrayList tunnelInvalidInfos;
12 |
13 | EmitResult(ArrayList tunnelInvalidInfos) {
14 | this.tunnelInvalidInfos = tunnelInvalidInfos;
15 | }
16 |
17 | /**
18 | * 获取无效信道列表
19 | * */
20 | public ArrayList getTunnelInvalidInfos() {
21 | return tunnelInvalidInfos;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/com.qcloud.weapp.sdk/src/com/qcloud/weapp/tunnel/Tunnel.java:
--------------------------------------------------------------------------------
1 | package com.qcloud.weapp.tunnel;
2 |
3 | /**
4 | * 表示一个信道,不可以被实例化。可以通过 Tunnel.getById() 获取。
5 | * */
6 | public class Tunnel {
7 |
8 | private String tunnelId;
9 | private String connectUrl;
10 |
11 | Tunnel(String tunnelId) {
12 | this.tunnelId = tunnelId;
13 | }
14 |
15 | String getConnectUrl() {
16 | return connectUrl;
17 | }
18 |
19 | void setConnectUrl(String connectUrl) {
20 | this.connectUrl = connectUrl;
21 | }
22 |
23 | public String getTunnelId() {
24 | return tunnelId;
25 | }
26 |
27 | void setTunnelId(String tunnelId) {
28 | this.tunnelId = tunnelId;
29 | }
30 |
31 | /**
32 | * 获取具有指定信道 ID 的信道
33 | * */
34 | public static Tunnel getById(String tunnelId) {
35 | return new Tunnel(tunnelId);
36 | }
37 |
38 | /**
39 | * 发送消息到信道中
40 | * @param messageType 消息类型
41 | * @param messageContent 消息内容,如果需要发送对象或者数组,需要使用 JSONObject 或 JSONArray 类型
42 | * */
43 | public EmitResult emit(String messageType, Object messageContent) throws EmitError {
44 | TunnelAPI api = new TunnelAPI();
45 | return api.emitMessage(new String[]{ tunnelId }, messageType, messageContent);
46 | }
47 |
48 | /**
49 | * 关闭当前信道
50 | * */
51 | public EmitResult close() throws EmitError {
52 | TunnelAPI api = new TunnelAPI();
53 | return api.emitPacket(new String[]{ tunnelId }, "close", null);
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/com.qcloud.weapp.sdk/src/com/qcloud/weapp/tunnel/TunnelAPI.java:
--------------------------------------------------------------------------------
1 | package com.qcloud.weapp.tunnel;
2 |
3 | import java.util.ArrayList;
4 |
5 | import org.json.JSONArray;
6 | import org.json.JSONException;
7 | import org.json.JSONObject;
8 |
9 | import com.qcloud.weapp.ConfigurationException;
10 | import com.qcloud.weapp.ConfigurationManager;
11 | import com.qcloud.weapp.Hash;
12 | import com.qcloud.weapp.HttpRequest;
13 |
14 | class TunnelAPI {
15 | private String getTunnelServerUrl() throws ConfigurationException {
16 | return ConfigurationManager.getCurrentConfiguration().getTunnelServerUrl();
17 | }
18 |
19 | public Tunnel requestConnect(String receiveUrl) throws Exception {
20 | JSONObject data = null;
21 |
22 | try {
23 | data = new JSONObject();
24 | data.put("receiveUrl", receiveUrl);
25 | data.put("protocolType", "wss");
26 | } catch (JSONException e) {
27 | // impossible
28 | }
29 |
30 | JSONObject result = request("/get/wsurl", data, true);
31 | Tunnel tunnel = new Tunnel(result.getString("tunnelId"));
32 | tunnel.setConnectUrl(result.getString("connectUrl"));
33 |
34 | return tunnel;
35 | }
36 |
37 | public EmitResult emitMessage(Tunnel[] tunnels, String messageType, Object messageContent) throws EmitError {
38 | String[] tunnelIds = new String[tunnels.length];
39 | Integer i = 0;
40 | for (Tunnel tunnel : tunnels) {
41 | tunnelIds[i++] = tunnel.getTunnelId();
42 | }
43 | return emitMessage(tunnelIds, messageType, messageContent);
44 | }
45 |
46 | public EmitResult emitMessage(String[] tunnelIds, String messageType, Object messageContent) throws EmitError {
47 | JSONObject packet = new JSONObject();
48 | try {
49 | packet.put("type", messageType);
50 | packet.put("content", messageContent);
51 | } catch (JSONException e) {
52 | e.printStackTrace();
53 | }
54 | return emitPacket(tunnelIds, "message", packet);
55 | }
56 |
57 | public EmitResult emitPacket(String[] tunnelIds, String packetType, JSONObject packetContent) throws EmitError {
58 | if (tunnelIds.length == 0) {
59 | return new EmitResult(new ArrayList());
60 | }
61 | JSONArray data = new JSONArray();
62 | JSONObject packet = new JSONObject();
63 | try {
64 | packet.put("type", packetType);
65 | packet.put("tunnelIds", tunnelIds);
66 | packet.put("content", packetContent == null ? null : packetContent.toString());
67 | data.put(packet);
68 | } catch (JSONException e) {
69 | e.printStackTrace();
70 | }
71 | try {
72 | JSONObject emitReturn = request("/ws/push", data, false);
73 | JSONArray invalidTunnelIds = emitReturn != null && emitReturn.has("invalidTunnelIds") ? emitReturn.getJSONArray("invalidTunnelIds") : new JSONArray();
74 | ArrayList infos = new ArrayList();
75 | for(int i = 0; i < invalidTunnelIds.length(); i++) {
76 | TunnelInvalidInfo info = new TunnelInvalidInfo();
77 | info.setTunnelId(invalidTunnelIds.getString(i));
78 | infos.add(info);
79 | }
80 | EmitResult emitResult = new EmitResult(infos);
81 | return emitResult;
82 | } catch (Exception e) {
83 | e.printStackTrace();
84 | throw new EmitError("网络不可用或者信道服务器不可用", e);
85 | }
86 | }
87 |
88 | public JSONObject request(String path, Object data, Boolean isSendTcKey) throws Exception {
89 | boolean isValidData = data instanceof JSONObject || data instanceof JSONArray;
90 | if (!isValidData) {
91 | throw new Exception("数据只能是 JSONObject 或者 JSONArray 类型");
92 | }
93 |
94 | String url = getTunnelServerUrl() + path;
95 | String responseContent;
96 |
97 | try {
98 | String requestContent = buildRequestContent(data, isSendTcKey);
99 | responseContent = new HttpRequest(url).post(requestContent);
100 | } catch (Exception e) {
101 | throw new Exception("请求信道 API 失败,网络异常或鉴权服务器错误", e);
102 | }
103 |
104 | try {
105 | JSONObject body = new JSONObject(responseContent);
106 | if (body.getInt("code") != 0) {
107 | throw new Exception(String.format("信道服务调用失败:#%d - %s", body.get("code"), body.get("message")));
108 | }
109 | return body.has("data") ? new JSONObject(body.getString("data")) : null;
110 | } catch (JSONException e) {
111 | throw new Exception("信道服务器响应格式错误,无法解析 JSON 字符串", e);
112 | }
113 |
114 | }
115 |
116 | private String buildRequestContent(Object data, boolean includeTckey) throws ConfigurationException {
117 | // data must be JsonObject or JsonArray
118 | String encodeData = data.toString();
119 | JSONObject requestPayload = new JSONObject();
120 | try {
121 | requestPayload.put("data", encodeData);
122 | requestPayload.put("dataEncode", "json");
123 | requestPayload.put("tcId", TunnelClient.getId());
124 | if (includeTckey) {
125 | requestPayload.put("tcKey", TunnelClient.getKey());
126 | }
127 | requestPayload.put("signature", signature(encodeData));
128 | } catch (JSONException e) {
129 | e.printStackTrace();
130 | }
131 | return requestPayload.toString();
132 | }
133 |
134 | private String signature(String data) throws ConfigurationException {
135 | return Hash.sha1(data + TunnelClient.getKey());
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/com.qcloud.weapp.sdk/src/com/qcloud/weapp/tunnel/TunnelClient.java:
--------------------------------------------------------------------------------
1 | package com.qcloud.weapp.tunnel;
2 |
3 | import com.qcloud.weapp.ConfigurationException;
4 | import com.qcloud.weapp.ConfigurationManager;
5 | import com.qcloud.weapp.Hash;
6 |
7 | class TunnelClient {
8 | private static String _id = null;
9 | public static String getId() throws ConfigurationException {
10 | if (_id == null) {
11 | _id = Hash.md5(ConfigurationManager.getCurrentConfiguration().getServerHost());
12 | }
13 | return _id;
14 | }
15 |
16 | public static String getKey() throws ConfigurationException {
17 | return ConfigurationManager.getCurrentConfiguration().getTunnelSignatureKey();
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/com.qcloud.weapp.sdk/src/com/qcloud/weapp/tunnel/TunnelHandleOptions.java:
--------------------------------------------------------------------------------
1 | package com.qcloud.weapp.tunnel;
2 |
3 | /**
4 | * 表示信道服务选项
5 | * */
6 | public class TunnelHandleOptions {
7 | private boolean checkLogin;
8 |
9 | /**
10 | * 是否配置为检查登录态
11 | * */
12 | public boolean isCheckLogin() {
13 | return checkLogin;
14 | }
15 |
16 | /**
17 | * 设置是否检查登录态,如果检查登录态,则在连接请求时可以获取到用户信息
18 | * */
19 | public void setCheckLogin(boolean checkLogin) {
20 | this.checkLogin = checkLogin;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/com.qcloud.weapp.sdk/src/com/qcloud/weapp/tunnel/TunnelHandler.java:
--------------------------------------------------------------------------------
1 | package com.qcloud.weapp.tunnel;
2 |
3 | import com.qcloud.weapp.authorization.UserInfo;
4 |
5 | /**
6 | * 信道事件处理接口,实现该接口处理信道事件。
7 | * 信道处理器需要处理信道的完整声明周期,包括:
8 | *
9 | * - onTunnelRequest() - 当用户发起信道请求的时候,会得到用户信息,此时可以关联信道 ID 和用户信息
10 | * - onTunnelConnect() - 当用户建立了信道连接之后,可以记录下已经连接的信道
11 | * - onTunnelMessage() - 当用户消息发送到信道上时,使用该函数处理信道的消息
12 | * - onTunnelClose() - 当信道关闭时,清理关于该信道的信息,以及回收相关资源
13 | *
14 | * */
15 | public interface TunnelHandler {
16 | /**
17 | * 当用户发起信道请求的时候调用,会得到用户信息,此时可以关联信道 ID 和用户信息
18 | * @param tunnel 发起连接请求的信道
19 | * @param userInfo 发起连接对应的用户(需要信道服务配置 checkLogin 为 true)
20 | * */
21 | void onTunnelRequest(Tunnel tunnel, UserInfo userInfo);
22 |
23 | /**
24 | * 当用户建立了信道连接之后调用,此时可以记录下已经连接的信道
25 | * @param tunnel 已经建立连接的信道,此时可以向信道发送消息
26 | * */
27 | void onTunnelConnect(Tunnel tunnel);
28 |
29 | /**
30 | * 当信道收到消息时调用,此时可以处理消息,也可以向信道发送消息
31 | * @param tunnel 收到消息的信道
32 | * @param message 收到的消息
33 | * */
34 | void onTunnelMessage(Tunnel tunnel, TunnelMessage message);
35 |
36 | /**
37 | * 当信道关闭的时候调用,此时可以清理信道使用的资源
38 | * @param tunnel 已经关闭的信道
39 | * */
40 | void onTunnelClose(Tunnel tunnel);
41 | }
42 |
--------------------------------------------------------------------------------
/com.qcloud.weapp.sdk/src/com/qcloud/weapp/tunnel/TunnelInvalidInfo.java:
--------------------------------------------------------------------------------
1 | package com.qcloud.weapp.tunnel;
2 |
3 | /**
4 | * 表示一个信道不可用的信息
5 | * */
6 | public class TunnelInvalidInfo {
7 | private String tunnelId;
8 | private TunnelInvalidType type;
9 |
10 | /**
11 | * 获取不可用信道的 ID
12 | * */
13 | public String getTunnelId() {
14 | return tunnelId;
15 | }
16 | void setTunnelId(String tunnelId) {
17 | this.tunnelId = tunnelId;
18 | }
19 | /**
20 | * 获取信道不可用的类型
21 | * */
22 | public TunnelInvalidType getType() {
23 | return type;
24 | }
25 | void setType(TunnelInvalidType type) {
26 | this.type = type;
27 | }
28 |
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/com.qcloud.weapp.sdk/src/com/qcloud/weapp/tunnel/TunnelInvalidType.java:
--------------------------------------------------------------------------------
1 | package com.qcloud.weapp.tunnel;
2 |
3 | /**
4 | * 表示信道不可用的类型
5 | * */
6 | public enum TunnelInvalidType {
7 | /**
8 | * 表示信道已经关闭,所以不可用
9 | * */
10 | TunnelHasClosed
11 | }
12 |
--------------------------------------------------------------------------------
/com.qcloud.weapp.sdk/src/com/qcloud/weapp/tunnel/TunnelMessage.java:
--------------------------------------------------------------------------------
1 | package com.qcloud.weapp.tunnel;
2 |
3 | import org.json.JSONException;
4 | import org.json.JSONObject;
5 |
6 | /**
7 | * 表示一个信道消息
8 | * */
9 | public class TunnelMessage {
10 | private String type;
11 | private Object content;
12 |
13 | TunnelMessage(String messageRaw) {
14 | try {
15 | JSONObject resolved = new JSONObject(messageRaw);
16 | this.type = resolved.getString("type");
17 | this.content = resolved.get("content");
18 | } catch (JSONException e) {
19 | this.type = "UnknownRaw";
20 | this.content = messageRaw;
21 | }
22 | }
23 | /**
24 | * 获取信道消息的类型
25 | * */
26 | public String getType() {
27 | return type;
28 | }
29 | /**
30 | * 获取信道消息的内容
31 | * */
32 | public Object getContent() {
33 | return content;
34 | }
35 | void setType(String type) {
36 | this.type = type;
37 | }
38 | void setContent(JSONObject content) {
39 | this.content = content;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/com.qcloud.weapp.sdk/src/com/qcloud/weapp/tunnel/TunnelRoom.java:
--------------------------------------------------------------------------------
1 | package com.qcloud.weapp.tunnel;
2 |
3 | import java.util.ArrayList;
4 | import java.util.function.Predicate;
5 |
6 | /**
7 | * 房间维护一批信道的集合,可以通过广播方法向房间里的所有信道推送消息
8 | * */
9 | public class TunnelRoom {
10 | private ArrayList tunnels;
11 |
12 | /**
13 | * 实例化一个信道房间,初始包含指定的信道集合
14 | * */
15 | public TunnelRoom(ArrayList tunnels) {
16 | if (tunnels == null) {
17 | tunnels = new ArrayList();
18 | }
19 | this.tunnels = tunnels;
20 | }
21 |
22 | /**
23 | * 实例化一个信道房间,初始不包含任何信道
24 | * */
25 | public TunnelRoom() {
26 | this(new ArrayList());
27 | }
28 |
29 | /**
30 | * 向房间里添加信道
31 | * @param tunnel 要添加的信道
32 | * */
33 | public void addTunnel(Tunnel tunnel) {
34 | tunnels.add(tunnel);
35 | }
36 |
37 | /**
38 | * 从房间移除指定的信道
39 | * @param tunnel 要移除的信道
40 | * */
41 | public void removeTunnel(Tunnel tunnel) {
42 | removeTunnelById(tunnel.getTunnelId());
43 | }
44 |
45 | /**
46 | * 从房间里移除具有指定 ID 的信道
47 | * @param tunnelId 要移除的信道的 ID
48 | * */
49 | public void removeTunnelById (final String tunnelId) {
50 | tunnels.removeIf(new Predicate() {
51 | @Override
52 | public boolean test(Tunnel t) {
53 | return t.getTunnelId().equals(tunnelId);
54 | }
55 | });
56 | }
57 |
58 | /**
59 | * 获取房间里信道的数量
60 | * */
61 | public int getTunnelCount() {
62 | return tunnels.size();
63 | }
64 |
65 | /**
66 | * 向房间里的每一个信道广播消息
67 | * @param messageType 要广播的消息的类型
68 | * @param messageContent 要广播的消息的内容
69 | * */
70 | public EmitResult broadcast(String messageType, Object messageContent) throws EmitError {
71 | TunnelAPI api = new TunnelAPI();
72 | return api.emitMessage(tunnels.toArray(new Tunnel[] {}), messageType, messageContent);
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/com.qcloud.weapp.sdk/src/com/qcloud/weapp/tunnel/TunnelService.java:
--------------------------------------------------------------------------------
1 | package com.qcloud.weapp.tunnel;
2 |
3 | import java.io.BufferedReader;
4 | import java.io.IOException;
5 | import java.io.InputStreamReader;
6 | import java.net.URI;
7 | import java.nio.charset.StandardCharsets;
8 |
9 | import javax.servlet.http.HttpServletRequest;
10 | import javax.servlet.http.HttpServletResponse;
11 |
12 | import org.json.JSONException;
13 | import org.json.JSONObject;
14 |
15 | import com.qcloud.weapp.ConfigurationException;
16 | import com.qcloud.weapp.ConfigurationManager;
17 | import com.qcloud.weapp.Hash;
18 | import com.qcloud.weapp.authorization.LoginService;
19 | import com.qcloud.weapp.authorization.LoginServiceException;
20 | import com.qcloud.weapp.authorization.UserInfo;
21 |
22 |
23 | /**
24 | * 提供信道服务
25 | * */
26 | public class TunnelService {
27 | private HttpServletRequest request;
28 | private HttpServletResponse response;
29 |
30 | /**
31 | * 从 Servlet Request 和 Servlet Response 实例化一个信道服务
32 | * */
33 | public TunnelService(HttpServletRequest request, HttpServletResponse response) {
34 | this.request = request;
35 | this.response = response;
36 | }
37 |
38 | private void writeJson(JSONObject json) {
39 | try {
40 | this.response.setContentType("application/json");
41 | this.response.setCharacterEncoding("utf-8");
42 | this.response.getWriter().print(json.toString());
43 | } catch (IOException e) {
44 | e.printStackTrace();
45 | }
46 | }
47 |
48 | private JSONObject getJsonForError(Exception error, int errorCode) {
49 | JSONObject json = new JSONObject();
50 | try {
51 | json.put("code", errorCode);
52 | if (error instanceof LoginServiceException) {
53 | json.put("error", ((LoginServiceException) error).getType());
54 | }
55 | json.put("message", error.getMessage());
56 | } catch (JSONException e) {
57 | e.printStackTrace();
58 | }
59 | return json;
60 | }
61 |
62 | private JSONObject getJsonForError(Exception error) {
63 | return getJsonForError(error, -1);
64 | }
65 |
66 |
67 | /**
68 | * 处理 WebSocket 信道请求
69 | * @param handler 指定信道处理器处理信道事件
70 | * @param options 指定信道服务的配置
71 | */
72 | public void handle(TunnelHandler handler, TunnelHandleOptions options) throws ConfigurationException {
73 | if (request.getMethod().toUpperCase() == "GET") {
74 | handleGet(handler, options);
75 | }
76 | if (request.getMethod().toUpperCase() == "POST") {
77 | handlePost(handler, options);
78 | }
79 | }
80 |
81 | /**
82 | * 处理 GET 请求
83 | *
84 | * GET 请求表示客户端请求进行信道连接,此时会向 SDK 申请信道连接地址,并且返回给客户端
85 | * 如果配置指定了要求登陆,还会调用登陆服务来校验登陆态并获得用户信息
86 | */
87 | private void handleGet(TunnelHandler handler, TunnelHandleOptions options) throws ConfigurationException {
88 | Tunnel tunnel = null;
89 | UserInfo user = null;
90 |
91 | if (options != null && options.isCheckLogin()) {
92 | try {
93 | LoginService loginService = new LoginService(request, response);
94 | user = loginService.check();
95 | } catch (Exception e) {
96 | return;
97 | }
98 | }
99 |
100 | TunnelAPI api = new TunnelAPI();
101 | try {
102 | String receiveUrl = buildReceiveUrl();
103 | tunnel = api.requestConnect(receiveUrl);
104 | } catch (Exception e) {
105 | writeJson(getJsonForError(e));
106 | return;
107 | }
108 |
109 | JSONObject result = new JSONObject();
110 | try {
111 | result.put("url", tunnel.getConnectUrl());
112 | } catch (JSONException e) {
113 | e.printStackTrace();
114 | }
115 | writeJson(result);
116 |
117 | handler.onTunnelRequest(tunnel, user);
118 | }
119 |
120 | private String buildReceiveUrl() throws ConfigurationException {
121 | URI tunnelServerUri = URI.create(ConfigurationManager.getCurrentConfiguration().getTunnelServerUrl());
122 | String schema = tunnelServerUri.getScheme();
123 | String host = ConfigurationManager.getCurrentConfiguration().getServerHost();
124 | String path = request.getRequestURI();
125 | return schema + "://" + host + path;
126 | }
127 |
128 | private void handlePost(TunnelHandler handler, TunnelHandleOptions options) throws ConfigurationException {
129 | String requestContent = null;
130 |
131 | // 1. 读取报文内容
132 | try {
133 | BufferedReader requestReader = new BufferedReader(new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8));
134 | requestContent = "";
135 | for (String line; (line = requestReader.readLine()) != null;) {
136 | requestContent += line;
137 | }
138 | } catch (IOException e) {
139 | e.printStackTrace();
140 | writeJson(getJsonForError(e));
141 | return;
142 | }
143 |
144 | // 2. 读取报文内容成 JSON 并保存在 body 变量中
145 | JSONObject body = null;
146 | String data = null, signature = null;
147 | try {
148 | body = new JSONObject(requestContent);
149 | data = body.getString("data");
150 | signature = body.getString("signature");
151 | // String signature = body.getString("signature");
152 | } catch (JSONException e) {
153 | JSONObject errJson = new JSONObject();
154 | try {
155 | errJson.put("code", 9001);
156 | errJson.put("message", "Cant not parse the request body: invalid json");
157 | } catch (JSONException e1) {
158 | e1.printStackTrace();
159 | }
160 | writeJson(errJson);
161 | }
162 |
163 | // 3. 检查报文签名
164 | String computedSignature = Hash.sha1(data + TunnelClient.getKey());
165 | if (!computedSignature.equals(signature)) {
166 | JSONObject json = new JSONObject();
167 | try {
168 | json.put("code", 9003);
169 | json.put("message", "Bad Request - 签名错误");
170 | } catch (JSONException e) {
171 | e.printStackTrace();
172 | }
173 | writeJson(json);
174 | return;
175 | }
176 |
177 | // 4. 解析报文中携带的数据
178 | JSONObject packet;
179 | String tunnelId = null;
180 | String packetType = null;
181 | String packetContent = null;
182 | try {
183 | packet = new JSONObject(data);
184 | tunnelId = packet.getString("tunnelId");
185 | packetType = packet.getString("type");
186 | if (packet.has("content")) {
187 | packetContent = packet.getString("content");
188 | }
189 |
190 | JSONObject response = new JSONObject();
191 | response.put("code", 0);
192 | response.put("message", "OK");
193 | writeJson(response);
194 | } catch (JSONException e) {
195 | JSONObject response = new JSONObject();
196 | try {
197 | response.put("code", 9004);
198 | response.put("message", "Bad Request - 无法解析的数据包");
199 | } catch (JSONException e1) {
200 | e1.printStackTrace();
201 | }
202 | writeJson(response);
203 | e.printStackTrace();
204 | }
205 |
206 | // 5. 交给客户处理实例处理报文
207 | Tunnel tunnel = Tunnel.getById(tunnelId);
208 | if (packetType.equals("connect")) {
209 | handler.onTunnelConnect(tunnel);
210 | }
211 | else if (packetType.equals("message")) {
212 | handler.onTunnelMessage(tunnel, new TunnelMessage(packetContent));
213 | } else if (packetType.equals("close")) {
214 | handler.onTunnelClose(tunnel);
215 | }
216 | }
217 |
218 | }
219 |
--------------------------------------------------------------------------------
/com.qcloud.weapp.test/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/com.qcloud.weapp.test/.gitignore:
--------------------------------------------------------------------------------
1 | /bin/
2 |
--------------------------------------------------------------------------------
/com.qcloud.weapp.test/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | com.qcloud.weapp.test
4 |
5 |
6 |
7 |
8 |
9 | org.eclipse.jdt.core.javabuilder
10 |
11 |
12 |
13 |
14 |
15 | org.eclipse.jdt.core.javanature
16 |
17 |
18 |
--------------------------------------------------------------------------------
/com.qcloud.weapp.test/.settings/org.eclipse.jdt.core.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
3 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
4 | org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
5 | org.eclipse.jdt.core.compiler.compliance=1.8
6 | org.eclipse.jdt.core.compiler.debug.lineNumber=generate
7 | org.eclipse.jdt.core.compiler.debug.localVariable=generate
8 | org.eclipse.jdt.core.compiler.debug.sourceFile=generate
9 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
10 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
11 | org.eclipse.jdt.core.compiler.source=1.8
12 |
--------------------------------------------------------------------------------
/com.qcloud.weapp.test/lib/mockito-all-1.10.19.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tencentyun/wafer-java-server-sdk/ccb281e985cc725c6f516f1ba47b46ad375e7b64/com.qcloud.weapp.test/lib/mockito-all-1.10.19.jar
--------------------------------------------------------------------------------
/com.qcloud.weapp.test/lib/org.json.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tencentyun/wafer-java-server-sdk/ccb281e985cc725c6f516f1ba47b46ad375e7b64/com.qcloud.weapp.test/lib/org.json.jar
--------------------------------------------------------------------------------
/com.qcloud.weapp.test/src/com/qcloud/weapp/test/HttpMock.java:
--------------------------------------------------------------------------------
1 | package com.qcloud.weapp.test;
2 |
3 | import static org.mockito.Mockito.*;
4 |
5 | import java.io.ByteArrayInputStream;
6 | import java.io.IOException;
7 | import java.io.PrintWriter;
8 | import java.io.StringWriter;
9 |
10 | import javax.servlet.ServletInputStream;
11 | import javax.servlet.http.HttpServletRequest;
12 | import javax.servlet.http.HttpServletResponse;
13 |
14 | import org.mockito.Matchers;
15 | import org.mockito.invocation.InvocationOnMock;
16 | import org.mockito.stubbing.Answer;
17 |
18 | public class HttpMock {
19 | public HttpServletRequest request;
20 | public HttpServletResponse response;
21 |
22 | private StringWriter sw;
23 | private PrintWriter pw;
24 | public void setupResponseWriter() {
25 | try {
26 | sw = new StringWriter();
27 | pw = new PrintWriter(sw);
28 | when(response.getWriter()).thenReturn(pw);
29 | } catch (IOException e) {
30 | e.printStackTrace();
31 | }
32 | }
33 |
34 | private String responseText = null;
35 | public String getResponseText() {
36 | if (responseText == null) {
37 | pw.flush();
38 | responseText = sw.toString();
39 | }
40 | return responseText;
41 | }
42 |
43 | public void setRequestBody(String requestBody) {
44 | try {
45 | // @see http://blog.timmattison.com/archives/2014/12/16/mockito-and-servletinputstreams/
46 | ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(requestBody.getBytes("utf-8"));
47 | ServletInputStream mockServletInputStream = mock(ServletInputStream.class);
48 | when(mockServletInputStream.read(Matchers.any(), anyInt(), anyInt())).thenAnswer(new Answer() {
49 | @Override
50 | public Integer answer(InvocationOnMock invocationOnMock) throws Throwable {
51 | Object[] args = invocationOnMock.getArguments();
52 | byte[] output = (byte[]) args[0];
53 | int offset = (int) args[1];
54 | int length = (int) args[2];
55 | return byteArrayInputStream.read(output, offset, length);
56 | }
57 | });
58 | when(request.getInputStream()).thenReturn(mockServletInputStream);
59 | } catch (IOException e) {
60 | e.printStackTrace();
61 | }
62 | }
63 | }
--------------------------------------------------------------------------------
/com.qcloud.weapp.test/src/com/qcloud/weapp/test/URLConnectionMock.java:
--------------------------------------------------------------------------------
1 | package com.qcloud.weapp.test;
2 |
3 | import java.io.ByteArrayInputStream;
4 | import java.io.ByteArrayOutputStream;
5 | import java.io.IOException;
6 | import java.io.UnsupportedEncodingException;
7 | import java.net.HttpURLConnection;
8 | import java.net.Proxy;
9 |
10 | import static org.mockito.Mockito.*;
11 | import com.qcloud.weapp.*;
12 |
13 | public class URLConnectionMock implements HttpRequest.ConnectionProvider {
14 |
15 | private ByteArrayInputStream responseStream;
16 | private ByteArrayOutputStream requestStream;
17 |
18 | public URLConnectionMock() {
19 | requestStream = new ByteArrayOutputStream();
20 | }
21 |
22 | public void setResponseBody(String body) {
23 | try {
24 | responseStream = new ByteArrayInputStream(body.getBytes("utf-8"));
25 | } catch (UnsupportedEncodingException e) {
26 | e.printStackTrace();
27 | }
28 | }
29 |
30 | public String getRequestBody() {
31 | try {
32 | return new String(requestStream.toByteArray(), "utf-8");
33 | } catch (UnsupportedEncodingException e) {
34 | e.printStackTrace();
35 | return null;
36 | }
37 | }
38 |
39 | @Override
40 | public HttpURLConnection getConnection(String url, Proxy proxy) throws IOException {
41 | HttpURLConnection connMock = mock(HttpURLConnection.class);
42 | when(connMock.getInputStream()).thenReturn(responseStream);
43 | when(connMock.getOutputStream()).thenReturn(requestStream);
44 | return connMock;
45 | }
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/com.qcloud.weapp.test/src/com/qcloud/weapp/test/authorization/LoginServiceTest.java:
--------------------------------------------------------------------------------
1 | package com.qcloud.weapp.test.authorization;
2 |
3 | import static org.junit.Assert.*;
4 | import static org.mockito.Mockito.*;
5 |
6 | import java.io.IOException;
7 |
8 | import org.json.JSONException;
9 | import org.json.JSONObject;
10 | import org.junit.*;
11 |
12 | import com.qcloud.weapp.Configuration;
13 | import com.qcloud.weapp.ConfigurationException;
14 | import com.qcloud.weapp.ConfigurationManager;
15 | import com.qcloud.weapp.authorization.*;
16 | import com.qcloud.weapp.test.HttpMock;
17 |
18 | @SuppressWarnings("unused")
19 | public class LoginServiceTest {
20 |
21 | private LoginServiceTestHelper helper = new LoginServiceTestHelper();
22 |
23 | @Before
24 | public void setup() {
25 | Configuration config = new Configuration();
26 | config.setServerHost("test.qcloud.la");
27 | config.setAuthServerUrl("http://127.0.0.1:10086/auth");
28 | config.setTunnelServerUrl("http://127.0.0.1:10086/tunnel");
29 | config.setTunnelSignatureKey("test key");
30 | config.setNetworkTimeout(1000);
31 | try {
32 | ConfigurationManager.setup(config);
33 | } catch (ConfigurationException e) {
34 | e.printStackTrace();
35 | }
36 | }
37 |
38 | @Test
39 | public void testLoginProcess() {
40 | HttpMock mock = helper.createLoginHttpMock("valid-code", "valid-data", "valid-iv");
41 | LoginService service = new LoginService(mock.request, mock.response);
42 |
43 | try {
44 | UserInfo userInfo = service.login();
45 | assertNotNull(userInfo);
46 | } catch (IllegalArgumentException | LoginServiceException | ConfigurationException e) {
47 | e.printStackTrace();
48 | fail(e.getMessage());
49 | }
50 |
51 | try {
52 | JSONObject body = new JSONObject(mock.getResponseText());
53 | assertTrue(helper.checkBodyHasMagicId(body));
54 | assertTrue(helper.checkBodyHasSession(body));
55 | } catch (JSONException e) {
56 | fail("invalid response body");
57 | }
58 | }
59 |
60 | @Test
61 | public void testLoginProcessWithoutCodeOrData() {
62 | testLoginProcessExpectError(null, "valid-data", "valid-iv");
63 | testLoginProcessExpectError("valid-code", null, "valid-iv");
64 | testLoginProcessExpectError("valid-code", "valid-data", null);
65 | }
66 |
67 | @Test
68 | public void testLoginProcessWithInvalidCodeOrData() {
69 | testLoginProcessExpectError("invalid-code", "valid-data", "valid-iv");
70 | testLoginProcessExpectError("valid-code", "invalid-data", "valid-iv");
71 | testLoginProcessExpectError("valid-code", "valid-data", "invalid-iv");
72 | }
73 |
74 | @Test
75 | public void testLoginProcessWithServerResponseError() {
76 | testLoginProcessExpectError("expect-valid-json", "valid-data", "valid-iv");
77 | }
78 |
79 | @Test
80 | public void testLoginProcessWithServer500() {
81 | testLoginProcessExpectError("expect-500", "valid-data", "valid-iv");
82 | }
83 |
84 | @Test
85 | public void testLoginProcessWithServerTimeout() {
86 | testLoginProcessExpectError("expect-timeout", "valid-data", "valid-iv");
87 | }
88 |
89 | private LoginServiceException testLoginProcessExpectError(String code, String encrypteData, String iv) {
90 | HttpMock mock = helper.createLoginHttpMock(code, encrypteData, iv);
91 | LoginService service = new LoginService(mock.request, mock.response);
92 |
93 | LoginServiceException errorShouldThrow = null;
94 | try {
95 | UserInfo userInfo = service.login();
96 | } catch (LoginServiceException e) {
97 | errorShouldThrow = e;
98 | } catch (ConfigurationException e) {
99 | e.printStackTrace();
100 | }
101 |
102 | assertNotNull(errorShouldThrow);
103 | try {
104 | JSONObject body = new JSONObject(mock.getResponseText());
105 | assertTrue(helper.checkBodyHasMagicId(body));
106 | assertNotNull(body.get("error"));
107 | } catch (JSONException e) {
108 | fail("invalid response body");
109 | }
110 | return errorShouldThrow;
111 | }
112 |
113 | @Test
114 | public void testCheck() {
115 | HttpMock mock = helper.createCheckHttpMock("valid-id", "valid-key");
116 | LoginService service = new LoginService(mock.request, mock.response);
117 | try {
118 | UserInfo userInfo = service.check();
119 | assertNotNull(userInfo);
120 | } catch (IllegalArgumentException | LoginServiceException | ConfigurationException e) {
121 | fail(e.getMessage());
122 | e.printStackTrace();
123 | }
124 | try {
125 | verify(mock.response, never()).getWriter();
126 | } catch (IOException e) {
127 | e.printStackTrace();
128 | }
129 | }
130 |
131 | @Test
132 | public void testCheckWithoutIdOrSkey() {
133 | testCheckExpectError(null, "valid-key", null);
134 | testCheckExpectError("valid-id", null, null);
135 | }
136 |
137 | @Test
138 | public void testCheckWithInvalidIdOrSkey() {
139 | testCheckExpectError("invalid-id", "valid-key", false);
140 | testCheckExpectError("valid-id", "invalid-key", false);
141 | }
142 |
143 | @Test
144 | public void testCheckWithInvalidSession() {
145 | testCheckExpectError("expect-60011", "valid-key", true);
146 | testCheckExpectError("expect-60012", "valid-key", true);
147 | }
148 |
149 | @Test
150 | public void testCheckWithServerInvalidResponse() {
151 | testCheckExpectError("expect-invalid-json", "valid-key", false);
152 | }
153 |
154 | @Test
155 | public void testCheckExpectError() {
156 | testCheckExpectError("expect-500", "valid-key", false);
157 | }
158 |
159 | @Test
160 | public void testCheckWithServerTimeout() {
161 | testCheckExpectError("expect-timeout", "valid-key", false);
162 | }
163 |
164 | private void testCheckExpectError(String id, String skey, Boolean expectInvalidSession) {
165 | HttpMock mock = helper.createCheckHttpMock(id, skey);
166 | LoginService service = new LoginService(mock.request, mock.response);
167 | LoginServiceException errorShouldThrow = null;
168 |
169 | try {
170 | UserInfo userInfo = service.check();
171 | } catch (LoginServiceException e) {
172 | errorShouldThrow = e;
173 | } catch (ConfigurationException e) {
174 | e.printStackTrace();
175 | }
176 |
177 | JSONObject body = null;
178 | try {
179 | body = new JSONObject(mock.getResponseText());
180 | assertTrue(helper.checkBodyHasMagicId(body));
181 | String error = body.getString("error");
182 | if (expectInvalidSession != null) {
183 | assertEquals(error.equals("ERR_INVALID_SESSION"), expectInvalidSession);
184 | }
185 | } catch (JSONException e) {
186 | fail("响应格式错误::" + e.getMessage());
187 | }
188 | try {
189 | verify(mock.response, times(1)).getWriter();
190 | } catch (IOException e) {
191 | e.printStackTrace();
192 | }
193 | }
194 | }
195 |
--------------------------------------------------------------------------------
/com.qcloud.weapp.test/src/com/qcloud/weapp/test/authorization/LoginServiceTestHelper.java:
--------------------------------------------------------------------------------
1 | package com.qcloud.weapp.test.authorization;
2 |
3 | import javax.servlet.http.HttpServletRequest;
4 | import javax.servlet.http.HttpServletResponse;
5 |
6 | import org.json.JSONException;
7 | import org.json.JSONObject;
8 |
9 | import com.qcloud.weapp.test.HttpMock;
10 |
11 | import static org.mockito.Mockito.*;
12 |
13 | public class LoginServiceTestHelper {
14 |
15 | public HttpMock createLoginHttpMock(String code, String encryptedData, String iv) {
16 | HttpServletRequest request = mock(HttpServletRequest.class);
17 | HttpServletResponse response = mock(HttpServletResponse.class);
18 |
19 | when(request.getHeader("X-WX-Code")).thenReturn(code);
20 | when(request.getHeader("X-WX-Encrypted-Data")).thenReturn(encryptedData);
21 | when(request.getHeader("X-WX-IV")).thenReturn(iv);
22 |
23 | HttpMock mock = new HttpMock();
24 | mock.request = request;
25 | mock.response = response;
26 | mock.setupResponseWriter();
27 |
28 | return mock;
29 | }
30 |
31 | public HttpMock createCheckHttpMock(String id, String skey) {
32 | HttpServletRequest request = mock(HttpServletRequest.class);
33 | HttpServletResponse response = mock(HttpServletResponse.class);
34 |
35 | when(request.getHeader("X-WX-Id")).thenReturn(id);
36 | when(request.getHeader("X-WX-Skey")).thenReturn(skey);
37 |
38 | HttpMock mock = new HttpMock();
39 | mock.request = request;
40 | mock.response = response;
41 | mock.setupResponseWriter();
42 |
43 | return mock;
44 | }
45 |
46 | public boolean checkBodyHasMagicId(JSONObject body) {
47 | return body.has("F2C224D4-2BCE-4C64-AF9F-A6D872000D1A");
48 | }
49 |
50 | public boolean checkBodyHasSession(JSONObject body) {
51 | if (!body.has("session")) return false;
52 | try {
53 | JSONObject session = body.getJSONObject("session");
54 | return session.has("id") && session.has("skey");
55 | } catch (JSONException e) {
56 | return false;
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/com.qcloud.weapp.test/src/com/qcloud/weapp/test/tunnel/TunnelServiceTest.java:
--------------------------------------------------------------------------------
1 | package com.qcloud.weapp.test.tunnel;
2 |
3 | import org.junit.*;
4 | import static org.junit.Assert.*;
5 | import org.mockito.ArgumentCaptor;
6 | import org.mockito.Mockito;
7 |
8 | import static org.mockito.Mockito.*;
9 |
10 | import org.json.JSONArray;
11 | import org.json.JSONException;
12 | import org.json.JSONObject;
13 |
14 | import com.qcloud.weapp.Configuration;
15 | import com.qcloud.weapp.ConfigurationException;
16 | import com.qcloud.weapp.ConfigurationManager;
17 | import com.qcloud.weapp.authorization.UserInfo;
18 | import com.qcloud.weapp.test.HttpMock;
19 | import com.qcloud.weapp.test.URLConnectionMock;
20 | import com.qcloud.weapp.tunnel.EmitError;
21 | import com.qcloud.weapp.tunnel.EmitResult;
22 | import com.qcloud.weapp.tunnel.Tunnel;
23 | import com.qcloud.weapp.tunnel.TunnelHandleOptions;
24 | import com.qcloud.weapp.tunnel.TunnelHandler;
25 | import com.qcloud.weapp.tunnel.TunnelMessage;
26 | import com.qcloud.weapp.tunnel.TunnelRoom;
27 | import com.qcloud.weapp.tunnel.TunnelService;
28 |
29 | public class TunnelServiceTest {
30 | private TunnelServiceTestHelper helper = new TunnelServiceTestHelper();
31 |
32 | @Before
33 | public void setup() {
34 | Configuration config = new Configuration();
35 | config.setServerHost("test.qcloud.la");
36 | config.setAuthServerUrl("http://127.0.0.1:10086/auth");
37 | config.setTunnelServerUrl("http://127.0.0.1:10086/tunnel");
38 | config.setTunnelSignatureKey("test key");
39 | config.setNetworkTimeout(1000);
40 | try {
41 | ConfigurationManager.setup(config);
42 | } catch (ConfigurationException e) {
43 | e.printStackTrace();
44 | }
45 | }
46 |
47 | @Test
48 | public void testGetConnectionWithSession() {
49 | HttpMock httpMock = helper.createTunnelHttpMock("GET", "valid");
50 | TunnelHandler handlerMock = mock(TunnelHandler.class);
51 |
52 | TunnelService service = new TunnelService(httpMock.request, httpMock.response);
53 | TunnelHandleOptions options = new TunnelHandleOptions();
54 | options.setCheckLogin(true);
55 | try {
56 | service.handle(handlerMock, options);
57 | } catch (ConfigurationException e) {
58 | e.printStackTrace();
59 | }
60 |
61 | try {
62 | JSONObject body = new JSONObject(httpMock.getResponseText());
63 | assertNotNull(body.getString("url"));
64 | } catch (JSONException e) {
65 | fail();
66 | }
67 |
68 | ArgumentCaptor tunnel = ArgumentCaptor.forClass(Tunnel.class);
69 | ArgumentCaptor userInfo = ArgumentCaptor.forClass(UserInfo.class);
70 | verify(handlerMock, only()).onTunnelRequest(tunnel.capture(), userInfo.capture());
71 | assertNotNull(tunnel.getValue());
72 | assertNotNull(userInfo.getValue());
73 | }
74 |
75 |
76 | @Test
77 | public void testGetConnectionWithoutSession() {
78 | HttpMock httpMock = helper.createTunnelHttpMock("GET");
79 | TunnelHandler handlerMock = mock(TunnelHandler.class);
80 |
81 | TunnelService service = new TunnelService(httpMock.request, httpMock.response);
82 | TunnelHandleOptions options = new TunnelHandleOptions();
83 | options.setCheckLogin(false);
84 | try {
85 | service.handle(handlerMock, options);
86 | } catch (ConfigurationException e) {
87 | e.printStackTrace();
88 | }
89 |
90 | try {
91 | JSONObject body = new JSONObject(httpMock.getResponseText());
92 | assertNotNull(body.getString("url"));
93 | } catch (JSONException e) {
94 | fail(e.getMessage());
95 | }
96 |
97 | ArgumentCaptor tunnel = ArgumentCaptor.forClass(Tunnel.class);
98 | ArgumentCaptor userInfo = ArgumentCaptor.forClass(UserInfo.class);
99 | verify(handlerMock, only()).onTunnelRequest(tunnel.capture(), userInfo.capture());
100 | assertNotNull(tunnel.getValue());
101 | assertNull(userInfo.getValue());
102 | }
103 |
104 |
105 | @Test
106 | public void testGetConnectionWithInvalidSession() {
107 | HttpMock httpMock = helper.createTunnelHttpMock("GET", "invalid");
108 | TunnelHandler handlerMock = mock(TunnelHandler.class);
109 |
110 | TunnelService service = new TunnelService(httpMock.request, httpMock.response);
111 | TunnelHandleOptions options = new TunnelHandleOptions();
112 | options.setCheckLogin(true);
113 | try {
114 | service.handle(handlerMock, options);
115 | } catch (ConfigurationException e) {
116 | e.printStackTrace();
117 | }
118 |
119 | try {
120 | JSONObject body = new JSONObject(httpMock.getResponseText());
121 | assertTrue(helper.checkBodyHasMagicId(body));
122 | assertNotNull(body.get("error"));
123 | } catch (JSONException e) {
124 | fail();
125 | }
126 | verify(handlerMock, never()).onTunnelRequest(any(Tunnel.class), any(UserInfo.class));
127 | }
128 |
129 | @Test
130 | public void testPostConnectPacket() throws JSONException, ConfigurationException {
131 | HttpMock httpMock = helper.createTunnelHttpMock("POST");
132 | httpMock.setRequestBody(helper.buildPacket(new JSONObject()
133 | .put("type", "connect")
134 | .put("tunnelId", "tunnel1")
135 | .toString()
136 | ));
137 |
138 | TunnelService service = new TunnelService(httpMock.request, httpMock.response);
139 | TunnelHandler handlerMock = mock(TunnelHandler.class);
140 |
141 | service.handle(handlerMock, null);
142 |
143 | ArgumentCaptor tunnel = ArgumentCaptor.forClass(Tunnel.class);
144 | verify(handlerMock, times(1)).onTunnelConnect(tunnel.capture());
145 | verify(handlerMock, only()).onTunnelConnect(tunnel.capture());
146 | assertEquals(tunnel.getValue().getTunnelId(), "tunnel1");
147 | assertTrue(helper.checkPostResponseSuccess(httpMock.getResponseText()));
148 | }
149 |
150 |
151 | @Test
152 | public void testPostClosePacket() throws JSONException, ConfigurationException {
153 | HttpMock httpMock = helper.createTunnelHttpMock("POST");
154 | httpMock.setRequestBody(helper.buildPacket(new JSONObject()
155 | .put("type", "close")
156 | .put("tunnelId", "tunnel1")
157 | .toString()
158 | ));
159 |
160 | TunnelService service = new TunnelService(httpMock.request, httpMock.response);
161 | TunnelHandler handlerMock = mock(TunnelHandler.class);
162 |
163 | service.handle(handlerMock, null);
164 |
165 | ArgumentCaptor tunnel = ArgumentCaptor.forClass(Tunnel.class);
166 | verify(handlerMock, times(1)).onTunnelClose(tunnel.capture());
167 | verify(handlerMock, only()).onTunnelClose(tunnel.capture());
168 | assertEquals(tunnel.getValue().getTunnelId(), "tunnel1");
169 | assertTrue(helper.checkPostResponseSuccess(httpMock.getResponseText()));
170 | }
171 |
172 | @Test
173 | public void testPostMessagePacket() throws JSONException, ConfigurationException {
174 | HttpMock httpMock = helper.createTunnelHttpMock("POST");
175 | httpMock.setRequestBody(helper.buildPacket(new JSONObject()
176 | .put("type", "message")
177 | .put("tunnelId", "tunnel1")
178 | .put("content", new JSONObject()
179 | .put("type", "test-type")
180 | .put("content", "test-content")
181 | .toString()
182 | )
183 | .toString()
184 | ));
185 |
186 | TunnelService service = new TunnelService(httpMock.request, httpMock.response);
187 | TunnelHandler handlerMock = mock(TunnelHandler.class);
188 |
189 | service.handle(handlerMock, null);
190 |
191 | ArgumentCaptor tunnel = ArgumentCaptor.forClass(Tunnel.class);
192 | ArgumentCaptor message = ArgumentCaptor.forClass(TunnelMessage.class);
193 | verify(handlerMock, times(1)).onTunnelMessage(tunnel.capture(), message.capture());
194 | verify(handlerMock, only()).onTunnelMessage(tunnel.capture(), message.capture());
195 | assertEquals("tunnel1", tunnel.getValue().getTunnelId());
196 | assertEquals("test-type", message.getValue().getType());
197 | assertEquals("test-content", message.getValue().getContent());
198 | assertTrue(helper.checkPostResponseSuccess(httpMock.getResponseText()));
199 | }
200 |
201 | @Test
202 | public void testPostUnknownMessagePacket() throws JSONException, ConfigurationException {
203 | HttpMock httpMock = helper.createTunnelHttpMock("POST");
204 | httpMock.setRequestBody(helper.buildPacket(new JSONObject()
205 | .put("type", "message")
206 | .put("tunnelId", "tunnel1")
207 | .put("content", "unknown-raw")
208 | .toString()
209 | ));
210 |
211 | TunnelService service = new TunnelService(httpMock.request, httpMock.response);
212 | TunnelHandler handlerMock = mock(TunnelHandler.class);
213 |
214 | service.handle(handlerMock, null);
215 |
216 | ArgumentCaptor tunnel = ArgumentCaptor.forClass(Tunnel.class);
217 | ArgumentCaptor message = ArgumentCaptor.forClass(TunnelMessage.class);
218 | verify(handlerMock, times(1)).onTunnelMessage(tunnel.capture(), message.capture());
219 | assertEquals("tunnel1", tunnel.getValue().getTunnelId());
220 | assertEquals("UnknownRaw", message.getValue().getType());
221 | assertEquals("unknown-raw", message.getValue().getContent());
222 | verifyNoMoreInteractions(handlerMock);
223 | assertTrue(helper.checkPostResponseSuccess(httpMock.getResponseText()));
224 | }
225 |
226 | @Test
227 | public void testPostUnknownPacket() throws JSONException, ConfigurationException {
228 | HttpMock httpMock = helper.createTunnelHttpMock("POST");
229 | httpMock.setRequestBody(helper.buildPacket(new JSONObject()
230 | .put("type", "unknown")
231 | .put("tunnelId", "tunnel1")
232 | .toString()
233 | ));
234 |
235 | TunnelService service = new TunnelService(httpMock.request, httpMock.response);
236 | TunnelHandler handlerMock = mock(TunnelHandler.class);
237 |
238 | service.handle(handlerMock, null);
239 |
240 | Mockito.verifyZeroInteractions(handlerMock);
241 | assertTrue(helper.checkPostResponseSuccess(httpMock.getResponseText()));
242 | }
243 |
244 | @Test
245 | public void testPostPacketWithErrorSignature() throws JSONException, ConfigurationException {
246 | HttpMock httpMock = helper.createTunnelHttpMock("POST");
247 | httpMock.setRequestBody(helper.buildPacket(
248 | new JSONObject()
249 | .put("type", "connect")
250 | .put("tunnelId", "tunnel1")
251 | .toString(),
252 | true
253 | ));
254 |
255 | TunnelService service = new TunnelService(httpMock.request, httpMock.response);
256 | TunnelHandler handlerMock = mock(TunnelHandler.class);
257 |
258 | service.handle(handlerMock, null);
259 |
260 | Mockito.verifyZeroInteractions(handlerMock);
261 | assertFalse(helper.checkPostResponseSuccess(httpMock.getResponseText()));
262 | }
263 |
264 |
265 | @Test
266 | public void testPostBadRequest() throws JSONException, ConfigurationException {
267 | HttpMock httpMock = helper.createTunnelHttpMock("POST");
268 | httpMock.setRequestBody("illegal request");
269 |
270 | TunnelService service = new TunnelService(httpMock.request, httpMock.response);
271 | TunnelHandler handlerMock = mock(TunnelHandler.class);
272 |
273 | service.handle(handlerMock, null);
274 |
275 | Mockito.verifyZeroInteractions(handlerMock);
276 | assertFalse(helper.checkPostResponseSuccess(httpMock.getResponseText()));
277 | }
278 |
279 | @Test
280 | public void testTunnelEmit() throws JSONException, EmitError {
281 | URLConnectionMock mock = helper.useURLConnectionMock();
282 | try {
283 | mock.setResponseBody(new JSONObject().put("code", 0).toString());
284 |
285 | Tunnel tunnel = Tunnel.getById("tunnel1");
286 | tunnel.emit("test-type", "test-content");
287 |
288 | JSONArray packets = helper.resolvePackets(mock.getRequestBody());
289 |
290 | assertEquals(1, packets.length());
291 |
292 | JSONObject firstPacket = packets.getJSONObject(0);
293 | assertEquals("tunnel1", firstPacket.getJSONArray("tunnelIds").getString(0));
294 | assertEquals("message", firstPacket.getString("type"));
295 |
296 | JSONObject message = new JSONObject(firstPacket.getString("content"));
297 | assertEquals("test-type", message.getString("type"));
298 | assertEquals("test-content", message.getString("content"));
299 | } finally {
300 | helper.restoreURLConnectionMock();
301 | }
302 | }
303 |
304 | @Test
305 | public void testEmitWithInvalidTunnels() throws JSONException, EmitError {
306 | URLConnectionMock mock = helper.useURLConnectionMock();
307 | try {
308 | mock.setResponseBody(new JSONObject()
309 | .put("code", 0)
310 | .put("data", new JSONObject()
311 | .put("invalidTunnelIds", new JSONArray().put("tunnel1"))
312 | .toString()
313 | )
314 | .toString()
315 | );
316 | Tunnel tunnel = Tunnel.getById("tunnel1");
317 | EmitResult result = tunnel.emit("test-type", new JSONObject().put("message", "test-content"));
318 | assertEquals(1, result.getTunnelInvalidInfos().size());
319 | assertEquals("tunnel1", result.getTunnelInvalidInfos().get(0).getTunnelId());
320 | } finally {
321 | helper.restoreURLConnectionMock();
322 | }
323 | }
324 |
325 | @Test
326 | public void testTunnelClose() throws JSONException, EmitError {
327 | URLConnectionMock mock = helper.useURLConnectionMock();
328 | try {
329 | mock.setResponseBody(new JSONObject().put("code", 0).toString());
330 | Tunnel tunnel = Tunnel.getById("tunnel1");
331 | tunnel.close();
332 |
333 | JSONArray packets = helper.resolvePackets(mock.getRequestBody());
334 |
335 | assertEquals(1, packets.length());
336 | assertEquals("tunnel1", packets.getJSONObject(0).getJSONArray("tunnelIds").getString(0));
337 | assertEquals("close", packets.getJSONObject(0).getString("type"));
338 | } finally {
339 | helper.restoreURLConnectionMock();
340 | }
341 | }
342 |
343 | @Test
344 | public void testRoomBroadcast() throws JSONException, EmitError {
345 | URLConnectionMock mock = helper.useURLConnectionMock();
346 | try {
347 | mock.setResponseBody(new JSONObject().put("code", 0).toString());
348 |
349 | TunnelRoom room = new TunnelRoom();
350 |
351 | room.addTunnel(Tunnel.getById("tunnel1"));
352 | room.addTunnel(Tunnel.getById("tunnel2"));
353 | assertEquals(2, room.getTunnelCount());
354 |
355 | room.removeTunnelById("tunnel1");
356 | assertEquals(1, room.getTunnelCount());
357 |
358 | room.addTunnel(Tunnel.getById("tunnel3"));
359 | room.broadcast("test-type", "test-message");
360 |
361 | JSONArray packets = helper.resolvePackets(mock.getRequestBody());
362 |
363 | assertEquals(1, packets.length());
364 |
365 | JSONObject firstPacket = packets.getJSONObject(0);
366 | assertEquals(2, firstPacket.getJSONArray("tunnelIds").length());
367 | assertEquals("tunnel2", firstPacket.getJSONArray("tunnelIds").get(0));
368 | assertEquals("tunnel3", firstPacket.getJSONArray("tunnelIds").get(1));
369 | assertEquals("message", firstPacket.getString("type"));
370 |
371 | JSONObject message = new JSONObject(firstPacket.getString("content"));
372 | assertEquals("test-type", message.getString("type"));
373 | assertEquals("test-message", message.getString("content"));
374 | } finally {
375 | helper.restoreURLConnectionMock();
376 | }
377 | }
378 |
379 |
380 | @Test
381 | public void testRoomBroadcastWithInvalidTunnels() throws JSONException, EmitError {
382 | URLConnectionMock mock = helper.useURLConnectionMock();
383 | try {
384 | mock.setResponseBody(new JSONObject()
385 | .put("code", 0)
386 | .put("data", new JSONObject()
387 | .put("invalidTunnelIds", new JSONArray().put("tunnel1").put("tunnel2"))
388 | )
389 | .toString()
390 | );
391 |
392 | TunnelRoom room = new TunnelRoom();
393 |
394 | room.addTunnel(Tunnel.getById("tunnel1"));
395 | room.addTunnel(Tunnel.getById("tunnel2"));
396 | room.addTunnel(Tunnel.getById("tunnel3"));
397 | EmitResult result = room.broadcast("test-type", "test-message");
398 |
399 | assertEquals(2, result.getTunnelInvalidInfos().size());
400 | assertEquals("tunnel1", result.getTunnelInvalidInfos().get(0).getTunnelId());
401 | assertEquals("tunnel2", result.getTunnelInvalidInfos().get(1).getTunnelId());
402 | } finally {
403 | helper.restoreURLConnectionMock();
404 | }
405 | }
406 | }
407 |
--------------------------------------------------------------------------------
/com.qcloud.weapp.test/src/com/qcloud/weapp/test/tunnel/TunnelServiceTestHelper.java:
--------------------------------------------------------------------------------
1 | package com.qcloud.weapp.test.tunnel;
2 |
3 | import javax.servlet.http.HttpServletRequest;
4 | import javax.servlet.http.HttpServletResponse;
5 |
6 | import org.json.JSONArray;
7 | import org.json.JSONException;
8 | import org.json.JSONObject;
9 |
10 | import com.qcloud.weapp.ConfigurationException;
11 | import com.qcloud.weapp.ConfigurationManager;
12 | import com.qcloud.weapp.Hash;
13 | import com.qcloud.weapp.HttpRequest;
14 | import com.qcloud.weapp.test.HttpMock;
15 | import com.qcloud.weapp.test.URLConnectionMock;
16 |
17 | import static org.mockito.Mockito.*;
18 |
19 | public class TunnelServiceTestHelper {
20 |
21 | public HttpMock createTunnelHttpMock(String method, String sessionType) {
22 | HttpServletRequest request = mock(HttpServletRequest.class);
23 | HttpServletResponse response = mock(HttpServletResponse.class);
24 |
25 | when(request.getMethod()).thenReturn(method);
26 |
27 | if (sessionType != null) {
28 | if (sessionType.equals("valid")) {
29 | when(request.getHeader("X-WX-Id")).thenReturn("valid-id");
30 | when(request.getHeader("X-WX-Skey")).thenReturn("valid-key");
31 | }
32 | else if (sessionType.equals("invalid")) {
33 | when(request.getHeader("X-WX-Id")).thenReturn("invalid-id");
34 | when(request.getHeader("X-WX-Skey")).thenReturn("invalid-key");
35 | }
36 | }
37 |
38 | HttpMock mock = new HttpMock();
39 | mock.request = request;
40 | mock.response = response;
41 | mock.setupResponseWriter();
42 |
43 | return mock;
44 | }
45 |
46 | public HttpMock createTunnelHttpMock(String method) {
47 | return createTunnelHttpMock(method, null);
48 | }
49 |
50 | public boolean checkBodyHasMagicId(JSONObject body) {
51 | return body.has("F2C224D4-2BCE-4C64-AF9F-A6D872000D1A");
52 | }
53 |
54 | public String buildPacket(String data, boolean fakeSignature) {
55 | JSONObject json = new JSONObject();
56 | try {
57 | json.put("data", data);
58 | json.put("dataEncode", "json");
59 | json.put("signature", fakeSignature ? "fake-signature" : Hash.sha1(data + ConfigurationManager.getCurrentConfiguration().getTunnelSignatureKey()));
60 | } catch (JSONException e) {
61 | e.printStackTrace();
62 | } catch (ConfigurationException e) {
63 | e.printStackTrace();
64 | }
65 | return json.toString();
66 | }
67 |
68 | public String buildPacket(String data) {
69 | return buildPacket(data, false);
70 | }
71 |
72 | public JSONArray resolvePackets(String body) throws JSONException {
73 | return new JSONArray(new JSONObject(body).getString(("data")));
74 | }
75 |
76 | public boolean checkPostResponseSuccess(String responseText) {
77 | try {
78 | JSONObject response = new JSONObject(responseText);
79 | return response.getInt("code") == 0;
80 | } catch (JSONException e) {
81 | return false;
82 | }
83 | }
84 |
85 | private HttpRequest.ConnectionProvider originProvider;
86 | public URLConnectionMock useURLConnectionMock() {
87 | originProvider = HttpRequest.getUrlProvider();
88 | URLConnectionMock mock = new URLConnectionMock();
89 | HttpRequest.setUrlProvider(mock);
90 | return mock;
91 | }
92 |
93 | public void restoreURLConnectionMock() {
94 | HttpRequest.setUrlProvider(originProvider);
95 | }
96 | }
97 |
--------------------------------------------------------------------------------