This repository has been archived by the owner on Sep 16, 2022. It is now read-only.
/
server.go
239 lines (205 loc) · 6.78 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
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
// Copyright 2009 The Go Authors. All rights reserved.
// Copyright 2012 The Gorilla Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package endpoints
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"reflect"
"strings"
"golang.org/x/net/context"
// Mainly for debug logging
"io/ioutil"
"google.golang.org/appengine/log"
)
// Server serves registered RPC services using registered codecs.
type Server struct {
root string
services *serviceMap
// ContextDecorator will be called as the last step of the creation of a new context.
// If nil the context will not be decorated.
ContextDecorator func(context.Context) (context.Context, error)
}
// NewServer returns a new RPC server.
func NewServer(root string) *Server {
if root == "" {
root = "/_ah/spi/"
} else if root[len(root)-1] != '/' {
root += "/"
}
server := &Server{root: root, services: new(serviceMap)}
backend := newBackendService(server)
server.services.register(backend, "BackendService", "", "", true, true)
return server
}
// RegisterService adds a new service to the server.
//
// The name parameter is optional: if empty it will be inferred from
// the receiver type name.
//
// Methods from the receiver will be extracted if these rules are satisfied:
//
// - The receiver is exported (begins with an upper case letter) or local
// (defined in the package registering the service).
// - The method name is exported.
// - The method has either 2 arguments and 2 return values:
// *http.Request|Context, *arg => *reply, error
// or 3 arguments and 1 return value:
// *http.Request|Context, *arg, *reply => error
// - The first argument is either *http.Request or Context.
// - Second argument (*arg) and *reply are exported or local.
// - First argument, *arg and *reply are all pointers.
// - First (or second, if method has 2 arguments) return value is of type error.
//
// All other methods are ignored.
func (s *Server) RegisterService(srv interface{}, name, ver, desc string, isDefault bool) (*RPCService, error) {
return s.services.register(srv, name, ver, desc, isDefault, false)
}
// RegisterServiceWithDefaults will register provided service and will try to
// infer Endpoints config params from its method names and types.
// See RegisterService for details.
func (s *Server) RegisterServiceWithDefaults(srv interface{}) (*RPCService, error) {
return s.RegisterService(srv, "", "", "", true)
}
// Must is a helper that wraps a call to a function returning (*Template, error) and
// panics if the error is non-nil. It is intended for use in variable initializations
// such as:
// var s = endpoints.Must(endpoints.RegisterService(s, "Service", "v1", "some service", true))
//
func Must(s *RPCService, err error) *RPCService {
if err != nil {
panic(err)
}
return s
}
// ServiceByName returns a registered service or nil if there's no service
// registered by that name.
func (s *Server) ServiceByName(serviceName string) *RPCService {
return s.services.serviceByName(serviceName)
}
// HandleHTTP adds Server s to specified http.ServeMux.
// If no mux is provided http.DefaultServeMux will be used.
func (s *Server) HandleHTTP(mux *http.ServeMux) {
if mux == nil {
mux = http.DefaultServeMux
}
mux.Handle(s.root, s)
}
// ServeHTTP is Server's implementation of http.Handler interface.
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// Always respond with JSON, even when an error occurs.
// Note: API server doesn't expect an encoding in Content-Type header.
w.Header().Set("Content-Type", "application/json")
c := NewContext(r)
if s.ContextDecorator != nil {
ctx, err := s.ContextDecorator(c)
if err != nil {
writeError(w, err)
return
}
c = ctx
}
if r.Method != "POST" {
err := fmt.Errorf("rpc: POST method required, got %q", r.Method)
writeError(w, err)
return
}
// methodName has "ServiceName.MethodName" format.
var methodName string
idx := strings.LastIndex(r.URL.Path, "/")
if idx < 0 {
writeError(w, fmt.Errorf("rpc: no method in path %q", r.URL.Path))
return
}
methodName = r.URL.Path[idx+1:]
// Get service method specs
serviceSpec, methodSpec, err := s.services.get(methodName)
if err != nil {
writeError(w, err)
return
}
// Initialize RPC method request
reqValue := reflect.New(methodSpec.ReqType)
body, err := ioutil.ReadAll(r.Body)
r.Body.Close()
if err != nil {
writeError(w, err)
return
}
log.Debugf(c, "SPI request body: %s", body)
// if err := json.NewDecoder(r.Body).Decode(req.Interface()); err != nil {
// writeError(w, fmt.Errorf("Error while decoding JSON: %q", err))
// return
// }
if err := json.Unmarshal(body, reqValue.Interface()); err != nil {
writeError(w, err)
return
}
if err := validateRequest(reqValue.Interface()); err != nil {
writeError(w, err)
return
}
// Restore the body in the original request.
r.Body = ioutil.NopCloser(bytes.NewReader(body))
numIn, numOut := methodSpec.method.Type.NumIn(), methodSpec.method.Type.NumOut()
// Construct arguments for the method call
var httpReqOrCtx interface{} = r
if methodSpec.wantsContext {
httpReqOrCtx = c
}
args := []reflect.Value{serviceSpec.rcvr, reflect.ValueOf(httpReqOrCtx)}
if numIn > 2 {
args = append(args, reqValue)
}
var respValue reflect.Value
if numIn > 3 {
respValue = reflect.New(methodSpec.RespType)
args = append(args, respValue)
}
// Invoke the service method
var errValue reflect.Value
res := methodSpec.method.Func.Call(args)
if numOut == 2 {
respValue = res[0]
errValue = res[1]
} else {
errValue = res[0]
}
// Check if method returned an error
if err := errValue.Interface(); err != nil {
writeError(w, err.(error))
return
}
// Encode non-error response
if numIn == 4 || numOut == 2 {
if err := json.NewEncoder(w).Encode(respValue.Interface()); err != nil {
writeError(w, err)
}
}
}
// DefaultServer is the default RPC server, so you don't have to explicitly
// create one.
var DefaultServer *Server
// RegisterService registers a service using DefaultServer.
// See Server.RegisterService for details.
func RegisterService(srv interface{}, name, ver, desc string, isDefault bool) (
*RPCService, error) {
return DefaultServer.RegisterService(srv, name, ver, desc, isDefault)
}
// RegisterServiceWithDefaults registers a service using DefaultServer.
// See Server.RegisterServiceWithDefaults for details.
func RegisterServiceWithDefaults(srv interface{}) (*RPCService, error) {
return DefaultServer.RegisterServiceWithDefaults(srv)
}
// HandleHTTP calls DefaultServer's HandleHTTP method using default serve mux.
func HandleHTTP() {
DefaultServer.HandleHTTP(nil)
}
// TODO: var DefaultServer = NewServer("") won't work so it's in the init()
// function for now.
func init() {
DefaultServer = NewServer("")
}