internal/jsonrpc2: a basic json rpc library to build an lsp on top of

Change-Id: I6aa47fffcb29842e3194231e4ad4b6be4386d329
Reviewed-on: https://go-review.googlesource.com/136675
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
diff --git a/internal/jsonrpc2/jsonrpc2.go b/internal/jsonrpc2/jsonrpc2.go
new file mode 100644
index 0000000..f981e86
--- /dev/null
+++ b/internal/jsonrpc2/jsonrpc2.go
@@ -0,0 +1,341 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package jsonrpc2 is a minimal implementation of the JSON RPC 2 spec.
+// https://www.jsonrpc.org/specification
+// It is intended to be compatible with other implementations at the wire level.
+package jsonrpc2
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"sync"
+	"sync/atomic"
+)
+
+// Conn is a JSON RPC 2 client server connection.
+// Conn is bidirectional; it does not have a designated server or client end.
+type Conn struct {
+	handle     Handler
+	cancel     Canceler
+	log        Logger
+	stream     Stream
+	done       chan struct{}
+	err        error
+	seq        int64      // must only be accessed using atomic operations
+	pendingMu  sync.Mutex // protects the pending map
+	pending    map[ID]chan *Response
+	handlingMu sync.Mutex // protects the handling map
+	handling   map[ID]context.CancelFunc
+}
+
+// Handler is an option you can pass to NewConn to handle incoming requests.
+// If the request returns true from IsNotify then the Handler should not return a
+// result or error, otherwise it should handle the Request and return either
+// an encoded result, or an error.
+// Handlers must be concurrency-safe.
+type Handler = func(context.Context, *Conn, *Request) (interface{}, *Error)
+
+// Canceler is an option you can pass to NewConn which is invoked for
+// cancelled outgoing requests.
+// The request will have the ID filled in, which can be used to propagate the
+// cancel to the other process if needed.
+// It is okay to use the connection to send notifications, but the context will
+// be in the cancelled state, so you must do it with the background context
+// instead.
+type Canceler = func(context.Context, *Conn, *Request)
+
+// Logger is an option you can pass to NewConn which is invoked for
+// all messages flowing through a Conn.
+type Logger = func(mode string, id *ID, method string, payload *json.RawMessage, err *Error)
+
+// NewErrorf builds a Error struct for the suppied message and code.
+// If args is not empty, message and args will be passed to Sprintf.
+func NewErrorf(code int64, format string, args ...interface{}) *Error {
+	return &Error{
+		Code:    code,
+		Message: fmt.Sprintf(format, args...),
+	}
+}
+
+// NewConn creates a new connection object that reads and writes messages from
+// the supplied stream and dispatches incoming messages to the supplied handler.
+func NewConn(ctx context.Context, s Stream, options ...interface{}) *Conn {
+	conn := &Conn{
+		stream:   s,
+		done:     make(chan struct{}),
+		pending:  make(map[ID]chan *Response),
+		handling: make(map[ID]context.CancelFunc),
+	}
+	for _, opt := range options {
+		switch opt := opt.(type) {
+		case Handler:
+			if conn.handle != nil {
+				panic("Duplicate Handler function in options list")
+			}
+			conn.handle = opt
+		case Canceler:
+			if conn.cancel != nil {
+				panic("Duplicate Canceler function in options list")
+			}
+			conn.cancel = opt
+		case Logger:
+			if conn.log != nil {
+				panic("Duplicate Logger function in options list")
+			}
+			conn.log = opt
+		default:
+			panic(fmt.Errorf("Unknown option type %T in options list", opt))
+		}
+	}
+	if conn.handle == nil {
+		// the default handler reports a method error
+		conn.handle = func(ctx context.Context, c *Conn, r *Request) (interface{}, *Error) {
+			return nil, NewErrorf(CodeMethodNotFound, "method %q not found", r.Method)
+		}
+	}
+	if conn.cancel == nil {
+		// the default canceller does nothing
+		conn.cancel = func(context.Context, *Conn, *Request) {}
+	}
+	if conn.log == nil {
+		// the default logger does nothing
+		conn.log = func(string, *ID, string, *json.RawMessage, *Error) {}
+	}
+	go func() {
+		conn.err = conn.run(ctx)
+		close(conn.done)
+	}()
+	return conn
+}
+
+// Wait blocks until the connection is terminated, and returns any error that
+// cause the termination.
+func (c *Conn) Wait(ctx context.Context) error {
+	select {
+	case <-c.done:
+		return c.err
+	case <-ctx.Done():
+		return ctx.Err()
+	}
+}
+
+// Cancel cancels a pending Call on the server side.
+// The call is identified by its id.
+// JSON RPC 2 does not specify a cancel message, so cancellation support is not
+// directly wired in. This method allows a higher level protocol to choose how
+// to propagate the cancel.
+func (c *Conn) Cancel(id ID) {
+	c.handlingMu.Lock()
+	cancel := c.handling[id]
+	c.handlingMu.Unlock()
+	if cancel != nil {
+		cancel()
+	}
+}
+
+// Notify is called to send a notification request over the connection.
+// It will return as soon as the notification has been sent, as no response is
+// possible.
+func (c *Conn) Notify(ctx context.Context, method string, params interface{}) error {
+	jsonParams, err := marshalToRaw(params)
+	if err != nil {
+		return fmt.Errorf("marshalling notify parameters: %v", err)
+	}
+	request := &Request{
+		Method: method,
+		Params: jsonParams,
+	}
+	data, err := json.Marshal(request)
+	if err != nil {
+		return fmt.Errorf("marshalling notify request: %v", err)
+	}
+	c.log("notify <=", nil, request.Method, request.Params, nil)
+	return c.stream.Write(ctx, data)
+}
+
+// Call sends a request over the connection and then waits for a response.
+// If the response is not an error, it will be decoded into result.
+// result must be of a type you an pass to json.Unmarshal.
+func (c *Conn) Call(ctx context.Context, method string, params, result interface{}) error {
+	jsonParams, err := marshalToRaw(params)
+	if err != nil {
+		return fmt.Errorf("marshalling call parameters: %v", err)
+	}
+	// generate a new request identifier
+	id := ID{Number: atomic.AddInt64(&c.seq, 1)}
+	request := &Request{
+		ID:     &id,
+		Method: method,
+		Params: jsonParams,
+	}
+	// marshal the request now it is complete
+	data, err := json.Marshal(request)
+	if err != nil {
+		return fmt.Errorf("marshalling call request: %v", err)
+	}
+	// we have to add ourselves to the pending map before we send, otherwise we
+	// are racing the response
+	rchan := make(chan *Response)
+	c.pendingMu.Lock()
+	c.pending[id] = rchan
+	c.pendingMu.Unlock()
+	defer func() {
+		// clean up the pending response handler on the way out
+		c.pendingMu.Lock()
+		delete(c.pending, id)
+		c.pendingMu.Unlock()
+	}()
+	// now we are ready to send
+	c.log("call <=", request.ID, request.Method, request.Params, nil)
+	if err := c.stream.Write(ctx, data); err != nil {
+		// sending failed, we will never get a response, so don't leave it pending
+		return err
+	}
+	// now wait for the response
+	select {
+	case response := <-rchan:
+		// is it an error response?
+		if response.Error != nil {
+			return response.Error
+		}
+		if result == nil || response.Result == nil {
+			return nil
+		}
+		if err := json.Unmarshal(*response.Result, result); err != nil {
+			return fmt.Errorf("unmarshalling result: %v", err)
+		}
+		return nil
+	case <-ctx.Done():
+		// allow the handler to propagate the cancel
+		c.cancel(ctx, c, request)
+		return ctx.Err()
+	}
+}
+
+// combined has all the fields of both Request and Response.
+// We can decode this and then work out which it is.
+type combined struct {
+	VersionTag VersionTag       `json:"jsonrpc"`
+	ID         *ID              `json:"id,omitempty"`
+	Method     string           `json:"method"`
+	Params     *json.RawMessage `json:"params,omitempty"`
+	Result     *json.RawMessage `json:"result,omitempty"`
+	Error      *Error           `json:"error,omitempty"`
+}
+
+// Run starts a read loop on the supplied reader.
+// It must be called exactly once for each Conn.
+// It returns only when the reader is closed or there is an error in the stream.
+func (c *Conn) run(ctx context.Context) error {
+	ctx, cancelRun := context.WithCancel(ctx)
+	for {
+		// get the data for a message
+		data, err := c.stream.Read(ctx)
+		if err != nil {
+			// the stream failed, we cannot continue
+			return err
+		}
+		// read a combined message
+		msg := &combined{}
+		if err := json.Unmarshal(data, msg); err != nil {
+			// a badly formed message arrived, log it and continue
+			// we trust the stream to have isolated the error to just this message
+			c.log("read", nil, "", nil, NewErrorf(0, "unmarshal failed: %v", err))
+			continue
+		}
+		// work out which kind of message we have
+		switch {
+		case msg.Method != "":
+			// if method is set it must be a request
+			request := &Request{
+				Method: msg.Method,
+				Params: msg.Params,
+				ID:     msg.ID,
+			}
+			if request.IsNotify() {
+				c.log("notify =>", request.ID, request.Method, request.Params, nil)
+				// we have a Notify, forward to the handler in a go routine
+				go func() {
+					if _, err := c.handle(ctx, c, request); err != nil {
+						// notify produced an error, we can't forward it to the other side
+						// because there is no id, so we just log it
+						c.log("notify failed", nil, request.Method, nil, err)
+					}
+				}()
+			} else {
+				// we have a Call, forward to the handler in another go routine
+				reqCtx, cancelReq := context.WithCancel(ctx)
+				c.handlingMu.Lock()
+				c.handling[*request.ID] = cancelReq
+				c.handlingMu.Unlock()
+				go func() {
+					defer func() {
+						// clean up the cancel handler on the way out
+						c.handlingMu.Lock()
+						delete(c.handling, *request.ID)
+						c.handlingMu.Unlock()
+						cancelReq()
+					}()
+					c.log("call =>", request.ID, request.Method, request.Params, nil)
+					resp, callErr := c.handle(reqCtx, c, request)
+					var result *json.RawMessage
+					if result, err = marshalToRaw(resp); err != nil {
+						callErr = &Error{Message: err.Error()}
+					}
+					response := &Response{
+						Result: result,
+						Error:  callErr,
+						ID:     request.ID,
+					}
+					data, err := json.Marshal(response)
+					if err != nil {
+						// failure to marshal leaves the call without a response
+						// possibly we could attempt to respond with a different message
+						// but we can probably rely on timeouts instead
+						c.log("respond =!>", request.ID, request.Method, nil, NewErrorf(0, "%s", err))
+						return
+					}
+					c.log("respond =>", response.ID, "", response.Result, response.Error)
+					if err = c.stream.Write(ctx, data); err != nil {
+						// if a stream write fails, we really need to shut down the whole
+						// stream and return from the run
+						c.log("respond =!>", nil, request.Method, nil, NewErrorf(0, "%s", err))
+						cancelRun()
+						return
+					}
+				}()
+			}
+		case msg.ID != nil:
+			// we have a response, get the pending entry from the map
+			c.pendingMu.Lock()
+			rchan := c.pending[*msg.ID]
+			if rchan != nil {
+				delete(c.pending, *msg.ID)
+			}
+			c.pendingMu.Unlock()
+			// and send the reply to the channel
+			response := &Response{
+				Result: msg.Result,
+				Error:  msg.Error,
+				ID:     msg.ID,
+			}
+			c.log("response =>", response.ID, "", response.Result, response.Error)
+			rchan <- response
+			close(rchan)
+		default:
+			c.log("invalid =>", nil, "", nil, NewErrorf(0, "message not a call, notify or response, ignoring"))
+		}
+	}
+}
+
+func marshalToRaw(obj interface{}) (*json.RawMessage, error) {
+	data, err := json.Marshal(obj)
+	if err != nil {
+		return nil, err
+	}
+	raw := json.RawMessage(data)
+	return &raw, nil
+}
diff --git a/internal/jsonrpc2/jsonrpc2_test.go b/internal/jsonrpc2/jsonrpc2_test.go
new file mode 100644
index 0000000..2556083
--- /dev/null
+++ b/internal/jsonrpc2/jsonrpc2_test.go
@@ -0,0 +1,159 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package jsonrpc2_test
+
+import (
+	"context"
+	"encoding/json"
+	"flag"
+	"fmt"
+	"io"
+	"path"
+	"reflect"
+	"testing"
+
+	"golang.org/x/tools/internal/jsonrpc2"
+)
+
+var logRPC = flag.Bool("logrpc", false, "Enable jsonrpc2 communication logging")
+
+type callTest struct {
+	method string
+	params interface{}
+	expect interface{}
+}
+
+var callTests = []callTest{
+	{"no_args", nil, true},
+	{"one_string", "fish", "got:fish"},
+	{"one_number", 10, "got:10"},
+	{"join", []string{"a", "b", "c"}, "a/b/c"},
+	//TODO: expand the test cases
+}
+
+func (test *callTest) newResults() interface{} {
+	switch e := test.expect.(type) {
+	case []interface{}:
+		var r []interface{}
+		for _, v := range e {
+			r = append(r, reflect.New(reflect.TypeOf(v)).Interface())
+		}
+		return r
+	case nil:
+		return nil
+	default:
+		return reflect.New(reflect.TypeOf(test.expect)).Interface()
+	}
+}
+
+func (test *callTest) verifyResults(t *testing.T, results interface{}) {
+	if results == nil {
+		return
+	}
+	val := reflect.Indirect(reflect.ValueOf(results)).Interface()
+	if !reflect.DeepEqual(val, test.expect) {
+		t.Errorf("%v:Results are incorrect, got %+v expect %+v", test.method, val, test.expect)
+	}
+}
+
+func TestPlainCall(t *testing.T) {
+	ctx := context.Background()
+	a, b := prepare(ctx, t, false)
+	for _, test := range callTests {
+		results := test.newResults()
+		if err := a.Call(ctx, test.method, test.params, results); err != nil {
+			t.Fatalf("%v:Call failed: %v", test.method, err)
+		}
+		test.verifyResults(t, results)
+		if err := b.Call(ctx, test.method, test.params, results); err != nil {
+			t.Fatalf("%v:Call failed: %v", test.method, err)
+		}
+		test.verifyResults(t, results)
+	}
+}
+
+func TestHeaderCall(t *testing.T) {
+	ctx := context.Background()
+	a, b := prepare(ctx, t, true)
+	for _, test := range callTests {
+		results := test.newResults()
+		if err := a.Call(ctx, test.method, test.params, results); err != nil {
+			t.Fatalf("%v:Call failed: %v", test.method, err)
+		}
+		test.verifyResults(t, results)
+		if err := b.Call(ctx, test.method, test.params, results); err != nil {
+			t.Fatalf("%v:Call failed: %v", test.method, err)
+		}
+		test.verifyResults(t, results)
+	}
+}
+
+func prepare(ctx context.Context, t *testing.T, withHeaders bool) (*testHandler, *testHandler) {
+	a := &testHandler{t: t}
+	b := &testHandler{t: t}
+	a.reader, b.writer = io.Pipe()
+	b.reader, a.writer = io.Pipe()
+	for _, h := range []*testHandler{a, b} {
+		h := h
+		if withHeaders {
+			h.stream = jsonrpc2.NewHeaderStream(h.reader, h.writer)
+		} else {
+			h.stream = jsonrpc2.NewStream(h.reader, h.writer)
+		}
+		args := []interface{}{handle}
+		if *logRPC {
+			args = append(args, jsonrpc2.Log)
+		}
+		h.Conn = jsonrpc2.NewConn(ctx, h.stream, args...)
+		go func() {
+			defer func() {
+				h.reader.Close()
+				h.writer.Close()
+			}()
+			if err := h.Conn.Wait(ctx); err != nil {
+				t.Fatalf("Stream failed: %v", err)
+			}
+		}()
+	}
+	return a, b
+}
+
+type testHandler struct {
+	t      *testing.T
+	reader *io.PipeReader
+	writer *io.PipeWriter
+	stream jsonrpc2.Stream
+	*jsonrpc2.Conn
+}
+
+func handle(ctx context.Context, c *jsonrpc2.Conn, r *jsonrpc2.Request) (interface{}, *jsonrpc2.Error) {
+	switch r.Method {
+	case "no_args":
+		if r.Params != nil {
+			return nil, jsonrpc2.NewErrorf(jsonrpc2.CodeInvalidParams, "Expected no params")
+		}
+		return true, nil
+	case "one_string":
+		var v string
+		if err := json.Unmarshal(*r.Params, &v); err != nil {
+			return nil, jsonrpc2.NewErrorf(jsonrpc2.CodeParseError, "%v", err.Error())
+		}
+		return "got:" + v, nil
+	case "one_number":
+		var v int
+		if err := json.Unmarshal(*r.Params, &v); err != nil {
+			return nil, jsonrpc2.NewErrorf(jsonrpc2.CodeParseError, "%v", err.Error())
+		}
+		return fmt.Sprintf("got:%d", v), nil
+	case "join":
+		var v []string
+		if err := json.Unmarshal(*r.Params, &v); err != nil {
+			return nil, jsonrpc2.NewErrorf(jsonrpc2.CodeParseError, "%v", err.Error())
+		}
+		return path.Join(v...), nil
+	default:
+		return nil, jsonrpc2.NewErrorf(jsonrpc2.CodeMethodNotFound, "method %q not found", r.Method)
+	}
+}
diff --git a/internal/jsonrpc2/log.go b/internal/jsonrpc2/log.go
new file mode 100644
index 0000000..3dbde8f
--- /dev/null
+++ b/internal/jsonrpc2/log.go
@@ -0,0 +1,34 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package jsonrpc2
+
+import (
+	"bytes"
+	"encoding/json"
+	"fmt"
+	"log"
+)
+
+// Log is an implementation of Logger that outputs using log.Print
+// It is not used by default, but is provided for easy logging in users code.
+func Log(mode string, id *ID, method string, payload *json.RawMessage, err *Error) {
+	buf := &bytes.Buffer{}
+	fmt.Fprint(buf, mode)
+	if id == nil {
+		fmt.Fprintf(buf, " []")
+	} else {
+		fmt.Fprintf(buf, " [%v]", id)
+	}
+	if method != "" {
+		fmt.Fprintf(buf, " %s", method)
+	}
+	if payload != nil {
+		fmt.Fprintf(buf, " %s", *payload)
+	}
+	if err != nil {
+		fmt.Fprintf(buf, " failed: %s", err)
+	}
+	log.Print(buf)
+}
diff --git a/internal/jsonrpc2/stream.go b/internal/jsonrpc2/stream.go
new file mode 100644
index 0000000..fe28c55
--- /dev/null
+++ b/internal/jsonrpc2/stream.go
@@ -0,0 +1,146 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package jsonrpc2
+
+import (
+	"bufio"
+	"context"
+	"encoding/json"
+	"fmt"
+	"io"
+	"strconv"
+	"strings"
+	"sync"
+)
+
+// Stream abstracts the transport mechanics from the JSON RPC protocol.
+// A Conn reads and writes messages using the stream it was provided on
+// construction, and assumes that each call to Read or Write fully transfers
+// a single message, or returns an error.
+type Stream interface {
+	// Read gets the next message from the stream.
+	// It is never called concurrently.
+	Read(context.Context) ([]byte, error)
+	// Write sends a message to the stream.
+	// It must be safe for concurrent use.
+	Write(context.Context, []byte) error
+}
+
+// NewStream returns a Stream built on top of an io.Reader and io.Writer
+// The messages are sent with no wrapping, and rely on json decode consistency
+// to determine message boundaries.
+func NewStream(in io.Reader, out io.Writer) Stream {
+	return &plainStream{
+		in:  json.NewDecoder(in),
+		out: out,
+	}
+}
+
+type plainStream struct {
+	in    *json.Decoder
+	outMu sync.Mutex
+	out   io.Writer
+}
+
+func (s *plainStream) Read(ctx context.Context) ([]byte, error) {
+	select {
+	case <-ctx.Done():
+		return nil, ctx.Err()
+	default:
+	}
+	var raw json.RawMessage
+	if err := s.in.Decode(&raw); err != nil {
+		return nil, err
+	}
+	return raw, nil
+}
+
+func (s *plainStream) Write(ctx context.Context, data []byte) error {
+	select {
+	case <-ctx.Done():
+		return ctx.Err()
+	default:
+	}
+	s.outMu.Lock()
+	_, err := s.out.Write(data)
+	s.outMu.Unlock()
+	return err
+}
+
+// NewHeaderStream returns a Stream built on top of an io.Reader and io.Writer
+// The messages are sent with HTTP content length and MIME type headers.
+// This is the format used by LSP and others.
+func NewHeaderStream(in io.Reader, out io.Writer) Stream {
+	return &headerStream{
+		in:  bufio.NewReader(in),
+		out: out,
+	}
+}
+
+type headerStream struct {
+	in    *bufio.Reader
+	outMu sync.Mutex
+	out   io.Writer
+}
+
+func (s *headerStream) Read(ctx context.Context) ([]byte, error) {
+	select {
+	case <-ctx.Done():
+		return nil, ctx.Err()
+	default:
+	}
+	var length int64
+	// read the header, stop on the first empty line
+	for {
+		line, err := s.in.ReadString('\n')
+		if err != nil {
+			return nil, fmt.Errorf("failed reading header line %q", err)
+		}
+		line = strings.TrimSpace(line)
+		// check we have a header line
+		if line == "" {
+			break
+		}
+		colon := strings.IndexRune(line, ':')
+		if colon < 0 {
+			return nil, fmt.Errorf("invalid header line %q", line)
+		}
+		name, value := line[:colon], strings.TrimSpace(line[colon+1:])
+		switch name {
+		case "Content-Length":
+			if length, err = strconv.ParseInt(value, 10, 32); err != nil {
+				return nil, fmt.Errorf("failed parsing Content-Length: %v", value)
+			}
+			if length <= 0 {
+				return nil, fmt.Errorf("invalid Content-Length: %v", length)
+			}
+		default:
+			// ignoring unknown headers
+		}
+	}
+	if length == 0 {
+		return nil, fmt.Errorf("missing Content-Length header")
+	}
+	data := make([]byte, length)
+	if _, err := io.ReadFull(s.in, data); err != nil {
+		return nil, err
+	}
+	return data, nil
+}
+
+func (s *headerStream) Write(ctx context.Context, data []byte) error {
+	select {
+	case <-ctx.Done():
+		return ctx.Err()
+	default:
+	}
+	s.outMu.Lock()
+	_, err := fmt.Fprintf(s.out, "Content-Length: %v\r\n\r\n", len(data))
+	if err == nil {
+		_, err = s.out.Write(data)
+	}
+	s.outMu.Unlock()
+	return err
+}
diff --git a/internal/jsonrpc2/wire.go b/internal/jsonrpc2/wire.go
new file mode 100644
index 0000000..3ff0c38
--- /dev/null
+++ b/internal/jsonrpc2/wire.go
@@ -0,0 +1,136 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package jsonrpc2
+
+import (
+	"encoding/json"
+	"fmt"
+	"strconv"
+)
+
+// this file contains the go forms of the wire specification
+// see http://www.jsonrpc.org/specification for details
+
+const (
+	// CodeUnknownError should be used for all non coded errors.
+	CodeUnknownError = -32001
+	// CodeParseError is used when invalid JSON was received by the server.
+	CodeParseError = -32700
+	//CodeInvalidRequest is used when the JSON sent is not a valid Request object.
+	CodeInvalidRequest = -32600
+	// CodeMethodNotFound should be returned by the handler when the method does
+	// not exist / is not available.
+	CodeMethodNotFound = -32601
+	// CodeInvalidParams should be returned by the handler when method
+	// parameter(s) were invalid.
+	CodeInvalidParams = -32602
+	// CodeInternalError is not currently returned but defined for completeness.
+	CodeInternalError = -32603
+)
+
+// Request is sent to a server to represent a Call or Notify operaton.
+type Request struct {
+	// VersionTag is always encoded as the string "2.0"
+	VersionTag VersionTag `json:"jsonrpc"`
+	// Method is a string containing the method name to invoke.
+	Method string `json:"method"`
+	// Params is either a struct or an array with the parameters of the method.
+	Params *json.RawMessage `json:"params,omitempty"`
+	// The id of this request, used to tie the Response back to the request.
+	// Will be either a string or a number. If not set, the Request is a notify,
+	// and no response is possible.
+	ID *ID `json:"id,omitempty"`
+}
+
+// Response is a reply to a Request.
+// It will always have the ID field set to tie it back to a request, and will
+// have either the Result or Error fields set depending on whether it is a
+// success or failure response.
+type Response struct {
+	// VersionTag is always encoded as the string "2.0"
+	VersionTag VersionTag `json:"jsonrpc"`
+	// Result is the response value, and is required on success.
+	Result *json.RawMessage `json:"result,omitempty"`
+	// Error is a structured error response if the call fails.
+	Error *Error `json:"error,omitempty"`
+	// ID must be set and is the identifier of the Request this is a response to.
+	ID *ID `json:"id,omitempty"`
+}
+
+// Error represents a structured error in a Response.
+type Error struct {
+	// Code is an error code indicating the type of failure.
+	Code int64 `json:"code"`
+	// Message is a short description of the error.
+	Message string `json:"message"`
+	// Data is optional structured data containing additional information about the error.
+	Data *json.RawMessage `json:"data"`
+}
+
+// VersionTag is a special 0 sized struct that encodes as the jsonrpc version
+// tag.
+// It will fail during decode if it is not the correct version tag in the
+// stream.
+type VersionTag struct{}
+
+// ID is a Request identifier.
+// Only one of either the Name or Number members will be set, using the
+// number form if the Name is the empty string.
+type ID struct {
+	Name   string
+	Number int64
+}
+
+// IsNotify returns true if this request is a notification.
+func (r *Request) IsNotify() bool {
+	return r.ID == nil
+}
+
+func (err *Error) Error() string {
+	if err == nil {
+		return ""
+	}
+	return err.Message
+}
+
+func (VersionTag) MarshalJSON() ([]byte, error) {
+	return json.Marshal("2.0")
+}
+
+func (VersionTag) UnmarshalJSON(data []byte) error {
+	version := ""
+	if err := json.Unmarshal(data, &version); err != nil {
+		return err
+	}
+	if version != "2.0" {
+		return fmt.Errorf("Invalid RPC version %v", version)
+	}
+	return nil
+}
+
+// String returns a string representation of the ID.
+// The representation is non ambiguous, string forms are quoted, number forms
+// are preceded by a #
+func (id *ID) String() string {
+	if id.Name != "" {
+		return strconv.Quote(id.Name)
+	}
+	return "#" + strconv.FormatInt(id.Number, 10)
+}
+
+func (id *ID) MarshalJSON() ([]byte, error) {
+	if id.Name != "" {
+		return json.Marshal(id.Name)
+	}
+	return json.Marshal(id.Number)
+}
+
+func (id *ID) UnmarshalJSON(data []byte) error {
+	*id = ID{}
+	if err := json.Unmarshal(data, &id.Number); err == nil {
+		return nil
+	}
+	return json.Unmarshal(data, &id.Name)
+}