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  }