github.com/core-coin/go-core/v2@v2.1.9/rpc/server.go (about) 1 // Copyright 2015 by the Authors 2 // This file is part of the go-core library. 3 // 4 // The go-core library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-core library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-core library. If not, see <http://www.gnu.org/licenses/>. 16 17 package rpc 18 19 import ( 20 "context" 21 "io" 22 "sync/atomic" 23 24 mapset "github.com/deckarep/golang-set" 25 26 "github.com/core-coin/go-core/v2/log" 27 ) 28 29 const MetadataApi = "rpc" 30 const EngineApi = "engine" 31 32 // CodecOption specifies which type of messages a codec supports. 33 // 34 // Deprecated: this option is no longer honored by Server. 35 type CodecOption int 36 37 const ( 38 // OptionMethodInvocation is an indication that the codec supports RPC method calls 39 OptionMethodInvocation CodecOption = 1 << iota 40 41 // OptionSubscriptions is an indication that the codec supports RPC notifications 42 OptionSubscriptions = 1 << iota // support pub sub 43 ) 44 45 // Server is an RPC server. 46 type Server struct { 47 services serviceRegistry 48 idgen func() ID 49 run int32 50 codecs mapset.Set 51 } 52 53 // NewServer creates a new server instance with no registered handlers. 54 func NewServer() *Server { 55 server := &Server{idgen: randomIDGenerator(), codecs: mapset.NewSet(), run: 1} 56 // Register the default service providing meta information about the RPC service such 57 // as the services and methods it offers. 58 rpcService := &RPCService{server} 59 server.RegisterName(MetadataApi, rpcService) 60 return server 61 } 62 63 // RegisterName creates a service for the given receiver type under the given name. When no 64 // methods on the given receiver match the criteria to be either a RPC method or a 65 // subscription an error is returned. Otherwise a new service is created and added to the 66 // service collection this server provides to clients. 67 func (s *Server) RegisterName(name string, receiver interface{}) error { 68 return s.services.registerName(name, receiver) 69 } 70 71 // ServeCodec reads incoming requests from codec, calls the appropriate callback and writes 72 // the response back using the given codec. It will block until the codec is closed or the 73 // server is stopped. In either case the codec is closed. 74 // 75 // Note that codec options are no longer supported. 76 func (s *Server) ServeCodec(codec ServerCodec, options CodecOption) { 77 defer codec.close() 78 79 // Don't serve if server is stopped. 80 if atomic.LoadInt32(&s.run) == 0 { 81 return 82 } 83 84 // Add the codec to the set so it can be closed by Stop. 85 s.codecs.Add(codec) 86 defer s.codecs.Remove(codec) 87 88 c := initClient(codec, s.idgen, &s.services) 89 <-codec.closed() 90 c.Close() 91 } 92 93 // serveSingleRequest reads and processes a single RPC request from the given codec. This 94 // is used to serve HTTP connections. Subscriptions and reverse calls are not allowed in 95 // this mode. 96 func (s *Server) serveSingleRequest(ctx context.Context, codec ServerCodec) { 97 // Don't serve if server is stopped. 98 if atomic.LoadInt32(&s.run) == 0 { 99 return 100 } 101 102 h := newHandler(ctx, codec, s.idgen, &s.services) 103 h.allowSubscribe = false 104 defer h.close(io.EOF, nil) 105 106 reqs, batch, err := codec.readBatch() 107 if err != nil { 108 if err != io.EOF { 109 codec.writeJSON(ctx, errorMessage(&invalidMessageError{"parse error"})) 110 } 111 return 112 } 113 if batch { 114 h.handleBatch(reqs) 115 } else { 116 h.handleMsg(reqs[0]) 117 } 118 } 119 120 // Stop stops reading new requests, waits for stopPendingRequestTimeout to allow pending 121 // requests to finish, then closes all codecs which will cancel pending requests and 122 // subscriptions. 123 func (s *Server) Stop() { 124 if atomic.CompareAndSwapInt32(&s.run, 1, 0) { 125 log.Debug("RPC server shutting down") 126 s.codecs.Each(func(c interface{}) bool { 127 c.(ServerCodec).close() 128 return true 129 }) 130 } 131 } 132 133 // RPCService gives meta information about the server. 134 // e.g. gives information about the loaded modules. 135 type RPCService struct { 136 server *Server 137 } 138 139 // Modules returns the list of RPC services with their version number 140 func (s *RPCService) Modules() map[string]string { 141 s.server.services.mu.Lock() 142 defer s.server.services.mu.Unlock() 143 144 modules := make(map[string]string) 145 for name := range s.server.services.services { 146 modules[name] = "1.0" 147 } 148 return modules 149 } 150 151 // PeerInfo contains information about the remote end of the network connection. 152 // 153 // This is available within RPC method handlers through the context. Call 154 // PeerInfoFromContext to get information about the client connection related to 155 // the current method call. 156 type PeerInfo struct { 157 // Transport is name of the protocol used by the client. 158 // This can be "http", "ws" or "ipc". 159 Transport string 160 161 // Address of client. This will usually contain the IP address and port. 162 RemoteAddr string 163 164 // Addditional information for HTTP and WebSocket connections. 165 HTTP struct { 166 // Protocol version, i.e. "HTTP/1.1". This is not set for WebSocket. 167 Version string 168 // Header values sent by the client. 169 UserAgent string 170 Origin string 171 Host string 172 } 173 } 174 175 type peerInfoContextKey struct{} 176 177 // PeerInfoFromContext returns information about the client's network connection. 178 // Use this with the context passed to RPC method handler functions. 179 // 180 // The zero value is returned if no connection info is present in ctx. 181 func PeerInfoFromContext(ctx context.Context) PeerInfo { 182 info, _ := ctx.Value(peerInfoContextKey{}).(PeerInfo) 183 return info 184 }