-
Notifications
You must be signed in to change notification settings - Fork 0
/
server.go
163 lines (137 loc) · 4.37 KB
/
server.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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
package jsonrpc
import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
httptransport "github.com/go-kit/kit/transport/http"
)
// Handlers maps the method to the proper handler
type Handlers map[string]Handlerer
// Set handler for given method, panics if method already exists
func (h Handlers) Set(method string, handler Handlerer) {
if _, ok := h[method]; ok {
panic(fmt.Sprintf("Handler for method %s already exists", method))
}
h[method] = handler
}
// Handlerer is the interface that provides method for serving JSON-RPC
type Handlerer interface {
ServeJSONRPC(ctx context.Context, requestHeader http.Header, params json.RawMessage) (response json.RawMessage, responseHeader http.Header, err error)
}
// ServerOption sets an optional parameter for servers
type ServerOption func(*Server)
// ServerErrorEncoder is used to encode errors to the http.ResponseWriter
// whenever they're encountered in the processing of a request. Clients can
// use this to provide custom error formatting and response codes. By default,
// errors will be written with the DefaultErrorEncoder.
func ServerErrorEncoder(ee httptransport.ErrorEncoder) ServerOption {
return func(s *Server) { s.errorEncoder = ee }
}
// NewServer constructs a new Server, which implements http.Handler
func NewServer(
sh Handlers,
options ...ServerOption,
) *Server {
s := &Server{
sh: sh,
errorEncoder: DefaultErrorEncoder,
}
for _, option := range options {
option(s)
}
return s
}
// Server wraps an list of handlers and implements http.Handler
type Server struct {
sh Handlers
errorEncoder httptransport.ErrorEncoder
}
// ServeHTTP implements http.Handler
func (s Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
w.WriteHeader(http.StatusMethodNotAllowed)
io.WriteString(w, "405 must POST\n")
return
}
ctx := r.Context()
ctx = httptransport.PopulateRequestContext(ctx, r)
// Decode the body into an object
var req Request
err := json.NewDecoder(r.Body).Decode(&req)
if err != nil {
s.errorEncoder(ctx, NewError(ParseError), w)
return
}
ctx = PopulateRequestContext(ctx, &req)
if err := req.Validate(); err != nil {
s.errorEncoder(ctx, NewError(InvalidRequestError), w)
return
}
// Get the endpoint and codecs from the map using the method
// defined in the JSON object
srv, ok := s.sh[req.Method]
if !ok {
s.errorEncoder(ctx, NewError(MethodNotFoundError), w)
return
}
// notification
if req.ID == nil {
go srv.ServeJSONRPC(ctx, r.Header, req.Params)
httptransport.EncodeJSONResponse(ctx, w, NotificationResponse{})
return
}
resp, respHeaders, err := srv.ServeJSONRPC(ctx, r.Header, req.Params)
if err != nil {
s.errorEncoder(ctx, err, w)
return
}
res := Response{
RespHeaders: respHeaders,
JSONRPC: Version,
ID: req.ID,
}
var respErr Error
err = json.Unmarshal(resp, &respErr)
// try to find out if resp is an error
if err == nil && respErr.ErrorCode() != 0 {
// it has to set a pointer otherwise in Go 1.7 base64 encoded string is returned.
// In Go 1.8 works as expected
// Golang release notes 1.8: A RawMessage value now marshals the same as its pointer type.
res.Error = &respErr
} else {
res.Result = &resp
}
httptransport.EncodeJSONResponse(ctx, w, res)
}
// DefaultErrorEncoder writes the error to the ResponseWriter,
// as a json-rpc error response, with an InternalError status code.
// The Error() string of the error will be used as the response error message.
// If the error implements ErrorCoder, the provided code will be set on the
// response error.
// If the error implements Headerer, the given headers will be set.
func DefaultErrorEncoder(ctx context.Context, err error, w http.ResponseWriter) {
w.Header().Set("Content-Type", ContentType)
e := NewError(InternalError)
if te, ok := err.(ErrorCoder); ok {
e.Code = te.ErrorCode()
}
if te, ok := err.(Errorer); ok {
e.Message = te.Error()
}
w.WriteHeader(http.StatusOK)
reqID, _ := ctx.Value(ContextKeyRequestID).(*RequestID)
json.NewEncoder(w).Encode(Response{
ID: reqID,
JSONRPC: Version,
Error: &e,
})
}
// Headerer is checked by DefaultErrorEncoder. If an error value implements
// Headerer, the provided headers will be applied to the response writer, after
// the Content-Type is set.
type Headerer interface {
Headers() http.Header
}