Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JSON RPC over HTTP #576

Merged
merged 45 commits into from
Feb 19, 2018
Merged

JSON RPC over HTTP #576

merged 45 commits into from
Feb 19, 2018

Conversation

rossmcf
Copy link
Contributor

@rossmcf rossmcf commented Jul 17, 2017

This PR continues @blainsmith's work from #451 to address issue #575.

This is still a work in progress, but I wanted to open the PR early, to invite discussion.

I've taken the liberty of staring a new fork. @blainsmith's commits have been imported, so that he's duly credited.

I've added some tests, and hopefully addressed some of the feedback from the previous PR, namely creating an RequestID object with a custom JSONUnmarshal, that can handle IDs as strings, ints or floats. @groob, I'd appreciate your thoughts on that.

I'll try to tackle some more of the feedback, regarding server and encoding options in due course.

Thanks in advance for any feedback you can offer.

Ross

@blainsmith
Copy link

Thanks for the props and also moving it forward! I got pulled away to do other things and never got a chance to move forward with it.

@peterbourgon
Copy link
Member

With the caveats that I don't know a whole lot about JSON RPC, and I've only just skimmed the code: the shape of this looks quite good to me. I'll have a few nits to pick in a final review, but otherwise 👍 from me so far.

@basvanbeek
Copy link
Member

I'll try to take a look too. I have used JSON-RPC extensively in the past.

@peterbourgon
Copy link
Member

@rossmcf Just ping me when you're ready for a final review! 👍

@rossmcf
Copy link
Contributor Author

rossmcf commented Jul 31, 2017

Thanks @peterbourgon. I should hopefully have some time to tidy it up towards the end of the week.

@rossmcf
Copy link
Contributor Author

rossmcf commented Aug 3, 2017

@peterbourgon I'm getting a little blind to this now, so would appreciate your input.

With regard to examples, would I be best to add a JSON RPC transport to the addsvc?

@rossmcf
Copy link
Contributor Author

rossmcf commented Aug 3, 2017

Also, any pointers on why the travis build is failing would be useful. I can't make sense of the output.

@rjeczalik
Copy link

I can't make sense of the output.

--- FAIL: TestDefaultEndpointer (0.03s)
	endpointer_test.go:33: want 2, have 0
	endpointer_test.go:39: endpoint a closed, good
	endpointer_test.go:45: endpoint b closed, good

This test is racy.

It expects the cache to have certain elements, however it's updated concurrently.

It's pretty easy to reproduce:

$ go get golang.org/x/tools/cmd/stress
$ go test -race -c .
$ stress ./sd.test 

/var/folders/dl/s7jq4vyn7358p1mtsmn3bk5w0000gn/T/go-stress749329897
--- FAIL: TestDefaultEndpointer (0.00s)
   endpointer_test.go:33: want 2, have 0
   endpointer_test.go:39: endpoint a closed, good
   endpointer_test.go:45: endpoint b closed, good
FAIL


ERROR: exit status 1

33 runs so far, 1 failures

@fredipevcin
Copy link

@rossmcf @peterbourgon What do you think about https://github.com/rossmcf/kit/compare/json-rpc-over-http...fredipevcin:json-rpc-over-http?expand=1

Idea is that method correlates to service.

For every service it is possible to specify logger, before and after instead of only for whole RPC handler.

handler := jsonrpc.NewServer(
    jsonrpc.ServiceMap{
        "add": jsonrpc.NewService(
            func(ctx context.Context, request interface{}) (response interface{}, err error) {
                return nil, nil
            },
            func(ctx context.Context, msg json.RawMessage) (interface{}, error) {
                return nil, nil
            },
            func(ctx context.Context, result interface{}) (json.RawMessage, error) {
                return []byte(`"result"`), nil
            },
            jsonrpc.ServiceBefore(func(ctx context.Context, _ http.Header) context.Context {
                return ctx
            }),
        ),
    },
)
http.Handle("/v1/rpc", handler)

Service is not aware about http.Request nor http.ResponseWritter because Server takes care of this.

Code is just WIP as I wanted your option on it if it makes sense.

@fredipevcin
Copy link

I'm currently working on JSON-RPC implementation https://github.com/fredipevcin/gokit-jsonrpc which we will use in-house. It is not yet completed.

@peterbourgon
Copy link
Member

@rossmcf Thanks and please forgive my delay! At a glance this seems to be alright. Can you help me review this more thoroughly (and help future users!) by adding a runnable example? I think the best thing would be to add JSON RPC support to examples/addsvc, but I'd also look at something standalone if there were good reasons that weren't feasible.

@rossmcf
Copy link
Contributor Author

rossmcf commented Feb 2, 2018

Thanks for your feedback, everyone.

Test coverage in the package is looking more respectable now, and I think the issues raised have been addressed. Over to you.

Copy link
Member

@peterbourgon peterbourgon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two outstanding issues from my perspective. Once those are resolved I'm happy to merge!

