diff --git a/cmd/nbe/docs.go b/cmd/nbe/docs.go
index 285aa97a..1141df18 100644
--- a/cmd/nbe/docs.go
+++ b/cmd/nbe/docs.go
@@ -27,7 +27,7 @@ var (
Deno,
Node,
Rust,
- CSharp,
+ DotNet,
Java,
Ruby,
Elixir,
@@ -389,6 +389,8 @@ func chromaFormat(code, lang string) (string, error) {
lang = "js"
case WebSocket:
lang = "js"
+ case DotNet:
+ lang = "cs"
}
lexer := lexers.Get(lang)
diff --git a/cmd/nbe/parse.go b/cmd/nbe/parse.go
index 90b13b5f..227fd1b0 100644
--- a/cmd/nbe/parse.go
+++ b/cmd/nbe/parse.go
@@ -21,7 +21,7 @@ const (
Go = "go"
Rust = "rust"
Java = "java"
- CSharp = "csharp"
+ DotNet = "dotnet"
Deno = "deno"
Node = "node"
Bun = "bun"
@@ -40,7 +40,7 @@ var (
Go: "Go",
Rust: "Rust",
Java: "Java",
- CSharp: "C#",
+ DotNet: "C#",
Deno: "Deno",
Node: "Node",
Bun: "Bun",
@@ -63,6 +63,7 @@ var (
Node: "main.js",
WebSocket: "main.js",
Java: "Main.java",
+ DotNet: "Main.cs",
}
languageMultiCommentDelims = map[string][2]string{
@@ -70,7 +71,7 @@ var (
// TODO: java has a few conventions..
// https://www.oracle.com/java/technologies/javase/codeconventions-comments.html
Java: {"/*", "*/"},
- CSharp: {"/**", "**/"},
+ DotNet: {"/**", "**/"},
Deno: {"/*", "*/"},
Node: {"/*", "*/"},
Bun: {"/*", "*/"},
@@ -84,7 +85,7 @@ var (
Go: "//",
Rust: "//",
Java: "//",
- CSharp: "///",
+ DotNet: "//",
Deno: "//",
Node: "//",
Bun: "//",
@@ -162,10 +163,12 @@ var (
// One limitiation is that it does not currently handle trailing multi-line
// comments, such as:
-// func() int {/*
-// a := 1
-// */
-// b := 2
+//
+// func() int {/*
+// a := 1
+// */
+// b := 2
+//
// Since this code is scoped to well written examples, it should not be an issue
// in practice.
func parseLineType(lang, line string) LineType {
@@ -183,7 +186,7 @@ func parseLineType(lang, line string) LineType {
}
return NormalLine
- case Go, CSharp, Java, Rust, C, Deno, Node, Bun:
+ case Go, DotNet, Java, Rust, C, Deno, Node, Bun:
if cStyleSingleCommentLineRe.MatchString(line) {
return SingleCommentLine
}
diff --git a/docker/dotnet/Dockerfile b/docker/dotnet/Dockerfile
new file mode 100644
index 00000000..081a62ff
--- /dev/null
+++ b/docker/dotnet/Dockerfile
@@ -0,0 +1,17 @@
+# https://hub.docker.com/_/microsoft-dotnet
+FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
+WORKDIR /source
+
+# copy csproj and restore as distinct layers
+COPY *.csproj .
+RUN dotnet restore --use-current-runtime
+
+# copy and publish app and libraries
+COPY . .
+RUN dotnet publish -c Release -o /app --use-current-runtime --self-contained false --no-restore
+
+# final stage/image
+FROM mcr.microsoft.com/dotnet/runtime:7.0
+WORKDIR /app
+COPY --from=build /app .
+ENTRYPOINT ["dotnet", "example.dll"]
diff --git a/docker/dotnet/example.csproj b/docker/dotnet/example.csproj
new file mode 100644
index 00000000..09fa9606
--- /dev/null
+++ b/docker/dotnet/example.csproj
@@ -0,0 +1,15 @@
+
+
+
+ Exe
+ net7.0
+ NATS by Example
+ enable
+ false
+
+
+
+
+
+
+
diff --git a/examples/issues/3724/cli/Dockerfile b/examples/issues/3724/cli/Dockerfile
new file mode 100644
index 00000000..39dc7992
--- /dev/null
+++ b/examples/issues/3724/cli/Dockerfile
@@ -0,0 +1,29 @@
+FROM golang:1.19-alpine3.17 AS build
+
+RUN go install github.com/nats-io/natscli/nats@latest #allow-online-peer-remove
+RUN go install github.com/nats-io/nats-server/v2@main
+
+FROM natsio/nats-box:0.13.2
+
+RUN apk add bash curl
+
+COPY --from=build /go/bin/nats-server /usr/local/bin/
+COPY --from=build /go/bin/nats /usr/local/bin/
+
+COPY . .
+
+ENTRYPOINT ["bash"]
+
+CMD ["main.sh"]
+
+#FROM natsio/nats-box:0.13.3
+
+#RUN apk add bash curl
+
+#COPY --from=nats:2.9.10 /nats-server /usr/local/bin/
+
+#COPY . .
+
+#ENTRYPOINT ["bash"]
+
+#CMD ["main.sh"]
diff --git a/examples/issues/3724/cli/main.sh b/examples/issues/3724/cli/main.sh
new file mode 100644
index 00000000..2591ec87
--- /dev/null
+++ b/examples/issues/3724/cli/main.sh
@@ -0,0 +1,413 @@
+#!/bin/bash
+
+set -euo pipefail
+
+unset NATS_URL
+
+cat <<- EOF > a_1.conf
+cluster: {
+ name: hengli
+ port: 15333
+ routes: [nats-route://127.0.0.1:15333,nats-route://127.0.0.1:15334,nats-route://127.0.0.1:15335]
+}
+http_port: 18222
+jetstream: {
+ domain: hengli
+ store_dir: "./jetstream/hengli_1"
+}
+leafnodes: {
+ port: 14333
+ remotes: [{
+ account: hengli
+ urls: [nats-leaf://_hengliConnUser_:_hengliConnUser_@127.0.0.1:24333,nats-leaf://_hengliConnUser_:_hengliConnUser_@127.0.0.1:24334,nats-leaf://_hengliConnUser_:_hengliConnUser_@127.0.0.1:24335]
+ }]
+}
+port: 14222
+server_name: hengli_1
+
+accounts: {
+ hengli: {
+ jetstream: enabled
+ users:[
+ {user: _hengliConnUser_, password: _hengliConnUser_}
+ {user: hengli, password: hengli, permissions: {
+ publish: {
+ allow:["hengli.to.wsw.test","\$JS.API.>"]
+ },
+ subscribe: {
+ allow:["\$JS.ACK.hengli_to_wsw_test.>","_INBOX.>"]
+ }
+ }}
+ ]
+ }
+}
+EOF
+
+cat <<- EOF > a_2.conf
+cluster: {
+ name: hengli
+ port: 15334
+ routes: [nats-route://127.0.0.1:15333,nats-route://127.0.0.1:15334,nats-route://127.0.0.1:15335]
+}
+http_port: 18223
+jetstream: {
+ domain: hengli
+ store_dir: "./jetstream/hengli_2"
+}
+leafnodes: {
+ port: 14334
+ remotes: [{
+ account: hengli
+ urls: [nats-leaf://_hengliConnUser_:_hengliConnUser_@127.0.0.1:24333,nats-leaf://_hengliConnUser_:_hengliConnUser_@127.0.0.1:24334,nats-leaf://_hengliConnUser_:_hengliConnUser_@127.0.0.1:24335]
+ }]
+}
+port: 14223
+server_name: hengli_2
+
+accounts: {
+ hengli: {
+ jetstream: enabled
+ users:[
+ {user: _hengliConnUser_, password: _hengliConnUser_},
+ {user: hengli, password: hengli, permissions: {
+ publish: {
+ allow:["hengli.to.wsw.test","\$JS.API.>"]
+ },
+ subscribe: {
+ allow:["\$JS.ACK.hengli_to_wsw_test.>","_INBOX.>"]
+ }
+ }}
+ ]
+ }
+}
+EOF
+
+# Define the server configs for cluster.
+cat <<- EOF > "a_3.conf"
+
+cluster: {
+ name: hengli
+ port: 15335
+ routes: [nats-route://127.0.0.1:15333,nats-route://127.0.0.1:15334,nats-route://127.0.0.1:15335]
+}
+http_port: 18224
+jetstream: {
+ domain: hengli
+ store_dir: "./jetstream/hengli_3"
+}
+leafnodes: {
+ port: 14335
+ remotes: [{
+ account: hengli
+ urls: [nats-leaf://_hengliConnUser_:_hengliConnUser_@127.0.0.1:24333,nats-leaf://_hengliConnUser_:_hengliConnUser_@127.0.0.1:24334,nats-leaf://_hengliConnUser_:_hengliConnUser_@127.0.0.1:24335]
+ }]
+}
+port: 14224
+server_name: hengli_3
+
+accounts: {
+ hengli: {
+ jetstream: enabled
+ users:[
+ {user: _hengliConnUser_, password: _hengliConnUser_},
+ {user: hengli, password: hengli, permissions: {
+ publish: {
+ allow:["hengli.to.wsw.test","\$JS.API.>"]
+ },
+ subscribe: {
+ allow:["\$JS.ACK.hengli_to_wsw_test.>","_INBOX.>"]
+ }
+ }}
+ ]
+ }
+}
+EOF
+
+# Setup the hub cluster.
+cat <<- EOF > "b_1.conf"
+http_port: 28222
+cluster: {
+ name: wsw
+ port: 25333
+ routes: [nats-route://127.0.0.1:25333,nats-route://127.0.0.1:25334,nats-route://127.0.0.1:25335]
+}
+jetstream: {
+ domain: wsw
+ store_dir: "./jetstream/wsw_1"
+}
+leafnodes: {
+ port: 24333
+}
+port: 24222
+server_name: wsw_1
+
+accounts: {
+ hengli: {
+ jetstream: enabled
+ users:[
+ {user: _hengliConnUser_, password: _hengliConnUser_}
+ ]
+ exports:[
+ {service: "\$JS.hengli.API.>", response: stream},
+ {service: "\$JS.FC.>"},
+ {stream: "deliver.hengli.wsw.>", accounts: ["wsw"]}
+ ]
+ },
+ wsw: {
+ jetstream: enabled
+ users:[
+ {user: _wswConnUser_, password: _wswConnUser_},
+ {user: wsw, password: wsw, permissions: {
+ publish: {
+ allow:["\$JS.ACK.hengli_to_wsw_test.>"]
+ },
+ subscribe: {
+ allow:["_recv_wsw.hengli.to.wsw.test"]
+ }
+ }}
+ ]
+ imports:[
+ {service: {account:"hengli", subject: "\$JS.hengli.API.>"}, to: "\$JS.hengli.wsw.API.>"},
+ {service: {account: "hengli", subject: "\$JS.FC.>"}},
+ {stream: {account:"hengli", subject:"deliver.hengli.wsw.>"}}
+ ]
+ }
+}
+EOF
+
+cat <<- EOF > "b_2.conf"
+
+cluster: {
+ name: wsw
+ port: 25334
+ routes: [nats-route://127.0.0.1:25333,nats-route://127.0.0.1:25334,nats-route://127.0.0.1:25335]
+}
+jetstream: {
+ domain: wsw
+ store_dir: "./jetstream/wsw_2"
+}
+leafnodes: {
+ port: 24334
+}
+port: 24223
+server_name: wsw_2
+
+accounts: {
+ hengli: {
+ jetstream: enabled
+ users:[
+ {user: _hengliConnUser_, password: _hengliConnUser_}
+ ]
+ exports:[
+ {service: "\$JS.hengli.API.>", response: stream},
+ {service: "\$JS.FC.>"},
+ {stream: "deliver.hengli.wsw.>", accounts: ["wsw"]}
+ ]
+ },
+ wsw: {
+ jetstream: enabled
+ users:[
+ {user: _wswConnUser_, password: _wswConnUser_},
+ {user: wsw, password: wsw, permissions: {
+ publish: {
+ allow:["\$JS.ACK.hengli_to_wsw_test.>"]
+ },
+ subscribe: {
+ allow:["_recv_wsw.hengli.to.wsw.test"]
+ }
+ }}
+ ]
+ imports:[
+ {service: {account:"hengli", subject: "\$JS.hengli.API.>"}, to: "\$JS.hengli.wsw.API.>"},
+ {service: {account: "hengli", subject: "\$JS.FC.>"}},
+ {stream: {account:"hengli", subject:"deliver.hengli.wsw.>"}}
+ ]
+ }
+}
+EOF
+
+cat <<- EOF > "b_3.conf"
+cluster: {
+ name: wsw
+ port: 25335
+ routes: [nats-route://127.0.0.1:25333,nats-route://127.0.0.1:25334,nats-route://127.0.0.1:25335]
+}
+jetstream: {
+ domain: wsw
+ store_dir: "./jetstream/wsw_3"
+}
+leafnodes: {
+ port: 24335
+}
+port: 24224
+server_name: wsw_3
+
+accounts: {
+ hengli: {
+ jetstream: enabled
+ users:[
+ {user: _hengliConnUser_, password: _hengliConnUser_}
+ ]
+ exports:[
+ {service: "\$JS.hengli.API.>", response: stream},
+ {service: "\$JS.FC.>"},
+ {stream: "deliver.hengli.wsw.>", accounts: ["wsw"]}
+ ]
+ },
+ wsw: {
+ jetstream: enabled
+ users:[
+ {user: _wswConnUser_, password: _wswConnUser_},
+ {user: wsw, password: wsw, permissions: {
+ publish: {
+ allow:["\$JS.ACK.hengli_to_wsw_test.>"]
+ },
+ subscribe: {
+ allow:["_recv_wsw.hengli.to.wsw.test"]
+ }
+ }}
+ ]
+ imports:[
+ {service: {account:"hengli", subject: "\$JS.hengli.API.>"}, to: "\$JS.hengli.wsw.API.>"},
+ {service: {account: "hengli", subject: "\$JS.FC.>"}},
+ {stream: {account:"hengli", subject:"deliver.hengli.wsw.>"}}
+ ]
+ }
+}
+EOF
+
+# ### Bring up the cluster
+
+# Start a server for each configuration and sleep a second in
+# between so the seeds can startup and get healthy.
+echo "Starting b_1..."
+nats-server -c b_1.conf -P b_1.pid > /dev/null 2>&1 &
+sleep 1
+
+echo "Starting b_2..."
+nats-server -c b_2.conf -P b_2.pid > /dev/null 2>&1 &
+sleep 1
+
+echo "Starting b_3..."
+nats-server -c b_3.conf -P b_3.pid > /dev/null 2>&1 &
+sleep 1
+
+# Wait until the servers up and healthy.
+echo 'Hub cluster healthy?'
+curl --fail --silent \
+ --retry 5 \
+ --retry-delay 2 \
+ http://localhost:28222/healthz; echo
+
+echo "Starting a_1..."
+nats-server -c a_1.conf -P a_1.pid > /dev/null 2>&1 &
+sleep 1
+
+echo "Starting a_2..."
+nats-server -c a_2.conf -P a_2.pid > /dev/null 2>&1 &
+sleep 1
+
+echo "Starting a_3..."
+nats-server -c a_3.conf -P a_3.pid > /dev/null 2>&1 &
+sleep 1
+
+
+# Wait until the servers up and healthy.
+echo 'Leaf cluster healthy?'
+curl --fail --silent \
+ --retry 5 \
+ --retry-delay 2 \
+ http://localhost:18222/healthz; echo
+
+cat <<- EOF > origin-stream.json
+{
+ "name": "hengli_to_wsw_test",
+ "subjects": [
+ "hengli.to.wsw.test"
+ ],
+ "retention": "limits",
+ "max_consumers": -1,
+ "max_msgs": -1,
+ "max_bytes": -1,
+ "max_age": 0,
+ "max_msgs_per_subject": -1,
+ "max_msg_size": -1,
+ "discard": "old",
+ "storage": "file",
+ "num_replicas": 2,
+ "duplicate_window": 120000000000,
+ "allow_direct": false,
+ "mirror_direct": false,
+ "sealed": false,
+ "deny_delete": false,
+ "deny_purge": false,
+ "allow_rollup_hdrs": false
+}
+EOF
+
+cat <<- EOF > mirror-stream.json
+{
+ "name": "_mirror_hengli_to_wsw_test_hengli",
+ "retention": "limits",
+ "max_consumers": -1,
+ "max_msgs": -1,
+ "max_bytes": -1,
+ "max_age": 3600000000000,
+ "max_msgs_per_subject": -1,
+ "max_msg_size": -1,
+ "discard": "old",
+ "storage": "file",
+ "num_replicas": 2,
+ "mirror": {
+ "name": "hengli_to_wsw_test",
+ "external": {
+ "api": "\$JS.hengli.wsw.API",
+ "deliver": "deliver.hengli.wsw"
+ }
+ },
+ "allow_direct": false,
+ "mirror_direct": true,
+ "sealed": false,
+ "deny_delete": false,
+ "deny_purge": false,
+ "allow_rollup_hdrs": false
+}
+EOF
+
+nats -s "nats://_hengliConnUser_:_hengliConnUser_@127.0.0.1:14222" \
+ stream add --config origin-stream.json
+
+nats -s "nats://_hengliConnUser_:_hengliConnUser_@127.0.0.1:14222" \
+ stream report
+
+nats -s "nats://_wswConnUser_:_wswConnUser_@127.0.0.1:24222" \
+ stream add --config mirror-stream.json
+
+nats -s "nats://_wswConnUser_:_wswConnUser_@127.0.0.1:24222" \
+ stream report
+
+echo 'Starting bench...'
+nats bench \
+ -s "nats://hengli:hengli@127.0.0.1:14222" \
+ --pub 1 \
+ --js \
+ --stream hengli_to_wsw_test \
+ --msgs 60000 \
+ --no-progress \
+ hengli.to.wsw.test
+
+nats bench \
+ -s "nats://hengli:hengli@127.0.0.1:14222" \
+ --pub 1 \
+ --js \
+ --stream hengli_to_wsw_test \
+ --msgs 60000 \
+ --no-progress \
+ hengli.to.wsw.test
+
+# Report the streams
+nats -s "nats://_hengliConnUser_:_hengliConnUser_@127.0.0.1:14222" \
+ stream report
+
+nats -s "nats://_wswConnUser_:_wswConnUser_@127.0.0.1:24222" \
+ stream report
diff --git a/examples/issues/3724/cli/main.sh_ b/examples/issues/3724/cli/main.sh_
new file mode 100644
index 00000000..2a12de05
--- /dev/null
+++ b/examples/issues/3724/cli/main.sh_
@@ -0,0 +1,330 @@
+#!/bin/bash
+
+set -euo pipefail
+
+unset NATS_URL
+
+cat <<- EOF > hub-shared.conf
+accounts: {
+ SYS: {
+ users: [{user: sys, pass: sys}]
+ }
+ APP: {
+ jetstream: true
+ users: [{user: app, pass: app}]
+ }
+}
+
+system_account: SYS
+EOF
+
+# Define the server configs for cluster.
+cat <<- EOF > "n1.conf"
+server_name: n1
+port: 4222
+http_port: 8222
+include hub-shared.conf
+jetstream {
+ domain: hub
+ store_dir: "./n1"
+}
+cluster: {
+ name: hub
+ port: 6222
+ routes: [
+ nats-route://127.0.0.1:6222,
+ nats-route://127.0.0.1:6223,
+ nats-route://127.0.0.1:6224,
+ ]
+}
+leafnodes {
+ port: 7422
+}
+EOF
+
+cat <<- EOF > "n2.conf"
+server_name: n2
+port: 4223
+include hub-shared.conf
+jetstream {
+ domain: hub
+ store_dir: "./n2"
+}
+cluster: {
+ name: hub
+ port: 6223
+ routes: [
+ nats-route://127.0.0.1:6222,
+ nats-route://127.0.0.1:6223,
+ nats-route://127.0.0.1:6224,
+ ]
+}
+leafnodes {
+ port: 7423
+}
+EOF
+
+cat <<- EOF > "n3.conf"
+server_name: n3
+port: 4224
+include hub-shared.conf
+jetstream {
+ domain: hub
+ store_dir: "./n3"
+}
+cluster: {
+ name: hub
+ port: 6224
+ routes: [
+ nats-route://127.0.0.1:6222,
+ nats-route://127.0.0.1:6223,
+ nats-route://127.0.0.1:6224,
+ ]
+}
+leafnodes {
+ port: 7424
+}
+EOF
+
+# ### Bring up the cluster
+
+# Start a server for each configuration and sleep a second in
+# between so the seeds can startup and get healthy.
+echo "Starting n1..."
+nats-server -c n1.conf -P n1.pid > /dev/null 2>&1 &
+sleep 1
+
+echo "Starting n2..."
+nats-server -c n2.conf -P n2.pid > /dev/null 2>&1 &
+sleep 1
+
+echo "Starting n3..."
+nats-server -c n3.conf -P n3.pid > /dev/null 2>&1 &
+sleep 1
+
+# Wait until the servers up and healthy.
+echo 'Hub cluster healthy?'
+curl --fail --silent \
+ --retry 5 \
+ --retry-delay 1 \
+ http://localhost:8222/healthz; echo
+
+cat <<- EOF > leaf-shared.conf
+accounts: {
+ SYS: {
+ users: [{user: sys, pass: sys}]
+ }
+ APP: {
+ jetstream: true
+ users: [{user: app, pass: app}]
+ }
+}
+
+system_account: SYS
+
+leafnodes {
+ remotes [
+ {
+ urls: [
+ nats-leaf://app:app@localhost:7422,
+ nats-leaf://app:app@localhost:7423,
+ nats-leaf://app:app@localhost:7424,
+ ]
+ account: APP
+ }
+ ]
+}
+EOF
+
+
+# Define the server configs for cluster.
+cat <<- EOF > "l1.conf"
+server_name: l1
+port: 4225
+http_port: 8223
+include leaf-shared.conf
+jetstream {
+ domain: leaf
+ store_dir: "./l1"
+}
+cluster: {
+ name: leaf
+ port: 6225
+ routes: [
+ nats-route://127.0.0.1:6225,
+ nats-route://127.0.0.1:6226,
+ nats-route://127.0.0.1:6227,
+ ]
+}
+EOF
+
+cat <<- EOF > "l2.conf"
+server_name: l2
+port: 4226
+include leaf-shared.conf
+jetstream {
+ domain: leaf
+ store_dir: "./l2"
+}
+cluster: {
+ name: leaf
+ port: 6226
+ routes: [
+ nats-route://127.0.0.1:6225,
+ nats-route://127.0.0.1:6226,
+ nats-route://127.0.0.1:6227,
+ ]
+}
+EOF
+
+cat <<- EOF > "l3.conf"
+server_name: l3
+port: 4227
+include leaf-shared.conf
+jetstream {
+ domain: leaf
+ store_dir: "./l3"
+}
+cluster: {
+ name: leaf
+ port: 6227
+ routes: [
+ nats-route://127.0.0.1:6225,
+ nats-route://127.0.0.1:6226,
+ nats-route://127.0.0.1:6227,
+ ]
+}
+EOF
+
+# ### Bring up the leaf cluster
+
+echo "Starting l1..."
+nats-server -c l1.conf -P l1.pid > /dev/null 2>&1 &
+sleep 1
+
+echo "Starting l2..."
+nats-server -c l2.conf -P l2.pid > /dev/null 2>&1 &
+sleep 1
+
+echo "Starting l3..."
+nats-server -c l3.conf -P l3.pid > /dev/null 2>&1 &
+sleep 1
+
+# Wait until the servers up and healthy.
+echo 'Leaf cluster healthy?'
+curl --fail --silent \
+ --retry 5 \
+ --retry-delay 1 \
+ http://localhost:8223/healthz; echo
+
+nats context save --user app --password app -s nats://localhost:4222 hub-app
+
+nats context save --user app --password app -s nats://localhost:4225 leaf-app
+
+
+nats context save --user sys --password sys -s nats://localhost:4222 hub-sys
+
+nats context save --user sys --password sys -s nats://localhost:4225 leaf-sys
+
+cat <<- EOF > origin.json
+{
+ "name": "events",
+ "subjects": [
+ "events.*"
+ ],
+ "retention": "limits",
+ "max_consumers": -1,
+ "max_msgs_per_subject": -1,
+ "max_msgs": -1,
+ "max_bytes": -1,
+ "max_age": 0,
+ "max_msg_size": -1,
+ "storage": "file",
+ "discard": "old",
+ "num_replicas": 2,
+ "duplicate_window": 120000000000,
+ "sealed": false,
+ "deny_delete": false,
+ "deny_purge": false,
+ "allow_rollup_hdrs": false,
+ "allow_direct": false,
+ "mirror_direct": false
+}
+EOF
+
+cat <<- EOF > hub-mirror.json
+{
+ "name": "events-m",
+ "retention": "limits",
+ "max_consumers": -1,
+ "max_msgs_per_subject": -1,
+ "max_msgs": -1,
+ "max_bytes": -1,
+ "max_age": 0,
+ "max_msg_size": -1,
+ "storage": "file",
+ "discard": "old",
+ "num_replicas": 1,
+ "mirror": {
+ "name": "events"
+ },
+ "sealed": false,
+ "deny_delete": false,
+ "deny_purge": false,
+ "allow_rollup_hdrs": false,
+ "allow_direct": false,
+ "mirror_direct": false
+}
+EOF
+
+cat <<- EOF > leaf-mirror.json
+{
+ "name": "events",
+ "retention": "limits",
+ "max_consumers": -1,
+ "max_msgs_per_subject": -1,
+ "max_msgs": -1,
+ "max_bytes": -1,
+ "max_age": 0,
+ "max_msg_size": -1,
+ "storage": "file",
+ "discard": "old",
+ "num_replicas": 1,
+ "mirror": {
+ "name": "events-m",
+ "external": {
+ "api": "\$JS.hub.API"
+ }
+ },
+ "sealed": false,
+ "deny_delete": false,
+ "deny_purge": false,
+ "allow_rollup_hdrs": false,
+ "allow_direct": false,
+ "mirror_direct": false
+}
+EOF
+
+nats --context hub-app stream add --config origin.json
+nats --context hub-app stream add --config hub-mirror.json
+nats --context leaf-app stream add --config leaf-mirror.json
+
+echo 'Starting bench...'
+nats bench \
+ --context hub-app \
+ --pub 1 \
+ --js \
+ --size 10k \
+ --stream events \
+ --msgs 10000 \
+ --pubbatch 100 \
+ --no-progress \
+ --multisubject \
+ events
+
+sleep 2
+
+# Report the streams
+nats --context hub-app stream report
+nats --context leaf-app stream report
+
diff --git a/examples/messaging/pub-sub/dotnet/Main.cs b/examples/messaging/pub-sub/dotnet/Main.cs
new file mode 100644
index 00000000..a850a123
--- /dev/null
+++ b/examples/messaging/pub-sub/dotnet/Main.cs
@@ -0,0 +1,77 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+using NATS.Client;
+
+// Create a new connection factory to create
+// a connection.
+Options opts = ConnectionFactory.GetDefaultOptions();
+opts.Url = "nats://nats:4222";
+
+// Creates a live connection to the default
+// NATS Server running locally
+ConnectionFactory cf = new ConnectionFactory();
+IConnection c = cf.CreateConnection(opts);
+
+// Setup an event handler to process incoming messages.
+// An anonymous delegate function is used for brevity.
+EventHandler h = (sender, args) =>
+{
+ // print the message
+ Console.WriteLine(args.Message);
+
+ // Here are some of the accessible properties from
+ // the message:
+ // args.Message.Data;
+ // args.Message.Reply;
+ // args.Message.Subject;
+ // args.Message.ArrivalSubcription.Subject;
+ // args.Message.ArrivalSubcription.QueuedMessageCount;
+ // args.Message.ArrivalSubcription.Queue;
+
+ // Unsubscribing from within the delegate function is supported.
+ args.Message.ArrivalSubcription.Unsubscribe();
+};
+
+// The simple way to create an asynchronous subscriber
+// is to simply pass the event in. Messages will start
+// arriving immediately.
+IAsyncSubscription s = c.SubscribeAsync("foo", h);
+
+// Alternatively, create an asynchronous subscriber on subject foo,
+// assign a message handler, then start the subscriber. When
+// multicasting delegates, this allows all message handlers
+// to be setup before messages start arriving.
+IAsyncSubscription sAsync = c.SubscribeAsync("foo");
+sAsync.MessageHandler += h;
+sAsync.Start();
+
+// Simple synchronous subscriber
+ISyncSubscription sSync = c.SubscribeSync("foo");
+
+// Using a synchronous subscriber, gets the first message available,
+// waiting up to 1000 milliseconds (1 second)
+Msg m = sSync.NextMessage(1000);
+
+c.Publish("foo", Encoding.UTF8.GetBytes("hello world"));
+
+// Unsubscribing
+sAsync.Unsubscribe();
+
+// Publish requests to the given reply subject:
+c.Publish("foo", "bar", Encoding.UTF8.GetBytes("help!"));
+
+// Sends a request (internally creates an inbox) and Auto-Unsubscribe the
+// internal subscriber, which means that the subscriber is unsubscribed
+// when receiving the first response from potentially many repliers.
+// This call will wait for the reply for up to 1000 milliseconds (1 second).
+m = c.Request("foo", Encoding.UTF8.GetBytes("help"), 1000);
+
+// Draining and closing a connection
+c.Drain();
+
+// Closing a connection
+c.Close();