1 package grpc
2
3 import (
4 "context"
5 "encoding/json"
6 "errors"
7 "fmt"
8 "strconv"
9 "time"
10
11 "google.golang.org/grpc"
12 "google.golang.org/grpc/metadata"
13 "google.golang.org/grpc/status"
14
15 berrors "github.com/letsencrypt/boulder/errors"
16 )
17
18
19
20
21
22
23 func wrapError(ctx context.Context, appErr error) error {
24 if appErr == nil {
25 return nil
26 }
27
28 var berr *berrors.BoulderError
29 if errors.As(appErr, &berr) {
30 pairs := []string{
31 "errortype", strconv.Itoa(int(berr.Type)),
32 }
33
34
35
36
37
38 if len(berr.SubErrors) > 0 {
39 jsonSubErrs, err := json.Marshal(berr.SubErrors)
40 if err != nil {
41 return berrors.InternalServerError(
42 "error marshaling json SubErrors, orig error %q", err)
43 }
44 pairs = append(pairs, "suberrors", string(jsonSubErrs))
45 }
46
47
48
49 if berr.RetryAfter != 0 {
50 pairs = append(pairs, "retryafter", berr.RetryAfter.String())
51 }
52
53 err := grpc.SetTrailer(ctx, metadata.Pairs(pairs...))
54 if err != nil {
55 return berrors.InternalServerError(
56 "error setting gRPC error metadata, orig error %q", appErr)
57 }
58 }
59
60 return appErr
61 }
62
63
64
65
66
67 func unwrapError(err error, md metadata.MD) error {
68 if err == nil {
69 return nil
70 }
71
72 errTypeStrs, ok := md["errortype"]
73 if !ok {
74 return err
75 }
76
77 inErrMsg := status.Convert(err).Message()
78 if len(errTypeStrs) != 1 {
79 return berrors.InternalServerError(
80 "multiple 'errortype' metadata, wrapped error %q",
81 inErrMsg,
82 )
83 }
84
85 inErrType, decErr := strconv.Atoi(errTypeStrs[0])
86 if decErr != nil {
87 return berrors.InternalServerError(
88 "failed to decode error type, decoding error %q, wrapped error %q",
89 decErr,
90 inErrMsg,
91 )
92 }
93 inErr := berrors.New(berrors.ErrorType(inErrType), inErrMsg)
94 var outErr *berrors.BoulderError
95 if !errors.As(inErr, &outErr) {
96 return fmt.Errorf(
97 "expected type of inErr to be %T got %T: %q",
98 outErr,
99 inErr,
100 inErr.Error(),
101 )
102 }
103
104 subErrorsVal, ok := md["suberrors"]
105 if ok {
106 if len(subErrorsVal) != 1 {
107 return berrors.InternalServerError(
108 "multiple 'suberrors' in metadata, wrapped error %q",
109 inErrMsg,
110 )
111 }
112
113 unmarshalErr := json.Unmarshal([]byte(subErrorsVal[0]), &outErr.SubErrors)
114 if unmarshalErr != nil {
115 return berrors.InternalServerError(
116 "JSON unmarshaling 'suberrors' %q, wrapped error %q: %s",
117 subErrorsVal[0],
118 inErrMsg,
119 unmarshalErr,
120 )
121 }
122 }
123
124 retryAfterVal, ok := md["retryafter"]
125 if ok {
126 if len(retryAfterVal) != 1 {
127 return berrors.InternalServerError(
128 "multiple 'retryafter' in metadata, wrapped error %q",
129 inErrMsg,
130 )
131 }
132 var parseErr error
133 outErr.RetryAfter, parseErr = time.ParseDuration(retryAfterVal[0])
134 if parseErr != nil {
135 return berrors.InternalServerError(
136 "parsing 'retryafter' as int64, wrapped error %q, parsing error: %s",
137 inErrMsg,
138 parseErr,
139 )
140 }
141 }
142 return outErr
143 }
144
View as plain text