Skip to content

Commit

Permalink
add custom HTTP client support
Browse files Browse the repository at this point in the history
  • Loading branch information
powerman committed Jul 20, 2016
1 parent bccf0e6 commit f64d347
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 4 deletions.
4 changes: 2 additions & 2 deletions jsonrpc2/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,8 @@ func (c *clientCodec) WriteRequest(r *rpc.Request, param interface{}) error {
type clientResponse struct {
Version string `json:"jsonrpc"`
ID *uint64 `json:"id"`
Result *json.RawMessage `json:"result"`
Error *Error `json:"error"`
Result *json.RawMessage `json:"result,omitempty"`
Error *Error `json:"error,omitempty"`
}

func (r *clientResponse) reset() {
Expand Down
22 changes: 22 additions & 0 deletions jsonrpc2/example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,19 @@ func Example() {
clientHTTP := jsonrpc2.NewHTTPClient("http://" + lnHTTP.Addr().String() + "/rpc")
defer clientHTTP.Close()

// Custom client use HTTP transport.
clientCustomHTTP := jsonrpc2.NewCustomHTTPClient(
"http://"+lnHTTP.Addr().String()+"/rpc",
jsonrpc2.DoerFunc(func(req *http.Request) (*http.Response, error) {
// Setup custom HTTP client.
client := &http.Client{}
// Modify request as needed.
req.Header.Set("Content-Type", "application/json-rpc")
return client.Do(req)
}),
)
defer clientCustomHTTP.Close()

var reply int

// Synchronous call using positional params and TCP.
Expand Down Expand Up @@ -126,6 +139,14 @@ func Example() {
fmt.Printf("Err1(): code=%d msg=%q data=%v\n", rpcerr.Code, rpcerr.Message, rpcerr.Data)
}

err = clientCustomHTTP.Call("ExampleSvc.Err2", nil, nil)
if err == rpc.ErrShutdown || err == io.ErrUnexpectedEOF {
fmt.Printf("Err2(): %q\n", err)
} else if err != nil {
rpcerr := jsonrpc2.ServerError(err)
fmt.Printf("Err2(): code=%d msg=%q data=%v\n", rpcerr.Code, rpcerr.Message, rpcerr.Data)
}

err = clientHTTP.Call("ExampleSvc.Err3", nil, nil)
if err == rpc.ErrShutdown || err == io.ErrUnexpectedEOF {
fmt.Printf("Err3(): %q\n", err)
Expand All @@ -139,5 +160,6 @@ func Example() {
// SumAll(3,5,-2)=6
// MapLen({a:10,b:20,c:30})=3
// Err1(): code=-32000 msg="some issue" data=<nil>
// Err2(): code=-32603 msg="bad HTTP Status: 415 Unsupported Media Type" data=<nil>
// Err3(): code=42 msg="some issue" data=[one two]
}
38 changes: 36 additions & 2 deletions jsonrpc2/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,24 @@ func (h *httpHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
}
}

// Doer is an interface for doing HTTP requests.
type Doer interface {

This comment has been minimized.

Copy link
@antness

antness Aug 25, 2016

Doesn't this match http.RoundTripper interface?

This comment has been minimized.

Copy link
@powerman

powerman Aug 25, 2016

Author Owner

Not really. It does match http.Client.Do, which differs from http.RoundTripper in behaviour related to handling high-level errors.

Do(req *http.Request) (resp *http.Response, err error)
}

// The DoerFunc type is an adapter to allow the use of ordinary
// functions as HTTP clients. If f is a function with the appropriate
// signature, DoerFunc(f) is a client that calls f.
type DoerFunc func(req *http.Request) (resp *http.Response, err error)

// DoerFunc calls f(req).
func (f DoerFunc) Do(req *http.Request) (resp *http.Response, err error) {
return f(req)
}

type httpClientConn struct {
url string
doer Doer
ready chan io.ReadCloser
body io.ReadCloser
}
Expand Down Expand Up @@ -98,7 +114,7 @@ func (conn *httpClientConn) Write(buf []byte) (int, error) {
req.Header.Add("Content-Type", contentType)
req.Header.Add("Accept", contentType)
var resp *http.Response
resp, err = (&http.Client{}).Do(req)
resp, err = conn.doer.Do(req)
if err != nil {
} else if resp.Header.Get("Content-Type") != contentType {
err = fmt.Errorf("bad HTTP Content-Type: %s", resp.Header.Get("Content-Type"))
Expand Down Expand Up @@ -134,5 +150,23 @@ func (conn *httpClientConn) Close() error {
// NewHTTPClient returns a new Client to handle requests to the
// set of services at the given url.
func NewHTTPClient(url string) *Client {
return NewClient(&httpClientConn{url: url, ready: make(chan io.ReadCloser, 16)})
return NewCustomHTTPClient(url, nil)
}

// NewCustomHTTPClient returns a new Client to handle requests to the
// set of services at the given url using provided doer (&http.Client{} by
// default).
//
// Use doer to customize HTTP authorization/headers/etc. sent with each
// request (it method Do() will receive already configured POST request
// with url, all required headers and body set according to specification).
func NewCustomHTTPClient(url string, doer Doer) *Client {
if doer == nil {
doer = &http.Client{}
}
return NewClient(&httpClientConn{
url: url,
doer: doer,
ready: make(chan io.ReadCloser, 16),
})
}

0 comments on commit f64d347

Please sign in to comment.