├── .editorconfig
├── .github
├── ISSUE_TEMPLATE.md
├── maven-cd-settings.xml
├── maven-ci-settings.xml
└── workflows
│ ├── ci-4.x.yml
│ ├── ci-5.x-stable.yml
│ ├── ci-5.x.yml
│ ├── ci-matrix-5.x.yml
│ ├── ci.yml
│ └── deploy.yml
├── .gitignore
├── LICENSE.txt
├── README.adoc
├── pom.xml
└── src
├── client
└── nodejs
│ ├── lib
│ └── tcp-vertx-eventbus.js
│ ├── package.json
│ └── test
│ ├── TcpEventBusBridgeEchoServer.java
│ └── index.js
├── main
├── asciidoc
│ └── index.adoc
└── java
│ ├── examples
│ └── TCPBridgeExamples.java
│ └── io
│ └── vertx
│ └── ext
│ └── eventbus
│ └── bridge
│ └── tcp
│ ├── BridgeEvent.java
│ ├── TcpEventBusBridge.java
│ ├── impl
│ ├── BridgeEventImpl.java
│ ├── TcpEventBusBridgeImpl.java
│ └── protocol
│ │ ├── FrameHelper.java
│ │ └── FrameParser.java
│ └── package-info.java
└── test
├── java
└── io
│ └── vertx
│ └── ext
│ └── eventbus
│ └── bridge
│ └── tcp
│ ├── SSLKeyPairCerts.java
│ ├── TcpEventBusBridgeEventTest.java
│ ├── TcpEventBusBridgeHookTest.java
│ ├── TcpEventBusBridgeInteropTest.java
│ ├── TcpEventBusBridgeTest.java
│ └── UnixDomainSocketTest.java
└── resources
└── .gitkeep
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 2
7 | trim_trailing_whitespace = true
8 | end_of_line = lf
9 | insert_final_newline = true
10 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ### Version
2 |
3 | * vert.x core:
4 | * vert.x tcp eventbus bridge:
5 |
6 | ### Context
7 |
8 | I encountered an exception which looks suspicious while ...
9 |
10 | ### Do you have a reproducer?
11 |
12 | * Link to github project/gist
13 |
14 | ### Steps to reproduce
15 |
16 | 1. ...
17 | 2. ...
18 | 3. ...
19 | 4. ...
20 |
21 | ### Extra
22 |
23 | * Anything that can be relevant
24 |
--------------------------------------------------------------------------------
/.github/maven-cd-settings.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 | false
20 |
21 |
22 |
23 | vertx-snapshots-repository
24 | ${env.VERTX_NEXUS_USERNAME}
25 | ${env.VERTX_NEXUS_PASSWORD}
26 |
27 |
28 |
29 |
30 |
31 | google-mirror
32 |
33 | true
34 |
35 |
36 |
37 | google-maven-central
38 | GCS Maven Central mirror EU
39 | https://maven-central.storage-download.googleapis.com/maven2/
40 |
41 | true
42 |
43 |
44 | false
45 |
46 |
47 |
48 |
49 |
50 | google-maven-central
51 | GCS Maven Central mirror
52 | https://maven-central.storage-download.googleapis.com/maven2/
53 |
54 | true
55 |
56 |
57 | false
58 |
59 |
60 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/.github/maven-ci-settings.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 | false
20 |
21 |
22 |
23 | google-mirror
24 |
25 | true
26 |
27 |
28 |
29 | google-maven-central
30 | GCS Maven Central mirror EU
31 | https://maven-central.storage-download.googleapis.com/maven2/
32 |
33 | true
34 |
35 |
36 | false
37 |
38 |
39 |
40 |
41 |
42 | google-maven-central
43 | GCS Maven Central mirror
44 | https://maven-central.storage-download.googleapis.com/maven2/
45 |
46 | true
47 |
48 |
49 | false
50 |
51 |
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/.github/workflows/ci-4.x.yml:
--------------------------------------------------------------------------------
1 | name: vertx-tcp-eventbus-bridge (4.x)
2 | on:
3 | schedule:
4 | - cron: '0 4 * * *'
5 | jobs:
6 | CI:
7 | strategy:
8 | matrix:
9 | include:
10 | - os: ubuntu-latest
11 | jdk: 8
12 | - os: ubuntu-latest
13 | jdk: 17
14 | uses: ./.github/workflows/ci.yml
15 | with:
16 | branch: 4.x
17 | jdk: ${{ matrix.jdk }}
18 | os: ${{ matrix.os }}
19 | secrets: inherit
20 | Deploy:
21 | if: ${{ github.repository_owner == 'vert-x3' && (github.event_name == 'push' || github.event_name == 'schedule') }}
22 | needs: CI
23 | uses: ./.github/workflows/deploy.yml
24 | with:
25 | branch: 4.x
26 | jdk: 8
27 | secrets: inherit
28 |
--------------------------------------------------------------------------------
/.github/workflows/ci-5.x-stable.yml:
--------------------------------------------------------------------------------
1 | name: vertx-tcp-eventbus-bridge (5.x-stable)
2 | on:
3 | push:
4 | branches:
5 | - '5.[0-9]+'
6 | pull_request:
7 | branches:
8 | - '5.[0-9]+'
9 | schedule:
10 | - cron: '0 6 * * *'
11 | jobs:
12 | CI-CD:
13 | uses: ./.github/workflows/ci-matrix-5.x.yml
14 | secrets: inherit
15 | with:
16 | branch: ${{ github.event_name == 'schedule' && vars.VERTX_5_STABLE_BRANCH || github.event.pull_request.head.sha || github.ref_name }}
17 |
--------------------------------------------------------------------------------
/.github/workflows/ci-5.x.yml:
--------------------------------------------------------------------------------
1 | name: vertx-tcp-eventbus-bridge (5.x)
2 | on:
3 | push:
4 | branches:
5 | - master
6 | pull_request:
7 | branches:
8 | - master
9 | schedule:
10 | - cron: '0 5 * * *'
11 | jobs:
12 | CI-CD:
13 | uses: ./.github/workflows/ci-matrix-5.x.yml
14 | secrets: inherit
15 | with:
16 | branch: ${{ github.event.pull_request.head.sha || github.ref_name }}
17 |
--------------------------------------------------------------------------------
/.github/workflows/ci-matrix-5.x.yml:
--------------------------------------------------------------------------------
1 | name: CI matrix (5.x)
2 | on:
3 | workflow_call:
4 | inputs:
5 | branch:
6 | required: true
7 | type: string
8 | jobs:
9 | CI:
10 | strategy:
11 | matrix:
12 | include:
13 | - os: ubuntu-latest
14 | jdk: 11
15 | - os: ubuntu-latest
16 | jdk: 21
17 | uses: ./.github/workflows/ci.yml
18 | with:
19 | branch: ${{ inputs.branch }}
20 | jdk: ${{ matrix.jdk }}
21 | os: ${{ matrix.os }}
22 | secrets: inherit
23 | Deploy:
24 | if: ${{ github.repository_owner == 'vert-x3' && (github.event_name == 'push' || github.event_name == 'schedule') }}
25 | needs: CI
26 | uses: ./.github/workflows/deploy.yml
27 | with:
28 | branch: ${{ inputs.branch }}
29 | jdk: 11
30 | secrets: inherit
31 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 | on:
3 | workflow_call:
4 | inputs:
5 | branch:
6 | required: true
7 | type: string
8 | jdk:
9 | default: 8
10 | type: string
11 | os:
12 | default: ubuntu-latest
13 | type: string
14 | jobs:
15 | Test:
16 | name: Run tests
17 | runs-on: ${{ inputs.os }}
18 | steps:
19 | - name: Checkout
20 | uses: actions/checkout@v2
21 | with:
22 | ref: ${{ inputs.branch }}
23 | - name: Install JDK
24 | uses: actions/setup-java@v2
25 | with:
26 | java-version: ${{ inputs.jdk }}
27 | distribution: temurin
28 | - name: Run tests
29 | run: mvn -s .github/maven-ci-settings.xml -q clean verify -B
30 |
--------------------------------------------------------------------------------
/.github/workflows/deploy.yml:
--------------------------------------------------------------------------------
1 | name: Deploy
2 | on:
3 | workflow_call:
4 | inputs:
5 | branch:
6 | required: true
7 | type: string
8 | jdk:
9 | default: 8
10 | type: string
11 | jobs:
12 | Deploy:
13 | name: Deploy to OSSRH
14 | runs-on: ubuntu-latest
15 | env:
16 | VERTX_NEXUS_USERNAME: ${{ secrets.VERTX_NEXUS_USERNAME }}
17 | VERTX_NEXUS_PASSWORD: ${{ secrets.VERTX_NEXUS_PASSWORD }}
18 | steps:
19 | - name: Checkout
20 | uses: actions/checkout@v2
21 | with:
22 | ref: ${{ inputs.branch }}
23 | - name: Install JDK
24 | uses: actions/setup-java@v2
25 | with:
26 | java-version: ${{ inputs.jdk }}
27 | distribution: temurin
28 | - name: Get project version
29 | run: echo "PROJECT_VERSION=$(mvn org.apache.maven.plugins:maven-help-plugin:evaluate -Dexpression=project.version -q -DforceStdout | grep -v '\[')" >> $GITHUB_ENV
30 | - name: Maven deploy
31 | if: ${{ endsWith(env.PROJECT_VERSION, '-SNAPSHOT') }}
32 | run: mvn deploy -s .github/maven-cd-settings.xml -DskipTests -B
33 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .gradle
3 | .idea
4 | .classpath
5 | .project
6 | .settings
7 | .yardoc
8 | .yardopts
9 | build
10 | target
11 | out
12 | *.iml
13 | *.ipr
14 | *.iws
15 | test-output
16 | Scratch.java
17 | ScratchTest.java
18 | test-results
19 | test-tmp
20 | *.class
21 | ScratchPad.java
22 | src/main/resources/ext-js/*.js
23 | src/main/java/io/vertx/java/**/*.java
24 | src/main/groovy/io/vertx/groovy/**/*.groovy
25 | src/main/generated
26 | *.swp
27 | cscope.*
28 | node_modules
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
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.adoc:
--------------------------------------------------------------------------------
1 | = Vert.x TCP EventBus bridge
2 |
3 | image:https://github.com/vert-x3/vertx-tcp-eventbus-bridge/actions/workflows/ci-5.x.yml/badge.svg[Build Status (5.x),link=https://github.com/vert-x3/vertx-tcp-eventbus-bridge/actions/workflows/ci-5.x.yml]
4 | image:https://github.com/vert-x3/vertx-tcp-eventbus-bridge/actions/workflows/ci-4.x.yml/badge.svg[Build Status (4.x),link=https://github.com/vert-x3/vertx-tcp-eventbus-bridge/actions/workflows/ci-4.x.yml]
5 |
6 | This is a TCP eventbus bridge implementation.
7 |
8 | Please see the in-source asciidoc documentation or the main documentation on the web-site for a full description of this component:
9 |
10 | * link:http://vertx.io/docs/vertx-tcp-eventbus-bridge/java/[web-site docs]
11 | * link:src/main/asciidoc/index.adoc[in-source docs]
12 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 | 4.0.0
19 |
20 |
21 | io.vertx
22 | vertx5-parent
23 | 12
24 |
25 |
26 | vertx-tcp-eventbus-bridge
27 | 5.1.0-SNAPSHOT
28 |
29 | Vert.x TCP EventBus Bridge
30 |
31 |
32 | scm:git:git@github.com:vert-x3/vertx-tcp-eventbus-bridge.git
33 | scm:git:git@github.com:vert-x3/vertx-tcp-eventbus-bridge.git
34 | git@github.com:vert-x3/vertx-tcp-eventbus-bridge.git
35 |
36 |
37 |
38 |
39 |
40 | io.vertx
41 | vertx-dependencies
42 | ${project.version}
43 | pom
44 | import
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 | io.vertx
53 | vertx-core
54 |
55 |
56 |
57 | io.vertx
58 | vertx-bridge-common
59 | ${project.version}
60 |
61 |
62 |
63 | io.vertx
64 | vertx-codegen-api
65 | true
66 |
67 |
68 | io.vertx
69 | vertx-codegen-json
70 | true
71 |
72 |
73 | io.vertx
74 | vertx-docgen-api
75 | true
76 |
77 |
78 | io.vertx
79 | vertx-core
80 | test-jar
81 | test
82 |
83 |
84 | junit
85 | junit
86 | test
87 | 4.13.1
88 |
89 |
90 | io.vertx
91 | vertx-unit
92 | test
93 |
94 |
95 | org.bouncycastle
96 | bcpkix-jdk15on
97 | 1.65
98 | test
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 | maven-compiler-plugin
107 |
108 |
109 | default-compile
110 |
111 |
112 |
113 | io.vertx
114 | vertx-codegen
115 | processor
116 |
117 |
118 | io.vertx
119 | vertx-docgen-processor
120 | processor
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 | maven-assembly-plugin
132 |
133 |
134 | package-docs
135 |
136 | single
137 |
138 |
139 |
140 |
141 |
142 |
143 |
--------------------------------------------------------------------------------
/src/client/nodejs/lib/tcp-vertx-eventbus.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by itersh on 25.10.16.
3 | */
4 | /*
5 | * Copyright (c) 2011-2015 The original author or authors
6 | * ------------------------------------------------------
7 | * All rights reserved. This program and the accompanying materials
8 | * are made available under the terms of the Eclipse Public License v1.0
9 | * and Apache License v2.0 which accompanies this distribution.
10 | *
11 | * The Eclipse Public License is available at
12 | * http://www.eclipse.org/legal/epl-v10.html
13 | *
14 | * The Apache License v2.0 is available at
15 | * http://www.opensource.org/licenses/apache2.0.php
16 | *
17 | * You may elect to redistribute this code under either of these licenses.
18 | */
19 | var net = require('net');
20 | var makeUUID = require('node-uuid').v4;
21 | var tls = require('tls');
22 |
23 | function mergeHeaders(defaultHeaders, headers) {
24 | if (defaultHeaders) {
25 | if(!headers) {
26 | return defaultHeaders;
27 | }
28 |
29 | for (var headerName in defaultHeaders) {
30 | if (defaultHeaders.hasOwnProperty(headerName)) {
31 | // user can overwrite the default headers
32 | if (typeof headers[headerName] === 'undefined') {
33 | headers[headerName] = defaultHeaders[headerName];
34 | }
35 | }
36 | }
37 | }
38 |
39 | // headers are required to be a object
40 | return headers || {};
41 | }
42 |
43 | /**
44 | * WireEncode the message to the bridge
45 | */
46 | function send(transport, message) {
47 | message = Buffer.from(message, "utf-8");
48 | var msgLen = message.length;
49 |
50 | var buffer = Buffer.alloc(4);
51 | buffer.writeInt32BE(msgLen, 0);
52 |
53 | transport.write(Buffer.concat([buffer, message], 4 + msgLen));
54 | }
55 |
56 | /**
57 | * EventBus
58 | *
59 | * @param {String} host
60 | * @param {Number} port
61 | * @param {Object} options
62 | * @constructor
63 | */
64 | var EventBus = function (host, port, options) {
65 |
66 | var self = this;
67 |
68 | options = options || {};
69 |
70 | var pingInterval = options.vertxbus_ping_interval || 5000;
71 | var pingTimerID;
72 |
73 | var sendPing = function () {
74 | send(self.transport, JSON.stringify({type: 'ping'}));
75 | };
76 |
77 | var callback = function (err) {
78 | if (err) {
79 | self.onerror(err);
80 | }
81 |
82 | // Send the first ping then send a ping every pingInterval milliseconds
83 | sendPing();
84 | pingTimerID = setInterval(sendPing, pingInterval);
85 | self.state = EventBus.OPEN;
86 | self.onopen && self.onopen();
87 | };
88 |
89 | // if user use certificate need use tls module
90 | var connectionModule = options.hasOwnProperty('pfx') || options.hasOwnProperty('cert') ? tls : net;
91 |
92 | // attributes
93 | this.transport = connectionModule.connect(port, host, options, callback);
94 |
95 | this.state = EventBus.CONNECTING;
96 | this.handlers = {};
97 | this.replyHandlers = {};
98 | this.defaultHeaders = null;
99 |
100 | // default event handlers
101 | this.onerror = console.error;
102 |
103 | // message buffer
104 | var buffer = Buffer.alloc(0);
105 | var len = 0;
106 |
107 | this.transport.on('close', function () {
108 | self.state = EventBus.CLOSED;
109 | if (pingTimerID) {
110 | clearInterval(pingTimerID);
111 | }
112 | self.onclose && self.onclose();
113 | });
114 |
115 | this.transport.on('error', self.onerror);
116 |
117 | this.transport.on('data', function (chunk) {
118 | buffer = Buffer.concat([buffer, chunk], buffer.length + chunk.length);
119 | // we need to loop since there can be several messages in a chunk
120 | do {
121 | !len && (len = buffer.readInt32BE(0));
122 |
123 | if (len && buffer.length >= len + 4) {
124 | // we have a full message
125 | var message = buffer.slice(4, len + 4);
126 | // slice the buffer to consume the next message
127 | buffer = buffer.slice(len + 4);
128 | len = 0;
129 |
130 | var json;
131 |
132 | try {
133 | json = JSON.parse(message.toString('utf8'));
134 | } catch (e) {
135 | self.onerror(e);
136 | return;
137 | }
138 |
139 | // define a reply function on the message itself
140 | if (json.replyAddress) {
141 | Object.defineProperty(json, 'reply', {
142 | value: function (message, headers, callback) {
143 | self.send(json.replyAddress, message, headers, callback);
144 | }
145 | });
146 | }
147 |
148 | var deliver = function (handler, json) {
149 | if (json.type === 'message' &&
150 | json.failureCode !== undefined) {
151 | handler({failureCode: json.failureCode, failureType: json.failureType, message: json.message});
152 | } else {
153 | handler(null, json);
154 | }
155 | };
156 |
157 | if (self.handlers[json.address]) {
158 | // iterate all registered handlers
159 | var handlers = self.handlers[json.address];
160 | // send only goes to one handler
161 | if (json.send &&
162 | handlers[0] !== undefined) {
163 | deliver(handlers[0], json);
164 | } else {
165 | for (var i = 0; i < handlers.length; i++) {
166 | deliver(handlers[i], json);
167 | }
168 | }
169 | } else if (self.replyHandlers[json.address]) {
170 | // Might be a reply message
171 | var handler = self.replyHandlers[json.address];
172 | delete self.replyHandlers[json.address];
173 | deliver(handler, json);
174 | } else {
175 | if (json.type === 'err') {
176 | self.onerror(json);
177 | } else {
178 | console.warn('No handler found for message: ', json);
179 | }
180 | }
181 | } // if data chunked into few frames need concatenate into buffer
182 | } while (buffer.length > 4 && !len)
183 | });
184 | };
185 |
186 | /**
187 | * Send a message
188 | *
189 | * @param {String} address
190 | * @param {Object} message
191 | * @param {Object} [headers]
192 | * @param {Function} [callback]
193 | */
194 | EventBus.prototype.send = function (address, message, headers, callback) {
195 | // are we ready?
196 | if (this.state != EventBus.OPEN) {
197 | throw new Error('INVALID_STATE_ERR');
198 | }
199 |
200 | if (typeof headers === 'function') {
201 | callback = headers;
202 | headers = {};
203 | }
204 |
205 | var envelope = {
206 | type: 'send',
207 | address: address,
208 | headers: mergeHeaders(this.defaultHeaders, headers),
209 | body: message
210 | };
211 |
212 | if (callback) {
213 | var replyAddress = makeUUID();
214 | envelope.replyAddress = replyAddress;
215 | this.replyHandlers[replyAddress] = callback;
216 | }
217 |
218 | send(this.transport, JSON.stringify(envelope));
219 | };
220 |
221 | /**
222 | * Publish a message
223 | *
224 | * @param {String} address
225 | * @param {Object} message
226 | * @param {Object} [headers]
227 | */
228 | EventBus.prototype.publish = function (address, message, headers) {
229 | // are we ready?
230 | if (this.state != EventBus.OPEN) {
231 | throw new Error('INVALID_STATE_ERR');
232 | }
233 |
234 | send(this.transport, JSON.stringify({
235 | type: 'publish',
236 | address: address,
237 | headers: mergeHeaders(this.defaultHeaders, headers),
238 | body: message
239 | }));
240 | };
241 |
242 | /**
243 | * Register a new handler
244 | *
245 | * @param {String} address
246 | * @param {Object} [headers]
247 | * @param {Function} callback
248 | */
249 | EventBus.prototype.registerHandler = function (address, headers, callback) {
250 | // are we ready?
251 | if (this.state != EventBus.OPEN) {
252 | throw new Error('INVALID_STATE_ERR');
253 | }
254 |
255 | if (typeof headers === 'function') {
256 | callback = headers;
257 | headers = {};
258 | }
259 |
260 | // ensure it is an array
261 | if (!this.handlers[address]) {
262 | this.handlers[address] = [];
263 | // First handler for this address so we should register the connection
264 | send(this.transport, JSON.stringify({
265 | type: 'register',
266 | address: address,
267 | headers: mergeHeaders(this.defaultHeaders, headers)
268 | }));
269 | }
270 |
271 | this.handlers[address].push(callback);
272 | };
273 |
274 | /**
275 | * Unregister a handler
276 | *
277 | * @param {String} address
278 | * @param {Object} [headers]
279 | * @param {Function} callback
280 | */
281 | EventBus.prototype.unregisterHandler = function (address, headers, callback) {
282 | // are we ready?
283 | if (this.state != EventBus.OPEN) {
284 | throw new Error('INVALID_STATE_ERR');
285 | }
286 |
287 | var handlers = this.handlers[address];
288 |
289 | if (handlers) {
290 |
291 | if (typeof headers === 'function') {
292 | callback = headers;
293 | headers = {};
294 | }
295 |
296 | var idx = handlers.indexOf(callback);
297 | if (idx != -1) {
298 | handlers.splice(idx, 1);
299 | if (handlers.length === 0) {
300 | // No more local handlers so we should unregister the connection
301 | send(this.transport, JSON.stringify({
302 | type: 'unregister',
303 | address: address,
304 | headers: mergeHeaders(this.defaultHeaders, headers)
305 | }));
306 |
307 | delete this.handlers[address];
308 | }
309 | }
310 | }
311 | };
312 |
313 | /**
314 | * Closes the connection to the EvenBus Bridge.
315 | */
316 | EventBus.prototype.close = function () {
317 | this.state = EventBus.CLOSING;
318 | this.transport.close();
319 | };
320 |
321 | EventBus.CONNECTING = 0;
322 | EventBus.OPEN = 1;
323 | EventBus.CLOSING = 2;
324 | EventBus.CLOSED = 3;
325 |
326 | module.exports = EventBus;
327 |
--------------------------------------------------------------------------------
/src/client/nodejs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vertx3-eventbus-client",
3 | "description": "Vert.x3 TCP event bus client",
4 | "dependencies": {
5 | "node-uuid": "1.4.8"
6 | },
7 | "devDependencies": {
8 | "mocha": "2.3.3"
9 | },
10 | "lib": "lib",
11 | "scripts": {
12 | "test": "mocha ./test/index.js"
13 | }
14 | }
--------------------------------------------------------------------------------
/src/client/nodejs/test/TcpEventBusBridgeEchoServer.java:
--------------------------------------------------------------------------------
1 | package test;
2 |
3 | import io.vertx.core.Vertx;
4 | import io.vertx.core.eventbus.EventBus;
5 | import io.vertx.core.eventbus.Message;
6 | import io.vertx.core.json.JsonObject;
7 | import io.vertx.ext.bridge.BridgeOptions;
8 | import io.vertx.ext.bridge.PermittedOptions;
9 | import io.vertx.ext.eventbus.bridge.tcp.TcpEventBusBridge;
10 |
11 | public class TcpEventBusBridgeEchoServer {
12 |
13 | public static void main(String[] args) {
14 | Vertx vertx = Vertx.vertx();
15 | EventBus eb = vertx.eventBus();
16 |
17 | eb.consumer("hello", (Message msg) -> {
18 | msg.reply(new JsonObject().put("value", "Hello " + msg.body().getString("value")));
19 | });
20 |
21 | eb.consumer("echo",
22 | (Message msg) -> msg.reply(msg.body()));
23 |
24 | eb.consumer("echo2",
25 | (Message msg) -> {
26 | if ("send".equals(msg.body().getString("response_type"))) {
27 | eb.send("echo2_response", msg.body());
28 | } else {
29 | eb.publish("echo2_response", msg.body());
30 | }
31 | });
32 |
33 | TcpEventBusBridge bridge = TcpEventBusBridge.create(
34 | vertx,
35 | new BridgeOptions()
36 | .addInboundPermitted(new PermittedOptions().setAddress("hello"))
37 | .addInboundPermitted(new PermittedOptions().setAddress("echo"))
38 | .addOutboundPermitted(new PermittedOptions().setAddress("echo"))
39 | .addInboundPermitted(new PermittedOptions().setAddress("echo2"))
40 | .addOutboundPermitted(new PermittedOptions().setAddress("echo2_response")));
41 |
42 | bridge.listen(7000, res -> System.out.println("Ready"));
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/client/nodejs/test/index.js:
--------------------------------------------------------------------------------
1 | var assert = require('assert');
2 | var EventBus = require('../lib/tcp-vertx-eventbus');
3 |
4 | describe('echo test', function () {
5 | it('should echo the same message that was sent', function (done) {
6 | var eb = new EventBus('localhost', 7000);
7 |
8 |
9 | eb.onerror = function (err) {
10 | console.error(err);
11 | assert.fail();
12 | };
13 |
14 | eb.onopen = function () {
15 | // send a echo message
16 | eb.send('echo', {value: 'vert.x'}, function (err, res) {
17 | if (err) {
18 | assert.fail();
19 | return;
20 | }
21 |
22 | assert.equal(res.body.value, 'vert.x');
23 | done();
24 | });
25 | };
26 | });
27 | });
28 |
29 |
30 | describe('send v. publish', function () {
31 | it('should deliver sends to one listener', function (done) {
32 | var eb = new EventBus('localhost', 7000);
33 |
34 | eb.onerror = function (err) {
35 | console.error(err);
36 | assert.fail();
37 | };
38 |
39 | var messages = [];
40 |
41 | var handler = function (err, msg) {
42 | if (err) {
43 | assert.fail();
44 | return;
45 | }
46 |
47 | messages.push(msg);
48 | };
49 |
50 | eb.onopen = function () {
51 | eb.registerHandler("echo2_response", handler);
52 | eb.registerHandler("echo2_response", handler);
53 |
54 | setTimeout(function () {
55 | assert.equal(messages.length, 1);
56 | done();
57 | }, 50);
58 |
59 | eb.send('echo2', {response_type: 'send'}, function (err, res) {
60 | if (err) {
61 | assert.fail();
62 | }
63 | });
64 | };
65 | });
66 |
67 | it('should deliver publishes to every listener', function (done) {
68 | var eb = new EventBus('localhost', 7000);
69 |
70 | eb.onerror = function (err) {
71 | console.error(err);
72 | assert.fail();
73 | };
74 |
75 | var messages = [];
76 |
77 | var handler = function (err, msg) {
78 | if (err) {
79 | assert.fail();
80 | return;
81 | }
82 |
83 | messages.push(msg);
84 | };
85 |
86 | eb.onopen = function () {
87 | eb.registerHandler("echo2_response", handler);
88 | eb.registerHandler("echo2_response", handler);
89 |
90 | setTimeout(function () {
91 | assert.equal(messages.length, 2);
92 | done();
93 | }, 50);
94 |
95 | eb.send('echo2', {response_type: 'publish'}, function (err, res) {
96 | if (err) {
97 | assert.fail();
98 | }
99 | });
100 | };
101 | });
102 | });
103 |
--------------------------------------------------------------------------------
/src/main/asciidoc/index.adoc:
--------------------------------------------------------------------------------
1 | = Vert.x TCP EventBus bridge
2 |
3 | Vert.x TCP EventBus bridge is a TCP bridge to Vert.x EventBus.
4 | To use this project, add the following dependency to the _dependencies_ section of your build descriptor:
5 |
6 | Maven (in your `pom.xml`):
7 |
8 | [source,xml,subs="+attributes"]
9 | ----
10 |
11 | ${maven.groupId}
12 | ${maven.artifactId}
13 | ${maven.version}
14 |
15 | ----
16 |
17 | Gradle (in your `build.gradle` file):
18 |
19 | [source,groovy,subs="+attributes"]
20 | ----
21 | compile '${maven.groupId}:${maven.artifactId}:${maven.version}'
22 | ----
23 |
24 | The TCP EventBus bridge is built on top of TCP, meaning that any application that can create TCP sockets can interact with a remote Vert.x instance via its event bus.
25 |
26 | The main use case for the TCP bridge _versus_ the SockJS bridge is for applications that are more resource-constrained and that need to be lightweight since the whole HTTP WebSockets is replaced with plain TCP sockets.
27 |
28 | It remains of course useful even for applications that don't have tight resource constraints:
29 | the protocol is simple enough to efficiently provide an integration interface with non-JVM applications.
30 |
31 | The protocol has been kept as simple as possible and communications use Frames both ways.
32 | The structure of a Frame looks like this:
33 |
34 | ----
35 | <{
36 | type: String,
37 | address: String,
38 | (replyAddress: String)?,
39 | headers: JsonObject,
40 | body: JsonObject
41 | }: JsonObject>
42 | ----
43 |
44 | The message consists of a JSON document that may or may not have been minified.
45 | The message must be prefixed by a _big endian_ 32 bits positive integer (4 bytes) that indicates the full length of the JSON document, in bytes.
46 |
47 | The message `type` can be the following for messages sent by the TCP client:
48 |
49 | 1. `send` to send a message to an `address`,
50 | 2. `publish` to publish a message to an `address`,
51 | 3. `register` to subscribe to the messages sent or published to an `address`,
52 | 4. `unregister` to unsubscribe to the messages sent or published to an `address`,
53 | 5. `ping` to send a `ping` request to the bridge.
54 |
55 | Note that the `replyAddress` field is optional and may only be used for a `send` message.
56 | A message with that field is expected to _eventually_ receive a message back from the server whose `address` field will be that of the original `replyAddress` value.
57 |
58 | The server posts messages back to the client, and they can be of the following `type`:
59 |
60 | 1. `message` for messages sent or published to an `address`, or
61 | 2. `err` to indicate an error (the `body` shall contain details), or
62 | 3. `pong` to respond the `ping` request sent from client.
63 |
64 | An example Node.js client is available in the source of the project.
65 | This client uses the same API as the SockJS counterpart so it should make it easier to switch between the TCP and SockJS implementations.
66 |
67 | An example on how to get started with this bridge could be:
68 |
69 | [source,$lang]
70 | ----
71 | {@link examples.TCPBridgeExamples#example1}
72 | ----
73 |
74 | == Listening to Unix domain sockets
75 |
76 | When running on JDK 16+, or using a https://vertx.io/docs/vertx-core/java/#_native_transports[native transport], a server can listen to Unix domain sockets:
77 |
78 | [source,$lang]
79 | ----
80 | {@link examples.TCPBridgeExamples#serverWithDomainSockets}
81 | ----
82 |
--------------------------------------------------------------------------------
/src/main/java/examples/TCPBridgeExamples.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Red Hat, Inc.
3 | *
4 | * Red Hat licenses this file to you under the Apache License, version 2.0
5 | * (the "License"); you may not use this file except in compliance with the
6 | * License. You may obtain a copy of the License at:
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 | * License for the specific language governing permissions and limitations
14 | * under the License.
15 | */
16 |
17 | package examples;
18 |
19 | import io.vertx.core.Vertx;
20 | import io.vertx.core.net.SocketAddress;
21 | import io.vertx.docgen.Source;
22 | import io.vertx.ext.bridge.BridgeOptions;
23 | import io.vertx.ext.bridge.PermittedOptions;
24 | import io.vertx.ext.eventbus.bridge.tcp.TcpEventBusBridge;
25 |
26 | /**
27 | *
28 | * @author Paulo Lopes
29 | */
30 | @Source
31 | public class TCPBridgeExamples {
32 |
33 | public void example1(Vertx vertx) {
34 | TcpEventBusBridge bridge = TcpEventBusBridge.create(
35 | vertx,
36 | new BridgeOptions()
37 | .addInboundPermitted(new PermittedOptions().setAddress("in"))
38 | .addOutboundPermitted(new PermittedOptions().setAddress("out")));
39 |
40 | bridge.listen(7000).onComplete(res -> {
41 | if (res.succeeded()) {
42 | // succeed...
43 | } else {
44 | // fail...
45 | }
46 | });
47 | }
48 |
49 | public void serverWithDomainSockets(TcpEventBusBridge bridge) {
50 | SocketAddress domainSocketAddress = SocketAddress.domainSocketAddress("/var/tmp/bridge.sock");
51 |
52 | bridge.listen(domainSocketAddress).onComplete(res -> {
53 | if (res.succeeded()) {
54 | // succeed...
55 | } else {
56 | // fail...
57 | }
58 | });
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/main/java/io/vertx/ext/eventbus/bridge/tcp/BridgeEvent.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 Red Hat, Inc.
3 | *
4 | * All rights reserved. This program and the accompanying materials
5 | * are made available under the terms of the Eclipse Public License v1.0
6 | * and Apache License v2.0 which accompanies this distribution.
7 | *
8 | * The Eclipse Public License is available at
9 | * http://www.eclipse.org/legal/epl-v10.html
10 | *
11 | * The Apache License v2.0 is available at
12 | * http://www.opensource.org/licenses/apache2.0.php
13 | *
14 | * You may elect to redistribute this code under either of these licenses.
15 | */
16 |
17 | package io.vertx.ext.eventbus.bridge.tcp;
18 |
19 | import io.vertx.codegen.annotations.CacheReturn;
20 | import io.vertx.codegen.annotations.Fluent;
21 | import io.vertx.codegen.annotations.VertxGen;
22 | import io.vertx.core.Future;
23 | import io.vertx.core.json.JsonObject;
24 | import io.vertx.core.net.NetSocket;
25 |
26 | /**
27 | * Represents an event that occurs on the event bus bridge.
28 | *
29 | * Please consult the documentation for a full explanation.
30 | *
31 | * @author Tim Fox
32 | */
33 | @VertxGen
34 | public interface BridgeEvent extends io.vertx.ext.bridge.BaseBridgeEvent {
35 |
36 | /**
37 | * Get the raw JSON message for the event. This will be null for SOCKET_CREATED or SOCKET_CLOSED events as there is
38 | * no message involved.
39 | *
40 | * @param message the raw message
41 | * @return this reference, so it can be used fluently
42 | */
43 | @Fluent
44 | BridgeEvent setRawMessage(JsonObject message);
45 |
46 | /**
47 | * Get the SockJSSocket instance corresponding to the event
48 | *
49 | * @return the SockJSSocket instance
50 | */
51 | @CacheReturn
52 | NetSocket socket();
53 | }
54 |
--------------------------------------------------------------------------------
/src/main/java/io/vertx/ext/eventbus/bridge/tcp/TcpEventBusBridge.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Red Hat, Inc.
3 | *
4 | * All rights reserved. This program and the accompanying materials
5 | * are made available under the terms of the Eclipse Public License v1.0
6 | * and Apache License v2.0 which accompanies this distribution.
7 | *
8 | * The Eclipse Public License is available at
9 | * http://www.eclipse.org/legal/epl-v10.html
10 | *
11 | * The Apache License v2.0 is available at
12 | * http://www.opensource.org/licenses/apache2.0.php
13 | *
14 | * You may elect to redistribute this code under either of these licenses.
15 | */
16 | package io.vertx.ext.eventbus.bridge.tcp;
17 |
18 | import io.vertx.codegen.annotations.VertxGen;
19 | import io.vertx.core.Future;
20 | import io.vertx.core.Handler;
21 | import io.vertx.core.Vertx;
22 | import io.vertx.core.net.NetServerOptions;
23 | import io.vertx.core.net.SocketAddress;
24 | import io.vertx.ext.bridge.BridgeOptions;
25 | import io.vertx.ext.eventbus.bridge.tcp.impl.TcpEventBusBridgeImpl;
26 |
27 | /**
28 | * TCP EventBus bridge for Vert.x
29 | *
30 | * @author Paulo Lopes
31 | */
32 | @VertxGen
33 | public interface TcpEventBusBridge {
34 |
35 | static TcpEventBusBridge create(Vertx vertx) {
36 | return create(vertx, null, null);
37 | }
38 |
39 | static TcpEventBusBridge create(Vertx vertx, BridgeOptions options) {
40 | return create(vertx, options, null);
41 | }
42 |
43 | static TcpEventBusBridge create(Vertx vertx, BridgeOptions options, NetServerOptions netServerOptions) {
44 | return new TcpEventBusBridgeImpl(vertx, options, netServerOptions,null);
45 | }
46 |
47 | static TcpEventBusBridge create(Vertx vertx, BridgeOptions options, NetServerOptions netServerOptions,Handler eventHandler) {
48 | return new TcpEventBusBridgeImpl(vertx, options, netServerOptions,eventHandler);
49 | }
50 |
51 | /**
52 | * Start listening on the port and host as configured in the {@link io.vertx.core.net.NetServerOptions} used when creating the server.
53 | *
54 | * @return a future of the result
55 | */
56 | Future listen();
57 |
58 | /**
59 | * Start listening on the specified port and host, ignoring port and host configured in the {@link io.vertx.core.net.NetServerOptions} used when creating the server.
60 | *
61 | * @param port the tcp port
62 | * @param address the local address
63 | *
64 | * @return a future of the result
65 | */
66 | Future listen(int port, String address);
67 |
68 | /**
69 | * Start listening on the specified port and host "0.0.0.0", ignoring port and host configured in the {@link io.vertx.core.net.NetServerOptions} used when creating the server.
70 | *
71 | * @param port the TCP port
72 | *
73 | * @return a future of the result
74 | */
75 | Future listen(int port);
76 |
77 | /**
78 | * Start listening on the specified local address, ignoring port and host configured in the {@link NetServerOptions} used when creating the server.
79 | *
80 | * @param localAddress the local address to listen on
81 | * @return a future of the result
82 | */
83 | default Future listen(SocketAddress localAddress) {
84 | return Future.failedFuture("Not supported");
85 | }
86 |
87 | /**
88 | * Close the current socket.
89 | *
90 | * @return a future of the result
91 | */
92 | Future close();
93 | }
94 |
--------------------------------------------------------------------------------
/src/main/java/io/vertx/ext/eventbus/bridge/tcp/impl/BridgeEventImpl.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 Red Hat, Inc.
3 | *
4 | * All rights reserved. This program and the accompanying materials
5 | * are made available under the terms of the Eclipse Public License v1.0
6 | * and Apache License v2.0 which accompanies this distribution.
7 | *
8 | * The Eclipse Public License is available at
9 | * http://www.eclipse.org/legal/epl-v10.html
10 | *
11 | * The Apache License v2.0 is available at
12 | * http://www.opensource.org/licenses/apache2.0.php
13 | *
14 | * You may elect to redistribute this code under either of these licenses.
15 | */
16 |
17 | package io.vertx.ext.eventbus.bridge.tcp.impl;
18 |
19 | import io.vertx.core.AsyncResult;
20 | import io.vertx.core.Future;
21 | import io.vertx.core.Promise;
22 | import io.vertx.core.json.JsonObject;
23 | import io.vertx.core.net.NetSocket;
24 | import io.vertx.ext.bridge.BridgeEventType;
25 | import io.vertx.ext.eventbus.bridge.tcp.BridgeEvent;
26 |
27 | /**
28 | * @author Tim Fox
29 | * @author grant@iowntheinter.net
30 | */
31 | class BridgeEventImpl implements BridgeEvent {
32 |
33 | private final BridgeEventType type;
34 | private final JsonObject rawMessage;
35 | private final NetSocket socket;
36 | private final Promise promise;
37 |
38 | public BridgeEventImpl(BridgeEventType type, JsonObject rawMessage, NetSocket socket) {
39 | this.type = type;
40 | this.rawMessage = rawMessage;
41 | this.socket = socket;
42 | this.promise = Promise.promise();
43 | }
44 |
45 | @Override
46 | public Future future() {
47 | return promise.future();
48 | }
49 |
50 | @Override
51 | public BridgeEventType type() {
52 | return type;
53 | }
54 |
55 | @Override
56 | public JsonObject getRawMessage() {
57 | return rawMessage;
58 | }
59 |
60 | @Override
61 | public BridgeEvent setRawMessage(JsonObject message) {
62 | if (message != rawMessage) {
63 | rawMessage.clear().mergeIn(message);
64 | }
65 | return this;
66 | }
67 |
68 | @Override
69 | public void handle(AsyncResult asyncResult) {
70 | promise.handle(asyncResult);
71 | }
72 |
73 | @Override
74 | public NetSocket socket() {
75 | return socket;
76 | }
77 |
78 | @Override
79 | public void complete(Boolean result) {
80 | promise.complete(result);
81 | }
82 |
83 | @Override
84 | public void complete() {
85 | promise.complete();
86 | }
87 |
88 | @Override
89 | public void fail(Throwable throwable) {
90 | promise.fail(throwable);
91 | }
92 |
93 | @Override
94 | public void fail(String failureMessage) {
95 | promise.fail(failureMessage);
96 | }
97 |
98 | @Override
99 | public boolean tryComplete(Boolean result) {
100 | return promise.tryComplete(result);
101 | }
102 |
103 | @Override
104 | public boolean tryComplete() {
105 | return promise.tryComplete();
106 | }
107 |
108 | @Override
109 | public boolean tryFail(Throwable cause) {
110 | return promise.tryFail(cause);
111 | }
112 |
113 | @Override
114 | public boolean tryFail(String failureMessage) {
115 | return promise.tryFail(failureMessage);
116 | }
117 |
118 | }
119 |
--------------------------------------------------------------------------------
/src/main/java/io/vertx/ext/eventbus/bridge/tcp/impl/TcpEventBusBridgeImpl.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Red Hat, Inc.
3 | *
4 | * All rights reserved. This program and the accompanying materials
5 | * are made available under the terms of the Eclipse Public License v1.0
6 | * and Apache License v2.0 which accompanies this distribution.
7 | *
8 | * The Eclipse Public License is available at
9 | * http://www.eclipse.org/legal/epl-v10.html
10 | *
11 | * The Apache License v2.0 is available at
12 | * http://www.opensource.org/licenses/apache2.0.php
13 | *
14 | * You may elect to redistribute this code under either of these licenses.
15 | */
16 | package io.vertx.ext.eventbus.bridge.tcp.impl;
17 |
18 | import io.vertx.core.AsyncResult;
19 | import io.vertx.core.Future;
20 | import io.vertx.core.Handler;
21 | import io.vertx.core.Vertx;
22 | import io.vertx.core.eventbus.*;
23 | import io.vertx.core.internal.logging.Logger;
24 | import io.vertx.core.internal.logging.LoggerFactory;
25 | import io.vertx.core.json.JsonObject;
26 | import io.vertx.core.net.NetServer;
27 | import io.vertx.core.net.NetServerOptions;
28 | import io.vertx.core.net.NetSocket;
29 | import io.vertx.core.net.SocketAddress;
30 | import io.vertx.ext.bridge.BridgeEventType;
31 | import io.vertx.ext.bridge.BridgeOptions;
32 | import io.vertx.ext.bridge.PermittedOptions;
33 | import io.vertx.ext.eventbus.bridge.tcp.BridgeEvent;
34 | import io.vertx.ext.eventbus.bridge.tcp.TcpEventBusBridge;
35 | import io.vertx.ext.eventbus.bridge.tcp.impl.protocol.FrameParser;
36 |
37 | import java.util.HashMap;
38 | import java.util.Iterator;
39 | import java.util.List;
40 | import java.util.Map;
41 | import java.util.concurrent.ConcurrentHashMap;
42 | import java.util.function.Supplier;
43 | import java.util.regex.Matcher;
44 | import java.util.regex.Pattern;
45 |
46 | import static io.vertx.ext.eventbus.bridge.tcp.impl.protocol.FrameHelper.sendErrFrame;
47 | import static io.vertx.ext.eventbus.bridge.tcp.impl.protocol.FrameHelper.sendFrame;
48 |
49 | /**
50 | * Abstract TCP EventBus bridge. Handles all common socket operations but has no knowledge on the payload.
51 | *
52 | * @author Paulo Lopes
53 | */
54 | public class TcpEventBusBridgeImpl implements TcpEventBusBridge {
55 |
56 | private static final Logger log = LoggerFactory.getLogger(TcpEventBusBridgeImpl.class);
57 |
58 | private final EventBus eb;
59 | private final NetServer server;
60 |
61 | private final Map compiledREs = new HashMap<>();
62 | private final BridgeOptions options;
63 | private final Handler bridgeEventHandler;
64 |
65 |
66 | public TcpEventBusBridgeImpl(Vertx vertx, BridgeOptions options, NetServerOptions netServerOptions, Handler eventHandler) {
67 | this.eb = vertx.eventBus();
68 | this.options = options != null ? options : new BridgeOptions();
69 | this.bridgeEventHandler = eventHandler;
70 |
71 | server = vertx.createNetServer(netServerOptions == null ? new NetServerOptions() : netServerOptions);
72 | server.connectHandler(this::handler);
73 | }
74 |
75 | @Override
76 | public Future listen() {
77 | return server.listen().map(this);
78 | }
79 |
80 | @Override
81 | public Future listen(int port) {
82 | return server.listen(port).map(this);
83 | }
84 |
85 | @Override
86 | public Future listen(SocketAddress localAddress) {
87 | return server.listen(localAddress).map(this);
88 | }
89 |
90 | @Override
91 | public Future listen(int port, String address) {
92 | return server.listen(port, address).map(this);
93 | }
94 |
95 | private void doSendOrPub(NetSocket socket, String address, JsonObject msg, Map> registry, Map> replies) {
97 | final Object body = msg.getValue("body");
98 | final JsonObject headers = msg.getJsonObject("headers");
99 |
100 |
101 | // default to message
102 | final String type = msg.getString("type", "message");
103 | DeliveryOptions deliveryOptions = parseMsgHeaders(new DeliveryOptions(), headers);
104 |
105 | switch (type) {
106 | case "send":
107 | if (checkMatches(true, address, replies)) {
108 | final String replyAddress = msg.getString("replyAddress");
109 |
110 | if (replyAddress != null) {
111 | // reply address is not null, it is a request from TCP endpoint that will wait for a response
112 | eb.request(address, body, deliveryOptions).onComplete((AsyncResult> res1) -> {
113 | if (res1.failed()) {
114 | sendErrFrame(address, replyAddress, (ReplyException) res1.cause(), socket);
115 | } else {
116 | final Message> response = res1.result();
117 | final JsonObject responseHeaders = new JsonObject();
118 |
119 | // clone the headers from / to
120 | for (Map.Entry entry : response.headers()) {
121 | responseHeaders.put(entry.getKey(), entry.getValue());
122 | }
123 |
124 | if (response.replyAddress() != null) {
125 | replies.put(response.replyAddress(), response);
126 | }
127 |
128 | sendFrame("message", replyAddress, response.replyAddress(), responseHeaders, true, response.body(), socket);
129 | }
130 | });
131 | } else {
132 | // no reply address it might be a response, a failure or a request that does not need a response
133 | if (replies.containsKey(address)) {
134 | // address is registered, it is not a request
135 | Integer failureCode = msg.getInteger("failureCode");
136 | if ( failureCode == null ) {
137 | //No failure code, it is a response
138 | replies.get(address).reply(body, deliveryOptions);
139 | } else {
140 | //Failure code, fail the original response
141 | replies.get(address).fail(msg.getInteger("failureCode"), msg.getString("message"));
142 | }
143 | } else {
144 | // it is a request that does not expect a response
145 | eb.send(address, body, deliveryOptions);
146 | }
147 | }
148 | // replies are a one time off operation
149 | replies.remove(address);
150 | } else {
151 | sendErrFrame("access_denied", socket);
152 | }
153 | break;
154 | case "publish":
155 | if (checkMatches(true, address)) {
156 | eb.publish(address, body, deliveryOptions);
157 | } else {
158 | sendErrFrame("access_denied", socket);
159 | }
160 | break;
161 | case "register":
162 | if (checkMatches(false, address)) {
163 | registry.put(address, eb.consumer(address, (Message
81 | * @return self
82 | */
83 | public SSLKeyPairCerts createTwoWaySSL() {
84 | try {
85 | KeyPair serverRSAKeyPair = generateRSAKeyPair(2048);
86 | Certificate serverCert = generateSelfSignedCert(SERVER_CERT_SUBJECT, serverRSAKeyPair);
87 |
88 | KeyPair clientRSAKeyPair = generateRSAKeyPair(2048);
89 | Certificate clientCert = generateSelfSignedCert(CLIENT_CERT_SUBJECT, clientRSAKeyPair);
90 |
91 | KeyStore serverKeyStore = emptyJKSStore(PASSWORD);
92 | serverKeyStore.setKeyEntry("localserver", serverRSAKeyPair.getPrivate(), PASSWORD.toCharArray(), new Certificate[]{serverCert});
93 |
94 | KeyStore serverTrustStore = emptyJKSStore(PASSWORD);
95 | serverTrustStore.setCertificateEntry("clientcert", clientCert);
96 |
97 | KeyStore clientKeyStore = emptyJKSStore(PASSWORD);
98 | clientKeyStore.setKeyEntry("localclient", clientRSAKeyPair.getPrivate(), PASSWORD.toCharArray(), new Certificate[]{clientCert});
99 |
100 | KeyStore clientTrustStore = emptyJKSStore(PASSWORD);
101 | clientTrustStore.setCertificateEntry("servercert", serverCert);
102 |
103 | ByteArrayOutputStream serverKeyStoreOutputStream = new ByteArrayOutputStream(512);
104 | serverKeyStore.store(serverKeyStoreOutputStream, PASSWORD.toCharArray());
105 | this.serverKeyStore = new JksOptions().setPassword(PASSWORD).setValue(Buffer.buffer(serverKeyStoreOutputStream.toByteArray()));
106 |
107 | ByteArrayOutputStream serverTrustStoreOutputStream = new ByteArrayOutputStream(512);
108 | serverTrustStore.store(serverTrustStoreOutputStream, PASSWORD.toCharArray());
109 | this.serverTrustStore = new JksOptions().setPassword(PASSWORD).setValue(Buffer.buffer(serverTrustStoreOutputStream.toByteArray()));
110 |
111 | ByteArrayOutputStream clientKeyStoreOutputStream = new ByteArrayOutputStream(512);
112 | clientKeyStore.store(clientKeyStoreOutputStream, PASSWORD.toCharArray());
113 | this.clientKeyStore = new JksOptions().setPassword(PASSWORD).setValue(Buffer.buffer(clientKeyStoreOutputStream.toByteArray()));
114 |
115 | ByteArrayOutputStream clientTrustStoreOutputStream = new ByteArrayOutputStream(512);
116 | clientTrustStore.store(clientTrustStoreOutputStream, PASSWORD.toCharArray());
117 | this.clientTrustStore = new JksOptions().setPassword(PASSWORD).setValue(Buffer.buffer(clientTrustStoreOutputStream.toByteArray()));
118 | } catch (Exception e) {
119 | throw new RuntimeException("Cannot generate SSL key pairs and certificates", e);
120 | }
121 | return this;
122 | }
123 |
124 | // refer to: https://github.com/vert-x3/vertx-config/blob/4.0.0-milestone4/vertx-config-vault/src/test/java/io/vertx/config/vault/utils/Certificates.java#L149
125 | private X509Certificate generateSelfSignedCert(String certSub, KeyPair keyPair) throws Exception {
126 | final X509v3CertificateBuilder certificateBuilder = new X509v3CertificateBuilder(
127 | new org.bouncycastle.asn1.x500.X500Name(certSub),
128 | BigInteger.ONE,
129 | new Date(System.currentTimeMillis() - 1000L * 60 * 60 * 24 * 30),
130 | new Date(System.currentTimeMillis() + (1000L * 60 * 60 * 24 * 30)),
131 | new X500Name(certSub),
132 | SubjectPublicKeyInfo.getInstance(keyPair.getPublic().getEncoded())
133 | );
134 | final GeneralNames subjectAltNames = new GeneralNames(new GeneralName(GeneralName.iPAddress, "127.0.0.1"));
135 | certificateBuilder.addExtension(org.bouncycastle.asn1.x509.Extension.subjectAlternativeName, false, subjectAltNames);
136 |
137 | final AlgorithmIdentifier sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder().find("SHA1WithRSAEncryption");
138 | final AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder().find(sigAlgId);
139 | final BcContentSignerBuilder signerBuilder = new BcRSAContentSignerBuilder(sigAlgId, digAlgId);
140 | final AsymmetricKeyParameter keyp = PrivateKeyFactory.createKey(keyPair.getPrivate().getEncoded());
141 | final ContentSigner signer = signerBuilder.build(keyp);
142 | final X509CertificateHolder x509CertificateHolder = certificateBuilder.build(signer);
143 | final X509Certificate certificate = new JcaX509CertificateConverter().getCertificate(x509CertificateHolder);
144 | certificate.checkValidity(new Date());
145 | certificate.verify(keyPair.getPublic());
146 | return certificate;
147 | }
148 |
149 | private KeyPair generateRSAKeyPair(int keySize) throws NoSuchAlgorithmException {
150 | final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
151 | keyPairGenerator.initialize(keySize);
152 | return keyPairGenerator.genKeyPair();
153 | }
154 |
155 | private KeyStore emptyJKSStore(String password) throws Exception {
156 | KeyStore ks = KeyStore.getInstance("JKS");
157 | ks.load(null, password.toCharArray());
158 | return ks;
159 | }
160 |
161 | /**
162 | * @return the server's keystore options
163 | */
164 | public JksOptions getServerKeyStore() {
165 | return serverKeyStore;
166 | }
167 |
168 | /**
169 | * @return the server's truststore options
170 | */
171 | public JksOptions getServerTrustStore() {
172 | return serverTrustStore;
173 | }
174 |
175 | /**
176 | * @return the client's keystore options
177 | */
178 | public JksOptions getClientKeyStore() {
179 | return clientKeyStore;
180 | }
181 |
182 | /**
183 | * @return the client's truststore options
184 | */
185 | public JksOptions getClientTrustStore() {
186 | return clientTrustStore;
187 | }
188 | }
189 |
--------------------------------------------------------------------------------
/src/test/java/io/vertx/ext/eventbus/bridge/tcp/TcpEventBusBridgeEventTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Red Hat, Inc.
3 | *
4 | * All rights reserved. This program and the accompanying materials
5 | * are made available under the terms of the Eclipse Public License v1.0
6 | * and Apache License v2.0 which accompanies this distribution.
7 | *
8 | * The Eclipse Public License is available at
9 | * http://www.eclipse.org/legal/epl-v10.html
10 | *
11 | * The Apache License v2.0 is available at
12 | * http://www.opensource.org/licenses/apache2.0.php
13 | *
14 | * You may elect to redistribute this code under either of these licenses.
15 | */
16 | package io.vertx.ext.eventbus.bridge.tcp;
17 |
18 | import io.vertx.core.Vertx;
19 | import io.vertx.core.eventbus.Message;
20 | import io.vertx.core.http.ClientAuth;
21 | import io.vertx.core.json.JsonObject;
22 | import io.vertx.core.internal.logging.Logger;
23 | import io.vertx.core.internal.logging.LoggerFactory;
24 | import io.vertx.core.net.*;
25 | import io.vertx.ext.bridge.BridgeOptions;
26 | import io.vertx.ext.bridge.PermittedOptions;
27 | import io.vertx.ext.eventbus.bridge.tcp.impl.protocol.FrameHelper;
28 | import io.vertx.ext.unit.Async;
29 | import io.vertx.ext.unit.TestContext;
30 | import io.vertx.ext.unit.junit.VertxUnitRunner;
31 | import org.junit.After;
32 | import org.junit.Before;
33 | import org.junit.Test;
34 | import org.junit.runner.RunWith;
35 |
36 | import javax.net.ssl.SSLPeerUnverifiedException;
37 | import java.security.cert.Certificate;
38 | import java.security.cert.X509Certificate;
39 |
40 | @RunWith(VertxUnitRunner.class)
41 | public class TcpEventBusBridgeEventTest {
42 |
43 | private static final Logger logger = LoggerFactory.getLogger(TcpEventBusBridgeEventTest.class);
44 |
45 | private Vertx vertx;
46 |
47 | private SSLKeyPairCerts sslKeyPairCerts;
48 |
49 | @Before
50 | public void before(TestContext context) {
51 | vertx = Vertx.vertx();
52 | final Async async = context.async();
53 | vertx.eventBus().consumer("hello", (Message msg) -> msg.reply(new JsonObject().put("value", "Hello " + msg.body().getString("value"))));
54 | vertx.eventBus().consumer("echo", (Message msg) -> msg.reply(msg.body()));
55 | vertx.setPeriodic(1000, __ -> vertx.eventBus().send("ping", new JsonObject().put("value", "hi")));
56 | sslKeyPairCerts = new SSLKeyPairCerts().createTwoWaySSL();
57 | TcpEventBusBridge bridge = TcpEventBusBridge.create(
58 | vertx,
59 | new BridgeOptions()
60 | .addInboundPermitted(new PermittedOptions().setAddress("hello"))
61 | .addInboundPermitted(new PermittedOptions().setAddress("echo"))
62 | .addInboundPermitted(new PermittedOptions().setAddress("test"))
63 | .addOutboundPermitted(new PermittedOptions().setAddress("echo"))
64 | .addOutboundPermitted(new PermittedOptions().setAddress("ping")),
65 | new NetServerOptions()
66 | .setClientAuth(ClientAuth.REQUEST)
67 | .setSsl(true)
68 | .setTrustOptions(sslKeyPairCerts.getServerTrustStore())
69 | .setKeyCertOptions(sslKeyPairCerts.getServerKeyStore()),
70 | be -> {
71 | logger.info("Handled a bridge event " + be.getRawMessage());
72 | if (be.socket().isSsl()) {
73 | try {
74 | for (Certificate c : be.socket().peerCertificates()) {
75 | logger.info(((X509Certificate)c).getSubjectDN().toString());
76 | }
77 | } catch (SSLPeerUnverifiedException e) {
78 | throw new RuntimeException("Failed to get peer certificates chain", e);
79 | }
80 | }
81 | be.complete(true);
82 | });
83 | bridge.listen(7000).onComplete(res -> {
84 | context.assertTrue(res.succeeded());
85 | async.complete();
86 | });
87 | }
88 |
89 | @After
90 | public void after(TestContext context) {
91 | vertx.close().onComplete(context.asyncAssertSuccess());
92 | }
93 |
94 | @Test
95 | public void testSendVoidMessage(TestContext context) {
96 | // Send a request and get a response
97 | NetClient client = vertx.createNetClient(new NetClientOptions()
98 | .setSsl(true)
99 | .setHostnameVerificationAlgorithm("")
100 | .setTrustOptions(sslKeyPairCerts.getClientTrustStore())
101 | .setKeyCertOptions(sslKeyPairCerts.getClientKeyStore()));
102 | final Async async = context.async();
103 | vertx.eventBus().consumer("test", (Message msg) -> {
104 | client.close();
105 | async.complete();
106 | });
107 | client.connect(7000, "localhost").onComplete(conn -> {
108 | context.assertFalse(conn.failed());
109 | NetSocket socket = conn.result();
110 | FrameHelper.sendFrame("send", "test", new JsonObject().put("value", "vert.x"), socket);
111 | });
112 | }
113 |
114 | @Test
115 | public void testSendVoidMessageTrustAll(TestContext context) {
116 | NetClient client = vertx.createNetClient(new NetClientOptions()
117 | .setSsl(true)
118 | .setTrustAll(true)
119 | .setHostnameVerificationAlgorithm("")
120 | .setKeyCertOptions(sslKeyPairCerts.getClientKeyStore())
121 | );
122 | final Async async = context.async();
123 | vertx.eventBus().consumer("test", (Message msg) -> {
124 | client.close();
125 | async.complete();
126 | });
127 | client.connect(7000, "localhost").onComplete(conn -> {
128 | context.assertFalse(conn.failed());
129 | NetSocket socket = conn.result();
130 | FrameHelper.sendFrame("send", "test", new JsonObject().put("value", "vert.x"), socket);
131 | });
132 | }
133 |
134 | }
135 |
--------------------------------------------------------------------------------
/src/test/java/io/vertx/ext/eventbus/bridge/tcp/TcpEventBusBridgeHookTest.java:
--------------------------------------------------------------------------------
1 | package io.vertx.ext.eventbus.bridge.tcp;
2 |
3 | import io.vertx.core.Vertx;
4 | import io.vertx.core.json.JsonObject;
5 | import io.vertx.core.net.NetClient;
6 | import io.vertx.core.net.NetServerOptions;
7 | import io.vertx.core.net.NetSocket;
8 | import io.vertx.ext.bridge.BridgeEventType;
9 | import io.vertx.ext.bridge.BridgeOptions;
10 | import io.vertx.ext.bridge.PermittedOptions;
11 | import io.vertx.ext.eventbus.bridge.tcp.impl.protocol.FrameHelper;
12 | import io.vertx.ext.eventbus.bridge.tcp.impl.protocol.FrameParser;
13 | import io.vertx.ext.unit.Async;
14 | import io.vertx.ext.unit.TestContext;
15 | import io.vertx.ext.unit.junit.VertxUnitRunner;
16 | import org.junit.After;
17 | import org.junit.Before;
18 | import org.junit.Test;
19 | import org.junit.runner.RunWith;
20 |
21 | @RunWith(VertxUnitRunner.class)
22 | public class TcpEventBusBridgeHookTest {
23 |
24 | private final String address = "hello";
25 |
26 | private Vertx vertx;
27 |
28 | @Before
29 | public void before(TestContext context) {
30 | vertx = Vertx.vertx();
31 | }
32 |
33 | @After
34 | public void after(TestContext context) {
35 | vertx.close().onComplete(context.asyncAssertSuccess());
36 | }
37 |
38 | @Test
39 | public void testRegister(TestContext context) {
40 |
41 | final JsonObject payload = new JsonObject().put("value", "Francesco");
42 |
43 | // 0. Start the bridge
44 | // 1. Check if REGISTER hook is called
45 | // 2. Check if REGISTERED hook is called
46 | // 3. Try to send a message while managing REGISTERED event
47 | // 4. Check if client receives the message
48 |
49 | Async bridgeStart = context.async();
50 | Async register = context.async();
51 | Async registered = context.async();
52 | Async request = context.async();
53 |
54 | TcpEventBusBridge bridge = TcpEventBusBridge.create(
55 | vertx,
56 | new BridgeOptions()
57 | .addInboundPermitted(new PermittedOptions())
58 | .addOutboundPermitted(new PermittedOptions()),
59 | new NetServerOptions(),
60 | be -> {
61 | if (be.type() == BridgeEventType.REGISTER) {
62 | context.assertNotNull(be.socket());
63 | context.assertEquals(address, be.getRawMessage().getString("address"));
64 | register.complete();
65 | } else if (be.type() == BridgeEventType.REGISTERED) {
66 | context.assertNotNull(be.socket());
67 | context.assertEquals(address, be.getRawMessage().getString("address"));
68 |
69 | // The client should be able to receive this message
70 | vertx.eventBus().send(address, payload);
71 |
72 | registered.complete();
73 | }
74 | be.complete(true);
75 | }
76 | );
77 | bridge.listen(7000).onComplete(res -> {
78 | context.assertTrue(res.succeeded());
79 | bridgeStart.complete();
80 | });
81 |
82 | bridgeStart.await();
83 |
84 | NetClient client = vertx.createNetClient();
85 |
86 | client.connect(7000, "localhost").onComplete(conn -> {
87 | context.assertFalse(conn.failed());
88 |
89 | NetSocket socket = conn.result();
90 |
91 | // 1 reply will arrive
92 | // MESSAGE for address
93 | final FrameParser parser = new FrameParser(parse -> {
94 | context.assertTrue(parse.succeeded());
95 | JsonObject frame = parse.result();
96 |
97 | context.assertNotEquals("err", frame.getString("type"));
98 | context.assertEquals("Francesco", frame.getJsonObject("body").getString("value"));
99 | client.close();
100 | request.complete();
101 | });
102 |
103 | socket.handler(parser);
104 |
105 | FrameHelper.sendFrame("register", address, null, socket);
106 | });
107 |
108 | register.await();
109 | registered.await();
110 | request.await();
111 |
112 | }
113 |
114 | }
115 |
--------------------------------------------------------------------------------
/src/test/java/io/vertx/ext/eventbus/bridge/tcp/TcpEventBusBridgeInteropTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Red Hat, Inc.
3 | *
4 | * All rights reserved. This program and the accompanying materials
5 | * are made available under the terms of the Eclipse Public License v1.0
6 | * and Apache License v2.0 which accompanies this distribution.
7 | *
8 | * The Eclipse Public License is available at
9 | * http://www.eclipse.org/legal/epl-v10.html
10 | *
11 | * The Apache License v2.0 is available at
12 | * http://www.opensource.org/licenses/apache2.0.php
13 | *
14 | * You may elect to redistribute this code under either of these licenses.
15 | */
16 | package io.vertx.ext.eventbus.bridge.tcp;
17 |
18 | import io.vertx.core.Vertx;
19 | import io.vertx.core.buffer.Buffer;
20 | import io.vertx.core.eventbus.EventBus;
21 | import io.vertx.core.eventbus.Message;
22 | import io.vertx.core.json.JsonObject;
23 | import io.vertx.core.net.NetClient;
24 | import io.vertx.core.net.NetSocket;
25 | import io.vertx.ext.bridge.BridgeOptions;
26 | import io.vertx.ext.bridge.PermittedOptions;
27 | import io.vertx.ext.eventbus.bridge.tcp.impl.protocol.FrameHelper;
28 | import io.vertx.ext.eventbus.bridge.tcp.impl.protocol.FrameParser;
29 | import io.vertx.ext.unit.Async;
30 | import io.vertx.ext.unit.TestContext;
31 | import io.vertx.ext.unit.junit.VertxUnitRunner;
32 | import org.junit.After;
33 | import org.junit.Before;
34 | import org.junit.Test;
35 | import org.junit.runner.RunWith;
36 |
37 | import java.io.DataInputStream;
38 | import java.io.DataOutputStream;
39 | import java.io.IOException;
40 | import java.net.Socket;
41 |
42 | @RunWith(VertxUnitRunner.class)
43 | public class TcpEventBusBridgeInteropTest {
44 |
45 | private Vertx vertx;
46 |
47 | @Before
48 | public void before(TestContext context) {
49 | vertx = Vertx.vertx();
50 | final Async async = context.async();
51 |
52 | vertx.eventBus().consumer("hello", (Message msg) -> msg.reply(new JsonObject().put("value", "Hello " + msg.body().getString("value"))));
53 |
54 | TcpEventBusBridge bridge = TcpEventBusBridge.create(
55 | vertx,
56 | new BridgeOptions()
57 | .addInboundPermitted(new PermittedOptions())
58 | .addOutboundPermitted(new PermittedOptions()));
59 |
60 | bridge.listen(7000).onComplete(res -> {
61 | context.assertTrue(res.succeeded());
62 | async.complete();
63 | });
64 | }
65 |
66 | @After
67 | public void after(TestContext context) {
68 | vertx.close().onComplete(context.asyncAssertSuccess());
69 | }
70 |
71 | @Test
72 | public void testInteropWithPlainJava(TestContext context) {
73 | final Async async = context.async();
74 |
75 | // register a local consumer
76 | final EventBus eb = vertx.eventBus();
77 |
78 | eb.consumer("io.vertx", msg -> {
79 | // assert that headers are received
80 | context.assertEquals("findAll", msg.headers().get("action"));
81 | // assert body is not null
82 | context.assertNotNull(msg.body());
83 | msg.reply(msg.body());
84 | });
85 |
86 | // run some plain java (blocking)
87 | vertx.executeBlocking(() -> {
88 | JsonObject headers = new JsonObject();
89 | headers.put("action", "findAll");
90 |
91 | JsonObject body = new JsonObject();
92 | body.put("model", "news");
93 |
94 | JsonObject protocol = new JsonObject();
95 | protocol.put("type", "send");
96 | protocol.put("headers", headers);
97 | protocol.put("body", body);
98 | protocol.put("address", "io.vertx");
99 | protocol.put("replyAddress", "durp");
100 |
101 |
102 | Buffer buffer = Buffer.buffer();
103 | buffer.appendInt(protocol.encode().getBytes().length);
104 | buffer.appendString(protocol.encode());
105 |
106 | Socket clientSocket = new Socket("localhost", 7000);
107 |
108 | DataOutputStream output = new DataOutputStream(clientSocket.getOutputStream());
109 | output.write(buffer.getBytes());
110 |
111 | DataInputStream input = new DataInputStream(clientSocket.getInputStream());
112 |
113 | int bytesLength = input.readInt();
114 | byte[] bytes = new byte[bytesLength];
115 | for(int i = 0; i < bytesLength; i++) {
116 | bytes[i] = input.readByte();
117 | }
118 |
119 | input.close();
120 | output.close();
121 | clientSocket.close();
122 |
123 | JsonObject reply = new JsonObject(new String(bytes));
124 |
125 | // assert that the body is the same we sent
126 | context.assertEquals(body, reply.getJsonObject("body"));
127 |
128 | return null;
129 | }).onComplete(res -> {
130 | context.assertTrue(res.succeeded());
131 | async.complete();
132 | });
133 | }
134 |
135 | @Test
136 | public void testSendMessageWithReplyBacktrack(TestContext context) {
137 | // Send a request and get a response
138 | NetClient client = vertx.createNetClient();
139 | final Async async = context.async();
140 |
141 | client.connect(7000, "localhost").onComplete(conn -> {
142 | context.assertFalse(conn.failed());
143 |
144 | NetSocket socket = conn.result();
145 |
146 | final FrameParser parser = new FrameParser(parse -> {
147 | context.assertTrue(parse.succeeded());
148 | JsonObject frame = parse.result();
149 | context.assertNotEquals("err", frame.getString("type"));
150 | context.assertEquals("Hello vert.x", frame.getJsonObject("body").getString("value"));
151 | client.close();
152 | async.complete();
153 | });
154 |
155 | socket.handler(parser);
156 |
157 | FrameHelper.sendFrame("send", "hello", "#backtrack", new JsonObject().put("value", "vert.x"), socket);
158 | });
159 | }
160 |
161 | @Test
162 | public void testSendMessageWithDuplicateReplyID(TestContext context) {
163 | // replies must always return to the same origin
164 |
165 | NetClient client = vertx.createNetClient();
166 | final Async async = context.async();
167 |
168 | client.connect(7000, "localhost").onComplete(conn -> {
169 | context.assertFalse(conn.failed());
170 |
171 | NetSocket socket = conn.result();
172 |
173 | vertx.eventBus().consumer("third-party-receiver", msg -> context.fail());
174 |
175 | final FrameParser parser = new FrameParser(parse -> {
176 | context.assertTrue(parse.succeeded());
177 | client.close();
178 | async.complete();
179 | });
180 |
181 | socket.handler(parser);
182 |
183 |
184 | FrameHelper.sendFrame("send", "hello", "third-party-receiver", new JsonObject().put("value", "vert.x"), socket);
185 | });
186 | }
187 |
188 | @Test
189 | public void testRegister(TestContext context) {
190 | // Send a request and get a response
191 | NetClient client = vertx.createNetClient();
192 | final Async async = context.async();
193 |
194 | client.connect(7000, "localhost").onComplete(conn -> {
195 | context.assertFalse(conn.failed());
196 |
197 | NetSocket socket = conn.result();
198 |
199 | // 1 reply will arrive
200 | // MESSAGE for echo
201 | final FrameParser parser = new FrameParser(parse -> {
202 | context.assertTrue(parse.succeeded());
203 | JsonObject frame = parse.result();
204 |
205 | context.assertNotEquals("err", frame.getString("type"));
206 | context.assertEquals("Vert.x", frame.getJsonObject("body").getString("value"));
207 | client.close();
208 | async.complete();
209 | });
210 |
211 | socket.handler(parser);
212 |
213 | FrameHelper.sendFrame("register", "echo", null, socket);
214 |
215 | // now try to publish a message so it gets delivered both to the consumer registred on the startup and to this
216 | // remote consumer
217 |
218 | FrameHelper.sendFrame("publish", "echo", new JsonObject().put("value", "Vert.x"), socket);
219 | });
220 |
221 | }
222 | }
223 |
--------------------------------------------------------------------------------
/src/test/java/io/vertx/ext/eventbus/bridge/tcp/TcpEventBusBridgeTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Red Hat, Inc.
3 | *
4 | * All rights reserved. This program and the accompanying materials
5 | * are made available under the terms of the Eclipse Public License v1.0
6 | * and Apache License v2.0 which accompanies this distribution.
7 | *
8 | * The Eclipse Public License is available at
9 | * http://www.eclipse.org/legal/epl-v10.html
10 | *
11 | * The Apache License v2.0 is available at
12 | * http://www.opensource.org/licenses/apache2.0.php
13 | *
14 | * You may elect to redistribute this code under either of these licenses.
15 | */
16 | package io.vertx.ext.eventbus.bridge.tcp;
17 |
18 | import java.util.concurrent.atomic.AtomicBoolean;
19 |
20 | import org.junit.After;
21 | import org.junit.Before;
22 | import org.junit.Test;
23 | import org.junit.runner.RunWith;
24 | import io.vertx.core.Handler;
25 | import io.vertx.core.Vertx;
26 | import io.vertx.core.eventbus.Message;
27 | import io.vertx.core.json.JsonObject;
28 | import io.vertx.core.net.NetClient;
29 | import io.vertx.core.net.NetServerOptions;
30 | import io.vertx.core.net.NetSocket;
31 | import io.vertx.ext.bridge.BridgeEventType;
32 | import io.vertx.ext.bridge.BridgeOptions;
33 | import io.vertx.ext.bridge.PermittedOptions;
34 | import io.vertx.ext.eventbus.bridge.tcp.impl.protocol.FrameHelper;
35 | import io.vertx.ext.eventbus.bridge.tcp.impl.protocol.FrameParser;
36 | import io.vertx.ext.unit.Async;
37 | import io.vertx.ext.unit.TestContext;
38 | import io.vertx.ext.unit.junit.VertxUnitRunner;
39 |
40 | @RunWith(VertxUnitRunner.class)
41 | public class TcpEventBusBridgeTest {
42 |
43 | private Vertx vertx;
44 | private volatile Handler eventHandler = event -> event.complete(true);
45 |
46 | @Before
47 | public void before(TestContext context) {
48 | vertx = Vertx.vertx();
49 | final Async async = context.async();
50 |
51 | vertx.eventBus().consumer("hello", (Message msg) -> msg.reply(new JsonObject().put("value", "Hello " + msg.body().getString("value"))));
52 |
53 | vertx.eventBus().consumer("echo", (Message msg) -> msg.reply(msg.body()));
54 |
55 | vertx.setPeriodic(1000, __ -> vertx.eventBus().send("ping", new JsonObject().put("value", "hi")));
56 |
57 | TcpEventBusBridge bridge = TcpEventBusBridge.create(
58 | vertx,
59 | new BridgeOptions()
60 | .addInboundPermitted(new PermittedOptions().setAddress("hello"))
61 | .addInboundPermitted(new PermittedOptions().setAddress("echo"))
62 | .addInboundPermitted(new PermittedOptions().setAddress("test"))
63 | .addOutboundPermitted(new PermittedOptions().setAddress("echo"))
64 | .addOutboundPermitted(new PermittedOptions().setAddress("test"))
65 | .addOutboundPermitted(new PermittedOptions().setAddress("ping")), new NetServerOptions(), event -> eventHandler.handle(event));
66 |
67 | bridge.listen(7000).onComplete(res -> {
68 | context.assertTrue(res.succeeded());
69 | async.complete();
70 | });
71 | }
72 |
73 | @After
74 | public void after(TestContext context) {
75 | vertx.close().onComplete(context.asyncAssertSuccess());
76 | }
77 |
78 | @Test
79 | public void testSendVoidMessage(TestContext context) {
80 | // Send a request and get a response
81 | NetClient client = vertx.createNetClient();
82 | final Async async = context.async();
83 |
84 | vertx.eventBus().consumer("test", (Message msg) -> {
85 | client.close();
86 | async.complete();
87 | });
88 |
89 | client.connect(7000, "localhost").onComplete(context.asyncAssertSuccess(socket -> {
90 | FrameHelper.sendFrame("send", "test", new JsonObject().put("value", "vert.x"), socket);
91 | }));
92 | }
93 |
94 | @Test
95 | public void testSendVoidStringMessage(TestContext context) {
96 | // Send a request and get a response
97 | NetClient client = vertx.createNetClient();
98 | final Async async = context.async();
99 |
100 | vertx.eventBus().consumer("test", (Message