github.com/goplus/gop@v1.2.6/x/jsonrpc2/frame.go (about)

     1  /*
     2   * Copyright (c) 2023 The GoPlus Authors (goplus.org). All rights reserved.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  // Copyright 2018 The Go Authors. All rights reserved.
    18  // Use of this source code is governed by a BSD-style
    19  // license that can be found in the LICENSE file.
    20  
    21  package jsonrpc2
    22  
    23  import (
    24  	"bufio"
    25  	"context"
    26  	"fmt"
    27  	"io"
    28  	"strconv"
    29  	"strings"
    30  )
    31  
    32  // Reader abstracts the transport mechanics from the JSON RPC protocol.
    33  // A Conn reads messages from the reader it was provided on construction,
    34  // and assumes that each call to Read fully transfers a single message,
    35  // or returns an error.
    36  // A reader is not safe for concurrent use, it is expected it will be used by
    37  // a single Conn in a safe manner.
    38  type Reader interface {
    39  	// Read gets the next message from the stream.
    40  	Read(context.Context) (Message, int64, error)
    41  }
    42  
    43  // Writer abstracts the transport mechanics from the JSON RPC protocol.
    44  // A Conn writes messages using the writer it was provided on construction,
    45  // and assumes that each call to Write fully transfers a single message,
    46  // or returns an error.
    47  // A writer is not safe for concurrent use, it is expected it will be used by
    48  // a single Conn in a safe manner.
    49  type Writer interface {
    50  	// Write sends a message to the stream.
    51  	Write(context.Context, Message) (int64, error)
    52  }
    53  
    54  // Framer wraps low level byte readers and writers into jsonrpc2 message
    55  // readers and writers.
    56  // It is responsible for the framing and encoding of messages into wire form.
    57  type Framer interface {
    58  	// Reader wraps a byte reader into a message reader.
    59  	Reader(rw io.Reader) Reader
    60  	// Writer wraps a byte writer into a message writer.
    61  	Writer(rw io.Writer) Writer
    62  }
    63  
    64  // HeaderFramer returns a new Framer.
    65  // The messages are sent with HTTP content length and MIME type headers.
    66  // This is the format used by LSP and others.
    67  func HeaderFramer() Framer { return headerFramer{} }
    68  
    69  type headerFramer struct{}
    70  type headerReader struct{ in *bufio.Reader }
    71  type headerWriter struct{ out io.Writer }
    72  
    73  func (headerFramer) Reader(rw io.Reader) Reader {
    74  	return &headerReader{in: bufio.NewReader(rw)}
    75  }
    76  
    77  func (headerFramer) Writer(rw io.Writer) Writer {
    78  	return &headerWriter{out: rw}
    79  }
    80  
    81  func (r *headerReader) Read(ctx context.Context) (Message, int64, error) {
    82  	select {
    83  	case <-ctx.Done():
    84  		return nil, 0, ctx.Err()
    85  	default:
    86  	}
    87  	var total, length int64
    88  	// read the header, stop on the first empty line
    89  	for {
    90  		line, err := r.in.ReadString('\n')
    91  		total += int64(len(line))
    92  		if err != nil {
    93  			if err == io.EOF {
    94  				if total == 0 {
    95  					return nil, 0, io.EOF
    96  				}
    97  				err = io.ErrUnexpectedEOF
    98  			}
    99  			return nil, total, fmt.Errorf("failed reading header line: %w", err)
   100  		}
   101  		line = strings.TrimSpace(line)
   102  		// check we have a header line
   103  		if line == "" {
   104  			break
   105  		}
   106  		colon := strings.IndexRune(line, ':')
   107  		if colon < 0 {
   108  			return nil, total, fmt.Errorf("invalid header line %q", line)
   109  		}
   110  		name, value := line[:colon], strings.TrimSpace(line[colon+1:])
   111  		switch name {
   112  		case "Content-Length":
   113  			if length, err = strconv.ParseInt(value, 10, 32); err != nil {
   114  				return nil, total, fmt.Errorf("failed parsing Content-Length: %v", value)
   115  			}
   116  			if length <= 0 {
   117  				return nil, total, fmt.Errorf("invalid Content-Length: %v", length)
   118  			}
   119  		default:
   120  			// ignoring unknown headers
   121  		}
   122  	}
   123  	if length == 0 {
   124  		return nil, total, fmt.Errorf("missing Content-Length header")
   125  	}
   126  	data := make([]byte, length)
   127  	n, err := io.ReadFull(r.in, data)
   128  	total += int64(n)
   129  	if err != nil {
   130  		return nil, total, err
   131  	}
   132  	msg, err := DecodeMessage(data)
   133  	return msg, total, err
   134  }
   135  
   136  func (w *headerWriter) Write(ctx context.Context, msg Message) (int64, error) {
   137  	select {
   138  	case <-ctx.Done():
   139  		return 0, ctx.Err()
   140  	default:
   141  	}
   142  	data, err := EncodeMessage(msg)
   143  	if err != nil {
   144  		return 0, fmt.Errorf("marshaling message: %v", err)
   145  	}
   146  	n, err := fmt.Fprintf(w.out, "Content-Length: %v\r\n\r\n", len(data))
   147  	total := int64(n)
   148  	if err == nil {
   149  		n, err = w.out.Write(data)
   150  		total += int64(n)
   151  	}
   152  	return total, err
   153  }