WIP mrpc/jstreamrpc make jstreamrpc simpler and implement mrpc.Debug
This commit is contained in:
parent
bdfd7500fb
commit
dfa32aa6d9
64
mrpc/debug.go
Normal file
64
mrpc/debug.go
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
package mrpc
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
|
// Debug data is arbitrary data embedded in a Call's request by the Client or in
|
||||||
|
// its Response by the Server. Debug data is organized into namespaces to help
|
||||||
|
// avoid conflicts while still preserving serializability.
|
||||||
|
//
|
||||||
|
// Debug data is intended to be used for debugging purposes only, and should
|
||||||
|
// never be used to effect the path-of-action a Call takes. Put another way:
|
||||||
|
// when implementing a Call always assume that the Debug info has been
|
||||||
|
// accidentally removed from the Call's request/response.
|
||||||
|
type Debug map[string]map[string]interface{}
|
||||||
|
|
||||||
|
// Copy returns an identical copy of the Debug being called upon
|
||||||
|
func (d Debug) Copy() Debug {
|
||||||
|
d2 := make(Debug, len(d))
|
||||||
|
for ns, kv := range d {
|
||||||
|
d2[ns] = make(map[string]interface{}, len(kv))
|
||||||
|
for k, v := range kv {
|
||||||
|
d2[ns][k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return d2
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set returns a copy of the Debug instance with the key set to the value within
|
||||||
|
// the given namespace.
|
||||||
|
func (d Debug) Set(ns, key string, val interface{}) Debug {
|
||||||
|
d = d.Copy()
|
||||||
|
if d[ns] == nil {
|
||||||
|
d[ns] = map[string]interface{}{}
|
||||||
|
}
|
||||||
|
d[ns][key] = val
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns the value for the key within the namespace. Also returns whether
|
||||||
|
// or not the key was set. This method will never panic.
|
||||||
|
func (d Debug) Get(ns, key string) (interface{}, bool) {
|
||||||
|
if d == nil || d[ns] == nil {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
val, ok := d[ns][key]
|
||||||
|
return val, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
type debugKey int
|
||||||
|
|
||||||
|
// CtxWithDebug returns a new Context with the given Debug embedded in it,
|
||||||
|
// overwriting any previously embedded Debug.
|
||||||
|
func CtxWithDebug(ctx context.Context, d Debug) context.Context {
|
||||||
|
return context.WithValue(ctx, debugKey(0), d)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CtxDebug returns the Debug instance embedded in the Context, or nil if none
|
||||||
|
// has been embedded.
|
||||||
|
func CtxDebug(ctx context.Context) Debug {
|
||||||
|
d := ctx.Value(debugKey(0))
|
||||||
|
if d == nil {
|
||||||
|
return Debug(nil)
|
||||||
|
}
|
||||||
|
return d.(Debug)
|
||||||
|
}
|
@ -14,20 +14,30 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// TODO Debug
|
// TODO Debug
|
||||||
|
// - ReqHead
|
||||||
|
// - client encodes into context
|
||||||
|
// - handler decodes from context
|
||||||
|
// - ResTail
|
||||||
|
// - handler ?
|
||||||
|
// - client ?
|
||||||
|
|
||||||
// TODO Error?
|
// TODO Error?
|
||||||
// TODO SizeHints
|
// TODO SizeHints
|
||||||
// TODO it seems like request tail and response head aren't useful nor
|
|
||||||
// convenient to use, might be better to leave them out
|
|
||||||
|
|
||||||
type headTail struct {
|
type debug struct {
|
||||||
Debug map[string]map[string]json.RawMessage `json:"debug,omitempty"`
|
Debug map[string]map[string]json.RawMessage `json:"debug,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type reqHead struct {
|
type reqHead struct {
|
||||||
headTail
|
debug
|
||||||
Method string `json:"method"`
|
Method string `json:"method"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type resTail struct {
|
||||||
|
debug
|
||||||
|
Error interface{} `json:"err,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type ctxVal int
|
type ctxVal int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -105,9 +115,7 @@ func HandleCall(
|
|||||||
// marshalBody is called they will block forever. Probably need to cancel
|
// marshalBody is called they will block forever. Probably need to cancel
|
||||||
// the context to let them know?
|
// the context to let them know?
|
||||||
|
|
||||||
if err := w.EncodeValue(headTail{}); err != nil {
|
if err := marshalBody(w, ret); err != nil {
|
||||||
return err
|
|
||||||
} else if err := marshalBody(w, ret); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,12 +127,11 @@ func HandleCall(
|
|||||||
// Reading the tail (and maybe discarding the body) should only be done once
|
// Reading the tail (and maybe discarding the body) should only be done once
|
||||||
// marshalBody has finished
|
// marshalBody has finished
|
||||||
if !didReadBody {
|
if !didReadBody {
|
||||||
// TODO if this errors then presumably reading the tail will too?
|
// TODO what if this errors?
|
||||||
r.Next().Discard()
|
r.Next().Discard()
|
||||||
}
|
}
|
||||||
if err := r.Next().Discard(); err != nil {
|
|
||||||
return err
|
// TODO write response tail
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,9 @@
|
|||||||
// This package contains a few fundamental types: Handler, Call, and
|
// This package contains a few fundamental types: Handler, Call, and
|
||||||
// Client. Together these form the components needed to implement nearly any RPC
|
// Client. Together these form the components needed to implement nearly any RPC
|
||||||
// system.
|
// system.
|
||||||
|
//
|
||||||
|
// TODO document examples
|
||||||
|
// TODO document Debug?
|
||||||
package mrpc
|
package mrpc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -21,8 +21,7 @@ this spec:
|
|||||||
|
|
||||||
* In all JSON object specs, a field which is not required can be omitted
|
* In all JSON object specs, a field which is not required can be omitted
|
||||||
entirely, and its value is assumed to be the expected type's empty value (e.g.
|
entirely, and its value is assumed to be the expected type's empty value (e.g.
|
||||||
`""` for strings, `0` for numbers, `{}` for objects, `null` for any-JSON
|
`""` for strings, `0` for numbers, `{}` for objects).
|
||||||
types).
|
|
||||||
|
|
||||||
## Debug
|
## Debug
|
||||||
|
|
||||||
@ -45,11 +44,11 @@ client.
|
|||||||
|
|
||||||
## Call request
|
## Call request
|
||||||
|
|
||||||
A call request is defined as being three jstream elements read off the pipe by
|
A call request is defined as being two jstream elements read off the pipe by the
|
||||||
the server. Once all three elements have been read the request is considered to
|
server. Once both elements have been read the request is considered to be
|
||||||
be completely consumed and the pipe may be used for a new request.
|
completely consumed and the pipe may be used for a new request.
|
||||||
|
|
||||||
The three elements of the request stream are specified as follows:
|
The two elements of the request stream are specified as follows:
|
||||||
|
|
||||||
* The first element, the head, is a JSON value with an object containing a
|
* The first element, the head, is a JSON value with an object containing a
|
||||||
`name` field, which identifies the call being made, and optionally a `debug`
|
`name` field, which identifies the call being made, and optionally a `debug`
|
||||||
@ -60,32 +59,23 @@ The three elements of the request stream are specified as follows:
|
|||||||
the call. It's up to the client and server to coordinate beforehand what to
|
the call. It's up to the client and server to coordinate beforehand what to
|
||||||
expect here.
|
expect here.
|
||||||
|
|
||||||
* The third element, the tail, is a JSON value with an object optionally
|
|
||||||
containing a `debug` field.
|
|
||||||
|
|
||||||
## Call response
|
## Call response
|
||||||
|
|
||||||
A call response almost the same as the call request. The only difference is the
|
A call response is defined as being two jstream elements read off the pipe by
|
||||||
lack of `name` field in the head, and the addition of the `err` field in the
|
the client. Once both elements have been read the response is considered to be
|
||||||
tail.
|
completely consumed and the pipe may be used for a new request.
|
||||||
|
|
||||||
A call response is defined as being three jstream elements read off the pipe by
|
The two elements of the response stream are specified as follows:
|
||||||
the client. Once all three elements have been read the response is considered to
|
|
||||||
be completely consumed and the pipe may be used for a new request.
|
|
||||||
|
|
||||||
The three elements of the response stream are specified as follows:
|
* The first element is the response from the call. This may be a JSON value, a
|
||||||
|
|
||||||
* The first element, the head, is a JSON value with an object containing
|
|
||||||
optionally containing a `debug` field.
|
|
||||||
|
|
||||||
* The second element is the response from the call. This may be a JSON value, a
|
|
||||||
byte blob, or an embedded stream containing even more elements, depending on
|
byte blob, or an embedded stream containing even more elements, depending on
|
||||||
the call. It's up to the client and server to coordinate beforehand what to
|
the call. It's up to the client and server to coordinate beforehand what to
|
||||||
expect here.
|
expect here.
|
||||||
|
|
||||||
* The third element, the tail, is a JSON value with an object optionally
|
* The second element, the tail, is a JSON value with an object optionally
|
||||||
containing an `err` field, and optionally containing `debug` field. The value
|
containing an `err` field, and optionally containing a `debug` field. The
|
||||||
of `err` may be any JSON value which is meaningful to the client and server.
|
value of `err` may be any JSON value which is meaningful to the client and
|
||||||
|
server. This element is required even if there's no `err` or `debug` data.
|
||||||
|
|
||||||
## Pipelining
|
## Pipelining
|
||||||
|
|
||||||
@ -95,8 +85,9 @@ effect this means that the server can be sending back response data while the
|
|||||||
client is still sending request data.
|
client is still sending request data.
|
||||||
|
|
||||||
Once the server has sent the response tail it can assume the call has completed
|
Once the server has sent the response tail it can assume the call has completed
|
||||||
successfully and ignore all subsequent request data (though it must still fully
|
successfully, and the server will ignore all subsequent request data (though it
|
||||||
read the three request elements off the pipe in order to use it again).
|
must still fully read the request body off the pipe in order to use the pipe
|
||||||
Likewise, once a client receives the response tail it can cancel whatever it's
|
again for a new call).
|
||||||
doing, finish sending the request argument and tail as soon as possible, and
|
|
||||||
assume the call has been completed.
|
From the client's perspective once the the response tail has been received it
|
||||||
|
can cancel whatever request body data it's in the process of sending.
|
||||||
|
Loading…
Reference in New Issue
Block a user