├── .autod.conf.js ├── .eslintignore ├── .eslintrc ├── .gitignore ├── .travis.yml ├── History.md ├── LICENSE ├── README.md ├── appveyor.yml ├── benchmark └── buffer.js ├── example ├── client.js └── server.js ├── lib ├── const.js ├── decoder.js ├── encoder.js ├── index.js ├── protocol │ ├── index.js │ ├── invocation.js │ ├── request.js │ └── response.js ├── serialize │ ├── hessian │ │ ├── compile.js │ │ ├── index.js │ │ ├── primitive_type │ │ │ ├── boolean.js │ │ │ ├── custom_map.js │ │ │ ├── double.js │ │ │ ├── int.js │ │ │ ├── java.lang.boolean.js │ │ │ ├── java.lang.class.js │ │ │ ├── java.lang.double.js │ │ │ ├── java.lang.exception.js │ │ │ ├── java.lang.integer.js │ │ │ ├── java.lang.long.js │ │ │ ├── java.lang.object.js │ │ │ ├── java.lang.stacktraceelement.js │ │ │ ├── java.lang.string.js │ │ │ ├── java.math.bigdecimal.js │ │ │ ├── java.util.arraylist.js │ │ │ ├── java.util.currency.js │ │ │ ├── java.util.date.js │ │ │ ├── java.util.list.js │ │ │ ├── java.util.locale.js │ │ │ ├── java.util.map.js │ │ │ └── long.js │ │ └── utils.js │ └── index.js └── utils.js ├── package.json └── test ├── fixtures ├── class_map.js ├── dubbo_req.bin ├── dubbo_req_java_class.bin ├── dubbo_res.bin ├── dubbo_res_with_error.bin ├── dubbo_res_with_null.bin ├── dubbo_res_with_sys_error.bin ├── heartbeat_req.bin ├── heartbeat_res.bin ├── login.bin └── multiple.bin ├── hessian.test.js ├── index.test.js ├── protocol.test.js ├── serialize.test.js ├── utils.js └── utils.test.js /.autod.conf.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | 'use strict'; 19 | 20 | module.exports = { 21 | write: true, 22 | prefix: '^', 23 | devprefix: '^', 24 | devdep: [ 25 | 'autod', 26 | 'egg-bin', 27 | 'egg-ci', 28 | 'eslint', 29 | 'eslint-config-egg' 30 | ], 31 | }; 32 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | test/fixtures 2 | examples/**/app/public 3 | logs 4 | run 5 | 6 | *. 7 | !.eslintrc 8 | !.eslintignore 9 | !.gitignore 10 | !.travis.yml -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "eslint-config-egg", 3 | "rules": { 4 | "no-bitwise": "off" 5 | } 6 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # nyc test coverage 18 | .nyc_output 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directories 30 | node_modules 31 | jspm_packages 32 | 33 | # Optional npm cache directory 34 | .npm 35 | 36 | # Optional REPL history 37 | .node_repl_history 38 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 4 | - '8' 5 | - '10' 6 | - '12' 7 | install: 8 | - npm i npminstall && npminstall 9 | script: 10 | - npm run ci 11 | after_script: 12 | - npminstall codecov && codecov 13 | -------------------------------------------------------------------------------- /History.md: -------------------------------------------------------------------------------- 1 | 2 | 2.1.4 / 2019-01-18 3 | ================== 4 | 5 | **fixes** 6 | * [[`e26b2cb`](http://github.com/dubbo/dubbo-remoting-js/commit/e26b2cbe364d8c35c6af7d755a0a2ce3dd5c6c01)] - fix: nested generic bug (gxcsoccer <>) 7 | 8 | **others** 9 | * [[`50d2b03`](http://github.com/dubbo/dubbo-remoting-js/commit/50d2b0309f273d08ed174a4124ef37e9416be320)] - chore: change license to apache v2 (gxcsoccer <>) 10 | 11 | 2.1.3 / 2018-10-31 12 | ================== 13 | 14 | **fixes** 15 | * [[`5d24550`](http://github.com/dubbo-js/dubbo-remoting/commit/5d24550bf606719aff2e355a95dbbaace11fb7c2)] - fix: add group into request (gxcsoccer <>) 16 | 17 | 2.1.2 / 2018-10-30 18 | ================== 19 | 20 | **fixes** 21 | * [[`3ce2c93`](http://github.com/dubbo-js/dubbo-remoting/commit/3ce2c930b325d89f976c43ac0906f6a9a579537b)] - fix: encode enum defaultValue issue & list generic issue (gxcsoccer <>) 22 | 23 | 2.1.1 / 2018-10-30 24 | ================== 25 | 26 | **fixes** 27 | * [[`faa3136`](http://github.com/dubbo-js/dubbo-remoting/commit/faa31362a16e84385f28886537d2c81aab651b2c)] - fix: support no version service (gxcsoccer <>) 28 | 29 | 2.1.0 / 2018-09-21 30 | ================== 31 | 32 | **others** 33 | ,fatal: 没有标签能描述 '3a3f83e8fbd9361ee0a4e56a96357401d1ea82e3'。 34 | 尝试 --always,或者创建一些标签。 35 | 36 | 37 | 2.0.0 / 2018-08-24 38 | ================== 39 | 40 | **features** 41 | * [[`e280fdb`](http://github.com/dubbo-js/dubbo-remoting/commit/e280fdbb483a53edeed25302ab8adbad569e71d9)] - feat: integrated with sofa-rpc-node (gxcsoccer <>) 42 | 43 | **others** 44 | * [[`bb7dca7`](http://github.com/dubbo-js/dubbo-remoting/commit/bb7dca7064abb6657302cf247aced34f6a756a14)] - chore: bump to 1.0.0 (gxcsoccer <>), 45 | 46 | 1.0.0 / 2017-03-01 47 | ================== 48 | 49 | * feat: implement dubbo remoting protocol -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # js-remoting-for-apache-dubbo 2 | dubbo remoting 3 | 4 | [![NPM version][npm-image]][npm-url] 5 | [![build status][travis-image]][travis-url] 6 | [![Test coverage][codecov-image]][codecov-url] 7 | [![David deps][david-image]][david-url] 8 | [![Known Vulnerabilities][snyk-image]][snyk-url] 9 | [![npm download][download-image]][download-url] 10 | 11 | [npm-image]: https://img.shields.io/npm/v/js-remoting-for-apache-dubbo.svg?style=flat-square 12 | [npm-url]: https://npmjs.org/package/js-remoting-for-apache-dubbo 13 | [travis-image]: https://img.shields.io/travis/dubbo/js-remoting-for-apache-dubbo.svg?style=flat-square 14 | [travis-url]: https://travis-ci.org/dubbo/js-remoting-for-apache-dubbo 15 | [codecov-image]: https://codecov.io/gh/dubbo/js-remoting-for-apache-dubbo/branch/master/graph/badge.svg 16 | [codecov-url]: https://codecov.io/gh/dubbo/js-remoting-for-apache-dubbo 17 | [david-image]: https://img.shields.io/david/dubbo/js-remoting-for-apache-dubbo.svg?style=flat-square 18 | [david-url]: https://david-dm.org/dubbo/js-remoting-for-apache-dubbo 19 | [snyk-image]: https://snyk.io/test/npm/js-remoting-for-apache-dubbo/badge.svg?style=flat-square 20 | [snyk-url]: https://snyk.io/test/npm/js-remoting-for-apache-dubbo 21 | [download-image]: https://img.shields.io/npm/dm/js-remoting-for-apache-dubbo.svg?style=flat-square 22 | [download-url]: https://npmjs.org/package/js-remoting-for-apache-dubbo 23 | 24 | ## Introduction 25 | 26 | [Dubbo](http://dubbo.apache.org/en-us/) Protocol Nodejs Implement 27 | 28 | - Common Exchange Packet 29 | 30 | ``` 31 | 0 1 2 4 6 8 10 12 32 | +------+------+------+------+------+------+------+------+------+------+------+------+ 33 | | MAGIC | flag |status| packet id | 34 | +-------------+-------------+-------------+-------------+-------------+-------------+ 35 | | body length | body | 36 | +---------------------------+ + 37 | | ... ... | 38 | +-----------------------------------------------------------------------------------+ 39 | ``` 40 | 41 | - Dubbo Request Packet 42 | 43 | ``` 44 | 0 1 2 4 6 8 10 12 45 | +------+------+------+------+------+------+------+------+------+------+------+------+ 46 | | MAGIC | flag | | packet id | 47 | +-------------+-------------+-----------------+-------------------+-----------------+ 48 | | body length | dubbo version | service path | service version | 49 | +---------------+-----------+-----------+-----+-------------------+-----------------+ 50 | | method name | arguments description | | 51 | +---------------+-----------------------+ arguments + 52 | | ... ... | 53 | +-----------------------------------------------------------------------------------+ 54 | | attachments | 55 | +-----------------------------------------------------------------------------------+ 56 | ``` 57 | 58 | - Dubbo Response Packet 59 | 60 | packet status ok 61 | ``` 62 | 0 1 2 4 6 8 10 12 63 | +------+------+------+------+------+------+------+------+------+------+------+------+ 64 | | MAGIC | flag |status| packet id | 65 | +-------------+-------------+---------------------------+---------------------------+ 66 | | body length | result flag | | 67 | +---------------------------+---------------------------+ + 68 | | result or exception ... | 69 | +-----------------------------------------------------------------------------------+ 70 | ``` 71 | 72 | packet status not ok 73 | ``` 74 | 0 1 2 4 6 8 10 12 75 | +------+------+------+------+------+------+------+------+------+------+------+------+ 76 | | MAGIC | flag |status| packet id | 77 | +-------------+-------------+---------------------------+---------------------------+ 78 | | body length | error message | 79 | +---------------------------+-------------------------------------------------------+ 80 | ``` 81 | 82 | ## Install 83 | 84 | ```bash 85 | $ npm install dubbo-remoting --save 86 | ``` 87 | 88 | ## Usage 89 | 90 | You can use this dubbo protocol implementation with the [sofa-rpc-node](https://github.com/alipay/sofa-rpc-node) 91 | 92 | ### 1. Install & Launch zk 93 | 94 | ```bash 95 | $ brew install zookeeper 96 | 97 | $ zkServer start 98 | ZooKeeper JMX enabled by default 99 | Using config: /usr/local/etc/zookeeper/zoo.cfg 100 | Starting zookeeper ... STARTED 101 | ``` 102 | 103 | ### 2. Expose a dubbo service 104 | 105 | ```js 106 | 'use strict'; 107 | 108 | const { RpcServer } = require('sofa-rpc-node').server; 109 | const { ZookeeperRegistry } = require('sofa-rpc-node').registry; 110 | const protocol = require('dubbo-remoting'); 111 | 112 | const logger = console; 113 | 114 | // 1. create zk registry client 115 | const registry = new ZookeeperRegistry({ 116 | logger, 117 | address: '127.0.0.1:2181', 118 | }); 119 | 120 | // 2. create rpc server 121 | const server = new RpcServer({ 122 | logger, 123 | registry, 124 | port: 12200, 125 | protocol, 126 | }); 127 | 128 | // 3. add service 129 | server.addService({ 130 | interfaceName: 'com.nodejs.test.TestService', 131 | }, { 132 | async plus(a, b) { 133 | return a + b; 134 | }, 135 | }); 136 | 137 | // 4. launch the server 138 | server.start() 139 | .then(() => { 140 | server.publish(); 141 | }); 142 | ``` 143 | 144 | ### 3. Call the dubbo service 145 | 146 | ```js 147 | 'use strict'; 148 | 149 | const { RpcClient } = require('sofa-rpc-node').client; 150 | const { ZookeeperRegistry } = require('sofa-rpc-node').registry; 151 | const protocol = require('dubbo-remoting'); 152 | const logger = console; 153 | 154 | // 1. create zk registry client 155 | const registry = new ZookeeperRegistry({ 156 | logger, 157 | address: '127.0.0.1:2181', 158 | }); 159 | 160 | async function invoke() { 161 | // 2. create rpc client with dubbo protocol 162 | const client = new RpcClient({ 163 | logger, 164 | registry, 165 | protocol, 166 | }); 167 | // 3. create rpc service consumer 168 | const consumer = client.createConsumer({ 169 | interfaceName: 'com.nodejs.test.TestService', 170 | }); 171 | // 4. wait consumer ready 172 | await consumer.ready(); 173 | 174 | // 5. call the service 175 | const result = await consumer.invoke('plus', [1, 2], { responseTimeout: 3000 }); 176 | console.log('1 + 2 = ' + result); 177 | } 178 | 179 | invoke().catch(console.error); 180 | ``` 181 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | matrix: 3 | - nodejs_version: '8' 4 | - nodejs_version: '10' 5 | - nodejs_version: '12' 6 | 7 | install: 8 | - ps: Install-Product node $env:nodejs_version 9 | - npm i npminstall && node_modules\.bin\npminstall 10 | 11 | test_script: 12 | - node --version 13 | - npm --version 14 | - npm run test 15 | 16 | build: off 17 | -------------------------------------------------------------------------------- /benchmark/buffer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | 'use strict'; 19 | 20 | const fs = require('fs'); 21 | const path = require('path'); 22 | const assert = require('assert'); 23 | const utils = require('../lib/utils'); 24 | const Benchmark = require('benchmark'); 25 | const benchmarks = require('beautify-benchmark'); 26 | const suite = new Benchmark.Suite(); 27 | 28 | const buf_1 = fs.readFileSync(path.join(__dirname, '../test/fixtures/dubbo_req.bin')); 29 | const buf_2 = fs.readFileSync(path.join(__dirname, '../test/fixtures/dubbo_req_java_class.bin')); 30 | const total = buf_1.length + buf_2.length; 31 | 32 | assert.deepEqual(Buffer.concat([buf_1, buf_2]), utils.concatBuffer(buf_1, buf_2)); 33 | 34 | // add tests 35 | suite 36 | .add('Buffer.concat()', function() { 37 | Buffer.concat([buf_1, buf_2]); 38 | }) 39 | .add('Buffer.concat() with size', function() { 40 | Buffer.concat([buf_1, buf_2], total); 41 | }) 42 | .add('Buffer.copy()', function() { 43 | utils.concatBuffer(buf_1, buf_2); 44 | }) 45 | .on('cycle', function(event) { 46 | benchmarks.add(event.target); 47 | }) 48 | .on('start', function() { 49 | console.log('\n node version: %s, date: %s\n Starting...', process.version, Date()); 50 | }) 51 | .on('complete', function done() { 52 | benchmarks.log(); 53 | }) 54 | .run({ async: false }); 55 | -------------------------------------------------------------------------------- /example/client.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | 'use strict'; 19 | 20 | const { RpcClient } = require('sofa-rpc-node').client; 21 | const { ZookeeperRegistry } = require('sofa-rpc-node').registry; 22 | const protocol = require('..'); 23 | const logger = console; 24 | 25 | // 1. 创建 zk 注册中心客户端 26 | const registry = new ZookeeperRegistry({ 27 | logger, 28 | address: '127.0.0.1:2181', 29 | }); 30 | 31 | async function invoke() { 32 | // 2. 创建 RPC Client 实例 33 | const client = new RpcClient({ 34 | logger, 35 | registry, 36 | protocol, 37 | }); 38 | // 3. 创建服务的 consumer 39 | const consumer = client.createConsumer({ 40 | interfaceName: 'com.nodejs.test.TestService', 41 | }); 42 | // 4. 等待 consumer ready(从注册中心订阅服务列表...) 43 | await consumer.ready(); 44 | 45 | // 5. 执行泛化调用 46 | const result = await consumer.invoke('plus', [1, 2], { responseTimeout: 3000 }); 47 | console.log('1 + 2 = ' + result); 48 | } 49 | 50 | invoke().catch(console.error); 51 | -------------------------------------------------------------------------------- /example/server.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | 'use strict'; 19 | 20 | const { RpcServer } = require('sofa-rpc-node').server; 21 | const { ZookeeperRegistry } = require('sofa-rpc-node').registry; 22 | const protocol = require('..'); 23 | 24 | const logger = console; 25 | 26 | // 1. 创建 zk 注册中心客户端 27 | const registry = new ZookeeperRegistry({ 28 | logger, 29 | address: '127.0.0.1:2181', // 需要本地启动一个 zkServer 30 | }); 31 | 32 | // 2. 创建 RPC Server 实例 33 | const server = new RpcServer({ 34 | logger, 35 | registry, // 传入注册中心客户端 36 | port: 12200, 37 | protocol, 38 | }); 39 | 40 | // 3. 添加服务 41 | server.addService({ 42 | interfaceName: 'com.nodejs.test.TestService', 43 | }, { 44 | async plus(a, b) { 45 | return a + b; 46 | }, 47 | }); 48 | 49 | // 4. 启动 Server 并发布服务 50 | server.start() 51 | .then(() => { 52 | server.publish(); 53 | }); 54 | -------------------------------------------------------------------------------- /lib/const.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | 'use strict'; 19 | 20 | exports.PROVIDER = 'provider'; 21 | exports.CONSUMER = 'consumer'; 22 | exports.REGISTER = 'register'; 23 | exports.UNREGISTER = 'unregister'; 24 | exports.SUBSCRIBE = 'subscribe'; 25 | exports.UNSUBSCRIBE = 'unsubscribe'; 26 | exports.CATEGORY_KEY = 'category'; 27 | exports.PROVIDERS_CATEGORY = 'providers'; 28 | exports.CONSUMERS_CATEGORY = 'consumers'; 29 | exports.ROUTERS_CATEGORY = 'routers'; 30 | exports.CONFIGURATORS_CATEGORY = 'configurators'; 31 | exports.DEFAULT_CATEGORY = exports.PROVIDERS_CATEGORY; 32 | exports.ENABLED_KEY = 'enabled'; 33 | exports.DISABLED_KEY = 'disabled'; 34 | exports.VALIDATION_KEY = 'validation'; 35 | exports.CACHE_KEY = 'cache'; 36 | exports.DYNAMIC_KEY = 'dynamic'; 37 | exports.DUBBO_PROPERTIES_KEY = 'dubbo.properties.file'; 38 | exports.DEFAULT_DUBBO_PROPERTIES = 'dubbo.properties'; 39 | exports.SENT_KEY = 'sent'; 40 | exports.DEFAULT_SENT = false; 41 | exports.REGISTRY_PROTOCOL = 'registry'; 42 | exports.$INVOKE = '$invoke'; 43 | exports.$ECHO = '$echo'; 44 | exports.DEFAULT_PROXY = 'javassist'; 45 | exports.DEFAULT_PAYLOAD = 8 * 1024 * 1024; // 8M 46 | exports.DEFAULT_CLUSTER = 'failover'; 47 | exports.DEFAULT_DIRECTORY = 'dubbo'; 48 | exports.DEFAULT_LOADBALANCE = 'random'; 49 | exports.DEFAULT_PROTOCOL = 'dubbo'; 50 | exports.DEFAULT_EXCHANGER = 'header'; 51 | exports.DEFAULT_TRANSPORTER = 'netty'; 52 | exports.DEFAULT_REMOTING_SERVER = 'netty'; 53 | exports.DEFAULT_REMOTING_CLIENT = 'netty'; 54 | exports.DEFAULT_REMOTING_CODEC = 'dubbo'; 55 | exports.DEFAULT_REMOTING_SERIALIZATION = 'hessian2'; 56 | exports.DEFAULT_HTTP_SERVER = 'servlet'; 57 | exports.DEFAULT_HTTP_CLIENT = 'jdk'; 58 | exports.DEFAULT_HTTP_SERIALIZATION = 'json'; 59 | exports.DEFAULT_CHARSET = 'UTF-8'; 60 | exports.DEFAULT_WEIGHT = 100; 61 | exports.DEFAULT_FORKS = 2; 62 | exports.DEFAULT_THREAD_NAME = 'Dubbo'; 63 | exports.DEFAULT_CORE_THREADS = 0; 64 | exports.DEFAULT_THREADS = 200; 65 | exports.DEFAULT_QUEUES = 0; 66 | exports.DEFAULT_ALIVE = 60 * 1000; 67 | exports.DEFAULT_CONNECTIONS = 0; 68 | exports.DEFAULT_ACCEPTS = 0; 69 | exports.DEFAULT_IDLE_TIMEOUT = 600 * 1000; 70 | exports.DEFAULT_HEARTBEAT = 60 * 1000; 71 | exports.DEFAULT_TIMEOUT = 1000; 72 | exports.DEFAULT_CONNECT_TIMEOUT = 3000; 73 | exports.DEFAULT_RETRIES = 2; 74 | // default buffer size is 8k. 75 | exports.DEFAULT_BUFFER_SIZE = 8 * 1024; 76 | exports.MAX_BUFFER_SIZE = 16 * 1024; 77 | exports.MIN_BUFFER_SIZE = 1 * 1024; 78 | exports.REMOVE_VALUE_PREFIX = '-'; 79 | exports.HIDE_KEY_PREFIX = '.'; 80 | exports.DEFAULT_KEY_PREFIX = 'default.'; 81 | exports.DEFAULT_KEY = 'default'; 82 | exports.LOADBALANCE_KEY = 'loadbalance'; 83 | // key for router type, for e.g., 'script'/'file', corresponding to ScriptRouterFactory.NAME, FileRouterFactory.NAME 84 | exports.ROUTER_KEY = 'router'; 85 | exports.CLUSTER_KEY = 'cluster'; 86 | exports.REGISTRY_KEY = 'registry'; 87 | exports.MONITOR_KEY = 'monitor'; 88 | exports.SIDE_KEY = 'side'; 89 | exports.PROVIDER_SIDE = 'provider'; 90 | exports.CONSUMER_SIDE = 'consumer'; 91 | exports.DEFAULT_REGISTRY = 'dubbo'; 92 | exports.BACKUP_KEY = 'backup'; 93 | exports.DIRECTORY_KEY = 'directory'; 94 | exports.DEPRECATED_KEY = 'deprecated'; 95 | exports.ANYHOST_KEY = 'anyhost'; 96 | exports.ANYHOST_VALUE = '0.0.0.0'; 97 | exports.LOCALHOST_KEY = 'localhost'; 98 | exports.LOCALHOST_VALUE = '127.0.0.1'; 99 | exports.APPLICATION_KEY = 'application'; 100 | exports.LOCAL_KEY = 'local'; 101 | exports.STUB_KEY = 'stub'; 102 | exports.MOCK_KEY = 'mock'; 103 | exports.PROTOCOL_KEY = 'protocol'; 104 | exports.PROXY_KEY = 'proxy'; 105 | exports.WEIGHT_KEY = 'weight'; 106 | exports.FORKS_KEY = 'forks'; 107 | exports.DEFAULT_THREADPOOL = 'limited'; 108 | exports.DEFAULT_CLIENT_THREADPOOL = 'cached'; 109 | exports.THREADPOOL_KEY = 'threadpool'; 110 | exports.THREAD_NAME_KEY = 'threadname'; 111 | exports.IO_THREADS_KEY = 'iothreads'; 112 | exports.CORE_THREADS_KEY = 'corethreads'; 113 | exports.THREADS_KEY = 'threads'; 114 | exports.QUEUES_KEY = 'queues'; 115 | exports.ALIVE_KEY = 'alive'; 116 | exports.EXECUTES_KEY = 'executes'; 117 | exports.BUFFER_KEY = 'buffer'; 118 | exports.PAYLOAD_KEY = 'payload'; 119 | exports.REFERENCE_FILTER_KEY = 'reference.filter'; 120 | exports.INVOKER_LISTENER_KEY = 'invoker.listener'; 121 | exports.SERVICE_FILTER_KEY = 'service.filter'; 122 | exports.EXPORTER_LISTENER_KEY = 'exporter.listener'; 123 | exports.ACCESS_LOG_KEY = 'accesslog'; 124 | exports.ACTIVES_KEY = 'actives'; 125 | exports.CONNECTIONS_KEY = 'connections'; 126 | exports.ACCEPTS_KEY = 'accepts'; 127 | exports.IDLE_TIMEOUT_KEY = 'idle.timeout'; 128 | exports.HEARTBEAT_KEY = 'heartbeat'; 129 | exports.HEARTBEAT_TIMEOUT_KEY = 'heartbeat.timeout'; 130 | exports.CONNECT_TIMEOUT_KEY = 'connect.timeout'; 131 | exports.TIMEOUT_KEY = 'timeout'; 132 | exports.RETRIES_KEY = 'retries'; 133 | exports.PROMPT_KEY = 'prompt'; 134 | exports.DEFAULT_PROMPT = 'dubbo>'; 135 | exports.CODEC_KEY = 'codec'; 136 | exports.SERIALIZATION_KEY = 'serialization'; 137 | exports.EXCHANGER_KEY = 'exchanger'; 138 | exports.TRANSPORTER_KEY = 'transporter'; 139 | exports.SERVER_KEY = 'server'; 140 | exports.CLIENT_KEY = 'client'; 141 | exports.ID_KEY = 'id'; 142 | exports.ASYNC_KEY = 'async'; 143 | exports.RETURN_KEY = 'return'; 144 | exports.TOKEN_KEY = 'token'; 145 | exports.METHOD_KEY = 'method'; 146 | exports.METHODS_KEY = 'methods'; 147 | exports.CHARSET_KEY = 'charset'; 148 | exports.RECONNECT_KEY = 'reconnect'; 149 | exports.SEND_RECONNECT_KEY = 'send.reconnect'; 150 | exports.DEFAULT_RECONNECT_PERIOD = 2000; 151 | exports.SHUTDOWN_TIMEOUT_KEY = 'shutdown.timeout'; 152 | exports.DEFAULT_SHUTDOWN_TIMEOUT = 1000 * 60 * 15; 153 | exports.PID_KEY = 'pid'; 154 | exports.TIMESTAMP_KEY = 'timestamp'; 155 | exports.WARMUP_KEY = 'warmup'; 156 | exports.DEFAULT_WARMUP = 10 * 60 * 1000; 157 | exports.CHECK_KEY = 'check'; 158 | exports.REGISTER_KEY = 'register'; 159 | exports.SUBSCRIBE_KEY = 'subscribe'; 160 | exports.GROUP_KEY = 'group'; 161 | exports.PATH_KEY = 'path'; 162 | exports.INTERFACE_KEY = 'interface'; 163 | exports.GENERIC_KEY = 'generic'; 164 | exports.FILE_KEY = 'file'; 165 | exports.WAIT_KEY = 'wait'; 166 | exports.CLASSIFIER_KEY = 'classifier'; 167 | exports.VERSION_KEY = 'version'; 168 | exports.REVISION_KEY = 'revision'; 169 | exports.DUBBO_VERSION_KEY = 'dubbo'; 170 | exports.HESSIAN_VERSION_KEY = 'hessian.version'; 171 | exports.DISPATCHER_KEY = 'dispatcher'; 172 | exports.CHANNEL_HANDLER_KEY = 'channel.handler'; 173 | exports.DEFAULT_CHANNEL_HANDLER = 'default'; 174 | exports.ANY_VALUE = '*'; 175 | exports.COMMA_SEPARATOR = ','; 176 | exports.PATH_SEPARATOR = '/'; 177 | exports.REGISTRY_SEPARATOR = '|'; 178 | exports.SEMICOLON_SEPARATOR = ';'; 179 | exports.CONNECT_QUEUE_CAPACITY = 'connect.queue.capacity'; 180 | exports.CONNECT_QUEUE_WARNING_SIZE = 'connect.queue.warning.size'; 181 | exports.DEFAULT_CONNECT_QUEUE_WARNING_SIZE = 1000; 182 | exports.CHANNEL_ATTRIBUTE_READONLY_KEY = 'channel.readonly'; 183 | exports.CHANNEL_READONLYEVENT_SENT_KEY = 'channel.readonly.sent'; 184 | exports.CHANNEL_SEND_READONLYEVENT_KEY = 'channel.readonly.send'; 185 | exports.COUNT_PROTOCOL = 'count'; 186 | exports.TRACE_PROTOCOL = 'trace'; 187 | exports.EMPTY_PROTOCOL = 'empty'; 188 | exports.ADMIN_PROTOCOL = 'admin'; 189 | exports.PROVIDER_PROTOCOL = 'provider'; 190 | exports.CONSUMER_PROTOCOL = 'consumer'; 191 | exports.ROUTE_PROTOCOL = 'route'; 192 | exports.SCRIPT_PROTOCOL = 'script'; 193 | exports.CONDITION_PROTOCOL = 'condition'; 194 | exports.MOCK_PROTOCOL = 'mock'; 195 | exports.RETURN_PREFIX = 'return '; 196 | exports.THROW_PREFIX = 'throw'; 197 | exports.FAIL_PREFIX = 'fail:'; 198 | exports.FORCE_PREFIX = 'force:'; 199 | exports.FORCE_KEY = 'force'; 200 | exports.MERGER_KEY = 'merger'; 201 | /** 202 | * 集群时是否排除非available的invoker 203 | */ 204 | exports.CLUSTER_AVAILABLE_CHECK_KEY = 'cluster.availablecheck'; 205 | /** 206 | */ 207 | exports.DEFAULT_CLUSTER_AVAILABLE_CHECK = true; 208 | /** 209 | * 集群时是否启用sticky策略 210 | */ 211 | exports.CLUSTER_STICKY_KEY = 'sticky'; 212 | /** 213 | * sticky默认值. 214 | */ 215 | exports.DEFAULT_CLUSTER_STICKY = false; 216 | /** 217 | * 创建client时,是否先要建立连接。 218 | */ 219 | exports.LAZY_CONNECT_KEY = 'lazy'; 220 | /** 221 | * lazy连接的初始状态是连接状态还是非连接状态? 222 | */ 223 | exports.LAZY_CONNECT_INITIAL_STATE_KEY = 'connect.lazy.initial.state'; 224 | /** 225 | * lazy连接的初始状态默认是连接状态. 226 | */ 227 | exports.DEFAULT_LAZY_CONNECT_INITIAL_STATE = true; 228 | /** 229 | * 注册中心是否同步存储文件,默认异步 230 | */ 231 | exports.REGISTRY_FILESAVE_SYNC_KEY = 'save.file'; 232 | /** 233 | * 注册中心失败事件重试事件 234 | */ 235 | exports.REGISTRY_RETRY_PERIOD_KEY = 'retry.period'; 236 | /** 237 | * 重试周期 238 | */ 239 | exports.DEFAULT_REGISTRY_RETRY_PERIOD = 5 * 1000; 240 | /** 241 | * 注册中心自动重连时间 242 | */ 243 | exports.REGISTRY_RECONNECT_PERIOD_KEY = 'reconnect.period'; 244 | exports.DEFAULT_REGISTRY_RECONNECT_PERIOD = 3 * 1000; 245 | exports.SESSION_TIMEOUT_KEY = 'session'; 246 | exports.DEFAULT_SESSION_TIMEOUT = 60 * 1000; 247 | /** 248 | * 注册中心导出URL参数的KEY 249 | */ 250 | exports.EXPORT_KEY = 'export'; 251 | /** 252 | * 注册中心引用URL参数的KEY 253 | */ 254 | exports.REFER_KEY = 'refer'; 255 | /** 256 | * callback inst id 257 | */ 258 | exports.CALLBACK_SERVICE_KEY = 'callback.service.instid'; 259 | /** 260 | * 每个客户端同一个接口 callback服务实例的限制 261 | */ 262 | exports.CALLBACK_INSTANCES_LIMIT_KEY = 'callbacks'; 263 | /** 264 | * 每个客户端同一个接口 callback服务实例的限制 265 | */ 266 | exports.DEFAULT_CALLBACK_INSTANCES = 1; 267 | exports.CALLBACK_SERVICE_PROXY_KEY = 'callback.service.proxy'; 268 | exports.IS_CALLBACK_SERVICE = 'is_callback_service'; 269 | /** 270 | * channel中callback的invokers 271 | */ 272 | exports.CHANNEL_CALLBACK_KEY = 'channel.callback.invokers.key'; 273 | exports.SHUTDOWN_WAIT_KEY = 'dubbo.service.shutdown.wait'; 274 | exports.IS_SERVER_KEY = 'isserver'; 275 | /** 276 | * 默认值毫秒,避免重新计算. 277 | */ 278 | exports.DEFAULT_SERVER_SHUTDOWN_TIMEOUT = 10000; 279 | exports.ON_CONNECT_KEY = 'onconnect'; 280 | exports.ON_DISCONNECT_KEY = 'ondisconnect'; 281 | exports.ON_INVOKE_METHOD_KEY = 'oninvoke.method'; 282 | exports.ON_RETURN_METHOD_KEY = 'onreturn.method'; 283 | exports.ON_THROW_METHOD_KEY = 'onthrow.method'; 284 | exports.ON_INVOKE_INSTANCE_KEY = 'oninvoke.instance'; 285 | exports.ON_RETURN_INSTANCE_KEY = 'onreturn.instance'; 286 | exports.ON_THROW_INSTANCE_KEY = 'onthrow.instance'; 287 | exports.OVERRIDE_PROTOCOL = 'override'; 288 | exports.PRIORITY_KEY = 'priority'; 289 | exports.RULE_KEY = 'rule'; 290 | exports.TYPE_KEY = 'type'; 291 | exports.RUNTIME_KEY = 'runtime'; 292 | // when ROUTER_KEY's value is set to ROUTER_TYPE_CLEAR, RegistryDirectory will clean all current routers 293 | exports.ROUTER_TYPE_CLEAR = 'clean'; 294 | exports.DEFAULT_SCRIPT_TYPE_KEY = 'javascript'; 295 | exports.STUB_EVENT_KEY = 'dubbo.stub.event'; 296 | exports.DEFAULT_STUB_EVENT = false; 297 | exports.STUB_EVENT_METHODS_KEY = 'dubbo.stub.event.methods'; 298 | // invocation attachment属性中如果有此值,则选择mock invoker 299 | exports.INVOCATION_NEED_MOCK = 'invocation.need.mock'; 300 | exports.LOCAL_PROTOCOL = 'injvm'; 301 | exports.AUTO_ATTACH_INVOCATIONID_KEY = 'invocationid.autoattach'; 302 | exports.SCOPE_KEY = 'scope'; 303 | exports.SCOPE_LOCAL = 'local'; 304 | exports.SCOPE_REMOTE = 'remote'; 305 | exports.SCOPE_NONE = 'none'; 306 | exports.RELIABLE_PROTOCOL = 'napoli'; 307 | exports.TPS_LIMIT_RATE_KEY = 'tps'; 308 | exports.TPS_LIMIT_INTERVAL_KEY = 'tps.interval'; 309 | exports.DEFAULT_TPS_LIMIT_INTERVAL = 60 * 1000; 310 | exports.DECODE_IN_IO_THREAD_KEY = 'decode.in.io'; 311 | exports.DEFAULT_DECODE_IN_IO_THREAD = true; 312 | exports.INPUT_KEY = 'input'; 313 | exports.OUTPUT_KEY = 'output'; 314 | exports.GENERIC_SERIALIZATION_NATIVE_JAVA = 'nativejava'; 315 | exports.GENERIC_SERIALIZATION_DEFAULT = 'true'; 316 | -------------------------------------------------------------------------------- /lib/decoder.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | 'use strict'; 19 | 20 | const protocol = require('./protocol'); 21 | const Writable = require('stream').Writable; 22 | 23 | const HEADER_LENGTH = 16; 24 | 25 | class DubboDecoder extends Writable { 26 | constructor(options = {}) { 27 | super(options); 28 | this._buf = null; 29 | this.options = options; 30 | } 31 | 32 | _write(chunk, encoding, callback) { 33 | // 合并 buf 中的数据 34 | this._buf = this._buf ? Buffer.concat([ this._buf, chunk ]) : chunk; 35 | try { 36 | let unfinish = false; 37 | do { 38 | unfinish = this._decode(); 39 | } while (unfinish); 40 | callback(); 41 | } catch (err) { 42 | err.name = 'DubboDecodeError'; 43 | err.data = this._buf ? this._buf.toString('base64') : ''; 44 | callback(err); 45 | } 46 | } 47 | 48 | _decode() { 49 | const bufLength = this._buf.length; 50 | 51 | if (bufLength < HEADER_LENGTH) { 52 | return false; 53 | } 54 | const bodyLen = this._buf.readInt32BE(12); 55 | const packetLength = bodyLen + HEADER_LENGTH; 56 | if (packetLength === 0 || bufLength < packetLength) { 57 | return false; 58 | } 59 | const packet = this._buf.slice(0, packetLength); 60 | // 调用反序列化方法获取对象 61 | const obj = protocol.decode(packet, this.options); 62 | this.emit(obj.packetType, obj); 63 | const restLen = bufLength - packetLength; 64 | if (restLen) { 65 | this._buf = this._buf.slice(packetLength); 66 | return true; 67 | } 68 | this._buf = null; 69 | return false; 70 | } 71 | 72 | _destroy() { 73 | this._buf = null; 74 | this.emit('close'); 75 | } 76 | } 77 | 78 | module.exports = DubboDecoder; 79 | -------------------------------------------------------------------------------- /lib/encoder.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | 'use strict'; 19 | 20 | const noop = require('utility').noop; 21 | const protocol = require('./protocol'); 22 | const Transform = require('stream').Transform; 23 | 24 | class DubboEncoder extends Transform { 25 | /** 26 | * 协议编码器 27 | * 28 | * @param {Object} options 29 | * - {Map} sentReqs - 请求集合 30 | * - {Map} classCache - 类定义缓存 31 | * - {Object} classMap - 针对 hessian 序列化的 schema 32 | * - {URL} [address] - TCP socket 地址 33 | * @class 34 | */ 35 | constructor(options = {}) { 36 | super(options); 37 | this.options = options; 38 | 39 | let codecType = 'hessian2'; 40 | if (options.codecType) { 41 | codecType = options.codecType; 42 | } else if (options.address && options.address.query && options.address.query.serialization) { 43 | codecType = options.address.query.serialization; 44 | } 45 | this.encodeOptions = { 46 | protocolType: 'dubbo', 47 | codecType, 48 | classMap: options.classMap, 49 | classCache: options.classCache, 50 | }; 51 | this.sentReqs = options.sentReqs; 52 | 53 | this._limited = false; 54 | this._queue = []; 55 | this.once('close', () => { this._queue = []; }); 56 | this.on('drain', () => { 57 | this._limited = false; 58 | do { 59 | const item = this._queue.shift(); 60 | if (!item) break; 61 | 62 | const packet = item[0]; 63 | const callback = item[1]; 64 | this._writePacket(packet, callback); 65 | } while (!this._limited); 66 | }); 67 | } 68 | 69 | get protocol() { 70 | return protocol; 71 | } 72 | 73 | get protocolType() { 74 | return this.encodeOptions.protocolType; 75 | } 76 | 77 | get codecType() { 78 | return this.encodeOptions.codecType; 79 | } 80 | 81 | set codecType(val) { 82 | this.encodeOptions.codecType = val; 83 | } 84 | 85 | writeRequest(id, req, callback) { 86 | this._writePacket({ 87 | packetId: id, 88 | packetType: 'request', 89 | req, 90 | meta: this._createMeta(this.encodeOptions), 91 | }, callback); 92 | } 93 | 94 | writeResponse(req, res, callback) { 95 | this._writePacket({ 96 | packetId: req.packetId, 97 | packetType: 'response', 98 | req, 99 | res, 100 | meta: this._createMeta(req.options), 101 | }, callback); 102 | } 103 | 104 | writeHeartbeat(id, hb, callback) { 105 | this._writePacket({ 106 | packetId: id, 107 | packetType: 'heartbeat', 108 | hb, 109 | meta: this._createMeta(this.encodeOptions), 110 | }, callback); 111 | } 112 | 113 | writeHeartbeatAck(hb, callback) { 114 | this._writePacket({ 115 | packetId: hb.packetId, 116 | packetType: 'heartbeatAck', 117 | hb, 118 | meta: this._createMeta(hb.options), 119 | }, callback); 120 | } 121 | 122 | _createMeta(proto) { 123 | return Object.create(proto, { 124 | start: { 125 | writable: true, 126 | configurable: true, 127 | value: Date.now(), 128 | }, 129 | data: { 130 | writable: true, 131 | configurable: true, 132 | value: null, 133 | }, 134 | size: { 135 | writable: true, 136 | configurable: true, 137 | value: 0, 138 | }, 139 | encodeRT: { 140 | writable: true, 141 | configurable: true, 142 | value: 0, 143 | }, 144 | }); 145 | } 146 | 147 | _writePacket(packet, callback = noop) { 148 | if (this._limited) { 149 | this._queue.push([ packet, callback ]); 150 | } else { 151 | const start = Date.now(); 152 | let buf; 153 | try { 154 | buf = this['_' + packet.packetType + 'Encode'](packet); 155 | } catch (err) { 156 | return callback(err, packet); 157 | } 158 | packet.meta.data = buf; 159 | packet.meta.size = buf.length; 160 | packet.meta.encodeRT = Date.now() - start; 161 | // @refer: https://nodejs.org/api/stream.html#stream_writable_write_chunk_encoding_callback 162 | // The return value is true if the internal buffer is less than the highWaterMark configured 163 | // when the stream was created after admitting chunk. If false is returned, further attempts to 164 | // write data to the stream should stop until the 'drain' event is emitted. 165 | this._limited = !this.write(buf, err => { 166 | callback(err, packet); 167 | }); 168 | } 169 | } 170 | 171 | _requestEncode(packet) { 172 | const id = packet.packetId; 173 | const req = packet.req; 174 | if (req.codecType) { 175 | packet.meta.codecType = req.codecType; 176 | } 177 | return this.protocol.requestEncode(id, req, packet.meta); 178 | } 179 | 180 | _responseEncode(packet) { 181 | const id = packet.packetId; 182 | const req = packet.req; 183 | const res = packet.res; 184 | res.appRequest = req.data; 185 | return this.protocol.responseEncode(id, res, packet.meta); 186 | } 187 | 188 | _heartbeatEncode(packet) { 189 | const id = packet.packetId; 190 | const hb = packet.hb; 191 | return this.protocol.heartbeatEncode(id, hb, this.encodeOptions); 192 | } 193 | 194 | _heartbeatAckEncode(packet) { 195 | const id = packet.packetId; 196 | return this.protocol.heartbeatAckEncode(id, packet.meta); 197 | } 198 | 199 | _transform(buf, encoding, callback) { 200 | callback(null, buf); 201 | } 202 | } 203 | 204 | module.exports = DubboEncoder; 205 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | 'use strict'; 19 | 20 | const ProtocolEncoder = require('./encoder'); 21 | const ProtocolDecoder = require('./decoder'); 22 | 23 | const globalOptions = {}; 24 | 25 | exports.setOptions = options => { 26 | Object.assign(globalOptions, options); 27 | }; 28 | 29 | exports.decoder = options => { 30 | return new ProtocolDecoder(Object.assign({}, globalOptions, options)); 31 | }; 32 | 33 | exports.encoder = options => { 34 | return new ProtocolEncoder(Object.assign({}, globalOptions, options)); 35 | }; 36 | 37 | exports.name = 'dubbo'; 38 | -------------------------------------------------------------------------------- /lib/protocol/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | 'use strict'; 19 | 20 | const Request = require('./request'); 21 | const Response = require('./response'); 22 | const Invocation = require('./invocation'); 23 | 24 | exports.requestEncode = (id, data, options) => { 25 | const req = new Request(id); 26 | const arr = data.serverSignature.split(':'); 27 | const attachments = Object.assign({ 28 | dubbo: '5.3.0', 29 | path: data.serverSignature, 30 | group: data.group || '', 31 | }, data.requestProps); 32 | if (arr.length === 2) { 33 | attachments.path = arr[0]; 34 | attachments.version = arr[1]; 35 | } 36 | req.data = new Invocation({ 37 | methodName: data.methodName, 38 | args: data.args, 39 | attachments, 40 | }); 41 | return req.encode(options); 42 | }; 43 | 44 | exports.responseEncode = (id, data, options) => { 45 | const res = new Response(id); 46 | res.status = data.isError ? Response.SERVER_ERROR : Response.OK; 47 | res.errorMsg = data.errorMsg; 48 | res.data = data.appResponse; 49 | return res.encode(options); 50 | }; 51 | 52 | exports.heartbeatEncode = (id, hb, options) => { 53 | const req = new Request(id); 54 | req.event = null; 55 | return req.encode(options); 56 | }; 57 | 58 | exports.heartbeatAckEncode = (id, options) => { 59 | const res = new Response(id); 60 | res.event = null; 61 | return res.encode(options); 62 | }; 63 | 64 | const FLAG_REQUEST = 0x80; 65 | 66 | /** 67 | * 反序列化 68 | * @param {ByteBuffer} buf - 二进制 69 | * @param {Object} options 70 | * - {Map} reqs - 请求集合 71 | * - {Object} [classCache] - 类定义缓存 72 | * @return {Object} 反序列化后的对象 73 | */ 74 | exports.decode = (buf, options) => { 75 | const start = Date.now(); 76 | 77 | const bufSize = buf.length; 78 | const flag = buf[2]; 79 | const ret = (flag & FLAG_REQUEST) === 0 ? 80 | Response.decode(buf, options) : 81 | Request.decode(buf, options); 82 | 83 | ret.meta = { 84 | size: bufSize, 85 | start, 86 | rt: Date.now() - start, 87 | }; 88 | return ret; 89 | }; 90 | -------------------------------------------------------------------------------- /lib/protocol/invocation.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | 'use strict'; 19 | 20 | class Invocation { 21 | constructor(options) { 22 | options = options || {}; 23 | this.methodName = options.methodName; 24 | this.args = options.args; 25 | this.attachments = options.attachments; 26 | } 27 | } 28 | 29 | module.exports = Invocation; 30 | -------------------------------------------------------------------------------- /lib/protocol/request.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | 'use strict'; 19 | 20 | const Long = require('long'); 21 | const is = require('is-type-of'); 22 | const utils = require('../utils'); 23 | const Constants = require('../const'); 24 | const Invocation = require('./invocation'); 25 | const getSerializationById = require('../serialize').getSerializationById; 26 | const getSerializationByName = require('../serialize').getSerializationByName; 27 | 28 | const DUBBO_VERSION = '2.5.3'; 29 | const HEADER_LENGTH = 16; 30 | const MAGIC_HIGH = 0xda; 31 | const MAGIC_LOW = 0xbb; 32 | const FLAG_EVENT = 0x20; 33 | const FLAG_TWOWAY = 0x40; 34 | const FLAG_REQUEST = 0x80; 35 | const HEARTBEAT_EVENT = null; 36 | const SERIALIZATION_MASK = 0x1f; 37 | 38 | class Request { 39 | constructor(id) { 40 | this.id = id != null ? id : utils.newId(); 41 | this.version = '2.0.0'; 42 | this.isTwoWay = true; 43 | this.isEvent = false; 44 | this.isBroken = false; 45 | this.data = null; 46 | } 47 | 48 | get isResponse() { 49 | return false; 50 | } 51 | 52 | get event() { 53 | return this.isEvent ? this.data : null; 54 | } 55 | 56 | set event(val) { 57 | this.isEvent = true; 58 | this.data = val; 59 | } 60 | 61 | get isHeartbeat() { 62 | return this.isEvent && this.event === HEARTBEAT_EVENT; 63 | } 64 | 65 | encode(options = {}) { 66 | const { codecType, classMap } = options; 67 | const output = getSerializationByName(codecType || Constants.DEFAULT_REMOTING_SERIALIZATION).serialize(); 68 | const bytes = output.bytes; 69 | 70 | bytes.put(MAGIC_HIGH); 71 | bytes.put(MAGIC_LOW); 72 | let flag = FLAG_REQUEST | output.contentTypeId; 73 | if (this.isTwoWay) { 74 | flag |= FLAG_TWOWAY; 75 | } 76 | if (this.isEvent) { 77 | flag |= FLAG_EVENT; 78 | } 79 | bytes.put(flag); 80 | bytes.put(0); 81 | bytes.putLong(this.id); 82 | bytes.skip(4); 83 | 84 | // data 如果是 invocation 需要特殊处理下,其余的当 hashmap 处理 85 | if (this.data instanceof Invocation) { 86 | const inv = this.data; 87 | output.writeUTF(inv.attachments[Constants.DUBBO_VERSION_KEY] || DUBBO_VERSION); 88 | output.writeUTF(inv.attachments[Constants.PATH_KEY]); 89 | output.writeUTF(inv.attachments[Constants.VERSION_KEY] || ''); 90 | 91 | output.writeUTF(inv.methodName); 92 | output.writeUTF(utils.getJavaArgsDesc(inv.args)); 93 | 94 | for (const arg of inv.args) { 95 | output.writeObject(arg, classMap); 96 | } 97 | output.writeObject(inv.attachments, classMap); 98 | } else { 99 | output.writeObject(this.data, classMap); 100 | } 101 | const bodyLen = bytes.position() - HEADER_LENGTH; 102 | bytes._bytes.writeInt32BE(bodyLen, 12); 103 | return output.get(); 104 | } 105 | 106 | static decode(buf, options = {}) { 107 | const packetId = utils.handleLong(new Long( 108 | buf.readInt32BE(8), // low, high 109 | buf.readInt32BE(4) 110 | )); 111 | 112 | const flag = buf[2]; 113 | const sType = flag & SERIALIZATION_MASK; 114 | const input = getSerializationById(sType).deserialize(buf); 115 | // skip header 116 | input.skip(16); 117 | 118 | let packetType = 'request'; 119 | let data; 120 | if ((flag & FLAG_EVENT) !== 0) { 121 | packetType = 'heartbeat'; 122 | data = input.readObject(); 123 | } else { 124 | const field = input.readObject(); 125 | if (is.string(field)) { 126 | const attachments = { 127 | [Constants.DUBBO_VERSION_KEY]: field, 128 | [Constants.PATH_KEY]: input.readUTF(), 129 | }; 130 | const version = input.readUTF(); 131 | const methodName = input.readUTF(); 132 | const desc = input.readUTF(); 133 | const methodArgSigs = utils.desc2classArray(desc); 134 | const argLen = methodArgSigs.length; 135 | const args = []; 136 | 137 | for (let i = 0; i < argLen; ++i) { 138 | args.push(input.readObject()); 139 | } 140 | Object.assign(attachments, input.readObject()); 141 | 142 | const serverSignature = version ? attachments.path + ':' + version : attachments.path; 143 | data = { 144 | methodName, 145 | serverSignature, 146 | args, 147 | methodArgSigs, 148 | requestProps: attachments, 149 | }; 150 | } else { 151 | data = field; 152 | } 153 | } 154 | 155 | return { 156 | packetId, 157 | packetType, 158 | data, 159 | options: { 160 | protocolType: 'dubbo', 161 | codecType: 'hessian2', 162 | classMap: options.classMap, 163 | }, 164 | meta: null, 165 | }; 166 | } 167 | } 168 | 169 | module.exports = Request; 170 | -------------------------------------------------------------------------------- /lib/protocol/response.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | 'use strict'; 19 | 20 | const Long = require('long'); 21 | const utils = require('../utils'); 22 | const Constants = require('../const'); 23 | const getSerializationById = require('../serialize').getSerializationById; 24 | const getSerializationByName = require('../serialize').getSerializationByName; 25 | 26 | const HEADER_LENGTH = 16; 27 | const MAGIC_HIGH = 0xda; 28 | const MAGIC_LOW = 0xbb; 29 | const FLAG_EVENT = 0x20; 30 | const HEARTBEAT_EVENT = null; 31 | const SERIALIZATION_MASK = 0x1f; 32 | 33 | const RESPONSE_WITH_EXCEPTION = 0; 34 | const RESPONSE_VALUE = 1; 35 | const RESPONSE_NULL_VALUE = 2; 36 | const RESPONSE_WITH_EXCEPTION_WITH_ATTACHMENTS = 3; 37 | const RESPONSE_VALUE_WITH_ATTACHMENTS = 4; 38 | const RESPONSE_NULL_VALUE_WITH_ATTACHMENTS = 5; 39 | 40 | class Response { 41 | constructor(id) { 42 | this.id = id; 43 | this.version = null; 44 | this.status = Response.OK; 45 | this.errorMsg = null; 46 | this.data = null; 47 | this.isEvent = false; 48 | } 49 | 50 | get isSuccess() { 51 | return this.status === Response.OK; 52 | } 53 | 54 | get isResponse() { 55 | return true; 56 | } 57 | 58 | get event() { 59 | return this.isEvent ? this.data : null; 60 | } 61 | 62 | set event(val) { 63 | this.isEvent = true; 64 | this.data = val; 65 | } 66 | 67 | get isHeartbeat() { 68 | return this.isEvent && this.event === HEARTBEAT_EVENT; 69 | } 70 | 71 | encode(options = {}) { 72 | const { codecType, classMap } = options; 73 | const output = getSerializationByName(codecType || Constants.DEFAULT_REMOTING_SERIALIZATION).serialize(); 74 | const bytes = output.bytes; 75 | 76 | bytes.put(MAGIC_HIGH); 77 | bytes.put(MAGIC_LOW); 78 | let flag = output.contentTypeId; 79 | if (this.isHeartbeat) { 80 | flag |= FLAG_EVENT; 81 | } 82 | bytes.put(flag); 83 | bytes.put(this.status); 84 | bytes.putLong(this.id); 85 | bytes.skip(4); 86 | 87 | if (this.status === Response.OK) { 88 | if (this.data) { 89 | if (this.data instanceof Error || (this.data.$class && this.data.$class.includes('Exception'))) { 90 | output.writeByte(RESPONSE_WITH_EXCEPTION); 91 | output.writeObject(this.data.$class ? this.data : { 92 | $class: 'java.lang.Exception', 93 | $: this.data, 94 | }, classMap); 95 | } else { 96 | if (this.data) { 97 | output.writeByte(RESPONSE_VALUE); 98 | output.writeObject(this.data, classMap); 99 | } else { 100 | output.writeByte(RESPONSE_NULL_VALUE); 101 | } 102 | } 103 | } else { 104 | output.writeObject(this.data, classMap); 105 | } 106 | } else { 107 | output.writeUTF(this.errorMsg || 'Exception caught in invocation'); 108 | } 109 | const bodyLen = bytes.position() - HEADER_LENGTH; 110 | bytes._bytes.writeInt32BE(bodyLen, 12); 111 | return output.get(); 112 | } 113 | 114 | static decode(buf, options = {}) { 115 | const packetId = utils.handleLong(new Long( 116 | buf.readInt32BE(8), // low, high 117 | buf.readInt32BE(4) 118 | )); 119 | const flag = buf[2]; 120 | const sType = flag & SERIALIZATION_MASK; 121 | const input = getSerializationById(sType).deserialize(buf); 122 | // skip header 123 | input.skip(16); 124 | 125 | let packetType = 'response'; 126 | if ((flag & FLAG_EVENT) !== 0) { 127 | packetType = 'heartbeat_ack'; 128 | } 129 | const status = buf[3]; 130 | let data = null; 131 | if (status === Response.OK) { 132 | if (packetType === 'heartbeat_ack') { 133 | data = input.readObject(); 134 | } else { 135 | data = { 136 | appResponse: null, 137 | error: null, 138 | responseProps: null, 139 | }; 140 | const rFlag = input.readObject(); 141 | switch (rFlag) { 142 | case RESPONSE_NULL_VALUE: 143 | break; 144 | case RESPONSE_VALUE: 145 | data.appResponse = input.readObject(); 146 | break; 147 | case RESPONSE_WITH_EXCEPTION: 148 | data.error = input.readObject(); 149 | break; 150 | case RESPONSE_NULL_VALUE_WITH_ATTACHMENTS: 151 | data.responseProps = input.readObject(); 152 | break; 153 | case RESPONSE_VALUE_WITH_ATTACHMENTS: 154 | data.appResponse = input.readObject(); 155 | data.responseProps = input.readObject(); 156 | break; 157 | case RESPONSE_WITH_EXCEPTION_WITH_ATTACHMENTS: 158 | data.error = input.readObject(); 159 | data.responseProps = input.readObject(); 160 | break; 161 | default: 162 | throw new Error('Unknown result flag, expect 0/1/2/3/4/5, get ' + rFlag); 163 | } 164 | } 165 | } else { 166 | const errorMsg = input.readUTF(); 167 | data = { 168 | appResponse: null, 169 | error: new Error(errorMsg), 170 | }; 171 | } 172 | return { 173 | packetId, 174 | packetType, 175 | data, 176 | options: { 177 | protocolType: 'dubbo', 178 | codecType: 'hessian2', 179 | classMap: options.classMap, 180 | }, 181 | meta: null, 182 | }; 183 | } 184 | } 185 | 186 | Response.OK = 20; 187 | Response.CLIENT_TIMEOUT = 30; 188 | Response.SERVER_TIMEOUT = 31; 189 | Response.BAD_REQUEST = 40; 190 | Response.BAD_RESPONSE = 50; 191 | Response.SERVICE_NOT_FOUND = 60; 192 | Response.SERVICE_ERROR = 70; 193 | Response.SERVER_ERROR = 80; 194 | Response.CLIENT_ERROR = 90; 195 | 196 | module.exports = Response; 197 | -------------------------------------------------------------------------------- /lib/serialize/hessian/compile.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | 'use strict'; 19 | 20 | const debug = require('debug')('hessian#compile'); 21 | const utils = require('./utils'); 22 | const has = require('utility').has; 23 | const codegen = require('@protobufjs/codegen'); 24 | 25 | const cache = new Map(); 26 | const typeMap = { 27 | bool: require('./primitive_type/boolean'), 28 | boolean: require('./primitive_type/boolean'), 29 | 'java.lang.Boolean': require('./primitive_type/java.lang.boolean'), 30 | int: require('./primitive_type/int'), 31 | 'java.lang.Integer': require('./primitive_type/java.lang.integer'), 32 | short: require('./primitive_type/int'), 33 | 'java.lang.Short': require('./primitive_type/java.lang.integer'), 34 | long: require('./primitive_type/long'), 35 | 'java.lang.Long': require('./primitive_type/java.lang.long'), 36 | double: require('./primitive_type/double'), 37 | 'java.lang.Double': require('./primitive_type/java.lang.double'), 38 | float: require('./primitive_type/double'), 39 | 'java.lang.Float': require('./primitive_type/java.lang.double'), 40 | byte: require('./primitive_type/int'), 41 | 'java.lang.Byte': require('./primitive_type/java.lang.integer'), 42 | char: require('./primitive_type/java.lang.string'), 43 | 'java.lang.Character': require('./primitive_type/java.lang.string'), 44 | 'java.lang.String': require('./primitive_type/java.lang.string'), 45 | 'java.util.Map': require('./primitive_type/java.util.map'), 46 | 'java.util.HashMap': require('./primitive_type/java.util.map'), 47 | 'java.util.List': require('./primitive_type/java.util.list'), 48 | 'java.util.Set': require('./primitive_type/java.util.list'), 49 | 'java.util.Collection': require('./primitive_type/java.util.list'), 50 | 'java.util.ArrayList': require('./primitive_type/java.util.arraylist'), 51 | 'java.util.Date': require('./primitive_type/java.util.date'), 52 | 'java.lang.Class': require('./primitive_type/java.lang.class'), 53 | 'java.util.Currency': require('./primitive_type/java.util.currency'), 54 | 'java.math.BigDecimal': require('./primitive_type/java.math.bigdecimal'), 55 | 'java.util.Locale': require('./primitive_type/java.util.locale'), 56 | 'java.lang.Exception': require('./primitive_type/java.lang.exception'), 57 | 'java.lang.StackTraceElement': require('./primitive_type/java.lang.stacktraceelement'), 58 | 'java.lang.Object': require('./primitive_type/java.lang.object'), 59 | customMap: require('./primitive_type/custom_map'), 60 | }; 61 | const arrayTypeMap = { 62 | 'java.util.Locale': 'com.caucho.hessian.io.LocaleHandle', 63 | }; 64 | const bufferType = { 65 | byte: true, 66 | 'java.lang.Byte': true, 67 | }; 68 | 69 | /** 70 | * 预编译 71 | * 72 | * @param {Object} info 73 | * - {String} $class - 类名 74 | * @param {String} version - hessian 版本:1.0, 2.0 75 | * @param {Object} classMap - 类型映射 76 | * @return {Function} serializeFn 77 | */ 78 | module.exports = (info, version, classMap) => { 79 | info.type = info.type || info.$class; 80 | const uniqueId = utils.normalizeUniqId(info, version); 81 | return compile(uniqueId, info, classMap, version); 82 | }; 83 | 84 | function compile(uniqueId, info, classMap, version) { 85 | let encodeFn = cache.get(uniqueId); 86 | if (encodeFn) return encodeFn; 87 | 88 | const type = info.type || info.$class; 89 | // 先获取 classInfo,因为 type 后面会变 90 | const classInfo = classMap && classMap[type]; 91 | 92 | const gen = codegen([ 'obj', 'encoder', 'appClassMap' ], 'encode'); 93 | // 默认值 94 | if (info.defaultValue) { 95 | gen('if (obj == null) { obj = %j; }', info.defaultValue); 96 | } 97 | if (info.isArray) { 98 | gen('if (obj == null) { return encoder.writeNull(); }'); 99 | const arrayDepth = info.arrayDepth || 1; 100 | if (bufferType[type] && arrayDepth === 1) { 101 | gen('encoder.writeBytes(obj);'); 102 | } else { 103 | let arrayType = arrayTypeMap[type] || type; 104 | for (let i = 0; i < arrayDepth; i++) arrayType = '[' + arrayType; 105 | 106 | gen('if (encoder._checkRef(obj)) { return; }'); 107 | gen('const hasEnd = encoder._writeListBegin(obj.length, \'%s\');', arrayType); 108 | 109 | const item = arrayDepth > 1 ? { 110 | type, 111 | arrayDepth: arrayDepth - 1, 112 | isMap: info.isMap, 113 | isEnum: info.isEnum, 114 | isArray: info.isArray, 115 | generic: info.generic, 116 | abstractClass: info.abstractClass, 117 | } : { 118 | type, 119 | isMap: info.isMap, 120 | isEnum: info.isEnum, 121 | generic: info.generic, 122 | abstractClass: info.abstractClass, 123 | }; 124 | const uniqueId = utils.normalizeUniqId(item, version); 125 | gen('for (const item of obj) {'); 126 | gen(' compile(\'%s\', %j, classMap, version)(item && item.$class ? item.$ : item, encoder, appClassMap);', uniqueId, item); 127 | gen('}'); 128 | gen('if (hasEnd) { encoder.byteBuffer.putChar(\'z\'); }'); 129 | } 130 | } else if (typeMap[type]) { 131 | typeMap[type](gen, info, version); 132 | } else if (info.isMap) { 133 | typeMap.customMap(gen, info, version); 134 | } else if (classInfo && !info.abstractClass && !info.$abstractClass) { 135 | gen('if (obj == null) { return encoder.writeNull(); }'); 136 | gen('if (obj && obj.$class) {'); 137 | gen(' const fnKey = utils.normalizeUniqId(obj, version);'); 138 | gen(' compile(fnKey, obj, classMap, version)(obj.$, encoder, appClassMap);'); 139 | gen(' return;'); 140 | gen('}'); 141 | gen('if (encoder._checkRef(obj)) { return; }'); 142 | 143 | const keys = classInfo ? Object.keys(classInfo).filter(key => { 144 | const attr = classInfo[key]; 145 | return !attr.isStatic && !attr.isTransient; 146 | }) : []; 147 | 148 | if (version === '1.0') { 149 | gen('encoder.byteBuffer.put(0x4d);'); 150 | gen('encoder.writeType(\'%s\');', type); 151 | for (const key of keys) { 152 | gen('encoder.writeString(\'%s\');', key); 153 | const attr = Object.assign({}, classInfo[key]); 154 | if (has(attr, 'typeAliasIndex') && Array.isArray(info.generic)) { 155 | attr.type = info.generic[attr.typeAliasIndex].type; 156 | if (info.generic[attr.typeAliasIndex].generic) { 157 | attr.generic = info.generic[attr.typeAliasIndex].generic; 158 | } 159 | } 160 | const uniqueId = utils.normalizeUniqId(attr, version); 161 | gen('compile(\'%s\', %j, classMap, version)(obj[\'%s\'], encoder, appClassMap);', uniqueId, attr, key); 162 | } 163 | gen('encoder.byteBuffer.put(0x7a);'); 164 | } else { 165 | gen('const ref = encoder._writeObjectBegin(\'%s\');', type); 166 | gen('if (ref === -1) {'); 167 | gen('encoder.writeInt(%d);', keys.length); 168 | for (const key of keys) { 169 | gen('encoder.writeString(\'%s\');', key); 170 | } 171 | gen('encoder._writeObjectBegin(\'%s\'); }', type); 172 | 173 | for (const key of keys) { 174 | const attr = Object.assign({}, classInfo[key]); 175 | if (has(attr, 'typeAliasIndex') && Array.isArray(info.generic)) { 176 | attr.type = info.generic[attr.typeAliasIndex].type; 177 | if (info.generic[attr.typeAliasIndex].generic) { 178 | attr.generic = info.generic[attr.typeAliasIndex].generic; 179 | } 180 | } 181 | const uniqueId = utils.normalizeUniqId(attr, version); 182 | gen('compile(\'%s\', %j, classMap, version)(obj[\'%s\'], encoder, appClassMap);', uniqueId, attr, key); 183 | } 184 | } 185 | } else if (info.isEnum) { 186 | gen('if (obj == null) { return encoder.writeNull(); }'); 187 | 188 | if (version === '1.0') { 189 | gen('encoder.byteBuffer.put(0x4d);'); 190 | gen('encoder.writeType(\'%s\');', type); 191 | gen('encoder.writeString(\'name\');'); 192 | gen('encoder.writeString(typeof obj.name === \'string\' ? obj.name : obj);'); 193 | gen('encoder.byteBuffer.put(0x7a);'); 194 | } else { 195 | gen('const ref = encoder._writeObjectBegin(\'%s\');', type); 196 | gen('if (ref === -1) {'); 197 | gen('encoder.writeInt(1);'); 198 | gen('encoder.writeString(\'name\');'); 199 | gen('encoder._writeObjectBegin(\'%s\'); }', type); 200 | gen('encoder.writeString(typeof obj.name === \'string\' ? obj.name : obj);'); 201 | } 202 | } else { 203 | gen('if (obj == null) { return encoder.writeNull(); }'); 204 | gen('if (obj && obj.$class) {'); 205 | gen(' const fnKey = utils.normalizeUniqId(obj, version);'); 206 | gen(' compile(fnKey, obj, classMap, version)(obj.$, encoder, appClassMap);'); 207 | gen('}'); 208 | gen('else { encoder.write({ $class: \'%s\', $: obj }); }', type); 209 | } 210 | encodeFn = gen({ compile, classMap, version, utils }); 211 | debug('gen encodeFn for [%s] => %s\n', uniqueId, '\n' + encodeFn.toString()); 212 | cache.set(uniqueId, encodeFn); 213 | return encodeFn; 214 | } 215 | -------------------------------------------------------------------------------- /lib/serialize/hessian/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | 'use strict'; 19 | 20 | const hessian = require('hessian.js'); 21 | const compile = require('./compile'); 22 | const encoder = hessian.encoderV2; 23 | const decoder = new hessian.DecoderV2(); 24 | 25 | const input = { 26 | skip(n) { 27 | decoder.byteBuffer.skip(n); 28 | }, 29 | readBool() { 30 | return decoder.readBool(); 31 | }, 32 | readByte() { 33 | return decoder.readInt(); 34 | }, 35 | readShort() { 36 | return decoder.readInt(); 37 | }, 38 | readInt() { 39 | return decoder.readInt(); 40 | }, 41 | readLong() { 42 | return decoder.readLong(); 43 | }, 44 | readFloat() { 45 | return decoder.readDouble(); 46 | }, 47 | readDouble() { 48 | return decoder.readDouble(); 49 | }, 50 | readBytes() { 51 | return decoder.readBytes(); 52 | }, 53 | readUTF() { 54 | return decoder.readString(); 55 | }, 56 | readObject() { 57 | return decoder.read(); 58 | }, 59 | }; 60 | const output = { 61 | get bytes() { 62 | return encoder.byteBuffer; 63 | }, 64 | get contentTypeId() { 65 | return 2; 66 | }, 67 | get() { 68 | return encoder.get(); 69 | }, 70 | writeBool(v) { 71 | return encoder.writeBool(v); 72 | }, 73 | writeByte(v) { 74 | return encoder.writeInt(v); 75 | }, 76 | writeShort(v) { 77 | return encoder.writeInt(v); 78 | }, 79 | writeInt(v) { 80 | return encoder.writeInt(v); 81 | }, 82 | writeLong(v) { 83 | return encoder.writeLong(v); 84 | }, 85 | writeFloat(v) { 86 | return encoder.writeDouble(v); 87 | }, 88 | writeDouble(v) { 89 | return encoder.writeDouble(v); 90 | }, 91 | writeBytes(v) { 92 | return encoder.writeBytes(v); 93 | }, 94 | writeUTF(v) { 95 | return encoder.writeString(v); 96 | }, 97 | writeObject(v, classMap) { 98 | if (v && v.$class) { 99 | compile(v, '2.0', classMap)(v.$, encoder); 100 | } else { 101 | encoder.write(v); 102 | } 103 | }, 104 | }; 105 | 106 | exports.serialize = () => { 107 | encoder.reset(); 108 | return output; 109 | }; 110 | exports.deserialize = buf => { 111 | decoder.clean(); 112 | decoder.init(buf); 113 | return input; 114 | }; 115 | -------------------------------------------------------------------------------- /lib/serialize/hessian/primitive_type/boolean.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | 'use strict'; 19 | 20 | module.exports = gen => { 21 | gen('if (obj == null) { obj = false; }'); 22 | gen('encoder.writeBool(obj);'); 23 | }; 24 | -------------------------------------------------------------------------------- /lib/serialize/hessian/primitive_type/custom_map.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | 'use strict'; 19 | 20 | const utils = require('../utils'); 21 | 22 | module.exports = (gen, classInfo, version) => { 23 | gen('if (obj == null) { return encoder.writeNull(); }'); 24 | gen('if (encoder._checkRef(obj)) { return; }'); 25 | 26 | const generic = classInfo.generic; 27 | 28 | if (version === '1.0') { 29 | gen('encoder.byteBuffer.put(0x4d);'); 30 | gen('encoder.writeType(\'%s\');', classInfo.type); 31 | if (generic && generic.length === 2) { 32 | gen('if (obj instanceof Map) {'); 33 | gen(' for (const entry of obj.entries()) {'); 34 | gen(' const key = entry[0];'); 35 | gen(' const value = entry[1];'); 36 | const genericKeyDefine = utils.normalizeType(generic[0]); 37 | const genericValueDefine = utils.normalizeType(generic[1]); 38 | const keyId = utils.normalizeUniqId(genericKeyDefine, version); 39 | const valueId = utils.normalizeUniqId(genericValueDefine, version); 40 | gen(' const encodeKey = compile(\'%s\', %j, classMap, version); encodeKey(key, encoder, appClassMap);', keyId, genericKeyDefine); 41 | gen(' const encodeValue = compile(\'%s\', %j, classMap, version); encodeValue(value, encoder, appClassMap);', valueId, genericValueDefine); 42 | gen(' }\n } else {'); 43 | gen(' for (const key in obj) {'); 44 | gen(' const value = obj[key];'); 45 | const convertor = utils.converts[genericKeyDefine.type]; 46 | if (convertor) { 47 | gen(' const encodeKey = compile(\'%s\', %j, classMap, version); encodeKey(%s(key), encoder, appClassMap);', keyId, genericKeyDefine, convertor); 48 | } else { 49 | gen(' const encodeKey = compile(\'%s\', %j, classMap, version); encodeKey(key, encoder, appClassMap);', keyId, genericKeyDefine); 50 | } 51 | gen(' const encodeValue = compile(\'%s\', %j, classMap, version); encodeValue(value, encoder, appClassMap);', valueId, genericValueDefine); 52 | gen(' }\n }'); 53 | } else { 54 | gen('if (obj instanceof Map) {'); 55 | gen(' for (const entry of obj.entries()) {'); 56 | gen(' const key = entry[0];'); 57 | gen(' const value = entry[1];'); 58 | gen(' encoder.writeString(key);'); 59 | gen(' if (value && value.$class) {'); 60 | gen(' const fnKey = utils.normalizeUniqId(value, version);'); 61 | gen(' compile(fnKey, value, appClassMap, version)(value.$, encoder);'); 62 | gen(' } else {'); 63 | gen(' encoder.write(value);'); 64 | gen(' }'); 65 | gen(' }\n } else {'); 66 | gen(' for (const key in obj) {'); 67 | gen(' const value = obj[key];'); 68 | gen(' encoder.writeString(key);'); 69 | gen(' if (value && value.$class) {'); 70 | gen(' const fnKey = utils.normalizeUniqId(value, version);'); 71 | gen(' compile(fnKey, value, appClassMap, version)(value.$, encoder);'); 72 | gen(' } else {'); 73 | gen(' encoder.write(value);'); 74 | gen(' }'); 75 | gen(' }\n }'); 76 | } 77 | gen('encoder.byteBuffer.put(0x7a);'); 78 | } else { 79 | gen('const keys = obj instanceof Map ? Array.from(obj.keys()) : Object.keys(obj)'); 80 | gen('const ref = encoder._writeObjectBegin(\'%s\', keys);', classInfo.type); 81 | if (generic && generic.length === 2) { 82 | const genericKeyDefine = utils.normalizeType(generic[0]); 83 | const genericValueDefine = utils.normalizeType(generic[1]); 84 | const keyId = utils.normalizeUniqId(genericKeyDefine, version); 85 | const valueId = utils.normalizeUniqId(genericValueDefine, version); 86 | gen('if (obj instanceof Map) {'); 87 | gen(' if (ref === -1) {'); 88 | gen(' encoder.writeInt(keys.length);'); 89 | gen(' for (const key of keys) {'); 90 | gen(' const encodeKey = compile(\'%s\', %j, classMap, version); encodeKey(key, encoder, appClassMap);', keyId, genericKeyDefine); 91 | gen(' }'); 92 | gen(' encoder._writeObjectBegin(\'%s\');', classInfo.type); 93 | gen(' }'); 94 | gen(' for (const value of obj.values()) {'); 95 | gen(' const encodeValue = compile(\'%s\', %j, classMap, version); encodeValue(value, encoder, appClassMap);', valueId, genericValueDefine); 96 | gen(' }'); 97 | gen('} else {'); 98 | gen(' if (ref === -1) {'); 99 | gen(' encoder.writeInt(keys.length);'); 100 | gen(' for (const key of keys) {'); 101 | const convertor = utils.converts[genericKeyDefine.type]; 102 | if (convertor) { 103 | gen(' const encodeKey = compile(\'%s\', %j, classMap, version); encodeKey(%s(key), encoder, appClassMap);', keyId, genericKeyDefine, convertor); 104 | } else { 105 | gen(' const encodeKey = compile(\'%s\', %j, classMap, version); encodeKey(key, encoder, appClassMap);', keyId, genericKeyDefine); 106 | } 107 | gen(' encoder._writeObjectBegin(\'%s\');', classInfo.type); 108 | gen(' }'); 109 | gen(' }'); 110 | gen(' for (const key of keys) {'); 111 | gen(' const value = obj[key];'); 112 | gen(' const encodeValue = compile(\'%s\', %j, classMap, version); encodeValue(value, encoder, appClassMap);', valueId, genericValueDefine); 113 | gen(' }'); 114 | gen('}'); 115 | } else { 116 | gen('if (obj instanceof Map) {'); 117 | gen(' if (ref === -1) {'); 118 | gen(' encoder.writeInt(keys.length);'); 119 | gen(' for (const key of keys) {'); 120 | gen(' encoder.writeString(key);'); 121 | gen(' }'); 122 | gen(' encoder._writeObjectBegin(\'%s\');', classInfo.type); 123 | gen(' }'); 124 | gen(' for (const value of obj.values()) {'); 125 | gen(' if (value && value.$class) {'); 126 | gen(' const fnKey = utils.normalizeUniqId(value, version);'); 127 | gen(' compile(fnKey, value, appClassMap, version)(value.$, encoder);'); 128 | gen(' } else {'); 129 | gen(' encoder.write(value);'); 130 | gen(' }'); 131 | gen(' }'); 132 | gen('} else {'); 133 | gen(' if (ref === -1) {'); 134 | gen(' encoder.writeInt(keys.length);'); 135 | gen(' for (const key of keys) {'); 136 | gen(' encoder.writeString(key);'); 137 | gen(' }'); 138 | gen(' encoder._writeObjectBegin(\'%s\');', classInfo.type); 139 | gen(' }'); 140 | gen(' for (const key of keys) {'); 141 | gen(' const value = obj[key];'); 142 | gen(' if (value && value.$class) {'); 143 | gen(' const fnKey = utils.normalizeUniqId(value, version);'); 144 | gen(' compile(fnKey, value, appClassMap, version)(value.$, encoder);'); 145 | gen(' } else {'); 146 | gen(' encoder.write(value);'); 147 | gen(' }'); 148 | gen(' }'); 149 | gen('}'); 150 | } 151 | } 152 | }; 153 | -------------------------------------------------------------------------------- /lib/serialize/hessian/primitive_type/double.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | 'use strict'; 19 | 20 | module.exports = gen => { 21 | gen('if (obj == null) { obj = 0; }'); 22 | gen('encoder.writeDouble(obj);'); 23 | }; 24 | -------------------------------------------------------------------------------- /lib/serialize/hessian/primitive_type/int.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | 'use strict'; 19 | 20 | module.exports = gen => { 21 | gen('if (obj == null) { obj = 0; }'); 22 | gen('encoder.writeInt(obj);'); 23 | }; 24 | -------------------------------------------------------------------------------- /lib/serialize/hessian/primitive_type/java.lang.boolean.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | 'use strict'; 19 | 20 | module.exports = gen => { 21 | gen('if (obj == null) { return encoder.writeNull(); }'); 22 | gen('encoder.writeBool(obj);'); 23 | }; 24 | -------------------------------------------------------------------------------- /lib/serialize/hessian/primitive_type/java.lang.class.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | 'use strict'; 19 | 20 | module.exports = (gen, classInfo, version) => { 21 | gen('if (obj == null) { return encoder.writeNull(); }'); 22 | gen('if (encoder._checkRef(obj)) { return; }'); 23 | 24 | gen('if (typeof obj === \'string\') {'); 25 | gen(' obj = obj.indexOf(\'[\') !== -1 ? (\'[L\' + obj.replace(/(\\[L)|(\\[)|;/g, \'\') + \';\') : obj'); 26 | gen('}'); 27 | 28 | gen('if (typeof obj === \'object\') {'); 29 | gen(' obj = obj.name'); 30 | gen('}'); 31 | 32 | if (version === '1.0') { 33 | gen('encoder.byteBuffer.put(0x4d);'); 34 | gen('encoder.writeType(\'%s\');', classInfo.type); 35 | gen('encoder.writeString(\'name\');'); 36 | gen('encoder.writeString(obj);'); 37 | gen('encoder.byteBuffer.put(0x7a);'); 38 | } else { 39 | gen('const ref = encoder._writeObjectBegin(\'%s\');', classInfo.type); 40 | gen('if (ref === -1) {'); 41 | gen('encoder.writeInt(1);'); 42 | gen('encoder.writeString(\'name\');'); 43 | gen('encoder._writeObjectBegin(\'%s\'); }', classInfo.type); 44 | gen('encoder.writeString(obj);'); 45 | } 46 | }; 47 | -------------------------------------------------------------------------------- /lib/serialize/hessian/primitive_type/java.lang.double.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | 'use strict'; 19 | 20 | module.exports = gen => { 21 | gen('if (obj == null) { return encoder.writeNull(); }'); 22 | gen('encoder.writeDouble(obj);'); 23 | }; 24 | -------------------------------------------------------------------------------- /lib/serialize/hessian/primitive_type/java.lang.exception.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | 'use strict'; 19 | 20 | const utils = require('../utils'); 21 | 22 | module.exports = (gen, classInfo, version) => { 23 | gen('if (obj == null) { return encoder.writeNull(); }'); 24 | 25 | gen('if (encoder._checkRef(obj)) { return; }'); 26 | 27 | classInfo = { 28 | detailMessage: { 29 | type: 'java.lang.String', 30 | }, 31 | stackTrace: { 32 | type: 'java.lang.StackTraceElement', 33 | isArray: true, 34 | }, 35 | cause: { 36 | type: 'java.lang.Throwable', 37 | }, 38 | }; 39 | const keys = [ 'detailMessage', 'stackTrace', 'cause' ]; 40 | gen('if (obj instanceof Error) {'); 41 | gen(' const stackTraceElements = [];'); 42 | gen(' const lines = obj.stack.match(/ at .+$/gm);'); 43 | gen(' if (lines) {'); 44 | gen(' for (var line of lines) {'); 45 | gen(' const splits = line.replace(\' at \', \'\').split(\'(\');'); 46 | gen(' if (splits.length < 2) {'); 47 | gen(' splits.push(splits[0]);'); 48 | gen(' splits[0] = \'.\';'); 49 | gen(' }'); 50 | gen(' const declaring = splits[0];'); 51 | gen(' const lastIndexDot = declaring.lastIndexOf(\'.\');'); 52 | gen(' const declaringClass = declaring.substring(0, lastIndexDot) || \'\';'); 53 | gen(' const methodName = declaring.substring(lastIndexDot + 1).trim();'); 54 | gen(' const fileSplits = splits[1].split(\':\');'); 55 | gen(' const fileName = fileSplits[0].replace(\')\', \'\');'); 56 | gen(' const lineNumber = parseInt(fileSplits[1]) || 0;'); 57 | gen(' stackTraceElements.push({ declaringClass, methodName, fileName, lineNumber });'); 58 | gen(' }'); 59 | gen(' }'); 60 | gen(' obj = { detailMessage: obj.name + \': \' + obj.message, stackTrace: stackTraceElements }'); 61 | gen('} else {'); 62 | gen(' return encoder.write({ $class: \'java.lang.Exception\', $: obj });'); 63 | gen('}'); 64 | 65 | if (version === '1.0') { 66 | gen('encoder.byteBuffer.put(0x4d);'); 67 | gen('encoder.writeType(\'java.lang.Exception\');'); 68 | for (const key of keys) { 69 | gen('encoder.writeString(\'%s\');', key); 70 | const attr = classInfo[key]; 71 | const uniqueId = utils.normalizeUniqId(attr, version); 72 | gen('compile(\'%s\', %j, classMap, version)(obj[\'%s\'], encoder);', uniqueId, attr, key); 73 | } 74 | gen('encoder.byteBuffer.put(0x7a);'); 75 | } else { 76 | gen('const ref = encoder._writeObjectBegin(\'java.lang.Exception\');'); 77 | gen('if (ref === -1) {'); 78 | gen('encoder.writeInt(%d);', keys.length); 79 | for (const key of keys) { 80 | gen('encoder.writeString(\'%s\');', key); 81 | } 82 | gen('encoder._writeObjectBegin(\'java.lang.Exception\'); }'); 83 | 84 | for (const key of keys) { 85 | const attr = classInfo[key]; 86 | const uniqueId = utils.normalizeUniqId(attr, version); 87 | gen('compile(\'%s\', %j, classMap, version)(obj[\'%s\'], encoder);', uniqueId, attr, key); 88 | } 89 | } 90 | }; 91 | -------------------------------------------------------------------------------- /lib/serialize/hessian/primitive_type/java.lang.integer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | 'use strict'; 19 | 20 | module.exports = gen => { 21 | gen('if (obj == null) { return encoder.writeNull(); }'); 22 | gen('encoder.writeInt(obj);'); 23 | }; 24 | -------------------------------------------------------------------------------- /lib/serialize/hessian/primitive_type/java.lang.long.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | 'use strict'; 19 | 20 | module.exports = gen => { 21 | gen('if (obj == null) { return encoder.writeNull(); }'); 22 | gen('encoder.writeLong(obj);'); 23 | }; 24 | -------------------------------------------------------------------------------- /lib/serialize/hessian/primitive_type/java.lang.object.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | 'use strict'; 19 | 20 | module.exports = gen => { 21 | gen('if (obj == null) { return encoder.writeNull(); }'); 22 | gen('if (obj && obj.$class) {'); 23 | gen(' const fnKey = utils.normalizeUniqId(obj, version);'); 24 | gen(' compile(fnKey, obj, appClassMap, version)(obj.$, encoder);'); 25 | gen('} else {'); 26 | gen(' encoder.write(obj);'); 27 | gen('}'); 28 | }; 29 | -------------------------------------------------------------------------------- /lib/serialize/hessian/primitive_type/java.lang.stacktraceelement.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | 'use strict'; 19 | 20 | const utils = require('../utils'); 21 | 22 | module.exports = (gen, classInfo, version) => { 23 | gen('if (obj == null) { return encoder.writeNull(); }'); 24 | gen('if (encoder._checkRef(obj)) { return; }'); 25 | 26 | classInfo = { 27 | declaringClass: { 28 | type: 'java.lang.String', 29 | }, 30 | methodName: { 31 | type: 'java.lang.String', 32 | }, 33 | fileName: { 34 | type: 'java.lang.String', 35 | }, 36 | lineNumber: { 37 | type: 'int', 38 | }, 39 | }; 40 | const keys = [ 'declaringClass', 'methodName', 'fileName', 'lineNumber' ]; 41 | 42 | if (version === '1.0') { 43 | gen('encoder.byteBuffer.put(0x4d);'); 44 | gen('encoder.writeType(\'java.lang.StackTraceElement\');'); 45 | for (const key of keys) { 46 | gen('encoder.writeString(\'%s\');', key); 47 | const attr = classInfo[key]; 48 | const uniqueId = utils.normalizeUniqId(attr, version); 49 | gen('compile(\'%s\', %j, classMap, version)(obj[\'%s\'], encoder);', uniqueId, attr, key); 50 | } 51 | gen('encoder.byteBuffer.put(0x7a);'); 52 | } else { 53 | gen('const ref = encoder._writeObjectBegin(\'java.lang.StackTraceElement\');'); 54 | gen('if (ref === -1) {'); 55 | gen('encoder.writeInt(%d);', keys.length); 56 | for (const key of keys) { 57 | gen('encoder.writeString(\'%s\');', key); 58 | } 59 | gen('encoder._writeObjectBegin(\'java.lang.StackTraceElement\'); }'); 60 | 61 | for (const key of keys) { 62 | const attr = classInfo[key]; 63 | const uniqueId = utils.normalizeUniqId(attr, version); 64 | gen('compile(\'%s\', %j, classMap, version)(obj[\'%s\'], encoder);', uniqueId, attr, key); 65 | } 66 | } 67 | }; 68 | -------------------------------------------------------------------------------- /lib/serialize/hessian/primitive_type/java.lang.string.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | 'use strict'; 19 | 20 | module.exports = gen => { 21 | gen('if (obj == null) { return encoder.writeNull(); }'); 22 | gen('if (typeof obj === \'number\') { obj = obj.toString(); }'); 23 | gen('encoder.writeString(obj);'); 24 | }; 25 | -------------------------------------------------------------------------------- /lib/serialize/hessian/primitive_type/java.math.bigdecimal.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | 'use strict'; 19 | 20 | module.exports = (gen, classInfo, version) => { 21 | gen('if (obj == null) { return encoder.writeNull(); }'); 22 | gen('if (encoder._checkRef(obj)) { return; }'); 23 | 24 | gen('if (typeof obj === \'object\') {'); 25 | gen(' obj = obj.value || 0'); 26 | gen('}'); 27 | 28 | if (version === '1.0') { 29 | gen('encoder.byteBuffer.put(0x4d);'); 30 | gen('encoder.writeType(\'%s\');', classInfo.type); 31 | gen('encoder.writeString(\'value\');'); 32 | gen('encoder.writeString(String(obj));'); 33 | gen('encoder.byteBuffer.put(0x7a);'); 34 | } else { 35 | gen('const ref = encoder._writeObjectBegin(\'%s\');', classInfo.type); 36 | gen('if (ref === -1) {'); 37 | gen('encoder.writeInt(1);'); 38 | gen('encoder.writeString(\'value\');'); 39 | gen('encoder._writeObjectBegin(\'%s\'); }', classInfo.type); 40 | gen('encoder.writeString(String(obj));'); 41 | } 42 | }; 43 | -------------------------------------------------------------------------------- /lib/serialize/hessian/primitive_type/java.util.arraylist.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | 'use strict'; 19 | 20 | const utils = require('../utils'); 21 | 22 | module.exports = (gen, classInfo, version) => { 23 | gen('if (obj == null) { return encoder.writeNull(); }'); 24 | gen('if (encoder._checkRef(obj)) { return; }'); 25 | 26 | gen('const hasEnd = encoder._writeListBegin(obj.length, \'\');'); 27 | 28 | const generic = classInfo.generic; 29 | if (generic && generic.length === 1) { 30 | gen('for (const item of obj) {'); 31 | const genericDefine = utils.normalizeType(generic[0]); 32 | const uniqueId = utils.normalizeUniqId(genericDefine, version); 33 | gen(' let desc;'); 34 | gen(' if (item && item.$class) {'); 35 | gen(' desc = item;'); 36 | gen(' } else {'); 37 | gen(' desc = %j;', genericDefine); 38 | gen(' }'); 39 | gen(' compile(\'%s\', desc, classMap, version)(item, encoder, appClassMap);', uniqueId); 40 | gen('}'); 41 | } else { 42 | gen('for (const item of obj) { encoder.write(item); }'); 43 | } 44 | gen('if (hasEnd) { encoder.byteBuffer.putChar(\'z\'); }'); 45 | }; 46 | -------------------------------------------------------------------------------- /lib/serialize/hessian/primitive_type/java.util.currency.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | 'use strict'; 19 | 20 | module.exports = (gen, classInfo, version) => { 21 | gen('if (obj == null) { return encoder.writeNull(); }'); 22 | gen('if (encoder._checkRef(obj)) { return; }'); 23 | 24 | gen('if (typeof obj === \'object\' && obj.currencyCode) {'); 25 | gen(' obj = obj.currencyCode'); 26 | gen('}'); 27 | 28 | if (version === '1.0') { 29 | gen('encoder.byteBuffer.put(0x4d);'); 30 | gen('encoder.writeType(\'%s\');', classInfo.type); 31 | gen('encoder.writeString(\'currencyCode\');'); 32 | gen('encoder.writeString(obj);'); 33 | gen('encoder.byteBuffer.put(0x7a);'); 34 | } else { 35 | gen('const ref = encoder._writeObjectBegin(\'%s\');', classInfo.type); 36 | gen('if (ref === -1) {'); 37 | gen(' encoder.writeInt(1);'); 38 | gen(' encoder.writeString(\'currencyCode\');'); 39 | gen(' encoder._writeObjectBegin(\'%s\');\n}', classInfo.type); 40 | gen('encoder.writeString(obj);'); 41 | } 42 | }; 43 | -------------------------------------------------------------------------------- /lib/serialize/hessian/primitive_type/java.util.date.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | 'use strict'; 19 | 20 | module.exports = gen => { 21 | gen('if (obj == null) { return encoder.writeNull(); }'); 22 | gen('if (typeof obj === \'string\') { obj = new Date(obj); }'); 23 | gen('encoder.writeDate(obj);'); 24 | }; 25 | -------------------------------------------------------------------------------- /lib/serialize/hessian/primitive_type/java.util.list.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | 'use strict'; 19 | 20 | const utils = require('../utils'); 21 | 22 | module.exports = (gen, classInfo, version) => { 23 | gen('if (obj == null) { return encoder.writeNull(); }'); 24 | gen('if (encoder._checkRef(obj)) { return; }'); 25 | 26 | gen('const hasEnd = encoder._writeListBegin(obj.length, \'%s\');', classInfo.type); 27 | 28 | const generic = classInfo.generic; 29 | if (generic && generic.length === 1) { 30 | gen('for (const item of obj) {'); 31 | const genericDefine = utils.normalizeType(generic[0]); 32 | const uniqueId = utils.normalizeUniqId(genericDefine, version); 33 | gen(' let desc;'); 34 | gen(' let val = item;'); 35 | gen(' let uniqueId = \'%s\'', uniqueId); 36 | gen(' if (item && item.$class) {'); 37 | gen(' desc = item;'); 38 | gen(' const type = item.$class;'); 39 | gen(' if (item.isArray) {'); 40 | gen(' let arrayDepth = item.arrayDepth || 1;'); 41 | gen(' while (arrayDepth--) type = \'[\' + type;'); 42 | gen(' }'); 43 | gen(' uniqueId = type;'); 44 | gen(' if (item.generic) {'); 45 | gen(' for (const it of item.generic) {'); 46 | gen(' uniqueId += (\'#\' + it.type);'); 47 | gen(' }'); 48 | gen(' }'); 49 | gen(' uniqueId += \'#\' + version;'); 50 | gen(' val = item.$;'); 51 | gen(' } else {'); 52 | gen(' desc = %j;', genericDefine); 53 | gen(' }'); 54 | gen(' compile(uniqueId, desc, classMap, version)(val, encoder, appClassMap);'); 55 | gen('}'); 56 | } else { 57 | gen('for (const item of obj) { encoder.write(item); }'); 58 | } 59 | gen('if (hasEnd) { encoder.byteBuffer.putChar(\'z\'); }'); 60 | }; 61 | -------------------------------------------------------------------------------- /lib/serialize/hessian/primitive_type/java.util.locale.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | 'use strict'; 19 | 20 | module.exports = (gen, classInfo, version) => { 21 | gen('if (obj == null) { return encoder.writeNull(); }'); 22 | gen('if (encoder._checkRef(obj)) { return; }'); 23 | 24 | gen('if (typeof obj === \'object\') {'); 25 | gen(' obj = obj.value'); 26 | gen('}'); 27 | 28 | if (version === '1.0') { 29 | gen('encoder.byteBuffer.put(0x4d);'); 30 | gen('encoder.writeType(\'com.caucho.hessian.io.LocaleHandle\');'); 31 | gen('encoder.writeString(\'value\');'); 32 | gen('encoder.writeString(obj);'); 33 | gen('encoder.byteBuffer.put(0x7a);'); 34 | } else { 35 | gen('const ref = encoder._writeObjectBegin(\'com.caucho.hessian.io.LocaleHandle\');'); 36 | gen('if (ref === -1) {'); 37 | gen('encoder.writeInt(1);'); 38 | gen('encoder.writeString(\'value\');'); 39 | gen('encoder._writeObjectBegin(\'com.caucho.hessian.io.LocaleHandle\'); }'); 40 | gen('encoder.writeString(obj);'); 41 | } 42 | }; 43 | -------------------------------------------------------------------------------- /lib/serialize/hessian/primitive_type/java.util.map.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | 'use strict'; 19 | 20 | const utils = require('../utils'); 21 | 22 | module.exports = (gen, classInfo, version) => { 23 | gen('if (obj == null) { return encoder.writeNull(); }'); 24 | gen('if (encoder._checkRef(obj)) { return; }'); 25 | 26 | if (version === '1.0') { 27 | gen('encoder.byteBuffer.put(0x4d);'); 28 | } else { 29 | gen('encoder.byteBuffer.put(0x48);'); 30 | } 31 | if (classInfo.type === 'java.util.HashMap' || classInfo.type === 'java.util.Map') { 32 | gen('encoder.writeType(\'\');'); 33 | } else { 34 | gen('encoder.writeType(\'%s\');', classInfo.type); 35 | } 36 | 37 | const generic = classInfo.generic; 38 | if (generic && generic.length === 2) { 39 | gen('if (obj instanceof Map) {'); 40 | gen(' for (const entry of obj.entries()) {'); 41 | gen(' const key = entry[0];'); 42 | gen(' const value = entry[1];'); 43 | const genericKeyDefine = utils.normalizeType(generic[0]); 44 | const genericValueDefine = utils.normalizeType(generic[1]); 45 | const keyId = utils.normalizeUniqId(genericKeyDefine, version); 46 | const valueId = utils.normalizeUniqId(genericValueDefine, version); 47 | gen(' const encodeKey = compile(\'%s\', %j, classMap, version); encodeKey(key, encoder, appClassMap);', keyId, genericKeyDefine); 48 | gen(' const encodeValue = compile(\'%s\', %j, classMap, version); encodeValue(value, encoder, appClassMap);', valueId, genericValueDefine); 49 | gen(' }\n } else {'); 50 | gen(' for (const key in obj) {'); 51 | gen(' const value = obj[key];'); 52 | const convertor = utils.converts[genericKeyDefine.type]; 53 | if (convertor) { 54 | gen(' const encodeKey = compile(\'%s\', %j, classMap, version); encodeKey(%s(key), encoder, appClassMap);', keyId, genericKeyDefine, convertor); 55 | } else { 56 | gen(' const encodeKey = compile(\'%s\', %j, classMap, version); encodeKey(key, encoder, appClassMap);', keyId, genericKeyDefine); 57 | } 58 | gen(' const encodeValue = compile(\'%s\', %j, classMap, version); encodeValue(value, encoder, appClassMap);', valueId, genericValueDefine); 59 | gen(' }\n }'); 60 | } else { 61 | gen('if (obj instanceof Map) {'); 62 | gen(' for (const entry of obj.entries()) {'); 63 | gen(' const key = entry[0];'); 64 | gen(' const value = entry[1];'); 65 | gen(' encoder.writeString(key);'); 66 | gen(' if (value && value.$class) {'); 67 | gen(' const fnKey = utils.normalizeUniqId(value, version);'); 68 | gen(' compile(fnKey, value, appClassMap, version)(value.$, encoder);'); 69 | gen(' } else {'); 70 | gen(' encoder.write(value);'); 71 | gen(' }'); 72 | gen(' }\n } else {'); 73 | gen(' for (const key in obj) {'); 74 | gen(' const value = obj[key];'); 75 | gen(' encoder.writeString(key);'); 76 | gen(' if (value && value.$class) {'); 77 | gen(' const fnKey = utils.normalizeUniqId(value, version);'); 78 | gen(' compile(fnKey, value, appClassMap, version)(value.$, encoder);'); 79 | gen(' } else {'); 80 | gen(' encoder.write(value);'); 81 | gen(' }'); 82 | gen(' }\n }'); 83 | } 84 | if (version === '1.0') { 85 | gen('encoder.byteBuffer.put(0x7a);'); 86 | } else { 87 | gen('encoder.byteBuffer.put(0x5a);'); 88 | } 89 | }; 90 | -------------------------------------------------------------------------------- /lib/serialize/hessian/primitive_type/long.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | 'use strict'; 19 | 20 | module.exports = gen => { 21 | gen('if (obj == null) { obj = 0; }'); 22 | gen('encoder.writeLong(obj);'); 23 | }; 24 | -------------------------------------------------------------------------------- /lib/serialize/hessian/utils.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | 'use strict'; 19 | 20 | const defaultValueMap = new Map(); 21 | let defaultValueId = 0; 22 | 23 | function normalizeGeneric(type) { 24 | if (!type.generic) return ''; 25 | let str = ''; 26 | for (const item of type.generic) { 27 | str += ('#' + item.type + normalizeGeneric(item)); 28 | } 29 | return str; 30 | } 31 | 32 | function normalizeUniqId(info, version) { 33 | let type = info.type || info.$class || info.$abstractClass; 34 | if (info.isArray) { 35 | let arrayDepth = info.arrayDepth || 1; 36 | while (arrayDepth--) type = '[' + type; 37 | } 38 | let fnKey = type; 39 | fnKey += normalizeGeneric(info); 40 | if (info.defaultValue) { 41 | if (!defaultValueMap.has(info.defaultValue)) { 42 | defaultValueMap.set(info.defaultValue, defaultValueId++); 43 | } 44 | fnKey += '#' + defaultValueId; 45 | } 46 | fnKey += '#' + version; 47 | return fnKey; 48 | } 49 | 50 | exports.normalizeUniqId = normalizeUniqId; 51 | 52 | const converts = { 53 | 'java.lang.Boolean': 'Boolean', 54 | boolean: 'Boolean', 55 | 'java.lang.Integer': 'Number', 56 | int: 'Number', 57 | 'java.lang.Short': 'Number', 58 | short: 'Number', 59 | 'java.lang.Double': 'Number', 60 | double: 'Number', 61 | 'java.lang.Float': 'Number', 62 | float: 'Number', 63 | }; 64 | 65 | exports.converts = converts; 66 | 67 | exports.normalizeType = type => { 68 | if (typeof type === 'string') { 69 | return { type }; 70 | } 71 | return type; 72 | }; 73 | -------------------------------------------------------------------------------- /lib/serialize/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | 'use strict'; 19 | 20 | const idToSerializations = { 21 | 2: require('./hessian'), 22 | }; 23 | const nameToSerializations = { 24 | hessian2: require('./hessian'), 25 | }; 26 | 27 | exports.getSerializationById = id => { 28 | return idToSerializations[id]; 29 | }; 30 | 31 | exports.getSerializationByName = name => { 32 | return nameToSerializations[name]; 33 | }; 34 | -------------------------------------------------------------------------------- /lib/utils.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | 'use strict'; 19 | 20 | const is = require('is-type-of'); 21 | const utility = require('utility'); 22 | 23 | const MAX_SAFE_INTEGER = utility.MAX_SAFE_INTEGER; 24 | 25 | let id = 0; 26 | exports.newId = () => { 27 | id = id < MAX_SAFE_INTEGER ? id + 1 : 0; 28 | return id; 29 | }; 30 | 31 | exports.concatBuffer = (buf_1, buf_2) => { 32 | const len_1 = buf_1.length; 33 | const len_2 = buf_2.length; 34 | const buf = Buffer.alloc(len_1 + len_2); 35 | buf_1.copy(buf, 0, 0, len_1); 36 | buf_2.copy(buf, len_1, 0, len_2); 37 | return buf; 38 | }; 39 | 40 | exports.handleLong = require('hessian.js/lib/utils').handleLong; 41 | 42 | const primitiveMap = { 43 | void: 'V', 44 | boolean: 'Z', 45 | byte: 'B', 46 | char: 'C', 47 | double: 'D', 48 | float: 'F', 49 | int: 'I', 50 | long: 'J', 51 | short: 'S', 52 | }; 53 | const isArray = c => c.startsWith('['); 54 | const getComponentType = c => { 55 | if (isArray(c)) { 56 | return c.slice(1); 57 | } 58 | return c; 59 | }; 60 | const getJavaClassDesc = val => { 61 | if (is.nullOrUndefined(val)) { 62 | return 'Ljava/lang/Object;'; 63 | } 64 | const type = typeof val; 65 | switch (type) { 66 | case 'boolean': 67 | return primitiveMap.boolean; 68 | case 'string': 69 | return 'Ljava/lang/String;'; 70 | case 'number': 71 | if (is.long(val)) { 72 | return primitiveMap.long; 73 | } 74 | if (is.int(val)) { 75 | return primitiveMap.int; 76 | } 77 | return primitiveMap.double; 78 | default: 79 | break; 80 | } 81 | if (is.date(val)) { 82 | return 'Ljava/util/Date;'; 83 | } 84 | if (is.buffer(val)) { 85 | return `[${primitiveMap.byte}`; 86 | } 87 | if (is.array(val)) { 88 | return 'Ljava/util/ArrayList;'; 89 | } 90 | if (is.error(val)) { 91 | return 'Ljava/lang/Exception;'; 92 | } 93 | if (!utility.has(val, '$class')) { 94 | return 'Ljava/util/HashMap;'; 95 | } 96 | let ret = ''; 97 | let $class = val.$abstractClass || val.$class; 98 | while (isArray($class)) { 99 | ret += '['; 100 | $class = getComponentType($class); 101 | } 102 | if (primitiveMap[$class]) { 103 | ret += primitiveMap[$class]; 104 | } else { 105 | ret += `L${$class.replace(/\./g, '/')};`; 106 | } 107 | return ret; 108 | }; 109 | 110 | exports.getJavaArgsDesc = args => args.map(arg => getJavaClassDesc(arg)).join(''); 111 | 112 | exports.desc2classArray = desc => { 113 | if (!desc) { 114 | return []; 115 | } 116 | const arr = []; 117 | const len = desc.length; 118 | for (let i = 0; i < len; ++i) { 119 | let type = desc[i]; 120 | let prefix = ''; 121 | let javaType; 122 | if (type === '[') { 123 | prefix = '['; 124 | type = desc[++i]; 125 | } 126 | switch (type) { 127 | case 'V': 128 | javaType = 'void'; 129 | break; 130 | case 'Z': 131 | javaType = 'boolean'; 132 | break; 133 | case 'B': 134 | javaType = 'byte'; 135 | break; 136 | case 'C': 137 | javaType = 'char'; 138 | break; 139 | case 'D': 140 | javaType = 'double'; 141 | break; 142 | case 'F': 143 | javaType = 'float'; 144 | break; 145 | case 'I': 146 | javaType = 'int'; 147 | break; 148 | case 'J': 149 | javaType = 'long'; 150 | break; 151 | case 'S': 152 | javaType = 'short'; 153 | break; 154 | case 'L': 155 | { 156 | let clazz = ''; 157 | while (i < len && desc[++i] !== ';') { 158 | clazz += desc[i]; 159 | } 160 | javaType = clazz.replace(/\//g, '.'); 161 | break; 162 | } 163 | default: 164 | throw new Error(`[double-remoting] unknown class type => ${type}`); 165 | } 166 | arr.push(`${prefix}${javaType}`); 167 | } 168 | return arr; 169 | }; 170 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "js-remoting-for-apache-dubbo", 3 | "version": "1.0.0", 4 | "description": "dubbo remoting nodejs implement", 5 | "main": "lib/index.js", 6 | "files": [ 7 | "lib", 8 | "index.js" 9 | ], 10 | "scripts": { 11 | "autod": "autod", 12 | "pkgfiles": "egg-bin pkgfiles --check", 13 | "lint": "eslint --ext .js lib test", 14 | "test": "npm run lint && npm run test-local", 15 | "test-local": "egg-bin test", 16 | "cov": "egg-bin cov", 17 | "ci": "npm run autod -- --check && npm run pkgfiles && npm run lint && npm run cov" 18 | }, 19 | "repository": { 20 | "type": "git", 21 | "url": "git+https://github.com/dubbo/js-remoting-for-apache-dubbo.git" 22 | }, 23 | "keywords": [ 24 | "dubbo", 25 | "remoting" 26 | ], 27 | "author": "gxcsoccer@126.com", 28 | "license": "MIT", 29 | "bugs": { 30 | "url": "https://github.com/dubbo/js-remoting-for-apache-dubbo/issues" 31 | }, 32 | "homepage": "https://github.com/dubbo/js-remoting-for-apache-dubbo#readme", 33 | "dependencies": { 34 | "@protobufjs/codegen": "^2.0.4", 35 | "debug": "^4.1.1", 36 | "hessian.js": "^2.8.1", 37 | "is-type-of": "^1.2.1", 38 | "long": "^4.0.0", 39 | "utility": "^1.16.1" 40 | }, 41 | "devDependencies": { 42 | "autod": "^3.1.0", 43 | "await-event": "^2.1.0", 44 | "beautify-benchmark": "^0.2.4", 45 | "benchmark": "^2.1.4", 46 | "egg-bin": "^4.13.0", 47 | "egg-ci": "^1.11.0", 48 | "eslint": "^5.16.0", 49 | "eslint-config-egg": "^7.3.1", 50 | "js-to-java": "^2.6.1", 51 | "sofa-rpc-node": "^1.12.0" 52 | }, 53 | "engines": { 54 | "node": ">= 8.0.0" 55 | }, 56 | "ci": { 57 | "version": "8, 10, 12" 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /test/fixtures/class_map.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | 'com.alipay.test.Father': { 5 | foo: { 6 | type: 'java.lang.String', 7 | }, 8 | }, 9 | 'com.alipay.test.Child': { 10 | foo: { 11 | type: 'java.lang.String', 12 | }, 13 | bar: { 14 | type: 'java.lang.String', 15 | }, 16 | }, 17 | 'com.test.service.ctx.UniformContextHeaders': { 18 | invokeId: { 19 | type: 'java.lang.String', 20 | }, 21 | serviceUniqueName: { 22 | type: 'java.lang.String', 23 | }, 24 | read: { 25 | type: 'boolean', 26 | defaultValue: false, 27 | }, 28 | idempotent: { 29 | type: 'boolean', 30 | defaultValue: false, 31 | }, 32 | batch: { 33 | type: 'boolean', 34 | defaultValue: false, 35 | }, 36 | version: { 37 | type: 'java.lang.String', 38 | }, 39 | counter: { 40 | type: 'java.util.concurrent.atomic.AtomicLong', 41 | }, 42 | ipGroup: { 43 | type: 'java.lang.String', 44 | }, 45 | caller: { 46 | type: 'com.test.service.ctx.Caller', 47 | }, 48 | callee: { 49 | type: 'com.test.service.ctx.Callee', 50 | }, 51 | webInfo: { 52 | type: 'com.test.service.ctx.WebUniformContextInfo', 53 | }, 54 | serviceProperies: { 55 | type: 'java.util.Properties', 56 | isMap: true, 57 | generic: [ 58 | { type: 'java.lang.Object' }, 59 | { type: 'java.lang.Object' }, 60 | ], 61 | }, 62 | protocol: { 63 | type: 'java.lang.String', 64 | }, 65 | invokeType: { 66 | type: 'java.lang.String', 67 | }, 68 | from_msg: { 69 | type: 'boolean', 70 | defaultValue: false, 71 | }, 72 | }, 73 | 'java.util.concurrent.atomic.AtomicLong': { 74 | value: { 75 | type: 'long', 76 | defaultValue: '0', 77 | }, 78 | }, 79 | 'com.test.service.ctx.Caller': { 80 | ip: { 81 | type: 'java.lang.String', 82 | }, 83 | hostName: { 84 | type: 'java.lang.String', 85 | }, 86 | appName: { 87 | type: 'java.lang.String', 88 | }, 89 | requestTime: { 90 | type: 'java.lang.String', 91 | }, 92 | }, 93 | 'com.test.service.ctx.Callee': { 94 | sid: { 95 | type: 'java.lang.String', 96 | }, 97 | timeout: { 98 | type: 'java.lang.String', 99 | }, 100 | version: { 101 | type: 'java.lang.String', 102 | }, 103 | }, 104 | 'com.test.service.ctx.WebUniformContextInfo': { 105 | pageUrl: { 106 | type: 'java.lang.String', 107 | }, 108 | uid: { 109 | type: 'java.lang.String', 110 | }, 111 | jsessionId: { 112 | type: 'java.lang.String', 113 | }, 114 | pageParams: { 115 | type: 'java.lang.String', 116 | }, 117 | ipGroup: { 118 | type: 'java.lang.String', 119 | }, 120 | from_msg: { 121 | type: 'boolean', 122 | defaultValue: false, 123 | }, 124 | business_type_id: { 125 | type: 'java.lang.String', 126 | }, 127 | extend_props: { 128 | type: 'java.util.Map', 129 | generic: [ 130 | { type: 'java.lang.String' }, 131 | { type: 'java.lang.Object' }, 132 | ], 133 | }, 134 | }, 135 | 136 | 'com.alipay.test.TestObj': { 137 | staticField: { 138 | type: 'java.lang.String', 139 | isStatic: true, 140 | }, 141 | transientField: { 142 | type: 'java.lang.String', 143 | isTransient: true, 144 | }, 145 | b: { 146 | type: 'boolean', 147 | defaultValue: false, 148 | }, 149 | testObj2: { 150 | type: 'com.alipay.test.sub.TestObj2', 151 | }, 152 | name: { 153 | type: 'java.lang.String', 154 | }, 155 | field: { 156 | type: 'java.lang.String', 157 | }, 158 | testEnum: { 159 | type: 'com.alipay.test.TestEnum', 160 | isEnum: true, 161 | }, 162 | testEnum2: { 163 | type: 'com.alipay.test.TestEnum', 164 | isArray: true, 165 | arrayDepth: 1, 166 | isEnum: true, 167 | }, 168 | bs: { 169 | type: 'byte', 170 | isArray: true, 171 | arrayDepth: 1, 172 | }, 173 | list1: { 174 | type: 'java.util.List', 175 | generic: [ 176 | { isEnum: true, type: 'com.alipay.test.TestEnum' }, 177 | ], 178 | }, 179 | list2: { 180 | type: 'java.util.List', 181 | generic: [ 182 | { type: 'java.lang.Integer' }, 183 | ], 184 | }, 185 | list3: { 186 | type: 'java.util.List', 187 | generic: [ 188 | { type: 'com.alipay.test.sub.TestObj2' }, 189 | ], 190 | }, 191 | list4: { 192 | type: 'java.util.List', 193 | generic: [ 194 | { type: 'java.lang.String' }, 195 | ], 196 | }, 197 | list5: { 198 | type: 'java.util.List', 199 | generic: [ 200 | { isArray: true, type: 'byte' }, 201 | ], 202 | }, 203 | map1: { 204 | type: 'java.util.Map', 205 | generic: [ 206 | { type: 'java.lang.Long' }, 207 | { isEnum: true, type: 'com.alipay.test.TestEnum' }, 208 | ], 209 | }, 210 | map2: { 211 | type: 'java.util.Map', 212 | generic: [ 213 | { type: 'java.lang.Integer' }, 214 | { type: 'java.lang.Integer' }, 215 | ], 216 | }, 217 | map3: { 218 | type: 'java.util.Map', 219 | generic: [ 220 | { type: 'java.lang.Boolean' }, 221 | { type: 'com.alipay.test.sub.TestObj2' }, 222 | ], 223 | }, 224 | map4: { 225 | type: 'java.util.Map', 226 | generic: [ 227 | { type: 'java.lang.String' }, 228 | { type: 'java.lang.String' }, 229 | ], 230 | }, 231 | map5: { 232 | type: 'java.util.Map', 233 | generic: [ 234 | { type: 'java.lang.String' }, 235 | { isArray: true, type: 'byte' }, 236 | ], 237 | }, 238 | }, 239 | 'com.alipay.test.sub.TestObj2': { 240 | name: { 241 | type: 'java.lang.String', 242 | }, 243 | transientField: { 244 | type: 'java.lang.String', 245 | isTransient: true, 246 | }, 247 | finalField: { 248 | type: 'java.lang.String', 249 | defaultValue: 'xxx', 250 | }, 251 | staticField: { 252 | type: 'java.lang.String', 253 | isStatic: true, 254 | }, 255 | }, 256 | 257 | 'com.alipay.test.Request': { 258 | 'data': { 259 | 'type': 'T', 260 | 'typeAliasIndex': 0, 261 | }, 262 | }, 263 | 264 | 'com.sofa.TestObject': { 265 | 'oneEnum': { 266 | 'type': 'com.sofa.OneEnum', 267 | 'defaultValue': 'DEFAULT', 268 | 'isEnum': true, 269 | }, 270 | 'twoEnum': { 271 | 'type': 'com.sofa.TwoEnum', 272 | 'defaultValue': 'DEFAULT', 273 | 'isEnum': true, 274 | }, 275 | }, 276 | 277 | 'com.eggjs.dubbo.GenericResult': { 278 | 'success': { 279 | 'type': 'java.lang.Boolean' 280 | }, 281 | 'result': { 282 | 'type': 'T', 283 | 'typeAliasIndex': 0 284 | }, 285 | 'error': { 286 | 'type': 'U', 287 | 'typeAliasIndex': 1 288 | } 289 | }, 290 | 'com.eggjs.dubbo.HelloResponse': { 291 | 'hello': { 292 | 'type': 'java.lang.String' 293 | } 294 | }, 295 | 'com.eggjs.dubbo.HelloError': { 296 | 'name': { 297 | 'type': 'java.lang.String' 298 | }, 299 | 'message': { 300 | 'type': 'java.lang.String' 301 | } 302 | } 303 | }; 304 | -------------------------------------------------------------------------------- /test/fixtures/dubbo_req.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-modules/js-remoting-for-apache-dubbo/67913200b178f2e57c97c260bd1345d150b8a30f/test/fixtures/dubbo_req.bin -------------------------------------------------------------------------------- /test/fixtures/dubbo_req_java_class.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-modules/js-remoting-for-apache-dubbo/67913200b178f2e57c97c260bd1345d150b8a30f/test/fixtures/dubbo_req_java_class.bin -------------------------------------------------------------------------------- /test/fixtures/dubbo_res.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-modules/js-remoting-for-apache-dubbo/67913200b178f2e57c97c260bd1345d150b8a30f/test/fixtures/dubbo_res.bin -------------------------------------------------------------------------------- /test/fixtures/dubbo_res_with_error.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-modules/js-remoting-for-apache-dubbo/67913200b178f2e57c97c260bd1345d150b8a30f/test/fixtures/dubbo_res_with_error.bin -------------------------------------------------------------------------------- /test/fixtures/dubbo_res_with_null.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-modules/js-remoting-for-apache-dubbo/67913200b178f2e57c97c260bd1345d150b8a30f/test/fixtures/dubbo_res_with_null.bin -------------------------------------------------------------------------------- /test/fixtures/dubbo_res_with_sys_error.bin: -------------------------------------------------------------------------------- 1 | ڻF 2 | sys error -------------------------------------------------------------------------------- /test/fixtures/heartbeat_req.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-modules/js-remoting-for-apache-dubbo/67913200b178f2e57c97c260bd1345d150b8a30f/test/fixtures/heartbeat_req.bin -------------------------------------------------------------------------------- /test/fixtures/heartbeat_res.bin: -------------------------------------------------------------------------------- 1 | ڻ"N -------------------------------------------------------------------------------- /test/fixtures/login.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-modules/js-remoting-for-apache-dubbo/67913200b178f2e57c97c260bd1345d150b8a30f/test/fixtures/login.bin -------------------------------------------------------------------------------- /test/fixtures/multiple.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/node-modules/js-remoting-for-apache-dubbo/67913200b178f2e57c97c260bd1345d150b8a30f/test/fixtures/multiple.bin -------------------------------------------------------------------------------- /test/hessian.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Long = require('long'); 4 | const assert = require('assert'); 5 | const java = require('js-to-java'); 6 | const hessian = require('hessian.js'); 7 | const classMap = require('./fixtures/class_map'); 8 | const { encoderV1, encoderV2 } = require('hessian.js'); 9 | const utils = require('../lib/serialize/hessian/utils'); 10 | const compile = require('../lib/serialize/hessian/compile'); 11 | 12 | const versions = [ '1.0', '2.0' ]; 13 | 14 | function encode(obj, version, classMap, appClassMap) { 15 | const encoder = version === '2.0' ? encoderV2 : encoderV1; 16 | encoder.reset(); 17 | if (classMap) { 18 | compile(obj, version, classMap)(obj.$, encoder, appClassMap); 19 | } else { 20 | encoder.write(obj); 21 | } 22 | return encoder.get(); 23 | } 24 | 25 | describe('test/hessian.test.js', () => { 26 | versions.forEach(version => { 27 | describe(version, () => { 28 | it('should encode java.util.Map without generic', () => { 29 | const obj = { 30 | $class: 'java.util.Map', 31 | $: { foo: 'bar' }, 32 | isMap: true, 33 | }; 34 | const buf1 = hessian.encode({ $class: 'java.util.Map', $: { foo: 'bar' } }, version); 35 | const buf2 = encode(obj, version, {}); 36 | assert.deepEqual(buf1, buf2); 37 | 38 | const buf3 = encode(obj, version, {}); 39 | assert.deepEqual(buf1, buf3); 40 | 41 | const map = new Map(); 42 | map.set('foo', 'bar'); 43 | obj.$ = map; 44 | const buf4 = hessian.encode({ 45 | $class: 'java.util.Map', 46 | $: { foo: { $class: 'java.lang.String', $: 'bar' } }, 47 | }, version); 48 | const buf5 = encode(obj, version, {}); 49 | assert.deepEqual(buf4, buf5); 50 | 51 | const buf6 = encode(obj, version, {}); 52 | assert.deepEqual(buf4, buf6); 53 | }); 54 | 55 | it('should encode java.util.Map with generic', () => { 56 | const map = new Map(); 57 | map.set(1, 'xxx'); 58 | const obj = { 59 | $class: 'java.util.Map', 60 | $: map, 61 | generic: [{ 62 | type: 'java.lang.Integer', 63 | }, { 64 | type: 'java.lang.String', 65 | }], 66 | }; 67 | 68 | const converted = { 69 | $class: 'java.util.Map', 70 | $: new Map(), 71 | }; 72 | converted.$.set({ $class: 'java.lang.Integer', $: 1 }, { $class: 'java.lang.String', $: 'xxx' }); 73 | 74 | const buf1 = hessian.encode(converted, version); 75 | const buf2 = encode(obj, version, {}); 76 | assert.deepEqual(buf1, buf2); 77 | 78 | const buf3 = encode(obj, version, {}); 79 | assert.deepEqual(buf1, buf3); 80 | 81 | obj.$ = { 1: 'xxx' }; 82 | const buf4 = hessian.encode(converted, version); 83 | const buf5 = encode(obj, version, {}); 84 | assert.deepEqual(buf4, buf5); 85 | 86 | const buf6 = encode(obj, version, {}); 87 | assert.deepEqual(buf4, buf6); 88 | }); 89 | 90 | it('should encode java.util.List with generic', () => { 91 | const obj = { 92 | $class: 'java.util.List', 93 | $: [ 'foo', 'bar' ], 94 | generic: [ 95 | 'java.lang.String', 96 | ], 97 | }; 98 | const buf1 = hessian.encode({ 99 | $class: 'java.util.List', 100 | $: [{ $class: 'java.lang.String', $: 'foo' }, 101 | { $class: 'java.lang.String', $: 'bar' }, 102 | ], 103 | }, version); 104 | const buf2 = encode(obj, version, {}); 105 | assert.deepEqual(buf1, buf2); 106 | 107 | const buf3 = encode(obj, version, {}); 108 | assert.deepEqual(buf1, buf3); 109 | }); 110 | 111 | it('should encode java.util.List without generic', () => { 112 | const obj = { 113 | $class: 'java.util.List', 114 | $: [ 'foo', 'bar' ], 115 | }; 116 | const buf1 = hessian.encode({ $class: 'java.util.List', $: [ 'foo', 'bar' ] }, version); 117 | const buf2 = encode(obj, version, {}); 118 | assert.deepEqual(buf1, buf2); 119 | 120 | const buf3 = encode(obj, version, {}); 121 | assert.deepEqual(buf1, buf3); 122 | }); 123 | 124 | it('should encode java.util.ArrayList with generic', () => { 125 | const obj = { 126 | $class: 'java.util.ArrayList', 127 | $: [ 'foo', 'bar' ], 128 | generic: [ 129 | 'java.lang.String', 130 | ], 131 | }; 132 | const buf1 = hessian.encode({ 133 | $class: 'java.util.ArrayList', 134 | $: [ 'foo', 'bar' ], 135 | generic: [ 'java.lang.String' ], 136 | }, version); 137 | const buf2 = encode(obj, version, {}); 138 | assert.deepEqual(buf1, buf2); 139 | 140 | const buf3 = encode(obj, version, {}); 141 | assert.deepEqual(buf1, buf3); 142 | }); 143 | 144 | it('should encode java.util.ArrayList without generic', () => { 145 | const obj = { 146 | $class: 'java.util.ArrayList', 147 | $: [ 'foo', 'bar' ], 148 | }; 149 | const buf1 = hessian.encode({ $class: 'java.util.ArrayList', $: [ 'foo', 'bar' ] }, version); 150 | const buf2 = encode(obj, version, {}); 151 | assert.deepEqual(buf1, buf2); 152 | 153 | const buf3 = encode(obj, version, {}); 154 | assert.deepEqual(buf1, buf3); 155 | }); 156 | 157 | it('should encode java.util.Set with generic', () => { 158 | const obj = { 159 | $class: 'java.util.Set', 160 | $: [ 'foo', 'bar' ], 161 | generic: [ 162 | 'java.lang.String', 163 | ], 164 | }; 165 | const buf1 = hessian.encode({ 166 | $class: 'java.util.Set', 167 | $: [{ $class: 'java.lang.String', $: 'foo' }, 168 | { $class: 'java.lang.String', $: 'bar' }, 169 | ], 170 | }, version); 171 | const buf2 = encode(obj, version, {}); 172 | assert.deepEqual(buf1, buf2); 173 | 174 | const buf3 = encode(obj, version, {}); 175 | assert.deepEqual(buf1, buf3); 176 | }); 177 | 178 | it('should encode java.util.Set without generic', () => { 179 | const obj = { 180 | $class: 'java.util.Set', 181 | $: [ 'foo', 'bar' ], 182 | }; 183 | const buf1 = hessian.encode({ $class: 'java.util.Set', $: [ 'foo', 'bar' ] }, version); 184 | const buf2 = encode(obj, version, {}); 185 | assert.deepEqual(buf1, buf2); 186 | 187 | const buf3 = encode(obj, version, {}); 188 | assert.deepEqual(buf1, buf3); 189 | }); 190 | 191 | it('should encode enum', () => { 192 | const obj = { 193 | $class: 'com.test.model.datum.DatumStaus', 194 | $: { 195 | code: 'PRERELEASING', 196 | name: 'PRERELEASING', 197 | message: '预发中', 198 | ordinal: 1, 199 | eql() { 200 | // 201 | }, 202 | }, 203 | isEnum: true, 204 | }; 205 | const buf1 = hessian.encode({ 206 | $class: 'com.test.model.datum.DatumStaus', 207 | $: { name: 'PRERELEASING' }, 208 | }, version); 209 | const buf2 = encode(obj, version, {}); 210 | assert.deepEqual(buf1, buf2); 211 | 212 | const buf3 = encode(obj, version, {}); 213 | assert.deepEqual(buf1, buf3); 214 | }); 215 | 216 | it('should encode class', () => { 217 | const obj = { 218 | $class: 'com.test.service.ctx.WebUniformContextInfo', 219 | $: { 220 | extend_props: { 221 | foo: 'bar', 222 | }, 223 | pageUrl: 'pageUrl', 224 | uid: 'uid', 225 | jsessionId: 'jsessionId', 226 | pageParams: 'pageParams', 227 | ipGroup: 'ipGroup', 228 | business_type_id: 'business_type_id', 229 | }, 230 | }; 231 | const buf1 = hessian.encode({ 232 | $class: 'com.test.service.ctx.WebUniformContextInfo', 233 | $: { 234 | pageUrl: { $class: 'java.lang.String', $: 'pageUrl' }, 235 | uid: { $class: 'java.lang.String', $: 'uid' }, 236 | jsessionId: { $class: 'java.lang.String', $: 'jsessionId' }, 237 | pageParams: { $class: 'java.lang.String', $: 'pageParams' }, 238 | ipGroup: { $class: 'java.lang.String', $: 'ipGroup' }, 239 | from_msg: { $class: 'boolean', $: false }, 240 | business_type_id: { $class: 'java.lang.String', $: 'business_type_id' }, 241 | extend_props: { $class: 'java.util.Map', $: { foo: 'bar' } }, 242 | }, 243 | }, version); 244 | const buf2 = encode(obj, version, classMap); 245 | assert.deepEqual(buf1, buf2); 246 | 247 | const buf3 = encode(obj, version, classMap); 248 | assert.deepEqual(buf1, buf3); 249 | }); 250 | 251 | it('should encode class that isMap = true', () => { 252 | const obj = { 253 | $class: 'com.test.service.ctx.UniformContextHeaders', 254 | $: { 255 | serviceProperies: { foo: 'bar' }, 256 | }, 257 | }; 258 | const buf1 = hessian.encode({ 259 | $class: 'com.test.service.ctx.UniformContextHeaders', 260 | $: { 261 | invokeId: null, 262 | serviceUniqueName: null, 263 | read: { $class: 'boolean', $: false }, 264 | idempotent: { $class: 'boolean', $: false }, 265 | batch: { $class: 'boolean', $: false }, 266 | version: null, 267 | counter: null, 268 | ipGroup: null, 269 | caller: null, 270 | callee: null, 271 | webInfo: null, 272 | serviceProperies: { $class: 'java.util.Properties', $: { foo: 'bar' } }, 273 | protocol: null, 274 | invokeType: null, 275 | from_msg: { $class: 'boolean', $: false }, 276 | }, 277 | }, version); 278 | const buf2 = encode(obj, version, classMap); 279 | assert.deepEqual(buf1, buf2); 280 | 281 | const buf3 = encode(obj, version, classMap); 282 | assert.deepEqual(buf1, buf3); 283 | }); 284 | 285 | it('should encode class that isMap = true 2', () => { 286 | const obj = { 287 | $class: 'com.test.service.ctx.UniformContextHeaders', 288 | $: { 289 | serviceProperies: new Map([ 290 | [ 'foo', 'bar' ], 291 | ]), 292 | }, 293 | }; 294 | const buf1 = hessian.encode({ 295 | $class: 'com.test.service.ctx.UniformContextHeaders', 296 | $: { 297 | invokeId: null, 298 | serviceUniqueName: null, 299 | read: { $class: 'boolean', $: false }, 300 | idempotent: { $class: 'boolean', $: false }, 301 | batch: { $class: 'boolean', $: false }, 302 | version: null, 303 | counter: null, 304 | ipGroup: null, 305 | caller: null, 306 | callee: null, 307 | webInfo: null, 308 | serviceProperies: { $class: 'java.util.Properties', $: { foo: 'bar' } }, 309 | protocol: null, 310 | invokeType: null, 311 | from_msg: { $class: 'boolean', $: false }, 312 | }, 313 | }, version); 314 | const buf2 = encode(obj, version, classMap); 315 | assert.deepEqual(buf1, buf2); 316 | 317 | const buf3 = encode(obj, version, classMap); 318 | assert.deepEqual(buf1, buf3); 319 | }); 320 | 321 | it('should encode class that isMap = true 2', () => { 322 | const obj = { 323 | $class: 'com.test.TestClass', 324 | $: { 325 | serviceProperies: { foo: 'bar' }, 326 | }, 327 | }; 328 | const buf1 = hessian.encode({ 329 | $class: 'com.test.TestClass', 330 | $: { 331 | serviceProperies: { 332 | $class: 'com.test.Map', 333 | $: { foo: 'bar' }, 334 | }, 335 | }, 336 | }, version); 337 | const buf2 = encode(obj, version, { 338 | 'com.test.TestClass': { 339 | serviceProperies: { 340 | type: 'com.test.Map', 341 | isMap: true, 342 | }, 343 | }, 344 | }); 345 | assert.deepEqual(buf1, buf2); 346 | 347 | const buf3 = encode(obj, version, { 348 | 'com.test.TestClass': { 349 | serviceProperies: { 350 | type: 'com.test.Map', 351 | isMap: true, 352 | }, 353 | }, 354 | }); 355 | assert.deepEqual(buf1, buf3); 356 | }); 357 | 358 | it('should encode java.util.Date', () => { 359 | const obj = { 360 | $class: 'java.util.Date', 361 | $: new Date(), 362 | }; 363 | const buf1 = hessian.encode(obj, version); 364 | const buf2 = encode(obj, version, classMap); 365 | assert.deepEqual(buf1, buf2); 366 | 367 | const buf3 = encode(obj, version, classMap); 368 | assert.deepEqual(buf1, buf3); 369 | }); 370 | 371 | it('should encode java.lang.Class', () => { 372 | const obj = { 373 | $class: 'java.lang.Class', 374 | $: { 375 | name: '[java.lang.String', 376 | }, 377 | }; 378 | const buf1 = hessian.encode(obj, version); 379 | const buf2 = encode(obj, version, classMap); 380 | assert.deepEqual(buf1, buf2); 381 | 382 | const buf3 = encode(obj, version, classMap); 383 | assert.deepEqual(buf1, buf3); 384 | }); 385 | 386 | it('should encode java.util.Currency', () => { 387 | const obj = { 388 | $class: 'java.util.Currency', 389 | $: 'CNY', 390 | }; 391 | const buf1 = hessian.encode({ $class: 'java.util.Currency', $: { currencyCode: 'CNY' } }, version); 392 | const buf2 = encode(obj, version, classMap); 393 | assert.deepEqual(buf1, buf2); 394 | 395 | const buf3 = encode(obj, version, classMap); 396 | assert.deepEqual(buf1, buf3); 397 | }); 398 | 399 | it('should encode java.math.BigDecimal', () => { 400 | const obj = { 401 | $class: 'java.math.BigDecimal', 402 | $: { value: '100.06' }, 403 | }; 404 | const buf1 = hessian.encode({ $class: 'java.math.BigDecimal', $: { value: '100.06' } }, version); 405 | const buf2 = encode(obj, version, classMap); 406 | assert.deepEqual(buf1, buf2); 407 | 408 | const buf3 = encode(obj, version, classMap); 409 | assert.deepEqual(buf1, buf3); 410 | }); 411 | 412 | it('should encode abstractClass', () => { 413 | const user = { 414 | $class: 'com.test.service.UserPrincipal', 415 | $: { 416 | name: 'name', 417 | }, 418 | }; 419 | const obj = { 420 | $class: 'java.security.Principal', 421 | $abstractClass: 'java.security.Principal', 422 | $: user, 423 | }; 424 | const buf1 = hessian.encode({ 425 | $abstractClass: 'java.security.Principal', 426 | $class: 'com.test.service.UserPrincipal', 427 | $: { name: 'name' }, 428 | }, version); 429 | const buf2 = encode(obj, version, classMap); 430 | assert.deepEqual(buf1, buf2); 431 | 432 | const buf3 = encode(obj, version, classMap); 433 | assert.deepEqual(buf1, buf3); 434 | }); 435 | 436 | it('should encode java.util.Collection', () => { 437 | const classMap = { 438 | 'com.test.service.PermissionQuery': { 439 | vAccountId: { 440 | type: 'java.lang.String', 441 | isFinal: true, 442 | }, 443 | personId: { 444 | type: 'java.lang.String', 445 | isFinal: true, 446 | }, 447 | packageNames: { 448 | type: 'java.util.Collection', 449 | isFinal: true, 450 | abstractClass: 'java.util.Collection', 451 | generic: [ 452 | { type: 'java.lang.String' }, 453 | ], 454 | }, 455 | roleNames: { 456 | type: 'java.util.Collection', 457 | isFinal: true, 458 | abstractClass: 'java.util.Collection', 459 | generic: [ 460 | { type: 'java.lang.String' }, 461 | ], 462 | }, 463 | featureNames: { 464 | type: 'java.util.Collection', 465 | isFinal: true, 466 | abstractClass: 'java.util.Collection', 467 | generic: [ 468 | { type: 'java.lang.String' }, 469 | ], 470 | }, 471 | featureParams: { 472 | type: 'java.util.Map', 473 | isFinal: true, 474 | generic: [ 475 | { type: 'java.lang.String' }, 476 | { generic: [{ type: 'java.lang.String' }], type: 'java.util.Collection' }, 477 | ], 478 | }, 479 | filters: { 480 | type: 'java.util.Collection', 481 | isFinal: true, 482 | abstractClass: 'java.util.Collection', 483 | generic: [ 484 | { type: 'java.lang.String' }, 485 | ], 486 | }, 487 | }, 488 | }; 489 | const queries = [{ 490 | vAccountId: '200012245', 491 | personId: null, 492 | packageNames: null, 493 | roleNames: null, 494 | featureNames: [ 'China_GS', 'China_Free' ], 495 | featureParams: null, 496 | filters: null, 497 | }]; 498 | const obj = { 499 | $class: 'java.util.Collection', 500 | $: queries, 501 | $abstractClass: 'java.util.Collection', 502 | generic: [{ type: 'com.test.service.PermissionQuery' }], 503 | }; 504 | 505 | const buf1 = hessian.encode({ 506 | $class: 'java.util.Collection', 507 | $: [{ 508 | $class: 'com.test.service.PermissionQuery', 509 | $: { 510 | vAccountId: { 511 | $class: 'java.lang.String', 512 | $: '200012245', 513 | }, 514 | personId: null, 515 | packageNames: null, 516 | roleNames: null, 517 | featureNames: { 518 | $class: 'java.util.Collection', 519 | $: [ 520 | { $class: 'java.lang.String', $: 'China_GS' }, 521 | { $class: 'java.lang.String', $: 'China_Free' }, 522 | ], 523 | }, 524 | featureParams: null, 525 | filters: null, 526 | }, 527 | }], 528 | }, version); 529 | const buf2 = encode(obj, version, classMap); 530 | assert.deepEqual(buf1, buf2); 531 | 532 | const buf3 = encode(obj, version, classMap); 533 | assert.deepEqual(buf1, buf3); 534 | }); 535 | 536 | it('classMap 中含有子类申明时, 子类类型不能丢掉', () => { 537 | const classMap = { 538 | 'com.test.service.facade.model.triggerrequests.Request': {}, 539 | 'com.test.service.facade.model.triggerrequests.UserTriggerRequest': { 540 | specifiedPrize: { 541 | type: 'com.alipay.promocore.common.service.facade.model.camp.trigger.SpecifiedPrize', 542 | }, 543 | prizeId: { 544 | type: 'java.lang.String', 545 | }, 546 | type: { 547 | type: 'com.test.service.facade.events.TriggerType', 548 | defaultValue: 'User', 549 | isEnum: true, 550 | }, 551 | campaignId: { 552 | type: 'java.lang.String', 553 | }, 554 | userId: { 555 | type: 'java.lang.String', 556 | }, 557 | seedId: { 558 | type: 'java.lang.String', 559 | }, 560 | requestContext: { 561 | type: 'java.util.Map', 562 | defaultValue: {}, 563 | generic: [ 564 | { type: 'java.lang.String' }, 565 | { type: 'java.lang.Object' }, 566 | ], 567 | }, 568 | transferProps: { 569 | type: 'java.util.Map', 570 | defaultValue: {}, 571 | generic: [ 572 | { type: 'java.lang.String' }, 573 | { type: 'java.lang.String' }, 574 | ], 575 | }, 576 | gmtDt: { 577 | type: 'java.util.Date', 578 | defaultValue: 1471096717898, 579 | }, 580 | idempotentNo: { 581 | type: 'java.lang.String', 582 | }, 583 | idempotent: { 584 | type: 'boolean', 585 | defaultValue: false, 586 | }, 587 | }, 588 | }; 589 | const userId = 'x'; 590 | const campaignId = 'a'; 591 | const request = { 592 | $class: 'com.test.service.facade.model.triggerrequests.UserTriggerRequest', 593 | $: { 594 | userId, 595 | campaignId, 596 | requestContext: { termId: 'd' }, 597 | transferProps: { fundOrderId: 'o' }, 598 | }, 599 | }; 600 | const obj = { 601 | $class: 'com.test.service.facade.model.triggerrequests.Request', 602 | $abstractClass: 'com.test.service.facade.model.triggerrequests.Request', 603 | $: request, 604 | }; 605 | 606 | const buf1 = hessian.encode({ 607 | $class: 'com.test.service.facade.model.triggerrequests.UserTriggerRequest', 608 | $: { 609 | specifiedPrize: null, 610 | prizeId: null, 611 | type: { $class: 'com.test.service.facade.events.TriggerType', $: { name: 'User' } }, 612 | campaignId: { $class: 'java.lang.String', $: 'a' }, 613 | userId: { $class: 'java.lang.String', $: 'x' }, 614 | seedId: null, 615 | requestContext: { $class: 'java.util.Map', $: { termId: 'd' } }, 616 | transferProps: { 617 | $class: 'java.util.Map', 618 | $: { 619 | fundOrderId: { $class: 'java.lang.String', $: 'o' }, 620 | }, 621 | }, 622 | gmtDt: { $class: 'java.util.Date', $: 1471096717898 }, 623 | idempotentNo: null, 624 | idempotent: { $class: 'boolean', $: false }, 625 | }, 626 | $abstractClass: 'com.test.service.facade.model.triggerrequests.Request', 627 | }, version); 628 | const buf2 = encode(obj, version, classMap); 629 | assert.deepEqual(buf1, buf2); 630 | 631 | const buf3 = encode(obj, version, classMap); 632 | assert.deepEqual(buf1, buf3); 633 | }); 634 | 635 | it('#convert() abstractClass manual appoint 2.', () => { 636 | const user = { 637 | $class: 'com.test.service.UserPrincipal', 638 | $: { 639 | name: 'name', 640 | test: null, 641 | }, 642 | }; 643 | const obj = { 644 | $class: 'java.util.Map', 645 | $: { 646 | value: { 647 | $class: 'java.security.Principal', 648 | $abstractClass: 'java.security.Principal', 649 | $: user, 650 | }, 651 | }, 652 | }; 653 | const buf1 = hessian.encode({ 654 | $class: 'java.util.Map', 655 | $: { 656 | value: { 657 | $abstractClass: 'java.security.Principal', 658 | $class: 'com.test.service.UserPrincipal', 659 | $: { name: 'name', test: null }, 660 | }, 661 | }, 662 | }, version); 663 | const buf2 = encode(obj, version, classMap); 664 | assert.deepEqual(buf1, buf2); 665 | 666 | const buf3 = encode(obj, version, classMap); 667 | assert.deepEqual(buf1, buf3); 668 | }); 669 | 670 | it('#convert() abstractClass manual appoint 21.', () => { 671 | const classMap = { 672 | 'com.test.service.MethodParam': { 673 | value: { 674 | type: 'java.security.Principal', 675 | $abstractClass: 'java.security.Principal', 676 | }, 677 | }, 678 | }; 679 | const user = { 680 | $class: 'com.test.service.UserPrincipal', 681 | $: { 682 | name: 'name', 683 | test: null, 684 | }, 685 | }; 686 | const obj = { 687 | $class: 'com.test.service.MethodParam', 688 | $: [ 689 | { value: user }, 690 | ], 691 | isArray: true, 692 | }; 693 | const buf1 = hessian.encode({ 694 | $class: '[com.test.service.MethodParam', 695 | $: [{ 696 | $class: 'com.test.service.MethodParam', 697 | $: { 698 | value: { 699 | $abstractClass: 'java.security.Principal', 700 | $class: 'com.test.service.UserPrincipal', 701 | $: { name: 'name', test: null }, 702 | }, 703 | }, 704 | }], 705 | }, version); 706 | const buf2 = encode(obj, version, classMap); 707 | assert.deepEqual(buf1, buf2); 708 | 709 | const buf3 = encode(obj, version, classMap); 710 | assert.deepEqual(buf1, buf3); 711 | }); 712 | 713 | it('should encode java.util.Locale', () => { 714 | const obj = { 715 | $class: 'java.util.Locale', 716 | $: 'zh_CN', 717 | }; 718 | const buf1 = hessian.encode({ $class: 'com.caucho.hessian.io.LocaleHandle', $: { value: 'zh_CN' } }, version); 719 | const buf2 = encode(obj, version, classMap); 720 | assert.deepEqual(buf1, buf2); 721 | 722 | const buf3 = encode(obj, version, classMap); 723 | assert.deepEqual(buf1, buf3); 724 | }); 725 | 726 | it('should encode java.lang.Exception', () => { 727 | const err = new Error('mock'); 728 | const buf1 = hessian.encode(java.exception(err), version); 729 | const buf2 = encode({ 730 | $class: 'java.lang.Exception', 731 | $: err, 732 | }, version, {}); 733 | assert.deepEqual(buf1, buf2); 734 | 735 | const buf3 = encode({ 736 | $class: 'java.lang.Exception', 737 | $: err, 738 | }, version, {}); 739 | assert.deepEqual(buf1, buf3); 740 | 741 | const buf4 = encode(java.exception(err), version, {}); 742 | assert.deepEqual(buf1, buf4); 743 | }); 744 | 745 | describe('primitive types', () => { 746 | [ 747 | [ 'boolean', true ], 748 | [ 'java.lang.Boolean', false ], 749 | [ 'int', 10 ], 750 | [ 'java.lang.Integer', 1000 ], 751 | [ 'short', 7 ], 752 | [ 'java.lang.Short', 9 ], 753 | [ 'long', Date.now() ], 754 | [ 'java.lang.Long', Date.now() ], 755 | [ 'double', 9.99 ], 756 | [ 'java.lang.Double', 100.1 ], 757 | [ 'float', 20.2 ], 758 | [ 'java.lang.Float', 7.77 ], 759 | [ 'byte', 1 ], 760 | [ 'java.lang.Byte', 0 ], 761 | [ 'char', 's' ], 762 | [ 'java.lang.Character', 'S' ], 763 | [ 'java.lang.String', 'hello world' ], 764 | ].forEach(item => { 765 | it('should encode ' + item[0], () => { 766 | const obj = { 767 | $class: item[0], 768 | $: item[1], 769 | }; 770 | const buf1 = hessian.encode(obj, version); 771 | const buf2 = encode(obj, version, classMap); 772 | assert.deepEqual(buf1, buf2); 773 | 774 | const buf3 = encode(obj, version, classMap); 775 | assert.deepEqual(buf1, buf3); 776 | }); 777 | }); 778 | }); 779 | 780 | it('should auto convert number to string for java.lang.String', () => { 781 | const obj = { 782 | $class: 'java.lang.String', 783 | $: 100, 784 | }; 785 | const buf1 = hessian.encode({ 786 | $class: 'java.lang.String', 787 | $: '100', 788 | }, version); 789 | const buf2 = encode(obj, version, classMap); 790 | assert.deepEqual(buf1, buf2); 791 | 792 | const buf3 = encode(obj, version, classMap); 793 | assert.deepEqual(buf1, buf3); 794 | }); 795 | 796 | describe('array', () => { 797 | it('should encode array', () => { 798 | const obj = { 799 | $class: 'int', 800 | $: [ 1, 2, 3 ], 801 | isArray: true, 802 | }; 803 | const buf1 = hessian.encode({ $class: '[int', $: [{ $class: 'int', $: 1 }, { $class: 'int', $: 2 }, { $class: 'int', $: 3 }] }, version); 804 | const buf2 = encode(obj, version, {}); 805 | assert.deepEqual(buf1, buf2); 806 | 807 | const buf3 = encode(obj, version, {}); 808 | assert.deepEqual(buf1, buf3); 809 | }); 810 | 811 | it('should encode array 2', () => { 812 | const obj = { 813 | $class: 'int', 814 | $: [ 1, 2, 3 ], 815 | isArray: true, 816 | }; 817 | const buf1 = hessian.encode({ $class: '[int', $: [{ $class: 'int', $: 1 }, { $class: 'int', $: 2 }, { $class: 'int', $: 3 }] }, version); 818 | const buf2 = encode(obj, version, {}); 819 | assert.deepEqual(buf1, buf2); 820 | 821 | const buf3 = encode({ $class: '[int', $: [{ $class: 'int', $: 1 }, { $class: 'int', $: 2 }, { $class: 'int', $: 3 }] }, version, {}); 822 | assert.deepEqual(buf1, buf3); 823 | }); 824 | 825 | it('should encode multi-dimentional array', () => { 826 | const obj = { 827 | $class: 'java.lang.String', 828 | $: [ 829 | [ null, 'a', '1' ], 830 | ], 831 | isArray: true, 832 | arrayDepth: 2, 833 | }; 834 | const buf1 = hessian.encode({ 835 | $class: '[[java.lang.String', 836 | $: [{ 837 | $class: '[java.lang.String', 838 | $: [ 839 | { $class: 'java.lang.String', $: null }, 840 | { $class: 'java.lang.String', $: 'a' }, 841 | { $class: 'java.lang.String', $: '1' }, 842 | ], 843 | }], 844 | }, version); 845 | const buf2 = encode(obj, version, {}); 846 | assert.deepEqual(buf1, buf2); 847 | 848 | const buf3 = encode(obj, version, {}); 849 | assert.deepEqual(buf1, buf3); 850 | }); 851 | 852 | it('should encode java.lang.Class array', () => { 853 | const obj = { 854 | $class: 'java.lang.Class', 855 | $: [{ 856 | name: '[java.lang.String', 857 | }, '[Ljava.lang.String;', '[Ljava.lang.String' ], 858 | isArray: true, 859 | }; 860 | const buf1 = hessian.encode({ 861 | $class: '[java.lang.Class', 862 | $: [{ 863 | $class: 'java.lang.Class', 864 | $: { name: '[java.lang.String' }, 865 | }, { 866 | $class: 'java.lang.Class', 867 | $: { name: '[Ljava.lang.String;' }, 868 | }, { 869 | $class: 'java.lang.Class', 870 | $: { name: '[Ljava.lang.String;' }, 871 | }], 872 | }, version); 873 | const buf2 = encode(obj, version, {}); 874 | assert.deepEqual(buf1, buf2); 875 | 876 | const buf3 = encode(obj, version, {}); 877 | assert.deepEqual(buf1, buf3); 878 | }); 879 | 880 | it('should encode java.util.Locale array', () => { 881 | const obj = { 882 | $class: 'java.util.Locale', 883 | $: [ 884 | 'zh_CN', 885 | 'en_US', 886 | ], 887 | isArray: true, 888 | }; 889 | const buf1 = hessian.encode({ 890 | $class: '[com.caucho.hessian.io.LocaleHandle', 891 | $: [ 892 | { $class: 'com.caucho.hessian.io.LocaleHandle', $: { value: 'zh_CN' } }, 893 | { $class: 'com.caucho.hessian.io.LocaleHandle', $: { value: 'en_US' } }, 894 | ], 895 | }, version); 896 | const buf2 = encode(obj, version, {}); 897 | assert.deepEqual(buf1, buf2); 898 | 899 | const buf3 = encode(obj, version, {}); 900 | assert.deepEqual(buf1, buf3); 901 | }); 902 | 903 | it('should encode java.math.BigDecimal array', () => { 904 | const obj = { 905 | $class: 'java.math.BigDecimal', 906 | $: [ 907 | '100.06', 908 | '200.07', 909 | ], 910 | isArray: true, 911 | }; 912 | const buf1 = hessian.encode(java.array.BigDecimal(obj.$), version); 913 | const buf2 = encode(obj, version, {}); 914 | assert.deepEqual(buf1, buf2); 915 | 916 | const buf3 = encode(obj, version, {}); 917 | assert.deepEqual(buf1, buf3); 918 | }); 919 | 920 | it('should encode java.util.Currency array', () => { 921 | const obj = { 922 | $class: 'java.util.Currency', 923 | $: [ 924 | 'CNY', 925 | 'USD', 926 | ], 927 | isArray: true, 928 | }; 929 | const buf1 = hessian.encode({ 930 | $class: '[java.util.Currency', 931 | $: [ 932 | { $class: 'java.util.Currency', $: { currencyCode: 'CNY' } }, 933 | { $class: 'java.util.Currency', $: { currencyCode: 'USD' } }, 934 | ], 935 | }, version); 936 | const buf2 = encode(obj, version, {}); 937 | assert.deepEqual(buf1, buf2); 938 | 939 | const buf3 = encode(obj, version, {}); 940 | assert.deepEqual(buf1, buf3); 941 | }); 942 | 943 | it('should encode byte array', () => { 944 | const obj = { 945 | $class: 'byte', 946 | $: Buffer.from('hello world'), 947 | isArray: true, 948 | }; 949 | const buf1 = hessian.encode(Buffer.from('hello world'), version); 950 | const buf2 = encode(obj, version, {}); 951 | assert.deepEqual(buf1, buf2); 952 | 953 | const buf3 = encode(obj, version, {}); 954 | assert.deepEqual(buf1, buf3); 955 | }); 956 | 957 | it('should encode java.lang.Byte array', () => { 958 | const obj = { 959 | $class: 'java.lang.Byte', 960 | $: Buffer.from('hello world'), 961 | isArray: true, 962 | }; 963 | const buf1 = hessian.encode(Buffer.from('hello world'), version); 964 | const buf2 = encode(obj, version, {}); 965 | assert.deepEqual(buf1, buf2); 966 | 967 | const buf3 = encode(obj, version, {}); 968 | assert.deepEqual(buf1, buf3); 969 | }); 970 | }); 971 | 972 | describe('java.lang.Object', () => { 973 | [ 974 | [ 'boolean', true ], 975 | [ 'java.lang.Boolean', false ], 976 | [ 'int', 10 ], 977 | [ 'java.lang.Integer', 1000 ], 978 | [ 'short', 7 ], 979 | [ 'java.lang.Short', 9 ], 980 | [ 'long', Date.now() ], 981 | [ 'java.lang.Long', Date.now() ], 982 | [ 'double', 9.99 ], 983 | [ 'java.lang.Double', 100.1 ], 984 | [ 'float', 20.2 ], 985 | [ 'java.lang.Float', 7.77 ], 986 | [ 'byte', 1 ], 987 | [ 'java.lang.Byte', 0 ], 988 | [ 'char', 's' ], 989 | [ 'java.lang.Character', 'S' ], 990 | [ 'java.lang.String', 'hello world' ], 991 | [ 'java.util.Map', { foo: 'bar' }], 992 | [ 'java.util.HashMap', { foo: 'bar' }], 993 | [ 'java.util.Date', new Date() ], 994 | [ 'array', [ 1, 2 ]], 995 | [ 'buffer', Buffer.from('hello world') ], 996 | [ 'long', Long.ZERO ], 997 | [ 'null', null ], 998 | ].forEach(item => { 999 | it('should encode ' + item[0], () => { 1000 | const obj = { 1001 | $class: 'java.lang.Object', 1002 | $: item[1], 1003 | }; 1004 | const buf1 = hessian.encode(item[1], version); 1005 | const buf2 = encode(obj, version, classMap); 1006 | assert.deepEqual(buf1, buf2); 1007 | 1008 | const buf3 = encode(obj, version, classMap); 1009 | assert.deepEqual(buf1, buf3); 1010 | }); 1011 | }); 1012 | }); 1013 | 1014 | it('should encode complex object', () => { 1015 | const obj = { 1016 | $class: 'com.alipay.test.TestObj', 1017 | $: { 1018 | b: true, 1019 | name: 'testname', 1020 | field: 'xxxxx', 1021 | testObj2: { name: 'xxx', finalField: 'xxx' }, 1022 | testEnum: { name: 'B' }, 1023 | testEnum2: [{ name: 'B' }, { name: 'C' }], 1024 | bs: Buffer.from([ 0x02, 0x00, 0x01, 0x07 ]), 1025 | list1: [{ name: 'A' }, { name: 'B' }], 1026 | list2: [ 2017, 2016 ], 1027 | list3: [{ name: 'aaa', finalField: 'xxx' }, 1028 | { name: 'bbb', finalField: 'xxx' }, 1029 | ], 1030 | list4: [ 'xxx', 'yyy' ], 1031 | list5: [ Buffer.from([ 0x02, 0x00, 0x01, 0x07 ]), Buffer.from([ 0x02, 0x00, 0x01, 0x06 ]) ], 1032 | map1: { 2017: { name: 'B' } }, 1033 | map2: new Map([ 1034 | [ 2107, 2016 ], 1035 | ]), 1036 | map3: {}, 1037 | map4: { xxx: 'yyy' }, 1038 | map5: { 2017: Buffer.from([ 0x02, 0x00, 0x01, 0x06 ]) }, 1039 | }, 1040 | }; 1041 | 1042 | const converted = { 1043 | $class: 'com.alipay.test.TestObj', 1044 | $: { 1045 | b: { $class: 'boolean', $: true }, 1046 | testObj2: { $class: 'com.alipay.test.sub.TestObj2', $: { name: { $class: 'java.lang.String', $: 'xxx' }, finalField: { $class: 'java.lang.String', $: 'xxx' } } }, 1047 | name: { $class: 'java.lang.String', $: 'testname' }, 1048 | field: { $class: 'java.lang.String', $: 'xxxxx' }, 1049 | testEnum: { $class: 'com.alipay.test.TestEnum', $: { name: 'B' } }, 1050 | testEnum2: { 1051 | $class: '[com.alipay.test.TestEnum', 1052 | $: [{ $class: 'com.alipay.test.TestEnum', $: { name: 'B' } }, 1053 | { $class: 'com.alipay.test.TestEnum', $: { name: 'C' } }, 1054 | ], 1055 | }, 1056 | bs: Buffer.from([ 0x02, 0x00, 0x01, 0x07 ]), 1057 | list1: { $class: 'java.util.List', $: [{ $class: 'com.alipay.test.TestEnum', $: { name: 'A' } }, { $class: 'com.alipay.test.TestEnum', $: { name: 'B' } }] }, 1058 | list2: { $class: 'java.util.List', $: [{ $class: 'java.lang.Integer', $: 2017 }, { $class: 'java.lang.Integer', $: 2016 }] }, 1059 | list3: { 1060 | $class: 'java.util.List', 1061 | $: [ 1062 | { $class: 'com.alipay.test.sub.TestObj2', $: { name: { $class: 'java.lang.String', $: 'aaa' }, finalField: { $class: 'java.lang.String', $: 'xxx' } } }, 1063 | { $class: 'com.alipay.test.sub.TestObj2', $: { name: { $class: 'java.lang.String', $: 'bbb' }, finalField: { $class: 'java.lang.String', $: 'xxx' } } }, 1064 | ], 1065 | }, 1066 | list4: { $class: 'java.util.List', $: [{ $class: 'java.lang.String', $: 'xxx' }, { $class: 'java.lang.String', $: 'yyy' }] }, 1067 | list5: { 1068 | $class: 'java.util.List', 1069 | $: [ 1070 | Buffer.from([ 0x02, 0x00, 0x01, 0x07 ]), Buffer.from([ 0x02, 0x00, 0x01, 0x06 ]), 1071 | ], 1072 | }, 1073 | map1: { 1074 | $class: 'java.util.Map', 1075 | $: new Map([ 1076 | [{ $class: 'java.lang.Long', $: 2017 }, { $class: 'com.alipay.test.TestEnum', $: { name: 'B' } }], 1077 | ]), 1078 | }, 1079 | map2: { 1080 | $class: 'java.util.Map', 1081 | $: new Map([ 1082 | [{ $class: 'java.lang.Integer', $: 2107 }, { $class: 'java.lang.Integer', $: 2016 }], 1083 | ]), 1084 | }, 1085 | map3: { $class: 'java.util.Map', $: {} }, 1086 | map4: { $class: 'java.util.Map', $: { xxx: { $class: 'java.lang.String', $: 'yyy' } } }, 1087 | map5: { $class: 'java.util.Map', $: { 2017: Buffer.from([ 0x02, 0x00, 0x01, 0x06 ]) } }, 1088 | }, 1089 | }; 1090 | const buf1 = hessian.encode(converted, version); 1091 | const buf2 = encode(obj, version, classMap); 1092 | assert.deepEqual(buf1, buf2); 1093 | 1094 | const buf3 = encode(obj, version, classMap); 1095 | assert.deepEqual(buf1, buf3); 1096 | 1097 | const buf4 = encode(converted, version); 1098 | assert.deepEqual(buf1, buf4); 1099 | }); 1100 | 1101 | it('should support generic with typeAliasIndex', () => { 1102 | const obj = { 1103 | $class: 'com.alipay.test.Request', 1104 | $: { 1105 | data: '123', 1106 | }, 1107 | generic: [{ type: 'java.lang.String' }], 1108 | }; 1109 | const buf1 = hessian.encode({ 1110 | $class: 'com.alipay.test.Request', 1111 | $: { 1112 | data: { $class: 'java.lang.String', $: '123' }, 1113 | }, 1114 | }, version); 1115 | const buf2 = encode(obj, version, classMap); 1116 | assert.deepEqual(buf1, buf2); 1117 | 1118 | const buf3 = encode(obj, version, classMap); 1119 | assert.deepEqual(buf1, buf3); 1120 | }); 1121 | 1122 | it('should support nested generic with typeAliasIndex', () => { 1123 | const obj = { 1124 | $class: 'com.eggjs.dubbo.GenericResult', 1125 | $: { 1126 | success: true, 1127 | result: [{ 1128 | hello: 'world', 1129 | }], 1130 | error: { 1131 | name: 'MockError', 1132 | message: 'mock MockError', 1133 | }, 1134 | }, 1135 | generic: [{ 1136 | type: 'java.util.List', 1137 | generic: [{ 1138 | type: 'com.eggjs.dubbo.HelloResponse', 1139 | }], 1140 | }, { 1141 | type: 'com.eggjs.dubbo.HelloError', 1142 | }], 1143 | }; 1144 | const buf1 = hessian.encode({ 1145 | $class: 'com.eggjs.dubbo.GenericResult', 1146 | $: { 1147 | success: { 1148 | $class: 'java.lang.Boolean', 1149 | $: true, 1150 | }, 1151 | result: { 1152 | $class: 'java.util.List', 1153 | $: [{ 1154 | $class: 'com.eggjs.dubbo.HelloResponse', 1155 | $: { 1156 | hello: { 1157 | $class: 'java.lang.String', 1158 | $: 'world', 1159 | }, 1160 | }, 1161 | }], 1162 | }, 1163 | error: { 1164 | $class: 'com.eggjs.dubbo.HelloError', 1165 | $: { 1166 | name: { 1167 | $class: 'java.lang.String', 1168 | $: 'MockError', 1169 | }, 1170 | message: { 1171 | $class: 'java.lang.String', 1172 | $: 'mock MockError', 1173 | }, 1174 | }, 1175 | }, 1176 | }, 1177 | }, version); 1178 | const buf2 = encode(obj, version, classMap); 1179 | assert.deepEqual(buf1, buf2); 1180 | 1181 | const buf3 = encode(obj, version, classMap); 1182 | assert.deepEqual(buf1, buf3); 1183 | }); 1184 | 1185 | it('should class inheritance', () => { 1186 | const obj = { 1187 | $class: 'com.alipay.test.Father', 1188 | $: { 1189 | $class: 'com.alipay.test.Child', 1190 | $: { 1191 | foo: 'bar', 1192 | bar: 'foo', 1193 | }, 1194 | }, 1195 | }; 1196 | const buf1 = hessian.encode({ 1197 | $class: 'com.alipay.test.Child', 1198 | $: { 1199 | foo: { 1200 | $class: 'java.lang.String', 1201 | $: 'bar', 1202 | }, 1203 | bar: { 1204 | $class: 'java.lang.String', 1205 | $: 'foo', 1206 | }, 1207 | }, 1208 | }, version); 1209 | const buf2 = encode(obj, version, classMap); 1210 | assert.deepEqual(buf1, buf2); 1211 | 1212 | const buf3 = encode(obj, version, classMap); 1213 | assert.deepEqual(buf1, buf3); 1214 | }); 1215 | 1216 | it('should handle same enum defaultValue', () => { 1217 | const buf1 = hessian.encode({ 1218 | $class: 'com.sofa.TestObject', 1219 | $: { 1220 | oneEnum: { $class: 'com.sofa.OneEnum', $: { name: 'DEFAULT' } }, 1221 | twoEnum: { $class: 'com.sofa.TwoEnum', $: { name: 'DEFAULT' } }, 1222 | }, 1223 | }, version); 1224 | const buf2 = encode({ 1225 | $class: 'com.sofa.TestObject', 1226 | $: {}, 1227 | }, version, classMap); 1228 | assert.deepEqual(buf1, buf2); 1229 | }); 1230 | }); 1231 | }); 1232 | 1233 | it('should handle generic', () => { 1234 | const info = { 1235 | $class: 'java.util.List', 1236 | $: [], 1237 | generic: [{ 1238 | type: 'java.util.List', 1239 | generic: [{ type: 'com.sofa.testObject' }], 1240 | }], 1241 | }; 1242 | let id = utils.normalizeUniqId(info, '2.0'); 1243 | assert(id === 'java.util.List#java.util.List#com.sofa.testObject#2.0'); 1244 | id = utils.normalizeUniqId(info, '1.0'); 1245 | assert(id === 'java.util.List#java.util.List#com.sofa.testObject#1.0'); 1246 | }); 1247 | }); 1248 | -------------------------------------------------------------------------------- /test/index.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const protocol = require('../'); 4 | const assert = require('assert'); 5 | const urlparse = require('url').parse; 6 | const awaitEvent = require('await-event'); 7 | const PassThrough = require('stream').PassThrough; 8 | 9 | describe('test/index.test.js', () => { 10 | it('should has name', () => { 11 | assert(protocol.name === 'dubbo'); 12 | }); 13 | 14 | it('should create encoder ok', () => { 15 | const sentReqs = new Map(); 16 | const address = urlparse('dubbo://127.0.0.1:12200?serialization=hessian', true); 17 | let encoder = protocol.encoder({ address, sentReqs }); 18 | assert(encoder.protocolType === 'dubbo'); 19 | assert(encoder.codecType === 'hessian'); 20 | 21 | encoder = protocol.encoder({ sentReqs }); 22 | assert(encoder.protocolType === 'dubbo'); 23 | assert(encoder.codecType === 'hessian2'); 24 | 25 | encoder = protocol.encoder({ sentReqs, codecType: 'protobuf' }); 26 | assert(encoder.protocolType === 'dubbo'); 27 | assert(encoder.codecType === 'protobuf'); 28 | 29 | encoder.codecType = 'hessian2'; 30 | assert(encoder.codecType === 'hessian2'); 31 | }); 32 | 33 | const reqSample = { 34 | args: [ 1, 2 ], 35 | serverSignature: 'com.alipay.test.TestService:1.0', 36 | methodName: 'plus', 37 | requestProps: { 38 | foo: 'bar', 39 | }, 40 | timeout: 3000, 41 | }; 42 | const resSample = { 43 | isError: false, 44 | errorMsg: null, 45 | appResponse: { 46 | $class: 'java.lang.Integer', 47 | $: 3, 48 | }, 49 | }; 50 | 51 | it('should encode request', async function() { 52 | const codecType = 'hessian2'; 53 | const protocolType = 'dubbo'; 54 | const address = urlparse('dubbo://127.0.0.1:12200?serialization=hessian2'); 55 | const sentReqs = new Map(); 56 | const socket = new PassThrough(); 57 | const encoder = protocol.encoder({ sentReqs, address }); 58 | const decoder = protocol.decoder({ sentReqs }); 59 | encoder.pipe(socket).pipe(decoder); 60 | 61 | setImmediate(() => { 62 | encoder.writeRequest(1, Object.assign({}, reqSample)); 63 | }); 64 | 65 | let req = await awaitEvent(decoder, 'request'); 66 | assert(req.packetId === 1); 67 | assert(req.packetType === 'request'); 68 | assert(req.data && req.data.methodName === reqSample.methodName); 69 | assert(req.data.serverSignature === reqSample.serverSignature); 70 | assert.deepEqual(req.data.args, reqSample.args); 71 | assert.deepEqual(req.data.requestProps, { dubbo: '5.3.0', path: 'com.alipay.test.TestService', version: '1.0', foo: 'bar', group: '' }); 72 | assert(req.options && req.options.protocolType === protocolType); 73 | assert(req.options.codecType === codecType); 74 | assert(req.meta); 75 | assert(req.meta.size > 0); 76 | assert(req.meta.start > 0); 77 | assert(req.meta.rt >= 0); 78 | 79 | setImmediate(() => { 80 | encoder.writeRequest(2, Object.assign({}, reqSample)); 81 | }); 82 | 83 | req = await awaitEvent(decoder, 'request'); 84 | assert(req.packetId === 2); 85 | assert(req.packetType === 'request'); 86 | assert(req.data && req.data.methodName === reqSample.methodName); 87 | assert(req.data.serverSignature === reqSample.serverSignature); 88 | assert.deepEqual(req.data.args, reqSample.args); 89 | assert.deepEqual(req.data.requestProps, { dubbo: '5.3.0', path: 'com.alipay.test.TestService', version: '1.0', foo: 'bar', group: '' }); 90 | assert(req.options && req.options.protocolType === protocolType); 91 | assert(req.options.codecType === codecType); 92 | assert(req.meta); 93 | assert(req.meta.size > 0); 94 | assert(req.meta.start > 0); 95 | assert(req.meta.rt >= 0); 96 | 97 | setImmediate(() => { 98 | encoder.writeResponse(req, resSample); 99 | }); 100 | 101 | const res = await awaitEvent(decoder, 'response'); 102 | assert(res.packetId === 2); 103 | assert(res.packetType === 'response'); 104 | assert.deepEqual(res.data, { error: null, appResponse: 3, responseProps: null }); 105 | assert(res.options && res.options.protocolType === protocolType); 106 | assert(res.options.codecType === codecType); 107 | assert(res.meta); 108 | assert(res.meta.size > 0); 109 | assert(res.meta.start > 0); 110 | assert(res.meta.rt >= 0); 111 | }); 112 | 113 | it('should encode request without version', async function() { 114 | const codecType = 'hessian2'; 115 | const protocolType = 'dubbo'; 116 | const address = urlparse('dubbo://127.0.0.1:12200?serialization=hessian2'); 117 | const sentReqs = new Map(); 118 | const socket = new PassThrough(); 119 | const encoder = protocol.encoder({ sentReqs, address }); 120 | const decoder = protocol.decoder({ sentReqs }); 121 | encoder.pipe(socket).pipe(decoder); 122 | 123 | setImmediate(() => { 124 | encoder.writeRequest(1, Object.assign({}, { 125 | args: [ 1, 2 ], 126 | serverSignature: 'com.alipay.test.TestService', 127 | methodName: 'plus', 128 | group: 'HSF', 129 | requestProps: { 130 | foo: 'bar', 131 | }, 132 | timeout: 3000, 133 | })); 134 | }); 135 | 136 | const req = await awaitEvent(decoder, 'request'); 137 | assert(req.packetId === 1); 138 | assert(req.packetType === 'request'); 139 | assert(req.data && req.data.methodName === reqSample.methodName); 140 | assert(req.data.serverSignature === 'com.alipay.test.TestService'); 141 | assert.deepEqual(req.data.args, reqSample.args); 142 | assert.deepEqual(req.data.requestProps, { dubbo: '5.3.0', path: 'com.alipay.test.TestService', foo: 'bar', group: 'HSF' }); 143 | assert(req.options && req.options.protocolType === protocolType); 144 | assert(req.options.codecType === codecType); 145 | assert(req.meta); 146 | assert(req.meta.size > 0); 147 | assert(req.meta.start > 0); 148 | assert(req.meta.rt >= 0); 149 | }); 150 | 151 | it('should encode error response', async function() { 152 | const codecType = 'hessian2'; 153 | const protocolType = 'dubbo'; 154 | const address = urlparse('dubbo://127.0.0.1:12200?serialization=hessian2'); 155 | const sentReqs = new Map(); 156 | const socket = new PassThrough(); 157 | const encoder = protocol.encoder({ sentReqs, address }); 158 | const decoder = protocol.decoder({ sentReqs }); 159 | encoder.pipe(socket).pipe(decoder); 160 | 161 | setImmediate(() => { 162 | encoder.writeRequest(1, { 163 | args: [ 1, 2 ], 164 | serverSignature: 'com.alipay.test.TestService:1.0', 165 | methodName: 'plus', 166 | requestProps: null, 167 | timeout: 3000, 168 | }, err => { 169 | err && console.log(err); 170 | }); 171 | }); 172 | 173 | const req = await awaitEvent(decoder, 'request'); 174 | 175 | setImmediate(() => { 176 | encoder.writeResponse(req, { 177 | isError: true, 178 | errorMsg: 'mock error message', 179 | appResponse: null, 180 | }); 181 | }); 182 | let res = await awaitEvent(decoder, 'response'); 183 | 184 | assert(res.packetId === 1); 185 | assert(res.packetType === 'response'); 186 | assert(res.options && res.options.protocolType === protocolType); 187 | assert(res.options.codecType === codecType); 188 | assert(res.data && res.data.error); 189 | assert(!res.data.appResponse); 190 | assert(res.data.error.message.includes('mock error message')); 191 | 192 | req.options.protocolType = 'dubbo'; 193 | req.options.codecType = 'hessian2'; 194 | 195 | setImmediate(() => { 196 | encoder.writeResponse(req, { 197 | isError: true, 198 | errorMsg: 'xxx error', 199 | appResponse: null, 200 | }); 201 | }); 202 | res = await awaitEvent(decoder, 'response'); 203 | assert(res.packetId === 1); 204 | assert(res.packetType === 'response'); 205 | assert(res.options && res.options.protocolType === 'dubbo'); 206 | assert(res.options.codecType === 'hessian2'); 207 | assert(res.data && res.data.error); 208 | assert(!res.data.appResponse); 209 | assert(res.data.error.message.includes('xxx error')); 210 | 211 | setImmediate(() => { 212 | encoder.writeResponse(req, { 213 | isError: true, 214 | errorMsg: null, 215 | appResponse: null, 216 | }); 217 | }); 218 | res = await awaitEvent(decoder, 'response'); 219 | assert(res.packetId === 1); 220 | assert(res.packetType === 'response'); 221 | assert(res.options && res.options.protocolType === 'dubbo'); 222 | assert(res.options.codecType === 'hessian2'); 223 | assert(res.data && res.data.error); 224 | assert(!res.data.appResponse); 225 | assert(res.data.error.message.includes('Exception caught in invocation')); 226 | }); 227 | 228 | it('should encode biz error response', async function() { 229 | const codecType = 'hessian2'; 230 | const protocolType = 'dubbo'; 231 | const address = urlparse('dubbo://127.0.0.1:12200?serialization=hessian2'); 232 | const sentReqs = new Map(); 233 | const socket = new PassThrough(); 234 | const encoder = protocol.encoder({ sentReqs, address }); 235 | const decoder = protocol.decoder({ sentReqs }); 236 | encoder.pipe(socket).pipe(decoder); 237 | 238 | setImmediate(() => { 239 | encoder.writeRequest(1, { 240 | args: [ 1, 2 ], 241 | serverSignature: 'com.alipay.test.TestService:1.0', 242 | methodName: 'plus', 243 | requestProps: null, 244 | uniformContextHeaders: null, 245 | timeout: 3000, 246 | }); 247 | }); 248 | const req = await awaitEvent(decoder, 'request'); 249 | 250 | setImmediate(() => { 251 | encoder.writeResponse(req, { 252 | isError: false, 253 | errorMsg: null, 254 | appResponse: { 255 | $class: 'java.lang.Exception', 256 | $: new Error('mock error'), 257 | }, 258 | }); 259 | }); 260 | const res = await awaitEvent(decoder, 'response'); 261 | 262 | assert(res.packetId === 1); 263 | assert(res.packetType === 'response'); 264 | assert(res.options && res.options.protocolType === protocolType); 265 | assert(res.options.codecType === codecType); 266 | assert(res.data && res.data.error); 267 | assert(!res.data.appResponse); 268 | assert(res.data.error.message.includes('mock error')); 269 | }); 270 | 271 | describe('heartbeat', () => { 272 | it('should encode heartbeat', async function() { 273 | const codecType = 'hessian2'; 274 | const protocolType = 'dubbo'; 275 | const address = urlparse('dubbo://127.0.0.1:12200?serialization=hessian2'); 276 | const sentReqs = new Map(); 277 | const socket = new PassThrough(); 278 | const encoder = protocol.encoder({ sentReqs, address }); 279 | const decoder = protocol.decoder({ sentReqs }); 280 | encoder.pipe(socket).pipe(decoder); 281 | 282 | setImmediate(() => { 283 | encoder.writeHeartbeat(1, { clientUrl: 'xxx' }); 284 | }); 285 | const hb = await awaitEvent(decoder, 'heartbeat'); 286 | 287 | assert(hb.packetId === 1); 288 | assert(hb.packetType === 'heartbeat'); 289 | assert(hb.options && hb.options.protocolType === protocolType); 290 | assert(hb.options.codecType === codecType); 291 | 292 | setImmediate(() => { 293 | encoder.writeHeartbeatAck(hb); 294 | }); 295 | 296 | const hbAck = await awaitEvent(decoder, 'heartbeat_ack'); 297 | assert(hbAck.packetId === 1); 298 | assert(hbAck.packetType === 'heartbeat_ack'); 299 | assert(hbAck.options && hbAck.options.protocolType === protocolType); 300 | assert(hbAck.options.codecType === codecType); 301 | }); 302 | }); 303 | }); 304 | -------------------------------------------------------------------------------- /test/protocol.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const protocol = require('../lib/protocol'); 5 | const classMap = require('./fixtures/class_map'); 6 | 7 | describe('test/protocol.test.js', () => { 8 | it('should requestEncode ok', () => { 9 | const buf = protocol.requestEncode(1, { 10 | args: [{ 11 | $class: 'java.lang.String', 12 | $: 'test', 13 | }], 14 | serverSignature: 'com.test.TestService:1.0', 15 | methodName: 'test', 16 | timeout: 300000, 17 | group: 'HSF', 18 | }); 19 | assert.deepEqual(Buffer.from('dabbc20000000000000000010000007a05352e332e3014636f6d2e746573742e546573745365727669636503312e300474657374124c6a6176612f6c616e672f537472696e673b04746573744805647562626f05352e332e300567726f757003485346047061746814636f6d2e746573742e54657374536572766963650776657273696f6e03312e305a', 'hex'), buf); 20 | 21 | const req = protocol.decode(buf); 22 | assert(req.meta && req.meta.size === 138); 23 | delete req.meta; 24 | assert.deepEqual({ 25 | packetId: 1, 26 | packetType: 'request', 27 | data: { 28 | methodName: 'test', 29 | serverSignature: 'com.test.TestService:1.0', 30 | args: [ 'test' ], 31 | methodArgSigs: [ 'java.lang.String' ], 32 | requestProps: { dubbo: '5.3.0', path: 'com.test.TestService', version: '1.0', group: 'HSF' }, 33 | }, 34 | options: { 35 | protocolType: 'dubbo', 36 | codecType: 'hessian2', 37 | classMap: undefined, 38 | }, 39 | }, req); 40 | 41 | }); 42 | 43 | it('should responseEncode ok', () => { 44 | const buf = protocol.responseEncode(1, { 45 | isError: false, 46 | appResponse: 'ok', 47 | }); 48 | assert.deepEqual(Buffer.from('dabb021400000000000000010000000491026f6b', 'hex'), buf); 49 | 50 | const res = protocol.decode(buf); 51 | assert(res.meta && res.meta.size === 20); 52 | delete res.meta; 53 | assert.deepEqual({ 54 | packetId: 1, 55 | packetType: 'response', 56 | data: { appResponse: 'ok', error: null, responseProps: null }, 57 | options: { 58 | protocolType: 'dubbo', 59 | codecType: 'hessian2', 60 | classMap: undefined, 61 | }, 62 | }, res); 63 | }); 64 | 65 | it('should heartbeatEncode ok', () => { 66 | const buf = protocol.heartbeatEncode(1); 67 | assert.deepEqual(Buffer.from('dabbe2000000000000000001000000014e', 'hex'), buf); 68 | 69 | const hb = protocol.decode(buf); 70 | assert(hb.meta && hb.meta.size === 17); 71 | delete hb.meta; 72 | assert.deepEqual({ 73 | packetId: 1, 74 | packetType: 'heartbeat', 75 | data: null, 76 | options: { 77 | protocolType: 'dubbo', 78 | codecType: 'hessian2', 79 | classMap: undefined, 80 | }, 81 | }, hb); 82 | 83 | }); 84 | 85 | it('should heartbeatAckEncode ok', () => { 86 | const buf = protocol.heartbeatAckEncode(1); 87 | assert.deepEqual(Buffer.from('dabb22140000000000000001000000014e', 'hex'), buf); 88 | 89 | const hbAck = protocol.decode(buf); 90 | assert(hbAck.meta && hbAck.meta.size === 17); 91 | delete hbAck.meta; 92 | assert.deepEqual({ 93 | packetId: 1, 94 | packetType: 'heartbeat_ack', 95 | data: null, 96 | options: { 97 | protocolType: 'dubbo', 98 | codecType: 'hessian2', 99 | classMap: undefined, 100 | }, 101 | }, hbAck); 102 | }); 103 | 104 | const obj = { 105 | b: true, 106 | name: 'testname', 107 | field: 'xxxxx', 108 | testObj2: { name: 'xxx', finalField: 'xxx' }, 109 | testEnum: { name: 'B' }, 110 | testEnum2: [{ name: 'B' }, { name: 'C' }], 111 | bs: Buffer.from([ 0x02, 0x00, 0x01, 0x07 ]), 112 | list1: [{ name: 'A' }, { name: 'B' }], 113 | list2: [ 2017, 2016 ], 114 | list3: [{ name: 'aaa', finalField: 'xxx' }, 115 | { name: 'bbb', finalField: 'xxx' }, 116 | ], 117 | list4: [ 'xxx', 'yyy' ], 118 | list5: [ Buffer.from([ 0x02, 0x00, 0x01, 0x07 ]), Buffer.from([ 0x02, 0x00, 0x01, 0x06 ]) ], 119 | map1: { 2017: { name: 'B' } }, 120 | map2: { 121 | 2107: 2106, 122 | }, 123 | map3: {}, 124 | map4: { xxx: 'yyy' }, 125 | map5: { 2017: Buffer.from([ 0x02, 0x00, 0x01, 0x06 ]) }, 126 | }; 127 | 128 | it('should encode complex object', () => { 129 | const options = { classMap }; 130 | const buf = protocol.requestEncode(1, { 131 | args: [{ 132 | $class: 'com.alipay.test.TestObj', 133 | $: obj, 134 | }], 135 | serverSignature: 'com.alipay.test.TestService:1.0', 136 | methodName: 'echoObj', 137 | requestProps: {}, 138 | timeout: 3000, 139 | group: 'HSF', 140 | }, options); 141 | const expect = Buffer.from('dabbc20000000000000000010000020905352e332e301b636f6d2e616c697061792e746573742e546573745365727669636503312e30076563686f4f626a194c636f6d2f616c697061792f746573742f546573744f626a3b4317636f6d2e616c697061792e746573742e546573744f626aa1016208746573744f626a32046e616d65056669656c640874657374456e756d0974657374456e756d32026273056c69737431056c69737432056c69737433056c69737434056c69737435046d617031046d617032046d617033046d617034046d6170356054431c636f6d2e616c697061792e746573742e7375622e546573744f626a3292046e616d650a66696e616c4669656c6461037878780378787808746573746e616d650578787878784318636f6d2e616c697061792e746573742e54657374456e756d91046e616d6562014272195b636f6d2e616c697061792e746573742e54657374456e756d6201426201432402000107720e6a6176612e7574696c2e4c6973746201416201427291cfe1cfe072916103616161037878786103626262037878787291037878780379797972912402000107240200010648ffe16201425a48d4083bd4083a5a485a4803787878037979795a48043230313724020001065a4805647562626f05352e332e300567726f75700348534604706174681b636f6d2e616c697061792e746573742e54657374536572766963650776657273696f6e03312e305a', 'hex'); 142 | assert.deepEqual(expect, buf); 143 | 144 | const req = protocol.decode(buf, options); 145 | assert(req.meta && req.meta.size === 537); 146 | delete req.meta; 147 | assert.deepEqual({ 148 | packetId: 1, 149 | packetType: 'request', 150 | data: { 151 | methodName: 'echoObj', 152 | serverSignature: 'com.alipay.test.TestService:1.0', 153 | args: [ obj ], 154 | methodArgSigs: [ 'com.alipay.test.TestObj' ], 155 | requestProps: { 156 | dubbo: '5.3.0', 157 | path: 'com.alipay.test.TestService', 158 | version: '1.0', 159 | group: 'HSF', 160 | }, 161 | }, 162 | options: { 163 | protocolType: 'dubbo', 164 | codecType: 'hessian2', 165 | classMap, 166 | }, 167 | }, req); 168 | }); 169 | 170 | it('should decode value with attachment', () => { 171 | const buf = Buffer.from('dabb021400000000000000010000004994303848656c6c6f207a6f6e6779752c20726573706f6e73652066726f6d2070726f76696465723a203139322e3136382e39392e313a32303838304805647562626f05322e302e325a', 'hex'); 172 | const resp = protocol.decode(buf); 173 | 174 | assert(resp.meta && resp.meta.size === 89); 175 | delete resp.meta; 176 | assert.deepEqual(resp, { 177 | packetId: 1, 178 | packetType: 'response', 179 | data: { 180 | appResponse: 'Hello zongyu, response from provider: 192.168.99.1:20880', 181 | error: null, 182 | responseProps: { dubbo: '2.0.2' }, 183 | }, 184 | options: { 185 | protocolType: 'dubbo', 186 | codecType: 'hessian2', 187 | classMap: undefined, 188 | }, 189 | }); 190 | }); 191 | }); 192 | -------------------------------------------------------------------------------- /test/serialize.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const serialization = require('../lib/serialize'); 5 | 6 | describe('test/serialize.test.js', () => { 7 | it('should getSerializationById & getSerializationByName', () => { 8 | assert(serialization.getSerializationById('2')); 9 | assert(serialization.getSerializationByName('hessian2')); 10 | }); 11 | 12 | describe('hessian2', () => { 13 | it('should readBool & writeBool ok', () => { 14 | const output = serialization.getSerializationById('2').serialize(); 15 | output.writeBool(true); 16 | const input = serialization.getSerializationById('2').deserialize(output.get()); 17 | assert(input.readBool() === true); 18 | }); 19 | 20 | it('should readByte & writeByte ok', () => { 21 | const output = serialization.getSerializationById('2').serialize(); 22 | output.writeByte(10); 23 | const input = serialization.getSerializationById('2').deserialize(output.get()); 24 | assert(input.readByte() === 10); 25 | }); 26 | 27 | it('should readShort & writeShort ok', () => { 28 | const output = serialization.getSerializationById('2').serialize(); 29 | output.writeShort(10); 30 | const input = serialization.getSerializationById('2').deserialize(output.get()); 31 | assert(input.readShort() === 10); 32 | }); 33 | 34 | it('should readInt & writeInt ok', () => { 35 | const output = serialization.getSerializationById('2').serialize(); 36 | output.writeInt(10); 37 | const input = serialization.getSerializationById('2').deserialize(output.get()); 38 | assert(input.readInt() === 10); 39 | }); 40 | 41 | it('should readLong & writeLong ok', () => { 42 | const output = serialization.getSerializationById('2').serialize(); 43 | output.writeLong(10); 44 | const input = serialization.getSerializationById('2').deserialize(output.get()); 45 | assert(input.readLong() === 10); 46 | }); 47 | 48 | it('should readFloat & writeFloat ok', () => { 49 | const output = serialization.getSerializationById('2').serialize(); 50 | output.writeFloat(10.1); 51 | const input = serialization.getSerializationById('2').deserialize(output.get()); 52 | assert(input.readFloat() === 10.1); 53 | }); 54 | 55 | it('should readDouble & writeDouble ok', () => { 56 | const output = serialization.getSerializationById('2').serialize(); 57 | output.writeDouble(10.1); 58 | const input = serialization.getSerializationById('2').deserialize(output.get()); 59 | assert(input.readDouble() === 10.1); 60 | }); 61 | 62 | it('should readBytes & writeBytes ok', () => { 63 | const output = serialization.getSerializationById('2').serialize(); 64 | output.writeBytes(Buffer.from('hello buffer')); 65 | const input = serialization.getSerializationById('2').deserialize(output.get()); 66 | assert.deepEqual(input.readBytes(), Buffer.from('hello buffer')); 67 | }); 68 | }); 69 | }); 70 | -------------------------------------------------------------------------------- /test/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | const path = require('path'); 5 | const fixtures = path.join(__dirname, 'fixtures'); 6 | 7 | exports.bytes = name => { 8 | if (!name.endsWith('.bin')) { 9 | name += '.bin'; 10 | } 11 | return fs.readFileSync(path.join(fixtures, name)); 12 | }; 13 | 14 | exports.write = (name, data, append) => { 15 | if (!name.endsWith('.bin')) { 16 | name += '.bin'; 17 | } 18 | if (append) { 19 | fs.appendFileSync(path.join(fixtures, name), data); 20 | } else { 21 | fs.writeFileSync(path.join(fixtures, name), data); 22 | } 23 | }; 24 | 25 | exports.createReadStream = name => { 26 | if (!name.endsWith('.bin')) { 27 | name += '.bin'; 28 | } 29 | return fs.createReadStream(path.join(fixtures, name)); 30 | }; 31 | -------------------------------------------------------------------------------- /test/utils.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const utils = require('../lib/utils'); 5 | 6 | describe('test/utils.test.js', () => { 7 | it('should getJavaArgsDesc & desc2classArray ok', () => { 8 | const desc = utils.getJavaArgsDesc([ 9 | 1, 1.22, 'a', true, null, undefined, 12323123123123, 10 | new Error('error'), new Date(), Buffer.from('buffer'), 11 | { foo: 'bar' }, { 12 | $class: 'testClass', 13 | $: {}, 14 | }, { 15 | $class: '[boolean', 16 | $: [ true ], 17 | }, { 18 | $class: 'long', 19 | $: 1, 20 | }, 21 | ]); 22 | assert(desc === 'IDLjava/lang/String;ZLjava/lang/Object;Ljava/lang/Object;JLjava/lang/Exception;Ljava/util/Date;[BLjava/util/HashMap;LtestClass;[ZJ'); 23 | const types = utils.desc2classArray(desc); 24 | assert.deepEqual(types, [ 25 | 'int', 26 | 'double', 27 | 'java.lang.String', 28 | 'boolean', 29 | 'java.lang.Object', 30 | 'java.lang.Object', 31 | 'long', 32 | 'java.lang.Exception', 33 | 'java.util.Date', 34 | '[byte', 35 | 'java.util.HashMap', 36 | 'testClass', 37 | '[boolean', 38 | 'long', 39 | ]); 40 | }); 41 | 42 | it('should desc2classArray ok', () => { 43 | assert.deepEqual(utils.desc2classArray('V'), [ 'void' ]); 44 | assert.deepEqual(utils.desc2classArray('Z'), [ 'boolean' ]); 45 | assert.deepEqual(utils.desc2classArray('B'), [ 'byte' ]); 46 | assert.deepEqual(utils.desc2classArray('C'), [ 'char' ]); 47 | assert.deepEqual(utils.desc2classArray('D'), [ 'double' ]); 48 | assert.deepEqual(utils.desc2classArray('F'), [ 'float' ]); 49 | assert.deepEqual(utils.desc2classArray('I'), [ 'int' ]); 50 | assert.deepEqual(utils.desc2classArray('J'), [ 'long' ]); 51 | assert.deepEqual(utils.desc2classArray('S'), [ 'short' ]); 52 | assert.throws(() => { 53 | utils.desc2classArray('A'); 54 | }, /\[double-remoting\] unknown class type => A/); 55 | }); 56 | }); 57 | --------------------------------------------------------------------------------