Skip to content

Commit e0a9028

Browse files
authored
[go] jsonrpc2 cleanup (#949)
* jsonrpc2 cleanup * code review comments * fix lint * review feedback
1 parent 9351e26 commit e0a9028

File tree

2 files changed

+199
-104
lines changed

2 files changed

+199
-104
lines changed

go/internal/jsonrpc2/frame.go

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package jsonrpc2
2+
3+
import (
4+
"bufio"
5+
"fmt"
6+
"io"
7+
"math"
8+
"strconv"
9+
"strings"
10+
)
11+
12+
// headerReader reads Content-Length delimited JSON-RPC frames from a stream.
13+
type headerReader struct {
14+
in *bufio.Reader
15+
}
16+
17+
func newHeaderReader(r io.Reader) *headerReader {
18+
return &headerReader{in: bufio.NewReader(r)}
19+
}
20+
21+
// Read reads the next complete frame from the stream. It returns io.EOF on a
22+
// clean end-of-stream (no partial data) and io.ErrUnexpectedEOF if the stream
23+
// was interrupted mid-header.
24+
func (r *headerReader) Read() ([]byte, error) {
25+
firstRead := true
26+
var contentLength int64
27+
// Read headers, stop on the first blank line.
28+
for {
29+
line, err := r.in.ReadString('\n')
30+
if err != nil {
31+
if err == io.EOF {
32+
if firstRead && line == "" {
33+
return nil, io.EOF // clean EOF
34+
}
35+
err = io.ErrUnexpectedEOF
36+
}
37+
return nil, fmt.Errorf("failed reading header line: %w", err)
38+
}
39+
firstRead = false
40+
41+
line = strings.TrimSpace(line)
42+
if line == "" {
43+
break
44+
}
45+
colon := strings.IndexRune(line, ':')
46+
if colon < 0 {
47+
return nil, fmt.Errorf("invalid header line %q", line)
48+
}
49+
name, value := line[:colon], strings.TrimSpace(line[colon+1:])
50+
switch name {
51+
case "Content-Length":
52+
contentLength, err = strconv.ParseInt(value, 10, 64)
53+
if err != nil {
54+
return nil, fmt.Errorf("failed parsing Content-Length: %v", value)
55+
}
56+
if contentLength <= 0 {
57+
return nil, fmt.Errorf("invalid Content-Length: %v", contentLength)
58+
}
59+
default:
60+
// ignoring unknown headers
61+
}
62+
}
63+
if contentLength == 0 {
64+
return nil, fmt.Errorf("missing Content-Length header")
65+
}
66+
if contentLength > math.MaxInt {
67+
return nil, fmt.Errorf("Content-Length too large: %d", contentLength)
68+
}
69+
data := make([]byte, contentLength)
70+
if _, err := io.ReadFull(r.in, data); err != nil {
71+
return nil, err
72+
}
73+
return data, nil
74+
}
75+
76+
// headerWriter writes Content-Length delimited JSON-RPC frames to a stream.
77+
type headerWriter struct {
78+
out io.Writer
79+
}
80+
81+
func newHeaderWriter(w io.Writer) *headerWriter {
82+
return &headerWriter{out: w}
83+
}
84+
85+
// Write sends a single frame with Content-Length header.
86+
func (w *headerWriter) Write(data []byte) error {
87+
if _, err := fmt.Fprintf(w.out, "Content-Length: %d\r\n\r\n", len(data)); err != nil {
88+
return err
89+
}
90+
_, err := w.out.Write(data)
91+
return err
92+
}

0 commit comments

Comments
 (0)