/
handler.go
136 lines (115 loc) · 4.19 KB
/
handler.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
// Copyright (c) 2016 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
package transport
import (
"context"
"fmt"
"log"
"runtime/debug"
"time"
"go.uber.org/yarpc/internal/errors"
)
//go:generate mockgen -destination=transporttest/handler.go -package=transporttest go.uber.org/yarpc/api/transport UnaryHandler,OnewayHandler
// Type is an enum of RPC types
type Type int
//go:generate stringer -type=Type
const (
// Unary types are traditional request/response RPCs
Unary Type = iota + 1
// Oneway types are fire and forget RPCs (no response)
Oneway
)
// HandlerSpec holds a handler and its Type
// one handler will be set, the other nil
type HandlerSpec struct {
t Type
unaryHandler UnaryHandler
onewayHandler OnewayHandler
}
// Type returns the associated handler's type
func (h HandlerSpec) Type() Type { return h.t }
// Unary returns the Unary Handler or nil
func (h HandlerSpec) Unary() UnaryHandler { return h.unaryHandler }
// Oneway returns the Oneway Handler or nil
func (h HandlerSpec) Oneway() OnewayHandler { return h.onewayHandler }
// NewUnaryHandlerSpec returns an new HandlerSpec with a UnaryHandler
func NewUnaryHandlerSpec(handler UnaryHandler) HandlerSpec {
return HandlerSpec{t: Unary, unaryHandler: handler}
}
// NewOnewayHandlerSpec returns an new HandlerSpec with a OnewayHandler
func NewOnewayHandlerSpec(handler OnewayHandler) HandlerSpec {
return HandlerSpec{t: Oneway, onewayHandler: handler}
}
// UnaryHandler handles a single, transport-level, unary request.
type UnaryHandler interface {
// Handle the given request, writing the response to the given
// ResponseWriter.
//
// An error may be returned in case of failures. BadRequestError must be
// returned for invalid requests. All other failures are treated as
// UnexpectedErrors.
Handle(ctx context.Context, req *Request, resw ResponseWriter) error
}
// OnewayHandler handles a single, transport-level, oneway request.
type OnewayHandler interface {
// Handle the given oneway request
//
// An error may be returned in case of failures.
HandleOneway(ctx context.Context, req *Request) error
}
// DispatchUnaryHandler calls the handler h, recovering panics and timeout errors,
// converting them to yarpc errors. All other errors are passed trough.
func DispatchUnaryHandler(
ctx context.Context,
h UnaryHandler,
start time.Time,
req *Request,
resq ResponseWriter,
) (err error) {
defer func() {
if r := recover(); r != nil {
log.Printf("Unary handler panicked: %v\n%s", r, debug.Stack())
err = fmt.Errorf("panic: %v", r)
}
}()
err = h.Handle(ctx, req, resq)
// The handler stopped work on context deadline.
if err == context.DeadlineExceeded && err == ctx.Err() {
deadline, _ := ctx.Deadline()
err = errors.HandlerTimeoutError(req.Caller, req.Service,
req.Procedure, deadline.Sub(start))
}
return err
}
// DispatchOnewayHandler calls the oneway handler, recovering from panics as
// errors
func DispatchOnewayHandler(
ctx context.Context,
h OnewayHandler,
req *Request,
) (err error) {
defer func() {
if r := recover(); r != nil {
log.Printf("Oneway handler panicked: %v\n%s", r, debug.Stack())
err = fmt.Errorf("panic: %v", r)
}
}()
return h.HandleOneway(ctx, req)
}