return &RequestID{
intValue: int(id),
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't goroutine-safe, and the type that includes it (the client) doesn't perform any synchronization. You've got a data race! You need to either put synchronization in the client, and un-export this type or explicitly mark it as not goroutine-safe; or put synchronization in this type directly.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I think that's addressed now, with an atomic.AddUint64 in Generate().


// ServerBefore functions are executed on the HTTP request object before the
func ServerBefore(before ...httptransport.RequestFunc) ServerOption {
// request is decoded.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still got a problem here.

@peterbourgon
Copy link
Member

peterbourgon commented Feb 5, 2018

Oh, and the test failure reports

# github.com/go-kit/kit/transport/http/jsonrpc_test
transport/http/jsonrpc/client_test.go:119: Fatalf format %d has arg requestAtServer.ID of wrong type *github.com/go-kit/kit/transport/http/jsonrpc.RequestID
transport/http/jsonrpc/client_test.go:171: Fatalf format %d has arg rs of wrong type string

which I reckon is fixable, too.

Make auto-incrementing IDs goroutine safe.
Make RequestIDGenerator interface public.
The client had been using the RequestID type in requests. Making this
serialize in a deterministic and predictable way was going to be fiddly, so
I decided to allow interface{} for IDs, client-side.
@rossmcf
Copy link
Contributor Author

rossmcf commented Feb 16, 2018

OK, coveralls is finally satisfied. Hopefully that's everything taken care of.

t.Fatalf("result is not string: (%T)%+v", result, result)
}
if rs != "boogaloo" {
t.Fatalf("want=boogaloo, got=%d", rs)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

# github.com/go-kit/kit/transport/http/jsonrpc_test
transport/http/jsonrpc/client_test.go:190: Fatalf format %d has arg rs of wrong type string
FAIL	github.com/go-kit/kit/transport/http/jsonrpc [build failed]

@peterbourgon
Copy link
Member

Build failure due to an aggressive lint.

@rossmcf
Copy link
Contributor Author

rossmcf commented Feb 19, 2018

@peterbourgon Thanks for spotting that. Oddly, it didn't come up when I ran lint. I've made a few more tweaks to quieten (although not completely silence) the linter.

@peterbourgon
Copy link
Member

Yes! Thank you so much! This is great. I'll cut a release this week, probably.

@peterbourgon peterbourgon merged commit 3bfaad9 into go-kit:master Feb 19, 2018
@rossmcf
Copy link
Contributor Author

rossmcf commented Feb 20, 2018

Thank you for all of your support with this.

In the release notes, might you be able to mention the original contribution of @blainsmith, and my employer, @senseyeio, on whose time I did most of the work for this?

@sas1024
Copy link

sas1024 commented Mar 5, 2018

Any plans for new release?

@peterbourgon
Copy link
Member

Want to wait for 1 or 2 more inflight PRs to land. Sorry for the delay.

jamesgist pushed a commit to jamesgist/kit that referenced this pull request Nov 1, 2024
* first pass at JSON RPC HTTP transport

* example implementation of JSON RPC over HTTP

* Add ID type for JSON RPC Request, with tests.

* Add basic server testing for JSON RPC.

Add basic server tests, following example from http transport. Switch Response.Error to pointer, to make absence clearer.

* Handle unregistered JSON RPC methods.

* Package tidy-up.

* Test ServerBefore / ServerAfter for JSON RPC.

* More JSON RPC tests.

* Remove JSON RPC from addsvc example, pending full JSON RPC example.

* Remove JSON RPC from addsvc example, pending full JSON RPC example.

* Remove context field from jsonrpc.Server.

* Add JSON content type to all JSON RPC responses.

* Add JSON content type to all JSON RPC responses.

* Remove client-side JSON RPC funcs for now.

* Document interceptingWriter

* Add JSON RPC doc.go.

* Add README for JSON RPC.

* Wire in JSON RPC addsvc.

* Add JSON RPC to Addsvc CLI.

* Set JSONRPC version in responses.

* Add JSON RPC client to addcli example.

* Wire in client middlewares for JSON RPC addsvc example.

* Fix rate limiter dependency.

* Add concat JSON RPC method.

* Improve JSON RPC server test coverage.

* Add error tests.

* Clarify ErrorCoder in comment.

* Make endpoint consistent in README.

* Gofmt handler example in README.

* Auto-increment client IDs. Allow for customisation.

* Add happy-path test for JSON RPC client.

* Provide default encoder/decoder in JSON RPC client.

* Fix comment line.

* RequestIDGenerator tidy-up.

Make auto-incrementing IDs goroutine safe.
Make RequestIDGenerator interface public.

* Fix client ID creation.

The client had been using the RequestID type in requests. Making this
serialize in a deterministic and predictable way was going to be fiddly, so
I decided to allow interface{} for IDs, client-side.

* Test client request ID more effectively.

* Cover client options in test.

* Improve error test coverage.

* Fix format spec in test output.

* Tweaks to satisfy the linter.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants