|  | // Copyright 2018 The Go 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 jsonrpc2 | 
|  |  | 
|  | import ( | 
|  | "bufio" | 
|  | "context" | 
|  | "encoding/json" | 
|  | "fmt" | 
|  | "io" | 
|  | "net" | 
|  | "strconv" | 
|  | "strings" | 
|  | ) | 
|  |  | 
|  | // Stream abstracts the transport mechanics from the JSON RPC protocol. | 
|  | // A Conn reads and writes messages using the stream it was provided on | 
|  | // construction, and assumes that each call to Read or Write fully transfers | 
|  | // a single message, or returns an error. | 
|  | // A stream is not safe for concurrent use, it is expected it will be used by | 
|  | // a single Conn in a safe manner. | 
|  | type Stream interface { | 
|  | // Read gets the next message from the stream. | 
|  | Read(context.Context) (Message, int64, error) | 
|  | // Write sends a message to the stream. | 
|  | Write(context.Context, Message) (int64, error) | 
|  | // Close closes the connection. | 
|  | // Any blocked Read or Write operations will be unblocked and return errors. | 
|  | Close() error | 
|  | } | 
|  |  | 
|  | // Framer wraps a network connection up into a Stream. | 
|  | // It is responsible for the framing and encoding of messages into wire form. | 
|  | // NewRawStream and NewHeaderStream are implementations of a Framer. | 
|  | type Framer func(conn net.Conn) Stream | 
|  |  | 
|  | // NewRawStream returns a Stream built on top of a net.Conn. | 
|  | // The messages are sent with no wrapping, and rely on json decode consistency | 
|  | // to determine message boundaries. | 
|  | func NewRawStream(conn net.Conn) Stream { | 
|  | return &rawStream{ | 
|  | conn: conn, | 
|  | in:   json.NewDecoder(conn), | 
|  | } | 
|  | } | 
|  |  | 
|  | type rawStream struct { | 
|  | conn net.Conn | 
|  | in   *json.Decoder | 
|  | } | 
|  |  | 
|  | func (s *rawStream) Read(ctx context.Context) (Message, int64, error) { | 
|  | select { | 
|  | case <-ctx.Done(): | 
|  | return nil, 0, ctx.Err() | 
|  | default: | 
|  | } | 
|  | var raw json.RawMessage | 
|  | if err := s.in.Decode(&raw); err != nil { | 
|  | return nil, 0, err | 
|  | } | 
|  | msg, err := DecodeMessage(raw) | 
|  | return msg, int64(len(raw)), err | 
|  | } | 
|  |  | 
|  | func (s *rawStream) Write(ctx context.Context, msg Message) (int64, error) { | 
|  | select { | 
|  | case <-ctx.Done(): | 
|  | return 0, ctx.Err() | 
|  | default: | 
|  | } | 
|  | data, err := json.Marshal(msg) | 
|  | if err != nil { | 
|  | return 0, fmt.Errorf("marshaling message: %v", err) | 
|  | } | 
|  | n, err := s.conn.Write(data) | 
|  | return int64(n), err | 
|  | } | 
|  |  | 
|  | func (s *rawStream) Close() error { | 
|  | return s.conn.Close() | 
|  | } | 
|  |  | 
|  | // NewHeaderStream returns a Stream built on top of a net.Conn. | 
|  | // The messages are sent with HTTP content length and MIME type headers. | 
|  | // This is the format used by LSP and others. | 
|  | func NewHeaderStream(conn net.Conn) Stream { | 
|  | return &headerStream{ | 
|  | conn: conn, | 
|  | in:   bufio.NewReader(conn), | 
|  | } | 
|  | } | 
|  |  | 
|  | type headerStream struct { | 
|  | conn net.Conn | 
|  | in   *bufio.Reader | 
|  | } | 
|  |  | 
|  | func (s *headerStream) Read(ctx context.Context) (Message, int64, error) { | 
|  | select { | 
|  | case <-ctx.Done(): | 
|  | return nil, 0, ctx.Err() | 
|  | default: | 
|  | } | 
|  | var total, length int64 | 
|  | // read the header, stop on the first empty line | 
|  | for { | 
|  | line, err := s.in.ReadString('\n') | 
|  | total += int64(len(line)) | 
|  | if err != nil { | 
|  | return nil, total, fmt.Errorf("failed reading header line: %w", err) | 
|  | } | 
|  | line = strings.TrimSpace(line) | 
|  | // check we have a header line | 
|  | if line == "" { | 
|  | break | 
|  | } | 
|  | colon := strings.IndexRune(line, ':') | 
|  | if colon < 0 { | 
|  | return nil, total, fmt.Errorf("invalid header line %q", line) | 
|  | } | 
|  | name, value := line[:colon], strings.TrimSpace(line[colon+1:]) | 
|  | switch name { | 
|  | case "Content-Length": | 
|  | if length, err = strconv.ParseInt(value, 10, 32); err != nil { | 
|  | return nil, total, fmt.Errorf("failed parsing Content-Length: %v", value) | 
|  | } | 
|  | if length <= 0 { | 
|  | return nil, total, fmt.Errorf("invalid Content-Length: %v", length) | 
|  | } | 
|  | default: | 
|  | // ignoring unknown headers | 
|  | } | 
|  | } | 
|  | if length == 0 { | 
|  | return nil, total, fmt.Errorf("missing Content-Length header") | 
|  | } | 
|  | data := make([]byte, length) | 
|  | if _, err := io.ReadFull(s.in, data); err != nil { | 
|  | return nil, total, err | 
|  | } | 
|  | total += length | 
|  | msg, err := DecodeMessage(data) | 
|  | return msg, total, err | 
|  | } | 
|  |  | 
|  | func (s *headerStream) Write(ctx context.Context, msg Message) (int64, error) { | 
|  | select { | 
|  | case <-ctx.Done(): | 
|  | return 0, ctx.Err() | 
|  | default: | 
|  | } | 
|  | data, err := json.Marshal(msg) | 
|  | if err != nil { | 
|  | return 0, fmt.Errorf("marshaling message: %v", err) | 
|  | } | 
|  | n, err := fmt.Fprintf(s.conn, "Content-Length: %v\r\n\r\n", len(data)) | 
|  | total := int64(n) | 
|  | if err == nil { | 
|  | n, err = s.conn.Write(data) | 
|  | total += int64(n) | 
|  | } | 
|  | return total, err | 
|  | } | 
|  |  | 
|  | func (s *headerStream) Close() error { | 
|  | return s.conn.Close() | 
|  | } |