GoPLS Viewer

Home|gopls/internal/jsonrpc2/stream.go
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
5package jsonrpc2
6
7import (
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.
24type Stream interface {
25    // Read gets the next message from the stream.
26    Read(context.Context) (Messageint64error)
27    // Write sends a message to the stream.
28    Write(context.ContextMessage) (int64error)
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.
37type Framer func(conn net.ConnStream
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.
42func NewRawStream(conn net.ConnStream {
43    return &rawStream{
44        connconn,
45        in:   json.NewDecoder(conn),
46    }
47}
48
49type rawStream struct {
50    conn net.Conn
51    in   *json.Decoder
52}
53
54func (s *rawStreamRead(ctx context.Context) (Messageint64error) {
55    select {
56    case <-ctx.Done():
57        return nil0ctx.Err()
58    default:
59    }
60    var raw json.RawMessage
61    if err := s.in.Decode(&raw); err != nil {
62        return nil0err
63    }
64    msgerr := DecodeMessage(raw)
65    return msgint64(len(raw)), err
66}
67
68func (s *rawStreamWrite(ctx context.Contextmsg Message) (int64error) {
69    select {
70    case <-ctx.Done():
71        return 0ctx.Err()
72    default:
73    }
74    dataerr := json.Marshal(msg)
75    if err != nil {
76        return 0fmt.Errorf("marshaling message: %v"err)
77    }
78    nerr := s.conn.Write(data)
79    return int64(n), err
80}
81
82func (s *rawStreamClose() 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.
89func NewHeaderStream(conn net.ConnStream {
90    return &headerStream{
91        connconn,
92        in:   bufio.NewReader(conn),
93    }
94}
95
96type headerStream struct {
97    conn net.Conn
98    in   *bufio.Reader
99}
100
101func (s *headerStreamRead(ctx context.Context) (Messageint64error) {
102    select {
103    case <-ctx.Done():
104        return nil0ctx.Err()
105    default:
106    }
107    var totallength int64
108    // read the header, stop on the first empty line
109    for {
110        lineerr := s.in.ReadString('\n')
111        total += int64(len(line))
112        if err != nil {
113            return niltotalfmt.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 niltotalfmt.Errorf("invalid header line %q"line)
123        }
124        namevalue := line[:colon], strings.TrimSpace(line[colon+1:])
125        switch name {
126        case "Content-Length":
127            if lengtherr = strconv.ParseInt(value1032); err != nil {
128                return niltotalfmt.Errorf("failed parsing Content-Length: %v"value)
129            }
130            if length <= 0 {
131                return niltotalfmt.Errorf("invalid Content-Length: %v"length)
132            }
133        default:
134            // ignoring unknown headers
135        }
136    }
137    if length == 0 {
138        return niltotalfmt.Errorf("missing Content-Length header")
139    }
140    data := make([]bytelength)
141    if _err := io.ReadFull(s.indata); err != nil {
142        return niltotalerr
143    }
144    total += length
145    msgerr := DecodeMessage(data)
146    return msgtotalerr
147}
148
149func (s *headerStreamWrite(ctx context.Contextmsg Message) (int64error) {
150    select {
151    case <-ctx.Done():
152        return 0ctx.Err()
153    default:
154    }
155    dataerr := json.Marshal(msg)
156    if err != nil {
157        return 0fmt.Errorf("marshaling message: %v"err)
158    }
159    nerr := fmt.Fprintf(s.conn"Content-Length: %v\r\n\r\n"len(data))
160    total := int64(n)
161    if err == nil {
162        nerr = s.conn.Write(data)
163        total += int64(n)
164    }
165    return totalerr
166}
167
168func (s *headerStreamClose() error {
169    return s.conn.Close()
170}
171
MembersX
rawStream.Read
headerStream.Write.ctx
NewHeaderStream
headerStream.Read.s
headerStream.Read.ctx
rawStream.Read.err
bufio
headerStream.Write
rawStream.Close
headerStream.conn
headerStream.Write.s
Framer
rawStream.Write.err
headerStream.Read.BlockStmt.err
headerStream.Read.data
headerStream.Close
rawStream.Read.msg
headerStream.Read.msg
headerStream.Write.err
strconv
rawStream
rawStream.Write.n
rawStream.Write.s
headerStream.in
headerStream.Read
headerStream.Write.n
rawStream.Write.msg
headerStream.Read.length
strings
NewRawStream.conn
rawStream.Read.ctx
rawStream.Write.ctx
headerStream.Read.BlockStmt.value
headerStream.Read._
rawStream.Write
NewHeaderStream.conn
rawStream.Close.s
headerStream.Read.err
Stream
rawStream.in
rawStream.Read.s
rawStream.Read.raw
headerStream.Write.msg
headerStream.Close.s
rawStream.Write.data
headerStream.Read.BlockStmt.colon
NewRawStream
headerStream.Read.total
headerStream.Read.BlockStmt.line
headerStream.Write.total
headerStream
headerStream.Write.data
rawStream.conn
Members
X