forked from davidfowl/signalr-ports
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathjsonhubprotocol.go
142 lines (124 loc) · 3.96 KB
/
jsonhubprotocol.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
package signalr
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"github.com/go-kit/kit/log"
"io"
)
// JSONHubProtocol is the JSON based SignalR protocol
type JSONHubProtocol struct {
dbg log.Logger
}
// Protocol specific message for correct unmarshaling of Arguments
type jsonInvocationMessage struct {
Type int `json:"type"`
Target string `json:"target"`
InvocationID string `json:"invocationId"`
Arguments []json.RawMessage `json:"arguments"`
StreamIds []string `json:"streamIds,omitempty"`
}
type jsonError struct {
raw string
err error
}
func (j *jsonError) Error() string {
return fmt.Sprintf("%v (source: %v)", j.err, j.raw)
}
// UnmarshalArgument unmarshals a json.RawMessage depending of the specified value type into value
func (j *JSONHubProtocol) UnmarshalArgument(argument interface{}, value interface{}) error {
if err := json.Unmarshal(argument.(json.RawMessage), value); err != nil {
return &jsonError{string(argument.(json.RawMessage)), err}
}
return nil
}
// ReadMessage reads a JSON message from buf and returns the message if the buf contained one completely.
// If buf does not contain the whole message, it returns a nil message and complete false
func (j *JSONHubProtocol) ReadMessage(buf *bytes.Buffer) (m interface{}, complete bool, err error) {
data, err := parseTextMessageFormat(buf)
switch {
case errors.Is(err, io.EOF):
return nil, false, err
// Other errors never happen, because parseTextMessageFormat will only return err
// from bytes.Buffer.ReadBytes() which is always io.EOF or nil
}
message := hubMessage{}
err = json.Unmarshal(data, &message)
_ = j.dbg.Log(evt, "read", msg, string(data))
if err != nil {
return nil, true, &jsonError{string(data), err}
}
switch message.Type {
case 1, 4:
jsonInvocation := jsonInvocationMessage{}
if err = json.Unmarshal(data, &jsonInvocation); err != nil {
err = &jsonError{string(data), err}
}
arguments := make([]interface{}, len(jsonInvocation.Arguments))
for i, a := range jsonInvocation.Arguments {
arguments[i] = a
}
invocation := invocationMessage{
Type: jsonInvocation.Type,
Target: jsonInvocation.Target,
InvocationID: jsonInvocation.InvocationID,
Arguments: arguments,
StreamIds: jsonInvocation.StreamIds,
}
return invocation, true, err
case 2:
streamItem := streamItemMessage{}
if err = json.Unmarshal(data, &streamItem); err != nil {
err = &jsonError{string(data), err}
}
return streamItem, true, err
case 3:
completion := completionMessage{}
if err = json.Unmarshal(data, &completion); err != nil {
err = &jsonError{string(data), err}
}
return completion, true, err
case 5:
invocation := cancelInvocationMessage{}
if err = json.Unmarshal(data, &invocation); err != nil {
err = &jsonError{string(data), err}
}
return invocation, true, err
case 7:
cm := closeMessage{}
if err = json.Unmarshal(data, &cm); err != nil {
err = &jsonError{string(data), err}
}
return cm, true, err
default:
return message, true, nil
}
}
func parseTextMessageFormat(buf *bytes.Buffer) ([]byte, error) {
// 30 = ASCII record separator
data, err := buf.ReadBytes(30)
if err != nil {
return data, err
}
// Remove the delimiter
return data[0 : len(data)-1], err
}
// WriteMessage writes a message as JSON to the specified writer
func (j *JSONHubProtocol) WriteMessage(message interface{}, writer io.Writer) error {
// TODO: Reduce the amount of copies
// We're copying because we want to write complete messages to the underlying Writer
buf := bytes.Buffer{}
if err := json.NewEncoder(&buf).Encode(message); err != nil {
return err
}
_ = j.dbg.Log(evt, "write", msg, string(buf.Bytes()))
if err := buf.WriteByte(30); err != nil {
return err
}
_, err := writer.Write(buf.Bytes())
return err
}
func (j *JSONHubProtocol) setDebugLogger(dbg StructuredLogger) {
j.dbg = log.WithPrefix(dbg, "ts", log.DefaultTimestampUTC, "protocol", "JSON")
}