golang.org/x/tools@v0.21.1-0.20240520172518-788d39e776b1/internal/jsonrpc2/stream.go (about)

     1  // Copyright 2018 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package jsonrpc2
     6  
     7  import (
     8  	"bufio"
     9  	"context"
    10  	"encoding/json"
    11  	"fmt"
    12  	"io"
    13  	"net"
    14  	"strconv"
    15  	"strings"
    16  )
    17  
    18  // Stream abstracts the transport mechanics from the JSON RPC protocol.
    19  // A Conn reads and writes messages using the stream it was provided on
    20  // construction, and assumes that each call to Read or Write fully transfers
    21  // a single message, or returns an error.
    22  // A stream is not safe for concurrent use, it is expected it will be used by
    23  // a single Conn in a safe manner.
    24  type Stream interface {
    25  	// Read gets the next message from the stream.
    26  	Read(context.Context) (Message, int64, error)
    27  	// Write sends a message to the stream.
    28  	Write(context.Context, Message) (int64, error)
    29  	// Close closes the connection.
    30  	// Any blocked Read or Write operations will be unblocked and return errors.
    31  	Close() error
    32  }
    33  
    34  // Framer wraps a network connection up into a Stream.
    35  // It is responsible for the framing and encoding of messages into wire form.
    36  // NewRawStream and NewHeaderStream are implementations of a Framer.
    37  type Framer func(conn net.Conn) Stream
    38  
    39  // NewRawStream returns a Stream built on top of a net.Conn.
    40  // The messages are sent with no wrapping, and rely on json decode consistency
    41  // to determine message boundaries.
    42  func NewRawStream(conn net.Conn) Stream {
    43  	return &rawStream{
    44  		conn: conn,
    45  		in:   json.NewDecoder(conn),
    46  	}
    47  }
    48  
    49  type rawStream struct {
    50  	conn net.Conn
    51  	in   *json.Decoder
    52  }
    53  
    54  func (s *rawStream) Read(ctx context.Context) (Message, int64, error) {
    55  	select {
    56  	case <-ctx.Done():
    57  		return nil, 0, ctx.Err()
    58  	default:
    59  	}
    60  	var raw json.RawMessage
    61  	if err := s.in.Decode(&raw); err != nil {
    62  		return nil, 0, err
    63  	}
    64  	msg, err := DecodeMessage(raw)
    65  	return msg, int64(len(raw)), err
    66  }
    67  
    68  func (s *rawStream) Write(ctx context.Context, msg Message) (int64, error) {
    69  	select {
    70  	case <-ctx.Done():
    71  		return 0, ctx.Err()
    72  	default:
    73  	}
    74  	data, err := json.Marshal(msg)
    75  	if err != nil {
    76  		return 0, fmt.Errorf("marshaling message: %v", err)
    77  	}
    78  	n, err := s.conn.Write(data)
    79  	return int64(n), err
    80  }
    81  
    82  func (s *rawStream) Close() error {
    83  	return s.conn.Close()
    84  }
    85  
    86  // NewHeaderStream returns a Stream built on top of a net.Conn.
    87  // The messages are sent with HTTP content length and MIME type headers.
    88  // This is the format used by LSP and others.
    89  func NewHeaderStream(conn net.Conn) Stream {
    90  	return &headerStream{
    91  		conn: conn,
    92  		in:   bufio.NewReader(conn),
    93  	}
    94  }
    95  
    96  type headerStream struct {
    97  	conn net.Conn
    98  	in   *bufio.Reader
    99  }
   100  
   101  func (s *headerStream) Read(ctx context.Context) (Message, int64, error) {
   102  	select {
   103  	case <-ctx.Done():
   104  		return nil, 0, ctx.Err()
   105  	default:
   106  	}
   107  	var total, length int64
   108  	// read the header, stop on the first empty line
   109  	for {
   110  		line, err := s.in.ReadString('\n')
   111  		total += int64(len(line))
   112  		if err != nil {
   113  			return nil, total, fmt.Errorf("failed reading header line: %w", err)
   114  		}
   115  		line = strings.TrimSpace(line)
   116  		// check we have a header line
   117  		if line == "" {
   118  			break
   119  		}
   120  		colon := strings.IndexRune(line, ':')
   121  		if colon < 0 {
   122  			return nil, total, fmt.Errorf("invalid header line %q", line)
   123  		}
   124  		name, value := line[:colon], strings.TrimSpace(line[colon+1:])
   125  		switch name {
   126  		case "Content-Length":
   127  			if length, err = strconv.ParseInt(value, 10, 32); err != nil {
   128  				return nil, total, fmt.Errorf("failed parsing Content-Length: %v", value)
   129  			}
   130  			if length <= 0 {
   131  				return nil, total, fmt.Errorf("invalid Content-Length: %v", length)
   132  			}
   133  		default:
   134  			// ignoring unknown headers
   135  		}
   136  	}
   137  	if length == 0 {
   138  		return nil, total, fmt.Errorf("missing Content-Length header")
   139  	}
   140  	data := make([]byte, length)
   141  	if _, err := io.ReadFull(s.in, data); err != nil {
   142  		return nil, total, err
   143  	}
   144  	total += length
   145  	msg, err := DecodeMessage(data)
   146  	return msg, total, err
   147  }
   148  
   149  func (s *headerStream) Write(ctx context.Context, msg Message) (int64, error) {
   150  	select {
   151  	case <-ctx.Done():
   152  		return 0, ctx.Err()
   153  	default:
   154  	}
   155  	data, err := json.Marshal(msg)
   156  	if err != nil {
   157  		return 0, fmt.Errorf("marshaling message: %v", err)
   158  	}
   159  	n, err := fmt.Fprintf(s.conn, "Content-Length: %v\r\n\r\n", len(data))
   160  	total := int64(n)
   161  	if err == nil {
   162  		n, err = s.conn.Write(data)
   163  		total += int64(n)
   164  	}
   165  	return total, err
   166  }
   167  
   168  func (s *headerStream) Close() error {
   169  	return s.conn.Close()
   170  }