forked from monzo/mercury
/
request.go
124 lines (103 loc) · 2.57 KB
/
request.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
package mercury
import (
"fmt"
"sync"
"time"
log "github.com/mondough/slog"
tmsg "github.com/mondough/typhon/message"
"golang.org/x/net/context"
"github.com/mondough/mercury/marshaling"
)
const (
errHeader = "Content-Error"
)
// A Request is a representation of an RPC call (inbound or outbound). It extends Typhon's Request to provide a
// Context, and also helpers for constructing a response.
type Request interface {
tmsg.Request
context.Context
// Response constructs a response to this request, with the (optional) given body. The response will share
// the request's ID, and be destined for the originator.
Response(body interface{}) Response
// A Context for the Request.
Context() context.Context
// SetContext replaces the Request's Context.
SetContext(ctx context.Context)
}
func responseFromRequest(req Request, body interface{}) Response {
rsp := NewResponse()
rsp.SetId(req.Id())
if body != nil {
rsp.SetBody(body)
ct := req.Headers()[marshaling.AcceptHeader]
marshaler := marshaling.Marshaler(ct)
if marshaler == nil { // Fall back to JSON
marshaler = marshaling.Marshaler(marshaling.JSONContentType)
}
if marshaler == nil {
log.Error(req, "[Mercury] No marshaler for response %s: %s", rsp.Id(), ct)
} else if err := marshaler.MarshalBody(rsp); err != nil {
log.Error(req, "[Mercury] Failed to marshal response %s: %v", rsp.Id(), err)
}
}
return rsp
}
type request struct {
sync.RWMutex
tmsg.Request
ctx context.Context
}
func (r *request) Response(body interface{}) Response {
return responseFromRequest(r, body)
}
func (r *request) Context() context.Context {
if r == nil {
return nil
}
r.RLock()
defer r.RUnlock()
return r.ctx
}
func (r *request) SetContext(ctx context.Context) {
r.Lock()
defer r.Unlock()
r.ctx = ctx
}
func (r *request) Copy() tmsg.Request {
r.RLock()
defer r.RUnlock()
return &request{
Request: r.Request.Copy(),
ctx: r.ctx,
}
}
func (r *request) String() string {
return fmt.Sprintf("%v", r.Request)
}
// Context implementation
func (r *request) Deadline() (time.Time, bool) {
return r.Context().Deadline()
}
func (r *request) Done() <-chan struct{} {
return r.Context().Done()
}
func (r *request) Err() error {
return r.Context().Err()
}
func (r *request) Value(key interface{}) interface{} {
return r.Context().Value(key)
}
func NewRequest() Request {
return FromTyphonRequest(tmsg.NewRequest())
}
func FromTyphonRequest(req tmsg.Request) Request {
switch req := req.(type) {
case Request:
return req
default:
return &request{
Request: req,
ctx: context.Background(),
}
}
}