Bump queue-worker and NATS

NATS moved its primary library to: github.com/nats-io/stan.go

This commit synchronises the library in the gateway to match the
queue worker.

Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
This commit is contained in:
Alex Ellis (OpenFaaS Ltd) 2020-04-22 10:01:28 +01:00 committed by Alex Ellis
parent 542d7c0b83
commit 9bde25aedb
63 changed files with 3615 additions and 551 deletions

44
gateway/Gopkg.lock generated
View File

@ -54,27 +54,24 @@
version = "v1.0.1"
[[projects]]
digest = "1:2ca73053216eb11c8eea2855c8099ad82773638522f91cc0542ec9759163ff3c"
name = "github.com/nats-io/go-nats"
digest = "1:9b8ee267e79a1730cde2e326cc53035913fce52ceef48142c24f3a15e3c65cb3"
name = "github.com/nats-io/jwt"
packages = ["."]
pruneopts = "UT"
revision = "0c3fc7aed8bb2534e7bfdf0968a75890402d48cd"
version = "v0.3.2"
[[projects]]
digest = "1:2e964212558ab7559cec820aebdf9f04c369f27aafe8581932ae5f0b6826572b"
name = "github.com/nats-io/nats.go"
packages = [
".",
"encoders/builtin",
"util",
]
pruneopts = "UT"
revision = "70fe06cee50d4b6f98248d9675fb55f2a3aa7228"
version = "v1.7.2"
[[projects]]
digest = "1:25a0458641a719a972fa7850c900aaae6b12e74efa6fe35c9bd7a772543bc635"
name = "github.com/nats-io/go-nats-streaming"
packages = [
".",
"pb",
]
pruneopts = "UT"
revision = "7cd94c239033c0405a43454be55a0810d3a3ca89"
version = "v0.4.4"
revision = "4d5d5d17e7df8f8e3bf3c1426a5ed759012faab8"
version = "v1.9.2"
[[projects]]
digest = "1:237d85e4f5e91ac6cfc13bad508627570ff29efa28f523d41aac2d39ca15950f"
@ -92,6 +89,17 @@
revision = "4b96681fa6d28dd0ab5fe79bac63b3a493d9ee94"
version = "v1.0.1"
[[projects]]
digest = "1:30147647e2ded41b85f4991e306053e478c009a817f4b53ce5e55ee7b5d2fbf6"
name = "github.com/nats-io/stan.go"
packages = [
".",
"pb",
]
pruneopts = "UT"
revision = "e2d4c025a5eea41d9566dc8e978c7ee697470b64"
version = "v0.6.0"
[[projects]]
digest = "1:340f4e2e095ead4e0a15b4646da3e4533f8b6520e3a382eaf586e8166f3bbcb5"
name = "github.com/openfaas/faas"
@ -112,15 +120,15 @@
version = "0.12.0"
[[projects]]
digest = "1:b7c06bdc590871ac5c88f8a85acc9f892c994d6327de311ec5871aaecb6f9489"
digest = "1:1cf86a1a93c110ebcf836468bd917e8c116d6d2fc7612829c15e946b02dbf864"
name = "github.com/openfaas/nats-queue-worker"
packages = [
"handler",
"nats",
]
pruneopts = "UT"
revision = "dea1c90b8cc66dc73597b7531a4fd29a32b5f88c"
version = "0.9.0"
revision = "a1835cb71db56e6b814b91df027acf62425a76ad"
version = "0.10.0"
[[projects]]
digest = "1:eb04f69c8991e52eff33c428bd729e04208bf03235be88e4df0d88497c6861b9"

View File

@ -12,7 +12,7 @@
[[constraint]]
name = "github.com/openfaas/nats-queue-worker"
version = "0.9.0"
version = "0.10.0"
[[constraint]]
name = "github.com/prometheus/client_golang"

View File

@ -1,3 +1 @@
module github.com/gorilla/mux
go 1.13

View File

@ -1,21 +0,0 @@
language: go
sudo: false
go:
- 1.11.x
- 1.10.x
go_import_path: github.com/nats-io/go-nats
install:
- go get -t ./...
- go get github.com/nats-io/gnatsd
- go get github.com/mattn/goveralls
- go get github.com/wadey/gocovmerge
- go get -u honnef.co/go/tools/cmd/staticcheck
- go get -u github.com/client9/misspell/cmd/misspell
before_script:
- $(exit $(go fmt ./... | wc -l))
- go vet ./...
- misspell -error -locale US .
- staticcheck -ignore "$(cat staticcheck.ignore)" ./...
script:
- go test -i -race ./...
- if [[ "$TRAVIS_GO_VERSION" =~ 1.11 ]]; then ./scripts/cov.sh TRAVIS; else go test -race ./...; fi

View File

@ -1,4 +0,0 @@
github.com/nats-io/go-nats/*_test.go:SA2002
github.com/nats-io/go-nats/*/*_test.go:SA2002
github.com/nats-io/go-nats/test/context_test.go:SA1012
github.com/nats-io/go-nats/nats.go:SA6000

16
gateway/vendor/github.com/nats-io/jwt/.gitignore generated vendored Normal file
View File

@ -0,0 +1,16 @@
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, build with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# IDE Files
.vscode
.idea/

View File

@ -1,22 +1,22 @@
language: go
sudo: false
go:
- 1.11.x
- 1.13.x
- 1.12.x
install:
- go get -t ./...
- go get github.com/nats-io/nats-streaming-server
- go get github.com/mattn/goveralls
- go get github.com/wadey/gocovmerge
- go get -u honnef.co/go/tools/cmd/staticcheck
- go get -u github.com/client9/misspell/cmd/misspell
before_script:
- $(exit $(go fmt ./... | wc -l))
- go vet ./...
- misspell -error -locale US .
- staticcheck ./...
script:
- go test -i -race ./...
- go test -v -race ./...
after_success:
- if [[ "$TRAVIS_GO_VERSION" =~ 1.12 ]]; then ./scripts/cov.sh TRAVIS; fi

19
gateway/vendor/github.com/nats-io/jwt/Makefile generated vendored Normal file
View File

@ -0,0 +1,19 @@
.PHONY: test cover
build:
go build
test:
gofmt -s -w *.go
goimports -w *.go
go vet ./...
go test -v
go test -v --race
staticcheck ./...
fmt:
gofmt -w -s *.go
cover:
go test -v -covermode=count -coverprofile=coverage.out
go tool cover -html=coverage.out

54
gateway/vendor/github.com/nats-io/jwt/README.md generated vendored Normal file
View File

