{"id":18722386,"url":"https://github.com/nleiva/gmessaging","last_synced_at":"2026-03-06T16:03:32.039Z","repository":{"id":57525452,"uuid":"87981337","full_name":"nleiva/gmessaging","owner":"nleiva","description":"GPB and gRPC testing","archived":false,"fork":false,"pushed_at":"2022-05-06T20:39:45.000Z","size":55,"stargazers_count":15,"open_issues_count":0,"forks_count":2,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-04-12T14:49:54.360Z","etag":null,"topics":["golang","gpb","grpc","grpc-testing","protobuf","protocol-buffers","router"],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nleiva.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-04-11T21:33:14.000Z","updated_at":"2022-05-16T13:29:31.000Z","dependencies_parsed_at":"2022-08-28T17:41:26.216Z","dependency_job_id":null,"html_url":"https://github.com/nleiva/gmessaging","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/nleiva/gmessaging","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nleiva%2Fgmessaging","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nleiva%2Fgmessaging/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nleiva%2Fgmessaging/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nleiva%2Fgmessaging/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nleiva","download_url":"https://codeload.github.com/nleiva/gmessaging/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nleiva%2Fgmessaging/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30184885,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-06T14:42:24.748Z","status":"ssl_error","status_checked_at":"2026-03-06T14:42:14.925Z","response_time":250,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["golang","gpb","grpc","grpc-testing","protobuf","protocol-buffers","router"],"created_at":"2024-11-07T13:41:13.540Z","updated_at":"2026-03-06T16:03:32.022Z","avatar_url":"https://github.com/nleiva.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# gRPC and GPB for Networking Engineers\n\nGPB and gRPC testing. Based on the [protobuf examples](https://github.com/google/protobuf/tree/master/examples) and [Pluralsight](https://www.pluralsight.com/) training.\n\n## Table of contents\n\n- [gmessaging](#gmessaging)\n  * [Code Examples](#code-examples)\n  * [Compiling your protocol buffers](#compiling-your-protocol-buffers)\n  * [Understanding GPB encoding](#understanding-gpb-encoding)\n  * [Understanding Go proto code](#understanding-go-proto-code)\n  * [Compiling the code](#compiling-the-code)\n  * [Running some examples](#running-some-examples)\n  * [Generating Server Certificate and Private Key](#generating-server-certificate-and-private-key)\n  * [Links](#links)\n\n## Code Examples\n\n* [add_router.go](add_router.go) takes a static router entry and adds it to [routers.data](routers.data). Example:\n\n```go\n\trouters := \u0026pb.Routers{}\n\trouter := \u0026pb.Router{}\n\n\trouter.IP = []byte(\"2001:db8::123:44:4\")\n\trouter.Hostname = \"router4.cisco.com\"\n\n\trouters.Router = append(routers.Router, router)\n```\n\nIf we inspect [routers.data](routers.data).\n\n```bash\n$ hexdump -c routers.data\n0000000  \\n   \u0026  \\n 020   r   o   u   t   e   r   .   c   i   s   c   o\n0000010   .   c   o   m 022 022   2   0   0   1   :   d   b   8   :   :\n0000020   1   2   3   :   1   2   :   1  \\n   '  \\n 021   r   o   u   t\n0000030   e   r   2   .   c   i   s   c   o   .   c   o   m 022 022   2\n0000040   0   0   1   :   d   b   8   :   :   1   2   3   :   1   2   :\n0000050   2  \\n   '  \\n 021   r   o   u   t   e   r   3   .   c   i   s\n0000060   c   o   .   c   o   m 022 022   2   0   0   1   :   d   b   8\n0000070   :   :   1   2   3   :   3   3   :   3  \\n   '  \\n 021   r   o\n0000080   u   t   e   r   4   .   c   i   s   c   o   .   c   o   m 022\n0000090 022   2   0   0   1   :   d   b   8   :   :   1   2   3   :   4\n00000a0   4   :   4\n00000a3\n```\n\n```bash\n$ cat routers.data | protoc --decode_raw\n1 {\n  1: \"router.cisco.com\"\n  2: \"2001:db8::123:12:1\"\n}\n1 {\n  1: \"router2.cisco.com\"\n  2: \"2001:db8::123:12:2\"\n}\n1 {\n  1: \"router3.cisco.com\"\n  2: \"2001:db8::123:33:3\"\n}\n1 {\n  1: \"router4.cisco.com\"\n  2: \"2001:db8::123:44:4\"\n}\n```\n\n* [list_router.go](list_router.go) reads [routers.data](routers.data) and prints it out.\n\n```go\nin, err := ioutil.ReadFile(fname)\nif err != nil {\n\tlog.Fatalln(\"Error reading file:\", err)\n}\nrouters := \u0026pb.Routers{}\nif err := proto.Unmarshal(in, routers); err != nil {\n\tlog.Fatalln(\"Failed to parse the routers file:\", err)\n}\n```\n\n* [data.go](data.go) assigns values to different instances of our `Routers` struct. Example:\n\n```go\nvar router = []*pb.Router{\n\t\u0026pb.Router{\n\t\tHostname: \"router1.cisco.com\",\n\t\tIP:       []byte(\"2001:db8::111:11:1\"),\n\t},\n}\n\nrouters := pb.Routers{router}\n```\n\n* [server.go](gserver/main.go) creates a Server that implements the [DeviceServiceServer](gproto/devices.pb.go#L256) interface.\n\n```go\ntype server struct{}\n\nfunc (s *server) GetByHostname(ctx context.Context,\n\tin *pb.GetByHostnameRequest) (*pb.Router, error) {\n\treturn nil, nil\n}\n...\n```\n\n* [client.go](gclient/main.go) creates a Client that creates a new [DeviceServiceClient](gproto/devices.pb.go#L165) type.\n\n```go\nconn, err := grpc.Dial(address, grpc.WithInsecure())\nif err != nil {\n\tlog.Fatalf(\"did not connect: %v\", err)\n}\ndefer conn.Close()\n\nclient := pb.NewDeviceServiceClient(conn)\n...\n```\n\n## Compiling your protocol buffers\n\n* `protoc --go_out=gproto devices.proto` only defines the GPB part, to read and write as demonstrated in [list_routers.go](list_routers.go) and [add_router.go](add_router.go).\n* `protoc --go_out=plugins=grpc:gproto devices.proto` adds the RPC services. It creates [gproto/devices.pb.go](gproto/devices.pb.go). You need this one to run the client and server below.\n\n## Understanding GPB encoding\n\nLet's print out the GPB encoded slice of bytes\n\n```go\nout, err := proto.Marshal(routers)\nif err != nil {\n\tlog.Fatalln(\"Failed to encode routers:\", err)\n}\nfmt.Printf(\"%X\", out)\n```\n\nAfter grouping the output for convenience, we get something like:\n\n```hexdump\n0A 26 0A 10 72 6F 75 74 65 72 2E 63 69 73 63 6F 2E 63 6F 6D\n12 12 32 30 30 31 3A 64 62 38 3A 3A 31 32 33 3A 31 32 3A 31\n0A 27 0A 11 72 6F 75 74 65 72 32 2E 63 69 73 63 6F 2E 63 6F 6D\n12 12 32 30 30 31 3A 64 62 38 3A 3A 31 32 33 3A 31 32 3A 32\n0A 27 0A 11 72 6F 75 74 65 72 33 2E 63 69 73 63 6F 2E 63 6F 6D\n12 12 32 30 30 31 3A 64 62 38 3A 3A 31 32 33 3A 33 33 3A 33\n0A 27 0A 11 72 6F 75 74 65 72 34 2E 63 69 73 63 6F 2E 63 6F 6D\n12 12 32 30 30 31 3A 64 62 38 3A 3A 31 32 33 3A 34 34 3A 34\n```\n\nConsidering the definitions on the proto file ([devices.proto](devices.proto))\n\n```proto\nmessage Router {\n  string hostname = 1;\n  bytes IP = 2; \n}\n\nmessage Routers {\n  repeated Router router = 1;\n}\n```\n\nProtobuf uses [Varint](https://developers.google.com/protocol-buffers/docs/encoding#varints) to serialize integers. The last three bits of the number store the wire type. Having this in mind and how to convert Hex to ASCII, the first 40 bytes (or two rows from the output) translate to:\n\n```bash\nHex  Description\n0a  tag: router(1), field encoding: LENGTH_DELIMITED(2)\n26  \"router\".length(): 38\n0a  tag: hostname(1), field encoding: LENGTH_DELIMITED(2)\n10  \"hostname\".length(): 16 \n72 'r'\n6F 'o'\n75 'u'\n74 't'\n65 'e'\n72 'r'\n2E '.'\n63 'c'\n69 'i'\n73 's'\n63 'c'\n6F 'o'\n2E '.'\n63 'c'\n6F 'o'\n6D 'm'\n12 tag: IP(2), field encoding: LENGTH_DELIMITED(2)\n12 \"IP\".length(): 18\n32 '2'\n30 '0'\n30 '0'\n31 '1'\n...\n31 '1'\n```\n\nIts equivalent in JSON would be something like this ([routers.json](routers.json)):\n\n```json\n{\n  \"Router\": [\n    {\n      \"Hostname\": \"router.cisco.com\",\n      \"IP\": \"2001:db8::123:12:1\"\n    }\n  ]\n}\n```\n\n## Understanding Go proto code\n\n[Marshal](https://godoc.org/github.com/golang/protobuf/proto#Marshal) takes the protocol buffer and encodes it into the wire format, returning the data.\n\n```go\nfunc Marshal(pb Message) ([]byte, error)\n```\n\n[Unmarshal](https://godoc.org/github.com/golang/protobuf/proto#Unmarshal) parses the protocol buffer representation in buf and places the decoded result in pb\n\n```go\nfunc Unmarshal(buf []byte, pb Message) error\n```\n\n[Message](https://github.com/golang/protobuf/blob/master/proto/lib.go#L277) is implemented by generated protocol buffer messages.\n\n```go\ntype Message interface {\n    Reset()\n    String() string\n    ProtoMessage()\n}\n```\n\nIn our example generated code [devices.pb.go](gproto/devices.pb.go), [Router](gproto/devices.pb.go#L42) and [Routers](gproto/devices.pb.go#L66) structs are defined\n\n```go\ntype Router struct {\n\tHostname string `protobuf:\"bytes,1,opt,name=hostname\" json:\"hostname,omitempty\"`\n\tIP       []byte `protobuf:\"bytes,2,opt,name=IP,proto3\" json:\"IP,omitempty\"`\n}\n```\n\n```go\ntype Routers struct {\n\tRouter []*Router `protobuf:\"bytes,1,rep,name=router\" json:\"router,omitempty\"`\n}\n```\n\nBoth implement the [Message](https://github.com/golang/protobuf/blob/master/proto/lib.go#L277) interface\n\n```go\nfunc (m *Router) Reset()                    { *m = Router{} }\nfunc (m *Router) String() string            { return proto.CompactTextString(m) }\nfunc (*Router) ProtoMessage()               {}\n```\n\n```go\nfunc (m *Routers) Reset()                    { *m = Routers{} }\nfunc (m *Routers) String() string            { return proto.CompactTextString(m) }\nfunc (*Routers) ProtoMessage()               {}\n```\n\n## Compiling the code\n\n* gRPC client: `go build -o client gclient/main.go`\n* gRPC server: `go build -o server gserver/*.go`\n\n## Running some examples\n\n* Examples are pretty static for now. The client just executes a method based on the arguments the command line provides.\n\n```go\nswitch *option {\ncase 1:\n\tSendMetadata(client)\ncase 2:\n\tGetByHostname(client)\ncase 3:\n\tGetAll(client)\ncase 4:\n\tSave(client)\ncase 5:\n\tSaveAll(client)\n}\n```\n* SaveAll looks like this, the client prints the devices it wants to add and the server prints the new complete list.\n\n```console\n$ ./client -o 5\nhostname:\"router8.cisco.com\" IP:\"2001:db8::888:88:8\" \nhostname:\"router9.cisco.com\" IP:\"2001:db8::999:99:9\" \n```\n\n```console\n$ ./server\n2017/04/29 20:27:35 Starting server on port :50051\nhostname:\"router1.cisco.com\" IP:\"2001:db8::111:11:1\" \nhostname:\"router2.cisco.com\" IP:\"2001:db8::222:22:2\" \nhostname:\"router3.cisco.com\" IP:\"2001:db8::333:33:3\" \nhostname:\"router8.cisco.com\" IP:\"2001:db8::888:88:8\" \nhostname:\"router9.cisco.com\" IP:\"2001:db8::999:99:9\"\n```\n\n## Generating Server Certificate and Private Key\n\nThis is optional in order to generate secure connections. We create a new private key 'key.pem' and a server certificate 'cert.pem'\n\n```console\n$ openssl req -new -x509 -nodes -subj '/C=US/CN=localhost' \\\n                  -addext \"subjectAltName = DNS:localhost\" \\\n                  -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365\n```\n\n## Links\n\n* [Sublime Protobuf Syntax Hightlighting](https://packagecontrol.io/packages/Protobuf%20Syntax%20Hightlighting)\n* [proto3 Language Guide](https://developers.google.com/protocol-buffers/docs/proto3)\n* [Protocol Buffer Basics: Go](https://developers.google.com/protocol-buffers/docs/gotutorial)\n* [Using protocol buffers with Go](https://github.com/golang/protobuf#using-protocol-buffers-with-go)\n* [gRPC Basics - Go](http://www.grpc.io/docs/tutorials/basic/go.html)\n* [Using Go to generate Certs and Private Keys](http://www.kaihag.com/https-and-go/)\n* [Authentication in gRPC](http://mycodesmells.com/post/authentication-in-grpc)\n* [Use cases for gRPC in network management](https://tools.ietf.org/html/draft-talwar-rtgwg-grpc-use-cases)\n* [gRPC Network Management Interface (gNMI)](https://tools.ietf.org/html/draft-openconfig-rtgwg-gnmi-spec)\n* [Network Management Datastore Architecture](https://datatracker.ietf.org/doc/html/draft-ietf-netmod-revised-datastores)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnleiva%2Fgmessaging","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnleiva%2Fgmessaging","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnleiva%2Fgmessaging/lists"}