# README
Package drpc
DRPC is a lightweight, drop-in, protocol buffer-based gRPC replacement. The DRPC protocol strips out huge swaths of unnecessary complexity; it is implemented in just a few thousand lines of straightforward Go! DRPC is small, extensible, efficient, and can still be autogenerated from your existing Protobuf definition files.
More information: https://github.com/storj/drpc
Server
This package simplifies the process of running a DRPC server in production by providing often required additional functionality, for example:
- Properly setup TLS to provide secure communication channels
- Protect against
panics
produced by external services - Generate structured logs for all processed requests
- Request authentication/authorization (authN, authZ)
- Rate limiting requests to avoid resource exhaustion attacks
For example, to start a typical production server.
// Token validation function. This will usually handle JWT or PASETO
// tokens for example.
myCustomCredsValidation := func(token string) bool {
return token == "super-secure-credentials"
}
// Server TLS settings. Cert, key and CA are usually read from a file
// or another secret-management tool.
tlsSettings := ServerTLS{
Cert: my-cert-pem,
PrivateKey: my-key-pem,
CustomCAs: [][]byte{my-ca-cert-pem},
IncludeSystemCAs: true,
}
// Define the middleware elements to use.
// In this case:
// - Use zerolog to produce "pretty" detailed output
// - Limit the processing to 100 RPC requests per-second
// - Provide a custom token-based authentication mechanism
// - Prevent the server from crashing on `panic` calls
smw := []srvmw.Middleware{
srvmw.Logging(xlog.WithZero(true, "error").Sub(xlog.Fields{"component": "server"}), nil),
srvmw.RateLimit(100),
srvmw.AuthByToken("auth.token", myCustomCredsValidation),
srvmw.PanicRecovery(),
}
// RPC server settings.
// - The server will listen on TCP port 8080
// - The server will handle DRPC and HTTP requests
// - The server will use TLS for secure connections
// - The server will provide bi-directional streaming over WebSockets (and DRPC)
opts := []Option{
WithServiceProvider(myServiceImplementation()),
WithPort(8080),
WithHTTP(),
WithMiddleware(smw...),
WithTLS(tlsSettings),
WithWebSocketProxy(
ws.EnableCompression(),
ws.CheckOrigin(func(r *http.Request) bool { return true }),
ws.HandshakeTimeout(2*time.Second),
ws.SubProtocols([]string{"rfb", "sip"}),
),
}
// Create and start the new server
srv, err := NewServer(opts...)
if err != nil {
panic(err)
}
srv.Start()
Client
This package simplifies the process of running a DRPC client in production by providing often required additional functionality, for example:
- Concurrent RPC requests (using connection pools)
- Properly setup TLS to provide secure communication channels
- Protect against
panics
produced by external services - Generate structured logs for all processed requests
- Provide custom metadata on all requests send to the server (e.g. credentials)
For example, to start a typical production client.
// Custom metadata. Can be used to provide identifiers, credentials or
// additional contextual details.
kv := map[string]string{
"metadata.user": "rick",
}
// Client TLS settings.
tlsSettings := ClientTLS{
IncludeSystemCAs: true,
CustomCAs: [][]byte{ca-cert-pem},
ServerName: "my-server.local.acme.com",
}
// Client middleware.
// - Provide custom details (`kv`) on every request
// - Use zerolog to produce "pretty" detailed output
// - Limit the client to 100 RPC requests per-second
// - Prevent the client from crashing on `panic` calls
cmw := []clmw.Middleware{
clmw.Metadata(kv),
clmw.Logging(xlog.WithZero(true, "error").Sub(xlog.Fields{"component": "client"}), nil),
clmw.RateLimit(100),
clmw.PanicRecovery(),
}
// Client options.
// - Up to 5 concurrent RPC calls
// - Include a protocol selection header (between DRPC and HTTP)
// - Use TLS to verify server's identity and establish secure connections
opts := []ClientOption{
WithProtocolHeader(),
WithPoolCapacity(5),
WithClientTLS(tlsSettings),
WithClientMiddleware(cmw...),
}
// Start new client
cl, err := NewClient("tcp", ":8080", opts...)
if err != nil {
panic(err)
}
// When no longer needed, the client must be closed
defer cl.Close()
// The client can be used directly to create stubs to consume
// specific DRPC services.
// RPC request
mySvc := samplev1.NewDRPCFooAPIClient(cl)
res, _ := mySvc.Ping(context.Background(), &emptypb.Empty{})
Custom Middleware
You can provide your own custom middleware to extend/adjust the processing
of RPC requests on both server and client using the decorator pattern. You
can then use the WithMiddleware
and WithClientMiddleware
options when
creating a new server or client instance.