{"id":16179876,"url":"https://github.com/bep/execrpc","last_synced_at":"2025-03-19T01:31:03.720Z","repository":{"id":57748259,"uuid":"523287770","full_name":"bep/execrpc","owner":"bep","description":"RCP via os/exec with generic types.","archived":false,"fork":false,"pushed_at":"2024-04-03T15:48:13.000Z","size":78,"stargazers_count":13,"open_issues_count":1,"forks_count":0,"subscribers_count":4,"default_branch":"main","last_synced_at":"2024-05-01T21:30:58.601Z","etag":null,"topics":["golang","rpc-framework"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bep.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["bep"]}},"created_at":"2022-08-10T09:51:27.000Z","updated_at":"2024-06-19T10:25:02.320Z","dependencies_parsed_at":"2024-04-03T16:45:08.156Z","dependency_job_id":null,"html_url":"https://github.com/bep/execrpc","commit_stats":null,"previous_names":[],"tags_count":11,"template":false,"template_full_name":"bep/golibtemplate","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bep%2Fexecrpc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bep%2Fexecrpc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bep%2Fexecrpc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bep%2Fexecrpc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bep","download_url":"https://codeload.github.com/bep/execrpc/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243960361,"owners_count":20375102,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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","rpc-framework"],"created_at":"2024-10-10T05:44:11.077Z","updated_at":"2025-03-19T01:31:03.469Z","avatar_url":"https://github.com/bep.png","language":"Go","funding_links":["https://github.com/sponsors/bep"],"categories":[],"sub_categories":[],"readme":"[![Tests on Linux, MacOS and Windows](https://github.com/bep/execrpc/workflows/Test/badge.svg)](https://github.com/bep/execrpc/actions?query=workflow:Test)\n[![Go Report Card](https://goreportcard.com/badge/github.com/bep/execrpc)](https://goreportcard.com/report/github.com/bep/execrpc)\n[![GoDoc](https://godoc.org/github.com/bep/execrpc?status.svg)](https://godoc.org/github.com/bep/execrpc)\n\nThis library implements a simple, custom [RPC protocol](https://en.wikipedia.org/wiki/Remote_procedure_call) via [os/exex](https://pkg.go.dev/os/exec) and stdin and stdout. Both server and client comes in a raw (`[]byte`) and strongly typed variant (using Go generics).\n\nA strongly typed client may look like this:\n\n```go\n// Define the request, message and receipt types for the RPC call.\nclient, err := execrpc.StartClient(\n\tclient, err := execrpc.StartClient(\n\texecrpc.ClientOptions[model.ExampleConfig, model.ExampleRequest, model.ExampleMessage, model.ExampleReceipt]{\n\t\tClientRawOptions: execrpc.ClientRawOptions{\n\t\t\tVersion: 1,\n\t\t\tCmd:     \"go\",\n\t\t\tDir:     \"./examples/servers/typed\",\n\t\t\tArgs:    []string{\"run\", \".\"},\n\t\t\tEnv:     env,\n\t\t\tTimeout: 30 * time.Second,\n\t\t},\n\t\tConfig: model.ExampleConfig{},\n\t\tCodec:  codec,\n\t},\n)\n\nif err != nil {\n\tlog.Fatal(err)\n}\n\n// Consume standalone messages (e.g. log messages) in its own goroutine.\ngo func() {\n\tfor msg := range client.MessagesRaw() {\n\t\tfmt.Println(\"got message\", string(msg.Body))\n\t}\n}()\n\n// Execute the request.\nresult := client.Execute(model.ExampleRequest{Text: \"world\"})\n\n// Check for errors.\nif err := result.Err(); err != nil {\n\tlog.Fatal(err)\n}\n\n// Consume the messages.\nfor m := range result.Messages() {\n\tfmt.Println(m)\n}\n\n// Wait for the receipt.\nreceipt := \u003c-result.Receipt()\n\n// Check again for errors.\nif err := result.Err(); err != nil {\n\tlog.Fatal(err)\n}\n\nfmt.Println(receipt.Text)\n\n// Close the client.\nif err := client.Close(); err != nil {\n\tlog.Fatal(err)\n}\n```\n\nTo get the best performance you should keep the client open as long as its needed – and store it as a shared object; it's safe and encouraged to call `Execute` from multiple goroutines.\n\nAnd the server side of the above:\n\n```go\n\nfunc main() {\n\tlog.SetFlags(0)\n\tlog.SetPrefix(\"readme-example: \")\n\n\tvar clientConfig model.ExampleConfig\n\n\tserver, err := execrpc.NewServer(\n\t\texecrpc.ServerOptions[model.ExampleConfig, model.ExampleRequest, model.ExampleMessage, model.ExampleReceipt]{\n\t\t\t// Optional function to provide a hasher for the ETag.\n\t\t\tGetHasher: func() hash.Hash {\n\t\t\t\treturn fnv.New64a()\n\t\t\t},\n\n\t\t\t// Allows you to delay message delivery, and drop\n\t\t\t// them after reading the receipt (e.g. the ETag matches the ETag seen by client).\n\t\t\tDelayDelivery: false,\n\n\t\t\t// Optional function to initialize the server\n\t\t\t// with the client configuration.\n\t\t\t// This will be called once on server start.\n\t\t\tInit: func(cfg model.ExampleConfig) error {\n\t\t\t\tclientConfig = cfg\n\t\t\t\treturn clientConfig.Init()\n\t\t\t},\n\n\t\t\t// Handle the incoming call.\n\t\t\tHandle: func(c *execrpc.Call[model.ExampleRequest, model.ExampleMessage, model.ExampleReceipt]) {\n\t\t\t\t// Raw messages are passed directly to the client,\n\t\t\t\t// typically used for log messages.\n\t\t\t\tc.SendRaw(\n\t\t\t\t\texecrpc.Message{\n\t\t\t\t\t\tHeader: execrpc.Header{\n\t\t\t\t\t\t\tVersion: 32,\n\t\t\t\t\t\t\tStatus:  150,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tBody: []byte(\"log message\"),\n\t\t\t\t\t},\n\t\t\t\t)\n\n\t\t\t\t// Enqueue one or more messages.\n\t\t\t\tc.Enqueue(\n\t\t\t\t\tmodel.ExampleMessage{\n\t\t\t\t\t\tHello: \"Hello 1!\",\n\t\t\t\t\t},\n\t\t\t\t\tmodel.ExampleMessage{\n\t\t\t\t\t\tHello: \"Hello 2!\",\n\t\t\t\t\t},\n\t\t\t\t)\n\n\t\t\t\tc.Enqueue(\n\t\t\t\t\tmodel.ExampleMessage{\n\t\t\t\t\t\tHello: \"Hello 3!\",\n\t\t\t\t\t},\n\t\t\t\t)\n\n\t\t\t\t// Wait for the framework generated receipt.\n\t\t\t\treceipt := \u003c-c.Receipt()\n\n\t\t\t\t// ETag provided by the framework.\n\t\t\t\t// A hash of all message bodies.\n\t\t\t\t// fmt.Println(\"Receipt:\", receipt.ETag)\n\n\t\t\t\t// Modify if needed.\n\t\t\t\treceipt.Size = uint32(123)\n\t\t\t\treceipt.Text = \"echoed: \" + c.Request.Text\n\n\t\t\t\t// Close the message stream and send the receipt.\n\t\t\t\t// Pass true to drop any queued messages,\n\t\t\t\t// this is only relevant if DelayDelivery is enabled.\n\t\t\t\tc.Close(false, receipt)\n\t\t\t},\n\t\t},\n\t)\n\tif err != nil {\n\t\thandleErr(err)\n\t}\n\n\tif err := server.Start(); err != nil {\n\t\thandleErr(err)\n\t}\n}\n\nfunc handleErr(err error) {\n\tlog.Fatalf(\"error: failed to start typed echo server: %s\", err)\n}\n\n```\n\n## Generate ETag\n\nThe server can generate an ETag for the messages. This is a hash of all message bodies. \n\nTo enable this:\n\n1. Provide a `GetHasher` function to the [server options](https://pkg.go.dev/github.com/bep/execrpc#ServerOptions).\n2. Have the `Receipt` implement the [TagProvider](https://pkg.go.dev/github.com/bep/execrpc#TagProvider) interface.\n\nNote that there are three different optional E-interfaces for the `Receipt`:\n\n1. [TagProvider](https://pkg.go.dev/github.com/bep/execrpc#TagProvider) for the ETag.\n2. [SizeProvider](https://pkg.go.dev/github.com/bep/execrpc#SizeProvider) for the size.\n3. [LastModifiedProvider](https://pkg.go.dev/github.com/bep/execrpc#LastModifiedProvider) for the last modified timestamp.\n\nA convenient struct that can be embedded in your `Receipt` that implements all of these is the [Identity](https://pkg.go.dev/github.com/bep/execrpc#Identity).\n\n## Status Codes\n\nThe status codes in the header between 1 and 99 are reserved for the system. This will typically be used to catch decoding/encoding errors on the server.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbep%2Fexecrpc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbep%2Fexecrpc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbep%2Fexecrpc/lists"}