github.com/jmigpin/editor@v1.6.0/core/lsproto/codec.go (about) 1 package lsproto 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "math" 8 "net/rpc" 9 "strconv" 10 "strings" 11 "sync" 12 13 "errors" 14 ) 15 16 //---------- 17 18 // Implements rpc.ClientCodec 19 type JsonCodec struct { 20 OnNotificationMessage func(*NotificationMessage) 21 OnUnexpectedServerReply func(*Response) 22 23 rwc io.ReadWriteCloser 24 responses chan interface{} 25 simulatedResp chan interface{} 26 27 readData readData // used by read response header/body 28 29 mu struct { 30 sync.Mutex 31 closed bool 32 } 33 } 34 35 // Needs a call to ReadLoop() to start reading. 36 func NewJsonCodec(rwc io.ReadWriteCloser) *JsonCodec { 37 c := &JsonCodec{rwc: rwc} 38 c.responses = make(chan interface{}, 4) 39 c.simulatedResp = make(chan interface{}, 4) 40 return c 41 } 42 43 //---------- 44 45 func (c *JsonCodec) WriteRequest(req *rpc.Request, data interface{}) error { 46 method := req.ServiceMethod 47 48 // internal: methods with a noreply prefix don't expect a reply. This was done throught the method name to be able to use net/rpc. This is not part of the lsp protocol. 49 noreply := false 50 if m, ok := noreplyMethod(method); ok { 51 noreply = true 52 method = m 53 } 54 55 msg := (interface{})(nil) 56 if noreply { 57 // don't send an Id on messages that don't need an acknowledgement 58 m := &NotificationMessage{ 59 Method: method, 60 Params: data, 61 } 62 m.Message = MakeMessage() 63 msg = m 64 } else { 65 // default case includes the Id 66 m := &RequestMessage{ 67 Id: int(req.Seq), 68 Method: method, 69 Params: data, 70 } 71 m.Message = MakeMessage() 72 msg = m 73 } 74 //logPrintf("write req -->: %v(%v)", msg.Method, msg.Id) 75 76 b, err := encodeJson(msg) 77 if err != nil { 78 return err 79 } 80 81 // build header+body 82 h := fmt.Sprintf("Content-Length: %v\r\n\r\n", len(b)) 83 buf := make([]byte, len(h)+len(b)) 84 copy(buf, []byte(h)) // header 85 copy(buf[len(h):], b) // body 86 87 logPrintf("write req -->: %s%s", h, string(b)) 88 89 _, err = c.rwc.Write(buf) 90 if err != nil { 91 return err 92 } 93 94 // simulate a response (noreply) with the seq if there is no err writing the msg 95 if noreply { 96 c.responses <- req.Seq 97 } 98 return nil 99 } 100 101 //---------- 102 103 func (c *JsonCodec) ReadLoop() error { 104 for { 105 b, err := c.read() 106 if c.isClosed() { 107 return nil // no error if done 108 } 109 if err != nil { 110 logPrintf("read resp err <--: %v\n", err) 111 return err 112 } 113 logPrintf("read resp <--: %s\n", b) 114 c.responses <- b 115 } 116 } 117 118 //---------- 119 120 // Sets response.Seq to have ReadResponseBody be called with the correct reply variable. 121 func (c *JsonCodec) ReadResponseHeader(resp *rpc.Response) error { 122 c.readData = readData{} // reset 123 resp.Seq = uint64(math.MaxInt64) // set to non-existent sequence 124 125 v, ok := <-c.responses 126 if !ok { 127 return fmt.Errorf("responses chan closed") 128 } 129 130 switch t := v.(type) { 131 case uint64: // request id that expects noreply 132 c.readData = readData{noReply: true} 133 resp.Seq = t 134 return nil 135 case []byte: 136 // decode json 137 lspResp := &Response{} 138 rd := bytes.NewReader(t) 139 if err := decodeJson(rd, lspResp); err != nil { 140 return fmt.Errorf("jsoncodec: decode: %v", err) 141 } 142 c.readData.resp = lspResp 143 // msg id (needed for the rpc to run the reply to the caller) 144 if !lspResp.IsNotification() { 145 resp.Seq = uint64(lspResp.Id) 146 } 147 return nil 148 default: 149 panic("!") 150 } 151 } 152 153 func (c *JsonCodec) ReadResponseBody(reply interface{}) error { 154 // exhaust requests with noreply 155 if c.readData.noReply { 156 return nil 157 } 158 159 // server push callback (no id) 160 if c.readData.resp.IsNotification() { 161 if reply != nil { 162 return fmt.Errorf("jsoncodec: server push with reply expecting data: %v", reply) 163 } 164 // run callback 165 nm := c.readData.resp.NotificationMessage 166 if c.OnNotificationMessage != nil { 167 c.OnNotificationMessage(&nm) 168 } 169 return nil 170 } 171 172 // assign data 173 if replyResp, ok := reply.(*Response); ok { 174 *replyResp = *c.readData.resp 175 return nil 176 } 177 178 // error 179 if reply == nil { 180 // Server returned a reply that was supposed to have no reply. 181 // Can happen if a "noreply:" func was called and the msg id was already thrown away because it was a notification (no reply was expected). A server can reply with an error in the case of not supporting that function. Or reply if it does support, but internaly it returns a msg saying that the function did not reply a value. 182 c.unexpectedServerReply(c.readData.resp) 183 //err := fmt.Errorf("jsoncodec: server msg without handler: %s", spew.Sdump(c.readData)) 184 // Returning an error would stop the connection. 185 return nil 186 } 187 return fmt.Errorf("jsoncodec: reply data not assigned: %v", reply) 188 } 189 190 //---------- 191 192 func (c *JsonCodec) read() ([]byte, error) { 193 cl, err := c.readContentLengthHeader() // content length 194 if err != nil { 195 return nil, err 196 } 197 return c.readContent(cl) 198 } 199 200 func (c *JsonCodec) readContentLengthHeader() (int, error) { 201 var length int 202 var headersSize int 203 for { 204 // read line 205 var line string 206 for { 207 b := make([]byte, 1) 208 _, err := io.ReadFull(c.rwc, b) 209 if err != nil { 210 return 0, err 211 } 212 213 if b[0] == '\n' { // end of header line 214 break 215 } 216 if len(line) > 1024 { 217 return 0, errors.New("header line too long") 218 } 219 220 line += string(b) 221 headersSize += len(b) 222 } 223 224 if headersSize > 10*1024 { 225 return 0, errors.New("headers too long") 226 } 227 228 // header finished (empty line) 229 line = strings.TrimSpace(line) 230 if line == "" { 231 break 232 } 233 // header line 234 colon := strings.IndexRune(line, ':') 235 if colon < 0 { 236 return 0, fmt.Errorf("invalid header line %q", line) 237 } 238 name := strings.ToLower(line[:colon]) 239 value := strings.TrimSpace(line[colon+1:]) 240 switch name { 241 case "content-length": 242 l, err := strconv.ParseInt(value, 10, 32) 243 if err != nil { 244 return 0, fmt.Errorf("failed parsing content-length: %v", value) 245 } 246 if l <= 0 { 247 return 0, fmt.Errorf("invalid content-length: %v", l) 248 } 249 length = int(l) 250 } 251 } 252 if length == 0 { 253 return 0, fmt.Errorf("missing content-length") 254 } 255 return length, nil 256 } 257 258 func (c *JsonCodec) readContent(length int) ([]byte, error) { 259 b := make([]byte, length) 260 _, err := io.ReadFull(c.rwc, b) 261 return b, err 262 } 263 264 //---------- 265 266 func (c *JsonCodec) unexpectedServerReply(resp *Response) { 267 if c.OnUnexpectedServerReply != nil { 268 c.OnUnexpectedServerReply(resp) 269 } 270 } 271 272 //---------- 273 274 func (c *JsonCodec) Close() error { 275 c.mu.Lock() 276 defer c.mu.Unlock() 277 if !c.mu.closed { 278 c.mu.closed = true 279 return c.rwc.Close() 280 } 281 return nil 282 } 283 284 func (c *JsonCodec) isClosed() bool { 285 c.mu.Lock() 286 defer c.mu.Unlock() 287 return c.mu.closed 288 } 289 290 //---------- 291 //---------- 292 //---------- 293 294 type readData struct { 295 noReply bool 296 resp *Response 297 } 298 299 //---------- 300 301 func noreplyMethod(method string) (string, bool) { 302 prefix := "noreply:" 303 if strings.HasPrefix(method, prefix) { 304 return method[len(prefix):], true 305 } 306 return method, false 307 }