130 lines
3.1 KiB
Go
130 lines
3.1 KiB
Go
package rpc
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"io"
|
|
"strings"
|
|
"unicode/utf8"
|
|
)
|
|
|
|
type envelope struct {
|
|
Version int `json:"v"`
|
|
Type string `json:"type"`
|
|
ID string `json:"id,omitempty"`
|
|
Method string `json:"method,omitempty"`
|
|
Params json.RawMessage `json:"params,omitempty"`
|
|
Result json.RawMessage `json:"result,omitempty"`
|
|
Error *Error `json:"error,omitempty"`
|
|
TimeoutMS int64 `json:"timeoutMs,omitempty"`
|
|
}
|
|
|
|
func validName(s string, max int) bool {
|
|
if s == "" || len(s) > max {
|
|
return false
|
|
}
|
|
for _, c := range s {
|
|
if !(c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9' || strings.ContainsRune("._-/:", c)) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
func validError(e *Error) bool {
|
|
return e != nil && validName(e.Code, 64) && len(e.Message) <= 1024 && utf8.ValidString(e.Message)
|
|
}
|
|
|
|
func decode(data []byte, limit int) (envelope, error) {
|
|
var e envelope
|
|
if len(data) > limit || !utf8.Valid(data) {
|
|
return e, ErrProtocol
|
|
}
|
|
// Reject duplicate keys at every depth instead of accepting last-key-wins.
|
|
scan := json.NewDecoder(bytes.NewReader(data))
|
|
scan.UseNumber()
|
|
if err := uniqueValue(scan, 0); err != nil {
|
|
return e, ErrProtocol
|
|
}
|
|
if _, err := scan.Token(); err != io.EOF {
|
|
return e, ErrProtocol
|
|
}
|
|
d := json.NewDecoder(bytes.NewReader(data))
|
|
d.DisallowUnknownFields()
|
|
if err := d.Decode(&e); err != nil {
|
|
return e, ErrProtocol
|
|
}
|
|
if e.Version != 1 || e.TimeoutMS < 0 || e.TimeoutMS > int64((24*60*60)*1000) {
|
|
return e, ErrProtocol
|
|
}
|
|
switch e.Type {
|
|
case "call":
|
|
if !validName(e.ID, 64) || !validName(e.Method, 128) || len(e.Params) == 0 || len(e.Result) != 0 || e.Error != nil {
|
|
return e, ErrProtocol
|
|
}
|
|
case "event":
|
|
if e.ID != "" || !validName(e.Method, 128) || len(e.Params) == 0 || len(e.Result) != 0 || e.Error != nil || e.TimeoutMS != 0 {
|
|
return e, ErrProtocol
|
|
}
|
|
case "reply":
|
|
if !validName(e.ID, 64) || e.Method != "" || len(e.Params) != 0 || e.TimeoutMS != 0 || (len(e.Result) == 0) == (e.Error == nil) || (e.Error != nil && !validError(e.Error)) {
|
|
return e, ErrProtocol
|
|
}
|
|
case "cancel":
|
|
if !validName(e.ID, 64) || e.Method != "" || len(e.Params) != 0 || len(e.Result) != 0 || e.Error != nil || e.TimeoutMS != 0 {
|
|
return e, ErrProtocol
|
|
}
|
|
default:
|
|
return e, ErrProtocol
|
|
}
|
|
return e, nil
|
|
}
|
|
|
|
func uniqueValue(d *json.Decoder, depth int) error {
|
|
if depth > 32 {
|
|
return ErrProtocol
|
|
}
|
|
t, err := d.Token()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
delim, ok := t.(json.Delim)
|
|
if !ok {
|
|
return nil
|
|
}
|
|
switch delim {
|
|
case '{':
|
|
seen := make(map[string]bool)
|
|
for d.More() {
|
|
k, err := d.Token()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
key, ok := k.(string)
|
|
if !ok || seen[key] {
|
|
return ErrProtocol
|
|
}
|
|
seen[key] = true
|
|
if err := uniqueValue(d, depth+1); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
t, err = d.Token()
|
|
if err != nil || t != json.Delim('}') {
|
|
return ErrProtocol
|
|
}
|
|
case '[':
|
|
for d.More() {
|
|
if err := uniqueValue(d, depth+1); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
t, err = d.Token()
|
|
if err != nil || t != json.Delim(']') {
|
|
return ErrProtocol
|
|
}
|
|
default:
|
|
return ErrProtocol
|
|
}
|
|
return nil
|
|
}
|