transport/http: remove stuttering in Error
Peter Bourgon
8 years ago
254 | 254 | case errAlreadyExists, errInconsistentIDs: |
255 | 255 | return stdhttp.StatusBadRequest |
256 | 256 | default: |
257 | if e, ok := err.(kithttp.TransportError); ok { | |
257 | if e, ok := err.(kithttp.Error); ok { | |
258 | 258 | switch e.Domain { |
259 | 259 | case kithttp.DomainDecode: |
260 | 260 | return stdhttp.StatusBadRequest |
73 | 73 | |
74 | 74 | req, err := http.NewRequest(c.method, c.tgt.String(), nil) |
75 | 75 | if err != nil { |
76 | return nil, TransportError{Domain: DomainNewRequest, Err: err} | |
76 | return nil, Error{Domain: DomainNewRequest, Err: err} | |
77 | 77 | } |
78 | 78 | |
79 | 79 | if err = c.enc(ctx, req, request); err != nil { |
80 | return nil, TransportError{Domain: DomainEncode, Err: err} | |
80 | return nil, Error{Domain: DomainEncode, Err: err} | |
81 | 81 | } |
82 | 82 | |
83 | 83 | for _, f := range c.before { |
86 | 86 | |
87 | 87 | resp, err := ctxhttp.Do(ctx, c.client, req) |
88 | 88 | if err != nil { |
89 | return nil, TransportError{Domain: DomainDo, Err: err} | |
89 | return nil, Error{Domain: DomainDo, Err: err} | |
90 | 90 | } |
91 | 91 | if !c.bufferedStream { |
92 | 92 | defer resp.Body.Close() |
94 | 94 | |
95 | 95 | response, err := c.dec(ctx, resp) |
96 | 96 | if err != nil { |
97 | return nil, TransportError{Domain: DomainDecode, Err: err} | |
97 | return nil, Error{Domain: DomainDecode, Err: err} | |
98 | 98 | } |
99 | 99 | |
100 | 100 | return response, nil |
17 | 17 | DomainDecode = "Decode" |
18 | 18 | ) |
19 | 19 | |
20 | // TransportError is an error that occurred at some phase within the transport. | |
21 | type TransportError struct { | |
20 | // Error is an error that occurred at some phase within the transport. | |
21 | type Error struct { | |
22 | 22 | // Domain is the phase in which the error was generated. |
23 | 23 | Domain string |
24 | 24 | |
27 | 27 | } |
28 | 28 | |
29 | 29 | // Error implements the error interface. |
30 | func (e TransportError) Error() string { | |
30 | func (e Error) Error() string { | |
31 | 31 | return fmt.Sprintf("%s: %s", e.Domain, e.Err) |
32 | 32 | } |
36 | 36 | t.Fatal("err == nil") |
37 | 37 | } |
38 | 38 | |
39 | e, ok := err.(httptransport.TransportError) | |
39 | e, ok := err.(httptransport.Error) | |
40 | 40 | if !ok { |
41 | t.Fatal("err is not of type github.com/go-kit/kit/transport/http.Err") | |
41 | t.Fatal("err is not of type github.com/go-kit/kit/transport/http.Error") | |
42 | 42 | } |
43 | 43 | |
44 | 44 | if want, have := sampleErr, e.Err; want != have { |
47 | 47 | } |
48 | 48 | |
49 | 49 | func ExampleErrOutput() { |
50 | sampleErr := errors.New("Oh no, an error") | |
51 | err := httptransport.TransportError{Domain: httptransport.DomainDo, Err: sampleErr} | |
50 | sampleErr := errors.New("oh no, an error") | |
51 | err := httptransport.Error{Domain: httptransport.DomainDo, Err: sampleErr} | |
52 | 52 | fmt.Println(err) |
53 | 53 | // Output: |
54 | // Do: Oh no, an error | |
54 | // Do: oh no, an error | |
55 | 55 | } |
85 | 85 | request, err := s.dec(ctx, r) |
86 | 86 | if err != nil { |
87 | 87 | s.logger.Log("err", err) |
88 | s.errorEncoder(ctx, TransportError{Domain: DomainDecode, Err: err}, w) | |
88 | s.errorEncoder(ctx, Error{Domain: DomainDecode, Err: err}, w) | |
89 | 89 | return |
90 | 90 | } |
91 | 91 | |
92 | 92 | response, err := s.e(ctx, request) |
93 | 93 | if err != nil { |
94 | 94 | s.logger.Log("err", err) |
95 | s.errorEncoder(ctx, TransportError{Domain: DomainDo, Err: err}, w) | |
95 | s.errorEncoder(ctx, Error{Domain: DomainDo, Err: err}, w) | |
96 | 96 | return |
97 | 97 | } |
98 | 98 | |
102 | 102 | |
103 | 103 | if err := s.enc(ctx, w, response); err != nil { |
104 | 104 | s.logger.Log("err", err) |
105 | s.errorEncoder(ctx, TransportError{Domain: DomainEncode, Err: err}, w) | |
105 | s.errorEncoder(ctx, Error{Domain: DomainEncode, Err: err}, w) | |
106 | 106 | return |
107 | 107 | } |
108 | 108 | } |
109 | 109 | |
110 | // ErrorEncoder is a function that's responsible for encoding an error to the ResponseWriter. | |
110 | // ErrorEncoder is responsible for encoding an error to the ResponseWriter. | |
111 | // | |
112 | // In the server implementation, only kit/transport/http.Error values are ever | |
113 | // passed to this function, so you might be tempted to have this function take | |
114 | // one of those directly. But, users are encouraged to use custom ErrorEncoders | |
115 | // to encode all HTTP errors to their clients, and so may want to pass and check | |
116 | // for their own error types. See the example shipping/handling service. | |
111 | 117 | type ErrorEncoder func(ctx context.Context, err error, w http.ResponseWriter) |
112 | 118 | |
113 | 119 | func defaultErrorEncoder(_ context.Context, err error, w http.ResponseWriter) { |
114 | 120 | switch e := err.(type) { |
115 | case TransportError: | |
121 | case Error: | |
116 | 122 | switch e.Domain { |
117 | 123 | case DomainDecode: |
118 | 124 | http.Error(w, err.Error(), http.StatusBadRequest) |
59 | 59 | func TestServerErrorEncoder(t *testing.T) { |
60 | 60 | errTeapot := errors.New("teapot") |
61 | 61 | code := func(err error) int { |
62 | if e, ok := err.(httptransport.TransportError); ok && e.Err == errTeapot { | |
62 | if e, ok := err.(httptransport.Error); ok && e.Err == errTeapot { | |
63 | 63 | return http.StatusTeapot |
64 | 64 | } |
65 | 65 | return http.StatusInternalServerError |