Left: | ||
Right: |
LEFT | RIGHT |
---|---|
1 // Copyright 2009 The Go Authors. All rights reserved. | 1 // Copyright 2009 The Go Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style | 2 // Use of this source code is governed by a BSD-style |
3 // license that can be found in the LICENSE file. | 3 // license that can be found in the LICENSE file. |
4 | 4 |
5 /* | 5 /* |
6 » The rpc package provides access to the public methods of an object acros s a | 6 » The rpc package provides access to the exported methods of an object acr oss a |
7 network or other I/O connection. A server registers an object, making i t visible | 7 network or other I/O connection. A server registers an object, making i t visible |
8 » as a service with the name of the type of the object. After registratio n, public | 8 » as a service with the name of the type of the object. After registratio n, exported |
9 methods of the object will be accessible remotely. A server may registe r multiple | 9 methods of the object will be accessible remotely. A server may registe r multiple |
10 objects (services) of different types but it is an error to register mul tiple | 10 objects (services) of different types but it is an error to register mul tiple |
11 objects of the same type. | 11 objects of the same type. |
12 | 12 |
13 Only methods that satisfy these criteria will be made available for remo te access; | 13 Only methods that satisfy these criteria will be made available for remo te access; |
14 other methods will be ignored: | 14 other methods will be ignored: |
15 | 15 |
16 » » - the method receiver and name are publicly visible, that is, be gin with an upper case letter. | 16 » » - the method receiver and name are exported, that is, begin with an upper case letter. |
17 » » - the method has two arguments, both pointers to publicly visibl e types. | 17 » » - the method has two arguments, both pointers to exported types. |
18 - the method has return type os.Error. | 18 - the method has return type os.Error. |
19 | 19 |
20 The method's first argument represents the arguments provided by the cal ler; the | 20 The method's first argument represents the arguments provided by the cal ler; the |
21 second argument represents the result parameters to be returned to the c aller. | 21 second argument represents the result parameters to be returned to the c aller. |
22 The method's return value, if non-nil, is passed back as a string that t he client | 22 The method's return value, if non-nil, is passed back as a string that t he client |
23 sees as an os.ErrorString. | 23 sees as an os.ErrorString. |
24 | 24 |
25 The server may handle requests on a single connection by calling ServeCo nn. More | 25 The server may handle requests on a single connection by calling ServeCo nn. More |
26 typically it will create a network listener and call Accept or, for an H TTP | 26 typically it will create a network listener and call Accept or, for an H TTP |
27 listener, HandleHTTP and http.Serve. | 27 listener, HandleHTTP and http.Serve. |
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
116 "io" | 116 "io" |
117 "net" | 117 "net" |
118 "os" | 118 "os" |
119 "reflect" | 119 "reflect" |
120 "strings" | 120 "strings" |
121 "sync" | 121 "sync" |
122 "unicode" | 122 "unicode" |
123 "utf8" | 123 "utf8" |
124 ) | 124 ) |
125 | 125 |
126 const ( | |
127 // Defaults used by HandleHTTP | |
128 DefaultRPCPath = "/_goRPC_" | |
129 DefaultDebugPath = "/debug/rpc" | |
130 ) | |
131 | |
126 // Precompute the reflect type for os.Error. Can't use os.Error directly | 132 // Precompute the reflect type for os.Error. Can't use os.Error directly |
127 // because Typeof takes an empty interface value. This is annoying. | 133 // because Typeof takes an empty interface value. This is annoying. |
128 var unusedError *os.Error | 134 var unusedError *os.Error |
129 var typeOfOsError = reflect.Typeof(unusedError).(*reflect.PtrType).Elem() | 135 var typeOfOsError = reflect.Typeof(unusedError).(*reflect.PtrType).Elem() |
130 | 136 |
131 type methodType struct { | 137 type methodType struct { |
132 sync.Mutex // protects counters | 138 sync.Mutex // protects counters |
133 method reflect.Method | 139 method reflect.Method |
134 argType *reflect.PtrType | 140 argType *reflect.PtrType |
135 replyType *reflect.PtrType | 141 replyType *reflect.PtrType |
(...skipping 23 matching lines...) Expand all Loading... | |
159 Seq uint64 // echoes that of the request | 165 Seq uint64 // echoes that of the request |
160 Error string // error, if any. | 166 Error string // error, if any. |
161 } | 167 } |
162 | 168 |
163 // ClientInfo records information about an RPC client connection. | 169 // ClientInfo records information about an RPC client connection. |
164 type ClientInfo struct { | 170 type ClientInfo struct { |
165 LocalAddr string | 171 LocalAddr string |
166 RemoteAddr string | 172 RemoteAddr string |
167 } | 173 } |
168 | 174 |
175 // Server represents an RPC Server. | |
169 type Server struct { | 176 type Server struct { |
170 sync.Mutex // protects the serviceMap | 177 sync.Mutex // protects the serviceMap |
171 serviceMap map[string]*service | 178 serviceMap map[string]*service |
172 } | 179 } |
173 | 180 |
181 // NewServer returns a new Server. | |
174 func NewServer() *Server { | 182 func NewServer() *Server { |
175 return &Server{serviceMap: make(map[string]*service)} | 183 return &Server{serviceMap: make(map[string]*service)} |
176 } | 184 } |
177 | 185 |
178 // DefaultServer is the default instance of *Server. | 186 // DefaultServer is the default instance of *Server. |
179 var DefaultServer = NewServer() | 187 var DefaultServer = NewServer() |
180 | 188 |
181 // Is this a publicly visible - upper case - name? | 189 // Is this an exported - upper case - name? |
182 func isPublic(name string) bool { | 190 func isExported(name string) bool { |
183 rune, _ := utf8.DecodeRuneInString(name) | 191 rune, _ := utf8.DecodeRuneInString(name) |
184 return unicode.IsUpper(rune) | 192 return unicode.IsUpper(rune) |
185 } | 193 } |
186 | 194 |
187 // Register publishes in the server the set of methods of the | 195 // Register publishes in the server the set of methods of the |
188 // receiver value that satisfy the following conditions: | 196 // receiver value that satisfy the following conditions: |
189 //» - public method | 197 //» - exported method |
rsc1
2010/10/24 03:47:23
s/public/exported/
| |
190 //» - two arguments, both pointers to public structs | 198 //» - two arguments, both pointers to exported structs |
191 //» - one return value of type os.Error | 199 //» - one return value, of type os.Error |
rsc1
2010/10/24 03:47:23
s/value/value,/
| |
192 // It returns an error if the receiver is not public or has no | 200 // It returns an error if the receiver is not an exported type or has no |
rsc1
2010/10/24 03:47:23
s/public/an exported type/.
I don't remember why t
| |
193 // suitable methods. | 201 // suitable methods. |
194 func (server *Server) Register(rcvr interface{}) os.Error { | 202 func (server *Server) Register(rcvr interface{}) os.Error { |
195 server.Lock() | 203 server.Lock() |
196 defer server.Unlock() | 204 defer server.Unlock() |
197 if server.serviceMap == nil { | 205 if server.serviceMap == nil { |
198 server.serviceMap = make(map[string]*service) | 206 server.serviceMap = make(map[string]*service) |
199 } | 207 } |
200 s := new(service) | 208 s := new(service) |
201 s.typ = reflect.Typeof(rcvr) | 209 s.typ = reflect.Typeof(rcvr) |
202 s.rcvr = reflect.NewValue(rcvr) | 210 s.rcvr = reflect.NewValue(rcvr) |
203 sname := reflect.Indirect(s.rcvr).Type().Name() | 211 sname := reflect.Indirect(s.rcvr).Type().Name() |
204 if sname == "" { | 212 if sname == "" { |
205 log.Exit("rpc: no service name for type", s.typ.String()) | 213 log.Exit("rpc: no service name for type", s.typ.String()) |
206 } | 214 } |
207 » if s.typ.PkgPath() != "" && !isPublic(sname) { | 215 » if s.typ.PkgPath() != "" && !isExported(sname) { |
rsc1
2010/10/24 03:47:23
i know it says public here but let's fix all these
| |
208 » » s := "rpc Register: type " + sname + " is not public" | 216 » » s := "rpc Register: type " + sname + " is not exported" |
209 log.Print(s) | 217 log.Print(s) |
210 return os.ErrorString(s) | 218 return os.ErrorString(s) |
211 } | 219 } |
212 if _, present := server.serviceMap[sname]; present { | 220 if _, present := server.serviceMap[sname]; present { |
213 return os.ErrorString("rpc: service already defined: " + sname) | 221 return os.ErrorString("rpc: service already defined: " + sname) |
214 } | 222 } |
215 s.name = sname | 223 s.name = sname |
216 s.method = make(map[string]*methodType) | 224 s.method = make(map[string]*methodType) |
217 | 225 |
218 // Install the methods | 226 // Install the methods |
219 for m := 0; m < s.typ.NumMethod(); m++ { | 227 for m := 0; m < s.typ.NumMethod(); m++ { |
220 method := s.typ.Method(m) | 228 method := s.typ.Method(m) |
221 mtype := method.Type | 229 mtype := method.Type |
222 mname := method.Name | 230 mname := method.Name |
223 » » if mtype.PkgPath() != "" || !isPublic(mname) { | 231 » » if mtype.PkgPath() != "" || !isExported(mname) { |
224 continue | 232 continue |
225 } | 233 } |
226 // Method needs three ins: receiver, *args, *reply. | 234 // Method needs three ins: receiver, *args, *reply. |
227 if mtype.NumIn() != 3 { | 235 if mtype.NumIn() != 3 { |
228 log.Println("method", mname, "has wrong number of ins:", mtype.NumIn()) | 236 log.Println("method", mname, "has wrong number of ins:", mtype.NumIn()) |
229 continue | 237 continue |
230 } | 238 } |
231 argType, ok := mtype.In(1).(*reflect.PtrType) | 239 argType, ok := mtype.In(1).(*reflect.PtrType) |
232 if !ok { | 240 if !ok { |
233 log.Println(mname, "arg type not a pointer:", mtype.In(1 )) | 241 log.Println(mname, "arg type not a pointer:", mtype.In(1 )) |
234 continue | 242 continue |
235 } | 243 } |
236 replyType, ok := mtype.In(2).(*reflect.PtrType) | 244 replyType, ok := mtype.In(2).(*reflect.PtrType) |
237 if !ok { | 245 if !ok { |
238 log.Println(mname, "reply type not a pointer:", mtype.In (2)) | 246 log.Println(mname, "reply type not a pointer:", mtype.In (2)) |
239 continue | 247 continue |
240 } | 248 } |
241 » » if argType.Elem().PkgPath() != "" && !isPublic(argType.Elem().Na me()) { | 249 » » if argType.Elem().PkgPath() != "" && !isExported(argType.Elem(). Name()) { |
242 » » » log.Println(mname, "argument type not public:", argType) | 250 » » » log.Println(mname, "argument type not exported:", argTyp e) |
243 » » » continue | 251 » » » continue |
244 » » } | 252 » » } |
245 » » if replyType.Elem().PkgPath() != "" && !isPublic(replyType.Elem( ).Name()) { | 253 » » if replyType.Elem().PkgPath() != "" && !isExported(replyType.Ele m().Name()) { |
246 » » » log.Println(mname, "reply type not public:", replyType) | 254 » » » log.Println(mname, "reply type not exported:", replyType ) |
247 continue | 255 continue |
248 } | 256 } |
249 if mtype.NumIn() == 4 { | 257 if mtype.NumIn() == 4 { |
250 t := mtype.In(3) | 258 t := mtype.In(3) |
251 if t != reflect.Typeof((*ClientInfo)(nil)) { | 259 if t != reflect.Typeof((*ClientInfo)(nil)) { |
252 log.Println(mname, "last argument not *ClientInf o") | 260 log.Println(mname, "last argument not *ClientInf o") |
253 continue | 261 continue |
254 } | 262 } |
255 } | 263 } |
256 // Method needs one out: os.Error. | 264 // Method needs one out: os.Error. |
257 if mtype.NumOut() != 1 { | 265 if mtype.NumOut() != 1 { |
258 log.Println("method", mname, "has wrong number of outs:" , mtype.NumOut()) | 266 log.Println("method", mname, "has wrong number of outs:" , mtype.NumOut()) |
259 continue | 267 continue |
260 } | 268 } |
261 if returnType := mtype.Out(0); returnType != typeOfOsError { | 269 if returnType := mtype.Out(0); returnType != typeOfOsError { |
262 log.Println("method", mname, "returns", returnType.Strin g(), "not os.Error") | 270 log.Println("method", mname, "returns", returnType.Strin g(), "not os.Error") |
263 continue | 271 continue |
264 } | 272 } |
265 s.method[mname] = &methodType{method: method, argType: argType, replyType: replyType} | 273 s.method[mname] = &methodType{method: method, argType: argType, replyType: replyType} |
266 } | 274 } |
267 | 275 |
268 if len(s.method) == 0 { | 276 if len(s.method) == 0 { |
269 » » s := "rpc Register: type " + sname + " has no public methods of suitable type" | 277 » » s := "rpc Register: type " + sname + " has no exported methods o f suitable type" |
270 log.Print(s) | 278 log.Print(s) |
271 return os.ErrorString(s) | 279 return os.ErrorString(s) |
272 } | 280 } |
273 server.serviceMap[s.name] = s | 281 server.serviceMap[s.name] = s |
274 return nil | 282 return nil |
275 } | 283 } |
276 | 284 |
277 // A value sent as a placeholder for the response when the server receives an in valid request. | 285 // A value sent as a placeholder for the response when the server receives an in valid request. |
278 type InvalidRequest struct { | 286 type InvalidRequest struct { |
279 marker int | 287 marker int |
(...skipping 30 matching lines...) Expand all Loading... | |
310 function := mtype.method.Func | 318 function := mtype.method.Func |
311 // Invoke the method, providing a new value for the reply. | 319 // Invoke the method, providing a new value for the reply. |
312 returnValues := function.Call([]reflect.Value{s.rcvr, argv, replyv}) | 320 returnValues := function.Call([]reflect.Value{s.rcvr, argv, replyv}) |
313 // The return value for the method is an os.Error. | 321 // The return value for the method is an os.Error. |
314 errInter := returnValues[0].Interface() | 322 errInter := returnValues[0].Interface() |
315 errmsg := "" | 323 errmsg := "" |
316 if errInter != nil { | 324 if errInter != nil { |
317 errmsg = errInter.(os.Error).String() | 325 errmsg = errInter.(os.Error).String() |
318 } | 326 } |
319 sendResponse(sending, req, replyv.Interface(), codec, errmsg) | 327 sendResponse(sending, req, replyv.Interface(), codec, errmsg) |
320 } | |
321 | |
322 // A ServerCodec implements reading of RPC requests and writing of | |
rsc1
2010/10/24 03:47:23
seems like a gratuitous move, but okay
| |
323 // RPC responses for the server side of an RPC session. | |
324 // The server calls ReadRequestHeader and ReadRequestBody in pairs | |
325 // to read requests from the connection, and it calls WriteResponse to | |
326 // write a response back. The server calls Close when finished with the | |
327 // connection. | |
328 type ServerCodec interface { | |
329 ReadRequestHeader(*Request) os.Error | |
330 ReadRequestBody(interface{}) os.Error | |
331 WriteResponse(*Response, interface{}) os.Error | |
332 | |
333 Close() os.Error | |
334 } | 328 } |
335 | 329 |
336 type gobServerCodec struct { | 330 type gobServerCodec struct { |
337 rwc io.ReadWriteCloser | 331 rwc io.ReadWriteCloser |
338 dec *gob.Decoder | 332 dec *gob.Decoder |
339 enc *gob.Encoder | 333 enc *gob.Encoder |
340 } | 334 } |
341 | 335 |
342 func (c *gobServerCodec) ReadRequestHeader(r *Request) os.Error { | 336 func (c *gobServerCodec) ReadRequestHeader(r *Request) os.Error { |
343 return c.dec.Decode(r) | 337 return c.dec.Decode(r) |
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
430 conn, err := lis.Accept() | 424 conn, err := lis.Accept() |
431 if err != nil { | 425 if err != nil { |
432 log.Exit("rpc.Serve: accept:", err.String()) // TODO(r): exit? | 426 log.Exit("rpc.Serve: accept:", err.String()) // TODO(r): exit? |
433 } | 427 } |
434 go server.ServeConn(conn) | 428 go server.ServeConn(conn) |
435 } | 429 } |
436 } | 430 } |
437 | 431 |
438 // Register publishes in the DefaultServer the set of methods· | 432 // Register publishes in the DefaultServer the set of methods· |
439 // of the receiver value that satisfy the following conditions: | 433 // of the receiver value that satisfy the following conditions: |
440 //» - public method | 434 //» - exported method |
441 //» - two arguments, both pointers to public structs | 435 //» - two arguments, both pointers to exported structs |
442 //» - one return value of type os.Error | 436 //» - one return value, of type os.Error |
443 // It returns an error if the receiver is not public or has no | 437 // It returns an error if the receiver is not an exported type or has no |
444 // suitable methods. | 438 // suitable methods. |
445 func Register(rcvr interface{}) os.Error { return DefaultServer.Register(rcvr) } | 439 func Register(rcvr interface{}) os.Error { return DefaultServer.Register(rcvr) } |
440 | |
441 // A ServerCodec implements reading of RPC requests and writing of | |
442 // RPC responses for the server side of an RPC session. | |
443 // The server calls ReadRequestHeader and ReadRequestBody in pairs | |
444 // to read requests from the connection, and it calls WriteResponse to | |
445 // write a response back. The server calls Close when finished with the | |
446 // connection. | |
447 type ServerCodec interface { | |
448 ReadRequestHeader(*Request) os.Error | |
449 ReadRequestBody(interface{}) os.Error | |
450 WriteResponse(*Response, interface{}) os.Error | |
451 | |
452 Close() os.Error | |
453 } | |
446 | 454 |
447 // ServeConn runs the DefaultServer on a single connection. | 455 // ServeConn runs the DefaultServer on a single connection. |
448 // ServeConn blocks, serving the connection until the client hangs up. | 456 // ServeConn blocks, serving the connection until the client hangs up. |
449 // The caller typically invokes ServeConn in a go statement. | 457 // The caller typically invokes ServeConn in a go statement. |
450 // ServeConn uses the gob wire format (see package gob) on the | 458 // ServeConn uses the gob wire format (see package gob) on the |
451 // connection. To use an alternate codec, use ServeCodec. | 459 // connection. To use an alternate codec, use ServeCodec. |
452 func ServeConn(conn io.ReadWriteCloser) { | 460 func ServeConn(conn io.ReadWriteCloser) { |
453 DefaultServer.ServeConn(conn) | 461 DefaultServer.ServeConn(conn) |
454 } | 462 } |
455 | 463 |
456 // ServeCodec is like ServeConn but uses the specified codec to | 464 // ServeCodec is like ServeConn but uses the specified codec to |
457 // decode requests and encode responses. | 465 // decode requests and encode responses. |
458 func ServeCodec(codec ServerCodec) { | 466 func ServeCodec(codec ServerCodec) { |
459 DefaultServer.ServeCodec(codec) | 467 DefaultServer.ServeCodec(codec) |
460 } | 468 } |
461 | 469 |
462 // Accept accepts connections on the listener and serves requests | 470 // Accept accepts connections on the listener and serves requests |
463 // to DefaultServer for each incoming connection.·· | 471 // to DefaultServer for each incoming connection.·· |
464 // Accept blocks; the caller typically invokes it in a go statement. | 472 // Accept blocks; the caller typically invokes it in a go statement. |
465 func Accept(lis net.Listener) { DefaultServer.Accept(lis) } | 473 func Accept(lis net.Listener) { DefaultServer.Accept(lis) } |
466 | 474 |
467 // Can connect to RPC service using HTTP CONNECT to rpcPath. | 475 // Can connect to RPC service using HTTP CONNECT to rpcPath. |
468 var rpcPath string = "/_goRPC_" | |
469 var debugPath string = "/debug/rpc" | |
470 var connected = "200 Connected to Go RPC" | 476 var connected = "200 Connected to Go RPC" |
471 | 477 |
472 // ServeHTTP implements an http.Handler that answers RPC requests. | 478 // ServeHTTP implements an http.Handler that answers RPC requests. |
rsc1
2010/10/24 03:47:23
Probably want to export rpcPath too?
And debugPath
| |
473 func (server *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) { | 479 func (server *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) { |
474 if req.Method != "CONNECT" { | 480 if req.Method != "CONNECT" { |
475 w.SetHeader("Content-Type", "text/plain; charset=utf-8") | 481 w.SetHeader("Content-Type", "text/plain; charset=utf-8") |
476 w.WriteHeader(http.StatusMethodNotAllowed) | 482 w.WriteHeader(http.StatusMethodNotAllowed) |
477 » » io.WriteString(w, "405 must CONNECT to "+rpcPath+"\n") | 483 » » io.WriteString(w, "405 must CONNECT\n") |
478 return | 484 return |
479 } | 485 } |
480 conn, _, err := w.Hijack() | 486 conn, _, err := w.Hijack() |
481 if err != nil { | 487 if err != nil { |
482 log.Print("rpc hijacking ", w.RemoteAddr(), ": ", err.String()) | 488 log.Print("rpc hijacking ", w.RemoteAddr(), ": ", err.String()) |
483 return | 489 return |
484 } | 490 } |
485 io.WriteString(conn, "HTTP/1.0 "+connected+"\n\n") | 491 io.WriteString(conn, "HTTP/1.0 "+connected+"\n\n") |
486 server.ServeConn(conn) | 492 server.ServeConn(conn) |
487 } | 493 } |
488 | 494 |
489 // HandleHTTP registers an HTTP handler for RPC messages. | 495 // HandleHTTP registers an HTTP handler for RPC messages on rpcPath, |
496 // and a debugging handler on debugPath. | |
490 // It is still necessary to invoke http.Serve(), typically in a go statement. | 497 // It is still necessary to invoke http.Serve(), typically in a go statement. |
491 func (server *Server) HandleHTTP() { | 498 func (server *Server) HandleHTTP(rpcPath, debugPath string) { |
492 http.Handle(rpcPath, server) | 499 http.Handle(rpcPath, server) |
493 http.Handle(debugPath, debugHTTP{server}) | 500 http.Handle(debugPath, debugHTTP{server}) |
494 } | 501 } |
495 | 502 |
496 // HandleHTTP registers an HTTP handler for RPC messages to DefaultServer. | 503 // HandleHTTP registers an HTTP handler for RPC messages to DefaultServer |
504 // on DefaultRPCPath and a debugging handler on DefaultDebugPath. | |
497 // It is still necessary to invoke http.Serve(), typically in a go statement. | 505 // It is still necessary to invoke http.Serve(), typically in a go statement. |
498 func HandleHTTP() { | 506 func HandleHTTP() { |
499 » DefaultServer.HandleHTTP() | 507 » DefaultServer.HandleHTTP(DefaultRPCPath, DefaultDebugPath) |
500 } | 508 } |
LEFT | RIGHT |