@ -0,0 +1,54 @@
# JWT
A [JWT](https://jwt.io/) implementation that uses [nkeys](https://github.com/nats-io/nkeys) to digitally sign JWT tokens.
Nkeys use [Ed25519](https://ed25519.cr.yp.to/) to provide authentication of JWT claims.
[![License Apache 2](https://img.shields.io/badge/License-Apache2-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0)
[![ReportCard](http://goreportcard.com/badge/nats-io/jwt)](http://goreportcard.com/report/nats-io/jwt)
[![Build Status](https://travis-ci.org/nats-io/jwt.svg?branch=master)](http://travis-ci.org/nats-io/jwt)
[![GoDoc](http://godoc.org/github.com/nats-io/jwt?status.png)](http://godoc.org/github.com/nats-io/jwt)
[![Coverage Status](https://coveralls.io/repos/github/nats-io/jwt/badge.svg?branch=master&t=NmEFup)](https://coveralls.io/github/nats-io/jwt?branch=master)
```go
// Need a private key to sign the claim, nkeys makes it easy to create
kp, err := nkeys.CreateAccount()
if err != nil {
t.Fatal("unable to create account key", err)
}
pk, err := kp.PublicKey()
if err != nil {
t.Fatal("error getting public key", err)
}
// create a new claim
claims := NewAccountClaims(pk)
claims.Expires = time.Now().Add(time.Duration(time.Hour)).Unix()
// add details by modifying claims.Account
// serialize the claim to a JWT token
token, err := claims.Encode(kp)
if err != nil {
t.Fatal("error encoding token", err)
}
// on the receiving side, decode the token
c, err := DecodeAccountClaims(token)
if err != nil {
t.Fatal(err)
}
// if the token was decoded, it means that it
// validated and it wasn't tampered. the remaining and
// required test is to insure the issuer is trusted
pk, err := kp.PublicKey()
if err != nil {
t.Fatalf("unable to read public key: %v", err)
}
if c.Issuer != pk {
t.Fatalf("the public key is not trusted")
}
```

View File

@ -0,0 +1,5 @@
# Release Notes
## 0.3.0
* Removed revocation claims in favor of timestamp-based revocation maps in account and export claims.

222
gateway/vendor/github.com/nats-io/jwt/account_claims.go generated vendored Normal file
View File

@ -0,0 +1,222 @@
/*
* Copyright 2018-2019 The NATS Authors
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jwt
import (
"errors"
"sort"
"time"
"github.com/nats-io/nkeys"
)
// NoLimit is used to indicate a limit field is unlimited in value.
const NoLimit = -1
// OperatorLimits are used to limit access by an account
type OperatorLimits struct {
Subs int64 `json:"subs,omitempty"` // Max number of subscriptions
Conn int64 `json:"conn,omitempty"` // Max number of active connections
LeafNodeConn int64 `json:"leaf,omitempty"` // Max number of active leaf node connections
Imports int64 `json:"imports,omitempty"` // Max number of imports
Exports int64 `json:"exports,omitempty"` // Max number of exports
Data int64 `json:"data,omitempty"` // Max number of bytes
Payload int64 `json:"payload,omitempty"` // Max message payload
WildcardExports bool `json:"wildcards,omitempty"` // Are wildcards allowed in exports
}
// IsEmpty returns true if all of the limits are 0/false.
func (o *OperatorLimits) IsEmpty() bool {
return *o == OperatorLimits{}
}
// IsUnlimited returns true if all limits are
func (o *OperatorLimits) IsUnlimited() bool {
return *o == OperatorLimits{NoLimit, NoLimit, NoLimit, NoLimit, NoLimit, NoLimit, NoLimit, true}
}
// Validate checks that the operator limits contain valid values
func (o *OperatorLimits) Validate(vr *ValidationResults) {
// negative values mean unlimited, so all numbers are valid
}
// Account holds account specific claims data
type Account struct {
Imports Imports `json:"imports,omitempty"`
Exports Exports `json:"exports,omitempty"`
Identities []Identity `json:"identity,omitempty"`
Limits OperatorLimits `json:"limits,omitempty"`
SigningKeys StringList `json:"signing_keys,omitempty"`
Revocations RevocationList `json:"revocations,omitempty"`
}
// Validate checks if the account is valid, based on the wrapper
func (a *Account) Validate(acct *AccountClaims, vr *ValidationResults) {
a.Imports.Validate(acct.Subject, vr)
a.Exports.Validate(vr)
a.Limits.Validate(vr)
for _, i := range a.Identities {
i.Validate(vr)
}
if !a.Limits.IsEmpty() && a.Limits.Imports >= 0 && int64(len(a.Imports)) > a.Limits.Imports {
vr.AddError("the account contains more imports than allowed by the operator")
}
// Check Imports and Exports for limit violations.
if a.Limits.Imports != NoLimit {
if int64(len(a.Imports)) > a.Limits.Imports {
vr.AddError("the account contains more imports than allowed by the operator")
}
}
if a.Limits.Exports != NoLimit {
if int64(len(a.Exports)) > a.Limits.Exports {
vr.AddError("the account contains more exports than allowed by the operator")
}
// Check for wildcard restrictions
if !a.Limits.WildcardExports {
for _, ex := range a.Exports {
if ex.Subject.HasWildCards() {
vr.AddError("the account contains wildcard exports that are not allowed by the operator")
}
}
}
}
for _, k := range a.SigningKeys {
if !nkeys.IsValidPublicAccountKey(k) {
vr.AddError("%s is not an account public key", k)
}
}
}
// AccountClaims defines the body of an account JWT
type AccountClaims struct {
ClaimsData
Account `json:"nats,omitempty"`
}
// NewAccountClaims creates a new account JWT
func NewAccountClaims(subject string) *AccountClaims {
if subject == "" {
return nil
}
c := &AccountClaims{}
// Set to unlimited to start. We do it this way so we get compiler
// errors if we add to the OperatorLimits.
c.Limits = OperatorLimits{NoLimit, NoLimit, NoLimit, NoLimit, NoLimit, NoLimit, NoLimit, true}
c.Subject = subject
return c
}
// Encode converts account claims into a JWT string
func (a *AccountClaims) Encode(pair nkeys.KeyPair) (string, error) {
if !nkeys.IsValidPublicAccountKey(a.Subject) {
return "", errors.New("expected subject to be account public key")
}
sort.Sort(a.Exports)
sort.Sort(a.Imports)
a.ClaimsData.Type = AccountClaim
return a.ClaimsData.Encode(pair, a)
}
// DecodeAccountClaims decodes account claims from a JWT string
func DecodeAccountClaims(token string) (*AccountClaims, error) {
v := AccountClaims{}
if err := Decode(token, &v); err != nil {
return nil, err
}
return &v, nil
}
func (a *AccountClaims) String() string {
return a.ClaimsData.String(a)
}
// Payload pulls the accounts specific payload out of the claims
func (a *AccountClaims) Payload() interface{} {
return &a.Account
}
// Validate checks the accounts contents
func (a *AccountClaims) Validate(vr *ValidationResults) {
a.ClaimsData.Validate(vr)
a.Account.Validate(a, vr)
if nkeys.IsValidPublicAccountKey(a.ClaimsData.Issuer) {
if len(a.Identities) > 0 {
vr.AddWarning("self-signed account JWTs shouldn't contain identity proofs")
}
if !a.Limits.IsEmpty() {
vr.AddWarning("self-signed account JWTs shouldn't contain operator limits")
}
}
}
// ExpectedPrefixes defines the types that can encode an account jwt, account and operator
func (a *AccountClaims) ExpectedPrefixes() []nkeys.PrefixByte {
return []nkeys.PrefixByte{nkeys.PrefixByteAccount, nkeys.PrefixByteOperator}
}
// Claims returns the accounts claims data
func (a *AccountClaims) Claims() *ClaimsData {
return &a.ClaimsData
}
// DidSign checks the claims against the account's public key and its signing keys
func (a *AccountClaims) DidSign(op Claims) bool {
if op != nil {
issuer := op.Claims().Issuer
if issuer == a.Subject {
return true
}
return a.SigningKeys.Contains(issuer)
}
return false
}
// Revoke enters a revocation by publickey using time.Now().
func (a *AccountClaims) Revoke(pubKey string) {
a.RevokeAt(pubKey, time.Now())
}
// RevokeAt enters a revocation by publickey and timestamp into this export
// If there is already a revocation for this public key that is newer, it is kept.
func (a *AccountClaims) RevokeAt(pubKey string, timestamp time.Time) {
if a.Revocations == nil {
a.Revocations = RevocationList{}
}
a.Revocations.Revoke(pubKey, timestamp)
}
// ClearRevocation removes any revocation for the public key
func (a *AccountClaims) ClearRevocation(pubKey string) {
a.Revocations.ClearRevocation(pubKey)
}
// IsRevokedAt checks if the public key is in the revoked list with a timestamp later than
// the one passed in. Generally this method is called with time.Now() but other time's can
// be used for testing.
func (a *AccountClaims) IsRevokedAt(pubKey string, timestamp time.Time) bool {
return a.Revocations.IsRevoked(pubKey, timestamp)
}
// IsRevoked checks if the public key is in the revoked list with time.Now()
func (a *AccountClaims) IsRevoked(pubKey string) bool {
return a.Revocations.IsRevoked(pubKey, time.Now())
}

View File

@ -0,0 +1,166 @@
/*
* Copyright 2018 The NATS Authors
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jwt
import (
"crypto/sha256"
"encoding/base32"
"errors"
"fmt"
"strings"
"github.com/nats-io/nkeys"
)
// Activation defines the custom parts of an activation claim
type Activation struct {
ImportSubject Subject `json:"subject,omitempty"`
ImportType ExportType `json:"type,omitempty"`
Limits
}
// IsService returns true if an Activation is for a service
func (a *Activation) IsService() bool {
return a.ImportType == Service
}
// IsStream returns true if an Activation is for a stream
func (a *Activation) IsStream() bool {
return a.ImportType == Stream
}
// Validate checks the exports and limits in an activation JWT
func (a *Activation) Validate(vr *ValidationResults) {
if !a.IsService() && !a.IsStream() {
vr.AddError("invalid export type: %q", a.ImportType)
}
if a.IsService() {
if a.ImportSubject.HasWildCards() {
vr.AddError("services cannot have wildcard subject: %q", a.ImportSubject)
}
}
a.ImportSubject.Validate(vr)
a.Limits.Validate(vr)
}
// ActivationClaims holds the data specific to an activation JWT
type ActivationClaims struct {
ClaimsData
Activation `json:"nats,omitempty"`
// IssuerAccount stores the public key for the account the issuer represents.
// When set, the claim was issued by a signing key.
IssuerAccount string `json:"issuer_account,omitempty"`
}
// NewActivationClaims creates a new activation claim with the provided sub
func NewActivationClaims(subject string) *ActivationClaims {
if subject == "" {
return nil
}
ac := &ActivationClaims{}
ac.Subject = subject
return ac
}
// Encode turns an activation claim into a JWT strimg
func (a *ActivationClaims) Encode(pair nkeys.KeyPair) (string, error) {
if !nkeys.IsValidPublicAccountKey(a.ClaimsData.Subject) {
return "", errors.New("expected subject to be an account")
}
a.ClaimsData.Type = ActivationClaim
return a.ClaimsData.Encode(pair, a)
}
// DecodeActivationClaims tries to create an activation claim from a JWT string
func DecodeActivationClaims(token string) (*ActivationClaims, error) {
v := ActivationClaims{}
if err := Decode(token, &v); err != nil {
return nil, err
}
return &v, nil
}
// Payload returns the activation specific part of the JWT
func (a *ActivationClaims) Payload() interface{} {
return a.Activation
}
// Validate checks the claims
func (a *ActivationClaims) Validate(vr *ValidationResults) {
a.ClaimsData.Validate(vr)
a.Activation.Validate(vr)
if a.IssuerAccount != "" && !nkeys.IsValidPublicAccountKey(a.IssuerAccount) {
vr.AddError("account_id is not an account public key")
}
}
// ExpectedPrefixes defines the types that can sign an activation jwt, account and oeprator
func (a *ActivationClaims) ExpectedPrefixes() []nkeys.PrefixByte {
return []nkeys.PrefixByte{nkeys.PrefixByteAccount, nkeys.PrefixByteOperator}
}
// Claims returns the generic part of the JWT
func (a *ActivationClaims) Claims() *ClaimsData {
return &a.ClaimsData
}
func (a *ActivationClaims) String() string {
return a.ClaimsData.String(a)
}
// HashID returns a hash of the claims that can be used to identify it.
// The hash is calculated by creating a string with
// issuerPubKey.subjectPubKey.<subject> and constructing the sha-256 hash and base32 encoding that.
// <subject> is the exported subject, minus any wildcards, so foo.* becomes foo.
// the one special case is that if the export start with "*" or is ">" the <subject> "_"
func (a *ActivationClaims) HashID() (string, error) {
if a.Issuer == "" || a.Subject == "" || a.ImportSubject == "" {
return "", fmt.Errorf("not enough data in the activaion claims to create a hash")
}
subject := cleanSubject(string(a.ImportSubject))
base := fmt.Sprintf("%s.%s.%s", a.Issuer, a.Subject, subject)
h := sha256.New()
h.Write([]byte(base))
sha := h.Sum(nil)
hash := base32.StdEncoding.EncodeToString(sha)
return hash, nil
}
func cleanSubject(subject string) string {
split := strings.Split(subject, ".")
cleaned := ""
for i, tok := range split {
if tok == "*" || tok == ">" {
if i == 0 {
cleaned = "_"
break
}
cleaned = strings.Join(split[:i], ".")
break
}
}
if cleaned == "" {
cleaned = subject
}
return cleaned
}

302
gateway/vendor/github.com/nats-io/jwt/claims.go generated vendored Normal file
View File

@ -0,0 +1,302 @@
/*
* Copyright 2018-2019 The NATS Authors
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jwt
import (
"crypto/sha512"
"encoding/base32"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"strings"
"time"
"github.com/nats-io/nkeys"
)
// ClaimType is used to indicate the type of JWT being stored in a Claim
type ClaimType string
const (
// AccountClaim is the type of an Account JWT
AccountClaim = "account"
//ActivationClaim is the type of an activation JWT
ActivationClaim = "activation"
//UserClaim is the type of an user JWT
UserClaim = "user"
//ServerClaim is the type of an server JWT
ServerClaim = "server"
//ClusterClaim is the type of an cluster JWT
ClusterClaim = "cluster"
//OperatorClaim is the type of an operator JWT
OperatorClaim = "operator"
)
// Claims is a JWT claims
type Claims interface {
Claims() *ClaimsData
Encode(kp nkeys.KeyPair) (string, error)
ExpectedPrefixes() []nkeys.PrefixByte
Payload() interface{}
String() string
Validate(vr *ValidationResults)
Verify(payload string, sig []byte) bool
}
// ClaimsData is the base struct for all claims
type ClaimsData struct {
Audience string `json:"aud,omitempty"`
Expires int64 `json:"exp,omitempty"`
ID string `json:"jti,omitempty"`
IssuedAt int64 `json:"iat,omitempty"`
Issuer string `json:"iss,omitempty"`
Name string `json:"name,omitempty"`
NotBefore int64 `json:"nbf,omitempty"`
Subject string `json:"sub,omitempty"`
Tags TagList `json:"tags,omitempty"`
Type ClaimType `json:"type,omitempty"`
}
// Prefix holds the prefix byte for an NKey
type Prefix struct {
nkeys.PrefixByte
}
func encodeToString(d []byte) string {
return base64.RawURLEncoding.EncodeToString(d)
}
func decodeString(s string) ([]byte, error) {
return base64.RawURLEncoding.DecodeString(s)
}
func serialize(v interface{}) (string, error) {
j, err := json.Marshal(v)
if err != nil {
return "", err
}
return encodeToString(j), nil
}
func (c *ClaimsData) doEncode(header *Header, kp nkeys.KeyPair, claim Claims) (string, error) {
if header == nil {
return "", errors.New("header is required")
}
if kp == nil {
return "", errors.New("keypair is required")
}
if c.Subject == "" {
return "", errors.New("subject is not set")
}
h, err := serialize(header)
if err != nil {
return "", err
}
issuerBytes, err := kp.PublicKey()
if err != nil {
return "", err
}
prefixes := claim.ExpectedPrefixes()
if prefixes != nil {
ok := false
for _, p := range prefixes {
switch p {
case nkeys.PrefixByteAccount:
if nkeys.IsValidPublicAccountKey(issuerBytes) {
ok = true
}
case nkeys.PrefixByteOperator:
if nkeys.IsValidPublicOperatorKey(issuerBytes) {
ok = true
}
case nkeys.PrefixByteServer:
if nkeys.IsValidPublicServerKey(issuerBytes) {
ok = true
}
case nkeys.PrefixByteCluster:
if nkeys.IsValidPublicClusterKey(issuerBytes) {
ok = true
}
case nkeys.PrefixByteUser:
if nkeys.IsValidPublicUserKey(issuerBytes) {
ok = true
}
}
}
if !ok {
return "", fmt.Errorf("unable to validate expected prefixes - %v", prefixes)
}
}
c.Issuer = string(issuerBytes)
c.IssuedAt = time.Now().UTC().Unix()
c.ID, err = c.hash()
if err != nil {
return "", err
}
payload, err := serialize(claim)
if err != nil {
return "", err
}
sig, err := kp.Sign([]byte(payload))
if err != nil {
return "", err
}
eSig := encodeToString(sig)
return fmt.Sprintf("%s.%s.%s", h, payload, eSig), nil
}
func (c *ClaimsData) hash() (string, error) {
j, err := json.Marshal(c)
if err != nil {
return "", err
}
h := sha512.New512_256()
h.Write(j)
return base32.StdEncoding.WithPadding(base32.NoPadding).EncodeToString(h.Sum(nil)), nil
}
// Encode encodes a claim into a JWT token. The claim is signed with the
// provided nkey's private key
func (c *ClaimsData) Encode(kp nkeys.KeyPair, payload Claims) (string, error) {
return c.doEncode(&Header{TokenTypeJwt, AlgorithmNkey}, kp, payload)
}
// Returns a JSON representation of the claim
func (c *ClaimsData) String(claim interface{}) string {
j, err := json.MarshalIndent(claim, "", " ")
if err != nil {
return ""
}
return string(j)
}
func parseClaims(s string, target Claims) error {
h, err := decodeString(s)
if err != nil {
return err
}
return json.Unmarshal(h, &target)
}
// Verify verifies that the encoded payload was signed by the
// provided public key. Verify is called automatically with
// the claims portion of the token and the public key in the claim.
// Client code need to insure that the public key in the
// claim is trusted.
func (c *ClaimsData) Verify(payload string, sig []byte) bool {
// decode the public key
kp, err := nkeys.FromPublicKey(c.Issuer)
if err != nil {
return false
}
if err := kp.Verify([]byte(payload), sig); err != nil {
return false
}
return true
}
// Validate checks a claim to make sure it is valid. Validity checks
// include expiration and not before constraints.
func (c *ClaimsData) Validate(vr *ValidationResults) {
now := time.Now().UTC().Unix()
if c.Expires > 0 && now > c.Expires {
vr.AddTimeCheck("claim is expired")
}
if c.NotBefore > 0 && c.NotBefore > now {
vr.AddTimeCheck("claim is not yet valid")
}
}
// IsSelfSigned returns true if the claims issuer is the subject
func (c *ClaimsData) IsSelfSigned() bool {
return c.Issuer == c.Subject
}
// Decode takes a JWT string decodes it and validates it
// and return the embedded Claims. If the token header
// doesn't match the expected algorithm, or the claim is
// not valid or verification fails an error is returned.
func Decode(token string, target Claims) error {
// must have 3 chunks
chunks := strings.Split(token, ".")
if len(chunks) != 3 {
return errors.New("expected 3 chunks")
}
_, err := parseHeaders(chunks[0])
if err != nil {
return err
}
if err := parseClaims(chunks[1], target); err != nil {
return err
}
sig, err := decodeString(chunks[2])
if err != nil {
return err
}
if !target.Verify(chunks[1], sig) {
return errors.New("claim failed signature verification")
}
prefixes := target.ExpectedPrefixes()
if prefixes != nil {
ok := false
issuer := target.Claims().Issuer
for _, p := range prefixes {
switch p {
case nkeys.PrefixByteAccount:
if nkeys.IsValidPublicAccountKey(issuer) {
ok = true
}
case nkeys.PrefixByteOperator:
if nkeys.IsValidPublicOperatorKey(issuer) {
ok = true
}
case nkeys.PrefixByteServer:
if nkeys.IsValidPublicServerKey(issuer) {
ok = true
}
case nkeys.PrefixByteCluster:
if nkeys.IsValidPublicClusterKey(issuer) {
ok = true
}
case nkeys.PrefixByteUser:
if nkeys.IsValidPublicUserKey(issuer) {
ok = true
}
}
}
if !ok {
return fmt.Errorf("unable to validate expected prefixes - %v", prefixes)
}
}
return nil
}

View File

@ -0,0 +1,94 @@
/*
* Copyright 2018 The NATS Authors
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jwt
import (
"errors"
"github.com/nats-io/nkeys"
)
// Cluster stores the cluster specific elements of a cluster JWT
type Cluster struct {
Trust []string `json:"identity,omitempty"`
Accounts []string `json:"accts,omitempty"`
AccountURL string `json:"accturl,omitempty"`
OperatorURL string `json:"opurl,omitempty"`
}
// Validate checks the cluster and permissions for a cluster JWT
func (c *Cluster) Validate(vr *ValidationResults) {
// fixme validate cluster data
}
// ClusterClaims defines the data in a cluster JWT
type ClusterClaims struct {
ClaimsData
Cluster `json:"nats,omitempty"`
}
// NewClusterClaims creates a new cluster JWT with the specified subject/public key
func NewClusterClaims(subject string) *ClusterClaims {
if subject == "" {
return nil
}
c := &ClusterClaims{}
c.Subject = subject
return c
}
// Encode tries to turn the cluster claims into a JWT string
func (c *ClusterClaims) Encode(pair nkeys.KeyPair) (string, error) {
if !nkeys.IsValidPublicClusterKey(c.Subject) {
return "", errors.New("expected subject to be a cluster public key")
}
c.ClaimsData.Type = ClusterClaim
return c.ClaimsData.Encode(pair, c)
}
// DecodeClusterClaims tries to parse cluster claims from a JWT string
func DecodeClusterClaims(token string) (*ClusterClaims, error) {
v := ClusterClaims{}
if err := Decode(token, &v); err != nil {
return nil, err
}
return &v, nil
}
func (c *ClusterClaims) String() string {
return c.ClaimsData.String(c)
}
// Payload returns the cluster specific data
func (c *ClusterClaims) Payload() interface{} {
return &c.Cluster
}
// Validate checks the generic and cluster data in the cluster claims
func (c *ClusterClaims) Validate(vr *ValidationResults) {
c.ClaimsData.Validate(vr)
c.Cluster.Validate(vr)
}
// ExpectedPrefixes defines the types that can encode a cluster JWT, operator or cluster
func (c *ClusterClaims) ExpectedPrefixes() []nkeys.PrefixByte {
return []nkeys.PrefixByte{nkeys.PrefixByteOperator, nkeys.PrefixByteCluster}
}
// Claims returns the generic data
func (c *ClusterClaims) Claims() *ClaimsData {
return &c.ClaimsData
}

203
gateway/vendor/github.com/nats-io/jwt/creds_utils.go generated vendored Normal file
View File

@ -0,0 +1,203 @@
package jwt
import (
"bytes"
"errors"
"fmt"
"regexp"
"strings"
"github.com/nats-io/nkeys"
)
// DecorateJWT returns a decorated JWT that describes the kind of JWT
func DecorateJWT(jwtString string) ([]byte, error) {
gc, err := DecodeGeneric(jwtString)
if err != nil {
return nil, err
}
return formatJwt(string(gc.Type), jwtString)
}
func formatJwt(kind string, jwtString string) ([]byte, error) {
templ := `-----BEGIN NATS %s JWT-----
%s
------END NATS %s JWT------
`
w := bytes.NewBuffer(nil)
kind = strings.ToUpper(kind)
_, err := fmt.Fprintf(w, templ, kind, jwtString, kind)
if err != nil {
return nil, err
}
return w.Bytes(), nil
}
// DecorateSeed takes a seed and returns a string that wraps
// the seed in the form:
// ************************* IMPORTANT *************************
// NKEY Seed printed below can be used sign and prove identity.
// NKEYs are sensitive and should be treated as secrets.
//
// -----BEGIN USER NKEY SEED-----
// SUAIO3FHUX5PNV2LQIIP7TZ3N4L7TX3W53MQGEIVYFIGA635OZCKEYHFLM
// ------END USER NKEY SEED------
func DecorateSeed(seed []byte) ([]byte, error) {
w := bytes.NewBuffer(nil)
ts := bytes.TrimSpace(seed)
pre := string(ts[0:2])
kind := ""
switch pre {
case "SU":
kind = "USER"
case "SA":
kind = "ACCOUNT"
case "SO":
kind = "OPERATOR"
default:
return nil, errors.New("seed is not an operator, account or user seed")
}
header := `************************* IMPORTANT *************************
NKEY Seed printed below can be used to sign and prove identity.
NKEYs are sensitive and should be treated as secrets.
-----BEGIN %s NKEY SEED-----
`
_, err := fmt.Fprintf(w, header, kind)
if err != nil {
return nil, err
}
w.Write(ts)
footer := `
------END %s NKEY SEED------
*************************************************************
`
_, err = fmt.Fprintf(w, footer, kind)
if err != nil {
return nil, err
}
return w.Bytes(), nil
}
var userConfigRE = regexp.MustCompile(`\s*(?:(?:[-]{3,}[^\n]*[-]{3,}\n)(.+)(?:\n\s*[-]{3,}[^\n]*[-]{3,}\n))`)
// An user config file looks like this:
// -----BEGIN NATS USER JWT-----
// eyJ0eXAiOiJqd3QiLCJhbGciOiJlZDI1NTE5...
// ------END NATS USER JWT------
//
// ************************* IMPORTANT *************************
// NKEY Seed printed below can be used sign and prove identity.
// NKEYs are sensitive and should be treated as secrets.
//
// -----BEGIN USER NKEY SEED-----
// SUAIO3FHUX5PNV2LQIIP7TZ3N4L7TX3W53MQGEIVYFIGA635OZCKEYHFLM
// ------END USER NKEY SEED------
// FormatUserConfig returns a decorated file with a decorated JWT and decorated seed
func FormatUserConfig(jwtString string, seed []byte) ([]byte, error) {
gc, err := DecodeGeneric(jwtString)
if err != nil {
return nil, err
}
if gc.Type != UserClaim {
return nil, fmt.Errorf("%q cannot be serialized as a user config", string(gc.Type))
}
w := bytes.NewBuffer(nil)
jd, err := formatJwt(string(gc.Type), jwtString)
if err != nil {
return nil, err
}
_, err = w.Write(jd)
if err != nil {
return nil, err
}
if !bytes.HasPrefix(bytes.TrimSpace(seed), []byte("SU")) {
return nil, fmt.Errorf("nkey seed is not an user seed")
}
d, err := DecorateSeed(seed)
if err != nil {
return nil, err
}
_, err = w.Write(d)
if err != nil {
return nil, err
}
return w.Bytes(), nil
}
// ParseDecoratedJWT takes a creds file and returns the JWT portion.
func ParseDecoratedJWT(contents []byte) (string, error) {
items := userConfigRE.FindAllSubmatch(contents, -1)
if len(items) == 0 {
return string(contents), nil
}
// First result should be the user JWT.
// We copy here so that if the file contained a seed file too we wipe appropriately.
raw := items[0][1]
tmp := make([]byte, len(raw))
copy(tmp, raw)
return string(tmp), nil
}
// ParseDecoratedNKey takes a creds file, finds the NKey portion and creates a
// key pair from it.
func ParseDecoratedNKey(contents []byte) (nkeys.KeyPair, error) {
var seed []byte
items := userConfigRE.FindAllSubmatch(contents, -1)
if len(items) > 1 {
seed = items[1][1]
} else {
lines := bytes.Split(contents, []byte("\n"))
for _, line := range lines {
if bytes.HasPrefix(bytes.TrimSpace(line), []byte("SO")) ||
bytes.HasPrefix(bytes.TrimSpace(line), []byte("SA")) ||
bytes.HasPrefix(bytes.TrimSpace(line), []byte("SU")) {
seed = line
break
}
}
}
if seed == nil {
return nil, errors.New("no nkey seed found")
}
if !bytes.HasPrefix(seed, []byte("SO")) &&
!bytes.HasPrefix(seed, []byte("SA")) &&
!bytes.HasPrefix(seed, []byte("SU")) {
return nil, errors.New("doesn't contain a seed nkey")
}
kp, err := nkeys.FromSeed(seed)
if err != nil {
return nil, err
}
return kp, nil
}
// ParseDecoratedUserNKey takes a creds file, finds the NKey portion and creates a
// key pair from it. Similar to ParseDecoratedNKey but fails for non-user keys.
func ParseDecoratedUserNKey(contents []byte) (nkeys.KeyPair, error) {
nk, err := ParseDecoratedNKey(contents)
if err != nil {
return nil, err
}
seed, err := nk.Seed()
if err != nil {
return nil, err
}
if !bytes.HasPrefix(seed, []byte("SU")) {
return nil, errors.New("doesn't contain an user seed nkey")
}
kp, err := nkeys.FromSeed(seed)
if err != nil {
return nil, err
}
return kp, nil
}

236
gateway/vendor/github.com/nats-io/jwt/exports.go generated vendored Normal file
View File

@ -0,0 +1,236 @@
/*
* Copyright 2018-2019 The NATS Authors
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jwt
import (
"fmt"
"time"
)
// ResponseType is used to store an export response type
type ResponseType string
const (
// ResponseTypeSingleton is used for a service that sends a single response only
ResponseTypeSingleton = "Singleton"
// ResponseTypeStream is used for a service that will send multiple responses
ResponseTypeStream = "Stream"
// ResponseTypeChunked is used for a service that sends a single response in chunks (so not quite a stream)
ResponseTypeChunked = "Chunked"
)
// ServiceLatency is used when observing and exported service for
// latency measurements.
// Sampling 1-100, represents sampling rate, defaults to 100.
// Results is the subject where the latency metrics are published.
// A metric will be defined by the nats-server's ServiceLatency. Time durations
// are in nanoseconds.
// see https://github.com/nats-io/nats-server/blob/master/server/accounts.go#L524
// e.g.
// {
// "app": "dlc22",
// "start": "2019-09-16T21:46:23.636869585-07:00",
// "svc": 219732,
// "nats": {
// "req": 320415,
// "resp": 228268,
// "sys": 0
// },
// "total": 768415
// }
//
type ServiceLatency struct {
Sampling int `json:"sampling,omitempty"`
Results Subject `json:"results"`
}
func (sl *ServiceLatency) Validate(vr *ValidationResults) {
if sl.Sampling < 1 || sl.Sampling > 100 {
vr.AddError("sampling percentage needs to be between 1-100")
}
sl.Results.Validate(vr)
if sl.Results.HasWildCards() {
vr.AddError("results subject can not contain wildcards")
}
}
// Export represents a single export
type Export struct {
Name string `json:"name,omitempty"`
Subject Subject `json:"subject,omitempty"`
Type ExportType `json:"type,omitempty"`
TokenReq bool `json:"token_req,omitempty"`
Revocations RevocationList `json:"revocations,omitempty"`
ResponseType ResponseType `json:"response_type,omitempty"`
Latency *ServiceLatency `json:"service_latency,omitempty"`
}
// IsService returns true if an export is for a service
func (e *Export) IsService() bool {
return e.Type == Service
}
// IsStream returns true if an export is for a stream
func (e *Export) IsStream() bool {
return e.Type == Stream
}
// IsSingleResponse returns true if an export has a single response
// or no resopnse type is set, also checks that the type is service
func (e *Export) IsSingleResponse() bool {
return e.Type == Service && (e.ResponseType == ResponseTypeSingleton || e.ResponseType == "")
}
// IsChunkedResponse returns true if an export has a chunked response
func (e *Export) IsChunkedResponse() bool {
return e.Type == Service && e.ResponseType == ResponseTypeChunked
}
// IsStreamResponse returns true if an export has a chunked response
func (e *Export) IsStreamResponse() bool {
return e.Type == Service && e.ResponseType == ResponseTypeStream
}
// Validate appends validation issues to the passed in results list
func (e *Export) Validate(vr *ValidationResults) {
if !e.IsService() && !e.IsStream() {
vr.AddError("invalid export type: %q", e.Type)
}
if e.IsService() && !e.IsSingleResponse() && !e.IsChunkedResponse() && !e.IsStreamResponse() {
vr.AddError("invalid response type for service: %q", e.ResponseType)
}
if e.IsStream() && e.ResponseType != "" {
vr.AddError("invalid response type for stream: %q", e.ResponseType)
}
if e.Latency != nil {
if !e.IsService() {
vr.AddError("latency tracking only permitted for services")
}
e.Latency.Validate(vr)
}
e.Subject.Validate(vr)
}
// Revoke enters a revocation by publickey using time.Now().
func (e *Export) Revoke(pubKey string) {
e.RevokeAt(pubKey, time.Now())
}
// RevokeAt enters a revocation by publickey and timestamp into this export
// If there is already a revocation for this public key that is newer, it is kept.
func (e *Export) RevokeAt(pubKey string, timestamp time.Time) {
if e.Revocations == nil {
e.Revocations = RevocationList{}
}
e.Revocations.Revoke(pubKey, timestamp)
}
// ClearRevocation removes any revocation for the public key
func (e *Export) ClearRevocation(pubKey string) {
e.Revocations.ClearRevocation(pubKey)
}
// IsRevokedAt checks if the public key is in the revoked list with a timestamp later than
// the one passed in. Generally this method is called with time.Now() but other time's can
// be used for testing.
func (e *Export) IsRevokedAt(pubKey string, timestamp time.Time) bool {
return e.Revocations.IsRevoked(pubKey, timestamp)
}
// IsRevoked checks if the public key is in the revoked list with time.Now()
func (e *Export) IsRevoked(pubKey string) bool {
return e.Revocations.IsRevoked(pubKey, time.Now())
}
// Exports is a slice of exports
type Exports []*Export
// Add appends exports to the list
func (e *Exports) Add(i ...*Export) {
*e = append(*e, i...)
}
func isContainedIn(kind ExportType, subjects []Subject, vr *ValidationResults) {
m := make(map[string]string)
for i, ns := range subjects {
for j, s := range subjects {
if i == j {
continue
}
if ns.IsContainedIn(s) {
str := string(s)
_, ok := m[str]
if !ok {
m[str] = string(ns)
}
}
}
}
if len(m) != 0 {
for k, v := range m {
var vi ValidationIssue
vi.Blocking = true
vi.Description = fmt.Sprintf("%s export subject %q already exports %q", kind, k, v)
vr.Add(&vi)
}
}
}
// Validate calls validate on all of the exports
func (e *Exports) Validate(vr *ValidationResults) error {
var serviceSubjects []Subject
var streamSubjects []Subject
for _, v := range *e {
if v.IsService() {
serviceSubjects = append(serviceSubjects, v.Subject)
} else {
streamSubjects = append(streamSubjects, v.Subject)
}
v.Validate(vr)
}
isContainedIn(Service, serviceSubjects, vr)
isContainedIn(Stream, streamSubjects, vr)
return nil
}
// HasExportContainingSubject checks if the export list has an export with the provided subject
func (e *Exports) HasExportContainingSubject(subject Subject) bool {
for _, s := range *e {
if subject.IsContainedIn(s.Subject) {
return true
}
}
return false
}
func (e Exports) Len() int {
return len(e)
}
func (e Exports) Swap(i, j int) {
e[i], e[j] = e[j], e[i]
}
func (e Exports) Less(i, j int) bool {
return e[i].Subject < e[j].Subject
}

73
gateway/vendor/github.com/nats-io/jwt/genericlaims.go generated vendored Normal file
View File

@ -0,0 +1,73 @@
/*
* Copyright 2018 The NATS Authors
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jwt
import "github.com/nats-io/nkeys"
// GenericClaims can be used to read a JWT as a map for any non-generic fields
type GenericClaims struct {
ClaimsData
Data map[string]interface{} `json:"nats,omitempty"`
}
// NewGenericClaims creates a map-based Claims
func NewGenericClaims(subject string) *GenericClaims {
if subject == "" {
return nil
}
c := GenericClaims{}
c.Subject = subject
c.Data = make(map[string]interface{})
return &c
}
// DecodeGeneric takes a JWT string and decodes it into a ClaimsData and map
func DecodeGeneric(token string) (*GenericClaims, error) {
v := GenericClaims{}
if err := Decode(token, &v); err != nil {
return nil, err
}
return &v, nil
}
// Claims returns the standard part of the generic claim
func (gc *GenericClaims) Claims() *ClaimsData {
return &gc.ClaimsData
}
// Payload returns the custom part of the claims data
func (gc *GenericClaims) Payload() interface{} {
return &gc.Data
}
// Encode takes a generic claims and creates a JWT string
func (gc *GenericClaims) Encode(pair nkeys.KeyPair) (string, error) {
return gc.ClaimsData.Encode(pair, gc)
}
// Validate checks the generic part of the claims data
func (gc *GenericClaims) Validate(vr *ValidationResults) {
gc.ClaimsData.Validate(vr)
}
func (gc *GenericClaims) String() string {
return gc.ClaimsData.String(gc)
}
// ExpectedPrefixes returns the types allowed to encode a generic JWT, which is nil for all
func (gc *GenericClaims) ExpectedPrefixes() []nkeys.PrefixByte {
return nil
}

3
gateway/vendor/github.com/nats-io/jwt/go.mod generated vendored Normal file
View File

@ -0,0 +1,3 @@
module github.com/nats-io/jwt
require github.com/nats-io/nkeys v0.1.3

9
gateway/vendor/github.com/nats-io/jwt/go.sum generated vendored Normal file
View File

@ -0,0 +1,9 @@
github.com/nats-io/nkeys v0.1.3 h1:6JrEfig+HzTH85yxzhSVbjHRJv9cn0p6n3IngIcM5/k=
github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=

71
gateway/vendor/github.com/nats-io/jwt/header.go generated vendored Normal file
View File

@ -0,0 +1,71 @@
/*
* Copyright 2018-2019 The NATS Authors
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jwt
import (
"encoding/json"
"fmt"
"strings"
)
const (
// Version is semantic version.
Version = "0.3.2"
// TokenTypeJwt is the JWT token type supported JWT tokens
// encoded and decoded by this library
TokenTypeJwt = "jwt"
// AlgorithmNkey is the algorithm supported by JWT tokens
// encoded and decoded by this library
AlgorithmNkey = "ed25519"
)
// Header is a JWT Jose Header
type Header struct {
Type string `json:"typ"`
Algorithm string `json:"alg"`
}
// Parses a header JWT token
func parseHeaders(s string) (*Header, error) {
h, err := decodeString(s)
if err != nil {
return nil, err
}
header := Header{}
if err := json.Unmarshal(h, &header); err != nil {
return nil, err
}
if err := header.Valid(); err != nil {
return nil, err
}
return &header, nil
}
// Valid validates the Header. It returns nil if the Header is
// a JWT header, and the algorithm used is the NKEY algorithm.
func (h *Header) Valid() error {
if TokenTypeJwt != strings.ToLower(h.Type) {
return fmt.Errorf("not supported type %q", h.Type)
}
if AlgorithmNkey != strings.ToLower(h.Algorithm) {
return fmt.Errorf("unexpected %q algorithm", h.Algorithm)
}
return nil
}

151
gateway/vendor/github.com/nats-io/jwt/imports.go generated vendored Normal file
View File

@ -0,0 +1,151 @@
/*
* Copyright 2018-2019 The NATS Authors
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jwt
import (
"io/ioutil"
"net/http"
"net/url"
"time"
)
// Import describes a mapping from another account into this one
type Import struct {
Name string `json:"name,omitempty"`
// Subject field in an import is always from the perspective of the
// initial publisher - in the case of a stream it is the account owning
// the stream (the exporter), and in the case of a service it is the
// account making the request (the importer).
Subject Subject `json:"subject,omitempty"`
Account string `json:"account,omitempty"`
Token string `json:"token,omitempty"`
// To field in an import is always from the perspective of the subscriber
// in the case of a stream it is the client of the stream (the importer),
// from the perspective of a service, it is the subscription waiting for
// requests (the exporter). If the field is empty, it will default to the
// value in the Subject field.
To Subject `json:"to,omitempty"`
Type ExportType `json:"type,omitempty"`
}
// IsService returns true if the import is of type service
func (i *Import) IsService() bool {
return i.Type == Service
}
// IsStream returns true if the import is of type stream
func (i *Import) IsStream() bool {
return i.Type == Stream
}
// Validate checks if an import is valid for the wrapping account
func (i *Import) Validate(actPubKey string, vr *ValidationResults) {
if !i.IsService() && !i.IsStream() {
vr.AddError("invalid import type: %q", i.Type)
}
if i.Account == "" {
vr.AddWarning("account to import from is not specified")
}
i.Subject.Validate(vr)
if i.IsService() && i.Subject.HasWildCards() {
vr.AddError("services cannot have wildcard subject: %q", i.Subject)
}
if i.IsStream() && i.To.HasWildCards() {
vr.AddError("streams cannot have wildcard to subject: %q", i.Subject)
}
var act *ActivationClaims
if i.Token != "" {
// Check to see if its an embedded JWT or a URL.
if url, err := url.Parse(i.Token); err == nil && url.Scheme != "" {
c := &http.Client{Timeout: 5 * time.Second}
resp, err := c.Get(url.String())
if err != nil {
vr.AddWarning("import %s contains an unreachable token URL %q", i.Subject, i.Token)
}
if resp != nil {
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
vr.AddWarning("import %s contains an unreadable token URL %q", i.Subject, i.Token)
} else {
act, err = DecodeActivationClaims(string(body))
if err != nil {
vr.AddWarning("import %s contains a url %q with an invalid activation token", i.Subject, i.Token)
}
}
}
} else {
var err error
act, err = DecodeActivationClaims(i.Token)
if err != nil {
vr.AddWarning("import %q contains an invalid activation token", i.Subject)
}
}
}
if act != nil {
if act.Issuer != i.Account {
vr.AddWarning("activation token doesn't match account for import %q", i.Subject)
}
if act.ClaimsData.Subject != actPubKey {
vr.AddWarning("activation token doesn't match account it is being included in, %q", i.Subject)
}
} else {
vr.AddWarning("no activation provided for import %s", i.Subject)
}
}
// Imports is a list of import structs
type Imports []*Import
// Validate checks if an import is valid for the wrapping account
func (i *Imports) Validate(acctPubKey string, vr *ValidationResults) {
toSet := make(map[Subject]bool, len(*i))
for _, v := range *i {
if v.Type == Service {
if _, ok := toSet[v.To]; ok {
vr.AddError("Duplicate To subjects for %q", v.To)
}
toSet[v.To] = true
}
v.Validate(acctPubKey, vr)
}
}
// Add is a simple way to add imports
func (i *Imports) Add(a ...*Import) {
*i = append(*i, a...)
}
func (i Imports) Len() int {
return len(i)
}
func (i Imports) Swap(j, k int) {
i[j], i[k] = i[k], i[j]
}
func (i Imports) Less(j, k int) bool {
return i[j].Subject < i[k].Subject
}

View File

@ -0,0 +1,204 @@
/*
* Copyright 2018 The NATS Authors
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jwt
import (
"errors"
"fmt"
"net/url"
"strings"
"github.com/nats-io/nkeys"
)
// Operator specific claims
type Operator struct {
// Slice of real identies (like websites) that can be used to identify the operator.
Identities []Identity `json:"identity,omitempty"`
// Slice of other operator NKeys that can be used to sign on behalf of the main
// operator identity.
SigningKeys StringList `json:"signing_keys,omitempty"`
// AccountServerURL is a partial URL like "https://host.domain.org:<port>/jwt/v1"
// tools will use the prefix and build queries by appending /accounts/<account_id>
// or /operator to the path provided. Note this assumes that the account server
// can handle requests in a nats-account-server compatible way. See
// https://github.com/nats-io/nats-account-server.
AccountServerURL string `json:"account_server_url,omitempty"`
// A list of NATS urls (tls://host:port) where tools can connect to the server
// using proper credentials.
OperatorServiceURLs StringList `json:"operator_service_urls,omitempty"`
}
// Validate checks the validity of the operators contents
func (o *Operator) Validate(vr *ValidationResults) {
if err := o.validateAccountServerURL(); err != nil {
vr.AddError(err.Error())
}
for _, v := range o.validateOperatorServiceURLs() {
if v != nil {
vr.AddError(v.Error())
}
}
for _, i := range o.Identities {
i.Validate(vr)
}
for _, k := range o.SigningKeys {
if !nkeys.IsValidPublicOperatorKey(k) {
vr.AddError("%s is not an operator public key", k)
}
}
}
func (o *Operator) validateAccountServerURL() error {
if o.AccountServerURL != "" {
// We don't care what kind of URL it is so long as it parses
// and has a protocol. The account server may impose additional
// constraints on the type of URLs that it is able to notify to
u, err := url.Parse(o.AccountServerURL)
if err != nil {
return fmt.Errorf("error parsing account server url: %v", err)
}
if u.Scheme == "" {
return fmt.Errorf("account server url %q requires a protocol", o.AccountServerURL)
}
}
return nil
}
// ValidateOperatorServiceURL returns an error if the URL is not a valid NATS or TLS url.
func ValidateOperatorServiceURL(v string) error {
// should be possible for the service url to not be expressed
if v == "" {
return nil
}
u, err := url.Parse(v)
if err != nil {
return fmt.Errorf("error parsing operator service url %q: %v", v, err)
}
if u.User != nil {
return fmt.Errorf("operator service url %q - credentials are not supported", v)
}
if u.Path != "" {
return fmt.Errorf("operator service url %q - paths are not supported", v)
}
lcs := strings.ToLower(u.Scheme)
switch lcs {
case "nats":
return nil
case "tls":
return nil
default:
return fmt.Errorf("operator service url %q - protocol not supported (only 'nats' or 'tls' only)", v)
}
}
func (o *Operator) validateOperatorServiceURLs() []error {
var errors []error
for _, v := range o.OperatorServiceURLs {
if v != "" {
if err := ValidateOperatorServiceURL(v); err != nil {
errors = append(errors, err)
}
}
}
return errors
}
// OperatorClaims define the data for an operator JWT
type OperatorClaims struct {
ClaimsData
Operator `json:"nats,omitempty"`
}
// NewOperatorClaims creates a new operator claim with the specified subject, which should be an operator public key
func NewOperatorClaims(subject string) *OperatorClaims {
if subject == "" {
return nil
}
c := &OperatorClaims{}
c.Subject = subject
return c
}
// DidSign checks the claims against the operator's public key and its signing keys
func (oc *OperatorClaims) DidSign(op Claims) bool {
if op == nil {
return false
}
issuer := op.Claims().Issuer
if issuer == oc.Subject {
return true
}
return oc.SigningKeys.Contains(issuer)
}
// Deprecated: AddSigningKey, use claim.SigningKeys.Add()
func (oc *OperatorClaims) AddSigningKey(pk string) {
oc.SigningKeys.Add(pk)
}
// Encode the claims into a JWT string
func (oc *OperatorClaims) Encode(pair nkeys.KeyPair) (string, error) {
if !nkeys.IsValidPublicOperatorKey(oc.Subject) {
return "", errors.New("expected subject to be an operator public key")
}
err := oc.validateAccountServerURL()
if err != nil {
return "", err
}
oc.ClaimsData.Type = OperatorClaim
return oc.ClaimsData.Encode(pair, oc)
}
// DecodeOperatorClaims tries to create an operator claims from a JWt string
func DecodeOperatorClaims(token string) (*OperatorClaims, error) {
v := OperatorClaims{}
if err := Decode(token, &v); err != nil {
return nil, err
}
return &v, nil
}
func (oc *OperatorClaims) String() string {
return oc.ClaimsData.String(oc)
}
// Payload returns the operator specific data for an operator JWT
func (oc *OperatorClaims) Payload() interface{} {
return &oc.Operator
}
// Validate the contents of the claims
func (oc *OperatorClaims) Validate(vr *ValidationResults) {
oc.ClaimsData.Validate(vr)
oc.Operator.Validate(vr)
}
// ExpectedPrefixes defines the nkey types that can sign operator claims, operator
func (oc *OperatorClaims) ExpectedPrefixes() []nkeys.PrefixByte {
return []nkeys.PrefixByte{nkeys.PrefixByteOperator}
}
// Claims returns the generic claims data
func (oc *OperatorClaims) Claims() *ClaimsData {
return &oc.ClaimsData
}

View File

@ -0,0 +1,32 @@
package jwt
import (
"time"
)
// RevocationList is used to store a mapping of public keys to unix timestamps
type RevocationList map[string]int64
// Revoke enters a revocation by publickey and timestamp into this export
// If there is already a revocation for this public key that is newer, it is kept.
func (r RevocationList) Revoke(pubKey string, timestamp time.Time) {
newTS := timestamp.Unix()
if ts, ok := r[pubKey]; ok && ts > newTS {
return
}
r[pubKey] = newTS
}
// ClearRevocation removes any revocation for the public key
func (r RevocationList) ClearRevocation(pubKey string) {
delete(r, pubKey)
}
// IsRevoked checks if the public key is in the revoked list with a timestamp later than
// the one passed in. Generally this method is called with time.Now() but other time's can
// be used for testing.
func (r RevocationList) IsRevoked(pubKey string, timestamp time.Time) bool {
ts, ok := r[pubKey]
return ok && ts > timestamp.Unix()
}

94
gateway/vendor/github.com/nats-io/jwt/server_claims.go generated vendored Normal file
View File

@ -0,0 +1,94 @@
/*
* Copyright 2018 The NATS Authors
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jwt
import (
"errors"
"github.com/nats-io/nkeys"
)
// Server defines the custom part of a server jwt
type Server struct {
Permissions
Cluster string `json:"cluster,omitempty"`
}
// Validate checks the cluster and permissions for a server JWT
func (s *Server) Validate(vr *ValidationResults) {
if s.Cluster == "" {
vr.AddError("servers can't contain an empty cluster")
}
}
// ServerClaims defines the data in a server JWT
type ServerClaims struct {
ClaimsData
Server `json:"nats,omitempty"`
}
// NewServerClaims creates a new server JWT with the specified subject/public key
func NewServerClaims(subject string) *ServerClaims {
if subject == "" {
return nil
}
c := &ServerClaims{}
c.Subject = subject
return c
}
// Encode tries to turn the server claims into a JWT string
func (s *ServerClaims) Encode(pair nkeys.KeyPair) (string, error) {
if !nkeys.IsValidPublicServerKey(s.Subject) {
return "", errors.New("expected subject to be a server public key")
}
s.ClaimsData.Type = ServerClaim
return s.ClaimsData.Encode(pair, s)
}
// DecodeServerClaims tries to parse server claims from a JWT string
func DecodeServerClaims(token string) (*ServerClaims, error) {
v := ServerClaims{}
if err := Decode(token, &v); err != nil {
return nil, err
}
return &v, nil
}
func (s *ServerClaims) String() string {
return s.ClaimsData.String(s)
}
// Payload returns the server specific data
func (s *ServerClaims) Payload() interface{} {
return &s.Server
}
// Validate checks the generic and server data in the server claims
func (s *ServerClaims) Validate(vr *ValidationResults) {
s.ClaimsData.Validate(vr)
s.Server.Validate(vr)
}
// ExpectedPrefixes defines the types that can encode a server JWT, operator or cluster
func (s *ServerClaims) ExpectedPrefixes() []nkeys.PrefixByte {
return []nkeys.PrefixByte{nkeys.PrefixByteOperator, nkeys.PrefixByteCluster}
}
// Claims returns the generic data
func (s *ServerClaims) Claims() *ClaimsData {
return &s.ClaimsData
}

334
gateway/vendor/github.com/nats-io/jwt/types.go generated vendored Normal file
View File

@ -0,0 +1,334 @@
/*
* Copyright 2018-2019 The NATS Authors
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jwt
import (
"encoding/json"
"fmt"
"net"
"strings"
"time"
)
// ExportType defines the type of import/export.
type ExportType int
const (
// Unknown is used if we don't know the type
Unknown ExportType = iota
// Stream defines the type field value for a stream "stream"
Stream
// Service defines the type field value for a service "service"
Service
)
func (t ExportType) String() string {
switch t {
case Stream:
return "stream"
case Service:
return "service"
}
return "unknown"
}
// MarshalJSON marshals the enum as a quoted json string
func (t *ExportType) MarshalJSON() ([]byte, error) {
switch *t {
case Stream:
return []byte("\"stream\""), nil
case Service:
return []byte("\"service\""), nil
}
return nil, fmt.Errorf("unknown export type")
}
// UnmarshalJSON unmashals a quoted json string to the enum value
func (t *ExportType) UnmarshalJSON(b []byte) error {
var j string
err := json.Unmarshal(b, &j)
if err != nil {
return err
}
switch j {
case "stream":
*t = Stream
return nil
case "service":
*t = Service
return nil
}
return fmt.Errorf("unknown export type")
}
// Subject is a string that represents a NATS subject
type Subject string
// Validate checks that a subject string is valid, ie not empty and without spaces
func (s Subject) Validate(vr *ValidationResults) {
v := string(s)
if v == "" {
vr.AddError("subject cannot be empty")
}
if strings.Contains(v, " ") {
vr.AddError("subject %q cannot have spaces", v)
}
}
// HasWildCards is used to check if a subject contains a > or *
func (s Subject) HasWildCards() bool {
v := string(s)
return strings.HasSuffix(v, ".>") ||
strings.Contains(v, ".*.") ||
strings.HasSuffix(v, ".*") ||
strings.HasPrefix(v, "*.") ||
v == "*" ||
v == ">"
}
// IsContainedIn does a simple test to see if the subject is contained in another subject
func (s Subject) IsContainedIn(other Subject) bool {
otherArray := strings.Split(string(other), ".")
myArray := strings.Split(string(s), ".")
if len(myArray) > len(otherArray) && otherArray[len(otherArray)-1] != ">" {
return false
}
if len(myArray) < len(otherArray) {
return false
}
for ind, tok := range otherArray {
myTok := myArray[ind]
if ind == len(otherArray)-1 && tok == ">" {
return true
}
if tok != myTok && tok != "*" {
return false
}
}
return true
}
// NamedSubject is the combination of a subject and a name for it
type NamedSubject struct {
Name string `json:"name,omitempty"`
Subject Subject `json:"subject,omitempty"`
}
// Validate checks the subject
func (ns *NamedSubject) Validate(vr *ValidationResults) {
ns.Subject.Validate(vr)
}
// TimeRange is used to represent a start and end time
type TimeRange struct {
Start string `json:"start,omitempty"`
End string `json:"end,omitempty"`
}
// Validate checks the values in a time range struct
func (tr *TimeRange) Validate(vr *ValidationResults) {
format := "15:04:05"
if tr.Start == "" {
vr.AddError("time ranges start must contain a start")
} else {
_, err := time.Parse(format, tr.Start)
if err != nil {
vr.AddError("start in time range is invalid %q", tr.Start)
}
}
if tr.End == "" {
vr.AddError("time ranges end must contain an end")
} else {
_, err := time.Parse(format, tr.End)
if err != nil {
vr.AddError("end in time range is invalid %q", tr.End)
}
}
}
// Limits are used to control acccess for users and importing accounts
// Src is a comma separated list of CIDR specifications
type Limits struct {
Max int64 `json:"max,omitempty"`
Payload int64 `json:"payload,omitempty"`
Src string `json:"src,omitempty"`
Times []TimeRange `json:"times,omitempty"`
}
// Validate checks the values in a limit struct
func (l *Limits) Validate(vr *ValidationResults) {
if l.Max < 0 {
vr.AddError("limits cannot contain a negative maximum, %d", l.Max)
}
if l.Payload < 0 {
vr.AddError("limits cannot contain a negative payload, %d", l.Payload)
}
if l.Src != "" {
elements := strings.Split(l.Src, ",")
for _, cidr := range elements {
cidr = strings.TrimSpace(cidr)
_, ipNet, err := net.ParseCIDR(cidr)
if err != nil || ipNet == nil {
vr.AddError("invalid cidr %q in user src limits", cidr)
}
}
}
if l.Times != nil && len(l.Times) > 0 {
for _, t := range l.Times {
t.Validate(vr)
}
}
}
// Permission defines allow/deny subjects
type Permission struct {
Allow StringList `json:"allow,omitempty"`
Deny StringList `json:"deny,omitempty"`
}
// Validate the allow, deny elements of a permission
func (p *Permission) Validate(vr *ValidationResults) {
for _, subj := range p.Allow {
Subject(subj).Validate(vr)
}
for _, subj := range p.Deny {
Subject(subj).Validate(vr)
}
}
// ResponsePermission can be used to allow responses to any reply subject
// that is received on a valid subscription.
type ResponsePermission struct {
MaxMsgs int `json:"max"`
Expires time.Duration `json:"ttl"`
}
// Validate the response permission.
func (p *ResponsePermission) Validate(vr *ValidationResults) {
// Any values can be valid for now.
}
// Permissions are used to restrict subject access, either on a user or for everyone on a server by default
type Permissions struct {
Pub Permission `json:"pub,omitempty"`
Sub Permission `json:"sub,omitempty"`
Resp *ResponsePermission `json:"resp,omitempty"`
}
// Validate the pub and sub fields in the permissions list
func (p *Permissions) Validate(vr *ValidationResults) {
p.Pub.Validate(vr)
p.Sub.Validate(vr)
if p.Resp != nil {
p.Resp.Validate(vr)
}
}
// StringList is a wrapper for an array of strings
type StringList []string
// Contains returns true if the list contains the string
func (u *StringList) Contains(p string) bool {
for _, t := range *u {
if t == p {
return true
}
}
return false
}
// Add appends 1 or more strings to a list
func (u *StringList) Add(p ...string) {
for _, v := range p {
if !u.Contains(v) && v != "" {
*u = append(*u, v)
}
}
}
// Remove removes 1 or more strings from a list
func (u *StringList) Remove(p ...string) {
for _, v := range p {
for i, t := range *u {
if t == v {
a := *u
*u = append(a[:i], a[i+1:]...)
break
}
}
}
}
// TagList is a unique array of lower case strings
// All tag list methods lower case the strings in the arguments
type TagList []string
// Contains returns true if the list contains the tags
func (u *TagList) Contains(p string) bool {
p = strings.ToLower(p)
for _, t := range *u {
if t == p {
return true
}
}
return false
}
// Add appends 1 or more tags to a list
func (u *TagList) Add(p ...string) {
for _, v := range p {
v = strings.ToLower(v)
if !u.Contains(v) && v != "" {
*u = append(*u, v)
}
}
}
// Remove removes 1 or more tags from a list
func (u *TagList) Remove(p ...string) {
for _, v := range p {
v = strings.ToLower(v)
for i, t := range *u {
if t == v {
a := *u
*u = append(a[:i], a[i+1:]...)
break
}
}
}
}
// Identity is used to associate an account or operator with a real entity
type Identity struct {
ID string `json:"id,omitempty"`
Proof string `json:"proof,omitempty"`
}
// Validate checks the values in an Identity
func (u *Identity) Validate(vr *ValidationResults) {
//Fixme identity validation
}

99
gateway/vendor/github.com/nats-io/jwt/user_claims.go generated vendored Normal file
View File

@ -0,0 +1,99 @@
/*
* Copyright 2018-2019 The NATS Authors
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jwt
import (
"errors"
"github.com/nats-io/nkeys"
)
// User defines the user specific data in a user JWT
type User struct {
Permissions
Limits
}
// Validate checks the permissions and limits in a User jwt
func (u *User) Validate(vr *ValidationResults) {
u.Permissions.Validate(vr)
u.Limits.Validate(vr)
}
// UserClaims defines a user JWT
type UserClaims struct {
ClaimsData
User `json:"nats,omitempty"`
// IssuerAccount stores the public key for the account the issuer represents.
// When set, the claim was issued by a signing key.
IssuerAccount string `json:"issuer_account,omitempty"`
}
// NewUserClaims creates a user JWT with the specific subject/public key
func NewUserClaims(subject string) *UserClaims {
if subject == "" {
return nil
}
c := &UserClaims{}
c.Subject = subject
return c
}
// Encode tries to turn the user claims into a JWT string
func (u *UserClaims) Encode(pair nkeys.KeyPair) (string, error) {
if !nkeys.IsValidPublicUserKey(u.Subject) {
return "", errors.New("expected subject to be user public key")
}
u.ClaimsData.Type = UserClaim
return u.ClaimsData.Encode(pair, u)
}
// DecodeUserClaims tries to parse a user claims from a JWT string
func DecodeUserClaims(token string) (*UserClaims, error) {
v := UserClaims{}
if err := Decode(token, &v); err != nil {
return nil, err
}
return &v, nil
}
// Validate checks the generic and specific parts of the user jwt
func (u *UserClaims) Validate(vr *ValidationResults) {
u.ClaimsData.Validate(vr)
u.User.Validate(vr)
if u.IssuerAccount != "" && !nkeys.IsValidPublicAccountKey(u.IssuerAccount) {
vr.AddError("account_id is not an account public key")
}
}
// ExpectedPrefixes defines the types that can encode a user JWT, account
func (u *UserClaims) ExpectedPrefixes() []nkeys.PrefixByte {
return []nkeys.PrefixByte{nkeys.PrefixByteAccount}
}
// Claims returns the generic data from a user jwt
func (u *UserClaims) Claims() *ClaimsData {
return &u.ClaimsData
}
// Payload returns the user specific data from a user JWT
func (u *UserClaims) Payload() interface{} {
return &u.User
}
func (u *UserClaims) String() string {
return u.ClaimsData.String(u)
}

107
gateway/vendor/github.com/nats-io/jwt/validation.go generated vendored Normal file
View File

@ -0,0 +1,107 @@
/*
* Copyright 2018 The NATS Authors
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jwt
import (
"errors"
"fmt"
)
// ValidationIssue represents an issue during JWT validation, it may or may not be a blocking error
type ValidationIssue struct {
Description string
Blocking bool
TimeCheck bool
}
func (ve *ValidationIssue) Error() string {
return ve.Description
}
// ValidationResults is a list of ValidationIssue pointers
type ValidationResults struct {
Issues []*ValidationIssue
}
// CreateValidationResults creates an empty list of validation issues
func CreateValidationResults() *ValidationResults {
issues := []*ValidationIssue{}
return &ValidationResults{
Issues: issues,
}
}
//Add appends an issue to the list
func (v *ValidationResults) Add(vi *ValidationIssue) {
v.Issues = append(v.Issues, vi)
}
// AddError creates a new validation error and adds it to the list
func (v *ValidationResults) AddError(format string, args ...interface{}) {
v.Add(&ValidationIssue{
Description: fmt.Sprintf(format, args...),
Blocking: true,
TimeCheck: false,
})
}
// AddTimeCheck creates a new validation issue related to a time check and adds it to the list
func (v *ValidationResults) AddTimeCheck(format string, args ...interface{}) {
v.Add(&ValidationIssue{
Description: fmt.Sprintf(format, args...),
Blocking: false,
TimeCheck: true,
})
}
// AddWarning creates a new validation warning and adds it to the list
func (v *ValidationResults) AddWarning(format string, args ...interface{}) {
v.Add(&ValidationIssue{
Description: fmt.Sprintf(format, args...),
Blocking: false,
TimeCheck: false,
})
}
// IsBlocking returns true if the list contains a blocking error
func (v *ValidationResults) IsBlocking(includeTimeChecks bool) bool {
for _, i := range v.Issues {
if i.Blocking {
return true
}
if includeTimeChecks && i.TimeCheck {
return true
}
}
return false
}
// IsEmpty returns true if the list is empty
func (v *ValidationResults) IsEmpty() bool {
return len(v.Issues) == 0
}
// Errors returns only blocking issues as errors
func (v *ValidationResults) Errors() []error {
var errs []error
for _, v := range v.Issues {
if v.Blocking {
errs = append(errs, errors.New(v.Description))
}
}
return errs
}

View File

@ -37,3 +37,6 @@ _testmain.go
.settings/
# bin
# Goland
.idea

24
gateway/vendor/github.com/nats-io/nats.go/.travis.yml generated vendored Normal file
View File

@ -0,0 +1,24 @@
language: go
sudo: false
go:
- 1.13.x
- 1.12.x
env:
- GO111MODULE=off
go_import_path: github.com/nats-io/nats.go
install:
- go get -t ./...
- go get github.com/nats-io/nats-server
- go get github.com/mattn/goveralls
- go get github.com/wadey/gocovmerge
- go get -u honnef.co/go/tools/cmd/staticcheck
- go get -u github.com/client9/misspell/cmd/misspell
before_script:
- $(exit $(go fmt ./... | wc -l))
- go vet ./...
- find . -type f -name "*.go" | xargs misspell -error -locale US
- staticcheck ./...
script:
- go test -i -race ./...
- go test -v -run=TestNoRace -p=1 ./...
- if [[ "$TRAVIS_GO_VERSION" =~ 1.13 ]]; then ./scripts/cov.sh TRAVIS; else go test -race -v -p=1 ./... --failfast; fi

View File

@ -0,0 +1,3 @@
## Community Code of Conduct
NATS follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md).

201
gateway/vendor/github.com/nats-io/nats.go/LICENSE generated vendored Normal file
View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -3,22 +3,36 @@ A [Go](http://golang.org) client for the [NATS messaging system](https://nats.io
[![License Apache 2](https://img.shields.io/badge/License-Apache2-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0)
[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fnats-io%2Fgo-nats.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fnats-io%2Fgo-nats?ref=badge_shield)
[![Go Report Card](https://goreportcard.com/badge/github.com/nats-io/go-nats)](https://goreportcard.com/report/github.com/nats-io/go-nats) [![Build Status](https://travis-ci.org/nats-io/go-nats.svg?branch=master)](http://travis-ci.org/nats-io/go-nats) [![GoDoc](https://godoc.org/github.com/nats-io/go-nats?status.svg)](http://godoc.org/github.com/nats-io/go-nats) [![Coverage Status](https://coveralls.io/repos/nats-io/go-nats/badge.svg?branch=master)](https://coveralls.io/r/nats-io/go-nats?branch=master)
[![Go Report Card](https://goreportcard.com/badge/github.com/nats-io/nats.go)](https://goreportcard.com/report/github.com/nats-io/nats.go) [![Build Status](https://travis-ci.org/nats-io/nats.go.svg?branch=master)](http://travis-ci.org/nats-io/nats.go) [![GoDoc](https://godoc.org/github.com/nats-io/nats.go?status.svg)](http://godoc.org/github.com/nats-io/nats.go) [![Coverage Status](https://coveralls.io/repos/nats-io/nats.go/badge.svg?branch=master)](https://coveralls.io/r/nats-io/nats.go?branch=master)
## Installation
```bash
# Go client
go get github.com/nats-io/go-nats
go get github.com/nats-io/nats.go/
# Server
go get github.com/nats-io/gnatsd
go get github.com/nats-io/nats-server
```
When using or transitioning to Go modules support:
```bash
# Go client latest or explicit version
go get github.com/nats-io/nats.go/@latest
go get github.com/nats-io/nats.go/@v1.9.2
# For latest NATS Server, add /v2 at the end
go get github.com/nats-io/nats-server/v2
# NATS Server v1 is installed otherwise
# go get github.com/nats-io/nats-server
```
## Basic Usage
```go
import nats "github.com/nats-io/go-nats"
import nats "github.com/nats-io/nats.go"
// Connect to a server
nc, _ := nats.Connect(nats.DefaultURL)
@ -31,6 +45,11 @@ nc.Subscribe("foo", func(m *nats.Msg) {
fmt.Printf("Received a message: %s\n", string(m.Data))
})
// Responding to a request message
nc.Subscribe("request", func(m *nats.Msg) {
m.Respond([]byte("answer is 42"))
})
// Simple Sync Subscriber
sub, err := nc.SubscribeSync("foo")
m, err := sub.NextMsg(timeout)
@ -50,7 +69,7 @@ sub.Drain()
msg, err := nc.Request("help", []byte("help me"), 10*time.Millisecond)
// Replies
nc.Subscribe("help", func(m *Msg) {
nc.Subscribe("help", func(m *nats.Msg) {
nc.Publish(m.Reply, []byte("I can help!"))
})
@ -97,12 +116,12 @@ c.Publish("hello", me)
// Unsubscribe
sub, err := c.Subscribe("foo", nil)
...
// ...
sub.Unsubscribe()
// Requests
var response string
err := c.Request("help", "help me", &response, 10*time.Millisecond)
err = c.Request("help", "help me", &response, 10*time.Millisecond)
if err != nil {
fmt.Printf("Request failed: %v\n", err)
}
@ -122,21 +141,21 @@ This requires server with version >= 2.0.0
NATS servers have a new security and authentication mechanism to authenticate with user credentials and Nkeys.
The simplest form is to use the helper method UserCredentials(credsFilepath).
```go
nc, err := nats.Connect(url, UserCredentials("user.creds"))
nc, err := nats.Connect(url, nats.UserCredentials("user.creds"))
```
The helper methos creates two callback handlers to present the user JWT and sign the nonce challenge from the server.
The helper methods creates two callback handlers to present the user JWT and sign the nonce challenge from the server.
The core client library never has direct access to your private key and simply performs the callback for signing the server challenge.
The helper will load and wipe and erase memory it uses for each connect or reconnect.
The helper also can take two entries, one for the JWT and one for the NKey seed file.
```go
nc, err := nats.Connect(url, UserCredentials("user.jwt", "user.nk"))
nc, err := nats.Connect(url, nats.UserCredentials("user.jwt", "user.nk"))
```
You can also set the callback handlers directly and manage challenge signing directly.
```go
nc, err := nats.Connect(url, UserJWT(jwtCB, sigCB))
nc, err := nats.Connect(url, nats.UserJWT(jwtCB, sigCB))
```
Bare Nkeys are also supported. The nkey seed should be in a read only file, e.g. seed.txt
@ -155,7 +174,7 @@ opt, err := nats.NkeyOptionFromSeed("seed.txt")
nc, err := nats.Connect(serverUrl, opt)
// Direct
nc, err := nats.Connect(serverUrl, Nkey(pubNkey, sigCB))
nc, err := nats.Connect(serverUrl, nats.Nkey(pubNkey, sigCB))
```
## TLS
@ -311,8 +330,8 @@ nc, err = nats.Connect(servers, nats.DontRandomize())
// Setup callbacks to be notified on disconnects, reconnects and connection closed.
nc, err = nats.Connect(servers,
nats.DisconnectHandler(func(nc *nats.Conn) {
fmt.Printf("Got disconnected!\n")
nats.DisconnectErrHandler(func(nc *nats.Conn, err error) {
fmt.Printf("Got disconnected! Reason: %q\n", err)
}),
nats.ReconnectHandler(func(nc *nats.Conn) {
fmt.Printf("Got reconnected to %v!\n", nc.ConnectedUrl())
@ -339,9 +358,9 @@ nc, err = nats.Connect("nats://localhost:4222",
nats.Token("S3cretT0ken"))
// Note that if credentials are specified in the initial URLs, they take
// precedence on the credentials specfied through the options.
// precedence on the credentials specified through the options.
// For instance, in the connect call below, the client library will use
// the user "my" and password "pwd" to connect to locahost:4222, however,
// the user "my" and password "pwd" to connect to localhost:4222, however,
// it will use username "foo" and password "bar" when (re)connecting to
// a different server URL that it got as part of the auto-discovery.
nc, err = nats.Connect("nats://my:pwd@localhost:4222", nats.UserInfo("foo", "bar"))

View File

@ -43,29 +43,7 @@ func (nc *Conn) RequestWithContext(ctx context.Context, subj string, data []byte
return nc.oldRequestWithContext(ctx, subj, data)
}
// Do setup for the new style.
if nc.respMap == nil {
nc.initNewResp()
}
// Create literal Inbox and map to a chan msg.
mch := make(chan *Msg, RequestChanLen)
respInbox := nc.newRespInbox()
token := respToken(respInbox)
nc.respMap[token] = mch
createSub := nc.respMux == nil
ginbox := nc.respSub
nc.mu.Unlock()
if createSub {
// Make sure scoped subscription is setup only once.
var err error
nc.respSetup.Do(func() { err = nc.createRespMux(ginbox) })
if err != nil {
return nil, err
}
}
err := nc.PublishRequest(subj, respInbox, data)
mch, token, err := nc.createNewRequestAndSend(subj, data)
if err != nil {
return nil, err
}
@ -93,7 +71,7 @@ func (nc *Conn) oldRequestWithContext(ctx context.Context, subj string, data []b
inbox := NewInbox()
ch := make(chan *Msg, RequestChanLen)
s, err := nc.subscribe(inbox, _EMPTY_, nil, ch)
s, err := nc.subscribe(inbox, _EMPTY_, nil, ch, true)
if err != nil {
return nil, err
}
@ -140,7 +118,7 @@ func (s *Subscription) NextMsgWithContext(ctx context.Context) (*Msg, error) {
select {
case msg, ok = <-mch:
if !ok {
return nil, ErrConnectionClosed
return nil, s.getNextMsgErr()
}
if err := s.processNextMsgDelivered(msg); err != nil {
return nil, err
@ -153,7 +131,7 @@ func (s *Subscription) NextMsgWithContext(ctx context.Context) (*Msg, error) {
select {
case msg, ok = <-mch:
if !ok {
return nil, ErrConnectionClosed
return nil, s.getNextMsgErr()
}
if err := s.processNextMsgDelivered(msg); err != nil {
return nil, err

View File

@ -1,4 +1,4 @@
// Copyright 2012-2018 The NATS Authors
// Copyright 2012-2019 The NATS Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
@ -21,7 +21,7 @@ import (
"time"
// Default Encoders
"github.com/nats-io/go-nats/encoders/builtin"
"github.com/nats-io/nats.go/encoders/builtin"
)
// Encoder interface is for all register encoders
@ -33,7 +33,7 @@ type Encoder interface {
var encMap map[string]Encoder
var encLock sync.Mutex
// Indexe names into the Registered Encoders.
// Indexed names into the Registered Encoders.
const (
JSON_ENCODER = "json"
GOB_ENCODER = "gob"
@ -234,7 +234,7 @@ func (c *EncodedConn) subscribe(subject, queue string, cb Handler) (*Subscriptio
cbValue.Call(oV)
}
return c.Conn.subscribe(subject, queue, natsCB, nil)
return c.Conn.subscribe(subject, queue, natsCB, nil, false)
}
// FlushTimeout allows a Flush operation to have an associated timeout.

7
gateway/vendor/github.com/nats-io/nats.go/go.mod generated vendored Normal file
View File

@ -0,0 +1,7 @@
module github.com/nats-io/nats.go
require (
github.com/nats-io/jwt v0.3.2
github.com/nats-io/nkeys v0.1.4
github.com/nats-io/nuid v1.0.1
)

15
gateway/vendor/github.com/nats-io/nats.go/go.sum generated vendored Normal file
View File

@ -0,0 +1,15 @@
github.com/nats-io/jwt v0.3.2 h1:+RB5hMpXUUA2dfxuhBTEkMOrYmM+gKIZYS1KjSostMI=
github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=
github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nkeys v0.1.4 h1:aEsHIssIk6ETN5m2/MD8Y4B2X7FfXrBAUdkyRvbVYzA=
github.com/nats-io/nkeys v0.1.4/go.mod h1:XdZpAbhgyyODYqjTawOnIOI7VlbKSarI9Gfy1tqEu/s=
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59 h1:3zb4D3T4G8jdExgVU/95+vQXfpEPiMdCaZgmGVxjNHM=
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=

View File

@ -107,5 +107,5 @@ func (c *EncodedConn) bindRecvChan(subject, queue string, channel interface{}) (
chVal.Send(oPtr)
}
return c.Conn.subscribe(subject, queue, cb, nil)
return c.Conn.subscribe(subject, queue, cb, nil, false)
}

View File

@ -26,3 +26,4 @@ _testmain.go
# Eclipse stuff
.project
.settings/
.idea

23
gateway/vendor/github.com/nats-io/stan.go/.travis.yml generated vendored Normal file
View File

@ -0,0 +1,23 @@
language: go
sudo: false
go:
- 1.12.x
- 1.13.x
env:
- GO111MODULE=off
go_import_path: github.com/nats-io/stan.go
install:
- go get -t -v ./...
- go get github.com/nats-io/nats-streaming-server
- go get -u honnef.co/go/tools/cmd/staticcheck
- go get -u github.com/client9/misspell/cmd/misspell
before_script:
- $(exit $(go fmt ./... | wc -l))
- go vet ./...
- find . -type f -name "*.go" | grep -v "/pb/" | xargs misspell -error -locale US
- staticcheck ./...
script:
- go test -i -v ./...
- go test -v -race ./...
after_success:
- if [[ "$TRAVIS_GO_VERSION" =~ 1.13 ]]; then ./scripts/cov.sh TRAVIS; fi

View File

@ -3,9 +3,9 @@
NATS Streaming is an extremely performant, lightweight reliable streaming platform powered by [NATS](https://nats.io).
[![License Apache 2](https://img.shields.io/badge/License-Apache2-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0)
[![Build Status](https://travis-ci.org/nats-io/go-nats-streaming.svg?branch=master)](http://travis-ci.org/nats-io/go-nats-streaming)
[![Coverage Status](https://coveralls.io/repos/nats-io/go-nats-streaming/badge.svg?branch=master)](https://coveralls.io/r/nats-io/go-nats-streaming?branch=master)
[![GoDoc](https://godoc.org/github.com/nats-io/go-nats-streaming?status.svg)](http://godoc.org/github.com/nats-io/go-nats-streaming)
[![Build Status](https://travis-ci.org/nats-io/stan.go.svg?branch=master)](http://travis-ci.org/nats-io/stan.go)
[![Coverage Status](https://coveralls.io/repos/nats-io/stan.go/badge.svg?branch=master)](https://coveralls.io/r/nats-io/stan.go?branch=master)
[![GoDoc](https://godoc.org/github.com/nats-io/stan.go?status.svg)](http://godoc.org/github.com/nats-io/stan.go)
NATS Streaming provides the following high-level feature set:
- Log based persistence
@ -16,19 +16,27 @@ NATS Streaming provides the following high-level feature set:
## Notes
- Please raise questions/issues via the [Issue Tracker](https://github.com/nats-io/go-nats-streaming/issues).
- Please raise questions/issues via the [Issue Tracker](https://github.com/nats-io/stan.go/issues).
## Installation
```bash
# Go client
go get github.com/nats-io/go-nats-streaming
go get github.com/nats-io/stan.go/
```
When using or transitioning to Go modules support:
```bash
# Go client latest or explicit version
go get github.com/nats-io/stan.go/@latest
go get github.com/nats-io/stan.go/@v0.6.0
```
## Basic Usage
```go
import stan "github.com/nats-io/go-nats-streaming"
import stan "github.com/nats-io/stan.go"
sc, _ := stan.Connect(clusterID, clientID)

7
gateway/vendor/github.com/nats-io/stan.go/go.mod generated vendored Normal file
View File

@ -0,0 +1,7 @@
module github.com/nats-io/stan.go
require (
github.com/gogo/protobuf v1.2.1
github.com/nats-io/nats.go v1.9.1
github.com/nats-io/nuid v1.0.1
)

20
gateway/vendor/github.com/nats-io/stan.go/go.sum generated vendored Normal file
View File

@ -0,0 +1,20 @@
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/nats-io/jwt v0.3.0 h1:xdnzwFETV++jNc4W1mw//qFyJGb2ABOombmZJQS4+Qo=
github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
github.com/nats-io/nats.go v1.9.1 h1:ik3HbLhZ0YABLto7iX80pZLPw/6dx3T+++MZJwLnMrQ=
github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w=
github.com/nats-io/nkeys v0.1.0 h1:qMd4+pRHgdr1nAClu+2h/2a5F2TmKcCzjCDazVgRoX4=
github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

View File

@ -109,6 +109,7 @@ type MsgProto struct {
Data []byte `protobuf:"bytes,4,opt,name=data,proto3" json:"data,omitempty"`
Timestamp int64 `protobuf:"varint,5,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
Redelivered bool `protobuf:"varint,6,opt,name=redelivered,proto3" json:"redelivered,omitempty"`
RedeliveryCount uint32 `protobuf:"varint,7,opt,name=redeliveryCount,proto3" json:"redeliveryCount,omitempty"`
CRC32 uint32 `protobuf:"varint,10,opt,name=CRC32,proto3" json:"CRC32,omitempty"`
}
@ -405,6 +406,11 @@ func (m *MsgProto) MarshalTo(dAtA []byte) (int, error) {
}
i++
}
if m.RedeliveryCount != 0 {
dAtA[i] = 0x38
i++
i = encodeVarintProtocol(dAtA, i, uint64(m.RedeliveryCount))
}
if m.CRC32 != 0 {
dAtA[i] = 0x50
i++
@ -898,6 +904,9 @@ func (m *MsgProto) Size() (n int) {
if m.Redelivered {
n += 2
}
if m.RedeliveryCount != 0 {
n += 1 + sovProtocol(uint64(m.RedeliveryCount))
}
if m.CRC32 != 0 {
n += 1 + sovProtocol(uint64(m.CRC32))
}
@ -1664,6 +1673,25 @@ func (m *MsgProto) Unmarshal(dAtA []byte) error {
}
}
m.Redelivered = bool(v != 0)
case 7:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field RedeliveryCount", wireType)
}
m.RedeliveryCount = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowProtocol
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.RedeliveryCount |= (uint32(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
case 10:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field CRC32", wireType)
@ -3327,57 +3355,58 @@ var (
func init() { proto.RegisterFile("protocol.proto", fileDescriptorProtocol) }
var fileDescriptorProtocol = []byte{
// 823 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x54, 0xc1, 0x8e, 0xe3, 0x44,
0x10, 0x1d, 0xc7, 0x76, 0x26, 0x53, 0x93, 0x84, 0x6c, 0x13, 0xad, 0xac, 0x08, 0x45, 0x23, 0x6b,
0x41, 0xab, 0x91, 0xc8, 0x4a, 0x59, 0x01, 0x07, 0x4e, 0x90, 0xd1, 0x42, 0x04, 0xb3, 0x1b, 0x39,
0x20, 0xae, 0xb4, 0x9d, 0x5e, 0xa7, 0x19, 0xa7, 0xed, 0x75, 0xb7, 0x87, 0xcc, 0x11, 0xbe, 0x80,
0x0f, 0xe1, 0x23, 0x38, 0xee, 0x81, 0x03, 0x9f, 0x00, 0xc3, 0x8d, 0xaf, 0x40, 0x5d, 0x76, 0x9c,
0x76, 0x86, 0x1d, 0x90, 0xb8, 0xf5, 0x7b, 0xa9, 0x6e, 0xd7, 0x7b, 0xaf, 0x2a, 0xd0, 0xcf, 0xf2,
0x54, 0xa5, 0x51, 0x9a, 0x4c, 0xf0, 0x40, 0x5a, 0x59, 0x38, 0x7a, 0x3f, 0xe6, 0x6a, 0x5d, 0x84,
0x93, 0x28, 0xdd, 0x3c, 0x89, 0xd3, 0x38, 0x7d, 0x82, 0x3f, 0x85, 0xc5, 0x4b, 0x44, 0x08, 0xf0,
0x54, 0x5e, 0xf1, 0x7f, 0xb6, 0xa0, 0xbd, 0x28, 0xc2, 0x4b, 0x19, 0x93, 0x11, 0x74, 0xa2, 0x84,
0x33, 0xa1, 0xe6, 0x17, 0x9e, 0x75, 0x66, 0x3d, 0x3e, 0x09, 0x6a, 0x4c, 0x08, 0x38, 0x71, 0xc1,
0x57, 0x5e, 0x0b, 0x79, 0x3c, 0x13, 0x0f, 0x8e, 0x65, 0x11, 0x7e, 0xc7, 0x22, 0xe5, 0xd9, 0x48,
0xef, 0x20, 0x19, 0x82, 0x9b, 0xb3, 0x2c, 0xb9, 0xf1, 0x1c, 0xe4, 0x4b, 0xa0, 0xdf, 0x58, 0x51,
0x45, 0x3d, 0xf7, 0xcc, 0x7a, 0xdc, 0x0d, 0xf0, 0x4c, 0x1e, 0x42, 0x3b, 0x4a, 0x85, 0x98, 0x5f,
0x78, 0x6d, 0x64, 0x2b, 0xa4, 0x79, 0xb9, 0xa6, 0xd3, 0x0f, 0x3e, 0xf4, 0xa0, 0xe4, 0x4b, 0xe4,
0x4f, 0xb1, 0xdb, 0x4f, 0xa2, 0xab, 0xba, 0x23, 0xcb, 0xe8, 0x68, 0x08, 0x2e, 0xcb, 0xf3, 0x34,
0xaf, 0xda, 0x2c, 0x81, 0xff, 0x8b, 0x05, 0x9d, 0x4b, 0x19, 0x2f, 0xd0, 0xa2, 0x11, 0x74, 0x24,
0x7b, 0x55, 0x30, 0x11, 0x31, 0xbc, 0xea, 0x04, 0x35, 0x36, 0x05, 0xb5, 0xde, 0x20, 0xc8, 0xfe,
0x27, 0x41, 0x8e, 0x21, 0xe8, 0x1d, 0x38, 0x51, 0x7c, 0xc3, 0xa4, 0xa2, 0x9b, 0x0c, 0x95, 0xda,
0xc1, 0x9e, 0x20, 0x67, 0x70, 0x9a, 0xb3, 0x15, 0x4b, 0xf8, 0x35, 0xcb, 0xd9, 0x0a, 0x35, 0x77,
0x02, 0x93, 0xd2, 0x5f, 0x9a, 0x05, 0xb3, 0xa7, 0x53, 0xd4, 0xdd, 0x0b, 0x4a, 0xe0, 0x7f, 0x0c,
0xb6, 0xd6, 0x6c, 0x34, 0x68, 0x35, 0x1b, 0x34, 0x65, 0xb5, 0x9a, 0xb2, 0xfc, 0x5f, 0x2d, 0xe8,
0xcf, 0x52, 0x21, 0x58, 0xa4, 0x02, 0xcd, 0x49, 0x75, 0x6f, 0xd4, 0xef, 0x41, 0x7f, 0xcd, 0x68,
0xae, 0x42, 0x46, 0xd5, 0x5c, 0x84, 0xe9, 0xb6, 0x32, 0xe3, 0x80, 0xd5, 0x6f, 0xec, 0xc6, 0x0f,
0x6d, 0x71, 0x83, 0x1a, 0x1b, 0xb1, 0x3a, 0x8d, 0x58, 0x7d, 0xe8, 0x66, 0x5c, 0xc4, 0x73, 0xa1,
0x58, 0x7e, 0x4d, 0x13, 0x34, 0xc8, 0x0d, 0x1a, 0x1c, 0x19, 0x03, 0x68, 0x7c, 0x49, 0xb7, 0x2f,
0x0a, 0x85, 0x16, 0xb9, 0x81, 0xc1, 0xf8, 0x3f, 0xd8, 0xf0, 0x56, 0x2d, 0x47, 0x66, 0xa9, 0x90,
0x4c, 0xbb, 0x9e, 0x15, 0xe1, 0x22, 0x67, 0x2f, 0xf9, 0xb6, 0x12, 0xb4, 0x27, 0xb4, 0xeb, 0xb2,
0x08, 0x2b, 0xed, 0xb2, 0x92, 0x63, 0x52, 0xe4, 0x11, 0xf4, 0x0a, 0x61, 0xd6, 0x94, 0x39, 0x37,
0x49, 0x5d, 0x15, 0x25, 0xa9, 0x64, 0x75, 0x55, 0x39, 0xde, 0x4d, 0x72, 0x3f, 0x84, 0xae, 0x31,
0x84, 0xe4, 0x1c, 0x06, 0xb2, 0x08, 0x67, 0x8d, 0xeb, 0x6d, 0x2c, 0xb8, 0xc3, 0xef, 0x5c, 0xaa,
0xeb, 0x8e, 0xb1, 0xae, 0xc1, 0xdd, 0x71, 0xb2, 0xf3, 0xaf, 0x4e, 0x9e, 0x1c, 0x3a, 0xd9, 0x48,
0x10, 0x0e, 0x12, 0x2c, 0x1d, 0x4d, 0x78, 0xf4, 0x05, 0xbb, 0xf1, 0x56, 0xb5, 0xa3, 0x25, 0xe1,
0x8f, 0xc1, 0x59, 0x70, 0x11, 0x1b, 0x39, 0x5b, 0x66, 0xce, 0xfe, 0x23, 0xe8, 0x2e, 0xb0, 0xdb,
0x2a, 0x9f, 0xda, 0x13, 0xcb, 0x5c, 0xcc, 0xbf, 0x5a, 0xf0, 0xf6, 0xb2, 0x08, 0x65, 0x94, 0xf3,
0x4c, 0xf1, 0x54, 0xfc, 0x97, 0xe9, 0x7c, 0xf3, 0x8e, 0x3e, 0x84, 0xf6, 0xab, 0xcf, 0xf2, 0xb4,
0xc8, 0xaa, 0xf0, 0x2a, 0xa4, 0xbf, 0xcd, 0x71, 0x8c, 0xab, 0x3f, 0x23, 0x04, 0x7a, 0x26, 0x36,
0x74, 0x3b, 0x17, 0xcf, 0x12, 0x1e, 0xaf, 0x55, 0x35, 0x88, 0x26, 0xa5, 0xd3, 0xa6, 0xd1, 0xd5,
0x37, 0x94, 0xab, 0xb9, 0x58, 0xb2, 0x48, 0x56, 0xa3, 0xd8, 0x24, 0xf5, 0x3b, 0xab, 0x22, 0xa7,
0x61, 0xc2, 0x9e, 0xd3, 0x0d, 0xab, 0xa2, 0x32, 0x29, 0xf2, 0x11, 0xf4, 0xa4, 0xa2, 0xb9, 0x5a,
0xa4, 0x92, 0x6b, 0x95, 0x68, 0x75, 0x7f, 0xfa, 0x60, 0x92, 0x85, 0x93, 0xa5, 0xf9, 0x43, 0xd0,
0xac, 0xd3, 0x0d, 0x20, 0xb1, 0xdc, 0x2d, 0xf6, 0x29, 0x2e, 0x76, 0x93, 0xd4, 0xeb, 0x8a, 0xc4,
0x57, 0x7c, 0xc3, 0x2e, 0x58, 0xa2, 0xa8, 0xd7, 0xc5, 0x7f, 0x9d, 0x03, 0xd6, 0xff, 0x1c, 0x86,
0x4d, 0xaf, 0xab, 0x68, 0x46, 0xd0, 0xa1, 0xd1, 0x95, 0xb9, 0xe8, 0x35, 0xde, 0xc7, 0x66, 0x9b,
0xb1, 0xfd, 0x68, 0x01, 0xf9, 0x5a, 0x2f, 0x86, 0x7e, 0x2c, 0x64, 0xff, 0x2f, 0xb5, 0x3a, 0x1d,
0xfb, 0x20, 0x1d, 0xd3, 0x55, 0xe7, 0x8e, 0xab, 0xfe, 0x39, 0x74, 0xcd, 0xa5, 0xb9, 0xef, 0xeb,
0xfe, 0xbb, 0xd0, 0xab, 0x6a, 0xef, 0x1b, 0xc7, 0xf3, 0x6f, 0xa1, 0xd7, 0xc8, 0x83, 0x9c, 0xc2,
0xf1, 0x73, 0xf6, 0xfd, 0x0b, 0x91, 0xdc, 0x0c, 0x8e, 0xc8, 0x00, 0xba, 0x5f, 0x52, 0xa9, 0x02,
0x16, 0x31, 0x7e, 0xcd, 0x56, 0x03, 0x8b, 0x10, 0xe8, 0xd7, 0xf6, 0xe2, 0xc5, 0x41, 0x8b, 0x3c,
0x80, 0xde, 0x2e, 0x99, 0x92, 0xb2, 0xc9, 0x09, 0xb8, 0xcf, 0x78, 0x2e, 0xd5, 0xc0, 0xf9, 0x74,
0xf8, 0xfa, 0x8f, 0xf1, 0xd1, 0xeb, 0xdb, 0xb1, 0xf5, 0xdb, 0xed, 0xd8, 0xfa, 0xfd, 0x76, 0x6c,
0xfd, 0xf4, 0xe7, 0xf8, 0x28, 0x6c, 0xe3, 0xd2, 0x3d, 0xfd, 0x3b, 0x00, 0x00, 0xff, 0xff, 0x66,
0xcd, 0xb9, 0x27, 0xce, 0x07, 0x00, 0x00,
// 840 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x94, 0xd1, 0x6e, 0xe3, 0x44,
0x14, 0x86, 0x3b, 0x89, 0x9d, 0xa6, 0xa7, 0x49, 0x36, 0x3b, 0x54, 0x2b, 0xab, 0x42, 0x51, 0x65,
0x2d, 0xa8, 0xaa, 0x44, 0x57, 0xea, 0x0a, 0xb8, 0xe0, 0x0a, 0x52, 0x2d, 0x44, 0xd0, 0xdd, 0xc8,
0x05, 0x71, 0xcb, 0xd8, 0x99, 0x75, 0x87, 0x3a, 0x33, 0x5e, 0xcf, 0xb8, 0xb4, 0x97, 0xf0, 0x04,
0x3c, 0x08, 0x0f, 0xb2, 0x17, 0x5c, 0xf0, 0x08, 0x50, 0xee, 0x78, 0x0a, 0x34, 0xc7, 0x8e, 0x33,
0x4e, 0x69, 0x41, 0xe2, 0xce, 0xff, 0x97, 0xe3, 0xc9, 0x9c, 0xff, 0x3f, 0xc7, 0x30, 0xca, 0x0b,
0x65, 0x54, 0xa2, 0xb2, 0x63, 0x7c, 0xa0, 0x9d, 0x3c, 0xde, 0xff, 0x20, 0x15, 0xe6, 0xa2, 0x8c,
0x8f, 0x13, 0xb5, 0x7c, 0x96, 0xaa, 0x54, 0x3d, 0xc3, 0x9f, 0xe2, 0xf2, 0x35, 0x2a, 0x14, 0xf8,
0x54, 0xbd, 0x12, 0xfe, 0x42, 0xa0, 0x37, 0x2f, 0xe3, 0x33, 0x9d, 0xd2, 0x7d, 0xe8, 0x27, 0x99,
0xe0, 0xd2, 0xcc, 0x4e, 0x03, 0x72, 0x40, 0x0e, 0x77, 0xa2, 0x46, 0x53, 0x0a, 0x5e, 0x5a, 0x8a,
0x45, 0xd0, 0x41, 0x8e, 0xcf, 0x34, 0x80, 0x6d, 0x5d, 0xc6, 0xdf, 0xf3, 0xc4, 0x04, 0x5d, 0xc4,
0x2b, 0x49, 0xf7, 0xc0, 0x2f, 0x78, 0x9e, 0xdd, 0x04, 0x1e, 0xf2, 0x4a, 0xd8, 0x33, 0x16, 0xcc,
0xb0, 0xc0, 0x3f, 0x20, 0x87, 0x83, 0x08, 0x9f, 0xe9, 0x13, 0xe8, 0x25, 0x4a, 0xca, 0xd9, 0x69,
0xd0, 0x43, 0x5a, 0x2b, 0xcb, 0xf5, 0x05, 0x3b, 0xf9, 0xf0, 0xa3, 0x00, 0x2a, 0x5e, 0xa9, 0xf0,
0x04, 0x6f, 0xfb, 0x69, 0x72, 0xd9, 0xdc, 0x88, 0x38, 0x37, 0xda, 0x03, 0x9f, 0x17, 0x85, 0x2a,
0xea, 0x6b, 0x56, 0x22, 0xfc, 0x8b, 0x40, 0xff, 0x4c, 0xa7, 0x73, 0xb4, 0x68, 0x1f, 0xfa, 0x9a,
0xbf, 0x29, 0xb9, 0x4c, 0x38, 0xbe, 0xea, 0x45, 0x8d, 0x76, 0x1b, 0xea, 0xdc, 0xd3, 0x50, 0xf7,
0x9f, 0x1a, 0xf2, 0x9c, 0x86, 0xde, 0x85, 0x1d, 0x23, 0x96, 0x5c, 0x1b, 0xb6, 0xcc, 0xb1, 0xd3,
0x6e, 0xb4, 0x06, 0xf4, 0x00, 0x76, 0x0b, 0xbe, 0xe0, 0x99, 0xb8, 0xe2, 0x05, 0x5f, 0x60, 0xcf,
0xfd, 0xc8, 0x45, 0xf4, 0x10, 0x1e, 0x35, 0xf2, 0x66, 0xaa, 0x4a, 0x69, 0x82, 0xed, 0x03, 0x72,
0x38, 0x8c, 0x36, 0xb1, 0xbd, 0xd3, 0x34, 0x9a, 0x3e, 0x3f, 0x41, 0x87, 0x86, 0x51, 0x25, 0xc2,
0x4f, 0xa0, 0x6b, 0xdd, 0x71, 0x5a, 0x21, 0xed, 0x56, 0x5c, 0x03, 0x3a, 0x6d, 0x03, 0xc2, 0x5f,
0x09, 0x8c, 0xa6, 0x4a, 0x4a, 0x9e, 0x98, 0xc8, 0x32, 0x6d, 0x1e, 0x1c, 0x8a, 0xf7, 0x61, 0x74,
0xc1, 0x59, 0x61, 0x62, 0xce, 0xcc, 0x4c, 0xc6, 0xea, 0xba, 0xb6, 0x6d, 0x83, 0xda, 0x33, 0x56,
0x83, 0x8a, 0x06, 0xfa, 0x51, 0xa3, 0x9d, 0x01, 0xf0, 0x5a, 0x03, 0x10, 0xc2, 0x20, 0x17, 0x32,
0x9d, 0x49, 0xc3, 0x8b, 0x2b, 0x96, 0xa1, 0x95, 0x7e, 0xd4, 0x62, 0x74, 0x02, 0x60, 0xf5, 0x19,
0xbb, 0x7e, 0x55, 0x1a, 0x34, 0xd3, 0x8f, 0x1c, 0x12, 0xfe, 0xd8, 0x85, 0x47, 0x4d, 0x3b, 0x3a,
0x57, 0x52, 0x73, 0x9b, 0x4f, 0x5e, 0xc6, 0xf3, 0x82, 0xbf, 0x16, 0xd7, 0x75, 0x43, 0x6b, 0x60,
0xf3, 0xd1, 0x65, 0x5c, 0xf7, 0xae, 0xeb, 0x76, 0x5c, 0x44, 0x9f, 0xc2, 0xb0, 0x94, 0x6e, 0x4d,
0x35, 0x11, 0x6d, 0x68, 0xab, 0x92, 0x4c, 0x69, 0xde, 0x54, 0x55, 0x8b, 0xd0, 0x86, 0xeb, 0x71,
0xf5, 0x9d, 0x71, 0xa5, 0x47, 0x30, 0xd6, 0x65, 0x3c, 0x6d, 0xbd, 0xde, 0xc3, 0x82, 0x3b, 0x7c,
0xe5, 0x52, 0x53, 0xb7, 0x8d, 0x75, 0x2d, 0x76, 0xc7, 0xc9, 0xfe, 0xbf, 0x3a, 0xb9, 0xb3, 0xe9,
0x64, 0x2b, 0x41, 0xd8, 0x48, 0xb0, 0x72, 0x34, 0x13, 0xc9, 0x97, 0xfc, 0x26, 0x58, 0x34, 0x8e,
0x56, 0x20, 0x9c, 0x80, 0x37, 0x17, 0x32, 0x75, 0x72, 0x26, 0x6e, 0xce, 0xe1, 0x53, 0x18, 0xcc,
0xf1, 0xb6, 0x75, 0x3e, 0x8d, 0x27, 0xa4, 0xb5, 0xc2, 0x1d, 0x78, 0xe7, 0xbc, 0x8c, 0x75, 0x52,
0x88, 0xdc, 0x08, 0x25, 0xff, 0xcb, 0x74, 0xde, 0xbf, 0xcd, 0x4f, 0xa0, 0xf7, 0xe6, 0xf3, 0x42,
0x95, 0x79, 0x1d, 0x5e, 0xad, 0xec, 0x7f, 0x0b, 0x1c, 0xe3, 0xfa, 0xb3, 0x85, 0xc2, 0xce, 0xc4,
0x92, 0x5d, 0xcf, 0xe4, 0x8b, 0x4c, 0xa4, 0x17, 0xa6, 0x1e, 0x44, 0x17, 0xd9, 0xb4, 0x59, 0x72,
0xf9, 0x2d, 0x13, 0x66, 0x26, 0xcf, 0x79, 0xa2, 0xeb, 0x51, 0x6c, 0x43, 0x7b, 0xce, 0xa2, 0x2c,
0x58, 0x9c, 0xf1, 0x97, 0x6c, 0xc9, 0xeb, 0xa8, 0x5c, 0x44, 0x3f, 0x86, 0xa1, 0x36, 0xac, 0x30,
0x73, 0xa5, 0x85, 0xed, 0x12, 0xad, 0x1e, 0x9d, 0x3c, 0x3e, 0xce, 0xe3, 0xe3, 0x73, 0xf7, 0x87,
0xa8, 0x5d, 0x67, 0x2f, 0x80, 0xe0, 0x7c, 0xb5, 0xd8, 0xbb, 0xb8, 0xd8, 0x6d, 0x68, 0xd7, 0x15,
0xc1, 0xd7, 0x62, 0xc9, 0x4f, 0x79, 0x66, 0x58, 0x30, 0xc0, 0xef, 0xd3, 0x06, 0x0d, 0xbf, 0x80,
0xbd, 0xb6, 0xd7, 0x75, 0x34, 0xfb, 0xd0, 0x67, 0xc9, 0xa5, 0xbb, 0xe8, 0x8d, 0x5e, 0xc7, 0xd6,
0x75, 0x63, 0xfb, 0x89, 0x00, 0xfd, 0xc6, 0x2e, 0x86, 0x3d, 0x2c, 0xe6, 0xff, 0x2f, 0xb5, 0x26,
0x9d, 0xee, 0x46, 0x3a, 0xae, 0xab, 0xde, 0x1d, 0x57, 0xc3, 0x23, 0x18, 0xb8, 0x4b, 0xf3, 0xd0,
0xbf, 0x87, 0xef, 0xc1, 0xb0, 0xae, 0x7d, 0x68, 0x1c, 0x8f, 0xbe, 0x83, 0x61, 0x2b, 0x0f, 0xba,
0x0b, 0xdb, 0x2f, 0xf9, 0x0f, 0xaf, 0x64, 0x76, 0x33, 0xde, 0xa2, 0x63, 0x18, 0x7c, 0xc5, 0xb4,
0x89, 0x78, 0xc2, 0xc5, 0x15, 0x5f, 0x8c, 0x09, 0xa5, 0x30, 0x6a, 0xec, 0xc5, 0x17, 0xc7, 0x1d,
0xfa, 0x18, 0x86, 0xab, 0x64, 0x2a, 0xd4, 0xa5, 0x3b, 0xe0, 0xbf, 0x10, 0x85, 0x36, 0x63, 0xef,
0xb3, 0xbd, 0xb7, 0x7f, 0x4c, 0xb6, 0xde, 0xde, 0x4e, 0xc8, 0x6f, 0xb7, 0x13, 0xf2, 0xfb, 0xed,
0x84, 0xfc, 0xfc, 0xe7, 0x64, 0x2b, 0xee, 0xe1, 0xd2, 0x3d, 0xff, 0x3b, 0x00, 0x00, 0xff, 0xff,
0x89, 0xd0, 0xaf, 0x8e, 0xf8, 0x07, 0x00, 0x00,
}

View File

@ -51,6 +51,7 @@ message MsgProto {
bytes data = 4; // payload
int64 timestamp = 5; // received timestamp
bool redelivered = 6; // Flag specifying if the message is being redelivered
uint32 redeliveryCount = 7; // Number of times the message has been redelivered (count currently not persisted)
uint32 CRC32 = 10; // optional IEEE CRC32
}

View File

@ -20,17 +20,17 @@ import (
"sync"
"time"
"github.com/nats-io/go-nats"
"github.com/nats-io/go-nats-streaming/pb"
"github.com/nats-io/nats.go"
"github.com/nats-io/nuid"
"github.com/nats-io/stan.go/pb"
)
// Version is the NATS Streaming Go Client version
const Version = "0.4.4"
const Version = "0.6.0"
const (
// DefaultNatsURL is the default URL the client connects to
DefaultNatsURL = "nats://localhost:4222"
DefaultNatsURL = "nats://127.0.0.1:4222"
// DefaultConnectWait is the default timeout used for the connect operation
DefaultConnectWait = 2 * time.Second
// DefaultDiscoverPrefix is the prefix subject used to connect to the NATS Streaming server
@ -48,6 +48,7 @@ const (
// Conn represents a connection to the NATS Streaming subsystem. It can Publish and
// Subscribe to messages within the NATS Streaming cluster.
// The connection is safe to use in multiple Go routines concurrently.
type Conn interface {
// Publish will publish to the cluster and wait for an ACK.
Publish(subject string, data []byte) error
@ -99,7 +100,7 @@ const (
// Errors
var (
ErrConnectReqTimeout = errors.New("stan: connect request timeout")
ErrConnectReqTimeout = errors.New("stan: connect request timeout (possibly wrong cluster ID?)")
ErrCloseReqTimeout = errors.New("stan: close request timeout")
ErrSubReqTimeout = errors.New("stan: subscribe request timeout")
ErrUnsubReqTimeout = errors.New("stan: unsubscribe request timeout")
@ -156,9 +157,12 @@ type Options struct {
// calls block.
MaxPubAcksInflight int
// DEPRECATED: Please use PingInterval instead
PingIterval int
// PingInterval is the interval at which client sends PINGs to the server
// to detect the loss of a connection.
PingIterval int
PingInterval int
// PingMaxOut specifies the maximum number of PINGs without a corresponding
// PONG before declaring the connection permanently lost.
@ -169,16 +173,24 @@ type Options struct {
ConnectionLostCB ConnectionLostHandler
}
// DefaultOptions are the NATS Streaming client's default options
var DefaultOptions = Options{
// GetDefaultOptions returns default configuration options for the client.
func GetDefaultOptions() Options {
return Options{
NatsURL: DefaultNatsURL,
ConnectTimeout: DefaultConnectWait,
AckTimeout: DefaultAckWait,
DiscoverPrefix: DefaultDiscoverPrefix,
MaxPubAcksInflight: DefaultMaxPubAcksInflight,
PingIterval: DefaultPingInterval,
PingInterval: DefaultPingInterval,
PingMaxOut: DefaultPingMaxOut,
}
}
// DEPRECATED: Use GetDefaultOptions() instead.
// DefaultOptions is not safe for use by multiple clients.
// For details see https://github.com/nats-io/nats.go/issues/308.
// DefaultOptions are the NATS Streaming client's default options
var DefaultOptions = GetDefaultOptions()
// Option is a function on the options for a connection.
type Option func(*Options) error
@ -242,11 +254,11 @@ func Pings(interval, maxOut int) Option {
// by the library as milliseconds. If this test boolean is set,
// do not check values.
if !testAllowMillisecInPings {
if interval < 1 || maxOut <= 2 {
if interval < 1 || maxOut < 2 {
return fmt.Errorf("invalid ping values: interval=%v (min>0) maxOut=%v (min=2)", interval, maxOut)
}
}
o.PingIterval = interval
o.PingInterval = interval
o.PingMaxOut = maxOut
return nil
}
@ -279,21 +291,27 @@ type conn struct {
subMap map[string]*subscription
pubAckMap map[string]*ack
pubAckChan chan (struct{})
pubAckCloseChan chan (struct{})
opts Options
nc *nats.Conn
ncOwned bool // NATS Streaming created the connection, so needs to close it.
pubNUID *nuid.NUID // NUID generator for published messages.
connLostCB ConnectionLostHandler
closed bool
ping pingInfo
}
pingMu sync.Mutex
pingSub *nats.Subscription
pingTimer *time.Timer
pingBytes []byte
pingRequests string
pingInbox string
pingInterval time.Duration
pingMaxOut int
pingOut int
// Holds all field related to the client-to-server pings
type pingInfo struct {
mu sync.Mutex
sub *nats.Subscription
timer *time.Timer
proto []byte
requests string
inbox string
interval time.Duration
maxOut int
out int
}
// Closure for ack contexts.
@ -307,7 +325,15 @@ type ack struct {
// Note that clientID can contain only alphanumeric and `-` or `_` characters.
func Connect(stanClusterID, clientID string, options ...Option) (Conn, error) {
// Process Options
c := conn{clientID: clientID, opts: DefaultOptions, connID: []byte(nuid.Next()), pubNUID: nuid.New()}
c := conn{
clientID: clientID,
opts: DefaultOptions,
connID: []byte(nuid.Next()),
pubNUID: nuid.New(),
pubAckMap: make(map[string]*ack),
pubAckCloseChan: make(chan struct{}),
subMap: make(map[string]*subscription),
}
for _, opt := range options {
if err := opt(&c.opts); err != nil {
return nil, err
@ -347,7 +373,8 @@ func Connect(stanClusterID, clientID string, options ...Option) (Conn, error) {
// Prepare a subscription on ping responses, even if we are not
// going to need it, so that if that fails, it fails before initiating
// a connection.
if c.pingSub, err = c.nc.Subscribe(nats.NewInbox(), c.processPingResponse); err != nil {
p := &c.ping
if p.sub, err = c.nc.Subscribe(nats.NewInbox(), c.processPingResponse); err != nil {
c.failConnect(err)
return nil, err
}
@ -359,7 +386,7 @@ func Connect(stanClusterID, clientID string, options ...Option) (Conn, error) {
HeartbeatInbox: hbInbox,
ConnID: c.connID,
Protocol: protocolOne,
PingInterval: int32(c.opts.PingIterval),
PingInterval: int32(c.opts.PingInterval),
PingMaxOut: int32(c.opts.PingMaxOut),
}
b, _ := req.Marshal()
@ -399,11 +426,7 @@ func Connect(stanClusterID, clientID string, options ...Option) (Conn, error) {
c.Close()
return nil, err
}
c.ackSubscription.SetPendingLimits(1024*1024, 32*1024*1024)
c.pubAckMap = make(map[string]*ack)
// Create Subscription map
c.subMap = make(map[string]*subscription)
c.ackSubscription.SetPendingLimits(-1, -1)
c.pubAckChan = make(chan struct{}, c.opts.MaxPubAcksInflight)
@ -423,28 +446,28 @@ func Connect(stanClusterID, clientID string, options ...Option) (Conn, error) {
unsubPingSub = false
// These will be immutable.
c.pingRequests = cr.PingRequests
c.pingInbox = c.pingSub.Subject
p.requests = cr.PingRequests
p.inbox = p.sub.Subject
// In test, it is possible that we get a negative value
// to represent milliseconds.
if testAllowMillisecInPings && cr.PingInterval < 0 {
c.pingInterval = time.Duration(cr.PingInterval*-1) * time.Millisecond
p.interval = time.Duration(cr.PingInterval*-1) * time.Millisecond
} else {
// PingInterval is otherwise assumed to be in seconds.
c.pingInterval = time.Duration(cr.PingInterval) * time.Second
p.interval = time.Duration(cr.PingInterval) * time.Second
}
c.pingMaxOut = int(cr.PingMaxOut)
c.pingBytes, _ = (&pb.Ping{ConnID: c.connID}).Marshal()
p.maxOut = int(cr.PingMaxOut)
p.proto, _ = (&pb.Ping{ConnID: c.connID}).Marshal()
// Set the timer now that we are set. Use lock to create
// synchronization point.
c.pingMu.Lock()
c.pingTimer = time.AfterFunc(c.pingInterval, c.pingServer)
c.pingMu.Unlock()
p.mu.Lock()
p.timer = time.AfterFunc(p.interval, c.pingServer)
p.mu.Unlock()
}
}
if unsubPingSub {
c.pingSub.Unsubscribe()
c.pingSub = nil
p.sub.Unsubscribe()
p.sub = nil
}
return &c, nil
@ -466,24 +489,24 @@ func (sc *conn) failConnect(err error) {
// If the total number is > than the PingMaxOut option, then the connection
// is closed, and connection error callback invoked if one was specified.
func (sc *conn) pingServer() {
sc.pingMu.Lock()
p := &sc.ping
p.mu.Lock()
// In case the timer fired while we were stopping it.
if sc.pingTimer == nil {
sc.pingMu.Unlock()
if p.timer == nil {
p.mu.Unlock()
return
}
sc.pingOut++
if sc.pingOut > sc.pingMaxOut {
sc.pingMu.Unlock()
p.out++
if p.out > p.maxOut {
p.mu.Unlock()
sc.closeDueToPing(ErrMaxPings)
return
}
sc.pingTimer.Reset(sc.pingInterval)
nc := sc.nc
sc.pingMu.Unlock()
// Send the PING now. If the NATS connection is reported closed,
// we are done.
if err := nc.PublishRequest(sc.pingRequests, sc.pingInbox, sc.pingBytes); err == nats.ErrConnectionClosed {
p.timer.Reset(p.interval)
p.mu.Unlock()
// Send the PING now. If the NATS connection is reported closed, we are done.
// sc.nc is immutable and never nil, even if connection is closed.
if err := sc.nc.PublishRequest(p.requests, p.inbox, p.proto); err == nats.ErrConnectionClosed {
sc.closeDueToPing(err)
}
}
@ -509,16 +532,17 @@ func (sc *conn) processPingResponse(m *nats.Msg) {
}
}
// Do not attempt to decrement, simply reset to 0.
sc.pingMu.Lock()
sc.pingOut = 0
sc.pingMu.Unlock()
p := &sc.ping
p.mu.Lock()
p.out = 0
p.mu.Unlock()
}
// Closes a connection and invoke the connection error callback if one
// was registered when the connection was created.
func (sc *conn) closeDueToPing(err error) {
sc.Lock()
if sc.nc == nil {
if sc.closed {
sc.Unlock()
return
}
@ -529,10 +553,8 @@ func (sc *conn) closeDueToPing(err error) {
if sc.ncOwned && !sc.nc.IsClosed() {
sc.nc.Close()
}
// Mark this streaming connection as closed. Do this under pingMu lock.
sc.pingMu.Lock()
sc.nc = nil
sc.pingMu.Unlock()
// Mark this streaming connection as closed.
sc.closed = true
// Capture callback (even though this is immutable).
cb := sc.connLostCB
sc.Unlock()
@ -545,12 +567,13 @@ func (sc *conn) closeDueToPing(err error) {
// Do some cleanup when connection is lost or closed.
// Connection lock is held on entry, and sc.nc is guaranteed not to be nil.
func (sc *conn) cleanupOnClose(err error) {
sc.pingMu.Lock()
if sc.pingTimer != nil {
sc.pingTimer.Stop()
sc.pingTimer = nil
p := &sc.ping
p.mu.Lock()
if p.timer != nil {
p.timer.Stop()
p.timer = nil
}
sc.pingMu.Unlock()
p.mu.Unlock()
// Unsubscribe only if the NATS connection is not already closed
// and we don't own it (otherwise connection is going to be closed
@ -559,8 +582,8 @@ func (sc *conn) cleanupOnClose(err error) {
if sc.hbSubscription != nil {
sc.hbSubscription.Unsubscribe()
}
if sc.pingSub != nil {
sc.pingSub.Unsubscribe()
if p.sub != nil {
p.sub.Unsubscribe()
}
if sc.ackSubscription != nil {
sc.ackSubscription.Unsubscribe()
@ -568,20 +591,34 @@ func (sc *conn) cleanupOnClose(err error) {
}
// Fail all pending pubs
if len(sc.pubAckMap) > 0 {
// Collect only the ones that have a timer that can be stopped.
// All others will be handled either in publishAsync() or their
// timer has already fired.
acks := map[string]*ack{}
for guid, pubAck := range sc.pubAckMap {
if pubAck.t != nil {
pubAck.t.Stop()
}
if pubAck.ah != nil {
pubAck.ah(guid, err)
} else if pubAck.ch != nil {
pubAck.ch <- err
}
if pubAck.t != nil && pubAck.t.Stop() {
delete(sc.pubAckMap, guid)
if len(sc.pubAckChan) > 0 {
<-sc.pubAckChan
acks[guid] = pubAck
}
}
// If we collected any, start a go routine that will do the job.
// We can't do it in place in case user's ackHandler uses the connection.
if len(acks) > 0 {
go func() {
for guid, a := range acks {
if a.ah != nil {
a.ah(guid, ErrConnectionClosed)
} else if a.ch != nil {
a.ch <- ErrConnectionClosed
}
}
}()
}
}
// Prevent publish calls that have passed the connection close check but
// not yet send to pubAckChan to be possibly blocked.
close(sc.pubAckCloseChan)
}
// Close a connection to the stan system.
@ -589,30 +626,24 @@ func (sc *conn) Close() error {
sc.Lock()
defer sc.Unlock()
if sc.nc == nil {
if sc.closed {
// We are already closed.
return nil
}
// Signals we are closed.
sc.closed = true
// Capture for NATS calls below.
nc := sc.nc
if sc.ncOwned {
defer nc.Close()
defer sc.nc.Close()
}
// Now close ourselves.
sc.cleanupOnClose(ErrConnectionClosed)
// Signals we are closed.
// Do this also under pingMu lock so that we don't need
// to grab sc's lock in pingServer.
sc.pingMu.Lock()
sc.nc = nil
sc.pingMu.Unlock()
req := &pb.CloseRequest{ClientID: sc.clientID}
b, _ := req.Marshal()
reply, err := nc.Request(sc.closeRequests, b, sc.opts.ConnectTimeout)
reply, err := sc.nc.Request(sc.closeRequests, b, sc.opts.ConnectTimeout)
if err != nil {
if err == nats.ErrTimeout {
return ErrCloseReqTimeout
@ -636,6 +667,9 @@ func (sc *conn) Close() error {
func (sc *conn) NatsConn() *nats.Conn {
sc.RLock()
nc := sc.nc
if sc.closed {
nc = nil
}
sc.RUnlock()
return nc
}
@ -643,12 +677,8 @@ func (sc *conn) NatsConn() *nats.Conn {
// Process a heartbeat from the NATS Streaming cluster
func (sc *conn) processHeartBeat(m *nats.Msg) {
// No payload assumed, just reply.
sc.RLock()
nc := sc.nc
sc.RUnlock()
if nc != nil {
nc.Publish(m.Reply, nil)
}
// sc.nc is immutable and never nil, even if connection is closed.
sc.nc.Publish(m.Reply, nil)
}
// Process an ack from the NATS Streaming cluster
@ -698,7 +728,7 @@ func (sc *conn) PublishAsync(subject string, data []byte, ah AckHandler) (string
func (sc *conn) publishAsync(subject string, data []byte, ah AckHandler, ch chan error) (string, error) {
a := &ack{ah: ah, ch: ch}
sc.Lock()
if sc.nc == nil {
if sc.closed {
sc.Unlock()
return "", ErrConnectionClosed
}
@ -717,18 +747,28 @@ func (sc *conn) publishAsync(subject string, data []byte, ah AckHandler, ch chan
// snapshot
ackSubject := sc.ackSubject
ackTimeout := sc.opts.AckTimeout
pac := sc.pubAckChan
nc := sc.nc
sc.Unlock()
// Use the buffered channel to control the number of outstanding acks.
pac <- struct{}{}
select {
case sc.pubAckChan <- struct{}{}:
default:
// It seems faster to first try to send to pubAckChan and only if
// it fails to retry with the check on pubAckCloseChan than having
// simply only the select with the 2 cases.
select {
case sc.pubAckChan <- struct{}{}:
case <-sc.pubAckCloseChan:
return "", ErrConnectionClosed
}
}
err := nc.PublishRequest(subj, ackSubject, b)
// sc.nc is immutable and never nil once connection is created.
err := sc.nc.PublishRequest(subj, ackSubject, b)
// Setup the timer for expiration.
sc.Lock()
if err != nil || sc.nc == nil {
if err != nil || sc.closed {
sc.Unlock()
// If we got and error on publish or the connection has been closed,
// we need to return an error only if:
@ -797,8 +837,7 @@ func (sc *conn) processMsg(raw *nats.Msg) {
var sub *subscription
// Lookup the subscription
sc.RLock()
nc := sc.nc
isClosed := nc == nil
isClosed := sc.closed
if !isClosed {
sub = sc.subMap[raw.Subject]
}
@ -825,10 +864,11 @@ func (sc *conn) processMsg(raw *nats.Msg) {
}
// Process auto-ack
if !isManualAck && nc != nil {
if !isManualAck {
ack := &pb.Ack{Subject: msg.Subject, Sequence: msg.Sequence}
b, _ := ack.Marshal()
// FIXME(dlc) - Async error handler? Retry?
nc.Publish(ackSubject, b)
// sc.nc is immutable and never nil once connection is created.
sc.nc.Publish(ackSubject, b)
}
}

View File

@ -11,7 +11,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
// Package stan is a Go client for the NATS Streaming messaging system (https://nats.io).
package stan
import (
@ -19,8 +18,8 @@ import (
"sync"
"time"
"github.com/nats-io/go-nats"
"github.com/nats-io/go-nats-streaming/pb"
"github.com/nats-io/nats.go"
"github.com/nats-io/stan.go/pb"
)
const (
@ -40,6 +39,7 @@ type Msg struct {
// Subscription represents a subscription within the NATS Streaming cluster. Subscriptions
// will be rate matched and follow at-least once delivery semantics.
// The subscription is safe to use in multiple Go routines concurrently.
type Subscription interface {
// Unsubscribe removes interest in the subscription.
// For durables, it means that the durable interest is also removed from
@ -56,7 +56,7 @@ type Subscription interface {
// These functions have been added for expert-users that need to get details
// about the low level NATS Subscription used internally to receive messages
// for this streaming subscription. They are documented in the Go client
// library: https://godoc.org/github.com/nats-io/go-nats#Subscription.ClearMaxPending
// library: https://godoc.org/github.com/nats-io/nats.go#Subscription.ClearMaxPending
// ClearMaxPending resets the maximums seen so far.
ClearMaxPending() error
@ -238,25 +238,27 @@ func (sc *conn) subscribe(subject, qgroup string, cb MsgHandler, options ...Subs
}
}
sc.Lock()
if sc.nc == nil {
if sc.closed {
sc.Unlock()
return nil, ErrConnectionClosed
}
// Register subscription.
sc.subMap[sub.inbox] = sub
nc := sc.nc
sc.Unlock()
// Hold lock throughout.
sub.Lock()
defer sub.Unlock()
// sc.nc is immutable and never nil once connection is created.
// Listen for actual messages.
nsub, err := nc.Subscribe(sub.inbox, sc.processMsg)
nsub, err := sc.nc.Subscribe(sub.inbox, sc.processMsg)
if err != nil {
return nil, err
}
nsub.SetPendingLimits(-1, -1)
sub.inboxSub = nsub
// Create a subscription request
@ -281,7 +283,7 @@ func (sc *conn) subscribe(subject, qgroup string, cb MsgHandler, options ...Subs
}
b, _ := sr.Marshal()
reply, err := nc.Request(sc.subRequests, b, sc.opts.ConnectTimeout)
reply, err := sc.nc.Request(sc.subRequests, b, sc.opts.ConnectTimeout)
if err != nil {
sub.inboxSub.Unsubscribe()
if err == nats.ErrTimeout {
@ -407,7 +409,7 @@ func (sub *subscription) closeOrUnsubscribe(doClose bool) error {
sub.Unlock()
sc.Lock()
if sc.nc == nil {
if sc.closed {
sc.Unlock()
return ErrConnectionClosed
}
@ -421,19 +423,17 @@ func (sub *subscription) closeOrUnsubscribe(doClose bool) error {
return ErrNoServerSupport
}
}
// Snapshot connection to avoid data race, since the connection may be
// closing while we try to send the request
nc := sc.nc
sc.Unlock()
// sc.nc is immutable and never nil once connection is created.
usr := &pb.UnsubscribeRequest{
ClientID: sc.clientID,
Subject: sub.subject,
Inbox: sub.ackInbox,
}
b, _ := usr.Marshal()
reply, err := nc.Request(reqSubject, b, sc.opts.ConnectTimeout)
reply, err := sc.nc.Request(reqSubject, b, sc.opts.ConnectTimeout)
if err != nil {
if err == nats.ErrTimeout {
if doClose {
@ -485,16 +485,15 @@ func (msg *Msg) Ack() error {
if sc == nil {
return ErrBadSubscription
}
// Get nc from the connection (needs locking to avoid race)
sc.RLock()
nc := sc.nc
sc.RUnlock()
if nc == nil {
return ErrBadConnection
}
// sc.nc is immutable and never nil once connection is created.
// Ack here.
ack := &pb.Ack{Subject: msg.Subject, Sequence: msg.Sequence}
b, _ := ack.Marshal()
return nc.Publish(ackSubject, b)
err := sc.nc.Publish(ackSubject, b)
if err == nats.ErrConnectionClosed {
return ErrBadConnection
}
return err
}

View File

@ -7,7 +7,7 @@ import (
"sync"
"time"
stan "github.com/nats-io/go-nats-streaming"
stan "github.com/nats-io/stan.go"
"github.com/openfaas/faas/gateway/queue"
)