package httpgrpc import ( "fmt" "github.com/golang/protobuf/ptypes" "github.com/golang/protobuf/ptypes/any" log "github.com/sirupsen/logrus" spb "google.golang.org/genproto/googleapis/rpc/status" "google.golang.org/grpc/status" ) // Errorf returns a HTTP gRPC error than is correctly forwarded over // gRPC, and can eventually be converted back to a HTTP response with // HTTPResponseFromError. func Errorf(code int, tmpl string, args ...interface{}) error { return ErrorFromHTTPResponse(&HTTPResponse{ Code: int32(code), Body: []byte(fmt.Sprintf(tmpl, args...)), }) } // ErrorFromHTTPResponse converts an HTTP response into a grpc error func ErrorFromHTTPResponse(resp *HTTPResponse) error { a, err := ptypes.MarshalAny(resp) if err != nil { return err } return status.ErrorProto(&spb.Status{ Code: resp.Code, Message: string(resp.Body), Details: []*any.Any{a}, }) } // HTTPResponseFromError converts a grpc error into an HTTP response func HTTPResponseFromError(err error) (*HTTPResponse, bool) { s, ok := status.FromError(err) if !ok { return nil, false } status := s.Proto() if len(status.Details) != 1 { return nil, false } var resp HTTPResponse if err := ptypes.UnmarshalAny(status.Details[0], &resp); err != nil { log.Errorf("Got error containing non-response: %v", err) return nil, false } return &resp, true }