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 }