Repository: rapidloop/nrpc Branch: master Commit: 8f47f6a864d1 Files: 42 Total size: 280.6 KB Directory structure: gitextract_mwgdltlr/ ├── .gitignore ├── .travis.yml ├── AUTHORS ├── LICENSE ├── README.md ├── alloptions_test.go ├── examples/ │ ├── alloptions/ │ │ ├── alloptions.nrpc.go │ │ ├── alloptions.pb.go │ │ ├── alloptions.proto │ │ ├── alloptions_test.go │ │ ├── main.go │ │ └── testrunner_test.go │ ├── helloworld/ │ │ ├── greeter_client/ │ │ │ └── main.go │ │ ├── greeter_server/ │ │ │ ├── main.go │ │ │ ├── main_test.go │ │ │ └── testrunner_test.go │ │ └── helloworld/ │ │ ├── helloworld.go │ │ ├── helloworld.nrpc.go │ │ ├── helloworld.pb.go │ │ └── helloworld.proto │ ├── metrics_helloworld/ │ │ ├── helloworld/ │ │ │ ├── helloworld.go │ │ │ ├── helloworld.nrpc.go │ │ │ ├── helloworld.pb.go │ │ │ └── helloworld.proto │ │ ├── metrics_greeter_client/ │ │ │ └── main.go │ │ └── metrics_greeter_server/ │ │ └── main.go │ └── nooption/ │ ├── nooption.go │ ├── nooption.nrpc.go │ ├── nooption.pb.go │ └── nooption.proto ├── go.mod ├── go.sum ├── helloworld_test.go ├── nrpc.go ├── nrpc.pb.go ├── nrpc.proto ├── nrpc_test.go ├── nrpc_test.proto ├── nrpcpb_test.go ├── protoc-gen-nrpc/ │ ├── main.go │ └── tmpl.go └── testrunner_test.go ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ examples/helloworld/greeter_client/greeter_client examples/helloworld/greeter_server/greeter_server examples/alloptions/alloptions ================================================ FILE: .travis.yml ================================================ language: go go: - 1.13 before_script: - curl -sSL https://github.com/google/protobuf/releases/download/v3.12.1/protoc-3.12.1-linux-x86_64.zip -o protoc.zip - sudo unzip -d /usr/local protoc.zip - sudo chmod a+x /usr/local/bin/protoc - sudo chmod -R a+rx /usr/local/include/google - go get google.golang.org/protobuf/cmd/protoc-gen-go - go install google.golang.org/protobuf/cmd/protoc-gen-go script: - go test ./... ================================================ FILE: AUTHORS ================================================ RapidLoop, Inc. Christophe de Vienne, Orus.io ================================================ FILE: LICENSE ================================================ 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. ================================================ FILE: README.md ================================================ # nRPC [![Build Status](https://travis-ci.org/nats-rpc/nrpc.svg?branch=master)](https://travis-ci.org/nats-rpc/nrpc) nRPC is an RPC framework like [gRPC](https://grpc.io/), but for [NATS](https://nats.io/). It can generate a Go client and server from the same .proto file that you'd use to generate gRPC clients and servers. The server is generated as a NATS [MsgHandler](https://godoc.org/github.com/nats-io/nats.go#MsgHandler). The [Specifications](https://github.com/nats-rpc/nrpc/wiki/Specifications) describes how nRPC translates protobuf services and methods into NATS patterns. ## Why NATS? Doing RPC over NATS' [request-response model](http://nats.io/documentation/concepts/nats-req-rep/) has some advantages over a gRPC model: - **Minimal service discovery**: The clients and servers only need to know the endpoints of a NATS cluster. The clients do not need to discover the endpoints of individual services they depend on. - **Load balancing without load balancers**: Stateless microservices can be hosted redundantly and connected to the same NATS cluster. The incoming requests can then be random-routed among these using NATS [queueing](http://nats.io/documentation/concepts/nats-queueing/). There is no need to setup a (high availability) load balancer per microservice. The lunch is not always free, however. At scale, the NATS cluster itself can become a bottleneck. Features of gRPC like streaming and advanced auth are not available. Still, NATS - and nRPC - offer much lower operational complexity if your scale and requirements fit. At RapidLoop, we use this model for our [OpsDash](https://www.opsdash.com) SaaS product in production and are quite happy with it. nRPC is the third iteration of an internal library. ## Overview nRPC comes with a protobuf compiler plugin `protoc-gen-nrpc`, which generates Go code from a .proto file. Given a .proto file like [helloworld.proto](https://github.com/grpc/grpc-go/blob/master/examples/helloworld/helloworld/helloworld.proto), the usage is like this: ``` $ ls helloworld.proto $ protoc --go_out=. --nrpc_out=. helloworld.proto $ ls helloworld.nrpc.go helloworld.pb.go helloworld.proto ``` The .pb.go file, which contains the definitions for the message classes, is generated by the standard Go plugin for protoc. The .nrpc.go file, which contains the definitions for a client, a server interface, and a NATS handler is generated by the nRPC plugin. Have a look at the generated and example files: - the service definition [helloworld.proto](https://github.com/nats-rpc/nrpc/tree/master/examples/helloworld/helloworld/helloworld.proto) - the generated nrpc go file [helloworld.nrpc.go](https://github.com/nats-rpc/nrpc/tree/master/examples/helloworld/helloworld/helloworld.nrpc.go) - an example server [greeter_server/main.go](https://github.com/nats-rpc/nrpc/tree/master/examples/helloworld/greeter_server/main.go) - an example client [greeter_client/main.go](https://github.com/nats-rpc/nrpc/tree/master/examples/helloworld/greeter_client/main.go) ### How It Works The .proto file defines messages (like HelloRequest and HelloReply in the example) and services (Greeter) that have methods (SayHello). The messages are generated as Go structs by the regular Go protobuf compiler plugin and gets written out to \*.pb.go files. For the rest, nRPC generates three logical pieces. The first is a Go interface type (GreeterServer) which your actual microservice code should implement: ``` // This is what is contained in the .proto file service Greeter { rpc SayHello (HelloRequest) returns (HelloReply) {} } // This is the generated interface which you've to implement type GreeterServer interface { SayHello(ctx context.Context, req HelloRequest) (resp HelloReply, err error) } ``` The second is a client (GreeterClient struct). This struct has methods with appropriate types, that correspond to the service definition. The client code will marshal and wrap the request object (HelloRequest) and do a NATS `Request`. ``` // The client is associated with a NATS connection. func NewGreeterClient(nc *nats.Conn) *GreeterClient {...} // And has properly typed methods that will marshal and perform a NATS request. func (c *GreeterClient) SayHello(req HelloRequest) (resp HelloReply, err error) {...} ``` The third and final piece is the handler (GreeterHandler). Given a NATS connection and a server implementation, it can accept NATS requests in the format sent by the client above. It should be installed as a message handler for a particular NATS subject (defaults to the name of the service) using the NATS Subscribe() or QueueSubscribe() methods. It will invoke the appropriate method of the GreeterServer interface upon receiving the appropriate request. ``` // A handler is associated with a NATS connection and a server implementation. func NewGreeterHandler(ctx context.Context, nc *nats.Conn, s GreeterServer) *GreeterHandler {...} // It has a method that can (should) be used as a NATS message handler. func (h *GreeterHandler) Handler(msg *nats.Msg) {...} ``` Standing up a microservice involves: - writing the .proto service definition file - generating the \*.pb.go and \*.nrpc.go files - implementing the server interface - writing a main app that will connect to NATS and start the handler ([see example](https://github.com/nats-rpc/nrpc/blob/master/examples/helloworld/greeter_server/main.go)) To call the service: - import the package that contains the generated *.nrpc.go files - in the client code, connect to NATS - create a Caller object and call the methods as necessary ([see example](https://github.com/nats-rpc/nrpc/blob/master/examples/helloworld/greeter_client/main.go)) ## Features The following wiki pages describe nRPC features in more detail: - [Load Balancing](https://github.com/nats-rpc/nrpc/wiki/Load-Balancing) - [Metrics Instrumentation](https://github.com/nats-rpc/nrpc/wiki/Metrics-Instrumentation) using Prometheus ## Installation nRPC needs Go 1.11 or higher. $GOPATH/bin needs to be in $PATH for the protoc invocation to work. To generate code, you need the protobuf compiler (which you can install from [here](https://github.com/google/protobuf/releases)) and the nRPC protoc plugin. To install the nRPC protoc plugin: ``` $ go install github.com/nats-rpc/nrpc/protoc-gen-nrpc@latest ``` To build and run the example greeter_server: ``` $ go install github.com/nats-rpc/nrpc/examples/helloworld/greeter_server@latest $ greeter_server server is running, ^C quits. ``` To build and run the example greeter_client: ``` $ go install github.com/nats-rpc/nrpc/examples/helloworld/greeter_client@latest $ greeter_client Greeting: Hello world $ ``` ## Documentation To learn more about describing gRPC services using .proto files, see [here](https://grpc.io/docs/guides/concepts.html). To learn more about NATS, start with their [website](https://nats.io/). To learn more about nRPC, um, read the source code. ## Status nRPC is in alpha. This means that it will work, but APIs may change without notice. Currently there is support only for Go clients and servers. Built by RapidLoop. Released under Apache 2.0 license. ================================================ FILE: alloptions_test.go ================================================ package nrpc import ( //"bytes" //"os" "os/exec" "testing" //"time" ) func TestAllOptionsExample(t *testing.T) { // make sure protoc-gen-nrpc is up to date installGenRPC := exec.Command("go", "install", "./protoc-gen-nrpc") if out, err := installGenRPC.CombinedOutput(); err != nil { t.Fatal("Install protoc-gen-nrpc failed", err, ":\n", string(out)) } // generate the sources generate := exec.Command("go", "generate", "./examples/alloptions") if out, err := generate.CombinedOutput(); err != nil { t.Fatal("Generate failed", err, ":\n", string(out)) } // build build := exec.Command("go", "build", "-o", "./examples/alloptions/alloptions", "./examples/alloptions") if out, err := build.CombinedOutput(); err != nil { t.Fatal("Buid failed", err, string(out)) } } ================================================ FILE: examples/alloptions/alloptions.nrpc.go ================================================ // This code was autogenerated from alloptions.proto, do not edit. package main import ( "context" "log" "time" "google.golang.org/protobuf/proto" "github.com/nats-io/nats.go" github_com_nats_rpc_nrpc "github.com/nats-rpc/nrpc" "github.com/nats-rpc/nrpc" ) // SvcCustomSubjectServer is the interface that providers of the service // SvcCustomSubject should implement. type SvcCustomSubjectServer interface { MtSimpleReply(ctx context.Context, req *StringArg) (*SimpleStringReply, error) MtVoidReply(ctx context.Context, req *StringArg) (error) MtStreamedReply(ctx context.Context, req *StringArg, pushRep func(*SimpleStringReply)) (error) MtVoidReqStreamedReply(ctx context.Context, pushRep func(*SimpleStringReply)) (error) } // SvcCustomSubjectHandler provides a NATS subscription handler that can serve a // subscription using a given SvcCustomSubjectServer implementation. type SvcCustomSubjectHandler struct { ctx context.Context workers *nrpc.WorkerPool nc nrpc.NatsConn server SvcCustomSubjectServer encodings []string } func NewSvcCustomSubjectHandler(ctx context.Context, nc nrpc.NatsConn, s SvcCustomSubjectServer) *SvcCustomSubjectHandler { return &SvcCustomSubjectHandler{ ctx: ctx, nc: nc, server: s, encodings: []string{"protobuf"}, } } func NewSvcCustomSubjectConcurrentHandler(workers *nrpc.WorkerPool, nc nrpc.NatsConn, s SvcCustomSubjectServer) *SvcCustomSubjectHandler { return &SvcCustomSubjectHandler{ workers: workers, nc: nc, server: s, } } // SetEncodings sets the output encodings when using a '*Publish' function func (h *SvcCustomSubjectHandler) SetEncodings(encodings []string) { h.encodings = encodings } func (h *SvcCustomSubjectHandler) Subject() string { return "root.*.custom_subject.>" } func (h *SvcCustomSubjectHandler) MtNoRequestPublish(pkginstance string, msg *SimpleStringReply) error { for _, encoding := range h.encodings { rawMsg, err := nrpc.Marshal(encoding, msg) if err != nil { log.Printf("SvcCustomSubjectHandler.MtNoRequestPublish: error marshaling the message: %s", err) return err } subject := "root." + pkginstance + "."+ "custom_subject."+ "mtnorequest" if encoding != "protobuf" { subject += "." + encoding } if err := h.nc.Publish(subject, rawMsg); err != nil { return err } } return nil } func (h *SvcCustomSubjectHandler) Handler(msg *nats.Msg) { var ctx context.Context if h.workers != nil { ctx = h.workers.Context } else { ctx = h.ctx } request := nrpc.NewRequest(ctx, h.nc, msg.Subject, msg.Reply) // extract method name & encoding from subject pkgParams, _, name, tail, err := nrpc.ParseSubject( "root", 1, "custom_subject", 0, msg.Subject) if err != nil { log.Printf("SvcCustomSubjectHanlder: SvcCustomSubject subject parsing failed: %v", err) return } request.MethodName = name request.SubjectTail = tail request.SetPackageParam("instance", pkgParams[0]) // call handler and form response var immediateError *nrpc.Error switch name { case "mt_simple_reply": _, request.Encoding, err = nrpc.ParseSubjectTail(0, request.SubjectTail) if err != nil { log.Printf("MtSimpleReplyHanlder: MtSimpleReply subject parsing failed: %v", err) break } var req StringArg if err := nrpc.Unmarshal(request.Encoding, msg.Data, &req); err != nil { log.Printf("MtSimpleReplyHandler: MtSimpleReply request unmarshal failed: %v", err) immediateError = &nrpc.Error{ Type: nrpc.Error_CLIENT, Message: "bad request received: " + err.Error(), } } else { request.Handler = func(ctx context.Context)(proto.Message, error){ innerResp, err := h.server.MtSimpleReply(ctx, &req) if err != nil { return nil, err } return innerResp, err } } case "mtvoidreply": _, request.Encoding, err = nrpc.ParseSubjectTail(0, request.SubjectTail) if err != nil { log.Printf("MtVoidReplyHanlder: MtVoidReply subject parsing failed: %v", err) break } var req StringArg if err := nrpc.Unmarshal(request.Encoding, msg.Data, &req); err != nil { log.Printf("MtVoidReplyHandler: MtVoidReply request unmarshal failed: %v", err) immediateError = &nrpc.Error{ Type: nrpc.Error_CLIENT, Message: "bad request received: " + err.Error(), } } else { request.Handler = func(ctx context.Context)(proto.Message, error){ var innerResp = &nrpc.Void{} err := h.server.MtVoidReply(ctx, &req) if err != nil { return nil, err } return innerResp, err } } case "mtnorequest": // MtNoRequest is a no-request method. Ignore it. return case "mtstreamedreply": _, request.Encoding, err = nrpc.ParseSubjectTail(0, request.SubjectTail) if err != nil { log.Printf("MtStreamedReplyHanlder: MtStreamedReply subject parsing failed: %v", err) break } var req StringArg if err := nrpc.Unmarshal(request.Encoding, msg.Data, &req); err != nil { log.Printf("MtStreamedReplyHandler: MtStreamedReply request unmarshal failed: %v", err) immediateError = &nrpc.Error{ Type: nrpc.Error_CLIENT, Message: "bad request received: " + err.Error(), } } else { request.EnableStreamedReply() request.Handler = func(ctx context.Context)(proto.Message, error){ err := h.server.MtStreamedReply(ctx, &req, func(rep *SimpleStringReply){ request.SendStreamReply(rep) }) return nil, err } } case "mtvoidreqstreamedreply": _, request.Encoding, err = nrpc.ParseSubjectTail(0, request.SubjectTail) if err != nil { log.Printf("MtVoidReqStreamedReplyHanlder: MtVoidReqStreamedReply subject parsing failed: %v", err) break } var req github_com_nats_rpc_nrpc.Void if err := nrpc.Unmarshal(request.Encoding, msg.Data, &req); err != nil { log.Printf("MtVoidReqStreamedReplyHandler: MtVoidReqStreamedReply request unmarshal failed: %v", err) immediateError = &nrpc.Error{ Type: nrpc.Error_CLIENT, Message: "bad request received: " + err.Error(), } } else { request.EnableStreamedReply() request.Handler = func(ctx context.Context)(proto.Message, error){ err := h.server.MtVoidReqStreamedReply(ctx, func(rep *SimpleStringReply){ request.SendStreamReply(rep) }) return nil, err } } default: log.Printf("SvcCustomSubjectHandler: unknown name %q", name) immediateError = &nrpc.Error{ Type: nrpc.Error_CLIENT, Message: "unknown name: " + name, } } if immediateError == nil { if h.workers != nil { // Try queuing the request if err := h.workers.QueueRequest(request); err != nil { log.Printf("nrpc: Error queuing the request: %s", err) } } else { // Run the handler synchronously request.RunAndReply() } } if immediateError != nil { if err := request.SendReply(nil, immediateError); err != nil { log.Printf("SvcCustomSubjectHandler: SvcCustomSubject handler failed to publish the response: %s", err) } } else { } } type SvcCustomSubjectClient struct { nc nrpc.NatsConn PkgSubject string PkgParaminstance string Subject string Encoding string Timeout time.Duration } func NewSvcCustomSubjectClient(nc nrpc.NatsConn, pkgParaminstance string) *SvcCustomSubjectClient { return &SvcCustomSubjectClient{ nc: nc, PkgSubject: "root", PkgParaminstance: pkgParaminstance, Subject: "custom_subject", Encoding: "protobuf", Timeout: 5 * time.Second, } } func (c *SvcCustomSubjectClient) MtSimpleReply(req *StringArg) (*SimpleStringReply, error) { subject := c.PkgSubject + "." + c.PkgParaminstance + "." + c.Subject + "." + "mt_simple_reply" // call var resp = SimpleStringReply{} if err := nrpc.Call(req, &resp, c.nc, subject, c.Encoding, c.Timeout); err != nil { return nil, err } return &resp, nil } func (c *SvcCustomSubjectClient) MtSimpleReplyPoll(req *StringArg,maxreplies int, cb func (*SimpleStringReply) error, ) (error) { subject := c.PkgSubject + "." + c.PkgParaminstance + "." + c.Subject + "." + "mt_simple_reply" var resp SimpleStringReply err := nrpc.Poll(req, &resp, c.nc, subject, c.Encoding, c.Timeout, maxreplies, func() error { return cb(&resp) }, ) if err != nil { return err } return nil } func (c *SvcCustomSubjectClient) MtVoidReply(req *StringArg) (error) { subject := c.PkgSubject + "." + c.PkgParaminstance + "." + c.Subject + "." + "mtvoidreply" // call var resp = github_com_nats_rpc_nrpc.Void{} if err := nrpc.Call(req, &resp, c.nc, subject, c.Encoding, c.Timeout); err != nil { return err } return nil } func (c *SvcCustomSubjectClient) MtNoRequestSubject( ) string { subject := c.PkgSubject + "." + c.PkgParaminstance + "." + c.Subject + "." + "mtnorequest" if c.Encoding != "protobuf" { subject += "." + c.Encoding } return subject } type SvcCustomSubjectMtNoRequestSubscription struct { *nats.Subscription encoding string } func (s *SvcCustomSubjectMtNoRequestSubscription) Next(timeout time.Duration) (next SimpleStringReply, err error) { msg, err := s.Subscription.NextMsg(timeout) if err != nil { return } err = nrpc.Unmarshal(s.encoding, msg.Data, &next) return } func (c *SvcCustomSubjectClient) MtNoRequestSubscribeSync( ) (sub *SvcCustomSubjectMtNoRequestSubscription, err error) { subject := c.MtNoRequestSubject( ) natsSub, err := c.nc.SubscribeSync(subject) if err != nil { return } sub = &SvcCustomSubjectMtNoRequestSubscription{natsSub, c.Encoding} return } func (c *SvcCustomSubjectClient) MtNoRequestSubscribe( handler func (*SimpleStringReply), ) (sub *nats.Subscription, err error) { subject := c.MtNoRequestSubject( ) sub, err = c.nc.Subscribe(subject, func(msg *nats.Msg){ var pmsg SimpleStringReply err := nrpc.Unmarshal(c.Encoding, msg.Data, &pmsg) if err != nil { log.Printf("SvcCustomSubjectClient.MtNoRequestSubscribe: Error decoding, %s", err) return } handler(&pmsg) }) return } func (c *SvcCustomSubjectClient) MtNoRequestSubscribeChan( ) (<-chan *SimpleStringReply, *nats.Subscription, error) { ch := make(chan *SimpleStringReply) sub, err := c.MtNoRequestSubscribe(func (msg *SimpleStringReply) { ch <- msg }) return ch, sub, err } func (c *SvcCustomSubjectClient) MtStreamedReply( ctx context.Context, req *StringArg, cb func (context.Context, *SimpleStringReply), ) error { subject := c.PkgSubject + "." + c.PkgParaminstance + "." + c.Subject + "." + "mtstreamedreply" sub, err := nrpc.StreamCall(ctx, c.nc, subject, req, c.Encoding, c.Timeout) if err != nil { return err } var res SimpleStringReply for { err = sub.Next(&res) if err != nil { break } cb(ctx, &res) } if err == nrpc.ErrEOS { err = nil } return err } func (c *SvcCustomSubjectClient) MtVoidReqStreamedReply( ctx context.Context, cb func (context.Context, *SimpleStringReply), ) error { subject := c.PkgSubject + "." + c.PkgParaminstance + "." + c.Subject + "." + "mtvoidreqstreamedreply" sub, err := nrpc.StreamCall(ctx, c.nc, subject, &nrpc.Void{}, c.Encoding, c.Timeout) if err != nil { return err } var res SimpleStringReply for { err = sub.Next(&res) if err != nil { break } cb(ctx, &res) } if err == nrpc.ErrEOS { err = nil } return err } // SvcSubjectParamsServer is the interface that providers of the service // SvcSubjectParams should implement. type SvcSubjectParamsServer interface { MtWithSubjectParams(ctx context.Context, mp1 string, mp2 string) (*SimpleStringReply, error) MtStreamedReplyWithSubjectParams(ctx context.Context, mp1 string, mp2 string, pushRep func(*SimpleStringReply)) (error) MtNoReply(ctx context.Context) } // SvcSubjectParamsHandler provides a NATS subscription handler that can serve a // subscription using a given SvcSubjectParamsServer implementation. type SvcSubjectParamsHandler struct { ctx context.Context workers *nrpc.WorkerPool nc nrpc.NatsConn server SvcSubjectParamsServer encodings []string } func NewSvcSubjectParamsHandler(ctx context.Context, nc nrpc.NatsConn, s SvcSubjectParamsServer) *SvcSubjectParamsHandler { return &SvcSubjectParamsHandler{ ctx: ctx, nc: nc, server: s, encodings: []string{"protobuf"}, } } func NewSvcSubjectParamsConcurrentHandler(workers *nrpc.WorkerPool, nc nrpc.NatsConn, s SvcSubjectParamsServer) *SvcSubjectParamsHandler { return &SvcSubjectParamsHandler{ workers: workers, nc: nc, server: s, } } // SetEncodings sets the output encodings when using a '*Publish' function func (h *SvcSubjectParamsHandler) SetEncodings(encodings []string) { h.encodings = encodings } func (h *SvcSubjectParamsHandler) Subject() string { return "root.*.svcsubjectparams.*.>" } func (h *SvcSubjectParamsHandler) MtNoRequestWParamsPublish(pkginstance string, svcclientid string, mtmp1 string, msg *SimpleStringReply) error { for _, encoding := range h.encodings { rawMsg, err := nrpc.Marshal(encoding, msg) if err != nil { log.Printf("SvcSubjectParamsHandler.MtNoRequestWParamsPublish: error marshaling the message: %s", err) return err } subject := "root." + pkginstance + "."+ "svcsubjectparams." + svcclientid + "."+ "mtnorequestwparams" + "." + mtmp1 if encoding != "protobuf" { subject += "." + encoding } if err := h.nc.Publish(subject, rawMsg); err != nil { return err } } return nil } func (h *SvcSubjectParamsHandler) Handler(msg *nats.Msg) { var ctx context.Context if h.workers != nil { ctx = h.workers.Context } else { ctx = h.ctx } request := nrpc.NewRequest(ctx, h.nc, msg.Subject, msg.Reply) // extract method name & encoding from subject pkgParams, svcParams, name, tail, err := nrpc.ParseSubject( "root", 1, "svcsubjectparams", 1, msg.Subject) if err != nil { log.Printf("SvcSubjectParamsHanlder: SvcSubjectParams subject parsing failed: %v", err) return } request.MethodName = name request.SubjectTail = tail request.SetPackageParam("instance", pkgParams[0]) request.SetServiceParam("clientid", svcParams[0]) // call handler and form response var immediateError *nrpc.Error switch name { case "mtwithsubjectparams": var mtParams []string mtParams, request.Encoding, err = nrpc.ParseSubjectTail(2, request.SubjectTail) if err != nil { log.Printf("MtWithSubjectParamsHanlder: MtWithSubjectParams subject parsing failed: %v", err) break } var req github_com_nats_rpc_nrpc.Void if err := nrpc.Unmarshal(request.Encoding, msg.Data, &req); err != nil { log.Printf("MtWithSubjectParamsHandler: MtWithSubjectParams request unmarshal failed: %v", err) immediateError = &nrpc.Error{ Type: nrpc.Error_CLIENT, Message: "bad request received: " + err.Error(), } } else { request.Handler = func(ctx context.Context)(proto.Message, error){ innerResp, err := h.server.MtWithSubjectParams(ctx, mtParams[0], mtParams[1]) if err != nil { return nil, err } return innerResp, err } } case "mtstreamedreplywithsubjectparams": var mtParams []string mtParams, request.Encoding, err = nrpc.ParseSubjectTail(2, request.SubjectTail) if err != nil { log.Printf("MtStreamedReplyWithSubjectParamsHanlder: MtStreamedReplyWithSubjectParams subject parsing failed: %v", err) break } var req github_com_nats_rpc_nrpc.Void if err := nrpc.Unmarshal(request.Encoding, msg.Data, &req); err != nil { log.Printf("MtStreamedReplyWithSubjectParamsHandler: MtStreamedReplyWithSubjectParams request unmarshal failed: %v", err) immediateError = &nrpc.Error{ Type: nrpc.Error_CLIENT, Message: "bad request received: " + err.Error(), } } else { request.EnableStreamedReply() request.Handler = func(ctx context.Context)(proto.Message, error){ err := h.server.MtStreamedReplyWithSubjectParams(ctx, mtParams[0], mtParams[1], func(rep *SimpleStringReply){ request.SendStreamReply(rep) }) return nil, err } } case "mtnoreply": request.NoReply = true _, request.Encoding, err = nrpc.ParseSubjectTail(0, request.SubjectTail) if err != nil { log.Printf("MtNoReplyHanlder: MtNoReply subject parsing failed: %v", err) break } var req github_com_nats_rpc_nrpc.Void if err := nrpc.Unmarshal(request.Encoding, msg.Data, &req); err != nil { log.Printf("MtNoReplyHandler: MtNoReply request unmarshal failed: %v", err) immediateError = &nrpc.Error{ Type: nrpc.Error_CLIENT, Message: "bad request received: " + err.Error(), } } else { request.Handler = func(ctx context.Context)(proto.Message, error){var innerResp = &nrpc.NoReply{} h.server.MtNoReply(ctx) if err != nil { return nil, err } return innerResp, err } } case "mtnorequestwparams": // MtNoRequestWParams is a no-request method. Ignore it. return default: log.Printf("SvcSubjectParamsHandler: unknown name %q", name) immediateError = &nrpc.Error{ Type: nrpc.Error_CLIENT, Message: "unknown name: " + name, } } if immediateError == nil { if h.workers != nil { // Try queuing the request if err := h.workers.QueueRequest(request); err != nil { log.Printf("nrpc: Error queuing the request: %s", err) } } else { // Run the handler synchronously request.RunAndReply() } } if immediateError != nil { if err := request.SendReply(nil, immediateError); err != nil { log.Printf("SvcSubjectParamsHandler: SvcSubjectParams handler failed to publish the response: %s", err) } } else { } } type SvcSubjectParamsClient struct { nc nrpc.NatsConn PkgSubject string PkgParaminstance string Subject string SvcParamclientid string Encoding string Timeout time.Duration } func NewSvcSubjectParamsClient(nc nrpc.NatsConn, pkgParaminstance string, svcParamclientid string) *SvcSubjectParamsClient { return &SvcSubjectParamsClient{ nc: nc, PkgSubject: "root", PkgParaminstance: pkgParaminstance, Subject: "svcsubjectparams", SvcParamclientid: svcParamclientid, Encoding: "protobuf", Timeout: 5 * time.Second, } } func (c *SvcSubjectParamsClient) MtWithSubjectParams(mp1 string, mp2 string, ) (*SimpleStringReply, error) { subject := c.PkgSubject + "." + c.PkgParaminstance + "." + c.Subject + "." + c.SvcParamclientid + "." + "mtwithsubjectparams" + "." + mp1 + "." + mp2 // call var req = &github_com_nats_rpc_nrpc.Void{} var resp = SimpleStringReply{} if err := nrpc.Call(req, &resp, c.nc, subject, c.Encoding, c.Timeout); err != nil { return nil, err } return &resp, nil } func (c *SvcSubjectParamsClient) MtStreamedReplyWithSubjectParams( ctx context.Context,mp1 string,mp2 string, cb func (context.Context, *SimpleStringReply), ) error { subject := c.PkgSubject + "." + c.PkgParaminstance + "." + c.Subject + "." + c.SvcParamclientid + "." + "mtstreamedreplywithsubjectparams" + "." + mp1 + "." + mp2 sub, err := nrpc.StreamCall(ctx, c.nc, subject, &nrpc.Void{}, c.Encoding, c.Timeout) if err != nil { return err } var res SimpleStringReply for { err = sub.Next(&res) if err != nil { break } cb(ctx, &res) } if err == nrpc.ErrEOS { err = nil } return err } func (c *SvcSubjectParamsClient) MtNoReply() (error) { subject := c.PkgSubject + "." + c.PkgParaminstance + "." + c.Subject + "." + c.SvcParamclientid + "." + "mtnoreply" // call var req = &github_com_nats_rpc_nrpc.Void{} var resp = github_com_nats_rpc_nrpc.NoReply{} if err := nrpc.Call(req, &resp, c.nc, subject, c.Encoding, c.Timeout); err != nil { return err } return nil } func (c *SvcSubjectParamsClient) MtNoRequestWParamsSubject( mtmp1 string, ) string { subject := c.PkgSubject + "." + c.PkgParaminstance + "." + c.Subject + "." + c.SvcParamclientid + "." + "mtnorequestwparams" + "." + mtmp1 if c.Encoding != "protobuf" { subject += "." + c.Encoding } return subject } type SvcSubjectParamsMtNoRequestWParamsSubscription struct { *nats.Subscription encoding string } func (s *SvcSubjectParamsMtNoRequestWParamsSubscription) Next(timeout time.Duration) (next SimpleStringReply, err error) { msg, err := s.Subscription.NextMsg(timeout) if err != nil { return } err = nrpc.Unmarshal(s.encoding, msg.Data, &next) return } func (c *SvcSubjectParamsClient) MtNoRequestWParamsSubscribeSync( mtmp1 string, ) (sub *SvcSubjectParamsMtNoRequestWParamsSubscription, err error) { subject := c.MtNoRequestWParamsSubject( mtmp1, ) natsSub, err := c.nc.SubscribeSync(subject) if err != nil { return } sub = &SvcSubjectParamsMtNoRequestWParamsSubscription{natsSub, c.Encoding} return } func (c *SvcSubjectParamsClient) MtNoRequestWParamsSubscribe( mtmp1 string, handler func (*SimpleStringReply), ) (sub *nats.Subscription, err error) { subject := c.MtNoRequestWParamsSubject( mtmp1, ) sub, err = c.nc.Subscribe(subject, func(msg *nats.Msg){ var pmsg SimpleStringReply err := nrpc.Unmarshal(c.Encoding, msg.Data, &pmsg) if err != nil { log.Printf("SvcSubjectParamsClient.MtNoRequestWParamsSubscribe: Error decoding, %s", err) return } handler(&pmsg) }) return } func (c *SvcSubjectParamsClient) MtNoRequestWParamsSubscribeChan( mtmp1 string, ) (<-chan *SimpleStringReply, *nats.Subscription, error) { ch := make(chan *SimpleStringReply) sub, err := c.MtNoRequestWParamsSubscribe(mtmp1, func (msg *SimpleStringReply) { ch <- msg }) return ch, sub, err } // NoRequestServiceServer is the interface that providers of the service // NoRequestService should implement. type NoRequestServiceServer interface { } // NoRequestServiceHandler provides a NATS subscription handler that can serve a // subscription using a given NoRequestServiceServer implementation. type NoRequestServiceHandler struct { ctx context.Context workers *nrpc.WorkerPool nc nrpc.NatsConn server NoRequestServiceServer encodings []string } func NewNoRequestServiceHandler(ctx context.Context, nc nrpc.NatsConn, s NoRequestServiceServer) *NoRequestServiceHandler { return &NoRequestServiceHandler{ ctx: ctx, nc: nc, server: s, encodings: []string{"protobuf"}, } } func NewNoRequestServiceConcurrentHandler(workers *nrpc.WorkerPool, nc nrpc.NatsConn, s NoRequestServiceServer) *NoRequestServiceHandler { return &NoRequestServiceHandler{ workers: workers, nc: nc, server: s, } } // SetEncodings sets the output encodings when using a '*Publish' function func (h *NoRequestServiceHandler) SetEncodings(encodings []string) { h.encodings = encodings } func (h *NoRequestServiceHandler) Subject() string { return "root.*.norequestservice.>" } func (h *NoRequestServiceHandler) MtNoRequestPublish(pkginstance string, msg *SimpleStringReply) error { for _, encoding := range h.encodings { rawMsg, err := nrpc.Marshal(encoding, msg) if err != nil { log.Printf("NoRequestServiceHandler.MtNoRequestPublish: error marshaling the message: %s", err) return err } subject := "root." + pkginstance + "."+ "norequestservice."+ "mtnorequest" if encoding != "protobuf" { subject += "." + encoding } if err := h.nc.Publish(subject, rawMsg); err != nil { return err } } return nil } type NoRequestServiceClient struct { nc nrpc.NatsConn PkgSubject string PkgParaminstance string Subject string Encoding string Timeout time.Duration } func NewNoRequestServiceClient(nc nrpc.NatsConn, pkgParaminstance string) *NoRequestServiceClient { return &NoRequestServiceClient{ nc: nc, PkgSubject: "root", PkgParaminstance: pkgParaminstance, Subject: "norequestservice", Encoding: "protobuf", Timeout: 5 * time.Second, } } func (c *NoRequestServiceClient) MtNoRequestSubject( ) string { subject := c.PkgSubject + "." + c.PkgParaminstance + "." + c.Subject + "." + "mtnorequest" if c.Encoding != "protobuf" { subject += "." + c.Encoding } return subject } type NoRequestServiceMtNoRequestSubscription struct { *nats.Subscription encoding string } func (s *NoRequestServiceMtNoRequestSubscription) Next(timeout time.Duration) (next SimpleStringReply, err error) { msg, err := s.Subscription.NextMsg(timeout) if err != nil { return } err = nrpc.Unmarshal(s.encoding, msg.Data, &next) return } func (c *NoRequestServiceClient) MtNoRequestSubscribeSync( ) (sub *NoRequestServiceMtNoRequestSubscription, err error) { subject := c.MtNoRequestSubject( ) natsSub, err := c.nc.SubscribeSync(subject) if err != nil { return } sub = &NoRequestServiceMtNoRequestSubscription{natsSub, c.Encoding} return } func (c *NoRequestServiceClient) MtNoRequestSubscribe( handler func (*SimpleStringReply), ) (sub *nats.Subscription, err error) { subject := c.MtNoRequestSubject( ) sub, err = c.nc.Subscribe(subject, func(msg *nats.Msg){ var pmsg SimpleStringReply err := nrpc.Unmarshal(c.Encoding, msg.Data, &pmsg) if err != nil { log.Printf("NoRequestServiceClient.MtNoRequestSubscribe: Error decoding, %s", err) return } handler(&pmsg) }) return } func (c *NoRequestServiceClient) MtNoRequestSubscribeChan( ) (<-chan *SimpleStringReply, *nats.Subscription, error) { ch := make(chan *SimpleStringReply) sub, err := c.MtNoRequestSubscribe(func (msg *SimpleStringReply) { ch <- msg }) return ch, sub, err } type Client struct { nc nrpc.NatsConn defaultEncoding string defaultTimeout time.Duration pkgSubject string pkgParaminstance string SvcCustomSubject *SvcCustomSubjectClient SvcSubjectParams *SvcSubjectParamsClient NoRequestService *NoRequestServiceClient } func NewClient(nc nrpc.NatsConn, pkgParaminstance string) *Client { c := Client{ nc: nc, defaultEncoding: "protobuf", defaultTimeout: 5*time.Second, pkgSubject: "root", pkgParaminstance: pkgParaminstance, } c.SvcCustomSubject = NewSvcCustomSubjectClient(nc, c.pkgParaminstance) c.NoRequestService = NewNoRequestServiceClient(nc, c.pkgParaminstance) return &c } func (c *Client) SetEncoding(encoding string) { c.defaultEncoding = encoding if c.SvcCustomSubject != nil { c.SvcCustomSubject.Encoding = encoding } if c.SvcSubjectParams != nil { c.SvcSubjectParams.Encoding = encoding } if c.NoRequestService != nil { c.NoRequestService.Encoding = encoding } } func (c *Client) SetTimeout(t time.Duration) { c.defaultTimeout = t if c.SvcCustomSubject != nil { c.SvcCustomSubject.Timeout = t } if c.SvcSubjectParams != nil { c.SvcSubjectParams.Timeout = t } if c.NoRequestService != nil { c.NoRequestService.Timeout = t } } func (c *Client) SetSvcSubjectParamsParams( clientid string, ) { c.SvcSubjectParams = NewSvcSubjectParamsClient( c.nc, c.pkgParaminstance, clientid, ) c.SvcSubjectParams.Encoding = c.defaultEncoding c.SvcSubjectParams.Timeout = c.defaultTimeout } func (c *Client) NewSvcSubjectParams( clientid string, ) *SvcSubjectParamsClient { client := NewSvcSubjectParamsClient( c.nc, c.pkgParaminstance, clientid, ) client.Encoding = c.defaultEncoding client.Timeout = c.defaultTimeout return client } ================================================ FILE: examples/alloptions/alloptions.pb.go ================================================ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.29.0 // protoc v4.22.2 // source: alloptions.proto package main import ( nrpc "github.com/nats-rpc/nrpc" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type StringArg struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Arg1 string `protobuf:"bytes,1,opt,name=arg1,proto3" json:"arg1,omitempty"` } func (x *StringArg) Reset() { *x = StringArg{} if protoimpl.UnsafeEnabled { mi := &file_alloptions_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *StringArg) String() string { return protoimpl.X.MessageStringOf(x) } func (*StringArg) ProtoMessage() {} func (x *StringArg) ProtoReflect() protoreflect.Message { mi := &file_alloptions_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use StringArg.ProtoReflect.Descriptor instead. func (*StringArg) Descriptor() ([]byte, []int) { return file_alloptions_proto_rawDescGZIP(), []int{0} } func (x *StringArg) GetArg1() string { if x != nil { return x.Arg1 } return "" } type SimpleStringReply struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Reply string `protobuf:"bytes,1,opt,name=reply,proto3" json:"reply,omitempty"` } func (x *SimpleStringReply) Reset() { *x = SimpleStringReply{} if protoimpl.UnsafeEnabled { mi := &file_alloptions_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *SimpleStringReply) String() string { return protoimpl.X.MessageStringOf(x) } func (*SimpleStringReply) ProtoMessage() {} func (x *SimpleStringReply) ProtoReflect() protoreflect.Message { mi := &file_alloptions_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SimpleStringReply.ProtoReflect.Descriptor instead. func (*SimpleStringReply) Descriptor() ([]byte, []int) { return file_alloptions_proto_rawDescGZIP(), []int{1} } func (x *SimpleStringReply) GetReply() string { if x != nil { return x.Reply } return "" } var File_alloptions_proto protoreflect.FileDescriptor var file_alloptions_proto_rawDesc = []byte{ 0x0a, 0x10, 0x61, 0x6c, 0x6c, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x04, 0x6d, 0x61, 0x69, 0x6e, 0x1a, 0x0f, 0x6e, 0x72, 0x70, 0x63, 0x2f, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x1f, 0x0a, 0x09, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x41, 0x72, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x72, 0x67, 0x31, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x72, 0x67, 0x31, 0x22, 0x29, 0x0a, 0x11, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x65, 0x70, 0x6c, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x72, 0x65, 0x70, 0x6c, 0x79, 0x32, 0xeb, 0x02, 0x0a, 0x10, 0x53, 0x76, 0x63, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x52, 0x0a, 0x0d, 0x4d, 0x74, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x0f, 0x2e, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x41, 0x72, 0x67, 0x1a, 0x17, 0x2e, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x17, 0x82, 0xb2, 0x19, 0x0f, 0x6d, 0x74, 0x5f, 0x73, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x79, 0x98, 0xb2, 0x19, 0x01, 0x12, 0x2c, 0x0a, 0x0b, 0x4d, 0x74, 0x56, 0x6f, 0x69, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x0f, 0x2e, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x41, 0x72, 0x67, 0x1a, 0x0a, 0x2e, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x56, 0x6f, 0x69, 0x64, 0x22, 0x00, 0x12, 0x39, 0x0a, 0x0b, 0x4d, 0x74, 0x4e, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0f, 0x2e, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x41, 0x0a, 0x0f, 0x4d, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x65, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x0f, 0x2e, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x41, 0x72, 0x67, 0x1a, 0x17, 0x2e, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x04, 0x90, 0xb2, 0x19, 0x01, 0x12, 0x43, 0x0a, 0x16, 0x4d, 0x74, 0x56, 0x6f, 0x69, 0x64, 0x52, 0x65, 0x71, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x65, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x0a, 0x2e, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x56, 0x6f, 0x69, 0x64, 0x1a, 0x17, 0x2e, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x04, 0x90, 0xb2, 0x19, 0x01, 0x1a, 0x12, 0xc2, 0xf3, 0x18, 0x0e, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x32, 0xbc, 0x02, 0x0a, 0x10, 0x53, 0x76, 0x63, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x4a, 0x0a, 0x13, 0x4d, 0x74, 0x57, 0x69, 0x74, 0x68, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x0a, 0x2e, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x56, 0x6f, 0x69, 0x64, 0x1a, 0x17, 0x2e, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x0e, 0x8a, 0xb2, 0x19, 0x03, 0x6d, 0x70, 0x31, 0x8a, 0xb2, 0x19, 0x03, 0x6d, 0x70, 0x32, 0x12, 0x5b, 0x0a, 0x20, 0x4d, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x65, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x57, 0x69, 0x74, 0x68, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x0a, 0x2e, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x56, 0x6f, 0x69, 0x64, 0x1a, 0x17, 0x2e, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x12, 0x8a, 0xb2, 0x19, 0x03, 0x6d, 0x70, 0x31, 0x8a, 0xb2, 0x19, 0x03, 0x6d, 0x70, 0x32, 0x90, 0xb2, 0x19, 0x01, 0x12, 0x28, 0x0a, 0x09, 0x4d, 0x74, 0x4e, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x0a, 0x2e, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x56, 0x6f, 0x69, 0x64, 0x1a, 0x0d, 0x2e, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x47, 0x0a, 0x12, 0x4d, 0x74, 0x4e, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x57, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x0f, 0x2e, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x07, 0x8a, 0xb2, 0x19, 0x03, 0x6d, 0x70, 0x31, 0x1a, 0x0c, 0xca, 0xf3, 0x18, 0x08, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x69, 0x64, 0x32, 0x4d, 0x0a, 0x10, 0x4e, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x39, 0x0a, 0x0b, 0x4d, 0x74, 0x4e, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0f, 0x2e, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x42, 0x24, 0x82, 0xb5, 0x18, 0x04, 0x72, 0x6f, 0x6f, 0x74, 0x8a, 0xb5, 0x18, 0x08, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x90, 0xb5, 0x18, 0x01, 0x98, 0xb5, 0x18, 0x01, 0x5a, 0x06, 0x2e, 0x3b, 0x6d, 0x61, 0x69, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( file_alloptions_proto_rawDescOnce sync.Once file_alloptions_proto_rawDescData = file_alloptions_proto_rawDesc ) func file_alloptions_proto_rawDescGZIP() []byte { file_alloptions_proto_rawDescOnce.Do(func() { file_alloptions_proto_rawDescData = protoimpl.X.CompressGZIP(file_alloptions_proto_rawDescData) }) return file_alloptions_proto_rawDescData } var file_alloptions_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_alloptions_proto_goTypes = []interface{}{ (*StringArg)(nil), // 0: main.StringArg (*SimpleStringReply)(nil), // 1: main.SimpleStringReply (*nrpc.NoRequest)(nil), // 2: nrpc.NoRequest (*nrpc.Void)(nil), // 3: nrpc.Void (*nrpc.NoReply)(nil), // 4: nrpc.NoReply } var file_alloptions_proto_depIdxs = []int32{ 0, // 0: main.SvcCustomSubject.MtSimpleReply:input_type -> main.StringArg 0, // 1: main.SvcCustomSubject.MtVoidReply:input_type -> main.StringArg 2, // 2: main.SvcCustomSubject.MtNoRequest:input_type -> nrpc.NoRequest 0, // 3: main.SvcCustomSubject.MtStreamedReply:input_type -> main.StringArg 3, // 4: main.SvcCustomSubject.MtVoidReqStreamedReply:input_type -> nrpc.Void 3, // 5: main.SvcSubjectParams.MtWithSubjectParams:input_type -> nrpc.Void 3, // 6: main.SvcSubjectParams.MtStreamedReplyWithSubjectParams:input_type -> nrpc.Void 3, // 7: main.SvcSubjectParams.MtNoReply:input_type -> nrpc.Void 2, // 8: main.SvcSubjectParams.MtNoRequestWParams:input_type -> nrpc.NoRequest 2, // 9: main.NoRequestService.MtNoRequest:input_type -> nrpc.NoRequest 1, // 10: main.SvcCustomSubject.MtSimpleReply:output_type -> main.SimpleStringReply 3, // 11: main.SvcCustomSubject.MtVoidReply:output_type -> nrpc.Void 1, // 12: main.SvcCustomSubject.MtNoRequest:output_type -> main.SimpleStringReply 1, // 13: main.SvcCustomSubject.MtStreamedReply:output_type -> main.SimpleStringReply 1, // 14: main.SvcCustomSubject.MtVoidReqStreamedReply:output_type -> main.SimpleStringReply 1, // 15: main.SvcSubjectParams.MtWithSubjectParams:output_type -> main.SimpleStringReply 1, // 16: main.SvcSubjectParams.MtStreamedReplyWithSubjectParams:output_type -> main.SimpleStringReply 4, // 17: main.SvcSubjectParams.MtNoReply:output_type -> nrpc.NoReply 1, // 18: main.SvcSubjectParams.MtNoRequestWParams:output_type -> main.SimpleStringReply 1, // 19: main.NoRequestService.MtNoRequest:output_type -> main.SimpleStringReply 10, // [10:20] is the sub-list for method output_type 0, // [0:10] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name } func init() { file_alloptions_proto_init() } func file_alloptions_proto_init() { if File_alloptions_proto != nil { return } if !protoimpl.UnsafeEnabled { file_alloptions_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*StringArg); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_alloptions_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SimpleStringReply); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_alloptions_proto_rawDesc, NumEnums: 0, NumMessages: 2, NumExtensions: 0, NumServices: 3, }, GoTypes: file_alloptions_proto_goTypes, DependencyIndexes: file_alloptions_proto_depIdxs, MessageInfos: file_alloptions_proto_msgTypes, }.Build() File_alloptions_proto = out.File file_alloptions_proto_rawDesc = nil file_alloptions_proto_goTypes = nil file_alloptions_proto_depIdxs = nil } ================================================ FILE: examples/alloptions/alloptions.proto ================================================ syntax = "proto3"; package main; option go_package = ".;main"; import "nrpc/nrpc.proto"; option (nrpc.packageSubject) = "root"; option (nrpc.packageSubjectParams) = "instance"; option (nrpc.serviceSubjectRule) = TOLOWER; option (nrpc.methodSubjectRule) = TOLOWER; service SvcCustomSubject { option (nrpc.serviceSubject) = 'custom_subject'; rpc MtSimpleReply(StringArg) returns (SimpleStringReply) { option (nrpc.methodSubject) = "mt_simple_reply"; option (nrpc.pollingEnabled) = true; } rpc MtVoidReply(StringArg) returns (nrpc.Void) {} rpc MtNoRequest(nrpc.NoRequest) returns (SimpleStringReply) {} rpc MtStreamedReply(StringArg) returns (SimpleStringReply) { option (nrpc.streamedReply) = true; } rpc MtVoidReqStreamedReply(nrpc.Void) returns (SimpleStringReply) { option (nrpc.streamedReply) = true; } } service SvcSubjectParams { option (nrpc.serviceSubjectParams) = "clientid"; rpc MtWithSubjectParams(nrpc.Void) returns (SimpleStringReply) { option (nrpc.methodSubjectParams) = "mp1"; option (nrpc.methodSubjectParams) = "mp2"; } rpc MtStreamedReplyWithSubjectParams(nrpc.Void) returns (SimpleStringReply) { option (nrpc.streamedReply) = true; option (nrpc.methodSubjectParams) = "mp1"; option (nrpc.methodSubjectParams) = "mp2"; } rpc MtNoReply(nrpc.Void) returns (nrpc.NoReply) {} rpc MtNoRequestWParams(nrpc.NoRequest) returns (SimpleStringReply) { option (nrpc.methodSubjectParams) = "mp1"; } } service NoRequestService { rpc MtNoRequest(nrpc.NoRequest) returns (SimpleStringReply) {} } message StringArg { string arg1 = 1; } message SimpleStringReply { string reply = 1; } ================================================ FILE: examples/alloptions/alloptions_test.go ================================================ package main import ( "context" "errors" "fmt" "log" "sync" "testing" "time" "github.com/nats-io/nats.go" "github.com/nats-rpc/nrpc" "github.com/stretchr/testify/require" ) type TestingLogWriter struct { t *testing.T } func (w TestingLogWriter) Write(p []byte) (int, error) { w.t.Log(string(p)) return len(p), nil } type BasicServerImpl struct { t *testing.T handler *SvcCustomSubjectHandler handler2 *SvcSubjectParamsHandler } func (s BasicServerImpl) MtSimpleReply( ctx context.Context, args *StringArg, ) (*SimpleStringReply, error) { if instance := nrpc.GetRequest(ctx).PackageParam("instance"); instance != "default" { s.t.Errorf("Got an invalid package param instance: '%s'", instance) } return &SimpleStringReply{Reply: args.Arg1}, nil } func (s BasicServerImpl) MtVoidReply( ctx context.Context, args *StringArg, ) error { if args.GetArg1() == "please fail" { return errors.New("Error") } return nil } func (s BasicServerImpl) MtStreamedReply( ctx context.Context, req *StringArg, send func(rep *SimpleStringReply), ) error { if req.GetArg1() == "please fail" { panic("Failing") } if req.GetArg1() == "very long call" { select { case <-ctx.Done(): return ctx.Err() case <-time.After(time.Minute): time.Sleep(time.Minute) return nil } } time.Sleep(time.Second) send(&SimpleStringReply{Reply: "msg1"}) time.Sleep(250 * time.Millisecond) send(&SimpleStringReply{Reply: "msg2"}) time.Sleep(250 * time.Millisecond) send(&SimpleStringReply{Reply: "msg3"}) time.Sleep(250 * time.Millisecond) return nil } func (s BasicServerImpl) MtVoidReqStreamedReply( ctx context.Context, send func(rep *SimpleStringReply), ) error { time.Sleep(2 * time.Second) send(&SimpleStringReply{Reply: "hi"}) return nil } func (s BasicServerImpl) MtNoReply(ctx context.Context) { s.t.Log("Will publish to MtNoRequest") s.handler.MtNoRequestPublish("default", &SimpleStringReply{Reply: "Hi there"}) s.handler2.MtNoRequestWParamsPublish("default", "me", "mtvalue", &SimpleStringReply{Reply: "Hi there"}) } func (s BasicServerImpl) MtWithSubjectParams( ctx context.Context, mp1 string, mp2 string, ) (*SimpleStringReply, error) { var err error if mp1 != "p1" { err = fmt.Errorf("Expects method param mp1 = 'p1', got '%s'", mp1) } if mp2 != "p2" { err = fmt.Errorf("Expects method param mp2 = 'p2', got '%s'", mp2) } return &SimpleStringReply{Reply: "Hi"}, err } func (s BasicServerImpl) MtStreamedReplyWithSubjectParams( ctx context.Context, mp1 string, mp2 string, send func(rep *SimpleStringReply), ) error { send(&SimpleStringReply{Reply: mp1}) send(&SimpleStringReply{Reply: mp2}) return nil } func TestAll(t *testing.T) { c, err := nats.Connect(natsURL) if err != nil { t.Fatal(err) } log.SetOutput(TestingLogWriter{t}) t.Run("MultiProtocolPublish", func(t *testing.T) { log.SetOutput(TestingLogWriter{t}) handler := NewSvcCustomSubjectHandler(context.Background(), c, BasicServerImpl{t, nil, nil}) handler.SetEncodings([]string{"protobuf", "json"}) c1 := NewSvcCustomSubjectClient(c, "default") for _, protocol := range []string{"protobuf", "json"} { t.Run(protocol, func(t *testing.T) { c1.Encoding = protocol if protocol == "protobuf" { require.Equal(t, "root.default.custom_subject.mtnorequest", c1.MtNoRequestSubject()) } else { require.Equal(t, "root.default.custom_subject.mtnorequest."+protocol, c1.MtNoRequestSubject()) } sub, err := c1.MtNoRequestSubscribeSync() if err != nil { t.Fatal(err) } defer sub.Unsubscribe() if err := handler.MtNoRequestPublish( "default", &SimpleStringReply{Reply: "test"}, ); err != nil { t.Fatal(t) } msg, err := sub.Next(time.Second) if err != nil { t.Fatal(err) } require.Equal(t, "test", msg.GetReply()) }) } }) t.Run("NoConcurrency", func(t *testing.T) { log.SetOutput(TestingLogWriter{t}) handler1 := NewSvcCustomSubjectHandler(context.Background(), c, BasicServerImpl{t, nil, nil}) impl := BasicServerImpl{t, handler1, nil} handler2 := NewSvcSubjectParamsHandler(context.Background(), c, &impl) impl.handler2 = handler2 if handler1.Subject() != "root.*.custom_subject.>" { t.Fatal("Invalid subject", handler1.Subject()) } if handler2.Subject() != "root.*.svcsubjectparams.*.>" { t.Fatal("Invalid subject", handler2.Subject()) } for _, encoding := range []string{"protobuf", "json"} { t.Run("Encoding_"+encoding, commonTests(c, handler1, handler2, encoding)) } }) t.Run("WithConcurrency", func(t *testing.T) { log.SetOutput(TestingLogWriter{t}) pool := nrpc.NewWorkerPool(context.Background(), 2, 5, 4*time.Second) handler1 := NewSvcCustomSubjectConcurrentHandler(pool, c, BasicServerImpl{t, nil, nil}) impl := BasicServerImpl{t, handler1, nil} handler2 := NewSvcSubjectParamsConcurrentHandler(pool, c, &impl) impl.handler2 = handler2 if handler1.Subject() != "root.*.custom_subject.>" { t.Fatal("Invalid subject", handler1.Subject()) } if handler2.Subject() != "root.*.svcsubjectparams.*.>" { t.Fatal("Invalid subject", handler2.Subject()) } for _, encoding := range []string{"protobuf", "json"} { t.Run("Encoding_"+encoding, commonTests(c, handler1, handler2, encoding)) } // Now a few tests very specific to concurrency handling s, err := c.QueueSubscribe(handler1.Subject(), "queue", handler1.Handler) if err != nil { t.Fatal(err) } defer s.Unsubscribe() s, err = c.QueueSubscribe(handler2.Subject(), "queue", handler2.Handler) if err != nil { t.Fatal(err) } defer s.Unsubscribe() c1 := NewSvcCustomSubjectClient(c, "default") // c2 := NewSvcSubjectParamsClient(c, "default", "me") t.Run("Concurrent Stream calls", func(t *testing.T) { log.SetOutput(TestingLogWriter{t}) var resList []string var wg sync.WaitGroup resChan := make(chan string, 2) go func() { for r := range resChan { resList = append(resList, r) } }() for i := 0; i != 2; i++ { wg.Add(1) go func() { defer wg.Done() err := c1.MtStreamedReply( context.Background(), &StringArg{Arg1: "arg"}, func(ctx context.Context, rep *SimpleStringReply) { fmt.Println("received", rep) resChan <- rep.GetReply() }) if err != nil { t.Error(err) } }() } wg.Wait() close(resChan) expectsStringSlice(t, []string{"msg1", "msg1", "msg2", "msg2", "msg3", "msg3"}, resList) }) t.Run("Too many concurrent Stream calls", func(t *testing.T) { log.SetOutput(TestingLogWriter{t}) pool.SetMaxPendingDuration(2 * time.Second) var resList []string var wg sync.WaitGroup resChan := make(chan string, 2) go func() { for r := range resChan { resList = append(resList, r) } }() for i := 0; i != 7; i++ { wg.Add(1) time.Sleep(50 * time.Millisecond) go func(i int) { defer wg.Done() err := c1.MtStreamedReply( context.Background(), &StringArg{Arg1: "arg"}, func(ctx context.Context, rep *SimpleStringReply) { fmt.Println("received", rep) resChan <- rep.GetReply() }) if i >= 4 { if nrpcErr, ok := err.(*nrpc.Error); !ok || nrpcErr.Type != nrpc.Error_SERVERTOOBUSY { t.Errorf("Should get a SERVERTOOBUSY error, got %v", err) } } else if err != nil { t.Errorf("Should succeed but got: %s", err) } }(i) } // Wait so the 7 calls are already queued time.Sleep(200 * time.Millisecond) // The 7th call should get a SERVERTOOBUSY error err := c1.MtStreamedReply( context.Background(), &StringArg{Arg1: "arg"}, func(ctx context.Context, rep *SimpleStringReply) { fmt.Println("received", rep) }) if err == nil { t.Error("Should get an error") } else if nrpcErr, ok := err.(*nrpc.Error); ok { if nrpcErr.Type != nrpc.Error_SERVERTOOBUSY { t.Errorf("Should get a SERVERTOOBUSY, got %v", nrpcErr.Type) } } else { t.Errorf("Should get a nrpcError, got %v", err) } wg.Wait() close(resChan) }) pool.Close(time.Second) }) } func commonTests( conn *nats.Conn, handler1 *SvcCustomSubjectHandler, handler2 *SvcSubjectParamsHandler, encoding string, ) func(t *testing.T) { return func(t *testing.T) { handler1.SetEncodings([]string{encoding}) handler2.SetEncodings([]string{encoding}) s, err := conn.QueueSubscribe(handler1.Subject(), "queue", handler1.Handler) if err != nil { t.Fatal(err) } defer s.Unsubscribe() s, err = conn.QueueSubscribe(handler2.Subject(), "queue", handler2.Handler) if err != nil { t.Fatal(err) } defer s.Unsubscribe() c1 := NewSvcCustomSubjectClient(conn, "default") c2 := NewSvcSubjectParamsClient(conn, "default", "me") c1.Encoding = encoding c2.Encoding = encoding r, err := c1.MtSimpleReply(&StringArg{Arg1: "hi"}) if err != nil { t.Fatal(err) } if r.GetReply() != "hi" { t.Error("Invalid reply:", r.GetReply()) } if err := c1.MtSimpleReplyPoll(&StringArg{Arg1: "hi"}, 1, func(r *SimpleStringReply) error { if r.GetReply() != "hi" { return fmt.Errorf("Invalid reply: %s", r.GetReply()) } return nil }, ); err != nil { t.Fatal(err) } if err := c1.MtVoidReply(&StringArg{Arg1: "hi"}); err != nil { t.Error("Unexpected error:", err) } err = c1.MtVoidReply(&StringArg{Arg1: "please fail"}) if err == nil { t.Error("Expected an error") } t.Run("StreamedReply", func(t *testing.T) { log.SetOutput(TestingLogWriter{t}) t.Run("Simple", func(t *testing.T) { log.SetOutput(TestingLogWriter{t}) var resList []string err := c1.MtStreamedReply( context.Background(), &StringArg{Arg1: "arg"}, func(ctx context.Context, rep *SimpleStringReply) { fmt.Println("received", rep) resList = append(resList, rep.GetReply()) }) if err != nil { t.Fatal(err) } if resList[0] != "msg1" { t.Errorf("Expected 'msg1', got '%s'", resList[0]) } if resList[1] != "msg2" { t.Errorf("Expected 'msg2', got '%s'", resList[1]) } if resList[2] != "msg3" { t.Errorf("Expected 'msg3', got '%s'", resList[2]) } }) t.Run("Error", func(t *testing.T) { log.SetOutput(TestingLogWriter{t}) err := c1.MtStreamedReply(context.Background(), &StringArg{Arg1: "please fail"}, func(ctx context.Context, rep *SimpleStringReply) { t.Fatal("Should not receive anything") }) if err == nil { t.Fatal("Expected an error, got nil") } }) t.Run("Cancel", func(t *testing.T) { log.SetOutput(TestingLogWriter{t}) ctx, cancel := context.WithTimeout(context.Background(), 7*time.Second) defer cancel() err := c1.MtStreamedReply(ctx, &StringArg{Arg1: "very long call"}, func(context.Context, *SimpleStringReply) { t.Fatal("Should not receive anything") }) if err != nrpc.ErrCanceled { t.Fatal("Expects a ErrCanceled error, got ", err) } }) t.Run("VoidRequest", func(t *testing.T) { log.SetOutput(TestingLogWriter{t}) ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() err := c1.MtVoidReqStreamedReply(ctx, func(context.Context, *SimpleStringReply) {}) if err != nil { fmt.Print(err) t.Error(err) } }) }) t.Run("SubjectParams", func(t *testing.T) { log.SetOutput(TestingLogWriter{t}) r, err = c2.MtWithSubjectParams("p1", "p2") if err != nil { t.Fatal(err) } if r.GetReply() != "Hi" { t.Error("Invalid reply:", r.GetReply()) } r, err = c2.MtWithSubjectParams("invalid", "p2") if err == nil { t.Error("Expected an error") } if nErr, ok := err.(*nrpc.Error); ok { if nErr.Type != nrpc.Error_CLIENT || nErr.Message != "Expects method param mp1 = 'p1', got 'invalid'" { t.Errorf("Unexpected error %#v", nErr) } } else { t.Errorf("Expected a nrpc.Error, got %#v", err) } }) t.Run("StreamedReply with SubjectParams", func(t *testing.T) { log.SetOutput(TestingLogWriter{t}) var resList []string err := c2.MtStreamedReplyWithSubjectParams( context.Background(), "arg1", "arg2", func(ctx context.Context, rep *SimpleStringReply) { resList = append(resList, rep.GetReply()) }) if err != nil { t.Fatal(err) } if resList[0] != "arg1" { t.Errorf("Expected 'arg1', got '%s'", resList[0]) } if resList[1] != "arg2" { t.Errorf("Expected 'arg2', got '%s'", resList[1]) } }) t.Run("NoRequest method with params", func(t *testing.T) { log.SetOutput(TestingLogWriter{t}) sub, err := c2.MtNoRequestWParamsSubscribeSync( "mtvalue", ) if err != nil { t.Fatal(err) } defer sub.Unsubscribe() c2.MtNoReply() reply, err := sub.Next(time.Second) if err != nil { t.Fatal(err) } if reply.GetReply() != "Hi there" { t.Errorf("Expected 'Hi there', got %s", reply.GetReply()) } }) t.Run("NoRequest method subscriptions", func(t *testing.T) { log.SetOutput(TestingLogWriter{t}) if encoding != "protobuf" { t.Skip() } fmt.Println("Subscribing") sub1, err := c1.MtNoRequestSubscribeSync() if err != nil { t.Fatal(err) } defer sub1.Unsubscribe() repChan := make(chan string, 2) sub2, err := c1.MtNoRequestSubscribe(func(msg *SimpleStringReply) { repChan <- msg.GetReply() }) if err != nil { t.Fatal(err) } defer sub2.Unsubscribe() msgChan, sub3, err := c1.MtNoRequestSubscribeChan() if err != nil { t.Fatal(err) } defer sub3.Unsubscribe() go func() { msg := <-msgChan repChan <- msg.GetReply() }() err = c2.MtNoReply() if err != nil { t.Fatal(err) } msg, err := sub1.Next(time.Second) if err != nil { t.Fatal(err) } if msg.GetReply() != "Hi there" { t.Errorf("Expected 'Hi there', got '%s'", msg.GetReply()) } for range []int{0, 1} { select { case rep := <-repChan: if rep != "Hi there" { t.Errorf("Expected 'Hi there', got '%s'", rep) } case <-time.After(time.Second): t.Fatal("timeout") } } }) } } func expectsStringSlice(t *testing.T, expected, actual []string) { if len(expected) != len(actual) { t.Errorf("Expected %v, got %v", expected, actual) return } for i := range expected { if expected[i] != actual[i] { t.Errorf("Expected %v, got %v", expected, actual) return } } } ================================================ FILE: examples/alloptions/main.go ================================================ package main import ( "context" "errors" "fmt" "os" "os/signal" "syscall" "time" "github.com/nats-io/nats.go" "github.com/nats-rpc/nrpc" ) //go:generate protoc -I. -I ../../.. --go_out . --nrpc_out . alloptions.proto type ServerImpl struct { handler *SvcCustomSubjectHandler handler2 *SvcSubjectParamsHandler } func (s ServerImpl) MtSimpleReply( ctx context.Context, args *StringArg, ) (*SimpleStringReply, error) { fmt.Println("MtSimpleReply: " + args.GetArg1()) if instance := nrpc.GetRequest(ctx).PackageParam("instance"); instance != "default" { return nil, fmt.Errorf("Got an invalid package param instance: '%s'", instance) } return &SimpleStringReply{Reply: args.Arg1}, nil } func (s ServerImpl) MtVoidReply( ctx context.Context, args *StringArg, ) error { fmt.Println("MtVoidReply: " + args.GetArg1()) if args.GetArg1() == "please fail" { return errors.New("Error") } return nil } func (s ServerImpl) MtStreamedReply( ctx context.Context, req *StringArg, send func(rep *SimpleStringReply), ) error { fmt.Println("MtStreamedReply: " + req.GetArg1()) if req.GetArg1() == "please fail" { panic("Failing") } if req.GetArg1() == "very long call" { select { case <-ctx.Done(): return ctx.Err() case <-time.After(time.Minute): time.Sleep(time.Minute) return nil } } time.Sleep(time.Second) send(&SimpleStringReply{Reply: "msg1"}) time.Sleep(250 * time.Millisecond) send(&SimpleStringReply{Reply: "msg2"}) time.Sleep(250 * time.Millisecond) send(&SimpleStringReply{Reply: "msg3"}) time.Sleep(250 * time.Millisecond) return nil } func (s ServerImpl) MtVoidReqStreamedReply( ctx context.Context, send func(rep *SimpleStringReply), ) error { fmt.Println("MtVoidReqStreamedReply") time.Sleep(2 * time.Second) send(&SimpleStringReply{Reply: "hi"}) return nil } func (s ServerImpl) MtNoReply(ctx context.Context) { fmt.Println("MtNoReply") if err := s.handler.MtNoRequestPublish("default", &SimpleStringReply{Reply: "Hi there"}); err != nil { fmt.Println(err) } if err := s.handler2.MtNoRequestWParamsPublish("default", "me", "mtvalue", &SimpleStringReply{Reply: "Hi there"}); err != nil { fmt.Println(err) } } func (s ServerImpl) MtWithSubjectParams( ctx context.Context, mp1 string, mp2 string, ) (*SimpleStringReply, error) { fmt.Println("MtWithSubjectParams: ", mp1, mp2) var err error if mp1 != "p1" { err = fmt.Errorf("Expects method param mp1 = 'p1', got '%s'", mp1) } if mp2 != "p2" { err = fmt.Errorf("Expects method param mp2 = 'p2', got '%s'", mp2) } return &SimpleStringReply{Reply: "Hi"}, err } func (s ServerImpl) MtStreamedReplyWithSubjectParams( ctx context.Context, mp1 string, mp2 string, send func(rep *SimpleStringReply), ) error { fmt.Println("MtStreamedReplyWithSubjectParams: ", mp1, mp2) send(&SimpleStringReply{Reply: mp1}) send(&SimpleStringReply{Reply: mp2}) return nil } func main() { c, err := nats.Connect("localhost:4222") if err != nil { fmt.Println(err) return } pool := nrpc.NewWorkerPool(context.Background(), 2, 5, 4*time.Second) impl := ServerImpl{nil, nil} handler1 := NewSvcCustomSubjectConcurrentHandler(pool, c, &impl) handler2 := NewSvcSubjectParamsConcurrentHandler(pool, c, &impl) impl.handler = handler1 impl.handler2 = handler2 s, err := c.QueueSubscribe(handler1.Subject(), "queue", handler1.Handler) if err != nil { panic(err) } defer s.Unsubscribe() s, err = c.QueueSubscribe(handler2.Subject(), "queue", handler2.Handler) if err != nil { panic(err) } defer s.Unsubscribe() done := make(chan os.Signal, 1) signal.Notify(done, syscall.SIGINT, syscall.SIGTERM) fmt.Println("Blocking, press ctrl+c to continue...") <-done // Will block here until user hits ctrl+c } ================================================ FILE: examples/alloptions/testrunner_test.go ================================================ package main import ( "log" "os" "testing" "time" "github.com/nats-io/nats-server/v2/logger" natsServer "github.com/nats-io/nats-server/v2/server" ) var natsURL string func TestMain(m *testing.M) { gnatsd := natsServer.New(&natsServer.Options{Port: natsServer.RANDOM_PORT}) gnatsd.SetLogger( logger.NewStdLogger(false, false, false, false, false), false, false) go gnatsd.Start() defer gnatsd.Shutdown() if !gnatsd.ReadyForConnections(time.Second) { log.Fatal("Cannot start the gnatsd server") } natsURL = "nats://" + gnatsd.Addr().String() os.Exit(m.Run()) } ================================================ FILE: examples/helloworld/greeter_client/main.go ================================================ package main import ( "fmt" "log" "os" "time" "github.com/nats-io/nats.go" // This is the package containing the generated *.pb.go and *.nrpc.go // files. "github.com/nats-rpc/nrpc/examples/helloworld/helloworld" ) func main() { var natsURL = nats.DefaultURL if len(os.Args) == 2 { natsURL = os.Args[1] } // Connect to the NATS server. nc, err := nats.Connect(natsURL, nats.Timeout(5*time.Second)) if err != nil { log.Fatal(err) } defer nc.Close() // This is our generated client. cli := helloworld.NewGreeterClient(nc) // Contact the server and print out its response. resp, err := cli.SayHello(&helloworld.HelloRequest{Name: "world"}) if err != nil { log.Fatal(err) } // print fmt.Printf("Greeting: %s\n", resp.GetMessage()) } ================================================ FILE: examples/helloworld/greeter_server/main.go ================================================ package main import ( "context" "fmt" "log" "os" "os/signal" "time" "github.com/nats-io/nats.go" // This is the package containing the generated *.pb.go and *.nrpc.go // files. "github.com/nats-rpc/nrpc/examples/helloworld/helloworld" ) // server implements the helloworld.GreeterServer interface. type server struct{} // SayHello is an implementation of the SayHello method from the definition of // the Greeter service. func (s *server) SayHello(ctx context.Context, req *helloworld.HelloRequest) (resp *helloworld.HelloReply, err error) { return &helloworld.HelloReply{Message: "Hello " + req.Name}, nil } func main() { var natsURL = nats.DefaultURL if len(os.Args) == 2 { natsURL = os.Args[1] } // Connect to the NATS server. nc, err := nats.Connect(natsURL, nats.Timeout(5*time.Second)) if err != nil { log.Fatal(err) } defer nc.Close() // Our server implementation. s := &server{} // The NATS handler from the helloworld.nrpc.proto file. h := helloworld.NewGreeterHandler(context.TODO(), nc, s) // Start a NATS subscription using the handler. You can also use the // QueueSubscribe() method for a load-balanced set of servers. sub, err := nc.Subscribe(h.Subject(), h.Handler) if err != nil { log.Fatal(err) } defer sub.Unsubscribe() // Keep running until ^C. fmt.Println("server is running, ^C quits.") c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt) <-c close(c) } ================================================ FILE: examples/helloworld/greeter_server/main_test.go ================================================ package main import ( "context" "testing" "time" "github.com/nats-io/nats.go" // This is the package containing the generated *.pb.go and *.nrpc.go // files. "github.com/nats-rpc/nrpc/examples/helloworld/helloworld" ) func TestBasic(t *testing.T) { // Connect to the NATS server. nc, err := nats.Connect(natsURL, nats.Timeout(5*time.Second)) if err != nil { t.Fatal(err) } defer nc.Close() // Our server implementation. s := &server{} // The NATS handler from the helloworld.nrpc.proto file. h := helloworld.NewGreeterHandler(context.TODO(), nc, s) // Start a NATS subscription using the handler. You can also use the // QueueSubscribe() method for a load-balanced set of servers. sub, err := nc.Subscribe(h.Subject(), h.Handler) if err != nil { t.Fatal(err) } defer sub.Unsubscribe() // This is our generated client. cli := helloworld.NewGreeterClient(nc) // Contact the server and print out its response. resp, err := cli.SayHello(&helloworld.HelloRequest{Name: "world"}) if err != nil { t.Fatal(err) } if resp.GetMessage() != "Hello world" { t.Fatalf("unexpected message: %s", resp.GetMessage()) } } ================================================ FILE: examples/helloworld/greeter_server/testrunner_test.go ================================================ package main import ( "log" "os" "testing" "time" "github.com/nats-io/nats-server/v2/logger" natsServer "github.com/nats-io/nats-server/v2/server" ) var natsURL string func TestMain(m *testing.M) { gnatsd := natsServer.New(&natsServer.Options{Port: natsServer.RANDOM_PORT}) gnatsd.SetLogger( logger.NewStdLogger(false, false, false, false, false), false, false) go gnatsd.Start() defer gnatsd.Shutdown() if !gnatsd.ReadyForConnections(time.Second) { log.Fatal("Cannot start the gnatsd server") } natsURL = "nats://" + gnatsd.Addr().String() os.Exit(m.Run()) } ================================================ FILE: examples/helloworld/helloworld/helloworld.go ================================================ package helloworld //go:generate protoc -I. -I../../.. --go_out . --go_opt=paths=source_relative --nrpc_out . --nrpc_opt=paths=source_relative helloworld.proto ================================================ FILE: examples/helloworld/helloworld/helloworld.nrpc.go ================================================ // This code was autogenerated from helloworld.proto, do not edit. package helloworld import ( "context" "log" "time" "google.golang.org/protobuf/proto" "github.com/nats-io/nats.go" "github.com/nats-rpc/nrpc" ) // GreeterServer is the interface that providers of the service // Greeter should implement. type GreeterServer interface { SayHello(ctx context.Context, req *HelloRequest) (*HelloReply, error) } // GreeterHandler provides a NATS subscription handler that can serve a // subscription using a given GreeterServer implementation. type GreeterHandler struct { ctx context.Context workers *nrpc.WorkerPool nc nrpc.NatsConn server GreeterServer encodings []string } func NewGreeterHandler(ctx context.Context, nc nrpc.NatsConn, s GreeterServer) *GreeterHandler { return &GreeterHandler{ ctx: ctx, nc: nc, server: s, encodings: []string{"protobuf"}, } } func NewGreeterConcurrentHandler(workers *nrpc.WorkerPool, nc nrpc.NatsConn, s GreeterServer) *GreeterHandler { return &GreeterHandler{ workers: workers, nc: nc, server: s, } } // SetEncodings sets the output encodings when using a '*Publish' function func (h *GreeterHandler) SetEncodings(encodings []string) { h.encodings = encodings } func (h *GreeterHandler) Subject() string { return "Greeter.>" } func (h *GreeterHandler) Handler(msg *nats.Msg) { var ctx context.Context if h.workers != nil { ctx = h.workers.Context } else { ctx = h.ctx } request := nrpc.NewRequest(ctx, h.nc, msg.Subject, msg.Reply) // extract method name & encoding from subject _, _, name, tail, err := nrpc.ParseSubject( "", 0, "Greeter", 0, msg.Subject) if err != nil { log.Printf("GreeterHanlder: Greeter subject parsing failed: %v", err) return } request.MethodName = name request.SubjectTail = tail // call handler and form response var immediateError *nrpc.Error switch name { case "SayHello": _, request.Encoding, err = nrpc.ParseSubjectTail(0, request.SubjectTail) if err != nil { log.Printf("SayHelloHanlder: SayHello subject parsing failed: %v", err) break } var req HelloRequest if err := nrpc.Unmarshal(request.Encoding, msg.Data, &req); err != nil { log.Printf("SayHelloHandler: SayHello request unmarshal failed: %v", err) immediateError = &nrpc.Error{ Type: nrpc.Error_CLIENT, Message: "bad request received: " + err.Error(), } } else { request.Handler = func(ctx context.Context)(proto.Message, error){ innerResp, err := h.server.SayHello(ctx, &req) if err != nil { return nil, err } return innerResp, err } } default: log.Printf("GreeterHandler: unknown name %q", name) immediateError = &nrpc.Error{ Type: nrpc.Error_CLIENT, Message: "unknown name: " + name, } } if immediateError == nil { if h.workers != nil { // Try queuing the request if err := h.workers.QueueRequest(request); err != nil { log.Printf("nrpc: Error queuing the request: %s", err) } } else { // Run the handler synchronously request.RunAndReply() } } if immediateError != nil { if err := request.SendReply(nil, immediateError); err != nil { log.Printf("GreeterHandler: Greeter handler failed to publish the response: %s", err) } } else { } } type GreeterClient struct { nc nrpc.NatsConn Subject string Encoding string Timeout time.Duration } func NewGreeterClient(nc nrpc.NatsConn) *GreeterClient { return &GreeterClient{ nc: nc, Subject: "Greeter", Encoding: "protobuf", Timeout: 5 * time.Second, } } func (c *GreeterClient) SayHello(req *HelloRequest) (*HelloReply, error) { subject := c.Subject + "." + "SayHello" // call var resp = HelloReply{} if err := nrpc.Call(req, &resp, c.nc, subject, c.Encoding, c.Timeout); err != nil { return nil, err } return &resp, nil } type Client struct { nc nrpc.NatsConn defaultEncoding string defaultTimeout time.Duration Greeter *GreeterClient } func NewClient(nc nrpc.NatsConn) *Client { c := Client{ nc: nc, defaultEncoding: "protobuf", defaultTimeout: 5*time.Second, } c.Greeter = NewGreeterClient(nc) return &c } func (c *Client) SetEncoding(encoding string) { c.defaultEncoding = encoding if c.Greeter != nil { c.Greeter.Encoding = encoding } } func (c *Client) SetTimeout(t time.Duration) { c.defaultTimeout = t if c.Greeter != nil { c.Greeter.Timeout = t } } ================================================ FILE: examples/helloworld/helloworld/helloworld.pb.go ================================================ // Copyright 2015 gRPC 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. // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.29.0 // protoc v4.22.2 // source: helloworld.proto package helloworld import ( _ "github.com/nats-rpc/nrpc" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // The request message containing the user's name. type HelloRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` } func (x *HelloRequest) Reset() { *x = HelloRequest{} if protoimpl.UnsafeEnabled { mi := &file_helloworld_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *HelloRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*HelloRequest) ProtoMessage() {} func (x *HelloRequest) ProtoReflect() protoreflect.Message { mi := &file_helloworld_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use HelloRequest.ProtoReflect.Descriptor instead. func (*HelloRequest) Descriptor() ([]byte, []int) { return file_helloworld_proto_rawDescGZIP(), []int{0} } func (x *HelloRequest) GetName() string { if x != nil { return x.Name } return "" } // The response message containing the greetings type HelloReply struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` } func (x *HelloReply) Reset() { *x = HelloReply{} if protoimpl.UnsafeEnabled { mi := &file_helloworld_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *HelloReply) String() string { return protoimpl.X.MessageStringOf(x) } func (*HelloReply) ProtoMessage() {} func (x *HelloReply) ProtoReflect() protoreflect.Message { mi := &file_helloworld_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use HelloReply.ProtoReflect.Descriptor instead. func (*HelloReply) Descriptor() ([]byte, []int) { return file_helloworld_proto_rawDescGZIP(), []int{1} } func (x *HelloReply) GetMessage() string { if x != nil { return x.Message } return "" } var File_helloworld_proto protoreflect.FileDescriptor var file_helloworld_proto_rawDesc = []byte{ 0x0a, 0x10, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0a, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x1a, 0x0a, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x22, 0x0a, 0x0c, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x26, 0x0a, 0x0a, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x32, 0x49, 0x0a, 0x07, 0x47, 0x72, 0x65, 0x65, 0x74, 0x65, 0x72, 0x12, 0x3e, 0x0a, 0x08, 0x53, 0x61, 0x79, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x12, 0x18, 0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x42, 0x69, 0x0a, 0x1b, 0x69, 0x6f, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x42, 0x0f, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x37, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6e, 0x61, 0x74, 0x73, 0x2d, 0x72, 0x70, 0x63, 0x2f, 0x6e, 0x72, 0x70, 0x63, 0x2f, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x2f, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2f, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( file_helloworld_proto_rawDescOnce sync.Once file_helloworld_proto_rawDescData = file_helloworld_proto_rawDesc ) func file_helloworld_proto_rawDescGZIP() []byte { file_helloworld_proto_rawDescOnce.Do(func() { file_helloworld_proto_rawDescData = protoimpl.X.CompressGZIP(file_helloworld_proto_rawDescData) }) return file_helloworld_proto_rawDescData } var file_helloworld_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_helloworld_proto_goTypes = []interface{}{ (*HelloRequest)(nil), // 0: helloworld.HelloRequest (*HelloReply)(nil), // 1: helloworld.HelloReply } var file_helloworld_proto_depIdxs = []int32{ 0, // 0: helloworld.Greeter.SayHello:input_type -> helloworld.HelloRequest 1, // 1: helloworld.Greeter.SayHello:output_type -> helloworld.HelloReply 1, // [1:2] is the sub-list for method output_type 0, // [0:1] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name } func init() { file_helloworld_proto_init() } func file_helloworld_proto_init() { if File_helloworld_proto != nil { return } if !protoimpl.UnsafeEnabled { file_helloworld_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*HelloRequest); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_helloworld_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*HelloReply); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_helloworld_proto_rawDesc, NumEnums: 0, NumMessages: 2, NumExtensions: 0, NumServices: 1, }, GoTypes: file_helloworld_proto_goTypes, DependencyIndexes: file_helloworld_proto_depIdxs, MessageInfos: file_helloworld_proto_msgTypes, }.Build() File_helloworld_proto = out.File file_helloworld_proto_rawDesc = nil file_helloworld_proto_goTypes = nil file_helloworld_proto_depIdxs = nil } ================================================ FILE: examples/helloworld/helloworld/helloworld.proto ================================================ // Copyright 2015 gRPC 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. syntax = "proto3"; option java_multiple_files = true; option java_package = "io.grpc.examples.helloworld"; option java_outer_classname = "HelloWorldProto"; import "nrpc.proto"; package helloworld; option go_package = "github.com/nats-rpc/nrpc/examples/helloworld/helloworld"; // The greeting service definition. service Greeter { // Sends a greeting rpc SayHello (HelloRequest) returns (HelloReply) {} } // The request message containing the user's name. message HelloRequest { string name = 1; } // The response message containing the greetings message HelloReply { string message = 1; } ================================================ FILE: examples/metrics_helloworld/helloworld/helloworld.go ================================================ package helloworld //go:generate protoc --go_out . --go_opt=paths=source_relative --nrpc_out plugins=prometheus:. --nrpc_opt=paths=source_relative helloworld.proto ================================================ FILE: examples/metrics_helloworld/helloworld/helloworld.nrpc.go ================================================ // This code was autogenerated from helloworld.proto, do not edit. package helloworld import ( "context" "log" "time" "google.golang.org/protobuf/proto" "github.com/nats-io/nats.go" "github.com/prometheus/client_golang/prometheus" "github.com/nats-rpc/nrpc" ) // GreeterServer is the interface that providers of the service // Greeter should implement. type GreeterServer interface { SayHello(ctx context.Context, req HelloRequest) (resp HelloReply, err error) } var ( // The request completion time, measured at client-side. clientRCTForGreeter = prometheus.NewSummaryVec( prometheus.SummaryOpts{ Name: "nrpc_client_request_completion_time_seconds", Help: "The request completion time for calls, measured client-side.", Objectives: map[float64]float64{0.9: 0.01, 0.95: 0.01, 0.99: 0.001}, ConstLabels: map[string]string{ "service": "Greeter", }, }, []string{"method"}) // The handler execution time, measured at server-side. serverHETForGreeter = prometheus.NewSummaryVec( prometheus.SummaryOpts{ Name: "nrpc_server_handler_execution_time_seconds", Help: "The handler execution time for calls, measured server-side.", Objectives: map[float64]float64{0.9: 0.01, 0.95: 0.01, 0.99: 0.001}, ConstLabels: map[string]string{ "service": "Greeter", }, }, []string{"method"}) // The counts of calls made by the client, classified by result type. clientCallsForGreeter = prometheus.NewCounterVec( prometheus.CounterOpts{ Name: "nrpc_client_calls_count", Help: "The count of calls made by the client.", ConstLabels: map[string]string{ "service": "Greeter", }, }, []string{"method", "encoding", "result_type"}) // The counts of requests handled by the server, classified by result type. serverRequestsForGreeter = prometheus.NewCounterVec( prometheus.CounterOpts{ Name: "nrpc_server_requests_count", Help: "The count of requests handled by the server.", ConstLabels: map[string]string{ "service": "Greeter", }, }, []string{"method", "encoding", "result_type"}) ) // GreeterHandler provides a NATS subscription handler that can serve a // subscription using a given GreeterServer implementation. type GreeterHandler struct { ctx context.Context workers *nrpc.WorkerPool nc nrpc.NatsConn server GreeterServer encodings []string } func NewGreeterHandler(ctx context.Context, nc nrpc.NatsConn, s GreeterServer) *GreeterHandler { return &GreeterHandler{ ctx: ctx, nc: nc, server: s, encodings: []string{"protobuf"}, } } func NewGreeterConcurrentHandler(workers *nrpc.WorkerPool, nc nrpc.NatsConn, s GreeterServer) *GreeterHandler { return &GreeterHandler{ workers: workers, nc: nc, server: s, } } // SetEncodings sets the output encodings when using a '*Publish' function func (h *GreeterHandler) SetEncodings(encodings []string) { h.encodings = encodings } func (h *GreeterHandler) Subject() string { return "Greeter.>" } func (h *GreeterHandler) Handler(msg *nats.Msg) { var ctx context.Context if h.workers != nil { ctx = h.workers.Context } else { ctx = h.ctx } request := nrpc.NewRequest(ctx, h.nc, msg.Subject, msg.Reply) // extract method name & encoding from subject _, _, name, tail, err := nrpc.ParseSubject( "", 0, "Greeter", 0, msg.Subject) if err != nil { log.Printf("GreeterHanlder: Greeter subject parsing failed: %v", err) return } request.MethodName = name request.SubjectTail = tail // call handler and form response var immediateError *nrpc.Error switch name { case "SayHello": _, request.Encoding, err = nrpc.ParseSubjectTail(0, request.SubjectTail) if err != nil { log.Printf("SayHelloHanlder: SayHello subject parsing failed: %v", err) break } var req HelloRequest if err := nrpc.Unmarshal(request.Encoding, msg.Data, &req); err != nil { log.Printf("SayHelloHandler: SayHello request unmarshal failed: %v", err) immediateError = &nrpc.Error{ Type: nrpc.Error_CLIENT, Message: "bad request received: " + err.Error(), } serverRequestsForGreeter.WithLabelValues( "SayHello", request.Encoding, "unmarshal_fail").Inc() } else { request.Handler = func(ctx context.Context)(proto.Message, error){ innerResp, err := h.server.SayHello(ctx, req) if err != nil { return nil, err } return &innerResp, err } } default: log.Printf("GreeterHandler: unknown name %q", name) immediateError = &nrpc.Error{ Type: nrpc.Error_CLIENT, Message: "unknown name: " + name, } serverRequestsForGreeter.WithLabelValues( "Greeter", request.Encoding, "name_fail").Inc() } request.AfterReply = func(request *nrpc.Request, success, replySuccess bool) { if !replySuccess { serverRequestsForGreeter.WithLabelValues( request.MethodName, request.Encoding, "sendreply_fail").Inc() } if success { serverRequestsForGreeter.WithLabelValues( request.MethodName, request.Encoding, "success").Inc() } else { serverRequestsForGreeter.WithLabelValues( request.MethodName, request.Encoding, "handler_fail").Inc() } // report metric to Prometheus serverHETForGreeter.WithLabelValues(request.MethodName).Observe( request.Elapsed().Seconds()) } if immediateError == nil { if h.workers != nil { // Try queuing the request if err := h.workers.QueueRequest(request); err != nil { log.Printf("nrpc: Error queuing the request: %s", err) } } else { // Run the handler synchronously request.RunAndReply() } } if immediateError != nil { if err := request.SendReply(nil, immediateError); err != nil { log.Printf("GreeterHandler: Greeter handler failed to publish the response: %s", err) serverRequestsForGreeter.WithLabelValues( request.MethodName, request.Encoding, "handler_fail").Inc() } serverHETForGreeter.WithLabelValues(request.MethodName).Observe( request.Elapsed().Seconds()) } else { } } type GreeterClient struct { nc nrpc.NatsConn Subject string Encoding string Timeout time.Duration } func NewGreeterClient(nc nrpc.NatsConn) *GreeterClient { return &GreeterClient{ nc: nc, Subject: "Greeter", Encoding: "protobuf", Timeout: 5 * time.Second, } } func (c *GreeterClient) SayHello(req HelloRequest) (resp HelloReply, err error) { start := time.Now() subject := c.Subject + "." + "SayHello" // call err = nrpc.Call(&req, &resp, c.nc, subject, c.Encoding, c.Timeout) if err != nil { clientCallsForGreeter.WithLabelValues( "SayHello", c.Encoding, "call_fail").Inc() return // already logged } // report total time taken to Prometheus elapsed := time.Since(start).Seconds() clientRCTForGreeter.WithLabelValues("SayHello").Observe(elapsed) clientCallsForGreeter.WithLabelValues( "SayHello", c.Encoding, "success").Inc() return } type Client struct { nc nrpc.NatsConn defaultEncoding string defaultTimeout time.Duration Greeter *GreeterClient } func NewClient(nc nrpc.NatsConn) *Client { c := Client{ nc: nc, defaultEncoding: "protobuf", defaultTimeout: 5*time.Second, } c.Greeter = NewGreeterClient(nc) return &c } func (c *Client) SetEncoding(encoding string) { c.defaultEncoding = encoding if c.Greeter != nil { c.Greeter.Encoding = encoding } } func (c *Client) SetTimeout(t time.Duration) { c.defaultTimeout = t if c.Greeter != nil { c.Greeter.Timeout = t } } func init() { // register metrics for service Greeter prometheus.MustRegister(clientRCTForGreeter) prometheus.MustRegister(serverHETForGreeter) prometheus.MustRegister(clientCallsForGreeter) prometheus.MustRegister(serverRequestsForGreeter) } ================================================ FILE: examples/metrics_helloworld/helloworld/helloworld.pb.go ================================================ // Copyright 2015 gRPC 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. // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.29.0 // protoc v4.22.2 // source: helloworld.proto package helloworld import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // The request message containing the user's name. type HelloRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` } func (x *HelloRequest) Reset() { *x = HelloRequest{} if protoimpl.UnsafeEnabled { mi := &file_helloworld_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *HelloRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*HelloRequest) ProtoMessage() {} func (x *HelloRequest) ProtoReflect() protoreflect.Message { mi := &file_helloworld_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use HelloRequest.ProtoReflect.Descriptor instead. func (*HelloRequest) Descriptor() ([]byte, []int) { return file_helloworld_proto_rawDescGZIP(), []int{0} } func (x *HelloRequest) GetName() string { if x != nil { return x.Name } return "" } // The response message containing the greetings type HelloReply struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` } func (x *HelloReply) Reset() { *x = HelloReply{} if protoimpl.UnsafeEnabled { mi := &file_helloworld_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *HelloReply) String() string { return protoimpl.X.MessageStringOf(x) } func (*HelloReply) ProtoMessage() {} func (x *HelloReply) ProtoReflect() protoreflect.Message { mi := &file_helloworld_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use HelloReply.ProtoReflect.Descriptor instead. func (*HelloReply) Descriptor() ([]byte, []int) { return file_helloworld_proto_rawDescGZIP(), []int{1} } func (x *HelloReply) GetMessage() string { if x != nil { return x.Message } return "" } var File_helloworld_proto protoreflect.FileDescriptor var file_helloworld_proto_rawDesc = []byte{ 0x0a, 0x10, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0a, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x22, 0x22, 0x0a, 0x0c, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x26, 0x0a, 0x0a, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x32, 0x49, 0x0a, 0x07, 0x47, 0x72, 0x65, 0x65, 0x74, 0x65, 0x72, 0x12, 0x3e, 0x0a, 0x08, 0x53, 0x61, 0x79, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x12, 0x18, 0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x42, 0x71, 0x0a, 0x1b, 0x69, 0x6f, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x42, 0x0f, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6e, 0x61, 0x74, 0x73, 0x2d, 0x72, 0x70, 0x63, 0x2f, 0x6e, 0x72, 0x70, 0x63, 0x2f, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2f, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( file_helloworld_proto_rawDescOnce sync.Once file_helloworld_proto_rawDescData = file_helloworld_proto_rawDesc ) func file_helloworld_proto_rawDescGZIP() []byte { file_helloworld_proto_rawDescOnce.Do(func() { file_helloworld_proto_rawDescData = protoimpl.X.CompressGZIP(file_helloworld_proto_rawDescData) }) return file_helloworld_proto_rawDescData } var file_helloworld_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_helloworld_proto_goTypes = []interface{}{ (*HelloRequest)(nil), // 0: helloworld.HelloRequest (*HelloReply)(nil), // 1: helloworld.HelloReply } var file_helloworld_proto_depIdxs = []int32{ 0, // 0: helloworld.Greeter.SayHello:input_type -> helloworld.HelloRequest 1, // 1: helloworld.Greeter.SayHello:output_type -> helloworld.HelloReply 1, // [1:2] is the sub-list for method output_type 0, // [0:1] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name } func init() { file_helloworld_proto_init() } func file_helloworld_proto_init() { if File_helloworld_proto != nil { return } if !protoimpl.UnsafeEnabled { file_helloworld_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*HelloRequest); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_helloworld_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*HelloReply); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_helloworld_proto_rawDesc, NumEnums: 0, NumMessages: 2, NumExtensions: 0, NumServices: 1, }, GoTypes: file_helloworld_proto_goTypes, DependencyIndexes: file_helloworld_proto_depIdxs, MessageInfos: file_helloworld_proto_msgTypes, }.Build() File_helloworld_proto = out.File file_helloworld_proto_rawDesc = nil file_helloworld_proto_goTypes = nil file_helloworld_proto_depIdxs = nil } ================================================ FILE: examples/metrics_helloworld/helloworld/helloworld.proto ================================================ // Copyright 2015 gRPC 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. syntax = "proto3"; option java_multiple_files = true; option java_package = "io.grpc.examples.helloworld"; option java_outer_classname = "HelloWorldProto"; package helloworld; option go_package = "github.com/nats-rpc/nrpc/examples/metrics_helloworld/helloworld"; // The greeting service definition. service Greeter { // Sends a greeting rpc SayHello (HelloRequest) returns (HelloReply) {} } // The request message containing the user's name. message HelloRequest { string name = 1; } // The response message containing the greetings message HelloReply { string message = 1; } ================================================ FILE: examples/metrics_helloworld/metrics_greeter_client/main.go ================================================ package main import ( "fmt" "log" "net/http" "time" "github.com/nats-io/nats.go" // This is the package containing the generated *.pb.go and *.nrpc.go // files. "github.com/nats-rpc/nrpc/examples/metrics_helloworld/helloworld" // If you've used the prometheus plugin when generating the code, you // can import the HTTP handler of Prometheus to serve up the metrics. "github.com/prometheus/client_golang/prometheus/promhttp" ) func main() { // Connect to the NATS server. nc, err := nats.Connect(nats.DefaultURL, nats.Timeout(5*time.Second)) if err != nil { log.Fatal(err) } defer nc.Close() // This is our generated client. cli := helloworld.NewGreeterClient(nc) // Contact the server and print out its response. resp, err := cli.SayHello(helloworld.HelloRequest{Name: "world"}) if err != nil { log.Fatal(err) } // print fmt.Printf("Greeting: %s\n", resp.Message) // Do this block only if you generated the code with the prometheus plugin. fmt.Println("Check metrics at http://localhost:6061/metrics. Hit ^C to exit.") http.Handle("/metrics", promhttp.Handler()) http.ListenAndServe(":6061", nil) } ================================================ FILE: examples/metrics_helloworld/metrics_greeter_server/main.go ================================================ package main import ( "context" "errors" "fmt" "log" "math/rand" "net/http" "os" "os/signal" "time" "github.com/nats-io/nats.go" // This is the package containing the generated *.pb.go and *.nrpc.go // files. "github.com/nats-rpc/nrpc/examples/metrics_helloworld/helloworld" // If you've used the prometheus plugin when generating the code, you // can import the HTTP handler of Prometheus to serve up the metrics. "github.com/prometheus/client_golang/prometheus/promhttp" ) // server implements the helloworld.GreeterServer interface. type server struct{} // SayHello is an implementation of the SayHello method from the definition of // the Greeter service. func (s *server) SayHello(ctx context.Context, req helloworld.HelloRequest) (resp helloworld.HelloReply, err error) { resp.Message = "Hello " + req.Name if rand.Intn(10) < 7 { // will fail 70% of the time err = errors.New("random failure simulated") } time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) // random delay return } func main() { // Connect to the NATS server. nc, err := nats.Connect(nats.DefaultURL, nats.Timeout(5*time.Second)) if err != nil { log.Fatal(err) } defer nc.Close() // Our server implementation. s := &server{} rand.Seed(time.Now().UnixNano()) // The NATS handler from the helloworld.nrpc.proto file. h := helloworld.NewGreeterHandler(context.TODO(), nc, s) // Start a NATS subscription using the handler. You can also use the // QueueSubscribe() method for a load-balanced set of servers. sub, err := nc.Subscribe(h.Subject(), h.Handler) if err != nil { log.Fatal(err) } defer sub.Unsubscribe() // Do this block only if you generated the code with the prometheus plugin. http.Handle("/metrics", promhttp.Handler()) go http.ListenAndServe(":6060", nil) // Keep running until ^C. fmt.Println("server is running, ^C quits.") c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt) <-c close(c) } ================================================ FILE: examples/nooption/nooption.go ================================================ package nooption //go:generate protoc --go_out . --go_opt=paths=source_relative --nrpc_out . --nrpc_opt=paths=source_relative nooption.proto ================================================ FILE: examples/nooption/nooption.nrpc.go ================================================ // This code was autogenerated from nooption.proto, do not edit. package nooption import ( "context" "log" "time" "google.golang.org/protobuf/proto" "github.com/nats-io/nats.go" "github.com/nats-rpc/nrpc" ) // GreeterServer is the interface that providers of the service // Greeter should implement. type GreeterServer interface { SayHello(ctx context.Context, req HelloRequest) (resp HelloReply, err error) } // GreeterHandler provides a NATS subscription handler that can serve a // subscription using a given GreeterServer implementation. type GreeterHandler struct { ctx context.Context workers *nrpc.WorkerPool nc nrpc.NatsConn server GreeterServer encodings []string } func NewGreeterHandler(ctx context.Context, nc nrpc.NatsConn, s GreeterServer) *GreeterHandler { return &GreeterHandler{ ctx: ctx, nc: nc, server: s, encodings: []string{"protobuf"}, } } func NewGreeterConcurrentHandler(workers *nrpc.WorkerPool, nc nrpc.NatsConn, s GreeterServer) *GreeterHandler { return &GreeterHandler{ workers: workers, nc: nc, server: s, } } // SetEncodings sets the output encodings when using a '*Publish' function func (h *GreeterHandler) SetEncodings(encodings []string) { h.encodings = encodings } func (h *GreeterHandler) Subject() string { return "Greeter.>" } func (h *GreeterHandler) Handler(msg *nats.Msg) { var ctx context.Context if h.workers != nil { ctx = h.workers.Context } else { ctx = h.ctx } request := nrpc.NewRequest(ctx, h.nc, msg.Subject, msg.Reply) // extract method name & encoding from subject _, _, name, tail, err := nrpc.ParseSubject( "", 0, "Greeter", 0, msg.Subject) if err != nil { log.Printf("GreeterHanlder: Greeter subject parsing failed: %v", err) return } request.MethodName = name request.SubjectTail = tail // call handler and form response var immediateError *nrpc.Error switch name { case "SayHello": _, request.Encoding, err = nrpc.ParseSubjectTail(0, request.SubjectTail) if err != nil { log.Printf("SayHelloHanlder: SayHello subject parsing failed: %v", err) break } var req HelloRequest if err := nrpc.Unmarshal(request.Encoding, msg.Data, &req); err != nil { log.Printf("SayHelloHandler: SayHello request unmarshal failed: %v", err) immediateError = &nrpc.Error{ Type: nrpc.Error_CLIENT, Message: "bad request received: " + err.Error(), } } else { request.Handler = func(ctx context.Context)(proto.Message, error){ innerResp, err := h.server.SayHello(ctx, req) if err != nil { return nil, err } return &innerResp, err } } default: log.Printf("GreeterHandler: unknown name %q", name) immediateError = &nrpc.Error{ Type: nrpc.Error_CLIENT, Message: "unknown name: " + name, } } if immediateError == nil { if h.workers != nil { // Try queuing the request if err := h.workers.QueueRequest(request); err != nil { log.Printf("nrpc: Error queuing the request: %s", err) } } else { // Run the handler synchronously request.RunAndReply() } } if immediateError != nil { if err := request.SendReply(nil, immediateError); err != nil { log.Printf("GreeterHandler: Greeter handler failed to publish the response: %s", err) } } else { } } type GreeterClient struct { nc nrpc.NatsConn Subject string Encoding string Timeout time.Duration } func NewGreeterClient(nc nrpc.NatsConn) *GreeterClient { return &GreeterClient{ nc: nc, Subject: "Greeter", Encoding: "protobuf", Timeout: 5 * time.Second, } } func (c *GreeterClient) SayHello(req HelloRequest) (resp HelloReply, err error) { subject := c.Subject + "." + "SayHello" // call err = nrpc.Call(&req, &resp, c.nc, subject, c.Encoding, c.Timeout) if err != nil { return // already logged } return } type Client struct { nc nrpc.NatsConn defaultEncoding string defaultTimeout time.Duration Greeter *GreeterClient } func NewClient(nc nrpc.NatsConn) *Client { c := Client{ nc: nc, defaultEncoding: "protobuf", defaultTimeout: 5*time.Second, } c.Greeter = NewGreeterClient(nc) return &c } func (c *Client) SetEncoding(encoding string) { c.defaultEncoding = encoding if c.Greeter != nil { c.Greeter.Encoding = encoding } } func (c *Client) SetTimeout(t time.Duration) { c.defaultTimeout = t if c.Greeter != nil { c.Greeter.Timeout = t } } ================================================ FILE: examples/nooption/nooption.pb.go ================================================ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.29.0 // protoc v4.22.2 // source: nooption.proto package nooption import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // The request message containing the user's name. type HelloRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` } func (x *HelloRequest) Reset() { *x = HelloRequest{} if protoimpl.UnsafeEnabled { mi := &file_nooption_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *HelloRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*HelloRequest) ProtoMessage() {} func (x *HelloRequest) ProtoReflect() protoreflect.Message { mi := &file_nooption_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use HelloRequest.ProtoReflect.Descriptor instead. func (*HelloRequest) Descriptor() ([]byte, []int) { return file_nooption_proto_rawDescGZIP(), []int{0} } func (x *HelloRequest) GetName() string { if x != nil { return x.Name } return "" } // The response message containing the greetings type HelloReply struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` } func (x *HelloReply) Reset() { *x = HelloReply{} if protoimpl.UnsafeEnabled { mi := &file_nooption_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *HelloReply) String() string { return protoimpl.X.MessageStringOf(x) } func (*HelloReply) ProtoMessage() {} func (x *HelloReply) ProtoReflect() protoreflect.Message { mi := &file_nooption_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use HelloReply.ProtoReflect.Descriptor instead. func (*HelloReply) Descriptor() ([]byte, []int) { return file_nooption_proto_rawDescGZIP(), []int{1} } func (x *HelloReply) GetMessage() string { if x != nil { return x.Message } return "" } var File_nooption_proto protoreflect.FileDescriptor var file_nooption_proto_rawDesc = []byte{ 0x0a, 0x0e, 0x6e, 0x6f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x6e, 0x6f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x22, 0x0a, 0x0c, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x26, 0x0a, 0x0a, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x32, 0x45, 0x0a, 0x07, 0x47, 0x72, 0x65, 0x65, 0x74, 0x65, 0x72, 0x12, 0x3a, 0x0a, 0x08, 0x53, 0x61, 0x79, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x12, 0x16, 0x2e, 0x6e, 0x6f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x6e, 0x6f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x42, 0x2c, 0x5a, 0x2a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6e, 0x61, 0x74, 0x73, 0x2d, 0x72, 0x70, 0x63, 0x2f, 0x6e, 0x72, 0x70, 0x63, 0x2f, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x2f, 0x6e, 0x6f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( file_nooption_proto_rawDescOnce sync.Once file_nooption_proto_rawDescData = file_nooption_proto_rawDesc ) func file_nooption_proto_rawDescGZIP() []byte { file_nooption_proto_rawDescOnce.Do(func() { file_nooption_proto_rawDescData = protoimpl.X.CompressGZIP(file_nooption_proto_rawDescData) }) return file_nooption_proto_rawDescData } var file_nooption_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_nooption_proto_goTypes = []interface{}{ (*HelloRequest)(nil), // 0: nooption.HelloRequest (*HelloReply)(nil), // 1: nooption.HelloReply } var file_nooption_proto_depIdxs = []int32{ 0, // 0: nooption.Greeter.SayHello:input_type -> nooption.HelloRequest 1, // 1: nooption.Greeter.SayHello:output_type -> nooption.HelloReply 1, // [1:2] is the sub-list for method output_type 0, // [0:1] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name } func init() { file_nooption_proto_init() } func file_nooption_proto_init() { if File_nooption_proto != nil { return } if !protoimpl.UnsafeEnabled { file_nooption_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*HelloRequest); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_nooption_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*HelloReply); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_nooption_proto_rawDesc, NumEnums: 0, NumMessages: 2, NumExtensions: 0, NumServices: 1, }, GoTypes: file_nooption_proto_goTypes, DependencyIndexes: file_nooption_proto_depIdxs, MessageInfos: file_nooption_proto_msgTypes, }.Build() File_nooption_proto = out.File file_nooption_proto_rawDesc = nil file_nooption_proto_goTypes = nil file_nooption_proto_depIdxs = nil } ================================================ FILE: examples/nooption/nooption.proto ================================================ syntax = "proto3"; package nooption; option go_package = "github.com/nats-rpc/nrpc/examples/nooption"; service Greeter { // Sends a greeting rpc SayHello (HelloRequest) returns (HelloReply) {} } // The request message containing the user's name. message HelloRequest { string name = 1; } // The response message containing the greetings message HelloReply { string message = 1; } ================================================ FILE: go.mod ================================================ module github.com/nats-rpc/nrpc go 1.11 require ( github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/nats-io/nats-server/v2 v2.9.23 github.com/nats-io/nats.go v1.28.0 github.com/prometheus/client_golang v1.14.0 github.com/prometheus/common v0.42.0 // indirect github.com/prometheus/procfs v0.9.0 // indirect github.com/stretchr/testify v1.8.1 google.golang.org/protobuf v1.33.0 ) ================================================ FILE: go.sum ================================================ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/alecthomas/kingpin/v2 v2.3.1/go.mod h1:oYL5vtsvEHZGHxU7DMp32Dvx+qL+ptGn6lWaot2vCNE= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g= github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nats-io/jwt/v2 v2.5.0 h1:WQQ40AAlqqfx+f6ku+i0pOVm+ASirD4fUh+oQsiE9Ak= github.com/nats-io/jwt/v2 v2.5.0/go.mod h1:24BeQtRwxRV8ruvC4CojXlx/WQ/VjuwlYiH+vu/+ibI= github.com/nats-io/nats-server/v2 v2.9.23 h1:6Wj6H6QpP9FMlpCyWUaNu2yeZ/qGj+mdRkZ1wbikExU= github.com/nats-io/nats-server/v2 v2.9.23/go.mod h1:wEjrEy9vnqIGE4Pqz4/c75v9Pmaq7My2IgFmnykc4C0= github.com/nats-io/nats.go v1.28.0 h1:Th4G6zdsz2d0OqXdfzKLClo6bOfoI/b1kInhRtFIy5c= github.com/nats-io/nats.go v1.28.0/go.mod h1:XpbWUlOElGwTYbMR7imivs7jJj9GtK7ypv321Wp6pjc= github.com/nats-io/nkeys v0.4.4 h1:xvBJ8d69TznjcQl9t6//Q5xXuVhyYiSos6RPtvQNTwA= github.com/nats-io/nkeys v0.4.4/go.mod h1:XUkxdLPTufzlihbamfzQ7mw/VGx6ObUs+0bN5sNvt64= github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/xhit/go-str2duration v1.2.0/go.mod h1:3cPSlfZlUHVlneIVfePFWcJZsuwf+P1v2SRTV4cUmp4= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= ================================================ FILE: helloworld_test.go ================================================ package nrpc import ( "bytes" "os" "os/exec" "testing" "time" ) func TestHelloWorldExample(t *testing.T) { // make sure protoc-gen-nrpc is up to date installGenRPC := exec.Command("go", "install", "./protoc-gen-nrpc") if out, err := installGenRPC.CombinedOutput(); err != nil { t.Fatal("Install protoc-gen-nrpc failed", err, ":\n", string(out)) } // generate the sources generate := exec.Command("go", "generate", "./examples/helloworld/helloworld") if out, err := generate.CombinedOutput(); err != nil { t.Fatal("Generate failed", err, ":\n", string(out)) } // build buildServer := exec.Command("go", "build", "-o", "./examples/helloworld/greeter_server/greeter_server", "./examples/helloworld/greeter_server") if out, err := buildServer.CombinedOutput(); err != nil { t.Fatal("Buid server failed", err, string(out)) } buildClient := exec.Command("go", "build", "-o", "./examples/helloworld/greeter_client/greeter_client", "./examples/helloworld/greeter_client") if out, err := buildClient.CombinedOutput(); err != nil { t.Fatal("Buid client failed", err, string(out)) } // run the server server := exec.Command("./examples/helloworld/greeter_server/greeter_server", NatsURL) var serverStdout bytes.Buffer server.Stdout = &serverStdout server.Start() defer func() { if server.Process != nil { server.Process.Signal(os.Interrupt) } if err := server.Wait(); err != nil { t.Error("Server run failed:", err) t.Error("Server output:", serverStdout.String()) } }() // Give the server a little time to be ready to handle requests time.Sleep(250 * time.Millisecond) // run the client and check its output client := exec.Command("./examples/helloworld/greeter_client/greeter_client", NatsURL) timeout := time.AfterFunc(time.Second, func() { client.Process.Kill() }) out, err := client.CombinedOutput() timeout.Stop() if err != nil { t.Fatal("Run client failed with:", err, ", output was:\n", string(out)) } expectedOuput := "Greeting: Hello world\n" if string(out) != expectedOuput { t.Errorf("Wrong client output. Expected '%s', got '%s'", expectedOuput, string(out)) } } ================================================ FILE: nrpc.go ================================================ package nrpc import ( "bytes" "context" "encoding/json" "errors" "fmt" "log" "runtime/debug" "strings" "sync" "time" "github.com/nats-io/nats.go" jsonpb "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/proto" ) const ( statusHeader = "Status" noResponderStatus = "503" ) // ContextKey type for storing values into context.Context type ContextKey int // ErrStreamInvalidMsgCount is when a stream reply gets a wrong number of messages var ErrStreamInvalidMsgCount = errors.New("Stream reply received an incorrect number of messages") //go:generate protoc --go_out=. --go_opt=paths=source_relative nrpc.proto type NatsConn interface { Publish(subj string, data []byte) error PublishRequest(subj, reply string, data []byte) error Request(subj string, data []byte, timeout time.Duration) (*nats.Msg, error) ChanSubscribe(subj string, ch chan *nats.Msg) (*nats.Subscription, error) Subscribe(subj string, handler nats.MsgHandler) (*nats.Subscription, error) SubscribeSync(subj string) (*nats.Subscription, error) } // ReplyInboxMaker returns a new inbox subject for a given nats connection. type ReplyInboxMaker func(NatsConn) string // GetReplyInbox is used by StreamCall to get a inbox subject // It can be changed by a client lib that needs custom inbox subjects var GetReplyInbox ReplyInboxMaker = func(NatsConn) string { return nats.NewInbox() } func (e *Error) Error() string { return fmt.Sprintf("%s error: %s", Error_Type_name[int32(e.Type)], e.Message) } func Unmarshal(encoding string, data []byte, msg proto.Message) error { switch encoding { case "protobuf": return proto.Unmarshal(data, msg) case "json": return jsonpb.Unmarshal(data, msg) default: return errors.New("Invalid encoding: " + encoding) } } func UnmarshalResponse(encoding string, data []byte, msg proto.Message) error { switch encoding { case "protobuf": if len(data) > 0 && data[0] == 0 { var repErr Error if err := proto.Unmarshal(data[1:], &repErr); err != nil { return err } return &repErr } return proto.Unmarshal(data, msg) case "json": if len(data) > 13 && bytes.Equal(data[:13], []byte("{\"__error__\":")) { var rep map[string]json.RawMessage if err := json.Unmarshal(data, &rep); err != nil { return err } errbuf, ok := rep["__error__"] if !ok { panic("invalid error message") } var nrpcErr Error if err := jsonpb.Unmarshal(errbuf, &nrpcErr); err != nil { return err } return &nrpcErr } return jsonpb.Unmarshal(data, msg) default: return errors.New("Invalid encoding: " + encoding) } } func Marshal(encoding string, msg proto.Message) ([]byte, error) { switch encoding { case "protobuf": return proto.Marshal(msg) case "json": return jsonpb.Marshal(msg) default: return nil, errors.New("Invalid encoding: " + encoding) } } func MarshalErrorResponse(encoding string, repErr *Error) ([]byte, error) { switch encoding { case "protobuf": b, err := proto.Marshal(repErr) if err != nil { return nil, err } return append([]byte{0}, b...), nil case "json": b, err := jsonpb.Marshal(repErr) if err != nil { return nil, err } return json.Marshal(map[string]json.RawMessage{ "__error__": json.RawMessage(b), }) default: return nil, errors.New("Invalid encoding: " + encoding) } } func ParseSubject( packageSubject string, packageParamsCount int, serviceSubject string, serviceParamsCount int, subject string, ) (packageParams []string, serviceParams []string, name string, tail []string, err error, ) { packageSubjectDepth := 0 if packageSubject != "" { packageSubjectDepth = strings.Count(packageSubject, ".") + 1 } serviceSubjectDepth := strings.Count(serviceSubject, ".") + 1 subjectMinSize := packageSubjectDepth + packageParamsCount + serviceSubjectDepth + serviceParamsCount + 1 tokens := strings.Split(subject, ".") if len(tokens) < subjectMinSize { err = fmt.Errorf( "Invalid subject len. Expects number of parts >= %d, got %d", subjectMinSize, len(tokens)) return } if packageSubject != "" { for i, packageSubjectPart := range strings.Split(packageSubject, ".") { if tokens[i] != packageSubjectPart { err = fmt.Errorf( "Invalid subject prefix. Expected '%s', got '%s'", packageSubjectPart, tokens[i]) return } } tokens = tokens[packageSubjectDepth:] } packageParams = tokens[0:packageParamsCount] tokens = tokens[packageParamsCount:] for i, serviceSubjectPart := range strings.Split(serviceSubject, ".") { if tokens[i] != serviceSubjectPart { err = fmt.Errorf( "Invalid subject. Service should be '%s', got '%s'", serviceSubjectPart, tokens[i]) return } } tokens = tokens[serviceSubjectDepth:] serviceParams = tokens[0:serviceParamsCount] tokens = tokens[serviceParamsCount:] name = tokens[0] tokens = tokens[1:] tail = tokens return } func ParseSubjectTail( methodParamsCount int, tail []string, ) ( methodParams []string, encoding string, err error, ) { if len(tail) < methodParamsCount || len(tail) > methodParamsCount+1 { err = fmt.Errorf( "Invalid subject tail length. Expects %d or %d parts, got %d", methodParamsCount, methodParamsCount+1, len(tail), ) return } methodParams = tail[:methodParamsCount] tail = tail[methodParamsCount:] switch len(tail) { case 0: encoding = "protobuf" case 1: encoding = tail[0] default: panic("Got extra tokens, which should be impossible at this point") } return } func Call(req proto.Message, rep proto.Message, nc NatsConn, subject string, encoding string, timeout time.Duration) error { // encode request rawRequest, err := Marshal(encoding, req) if err != nil { log.Printf("nrpc: inner request marshal failed: %v", err) return err } if encoding != "protobuf" { subject += "." + encoding } // call if _, noreply := rep.(*NoReply); noreply { err := nc.Publish(subject, rawRequest) if err != nil { log.Printf("nrpc: nats publish failed: %v", err) } return err } msg, err := nc.Request(subject, rawRequest, timeout) if err != nil { log.Printf("nrpc: nats request failed: %v", err) return err } data := msg.Data if err := UnmarshalResponse(encoding, data, rep); err != nil { if _, isError := err.(*Error); !isError { log.Printf("nrpc: response unmarshal failed: %v", err) } return err } return nil } func Poll( req proto.Message, rep proto.Message, nc NatsConn, subject string, encoding string, timeout time.Duration, maxreplies int, cb func() error, ) error { // encode request rawRequest, err := Marshal(encoding, req) if err != nil { log.Printf("nrpc: inner request marshal failed: %v", err) return err } if encoding != "protobuf" { subject += "." + encoding } reply := GetReplyInbox(nc) replyC := make(chan *nats.Msg) defer close(replyC) sub, err := nc.ChanSubscribe(reply, replyC) defer func() { if err := sub.Unsubscribe(); err != nil { log.Printf("nrpc: nats unsubscribe failed: %v", err) } }() if err := nc.PublishRequest(subject, reply, rawRequest); err != nil { log.Printf("nrpc: nats request failed: %v", err) return err } timeoutC := time.After(timeout) var replyCount int for { select { case msg := <-replyC: replyCount++ data := msg.Data if err := UnmarshalResponse(encoding, data, rep); err != nil { if _, isError := err.(*Error); !isError { log.Printf("nrpc: response unmarshal failed: %v", err) } return err } if err := cb(); err != nil { return err } if replyCount == maxreplies { return nil } case <-timeoutC: return nats.ErrTimeout } } } const ( // RequestContextKey is the key for string the request into the context RequestContextKey ContextKey = iota ) // NewRequest creates a Request instance func NewRequest(ctx context.Context, conn NatsConn, subject string, replySubject string) *Request { return &Request{ Context: ctx, Conn: conn, Subject: subject, ReplySubject: replySubject, CreatedAt: time.Now(), } } // GetRequest returns the Request associated with a context, or nil if absent func GetRequest(ctx context.Context) *Request { request, _ := ctx.Value(RequestContextKey).(*Request) return request } // Request is a server-side incoming request type Request struct { Context context.Context Conn NatsConn isStreamedReply bool KeepStreamAlive *KeepStreamAlive StreamContext context.Context StreamCancel func() StreamMsgCount uint32 streamLock sync.Mutex Subject string MethodName string SubjectTail []string CreatedAt time.Time StartedAt time.Time Encoding string NoReply bool ReplySubject string PackageParams map[string]string ServiceParams map[string]string AfterReply func(r *Request, success bool, replySuccess bool) Handler func(context.Context) (proto.Message, error) } // Elapsed duration since request was started func (r *Request) Elapsed() time.Duration { return time.Since(r.CreatedAt) } // Run the handler and capture any error. Returns the response or the error // that should be returned to the caller func (r *Request) Run() (msg proto.Message, replyError *Error) { r.StartedAt = time.Now() ctx := r.Context if r.StreamedReply() { ctx = r.StreamContext } ctx = context.WithValue(ctx, RequestContextKey, r) msg, replyError = CaptureErrors( func() (proto.Message, error) { return r.Handler(ctx) }) return } // RunAndReply calls Run() and send the reply back to the caller func (r *Request) RunAndReply() { var failed, replyFailed bool // In case RunAndReply was called directly, we may need to initialize the // streamed reply r.setupStreamedReply() resp, replyError := r.Run() if replyError != nil { failed = true log.Printf("%s handler failed: %s", r.MethodName, replyError) } if !r.NoReply { if err := r.SendReply(resp, replyError); err != nil { replyFailed = true log.Printf("%s failed to publish the response: %s", r.MethodName, err) } } if r.AfterReply != nil { r.AfterReply(r, !failed, !replyFailed) } } // PackageParam returns a package parameter value, or "" if absent func (r *Request) PackageParam(key string) string { if r == nil || r.PackageParams == nil { return "" } return r.PackageParams[key] } // ServiceParam returns a package parameter value, or "" if absent func (r *Request) ServiceParam(key string) string { if r == nil || r.ServiceParams == nil { return "" } return r.ServiceParams[key] } // SetPackageParam sets a package param value func (r *Request) SetPackageParam(key, value string) { if r.PackageParams == nil { r.PackageParams = make(map[string]string) } r.PackageParams[key] = value } // SetServiceParam sets a service param value func (r *Request) SetServiceParam(key, value string) { if r.ServiceParams == nil { r.ServiceParams = make(map[string]string) } r.ServiceParams[key] = value } // EnableStreamedReply enables the streamed reply mode func (r *Request) EnableStreamedReply() { r.isStreamedReply = true } // setupStreamedReply initializes the reply stream if needed. func (r *Request) setupStreamedReply() { r.streamLock.Lock() defer r.streamLock.Unlock() if !r.StreamedReply() || r.KeepStreamAlive != nil { return } r.StreamContext, r.StreamCancel = context.WithCancel(r.Context) r.KeepStreamAlive = NewKeepStreamAlive( r.Conn, r.ReplySubject, r.Encoding, r.StreamCancel) } // StreamedReply returns true if the request reply is streamed func (r *Request) StreamedReply() bool { return r.isStreamedReply } // SendStreamReply send a reply a part of a stream func (r *Request) SendStreamReply(msg proto.Message) { if err := r.sendReply(msg, nil); err != nil { log.Printf("nrpc: error publishing response: %s", err) r.StreamCancel() return } r.StreamMsgCount++ } // SendReply sends a reply to the caller func (r *Request) SendReply(resp proto.Message, withError *Error) error { if r.StreamedReply() { r.KeepStreamAlive.Stop() if withError == nil { return r.sendReply( nil, &Error{Type: Error_EOS, MsgCount: r.StreamMsgCount}, ) } } return r.sendReply(resp, withError) } // sendReply sends a reply to the caller func (r *Request) sendReply(resp proto.Message, withError *Error) error { return Publish(resp, withError, r.Conn, r.ReplySubject, r.Encoding) } // SendErrorTooBusy cancels the request with a 'SERVERTOOBUSY' error func (r *Request) SendErrorTooBusy(msg string) error { return r.SendReply(nil, &Error{ Type: Error_SERVERTOOBUSY, Message: msg, }) } var ErrEOS = errors.New("End of stream") var ErrCanceled = errors.New("Call canceled") func NewStreamCallSubscription( ctx context.Context, nc NatsConn, encoding string, subject string, timeout time.Duration, ) (*StreamCallSubscription, error) { sub := StreamCallSubscription{ ctx: ctx, nc: nc, encoding: encoding, subject: subject, timeout: timeout, timeoutT: time.NewTimer(timeout), closed: false, subCh: make(chan *nats.Msg, 256), nextCh: make(chan *nats.Msg), quit: make(chan struct{}), errCh: make(chan error, 1), } ssub, err := nc.ChanSubscribe(subject, sub.subCh) if err != nil { return nil, err } go sub.loop(ssub) return &sub, nil } type StreamCallSubscription struct { ctx context.Context nc NatsConn encoding string subject string timeout time.Duration timeoutT *time.Timer closed bool subCh chan *nats.Msg nextCh chan *nats.Msg quit chan struct{} errCh chan error msgCount uint32 } func (sub *StreamCallSubscription) stop() { close(sub.quit) } func (sub *StreamCallSubscription) loop(ssub *nats.Subscription) { hbSubject := sub.subject + ".heartbeat" ticker := time.NewTicker(time.Second) defer ticker.Stop() defer ssub.Unsubscribe() for { select { case msg := <-sub.subCh: sub.timeoutT.Reset(sub.timeout) if len(msg.Data) == 1 && msg.Data[0] == 0 { break } // Check for no responder status. if len(msg.Data) == 0 && msg.Header.Get(statusHeader) == noResponderStatus { sub.errCh <- nats.ErrNoResponders return } sub.nextCh <- msg case <-sub.timeoutT.C: sub.errCh <- nats.ErrTimeout return case <-sub.ctx.Done(): // send a 'lastbeat' and quit b, err := Marshal(sub.encoding, &HeartBeat{Lastbeat: true}) if err != nil { err = fmt.Errorf("Error marshaling heartbeat: %s", err) sub.errCh <- err return } if err := sub.nc.Publish(hbSubject, b); err != nil { err = fmt.Errorf("Error sending heartbeat: %s", err) sub.errCh <- err return } sub.errCh <- ErrCanceled return case <-ticker.C: msg, err := Marshal(sub.encoding, &HeartBeat{}) if err != nil { err = fmt.Errorf("Error marshaling heartbeat: %s", err) sub.errCh <- err return } if err := sub.nc.Publish(hbSubject, msg); err != nil { err = fmt.Errorf("Error sending heartbeat: %s", err) sub.errCh <- err return } case <-sub.quit: return } } } func (sub *StreamCallSubscription) Next(rep proto.Message) error { if sub.closed { return nats.ErrBadSubscription } select { case err := <-sub.errCh: sub.closed = true return err case msg := <-sub.nextCh: if err := UnmarshalResponse(sub.encoding, msg.Data, rep); err != nil { sub.stop() sub.closed = true if nrpcErr, ok := err.(*Error); ok { if nrpcErr.GetMsgCount() != sub.msgCount { log.Printf( "nrpc: received invalid number of messages. Expected %d, got %d", nrpcErr.GetMsgCount(), sub.msgCount) } if nrpcErr.GetType() == Error_EOS { if nrpcErr.GetMsgCount() != sub.msgCount { return ErrStreamInvalidMsgCount } return ErrEOS } } else { log.Printf("nrpc: response unmarshal failed: %v", err) } return err } sub.msgCount++ } return nil } func StreamCall(ctx context.Context, nc NatsConn, subject string, req proto.Message, encoding string, timeout time.Duration) (*StreamCallSubscription, error) { rawRequest, err := Marshal(encoding, req) if err != nil { log.Printf("nrpc: inner request marshal failed: %v", err) return nil, err } if encoding != "protobuf" { subject += "." + encoding } reply := GetReplyInbox(nc) streamSub, err := NewStreamCallSubscription(ctx, nc, encoding, reply, timeout) if err != nil { return nil, err } if err := nc.PublishRequest(subject, reply, rawRequest); err != nil { streamSub.stop() return nil, err } return streamSub, nil } func Publish(resp proto.Message, withError *Error, nc NatsConn, subject string, encoding string) error { var rawResponse []byte var err error if withError != nil { rawResponse, err = MarshalErrorResponse(encoding, withError) } else { rawResponse, err = Marshal(encoding, resp) } if err != nil { log.Printf("nrpc: rpc response marshal failed: %v", err) return err } // send response if err := nc.Publish(subject, rawResponse); err != nil { log.Printf("nrpc: response publish failed: %v", err) return err } return nil } // CaptureErrors runs a handler and convert error and panics into proper Error func CaptureErrors(fn func() (proto.Message, error)) (msg proto.Message, replyError *Error) { defer func() { if r := recover(); r != nil { log.Printf("Caught panic: %s\n%s", r, debug.Stack()) replyError = &Error{ Type: Error_SERVER, Message: fmt.Sprint(r), } } }() var err error msg, err = fn() if err != nil { var ok bool if replyError, ok = err.(*Error); !ok { replyError = &Error{ Type: Error_CLIENT, Message: err.Error(), } } } return } func NewKeepStreamAlive(nc NatsConn, subject string, encoding string, onError func()) *KeepStreamAlive { k := KeepStreamAlive{ nc: nc, subject: subject, encoding: encoding, c: make(chan struct{}), onError: onError, } go k.loop() return &k } type KeepStreamAlive struct { nc NatsConn subject string encoding string c chan struct{} onError func() } func (k *KeepStreamAlive) Stop() { close(k.c) } func (k *KeepStreamAlive) loop() { hbChan := make(chan *nats.Msg, 256) hbSub, err := k.nc.ChanSubscribe(k.subject+".heartbeat", hbChan) if err != nil { log.Printf("nrpc: could not subscribe to heartbeat: %s", err) k.onError() } defer func() { if err := hbSub.Unsubscribe(); err != nil { log.Printf("nrpc: error unsubscribing from heartbeat: %s", err) } }() hbDelay := 0 ticker := time.NewTicker(time.Second) for { select { case msg := <-hbChan: var hb HeartBeat if err := Unmarshal(k.encoding, msg.Data, &hb); err != nil { log.Printf("nrpc: error unmarshaling heartbeat: %s", err) ticker.Stop() k.onError() return } if hb.Lastbeat { log.Printf("nrpc: client canceled the streamed reply. (%s)", k.subject) ticker.Stop() k.onError() return } hbDelay = 0 case <-ticker.C: hbDelay++ if hbDelay >= 5 { log.Printf("nrpc: No heartbeat received in 5 seconds. Canceling.") ticker.Stop() k.onError() return } if err := k.nc.Publish(k.subject, []byte{0}); err != nil { log.Printf("nrpc: error publishing response: %s", err) ticker.Stop() k.onError() return } case <-k.c: ticker.Stop() return } } } // WorkerPool is a pool of workers type WorkerPool struct { Context context.Context contextCancel context.CancelFunc queue chan *Request schedule chan *Request waitGroup sync.WaitGroup m sync.Mutex size uint maxPending uint maxPendingDuration time.Duration } // NewWorkerPool creates a pool of workers func NewWorkerPool( ctx context.Context, size uint, maxPending uint, maxPendingDuration time.Duration, ) *WorkerPool { nCtx, cancel := context.WithCancel(ctx) pool := WorkerPool{ Context: nCtx, contextCancel: cancel, queue: make(chan *Request, maxPending), schedule: make(chan *Request), maxPending: maxPending, maxPendingDuration: maxPendingDuration, } pool.waitGroup.Add(1) go pool.scheduler() pool.SetSize(size) return &pool } func (pool *WorkerPool) getQueue() (queue chan *Request) { pool.m.Lock() queue = pool.queue pool.m.Unlock() return } func (pool *WorkerPool) scheduler() { defer pool.waitGroup.Done() for { queue := pool.getQueue() if queue == nil { return } queueLoop: for request := range queue { now := time.Now() pool.m.Lock() deadline := request.CreatedAt.Add(pool.maxPendingDuration) pool.m.Unlock() if deadline.After(now) { // Safety call to setupStreamedReply in case QueueRequest had // to time to do it yet request.setupStreamedReply() select { case pool.schedule <- request: continue queueLoop case <-time.After(deadline.Sub(now)): // Too late } } request.SendErrorTooBusy("No worker available") } } } func (pool *WorkerPool) worker() { defer pool.waitGroup.Done() for request := range pool.schedule { if request == nil { return } request.RunAndReply() } } // SetMaxPending changes the queue size func (pool *WorkerPool) SetMaxPending(value uint) { if pool.maxPending == value { return } pool.m.Lock() defer pool.m.Unlock() oldQueue := pool.queue pool.queue = make(chan *Request, value) pool.maxPending = value close(oldQueue) // drain the old queue and cancel requests if there are too many for request := range oldQueue { select { case pool.queue <- request: default: request.SendErrorTooBusy("too many pending requests") } } } // SetMaxPendingDuration changes the max pending delay func (pool *WorkerPool) SetMaxPendingDuration(value time.Duration) { pool.m.Lock() pool.maxPendingDuration = value pool.m.Unlock() } // SetSize changes the number of workers func (pool *WorkerPool) SetSize(size uint) { pool.m.Lock() defer pool.m.Unlock() if size == pool.size { return } for size < pool.size { pool.schedule <- nil pool.size-- } for size > pool.size { pool.waitGroup.Add(1) go pool.worker() pool.size++ } } // QueueRequest adds a request to the queue // Send a SERVERTOOBUSY error to the client if the queue is full func (pool *WorkerPool) QueueRequest(request *Request) error { select { case pool.getQueue() <- request: request.setupStreamedReply() return nil default: return request.SendErrorTooBusy("too many pending requests") } } // Close stops all the workers and wait for their completion // If the workers do not stop before the timeout, their context is canceled // Will never return if a request ignores the context func (pool *WorkerPool) Close(timeout time.Duration) { // Stops all the workers so nothing more gets scheduled pool.SetSize(0) pool.m.Lock() oldQueue := pool.queue pool.queue = nil pool.m.Unlock() close(oldQueue) for request := range oldQueue { request.SendErrorTooBusy("Worker pool shutting down") } // Now wait for the workers to stop and cancel the context if they don't timer := time.AfterFunc(timeout, pool.contextCancel) pool.waitGroup.Wait() timer.Stop() close(pool.schedule) } ================================================ FILE: nrpc.pb.go ================================================ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.29.0 // protoc v4.22.2 // source: nrpc.proto package nrpc import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" descriptorpb "google.golang.org/protobuf/types/descriptorpb" reflect "reflect" sync "sync" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type SubjectRule int32 const ( SubjectRule_COPY SubjectRule = 0 SubjectRule_TOLOWER SubjectRule = 1 ) // Enum value maps for SubjectRule. var ( SubjectRule_name = map[int32]string{ 0: "COPY", 1: "TOLOWER", } SubjectRule_value = map[string]int32{ "COPY": 0, "TOLOWER": 1, } ) func (x SubjectRule) Enum() *SubjectRule { p := new(SubjectRule) *p = x return p } func (x SubjectRule) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (SubjectRule) Descriptor() protoreflect.EnumDescriptor { return file_nrpc_proto_enumTypes[0].Descriptor() } func (SubjectRule) Type() protoreflect.EnumType { return &file_nrpc_proto_enumTypes[0] } func (x SubjectRule) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use SubjectRule.Descriptor instead. func (SubjectRule) EnumDescriptor() ([]byte, []int) { return file_nrpc_proto_rawDescGZIP(), []int{0} } type Error_Type int32 const ( Error_CLIENT Error_Type = 0 Error_SERVER Error_Type = 1 Error_EOS Error_Type = 3 Error_SERVERTOOBUSY Error_Type = 4 ) // Enum value maps for Error_Type. var ( Error_Type_name = map[int32]string{ 0: "CLIENT", 1: "SERVER", 3: "EOS", 4: "SERVERTOOBUSY", } Error_Type_value = map[string]int32{ "CLIENT": 0, "SERVER": 1, "EOS": 3, "SERVERTOOBUSY": 4, } ) func (x Error_Type) Enum() *Error_Type { p := new(Error_Type) *p = x return p } func (x Error_Type) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (Error_Type) Descriptor() protoreflect.EnumDescriptor { return file_nrpc_proto_enumTypes[1].Descriptor() } func (Error_Type) Type() protoreflect.EnumType { return &file_nrpc_proto_enumTypes[1] } func (x Error_Type) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use Error_Type.Descriptor instead. func (Error_Type) EnumDescriptor() ([]byte, []int) { return file_nrpc_proto_rawDescGZIP(), []int{0, 0} } type Error struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Type Error_Type `protobuf:"varint,1,opt,name=type,proto3,enum=nrpc.Error_Type" json:"type,omitempty"` Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` MsgCount uint32 `protobuf:"varint,3,opt,name=msgCount,proto3" json:"msgCount,omitempty"` } func (x *Error) Reset() { *x = Error{} if protoimpl.UnsafeEnabled { mi := &file_nrpc_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Error) String() string { return protoimpl.X.MessageStringOf(x) } func (*Error) ProtoMessage() {} func (x *Error) ProtoReflect() protoreflect.Message { mi := &file_nrpc_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Error.ProtoReflect.Descriptor instead. func (*Error) Descriptor() ([]byte, []int) { return file_nrpc_proto_rawDescGZIP(), []int{0} } func (x *Error) GetType() Error_Type { if x != nil { return x.Type } return Error_CLIENT } func (x *Error) GetMessage() string { if x != nil { return x.Message } return "" } func (x *Error) GetMsgCount() uint32 { if x != nil { return x.MsgCount } return 0 } type Void struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *Void) Reset() { *x = Void{} if protoimpl.UnsafeEnabled { mi := &file_nrpc_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Void) String() string { return protoimpl.X.MessageStringOf(x) } func (*Void) ProtoMessage() {} func (x *Void) ProtoReflect() protoreflect.Message { mi := &file_nrpc_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Void.ProtoReflect.Descriptor instead. func (*Void) Descriptor() ([]byte, []int) { return file_nrpc_proto_rawDescGZIP(), []int{1} } type NoRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *NoRequest) Reset() { *x = NoRequest{} if protoimpl.UnsafeEnabled { mi := &file_nrpc_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *NoRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*NoRequest) ProtoMessage() {} func (x *NoRequest) ProtoReflect() protoreflect.Message { mi := &file_nrpc_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NoRequest.ProtoReflect.Descriptor instead. func (*NoRequest) Descriptor() ([]byte, []int) { return file_nrpc_proto_rawDescGZIP(), []int{2} } type NoReply struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } func (x *NoReply) Reset() { *x = NoReply{} if protoimpl.UnsafeEnabled { mi := &file_nrpc_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *NoReply) String() string { return protoimpl.X.MessageStringOf(x) } func (*NoReply) ProtoMessage() {} func (x *NoReply) ProtoReflect() protoreflect.Message { mi := &file_nrpc_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NoReply.ProtoReflect.Descriptor instead. func (*NoReply) Descriptor() ([]byte, []int) { return file_nrpc_proto_rawDescGZIP(), []int{3} } type HeartBeat struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Lastbeat bool `protobuf:"varint,1,opt,name=lastbeat,proto3" json:"lastbeat,omitempty"` } func (x *HeartBeat) Reset() { *x = HeartBeat{} if protoimpl.UnsafeEnabled { mi := &file_nrpc_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *HeartBeat) String() string { return protoimpl.X.MessageStringOf(x) } func (*HeartBeat) ProtoMessage() {} func (x *HeartBeat) ProtoReflect() protoreflect.Message { mi := &file_nrpc_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use HeartBeat.ProtoReflect.Descriptor instead. func (*HeartBeat) Descriptor() ([]byte, []int) { return file_nrpc_proto_rawDescGZIP(), []int{4} } func (x *HeartBeat) GetLastbeat() bool { if x != nil { return x.Lastbeat } return false } var file_nrpc_proto_extTypes = []protoimpl.ExtensionInfo{ { ExtendedType: (*descriptorpb.FileOptions)(nil), ExtensionType: (*string)(nil), Field: 50000, Name: "nrpc.packageSubject", Tag: "bytes,50000,opt,name=packageSubject", Filename: "nrpc.proto", }, { ExtendedType: (*descriptorpb.FileOptions)(nil), ExtensionType: ([]string)(nil), Field: 50001, Name: "nrpc.packageSubjectParams", Tag: "bytes,50001,rep,name=packageSubjectParams", Filename: "nrpc.proto", }, { ExtendedType: (*descriptorpb.FileOptions)(nil), ExtensionType: (*SubjectRule)(nil), Field: 50002, Name: "nrpc.serviceSubjectRule", Tag: "varint,50002,opt,name=serviceSubjectRule,enum=nrpc.SubjectRule", Filename: "nrpc.proto", }, { ExtendedType: (*descriptorpb.FileOptions)(nil), ExtensionType: (*SubjectRule)(nil), Field: 50003, Name: "nrpc.methodSubjectRule", Tag: "varint,50003,opt,name=methodSubjectRule,enum=nrpc.SubjectRule", Filename: "nrpc.proto", }, { ExtendedType: (*descriptorpb.ServiceOptions)(nil), ExtensionType: (*string)(nil), Field: 51000, Name: "nrpc.serviceSubject", Tag: "bytes,51000,opt,name=serviceSubject", Filename: "nrpc.proto", }, { ExtendedType: (*descriptorpb.ServiceOptions)(nil), ExtensionType: ([]string)(nil), Field: 51001, Name: "nrpc.serviceSubjectParams", Tag: "bytes,51001,rep,name=serviceSubjectParams", Filename: "nrpc.proto", }, { ExtendedType: (*descriptorpb.MethodOptions)(nil), ExtensionType: (*string)(nil), Field: 52000, Name: "nrpc.methodSubject", Tag: "bytes,52000,opt,name=methodSubject", Filename: "nrpc.proto", }, { ExtendedType: (*descriptorpb.MethodOptions)(nil), ExtensionType: ([]string)(nil), Field: 52001, Name: "nrpc.methodSubjectParams", Tag: "bytes,52001,rep,name=methodSubjectParams", Filename: "nrpc.proto", }, { ExtendedType: (*descriptorpb.MethodOptions)(nil), ExtensionType: (*bool)(nil), Field: 52002, Name: "nrpc.streamedReply", Tag: "varint,52002,opt,name=streamedReply", Filename: "nrpc.proto", }, { ExtendedType: (*descriptorpb.MethodOptions)(nil), ExtensionType: (*bool)(nil), Field: 52003, Name: "nrpc.pollingEnabled", Tag: "varint,52003,opt,name=pollingEnabled", Filename: "nrpc.proto", }, } // Extension fields to descriptorpb.FileOptions. var ( // A custom subject prefix to use instead of the package name // // optional string packageSubject = 50000; E_PackageSubject = &file_nrpc_proto_extTypes[0] // Parameters included in the subject at the package level // // repeated string packageSubjectParams = 50001; E_PackageSubjectParams = &file_nrpc_proto_extTypes[1] // Default rule to build a service subject from the service name // // optional nrpc.SubjectRule serviceSubjectRule = 50002; E_ServiceSubjectRule = &file_nrpc_proto_extTypes[2] // Default rule to build a method subject from its name // // optional nrpc.SubjectRule methodSubjectRule = 50003; E_MethodSubjectRule = &file_nrpc_proto_extTypes[3] ) // Extension fields to descriptorpb.ServiceOptions. var ( // A custom subject token to use instead of (service name + serviceSubjectRule) // // optional string serviceSubject = 51000; E_ServiceSubject = &file_nrpc_proto_extTypes[4] // Parameters included in the subject at the service level // // repeated string serviceSubjectParams = 51001; E_ServiceSubjectParams = &file_nrpc_proto_extTypes[5] ) // Extension fields to descriptorpb.MethodOptions. var ( // A custom subject to use instead of (methor name + methodSubjectRule) // // optional string methodSubject = 52000; E_MethodSubject = &file_nrpc_proto_extTypes[6] // Parameters included in the subject at the method level // // repeated string methodSubjectParams = 52001; E_MethodSubjectParams = &file_nrpc_proto_extTypes[7] // If true, the method returns a stream of reply messages instead of just one // // optional bool streamedReply = 52002; E_StreamedReply = &file_nrpc_proto_extTypes[8] // If true, a 'Polling' version of the client method is generated // // optional bool pollingEnabled = 52003; E_PollingEnabled = &file_nrpc_proto_extTypes[9] ) var File_nrpc_proto protoreflect.FileDescriptor var file_nrpc_proto_rawDesc = []byte{ 0x0a, 0x0a, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x04, 0x6e, 0x72, 0x70, 0x63, 0x1a, 0x20, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x9f, 0x01, 0x0a, 0x05, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x24, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x10, 0x2e, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x73, 0x67, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x6d, 0x73, 0x67, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x3a, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x4c, 0x49, 0x45, 0x4e, 0x54, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x45, 0x4f, 0x53, 0x10, 0x03, 0x12, 0x11, 0x0a, 0x0d, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x54, 0x4f, 0x4f, 0x42, 0x55, 0x53, 0x59, 0x10, 0x04, 0x22, 0x06, 0x0a, 0x04, 0x56, 0x6f, 0x69, 0x64, 0x22, 0x0b, 0x0a, 0x09, 0x4e, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x09, 0x0a, 0x07, 0x4e, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x27, 0x0a, 0x09, 0x48, 0x65, 0x61, 0x72, 0x74, 0x42, 0x65, 0x61, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6c, 0x61, 0x73, 0x74, 0x62, 0x65, 0x61, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x6c, 0x61, 0x73, 0x74, 0x62, 0x65, 0x61, 0x74, 0x2a, 0x24, 0x0a, 0x0b, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x43, 0x4f, 0x50, 0x59, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x54, 0x4f, 0x4c, 0x4f, 0x57, 0x45, 0x52, 0x10, 0x01, 0x3a, 0x46, 0x0a, 0x0e, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xd0, 0x86, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x3a, 0x52, 0x0a, 0x14, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xd1, 0x86, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x14, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x3a, 0x61, 0x0a, 0x12, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xd2, 0x86, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11, 0x2e, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x12, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x75, 0x6c, 0x65, 0x3a, 0x5f, 0x0a, 0x11, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xd3, 0x86, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11, 0x2e, 0x6e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x11, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x75, 0x6c, 0x65, 0x3a, 0x49, 0x0a, 0x0e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x1f, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xb8, 0x8e, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x3a, 0x55, 0x0a, 0x14, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x1f, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xb9, 0x8e, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x14, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x3a, 0x46, 0x0a, 0x0d, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x1e, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xa0, 0x96, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x3a, 0x52, 0x0a, 0x13, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xa1, 0x96, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x13, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x3a, 0x46, 0x0a, 0x0d, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x65, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x1e, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xa2, 0x96, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x65, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x3a, 0x48, 0x0a, 0x0e, 0x70, 0x6f, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x1e, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xa3, 0x96, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x70, 0x6f, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x42, 0x1a, 0x5a, 0x18, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6e, 0x61, 0x74, 0x73, 0x2d, 0x72, 0x70, 0x63, 0x2f, 0x6e, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( file_nrpc_proto_rawDescOnce sync.Once file_nrpc_proto_rawDescData = file_nrpc_proto_rawDesc ) func file_nrpc_proto_rawDescGZIP() []byte { file_nrpc_proto_rawDescOnce.Do(func() { file_nrpc_proto_rawDescData = protoimpl.X.CompressGZIP(file_nrpc_proto_rawDescData) }) return file_nrpc_proto_rawDescData } var file_nrpc_proto_enumTypes = make([]protoimpl.EnumInfo, 2) var file_nrpc_proto_msgTypes = make([]protoimpl.MessageInfo, 5) var file_nrpc_proto_goTypes = []interface{}{ (SubjectRule)(0), // 0: nrpc.SubjectRule (Error_Type)(0), // 1: nrpc.Error.Type (*Error)(nil), // 2: nrpc.Error (*Void)(nil), // 3: nrpc.Void (*NoRequest)(nil), // 4: nrpc.NoRequest (*NoReply)(nil), // 5: nrpc.NoReply (*HeartBeat)(nil), // 6: nrpc.HeartBeat (*descriptorpb.FileOptions)(nil), // 7: google.protobuf.FileOptions (*descriptorpb.ServiceOptions)(nil), // 8: google.protobuf.ServiceOptions (*descriptorpb.MethodOptions)(nil), // 9: google.protobuf.MethodOptions } var file_nrpc_proto_depIdxs = []int32{ 1, // 0: nrpc.Error.type:type_name -> nrpc.Error.Type 7, // 1: nrpc.packageSubject:extendee -> google.protobuf.FileOptions 7, // 2: nrpc.packageSubjectParams:extendee -> google.protobuf.FileOptions 7, // 3: nrpc.serviceSubjectRule:extendee -> google.protobuf.FileOptions 7, // 4: nrpc.methodSubjectRule:extendee -> google.protobuf.FileOptions 8, // 5: nrpc.serviceSubject:extendee -> google.protobuf.ServiceOptions 8, // 6: nrpc.serviceSubjectParams:extendee -> google.protobuf.ServiceOptions 9, // 7: nrpc.methodSubject:extendee -> google.protobuf.MethodOptions 9, // 8: nrpc.methodSubjectParams:extendee -> google.protobuf.MethodOptions 9, // 9: nrpc.streamedReply:extendee -> google.protobuf.MethodOptions 9, // 10: nrpc.pollingEnabled:extendee -> google.protobuf.MethodOptions 0, // 11: nrpc.serviceSubjectRule:type_name -> nrpc.SubjectRule 0, // 12: nrpc.methodSubjectRule:type_name -> nrpc.SubjectRule 13, // [13:13] is the sub-list for method output_type 13, // [13:13] is the sub-list for method input_type 11, // [11:13] is the sub-list for extension type_name 1, // [1:11] is the sub-list for extension extendee 0, // [0:1] is the sub-list for field type_name } func init() { file_nrpc_proto_init() } func file_nrpc_proto_init() { if File_nrpc_proto != nil { return } if !protoimpl.UnsafeEnabled { file_nrpc_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Error); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_nrpc_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Void); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_nrpc_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*NoRequest); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_nrpc_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*NoReply); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_nrpc_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*HeartBeat); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_nrpc_proto_rawDesc, NumEnums: 2, NumMessages: 5, NumExtensions: 10, NumServices: 0, }, GoTypes: file_nrpc_proto_goTypes, DependencyIndexes: file_nrpc_proto_depIdxs, EnumInfos: file_nrpc_proto_enumTypes, MessageInfos: file_nrpc_proto_msgTypes, ExtensionInfos: file_nrpc_proto_extTypes, }.Build() File_nrpc_proto = out.File file_nrpc_proto_rawDesc = nil file_nrpc_proto_goTypes = nil file_nrpc_proto_depIdxs = nil } ================================================ FILE: nrpc.proto ================================================ syntax = "proto3"; package nrpc; option go_package = "github.com/nats-rpc/nrpc"; import "google/protobuf/descriptor.proto"; enum SubjectRule { COPY = 0; TOLOWER = 1; } extend google.protobuf.FileOptions { // A custom subject prefix to use instead of the package name string packageSubject = 50000; // Parameters included in the subject at the package level repeated string packageSubjectParams = 50001; // Default rule to build a service subject from the service name SubjectRule serviceSubjectRule = 50002; // Default rule to build a method subject from its name SubjectRule methodSubjectRule = 50003; } extend google.protobuf.ServiceOptions { // A custom subject token to use instead of (service name + serviceSubjectRule) string serviceSubject = 51000; // Parameters included in the subject at the service level repeated string serviceSubjectParams = 51001; } extend google.protobuf.MethodOptions { // A custom subject to use instead of (methor name + methodSubjectRule) string methodSubject = 52000; // Parameters included in the subject at the method level repeated string methodSubjectParams = 52001; // If true, the method returns a stream of reply messages instead of just one bool streamedReply = 52002; // If true, a 'Polling' version of the client method is generated bool pollingEnabled = 52003; } message Error { enum Type { CLIENT = 0; SERVER = 1; EOS = 3; SERVERTOOBUSY = 4; } Type type = 1; string message = 2; uint32 msgCount = 3; } message Void {} message NoRequest {} message NoReply {} message HeartBeat { bool lastbeat = 1; } ================================================ FILE: nrpc_test.go ================================================ package nrpc_test import ( "bytes" "context" "fmt" "log" "strings" "testing" "time" "github.com/nats-io/nats.go" "google.golang.org/protobuf/proto" "github.com/nats-rpc/nrpc" ) //go:generate protoc --go_out=. --go_opt=paths=source_relative nrpc_test.proto //go:generate mv nrpc_test.pb.go nrpcpb_test.go func TestBasic(t *testing.T) { nc, err := nats.Connect(nrpc.NatsURL, nats.Timeout(5*time.Second)) if err != nil { t.Fatal(err) } defer nc.Close() subn, err := nc.Subscribe("foo.*", func(m *nats.Msg) { if err := nrpc.Publish(&DummyMessage{Foobar: "world"}, nil, nc, m.Reply, "protobuf"); err != nil { t.Fatal(err) } }) if err != nil { t.Fatal(err) } defer subn.Unsubscribe() var dm DummyMessage if err := nrpc.Call( &DummyMessage{Foobar: "hello"}, &dm, nc, "foo.bar", "protobuf", 5*time.Second, ); err != nil { t.Fatal(err) } if dm.Foobar != "world" { t.Fatal("wrong response: ", string(dm.Foobar)) } } func TestDecode(t *testing.T) { nc, err := nats.Connect(nrpc.NatsURL, nats.Timeout(5*time.Second)) if err != nil { t.Fatal(err) } defer nc.Close() var name string subn, err := nc.Subscribe("foo.*", func(m *nats.Msg) { rname := strings.Split(m.Subject, ".")[1] var dm DummyMessage if rname != name { t.Fatal("unexpected name: " + rname) } else if err := proto.Unmarshal(m.Data, &dm); err != nil { t.Fatal(err) } else if dm.Foobar != "hello" { t.Fatal("unexpected inner request: " + dm.Foobar) } else if err := nrpc.Publish(&DummyMessage{Foobar: "world"}, nil, nc, m.Reply, "protobuf"); err != nil { t.Fatal(err) } }) if err != nil { t.Fatal(err) } defer subn.Unsubscribe() var names = []string{"lorem", "ipsum", "dolor"} for _, n := range names { name = n var dm DummyMessage if err := nrpc.Call( &DummyMessage{Foobar: "hello"}, &dm, nc, "foo."+name, "protobuf", 5*time.Second, ); err != nil { t.Fatal(err) } if dm.Foobar != "world" { t.Fatal("wrong response: ", string(dm.Foobar)) } } } func TestStreamCall(t *testing.T) { nc, err := nats.Connect(nrpc.NatsURL, nats.Timeout(5*time.Second)) if err != nil { t.Fatal(err) } defer nc.Close() t.Run("Simple stream", func(t *testing.T) { subn, err := nc.Subscribe("foo.*", func(m *nats.Msg) { time.Sleep(60 * time.Millisecond) // Send an empty message if err := nc.Publish(m.Reply, []byte{0}); err != nil { t.Fatal(err) } time.Sleep(60 * time.Millisecond) // Send a first message if err := nrpc.Publish( &DummyMessage{Foobar: "hello"}, nil, nc, m.Reply, "protobuf", ); err != nil { t.Fatal(err) } time.Sleep(60 * time.Millisecond) log.Print("Sending 'world'") // Send a second message if err := nrpc.Publish( &DummyMessage{Foobar: "world"}, nil, nc, m.Reply, "protobuf", ); err != nil { t.Fatal(err) } log.Print("Sending EOF") // Send the EOS marker if err := nrpc.Publish( nil, &nrpc.Error{Type: nrpc.Error_EOS, MsgCount: 2}, nc, m.Reply, "protobuf", ); err != nil { t.Fatal(err) } }) if err != nil { t.Fatal(err) } defer subn.Unsubscribe() sub, err := nrpc.StreamCall( context.TODO(), nc, "foo.*", &DummyMessage{}, "protobuf", 100*time.Millisecond) if err != nil { t.Fatal(err) } var dm DummyMessage var cont bool err = sub.Next(&dm) if err != nil { t.Fatal(err) } err = sub.Next(&dm) if err != nil { t.Fatal(err) } err = sub.Next(&dm) if err != nrpc.ErrEOS { t.Fatalf("Expected EOS, got %s", err) } if cont { t.Errorf("Expects cont=false") } err = sub.Next(&dm) if err != nats.ErrBadSubscription { t.Errorf("Expected a ErrBadSubscription, got %s", err) } }) t.Run("Error", func(t *testing.T) { subn, err := nc.Subscribe("foo.*", func(m *nats.Msg) { if err := nrpc.Publish( nil, &nrpc.Error{Type: nrpc.Error_CLIENT, Message: "error"}, nc, m.Reply, "protobuf", ); err != nil { t.Fatal(err) } }) if err != nil { t.Fatal(err) } defer subn.Unsubscribe() sub, err := nrpc.StreamCall( context.TODO(), nc, "foo.*", &DummyMessage{}, "protobuf", 100*time.Millisecond) if err != nil { t.Fatal(err) } err = sub.Next(&DummyMessage{}) if err == nil { t.Fatalf("Expected an error, got %s", err) } if nErr, ok := err.(*nrpc.Error); ok { if nErr.Message != "error" { t.Errorf("Expected message='error', got %s", nErr.Message) } } else { t.Errorf("Expected a nrpc.Error, got %s", err) } err = sub.Next(&DummyMessage{}) if err != nats.ErrBadSubscription { t.Errorf("Expected a ErrBadSubscription, got %s", err) } }) } func TestError(t *testing.T) { nc, err := nats.Connect(nrpc.NatsURL, nats.Timeout(5*time.Second)) if err != nil { t.Fatal(err) } defer nc.Close() subn, err := nc.Subscribe("foo.*", func(m *nats.Msg) { if err := nrpc.Publish( &DummyMessage{Foobar: "world"}, &nrpc.Error{Type: nrpc.Error_CLIENT, Message: "anerror"}, nc, m.Reply, "protobuf", ); err != nil { t.Fatal(err) } }) if err != nil { t.Fatal(err) } defer subn.Unsubscribe() err = nrpc.Call(&DummyMessage{Foobar: "hello"}, &DummyMessage{}, nc, "foo.bar", "protobuf", 5*time.Second) if err == nil { t.Fatal("error expected") } if err.Error() != "CLIENT error: anerror" { t.Fatal("wrong error: ", err.Error()) } } func TestTimeout(t *testing.T) { nc, err := nats.Connect(nrpc.NatsURL, nats.Timeout(5*time.Second)) if err != nil { t.Fatal(err) } defer nc.Close() subn, err := nc.Subscribe("foo.*", func(m *nats.Msg) { time.Sleep(time.Second) if err := nrpc.Publish(&DummyMessage{Foobar: "world"}, nil, nc, m.Reply, "protobuf"); err != nil { t.Fatal(err) } }) if err != nil { t.Fatal(err) } defer subn.Unsubscribe() err = nrpc.Call(&DummyMessage{Foobar: "hello"}, &DummyMessage{}, nc, "foo.bar", "protobuf", 500*time.Millisecond) if err == nil { t.Fatal("error expected") } else if err.Error() != "nats: timeout" { t.Fatal("unexpected error: " + err.Error()) } } var ( encodingTestMsg = DummyMessage{Foobar: "hello"} encodingTests = []struct { encoding string data []byte }{ {"protobuf", []byte{10, 5, 104, 101, 108, 108, 111}}, {"json", []byte(`{"foobar":"hello"}`)}, } ) func TestMarshal(t *testing.T) { for _, tt := range encodingTests { t.Run("Marshal"+tt.encoding, func(t *testing.T) { b, err := nrpc.Marshal(tt.encoding, &encodingTestMsg) if err != nil { t.Fatal(err) } if !bytes.Equal(b, tt.data) { t.Errorf("Marshal %s failed", tt.encoding) } }) } } func TestUnmarshal(t *testing.T) { for _, tt := range encodingTests { t.Run("Unmarshal"+tt.encoding, func(t *testing.T) { var msg DummyMessage err := nrpc.Unmarshal(tt.encoding, tt.data, &msg) if err != nil { t.Fatal(err) } if msg.Foobar != encodingTestMsg.Foobar { t.Errorf( "%s decode failed. Expected %#v, got %#v", tt.encoding, encodingTestMsg, msg) } }) } } func TestMarshalUnmarshalResponse(t *testing.T) { for _, tt := range encodingTests { t.Run("UnmarshalResponse"+tt.encoding, func(t *testing.T) { var msg DummyMessage err := nrpc.UnmarshalResponse(tt.encoding, tt.data, &msg) if err != nil { t.Fatal(err) } if msg.Foobar != encodingTestMsg.Foobar { t.Errorf( "%s decode failed. Expected %#v, got %#v", tt.encoding, encodingTestMsg, msg) } }) t.Run("MarshalErrorResponse"+tt.encoding, func(t *testing.T) { data, err := nrpc.MarshalErrorResponse(tt.encoding, &nrpc.Error{ Type: nrpc.Error_SERVER, Message: "Some error", }) if err != nil { t.Fatal("Unexpected error:", err) } switch tt.encoding { case "protobuf": if data[0] != 0 { t.Error("Expects payload to start with a '0', got", data[0]) } case "json": var expected = `{"__error__":{"type":"SERVER","message":"Some error"}}` if string(data) != expected { t.Errorf("Invalid json-encoded error. Expects %s, got %s", expected, string(data)) } } var msg DummyMessage err = nrpc.UnmarshalResponse(tt.encoding, data, &msg) if err == nil { t.Errorf("Expected an error, got nil") return } repErr, ok := err.(*nrpc.Error) if !ok { t.Errorf("Expected a nrpc.Error, got %#v", err) return } if repErr.Type != nrpc.Error_SERVER || repErr.Message != "Some error" { t.Errorf("Unexpected err: %#v", *repErr) } }) } } // MSG Greeter.SayHello-json 1 _INBOX.test 16\r\n{"foobar":"hello"}\r\n func compareStringSlices(t *testing.T, expected, actual []string) { if len(expected) != len(expected) { t.Errorf("String slices are different. Expected [%s], got [%s]", strings.Join(expected, ","), strings.Join(actual, ",")) return } for i, expectedValue := range expected { if actual[i] != expectedValue { t.Errorf("String slices are different. Expected [%s], got [%s]", strings.Join(expected, ","), strings.Join(actual, ",")) return } } } func TestParseSubject(t *testing.T) { for i, tt := range []struct { pkgSubject string pkgParamsCount int svcSubject string svcParamsCount int mtParamsCount int subject string pkgParams []string svcParams []string mtParams []string name string encoding string err string }{ {"", 0, "foo", 0, 0, "foo.bar", nil, nil, nil, "bar", "protobuf", ""}, {"", 0, "foo", 0, 0, "foo.bar.protobuf", nil, nil, nil, "bar", "protobuf", ""}, {"", 0, "foo", 0, 0, "foo.bar.json", nil, nil, nil, "bar", "json", ""}, {"", 0, "foo", 0, 0, "foo.bar.json.protobuf", nil, nil, nil, "bar", "", "Invalid subject tail length. Expects 0 or 1 parts, got 2"}, {"demo", 0, "foo", 0, 0, "demo.foo.bar", nil, nil, nil, "bar", "protobuf", ""}, {"demo", 0, "foo", 0, 0, "demo.foo.bar.json", nil, nil, nil, "bar", "json", ""}, {"demo", 0, "foo", 0, 0, "foo.bar.json", nil, nil, nil, "", "", "Invalid subject prefix. Expected 'demo', got 'foo'"}, {"demo", 2, "foo", 0, 0, "demo.p1.p2.foo.bar.json", []string{"p1", "p2"}, nil, nil, "bar", "json", ""}, {"demo", 2, "foo", 1, 0, "demo.p1.p2.foo.sp1.bar.json", []string{"p1", "p2"}, []string{"sp1"}, nil, "bar", "json", ""}, {"demo.pkg", 1, "nested.svc", 1, 0, "demo.pkg.p1.nested.svc.sp1.bar", []string{"p1"}, []string{"sp1"}, nil, "bar", "protobuf", ""}, } { pkgParams, svcParams, name, tail, err := nrpc.ParseSubject( tt.pkgSubject, tt.pkgParamsCount, tt.svcSubject, tt.svcParamsCount, tt.subject) var mtParams []string var encoding string if err == nil { mtParams, encoding, err = nrpc.ParseSubjectTail(tt.mtParamsCount, tail) } compareStringSlices(t, tt.pkgParams, pkgParams) compareStringSlices(t, tt.svcParams, svcParams) compareStringSlices(t, tt.mtParams, mtParams) if name != tt.name { t.Errorf("test %d: Expected name=%s, got %s", i, tt.name, name) } if encoding != tt.encoding { t.Errorf("text %d: Expected encoding=%s, got %s", i, tt.encoding, encoding) } if tt.err == "" && err != nil { t.Errorf("text %d: Unexpected error %s", i, err) } else if tt.err != "" && err == nil { t.Errorf("text %d: Expected error, got nothing", i) } else if tt.err != "" && tt.err != err.Error() { t.Errorf("text %d: Expected error '%s', got '%s'", i, tt.err, err) } } } func TestCaptureErrors(t *testing.T) { t.Run("NoError", func(t *testing.T) { msg, err := nrpc.CaptureErrors(func() (proto.Message, error) { return &DummyMessage{Foobar: "Hi"}, nil }) if err != nil { t.Error("Unexpected error:", err) } dm, ok := msg.(*DummyMessage) if !ok { t.Error("Expected a DummyMessage, got", msg) } if dm.Foobar != "Hi" { t.Error("Message was not passed properly") } }) t.Run("ClientError", func(t *testing.T) { msg, err := nrpc.CaptureErrors(func() (proto.Message, error) { return nil, fmt.Errorf("anerror") }) if err == nil { t.Fatal("Expected an error, got nothing") } if err.Type != nrpc.Error_CLIENT { t.Errorf("Invalid error type. Expected 'CLIENT' (0), got %s", err.Type) } if err.Message != "anerror" { t.Error("Unexpected error message. Expected 'anerror', got", err.Message) } if msg != nil { t.Error("Expected a nil msg, got", msg) } }) t.Run("DirectError", func(t *testing.T) { msg, err := nrpc.CaptureErrors(func() (proto.Message, error) { return nil, &nrpc.Error{Type: nrpc.Error_SERVER, Message: "anerror"} }) if err == nil { t.Fatal("Expected an error, got nothing") } if err.Type != nrpc.Error_SERVER { t.Errorf("Invalid error type. Expected 'SERVER' (1), got %s", err.Type) } if err.Message != "anerror" { t.Error("Unexpected error message. Expected 'anerror', got", err.Message) } if msg != nil { t.Error("Expected a nil msg, got", msg) } }) t.Run("ServerError", func(t *testing.T) { msg, err := nrpc.CaptureErrors(func() (proto.Message, error) { panic("panicking") }) if err == nil { t.Fatal("Expected an error, got nothing") } if err.Type != nrpc.Error_SERVER { t.Errorf("Invalid error type. Expected 'SERVER' (1), got %s", err.Type) } if err.Message != "panicking" { t.Error("Unexpected error message. Expected 'panicking', got", err.Message) } if msg != nil { t.Error("Expected a nil msg, got", msg) } }) } ================================================ FILE: nrpc_test.proto ================================================ syntax = "proto3"; option go_package = "github.com/nats-rpc/nrpc_test"; message DummyMessage { string foobar = 1; } ================================================ FILE: nrpcpb_test.go ================================================ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.29.0 // protoc v4.22.2 // source: nrpc_test.proto package nrpc_test import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type DummyMessage struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Foobar string `protobuf:"bytes,1,opt,name=foobar,proto3" json:"foobar,omitempty"` } func (x *DummyMessage) Reset() { *x = DummyMessage{} if protoimpl.UnsafeEnabled { mi := &file_nrpc_test_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *DummyMessage) String() string { return protoimpl.X.MessageStringOf(x) } func (*DummyMessage) ProtoMessage() {} func (x *DummyMessage) ProtoReflect() protoreflect.Message { mi := &file_nrpc_test_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use DummyMessage.ProtoReflect.Descriptor instead. func (*DummyMessage) Descriptor() ([]byte, []int) { return file_nrpc_test_proto_rawDescGZIP(), []int{0} } func (x *DummyMessage) GetFoobar() string { if x != nil { return x.Foobar } return "" } var File_nrpc_test_proto protoreflect.FileDescriptor var file_nrpc_test_proto_rawDesc = []byte{ 0x0a, 0x0f, 0x6e, 0x72, 0x70, 0x63, 0x5f, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x26, 0x0a, 0x0c, 0x44, 0x75, 0x6d, 0x6d, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72, 0x42, 0x1f, 0x5a, 0x1d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6e, 0x61, 0x74, 0x73, 0x2d, 0x72, 0x70, 0x63, 0x2f, 0x6e, 0x72, 0x70, 0x63, 0x5f, 0x74, 0x65, 0x73, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( file_nrpc_test_proto_rawDescOnce sync.Once file_nrpc_test_proto_rawDescData = file_nrpc_test_proto_rawDesc ) func file_nrpc_test_proto_rawDescGZIP() []byte { file_nrpc_test_proto_rawDescOnce.Do(func() { file_nrpc_test_proto_rawDescData = protoimpl.X.CompressGZIP(file_nrpc_test_proto_rawDescData) }) return file_nrpc_test_proto_rawDescData } var file_nrpc_test_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_nrpc_test_proto_goTypes = []interface{}{ (*DummyMessage)(nil), // 0: DummyMessage } var file_nrpc_test_proto_depIdxs = []int32{ 0, // [0:0] is the sub-list for method output_type 0, // [0:0] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name } func init() { file_nrpc_test_proto_init() } func file_nrpc_test_proto_init() { if File_nrpc_test_proto != nil { return } if !protoimpl.UnsafeEnabled { file_nrpc_test_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*DummyMessage); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_nrpc_test_proto_rawDesc, NumEnums: 0, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, GoTypes: file_nrpc_test_proto_goTypes, DependencyIndexes: file_nrpc_test_proto_depIdxs, MessageInfos: file_nrpc_test_proto_msgTypes, }.Build() File_nrpc_test_proto = out.File file_nrpc_test_proto_rawDesc = nil file_nrpc_test_proto_goTypes = nil file_nrpc_test_proto_depIdxs = nil } ================================================ FILE: protoc-gen-nrpc/main.go ================================================ package main import ( "bytes" "fmt" "io/ioutil" "log" "os" "path" "strings" "text/template" "github.com/nats-rpc/nrpc" "google.golang.org/protobuf/proto" descriptor "google.golang.org/protobuf/types/descriptorpb" plugin "google.golang.org/protobuf/types/pluginpb" ) // baseName returns the last path element of the name, with the last dotted suffix removed. func baseName(name string) string { // First, find the last element if i := strings.LastIndex(name, "/"); i >= 0 { name = name[i+1:] } // Now drop the suffix if i := strings.LastIndex(name, "."); i >= 0 { name = name[0:i] } return name } // getGoPackage returns the file's go_package option. // If it containts a semicolon, only the part before it is returned. func getGoPackage(fd *descriptor.FileDescriptorProto) string { pkg := fd.GetOptions().GetGoPackage() if strings.Contains(pkg, ";") { parts := strings.Split(pkg, ";") if len(parts) > 2 { log.Fatalf( "protoc-gen-nrpc: go_package '%s' contains more than 1 ';'", pkg) } pkg = parts[1] } return pkg } // goPackageOption interprets the file's go_package option. // If there is no go_package, it returns ("", "", false). // If there's a simple name, it returns ("", pkg, true). // If the option implies an import path, it returns (impPath, pkg, true). func goPackageOption(d *descriptor.FileDescriptorProto) (impPath, pkg string, ok bool) { pkg = getGoPackage(d) if pkg == "" { return } ok = true // The presence of a slash implies there's an import path. slash := strings.LastIndex(pkg, "/") if slash < 0 { return } impPath, pkg = pkg, pkg[slash+1:] // A semicolon-delimited suffix overrides the package name. sc := strings.IndexByte(impPath, ';') if sc < 0 { return } impPath, pkg = impPath[:sc], impPath[sc+1:] return } // goPackageName returns the Go package name to use in the // generated Go file. The result explicit reports whether the name // came from an option go_package statement. If explicit is false, // the name was derived from the protocol buffer's package statement // or the input file name. func goPackageName(d *descriptor.FileDescriptorProto) (name string, explicit bool) { // Does the file have a "go_package" option? if _, pkg, ok := goPackageOption(d); ok { return pkg, true } // Does the file have a package clause? if pkg := d.GetPackage(); pkg != "" { return pkg, false } // Use the file base name. return baseName(d.GetName()), false } // goFileName returns the output name for the generated Go file. func goFileName(d *descriptor.FileDescriptorProto) string { name := *d.Name if ext := path.Ext(name); ext == ".proto" || ext == ".protodevel" { name = name[:len(name)-len(ext)] } name += ".nrpc.go" if pathsSourceRelative { return name } // Does the file have a "go_package" option? // If it does, it may override the filename. if impPath, _, ok := goPackageOption(d); ok && impPath != "" { // Replace the existing dirname with the declared import path. _, name = path.Split(name) name = path.Join(impPath, name) return name } return name } // splitMessageTypeName split a message type into (package name, type name) func splitMessageTypeName(name string) (string, string) { if len(name) == 0 { log.Fatal("Empty message type") } if name[0] != '.' { log.Fatalf("Expect message type name to start with '.', but it is '%s'", name) } lastDot := strings.LastIndex(name, ".") return name[1:lastDot], name[lastDot+1:] } func splitTypePath(name string) []string { if len(name) == 0 { log.Fatal("Empty message type") } if name[0] != '.' { log.Fatalf("Expect message type name to start with '.', but it is '%s'", name) } return strings.Split(name[1:], ".") } func lookupFileDescriptor(name string) *descriptor.FileDescriptorProto { for _, fd := range request.GetProtoFile() { if fd.GetPackage() == name { return fd } } return nil } func lookupMessageType(name string) (*descriptor.FileDescriptorProto, *descriptor.DescriptorProto) { path := splitTypePath(name) pkgpath := path[:len(path)-1] var fd *descriptor.FileDescriptorProto for { pkgname := strings.Join(pkgpath, ".") fd = lookupFileDescriptor(pkgname) if fd != nil { break } if len(pkgpath) == 1 { log.Fatalf("Could not find the .proto file for package '%s' (from message %s)", pkgname, name) } pkgpath = pkgpath[:len(pkgpath)-1] } path = path[len(pkgpath):] var d *descriptor.DescriptorProto for _, mt := range fd.GetMessageType() { if mt.GetName() == path[0] { d = mt break } } if d == nil { log.Fatalf("No such type '%s' in package '%s'", path[0], strings.Join(pkgpath, ".")) } for i, token := range path[1:] { var found bool for _, nd := range d.GetNestedType() { if nd.GetName() == token { d = nd found = true break } } if !found { log.Fatalf("No such nested type '%s' in '%s.%s'", token, strings.Join(pkgpath, "."), strings.Join(path[:i+1], ".")) } } return fd, d } func getField(d *descriptor.DescriptorProto, name string) *descriptor.FieldDescriptorProto { for _, f := range d.GetField() { if f.GetName() == name { return f } } return nil } func getOneofDecl(d *descriptor.DescriptorProto, name string) *descriptor.OneofDescriptorProto { for _, of := range d.GetOneofDecl() { if of.GetName() == name { return of } } return nil } func pkgSubject(fd *descriptor.FileDescriptorProto) string { if options := fd.GetOptions(); options != nil { e := proto.GetExtension(options, nrpc.E_PackageSubject) if value, ok := e.(string); ok { return value } } return fd.GetPackage() } func getResultType( md *descriptor.MethodDescriptorProto, ) string { return md.GetOutputType() } func getGoType(pbType string) (string, string) { if !strings.Contains(pbType, ".") { return "", pbType } fd, _ := lookupMessageType(pbType) name := strings.TrimPrefix(pbType, "."+fd.GetPackage()+".") name = strings.Replace(name, ".", "_", -1) return getGoPackage(fd), name } func getPkgImportName(goPkg string) string { if goPkg == getGoPackage(currentFile) { return "" } replacer := strings.NewReplacer(".", "_", "/", "_", "-", "_") return replacer.Replace(goPkg) } var pluginPrometheus bool var pathsSourceRelative bool var funcMap = template.FuncMap{ "GoPackageName": func(fd *descriptor.FileDescriptorProto) string { p, _ := goPackageName(fd) return p }, "GetPkg": func(pkg, s string) string { s = strings.TrimPrefix(s, ".") s = strings.TrimPrefix(s, pkg) s = strings.TrimPrefix(s, ".") return s }, "GetExtraImports": func(fd *descriptor.FileDescriptorProto) []string { // check all the types used and imports packages from where they come var imports = make(map[string]string) for _, sd := range fd.GetService() { for _, md := range sd.GetMethod() { goPkg, _ := getGoType(md.GetInputType()) pkgImportName := getPkgImportName(goPkg) if pkgImportName != "" { imports[pkgImportName] = goPkg } goPkg, _ = getGoType(getResultType(md)) pkgImportName = getPkgImportName(goPkg) if pkgImportName != "" { imports[pkgImportName] = goPkg } } } var result []string for importName, goPkg := range imports { result = append(result, fmt.Sprintf("%s \"%s\"", importName, goPkg, ), ) } return result }, "GetPkgSubjectPrefix": func(fd *descriptor.FileDescriptorProto) string { if s := pkgSubject(fd); s != "" { return s + "." } return "" }, "GetPkgSubject": pkgSubject, "GetPkgSubjectParams": func(fd *descriptor.FileDescriptorProto) []string { if opts := fd.GetOptions(); opts != nil { e := proto.GetExtension(opts, nrpc.E_PackageSubjectParams) value := e.([]string) return value } return nil }, "GetServiceSubject": func(sd *descriptor.ServiceDescriptorProto) string { if opts := sd.GetOptions(); opts != nil { s := proto.GetExtension(opts, nrpc.E_ServiceSubject) if value, ok := s.(string); ok && s != "" { return value } } if opts := currentFile.GetOptions(); opts != nil { s := proto.GetExtension(opts, nrpc.E_ServiceSubjectRule) switch s.(nrpc.SubjectRule) { case nrpc.SubjectRule_COPY: return sd.GetName() case nrpc.SubjectRule_TOLOWER: return strings.ToLower(sd.GetName()) } } return sd.GetName() }, "ServiceNeedsHandler": func(sd *descriptor.ServiceDescriptorProto) bool { for _, md := range sd.GetMethod() { if md.GetInputType() != ".nrpc.NoRequest" { return true } } return false }, "GetMethodSubject": func(md *descriptor.MethodDescriptorProto) string { if opts := md.GetOptions(); opts != nil { s := proto.GetExtension(opts, nrpc.E_MethodSubject) if value, ok := s.(string); ok && value != "" { return value } } if opts := currentFile.GetOptions(); opts != nil { s := proto.GetExtension(opts, nrpc.E_MethodSubjectRule) switch s.(nrpc.SubjectRule) { case nrpc.SubjectRule_COPY: return md.GetName() case nrpc.SubjectRule_TOLOWER: return strings.ToLower(md.GetName()) } } return md.GetName() }, "GetMethodSubjectParams": func(md *descriptor.MethodDescriptorProto) []string { if opts := md.GetOptions(); opts != nil { e := proto.GetExtension(opts, nrpc.E_MethodSubjectParams) if s, ok := e.([]string); ok { return s } } return []string{} }, "GetServiceSubjectParams": func(sd *descriptor.ServiceDescriptorProto) []string { if opts := sd.GetOptions(); opts != nil { e := proto.GetExtension(opts, nrpc.E_ServiceSubjectParams) if s, ok := e.([]string); ok { return s } } return []string{} }, "HasStreamedReply": func(md *descriptor.MethodDescriptorProto) bool { if opts := md.GetOptions(); opts != nil { e := proto.GetExtension(opts, nrpc.E_StreamedReply) if s, ok := e.(bool); ok { return s } } return false }, "HasPollEnabled": func(md *descriptor.MethodDescriptorProto) bool { if opts := md.GetOptions(); opts != nil { e := proto.GetExtension(opts, nrpc.E_PollingEnabled) if s, ok := e.(bool); ok { return s } } return false }, "Prometheus": func() bool { return pluginPrometheus }, "GetResultType": getResultType, "GoType": func(pbType string) string { goPkg, goType := getGoType(pbType) if goPkg != "" { importName := getPkgImportName(goPkg) if importName != "" { goType = importName + "." + goType } } return goType }, } var request plugin.CodeGeneratorRequest var currentFile *descriptor.FileDescriptorProto func main() { log.SetPrefix("protoc-gen-nrpc: ") data, err := ioutil.ReadAll(os.Stdin) if err != nil { log.Fatalf("error: reading input: %v", err) } var response plugin.CodeGeneratorResponse if err := proto.Unmarshal(data, &request); err != nil { log.Fatalf("error: parsing input proto: %v", err) } if len(request.GetFileToGenerate()) == 0 { log.Fatal("error: no files to generate") } for _, param := range strings.Split(request.GetParameter(), ",") { var value string if i := strings.Index(param, "="); i >= 0 { value = param[i+1:] param = param[0:i] } switch param { case "": // Ignore. case "plugins": for _, plugin := range strings.Split(value, ";") { switch plugin { case "prometheus": pluginPrometheus = true default: log.Fatalf("invalid plugin: %s", plugin) } } case "paths": if value == "source_relative" { pathsSourceRelative = true } else if value == "import" { pathsSourceRelative = false } else { log.Fatalf(`unknown path type %q: want "import" or "source_relative"`, value) } } } tmpl, err := template.New(".").Funcs(funcMap).Parse(tFile) if err != nil { log.Fatal(err) } for _, name := range request.GetFileToGenerate() { var fd *descriptor.FileDescriptorProto for _, fd = range request.GetProtoFile() { if name == fd.GetName() { break } } if fd == nil { log.Fatalf("could not find the .proto file for %s", name) } currentFile = fd var buf bytes.Buffer if err := tmpl.Execute(&buf, fd); err != nil { log.Fatal(err) } currentFile = nil response.File = append(response.File, &plugin.CodeGeneratorResponse_File{ Name: proto.String(goFileName(fd)), Content: proto.String(buf.String()), }) } if data, err = proto.Marshal(&response); err != nil { log.Fatalf("error: failed to marshal output proto: %v", err) } if _, err := os.Stdout.Write(data); err != nil { log.Fatalf("error: failed to write output proto: %v", err) } } ================================================ FILE: protoc-gen-nrpc/tmpl.go ================================================ package main const tFile = `// This code was autogenerated from {{.GetName}}, do not edit. {{- $pkgName := GoPackageName .}} {{- $pkgSubject := GetPkgSubject .}} {{- $pkgSubjectPrefix := GetPkgSubjectPrefix .}} {{- $pkgSubjectParams := GetPkgSubjectParams .}} package {{$pkgName}} import ( "context" "log" "time" "google.golang.org/protobuf/proto" "github.com/nats-io/nats.go" {{- range GetExtraImports .}} {{.}} {{- end}} {{- if Prometheus}} "github.com/prometheus/client_golang/prometheus" {{- end}} "github.com/nats-rpc/nrpc" ) {{- range .Service}} // {{.GetName}}Server is the interface that providers of the service // {{.GetName}} should implement. type {{.GetName}}Server interface { {{- range .Method}} {{- if ne .GetInputType ".nrpc.NoRequest"}} {{- $resultType := GetResultType .}} {{.GetName}}(ctx context.Context {{- range GetMethodSubjectParams . -}} , {{ . }} string {{- end -}} {{- if ne .GetInputType ".nrpc.Void" -}} , req *{{GoType .GetInputType}} {{- end -}} {{- if HasStreamedReply . -}} , pushRep func(*{{GoType .GetOutputType}}) {{- end -}} ) {{- if ne $resultType ".nrpc.NoReply" }} ( {{- if and (ne $resultType ".nrpc.Void") (not (HasStreamedReply .)) -}} *{{GoType $resultType}}, {{end -}} error) {{- end -}} {{- end}} {{- end}} } {{- if Prometheus}} var ( // The request completion time, measured at client-side. clientRCTFor{{.GetName}} = prometheus.NewSummaryVec( prometheus.SummaryOpts{ Name: "nrpc_client_request_completion_time_seconds", Help: "The request completion time for calls, measured client-side.", Objectives: map[float64]float64{0.9: 0.01, 0.95: 0.01, 0.99: 0.001}, ConstLabels: map[string]string{ "service": "{{.GetName}}", }, }, []string{"method"}) // The handler execution time, measured at server-side. serverHETFor{{.GetName}} = prometheus.NewSummaryVec( prometheus.SummaryOpts{ Name: "nrpc_server_handler_execution_time_seconds", Help: "The handler execution time for calls, measured server-side.", Objectives: map[float64]float64{0.9: 0.01, 0.95: 0.01, 0.99: 0.001}, ConstLabels: map[string]string{ "service": "{{.GetName}}", }, }, []string{"method"}) // The counts of calls made by the client, classified by result type. clientCallsFor{{.GetName}} = prometheus.NewCounterVec( prometheus.CounterOpts{ Name: "nrpc_client_calls_count", Help: "The count of calls made by the client.", ConstLabels: map[string]string{ "service": "{{.GetName}}", }, }, []string{"method", "encoding", "result_type"}) // The counts of requests handled by the server, classified by result type. serverRequestsFor{{.GetName}} = prometheus.NewCounterVec( prometheus.CounterOpts{ Name: "nrpc_server_requests_count", Help: "The count of requests handled by the server.", ConstLabels: map[string]string{ "service": "{{.GetName}}", }, }, []string{"method", "encoding", "result_type"}) ) {{- end}} // {{.GetName}}Handler provides a NATS subscription handler that can serve a // subscription using a given {{.GetName}}Server implementation. type {{.GetName}}Handler struct { ctx context.Context workers *nrpc.WorkerPool nc nrpc.NatsConn server {{.GetName}}Server encodings []string } func New{{.GetName}}Handler(ctx context.Context, nc nrpc.NatsConn, s {{.GetName}}Server) *{{.GetName}}Handler { return &{{.GetName}}Handler{ ctx: ctx, nc: nc, server: s, encodings: []string{"protobuf"}, } } func New{{.GetName}}ConcurrentHandler(workers *nrpc.WorkerPool, nc nrpc.NatsConn, s {{.GetName}}Server) *{{.GetName}}Handler { return &{{.GetName}}Handler{ workers: workers, nc: nc, server: s, } } // SetEncodings sets the output encodings when using a '*Publish' function func (h *{{.GetName}}Handler) SetEncodings(encodings []string) { h.encodings = encodings } func (h *{{.GetName}}Handler) Subject() string { return "{{$pkgSubjectPrefix}} {{- range $pkgSubjectParams -}} *. {{- end -}} {{GetServiceSubject .}} {{- range GetServiceSubjectParams . -}} .* {{- end -}} .>" } {{- $serviceName := .GetName}} {{- $serviceSubject := GetServiceSubject .}} {{- $serviceSubjectParams := GetServiceSubjectParams .}} {{- range .Method}} {{- if eq .GetInputType ".nrpc.NoRequest"}} func (h *{{$serviceName}}Handler) {{.GetName}}Publish( {{- range $pkgSubjectParams}}pkg{{.}} string, {{end -}} {{- range $serviceSubjectParams}}svc{{.}} string, {{end -}} {{- range GetMethodSubjectParams .}}mt{{.}} string, {{end -}} msg *{{GoType .GetOutputType}}) error { for _, encoding := range h.encodings { rawMsg, err := nrpc.Marshal(encoding, msg) if err != nil { log.Printf("{{$serviceName}}Handler.{{.GetName}}Publish: error marshaling the message: %s", err) return err } subject := "{{$pkgSubject}}." {{- range $pkgSubjectParams}} + pkg{{.}} + "."{{end -}} + "{{$serviceSubject}}." {{- range $serviceSubjectParams}} + svc{{.}} + "."{{end -}} + "{{GetMethodSubject .}}" {{- range GetMethodSubjectParams .}} + "." + mt{{.}}{{end}} if encoding != "protobuf" { subject += "." + encoding } if err := h.nc.Publish(subject, rawMsg); err != nil { return err } } return nil } {{- end}} {{- end}} {{- if ServiceNeedsHandler .}} func (h *{{.GetName}}Handler) Handler(msg *nats.Msg) { var ctx context.Context if h.workers != nil { ctx = h.workers.Context } else { ctx = h.ctx } request := nrpc.NewRequest(ctx, h.nc, msg.Subject, msg.Reply) // extract method name & encoding from subject {{ if ne 0 (len $pkgSubjectParams)}}pkgParams{{else}}_{{end -}}, {{- if ne 0 (len (GetServiceSubjectParams .))}} svcParams{{else}} _{{end -}} , name, tail, err := nrpc.ParseSubject( "{{$pkgSubject}}", {{len $pkgSubjectParams}}, "{{GetServiceSubject .}}", {{len (GetServiceSubjectParams .)}}, msg.Subject) if err != nil { log.Printf("{{.GetName}}Hanlder: {{.GetName}} subject parsing failed: %v", err) return } request.MethodName = name request.SubjectTail = tail {{- range $i, $name := $pkgSubjectParams }} request.SetPackageParam("{{$name}}", pkgParams[{{$i}}]) {{- end }} {{- range $i, $name := GetServiceSubjectParams . }} request.SetServiceParam("{{$name}}", svcParams[{{$i}}]) {{- end }} // call handler and form response var immediateError *nrpc.Error switch name { {{- range .Method}} case "{{GetMethodSubject .}}": {{- if eq .GetInputType ".nrpc.NoRequest"}} // {{.GetName}} is a no-request method. Ignore it. return {{- else}}{{/* !NoRequest */}} {{- if ne 0 (len (GetMethodSubjectParams .))}} var mtParams []string {{- end}} {{- if eq .GetOutputType ".nrpc.NoReply"}} request.NoReply = true {{- end}} {{if eq 0 (len (GetMethodSubjectParams .))}}_{{else}}mtParams{{end}}, request.Encoding, err = nrpc.ParseSubjectTail({{len (GetMethodSubjectParams .)}}, request.SubjectTail) if err != nil { log.Printf("{{.GetName}}Hanlder: {{.GetName}} subject parsing failed: %v", err) break } var req {{GoType .GetInputType}} if err := nrpc.Unmarshal(request.Encoding, msg.Data, &req); err != nil { log.Printf("{{.GetName}}Handler: {{.GetName}} request unmarshal failed: %v", err) immediateError = &nrpc.Error{ Type: nrpc.Error_CLIENT, Message: "bad request received: " + err.Error(), } {{- if Prometheus}} serverRequestsFor{{$serviceName}}.WithLabelValues( "{{.GetName}}", request.Encoding, "unmarshal_fail").Inc() {{- end}} } else { {{- if HasStreamedReply .}} request.EnableStreamedReply() request.Handler = func(ctx context.Context)(proto.Message, error){ err := h.server.{{.GetName}}(ctx {{- range $i, $p := GetMethodSubjectParams . -}} , mtParams[{{ $i }}] {{- end -}} {{- if ne .GetInputType ".nrpc.Void" -}} , &req {{- end -}} , func(rep *{{GoType .GetOutputType}}){ request.SendStreamReply(rep) }) return nil, err } {{- else }} request.Handler = func(ctx context.Context)(proto.Message, error){ {{- if eq .GetOutputType ".nrpc.NoReply" -}} var innerResp = &nrpc.NoReply{} {{else}} {{if eq .GetOutputType ".nrpc.Void" -}} var innerResp = &nrpc.Void{} {{else}}innerResp, {{end -}} err := {{end -}} h.server.{{.GetName}}(ctx {{- range $i, $p := GetMethodSubjectParams . -}} , mtParams[{{ $i }}] {{- end -}} {{- if ne .GetInputType ".nrpc.Void" -}} , &req {{- end -}} ) if err != nil { return nil, err } return innerResp, err } {{- end }} } {{- end}}{{/* not HasStreamedReply */}} {{- end}}{{/* range .Method */}} default: log.Printf("{{.GetName}}Handler: unknown name %q", name) immediateError = &nrpc.Error{ Type: nrpc.Error_CLIENT, Message: "unknown name: " + name, } {{- if Prometheus}} serverRequestsFor{{.GetName}}.WithLabelValues( "{{.GetName}}", request.Encoding, "name_fail").Inc() {{- end}} } {{- if Prometheus}} request.AfterReply = func(request *nrpc.Request, success, replySuccess bool) { if !replySuccess { serverRequestsFor{{$serviceName}}.WithLabelValues( request.MethodName, request.Encoding, "sendreply_fail").Inc() } if success { serverRequestsFor{{$serviceName}}.WithLabelValues( request.MethodName, request.Encoding, "success").Inc() } else { serverRequestsFor{{$serviceName}}.WithLabelValues( request.MethodName, request.Encoding, "handler_fail").Inc() } // report metric to Prometheus serverHETFor{{$serviceName}}.WithLabelValues(request.MethodName).Observe( request.Elapsed().Seconds()) } {{- end}} if immediateError == nil { if h.workers != nil { // Try queuing the request if err := h.workers.QueueRequest(request); err != nil { log.Printf("nrpc: Error queuing the request: %s", err) } } else { // Run the handler synchronously request.RunAndReply() } } if immediateError != nil { if err := request.SendReply(nil, immediateError); err != nil { log.Printf("{{.GetName}}Handler: {{.GetName}} handler failed to publish the response: %s", err) {{- if Prometheus}} serverRequestsFor{{$serviceName}}.WithLabelValues( request.MethodName, request.Encoding, "handler_fail").Inc() {{- end}} } {{- if Prometheus}} serverHETFor{{$serviceName}}.WithLabelValues(request.MethodName).Observe( request.Elapsed().Seconds()) {{- end}} } else { } } {{- end}} type {{.GetName}}Client struct { nc nrpc.NatsConn {{- if ne 0 (len $pkgSubject)}} PkgSubject string {{- end}} {{- range $pkgSubjectParams}} PkgParam{{ . }} string {{- end}} Subject string {{- range GetServiceSubjectParams .}} SvcParam{{ . }} string {{- end}} Encoding string Timeout time.Duration } func New{{.GetName}}Client(nc nrpc.NatsConn {{- range $pkgSubjectParams -}} , pkgParam{{.}} string {{- end -}} {{- range GetServiceSubjectParams . -}} , svcParam{{ . }} string {{- end -}} ) *{{.GetName}}Client { return &{{.GetName}}Client{ nc: nc, {{- if ne 0 (len $pkgSubject)}} PkgSubject: "{{$pkgSubject}}", {{- end}} {{- range $pkgSubjectParams}} PkgParam{{.}}: pkgParam{{.}}, {{- end}} Subject: "{{GetServiceSubject .}}", {{- range GetServiceSubjectParams .}} SvcParam{{.}}: svcParam{{.}}, {{- end}} Encoding: "protobuf", Timeout: 5 * time.Second, } } {{- $serviceName := .GetName}} {{- $serviceSubjectParams := GetServiceSubjectParams .}} {{- range .Method}} {{- $resultType := GetResultType .}} {{- if eq .GetInputType ".nrpc.NoRequest"}} func (c *{{$serviceName}}Client) {{.GetName}}Subject( {{range GetMethodSubjectParams .}}mt{{.}} string,{{end}} ) string { subject := {{ if ne 0 (len $pkgSubject) -}} c.PkgSubject + "." + {{end}} {{- range $pkgSubjectParams -}} c.PkgParam{{.}} + "." + {{end -}} c.Subject + "." + {{range $serviceSubjectParams -}} c.SvcParam{{.}} + "." + {{end -}} "{{GetMethodSubject .}}" {{- range GetMethodSubjectParams .}} + "." + mt{{.}}{{end}} if c.Encoding != "protobuf" { subject += "." + c.Encoding } return subject } type {{$serviceName}}{{.GetName}}Subscription struct { *nats.Subscription encoding string } func (s *{{$serviceName}}{{.GetName}}Subscription) Next(timeout time.Duration) (next {{GoType .GetOutputType}}, err error) { msg, err := s.Subscription.NextMsg(timeout) if err != nil { return } err = nrpc.Unmarshal(s.encoding, msg.Data, &next) return } func (c *{{$serviceName}}Client) {{.GetName}}SubscribeSync( {{range GetMethodSubjectParams .}}mt{{.}} string,{{end}} ) (sub *{{$serviceName}}{{.GetName}}Subscription, err error) { subject := c.{{.GetName}}Subject( {{range GetMethodSubjectParams .}}mt{{.}},{{end}} ) natsSub, err := c.nc.SubscribeSync(subject) if err != nil { return } sub = &{{$serviceName}}{{.GetName}}Subscription{natsSub, c.Encoding} return } func (c *{{$serviceName}}Client) {{.GetName}}Subscribe( {{range GetMethodSubjectParams .}}mt{{.}} string,{{end}} handler func (*{{GoType .GetOutputType}}), ) (sub *nats.Subscription, err error) { subject := c.{{.GetName}}Subject( {{range GetMethodSubjectParams .}}mt{{.}},{{end}} ) sub, err = c.nc.Subscribe(subject, func(msg *nats.Msg){ var pmsg {{GoType .GetOutputType}} err := nrpc.Unmarshal(c.Encoding, msg.Data, &pmsg) if err != nil { log.Printf("{{$serviceName}}Client.{{.GetName}}Subscribe: Error decoding, %s", err) return } handler(&pmsg) }) return } func (c *{{$serviceName}}Client) {{.GetName}}SubscribeChan( {{range GetMethodSubjectParams .}}mt{{.}} string,{{end}} ) (<-chan *{{GoType .GetOutputType}}, *nats.Subscription, error) { ch := make(chan *{{GoType .GetOutputType}}) sub, err := c.{{.GetName}}Subscribe( {{- range GetMethodSubjectParams .}}mt{{.}}, {{end -}} func (msg *{{GoType .GetOutputType}}) { ch <- msg }) return ch, sub, err } {{- else if HasStreamedReply .}} func (c *{{$serviceName}}Client) {{.GetName}}( ctx context.Context, {{- range GetMethodSubjectParams . -}} {{ . }} string, {{- end}} {{- if ne .GetInputType ".nrpc.Void"}} req *{{GoType .GetInputType}}, {{- end}} cb func (context.Context, *{{GoType .GetOutputType}}), ) error { {{- if Prometheus}} start := time.Now() {{- end}} subject := {{ if ne 0 (len $pkgSubject) -}} c.PkgSubject + "." + {{end}} {{- range $pkgSubjectParams -}} c.PkgParam{{.}} + "." + {{end -}} c.Subject + "." + {{range $serviceSubjectParams -}} c.SvcParam{{.}} + "." + {{end -}} "{{GetMethodSubject .}}" {{- range GetMethodSubjectParams . }} + "." + {{ . }}{{ end }} sub, err := nrpc.StreamCall(ctx, c.nc, subject {{- if ne .GetInputType ".nrpc.Void" -}} , req {{- else -}} , &nrpc.Void{} {{- end -}} , c.Encoding, c.Timeout) if err != nil { {{- if Prometheus}} clientCallsFor{{$serviceName}}.WithLabelValues( "{{.GetName}}", c.Encoding, "error").Inc() {{- end}} return err } var res {{GoType .GetOutputType}} for { err = sub.Next(&res) if err != nil { break } cb(ctx, &res) } if err == nrpc.ErrEOS { err = nil } {{- if Prometheus}} // report total time taken to Prometheus elapsed := time.Since(start).Seconds() clientRCTFor{{$serviceName}}.WithLabelValues("{{.GetName}}").Observe(elapsed) clientCallsFor{{$serviceName}}.WithLabelValues( "{{.GetName}}", c.Encoding, "success").Inc() {{- end}} return err } {{- else}} func (c *{{$serviceName}}Client) {{.GetName}}( {{- range GetMethodSubjectParams . -}} {{ . }} string, {{ end -}} {{- if ne .GetInputType ".nrpc.Void" -}} req *{{GoType .GetInputType}} {{- end -}}) ( {{- if not (eq $resultType ".nrpc.Void" ".nrpc.NoReply") -}} *{{GoType $resultType}}, {{end -}} error) { {{- if Prometheus}} start := time.Now() {{- end}} subject := {{ if ne 0 (len $pkgSubject) -}} c.PkgSubject + "." + {{end}} {{- range $pkgSubjectParams -}} c.PkgParam{{.}} + "." + {{end -}} c.Subject + "." + {{range $serviceSubjectParams -}} c.SvcParam{{.}} + "." + {{end -}} "{{GetMethodSubject .}}" {{- range GetMethodSubjectParams . }} + "." + {{ . }}{{ end }} // call {{- if eq .GetInputType ".nrpc.Void"}} var req = &{{GoType .GetInputType}}{} {{- end}} var resp = {{GoType $resultType}}{} if err := nrpc.Call(req, &resp, c.nc, subject, c.Encoding, c.Timeout); err != nil { {{- if Prometheus}} clientCallsFor{{$serviceName}}.WithLabelValues( "{{.GetName}}", c.Encoding, "call_fail").Inc() {{- end}} return {{ if not (eq $resultType ".nrpc.Void" ".nrpc.NoReply") }}nil, {{ end }}err } {{- if Prometheus}} // report total time taken to Prometheus elapsed := time.Since(start).Seconds() clientRCTFor{{$serviceName}}.WithLabelValues("{{.GetName}}").Observe(elapsed) clientCallsFor{{$serviceName}}.WithLabelValues( "{{.GetName}}", c.Encoding, "success").Inc() {{- end}} return {{ if not (eq $resultType ".nrpc.Void" ".nrpc.NoReply") }}&resp, {{ end }}nil } {{- if HasPollEnabled .}} func (c *{{$serviceName}}Client) {{.GetName}}Poll( {{- range GetMethodSubjectParams . -}} {{ . }} string, {{ end -}} {{- if ne .GetInputType ".nrpc.Void" -}} req *{{GoType .GetInputType}}, {{- end -}} maxreplies int, cb func (*{{GoType .GetOutputType}}) error, ) (error) { {{- if Prometheus}} start := time.Now() {{- end}} subject := {{ if ne 0 (len $pkgSubject) -}} c.PkgSubject + "." + {{end}} {{- range $pkgSubjectParams -}} c.PkgParam{{.}} + "." + {{end -}} c.Subject + "." + {{range $serviceSubjectParams -}} c.SvcParam{{.}} + "." + {{end -}} "{{GetMethodSubject .}}" {{- range GetMethodSubjectParams . }} + "." + {{ . }}{{ end }} {{- if eq .GetInputType ".nrpc.Void"}} var req = &{{GoType .GetInputType}}{} {{- end}} var resp {{GoType .GetOutputType}} err := nrpc.Poll(req, &resp, c.nc, subject, c.Encoding, c.Timeout, maxreplies, func() error { return cb(&resp) }, ) if err != nil { {{- if Prometheus}} clientCallsFor{{$serviceName}}.WithLabelValues( "{{.GetName}}", c.Encoding, "poll_fail").Inc() {{- end}} return err } {{- if Prometheus}} // report total time taken to Prometheus elapsed := time.Since(start).Seconds() clientRCTFor{{$serviceName}}.WithLabelValues("{{.GetName}}").Observe(elapsed) clientCallsFor{{$serviceName}}.WithLabelValues( "{{.GetName}}", c.Encoding, "poll_success").Inc() {{- end}} return nil } {{- end}} {{- end}} {{- end}} {{- end}} type Client struct { nc nrpc.NatsConn defaultEncoding string defaultTimeout time.Duration {{- if ne 0 (len $pkgSubject)}} pkgSubject string {{- end}} {{- range $pkgSubjectParams}} pkgParam{{ . }} string {{- end}} {{- range .Service}} {{.GetName}} *{{.GetName}}Client {{- end}} } func NewClient(nc nrpc.NatsConn {{- range $pkgSubjectParams -}} , pkgParam{{.}} string {{- end -}}) *Client { c := Client{ nc: nc, defaultEncoding: "protobuf", defaultTimeout: 5*time.Second, {{- if ne 0 (len $pkgSubject)}} pkgSubject: "{{$pkgSubject}}", {{- end}} {{- range $pkgSubjectParams}} pkgParam{{.}}: pkgParam{{.}}, {{- end}} } {{- range .Service}} {{- if eq 0 (len (GetServiceSubjectParams .))}} c.{{.GetName}} = New{{.GetName}}Client(nc {{- range $pkgSubjectParams -}} , c.pkgParam{{ . }} {{- end}}) {{- end}} {{- end}} return &c } func (c *Client) SetEncoding(encoding string) { c.defaultEncoding = encoding {{- range .Service}} if c.{{.GetName}} != nil { c.{{.GetName}}.Encoding = encoding } {{- end}} } func (c *Client) SetTimeout(t time.Duration) { c.defaultTimeout = t {{- range .Service}} if c.{{.GetName}} != nil { c.{{.GetName}}.Timeout = t } {{- end}} } {{- range .Service}} {{- if ne 0 (len (GetServiceSubjectParams .))}} func (c *Client) Set{{.GetName}}Params( {{- range GetServiceSubjectParams .}} {{ . }} string, {{- end}} ) { c.{{.GetName}} = New{{.GetName}}Client( c.nc, {{- range $pkgSubjectParams}} c.pkgParam{{ . }}, {{- end}} {{- range GetServiceSubjectParams .}} {{ . }}, {{- end}} ) c.{{.GetName}}.Encoding = c.defaultEncoding c.{{.GetName}}.Timeout = c.defaultTimeout } func (c *Client) New{{.GetName}}( {{- range GetServiceSubjectParams .}} {{ . }} string, {{- end}} ) *{{.GetName}}Client { client := New{{.GetName}}Client( c.nc, {{- range $pkgSubjectParams}} c.pkgParam{{ . }}, {{- end}} {{- range GetServiceSubjectParams .}} {{ . }}, {{- end}} ) client.Encoding = c.defaultEncoding client.Timeout = c.defaultTimeout return client } {{- end}} {{- end}} {{- if Prometheus}} func init() { {{- range .Service}} // register metrics for service {{.GetName}} prometheus.MustRegister(clientRCTFor{{.GetName}}) prometheus.MustRegister(serverHETFor{{.GetName}}) prometheus.MustRegister(clientCallsFor{{.GetName}}) prometheus.MustRegister(serverRequestsFor{{.GetName}}) {{- end}} } {{- end}}` ================================================ FILE: testrunner_test.go ================================================ package nrpc import ( "log" "os" "testing" "time" "github.com/nats-io/nats-server/v2/logger" "github.com/nats-io/nats-server/v2/server" ) var NatsURL string func TestMain(m *testing.M) { gnatsd := server.New(&server.Options{Port: server.RANDOM_PORT}) gnatsd.SetLogger( logger.NewStdLogger(false, false, false, false, false), false, false) go gnatsd.Start() defer gnatsd.Shutdown() if !gnatsd.ReadyForConnections(time.Second) { log.Fatal("Cannot start the gnatsd server") } NatsURL = "nats://" + gnatsd.Addr().String() os.Exit(m.Run()) }